1 /*< ilib.js */
  2 /*
  3  * ilib.js - define the ilib name space
  4  * 
  5  * Copyright © 2012-2015, JEDLSoft
  6  *
  7  * Licensed under the Apache License, Version 2.0 (the "License");
  8  * you may not use this file except in compliance with the License.
  9  * You may obtain a copy of the License at
 10  *
 11  *     http://www.apache.org/licenses/LICENSE-2.0
 12  *
 13  * Unless required by applicable law or agreed to in writing, software
 14  * distributed under the License is distributed on an "AS IS" BASIS,
 15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  *
 17  * See the License for the specific language governing permissions and
 18  * limitations under the License.
 19  */
 20 
 21 /**
 22  * @namespace The global namespace that contains general ilib functions useful
 23  * to all of ilib
 24  * 
 25  * @version "12.0.4"
 26  */
 27 var ilib = ilib || {};
 28 
 29 /** @private */
 30 ilib._ver = function() {
 31     return "12.0.4"
 32     ;
 33 };
 34 
 35 /**
 36  * Return the current version of ilib.
 37  * 
 38  * @static
 39  * @return {string} a version string for this instance of ilib
 40  */
 41 ilib.getVersion = function () {
 42 	// TODO: need some way of getting the version number under dynamic load code
 43     return ilib._ver() || "12.0"; 
 44 };
 45 
 46 /**
 47  * Place where resources and such are eventually assigned.
 48  */
 49 ilib.data = {
 50 	/** @type {{ccc:Object.<string,number>,nfd:Object.<string,string>,nfc:Object.<string,string>,nfkd:Object.<string,string>,nfkc:Object.<string,string>}} */
 51     norm: {
 52     	ccc: {},
 53     	nfd: {},
 54     	nfc: {},
 55     	nfkd: {},
 56     	nfkc: {}
 57     },
 58     zoneinfo: {
 59         "Etc/UTC":{"o":"0:0","f":"UTC"},
 60         "local":{"f":"local"}
 61     },
 62     /** @type {Object.<string,{to:Object.<string,string>,from:Object.<string,number>}>} */ charmaps: {},
 63     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype: null,
 64     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_c: null,
 65     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_l: null,
 66     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_m: null,
 67     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_p: null,
 68     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_z: null,
 69     /** @type {null|Object.<string,Array.<Array.<number>>>} */ scriptToRange: null,
 70     /** @type {null|Object.<string,string|Object.<string|Object.<string,string>>>} */ dateformats: null,
 71     /** @type {null|Array.<string>} */ timezones: []
 72 };
 73 
 74 /*
 75 if (typeof(window) !== 'undefined') {
 76     window["ilib"] = ilib;
 77 }
 78 */
 79 
 80 // export ilib for use as a module in nodejs
 81 if (typeof(module) !== 'undefined') {
 82     
 83     module.exports.ilib = ilib;  // for backwards compatibility with older versions of ilib
 84 }
 85 
 86 /**
 87  * Sets the pseudo locale. Pseudolocalization (or pseudo-localization) is used for testing
 88  * internationalization aspects of software. Instead of translating the text of the software
 89  * into a foreign language, as in the process of localization, the textual elements of an application
 90  * are replaced with an altered version of the original language.These specific alterations make
 91  * the original words appear readable, but include the most problematic characteristics of 
 92  * the world's languages: varying length of text or characters, language direction, and so on.
 93  * Regular Latin pseudo locale: eu-ES and RTL pseudo locale: ps-AF
 94  * 
 95  * @param {string|undefined|null} localename the locale specifier for the pseudo locale
 96  */
 97 ilib.setAsPseudoLocale = function (localename) {
 98    if (localename) {
 99 	   ilib.pseudoLocales.push(localename)
100    }
101 };
102 
103 /**
104  * Reset the list of pseudo locales back to the default single locale of zxx-XX.
105  * @static
106  */
107 ilib.clearPseudoLocales = function() {
108 	ilib.pseudoLocales = [
109         "zxx-XX",
110         "zxx-Cyrl-XX",
111         "zxx-Hans-XX",
112         "zxx-Hebr-XX"
113     ];
114 };
115 
116 ilib.clearPseudoLocales();
117 
118 /**
119  * Return the name of the platform
120  * @private
121  * @static
122  * @return {string} string naming the platform
123  */
124 ilib._getPlatform = function () {
125     if (!ilib._platform) {
126     	try {
127     		if (typeof(java.lang.Object) !== 'undefined') {
128     			ilib._platform = (typeof(process) !== 'undefined') ? "trireme" : "rhino";
129     			return ilib._platform;
130     		}
131     	} catch (e) {}
132     	
133         if (typeof(process) !== 'undefined' && typeof(module) !== 'undefined') {
134             ilib._platform = "nodejs";
135         } else if (typeof(Qt) !== 'undefined') {
136         	ilib._platform = "qt";
137         } else if (typeof(window) !== 'undefined') {
138             ilib._platform = (typeof(PalmSystem) !== 'undefined') ? "webos" : "browser";
139         } else {
140             ilib._platform = "unknown";
141         }
142     }    
143     return ilib._platform;
144 };
145 
146 /**
147  * If this ilib is running in a browser, return the name of that browser.
148  * @private
149  * @static
150  * @return {string|undefined} the name of the browser that this is running in ("firefox", "chrome", "ie", 
151  * "safari", or "opera"), or undefined if this is not running in a browser or if
152  * the browser name could not be determined 
153  */
154 ilib._getBrowser = function () {
155 	var browser = undefined;
156 	if (ilib._getPlatform() === "browser") {
157 		if (navigator && navigator.userAgent) {
158 			if (navigator.userAgent.indexOf("Firefox") > -1) {
159 				browser = "firefox";
160 			}
161 			if (navigator.userAgent.indexOf("Opera") > -1) {
162 				browser = "opera";
163 			}
164 			if (navigator.userAgent.indexOf("Chrome") > -1) {
165 				browser = "chrome";
166 			}
167 			if (navigator.userAgent.indexOf(" .NET") > -1) {
168 				browser = "ie";
169 			}
170 			if (navigator.userAgent.indexOf("Safari") > -1) {
171 				// chrome also has the string Safari in its userAgent, but the chrome case is 
172 				// already taken care of above
173 				browser = "safari";
174 			}
175             if (navigator.userAgent.indexOf("Edge") > -1) {                
176                 browser = "Edge";
177             }
178             if (navigator.userAgent.search(/iPad|iPhone|iPod/) > -1) {
179                 // Due to constraints of the iOS platform, 
180                 // all browser must be built on top of the WebKit rendering engine
181                 browser = "iOS";
182             }
183 		}
184 	}
185 	return browser;
186 };
187 
188 /**
189  * Return the value of a global variable given its name in a way that works 
190  * correctly for the current platform.
191  * @private
192  * @static
193  * @param {string} name the name of the variable to return
194  * @return {*} the global variable, or undefined if it does not exist
195  */
196 ilib._global = function(name) {
197     switch (ilib._getPlatform()) {
198         case "rhino":
199             var top = (function() {
200               return (typeof global === 'object') ? global : this;
201             })();
202             break;
203         case "nodejs":
204         case "trireme":
205             top = typeof(global) !== 'undefined' ? global : this;
206             //console.log("ilib._global: top is " + (typeof(global) !== 'undefined' ? "global" : "this"));
207             break;
208         case "qt":
209         	return undefined;
210         default:
211         	top = window;
212         	break;
213     }
214     try {
215 		return top[name];
216 	} catch (e) {
217 		return undefined;
218 	}
219 };
220 
221 /**
222  * Return true if the global variable is defined on this platform.
223  * @private
224  * @static
225  * @param {string} name the name of the variable to check
226  * @return {boolean} true if the global variable is defined on this platform, false otherwise
227  */
228 ilib._isGlobal = function(name) {
229 	return typeof(ilib._global(name)) !== 'undefined';
230 };
231 
232 /**
233  * Sets the default locale for all of ilib. This locale will be used
234  * when no explicit locale is passed to any ilib class. If the default
235  * locale is not set, ilib will attempt to use the locale of the
236  * environment it is running in, if it can find that. If not, it will
237  * default to the locale "en-US". If a type of parameter is string, 
238  * ilib will take only well-formed BCP-47 tag  <p>
239  * 
240  * 
241  * @static
242  * @param {string|undefined|null} spec the locale specifier for the default locale
243  */
244 ilib.setLocale = function (spec) {
245     if (typeof(spec) === 'string' || !spec) {
246         ilib.locale = spec;
247     }
248     // else ignore other data types, as we don't have the dependencies
249     // to look into them to find a locale
250 };
251 
252 /**
253  * Return the default locale for all of ilib if one has been set. This 
254  * locale will be used when no explicit locale is passed to any ilib 
255  * class. If the default
256  * locale is not set, ilib will attempt to use the locale of the
257  * environment it is running in, if it can find that. If not, it will
258  * default to the locale "en-US".<p>
259  * 
260  * 
261  * @static
262  * @return {string} the locale specifier for the default locale
263  */
264 ilib.getLocale = function () {
265     if (typeof(ilib.locale) !== 'string') {
266     	var plat = ilib._getPlatform();
267     	switch (plat) {
268     		case 'browser':
269             	// running in a browser
270                 if(typeof(navigator.language) !== 'undefined') {
271                     ilib.locale = navigator.language.substring(0,3) + navigator.language.substring(3,5).toUpperCase();  // FF/Opera/Chrome/Webkit    
272                 }
273                 if (!ilib.locale) {
274                     // IE on Windows
275                     var lang = typeof(navigator.browserLanguage) !== 'undefined' ? 
276                         navigator.browserLanguage :
277                         (typeof(navigator.userLanguage) !== 'undefined' ? 
278                             navigator.userLanguage :
279                             (typeof(navigator.systemLanguage) !== 'undefined' ?
280                                 navigator.systemLanguage :
281                                 undefined));
282                     if (typeof(lang) !== 'undefined' && lang) {
283                         // for some reason, MS uses lower case region tags
284                         ilib.locale = lang.substring(0,3) + lang.substring(3,5).toUpperCase();
285                     }
286                 }
287                 break;
288     		case 'webos':
289                 // webOS
290                 if (typeof(PalmSystem.locales) !== 'undefined' && 
291                 		typeof(PalmSystem.locales.UI) != 'undefined' && 
292                 		PalmSystem.locales.UI.length > 0) {
293                     ilib.locale = PalmSystem.locales.UI;
294                 } else if (typeof(PalmSystem.locale) !== 'undefined') {
295                 	ilib.locale = PalmSystem.locale;
296                 }
297     			break;
298     		case 'rhino':
299                 if (typeof(environment) !== 'undefined' && environment.user && typeof(environment.user.language) === 'string' && environment.user.language.length > 0) {
300                 	// running under plain rhino
301                     ilib.locale = environment.user.language;
302                     if (typeof(environment.user.country) === 'string' && environment.user.country.length > 0) {
303                         ilib.locale += '-' + environment.user.country;
304                     }
305                 }
306                 break;
307     		case "trireme":
308             	// under trireme on rhino emulating nodejs
309             	var lang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL;
310                 // the LANG variable on unix is in the form "lang_REGION.CHARSET"
311                 // where language and region are the correct ISO codes separated by
312                 // an underscore. This translate it back to the BCP-47 form.
313                 if (lang && typeof(lang) !== 'undefined') {
314                     ilib.locale = lang.substring(0,2).toLowerCase() + '-' + lang.substring(3,5).toUpperCase();
315                 }
316             	break;
317     		case 'nodejs':
318                 // running under nodejs
319                 var lang = process.env.LANG || process.env.LC_ALL;
320                 // the LANG variable on unix is in the form "lang_REGION.CHARSET"
321                 // where language and region are the correct ISO codes separated by
322                 // an underscore. This translate it back to the BCP-47 form.
323                 if (lang && typeof(lang) !== 'undefined') {
324                     ilib.locale = lang.substring(0,2).toLowerCase() + '-' + lang.substring(3,5).toUpperCase();
325                 }
326     			break;
327     		case 'qt':
328             	// running in the Javascript engine under Qt/QML
329             	var locobj = Qt.locale();
330             	var lang = locobj.name && locobj.name.replace("_", "-") || "en-US";
331     			break;
332     	}
333         ilib.locale = typeof(ilib.locale) === 'string' && ilib.locale ? ilib.locale : 'en-US';
334         if (ilib.locale === "en") {
335         	ilib.locale = "en-US"; // hack to get various platforms working correctly
336         }
337     }
338     return ilib.locale;
339 };
340 
341 /**
342  * Sets the default time zone for all of ilib. This time zone will be used when
343  * no explicit time zone is passed to any ilib class. If the default time zone
344  * is not set, ilib will attempt to use the time zone of the
345  * environment it is running in, if it can find that. If not, it will
346  * default to the the UTC zone "Etc/UTC".<p>
347  * 
348  * 
349  * @static
350  * @param {string} tz the name of the time zone to set as the default time zone
351  */
352 ilib.setTimeZone = function (tz) {
353     ilib.tz = tz || ilib.tz;
354 };
355 
356 /**
357  * Return the default time zone for all of ilib if one has been set. This 
358  * time zone will be used when no explicit time zone is passed to any ilib 
359  * class. If the default time zone
360  * is not set, ilib will attempt to use the locale of the
361  * environment it is running in, if it can find that. If not, it will
362  * default to the the zone "local".<p>
363  * 
364  * 
365  * @static
366  * @return {string} the default time zone for ilib
367  */
368 ilib.getTimeZone = function() {
369     if (typeof(ilib.tz) === 'undefined') {
370         if (typeof(navigator) !== 'undefined' && typeof(navigator.timezone) !== 'undefined') {
371             // running in a browser
372             if (navigator.timezone.length > 0) {
373                 ilib.tz = navigator.timezone;
374             }
375         } else if (typeof(PalmSystem) !== 'undefined' && typeof(PalmSystem.timezone) !== 'undefined') {
376             // running in webkit on webOS
377             if (PalmSystem.timezone.length > 0) {
378                 ilib.tz = PalmSystem.timezone;
379             }
380         } else if (typeof(environment) !== 'undefined' && typeof(environment.user) !== 'undefined') {
381             // running under rhino
382             if (typeof(environment.user.timezone) !== 'undefined' && environment.user.timezone.length > 0) {
383                 ilib.tz = environment.user.timezone;
384             }
385         } else if (typeof(process) !== 'undefined' && typeof(process.env) !== 'undefined') {
386             // running in nodejs
387             if (process.env.TZ && typeof(process.env.TZ) !== "undefined") {
388                 ilib.tz = process.env.TZ;
389             }
390         }
391         
392         ilib.tz = ilib.tz || "local"; 
393     }
394 
395     return ilib.tz;
396 };
397 
398 /**
399  * @class
400  * Defines the interface for the loader class for ilib. The main method of the
401  * loader object is loadFiles(), which loads a set of requested locale data files
402  * from where-ever it is stored.
403  * @interface
404  */
405 ilib.Loader = function() {};
406 
407 /**
408  * Load a set of files from where-ever it is stored.<p>
409  * 
410  * This is the main function define a callback function for loading missing locale 
411  * data or resources.
412  * If this copy of ilib is assembled without including the required locale data
413  * or resources, then that data can be lazy loaded dynamically when it is 
414  * needed by calling this method. Each ilib class will first
415  * check for the existence of data under ilib.data, and if it is not there, 
416  * it will attempt to load it by calling this method of the laoder, and then place
417  * it there.<p>
418  * 
419  * Suggested implementations of this method might load files 
420  * directly from disk under nodejs or rhino, or within web pages, to load 
421  * files from the server with XHR calls.<p>
422  * 
423  * The first parameter to this method, paths, is an array of relative paths within 
424  * the ilib dir structure for the 
425  * requested data. These paths will already have the locale spec integrated 
426  * into them, so no further tweaking needs to happen to load the data. Simply
427  * load the named files. The second
428  * parameter tells the loader whether to load the files synchronously or asynchronously.
429  * If the sync parameters is false, then the onLoad function must also be specified.
430  * The third parameter gives extra parameters to the loader passed from the calling
431  * code. This may contain any property/value pairs.  The last parameter, callback,
432  * is a callback function to call when all of the data is finishing loading. Make
433  * sure to call the callback with the context of "this" so that the caller has their 
434  * context back again.<p>
435  * 
436  * The loader function must be able to operate either synchronously or asychronously. 
437  * If the loader function is called with an undefined callback function, it is
438  * expected to load the data synchronously, convert it to javascript
439  * objects, and return the array of json objects as the return value of the 
440  * function. If the loader 
441  * function is called with a callback function, it may load the data 
442  * synchronously or asynchronously (doesn't matter which) as long as it calls
443  * the callback function with the data converted to a javascript objects
444  * when it becomes available. If a particular file could not be loaded, the 
445  * loader function should put undefined into the corresponding entry in the
446  * results array. 
447  * Note that it is important that all the data is loaded before the callback
448  * is called.<p>
449  * 
450  * An example implementation for nodejs might be:
451  * 
452  * <pre>
453  *  * 
454  * var myLoader = function() {};
455  * myLoader.prototype = new Loader();
456  * myLoader.prototype.constructor = myLoader;
457  * myLoader.prototype.loadFiles = function(paths, sync, params, callback) {
458  *    if (sync) {
459  *        var ret = [];
460  *        // synchronous load -- just return the result
461  *        paths.forEach(function (path) {
462  *            var json = fs.readFileSync(path, "utf-8");
463  *            ret.push(json ? JSON.parse(json) : undefined);
464  *        });
465  *        
466  *        return ret;
467  *    }
468  *    this.callback = callback;
469  *
470  *    // asynchronous
471  *    this.results = [];
472  *    this._loadFilesAsync(paths);
473  * }
474  * myLoader.prototype._loadFilesAsync = function (paths) {
475  *    if (paths.length > 0) {
476  *        var file = paths.shift();
477  *        fs.readFile(file, "utf-8", function(err, json) {
478  *            this.results.push(err ? undefined : JSON.parse(json));
479  *            // call self recursively so that the callback is only called at the end
480  *            // when all the files are loaded sequentially
481  *            if (paths.length > 0) {
482  *                this._loadFilesAsync(paths);
483  *            } else {
484  *                this.callback(this.results);
485  *            }
486  *        });
487  *     }
488  * }
489  * 
490  * // bind to "this" so that "this" is relative to your own instance
491  * ilib.setLoaderCallback(new myLoader());
492  * </pre>
493 
494  * @param {Array.<string>} paths An array of paths to load from wherever the files are stored 
495  * @param {Boolean} sync if true, load the files synchronously, and false means asynchronously
496  * @param {Object} params an object with any extra parameters for the loader. These can be 
497  * anything. The caller of the ilib class passes these parameters in. Presumably, the code that
498  * calls ilib and the code that provides the loader are together and can have a private 
499  * agreement between them about what the parameters should contain.
500  * @param {function(Object)} callback function to call when the files are all loaded. The 
501  * parameter of the callback function is the contents of the files.
502  */
503 ilib.Loader.prototype.loadFiles = function (paths, sync, params, callback) {};
504 
505 /**
506  * Return all files available for loading using this loader instance.
507  * This method returns an object where the properties are the paths to
508  * directories where files are loaded from and the values are an array
509  * of strings containing the relative paths under the directory of each
510  * file that can be loaded.<p>
511  * 
512  * Example:
513  *  <pre>
514  *  {
515  *      "/usr/share/javascript/ilib/locale": [
516  *          "dateformats.json",
517  *          "aa/dateformats.json",
518  *          "af/dateformats.json",
519  *          "agq/dateformats.json",
520  *          "ak/dateformats.json",
521  *          ...
522  *          "zxx/dateformats.json"
523  *      ]
524  *  }
525  *  </pre>
526  * @returns {Object} a hash containing directory names and
527  * paths to file that can be loaded by this loader 
528  */
529 ilib.Loader.prototype.listAvailableFiles = function() {};
530 
531 /**
532  * Return true if the file in the named path is available for loading using
533  * this loader. The path may be given as an absolute path, in which case
534  * only that file is checked, or as a relative path, in which case, the
535  * relative path may appear underneath any of the directories that the loader
536  * knows about.
537  * @returns {boolean} true if the file in the named path is available for loading, and
538  * false otherwise
539  */
540 ilib.Loader.prototype.isAvailable = function(path) {};
541 
542 /**
543  * Set the custom loader used to load ilib's locale data in your environment. 
544  * The instance passed in must implement the Loader interface. See the
545  * Loader class documentation for more information about loaders. 
546  * 
547  * @static
548  * @param {ilib.Loader} loader class to call to access the requested data.
549  * @return {boolean} true if the loader was installed correctly, or false
550  * if not
551  */
552 ilib.setLoaderCallback = function(loader) {
553     // only a basic check
554     if ((typeof(loader) === 'object' && typeof(loader.loadFiles) === 'function') || 
555             typeof(loader) === 'function' || typeof(loader) === 'undefined') {
556         //console.log("setting callback loader to " + (loader ? loader.name : "undefined"));
557         ilib._load = loader;
558         return true;
559     }
560     return false;
561 };
562 
563 /**
564  * Return the custom Loader instance currently in use with this instance 
565  * of ilib. If there is no loader, this method returns undefined.
566  * 
567  * @protected
568  * @static
569  * @return {ilib.Loader|undefined} the loader instance currently in use, or 
570  * undefined if there is no such loader
571  */
572 ilib.getLoader = function() {
573 	return ilib._load;
574 };
575 
576 /**
577  * Test whether an object is an javascript array. 
578  * 
579  * @static
580  * @param {*} object The object to test
581  * @return {boolean} return true if the object is an array
582  * and false otherwise
583  */
584 ilib.isArray = function(object) {
585 	if (typeof(object) === 'object') {
586 		return Object.prototype.toString.call(object) === '[object Array]';
587 	}
588 	return false; 
589 };
590 
591 /**
592  * Extend object1 by mixing in everything from object2 into it. The objects
593  * are deeply extended, meaning that this method recursively descends the
594  * tree in the objects and mixes them in at each level. Arrays are extended
595  * by concatenating the elements of object2 onto those of object1.  
596  * 
597  * @static
598  * @param {Object} object1 the target object to extend
599  * @param {Object=} object2 the object to mix in to object1
600  * @return {Object} returns object1
601  */
602 ilib.extend = function (object1, object2) {
603 	var prop = undefined;
604 	if (object2) {
605 		for (prop in object2) {
606 			// don't extend object with undefined or functions
607 			if (prop && typeof(object2[prop]) !== 'undefined' && typeof(object2[prop]) !== "function") {
608 				if (ilib.isArray(object1[prop]) && ilib.isArray(object2[prop])) {
609 					//console.log("Merging array prop " + prop);
610 					object1[prop] = object1[prop].concat(object2[prop]);
611 				} else if (typeof(object1[prop]) === 'object' && typeof(object2[prop]) === 'object') {
612 					//console.log("Merging object prop " + prop);
613 					if (prop !== "ilib") {
614 						object1[prop] = ilib.extend(object1[prop], object2[prop]);
615 					}
616 				} else {
617 					//console.log("Copying prop " + prop);
618 					// for debugging. Used to determine whether or not json files are overriding their parents unnecessarily
619 					object1[prop] = object2[prop];
620 				}
621 			}
622 		}
623 	}
624 	return object1;
625 };
626 
627 ilib.extend2 = function (object1, object2) {
628 	var prop = undefined;
629 	if (object2) {
630 		for (prop in object2) {
631 			// don't extend object with undefined or functions
632 			if (prop && typeof(object2[prop]) !== 'undefined') {
633 				if (ilib.isArray(object1[prop]) && ilib.isArray(object2[prop])) {
634 					//console.log("Merging array prop " + prop);
635 					object1[prop] = object1[prop].concat(object2[prop]);
636 				} else if (typeof(object1[prop]) === 'object' && typeof(object2[prop]) === 'object') {
637 					//console.log("Merging object prop " + prop);
638 					if (prop !== "ilib") {
639 						object1[prop] = ilib.extend2(object1[prop], object2[prop]);
640 					}
641 				} else {
642 					//console.log("Copying prop " + prop);
643 					// for debugging. Used to determine whether or not json files are overriding their parents unnecessarily
644 					object1[prop] = object2[prop];
645 				}
646 			}
647 		}
648 	}
649 	return object1;
650 };
651 
652 /**
653  * If Function.prototype.bind does not exist in this JS engine, this
654  * function reimplements it in terms of older JS functions.
655  * bind() doesn't exist in many older browsers.
656  * 
657  * @static
658  * @param {Object} scope object that the method should operate on
659  * @param {function(...)} method method to call
660  * @return {function(...)|undefined} function that calls the given method 
661  * in the given scope with all of its arguments properly attached, or
662  * undefined if there was a problem with the arguments
663  */
664 ilib.bind = function(scope, method/*, bound arguments*/){
665 	if (!scope || !method) {
666 		return undefined;
667 	}
668 	
669 	/** @protected 
670 	 * @param {Arguments} inArrayLike
671 	 * @param {number=} inOffset
672 	 */
673 	function cloneArray(inArrayLike, inOffset) {
674 		var arr = [];
675 		for(var i = inOffset || 0, l = inArrayLike.length; i<l; i++){
676 			arr.push(inArrayLike[i]);
677 		}
678 		return arr;
679 	}
680 
681 	if (typeof(method) === 'function') {
682 		var func, args = cloneArray(arguments, 2);
683 		if (typeof(method.bind) === 'function') {
684 			func = method.bind.apply(method, [scope].concat(args));
685 		} else {
686 			func = function() {
687 				var nargs = cloneArray(arguments);
688 				// invoke with collected args
689 				return method.apply(scope, args.concat(nargs));
690 			};
691 		}
692 		return func;
693 	}
694 	return undefined;
695 };
696 
697 /**
698  * @private
699  */
700 ilib._dyncode = false;
701 
702 /**
703  * Return true if this copy of ilib is using dynamically loaded code. It returns
704  * false for pre-assembled code.
705  * 
706  * @static
707  * @return {boolean} true if this ilib uses dynamically loaded code, and false otherwise
708  */
709 ilib.isDynCode = function() {
710 	return ilib._dyncode;
711 };
712 
713 /**
714  * @private
715  */
716 ilib._dyndata = false;
717 
718 /**
719  * Return true if this copy of ilib is using dynamically loaded locale data. It returns
720  * false for pre-assembled data.
721  * 
722  * @static
723  * @return {boolean} true if this ilib uses dynamically loaded locale data, and false otherwise
724  */
725 ilib.isDynData = function() {
726 	return ilib._dyndata;
727 };
728 
729 ilib._loadtime = new Date().getTime();
730 
731 /*< JSUtils.js */
732 /*
733  * JSUtils.js - Misc utilities to work around Javascript engine differences
734  * 
735  * Copyright © 2013-2015, JEDLSoft
736  *
737  * Licensed under the Apache License, Version 2.0 (the "License");
738  * you may not use this file except in compliance with the License.
739  * You may obtain a copy of the License at
740  *
741  *     http://www.apache.org/licenses/LICENSE-2.0
742  *
743  * Unless required by applicable law or agreed to in writing, software
744  * distributed under the License is distributed on an "AS IS" BASIS,
745  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
746  *
747  * See the License for the specific language governing permissions and
748  * limitations under the License.
749  */
750 
751 // !depends ilib.js
752 
753 
754 var JSUtils = {};
755 
756 /**
757  * Perform a shallow copy of the source object to the target object. This only 
758  * copies the assignments of the source properties to the target properties, 
759  * but not recursively from there.<p>
760  * 
761  * 
762  * @static
763  * @param {Object} source the source object to copy properties from
764  * @param {Object} target the target object to copy properties into
765  */
766 JSUtils.shallowCopy = function (source, target) {
767 	var prop = undefined;
768 	if (source && target) {
769 		for (prop in source) {
770 			if (prop !== undefined && typeof(source[prop]) !== 'undefined') {
771 				target[prop] = source[prop];
772 			}
773 		}
774 	}
775 };
776 
777 /**
778  * Perform a recursive deep copy from the "from" object to the "deep" object.
779  * 
780  * @static
781  * @param {Object} from the object to copy from
782  * @param {Object} to the object to copy to
783  * @return {Object} a reference to the the "to" object
784  */
785 JSUtils.deepCopy = function(from, to) {
786 	var prop;
787 
788 	for (prop in from) {
789 		if (prop) {
790 			if (typeof(from[prop]) === 'object') {
791 				to[prop] = {};
792 				JSUtils.deepCopy(from[prop], to[prop]);
793 			} else {
794 				to[prop] = from[prop];
795 			}
796 		}
797 	}
798 	return to;
799 };
800 
801 /**
802  * Map a string to the given set of alternate characters. If the target set
803  * does not contain a particular character in the input string, then that
804  * character will be copied to the output unmapped.
805  * 
806  * @static
807  * @param {string} str a string to map to an alternate set of characters
808  * @param {Array.<string>|Object} map a mapping to alternate characters
809  * @return {string} the source string where each character is mapped to alternate characters
810  */
811 JSUtils.mapString = function (str, map) {
812 	var mapped = "";
813 	if (map && str) {
814 		for (var i = 0; i < str.length; i++) {
815 			var c = str.charAt(i); // TODO use a char iterator?
816 			mapped += map[c] || c; 
817 		}
818 	} else {
819 		mapped = str;
820 	}
821 	return mapped;
822 };
823 
824 /**
825  * Check if an object is a member of the given array. If this javascript engine
826  * support indexOf, it is used directly. Otherwise, this function implements it
827  * itself. The idea is to make sure that you can use the quick indexOf if it is
828  * available, but use a slower implementation in older engines as well.
829  * 
830  * @static
831  * @param {Array.<Object>} array array to search
832  * @param {Object} obj object being sought. This should be of the same type as the
833  * members of the array being searched. If not, this function will not return
834  * any results.
835  * @return {number} index of the object in the array, or -1 if it is not in the array.
836  */
837 JSUtils.indexOf = function(array, obj) {
838 	if (!array || !obj) {
839 		return -1;
840 	}
841 	if (typeof(array.indexOf) === 'function') {
842 		return array.indexOf(obj);
843 	} else {
844 		for (var i = 0; i < array.length; i++) {
845 	        if (array[i] === obj) {
846 	            return i;
847 	        }
848 	    }
849 	    return -1;
850 	}
851 };
852 
853 /**
854  * Pad the str with zeros to the given length of digits.
855  * 
856  * @static
857  * @param {string|number} str the string or number to pad
858  * @param {number} length the desired total length of the output string, padded
859  * @param {boolean=} right if true, pad on the right side of the number rather than the left.
860  * Default is false.
861  */
862 JSUtils.pad = function (str, length, right) {
863 	if (typeof(str) !== 'string') {
864 		str = "" + str;
865 	}
866 	var start = 0;
867 	// take care of negative numbers
868 	if (str.charAt(0) === '-') {
869 		start++;
870 	}
871 	return (str.length >= length+start) ? str : 
872 		(right ? str + JSUtils.pad.zeros.substring(0,length-str.length+start) : 
873 			str.substring(0, start) + JSUtils.pad.zeros.substring(0,length-str.length+start) + str.substring(start));
874 };
875 
876 /** @private */
877 JSUtils.pad.zeros = "00000000000000000000000000000000";
878 
879 /**
880  * Convert a string into the hexadecimal representation
881  * of the Unicode characters in that string.
882  * 
883  * @static
884  * @param {string} string The string to convert
885  * @param {number=} limit the number of digits to use to represent the character (1 to 8)
886  * @return {string} a hexadecimal representation of the
887  * Unicode characters in the input string
888  */
889 JSUtils.toHexString = function(string, limit) {
890 	var i, 
891 		result = "", 
892 		lim = (limit && limit < 9) ? limit : 4;
893 	
894 	if (!string) {
895 		return "";
896 	}
897 	for (i = 0; i < string.length; i++) {
898 		var ch = string.charCodeAt(i).toString(16);
899 		result += JSUtils.pad(ch, lim);
900 	}
901 	return result.toUpperCase();
902 };
903 
904 /**
905  * Test whether an object in a Javascript Date. 
906  * 
907  * @static
908  * @param {Object|null|undefined} object The object to test
909  * @return {boolean} return true if the object is a Date
910  * and false otherwise
911  */
912 JSUtils.isDate = function(object) {
913 	if (typeof(object) === 'object') {
914 		return Object.prototype.toString.call(object) === '[object Date]';
915 	}
916 	return false; 
917 };
918 
919 /**
920  * Merge the properties of object2 into object1 in a deep manner and return a merged
921  * object. If the property exists in both objects, the value in object2 will overwrite 
922  * the value in object1. If a property exists in object1, but not in object2, its value
923  * will not be touched. If a property exists in object2, but not in object1, it will be 
924  * added to the merged result.<p>
925  * 
926  * Name1 and name2 are for creating debug output only. They are not necessary.<p>
927  * 
928  * 
929  * @static
930  * @param {*} object1 the object to merge into
931  * @param {*} object2 the object to merge
932  * @param {boolean=} replace if true, replace the array elements in object1 with those in object2.
933  * If false, concatenate array elements in object1 with items in object2.
934  * @param {string=} name1 name of the object being merged into
935  * @param {string=} name2 name of the object being merged in
936  * @return {Object} the merged object
937  */
938 JSUtils.merge = function (object1, object2, replace, name1, name2) {
939 	var prop = undefined,
940 		newObj = {};
941 	for (prop in object1) {
942 		if (prop && typeof(object1[prop]) !== 'undefined') {
943 			newObj[prop] = object1[prop];
944 		}
945 	}
946 	for (prop in object2) {
947 		if (prop && typeof(object2[prop]) !== 'undefined') {
948 			if (ilib.isArray(object1[prop]) && ilib.isArray(object2[prop])) {
949 				if (typeof(replace) !== 'boolean' || !replace) {
950 					newObj[prop] = [].concat(object1[prop]);
951 					newObj[prop] = newObj[prop].concat(object2[prop]);
952 				} else {
953 					newObj[prop] = object2[prop];
954 				}
955 			} else if (typeof(object1[prop]) === 'object' && typeof(object2[prop]) === 'object') {
956 				newObj[prop] = JSUtils.merge(object1[prop], object2[prop], replace);
957 			} else {
958 				// for debugging. Used to determine whether or not json files are overriding their parents unnecessarily
959 				if (name1 && name2 && newObj[prop] == object2[prop]) {
960 					console.log("Property " + prop + " in " + name1 + " is being overridden by the same value in " + name2);
961 				}
962 				newObj[prop] = object2[prop];
963 			}
964 		}
965 	}
966 	return newObj;
967 };
968 
969 /**
970  * Return true if the given object has no properties.<p>
971  * 
972  * 
973  * @static
974  * @param {Object} obj the object to check
975  * @return {boolean} true if the given object has no properties, false otherwise
976  */
977 JSUtils.isEmpty = function (obj) {
978 	var prop = undefined;
979 	
980 	if (!obj) {
981 		return true;
982 	}
983 	
984 	for (prop in obj) {
985 		if (prop && typeof(obj[prop]) !== 'undefined') {
986 			return false;
987 		}
988 	}
989 	return true;
990 };
991 
992 /**
993  * @static
994  */
995 JSUtils.hashCode = function(obj) {
996 	var hash = 0;
997 	
998 	function addHash(hash, newValue) {
999 		// co-prime numbers creates a nicely distributed hash
1000 		hash *= 65543;
1001 		hash += newValue;
1002 		hash %= 2147483647; 
1003 		return hash;
1004 	}
1005 	
1006 	function stringHash(str) {
1007 		var hash = 0;
1008 		for (var i = 0; i < str.length; i++) {
1009 			hash = addHash(hash, str.charCodeAt(i));
1010 		}
1011 		return hash;
1012 	}
1013 	
1014 	switch (typeof(obj)) {
1015 		case 'undefined':
1016 			hash = 0;
1017 			break;
1018 		case 'string':
1019 			hash = stringHash(obj);
1020 			break;
1021 		case 'function':
1022 		case 'number':
1023 		case 'xml':
1024 			hash = stringHash(String(obj));
1025 			break;
1026 		case 'boolean':
1027 			hash = obj ? 1 : 0;
1028 			break;
1029 		case 'object':
1030 			var props = [];
1031 			for (var p in obj) {
1032 				if (obj.hasOwnProperty(p)) {
1033 					props.push(p);
1034 				}
1035 			}
1036 			// make sure the order of the properties doesn't matter
1037 			props.sort();
1038 			for (var i = 0; i < props.length; i++) {
1039 				hash = addHash(hash, stringHash(props[i]));
1040 				hash = addHash(hash, JSUtils.hashCode(obj[props[i]]));
1041 			}
1042 			break;
1043 	}
1044 	
1045 	return hash;
1046 };
1047 
1048 
1049 
1050 
1051 /*< Locale.js */
1052 /*
1053  * Locale.js - Locale specifier definition
1054  * 
1055  * Copyright © 2012-2015, JEDLSoft
1056  *
1057  * Licensed under the Apache License, Version 2.0 (the "License");
1058  * you may not use this file except in compliance with the License.
1059  * You may obtain a copy of the License at
1060  *
1061  *     http://www.apache.org/licenses/LICENSE-2.0
1062  *
1063  * Unless required by applicable law or agreed to in writing, software
1064  * distributed under the License is distributed on an "AS IS" BASIS,
1065  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1066  *
1067  * See the License for the specific language governing permissions and
1068  * limitations under the License.
1069  */
1070 
1071 // !depends ilib.js JSUtils.js
1072 
1073 
1074 /**
1075  * @class
1076  * Create a new locale instance. Locales are specified either with a specifier string 
1077  * that follows the BCP-47 convention (roughly: "language-region-script-variant") or 
1078  * with 4 parameters that specify the language, region, variant, and script individually.<p>
1079  * 
1080  * The language is given as an ISO 639-1 two-letter, lower-case language code. You
1081  * can find a full list of these codes at 
1082  * <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes</a><p>
1083  * 
1084  * The region is given as an ISO 3166-1 two-letter, upper-case region code. You can
1085  * find a full list of these codes at 
1086  * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2</a>.<p>
1087  * 
1088  * The variant is any string that does not contain a dash which further differentiates
1089  * locales from each other.<p>
1090  * 
1091  * The script is given as the ISO 15924 four-letter script code. In some locales,
1092  * text may be validly written in more than one script. For example, Serbian is often
1093  * written in both Latin and Cyrillic, though not usually mixed together. You can find a
1094  * full list of these codes at 
1095  * <a href="http://en.wikipedia.org/wiki/ISO_15924#List_of_codes">http://en.wikipedia.org/wiki/ISO_15924#List_of_codes</a>.<p>
1096  * 
1097  * As an example in ilib, the script can be used in the date formatter. Dates formatted 
1098  * in Serbian could have day-of-week names or month names written in the Latin
1099  * or Cyrillic script. Often one script is default such that sr-SR-Latn is the same
1100  * as sr-SR so the script code "Latn" can be left off of the locale spec.<p> 
1101  * 
1102  * Each part is optional, and an empty string in the specifier before or after a 
1103  * dash or as a parameter to the constructor denotes an unspecified value. In this
1104  * case, many of the ilib functions will treat the locale as generic. For example
1105  * the locale "en-" is equivalent to "en" and to "en--" and denotes a locale
1106  * of "English" with an unspecified region and variant, which typically matches
1107  * any region or variant.<p>
1108  * 
1109  * Without any arguments to the constructor, this function returns the locale of
1110  * the host Javascript engine.<p>
1111  * 
1112  * 
1113  * @constructor
1114  * @param {?string|Locale=} language the ISO 639 2-letter code for the language, or a full 
1115  * locale spec in BCP-47 format, or another Locale instance to copy from
1116  * @param {string=} region the ISO 3166 2-letter code for the region
1117  * @param {string=} variant the name of the variant of this locale, if any
1118  * @param {string=} script the ISO 15924 code of the script for this locale, if any
1119  */
1120 var Locale = function(language, region, variant, script) {
1121 	if (typeof(region) === 'undefined') {
1122 		var spec = language || ilib.getLocale();
1123 		if (typeof(spec) === 'string') {
1124 			var parts = spec.split('-');
1125 	        for ( var i = 0; i < parts.length; i++ ) {
1126 	        	if (Locale._isLanguageCode(parts[i])) {
1127 	    			/** 
1128 	    			 * @private
1129 	    			 * @type {string|undefined}
1130 	    			 */
1131 	        		this.language = parts[i];
1132 	        	} else if (Locale._isRegionCode(parts[i])) {
1133 	    			/** 
1134 	    			 * @private
1135 	    			 * @type {string|undefined}
1136 	    			 */
1137 	        		this.region = parts[i];
1138 	        	} else if (Locale._isScriptCode(parts[i])) {
1139 	    			/** 
1140 	    			 * @private
1141 	    			 * @type {string|undefined}
1142 	    			 */
1143 	        		this.script = parts[i];
1144 	        	} else {
1145 	    			/** 
1146 	    			 * @private
1147 	    			 * @type {string|undefined}
1148 	    			 */
1149 	        		this.variant = parts[i];
1150 	        	}
1151 	        }
1152 	        this.language = this.language || undefined;
1153 	        this.region = this.region || undefined;
1154 	        this.script = this.script || undefined;
1155 	        this.variant = this.variant || undefined;
1156 		} else if (typeof(spec) === 'object') {
1157 	        this.language = spec.language || undefined;
1158 	        this.region = spec.region || undefined;
1159 	        this.script = spec.script || undefined;
1160 	        this.variant = spec.variant || undefined;
1161 		}
1162 	} else {
1163 		if (language) {
1164 			language = language.trim();
1165 			this.language = language.length > 0 ? language.toLowerCase() : undefined;
1166 		} else {
1167 			this.language = undefined;
1168 		}
1169 		if (region) {
1170 			region = region.trim();
1171 			this.region = region.length > 0 ? region.toUpperCase() : undefined;
1172 		} else {
1173 			this.region = undefined;
1174 		}
1175 		if (variant) {
1176 			variant = variant.trim();
1177 			this.variant = variant.length > 0 ? variant : undefined;
1178 		} else {
1179 			this.variant = undefined;
1180 		}
1181 		if (script) {
1182 			script = script.trim();
1183 			this.script = script.length > 0 ? script : undefined;
1184 		} else {
1185 			this.script = undefined;
1186 		}
1187 	}
1188 	this._genSpec();
1189 };
1190 
1191 // from http://en.wikipedia.org/wiki/ISO_3166-1
1192 Locale.a2toa3regmap = {
1193 	"AF": "AFG",
1194 	"AX": "ALA",
1195 	"AL": "ALB",
1196 	"DZ": "DZA",
1197 	"AS": "ASM",
1198 	"AD": "AND",
1199 	"AO": "AGO",
1200 	"AI": "AIA",
1201 	"AQ": "ATA",
1202 	"AG": "ATG",
1203 	"AR": "ARG",
1204 	"AM": "ARM",
1205 	"AW": "ABW",
1206 	"AU": "AUS",
1207 	"AT": "AUT",
1208 	"AZ": "AZE",
1209 	"BS": "BHS",
1210 	"BH": "BHR",
1211 	"BD": "BGD",
1212 	"BB": "BRB",
1213 	"BY": "BLR",
1214 	"BE": "BEL",
1215 	"BZ": "BLZ",
1216 	"BJ": "BEN",
1217 	"BM": "BMU",
1218 	"BT": "BTN",
1219 	"BO": "BOL",
1220 	"BQ": "BES",
1221 	"BA": "BIH",
1222 	"BW": "BWA",
1223 	"BV": "BVT",
1224 	"BR": "BRA",
1225 	"IO": "IOT",
1226 	"BN": "BRN",
1227 	"BG": "BGR",
1228 	"BF": "BFA",
1229 	"BI": "BDI",
1230 	"KH": "KHM",
1231 	"CM": "CMR",
1232 	"CA": "CAN",
1233 	"CV": "CPV",
1234 	"KY": "CYM",
1235 	"CF": "CAF",
1236 	"TD": "TCD",
1237 	"CL": "CHL",
1238 	"CN": "CHN",
1239 	"CX": "CXR",
1240 	"CC": "CCK",
1241 	"CO": "COL",
1242 	"KM": "COM",
1243 	"CG": "COG",
1244 	"CD": "COD",
1245 	"CK": "COK",
1246 	"CR": "CRI",
1247 	"CI": "CIV",
1248 	"HR": "HRV",
1249 	"CU": "CUB",
1250 	"CW": "CUW",
1251 	"CY": "CYP",
1252 	"CZ": "CZE",
1253 	"DK": "DNK",
1254 	"DJ": "DJI",
1255 	"DM": "DMA",
1256 	"DO": "DOM",
1257 	"EC": "ECU",
1258 	"EG": "EGY",
1259 	"SV": "SLV",
1260 	"GQ": "GNQ",
1261 	"ER": "ERI",
1262 	"EE": "EST",
1263 	"ET": "ETH",
1264 	"FK": "FLK",
1265 	"FO": "FRO",
1266 	"FJ": "FJI",
1267 	"FI": "FIN",
1268 	"FR": "FRA",
1269 	"GF": "GUF",
1270 	"PF": "PYF",
1271 	"TF": "ATF",
1272 	"GA": "GAB",
1273 	"GM": "GMB",
1274 	"GE": "GEO",
1275 	"DE": "DEU",
1276 	"GH": "GHA",
1277 	"GI": "GIB",
1278 	"GR": "GRC",
1279 	"GL": "GRL",
1280 	"GD": "GRD",
1281 	"GP": "GLP",
1282 	"GU": "GUM",
1283 	"GT": "GTM",
1284 	"GG": "GGY",
1285 	"GN": "GIN",
1286 	"GW": "GNB",
1287 	"GY": "GUY",
1288 	"HT": "HTI",
1289 	"HM": "HMD",
1290 	"VA": "VAT",
1291 	"HN": "HND",
1292 	"HK": "HKG",
1293 	"HU": "HUN",
1294 	"IS": "ISL",
1295 	"IN": "IND",
1296 	"ID": "IDN",
1297 	"IR": "IRN",
1298 	"IQ": "IRQ",
1299 	"IE": "IRL",
1300 	"IM": "IMN",
1301 	"IL": "ISR",
1302 	"IT": "ITA",
1303 	"JM": "JAM",
1304 	"JP": "JPN",
1305 	"JE": "JEY",
1306 	"JO": "JOR",
1307 	"KZ": "KAZ",
1308 	"KE": "KEN",
1309 	"KI": "KIR",
1310 	"KP": "PRK",
1311 	"KR": "KOR",
1312 	"KW": "KWT",
1313 	"KG": "KGZ",
1314 	"LA": "LAO",
1315 	"LV": "LVA",
1316 	"LB": "LBN",
1317 	"LS": "LSO",
1318 	"LR": "LBR",
1319 	"LY": "LBY",
1320 	"LI": "LIE",
1321 	"LT": "LTU",
1322 	"LU": "LUX",
1323 	"MO": "MAC",
1324 	"MK": "MKD",
1325 	"MG": "MDG",
1326 	"MW": "MWI",
1327 	"MY": "MYS",
1328 	"MV": "MDV",
1329 	"ML": "MLI",
1330 	"MT": "MLT",
1331 	"MH": "MHL",
1332 	"MQ": "MTQ",
1333 	"MR": "MRT",
1334 	"MU": "MUS",
1335 	"YT": "MYT",
1336 	"MX": "MEX",
1337 	"FM": "FSM",
1338 	"MD": "MDA",
1339 	"MC": "MCO",
1340 	"MN": "MNG",
1341 	"ME": "MNE",
1342 	"MS": "MSR",
1343 	"MA": "MAR",
1344 	"MZ": "MOZ",
1345 	"MM": "MMR",
1346 	"NA": "NAM",
1347 	"NR": "NRU",
1348 	"NP": "NPL",
1349 	"NL": "NLD",
1350 	"NC": "NCL",
1351 	"NZ": "NZL",
1352 	"NI": "NIC",
1353 	"NE": "NER",
1354 	"NG": "NGA",
1355 	"NU": "NIU",
1356 	"NF": "NFK",
1357 	"MP": "MNP",
1358 	"NO": "NOR",
1359 	"OM": "OMN",
1360 	"PK": "PAK",
1361 	"PW": "PLW",
1362 	"PS": "PSE",
1363 	"PA": "PAN",
1364 	"PG": "PNG",
1365 	"PY": "PRY",
1366 	"PE": "PER",
1367 	"PH": "PHL",
1368 	"PN": "PCN",
1369 	"PL": "POL",
1370 	"PT": "PRT",
1371 	"PR": "PRI",
1372 	"QA": "QAT",
1373 	"RE": "REU",
1374 	"RO": "ROU",
1375 	"RU": "RUS",
1376 	"RW": "RWA",
1377 	"BL": "BLM",
1378 	"SH": "SHN",
1379 	"KN": "KNA",
1380 	"LC": "LCA",
1381 	"MF": "MAF",
1382 	"PM": "SPM",
1383 	"VC": "VCT",
1384 	"WS": "WSM",
1385 	"SM": "SMR",
1386 	"ST": "STP",
1387 	"SA": "SAU",
1388 	"SN": "SEN",
1389 	"RS": "SRB",
1390 	"SC": "SYC",
1391 	"SL": "SLE",
1392 	"SG": "SGP",
1393 	"SX": "SXM",
1394 	"SK": "SVK",
1395 	"SI": "SVN",
1396 	"SB": "SLB",
1397 	"SO": "SOM",
1398 	"ZA": "ZAF",
1399 	"GS": "SGS",
1400 	"SS": "SSD",
1401 	"ES": "ESP",
1402 	"LK": "LKA",
1403 	"SD": "SDN",
1404 	"SR": "SUR",
1405 	"SJ": "SJM",
1406 	"SZ": "SWZ",
1407 	"SE": "SWE",
1408 	"CH": "CHE",
1409 	"SY": "SYR",
1410 	"TW": "TWN",
1411 	"TJ": "TJK",
1412 	"TZ": "TZA",
1413 	"TH": "THA",
1414 	"TL": "TLS",
1415 	"TG": "TGO",
1416 	"TK": "TKL",
1417 	"TO": "TON",
1418 	"TT": "TTO",
1419 	"TN": "TUN",
1420 	"TR": "TUR",
1421 	"TM": "TKM",
1422 	"TC": "TCA",
1423 	"TV": "TUV",
1424 	"UG": "UGA",
1425 	"UA": "UKR",
1426 	"AE": "ARE",
1427 	"GB": "GBR",
1428 	"US": "USA",
1429 	"UM": "UMI",
1430 	"UY": "URY",
1431 	"UZ": "UZB",
1432 	"VU": "VUT",
1433 	"VE": "VEN",
1434 	"VN": "VNM",
1435 	"VG": "VGB",
1436 	"VI": "VIR",
1437 	"WF": "WLF",
1438 	"EH": "ESH",
1439 	"YE": "YEM",
1440 	"ZM": "ZMB",
1441 	"ZW": "ZWE"
1442 };
1443 
1444 
1445 Locale.a1toa3langmap = {
1446 	"ab": "abk",
1447 	"aa": "aar",
1448 	"af": "afr",
1449 	"ak": "aka",
1450 	"sq": "sqi",
1451 	"am": "amh",
1452 	"ar": "ara",
1453 	"an": "arg",
1454 	"hy": "hye",
1455 	"as": "asm",
1456 	"av": "ava",
1457 	"ae": "ave",
1458 	"ay": "aym",
1459 	"az": "aze",
1460 	"bm": "bam",
1461 	"ba": "bak",
1462 	"eu": "eus",
1463 	"be": "bel",
1464 	"bn": "ben",
1465 	"bh": "bih",
1466 	"bi": "bis",
1467 	"bs": "bos",
1468 	"br": "bre",
1469 	"bg": "bul",
1470 	"my": "mya",
1471 	"ca": "cat",
1472 	"ch": "cha",
1473 	"ce": "che",
1474 	"ny": "nya",
1475 	"zh": "zho",
1476 	"cv": "chv",
1477 	"kw": "cor",
1478 	"co": "cos",
1479 	"cr": "cre",
1480 	"hr": "hrv",
1481 	"cs": "ces",
1482 	"da": "dan",
1483 	"dv": "div",
1484 	"nl": "nld",
1485 	"dz": "dzo",
1486 	"en": "eng",
1487 	"eo": "epo",
1488 	"et": "est",
1489 	"ee": "ewe",
1490 	"fo": "fao",
1491 	"fj": "fij",
1492 	"fi": "fin",
1493 	"fr": "fra",
1494 	"ff": "ful",
1495 	"gl": "glg",
1496 	"ka": "kat",
1497 	"de": "deu",
1498 	"el": "ell",
1499 	"gn": "grn",
1500 	"gu": "guj",
1501 	"ht": "hat",
1502 	"ha": "hau",
1503 	"he": "heb",
1504 	"hz": "her",
1505 	"hi": "hin",
1506 	"ho": "hmo",
1507 	"hu": "hun",
1508 	"ia": "ina",
1509 	"id": "ind",
1510 	"ie": "ile",
1511 	"ga": "gle",
1512 	"ig": "ibo",
1513 	"ik": "ipk",
1514 	"io": "ido",
1515 	"is": "isl",
1516 	"it": "ita",
1517 	"iu": "iku",
1518 	"ja": "jpn",
1519 	"jv": "jav",
1520 	"kl": "kal",
1521 	"kn": "kan",
1522 	"kr": "kau",
1523 	"ks": "kas",
1524 	"kk": "kaz",
1525 	"km": "khm",
1526 	"ki": "kik",
1527 	"rw": "kin",
1528 	"ky": "kir",
1529 	"kv": "kom",
1530 	"kg": "kon",
1531 	"ko": "kor",
1532 	"ku": "kur",
1533 	"kj": "kua",
1534 	"la": "lat",
1535 	"lb": "ltz",
1536 	"lg": "lug",
1537 	"li": "lim",
1538 	"ln": "lin",
1539 	"lo": "lao",
1540 	"lt": "lit",
1541 	"lu": "lub",
1542 	"lv": "lav",
1543 	"gv": "glv",
1544 	"mk": "mkd",
1545 	"mg": "mlg",
1546 	"ms": "msa",
1547 	"ml": "mal",
1548 	"mt": "mlt",
1549 	"mi": "mri",
1550 	"mr": "mar",
1551 	"mh": "mah",
1552 	"mn": "mon",
1553 	"na": "nau",
1554 	"nv": "nav",
1555 	"nb": "nob",
1556 	"nd": "nde",
1557 	"ne": "nep",
1558 	"ng": "ndo",
1559 	"nn": "nno",
1560 	"no": "nor",
1561 	"ii": "iii",
1562 	"nr": "nbl",
1563 	"oc": "oci",
1564 	"oj": "oji",
1565 	"cu": "chu",
1566 	"om": "orm",
1567 	"or": "ori",
1568 	"os": "oss",
1569 	"pa": "pan",
1570 	"pi": "pli",
1571 	"fa": "fas",
1572 	"pl": "pol",
1573 	"ps": "pus",
1574 	"pt": "por",
1575 	"qu": "que",
1576 	"rm": "roh",
1577 	"rn": "run",
1578 	"ro": "ron",
1579 	"ru": "rus",
1580 	"sa": "san",
1581 	"sc": "srd",
1582 	"sd": "snd",
1583 	"se": "sme",
1584 	"sm": "smo",
1585 	"sg": "sag",
1586 	"sr": "srp",
1587 	"gd": "gla",
1588 	"sn": "sna",
1589 	"si": "sin",
1590 	"sk": "slk",
1591 	"sl": "slv",
1592 	"so": "som",
1593 	"st": "sot",
1594 	"es": "spa",
1595 	"su": "sun",
1596 	"sw": "swa",
1597 	"ss": "ssw",
1598 	"sv": "swe",
1599 	"ta": "tam",
1600 	"te": "tel",
1601 	"tg": "tgk",
1602 	"th": "tha",
1603 	"ti": "tir",
1604 	"bo": "bod",
1605 	"tk": "tuk",
1606 	"tl": "tgl",
1607 	"tn": "tsn",
1608 	"to": "ton",
1609 	"tr": "tur",
1610 	"ts": "tso",
1611 	"tt": "tat",
1612 	"tw": "twi",
1613 	"ty": "tah",
1614 	"ug": "uig",
1615 	"uk": "ukr",
1616 	"ur": "urd",
1617 	"uz": "uzb",
1618 	"ve": "ven",
1619 	"vi": "vie",
1620 	"vo": "vol",
1621 	"wa": "wln",
1622 	"cy": "cym",
1623 	"wo": "wol",
1624 	"fy": "fry",
1625 	"xh": "xho",
1626 	"yi": "yid",
1627 	"yo": "yor",
1628 	"za": "zha",
1629 	"zu": "zul"
1630 };
1631 
1632 /**
1633  * Tell whether or not the str does not start with a lower case ASCII char.
1634  * @private
1635  * @param {string} str the char to check
1636  * @return {boolean} true if the char is not a lower case ASCII char
1637  */
1638 Locale._notLower = function(str) {
1639 	// do this with ASCII only so we don't have to depend on the CType functions
1640 	var ch = str.charCodeAt(0);
1641 	return ch < 97 || ch > 122;
1642 };
1643 
1644 /**
1645  * Tell whether or not the str does not start with an upper case ASCII char.
1646  * @private
1647  * @param {string} str the char to check
1648  * @return {boolean} true if the char is a not an upper case ASCII char
1649  */
1650 Locale._notUpper = function(str) {
1651 	// do this with ASCII only so we don't have to depend on the CType functions
1652 	var ch = str.charCodeAt(0);
1653 	return ch < 65 || ch > 90;
1654 };
1655 
1656 /**
1657  * Tell whether or not the str does not start with a digit char.
1658  * @private
1659  * @param {string} str the char to check
1660  * @return {boolean} true if the char is a not an upper case ASCII char
1661  */
1662 Locale._notDigit = function(str) {
1663 	// do this with ASCII only so we don't have to depend on the CType functions
1664 	var ch = str.charCodeAt(0);
1665 	return ch < 48 || ch > 57;
1666 };
1667 
1668 /**
1669  * Tell whether or not the given string has the correct syntax to be 
1670  * an ISO 639 language code.
1671  * 
1672  * @private
1673  * @param {string} str the string to parse
1674  * @return {boolean} true if the string could syntactically be a language code.
1675  */
1676 Locale._isLanguageCode = function(str) {
1677 	if (typeof(str) === 'undefined' || str.length < 2 || str.length > 3) {
1678 		return false;
1679 	}
1680 
1681 	for (var i = 0; i < str.length; i++) {
1682 		if (Locale._notLower(str.charAt(i))) {
1683 			return false;
1684 		}
1685 	}
1686 	
1687 	return true;
1688 };
1689 
1690 /**
1691  * Tell whether or not the given string has the correct syntax to be 
1692  * an ISO 3166 2-letter region code or M.49 3-digit region code.
1693  * 
1694  * @private
1695  * @param {string} str the string to parse
1696  * @return {boolean} true if the string could syntactically be a language code.
1697  */
1698 Locale._isRegionCode = function (str) {
1699 	if (typeof(str) === 'undefined' || str.length < 2 || str.length > 3) {
1700 		return false;
1701 	}
1702 	
1703 	if (str.length === 2) {
1704 		for (var i = 0; i < str.length; i++) {
1705 			if (Locale._notUpper(str.charAt(i))) {
1706 				return false;
1707 			}
1708 		}
1709 	} else {
1710 		for (var i = 0; i < str.length; i++) {
1711 			if (Locale._notDigit(str.charAt(i))) {
1712 				return false;
1713 			}
1714 		}
1715 	}
1716 	
1717 	return true;
1718 };
1719 
1720 /**
1721  * Tell whether or not the given string has the correct syntax to be 
1722  * an ISO 639 language code.
1723  * 
1724  * @private
1725  * @param {string} str the string to parse
1726  * @return {boolean} true if the string could syntactically be a language code.
1727  */
1728 Locale._isScriptCode = function(str) {
1729 	if (typeof(str) === 'undefined' || str.length !== 4 || Locale._notUpper(str.charAt(0))) {
1730 		return false;
1731 	}
1732 	
1733 	for (var i = 1; i < 4; i++) {
1734 		if (Locale._notLower(str.charAt(i))) {
1735 			return false;
1736 		}
1737 	}
1738 	
1739 	return true;
1740 };
1741 
1742 /**
1743  * Return the ISO-3166 alpha3 equivalent region code for the given ISO 3166 alpha2
1744  * region code. If the given alpha2 code is not found, this function returns its
1745  * argument unchanged.
1746  * @static
1747  * @param {string|undefined} alpha2 the alpha2 code to map
1748  * @return {string|undefined} the alpha3 equivalent of the given alpha2 code, or the alpha2
1749  * parameter if the alpha2 value is not found
1750  */
1751 Locale.regionAlpha2ToAlpha3 = function(alpha2) {
1752 	return Locale.a2toa3regmap[alpha2] || alpha2;
1753 };
1754 
1755 /**
1756  * Return the ISO-639 alpha3 equivalent language code for the given ISO 639 alpha1
1757  * language code. If the given alpha1 code is not found, this function returns its
1758  * argument unchanged.
1759  * @static
1760  * @param {string|undefined} alpha1 the alpha1 code to map
1761  * @return {string|undefined} the alpha3 equivalent of the given alpha1 code, or the alpha1
1762  * parameter if the alpha1 value is not found
1763  */
1764 Locale.languageAlpha1ToAlpha3 = function(alpha1) {
1765 	return Locale.a1toa3langmap[alpha1] || alpha1;
1766 };
1767 
1768 Locale.prototype = {
1769 	/**
1770 	 * @private
1771 	 */
1772 	_genSpec: function () {
1773 		this.spec = this.language || "";
1774 		
1775 		if (this.script) {
1776 			if (this.spec.length > 0) {
1777 				this.spec += "-";
1778 			}
1779 			this.spec += this.script;
1780 		}
1781 		
1782 		if (this.region) {
1783 			if (this.spec.length > 0) {
1784 				this.spec += "-";
1785 			}
1786 			this.spec += this.region;
1787 		}
1788 		
1789 		if (this.variant) {
1790 			if (this.spec.length > 0) {
1791 				this.spec += "-";
1792 			}
1793 			this.spec += this.variant;
1794 		}
1795 	},
1796 
1797 	/**
1798 	 * Return the ISO 639 language code for this locale. 
1799 	 * @return {string|undefined} the language code for this locale 
1800 	 */
1801 	getLanguage: function() {
1802 		return this.language;
1803 	},
1804 	
1805 	/**
1806 	 * Return the language of this locale as an ISO-639-alpha3 language code
1807 	 * @return {string|undefined} the alpha3 language code of this locale
1808 	 */
1809 	getLanguageAlpha3: function() {
1810 		return Locale.languageAlpha1ToAlpha3(this.language);
1811 	},
1812 	
1813 	/**
1814 	 * Return the ISO 3166 region code for this locale.
1815 	 * @return {string|undefined} the region code of this locale
1816 	 */
1817 	getRegion: function() {
1818 		return this.region;
1819 	},
1820 	
1821 	/**
1822 	 * Return the region of this locale as an ISO-3166-alpha3 region code
1823 	 * @return {string|undefined} the alpha3 region code of this locale
1824 	 */
1825 	getRegionAlpha3: function() {
1826 		return Locale.regionAlpha2ToAlpha3(this.region);
1827 	},
1828 	
1829 	/**
1830 	 * Return the ISO 15924 script code for this locale
1831 	 * @return {string|undefined} the script code of this locale
1832 	 */
1833 	getScript: function () {
1834 		return this.script;
1835 	},
1836 	
1837 	/**
1838 	 * Return the variant code for this locale
1839 	 * @return {string|undefined} the variant code of this locale, if any
1840 	 */
1841 	getVariant: function() {
1842 		return this.variant;
1843 	},
1844 	
1845 	/**
1846 	 * Return the whole locale specifier as a string.
1847 	 * @return {string} the locale specifier
1848 	 */
1849 	getSpec: function() {
1850 		return this.spec;
1851 	},
1852 	
1853 	/**
1854 	 * Express this locale object as a string. Currently, this simply calls the getSpec
1855 	 * function to represent the locale as its specifier.
1856 	 * 
1857 	 * @return {string} the locale specifier
1858 	 */
1859 	toString: function() {
1860 		return this.getSpec();
1861 	},
1862 	
1863 	/**
1864 	 * Return true if the the other locale is exactly equal to the current one.
1865 	 * @return {boolean} whether or not the other locale is equal to the current one 
1866 	 */
1867 	equals: function(other) {
1868 		return this.language === other.language &&
1869 			this.region === other.region &&
1870 			this.script === other.script &&
1871 			this.variant === other.variant;
1872 	},
1873 
1874 	/**
1875 	 * Return true if the current locale is the special pseudo locale.
1876 	 * @return {boolean} true if the current locale is the special pseudo locale
1877 	 */
1878 	isPseudo: function () {
1879 		return JSUtils.indexOf(ilib.pseudoLocales, this.spec) > -1;
1880 	}
1881 };
1882 
1883 // static functions
1884 /**
1885  * @private
1886  */
1887 Locale.locales = [
1888 	
1889 ];
1890 
1891 /**
1892  * Return the list of available locales that this iLib file supports.
1893  * If this copy of ilib is pre-assembled with locale data, then the 
1894  * list locales may be much smaller
1895  * than the list of all available locales in the iLib repository. The
1896  * assembly tool will automatically fill in the list for an assembled
1897  * copy of iLib. If this copy is being used with dynamically loaded 
1898  * data, then you 
1899  * can load any locale that iLib supports. You can form a locale with any 
1900  * combination of a language and region tags that exist in the locale
1901  * data directory. Language tags are in the root of the locale data dir,
1902  * and region tags can be found underneath the "und" directory. (The 
1903  * region tags are separated into a different dir because the region names 
1904  * conflict with language names on file systems that are case-insensitive.) 
1905  * If you have culled the locale data directory to limit the size of
1906  * your app, then this function should return only those files that actually exist
1907  * according to the ilibmanifest.json file in the root of that locale
1908  * data dir. Make sure your ilibmanifest.json file is up-to-date with
1909  * respect to the list of files that exist in the locale data dir.
1910  * 
1911  * @param {boolean} sync if false, load the list of available files from disk
1912  * asynchronously, otherwise load them synchronously. (Default: true/synchronously)
1913  * @param {Function} onLoad a callback function to call if asynchronous
1914  * load was requested and the list of files have been loaded.
1915  * @return {Array.<string>} this is an array of locale specs for which 
1916  * this iLib file has locale data for
1917  */
1918 Locale.getAvailableLocales = function (sync, onLoad) {
1919 	var locales = [];
1920 	if (Locale.locales.length || typeof(ilib._load.listAvailableFiles) !== 'function') {
1921 		locales = Locale.locales;
1922 		if (onLoad && typeof(onLoad) === 'function') {
1923 			onLoad(locales);
1924 		}
1925 	} else {
1926 		if (typeof(sync) === 'undefined') {
1927 			sync = true;
1928 		}
1929 		ilib._load.listAvailableFiles(sync, function(manifest) {
1930 			if (manifest) {
1931 				for (var dir in manifest) {
1932 					var filelist = manifest[dir];
1933 					for (var i = 0; i < filelist.length; i++) {
1934 						if (filelist[i].length > 15 && filelist[i].substr(-15) === "localeinfo.json") {
1935 							locales.push(filelist[i].substring(0,filelist[i].length-16).replace(/\//g, "-"));
1936 						}
1937 					}
1938 				}
1939 			}
1940 			if (onLoad && typeof(onLoad) === 'function') {
1941 				onLoad(locales);
1942 			}
1943 		});
1944 	}
1945 	return locales;
1946 };
1947 
1948 
1949 
1950 /*< Utils.js */
1951 /*
1952  * Utils.js - Core utility routines
1953  * 
1954  * Copyright © 2012-2015, JEDLSoft
1955  *
1956  * Licensed under the Apache License, Version 2.0 (the "License");
1957  * you may not use this file except in compliance with the License.
1958  * You may obtain a copy of the License at
1959  *
1960  *     http://www.apache.org/licenses/LICENSE-2.0
1961  *
1962  * Unless required by applicable law or agreed to in writing, software
1963  * distributed under the License is distributed on an "AS IS" BASIS,
1964  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1965  *
1966  * See the License for the specific language governing permissions and
1967  * limitations under the License.
1968  */
1969 
1970 // !depends ilib.js Locale.js JSUtils.js
1971 
1972 
1973 var Utils = {};
1974 
1975 /**
1976  * Find and merge all the locale data for a particular prefix in the given locale
1977  * and return it as a single javascript object. This merges the data in the 
1978  * correct order:
1979  * 
1980  * <ol>
1981  * <li>shared data (usually English)
1982  * <li>data for language
1983  * <li>data for language + region
1984  * <li>data for language + region + script
1985  * <li>data for language + region + script + variant
1986  * </ol>
1987  * 
1988  * It is okay for any of the above to be missing. This function will just skip the 
1989  * missing data. However, if everything except the shared data is missing, this 
1990  * function returns undefined, allowing the caller to go and dynamically load the
1991  * data instead.
1992  * 
1993  * @static
1994  * @param {string} prefix prefix under ilib.data of the data to merge
1995  * @param {Locale} locale locale of the data being sought
1996  * @param {boolean=} replaceArrays if true, replace the array elements in object1 with those in object2.
1997  * If false, concatenate array elements in object1 with items in object2.
1998  * @param {boolean=} returnOne if true, only return the most locale-specific data. If false,
1999  * merge all the relevant locale data together.
2000  * @return {Object?} the merged locale data
2001  */
2002 Utils.mergeLocData = function (prefix, locale, replaceArrays, returnOne) {
2003 	var data = undefined;
2004 	var loc = locale || new Locale();
2005 	var foundLocaleData = false;
2006 	var property = prefix;
2007 	var mostSpecific;
2008 
2009 	data = ilib.data[prefix] || {};
2010 
2011 	mostSpecific = data;
2012 
2013 	if (loc.getLanguage()) {
2014 		property = prefix + '_' + loc.getLanguage();
2015 		if (ilib.data[property]) {
2016 			foundLocaleData = true;
2017 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2018 			mostSpecific = ilib.data[property];
2019 		}
2020 	}
2021 	
2022 	if (loc.getRegion()) {
2023 		property = prefix + '_' + loc.getRegion();
2024 		if (ilib.data[property]) {
2025 			foundLocaleData = true;
2026 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2027 			mostSpecific = ilib.data[property];
2028 		}
2029 	}
2030 	
2031 	if (loc.getLanguage()) {
2032 		property = prefix + '_' + loc.getLanguage();
2033 		
2034 		if (loc.getScript()) {
2035 			property = prefix + '_' + loc.getLanguage() + '_' + loc.getScript();
2036 			if (ilib.data[property]) {
2037 				foundLocaleData = true;
2038 				data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2039 				mostSpecific = ilib.data[property];
2040 			}
2041 		}
2042 		
2043 		if (loc.getRegion()) {
2044 			property = prefix + '_' + loc.getLanguage() + '_' + loc.getRegion();
2045 			if (ilib.data[property]) {
2046 				foundLocaleData = true;
2047 				data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2048 				mostSpecific = ilib.data[property];
2049 			}
2050 		}		
2051 	}
2052 	
2053 	if (loc.getRegion() && loc.getVariant()) {
2054 		property = prefix + '_' + loc.getLanguage() + '_' + loc.getVariant();
2055 		if (ilib.data[property]) {
2056 			foundLocaleData = true;
2057 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2058 			mostSpecific = ilib.data[property];
2059 		}
2060 	}
2061 
2062 	if (loc.getLanguage() && loc.getScript() && loc.getRegion()) {
2063 		property = prefix + '_' + loc.getLanguage() + '_' + loc.getScript() + '_' + loc.getRegion();
2064 		if (ilib.data[property]) {
2065 			foundLocaleData = true;
2066 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2067 			mostSpecific = ilib.data[property];
2068 		}
2069 	}
2070 
2071 	if (loc.getLanguage() && loc.getRegion() && loc.getVariant()) {
2072 		property = prefix + '_' + loc.getLanguage() + '_' + loc.getRegion() + '_' + loc.getVariant();
2073 		if (ilib.data[property]) {
2074 			foundLocaleData = true;
2075 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2076 			mostSpecific = ilib.data[property];
2077 		}
2078 	}
2079 
2080 	if (loc.getLanguage() && loc.getScript() && loc.getRegion() && loc.getVariant()) {
2081 		property = prefix + '_' + loc.getLanguage() + '_' + loc.getScript() + '_' + loc.getRegion() + '_' + loc.getVariant();
2082 		if (ilib.data[property]) {
2083 			foundLocaleData = true;
2084 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2085 			mostSpecific = ilib.data[property];
2086 		}
2087 	}
2088 	
2089 	return foundLocaleData ? (returnOne ? mostSpecific : data) : undefined;
2090 };
2091 
2092 /**
2093  * Return an array of relative path names for the
2094  * files that represent the data for the given locale.<p>
2095  * 
2096  * Note that to prevent the situation where a directory for
2097  * a language exists next to the directory for a region where
2098  * the language code and region code differ only by case, the 
2099  * plain region directories are located under the special 
2100  * "undefined" language directory which has the ISO code "und".
2101  * The reason is that some platforms have case-insensitive 
2102  * file systems, and you cannot have 2 directories with the 
2103  * same name which only differ by case. For example, "es" is
2104  * the ISO 639 code for the language "Spanish" and "ES" is
2105  * the ISO 3166 code for the region "Spain", so both the
2106  * directories cannot exist underneath "locale". The region
2107  * therefore will be loaded from "und/ES" instead.<p>  
2108  * 
2109  * <h4>Variations</h4>
2110  * 
2111  * With only language and region specified, the following
2112  * sequence of paths will be generated:<p>
2113  * 
2114  * <pre>
2115  * language
2116  * und/region
2117  * language/region
2118  * </pre>
2119  * 
2120  * With only language and script specified:<p>
2121  * 
2122  * <pre>
2123  * language
2124  * language/script
2125  * </pre>
2126  * 
2127  * With only script and region specified:<p>
2128  * 
2129  * <pre>
2130  * und/region  
2131  * </pre>
2132  * 
2133  * With only region and variant specified:<p>
2134  * 
2135  * <pre>
2136  * und/region
2137  * region/variant
2138  * </pre>
2139  * 
2140  * With only language, script, and region specified:<p>
2141  * 
2142  * <pre>
2143  * language
2144  * und/region
2145  * language/script
2146  * language/region
2147  * language/script/region
2148  * </pre>
2149  * 
2150  * With only language, region, and variant specified:<p>
2151  * 
2152  * <pre>
2153  * language
2154  * und/region
2155  * language/region
2156  * region/variant
2157  * language/region/variant
2158  * </pre>
2159  * 
2160  * With all parts specified:<p>
2161  * 
2162  * <pre>
2163  * language
2164  * und/region
2165  * language/script
2166  * language/region
2167  * region/variant
2168  * language/script/region
2169  * language/region/variant
2170  * language/script/region/variant
2171  * </pre>
2172  * 
2173  * @static
2174  * @param {Locale} locale load the files for this locale
2175  * @param {string?} name the file name of each file to load without
2176  * any path
2177  * @return {Array.<string>} An array of relative path names
2178  * for the files that contain the locale data
2179  */
2180 Utils.getLocFiles = function(locale, name) {
2181 	var dir = "";
2182 	var files = [];
2183 	var filename = name || "resources.json";
2184 	var loc = locale || new Locale();
2185 	
2186 	var language = loc.getLanguage();
2187 	var region = loc.getRegion();
2188 	var script = loc.getScript();
2189 	var variant = loc.getVariant();
2190 	
2191 	files.push(filename); // generic shared file
2192 	
2193 	if (language) {
2194 		dir = language + "/";
2195 		files.push(dir + filename);
2196 	}
2197 	
2198 	if (region) {
2199 		dir = "und/" + region + "/";
2200 		files.push(dir + filename);
2201 	}
2202 	
2203 	if (language) {
2204 		if (script) {
2205 			dir = language + "/" + script + "/";
2206 			files.push(dir + filename);
2207 		}
2208 		if (region) {
2209 			dir = language + "/" + region + "/";
2210 			files.push(dir + filename);
2211 		}
2212 	}
2213 	
2214 	if (region && variant) {
2215 		dir = "und/" + region + "/" + variant + "/";
2216 		files.push(dir + filename);
2217 	}
2218 
2219 	if (language && script && region) {
2220 		dir = language + "/" + script + "/" + region + "/";
2221 		files.push(dir + filename);
2222 	}
2223 
2224 	if (language && region && variant) {
2225 		dir = language + "/" + region + "/" + variant + "/";
2226 		files.push(dir + filename);
2227 	}
2228 
2229 	if (language && script && region && variant) {
2230 		dir = language + "/" + script + "/" + region + "/" + variant + "/";
2231 		files.push(dir + filename);
2232 	}
2233 	
2234 	return files;
2235 };
2236 
2237 /**
2238  * Load data using the new loader object or via the old function callback.
2239  * @static
2240  * @private
2241  */
2242 Utils._callLoadData = function (files, sync, params, callback) {
2243 	// console.log("Utils._callLoadData called");
2244 	if (typeof(ilib._load) === 'function') {
2245 		// console.log("Utils._callLoadData: calling as a regular function");
2246 		return ilib._load(files, sync, params, callback);
2247 	} else if (typeof(ilib._load) === 'object' && typeof(ilib._load.loadFiles) === 'function') {
2248 		// console.log("Utils._callLoadData: calling as an object");
2249 		return ilib._load.loadFiles(files, sync, params, callback);
2250 	}
2251 	
2252 	// console.log("Utils._callLoadData: not calling. Type is " + typeof(ilib._load) + " and instanceof says " + (ilib._load instanceof Loader));
2253 	return undefined;
2254 };
2255 
2256 /**
2257  * Find locale data or load it in. If the data with the given name is preassembled, it will
2258  * find the data in ilib.data. If the data is not preassembled but there is a loader function,
2259  * this function will call it to load the data. Otherwise, the callback will be called with
2260  * undefined as the data. This function will create a cache under the given class object.
2261  * If data was successfully loaded, it will be set into the cache so that future access to 
2262  * the same data for the same locale is much quicker.<p>
2263  * 
2264  * The parameters can specify any of the following properties:<p>
2265  * 
2266  * <ul>
2267  * <li><i>name</i> - String. The name of the file being loaded. Default: ResBundle.json
2268  * <li><i>object</i> - Object. The class attempting to load data. The cache is stored inside of here.
2269  * <li><i>locale</i> - Locale. The locale for which data is loaded. Default is the current locale.
2270  * <li><i>nonlocale</i> - boolean. If true, the data being loaded is not locale-specific.
2271  * <li><i>type</i> - String. Type of file to load. This can be "json" or "other" type. Default: "json" 
2272  * <li><i>replace</i> - boolean. When merging json objects, this parameter controls whether to merge arrays
2273  * or have arrays replace each other. If true, arrays in child objects replace the arrays in parent 
2274  * objects. When false, the arrays in child objects are concatenated with the arrays in parent objects.  
2275  * <li><i>loadParams</i> - Object. An object with parameters to pass to the loader function
2276  * <li><i>sync</i> - boolean. Whether or not to load the data synchronously
2277  * <li><i>callback</i> - function(?)=. callback Call back function to call when the data is available.
2278  * Data is not returned from this method, so a callback function is mandatory.
2279  * </ul>
2280  * 
2281  * @static
2282  * @param {Object} params Parameters configuring how to load the files (see above)
2283  */
2284 Utils.loadData = function(params) {
2285 	var name = "resources.json",
2286 		object = undefined, 
2287 		locale = new Locale(ilib.getLocale()), 
2288 		sync = false, 
2289 		type = undefined,
2290 		loadParams = {},
2291 		callback = undefined,
2292 		nonlocale = false,
2293 		replace = false,
2294 		basename;
2295 	
2296 	if (!params || typeof(params.callback) !== 'function') {
2297 		return;
2298 	}
2299 
2300 	if (params.name) {
2301 		name = params.name;
2302 	}
2303 	if (params.object) {
2304 		object = params.object;
2305 	}
2306 	if (params.locale) {
2307 		locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
2308 	}			
2309 	if (params.type) {
2310 		type = params.type;
2311 	}
2312 	if (params.loadParams) {
2313 		loadParams = params.loadParams;
2314 	}
2315 	if (params.sync) {
2316 		sync = params.sync;
2317 	}
2318 	if (params.nonlocale) {
2319 		nonlocale = !!params.nonlocale;
2320 	}
2321 	if (typeof(params.replace) === 'boolean') {
2322 		replace = params.replace;
2323 	}
2324 	
2325 	callback = params.callback;
2326 	
2327 	if (object && !object.cache) {
2328 		object.cache = {};
2329 	}
2330 	
2331 	if (!type) {
2332 		var dot = name.lastIndexOf(".");
2333 		type = (dot !== -1) ? name.substring(dot+1) : "text";
2334 	}
2335 
2336 	var spec = ((!nonlocale && locale.getSpec().replace(/-/g, '_')) || "root") + "," + name + "," + String(JSUtils.hashCode(loadParams));
2337 	if (!object || typeof(object.cache[spec]) === 'undefined') {
2338 		var data, returnOne = (loadParams && loadParams.returnOne);
2339 		
2340 		if (type === "json") {
2341 			// console.log("type is json");
2342 			basename = name.substring(0, name.lastIndexOf("."));
2343 			if (nonlocale) {
2344 				basename = basename.replace(/[\.:\(\)\/\\\+\-]/g, "_");
2345 				data = ilib.data[basename];
2346 			} else {
2347 				data = Utils.mergeLocData(basename, locale, replace, returnOne);
2348 			}
2349 			if (data) {
2350 				// console.log("found assembled data");
2351 				if (object) {
2352 					object.cache[spec] = data;
2353 				}
2354 				callback(data);
2355 				return;
2356 			}
2357 		}
2358 		
2359 		// console.log("ilib._load is " + typeof(ilib._load));
2360 		if (typeof(ilib._load) !== 'undefined') {
2361 			// the data is not preassembled, so attempt to load it dynamically
2362 			var files = nonlocale ? [ name || "resources.json" ] : Utils.getLocFiles(locale, name);
2363 			if (type !== "json") {
2364 				loadParams.returnOne = true;
2365 			}
2366 			
2367 			Utils._callLoadData(files, sync, loadParams, ilib.bind(this, function(arr) {
2368 				if (type === "json") {
2369 					data = ilib.data[basename] || {};
2370 					for (var i = 0; i < arr.length; i++) {
2371 						if (typeof(arr[i]) !== 'undefined') {
2372 							data = loadParams.returnOne ? arr[i] : JSUtils.merge(data, arr[i], replace);
2373 						}
2374 					}
2375 					
2376 					if (object) {
2377 						object.cache[spec] = data;
2378 					}
2379 					callback(data);
2380 				} else {
2381 					var i = arr.length-1; 
2382 					while (i > -1 && !arr[i]) {
2383 						i--;
2384 					}
2385 					if (i > -1) {
2386 						if (object) {
2387 							object.cache[spec] = arr[i];
2388 						}
2389 						callback(arr[i]);
2390 					} else {
2391 						callback(undefined);
2392 					}
2393 				}
2394 			}));
2395 		} else {
2396 			// no data other than the generic shared data
2397 			if (type === "json") {
2398 				data = ilib.data[basename];
2399 			}
2400 			if (object && data) {
2401 				object.cache[spec] = data;
2402 			}
2403 			callback(data);
2404 		}
2405 	} else {
2406 		callback(object.cache[spec]);
2407 	}
2408 };
2409 
2410 
2411 /*< LocaleInfo.js */
2412 /*
2413  * LocaleInfo.js - Encode locale-specific defaults
2414  * 
2415  * Copyright © 2012-2015, JEDLSoft
2416  *
2417  * Licensed under the Apache License, Version 2.0 (the "License");
2418  * you may not use this file except in compliance with the License.
2419  * You may obtain a copy of the License at
2420  *
2421  *     http://www.apache.org/licenses/LICENSE-2.0
2422  *
2423  * Unless required by applicable law or agreed to in writing, software
2424  * distributed under the License is distributed on an "AS IS" BASIS,
2425  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2426  *
2427  * See the License for the specific language governing permissions and
2428  * limitations under the License.
2429  */
2430 
2431 // !depends ilib.js Locale.js Utils.js
2432 
2433 // !data localeinfo
2434 
2435 
2436 /**
2437  * @class
2438  * Create a new locale info instance. Locale info instances give information about
2439  * the default settings for a particular locale. These settings may be overridden
2440  * by various parts of the code, and should be used as a fall-back setting of last
2441  * resort. <p>
2442  * 
2443  * The optional options object holds extra parameters if they are necessary. The
2444  * current list of supported options are:
2445  * 
2446  * <ul>
2447  * <li><i>onLoad</i> - a callback function to call when the locale info object is fully 
2448  * loaded. When the onLoad option is given, the localeinfo object will attempt to
2449  * load any missing locale data using the ilib loader callback.
2450  * When the constructor is done (even if the data is already preassembled), the 
2451  * onLoad function is called with the current instance as a parameter, so this
2452  * callback can be used with preassembled or dynamic loading or a mix of the two.
2453  * 
2454  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
2455  * asynchronously. If this option is given as "false", then the "onLoad"
2456  * callback must be given, as the instance returned from this constructor will
2457  * not be usable for a while. 
2458  *
2459  * <li><i>loadParams</i> - an object containing parameters to pass to the 
2460  * loader callback function when locale data is missing. The parameters are not
2461  * interpretted or modified in any way. They are simply passed along. The object 
2462  * may contain any property/value pairs as long as the calling code is in
2463  * agreement with the loader callback function as to what those parameters mean.
2464  * </ul>
2465  * 
2466  * If this copy of ilib is pre-assembled and all the data is already available, 
2467  * or if the data was already previously loaded, then this constructor will call
2468  * the onLoad callback immediately when the initialization is done. 
2469  * If the onLoad option is not given, this class will only attempt to load any
2470  * missing locale data synchronously.
2471  * 
2472  * 
2473  * @constructor
2474  * @see {ilib.setLoaderCallback} for information about registering a loader callback
2475  * function
2476  * @param {Locale|string=} locale the locale for which the info is sought, or undefined for
2477  * @param {Object=} options the locale for which the info is sought, or undefined for
2478  * the current locale
2479  */
2480 var LocaleInfo = function(locale, options) {
2481 	var sync = true,
2482 	    loadParams = undefined;
2483 	
2484 	/**
2485 	  @private 
2486 	  @type {{
2487 		calendar:string,
2488 		clock:string,
2489 		currency:string,
2490 		delimiter: {quotationStart:string,quotationEnd:string,alternateQuotationStart:string,alternateQuotationEnd:string},
2491 		firstDayOfWeek:number,
2492 		meridiems:string,
2493 		numfmt:{
2494 			currencyFormats:{common:string,commonNegative:string,iso:string,isoNegative:string},
2495 			decimalChar:string,
2496 			exponential:string,
2497 			groupChar:string,
2498 			negativenumFmt:string,
2499 			negativepctFmt:string,
2500 			pctChar:string,
2501 			pctFmt:string,
2502 			prigroupSize:number,
2503 			roundingMode:string,
2504 			script:string,
2505 			secgroupSize:number
2506 		},
2507 		timezone:string,
2508 		units:string,
2509 		weekendEnd:number,
2510 		weekendStart:number,
2511 		paperSizes:{regular:string}
2512 	  }}
2513 	*/
2514 	this.info = LocaleInfo.defaultInfo;
2515 	
2516 	switch (typeof(locale)) {
2517 		case "string":
2518 			this.locale = new Locale(locale);
2519 			break;
2520 		default:
2521 		case "undefined":
2522 			this.locale = new Locale();
2523 			break;
2524 		case "object":
2525 			this.locale = locale;
2526 			break;
2527 	}
2528 	
2529 	if (options) {
2530 		if (typeof(options.sync) !== 'undefined') {
2531 			sync = (options.sync == true);
2532 		}
2533 		
2534 		if (typeof(options.loadParams) !== 'undefined') {
2535 			loadParams = options.loadParams;
2536 		}
2537 	}
2538 
2539 	if (!LocaleInfo.cache) {
2540 		LocaleInfo.cache = {};
2541 	}
2542 
2543 	Utils.loadData({
2544 		object: LocaleInfo, 
2545 		locale: this.locale, 
2546 		name: "localeinfo.json", 
2547 		sync: sync, 
2548 		loadParams: loadParams, 
2549 		callback: ilib.bind(this, function (info) {
2550 			if (!info) {
2551 				info = LocaleInfo.defaultInfo;
2552 				var spec = this.locale.getSpec().replace(/-/g, "_");
2553 				LocaleInfo.cache[spec] = info;
2554 			}
2555 			this.info = info;
2556 			if (options && typeof(options.onLoad) === 'function') {
2557 				options.onLoad(this);
2558 			}
2559 		})
2560 	});
2561 };
2562 
2563 LocaleInfo.defaultInfo = ilib.data.localeinfo;
2564 LocaleInfo.defaultInfo = LocaleInfo.defaultInfo || {
2565 	"calendar": "gregorian",
2566 	"clock": "24",
2567 	"currency": "USD",
2568 	"delimiter": {
2569     	"quotationStart": "“",
2570     	"quotationEnd": "”",
2571     	"alternateQuotationStart": "‘",
2572     	"alternateQuotationEnd": "’"
2573     },
2574     "firstDayOfWeek": 1,
2575     "meridiems": "gregorian",
2576     "numfmt": {
2577         "currencyFormats": {
2578             "common": "{s}{n}",
2579             "commonNegative": "({s}{n})",
2580             "iso": "{s}{n}",
2581             "isoNegative": "({s}{n})"
2582         },
2583         "decimalChar": ".",
2584         "exponential": "E",
2585         "groupChar": ",",
2586         "negativenumFmt": "-{n}",
2587         "negativepctFmt": "-{n}%",
2588         "pctChar": "%",
2589         "pctFmt": "{n}%",
2590         "prigroupSize": 3,
2591         "roundingMode": "halfdown",
2592         "script": "Latn",
2593         "secgroupSize": 0,
2594         "useNative": false
2595     },
2596     "timezone": "Etc/UTC",
2597     "units": "metric",
2598     "weekendStart": 6,
2599     "weekendEnd": 0
2600 };
2601 
2602 LocaleInfo.prototype = {
2603     /**
2604      * Return the name of the locale's language in English.
2605      * @returns {string} the name of the locale's language in English
2606      */
2607     getLanguageName: function () {
2608     	return this.info["language.name"];	
2609     },
2610     
2611     /**
2612      * Return the name of the locale's region in English. If the locale
2613      * has no region, this returns undefined.
2614      * 
2615      * @returns {string|undefined} the name of the locale's region in English
2616      */
2617     getRegionName: function () {
2618     	return this.info["region.name"];	
2619     },
2620 
2621     /**
2622 	 * Return whether this locale commonly uses the 12- or the 24-hour clock.
2623 	 *  
2624 	 * @returns {string} "12" if the locale commonly uses a 12-hour clock, or "24"
2625 	 * if the locale commonly uses a 24-hour clock. 
2626 	 */
2627 	getClock: function() {
2628 		return this.info.clock;
2629 	},
2630 
2631 	/**
2632 	 * Return the locale that this info object was created with.
2633 	 * @returns {Locale} The locale spec of the locale used to construct this info instance
2634 	 */
2635 	getLocale: function () {
2636 		return this.locale;
2637 	},
2638 	
2639 	/**
2640 	 * Return the name of the measuring system that is commonly used in the given locale.
2641 	 * Valid values are "uscustomary", "imperial", and "metric".
2642 	 * 
2643 	 * @returns {string} The name of the measuring system commonly used in the locale
2644 	 */
2645 	getUnits: function () {
2646 		return this.info.units;
2647 	},
2648 	
2649 	/**
2650 	 * Return the name of the calendar that is commonly used in the given locale.
2651 	 * 
2652 	 * @returns {string} The name of the calendar commonly used in the locale
2653 	 */
2654 	getCalendar: function () {
2655 		return this.info.calendar;
2656 	},
2657 	
2658 	/**
2659 	 * Return the day of week that starts weeks in the current locale. Days are still
2660 	 * numbered the standard way with 0 for Sunday through 6 for Saturday, but calendars 
2661 	 * should be displayed and weeks calculated with the day of week returned from this 
2662 	 * function as the first day of the week.
2663 	 * 
2664 	 * @returns {number} the day of the week that starts weeks in the current locale.
2665 	 */
2666 	getFirstDayOfWeek: function () {
2667 		return this.info.firstDayOfWeek;
2668 	},
2669 	
2670 	/**
2671 	 * Return the day of week that starts weekend in the current locale. Days are still
2672 	 * numbered the standard way with 0 for Sunday through 6 for Saturday.
2673 	 * 
2674 	 * @returns {number} the day of the week that starts weeks in the current locale.
2675 	 */
2676 	getWeekEndStart: function () {
2677 		return this.info.weekendStart;
2678 	},
2679 
2680 	/**
2681 	 * Return the day of week that starts weekend in the current locale. Days are still
2682 	 * numbered the standard way with 0 for Sunday through 6 for Saturday.
2683 	 * 
2684 	 * @returns {number} the day of the week that starts weeks in the current locale.
2685 	 */
2686 	getWeekEndEnd: function () {
2687 		return this.info.weekendEnd;
2688 	},
2689 
2690 	/**
2691 	 * Return the default time zone for this locale. Many locales span across multiple
2692 	 * time zones. In this case, the time zone with the largest population is chosen
2693 	 * to represent the locale. This is obviously not that accurate, but then again,
2694 	 * this method's return value should only be used as a default anyways.
2695 	 * @returns {string} the default time zone for this locale.
2696 	 */
2697 	getTimeZone: function () {
2698 		return this.info.timezone;
2699 	},
2700 	
2701 	/**
2702 	 * Return the decimal separator for formatted numbers in this locale.
2703 	 * @returns {string} the decimal separator char
2704 	 */
2705 	getDecimalSeparator: function () {
2706 		return this.info.numfmt.decimalChar;
2707 	},
2708 	
2709 	/**
2710 	 * Return the decimal separator for formatted numbers in this locale for native script.
2711 	 * @returns {string} the decimal separator char
2712 	 */
2713 	getNativeDecimalSeparator: function () {
2714 		return (this.info.native_numfmt && this.info.native_numfmt.decimalChar) || this.info.numfmt.decimalChar;
2715 	},
2716 	
2717 	/**
2718 	 * Return the separator character used to separate groups of digits on the 
2719 	 * integer side of the decimal character.
2720 	 * @returns {string} the grouping separator char
2721 	 */
2722 	getGroupingSeparator: function () {
2723 		return this.info.numfmt.groupChar;
2724 	},
2725 
2726 	/**
2727 	 * Return the separator character used to separate groups of digits on the 
2728 	 * integer side of the decimal character for the native script if present other than the default script.
2729 	 * @returns {string} the grouping separator char
2730 	 */
2731 	getNativeGroupingSeparator: function () {
2732 		return (this.info.native_numfmt && this.info.native_numfmt.groupChar) || this.info.numfmt.groupChar;
2733 	},
2734 	
2735 	/**
2736 	 * Return the minimum number of digits grouped together on the integer side 
2737 	 * for the first (primary) group. 
2738 	 * In western European cultures, groupings are in 1000s, so the number of digits
2739 	 * is 3. 
2740 	 * @returns {number} the number of digits in a primary grouping, or 0 for no grouping
2741 	 */
2742 	getPrimaryGroupingDigits: function () {
2743 		return (typeof(this.info.numfmt.prigroupSize) !== 'undefined' && this.info.numfmt.prigroupSize) || 0;
2744 	},
2745 
2746 	/**
2747 	 * Return the minimum number of digits grouped together on the integer side
2748 	 * for the second or more (secondary) group.<p>
2749 	 *   
2750 	 * In western European cultures, all groupings are by 1000s, so the secondary
2751 	 * size should be 0 because there is no secondary size. In general, if this 
2752 	 * method returns 0, then all groupings are of the primary size.<p> 
2753 	 * 
2754 	 * For some other cultures, the first grouping (primary)
2755 	 * is 3 and any subsequent groupings (secondary) are two. So, 100000 would be
2756 	 * written as: "1,00,000".
2757 	 * 
2758 	 * @returns {number} the number of digits in a secondary grouping, or 0 for no 
2759 	 * secondary grouping. 
2760 	 */
2761 	getSecondaryGroupingDigits: function () {
2762 		return this.info.numfmt.secgroupSize || 0;
2763 	},
2764 
2765 	/**
2766 	 * Return the format template used to format percentages in this locale.
2767 	 * @returns {string} the format template for formatting percentages
2768 	 */
2769 	getPercentageFormat: function () {
2770 		return this.info.numfmt.pctFmt;
2771 	},
2772 
2773 	/**
2774 	 * Return the format template used to format percentages in this locale
2775 	 * with negative amounts.
2776 	 * @returns {string} the format template for formatting percentages
2777 	 */
2778 	getNegativePercentageFormat: function () {
2779 		return this.info.numfmt.negativepctFmt;
2780 	},
2781 
2782 	/**
2783 	 * Return the symbol used for percentages in this locale.
2784 	 * @returns {string} the symbol used for percentages in this locale
2785 	 */
2786 	getPercentageSymbol: function () {
2787 		return this.info.numfmt.pctChar || "%";
2788 	},
2789 
2790 	/**
2791 	 * Return the symbol used for exponential in this locale.
2792 	 * @returns {string} the symbol used for exponential in this locale
2793 	 */
2794 	getExponential: function () {
2795 		return this.info.numfmt.exponential;
2796 	},
2797 
2798 	/**
2799 	 * Return the symbol used for exponential in this locale for native script.
2800 	 * @returns {string} the symbol used for exponential in this locale for native script
2801 	 */
2802 	getNativeExponential: function () {
2803 		return (this.info.native_numfmt && this.info.native_numfmt.exponential) || this.info.numfmt.exponential;
2804 	},
2805 
2806 	/**
2807 	 * Return the symbol used for percentages in this locale for native script.
2808 	 * @returns {string} the symbol used for percentages in this locale for native script
2809 	 */
2810 	getNativePercentageSymbol: function () {
2811 		return (this.info.native_numfmt && this.info.native_numfmt.pctChar) || this.info.numfmt.pctChar || "%";
2812 	
2813 	},
2814 	/**
2815 	 * Return the format template used to format negative numbers in this locale.
2816 	 * @returns {string} the format template for formatting negative numbers
2817 	 */
2818 	getNegativeNumberFormat: function () { 
2819 		return this.info.numfmt.negativenumFmt;
2820 	},
2821 	
2822 	/**
2823 	 * Return an object containing the format templates for formatting currencies
2824 	 * in this locale. The object has a number of properties in it that each are
2825 	 * a particular style of format. Normally, this contains a "common" and an "iso"
2826 	 * style, but may contain others in the future.
2827 	 * @returns {Object} an object containing the format templates for currencies
2828 	 */
2829 	getCurrencyFormats: function () {
2830 		return this.info.numfmt.currencyFormats;
2831 	},
2832 	
2833 	/**
2834 	 * Return the currency that is legal in the locale, or which is most commonly 
2835 	 * used in regular commerce.
2836 	 * @returns {string} the ISO 4217 code for the currency of this locale
2837 	 */
2838 	getCurrency: function () {
2839 		return this.info.currency;
2840 	},
2841 	
2842 	/**
2843 	 * Return a string that describes the style of digits used by this locale.
2844 	 * Possible return values are:
2845 	 * <ul>
2846 	 * <li><i>western</i> - uses the regular western 10-based digits 0 through 9
2847 	 * <li><i>optional</i> - native 10-based digits exist, but in modern usage,
2848 	 * this locale most often uses western digits
2849 	 * <li><i>native</i> - native 10-based native digits exist and are used
2850 	 * regularly by this locale
2851 	 * <li><i>custom</i> - uses native digits by default that are not 10-based
2852 	 * </ul>
2853 	 * @returns {string} string that describes the style of digits used in this locale
2854 	 */
2855 	getDigitsStyle: function () {
2856 		if (this.info.numfmt.useNative) {
2857 			return "native";
2858 		}
2859 		if (typeof(this.info.native_numfmt) !== 'undefined') {
2860 			return "optional";
2861 		}
2862 		return "western";
2863 	},
2864 	
2865 	/**
2866 	 * Return the digits of the default script if they are defined.
2867 	 * If not defined, the default should be the regular "Arabic numerals"
2868 	 * used in the Latin script. (0-9)
2869 	 * @returns {string|undefined} the digits used in the default script 
2870 	 */
2871 	getDigits: function () {
2872 		return this.info.numfmt.digits;
2873 	},
2874 	
2875 	/**
2876 	 * Return the digits of the native script if they are defined. 
2877 	 * @returns {string|undefined} the digits used in the default script 
2878 	 */
2879 	getNativeDigits: function () {
2880 		return (this.info.numfmt.useNative && this.info.numfmt.digits) || (this.info.native_numfmt && this.info.native_numfmt.digits);
2881 	},
2882 	
2883 	/**
2884 	 * If this locale typically uses a different type of rounding for numeric
2885 	 * formatting other than halfdown, especially for currency, then it can be 
2886 	 * specified in the localeinfo. If the locale uses the default, then this 
2887 	 * method returns undefined. The locale's rounding method overrides the 
2888 	 * rounding method for the currency itself, which can sometimes shared 
2889 	 * between various locales so it is less specific.
2890 	 * @returns {string} the name of the rounding mode typically used in this
2891 	 * locale, or "halfdown" if the locale does not override the default
2892 	 */
2893 	getRoundingMode: function () {
2894 		return this.info.numfmt.roundingMode;
2895 	},
2896 	
2897 	/**
2898 	 * Return the default script used to write text in the language of this 
2899 	 * locale. Text for most languages is written in only one script, but there
2900 	 * are some languages where the text can be written in a number of scripts,
2901 	 * depending on a variety of things such as the region, ethnicity, religion, 
2902 	 * etc. of the author. This method returns the default script for the
2903 	 * locale, in which the language is most commonly written.<p> 
2904 	 * 
2905 	 * The script is returned as an ISO 15924 4-letter code.
2906 	 * 
2907 	 * @returns {string} the ISO 15924 code for the default script used to write
2908 	 * text in this locale 
2909 	 */
2910 	getDefaultScript: function() {
2911 		return (this.info.scripts) ? this.info.scripts[0] : "Latn";
2912 	},
2913 	
2914 	/**
2915 	 * Return the script used for the current locale. If the current locale
2916 	 * explicitly defines a script, then this script is returned. If not, then 
2917 	 * the default script for the locale is returned.
2918 	 * 
2919 	 * @see LocaleInfo.getDefaultScript
2920 	 * @returns {string} the ISO 15924 code for the script used to write
2921 	 * text in this locale
2922 	 */
2923 	getScript: function() {
2924 		return this.locale.getScript() || this.getDefaultScript(); 
2925 	},
2926 	
2927 	/**
2928 	 * Return an array of script codes which are used to write text in the current
2929 	 * language. Text for most languages is written in only one script, but there
2930 	 * are some languages where the text can be written in a number of scripts,
2931 	 * depending on a variety of things such as the region, ethnicity, religion, 
2932 	 * etc. of the author. This method returns an array of script codes in which 
2933 	 * the language is commonly written.
2934 	 * 
2935 	 * @returns {Array.<string>} an array of ISO 15924 codes for the scripts used 
2936 	 * to write text in this language
2937 	 */
2938 	getAllScripts: function() {
2939 		return this.info.scripts || ["Latn"];
2940 	},
2941 	
2942 	/**
2943 	 * Return the default style of meridiems used in this locale. Meridiems are 
2944 	 * times of day like AM/PM. In a few locales with some calendars, for example
2945 	 * Amharic/Ethiopia using the Ethiopic calendar, the times of day may be
2946 	 * split into different segments than simple AM/PM as in the Gregorian 
2947 	 * calendar. Only a few locales are like that. For most locales, formatting 
2948 	 * a Gregorian date will use the regular Gregorian AM/PM meridiems.
2949 	 *  
2950 	 * @returns {string} the default meridiems style used in this locale. Possible
2951 	 * values are "gregorian", "chinese", and "ethiopic"
2952 	 */
2953 	getMeridiemsStyle: function () {
2954 		return this.info.meridiems || "gregorian";
2955 	},	
2956 	/**
2957 	 * Return the default PaperSize information in this locale.
2958 	 * @returns {string} default PaperSize in this locale
2959 	 */
2960 	getPaperSize: function () {
2961 		return this.info.paperSizes.regular;
2962 	},
2963 	/**
2964 	 * Return the default Delimiter QuotationStart information in this locale.
2965 	 * @returns {string} default QuotationStart in this locale
2966 	 */
2967 	getDelimiterQuotationStart: function () {
2968 		return this.info.delimiter.quotationStart;
2969 	},
2970 	/**
2971 	 * Return the default Delimiter QuotationEnd information in this locale.
2972 	 * @returns {string} default QuotationEnd in this locale
2973 	 */
2974 	getDelimiterQuotationEnd: function () {
2975 		return this.info.delimiter.quotationEnd;
2976 	}
2977 };
2978 
2979 
2980 
2981 /*< IDate.js */
2982 /*
2983  * IDate.js - Represent a date in any calendar. This class is subclassed for each 
2984  * calendar and includes some shared functionality.
2985  * 
2986  * Copyright © 2012-2015, JEDLSoft
2987  *
2988  * Licensed under the Apache License, Version 2.0 (the "License");
2989  * you may not use this file except in compliance with the License.
2990  * You may obtain a copy of the License at
2991  *
2992  *     http://www.apache.org/licenses/LICENSE-2.0
2993  *
2994  * Unless required by applicable law or agreed to in writing, software
2995  * distributed under the License is distributed on an "AS IS" BASIS,
2996  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2997  *
2998  * See the License for the specific language governing permissions and
2999  * limitations under the License.
3000  */
3001 
3002 /* !depends LocaleInfo.js */
3003 
3004 
3005 /**
3006  * @class
3007  * Superclass for all the calendar date classes that contains shared 
3008  * functionality. This class is never instantiated on its own. Instead,
3009  * you should use the {@link DateFactory} function to manufacture a new
3010  * instance of a subclass of IDate. This class is called IDate for "ilib
3011  * date" so that it does not conflict with the built-in Javascript Date
3012  * class.
3013  * 
3014  * @private
3015  * @constructor
3016  * @param {Object=} options The date components to initialize this date with
3017  */
3018 var IDate = function(options) {
3019 };
3020 
3021 /* place for the subclasses to put their constructors so that the factory method
3022  * can find them. Do this to add your date after it's defined: 
3023  * IDate._constructors["mytype"] = IDate.MyTypeConstructor;
3024  */
3025 IDate._constructors = {};
3026 
3027 IDate.prototype = {
3028 	getType: function() {
3029 		return "date";
3030 	},
3031 	
3032 	/**
3033 	 * Return the unix time equivalent to this date instance. Unix time is
3034 	 * the number of milliseconds since midnight on Jan 1, 1970 UTC (Gregorian). This 
3035 	 * method only returns a valid number for dates between midnight, 
3036 	 * Jan 1, 1970 UTC (Gregorian) and Jan 19, 2038 at 3:14:07am UTC (Gregorian) when 
3037 	 * the unix time runs out. If this instance encodes a date outside of that range, 
3038 	 * this method will return -1. For date types that are not Gregorian, the point 
3039 	 * in time represented by this date object will only give a return value if it
3040 	 * is in the correct range in the Gregorian calendar as given previously.
3041 	 * 
3042 	 * @return {number} a number giving the unix time, or -1 if the date is outside the
3043 	 * valid unix time range
3044 	 */
3045 	getTime: function() {
3046 		return this.rd.getTime(); 
3047 	},
3048 	
3049 	/**
3050 	 * Return the extended unix time equivalent to this Gregorian date instance. Unix time is
3051 	 * the number of milliseconds since midnight on Jan 1, 1970 UTC. Traditionally unix time
3052 	 * (or the type "time_t" in C/C++) is only encoded with an unsigned 32 bit integer, and thus 
3053 	 * runs out on Jan 19, 2038. However, most Javascript engines encode numbers well above 
3054 	 * 32 bits and the Date object allows you to encode up to 100 million days worth of time 
3055 	 * after Jan 1, 1970, and even more interestingly, 100 million days worth of time before
3056 	 * Jan 1, 1970 as well. This method returns the number of milliseconds in that extended 
3057 	 * range. If this instance encodes a date outside of that range, this method will return
3058 	 * NaN.
3059 	 * 
3060 	 * @return {number} a number giving the extended unix time, or Nan if the date is outside 
3061 	 * the valid extended unix time range
3062 	 */
3063 	getTimeExtended: function() {
3064 		return this.rd.getTimeExtended();
3065 	},
3066 
3067 	/**
3068 	 * Set the time of this instance according to the given unix time. Unix time is
3069 	 * the number of milliseconds since midnight on Jan 1, 1970.
3070 	 * 
3071 	 * @param {number} millis the unix time to set this date to in milliseconds 
3072 	 */
3073 	setTime: function(millis) {
3074 		this.rd = this.newRd({
3075 			unixtime: millis,
3076 			cal: this.cal
3077 		});
3078 		this._calcDateComponents();
3079 	},
3080 	
3081 	getDays: function() {
3082 		return this.day;
3083 	},
3084 	getMonths: function() {
3085 		return this.month;
3086 	},
3087 	getYears: function() {
3088 		return this.year;
3089 	},
3090 	getHours: function() {
3091 		return this.hour;
3092 	},
3093 	getMinutes: function() {
3094 		return this.minute;
3095 	},
3096 	getSeconds: function() {
3097 		return this.second;
3098 	},
3099 	getMilliseconds: function() {
3100 		return this.millisecond;
3101 	},
3102 	getEra: function() {
3103 		return (this.year < 1) ? -1 : 1;
3104 	},
3105 
3106 	setDays: function(day) {
3107 		this.day = parseInt(day, 10) || 1;
3108 		this.rd._setDateComponents(this);
3109 	},
3110 	setMonths: function(month) {
3111 		this.month = parseInt(month, 10) || 1;
3112 		this.rd._setDateComponents(this);
3113 	},
3114 	setYears: function(year) {
3115 		this.year = parseInt(year, 10) || 0;
3116 		this.rd._setDateComponents(this);
3117 	},
3118 	
3119 	setHours: function(hour) {
3120 		this.hour = parseInt(hour, 10) || 0;
3121 		this.rd._setDateComponents(this);
3122 	},
3123 	setMinutes: function(minute) {
3124 		this.minute = parseInt(minute, 10) || 0;
3125 		this.rd._setDateComponents(this);
3126 	},
3127 	setSeconds: function(second) {
3128 		this.second = parseInt(second, 10) || 0;
3129 		this.rd._setDateComponents(this);
3130 	},
3131 	setMilliseconds: function(milli) {
3132 		this.millisecond = parseInt(milli, 10) || 0;
3133 		this.rd._setDateComponents(this);
3134 	},
3135 	
3136 	/**
3137 	 * Return a new date instance in the current calendar that represents the first instance 
3138 	 * of the given day of the week before the current date. The day of the week is encoded
3139 	 * as a number where 0 = Sunday, 1 = Monday, etc.
3140 	 * 
3141 	 * @param {number} dow the day of the week before the current date that is being sought
3142 	 * @return {IDate} the date being sought
3143 	 */
3144 	before: function (dow) {
3145 		return new this.constructor({
3146 			rd: this.rd.before(dow, this.offset),
3147 			timezone: this.timezone
3148 		});
3149 	},
3150 	
3151 	/**
3152 	 * Return a new date instance in the current calendar that represents the first instance 
3153 	 * of the given day of the week after the current date. The day of the week is encoded
3154 	 * as a number where 0 = Sunday, 1 = Monday, etc.
3155 	 * 
3156 	 * @param {number} dow the day of the week after the current date that is being sought
3157 	 * @return {IDate} the date being sought
3158 	 */
3159 	after: function (dow) {
3160 		return new this.constructor({
3161 			rd: this.rd.after(dow, this.offset),
3162 			timezone: this.timezone
3163 		});
3164 	},
3165 
3166 	/**
3167 	 * Return a new Gregorian date instance that represents the first instance of the 
3168 	 * given day of the week on or before the current date. The day of the week is encoded
3169 	 * as a number where 0 = Sunday, 1 = Monday, etc.
3170 	 * 
3171 	 * @param {number} dow the day of the week on or before the current date that is being sought
3172 	 * @return {IDate} the date being sought
3173 	 */
3174 	onOrBefore: function (dow) {
3175 		return new this.constructor({
3176 			rd: this.rd.onOrBefore(dow, this.offset),
3177 			timezone: this.timezone
3178 		});
3179 	},
3180 
3181 	/**
3182 	 * Return a new Gregorian date instance that represents the first instance of the 
3183 	 * given day of the week on or after the current date. The day of the week is encoded
3184 	 * as a number where 0 = Sunday, 1 = Monday, etc.
3185 	 * 
3186 	 * @param {number} dow the day of the week on or after the current date that is being sought
3187 	 * @return {IDate} the date being sought
3188 	 */
3189 	onOrAfter: function (dow) {
3190 		return new this.constructor({
3191 			rd: this.rd.onOrAfter(dow, this.offset),
3192 			timezone: this.timezone
3193 		});
3194 	},
3195 	
3196 	/**
3197 	 * Return a Javascript Date object that is equivalent to this date
3198 	 * object.
3199 	 * 
3200 	 * @return {Date|undefined} a javascript Date object
3201 	 */
3202 	getJSDate: function() {
3203 		var unix = this.rd.getTimeExtended();
3204 		return isNaN(unix) ? undefined : new Date(unix); 
3205 	},
3206 	
3207 	/**
3208 	 * Return the Rata Die (fixed day) number of this date.
3209 	 * 
3210 	 * @protected
3211 	 * @return {number} the rd date as a number
3212 	 */
3213 	getRataDie: function() {
3214 		return this.rd.getRataDie();
3215 	},
3216 	
3217 	/**
3218 	 * Set the date components of this instance based on the given rd.
3219 	 * @protected
3220 	 * @param {number} rd the rata die date to set
3221 	 */
3222 	setRd: function (rd) {
3223 		this.rd = this.newRd({
3224 			rd: rd,
3225 			cal: this.cal
3226 		});
3227 		this._calcDateComponents();
3228 	},
3229 	
3230 	/**
3231 	 * Return the Julian Day equivalent to this calendar date as a number.
3232 	 * 
3233 	 * @return {number} the julian date equivalent of this date
3234 	 */
3235 	getJulianDay: function() {
3236 		return this.rd.getJulianDay();
3237 	},
3238 	
3239 	/**
3240 	 * Set the date of this instance using a Julian Day.
3241 	 * @param {number|JulianDay} date the Julian Day to use to set this date
3242 	 */
3243 	setJulianDay: function (date) {
3244 		this.rd = this.newRd({
3245 			julianday: (typeof(date) === 'object') ? date.getDate() : date,
3246 			cal: this.cal
3247 		});
3248 		this._calcDateComponents();
3249 	},
3250 
3251 	/**
3252 	 * Return the time zone associated with this date, or 
3253 	 * undefined if none was specified in the constructor.
3254 	 * 
3255 	 * @return {string|undefined} the name of the time zone for this date instance
3256 	 */
3257 	getTimeZone: function() {
3258 		return this.timezone || "local";
3259 	},
3260 	
3261 	/**
3262 	 * Set the time zone associated with this date.
3263 	 * @param {string=} tzName the name of the time zone to set into this date instance,
3264 	 * or "undefined" to unset the time zone 
3265 	 */
3266 	setTimeZone: function (tzName) {
3267 		if (!tzName || tzName === "") {
3268 			// same as undefining it
3269 			this.timezone = undefined;
3270 			this.tz = undefined;
3271 		} else if (typeof(tzName) === 'string') {
3272 			this.timezone = tzName;
3273 			this.tz = undefined;
3274 			// assuming the same UTC time, but a new time zone, now we have to 
3275 			// recalculate what the date components are
3276 			this._calcDateComponents();
3277 		}
3278 	},
3279 	
3280 	/**
3281 	 * Return the rd number of the first Sunday of the given ISO year.
3282 	 * @protected
3283 	 * @param {number} year the year for which the first Sunday is being sought
3284 	 * @return {number} the rd of the first Sunday of the ISO year
3285 	 */
3286 	firstSunday: function (year) {
3287 		var firstDay = this.newRd({
3288 			year: year,
3289 			month: 1,
3290 			day: 1,
3291 			hour: 0,
3292 			minute: 0,
3293 			second: 0,
3294 			millisecond: 0,
3295 			cal: this.cal
3296 		});
3297 		var firstThu = this.newRd({
3298 			rd: firstDay.onOrAfter(4),
3299 			cal: this.cal
3300 		});
3301 		return firstThu.before(0);
3302 	},
3303 	
3304 	/**
3305 	 * Return the ISO 8601 week number in the current year for the current date. The week
3306 	 * number ranges from 0 to 55, as some years have 55 weeks assigned to them in some
3307 	 * calendars.
3308 	 * 
3309 	 * @return {number} the week number for the current date
3310 	 */
3311 	getWeekOfYear: function() {
3312 		var rd = Math.floor(this.rd.getRataDie());
3313 		var year = this._calcYear(rd + this.offset);
3314 		var yearStart = this.firstSunday(year);
3315 		var nextYear;
3316 		
3317 		// if we have a January date, it may be in this ISO year or the previous year
3318 		if (rd < yearStart) {
3319 			yearStart = this.firstSunday(year-1);
3320 		} else {
3321 			// if we have a late December date, it may be in this ISO year, or the next year
3322 			nextYear = this.firstSunday(year+1);
3323 			if (rd >= nextYear) {
3324 				yearStart = nextYear;
3325 			}
3326 		}
3327 		
3328 		return Math.floor((rd-yearStart)/7) + 1;
3329 	},
3330 	
3331 	/**
3332 	 * Return the ordinal number of the week within the month. The first week of a month is
3333 	 * the first one that contains 4 or more days in that month. If any days precede this
3334 	 * first week, they are marked as being in week 0. This function returns values from 0
3335 	 * through 6.<p>
3336 	 * 
3337 	 * The locale is a required parameter because different locales that use the same 
3338 	 * Gregorian calendar consider different days of the week to be the beginning of
3339 	 * the week. This can affect the week of the month in which some days are located.
3340 	 * 
3341 	 * @param {Locale|string} locale the locale or locale spec to use when figuring out 
3342 	 * the first day of the week
3343 	 * @return {number} the ordinal number of the week within the current month
3344 	 */
3345 	getWeekOfMonth: function(locale) {
3346 		var li = new LocaleInfo(locale);
3347 		
3348 		var first = this.newRd({
3349 			year: this._calcYear(this.rd.getRataDie()+this.offset),
3350 			month: this.getMonths(),
3351 			day: 1,
3352 			hour: 0,
3353 			minute: 0,
3354 			second: 0,
3355 			millisecond: 0,
3356 			cal: this.cal
3357 		});
3358 		var weekStart = first.onOrAfter(li.getFirstDayOfWeek());
3359 		
3360 		if (weekStart - first.getRataDie() > 3) {
3361 			// if the first week has 4 or more days in it of the current month, then consider
3362 			// that week 1. Otherwise, it is week 0. To make it week 1, move the week start
3363 			// one week earlier.
3364 			weekStart -= 7;
3365 		}
3366 		return Math.floor((this.rd.getRataDie() - weekStart) / 7) + 1;
3367 	}
3368 };
3369 
3370 
3371 /*< MathUtils.js */
3372 /*
3373  * MathUtils.js - Misc math utility routines
3374  * 
3375  * Copyright © 2013-2015, JEDLSoft
3376  *
3377  * Licensed under the Apache License, Version 2.0 (the "License");
3378  * you may not use this file except in compliance with the License.
3379  * You may obtain a copy of the License at
3380  *
3381  *     http://www.apache.org/licenses/LICENSE-2.0
3382  *
3383  * Unless required by applicable law or agreed to in writing, software
3384  * distributed under the License is distributed on an "AS IS" BASIS,
3385  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3386  *
3387  * See the License for the specific language governing permissions and
3388  * limitations under the License.
3389  */
3390 
3391 var MathUtils = {};
3392 
3393 /**
3394  * Return the sign of the given number. If the sign is negative, this function
3395  * returns -1. If the sign is positive or zero, this function returns 1.
3396  * @static
3397  * @param {number} num the number to test
3398  * @return {number} -1 if the number is negative, and 1 otherwise
3399  */
3400 MathUtils.signum = function (num) {
3401 	var n = num;
3402 	if (typeof(num) === 'string') {
3403 		n = parseInt(num, 10);
3404 	} else if (typeof(num) !== 'number') {
3405 		return 1;
3406 	}
3407 	return (n < 0) ? -1 : 1;
3408 };
3409 
3410 /**
3411  * @static
3412  * @protected
3413  * @param {number} num number to round
3414  * @return {number} rounded number
3415  */
3416 MathUtils.floor = function (num) {
3417 	return Math.floor(num);
3418 };
3419 
3420 /**
3421  * @static
3422  * @protected
3423  * @param {number} num number to round
3424  * @return {number} rounded number
3425  */
3426 MathUtils.ceiling = function (num) {
3427 	return Math.ceil(num);
3428 };
3429 
3430 /**
3431  * @static
3432  * @protected
3433  * @param {number} num number to round
3434  * @return {number} rounded number
3435  */
3436 MathUtils.down = function (num) {
3437 	return (num < 0) ? Math.ceil(num) : Math.floor(num);
3438 };
3439 
3440 /**
3441  * @static
3442  * @protected
3443  * @param {number} num number to round
3444  * @return {number} rounded number
3445  */
3446 MathUtils.up = function (num) {
3447 	return (num < 0) ? Math.floor(num) : Math.ceil(num);
3448 };
3449 
3450 /**
3451  * @static
3452  * @protected
3453  * @param {number} num number to round
3454  * @return {number} rounded number
3455  */
3456 MathUtils.halfup = function (num) {
3457 	return (num < 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5);
3458 };
3459 
3460 /**
3461  * @static
3462  * @protected
3463  * @param {number} num number to round
3464  * @return {number} rounded number
3465  */
3466 MathUtils.halfdown = function (num) {
3467 	return (num < 0) ? Math.floor(num + 0.5) : Math.ceil(num - 0.5);
3468 };
3469 
3470 /**
3471  * @static
3472  * @protected
3473  * @param {number} num number to round
3474  * @return {number} rounded number
3475  */
3476 MathUtils.halfeven = function (num) {
3477 	return (Math.floor(num) % 2 === 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5);
3478 };
3479 
3480 /**
3481  * @static
3482  * @protected
3483  * @param {number} num number to round
3484  * @return {number} rounded number
3485  */
3486 MathUtils.halfodd = function (num) {
3487 	return (Math.floor(num) % 2 !== 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5);
3488 };
3489 
3490 /**
3491  * Do a proper modulo function. The Javascript % operator will give the truncated
3492  * division algorithm, but for calendrical calculations, we need the Euclidean
3493  * division algorithm where the remainder of any division, whether the dividend
3494  * is negative or not, is always a positive number in the range [0, modulus).<p>
3495  * 
3496  * 
3497  * @static
3498  * @param {number} dividend the number being divided
3499  * @param {number} modulus the number dividing the dividend. This should always be a positive number.
3500  * @return the remainder of dividing the dividend by the modulus.  
3501  */
3502 MathUtils.mod = function (dividend, modulus) {
3503 	if (modulus == 0) {
3504 		return 0;
3505 	}
3506 	var x = dividend % modulus;
3507 	return (x < 0) ? x + modulus : x;
3508 };
3509 
3510 /**
3511  * Do a proper adjusted modulo function. The Javascript % operator will give the truncated
3512  * division algorithm, but for calendrical calculations, we need the Euclidean
3513  * division algorithm where the remainder of any division, whether the dividend
3514  * is negative or not, is always a positive number in the range (0, modulus]. The adjusted
3515  * modulo function differs from the regular modulo function in that when the remainder is
3516  * zero, the modulus should be returned instead.<p>
3517  * 
3518  * 
3519  * @static
3520  * @param {number} dividend the number being divided
3521  * @param {number} modulus the number dividing the dividend. This should always be a positive number.
3522  * @return the remainder of dividing the dividend by the modulus.  
3523  */
3524 MathUtils.amod = function (dividend, modulus) {
3525 	if (modulus == 0) {
3526 		return 0;
3527 	}
3528 	var x = dividend % modulus;
3529 	return (x <= 0) ? x + modulus : x;
3530 };
3531 
3532 
3533 
3534 /*< IString.js */
3535 /*
3536  * IString.js - ilib string subclass definition
3537  * 
3538  * Copyright © 2012-2015, JEDLSoft
3539  *
3540  * Licensed under the Apache License, Version 2.0 (the "License");
3541  * you may not use this file except in compliance with the License.
3542  * You may obtain a copy of the License at
3543  *
3544  *     http://www.apache.org/licenses/LICENSE-2.0
3545  *
3546  * Unless required by applicable law or agreed to in writing, software
3547  * distributed under the License is distributed on an "AS IS" BASIS,
3548  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3549  *
3550  * See the License for the specific language governing permissions and
3551  * limitations under the License.
3552  */
3553 
3554 // !depends ilib.js Utils.js Locale.js MathUtils.js
3555 
3556 // !data plurals
3557 
3558 
3559 /**
3560  * @class
3561  * Create a new ilib string instance. This string inherits from and
3562  * extends the Javascript String class. It can be
3563  * used almost anywhere that a normal Javascript string is used, though in
3564  * some instances you will need to call the {@link #toString} method when
3565  * a built-in Javascript string is needed. The formatting methods are 
3566  * methods that are not in the intrinsic String class and are most useful
3567  * when localizing strings in an app or web site in combination with 
3568  * the ResBundle class.<p>
3569  * 
3570  * This class is named IString ("ilib string") so as not to conflict with the 
3571  * built-in Javascript String class.
3572  * 
3573  * @constructor
3574  * @param {string|IString=} string initialize this instance with this string 
3575  */
3576 var IString = function (string) {
3577 	if (typeof(string) === 'object') {
3578 		if (string instanceof IString) {
3579 			this.str = string.str;	
3580 		} else {
3581 			this.str = string.toString();
3582 		}
3583 	} else if (typeof(string) === 'string') {
3584 		this.str = new String(string);
3585 	} else {
3586 		this.str = "";
3587 	}
3588 	this.length = this.str.length;
3589 	this.cpLength = -1;
3590 	this.localeSpec = ilib.getLocale();
3591 };
3592 
3593 /**
3594  * Return true if the given character is a Unicode surrogate character,
3595  * either high or low.
3596  * 
3597  * @private
3598  * @static
3599  * @param {string} ch character to check
3600  * @return {boolean} true if the character is a surrogate
3601  */
3602 IString._isSurrogate = function (ch) {
3603 	var n = ch.charCodeAt(0);
3604 	return ((n >= 0xDC00 && n <= 0xDFFF) || (n >= 0xD800 && n <= 0xDBFF));
3605 };
3606 
3607 /**
3608  * Convert a UCS-4 code point to a Javascript string. The codepoint can be any valid 
3609  * UCS-4 Unicode character, including supplementary characters. Standard Javascript
3610  * only supports supplementary characters using the UTF-16 encoding, which has 
3611  * values in the range 0x0000-0xFFFF. String.fromCharCode() will only
3612  * give you a string containing 16-bit characters, and will not properly convert 
3613  * the code point for a supplementary character (which has a value > 0xFFFF) into 
3614  * two UTF-16 surrogate characters. Instead, it will just just give you whatever
3615  * single character happens to be the same as your code point modulo 0x10000, which
3616  * is almost never what you want.<p> 
3617  * 
3618  * Similarly, that means if you use String.charCodeAt()
3619  * you will only retrieve a 16-bit value, which may possibly be a single
3620  * surrogate character that is part of a surrogate pair representing a character
3621  * in the supplementary plane. It will not give you a code point. Use 
3622  * IString.codePointAt() to access code points in a string, or use 
3623  * an iterator to walk through the code points in a string. 
3624  * 
3625  * @static
3626  * @param {number} codepoint UCS-4 code point to convert to a character
3627  * @return {string} a string containing the character represented by the codepoint
3628  */
3629 IString.fromCodePoint = function (codepoint) {
3630 	if (codepoint < 0x10000) {
3631 		return String.fromCharCode(codepoint);
3632 	} else {
3633 		var high = Math.floor(codepoint / 0x10000) - 1;
3634 		var low = codepoint & 0xFFFF;
3635 		
3636 		return String.fromCharCode(0xD800 | ((high & 0x000F) << 6) |  ((low & 0xFC00) >> 10)) +
3637 			String.fromCharCode(0xDC00 | (low & 0x3FF));
3638 	}
3639 };
3640 
3641 /**
3642  * Convert the character or the surrogate pair at the given
3643  * index into the intrinsic Javascript string to a Unicode 
3644  * UCS-4 code point.
3645  * 
3646  * @static
3647  * @param {string} str string to get the code point from
3648  * @param {number} index index into the string
3649  * @return {number} code point of the character at the
3650  * given index into the string
3651  */
3652 IString.toCodePoint = function(str, index) {
3653 	if (!str || str.length === 0) {
3654 		return -1;
3655 	}
3656 	var code = -1, high = str.charCodeAt(index);
3657 	if (high >= 0xD800 && high <= 0xDBFF) {
3658 		if (str.length > index+1) {
3659 			var low = str.charCodeAt(index+1);
3660 			if (low >= 0xDC00 && low <= 0xDFFF) {
3661 				code = (((high & 0x3C0) >> 6) + 1) << 16 |
3662 					(((high & 0x3F) << 10) | (low & 0x3FF));
3663 			}
3664 		}
3665 	} else {
3666 		code = high;
3667 	}
3668 	
3669 	return code;
3670 };
3671 
3672 /**
3673  * Load the plural the definitions of plurals for the locale.
3674  * @param {boolean=} sync
3675  * @param {Locale|string=} locale
3676  * @param {Object=} loadParams
3677  * @param {function(*)=} onLoad
3678  */
3679 IString.loadPlurals = function (sync, locale, loadParams, onLoad) {
3680 	var loc;
3681 	if (locale) {
3682 		loc = (typeof(locale) === 'string') ? new Locale(locale) : locale;
3683 	} else {
3684 		loc = new Locale(ilib.getLocale());
3685 	}
3686 	var spec = loc.getLanguage();
3687 	if (!ilib.data["plurals_" + spec]) {
3688 		Utils.loadData({
3689 			name: "plurals.json",
3690 			object: IString,
3691 			locale: loc,
3692 			sync: sync,
3693 			loadParams: loadParams,
3694 			callback: ilib.bind(this, function(plurals) {
3695 				if (!plurals) {
3696 					IString.cache[spec] = {};
3697 				}
3698 				ilib.data["plurals_" + spec] = plurals || {};
3699 				if (onLoad && typeof(onLoad) === 'function') {
3700 					onLoad(ilib.data["plurals_" + spec]);
3701 				}
3702 			})
3703 		});
3704 	} else {
3705 		if (onLoad && typeof(onLoad) === 'function') {
3706 			onLoad(ilib.data["plurals_" + spec]);
3707 		}
3708 	}
3709 };
3710 
3711 /**
3712  * @private
3713  * @static
3714  */
3715 IString._fncs = {
3716 	/**
3717 	 * @private
3718 	 * @param {Object} obj
3719 	 * @return {string|undefined}
3720 	 */
3721 	firstProp: function (obj) {
3722 		for (var p in obj) {
3723 			if (p && obj[p]) {
3724 				return p;
3725 			}
3726 		}
3727 		return undefined; // should never get here
3728 	},
3729 
3730 	/**
3731 	 * @private
3732 	 * @param {Object} obj
3733 	 * @return {string|undefined}
3734 	 */
3735 	firstPropRule: function (obj) {
3736 		if (Object.prototype.toString.call(obj) === '[object Array]') {
3737 			return "inrange";
3738 		} else if (Object.prototype.toString.call(obj) === '[object Object]') {
3739 			for (var p in obj) {
3740 				if (p && obj[p]) {
3741 					return p;
3742 				}
3743 			}
3744 
3745 		}
3746 		return undefined; // should never get here
3747 	},
3748 	
3749 	/**
3750 	 * @private
3751 	 * @param {Object} obj
3752 	 * @param {number|Object} n
3753 	 * @return {?}
3754 	 */
3755 	getValue: function (obj, n) {
3756 		if (typeof(obj) === 'object') {
3757 			var subrule = IString._fncs.firstPropRule(obj);
3758 			if (subrule === "inrange") {
3759 				return IString._fncs[subrule](obj, n);
3760 			}
3761 			return IString._fncs[subrule](obj[subrule], n);
3762 		} else if (typeof(obj) === 'string') {
3763 			if (typeof(n) === 'object'){
3764 				return n[obj];
3765 			}
3766 			return n;
3767 		} else {
3768 			return obj;
3769 		}
3770 	},
3771 	
3772 	/**
3773 	 * @private
3774 	 * @param {number|Object} n
3775 	 * @param {Array.<number|Array.<number>>|Object} range
3776 	 * @return {boolean}
3777 	 */
3778 	matchRangeContinuous: function(n, range) {
3779 		
3780 		for (var num in range) {
3781 			if (typeof(num) !== 'undefined' && typeof(range[num]) !== 'undefined') {
3782 				var obj = range[num];
3783 				if (typeof(obj) === 'number') {
3784 					if (n === range[num]) {
3785 						return true;
3786 					} else if (n >= range[0] && n <= range[1]) {
3787 						return true;
3788 					}
3789 				} else if (Object.prototype.toString.call(obj) === '[object Array]') {
3790 					if (n >= obj[0] && n <= obj[1]) {
3791 						return true;
3792 					}
3793 				}
3794 			}
3795 		}
3796 		return false;
3797 	},
3798 	
3799 	/**
3800 	 * @private
3801 	 * @param {*} number
3802 	 * @return {Object}
3803 	 */
3804 	calculateNumberDigits: function(number) {
3805 		var numberToString = number.toString();
3806 		var parts = [];
3807 		var numberDigits =  {};
3808 		var operandSymbol =  {};
3809 		var integerPart, decimalPartLength, decimalPart;
3810 
3811 		if (numberToString.indexOf('.') !== -1) { //decimal
3812 			parts = numberToString.split('.', 2);
3813 			numberDigits.integerPart = parseInt(parts[0], 10);
3814 			numberDigits.decimalPartLength = parts[1].length;
3815 			numberDigits.decimalPart = parseInt(parts[1], 10);
3816 
3817 			operandSymbol.n = parseFloat(number);
3818 			operandSymbol.i = numberDigits.integerPart;
3819 			operandSymbol.v = numberDigits.decimalPartLength;
3820 			operandSymbol.w = numberDigits.decimalPartLength;
3821 			operandSymbol.f = numberDigits.decimalPart;
3822 			operandSymbol.t = numberDigits.decimalPart;
3823 
3824 		} else {
3825 			numberDigits.integerPart = number;
3826 			numberDigits.decimalPartLength = 0;
3827 			numberDigits.decimalPart = 0;
3828 
3829 			operandSymbol.n = parseInt(number, 10);
3830 			operandSymbol.i = numberDigits.integerPart;
3831 			operandSymbol.v = 0;
3832 			operandSymbol.w = 0;
3833 			operandSymbol.f = 0;
3834 			operandSymbol.t = 0;
3835 
3836 		}
3837 		return operandSymbol
3838 	},
3839 
3840 	/**
3841 	 * @private
3842 	 * @param {number|Object} n
3843 	 * @param {Array.<number|Array.<number>>|Object} range
3844 	 * @return {boolean}
3845 	 */
3846 	matchRange: function(n, range) {
3847 		return IString._fncs.matchRangeContinuous(n, range);
3848 	},
3849 	
3850 	/**
3851 	 * @private
3852 	 * @param {Object} rule
3853 	 * @param {number} n
3854 	 * @return {boolean}
3855 	 */
3856 	is: function(rule, n) {
3857 		var left = IString._fncs.getValue(rule[0], n);
3858 		var right = IString._fncs.getValue(rule[1], n);
3859 		return left == right;
3860 	},
3861 	
3862 	/**
3863 	 * @private
3864 	 * @param {Object} rule
3865 	 * @param {number} n
3866 	 * @return {boolean}
3867 	 */
3868 	isnot: function(rule, n) {
3869 		return IString._fncs.getValue(rule[0], n) != IString._fncs.getValue(rule[1], n);
3870 	},
3871 	
3872 	/**
3873 	 * @private
3874 	 * @param {Object} rule
3875 	 * @param {number|Object} n
3876 	 * @return {boolean}
3877 	 */
3878 	inrange: function(rule, n) {
3879 		if (typeof(rule[0]) === 'number') {
3880 			if(typeof(n) === 'object') {
3881 				return IString._fncs.matchRange(n.n,rule);
3882 			}
3883 			return IString._fncs.matchRange(n,rule);	
3884 		} else if (typeof(rule[0]) === 'undefined') {
3885 			var subrule = IString._fncs.firstPropRule(rule);
3886 			return IString._fncs[subrule](rule[subrule], n);
3887 		} else {
3888 			return IString._fncs.matchRange(IString._fncs.getValue(rule[0], n), rule[1]);	
3889 		}
3890 	},
3891 	/**
3892 	 * @private
3893 	 * @param {Object} rule
3894 	 * @param {number} n
3895 	 * @return {boolean}
3896 	 */
3897 	notin: function(rule, n) {
3898 		return !IString._fncs.matchRange(IString._fncs.getValue(rule[0], n), rule[1]);
3899 	},
3900 	
3901 	/**
3902 	 * @private
3903 	 * @param {Object} rule
3904 	 * @param {number} n
3905 	 * @return {boolean}
3906 	 */
3907 	within: function(rule, n) {
3908 		return IString._fncs.matchRangeContinuous(IString._fncs.getValue(rule[0], n), rule[1]);		
3909 	},
3910 	
3911 	/**
3912 	 * @private
3913 	 * @param {Object} rule
3914 	 * @param {number} n
3915 	 * @return {number}
3916 	 */
3917 	mod: function(rule, n) {
3918 		return MathUtils.mod(IString._fncs.getValue(rule[0], n), IString._fncs.getValue(rule[1], n));
3919 	},
3920 	
3921 	/**
3922 	 * @private
3923 	 * @param {Object} rule
3924 	 * @param {number} n
3925 	 * @return {number}
3926 	 */
3927 	n: function(rule, n) {
3928 		return n;
3929 	},
3930 	
3931 	/**
3932 	 * @private
3933 	 * @param {Object} rule
3934 	 * @param {number|Object} n
3935 	 * @return {boolean}
3936 	 */
3937 	or: function(rule, n) {
3938 		var ruleLength = rule.length;
3939 		var result, i;
3940 		for (i=0; i < ruleLength; i++) {
3941 			result = IString._fncs.getValue(rule[i], n);
3942 			if (result) {
3943 				return true;
3944 			} 
3945 		}
3946 		return false;
3947 	},
3948 	/**
3949 	 * @private
3950 	 * @param {Object} rule
3951 	 * @param {number|Object} n
3952 	 * @return {boolean}
3953 	 */
3954 	and: function(rule, n) {
3955 		var ruleLength = rule.length;
3956 		var result, i;
3957 		for (i=0; i < ruleLength; i++) {
3958 			result= IString._fncs.getValue(rule[i], n);
3959 			if (!result) {
3960 				return false;
3961 			} 
3962 		}
3963 		return true;
3964 	},
3965 	/**
3966 	 * @private
3967 	 * @param {Object} rule
3968 	 * @param {number|Object} n
3969 	 * @return {boolean}
3970 	 */
3971 	eq: function(rule, n) {
3972 		var valueLeft = IString._fncs.getValue(rule[0], n);
3973 		var valueRight;
3974 
3975 		if (typeof(rule[0]) === 'string') {
3976 			if (typeof(n) === 'object'){
3977 				valueRight = n[rule[0]];
3978 				if (typeof(rule[1])=== 'number'){
3979 					valueRight = IString._fncs.getValue(rule[1], n);	
3980 				} else if (typeof(rule[1])=== 'object' && (IString._fncs.firstPropRule(rule[1]) === "inrange" )){
3981 					valueRight = IString._fncs.getValue(rule[1], n);	
3982 				}
3983 			}
3984 		} else {
3985 			if (IString._fncs.firstPropRule(rule[1]) === "inrange") { // mod
3986 				valueRight = IString._fncs.getValue(rule[1], valueLeft);
3987 			} else {
3988 				valueRight = IString._fncs.getValue(rule[1], n);
3989 			}
3990 		} 
3991 		if(typeof(valueRight) === 'boolean') {
3992 			return (valueRight ? true : false);
3993 		} else {
3994 			return (valueLeft == valueRight ? true :false);	
3995 		}
3996 	},
3997 	/**
3998 	 * @private
3999 	 * @param {Object} rule
4000 	 * @param {number|Object} n
4001 	 * @return {boolean}
4002 	 */
4003 	neq: function(rule, n) {
4004 		var valueLeft = IString._fncs.getValue(rule[0], n);
4005 		var valueRight;
4006 
4007 		if (typeof(rule[0]) === 'string') {
4008 			valueRight = n[rule[0]];
4009 			if (typeof(rule[1])=== 'number'){
4010 				valueRight = IString._fncs.getValue(rule[1], n);
4011 			}
4012 		} else {
4013 			if (IString._fncs.firstPropRule(rule[1]) === "inrange") { // mod
4014 				valueRight = IString._fncs.getValue(rule[1], valueLeft);
4015 			} else {
4016 				valueRight = IString._fncs.getValue(rule[1], n);	
4017 			}	
4018 		}
4019 
4020 		if(typeof(valueRight) === 'boolean') {//mod
4021 			return (valueRight? false : true);
4022 		} else {
4023 			return (valueLeft !== valueRight ? true :false);
4024 		}
4025 
4026 	}
4027 };
4028 
4029 IString.prototype = {
4030 	/**
4031 	 * Return the length of this string in characters. This function defers to the regular
4032 	 * Javascript string class in order to perform the length function. Please note that this
4033 	 * method is a real method, whereas the length property of Javascript strings is 
4034 	 * implemented by native code and appears as a property.<p>
4035 	 * 
4036 	 * Example:
4037 	 * 
4038 	 * <pre>
4039 	 * var str = new IString("this is a string");
4040 	 * console.log("String is " + str._length() + " characters long.");
4041 	 * </pre>
4042 	 * @private
4043 	 */
4044 	_length: function () {
4045 		return this.str.length;
4046 	},
4047 	
4048 	/**
4049 	 * Format this string instance as a message, replacing the parameters with 
4050 	 * the given values.<p>
4051 	 * 
4052 	 * The string can contain any text that a regular Javascript string can
4053 	 * contain. Replacement parameters have the syntax:
4054 	 * 
4055 	 * <pre>
4056 	 * {name}
4057 	 * </pre>
4058 	 * 
4059 	 * Where "name" can be any string surrounded by curly brackets. The value of 
4060 	 * "name" is taken from the parameters argument.<p>
4061 	 * 
4062 	 * Example:
4063 	 * 
4064 	 * <pre>
4065 	 * var str = new IString("There are {num} objects.");
4066 	 * console.log(str.format({
4067 	 *   num: 12
4068 	 * });
4069 	 * </pre>
4070 	 * 
4071 	 * Would give the output:
4072 	 * 
4073 	 * <pre>
4074 	 * There are 12 objects.
4075 	 * </pre>
4076 	 * 
4077 	 * If a property is missing from the parameter block, the replacement
4078 	 * parameter substring is left untouched in the string, and a different
4079 	 * set of parameters may be applied a second time. This way, different
4080 	 * parts of the code may format different parts of the message that they
4081 	 * happen to know about.<p>
4082 	 * 
4083 	 * Example:
4084 	 * 
4085 	 * <pre>
4086 	 * var str = new IString("There are {num} objects in the {container}.");
4087 	 * console.log(str.format({
4088 	 *   num: 12
4089 	 * });
4090 	 * </pre>
4091 	 * 
4092 	 * Would give the output:<p>
4093 	 * 
4094 	 * <pre>
4095 	 * There are 12 objects in the {container}.
4096 	 * </pre>
4097 	 * 
4098 	 * The result can then be formatted again with a different parameter block that
4099 	 * specifies a value for the container property.
4100 	 * 
4101 	 * @param params a Javascript object containing values for the replacement 
4102 	 * parameters in the current string
4103 	 * @return a new IString instance with as many replacement parameters filled
4104 	 * out as possible with real values.
4105 	 */
4106 	format: function (params) {
4107 		var formatted = this.str;
4108 		if (params) {
4109 			var regex;
4110 			for (var p in params) {
4111 				if (typeof(params[p]) !== 'undefined') {
4112 					regex = new RegExp("\{"+p+"\}", "g");
4113 					formatted = formatted.replace(regex, params[p]);
4114 				}
4115 			}
4116 		}
4117 		return formatted.toString();
4118 	},
4119 	
4120 	/**
4121 	 * Format a string as one of a choice of strings dependent on the value of
4122 	 * a particular argument index.<p>
4123 	 * 
4124 	 * The syntax of the choice string is as follows. The string contains a
4125 	 * series of choices separated by a vertical bar character "|". Each choice
4126 	 * has a value or range of values to match followed by a hash character "#"
4127 	 * followed by the string to use if the variable matches the criteria.<p>
4128 	 * 
4129 	 * Example string:
4130 	 * 
4131 	 * <pre>
4132 	 * var num = 2;
4133 	 * var str = new IString("0#There are no objects.|1#There is one object.|2#There are {number} objects.");
4134 	 * console.log(str.formatChoice(num, {
4135 	 *   number: num
4136 	 * }));
4137 	 * </pre>
4138 	 * 
4139 	 * Gives the output:
4140 	 * 
4141 	 * <pre>
4142 	 * "There are 2 objects."
4143 	 * </pre>
4144 	 * 
4145 	 * The strings to format may contain replacement variables that will be formatted
4146 	 * using the format() method above and the params argument as a source of values
4147 	 * to use while formatting those variables.<p>
4148 	 * 
4149 	 * If the criterion for a particular choice is empty, that choice will be used
4150 	 * as the default one for use when none of the other choice's criteria match.<p>
4151 	 * 
4152 	 * Example string:
4153 	 * 
4154 	 * <pre>
4155 	 * var num = 22;
4156 	 * var str = new IString("0#There are no objects.|1#There is one object.|#There are {number} objects.");
4157 	 * console.log(str.formatChoice(num, {
4158 	 *   number: num
4159 	 * }));
4160 	 * </pre>
4161 	 * 
4162 	 * Gives the output:
4163 	 * 
4164 	 * <pre>
4165 	 * "There are 22 objects."
4166 	 * </pre>
4167 	 * 
4168 	 * If multiple choice patterns can match a given argument index, the first one 
4169 	 * encountered in the string will be used. If no choice patterns match the 
4170 	 * argument index, then the default choice will be used. If there is no default
4171 	 * choice defined, then this method will return an empty string.<p>
4172 	 * 
4173 	 * <b>Special Syntax</b><p>
4174 	 * 
4175 	 * For any choice format string, all of the patterns in the string should be
4176 	 * of a single type: numeric, boolean, or string/regexp. The type of the 
4177 	 * patterns is determined by the type of the argument index parameter.<p>
4178 	 * 
4179 	 * If the argument index is numeric, then some special syntax can be used 
4180 	 * in the patterns to match numeric ranges.<p>
4181 	 * 
4182 	 * <ul>
4183 	 * <li><i>>x</i> - match any number that is greater than x 
4184 	 * <li><i>>=x</i> - match any number that is greater than or equal to x
4185 	 * <li><i><x</i> - match any number that is less than x
4186 	 * <li><i><=x</i> - match any number that is less than or equal to x
4187 	 * <li><i>start-end</i> - match any number in the range [start,end)
4188 	 * <li><i>zero</i> - match any number in the class "zero". (See below for
4189 	 * a description of number classes.)
4190 	 * <li><i>one</i> - match any number in the class "one"
4191 	 * <li><i>two</i> - match any number in the class "two"
4192 	 * <li><i>few</i> - match any number in the class "few"
4193 	 * <li><i>many</i> - match any number in the class "many"
4194 	 * </ul>
4195 	 * 
4196 	 * A number class defines a set of numbers that receive a particular syntax
4197 	 * in the strings. For example, in Slovenian, integers ending in the digit
4198 	 * "1" are in the "one" class, including 1, 21, 31, ... 101, 111, etc.
4199 	 * Similarly, integers ending in the digit "2" are in the "two" class. 
4200 	 * Integers ending in the digits "3" or "4" are in the "few" class, and
4201 	 * every other integer is handled by the default string.<p>
4202 	 * 
4203 	 * The definition of what numbers are included in a class is locale-dependent.
4204 	 * They are defined in the data file plurals.json. If your string is in a
4205 	 * different locale than the default for ilib, you should call the setLocale()
4206 	 * method of the string instance before calling this method.<p> 
4207 	 * 
4208 	 * <b>Other Pattern Types</b><p>
4209 	 * 
4210 	 * If the argument index is a boolean, the string values "true" and "false" 
4211 	 * may appear as the choice patterns.<p>
4212 	 * 
4213 	 * If the argument index is of type string, then the choice patterns may contain
4214 	 * regular expressions, or static strings as degenerate regexps.
4215 	 * 
4216 	 * @param {*} argIndex The index into the choice array of the current parameter
4217 	 * @param {Object} params The hash of parameter values that replace the replacement 
4218 	 * variables in the string
4219 	 * @throws "syntax error in choice format pattern: " if there is a syntax error
4220 	 * @return {string} the formatted string
4221 	 */
4222 	formatChoice: function(argIndex, params) {
4223 		var choices = this.str.split("|");
4224 		var type = typeof(argIndex);
4225 		var limits = [];
4226 		var strings = [];
4227 		var i;
4228 		var parts;
4229 		var limit;
4230 		var arg;
4231 		var result = undefined;
4232 		var defaultCase = "";
4233 		var	numberDigits = {};
4234 		var operandValue = {};
4235 	
4236 		if (this.str.length === 0) {
4237 			// nothing to do
4238 			return "";
4239 		}
4240 		
4241 		// first parse all the choices
4242 		for (i = 0; i < choices.length; i++) {		
4243 			parts = choices[i].split("#");		
4244 			if (parts.length > 2) {
4245 				limits[i] = parts[0];
4246 				parts = parts.shift();			
4247 				strings[i] = parts.join("#");
4248 			} else if (parts.length === 2) {
4249 				limits[i] = parts[0];
4250 				strings[i] = parts[1];
4251 			} else {
4252 				// syntax error
4253 				throw "syntax error in choice format pattern: " + choices[i];
4254 			}		
4255 		}
4256 		
4257 		// then apply the argument index
4258 		for (i = 0; i < limits.length; i++) {
4259 			if (limits[i].length === 0) {
4260 				// this is default case
4261 				defaultCase = new IString(strings[i]);			
4262 			} else {
4263 				switch (type) {
4264 					case 'number':
4265 						operandValue = IString._fncs.calculateNumberDigits(argIndex);
4266 											
4267 						if (limits[i].substring(0,2) === "<=") {						
4268 							limit = parseFloat(limits[i].substring(2));
4269 							if (operandValue.n <= limit) {
4270 								result = new IString(strings[i]);
4271 								i = limits.length;
4272 							}
4273 						} else if (limits[i].substring(0,2) === ">=") {						
4274 							limit = parseFloat(limits[i].substring(2));
4275 							if (operandValue.n >= limit) {
4276 								result = new IString(strings[i]);
4277 								i = limits.length;
4278 							}
4279 						} else if (limits[i].charAt(0) === "<") {						
4280 							limit = parseFloat(limits[i].substring(1));
4281 							if (operandValue.n < limit) {
4282 								result = new IString(strings[i]);
4283 								i = limits.length;
4284 							}
4285 						} else if (limits[i].charAt(0) === ">") {						
4286 							limit = parseFloat(limits[i].substring(1));
4287 							if (operandValue.n > limit) {
4288 								result = new IString(strings[i]);
4289 								i = limits.length;
4290 							}
4291 						} else {
4292 							this.locale = this.locale || new Locale(this.localeSpec);
4293 							switch (limits[i]) {
4294 								case "zero":
4295 								case "one":
4296 								case "two":
4297 								case "few":
4298 								case "many":
4299 									// CLDR locale-dependent number classes
4300 									var ruleset = ilib.data["plurals_" + this.locale.getLanguage()];
4301 									if (ruleset) {
4302 										var rule = ruleset[limits[i]];
4303 										if (IString._fncs.getValue(rule, operandValue)) {
4304 											result = new IString(strings[i]);
4305 											i = limits.length;
4306 										}
4307 									}
4308 									break;
4309 								default:
4310 									var dash = limits[i].indexOf("-");
4311 									if (dash !== -1) {							
4312 										// range
4313 										var start = limits[i].substring(0, dash);
4314 										var end = limits[i].substring(dash+1);							
4315 										if (operandValue.n >= parseInt(start, 10) && operandValue.n <= parseInt(end, 10)) {								
4316 											result = new IString(strings[i]);
4317 											i = limits.length;
4318 										}
4319 									} else if (operandValue.n === parseInt(limits[i], 10)) {							
4320 										// exact amount
4321 										result = new IString(strings[i]);
4322 										i = limits.length;
4323 									}
4324 									break;
4325 							}
4326 						}
4327 						break;
4328 					case 'boolean':					
4329 						if (limits[i] === "true" && argIndex === true) {						
4330 							result = new IString(strings[i]);
4331 							i = limits.length;
4332 						} else if (limits[i] === "false" && argIndex === false) {						
4333 							result = new IString(strings[i]);
4334 							i = limits.length;
4335 						}
4336 						break;
4337 					case 'string':					
4338 						var regexp = new RegExp(limits[i], "i");
4339 						if (regexp.test(argIndex)) {
4340 							result = new IString(strings[i]);
4341 							i = limits.length;
4342 						}
4343 						break;
4344 					case 'object':
4345 						throw "syntax error: fmtChoice parameter for the argument index cannot be an object";
4346 				}
4347 			}
4348 		}
4349 		
4350 		if (!result) {		
4351 			result = defaultCase || new IString("");
4352 		}
4353 		
4354 		result = result.format(params);
4355 		
4356 		return result.toString();
4357 	},
4358 	
4359 	// delegates
4360 	/**
4361 	 * Same as String.toString()
4362 	 * @return {string} this instance as regular Javascript string
4363 	 */
4364 	toString: function () {
4365 		return this.str.toString();
4366 	},
4367 	
4368 	/**
4369 	 * Same as String.valueOf()
4370 	 * @return {string} this instance as a regular Javascript string
4371 	 */
4372 	valueOf: function () {
4373 		return this.str.valueOf();
4374 	},
4375 	
4376 	/**
4377 	 * Same as String.charAt()
4378 	 * @param {number} index the index of the character being sought
4379 	 * @return {IString} the character at the given index
4380 	 */
4381 	charAt: function(index) {
4382 		return new IString(this.str.charAt(index));
4383 	},
4384 	
4385 	/**
4386 	 * Same as String.charCodeAt(). This only reports on 
4387 	 * 2-byte UCS-2 Unicode values, and does not take into
4388 	 * account supplementary characters encoded in UTF-16.
4389 	 * If you would like to take account of those characters,
4390 	 * use codePointAt() instead.
4391 	 * @param {number} index the index of the character being sought
4392 	 * @return {number} the character code of the character at the 
4393 	 * given index in the string 
4394 	 */
4395 	charCodeAt: function(index) {
4396 		return this.str.charCodeAt(index);
4397 	},
4398 	
4399 	/**
4400 	 * Same as String.concat()
4401 	 * @param {string} strings strings to concatenate to the current one
4402 	 * @return {IString} a concatenation of the given strings
4403 	 */
4404 	concat: function(strings) {
4405 		return new IString(this.str.concat(strings));
4406 	},
4407 	
4408 	/**
4409 	 * Same as String.indexOf()
4410 	 * @param {string} searchValue string to search for
4411 	 * @param {number} start index into the string to start searching, or
4412 	 * undefined to search the entire string
4413 	 * @return {number} index into the string of the string being sought,
4414 	 * or -1 if the string is not found 
4415 	 */
4416 	indexOf: function(searchValue, start) {
4417 		return this.str.indexOf(searchValue, start);
4418 	},
4419 	
4420 	/**
4421 	 * Same as String.lastIndexOf()
4422 	 * @param {string} searchValue string to search for
4423 	 * @param {number} start index into the string to start searching, or
4424 	 * undefined to search the entire string
4425 	 * @return {number} index into the string of the string being sought,
4426 	 * or -1 if the string is not found 
4427 	 */
4428 	lastIndexOf: function(searchValue, start) {
4429 		return this.str.lastIndexOf(searchValue, start);
4430 	},
4431 	
4432 	/**
4433 	 * Same as String.match()
4434 	 * @param {string} regexp the regular expression to match
4435 	 * @return {Array.<string>} an array of matches
4436 	 */
4437 	match: function(regexp) {
4438 		return this.str.match(regexp);
4439 	},
4440 	
4441 	/**
4442 	 * Same as String.replace()
4443 	 * @param {string} searchValue a regular expression to search for
4444 	 * @param {string} newValue the string to replace the matches with
4445 	 * @return {IString} a new string with all the matches replaced
4446 	 * with the new value
4447 	 */
4448 	replace: function(searchValue, newValue) {
4449 		return new IString(this.str.replace(searchValue, newValue));
4450 	},
4451 	
4452 	/**
4453 	 * Same as String.search()
4454 	 * @param {string} regexp the regular expression to search for
4455 	 * @return {number} position of the match, or -1 for no match
4456 	 */
4457 	search: function(regexp) {
4458 		return this.str.search(regexp);
4459 	},
4460 	
4461 	/**
4462 	 * Same as String.slice()
4463 	 * @param {number} start first character to include in the string
4464 	 * @param {number} end include all characters up to, but not including
4465 	 * the end character
4466 	 * @return {IString} a slice of the current string
4467 	 */
4468 	slice: function(start, end) {
4469 		return new IString(this.str.slice(start, end));
4470 	},
4471 	
4472 	/**
4473 	 * Same as String.split()
4474 	 * @param {string} separator regular expression to match to find
4475 	 * separations between the parts of the text
4476 	 * @param {number} limit maximum number of items in the final 
4477 	 * output array. Any items beyond that limit will be ignored.
4478 	 * @return {Array.<string>} the parts of the current string split 
4479 	 * by the separator
4480 	 */
4481 	split: function(separator, limit) {
4482 		return this.str.split(separator, limit);
4483 	},
4484 	
4485 	/**
4486 	 * Same as String.substr()
4487 	 * @param {number} start the index of the character that should 
4488 	 * begin the returned substring
4489 	 * @param {number} length the number of characters to return after
4490 	 * the start character.
4491 	 * @return {IString} the requested substring 
4492 	 */
4493 	substr: function(start, length) {
4494 		var plat = ilib._getPlatform();
4495 		if (plat === "qt" || plat === "rhino" || plat === "trireme") {
4496 			// qt and rhino have a broken implementation of substr(), so
4497 			// work around it
4498 			if (typeof(length) === "undefined") {
4499 				length = this.str.length - start;
4500 			}
4501 		}
4502 		return new IString(this.str.substr(start, length));
4503 	},
4504 	
4505 	/**
4506 	 * Same as String.substring()
4507 	 * @param {number} from the index of the character that should 
4508 	 * begin the returned substring
4509 	 * @param {number} to the index where to stop the extraction. If
4510 	 * omitted, extracts the rest of the string
4511 	 * @return {IString} the requested substring 
4512 	 */
4513 	substring: function(from, to) {
4514 		return this.str.substring(from, to);
4515 	},
4516 	
4517 	/**
4518 	 * Same as String.toLowerCase(). Note that this method is
4519 	 * not locale-sensitive. 
4520 	 * @return {IString} a string with the first character
4521 	 * lower-cased
4522 	 */
4523 	toLowerCase: function() {
4524 		return this.str.toLowerCase();
4525 	},
4526 	
4527 	/**
4528 	 * Same as String.toUpperCase(). Note that this method is
4529 	 * not locale-sensitive. Use toLocaleUpperCase() instead
4530 	 * to get locale-sensitive behaviour. 
4531 	 * @return {IString} a string with the first character
4532 	 * upper-cased
4533 	 */
4534 	toUpperCase: function() {
4535 		return this.str.toUpperCase();
4536 	},
4537 	
4538 	/**
4539 	 * Convert the character or the surrogate pair at the given
4540 	 * index into the string to a Unicode UCS-4 code point.
4541 	 * @protected
4542 	 * @param {number} index index into the string
4543 	 * @return {number} code point of the character at the
4544 	 * given index into the string
4545 	 */
4546 	_toCodePoint: function (index) {
4547 		return IString.toCodePoint(this.str, index);
4548 	},
4549 	
4550 	/**
4551 	 * Call the callback with each character in the string one at 
4552 	 * a time, taking care to step through the surrogate pairs in 
4553 	 * the UTF-16 encoding properly.<p>
4554 	 * 
4555 	 * The standard Javascript String's charAt() method only
4556 	 * returns a particular 16-bit character in the 
4557 	 * UTF-16 encoding scheme.
4558 	 * If the index to charAt() is pointing to a low- or 
4559 	 * high-surrogate character,
4560 	 * it will return the surrogate character rather 
4561 	 * than the the character 
4562 	 * in the supplementary planes that the two surrogates together 
4563 	 * encode. This function will call the callback with the full
4564 	 * character, making sure to join two  
4565 	 * surrogates into one character in the supplementary planes
4566 	 * where necessary.<p>
4567 	 * 
4568 	 * @param {function(string)} callback a callback function to call with each
4569 	 * full character in the current string
4570 	 */
4571 	forEach: function(callback) {
4572 		if (typeof(callback) === 'function') {
4573 			var it = this.charIterator();
4574 			while (it.hasNext()) {
4575 				callback(it.next());
4576 			}
4577 		}
4578 	},
4579 
4580 	/**
4581 	 * Call the callback with each numeric code point in the string one at 
4582 	 * a time, taking care to step through the surrogate pairs in 
4583 	 * the UTF-16 encoding properly.<p>
4584 	 * 
4585 	 * The standard Javascript String's charCodeAt() method only
4586 	 * returns information about a particular 16-bit character in the 
4587 	 * UTF-16 encoding scheme.
4588 	 * If the index to charCodeAt() is pointing to a low- or 
4589 	 * high-surrogate character,
4590 	 * it will return the code point of the surrogate character rather 
4591 	 * than the code point of the character 
4592 	 * in the supplementary planes that the two surrogates together 
4593 	 * encode. This function will call the callback with the full
4594 	 * code point of each character, making sure to join two  
4595 	 * surrogates into one code point in the supplementary planes.<p>
4596 	 * 
4597 	 * @param {function(string)} callback a callback function to call with each
4598 	 * code point in the current string
4599 	 */
4600 	forEachCodePoint: function(callback) {
4601 		if (typeof(callback) === 'function') {
4602 			var it = this.iterator();
4603 			while (it.hasNext()) {
4604 				callback(it.next());
4605 			}
4606 		}
4607 	},
4608 
4609 	/**
4610 	 * Return an iterator that will step through all of the characters
4611 	 * in the string one at a time and return their code points, taking 
4612 	 * care to step through the surrogate pairs in UTF-16 encoding 
4613 	 * properly.<p>
4614 	 * 
4615 	 * The standard Javascript String's charCodeAt() method only
4616 	 * returns information about a particular 16-bit character in the 
4617 	 * UTF-16 encoding scheme.
4618 	 * If the index is pointing to a low- or high-surrogate character,
4619 	 * it will return a code point of the surrogate character rather 
4620 	 * than the code point of the character 
4621 	 * in the supplementary planes that the two surrogates together 
4622 	 * encode.<p>
4623 	 * 
4624 	 * The iterator instance returned has two methods, hasNext() which
4625 	 * returns true if the iterator has more code points to iterate through,
4626 	 * and next() which returns the next code point as a number.<p>
4627 	 * 
4628 	 * @return {Object} an iterator 
4629 	 * that iterates through all the code points in the string
4630 	 */
4631 	iterator: function() {
4632 		/**
4633 		 * @constructor
4634 		 */
4635 		function _iterator (istring) {
4636 			this.index = 0;
4637 			this.hasNext = function () {
4638 				return (this.index < istring.str.length);
4639 			};
4640 			this.next = function () {
4641 				if (this.index < istring.str.length) {
4642 					var num = istring._toCodePoint(this.index);
4643 					this.index += ((num > 0xFFFF) ? 2 : 1);
4644 				} else {
4645 					num = -1;
4646 				}
4647 				return num;
4648 			};
4649 		};
4650 		return new _iterator(this);
4651 	},
4652 
4653 	/**
4654 	 * Return an iterator that will step through all of the characters
4655 	 * in the string one at a time, taking 
4656 	 * care to step through the surrogate pairs in UTF-16 encoding 
4657 	 * properly.<p>
4658 	 * 
4659 	 * The standard Javascript String's charAt() method only
4660 	 * returns information about a particular 16-bit character in the 
4661 	 * UTF-16 encoding scheme.
4662 	 * If the index is pointing to a low- or high-surrogate character,
4663 	 * it will return that surrogate character rather 
4664 	 * than the surrogate pair which represents a character 
4665 	 * in the supplementary planes.<p>
4666 	 * 
4667 	 * The iterator instance returned has two methods, hasNext() which
4668 	 * returns true if the iterator has more characters to iterate through,
4669 	 * and next() which returns the next character.<p>
4670 	 * 
4671 	 * @return {Object} an iterator 
4672 	 * that iterates through all the characters in the string
4673 	 */
4674 	charIterator: function() {
4675 		/**
4676 		 * @constructor
4677 		 */
4678 		function _chiterator (istring) {
4679 			this.index = 0;
4680 			this.hasNext = function () {
4681 				return (this.index < istring.str.length);
4682 			};
4683 			this.next = function () {
4684 				var ch;
4685 				if (this.index < istring.str.length) {
4686 					ch = istring.str.charAt(this.index);
4687 					if (IString._isSurrogate(ch) && 
4688 							this.index+1 < istring.str.length && 
4689 							IString._isSurrogate(istring.str.charAt(this.index+1))) {
4690 						this.index++;
4691 						ch += istring.str.charAt(this.index);
4692 					}
4693 					this.index++;
4694 				}
4695 				return ch;
4696 			};
4697 		};
4698 		return new _chiterator(this);
4699 	},
4700 	
4701 	/**
4702 	 * Return the code point at the given index when the string is viewed 
4703 	 * as an array of code points. If the index is beyond the end of the
4704 	 * array of code points or if the index is negative, -1 is returned.
4705 	 * @param {number} index index of the code point 
4706 	 * @return {number} code point of the character at the given index into
4707 	 * the string
4708 	 */
4709 	codePointAt: function (index) {
4710 		if (index < 0) {
4711 			return -1;
4712 		}
4713 		var count,
4714 			it = this.iterator(),
4715 			ch;
4716 		for (count = index; count >= 0 && it.hasNext(); count--) {
4717 			ch = it.next();
4718 		}
4719 		return (count < 0) ? ch : -1;
4720 	},
4721 	
4722 	/**
4723 	 * Set the locale to use when processing choice formats. The locale
4724 	 * affects how number classes are interpretted. In some cultures,
4725 	 * the limit "few" maps to "any integer that ends in the digits 2 to 9" and
4726 	 * in yet others, "few" maps to "any integer that ends in the digits
4727 	 * 3 or 4".
4728 	 * @param {Locale|string} locale locale to use when processing choice
4729 	 * formats with this string
4730 	 * @param {boolean=} sync [optional] whether to load the locale data synchronously 
4731 	 * or not
4732 	 * @param {Object=} loadParams [optional] parameters to pass to the loader function
4733 	 * @param {function(*)=} onLoad [optional] function to call when the loading is done
4734 	 */
4735 	setLocale: function (locale, sync, loadParams, onLoad) {
4736 		if (typeof(locale) === 'object') {
4737 			this.locale = locale;
4738 		} else {
4739 			this.localeSpec = locale;
4740 			this.locale = new Locale(locale);
4741 		}
4742 		
4743 		IString.loadPlurals(typeof(sync) !== 'undefined' ? sync : true, this.locale, loadParams, onLoad);
4744 	},
4745 
4746 	/**
4747 	 * Return the locale to use when processing choice formats. The locale
4748 	 * affects how number classes are interpretted. In some cultures,
4749 	 * the limit "few" maps to "any integer that ends in the digits 2 to 9" and
4750 	 * in yet others, "few" maps to "any integer that ends in the digits
4751 	 * 3 or 4".
4752 	 * @return {string} localespec to use when processing choice
4753 	 * formats with this string
4754 	 */
4755 	getLocale: function () {
4756 		return (this.locale ? this.locale.getSpec() : this.localeSpec) || ilib.getLocale();
4757 	},
4758 
4759 	/**
4760 	 * Return the number of code points in this string. This may be different
4761 	 * than the number of characters, as the UTF-16 encoding that Javascript
4762 	 * uses for its basis returns surrogate pairs separately. Two 2-byte 
4763 	 * surrogate characters together make up one character/code point in 
4764 	 * the supplementary character planes. If your string contains no
4765 	 * characters in the supplementary planes, this method will return the
4766 	 * same thing as the length() method.
4767 	 * @return {number} the number of code points in this string
4768 	 */
4769 	codePointLength: function () {
4770 		if (this.cpLength === -1) {
4771 			var it = this.iterator();
4772 			this.cpLength = 0;
4773 			while (it.hasNext()) { 
4774 				this.cpLength++;
4775 				it.next();
4776 			};
4777 		}
4778 		return this.cpLength;	
4779 	}
4780 };
4781 
4782 
4783 /*< Calendar.js */
4784 /*
4785  * Calendar.js - Represent a calendar object.
4786  * 
4787  * Copyright © 2012-2015, JEDLSoft
4788  *
4789  * Licensed under the Apache License, Version 2.0 (the "License");
4790  * you may not use this file except in compliance with the License.
4791  * You may obtain a copy of the License at
4792  *
4793  *     http://www.apache.org/licenses/LICENSE-2.0
4794  *
4795  * Unless required by applicable law or agreed to in writing, software
4796  * distributed under the License is distributed on an "AS IS" BASIS,
4797  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4798  *
4799  * See the License for the specific language governing permissions and
4800  * limitations under the License.
4801  */
4802 
4803 /**
4804  * @class
4805  * Superclass for all calendar subclasses that contains shared 
4806  * functionality. This class is never instantiated on its own. Instead,
4807  * you should use the {@link CalendarFactory} function to manufacture a new
4808  * instance of a subclass of Calendar. 
4809  * 
4810  * @private
4811  * @constructor
4812  */
4813 var Calendar = function() {
4814 };
4815 
4816 /* place for the subclasses to put their constructors so that the factory method
4817  * can find them. Do this to add your calendar after it's defined: 
4818  * Calendar._constructors["mytype"] = Calendar.MyTypeConstructor;
4819  */
4820 Calendar._constructors = {};
4821 
4822 Calendar.prototype = {
4823 	/**
4824 	 * Return the type of this calendar.
4825 	 * 
4826 	 * @return {string} the name of the type of this calendar 
4827 	 */
4828 	getType: function() {
4829 		throw "Cannot call methods of abstract class Calendar";
4830 	},
4831 	
4832 	/**
4833 	 * Return the number of months in the given year. The number of months in a year varies
4834 	 * for some luni-solar calendars because in some years, an extra month is needed to extend the 
4835 	 * days in a year to an entire solar year. The month is represented as a 1-based number
4836 	 * where 1=first month, 2=second month, etc.
4837 	 * 
4838 	 * @param {number} year a year for which the number of months is sought
4839 	 * @return {number} The number of months in the given year
4840 	 */
4841 	getNumMonths: function(year) {
4842 		throw "Cannot call methods of abstract class Calendar";
4843 	},
4844 	
4845 	/**
4846 	 * Return the number of days in a particular month in a particular year. This function
4847 	 * can return a different number for a month depending on the year because of things
4848 	 * like leap years.
4849 	 * 
4850 	 * @param {number} month the month for which the length is sought
4851 	 * @param {number} year the year within which that month can be found
4852 	 * @return {number} the number of days within the given month in the given year
4853 	 */
4854 	getMonLength: function(month, year) {
4855 		throw "Cannot call methods of abstract class Calendar";
4856 	},
4857 	
4858 	/**
4859 	 * Return true if the given year is a leap year in this calendar.
4860 	 * The year parameter may be given as a number.
4861 	 * 
4862 	 * @param {number} year the year for which the leap year information is being sought
4863 	 * @return {boolean} true if the given year is a leap year
4864 	 */
4865 	isLeapYear: function(year) {
4866 		throw "Cannot call methods of abstract class Calendar";
4867 	}
4868 };
4869 
4870 
4871 /*< CalendarFactory.js */
4872 /*
4873  * CalendarFactory.js - Constructs new instances of the right subclass of Calendar
4874  * 
4875  * Copyright © 2015, JEDLSoft
4876  *
4877  * Licensed under the Apache License, Version 2.0 (the "License");
4878  * you may not use this file except in compliance with the License.
4879  * You may obtain a copy of the License at
4880  *
4881  *     http://www.apache.org/licenses/LICENSE-2.0
4882  *
4883  * Unless required by applicable law or agreed to in writing, software
4884  * distributed under the License is distributed on an "AS IS" BASIS,
4885  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4886  *
4887  * See the License for the specific language governing permissions and
4888  * limitations under the License.
4889  */
4890 
4891 /* !depends
4892 ilib.js
4893 Locale.js
4894 LocaleInfo.js
4895 Calendar.js
4896 */
4897 
4898 
4899 /**
4900  * Factory method to create a new instance of a calendar subclass.<p>
4901  * 
4902  * The options parameter can be an object that contains the following
4903  * properties:
4904  * 
4905  * <ul>
4906  * <li><i>type</i> - specify the type of the calendar desired. The
4907  * list of valid values changes depending on which calendars are 
4908  * defined. When assembling your iliball.js, include those calendars 
4909  * you wish to use in your program or web page, and they will register 
4910  * themselves with this factory method. The "official", "gregorian",
4911  * and "julian" calendars are all included by default, as they are the
4912  * standard calendars for much of the world.
4913  * <li><i>locale</i> - some calendars vary depending on the locale.
4914  * For example, the "official" calendar transitions from a Julian-style
4915  * calendar to a Gregorian-style calendar on a different date for
4916  * each country, as the governments of those countries decided to
4917  * adopt the Gregorian calendar at different times.
4918  *  
4919  * <li><i>onLoad</i> - a callback function to call when the calendar object is fully 
4920  * loaded. When the onLoad option is given, the calendar factory will attempt to
4921  * load any missing locale data using the ilib loader callback.
4922  * When the constructor is done (even if the data is already preassembled), the 
4923  * onLoad function is called with the current instance as a parameter, so this
4924  * callback can be used with preassembled or dynamic loading or a mix of the two.
4925  * 
4926  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
4927  * asynchronously. If this option is given as "false", then the "onLoad"
4928  * callback must be given, as the instance returned from this constructor will
4929  * not be usable for a while.
4930  *  
4931  * <li><i>loadParams</i> - an object containing parameters to pass to the 
4932  * loader callback function when locale data is missing. The parameters are not
4933  * interpretted or modified in any way. They are simply passed along. The object 
4934  * may contain any property/value pairs as long as the calling code is in
4935  * agreement with the loader callback function as to what those parameters mean.
4936  * </ul>
4937  * 
4938  * If a locale is specified, but no type, then the calendar that is default for
4939  * the locale will be instantiated and returned. If neither the type nor
4940  * the locale are specified, then the calendar for the default locale will
4941  * be used. 
4942  * 
4943  * @static
4944  * @param {Object=} options options controlling the construction of this instance, or
4945  * undefined to use the default options
4946  * @return {Calendar} an instance of a calendar object of the appropriate type
4947  */
4948 var CalendarFactory = function (options) {
4949 	var locale,
4950 		type,
4951 		sync = true,
4952 		instance;
4953 
4954 	if (options) {
4955 		if (options.locale) {
4956 			locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
4957 		}
4958 		
4959 		type = options.type || options.calendar;
4960 		
4961 		if (typeof(options.sync) === 'boolean') {
4962 			sync = options.sync;
4963 		}
4964 	}
4965 	
4966 	if (!locale) {
4967 		locale = new Locale();	// default locale
4968 	}
4969 	
4970 	if (!type) {
4971 		new LocaleInfo(locale, {
4972 			sync: sync,
4973 			loadParams: options && options.loadParams,
4974 			onLoad: ilib.bind(this, function(info) {
4975 				type = info.getCalendar();
4976 				
4977 				instance = CalendarFactory._init(type, options);
4978 				
4979 				if (options && typeof(options.onLoad) === 'function') {
4980 					options.onLoad(instance);
4981 				}
4982 			})
4983 		});
4984 	} else {
4985 		instance = CalendarFactory._init(type, options);
4986 	}
4987 	
4988 	return instance;
4989 };
4990 
4991 /**
4992  * Map calendar names to classes to initialize in the dynamic code model.
4993  * TODO: Need to figure out some way that this doesn't have to be updated by hand.
4994  * @private
4995  */
4996 CalendarFactory._dynMap = {
4997 	"coptic":       "Coptic",
4998 	"ethiopic":     "Ethiopic",
4999 	"gregorian":    "Gregorian",
5000 	"han":          "Han",
5001 	"hebrew":       "Hebrew",
5002 	"islamic":      "Islamic",
5003 	"julian":       "Julian",
5004 	"persian":      "Persian",
5005 	"persian-algo": "PersianAlgo",
5006 	"thaisolar":    "ThaiSolar"
5007 };
5008 
5009 /**
5010  * Dynamically load the code for a calendar and calendar class if necessary.
5011  * @protected
5012  */
5013 CalendarFactory._dynLoadCalendar = function (name) {
5014 	if (!Calendar._constructors[name]) {
5015 		var entry = CalendarFactory._dynMap[name];
5016 		if (entry) {
5017 			Calendar._constructors[name] = require("./" + entry + "Cal.js");
5018 		}
5019 	}
5020 	return Calendar._constructors[name];
5021 };
5022 
5023 /** @private */
5024 CalendarFactory._init = function(type, options) {
5025 	var cons;
5026 	
5027 	if (ilib.isDynCode()) {
5028 		CalendarFactory._dynLoadCalendar(type);
5029 	}
5030 	
5031 	cons = Calendar._constructors[type];
5032 	
5033 	// pass the same options through to the constructor so the subclass
5034 	// has the ability to do something with if it needs to
5035 	return cons && new cons(options);
5036 };
5037 
5038 /**
5039  * Return an array of known calendar types that the factory method can instantiate.
5040  * 
5041  * @return {Array.<string>} an array of calendar types
5042  */
5043 CalendarFactory.getCalendars = function () {
5044 	var arr = [],
5045 		c;
5046 	
5047 	if (ilib.isDynCode()) {
5048 		for (c in CalendarFactory._dynMap) {
5049 			CalendarFactory._dynLoadCalendar(c);
5050 		}
5051 	}
5052 	
5053 	for (c in Calendar._constructors) {
5054 		if (c && Calendar._constructors[c]) {
5055 			arr.push(c); // code like a pirate
5056 		}
5057 	}
5058 	
5059 	return arr;
5060 };
5061 
5062 
5063 /*< GregorianCal.js */
5064 /*
5065  * gregorian.js - Represent a Gregorian calendar object.
5066  * 
5067  * Copyright © 2012-2015, JEDLSoft
5068  *
5069  * Licensed under the Apache License, Version 2.0 (the "License");
5070  * you may not use this file except in compliance with the License.
5071  * You may obtain a copy of the License at
5072  *
5073  *     http://www.apache.org/licenses/LICENSE-2.0
5074  *
5075  * Unless required by applicable law or agreed to in writing, software
5076  * distributed under the License is distributed on an "AS IS" BASIS,
5077  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5078  *
5079  * See the License for the specific language governing permissions and
5080  * limitations under the License.
5081  */
5082 
5083 
5084 /* !depends ilib.js Calendar.js Utils.js MathUtils.js */
5085 
5086 
5087 /**
5088  * @class
5089  * Construct a new Gregorian calendar object. This class encodes information about
5090  * a Gregorian calendar.<p>
5091  * 
5092  * 
5093  * @constructor
5094  * @param {{noinstance:boolean}=} options
5095  * @extends Calendar
5096  */
5097 var GregorianCal = function(options) {
5098 	if (!options || !options.noinstance) {
5099 		this.type = "gregorian";
5100 	}
5101 };
5102 
5103 /**
5104  * the lengths of each month 
5105  * @private
5106  * @const
5107  * @type Array.<number> 
5108  */
5109 GregorianCal.monthLengths = [
5110 	31,  /* Jan */
5111 	28,  /* Feb */
5112 	31,  /* Mar */
5113 	30,  /* Apr */
5114 	31,  /* May */
5115 	30,  /* Jun */
5116 	31,  /* Jul */
5117 	31,  /* Aug */
5118 	30,  /* Sep */
5119 	31,  /* Oct */
5120 	30,  /* Nov */
5121 	31   /* Dec */
5122 ];
5123 
5124 /**
5125  * Return the number of months in the given year. The number of months in a year varies
5126  * for some luni-solar calendars because in some years, an extra month is needed to extend the 
5127  * days in a year to an entire solar year. The month is represented as a 1-based number
5128  * where 1=first month, 2=second month, etc.
5129  * 
5130  * @param {number} year a year for which the number of months is sought
5131  * @return {number} The number of months in the given year
5132  */
5133 GregorianCal.prototype.getNumMonths = function(year) {
5134 	return 12;
5135 };
5136 
5137 /**
5138  * Return the number of days in a particular month in a particular year. This function
5139  * can return a different number for a month depending on the year because of things
5140  * like leap years.
5141  * 
5142  * @param {number} month the month for which the length is sought
5143  * @param {number} year the year within which that month can be found
5144  * @return {number} the number of days within the given month in the given year
5145  */
5146 GregorianCal.prototype.getMonLength = function(month, year) {
5147 	if (month !== 2 || !this.isLeapYear(year)) {
5148 		return GregorianCal.monthLengths[month-1];
5149 	} else {
5150 		return 29;
5151 	}
5152 };
5153 
5154 /**
5155  * Return true if the given year is a leap year in the Gregorian calendar.
5156  * The year parameter may be given as a number, or as a GregDate object.
5157  * @param {number|GregorianDate} year the year for which the leap year information is being sought
5158  * @return {boolean} true if the given year is a leap year
5159  */
5160 GregorianCal.prototype.isLeapYear = function(year) {
5161 	var y = (typeof(year) === 'number' ? year : year.getYears());
5162 	var centuries = MathUtils.mod(y, 400);
5163 	return (MathUtils.mod(y, 4) === 0 && centuries !== 100 && centuries !== 200 && centuries !== 300);
5164 };
5165 
5166 /**
5167  * Return the type of this calendar.
5168  * 
5169  * @return {string} the name of the type of this calendar 
5170  */
5171 GregorianCal.prototype.getType = function() {
5172 	return this.type;
5173 };
5174 
5175 /**
5176  * Return a date instance for this calendar type using the given
5177  * options.
5178  * @param {Object} options options controlling the construction of 
5179  * the date instance
5180  * @return {IDate} a date appropriate for this calendar type
5181  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
5182  */
5183 GregorianCal.prototype.newDateInstance = function (options) {
5184 		return new GregorianDate(options);
5185 };
5186 
5187 /* register this calendar for the factory method */
5188 Calendar._constructors["gregorian"] = GregorianCal;
5189 
5190 
5191 /*< JulianDay.js */
5192 /*
5193  * JulianDay.js - A Julian Day object.
5194  * 
5195  * Copyright © 2012-2015, JEDLSoft
5196  *
5197  * Licensed under the Apache License, Version 2.0 (the "License");
5198  * you may not use this file except in compliance with the License.
5199  * You may obtain a copy of the License at
5200  *
5201  *     http://www.apache.org/licenses/LICENSE-2.0
5202  *
5203  * Unless required by applicable law or agreed to in writing, software
5204  * distributed under the License is distributed on an "AS IS" BASIS,
5205  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5206  *
5207  * See the License for the specific language governing permissions and
5208  * limitations under the License.
5209  */
5210 
5211 /**
5212  * @class
5213  * A Julian Day class. A Julian Day is a date based on the Julian Day count
5214  * of time invented by Joseph Scaliger in 1583 for use with astronomical calculations. 
5215  * Do not confuse it with a date in the Julian calendar, which it has very
5216  * little in common with. The naming is unfortunately close, and comes from history.<p>
5217  * 
5218  * 
5219  * @constructor
5220  * @param {number} num the Julian Day expressed as a floating point number 
5221  */
5222 var JulianDay = function(num) {
5223 	this.jd = num;
5224 	this.days = Math.floor(this.jd);
5225 	this.frac = num - this.days;
5226 };
5227 
5228 JulianDay.prototype = {
5229 	/**
5230 	 * Return the integral portion of this Julian Day instance. This corresponds to
5231 	 * the number of days since the beginning of the epoch.
5232 	 * 
5233 	 * @return {number} the integral portion of this Julian Day
5234 	 */
5235 	getDays: function() {
5236 		return this.days;
5237 	},
5238 	
5239 	/**
5240 	 * Set the date of this Julian Day instance.
5241 	 * 
5242 	 * @param {number} days the julian date expressed as a floating point number
5243 	 */
5244 	setDays: function(days) {
5245 		this.days = Math.floor(days);
5246 		this.jd = this.days + this.frac;
5247 	},
5248 	
5249 	/**
5250 	 * Return the fractional portion of this Julian Day instance. This portion 
5251 	 * corresponds to the time of day for the instance.
5252 	 */
5253 	getDayFraction: function() {
5254 		return this.frac;
5255 	},
5256 	
5257 	/**
5258 	 * Set the fractional part of the Julian Day. The fractional part represents
5259 	 * the portion of a fully day. Julian dates start at noon, and proceed until
5260 	 * noon of the next day. That would mean midnight is represented as a fractional
5261 	 * part of 0.5.
5262 	 * 
5263 	 * @param {number} fraction The fractional part of the Julian date
5264 	 */
5265 	setDayFraction: function(fraction) {
5266 		var t = Math.floor(fraction);
5267 		this.frac = fraction - t;
5268 		this.jd = this.days + this.frac;
5269 	},
5270 	
5271 	/** 
5272 	 * Return the Julian Day expressed as a floating point number.
5273 	 * @return {number} the Julian Day as a number
5274 	 */
5275 	getDate: function () {
5276 		return this.jd;
5277 	},
5278 	
5279 	/**
5280 	 * Set the date of this Julian Day instance.
5281 	 * 
5282 	 * @param {number} num the numeric Julian Day to set into this instance
5283 	 */
5284 	setDate: function (num) {
5285 		this.jd = num;
5286 	},
5287 	
5288 	/**
5289 	 * Add an offset to the current date instance. The offset should be expressed in
5290 	 * terms of Julian days. That is, each integral unit represents one day of time, and
5291 	 * fractional part represents a fraction of a regular 24-hour day.
5292 	 * 
5293 	 * @param {number} offset an amount to add (or subtract) to the current result instance.
5294 	 */
5295 	addDate: function(offset) {
5296 		if (typeof(offset) === 'number') {
5297 			this.jd += offset;
5298 			this.days = Math.floor(this.jd);
5299 			this.frac = this.jd - this.days;
5300 		}
5301 	}
5302 };
5303 
5304 
5305 
5306 /*< RataDie.js */
5307 /*
5308  * ratadie.js - Represent the RD date number in the calendar
5309  * 
5310  * Copyright © 2014-2015, JEDLSoft
5311  *
5312  * Licensed under the Apache License, Version 2.0 (the "License");
5313  * you may not use this file except in compliance with the License.
5314  * You may obtain a copy of the License at
5315  *
5316  *     http://www.apache.org/licenses/LICENSE-2.0
5317  *
5318  * Unless required by applicable law or agreed to in writing, software
5319  * distributed under the License is distributed on an "AS IS" BASIS,
5320  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5321  *
5322  * See the License for the specific language governing permissions and
5323  * limitations under the License.
5324  */
5325 
5326 /* !depends 
5327 ilib.js
5328 JulianDay.js
5329 MathUtils.js
5330 JSUtils.js
5331 */
5332 
5333 
5334 /**
5335  * @class
5336  * Construct a new RD date number object. The constructor parameters can 
5337  * contain any of the following properties:
5338  * 
5339  * <ul>
5340  * <li><i>unixtime<i> - sets the time of this instance according to the given 
5341  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
5342  * 
5343  * <li><i>julianday</i> - sets the time of this instance according to the given
5344  * Julian Day instance or the Julian Day given as a float
5345  * 
5346  * <li><i>cycle</i> - any integer giving the number of 60-year cycle in which the date is located.
5347  * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious 
5348  * linear count of years since the beginning of the epoch, much like other calendars. This linear
5349  * count is never used. If both the cycle and year are given, the year is wrapped to the range 0 
5350  * to 60 and treated as if it were a year in the regular 60-year cycle.
5351  * 
5352  * <li><i>year</i> - any integer, including 0
5353  * 
5354  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
5355  * 
5356  * <li><i>day</i> - 1 to 31
5357  * 
5358  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
5359  * is always done with an unambiguous 24 hour representation
5360  * 
5361  * <li><i>minute</i> - 0 to 59
5362  * 
5363  * <li><i>second</i> - 0 to 59
5364  * 
5365  * <li><i>millisecond</i> - 0 to 999
5366  * 
5367  * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify 
5368  * the parts or specify the minutes, seconds, and milliseconds, but not both. This is only used
5369  * in the Hebrew calendar. 
5370  * 
5371  * <li><i>minute</i> - 0 to 59
5372  * 
5373  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
5374  * </ul>
5375  *
5376  * If the constructor is called with another date instance instead of
5377  * a parameter block, the other instance acts as a parameter block and its
5378  * settings are copied into the current instance.<p>
5379  * 
5380  * If the constructor is called with no arguments at all or if none of the 
5381  * properties listed above are present, then the RD is calculate based on 
5382  * the current date at the time of instantiation. <p>
5383  * 
5384  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
5385  * specified in the params, it is assumed that they have the smallest possible
5386  * value in the range for the property (zero or one).<p>
5387  * 
5388  * 
5389  * @private
5390  * @constructor
5391  * @param {Object=} params parameters that govern the settings and behaviour of this RD date
5392  */
5393 var RataDie = function(params) {
5394 	if (params) {
5395 		if (typeof(params.date) !== 'undefined') {
5396 			// accept JS Date classes or strings
5397 			var date = params.date;
5398 			if (!(JSUtils.isDate(date))) {
5399 				date = new Date(date); // maybe a string initializer?
5400 			}
5401 			this._setTime(date.getTime());
5402 		} else if (typeof(params.unixtime) !== 'undefined') {
5403 			this._setTime(parseInt(params.unixtime, 10));
5404 		} else if (typeof(params.julianday) !== 'undefined') {
5405 			// JD time is defined to be UTC
5406 			this._setJulianDay(parseFloat(params.julianday));
5407 		} else if (params.year || params.month || params.day || params.hour ||
5408 				params.minute || params.second || params.millisecond || params.parts || params.cycle) {
5409 			this._setDateComponents(params);
5410 		} else if (typeof(params.rd) !== 'undefined') {
5411 			/**
5412 			 * @type {number} the Rata Die number of this date for this calendar type
5413 			 */
5414 			this.rd = (typeof(params.rd) === 'object' && params.rd instanceof RataDie) ? params.rd.rd : params.rd;
5415 		}
5416 	}
5417 	
5418 	if (typeof(this.rd) === 'undefined' || isNaN(this.rd)) {
5419 		var now = new Date();
5420 		this._setTime(now.getTime());
5421 	}
5422 };
5423 
5424 /**
5425  * @private
5426  * @const
5427  * @type {number}
5428  */
5429 RataDie.gregorianEpoch = 1721424.5;
5430 
5431 RataDie.prototype = {
5432 	/**
5433 	 * @protected
5434 	 * @type {number}
5435 	 * the difference between a zero Julian day and the zero Gregorian date. 
5436 	 */
5437 	epoch: RataDie.gregorianEpoch,
5438 	
5439 	/**
5440 	 * Set the RD of this instance according to the given unix time. Unix time is
5441 	 * the number of milliseconds since midnight on Jan 1, 1970.
5442 	 *
5443 	 * @protected
5444 	 * @param {number} millis the unix time to set this date to in milliseconds 
5445 	 */
5446 	_setTime: function(millis) {
5447 		// 2440587.5 is the julian day of midnight Jan 1, 1970, UTC (Gregorian)
5448 		this._setJulianDay(2440587.5 + millis / 86400000);
5449 	},
5450 
5451 	/**
5452 	 * Set the date of this instance using a Julian Day.
5453 	 * @protected
5454 	 * @param {number} date the Julian Day to use to set this date
5455 	 */
5456 	_setJulianDay: function (date) {
5457 		var jd = (typeof(date) === 'number') ? new JulianDay(date) : date;
5458 		// round to the nearest millisecond
5459 		this.rd = MathUtils.halfup((jd.getDate() - this.epoch) * 86400000) / 86400000;
5460 	},
5461 
5462 	/**
5463 	 * Return the rd number of the particular day of the week on or before the 
5464 	 * given rd. eg. The Sunday on or before the given rd.
5465 	 * @protected
5466 	 * @param {number} rd the rata die date of the reference date
5467 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
5468 	 * to the current date
5469 	 * @return {number} the rd of the day of the week
5470 	 */
5471 	_onOrBefore: function(rd, dayOfWeek) {
5472 		return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek - 2, 7);
5473 	},
5474 	
5475 	/**
5476 	 * Return the rd number of the particular day of the week on or before the current rd.
5477 	 * eg. The Sunday on or before the current rd. If the offset is given, the calculation
5478 	 * happens in wall time instead of UTC. UTC time may be a day before or day behind 
5479 	 * wall time, so it it would give the wrong day of the week if this calculation was
5480 	 * done in UTC time when the caller really wanted wall time. Even though the calculation
5481 	 * may be done in wall time, the return value is nonetheless always given in UTC.
5482 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
5483 	 * to the current date
5484 	 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is
5485 	 * not given
5486 	 * @return {number} the rd of the day of the week
5487 	 */
5488 	onOrBefore: function(dayOfWeek, offset) {
5489 		offset = offset || 0;
5490 		return this._onOrBefore(this.rd + offset, dayOfWeek) - offset;
5491 	},
5492 	
5493 	/**
5494 	 * Return the rd number of the particular day of the week on or before the current rd.
5495 	 * eg. The Sunday on or before the current rd. If the offset is given, the calculation
5496 	 * happens in wall time instead of UTC. UTC time may be a day before or day behind 
5497 	 * wall time, so it it would give the wrong day of the week if this calculation was
5498 	 * done in UTC time when the caller really wanted wall time. Even though the calculation
5499 	 * may be done in wall time, the return value is nonetheless always given in UTC.
5500 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
5501 	 * to the reference date
5502 	 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is
5503 	 * not given
5504 	 * @return {number} the day of the week
5505 	 */
5506 	onOrAfter: function(dayOfWeek, offset) {
5507 		offset = offset || 0;
5508 		return this._onOrBefore(this.rd+6+offset, dayOfWeek) - offset;
5509 	},
5510 	
5511 	/**
5512 	 * Return the rd number of the particular day of the week before the current rd.
5513 	 * eg. The Sunday before the current rd. If the offset is given, the calculation
5514 	 * happens in wall time instead of UTC. UTC time may be a day before or day behind 
5515 	 * wall time, so it it would give the wrong day of the week if this calculation was
5516 	 * done in UTC time when the caller really wanted wall time. Even though the calculation
5517 	 * may be done in wall time, the return value is nonetheless always given in UTC.
5518 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
5519 	 * to the reference date
5520 	 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is
5521 	 * not given
5522 	 * @return {number} the day of the week
5523 	 */
5524 	before: function(dayOfWeek, offset) {
5525 		offset = offset || 0;
5526 		return this._onOrBefore(this.rd-1+offset, dayOfWeek) - offset;
5527 	},
5528 	
5529 	/**
5530 	 * Return the rd number of the particular day of the week after the current rd.
5531 	 * eg. The Sunday after the current rd. If the offset is given, the calculation
5532 	 * happens in wall time instead of UTC. UTC time may be a day before or day behind 
5533 	 * wall time, so it it would give the wrong day of the week if this calculation was
5534 	 * done in UTC time when the caller really wanted wall time. Even though the calculation
5535 	 * may be done in wall time, the return value is nonetheless always given in UTC.
5536 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
5537 	 * to the reference date
5538 	 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is
5539 	 * not given
5540 	 * @return {number} the day of the week
5541 	 */
5542 	after: function(dayOfWeek, offset) {
5543 		offset = offset || 0;
5544 		return this._onOrBefore(this.rd+7+offset, dayOfWeek) - offset;
5545 	},
5546 
5547 	/**
5548 	 * Return the unix time equivalent to this Gregorian date instance. Unix time is
5549 	 * the number of milliseconds since midnight on Jan 1, 1970 UTC. This method only
5550 	 * returns a valid number for dates between midnight, Jan 1, 1970 and  
5551 	 * Jan 19, 2038 at 3:14:07am when the unix time runs out. If this instance 
5552 	 * encodes a date outside of that range, this method will return -1.
5553 	 * 
5554 	 * @return {number} a number giving the unix time, or -1 if the date is outside the
5555 	 * valid unix time range
5556 	 */
5557 	getTime: function() {
5558 		// earlier than Jan 1, 1970
5559 		// or later than Jan 19, 2038 at 3:14:07am
5560 		var jd = this.getJulianDay();
5561 		if (jd < 2440587.5 || jd > 2465442.634803241) { 
5562 			return -1;
5563 		}
5564 	
5565 		// avoid the rounding errors in the floating point math by only using
5566 		// the whole days from the rd, and then calculating the milliseconds directly
5567 		return Math.round((jd - 2440587.5) * 86400000);
5568 	},
5569 
5570 	/**
5571 	 * Return the extended unix time equivalent to this Gregorian date instance. Unix time is
5572 	 * the number of milliseconds since midnight on Jan 1, 1970 UTC. Traditionally unix time
5573 	 * (or the type "time_t" in C/C++) is only encoded with a unsigned 32 bit integer, and thus 
5574 	 * runs out on Jan 19, 2038. However, most Javascript engines encode numbers well above 
5575 	 * 32 bits and the Date object allows you to encode up to 100 million days worth of time 
5576 	 * after Jan 1, 1970, and even more interestingly 100 million days worth of time before
5577 	 * Jan 1, 1970 as well. This method returns the number of milliseconds in that extended 
5578 	 * range. If this instance encodes a date outside of that range, this method will return
5579 	 * NaN.
5580 	 * 
5581 	 * @return {number} a number giving the extended unix time, or NaN if the date is outside 
5582 	 * the valid extended unix time range
5583 	 */
5584 	getTimeExtended: function() {
5585 		var jd = this.getJulianDay();
5586 		
5587 		// test if earlier than Jan 1, 1970 - 100 million days
5588 		// or later than Jan 1, 1970 + 100 million days
5589 		if (jd < -97559412.5 || jd > 102440587.5) { 
5590 			return NaN;
5591 		}
5592 	
5593 		// avoid the rounding errors in the floating point math by only using
5594 		// the whole days from the rd, and then calculating the milliseconds directly
5595 		return Math.round((jd - 2440587.5) * 86400000);
5596 	},
5597 
5598 	/**
5599 	 * Return the Julian Day equivalent to this calendar date as a number.
5600 	 * This returns the julian day in UTC.
5601 	 * 
5602 	 * @return {number} the julian date equivalent of this date
5603 	 */
5604 	getJulianDay: function() {
5605 		return this.rd + this.epoch;
5606 	},
5607 
5608 	/**
5609 	 * Return the Rata Die (fixed day) number of this RD date.
5610 	 * 
5611 	 * @return {number} the rd date as a number
5612 	 */
5613 	getRataDie: function() {
5614 		return this.rd;
5615 	}
5616 };
5617 
5618 
5619 /*< GregRataDie.js */
5620 /*
5621  * gregratadie.js - Represent the RD date number in the Gregorian calendar
5622  * 
5623  * Copyright © 2014-2015, JEDLSoft
5624  *
5625  * Licensed under the Apache License, Version 2.0 (the "License");
5626  * you may not use this file except in compliance with the License.
5627  * You may obtain a copy of the License at
5628  *
5629  *     http://www.apache.org/licenses/LICENSE-2.0
5630  *
5631  * Unless required by applicable law or agreed to in writing, software
5632  * distributed under the License is distributed on an "AS IS" BASIS,
5633  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5634  *
5635  * See the License for the specific language governing permissions and
5636  * limitations under the License.
5637  */
5638 
5639 /* !depends 
5640 ilib.js
5641 GregorianCal.js
5642 RataDie.js
5643 MathUtils.js
5644 */
5645 
5646 
5647 /**
5648  * @class
5649  * Construct a new Gregorian RD date number object. The constructor parameters can 
5650  * contain any of the following properties:
5651  * 
5652  * <ul>
5653  * <li><i>unixtime<i> - sets the time of this instance according to the given 
5654  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
5655  * 
5656  * <li><i>julianday</i> - sets the time of this instance according to the given
5657  * Julian Day instance or the Julian Day given as a float
5658  * 
5659  * <li><i>year</i> - any integer, including 0
5660  * 
5661  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
5662  * 
5663  * <li><i>day</i> - 1 to 31
5664  * 
5665  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
5666  * is always done with an unambiguous 24 hour representation
5667  * 
5668  * <li><i>minute</i> - 0 to 59
5669  * 
5670  * <li><i>second</i> - 0 to 59
5671  * 
5672  * <li><i>millisecond</i> - 0 to 999
5673  * 
5674  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
5675  * </ul>
5676  *
5677  * If the constructor is called with another Gregorian date instance instead of
5678  * a parameter block, the other instance acts as a parameter block and its
5679  * settings are copied into the current instance.<p>
5680  * 
5681  * If the constructor is called with no arguments at all or if none of the 
5682  * properties listed above are present, then the RD is calculate based on 
5683  * the current date at the time of instantiation. <p>
5684  * 
5685  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
5686  * specified in the params, it is assumed that they have the smallest possible
5687  * value in the range for the property (zero or one).<p>
5688  * 
5689  * 
5690  * @private
5691  * @constructor
5692  * @extends RataDie
5693  * @param {Object=} params parameters that govern the settings and behaviour of this Gregorian RD date
5694  */
5695 var GregRataDie = function(params) {
5696 	this.cal = params && params.cal || new GregorianCal();
5697 	/** @type {number|undefined} */
5698 	this.rd = NaN;
5699 	RataDie.call(this, params);
5700 };
5701 
5702 GregRataDie.prototype = new RataDie();
5703 GregRataDie.prototype.parent = RataDie;
5704 GregRataDie.prototype.constructor = GregRataDie;
5705 
5706 /**
5707  * the cumulative lengths of each month, for a non-leap year 
5708  * @private
5709  * @const
5710  * @type Array.<number>
5711  */
5712 GregRataDie.cumMonthLengths = [
5713     0,   /* Jan */
5714 	31,  /* Feb */
5715 	59,  /* Mar */
5716 	90,  /* Apr */
5717 	120, /* May */
5718 	151, /* Jun */
5719 	181, /* Jul */
5720 	212, /* Aug */
5721 	243, /* Sep */
5722 	273, /* Oct */
5723 	304, /* Nov */
5724 	334, /* Dec */
5725 	365
5726 ];
5727 
5728 /**
5729  * the cumulative lengths of each month, for a leap year 
5730  * @private
5731  * @const
5732  * @type Array.<number>
5733  */
5734 GregRataDie.cumMonthLengthsLeap = [
5735 	0,   /* Jan */
5736 	31,  /* Feb */
5737 	60,  /* Mar */
5738 	91,  /* Apr */
5739 	121, /* May */
5740 	152, /* Jun */
5741 	182, /* Jul */
5742 	213, /* Aug */
5743 	244, /* Sep */
5744 	274, /* Oct */
5745 	305, /* Nov */
5746 	335, /* Dec */
5747 	366
5748 ];
5749 
5750 /**
5751  * Calculate the Rata Die (fixed day) number of the given date.
5752  * 
5753  * @private
5754  * @param {Object} date the date components to calculate the RD from
5755  */
5756 GregRataDie.prototype._setDateComponents = function(date) {
5757 	var year = parseInt(date.year, 10) || 0;
5758 	var month = parseInt(date.month, 10) || 1;
5759 	var day = parseInt(date.day, 10) || 1;
5760 	var hour = parseInt(date.hour, 10) || 0;
5761 	var minute = parseInt(date.minute, 10) || 0;
5762 	var second = parseInt(date.second, 10) || 0;
5763 	var millisecond = parseInt(date.millisecond, 10) || 0;
5764 
5765 	var years = 365 * (year - 1) +
5766 		Math.floor((year-1)/4) -
5767 		Math.floor((year-1)/100) +
5768 		Math.floor((year-1)/400);
5769 	
5770 	var dayInYear = (month > 1 ? GregRataDie.cumMonthLengths[month-1] : 0) +
5771 		day +
5772 		(GregorianCal.prototype.isLeapYear.call(this.cal, year) && month > 2 ? 1 : 0);
5773 	var rdtime = (hour * 3600000 +
5774 		minute * 60000 +
5775 		second * 1000 +
5776 		millisecond) / 
5777 		86400000; 
5778 	/*
5779 	debug("getRataDie: converting " +  JSON.stringify(this));
5780 	debug("getRataDie: year is " +  years);
5781 	debug("getRataDie: day in year is " +  dayInYear);
5782 	debug("getRataDie: rdtime is " +  rdtime);
5783 	debug("getRataDie: rd is " +  (years + dayInYear + rdtime));
5784 	*/
5785 	
5786 	/**
5787 	 * @type {number|undefined} the RD number of this Gregorian date
5788 	 */
5789 	this.rd = years + dayInYear + rdtime;
5790 };
5791 
5792 /**
5793  * Return the rd number of the particular day of the week on or before the 
5794  * given rd. eg. The Sunday on or before the given rd.
5795  * @private
5796  * @param {number} rd the rata die date of the reference date
5797  * @param {number} dayOfWeek the day of the week that is being sought relative 
5798  * to the current date
5799  * @return {number} the rd of the day of the week
5800  */
5801 GregRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
5802 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek, 7);
5803 };
5804 
5805 
5806 /*< TimeZone.js */
5807 /*
5808  * TimeZone.js - Definition of a time zone class
5809  * 
5810  * Copyright © 2012-2015, JEDLSoft
5811  *
5812  * Licensed under the Apache License, Version 2.0 (the "License");
5813  * you may not use this file except in compliance with the License.
5814  * You may obtain a copy of the License at
5815  *
5816  *     http://www.apache.org/licenses/LICENSE-2.0
5817  *
5818  * Unless required by applicable law or agreed to in writing, software
5819  * distributed under the License is distributed on an "AS IS" BASIS,
5820  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5821  *
5822  * See the License for the specific language governing permissions and
5823  * limitations under the License.
5824  */
5825 
5826 /*
5827 !depends 
5828 ilib.js 
5829 Locale.js
5830 LocaleInfo.js
5831 Utils.js
5832 MathUtils.js
5833 JSUtils.js
5834 GregRataDie.js
5835 IString.js
5836 CalendarFactory.js
5837 */
5838 
5839 // !data localeinfo zoneinfo
5840 
5841 
5842 
5843 
5844 /**
5845  * @class
5846  * Create a time zone instance. 
5847  * 
5848  * This class reports and transforms
5849  * information about particular time zones.<p>
5850  * 
5851  * The options parameter may contain any of the following properties:
5852  * 
5853  * <ul>
5854  * <li><i>id</i> - The id of the requested time zone such as "Europe/London" or 
5855  * "America/Los_Angeles". These are taken from the IANA time zone database. (See
5856  * http://www.iana.org/time-zones for more information.) <p>
5857  * 
5858  * There is one special 
5859  * time zone that is not taken from the IANA database called simply "local". In
5860  * this case, this class will attempt to discover the current time zone and
5861  * daylight savings time settings by calling standard Javascript classes to 
5862  * determine the offsets from UTC. 
5863  * 
5864  * <li><i>locale</i> - The locale for this time zone.
5865  * 
5866  * <li><i>offset</i> - Choose the time zone based on the offset from UTC given in
5867  * number of minutes (negative is west, positive is east).
5868  * 
5869  * <li><i>onLoad</i> - a callback function to call when the data is fully 
5870  * loaded. When the onLoad option is given, this class will attempt to
5871  * load any missing locale data using the ilib loader callback.
5872  * When the data is loaded, the onLoad function is called with the current 
5873  * instance as a parameter. 
5874  * 
5875  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
5876  * asynchronously. If this option is given as "false", then the "onLoad"
5877  * callback must be given, as the instance returned from this constructor will
5878  * not be usable for a while.
5879  *  
5880  * <li><i>loadParams</i> - an object containing parameters to pass to the 
5881  * loader callback function when locale data is missing. The parameters are not
5882  * interpretted or modified in any way. They are simply passed along. The object 
5883  * may contain any property/value pairs as long as the calling code is in
5884  * agreement with the loader callback function as to what those parameters mean.
5885  * </ul>
5886  * 
5887  * There is currently no way in the ECMAscript
5888  * standard to tell which exact time zone is currently in use. Choosing the
5889  * id "locale" or specifying an explicit offset will not give a specific time zone, 
5890  * as it is impossible to tell with certainty which zone the offsets 
5891  * match.<p>
5892  * 
5893  * When the id "local" is given or the offset option is specified, this class will
5894  * have the following behaviours:
5895  * <ul>
5896  * <li>The display name will always be given as the RFC822 style, no matter what
5897  * style is requested
5898  * <li>The id will also be returned as the RFC822 style display name
5899  * <li>When the offset is explicitly given, this class will assume the time zone 
5900  * does not support daylight savings time, and the offsets will be calculated 
5901  * the same way year round.
5902  * <li>When the offset is explicitly given, the inDaylightSavings() method will 
5903  * always return false.
5904  * <li>When the id "local" is given, this class will attempt to determine the 
5905  * daylight savings time settings by examining the offset from UTC on Jan 1
5906  * and June 1 of the current year. If they are different, this class assumes
5907  * that the local time zone uses DST. When the offset for a particular date is
5908  * requested, it will use the built-in Javascript support to determine the 
5909  * offset for that date.
5910  * </ul> 
5911  * 
5912  * If a more specific time zone is 
5913  * needed with display names and known start/stop times for DST, use the "id" 
5914  * property instead to specify the time zone exactly. You can perhaps ask the
5915  * user which time zone they prefer so that your app does not need to guess.<p>
5916  * 
5917  * If the id and the offset are both not given, the default time zone for the 
5918  * locale is retrieved from
5919  * the locale info. If the locale is not specified, the default locale for the
5920  * library is used.<p>
5921  * 
5922  * Because this class was designed for use in web sites, and the vast majority
5923  * of dates and times being formatted are recent date/times, this class is simplified
5924  * by not implementing historical time zones. That is, when governments change the 
5925  * time zone rules for a particular zone, only the latest such rule is implemented 
5926  * in this class. That means that determining the offset for a date that is prior 
5927  * to the last change may give the wrong result. Historical time zone calculations
5928  * may be implemented in a later version of iLib if there is enough demand for it,
5929  * but it would entail a much larger set of time zone data that would have to be
5930  * loaded.  
5931  * 
5932  * 
5933  * @constructor
5934  * @param {Object} options Options guiding the construction of this time zone instance
5935  */
5936 var TimeZone = function(options) {
5937 	this.sync = true;
5938 	this.locale = new Locale();
5939 	this.isLocal = false;
5940 	
5941 	if (options) {
5942 		if (options.locale) {
5943 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
5944 		}
5945 		
5946 		if (options.id) {
5947 			var id = options.id.toString();
5948 			if (id === 'local') {
5949 				this.isLocal = true;
5950 				
5951 				// use standard Javascript Date to figure out the time zone offsets
5952 				var now = new Date(), 
5953 					jan1 = new Date(now.getFullYear(), 0, 1),  // months in std JS Date object are 0-based
5954 					jun1 = new Date(now.getFullYear(), 5, 1);
5955 				
5956 				// Javascript's method returns the offset backwards, so we have to
5957 				// take the negative to get the correct offset
5958 				this.offsetJan1 = -jan1.getTimezoneOffset();
5959 				this.offsetJun1 = -jun1.getTimezoneOffset();
5960 				// the offset of the standard time for the time zone is always the one that is closest 
5961 				// to negative infinity of the two, no matter whether you are in the northern or southern 
5962 				// hemisphere, east or west
5963 				this.offset = Math.min(this.offsetJan1, this.offsetJun1);
5964 			}
5965 			this.id = id;
5966 		} else if (options.offset) {
5967 			this.offset = (typeof(options.offset) === 'string') ? parseInt(options.offset, 10) : options.offset;
5968 			this.id = this.getDisplayName(undefined, undefined);
5969 		}
5970 		
5971 		if (typeof(options.sync) !== 'undefined') {
5972 			this.sync = !!options.sync;
5973 		}
5974 		
5975 		this.loadParams = options.loadParams;
5976 		this.onLoad = options.onLoad;
5977 	}
5978 
5979 	//console.log("timezone: locale is " + this.locale);
5980 	
5981 	if (!this.id) {
5982 		new LocaleInfo(this.locale, {
5983 			sync: this.sync,
5984 			onLoad: ilib.bind(this, function (li) {
5985 				this.id = li.getTimeZone() || "Etc/UTC";
5986 				this._loadtzdata();
5987 			})
5988 		});
5989 	} else {
5990 		this._loadtzdata();
5991 	}
5992 
5993 	//console.log("localeinfo is: " + JSON.stringify(this.locinfo));
5994 	//console.log("id is: " + JSON.stringify(this.id));
5995 };
5996 
5997 /*
5998  * Explanation of the compressed time zone info properties.
5999  * {
6000  *     "o": "8:0",      // offset from UTC
6001  *     "f": "W{c}T",    // standard abbreviation. For time zones that observe DST, the {c} replacement is replaced with the 
6002  *                      // letter in the e.c or s.c properties below 
6003  *     "e": {           // info about the end of DST
6004  *         "j": 78322.5 // Julian day when the transition happens. Either specify the "j" property or all of the "m", "r", and 
6005  *                      // "t" properties, but not both sets.
6006  *         "m": 3,      // month that it ends
6007  *         "r": "l0",   // rule for the day it ends "l" = "last", numbers are Sun=0 through Sat=6. Other syntax is "0>7". 
6008  *                      // This means the 0-day (Sun) after the 7th of the month. Other possible operators are <, >, <=, >=
6009  *         "t": "2:0",  // time of day that the DST turns off, hours:minutes
6010  *         "c": "S"     // character to replace into the abbreviation for standard time 
6011  *     },
6012  *     "s": {           // info about the start of DST
6013  *         "j": 78189.5 // Julian day when the transition happens. Either specify the "j" property or all of the "m", "r", and 
6014  *                      // "t" properties, but not both sets.
6015  *         "m": 10,     // month that it starts
6016  *         "r": "l0",   // rule for the day it starts "l" = "last", numbers are Sun=0 through Sat=6. Other syntax is "0>7".
6017  *                      // This means the 0-day (Sun) after the 7th of the month. Other possible operators are <, >, <=, >=
6018  *         "t": "2:0",  // time of day that the DST turns on, hours:minutes
6019  *         "v": "1:0",  // amount of time saved in hours:minutes
6020  *         "c": "D"     // character to replace into the abbreviation for daylight time
6021  *     },
6022  *     "c": "AU",       // ISO code for the country that contains this time zone
6023  *     "n": "W. Australia {c} Time"
6024  *                      // long English name of the zone. The {c} replacement is for the word "Standard" or "Daylight" as appropriate
6025  * }
6026  */
6027 TimeZone.prototype._loadtzdata = function () {
6028 	var zoneName = this.id.replace(/-/g, "m").replace(/\+/g, "p");
6029 	// console.log("id is: " + JSON.stringify(this.id));
6030 	// console.log("zoneinfo is: " + JSON.stringify(ilib.data.zoneinfo[zoneName]));
6031 	if (!ilib.data.zoneinfo[zoneName] && typeof(this.offset) === 'undefined') {
6032 		Utils.loadData({
6033 			object: TimeZone, 
6034 			nonlocale: true,	// locale independent 
6035 			name: "zoneinfo/" + this.id + ".json", 
6036 			sync: this.sync, 
6037 			loadParams: this.loadParams, 
6038 			callback: ilib.bind(this, function (tzdata) {
6039 				if (tzdata && !JSUtils.isEmpty(tzdata)) {
6040 					ilib.data.zoneinfo[zoneName] = tzdata;
6041 				}
6042 				this._initZone(zoneName);
6043 			})
6044 		});
6045 	} else {
6046 		this._initZone(zoneName);
6047 	}
6048 };
6049 
6050 TimeZone.prototype._initZone = function(zoneName) {
6051 	/** 
6052 	 * @private
6053 	 * @type {{o:string,f:string,e:Object.<{m:number,r:string,t:string,z:string}>,s:Object.<{m:number,r:string,t:string,z:string,v:string,c:string}>,c:string,n:string}} 
6054 	 */
6055 	this.zone = ilib.data.zoneinfo[zoneName];
6056 	if (!this.zone && typeof(this.offset) === 'undefined') {
6057 		this.id = "Etc/UTC";
6058 		this.zone = ilib.data.zoneinfo[this.id];
6059 	}
6060 	
6061 	this._calcDSTSavings();
6062 	
6063 	if (typeof(this.offset) === 'undefined' && this.zone.o) {
6064 		var offsetParts = this._offsetStringToObj(this.zone.o);
6065 		/**
6066 		 * @private
6067 		 * @type {number} raw offset from UTC without DST, in minutes
6068 		 */
6069 		this.offset = (Math.abs(offsetParts.h || 0) * 60 + (offsetParts.m || 0)) * MathUtils.signum(offsetParts.h || 0);
6070 	}
6071 	
6072 	if (this.onLoad && typeof(this.onLoad) === 'function') {
6073 		this.onLoad(this);
6074 	}
6075 };
6076 
6077 /** @private */
6078 TimeZone._marshallIds = function (country, sync, callback) {
6079 	var tz, ids = [];
6080 	
6081 	if (!country) {
6082 		// local is a special zone meaning "the local time zone according to the JS engine we are running upon"
6083 		ids.push("local");
6084 		for (tz in ilib.data.timezones) {
6085 			if (ilib.data.timezones[tz]) {
6086 				ids.push(ilib.data.timezones[tz]);
6087 			}
6088 		}
6089 		if (typeof(callback) === 'function') {
6090 			callback(ids);
6091 		}
6092 	} else {
6093 		if (!ilib.data.zoneinfo.zonetab) {
6094 			Utils.loadData({
6095 				object: TimeZone, 
6096 				nonlocale: true,	// locale independent 
6097 				name: "zoneinfo/zonetab.json", 
6098 				sync: sync, 
6099 				callback: ilib.bind(this, function (tzdata) {
6100 					if (tzdata) {
6101 						ilib.data.zoneinfo.zonetab = tzdata;
6102 					}
6103 					
6104 					ids = ilib.data.zoneinfo.zonetab[country];
6105 					
6106 					if (typeof(callback) === 'function') {
6107 						callback(ids);
6108 					}
6109 				})
6110 			});
6111 		} else {
6112 			ids = ilib.data.zoneinfo.zonetab[country];
6113 			if (typeof(callback) === 'function') {
6114 				callback(ids);
6115 			}
6116 		}
6117 	}
6118 	
6119 	return ids;
6120 };
6121 
6122 /**
6123  * Return an array of available zone ids that the constructor knows about.
6124  * The country parameter is optional. If it is not given, all time zones will
6125  * be returned. If it specifies a country code, then only time zones for that
6126  * country will be returned.
6127  * 
6128  * @param {string|undefined} country country code for which time zones are being sought
6129  * @param {boolean} sync whether to find the available ids synchronously (true) or asynchronously (false)
6130  * @param {function(Array.<string>)} onLoad callback function to call when the data is finished loading
6131  * @return {Array.<string>} an array of zone id strings
6132  */
6133 TimeZone.getAvailableIds = function (country, sync, onLoad) {
6134 	var tz, ids = [];
6135 	
6136 	if (typeof(sync) !== 'boolean') {
6137 		sync = true;
6138 	}
6139 	
6140 	if (ilib.data.timezones.length === 0) {
6141 		if (typeof(ilib._load) !== 'undefined' && typeof(ilib._load.listAvailableFiles) === 'function') {
6142 			ilib._load.listAvailableFiles(sync, function(hash) {
6143 				for (var dir in hash) {
6144 					var files = hash[dir];
6145 					if (ilib.isArray(files)) {
6146 						files.forEach(function (filename) {
6147 							if (filename && filename.match(/^zoneinfo/)) {
6148 								ilib.data.timezones.push(filename.replace(/^zoneinfo\//, "").replace(/\.json$/, ""));
6149 							}
6150 						});
6151 					}
6152 				}
6153 				ids = TimeZone._marshallIds(country, sync, onLoad);
6154 			});
6155 		} else {
6156 			for (tz in ilib.data.zoneinfo) {
6157 				if (ilib.data.zoneinfo[tz]) {
6158 					ilib.data.timezones.push(tz);
6159 				}
6160 			}
6161 			ids = TimeZone._marshallIds(country, sync, onLoad);
6162 		}
6163 	} else {
6164 		ids = TimeZone._marshallIds(country, sync, onLoad);
6165 	}
6166 	
6167 	return ids;
6168 };
6169 
6170 /**
6171  * Return the id used to uniquely identify this time zone.
6172  * @return {string} a unique id for this time zone
6173  */
6174 TimeZone.prototype.getId = function () {
6175 	return this.id.toString();
6176 };
6177 
6178 /**
6179  * Return the abbreviation that is used for the current time zone on the given date.
6180  * The date may be in DST or during standard time, and many zone names have different
6181  * abbreviations depending on whether or not the date is falls within DST.<p>
6182  * 
6183  * There are two styles that are supported:
6184  * 
6185  * <ol>
6186  * <li>standard - returns the 3 to 5 letter abbreviation of the time zone name such 
6187  * as "CET" for "Central European Time" or "PDT" for "Pacific Daylight Time"
6188  * <li>rfc822 - returns an RFC 822 style time zone specifier, which specifies more
6189  * explicitly what the offset is from UTC
6190  * <li>long - returns the long name of the zone in English
6191  * </ol>
6192  *  
6193  * @param {IDate=} date a date to determine if it is in daylight time or standard time
6194  * @param {string=} style one of "standard" or "rfc822". Default if not specified is "standard"
6195  * @return {string} the name of the time zone, abbreviated according to the style 
6196  */
6197 TimeZone.prototype.getDisplayName = function (date, style) {
6198 	style = (this.isLocal || typeof(this.zone) === 'undefined') ? "rfc822" : (style || "standard");
6199 	switch (style) {
6200 		default:
6201 		case 'standard':
6202 			if (this.zone.f && this.zone.f !== "zzz") {
6203 				if (this.zone.f.indexOf("{c}") !== -1) {
6204 					var letter = "";
6205 					letter = this.inDaylightTime(date) ? this.zone.s && this.zone.s.c : this.zone.e && this.zone.e.c; 
6206 					var temp = new IString(this.zone.f);
6207 					return temp.format({c: letter || ""});
6208 				}
6209 				return this.zone.f;
6210 			} 
6211 			var temp = "GMT" + this.zone.o;
6212 			if (this.inDaylightTime(date)) {
6213 				temp += "+" + this.zone.s.v;
6214 			}
6215 			return temp;
6216 			break;
6217 		case 'rfc822':
6218 			var offset = this.getOffset(date), // includes the DST if applicable
6219 				ret = "UTC",
6220 				hour = offset.h || 0,
6221 				minute = offset.m || 0;
6222 			
6223 			if (hour !== 0) {
6224 				ret += (hour > 0) ? "+" : "-";
6225 				if (Math.abs(hour) < 10) {
6226 					ret += "0";
6227 				}
6228 				ret += (hour < 0) ? -hour : hour;
6229 				if (minute < 10) {
6230 					ret += "0";
6231 				}
6232 				ret += minute;
6233 			}
6234 			return ret; 
6235 		case 'long':
6236 			if (this.zone.n) {
6237 				if (this.zone.n.indexOf("{c}") !== -1) {
6238 					var str = this.inDaylightTime(date) ? "Daylight" : "Standard"; 
6239 					var temp = new IString(this.zone.n);
6240 					return temp.format({c: str || ""});
6241 				}
6242 				return this.zone.n;
6243 			}
6244 			var temp = "GMT" + this.zone.o;
6245 			if (this.inDaylightTime(date)) {
6246 				temp += "+" + this.zone.s.v;
6247 			}
6248 			return temp;
6249 			break;
6250 	}
6251 };
6252 
6253 /**
6254  * Convert the offset string to an object with an h, m, and possibly s property
6255  * to indicate the hours, minutes, and seconds.
6256  * 
6257  * @private
6258  * @param {string} str the offset string to convert to an object
6259  * @return {Object.<{h:number,m:number,s:number}>} an object giving the offset for the zone at 
6260  * the given date/time, in hours, minutes, and seconds
6261  */
6262 TimeZone.prototype._offsetStringToObj = function (str) {
6263 	var offsetParts = (typeof(str) === 'string') ? str.split(":") : [],
6264 		ret = {h:0},
6265 		temp;
6266 	
6267 	if (offsetParts.length > 0) {
6268 		ret.h = parseInt(offsetParts[0], 10);
6269 		if (offsetParts.length > 1) {
6270 			temp = parseInt(offsetParts[1], 10);
6271 			if (temp) {
6272 				ret.m = temp;
6273 			}
6274 			if (offsetParts.length > 2) {
6275 				temp = parseInt(offsetParts[2], 10);
6276 				if (temp) {
6277 					ret.s = temp;
6278 				}
6279 			}
6280 		}
6281 	}
6282 
6283 	return ret;
6284 };
6285 
6286 /**
6287  * Returns the offset of this time zone from UTC at the given date/time. If daylight saving 
6288  * time is in effect at the given date/time, this method will return the offset value 
6289  * adjusted by the amount of daylight saving.
6290  * @param {IDate=} date the date for which the offset is needed
6291  * @return {Object.<{h:number,m:number}>} an object giving the offset for the zone at 
6292  * the given date/time, in hours, minutes, and seconds  
6293  */
6294 TimeZone.prototype.getOffset = function (date) {
6295 	if (!date) {
6296 		return this.getRawOffset();
6297 	}
6298 	var offset = this.getOffsetMillis(date)/60000;
6299 	
6300 	var hours = MathUtils.down(offset/60),
6301 		minutes = Math.abs(offset) - Math.abs(hours)*60;
6302 
6303 	var ret = {
6304 		h: hours
6305 	};
6306 	if (minutes != 0) {
6307 		ret.m = minutes;
6308 	}
6309 	return ret;
6310 };
6311 
6312 /**
6313  * Returns the offset of this time zone from UTC at the given date/time expressed in 
6314  * milliseconds. If daylight saving 
6315  * time is in effect at the given date/time, this method will return the offset value 
6316  * adjusted by the amount of daylight saving. Negative numbers indicate offsets west
6317  * of UTC and conversely, positive numbers indicate offset east of UTC.
6318  *  
6319  * @param {IDate=} date the date for which the offset is needed, or null for the
6320  * present date
6321  * @return {number} the number of milliseconds of offset from UTC that the given date is
6322  */
6323 TimeZone.prototype.getOffsetMillis = function (date) {
6324 	var ret;
6325 	
6326 	// check if the dst property is defined -- the intrinsic JS Date object doesn't work so
6327 	// well if we are in the overlap time at the end of DST
6328 	if (this.isLocal && typeof(date.dst) === 'undefined') {
6329 		var d = (!date) ? new Date() : new Date(date.getTimeExtended());
6330 		return -d.getTimezoneOffset() * 60000;
6331 	} 
6332 	
6333 	ret = this.offset;
6334 	
6335 	if (date && this.inDaylightTime(date)) {
6336 		ret += this.dstSavings;
6337 	}
6338 	
6339 	return ret * 60000;
6340 };
6341 
6342 /**
6343  * Return the offset in milliseconds when the date has an RD number in wall
6344  * time rather than in UTC time.
6345  * @protected
6346  * @param date the date to check in wall time
6347  * @returns {number} the number of milliseconds of offset from UTC that the given date is
6348  */
6349 TimeZone.prototype._getOffsetMillisWallTime = function (date) {
6350 	var ret;
6351 	
6352 	ret = this.offset;
6353 	
6354 	if (date && this.inDaylightTime(date, true)) {
6355 		ret += this.dstSavings;
6356 	}
6357 	
6358 	return ret * 60000;
6359 };
6360 
6361 /**
6362  * Returns the offset of this time zone from UTC at the given date/time. If daylight saving 
6363  * time is in effect at the given date/time, this method will return the offset value 
6364  * adjusted by the amount of daylight saving.
6365  * @param {IDate=} date the date for which the offset is needed
6366  * @return {string} the offset for the zone at the given date/time as a string in the 
6367  * format "h:m:s" 
6368  */
6369 TimeZone.prototype.getOffsetStr = function (date) {
6370 	var offset = this.getOffset(date),
6371 		ret;
6372 	
6373 	ret = offset.h;
6374 	if (typeof(offset.m) !== 'undefined') {
6375 		ret += ":" + offset.m;
6376 		if (typeof(offset.s) !== 'undefined') {
6377 			ret += ":" + offset.s;
6378 		}
6379 	} else {
6380 		ret += ":0";
6381 	}
6382 	
6383 	return ret;
6384 };
6385 
6386 /**
6387  * Gets the offset from UTC for this time zone.
6388  * @return {Object.<{h:number,m:number,s:number}>} an object giving the offset from 
6389  * UTC for this time zone, in hours, minutes, and seconds 
6390  */
6391 TimeZone.prototype.getRawOffset = function () {
6392 	var hours = MathUtils.down(this.offset/60),
6393 		minutes = Math.abs(this.offset) - Math.abs(hours)*60;
6394 	
6395 	var ret = {
6396 		h: hours
6397 	};
6398 	if (minutes != 0) {
6399 		ret.m = minutes;
6400 	}
6401 	return ret;
6402 };
6403 
6404 /**
6405  * Gets the offset from UTC for this time zone expressed in milliseconds. Negative numbers
6406  * indicate zones west of UTC, and positive numbers indicate zones east of UTC.
6407  * 
6408  * @return {number} an number giving the offset from 
6409  * UTC for this time zone in milliseconds 
6410  */
6411 TimeZone.prototype.getRawOffsetMillis = function () {
6412 	return this.offset * 60000;
6413 };
6414 
6415 /**
6416  * Gets the offset from UTC for this time zone without DST savings.
6417  * @return {string} the offset from UTC for this time zone, in the format "h:m:s" 
6418  */
6419 TimeZone.prototype.getRawOffsetStr = function () {
6420 	var off = this.getRawOffset();
6421 	return off.h + ":" + (off.m || "0");
6422 };
6423 
6424 /**
6425  * Return the amount of time in hours:minutes that the clock is advanced during
6426  * daylight savings time.
6427  * @return {Object.<{h:number,m:number,s:number}>} the amount of time that the 
6428  * clock advances for DST in hours, minutes, and seconds 
6429  */
6430 TimeZone.prototype.getDSTSavings = function () {
6431 	if (this.isLocal) {
6432 		// take the absolute because the difference in the offsets may be positive or
6433 		// negative, depending on the hemisphere
6434 		var savings = Math.abs(this.offsetJan1 - this.offsetJun1);
6435 		var hours = MathUtils.down(savings/60),
6436 			minutes = savings - hours*60;
6437 		return {
6438 			h: hours,
6439 			m: minutes
6440 		};
6441 	} else if (this.zone && this.zone.s) {
6442 		return this._offsetStringToObj(this.zone.s.v);	// this.zone.start.savings
6443 	}
6444 	return {h:0};
6445 };
6446 
6447 /**
6448  * Return the amount of time in hours:minutes that the clock is advanced during
6449  * daylight savings time.
6450  * @return {string} the amount of time that the clock advances for DST in the
6451  * format "h:m:s"
6452  */
6453 TimeZone.prototype.getDSTSavingsStr = function () {
6454 	if (this.isLocal) {
6455 		var savings = this.getDSTSavings();
6456 		return savings.h + ":" + savings.m;
6457 	} else if (typeof(this.offset) !== 'undefined' && this.zone && this.zone.s) {
6458 		return this.zone.s.v;	// this.zone.start.savings
6459 	}
6460 	return "0:0";
6461 };
6462 
6463 /**
6464  * return the rd of the start of DST transition for the given year
6465  * @protected
6466  * @param {Object} rule set of rules
6467  * @param {number} year year to check
6468  * @return {number} the rd of the start of DST for the year
6469  */
6470 TimeZone.prototype._calcRuleStart = function (rule, year) {
6471 	var type = "=", 
6472 		weekday = 0, 
6473 		day, 
6474 		refDay, 
6475 		cal, 
6476 		hour = 0, 
6477 		minute = 0, 
6478 		second = 0,
6479 		time,
6480 		i;
6481 	
6482 	if (typeof(rule.j) !== 'undefined') {
6483 		refDay = new GregRataDie({
6484 			julianday: rule.j
6485 		});
6486 	} else {
6487 		if (rule.r.charAt(0) == 'l' || rule.r.charAt(0) == 'f') {
6488 			cal = CalendarFactory({type: "gregorian"});
6489 			type = rule.r.charAt(0);
6490 			weekday = parseInt(rule.r.substring(1), 10);
6491 			day = (type === 'l') ? cal.getMonLength(rule.m, year) : 1;
6492 			//console.log("_calcRuleStart: Calculating the " + 
6493 			//		(rule.r.charAt(0) == 'f' ? "first " : "last ") + weekday + 
6494 			//		" of month " + rule.m);
6495 		} else {
6496 			i = rule.r.indexOf('<');
6497 			if (i == -1) {
6498 				i = rule.r.indexOf('>');
6499 			}
6500 			
6501 			if (i != -1) {
6502 				type = rule.r.charAt(i);
6503 				weekday = parseInt(rule.r.substring(0, i), 10);
6504 				day = parseInt(rule.r.substring(i+1), 10); 
6505 				//console.log("_calcRuleStart: Calculating the " + weekday + 
6506 				//		type + day + " of month " + rule.m);
6507 			} else {
6508 				day = parseInt(rule.r, 10);
6509 				//console.log("_calcRuleStart: Calculating the " + day + " of month " + rule.m);
6510 			}
6511 		}
6512 	
6513 		if (rule.t) {
6514 			time = rule.t.split(":");
6515 			hour = parseInt(time[0], 10);
6516 			if (time.length > 1) {
6517 				minute = parseInt(time[1], 10);
6518 				if (time.length > 2) {
6519 					second = parseInt(time[2], 10);
6520 				}
6521 			}
6522 		}
6523 		//console.log("calculating rd of " + year + "/" + rule.m + "/" + day);
6524 		refDay = new GregRataDie({
6525 			year: year, 
6526 			month: rule.m, 
6527 			day: day, 
6528 			hour: hour, 
6529 			minute: minute, 
6530 			second: second
6531 		});
6532 	}
6533 	//console.log("refDay is " + JSON.stringify(refDay));
6534 	var d = refDay.getRataDie();
6535 	
6536 	switch (type) {
6537 		case 'l':
6538 		case '<':
6539 			//console.log("returning " + refDay.onOrBefore(rd, weekday));
6540 			d = refDay.onOrBefore(weekday); 
6541 			break;
6542 		case 'f':
6543 		case '>':
6544 			//console.log("returning " + refDay.onOrAfterRd(rd, weekday));
6545 			d = refDay.onOrAfter(weekday); 
6546 			break;
6547 	}
6548 	return d;
6549 };
6550 
6551 /**
6552  * @private
6553  */
6554 TimeZone.prototype._calcDSTSavings = function () {
6555 	var saveParts = this.getDSTSavings();
6556 	
6557 	/**
6558 	 * @private
6559 	 * @type {number} savings in minutes when DST is in effect 
6560 	 */
6561 	this.dstSavings = (Math.abs(saveParts.h || 0) * 60 + (saveParts.m || 0)) * MathUtils.signum(saveParts.h || 0);
6562 };
6563 
6564 /**
6565  * @private
6566  */
6567 TimeZone.prototype._getDSTStartRule = function (year) {
6568 	// TODO: update this when historic/future zones are supported
6569 	return this.zone.s;
6570 };
6571 
6572 /**
6573  * @private
6574  */
6575 TimeZone.prototype._getDSTEndRule = function (year) {
6576 	// TODO: update this when historic/future zones are supported
6577 	return this.zone.e;
6578 };
6579 
6580 /**
6581  * Returns whether or not the given date is in daylight saving time for the current
6582  * zone. Note that daylight savings time is observed for the summer. Because
6583  * the seasons are reversed, daylight savings time in the southern hemisphere usually
6584  * runs from the end of the year through New Years into the first few months of the
6585  * next year. This method will correctly calculate the start and end of DST for any
6586  * location.
6587  * 
6588  * @param {IDate=} date a date for which the info about daylight time is being sought,
6589  * or undefined to tell whether we are currently in daylight savings time
6590  * @param {boolean=} wallTime if true, then the given date is in wall time. If false or
6591  * undefined, it is in the usual UTC time.
6592  * @return {boolean} true if the given date is in DST for the current zone, and false
6593  * otherwise.
6594  */
6595 TimeZone.prototype.inDaylightTime = function (date, wallTime) {
6596 	var rd, startRd, endRd, year;
6597 
6598 	if (this.isLocal) {
6599 		// check if the dst property is defined -- the intrinsic JS Date object doesn't work so
6600 		// well if we are in the overlap time at the end of DST, so we have to work around that
6601 		// problem by adding in the savings ourselves
6602 		var offset = 0;
6603 		if (typeof(date.dst) !== 'undefined' && !date.dst) {
6604 			offset = this.dstSavings * 60000;
6605 		}
6606 		
6607 		var d = new Date(date ? date.getTimeExtended() + offset: undefined);
6608 		// the DST offset is always the one that is closest to positive infinity, no matter 
6609 		// if you are in the northern or southern hemisphere, east or west
6610 		var dst = Math.max(this.offsetJan1, this.offsetJun1);
6611 		return (-d.getTimezoneOffset() === dst);
6612 	}
6613 	
6614 	if (!date || !date.cal || date.cal.type !== "gregorian") {
6615 		// convert to Gregorian so that we can tell if it is in DST or not
6616 		var time = date && typeof(date.getTimeExtended) === 'function' ? date.getTimeExtended() : undefined;
6617 		rd = new GregRataDie({unixtime: time}).getRataDie();
6618 		year = new Date(time).getUTCFullYear();
6619 	} else {
6620 		rd = date.rd.getRataDie();
6621 		year = date.year;
6622 	}
6623 	// rd should be a Gregorian RD number now, in UTC
6624 	
6625 	// if we aren't using daylight time in this zone for the given year, then we are 
6626 	// not in daylight time
6627 	if (!this.useDaylightTime(year)) {
6628 		return false;
6629 	}
6630 	
6631 	// these calculate the start/end in local wall time
6632 	var startrule = this._getDSTStartRule(year);
6633 	var endrule = this._getDSTEndRule(year);
6634 	startRd = this._calcRuleStart(startrule, year);
6635 	endRd = this._calcRuleStart(endrule, year);
6636 	
6637 	if (wallTime) {
6638 		// rd is in wall time, so we have to make sure to skip the missing time
6639 		// at the start of DST when standard time ends and daylight time begins
6640 		startRd += this.dstSavings/1440;
6641 	} else {
6642 		// rd is in UTC, so we have to convert the start/end to UTC time so 
6643 		// that they can be compared directly to the UTC rd number of the date
6644 		
6645 		// when DST starts, time is standard time already, so we only have
6646 		// to subtract the offset to get to UTC and not worry about the DST savings
6647 		startRd -= this.offset/1440;  
6648 		
6649 		// when DST ends, time is in daylight time already, so we have to
6650 		// subtract the DST savings to get back to standard time, then the
6651 		// offset to get to UTC
6652 		endRd -= (this.offset + this.dstSavings)/1440;
6653 	}
6654 	
6655 	// In the northern hemisphere, the start comes first some time in spring (Feb-Apr), 
6656 	// then the end some time in the fall (Sept-Nov). In the southern
6657 	// hemisphere, it is the other way around because the seasons are reversed. Standard
6658 	// time is still in the winter, but the winter months are May-Aug, and daylight 
6659 	// savings time usually starts Aug-Oct of one year and runs through Mar-May of the 
6660 	// next year.
6661 	if (rd < endRd && endRd - rd <= this.dstSavings/1440 && typeof(date.dst) === 'boolean') {
6662 		// take care of the magic overlap time at the end of DST
6663 		return date.dst;
6664 	}
6665 	if (startRd < endRd) {
6666 		// northern hemisphere
6667 		return (rd >= startRd && rd < endRd) ? true : false;
6668 	} 
6669 	// southern hemisphere
6670 	return (rd >= startRd || rd < endRd) ? true : false;
6671 };
6672 
6673 /**
6674  * Returns true if this time zone switches to daylight savings time at some point
6675  * in the year, and false otherwise.
6676  * @param {number} year Whether or not the time zone uses daylight time in the given year. If
6677  * this parameter is not given, the current year is assumed.
6678  * @return {boolean} true if the time zone uses daylight savings time
6679  */
6680 TimeZone.prototype.useDaylightTime = function (year) {
6681 	
6682 	// this zone uses daylight savings time iff there is a rule defining when to start
6683 	// and when to stop the DST
6684 	return (this.isLocal && this.offsetJan1 !== this.offsetJun1) ||
6685 		(typeof(this.zone) !== 'undefined' && 
6686 		typeof(this.zone.s) !== 'undefined' && 
6687 		typeof(this.zone.e) !== 'undefined');
6688 };
6689 
6690 /**
6691  * Returns the ISO 3166 code of the country for which this time zone is defined.
6692  * @return {string} the ISO 3166 code of the country for this zone
6693  */
6694 TimeZone.prototype.getCountry = function () {
6695 	return this.zone.c;
6696 };
6697 
6698 
6699 
6700 /*< SearchUtils.js */
6701 /*
6702  * SearchUtils.js - Misc search utility routines
6703  * 
6704  * Copyright © 2013-2015, JEDLSoft
6705  *
6706  * Licensed under the Apache License, Version 2.0 (the "License");
6707  * you may not use this file except in compliance with the License.
6708  * You may obtain a copy of the License at
6709  *
6710  *     http://www.apache.org/licenses/LICENSE-2.0
6711  *
6712  * Unless required by applicable law or agreed to in writing, software
6713  * distributed under the License is distributed on an "AS IS" BASIS,
6714  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6715  *
6716  * See the License for the specific language governing permissions and
6717  * limitations under the License.
6718  */
6719 
6720 var SearchUtils = {};
6721 
6722 /**
6723  * Binary search a sorted array for a particular target value.
6724  * If the exact value is not found, it returns the index of the smallest 
6725  * entry that is greater than the given target value.<p> 
6726  * 
6727  * The comparator
6728  * parameter is a function that knows how to compare elements of the 
6729  * array and the target. The function should return a value greater than 0
6730  * if the array element is greater than the target, a value less than 0 if
6731  * the array element is less than the target, and 0 if the array element 
6732  * and the target are equivalent.<p>
6733  * 
6734  * If the comparator function is not specified, this function assumes
6735  * the array and the target are numeric values and should be compared 
6736  * as such.<p>
6737  * 
6738  * 
6739  * @static
6740  * @param {*} target element being sought 
6741  * @param {Array} arr the array being searched
6742  * @param {?function(*,*)=} comparator a comparator that is appropriate for comparing two entries
6743  * in the array  
6744  * @return the index of the array into which the value would fit if 
6745  * inserted, or -1 if given array is not an array or the target is not 
6746  * a number
6747  */
6748 SearchUtils.bsearch = function(target, arr, comparator) {
6749 	if (typeof(arr) === 'undefined' || !arr || typeof(target) === 'undefined') {
6750 		return -1;
6751 	}
6752 	
6753 	var high = arr.length - 1,
6754 		low = 0,
6755 		mid = 0,
6756 		value,
6757 		cmp = comparator || SearchUtils.bsearch.numbers;
6758 	
6759 	while (low <= high) {
6760 		mid = Math.floor((high+low)/2);
6761 		value = cmp(arr[mid], target);
6762 		if (value > 0) {
6763 			high = mid - 1;
6764 		} else if (value < 0) {
6765 			low = mid + 1;
6766 		} else {
6767 			return mid;
6768 		}
6769 	}
6770 	
6771 	return low;
6772 };
6773 
6774 /**
6775  * Returns whether or not the given element is greater than, less than,
6776  * or equal to the given target.<p>
6777  * 
6778  * @private
6779  * @static
6780  * @param {number} element the element being tested
6781  * @param {number} target the target being sought
6782  */
6783 SearchUtils.bsearch.numbers = function(element, target) {
6784 	return element - target;
6785 };
6786 
6787 /**
6788  * Do a bisection search of a function for a particular target value.<p> 
6789  * 
6790  * The function to search is a function that takes a numeric parameter, 
6791  * does calculations, and returns gives a numeric result. The 
6792  * function should should be smooth and not have any discontinuities 
6793  * between the low and high values of the parameter.
6794  *  
6795  * 
6796  * @static
6797  * @param {number} target value being sought
6798  * @param {number} low the lower bounds to start searching
6799  * @param {number} high the upper bounds to start searching
6800  * @param {number} precision minimum precision to support. Use 0 if you want to use the default.
6801  * @param {?function(number)=} func function to search 
6802  * @return an approximation of the input value to the function that gives the desired
6803  * target output value, correct to within the error range of Javascript floating point 
6804  * arithmetic, or NaN if there was some error
6805  */
6806 SearchUtils.bisectionSearch = function(target, low, high, precision, func) {
6807 	if (typeof(target) !== 'number' || 
6808 			typeof(low) !== 'number' || 
6809 			typeof(high) !== 'number' || 
6810 			typeof(func) !== 'function') {
6811 		return NaN;
6812 	}
6813 	
6814 	var mid = 0,
6815 		value,
6816 		pre = precision > 0 ? precision : 1e-13;
6817 	
6818 	do {
6819 		mid = (high+low)/2;
6820 		value = func(mid);
6821 		if (value > target) {
6822 			high = mid;
6823 		} else if (value < target) {
6824 			low = mid;
6825 		}
6826 	} while (high - low > pre);
6827 	
6828 	return mid;
6829 };
6830 
6831 
6832 
6833 /*< GregorianDate.js */
6834 /*
6835  * GregorianDate.js - Represent a date in the Gregorian calendar
6836  * 
6837  * Copyright © 2012-2015, JEDLSoft
6838  *
6839  * Licensed under the Apache License, Version 2.0 (the "License");
6840  * you may not use this file except in compliance with the License.
6841  * You may obtain a copy of the License at
6842  *
6843  *     http://www.apache.org/licenses/LICENSE-2.0
6844  *
6845  * Unless required by applicable law or agreed to in writing, software
6846  * distributed under the License is distributed on an "AS IS" BASIS,
6847  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6848  *
6849  * See the License for the specific language governing permissions and
6850  * limitations under the License.
6851  */
6852 
6853 /* !depends 
6854 ilib.js
6855 IDate.js 
6856 GregorianCal.js 
6857 SearchUtils.js
6858 MathUtils.js
6859 Locale.js
6860 LocaleInfo.js 
6861 JulianDay.js
6862 GregRataDie.js
6863 TimeZone.js
6864 */
6865 
6866 
6867 
6868 
6869 /**
6870  * @class
6871  * Construct a new Gregorian date object. The constructor parameters can 
6872  * contain any of the following properties:
6873  * 
6874  * <ul>
6875  * <li><i>unixtime<i> - sets the time of this instance according to the given 
6876  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
6877  * 
6878  * <li><i>julianday</i> - sets the time of this instance according to the given
6879  * Julian Day instance or the Julian Day given as a float
6880  * 
6881  * <li><i>year</i> - any integer, including 0
6882  * 
6883  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
6884  * 
6885  * <li><i>day</i> - 1 to 31
6886  * 
6887  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
6888  * is always done with an unambiguous 24 hour representation
6889  * 
6890  * <li><i>minute</i> - 0 to 59
6891  * 
6892  * <li><i>second</i> - 0 to 59
6893  * 
6894  * <li><i>millisecond</i> - 0 to 999
6895  * 
6896  * <li><i>dst</i> - boolean used to specify whether the given time components are
6897  * intended to be in daylight time or not. This is only used in the overlap
6898  * time when transitioning from DST to standard time, and the time components are 
6899  * ambiguous. Otherwise at all other times of the year, this flag is ignored.
6900  * If you specify the date using unix time (UTC) or a julian day, then the time is
6901  * already unambiguous and this flag does not need to be specified.
6902  * <p>
6903  * For example, in the US, the transition out of daylight savings time 
6904  * in 2014 happens at Nov 2, 2014 2:00am Daylight Time, when the time falls 
6905  * back to Nov 2, 2014 1:00am Standard Time. If you give a date/time components as 
6906  * "Nov 2, 2014 1:30am", then there are two 1:30am times in that day, and you would 
6907  * have to give the standard flag to indicate which of those two you mean. 
6908  * (dst=true means daylight time, dst=false means standard time).   
6909  * 
6910  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string 
6911  * of this gregorian date. The date/time is kept in the local time. The time zone
6912  * is used later if this date is formatted according to a different time zone and
6913  * the difference has to be calculated, or when the date format has a time zone
6914  * component in it.
6915  * 
6916  * <li><i>locale</i> - locale for this gregorian date. If the time zone is not 
6917  * given, it can be inferred from this locale. For locales that span multiple
6918  * time zones, the one with the largest population is chosen as the one that 
6919  * represents the locale.
6920  * 
6921  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
6922  * </ul>
6923  *
6924  * If the constructor is called with another Gregorian date instance instead of
6925  * a parameter block, the other instance acts as a parameter block and its
6926  * settings are copied into the current instance.<p>
6927  * 
6928  * If the constructor is called with no arguments at all or if none of the 
6929  * properties listed above 
6930  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
6931  * components are 
6932  * filled in with the current date at the time of instantiation. Note that if
6933  * you do not give the time zone when defaulting to the current time and the 
6934  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
6935  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
6936  * Mean Time").<p>
6937  * 
6938  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
6939  * specified in the params, it is assumed that they have the smallest possible
6940  * value in the range for the property (zero or one).<p>
6941  * 
6942  * 
6943  * @constructor
6944  * @extends IDate
6945  * @param {Object=} params parameters that govern the settings and behaviour of this Gregorian date
6946  */
6947 var GregorianDate = function(params) {
6948 	this.cal = new GregorianCal();
6949 	this.timezone = "local";
6950 
6951 	if (params) {
6952 		if (typeof(params.noinstance) === 'boolean' && params.noinstance) {
6953 			// for doing inheritance, so don't need to fill in the data. The inheriting class only wants the methods.
6954 			return;
6955 		}
6956 		if (params.locale) {
6957 			this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
6958 			var li = new LocaleInfo(this.locale);
6959 			this.timezone = li.getTimeZone(); 
6960 		}
6961 		if (params.timezone) {
6962 			this.timezone = params.timezone.toString();
6963 		}
6964 		
6965 		if (params.year || params.month || params.day || params.hour ||
6966 				params.minute || params.second || params.millisecond ) {
6967 			this.year = parseInt(params.year, 10) || 0;
6968 			this.month = parseInt(params.month, 10) || 1;
6969 			this.day = parseInt(params.day, 10) || 1;
6970 			this.hour = parseInt(params.hour, 10) || 0;
6971 			this.minute = parseInt(params.minute, 10) || 0;
6972 			this.second = parseInt(params.second, 10) || 0;
6973 			this.millisecond = parseInt(params.millisecond, 10) || 0;
6974 			if (typeof(params.dst) === 'boolean') {
6975 				this.dst = params.dst;
6976 			}
6977 			this.rd = this.newRd(params);
6978 			
6979 			// add the time zone offset to the rd to convert to UTC
6980 			this.offset = 0;
6981 			if (this.timezone === "local" && typeof(params.dst) === 'undefined') {
6982 				// if dst is defined, the intrinsic Date object has no way of specifying which version of a time you mean
6983 				// in the overlap time at the end of DST. Do you mean the daylight 1:30am or the standard 1:30am? In this
6984 				// case, use the ilib calculations below, which can distinguish between the two properly
6985 				var d = new Date(this.year, this.month-1, this.day, this.hour, this.minute, this.second, this.millisecond);
6986 				this.offset = -d.getTimezoneOffset() / 1440;
6987 			} else {
6988 				if (!this.tz) {
6989 					this.tz = new TimeZone({id: this.timezone});
6990 				}
6991 				// getOffsetMillis requires that this.year, this.rd, and this.dst 
6992 				// are set in order to figure out which time zone rules apply and 
6993 				// what the offset is at that point in the year
6994 				this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
6995 			}
6996 			if (this.offset !== 0) {
6997 				this.rd = this.newRd({
6998 					rd: this.rd.getRataDie() - this.offset
6999 				});
7000 			}
7001 		}
7002 	} 
7003 
7004 	if (!this.rd) {
7005 		this.rd = this.newRd(params);
7006 		this._calcDateComponents();
7007 	}
7008 };
7009 
7010 GregorianDate.prototype = new IDate({noinstance: true});
7011 GregorianDate.prototype.parent = IDate;
7012 GregorianDate.prototype.constructor = GregorianDate;
7013 
7014 /**
7015  * Return a new RD for this date type using the given params.
7016  * @private
7017  * @param {Object=} params the parameters used to create this rata die instance
7018  * @returns {RataDie} the new RD instance for the given params
7019  */
7020 GregorianDate.prototype.newRd = function (params) {
7021 	return new GregRataDie(params);
7022 };
7023 
7024 /**
7025  * Calculates the Gregorian year for a given rd number.
7026  * @private
7027  * @static
7028  */
7029 GregorianDate._calcYear = function(rd) {
7030 	var days400,
7031 		days100,
7032 		days4,
7033 		years400,
7034 		years100,
7035 		years4,
7036 		years1,
7037 		year;
7038 
7039 	years400 = Math.floor((rd - 1) / 146097);
7040 	days400 = MathUtils.mod((rd - 1), 146097);
7041 	years100 = Math.floor(days400 / 36524);
7042 	days100 = MathUtils.mod(days400, 36524);
7043 	years4 = Math.floor(days100 / 1461);
7044 	days4 = MathUtils.mod(days100, 1461);
7045 	years1 = Math.floor(days4 / 365);
7046 	
7047 	year = 400 * years400 + 100 * years100 + 4 * years4 + years1;
7048 	if (years100 !== 4 && years1 !== 4) {
7049 		year++;
7050 	}
7051 	return year;
7052 };
7053 
7054 /**
7055  * @private
7056  */
7057 GregorianDate.prototype._calcYear = function(rd) {
7058 	return GregorianDate._calcYear(rd);
7059 };
7060 
7061 /**
7062  * Calculate the date components for the current time zone
7063  * @private
7064  */
7065 GregorianDate.prototype._calcDateComponents = function () {
7066 	if (this.timezone === "local" && this.rd.getRataDie() >= -99280837 && this.rd.getRataDie() <= 100719163) {
7067 		// console.log("using js Date to calculate offset");
7068 		// use the intrinsic JS Date object to do the tz conversion for us, which 
7069 		// guarantees that it follows the system tz database settings 
7070 		var d = new Date(this.rd.getTimeExtended());
7071 	
7072 		/**
7073 		 * Year in the Gregorian calendar.
7074 		 * @type number
7075 		 */
7076 		this.year = d.getFullYear();
7077 		
7078 		/**
7079 		 * The month number, ranging from 1 (January) to 12 (December).
7080 		 * @type number
7081 		 */
7082 		this.month = d.getMonth()+1;
7083 		
7084 		/**
7085 		 * The day of the month. This ranges from 1 to 31.
7086 		 * @type number
7087 		 */
7088 		this.day = d.getDate();
7089 		
7090 		/**
7091 		 * The hour of the day. This can be a number from 0 to 23, as times are
7092 		 * stored unambiguously in the 24-hour clock.
7093 		 * @type number
7094 		 */
7095 		this.hour = d.getHours();
7096 		
7097 		/**
7098 		 * The minute of the hours. Ranges from 0 to 59.
7099 		 * @type number
7100 		 */
7101 		this.minute = d.getMinutes();
7102 		
7103 		/**
7104 		 * The second of the minute. Ranges from 0 to 59.
7105 		 * @type number
7106 		 */
7107 		this.second = d.getSeconds();
7108 		
7109 		/**
7110 		 * The millisecond of the second. Ranges from 0 to 999.
7111 		 * @type number
7112 		 */
7113 		this.millisecond = d.getMilliseconds();
7114 		
7115 		this.offset = -d.getTimezoneOffset() / 1440;
7116 	} else {
7117 		// console.log("using ilib to calculate offset. tz is " + this.timezone);
7118 		// console.log("GregDate._calcDateComponents: date is " + JSON.stringify(this) + " parent is " + JSON.stringify(this.parent) + " and parent.parent is " + JSON.stringify(this.parent.parent));
7119 		if (typeof(this.offset) === "undefined") {
7120 			// console.log("calculating offset");
7121 			this.year = this._calcYear(this.rd.getRataDie());
7122 			
7123 			// now offset the RD by the time zone, then recalculate in case we were 
7124 			// near the year boundary
7125 			if (!this.tz) {
7126 				this.tz = new TimeZone({id: this.timezone});
7127 			}
7128 			this.offset = this.tz.getOffsetMillis(this) / 86400000;
7129 		// } else {
7130 			// console.log("offset is already defined somehow. type is " + typeof(this.offset));
7131 			// console.trace("Stack is this one");
7132 		}
7133 		// console.log("offset is " + this.offset);
7134 		var rd = this.rd.getRataDie();
7135 		if (this.offset !== 0) {
7136 			rd += this.offset;
7137 		}
7138 		this.year = this._calcYear(rd);
7139 		
7140 		var yearStartRd = this.newRd({
7141 			year: this.year,
7142 			month: 1,
7143 			day: 1,
7144 			cal: this.cal
7145 		});
7146 		
7147 		// remainder is days into the year
7148 		var remainder = rd - yearStartRd.getRataDie() + 1;
7149 		
7150 		var cumulative = GregorianCal.prototype.isLeapYear.call(this.cal, this.year) ? 
7151 			GregRataDie.cumMonthLengthsLeap : 
7152 			GregRataDie.cumMonthLengths; 
7153 		
7154 		this.month = SearchUtils.bsearch(Math.floor(remainder), cumulative);
7155 		remainder = remainder - cumulative[this.month-1];
7156 		
7157 		this.day = Math.floor(remainder);
7158 		remainder -= this.day;
7159 		// now convert to milliseconds for the rest of the calculation
7160 		remainder = Math.round(remainder * 86400000);
7161 		
7162 		this.hour = Math.floor(remainder/3600000);
7163 		remainder -= this.hour * 3600000;
7164 		
7165 		this.minute = Math.floor(remainder/60000);
7166 		remainder -= this.minute * 60000;
7167 		
7168 		this.second = Math.floor(remainder/1000);
7169 		remainder -= this.second * 1000;
7170 		
7171 		this.millisecond = Math.floor(remainder);
7172 	}
7173 };
7174 
7175 /**
7176  * Return the day of the week of this date. The day of the week is encoded
7177  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
7178  * 
7179  * @return {number} the day of the week
7180  */
7181 GregorianDate.prototype.getDayOfWeek = function() {
7182 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
7183 	return MathUtils.mod(rd, 7);
7184 };
7185 
7186 /**
7187  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 
7188  * 365, regardless of months or weeks, etc. That is, January 1st is day 1, and 
7189  * December 31st is 365 in regular years, or 366 in leap years.
7190  * @return {number} the ordinal day of the year
7191  */
7192 GregorianDate.prototype.getDayOfYear = function() {
7193 	var cumulativeMap = this.cal.isLeapYear(this.year) ? 
7194 		GregRataDie.cumMonthLengthsLeap : 
7195 		GregRataDie.cumMonthLengths; 
7196 		
7197 	return cumulativeMap[this.month-1] + this.day;
7198 };
7199 
7200 /**
7201  * Return the era for this date as a number. The value for the era for Gregorian 
7202  * calendars is -1 for "before the common era" (BCE) and 1 for "the common era" (CE). 
7203  * BCE dates are any date before Jan 1, 1 CE. In the proleptic Gregorian calendar, 
7204  * there is a year 0, so any years that are negative or zero are BCE. In the Julian
7205  * calendar, there is no year 0. Instead, the calendar goes straight from year -1 to 
7206  * 1.
7207  * @return {number} 1 if this date is in the common era, -1 if it is before the 
7208  * common era 
7209  */
7210 GregorianDate.prototype.getEra = function() {
7211 	return (this.year < 1) ? -1 : 1;
7212 };
7213 
7214 /**
7215  * Return the name of the calendar that governs this date.
7216  * 
7217  * @return {string} a string giving the name of the calendar
7218  */
7219 GregorianDate.prototype.getCalendar = function() {
7220 	return "gregorian";
7221 };
7222 
7223 // register with the factory method
7224 IDate._constructors["gregorian"] = GregorianDate;
7225 
7226 
7227 /*< DateFactory.js */
7228 /*
7229  * DateFactory.js - Factory class to create the right subclasses of a date for any 
7230  * calendar or locale.
7231  * 
7232  * Copyright © 2012-2015, JEDLSoft
7233  *
7234  * Licensed under the Apache License, Version 2.0 (the "License");
7235  * you may not use this file except in compliance with the License.
7236  * You may obtain a copy of the License at
7237  *
7238  *     http://www.apache.org/licenses/LICENSE-2.0
7239  *
7240  * Unless required by applicable law or agreed to in writing, software
7241  * distributed under the License is distributed on an "AS IS" BASIS,
7242  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7243  *
7244  * See the License for the specific language governing permissions and
7245  * limitations under the License.
7246  */
7247 
7248 /* !depends ilib.js Locale.js LocaleInfo.js JulianDay.js JSUtils.js CalendarFactory.js IDate.js GregorianDate.js*/
7249 
7250 
7251 
7252 // Statically depend on these even though we don't use them
7253 // to guarantee they are loaded into the cache already.
7254 
7255 /**
7256  * Factory method to create a new instance of a date subclass.<p>
7257  * 
7258  * The options parameter can be an object that contains the following
7259  * properties:
7260  * 
7261  * <ul>
7262  * <li><i>type</i> - specify the type/calendar of the date desired. The
7263  * list of valid values changes depending on which calendars are 
7264  * defined. When assembling your iliball.js, include those date type 
7265  * you wish to use in your program or web page, and they will register 
7266  * themselves with this factory method. The "gregorian",
7267  * and "julian" calendars are all included by default, as they are the
7268  * standard calendars for much of the world. If not specified, the type
7269  * of the date returned is the one that is appropriate for the locale.
7270  * This property may also be given as "calendar" instead of "type".
7271  * 
7272  * <li><i>onLoad</i> - a callback function to call when the date object is fully 
7273  * loaded. When the onLoad option is given, the date factory will attempt to
7274  * load any missing locale data using the ilib loader callback.
7275  * When the constructor is done (even if the data is already preassembled), the 
7276  * onLoad function is called with the current instance as a parameter, so this
7277  * callback can be used with preassembled or dynamic loading or a mix of the two.
7278  * 
7279  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
7280  * asynchronously. If this option is given as "false", then the "onLoad"
7281  * callback must be given, as the instance returned from this constructor will
7282  * not be usable for a while.
7283  *  
7284  * <li><i>loadParams</i> - an object containing parameters to pass to the 
7285  * loader callback function when locale data is missing. The parameters are not
7286  * interpretted or modified in any way. They are simply passed along. The object 
7287  * may contain any property/value pairs as long as the calling code is in
7288  * agreement with the loader callback function as to what those parameters mean.
7289  * </ul>
7290  * 
7291  * The options object is also passed down to the date constructor, and 
7292  * thus can contain the the properties as the date object being instantiated.
7293  * See the documentation for {@link GregorianDate}, and other
7294  * subclasses for more details on other parameter that may be passed in.<p>
7295  * 
7296  * Please note that if you do not give the type parameter, this factory
7297  * method will create a date object that is appropriate for the calendar
7298  * that is most commonly used in the specified or current ilib locale. 
7299  * For example, in Thailand, the most common calendar is the Thai solar 
7300  * calendar. If the current locale is "th-TH" (Thai for Thailand) and you 
7301  * use this factory method to construct a new date without specifying the
7302  * type, it will automatically give you back an instance of 
7303  * {@link ThaiSolarDate}. This is convenient because you do not 
7304  * need to know which locales use which types of dates. In fact, you 
7305  * should always use this factory method to make new date instances unless
7306  * you know that you specifically need a date in a particular calendar.<p>
7307  * 
7308  * Also note that when you pass in the date components such as year, month,
7309  * day, etc., these components should be appropriate for the given date
7310  * being instantiated. That is, in our Thai example in the previous
7311  * paragraph, the year and such should be given as a Thai solar year, not
7312  * the Gregorian year that you get from the Javascript Date class. In
7313  * order to initialize a date instance when you don't know what subclass
7314  * will be instantiated for the locale, use a parameter such as "unixtime" 
7315  * or "julianday" which are unambiguous and based on UTC time, instead of
7316  * the year/month/date date components. The date components for that UTC 
7317  * time will be calculated and the time zone offset will be automatically 
7318  * factored in.
7319  * 
7320  * @static
7321  * @param {Object=} options options controlling the construction of this instance, or
7322  * undefined to use the default options
7323  * @return {IDate} an instance of a calendar object of the appropriate type 
7324  */
7325 var DateFactory = function(options) {
7326 	var locale,
7327 		type,
7328 		cons,
7329 		sync = true,
7330 		obj;
7331 
7332 	if (options) {
7333 		if (options.locale) {
7334 			locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
7335 		}
7336 		
7337 		type = options.type || options.calendar;
7338 		
7339 		if (typeof(options.sync) === 'boolean') {
7340 			sync = options.sync;
7341 		}
7342 	}
7343 	
7344 	if (!locale) {
7345 		locale = new Locale();	// default locale
7346 	}
7347 
7348 	if (!type) {
7349 		new LocaleInfo(locale, {
7350 			sync: sync,
7351 			loadParams: options && options.loadParams,
7352 			onLoad: ilib.bind(this, function(info) {
7353 				type = info.getCalendar();
7354 				
7355 				obj = DateFactory._init(type, options);
7356 				
7357 				if (options && typeof(options.onLoad) === 'function') {
7358 					options.onLoad(obj);
7359 				}
7360 			})
7361 		});
7362 	} else {
7363 		obj = DateFactory._init(type, options);
7364 	}
7365 	
7366 	return obj
7367 };
7368 
7369 /**
7370  * Map calendar names to classes to initialize in the dynamic code model.
7371  * TODO: Need to figure out some way that this doesn't have to be updated by hand.
7372  * @private
7373  */
7374 DateFactory._dynMap = {
7375 	"coptic":       "Coptic",
7376 	"ethiopic":     "Ethiopic",
7377 	"gregorian":    "Gregorian",
7378 	"han":          "Han",
7379 	"hebrew":       "Hebrew",
7380 	"islamic":      "Islamic",
7381 	"julian":       "Julian",
7382 	"persian":      "Persian",
7383 	"persian-algo": "PersianAlgo",
7384 	"thaisolar":    "ThaiSolar"
7385 };
7386 
7387 /**
7388  * Dynamically load the code for a calendar and calendar class if necessary.
7389  * @protected
7390  */
7391 DateFactory._dynLoadDate = function (name) {
7392 	if (!IDate._constructors[name]) {
7393 		var entry = DateFactory._dynMap[name];
7394 		if (entry) {
7395 			IDate._constructors[name] = require("./" + entry + "Date.js");
7396 		}
7397 	}
7398 	return IDate._constructors[name];
7399 };
7400 
7401 /** 
7402  * @protected
7403  * @static 
7404  */
7405 DateFactory._init = function(type, options) {
7406 	var cons;
7407 	
7408 	if (ilib.isDynCode()) {
7409 		DateFactory._dynLoadDate(type);
7410 		CalendarFactory._dynLoadCalendar(type);
7411 	}
7412 	
7413 	cons = IDate._constructors[type];
7414 	
7415 	// pass the same options through to the constructor so the subclass
7416 	// has the ability to do something with if it needs to
7417 	return cons && new cons(options);
7418 };
7419 
7420 /**
7421  * Convert JavaScript Date objects and other types into native Dates. This accepts any
7422  * string or number that can be translated by the JavaScript Date class,
7423  * (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse)
7424  * any JavaScript Date classed object, any IDate subclass, an JulianDay object, an object
7425  * containing the normal options to initialize an IDate instance, or null (will 
7426  * return null or undefined if input is null or undefined). Normal output is 
7427  * a standard native subclass of the IDate object as appropriate for the locale.
7428  * 
7429  * @static
7430  * @protected
7431  * @param {IDate|Object|JulianDay|Date|string|number=} inDate The input date object, string or Number.
7432  * @param {IString|string=} timezone timezone to use if a new date object is created
7433  * @param {Locale|string=} locale locale to use when constructing an IDate
7434  * @return {IDate|null|undefined} an IDate subclass equivalent to the given inDate
7435  */
7436 DateFactory._dateToIlib = function(inDate, timezone, locale) {
7437 	if (typeof(inDate) === 'undefined' || inDate === null) {
7438 		return inDate;
7439 	}
7440 	if (inDate instanceof IDate) {
7441 		return inDate;
7442 	}
7443 	if (typeof(inDate) === 'number') {
7444 		return DateFactory({
7445 			unixtime: inDate,
7446 			timezone: timezone,
7447 			locale: locale
7448 		});
7449 	}
7450 	if (typeof(inDate) === 'string') {
7451 		inDate = new Date(inDate);
7452 	}
7453 	if (JSUtils.isDate(inDate)) {
7454 		return DateFactory({
7455 			unixtime: inDate.getTime(),
7456 			timezone: timezone,
7457 			locale: locale
7458 		});
7459 	}
7460 	if (inDate instanceof JulianDay) {
7461 		return DateFactory({
7462 			jd: inDate,
7463 			timezone: timezone,
7464 			locale: locale
7465 		});
7466 	}
7467 	if (typeof(inDate) === 'object') {
7468 		return DateFactory(inDate);
7469 	}
7470 	return DateFactory({
7471 		unixtime: inDate.getTime(),
7472 		timezone: timezone,
7473 		locale: locale
7474 	});
7475 };
7476 
7477 
7478 /*< ResBundle.js */
7479 /*
7480  * ResBundle.js - Resource bundle definition
7481  * 
7482  * Copyright © 2012-2016, JEDLSoft
7483  *
7484  * Licensed under the Apache License, Version 2.0 (the "License");
7485  * you may not use this file except in compliance with the License.
7486  * You may obtain a copy of the License at
7487  *
7488  *     http://www.apache.org/licenses/LICENSE-2.0
7489  *
7490  * Unless required by applicable law or agreed to in writing, software
7491  * distributed under the License is distributed on an "AS IS" BASIS,
7492  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7493  *
7494  * See the License for the specific language governing permissions and
7495  * limitations under the License.
7496  */
7497 
7498 // !depends ilib.js Locale.js LocaleInfo.js IString.js Utils.js JSUtils.js
7499 
7500 // !data pseudomap
7501 
7502 
7503 
7504 
7505 /**
7506  * @class
7507  * Create a new resource bundle instance. The resource bundle loads strings
7508  * appropriate for a particular locale and provides them via the getString 
7509  * method.<p>
7510  * 
7511  * The options object may contain any (or none) of the following properties:
7512  * 
7513  * <ul>
7514  * <li><i>locale</i> - The locale of the strings to load. If not specified, the default
7515  * locale is the the default for the web page or app in which the bundle is 
7516  * being loaded.
7517  * 
7518  * <li><i>name</i> - Base name of the resource bundle to load. If not specified the default
7519  * base name is "resources".
7520  * 
7521  * <li><i>type</i> - Name the type of strings this bundle contains. Valid values are 
7522  * "xml", "html", "text", "c", or "raw". The default is "text". If the type is "xml" or "html",
7523  * then XML/HTML entities and tags are not pseudo-translated. During a real translation, 
7524  * HTML character entities are translated to their corresponding characters in a source
7525  * string before looking that string up in the translations. Also, the characters "<", ">",
7526  * and "&" are converted to entities again in the output, but characters are left as they
7527  * are. If the type is "xml", "html", or "text" types, then the replacement parameter names
7528  * are not pseudo-translated as well so that the output can be used for formatting with 
7529  * the IString class. If the type is "c" then all C language style printf replacement
7530  * parameters (eg. "%s" and "%d") are skipped automatically. If the type is raw, all characters 
7531  * are pseudo-translated, including replacement parameters as well as XML/HTML tags and entities.
7532  * 
7533  * <li><i>lengthen</i> - when pseudo-translating the string, tell whether or not to 
7534  * automatically lengthen the string to simulate "long" languages such as German
7535  * or French. This is a boolean value. Default is false.
7536  * 
7537  * <li><i>missing</i> - what to do when a resource is missing. The choices are:
7538  * <ul>
7539  *   <li><i>source</i> - return the source string unchanged
7540  *   <li><i>pseudo</i> - return the pseudo-translated source string, translated to the
7541  *   script of the locale if the mapping is available, or just the default Latin 
7542  *   pseudo-translation if not
7543  *   <li><i>empty</i> - return the empty string 
7544  * </ul>
7545  * The default behaviour is the same as before, which is to return the source string
7546  * unchanged.
7547  * 
7548  * <li><i>onLoad</i> - a callback function to call when the resources are fully 
7549  * loaded. When the onLoad option is given, this class will attempt to
7550  * load any missing locale data using the ilib loader callback.
7551  * When the constructor is done (even if the data is already preassembled), the 
7552  * onLoad function is called with the current instance as a parameter, so this
7553  * callback can be used with preassembled or dynamic loading or a mix of the two. 
7554  * 
7555  * <li>sync - tell whether to load any missing locale data synchronously or 
7556  * asynchronously. If this option is given as "false", then the "onLoad"
7557  * callback must be given, as the instance returned from this constructor will
7558  * not be usable for a while. 
7559  *
7560  * <li><i>loadParams</i> - an object containing parameters to pass to the 
7561  * loader callback function when locale data is missing. The parameters are not
7562  * interpretted or modified in any way. They are simply passed along. The object 
7563  * may contain any property/value pairs as long as the calling code is in
7564  * agreement with the loader callback function as to what those parameters mean.
7565  * </ul>
7566  * 
7567  * The locale option may be given as a locale spec string or as an 
7568  * Locale object. If the locale option is not specified, then strings for
7569  * the default locale will be loaded.<p> 
7570  * 
7571  * The name option can be used to put groups of strings together in a
7572  * single bundle. The strings will then appear together in a JS object in
7573  * a JS file that can be included before the ilib.<p>
7574  * 
7575  * A resource bundle with a particular name is actually a set of bundles
7576  * that are each specific to a language, a language plus a region, etc. 
7577  * All bundles with the same base name should
7578  * contain the same set of source strings, but with different translations for 
7579  * the given locale. The user of the bundle does not need to be aware of 
7580  * the locale of the bundle, as long as it contains values for the strings 
7581  * it needs.<p>
7582  * 
7583  * Strings in bundles for a particular locale are inherited from parent bundles
7584  * that are more generic. In general, the hierarchy is as follows (from 
7585  * least locale-specific to most locale-specific):
7586  * 
7587  * <ol>
7588  * <li> language
7589  * <li> region
7590  * <li> language_script
7591  * <li> language_region
7592  * <li> region_variant
7593  * <li> language_script_region
7594  * <li> language_region_variant
7595  * <li> language_script_region_variant
7596  * </ol>
7597  * 
7598  * That is, if the translation for a string does not exist in the current
7599  * locale, the more-generic parent locale is searched for the string. In the
7600  * worst case scenario, the string is not found in the base locale's strings. 
7601  * In this case, the missing option guides this class on what to do. If
7602  * the missing option is "source", then the original source is returned as 
7603  * the translation. If it is "empty", the empty string is returned. If it
7604  * is "pseudo", then the pseudo-translated string that is appropriate for
7605  * the default script of the locale is returned.<p> 
7606  * 
7607  * This allows developers to create code with new or changed strings in it and check in that
7608  * code without waiting for the translations to be done first. The translated
7609  * version of the app or web site will still function properly, but will show 
7610  * a spurious untranslated string here and there until the translations are 
7611  * done and also checked in.<p>   
7612  *  
7613  * The base is whatever language your developers use to code in. For
7614  * a German web site, strings in the source code may be written in German 
7615  * for example. Often this base is English, as many web sites are coded in
7616  * English, but that is not required.<p>
7617  * 
7618  * The strings can be extracted with the ilib localization tool (which will be
7619  * shipped at some future time.) Once the strings
7620  * have been translated, the set of translated files can be generated with the
7621  * same tool. The output from the tool can be used as input to the ResBundle
7622  * object. It is up to the web page or app to make sure the JS file that defines
7623  * the bundle is included before creating the ResBundle instance.<p>
7624  * 
7625  * A special locale "zxx-XX" is used as the pseudo-translation locale because
7626  * zxx means "no linguistic information" in the ISO 639 standard, and the region 
7627  * code XX is defined to be user-defined in the ISO 3166 standard. 
7628  * Pseudo-translation is a locale where the translations are generated on
7629  * the fly based on the contents of the source string. Characters in the source 
7630  * string are replaced with other characters and returned. 
7631  * 
7632  * Example. If the source string is:
7633  * 
7634  * <pre>
7635  * "This is a string"
7636  * </pre>
7637  * 
7638  * then the pseudo-translated version might look something like this: 
7639  * 
7640  * <pre>
7641  * "Ţħïş ïş á şţřïñĝ"
7642  * </pre>
7643  * <p>
7644  * 
7645  * Pseudo-translation can be used to test that your app or web site is translatable
7646  * before an actual translation has happened. These bugs can then be fixed 
7647  * before the translation starts, avoiding an explosion of bugs later when
7648  * each language's tester registers the same bug complaining that the same 
7649  * string is not translated. When pseudo-localizing with
7650  * the Latin script, this allows the strings to be readable in the UI in the 
7651  * source language (if somewhat funky-looking), 
7652  * so that a tester can easily verify that the string is properly externalized 
7653  * and loaded from a resource bundle without the need to be able to read a
7654  * foreign language.<p> 
7655  * 
7656  * If one of a list of script tags is given in the pseudo-locale specifier, then the
7657  * pseudo-localization can map characters to very rough transliterations of
7658  * characters in the given script. For example, zxx-Hebr-XX maps strings to
7659  * Hebrew characters, which can be used to test your UI in a right-to-left
7660  * language to catch bidi bugs before a translation is done. Currently, the
7661  * list of target scripts includes Hebrew (Hebr), Chinese Simplified Han (Hans),
7662  * and Cyrillic (Cyrl) with more to be added later. If no script is explicitly
7663  * specified in the locale spec, or if the script is not supported,
7664  * then the default mapping maps Latin base characters to accented versions of
7665  * those Latin characters as in the example above.
7666  *  
7667  * When the "lengthen" property is set to true in the options, the 
7668  * pseudotranslation code will add digits to the end of the string to simulate
7669  * the lengthening that occurs when translating to other languages. The above 
7670  * example will come out like this:
7671  * 
7672  * <pre>
7673  * "Ţħïş ïş á şţřïñĝ76543210"
7674  * </pre>
7675  * 
7676  * The string is lengthened according to the length of the source string. If
7677  * the source string is less than 20 characters long, the string is lengthened 
7678  * by 50%. If the source string is 20-40 
7679  * characters long, the string is lengthened by 33%. If te string is greater
7680  * than 40 characters long, the string is lengthened by 20%.<p>
7681  * 
7682  * The pseudotranslation always ends a string with the digit "0". If you do
7683  * not see the digit "0" in the UI for your app, you know that truncation
7684  * has occurred, and the number you see at the end of the string tells you 
7685  * how many characters were truncated.<p>
7686  * 
7687  * 
7688  * @constructor
7689  * @param {?Object} options Options controlling how the bundle is created
7690  */
7691 var ResBundle = function (options) {
7692 	var lookupLocale, spec;
7693 	
7694 	this.locale = new Locale();	// use the default locale
7695 	this.baseName = "strings";
7696 	this.type = "text";
7697 	this.loadParams = {};
7698 	this.missing = "source";
7699 	this.sync = true;
7700 	
7701 	if (options) {
7702 		if (options.locale) {
7703 			this.locale = (typeof(options.locale) === 'string') ? 
7704 					new Locale(options.locale) :
7705 					options.locale;
7706 		}
7707 		if (options.name) {
7708 			this.baseName = options.name;
7709 		}
7710 		if (options.type) {
7711 			this.type = options.type;
7712 		}
7713 		this.lengthen = options.lengthen || false;
7714 		
7715 		if (typeof(options.sync) !== 'undefined') {
7716 			this.sync = (options.sync == true);
7717 		}
7718 		
7719 		if (typeof(options.loadParams) !== 'undefined') {
7720 			this.loadParams = options.loadParams;
7721 		}
7722 		if (typeof(options.missing) !== 'undefined') {
7723 			if (options.missing === "pseudo" || options.missing === "empty") {
7724 				this.missing = options.missing;
7725 			}
7726 		}
7727 	} else {
7728 		options = {};
7729 	}
7730 	
7731 	this.map = {};
7732 
7733 	if (!ResBundle[this.baseName]) {
7734 		ResBundle[this.baseName] = {};
7735 	}
7736 
7737 	lookupLocale = this.locale.isPseudo() ? new Locale("en-US") : this.locale;
7738 
7739 	Utils.loadData({
7740 		object: ResBundle[this.baseName], 
7741 		locale: lookupLocale, 
7742 		name: this.baseName + ".json", 
7743 		sync: this.sync, 
7744 		loadParams: this.loadParams, 
7745 		callback: ilib.bind(this, function (map) {
7746 			if (!map) {
7747 				map = ilib.data[this.baseName] || {};
7748 				spec = lookupLocale.getSpec().replace(/-/g, '_');
7749 				ResBundle[this.baseName].cache[spec] = map;
7750 			}
7751 			this.map = map;
7752 			if (this.locale.isPseudo()) {
7753 				if (!ResBundle.pseudomap) {
7754 					ResBundle.pseudomap = {};
7755 				}
7756 	
7757 				this._loadPseudo(this.locale, options.onLoad);
7758 			} else if (this.missing === "pseudo") {
7759 				if (!ResBundle.pseudomap) {
7760 					ResBundle.pseudomap = {};
7761 				}
7762 	
7763 				new LocaleInfo(this.locale, {
7764 					sync: this.sync,
7765 					loadParams: this.loadParams,
7766 					onLoad: ilib.bind(this, function (li) {
7767 						var pseudoLocale = new Locale("zxx", "XX", undefined, li.getDefaultScript());
7768 						this._loadPseudo(pseudoLocale, options.onLoad);
7769 					})
7770 				});
7771 			} else {
7772 				if (typeof(options.onLoad) === 'function') {
7773 					options.onLoad(this);
7774 				}
7775 			}
7776 		})
7777 	});
7778 
7779 	// console.log("Merged resources " + this.locale.toString() + " are: " + JSON.stringify(this.map));
7780 	//if (!this.locale.isPseudo() && JSUtils.isEmpty(this.map)) {
7781 	//	console.log("Resources for bundle " + this.baseName + " locale " + this.locale.toString() + " are not available.");
7782 	//}
7783 };
7784 
7785 ResBundle.defaultPseudo = ilib.data.pseudomap || {
7786 	"a": "à",
7787 	"e": "ë",
7788 	"i": "í",
7789 	"o": "õ",
7790 	"u": "ü",
7791 	"y": "ÿ",
7792 	"A": "Ã",
7793 	"E": "Ë",
7794 	"I": "Ï",
7795 	"O": "Ø",
7796 	"U": "Ú",
7797 	"Y": "Ŷ"
7798 };
7799 
7800 ResBundle.prototype = {
7801     /**
7802      * @protected
7803      */
7804     _loadPseudo: function (pseudoLocale, onLoad) {
7805 		Utils.loadData({
7806 			object: ResBundle.pseudomap, 
7807 			locale: pseudoLocale, 
7808 			name: "pseudomap.json", 
7809 			sync: this.sync, 
7810 			loadParams: this.loadParams, 
7811 			callback: ilib.bind(this, function (map) {
7812 				if (!map || JSUtils.isEmpty(map)) {
7813 					map = ResBundle.defaultPseudo;
7814 					var spec = pseudoLocale.getSpec().replace(/-/g, '_');
7815 					ResBundle.pseudomap.cache[spec] = map;
7816 				}
7817 				this.pseudomap = map;
7818 				if (typeof(onLoad) === 'function') {
7819 					onLoad(this);
7820 				}	
7821 			})
7822 		});
7823     },
7824     
7825 	/**
7826 	 * Return the locale of this resource bundle.
7827 	 * @return {Locale} the locale of this resource bundle object 
7828 	 */
7829 	getLocale: function () {
7830 		return this.locale;
7831 	},
7832 	
7833 	/**
7834 	 * Return the name of this resource bundle. This corresponds to the name option
7835 	 * given to the constructor.
7836 	 * @return {string} name of the the current instance
7837 	 */
7838 	getName: function () {
7839 		return this.baseName;
7840 	},
7841 	
7842 	/**
7843 	 * Return the type of this resource bundle. This corresponds to the type option
7844 	 * given to the constructor.
7845 	 * @return {string} type of the the current instance
7846 	 */
7847 	getType: function () {
7848 		return this.type;
7849 	},
7850 
7851 	percentRE: new RegExp("%(\\d+\\$)?([\\-#\\+ 0,\\(])?(\\d+)?(\\.\\d+)?[bBhHsScCdoxXeEfgGaAtT%n]"),
7852 
7853 	/**
7854 	 * @private
7855 	 * Pseudo-translate a string
7856 	 */
7857 	_pseudo: function (str) {
7858 		if (!str) {
7859 			return undefined;
7860 		}
7861 		var ret = "", i;
7862 		for (i = 0; i < str.length; i++) {
7863 			if (this.type !== "raw") {
7864 				if (this.type === "html" || this.type === "xml") {
7865 					if (str.charAt(i) === '<') {
7866 						ret += str.charAt(i++);
7867 						while (i < str.length && str.charAt(i) !== '>') {
7868 							ret += str.charAt(i++);
7869 						}
7870 					} else if (str.charAt(i) === '&') {
7871 						ret += str.charAt(i++);
7872 						while (i < str.length && str.charAt(i) !== ';' && str.charAt(i) !== ' ') {
7873 							ret += str.charAt(i++);
7874 						}
7875 					} else if (str.charAt(i) === '\\' && str.charAt(i+1) === "u") {
7876 						ret += str.substring(i, i+6);
7877 						i += 6;
7878 					}
7879 				} else if (this.type === "c") {
7880 					if (str.charAt(i) === "%") {
7881 						var m = this.percentRE.exec(str.substring(i));
7882 						if (m && m.length) {
7883 							// console.log("Match found: " + JSON.stringify(m[0].replace("%", "%%")));
7884 							ret += m[0];
7885 							i += m[0].length;
7886 						}
7887 					}
7888 
7889 				}
7890 				if (i < str.length) { 
7891 					if (str.charAt(i) === '{') {
7892 						ret += str.charAt(i++);
7893 						while (i < str.length && str.charAt(i) !== '}') {
7894 							ret += str.charAt(i++);
7895 						}
7896 						if (i < str.length) {
7897 							ret += str.charAt(i);
7898 						}
7899 					} else {
7900 						ret += this.pseudomap[str.charAt(i)] || str.charAt(i);
7901 					}
7902 				}
7903 			} else {
7904 				ret += this.pseudomap[str.charAt(i)] || str.charAt(i);
7905 			}
7906 		}
7907 		if (this.lengthen) {
7908 			var add;
7909 			if (ret.length <= 20) {
7910 				add = Math.round(ret.length / 2);
7911 			} else if (ret.length > 20 && ret.length <= 40) {
7912 				add = Math.round(ret.length / 3);
7913 			} else {
7914 				add = Math.round(ret.length / 5);
7915 			}
7916 			for (i = add-1; i >= 0; i--) {
7917 				ret += (i % 10);
7918 			}
7919 		}
7920 		if (this.locale.getScript() === "Hans" || this.locale.getScript() === "Hant" ||
7921 				this.locale.getScript() === "Hani" ||
7922 				this.locale.getScript() === "Hrkt" || this.locale.getScript() === "Jpan" ||
7923 				this.locale.getScript() === "Hira" || this.locale.getScript() === "Kana" ) {
7924 			// simulate Asian languages by getting rid of all the spaces
7925 			ret = ret.replace(/ /g, "");
7926 		}
7927 		return ret;
7928 	},
7929 	
7930 	/**
7931 	 * @private
7932 	 * Escape html characters in the output.
7933 	 */
7934 	_escapeXml: function (str) {
7935 		str = str.replace(/&/g, '&');
7936 		str = str.replace(/</g, '<');
7937 		str = str.replace(/>/g, '>');
7938 		return str;
7939 	},
7940 
7941 	/**
7942 	 * @private
7943 	 * @param {string} str the string to unescape
7944 	 */
7945 	_unescapeXml: function (str) {
7946 		str = str.replace(/&/g, '&');
7947 		str = str.replace(/</g, '<');
7948 		str = str.replace(/>/g, '>');
7949 		return str;
7950 	},
7951 	
7952 	/**
7953 	 * @private
7954 	 * Create a key name out of a source string. All this does so far is 
7955 	 * compress sequences of white space into a single space on the assumption
7956 	 * that this doesn't really change the meaning of the string, and therefore
7957 	 * all such strings that compress to the same thing should share the same
7958 	 * translation.
7959 	 * @param {null|string=} source the source string to make a key out of
7960 	 */
7961 	_makeKey: function (source) {
7962 		if (!source) return undefined;
7963 		var key = source.replace(/\s+/gm, ' ');
7964 		return (this.type === "xml" || this.type === "html") ? this._unescapeXml(key) : key;
7965 	},
7966 	
7967 	/**
7968 	 * Return a localized string. If the string is not found in the loaded set of
7969 	 * resources, the original source string is returned. If the key is not given,
7970 	 * then the source string itself is used as the key. In the case where the 
7971 	 * source string is used as the key, the whitespace is compressed down to 1 space
7972 	 * each, and the whitespace at the beginning and end of the string is trimmed.<p>
7973 	 * 
7974 	 * The escape mode specifies what type of output you are escaping the returned
7975 	 * string for. Modes are similar to the types: 
7976 	 * 
7977 	 * <ul>
7978 	 * <li>"html" -- prevents HTML injection by escaping the characters < > and &
7979 	 * <li>"xml" -- currently same as "html" mode
7980 	 * <li>"js" -- prevents breaking Javascript syntax by backslash escaping all quote and 
7981 	 * double-quote characters
7982 	 * <li>"attribute" -- meant for HTML attribute values. Currently this is the same as
7983 	 * "js" escape mode.
7984 	 * <li>"default" -- use the type parameter from the constructor as the escape mode as well
7985 	 * <li>"none" or undefined -- no escaping at all.
7986 	 * </ul>
7987 	 * 
7988 	 * The type parameter of the constructor specifies what type of strings this bundle
7989 	 * is operating upon. This allows pseudo-translation and automatic key generation
7990 	 * to happen properly by telling this class how to parse the string. The escape mode 
7991 	 * for this method is different in that it specifies how this string will be used in 
7992 	 * the calling code and therefore how to escape it properly.<p> 
7993 	 * 
7994 	 * For example, a section of Javascript code may be constructing an HTML snippet in a 
7995 	 * string to add to the web page. In this case, the type parameter in the constructor should
7996 	 * be "html" so that the source string can be parsed properly, but the escape mode should
7997 	 * be "js" so that the output string can be used in Javascript without causing syntax
7998 	 * errors.
7999 	 * 
8000 	 * @param {?string=} source the source string to translate
8001 	 * @param {?string=} key optional name of the key, if any
8002 	 * @param {?string=} escapeMode escape mode, if any
8003 	 * @return {IString|undefined} the translation of the given source/key or undefined 
8004 	 * if the translation is not found and the source is undefined 
8005 	 */
8006 	getString: function (source, key, escapeMode) {
8007 		if (!source && !key) return new IString("");
8008 
8009 		var trans;
8010 		if (this.locale.isPseudo()) {
8011 			var str = source ? source : this.map[key];
8012 			trans = this._pseudo(str || key);
8013 		} else {
8014 			var keyName = key || this._makeKey(source);
8015 			if (typeof(this.map[keyName]) !== 'undefined') {
8016 				trans = this.map[keyName];
8017 			} else if (this.missing === "pseudo") {
8018 				trans = this._pseudo(source || key);
8019 			} else if (this.missing === "empty") {
8020 				trans = "";
8021 			} else {
8022 				trans = source;
8023 			}
8024 		}
8025 
8026 		if (escapeMode && escapeMode !== "none") {
8027 			if (escapeMode == "default") {
8028 				escapeMode = this.type;
8029 			}
8030 			if (escapeMode === "xml" || escapeMode === "html") {
8031 				trans = this._escapeXml(trans);
8032 			} else if (escapeMode == "js" || escapeMode === "attribute") {
8033 				trans = trans.replace(/'/g, "\\\'").replace(/"/g, "\\\"");
8034 			}
8035 		}
8036 		if (trans === undefined) {
8037 			return undefined;
8038 		} else {
8039 			var ret = new IString(trans);
8040 			ret.setLocale(this.locale.getSpec(), true, this.loadParams); // no callback
8041 			return ret;
8042 		}
8043 	},
8044 	
8045 	/**
8046 	 * Return a localized string as a Javascript object. This does the same thing as
8047 	 * the getString() method, but it returns a regular Javascript string instead of
8048 	 * and IString instance. This means it cannot be formatted with the format()
8049 	 * method without being wrapped in an IString instance first.
8050 	 * 
8051 	 * @param {?string=} source the source string to translate
8052 	 * @param {?string=} key optional name of the key, if any
8053 	 * @param {?string=} escapeMode escape mode, if any
8054 	 * @return {string|undefined} the translation of the given source/key or undefined 
8055 	 * if the translation is not found and the source is undefined
8056 	 */
8057 	getStringJS: function(source, key, escapeMode) {
8058 		return this.getString(source, key, escapeMode).toString();
8059 	},
8060 	
8061 	/**
8062 	 * Return true if the current bundle contains a translation for the given key and
8063 	 * source. The
8064 	 * getString method will always return a string for any given key and source 
8065 	 * combination, so it cannot be used to tell if a translation exists. Either one
8066 	 * or both of the source and key must be specified. If both are not specified,
8067 	 * this method will return false.
8068 	 * 
8069 	 * @param {?string=} source source string to look up
8070 	 * @param {?string=} key key to look up
8071 	 * @return {boolean} true if this bundle contains a translation for the key, and 
8072 	 * false otherwise
8073 	 */
8074 	containsKey: function(source, key) {
8075 		if (typeof(source) === 'undefined' && typeof(key) === 'undefined') {
8076 			return false;
8077 		}
8078 		
8079 		var keyName = key || this._makeKey(source);
8080 		return typeof(this.map[keyName]) !== 'undefined';
8081 	},
8082 	
8083 	/**
8084 	 * Return the merged resources as an entire object. When loading resources for a
8085 	 * locale that are not just a set of translated strings, but instead an entire 
8086 	 * structured javascript object, you can gain access to that object via this call. This method
8087 	 * will ensure that all the of the parts of the object are correct for the locale.<p>
8088 	 * 
8089 	 * For pre-assembled data, it starts by loading <i>ilib.data[name]</i>, where 
8090 	 * <i>name</i> is the base name for this set of resources. Then, it successively 
8091 	 * merges objects in the base data using progressively more locale-specific data. 
8092 	 * It loads it in this order from <i>ilib.data</i>:
8093 	 * 
8094 	 * <ol>
8095 	 * <li> language
8096 	 * <li> region
8097 	 * <li> language_script
8098 	 * <li> language_region
8099 	 * <li> region_variant
8100 	 * <li> language_script_region
8101 	 * <li> language_region_variant
8102 	 * <li> language_script_region_variant
8103 	 * </ol>
8104 	 * 
8105 	 * For dynamically loaded data, the code attempts to load the same sequence as
8106 	 * above, but with slash path separators instead of underscores.<p>
8107 	 *  
8108 	 * Loading the resources this way allows the program to share resources between all
8109 	 * locales that share a common language, region, or script. As a 
8110 	 * general rule-of-thumb, resources should be as generic as possible in order to
8111 	 * cover as many locales as possible.
8112 	 * 
8113 	 * @return {Object} returns the object that is the basis for this resources instance
8114 	 */
8115 	getResObj: function () {
8116 		return this.map;
8117 	}
8118 };
8119 
8120 
8121 
8122 /*< ISet.js */
8123 /*
8124  * ISet.js - ilib Set class definition for platforms older than ES6
8125  * 
8126  * Copyright © 2015, JEDLSoft
8127  *
8128  * Licensed under the Apache License, Version 2.0 (the "License");
8129  * you may not use this file except in compliance with the License.
8130  * You may obtain a copy of the License at
8131  *
8132  *     http://www.apache.org/licenses/LICENSE-2.0
8133  *
8134  * Unless required by applicable law or agreed to in writing, software
8135  * distributed under the License is distributed on an "AS IS" BASIS,
8136  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8137  *
8138  * See the License for the specific language governing permissions and
8139  * limitations under the License.
8140  */
8141 
8142 /**
8143  * Create a new set with elements in the given array. The type of
8144  * the set is gleaned from the type of the first element in the
8145  * elements array, or the first element added to the set. The type
8146  * may be "string" or "number", and all elements will be returned
8147  * as elements of that type.
8148  * 
8149  * @class
8150  * @param {Array.<string|number>=} elements initial elements to add to the set
8151  * @constructor
8152  */
8153 var ISet = function(elements) {
8154 	this.elements = {};
8155 
8156 	if (elements && elements.length) {
8157 		for (var i = 0; i < elements.length; i++) {
8158 			this.elements[elements[i]] = true;
8159 		}
8160 		
8161 		this.type = typeof(elements[0]);
8162 	}
8163 };
8164 
8165 /**
8166  * @private
8167  */
8168 ISet.prototype._addOne = function(element) {
8169 	if (this.isEmpty()) {
8170 		this.type = typeof(element);
8171 	}
8172 	
8173 	if (!this.elements[element]) {
8174 		this.elements[element] = true;
8175 		return true;
8176 	}
8177 
8178 	return false;
8179 };
8180 
8181 /**
8182  * Adds the specified element or array of elements to this set if it is or they are not 
8183  * already present.
8184  * 
8185  * @param {*|Array.<*>} element element or array of elements to add
8186  * @return {boolean} true if this set did not already contain the specified element[s]
8187  */
8188 ISet.prototype.add = function(element) {
8189 	var ret = false;
8190 	
8191 	if (typeof(element) === "object") {
8192 		for (var i = 0; i < element.length; i++) {
8193 			ret = this._addOne(element[i]) || ret;
8194 		}
8195 	} else {
8196 		ret = this._addOne(element);
8197 	}
8198 	
8199 	return ret;
8200 };
8201 
8202 /**
8203  * Removes all of the elements from this set.
8204  */
8205 ISet.prototype.clear = function() {
8206 	this.elements = {};
8207 };
8208 
8209 /**
8210  * Returns true if this set contains the specified element.
8211  * @param {*} element the element to test
8212  * @return {boolean}
8213  */
8214 ISet.prototype.contains = function(element) {
8215 	return this.elements[element] || false;
8216 };
8217 
8218 /**
8219  * Returns true if this set contains no elements.
8220  * @return {boolean}
8221  */
8222 ISet.prototype.isEmpty = function() {
8223 	return (Object.keys(this.elements).length === 0);
8224 };
8225 
8226 /**
8227  * Removes the specified element from this set if it is present.
8228  * @param {*} element the element to remove
8229  * @return {boolean} true if the set contained the specified element
8230  */
8231 ISet.prototype.remove = function(element) {
8232 	if (this.elements[element]) {
8233 		delete this.elements[element];
8234 		return true;
8235 	}
8236 	
8237 	return false;
8238 };
8239 
8240 /**
8241  * Return the set as a javascript array.
8242  * @return {Array.<*>} the set represented as a javascript array
8243  */
8244 ISet.prototype.asArray = function() {
8245 	var keys = Object.keys(this.elements);
8246 	
8247 	// keys is an array of strings. Convert to numbers if necessary
8248 	if (this.type === "number") {
8249 		var tmp = [];
8250 		for (var i = 0; i < keys.length; i++) {
8251 			tmp.push(Number(keys[i]).valueOf());
8252 		}
8253 		keys = tmp;
8254 	}
8255 	
8256 	return keys;
8257 };
8258 
8259 /**
8260  * Represents the current set as json.
8261  * @return {string} the current set represented as json
8262  */
8263 ISet.prototype.toJson = function() {
8264 	return JSON.stringify(this.asArray());
8265 };
8266 
8267 /**
8268  * Convert to a javascript representation of this object.
8269  * In this case, it is a normal JS array.
8270  * @return {*} the JS representation of this object
8271  */
8272 ISet.prototype.toJS = function() {
8273 	return this.asArray();
8274 };
8275 
8276 /**
8277  * Convert from a js representation to an internal one.
8278  * @return {ISet|undefined} the current object, or undefined if the conversion did not work
8279  */
8280 ISet.prototype.fromJS = function(obj) {
8281 	return this.add(obj) ? this : undefined;
8282 };
8283 
8284 
8285 
8286 /*< DateFmt.js */
8287 /*
8288  * DateFmt.js - Date formatter definition
8289  * 
8290  * Copyright © 2012-2015, JEDLSoft
8291  *
8292  * Licensed under the Apache License, Version 2.0 (the "License");
8293  * you may not use this file except in compliance with the License.
8294  * You may obtain a copy of the License at
8295  *
8296  *     http://www.apache.org/licenses/LICENSE-2.0
8297  *
8298  * Unless required by applicable law or agreed to in writing, software
8299  * distributed under the License is distributed on an "AS IS" BASIS,
8300  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8301  *
8302  * See the License for the specific language governing permissions and
8303  * limitations under the License.
8304  */
8305 
8306 /*
8307 !depends 
8308 ilib.js 
8309 Locale.js 
8310 IDate.js
8311 DateFactory.js  
8312 IString.js 
8313 ResBundle.js 
8314 Calendar.js
8315 CalendarFactory.js
8316 LocaleInfo.js
8317 TimeZone.js
8318 GregorianCal.js
8319 JSUtils.js
8320 Utils.js
8321 ISet.js
8322 */
8323 
8324 // !data dateformats sysres
8325 
8326 
8327 
8328 
8329 
8330 
8331 /**
8332  * @class
8333  * Create a new date formatter instance. The date formatter is immutable once
8334  * it is created, but can format as many different dates as needed with the same
8335  * options. Create different date formatter instances for different purposes
8336  * and then keep them cached for use later if you have more than one date to
8337  * format.<p>
8338  * 
8339  * The options may contain any of the following properties:
8340  * 
8341  * <ul>
8342  * <li><i>locale</i> - locale to use when formatting the date/time. If the locale is
8343  * not specified, then the default locale of the app or web page will be used.
8344  * 
8345  * <li><i>calendar</i> - the type of calendar to use for this format. The value should
8346  * be a sting containing the name of the calendar. Currently, the supported
8347  * types are "gregorian", "julian", "arabic", "hebrew", or "chinese". If the
8348  * calendar is not specified, then the default calendar for the locale is used. When the
8349  * calendar type is specified, then the format method must be called with an instance of
8350  * the appropriate date type. (eg. Gregorian calendar means that the format method must 
8351  * be called with a GregDate instance.)
8352  *  
8353  * <li><i>timezone</i> - time zone to use when formatting times. This may be a time zone
8354  * instance or a time zone specifier from the IANA list of time zone database names 
8355  * (eg. "America/Los_Angeles"), 
8356  * the string "local", or a string specifying the offset in RFC 822 format. The IANA
8357  * list of time zone names can be viewed at 
8358  * <a href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">this page</a>.
8359  * If the time zone is given as "local", the offset from UTC as given by
8360  * the Javascript system is used. If the offset is given as an RFC 822 style offset
8361  * specifier, it will parse that string and use the resulting offset. If the time zone
8362  * is not specified, the
8363  * default time zone for the locale is used. If both the date object and this formatter
8364  * instance contain time zones and those time zones are different from each other, the 
8365  * formatter will calculate the offset between the time zones and subtract it from the 
8366  * date before formatting the result for the current time zone. The theory is that a date
8367  * object that contains a time zone specifies a specific instant in time that is valid
8368  * around the world, whereas a date object without one is a local time and can only be
8369  * used for doing things in the local time zone of the user.
8370  * 
8371  * <li><i>type</i> - Specify whether this formatter should format times only, dates only, or
8372  * both times and dates together. Valid values are "time", "date", and "datetime". Note that
8373  * in some locales, the standard format uses the order "time followed by date" and in others, 
8374  * the order is exactly opposite, so it is better to create a single "datetime" formatter 
8375  * than it is to create a time formatter and a date formatter separately and concatenate the 
8376  * results. A "datetime" formatter will get the order correct for the locale.<p>
8377  * 
8378  * The default type if none is specified in with the type option is "date".
8379  * 
8380  * <li><i>length</i> - Specify the length of the format to use. The length is the approximate size of the 
8381  * formatted string.
8382  * 
8383  * <ul>
8384  * <li><i>short</i> - use a short representation of the time. This is the most compact format possible for the locale.
8385  * <li><i>medium</i> - use a medium length representation of the time. This is a slightly longer format.
8386  * <li><i>long</i> - use a long representation of the time. This is a fully specified format, but some of the textual 
8387  * components may still be abbreviated
8388  * <li><i>full</i> - use a full representation of the time. This is a fully specified format where all the textual 
8389  * components are spelled out completely
8390  * </ul>
8391  * 
8392  * eg. The "short" format for an en_US date may be "MM/dd/yy", whereas the long format might be "d MMM, yyyy". In the long
8393  * format, the month name is textual instead of numeric and is longer, the year is 4 digits instead of 2, and the format 
8394  * contains slightly more spaces and formatting characters.<p>
8395  * 
8396  * Note that the length parameter does not specify which components are to be formatted. Use the "date" and the "time"
8397  * properties to specify the components. Also, very few of the components of a time format differ according to the length,
8398  * so this property has little to no affect on time formatting.
8399  * 
8400  * <li><i>date</i> - This property tells
8401  * which components of a date format to use. For example,
8402  * sometimes you may wish to format a date that only contains the month and date
8403  * without the year, such as when displaying a person's yearly birthday. The value
8404  * of this property allows you to specify only those components you want to see in the
8405  * final output, ordered correctly for the locale. <p>
8406  * 
8407  * Valid values are:
8408  * 
8409  * <ul>
8410  * <li><i>dmwy</i> - format all components, weekday, date, month, and year
8411  * <li><i>dmy</i> - format only date, month, and year
8412  * <li><i>dmw</i> - format only weekday, date, and month
8413  * <li><i>dm</i> - format only date and month
8414  * <li><i>my</i> - format only month and year
8415  * <li><i>dw</i> - format only the weekday and date
8416  * <li><i>d</i> - format only the date
8417  * <li><i>m</i> - format only the month, in numbers for shorter lengths, and letters for 
8418  * longer lengths
8419  * <li><i>n</i> - format only the month, in letters only for all lengths
8420  * <li><i>y</i> - format only the year
8421  * </ul>
8422  * Default components, if this property is not specified, is "dmy". This property may be specified
8423  * but has no affect if the current formatter is for times only.<p>
8424  * 
8425  * As of ilib 12.0, you can now pass ICU style skeletons in this option similar to the ones you 
8426  * get from <a href="http://icu-project.org/apiref/icu4c432/classDateTimePatternGenerator.html#aa30c251609c1eea5ad60c95fc497251e">DateTimePatternGenerator.getSkeleton()</a>. 
8427  * It will not extract the length from the skeleton so you still need to pass the length property, 
8428  * but it will extract the date components.
8429  * 
8430  * <li><i>time</i> - This property gives which components of a time format to use. The time will be formatted 
8431  * correctly for the locale with only the time components requested. For example, a clock might only display 
8432  * the hour and minute and not need the seconds or the am/pm component. In this case, the time property should be set 
8433  * to "hm". <p>
8434  * 
8435  * Valid values for this property are:
8436  * 
8437  * <ul>
8438  * <li><i>ahmsz</i> - format the hours, minutes, seconds, am/pm (if using a 12 hour clock), and the time zone
8439  * <li><i>ahms</i> - format the hours, minutes, seconds, and am/pm (if using a 12 hour clock)
8440  * <li><i>hmsz</i> - format the hours, minutes, seconds, and the time zone
8441  * <li><i>hms</i> - format the hours, minutes, and seconds
8442  * <li><i>ahmz</i> - format the hours, minutes, am/pm (if using a 12 hour clock), and the time zone
8443  * <li><i>ahm</i> - format the hours, minutes, and am/pm (if using a 12 hour clock)
8444  * <li><i>hmz</i> - format the hours, minutes, and the time zone
8445  * <li><i>ah</i> - format only the hours and am/pm if using a 12 hour clock
8446  * <li><i>hm</i> - format only the hours and minutes
8447  * <li><i>ms</i> - format only the minutes and seconds
8448  * <li><i>h</i> - format only the hours
8449  * <li><i>m</i> - format only the minutes
8450  * <li><i>s</i> - format only the seconds
8451  * </ul>
8452  * 
8453  * If you want to format a length of time instead of a particular instant
8454  * in time, use the duration formatter object (DurationFmt) instead because this
8455  * formatter is geared towards instants. A date formatter will make sure that each component of the 
8456  * time is within the normal range
8457  * for that component. That is, the minutes will always be between 0 and 59, no matter
8458  * what is specified in the date to format. A duration format will allow the number
8459  * of minutes to exceed 59 if, for example, you were displaying the length of
8460  * a movie of 198 minutes.<p>
8461  * 
8462  * Default value if this property is not specified is "hma".<p>
8463  * 
8464  * As of ilib 12.0, you can now pass ICU style skeletons in this option similar to the ones you 
8465  * get from <a href="http://icu-project.org/apiref/icu4c432/classDateTimePatternGenerator.html#aa30c251609c1eea5ad60c95fc497251e">DateTimePatternGenerator.getSkeleton()</a>. 
8466  * It will not extract the length from the skeleton so you still need to pass the length property, 
8467  * but it will extract the time components.
8468  * 
8469  * <li><i>clock</i> - specify that the time formatter should use a 12 or 24 hour clock. 
8470  * Valid values are "12" and "24".<p>
8471  * 
8472  * In some locales, both clocks are used. For example, in en_US, the general populace uses
8473  * a 12 hour clock with am/pm, but in the US military or in nautical or aeronautical or 
8474  * scientific writing, it is more common to use a 24 hour clock. This property allows you to
8475  * construct a formatter that overrides the default for the locale.<p>
8476  * 
8477  * If this property is not specified, the default is to use the most widely used convention
8478  * for the locale.
8479  *  
8480  * <li><i>template</i> - use the given template string as a fixed format when formatting 
8481  * the date/time. Valid codes to use in a template string are as follows:
8482  * 
8483  * <ul>
8484  * <li><i>a</i> - am/pm marker
8485  * <li><i>d</i> - 1 or 2 digit date of month, not padded
8486  * <li><i>dd</i> - 1 or 2 digit date of month, 0 padded to 2 digits
8487  * <li><i>O</i> - ordinal representation of the date of month (eg. "1st", "2nd", etc.)
8488  * <li><i>D</i> - 1 to 3 digit day of year
8489  * <li><i>DD</i> - 1 to 3 digit day of year, 0 padded to 2 digits
8490  * <li><i>DDD</i> - 1 to 3 digit day of year, 0 padded to 3 digits
8491  * <li><i>M</i> - 1 or 2 digit month number, not padded
8492  * <li><i>MM</i> - 1 or 2 digit month number, 0 padded to 2 digits
8493  * <li><i>N</i> - 1 character month name abbreviation
8494  * <li><i>NN</i> - 2 character month name abbreviation
8495  * <li><i>MMM</i> - 3 character month month name abbreviation
8496  * <li><i>MMMM</i> - fully spelled out month name
8497  * <li><i>yy</i> - 2 digit year
8498  * <li><i>yyyy</i> - 4 digit year
8499  * <li><i>E</i> - day-of-week name, abbreviated to a single character
8500  * <li><i>EE</i> - day-of-week name, abbreviated to a max of 2 characters
8501  * <li><i>EEE</i> - day-of-week name, abbreviated to a max of 3 characters
8502  * <li><i>EEEE</i> - day-of-week name fully spelled out 
8503  * <li><i>G</i> - era designator
8504  * <li><i>w</i> - week number in year
8505  * <li><i>ww</i> - week number in year, 0 padded to 2 digits
8506  * <li><i>W</i> - week in month
8507  * <li><i>h</i> - hour (12 followed by 1 to 11)
8508  * <li><i>hh</i> - hour (12, followed by 1 to 11), 0 padded to 2 digits
8509  * <li><i>k</i> - hour (1 to 24)
8510  * <li><i>kk</i> - hour (1 to 24), 0 padded to 2 digits
8511  * <li><i>H</i> - hour (0 to 23)
8512  * <li><i>HH</i> - hour (0 to 23), 0 padded to 2 digits
8513  * <li><i>K</i> - hour (0 to 11)
8514  * <li><i>KK</i> - hour (0 to 11), 0 padded to 2 digits
8515  * <li><i>m</i> - minute in hour
8516  * <li><i>mm</i> - minute in hour, 0 padded to 2 digits
8517  * <li><i>s</i> - second in minute
8518  * <li><i>ss</i> - second in minute, 0 padded to 2 digits
8519  * <li><i>S</i> - millisecond (1 to 3 digits)
8520  * <li><i>SSS</i> - millisecond, 0 padded to 3 digits
8521  * <li><i>z</i> - general time zone
8522  * <li><i>Z</i> - RFC 822 time zone
8523  * </ul>
8524  * 
8525  * <li><i>useNative</i> - the flag used to determine whether to use the native script settings 
8526  * for formatting the numbers.
8527  *
8528  * <li><i>meridiems</i> - string that specifies what style of meridiems to use with this 
8529  * format. The choices are "default", "gregorian", "ethiopic", and "chinese". The "default" 
8530  * style is often the simple Gregorian AM/PM, but the actual style is chosen by the locale. 
8531  * (For almost all locales, the Gregorian AM/PM style is most frequently used.)
8532  * The "ethiopic" style uses 5 different meridiems for "morning", "noon", "afternoon", 
8533  * "evening", and "night". The "chinese" style uses 7 different meridiems corresponding 
8534  * to the various parts of the day. N.B. Even for the Chinese locales, the default is "gregorian"
8535  * when formatting dates in the Gregorian calendar.
8536  *
8537  * <li><i>onLoad</i> - a callback function to call when the date format object is fully 
8538  * loaded. When the onLoad option is given, the DateFmt object will attempt to
8539  * load any missing locale data using the ilib loader callback.
8540  * When the constructor is done (even if the data is already preassembled), the 
8541  * onLoad function is called with the current instance as a parameter, so this
8542  * callback can be used with preassembled or dynamic loading or a mix of the two.
8543  * 
8544  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
8545  * asynchronously. If this option is given as "false", then the "onLoad"
8546  * callback must be given, as the instance returned from this constructor will
8547  * not be usable for a while.
8548  *  
8549  * <li><i>loadParams</i> - an object containing parameters to pass to the 
8550  * loader callback function when locale data is missing. The parameters are not
8551  * interpretted or modified in any way. They are simply passed along. The object 
8552  * may contain any property/value pairs as long as the calling code is in
8553  * agreement with the loader callback function as to what those parameters mean.
8554  * </ul>
8555  * 
8556  * Any substring containing letters within single or double quotes will be used 
8557  * as-is in the final output and will not be interpretted for codes as above.<p>
8558  * 
8559  * Example: a date format in Spanish might be given as: "'El' d. 'de' MMMM", where
8560  * the 'El' and the 'de' are left as-is in the output because they are quoted. Typical 
8561  * output for this example template might be, "El 5. de Mayo".
8562  * 
8563  * The following options will be used when formatting a date/time with an explicit
8564  * template:
8565  * 
8566  * <ul>
8567  * <li>locale - the locale is only used for 
8568  * translations of things like month names or day-of-week names.
8569  * <li>calendar - used to translate a date instance into date/time component values 
8570  * that can be formatted into the template
8571  * <li>timezone - used to figure out the offset to add or subtract from the time to
8572  * get the final time component values
8573  * <li>clock - used to figure out whether to format times with a 12 or 24 hour clock.
8574  * If this option is specified, it will override the hours portion of a time format.
8575  * That is, "hh" is switched with "HH" and "kk" is switched with "KK" as appropriate. 
8576  * If this option is not specified, the 12/24 code in the template will dictate whether 
8577  * to use the 12 or 24 clock, and the 12/24 default in the locale will be ignored.
8578  * </ul>
8579  * 
8580  * All other options will be ignored and their corresponding getter methods will
8581  * return the empty string.<p>
8582  * 
8583  * 
8584  * @constructor
8585  * @param {Object} options options governing the way this date formatter instance works
8586  */
8587 var DateFmt = function(options) {
8588 	var arr, i, bad, 
8589 		sync = true, 
8590 		loadParams = undefined;
8591 	
8592 	this.locale = new Locale();
8593 	this.type = "date";
8594 	this.length = "s";
8595 	this.dateComponents = "dmy";
8596 	this.timeComponents = "ahm";
8597 	this.meridiems = "default";
8598 	
8599 	if (options) {
8600 		if (options.locale) {
8601 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
8602 		}
8603 		
8604 		if (options.type) {
8605 			if (options.type === 'date' || options.type === 'time' || options.type === 'datetime') {
8606 				this.type = options.type;
8607 			}
8608 		}
8609 		
8610 		if (options.calendar) {
8611 			this.calName = options.calendar;
8612 		}
8613 		
8614 		if (options.length) {
8615 			if (options.length === 'short' ||
8616 				options.length === 'medium' ||
8617 				options.length === 'long' ||
8618 				options.length === 'full') {
8619 				// only use the first char to save space in the json files
8620 				this.length = options.length.charAt(0);
8621 			}
8622 		}
8623 		
8624 		if (options.date) {
8625 			arr = options.date.split("");
8626 			var dateComps = new ISet();
8627 			bad = false;
8628 			for (i = 0; i < arr.length; i++) {
8629 				var c = arr[i].toLowerCase();
8630 				if (c === "e") c = "w"; // map ICU -> ilib
8631 				if (c !== 'd' && c !== 'm' && c !== 'y' && c !== 'w' && c !== 'n') {
8632 					// ignore time components and the era
8633 					if (c !== 'h' && c !== 'm'  && c !== 's' && c !== 'a' && c !== 'z' && c !== 'g') {
8634     					bad = true;
8635     					break;
8636 					}
8637 				} else {
8638     				dateComps.add(c);
8639 				}
8640 			}
8641 			if (!bad) {
8642 				var comps = dateComps.asArray().sort(function (left, right) {
8643 					return (left < right) ? -1 : ((right < left) ? 1 : 0);
8644 				});
8645 				this.dateComponents = comps.join("");
8646 			}
8647 		}
8648 
8649 		if (options.time) {
8650 			arr = options.time.split("");
8651 			var timeComps = new ISet();
8652 			this.badTime = false;
8653 			for (i = 0; i < arr.length; i++) {
8654 				var c = arr[i].toLowerCase();
8655 				if (c !== 'h' && c !== 'm' && c !== 's' && c !== 'a' && c !== 'z') {
8656 					// ignore the date components
8657 					if (c !== 'd' && c !== 'm' && c !== 'y' && c !== 'w' && c !== 'e' && c !== 'n' && c !== 'g') {
8658     					this.badTime = true;
8659     					break;
8660 					}
8661 				} else {
8662 					timeComps.add(c);
8663 				}
8664 			}
8665 			if (!this.badTime) {
8666 				var comps = timeComps.asArray().sort(function (left, right) {
8667 					return (left < right) ? -1 : ((right < left) ? 1 : 0);
8668 				});
8669 				this.timeComponents = comps.join("");
8670 			}
8671 		}
8672 		
8673 		if (options.clock && (options.clock === '12' || options.clock === '24')) {
8674 			this.clock = options.clock;
8675 		}
8676 		
8677 		if (options.template) {
8678 			// many options are not useful when specifying the template directly, so zero
8679 			// them out.
8680 			this.type = "";
8681 			this.length = "";
8682 			this.dateComponents = "";
8683 			this.timeComponents = "";
8684 			
8685 			this.template = options.template;
8686 		}
8687 		
8688 		if (options.timezone) {
8689 			if (options.timezone instanceof TimeZone) {
8690 				this.tz = options.timezone;
8691 			} else {
8692 				this.tz = new TimeZone({
8693 					locale: this.locale, 
8694 					id: options.timezone
8695 				});
8696 			}
8697 		} else if (options.locale) {
8698 			// if an explicit locale was given, then get the time zone for that locale
8699 			this.tz = new TimeZone({
8700 				locale: this.locale
8701 			});
8702 		} // else just assume time zone "local"
8703 		
8704 		if (typeof(options.useNative) === 'boolean') {
8705 			this.useNative = options.useNative;
8706 		}
8707 		
8708 		if (typeof(options.meridiems) !== 'undefined' && 
8709 				(options.meridiems === "chinese" || 
8710 				 options.meridiems === "gregorian" || 
8711 				 options.meridiems === "ethiopic")) {
8712 			this.meridiems = options.meridiems;
8713 		}
8714 		
8715 		if (typeof(options.sync) !== 'undefined') {
8716 			sync = (options.sync === true);
8717 		}
8718 		
8719 		loadParams = options.loadParams;
8720 	}
8721 
8722 	if (!DateFmt.cache) {
8723 		DateFmt.cache = {};
8724 	}
8725 
8726 	new LocaleInfo(this.locale, {
8727 		sync: sync,
8728 		loadParams: loadParams, 
8729 		onLoad: ilib.bind(this, function (li) {
8730 			this.locinfo = li;
8731 			
8732 			// get the default calendar name from the locale, and if the locale doesn't define
8733 			// one, use the hard-coded gregorian as the last resort
8734 			this.calName = this.calName || this.locinfo.getCalendar() || "gregorian";
8735 			if (ilib.isDynCode()) {
8736 				// If we are running in the dynamic code loading assembly of ilib, the following
8737 				// will attempt to dynamically load the calendar date class for this calendar. If 
8738 				// it doesn't work, this just goes on and it will use Gregorian instead.
8739 				DateFactory._dynLoadDate(this.calName);
8740 			}
8741 			
8742 			this.cal = CalendarFactory({
8743 				type: this.calName
8744 			});
8745 			if (!this.cal) {
8746 				this.cal = new GregorianCal();
8747 			}
8748 			if (this.meridiems === "default") {
8749 				this.meridiems = li.getMeridiemsStyle();
8750 			}
8751 
8752 			/*
8753 			if (this.timeComponents &&
8754 					(this.clock === '24' || 
8755 					(!this.clock && this.locinfo.getClock() === "24"))) {
8756 				// make sure we don't have am/pm in 24 hour mode unless the user specifically
8757 				// requested it in the time component option
8758 				this.timeComponents = this.timeComponents.replace("a", "");
8759 			}
8760 			*/
8761 
8762 			// load the strings used to translate the components
8763 			new ResBundle({
8764 				locale: this.locale,
8765 				name: "sysres",
8766 				sync: sync,
8767 				loadParams: loadParams, 
8768 				onLoad: ilib.bind(this, function (rb) {
8769 					this.sysres = rb;
8770 					
8771 					if (!this.template) {
8772 						Utils.loadData({
8773 							object: DateFmt, 
8774 							locale: this.locale, 
8775 							name: "dateformats.json", 
8776 							sync: sync, 
8777 							loadParams: loadParams, 
8778 							callback: ilib.bind(this, function (formats) {
8779 								var spec = this.locale.getSpec().replace(/-/g, '_');
8780 								if (!formats) {
8781 									formats = ilib.data.dateformats || DateFmt.defaultFmt;
8782 									DateFmt.cache[spec] = formats;
8783 								}
8784 /*
8785 								if (!ilib.data.dateformats) {
8786 									ilib.data.dateformats = {};
8787 								}
8788 								if (!ilib.data.dateformats[spec]) {
8789 									ilib.data.dateformats[spec] = formats;
8790 								}
8791 */
8792 								if (typeof(this.clock) === 'undefined') {
8793 									// default to the locale instead
8794 									this.clock = this.locinfo.getClock();
8795 								}
8796 								this._initTemplate(formats);
8797 								this._massageTemplate();
8798 								if (options && typeof(options.onLoad) === 'function') {
8799 									options.onLoad(this);
8800 								}
8801 							})
8802 						});
8803 					} else {
8804 						this._massageTemplate();
8805 						if (options && typeof(options.onLoad) === 'function') {
8806 							options.onLoad(this);
8807 						}
8808 					}
8809 				})
8810 			});	
8811 		})
8812 	});
8813 };
8814 
8815 // used in getLength
8816 DateFmt.lenmap = {
8817 	"s": "short",
8818 	"m": "medium",
8819 	"l": "long",
8820 	"f": "full"
8821 };
8822 
8823 DateFmt.defaultFmt = {
8824 	"gregorian": {
8825 		"order": "{date} {time}",
8826 		"date": {
8827 			"dmwy": "EEE d/MM/yyyy",
8828 			"dmy": "d/MM/yyyy",
8829 			"dmw": "EEE d/MM",
8830 			"dm": "d/MM",
8831 			"my": "MM/yyyy",
8832 			"dw": "EEE d",
8833 			"d": "dd",
8834 			"m": "MM",
8835 			"y": "yyyy",
8836 			"n": "NN",
8837 			"w": "EEE"
8838 		},
8839 		"time": {
8840 			"12": "h:mm:ssa",
8841 			"24": "H:mm:ss"
8842 		},
8843 		"range": {
8844 			"c00": "{st} - {et}, {sd}/{sm}/{sy}",
8845 			"c01": "{sd}/{sm} {st} - {ed}/{em} {et}, {sy}",
8846 			"c02": "{sd}/{sm} {st} - {ed}/{em} {et}, {sy}",
8847 			"c03": "{sd}/{sm}/{sy} {st} - {ed}/{em}/{ey} {et}",
8848 			"c10": "{sd}-{ed}/{sm}/{sy}",
8849 			"c11": "{sd}/{sm} - {ed}/{em} {sy}",
8850 			"c12": "{sd}/{sm}/{sy} - {ed}/{em}/{ey}",
8851 			"c20": "{sm}/{sy} - {em}/{ey}",
8852 			"c30": "{sy} - {ey}"
8853 		}
8854 	},
8855 	"islamic": "gregorian",
8856 	"hebrew": "gregorian",
8857 	"julian": "gregorian",
8858 	"buddhist": "gregorian",
8859 	"persian": "gregorian",
8860 	"persian-algo": "gregorian",
8861 	"han": "gregorian"
8862 };
8863 
8864 /**
8865 * @static
8866 * @private
8867 */
8868 DateFmt.monthNameLenMap = {
8869 	"short" : "N",
8870 	"medium": "NN",
8871 	"long":   "MMM",
8872 	"full":   "MMMM"
8873 };
8874 
8875 /**
8876 * @static
8877 * @private
8878 */
8879 DateFmt.weekDayLenMap = {
8880 	"short" : "E",
8881 	"medium": "EE",
8882 	"long":   "EEE",
8883 	"full":   "EEEE"
8884 };
8885 
8886 /**
8887  * Return the range of possible meridiems (times of day like "AM" or 
8888  * "PM") in this date formatter.<p>
8889  *
8890  * The options may contain any of the following properties:
8891  *
8892  * <ul>
8893  * <li><i>locale</i> - locale to use when formatting the date/time. If the locale is
8894  * not specified, then the default locale of the app or web page will be used.
8895  * 
8896  * <li><i>meridiems</i> - string that specifies what style of meridiems to use with this 
8897  * format. The choices are "default", "gregorian", "ethiopic", and "chinese". The "default" 
8898  * style is often the simple Gregorian AM/PM, but the actual style is chosen by the locale. 
8899  * (For almost all locales, the Gregorian AM/PM style is most frequently used.)
8900  * The "ethiopic" style uses 5 different meridiems for "morning", "noon", "afternoon", 
8901  * "evening", and "night". The "chinese" style uses 7 different meridiems corresponding 
8902  * to the various parts of the day. N.B. Even for the Chinese locales, the default is "gregorian"
8903  * when formatting dates in the Gregorian calendar.
8904  * </ul>
8905  *
8906  * @static
8907  * @public
8908  * @param {Object} options options governing the way this date formatter instance works for getting meridiems range
8909  * @return {Array.<{name:string,start:string,end:string}>}
8910  */
8911 DateFmt.getMeridiemsRange = function (options) {
8912 	options = options || {};
8913 	var args = {};
8914 	if (options.locale) {
8915 		args.locale = options.locale;
8916 	}
8917 
8918 	if (options.meridiems) {
8919 		args.meridiems = options.meridiems;
8920 	}
8921 
8922 	var fmt = new DateFmt(args);
8923 
8924 	return fmt.getMeridiemsRange();
8925 };
8926 
8927 DateFmt.prototype = {
8928 	/**
8929 	 * @protected
8930 	 * @param {string|{
8931 	 * 		order:(string|{
8932 	 * 			s:string,
8933 	 * 			m:string,
8934 	 * 			l:string,
8935 	 * 			f:string
8936 	 * 		}),
8937 	 * 		date:Object.<string, (string|{
8938 	 * 			s:string,
8939 	 * 			m:string,
8940 	 * 			l:string,
8941 	 * 			f:string
8942 	 * 		})>,
8943 	 * 		time:Object.<string,Object.<string,(string|{
8944 	 * 			s:string,
8945 	 * 			m:string,
8946 	 * 			l:string,
8947 	 * 			f:string
8948 	 * 		})>>,
8949 	 * 		range:Object.<string, (string|{
8950 	 * 			s:string,
8951 	 * 			m:string,
8952 	 * 			l:string,
8953 	 * 			f:string
8954 	 * 		})>
8955 	 * 	}} formats
8956 	 */
8957 	_initTemplate: function (formats) {
8958 		if (formats[this.calName]) {
8959 			var name = formats[this.calName];
8960 			// may be an alias to another calendar type
8961 			this.formats = (typeof(name) === "string") ? formats[name] : name;
8962 			
8963 			this.template = "";
8964 			
8965 			switch (this.type) {
8966 				case "datetime":
8967 					this.template = (this.formats && this._getLengthFormat(this.formats.order, this.length)) || "{date} {time}";
8968 					this.template = this.template.replace("{date}", this._getFormat(this.formats.date, this.dateComponents, this.length) || "");
8969 					this.template = this.template.replace("{time}", this._getFormat(this.formats.time[this.clock], this.timeComponents, this.length) || "");
8970 					break;
8971 				case "date":
8972 					this.template = this._getFormat(this.formats.date, this.dateComponents, this.length);
8973 					break;
8974 				case "time":
8975 					this.template = this._getFormat(this.formats.time[this.clock], this.timeComponents, this.length);
8976 					break;
8977 			}
8978 		} else {
8979 			throw "No formats available for calendar " + this.calName + " in locale " + this.locale.toString();
8980 		}
8981 	},
8982 	
8983 	/**
8984 	 * @protected
8985 	 */
8986 	_massageTemplate: function () {
8987 		var i;
8988 		
8989 		if (this.clock && this.template) {
8990 			// explicitly set the hours to the requested type
8991 			var temp = "";
8992 			switch (this.clock) {
8993 				case "24":
8994 					for (i = 0; i < this.template.length; i++) {
8995 						if (this.template.charAt(i) == "'") {
8996 							temp += this.template.charAt(i++);
8997 							while (i < this.template.length && this.template.charAt(i) !== "'") {
8998 								temp += this.template.charAt(i++);
8999 							}
9000 							if (i < this.template.length) {
9001 								temp += this.template.charAt(i);
9002 							}
9003 						} else if (this.template.charAt(i) == 'K') {
9004 							temp += 'k';
9005 						} else if (this.template.charAt(i) == 'h') {
9006 							temp += 'H';
9007 						} else {
9008 							temp += this.template.charAt(i);
9009 						}
9010 					}
9011 					this.template = temp;
9012 					break;
9013 				case "12":
9014 					for (i = 0; i < this.template.length; i++) {
9015 						if (this.template.charAt(i) == "'") {
9016 							temp += this.template.charAt(i++);
9017 							while (i < this.template.length && this.template.charAt(i) !== "'") {
9018 								temp += this.template.charAt(i++);
9019 							}
9020 							if (i < this.template.length) {
9021 								temp += this.template.charAt(i);
9022 							}
9023 						} else if (this.template.charAt(i) == 'k') {
9024 							temp += 'K';
9025 						} else if (this.template.charAt(i) == 'H') {
9026 							temp += 'h';
9027 						} else {
9028 							temp += this.template.charAt(i);
9029 						}
9030 					}
9031 					this.template = temp;
9032 					break;
9033 			}
9034 		}
9035 		
9036 		// tokenize it now for easy formatting
9037 		this.templateArr = this._tokenize(this.template);
9038 
9039 		var digits;
9040 		// set up the mapping to native or alternate digits if necessary
9041 		if (typeof(this.useNative) === "boolean") {
9042 			if (this.useNative) {
9043 				digits = this.locinfo.getNativeDigits();
9044 				if (digits) {
9045 					this.digits = digits;
9046 				}
9047 			}
9048 		} else if (this.locinfo.getDigitsStyle() === "native") {
9049 			digits = this.locinfo.getNativeDigits();
9050 			if (digits) {
9051 				this.useNative = true;
9052 				this.digits = digits;
9053 			}
9054 		}
9055 	},
9056     
9057 	/**
9058 	 * Convert the template into an array of date components separated by formatting chars.
9059 	 * @protected
9060 	 * @param {string} template Format template to tokenize into components
9061 	 * @return {Array.<string>} a tokenized array of date format components
9062 	 */
9063 	_tokenize: function (template) {
9064 		var i = 0, start, ch, letter, arr = [];
9065 		
9066 		// console.log("_tokenize: tokenizing template " + template);
9067 		if (template) {
9068 			while (i < template.length) {
9069 				ch = template.charAt(i);
9070 				start = i;
9071 				if (ch === "'") {
9072 					// console.log("found quoted string");
9073 					i++;
9074 					// escaped string - push as-is, then dequote later
9075 					while (i < template.length && template.charAt(i) !== "'") {
9076 						i++;
9077 					}
9078 					if (i < template.length) {
9079 						i++;	// grab the other quote too
9080 					}
9081 				} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
9082 					letter = template.charAt(i);
9083 					// console.log("found letters " + letter);
9084 					while (i < template.length && ch === letter) {
9085 						ch = template.charAt(++i);
9086 					}
9087 				} else {
9088 					// console.log("found other");
9089 					while (i < template.length && ch !== "'" && (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')) {
9090 						ch = template.charAt(++i);
9091 					}
9092 				}
9093 				arr.push(template.substring(start,i));
9094 				// console.log("start is " + start + " i is " + i + " and substr is " + template.substring(start,i));
9095 			}
9096 		}
9097 		return arr;
9098 	},
9099     
9100 	/**
9101 	 * @protected
9102 	 * @param {Object.<string, (string|{s:string,m:string,l:string,f:string})>} obj Object to search
9103 	 * @param {string} components Format components to search
9104 	 * @param {string} length Length of the requested format
9105 	 * @return {string|undefined} the requested format
9106 	 */
9107 	_getFormatInternal: function getFormatInternal(obj, components, length) {
9108 		if (typeof(components) !== 'undefined' && obj && obj[components]) {
9109 			return this._getLengthFormat(obj[components], length);
9110 		}
9111 		return undefined;
9112 	},
9113 
9114 	// stand-alone of m (month) is l
9115 	// stand-alone of d (day) is a
9116 	// stand-alone of w (weekday) is e
9117 	// stand-alone of y (year) is r
9118 	_standAlones: {
9119 		"m": "l",
9120 		"d": "a",
9121 		"w": "e",
9122 		"y": "r"
9123 	},
9124 	
9125 	/**
9126 	 * @protected
9127 	 * @param {Object.<string, (string|{s:string,m:string,l:string,f:string})>} obj Object to search
9128 	 * @param {string} components Format components to search
9129 	 * @param {string} length Length of the requested format
9130 	 * @return {string|undefined} the requested format
9131 	 */
9132 	_getFormat: function getFormat(obj, components, length) {
9133 		// handle some special cases for stand-alone formats
9134 		if (components && this._standAlones[components]) {
9135     		var tmp = this._getFormatInternal(obj, this._standAlones[components], length);
9136     		if (tmp) {
9137     			return tmp;
9138     		}
9139 		}
9140 		
9141 		// if no stand-alone format is available, fall back to the regular format
9142 		return this._getFormatInternal(obj, components, length);
9143 	},
9144 
9145 	/**
9146 	 * @protected
9147 	 * @param {(string|{s:string,m:string,l:string,f:string})} obj Object to search
9148 	 * @param {string} length Length of the requested format
9149 	 * @return {(string|undefined)} the requested format
9150 	 */
9151 	_getLengthFormat: function getLengthFormat(obj, length) {
9152 		if (typeof(obj) === 'string') {
9153 			return obj;
9154 		} else if (obj[length]) {
9155 			return obj[length];
9156 		}
9157 		return undefined;
9158 	},
9159 
9160 	/**
9161 	 * Return the locale used with this formatter instance.
9162 	 * @return {Locale} the Locale instance for this formatter
9163 	 */
9164 	getLocale: function() {
9165 		return this.locale;
9166 	},
9167 	
9168 	/**
9169 	 * Return the template string that is used to format date/times for this
9170 	 * formatter instance. This will work, even when the template property is not explicitly 
9171 	 * given in the options to the constructor. Without the template option, the constructor 
9172 	 * will build the appropriate template according to the options and use that template
9173 	 * in the format method. 
9174 	 * 
9175 	 * @return {string} the format template for this formatter
9176 	 */
9177 	getTemplate: function() {
9178 		return this.template;
9179 	},
9180 	
9181 	/**
9182 	 * Return the type of this formatter. The type is a string that has one of the following
9183 	 * values: "time", "date", "datetime".
9184 	 * @return {string} the type of the formatter
9185 	 */
9186 	getType: function() {
9187 		return this.type;
9188 	},
9189 	
9190 	/**
9191 	 * Return the name of the calendar used to format date/times for this
9192 	 * formatter instance.
9193 	 * @return {string} the name of the calendar used by this formatter
9194 	 */
9195 	getCalendar: function () {
9196 		return this.cal.getType();
9197 	},
9198 	
9199 	/**
9200 	 * Return the length used to format date/times in this formatter. This is either the
9201 	 * value of the length option to the constructor, or the default value.
9202 	 * 
9203 	 * @return {string} the length of formats this formatter returns
9204 	 */
9205 	getLength: function () {
9206 		return DateFmt.lenmap[this.length] || "";
9207 	},
9208 	
9209 	/**
9210 	 * Return the date components that this formatter formats. This is either the 
9211 	 * value of the date option to the constructor, or the default value. If this
9212 	 * formatter is a time-only formatter, this method will return the empty 
9213 	 * string. The date component letters may be specified in any order in the 
9214 	 * constructor, but this method will reorder the given components to a standard 
9215 	 * order.
9216 	 * 
9217 	 * @return {string} the date components that this formatter formats
9218 	 */
9219 	getDateComponents: function () {
9220 		return this.dateComponents || "";
9221 	},
9222 
9223 	/**
9224 	 * Return the time components that this formatter formats. This is either the 
9225 	 * value of the time option to the constructor, or the default value. If this
9226 	 * formatter is a date-only formatter, this method will return the empty 
9227 	 * string. The time component letters may be specified in any order in the 
9228 	 * constructor, but this method will reorder the given components to a standard 
9229 	 * order.
9230 	 * 
9231 	 * @return {string} the time components that this formatter formats
9232 	 */
9233 	getTimeComponents: function () {
9234 		return this.timeComponents || "";
9235 	},
9236 
9237 	/**
9238 	 * Return the time zone used to format date/times for this formatter
9239 	 * instance.
9240 	 * @return a string naming the time zone
9241 	 */
9242 	getTimeZone: function () {
9243 		// Lazy load the time zone. If it wasn't explicitly set up before, set 
9244 		// it up now, but use the 
9245 		// default TZ for the locale. This way, if the caller never uses the
9246 		// time zone in their format, we never have to load up a TimeZone
9247 		// instance into this formatter.
9248 		if (!this.tz) {
9249 			this.tz = new TimeZone({id: ilib.getTimeZone()});
9250 		}
9251 		return this.tz;
9252 	},
9253 	/**
9254 	 * Return the clock option set in the constructor. If the clock option was
9255 	 * not given, the default from the locale is returned instead.
9256 	 * @return {string} "12" or "24" depending on whether this formatter uses
9257 	 * the 12-hour or 24-hour clock
9258 	 */
9259 	getClock: function () {
9260 		return this.clock || this.locinfo.getClock();
9261 	},
9262 	/**
9263 	 * Return the meridiems range in current locale. 
9264 	 * @return {Array.<{name:string,start:string,end:string}>} the range of available meridiems
9265 	 */
9266 	getMeridiemsRange: function () {
9267 		var result;
9268 		var _getSysString = function (key) {
9269 			return (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key)).toString();
9270 		};
9271 
9272 		switch (this.meridiems) {
9273 		case "chinese":
9274 			result = [
9275 				{
9276 					name: _getSysString.call(this, "azh0"),
9277 					start: "00:00",
9278 					end: "05:59"
9279 				},
9280 				{
9281 					name: _getSysString.call(this, "azh1"),
9282 					start: "06:00",
9283 					end: "08:59"
9284 				},
9285 				{
9286 					name: _getSysString.call(this, "azh2"),
9287 					start: "09:00",
9288 					end: "11:59"
9289 				},
9290 				{
9291 					name: _getSysString.call(this, "azh3"),
9292 					start: "12:00",
9293 					end: "12:59"
9294 				},
9295 				{
9296 					name: _getSysString.call(this, "azh4"),
9297 					start: "13:00",
9298 					end: "17:59"
9299 				},
9300 				{
9301 					name: _getSysString.call(this, "azh5"),
9302 					start: "18:00",
9303 					end: "20:59"
9304 				},
9305 				{
9306 					name: _getSysString.call(this, "azh6"),
9307 					start: "21:00",
9308 					end: "23:59"
9309 				}
9310 			];
9311 			break;
9312 		case "ethiopic":
9313 			result = [
9314 				{
9315 					name: _getSysString.call(this, "a0-ethiopic"),
9316 					start: "00:00",
9317 					end: "05:59"
9318 				},
9319 				{
9320 					name: _getSysString.call(this, "a1-ethiopic"),
9321 					start: "06:00",
9322 					end: "06:00"
9323 				},
9324 				{
9325 					name: _getSysString.call(this, "a2-ethiopic"),
9326 					start: "06:01",
9327 					end: "11:59"
9328 				},
9329 				{
9330 					name: _getSysString.call(this, "a3-ethiopic"),
9331 					start: "12:00",
9332 					end: "17:59"
9333 				},
9334 				{
9335 					name: _getSysString.call(this, "a4-ethiopic"),
9336 					start: "18:00",
9337 					end: "23:59"
9338 				}
9339 			];
9340 			break;
9341 		default:
9342 			result = [
9343 				{
9344 					name: _getSysString.call(this, "a0"),
9345 					start: "00:00",
9346 					end: "11:59"
9347 				},
9348 				{
9349 					name: _getSysString.call(this, "a1"),
9350 					start: "12:00",
9351 					end: "23:59"
9352 				}
9353 			];
9354 			break;
9355 		}
9356 
9357 		return result;
9358 	},
9359 	
9360 	/**
9361 	 * @private
9362 	 */
9363 	_getTemplate: function (prefix, calendar) {
9364 		if (calendar !== "gregorian") {
9365 			return prefix + "-" + calendar;
9366 		}
9367 		return prefix;
9368 	},
9369 
9370 	/**
9371 	 * Returns an array of the months of the year, formatted to the optional length specified.
9372 	 * i.e. ...getMonthsOfYear() OR ...getMonthsOfYear({length: "short"})
9373 	 * <p>
9374 	 * The options parameter may contain any of the following properties:
9375 	 * 
9376 	 * <ul>
9377 	 * <li><i>length</i> - length of the names of the months being sought. This may be one of
9378 	 * "short", "medium", "long", or "full"
9379 	 * <li><i>date</i> - retrieve the names of the months in the date of the given date
9380 	 * <li><i>year</i> - retrieve the names of the months in the given year. In some calendars,
9381 	 * the months have different names depending if that year is a leap year or not.
9382 	 * </ul>
9383 	 * 
9384 	 * @param  {Object=} options an object-literal that contains any of the above properties
9385 	 * @return {Array} an array of the names of all of the months of the year in the current calendar
9386 	 */
9387 	getMonthsOfYear: function(options) {
9388 		var length = (options && options.length) || this.getLength(),
9389 			template = DateFmt.monthNameLenMap[length],
9390 			months = [undefined],
9391 			date,
9392 			monthCount;
9393 		
9394 		if (options) {
9395 			if (options.date) {
9396 				date = DateFactory._dateToIlib(options.date); 	
9397 			}
9398 			
9399 			if (options.year) {
9400 				date = DateFactory({year: options.year, month: 1, day: 1, type: this.cal.getType()});
9401 			}
9402 		}
9403 		
9404 		if (!date) {
9405 			date = DateFactory({
9406 				calendar: this.cal.getType()
9407 			});
9408 		}
9409 
9410 		monthCount = this.cal.getNumMonths(date.getYears());
9411 		for (var i = 1; i <= monthCount; i++) {
9412 			months[i] = this.sysres.getString(this._getTemplate(template + i, this.cal.getType())).toString();
9413 		}
9414 		return months;
9415 	},
9416 
9417 	/**
9418 	 * Returns an array of the days of the week, formatted to the optional length specified.
9419 	 * i.e. ...getDaysOfWeek() OR ...getDaysOfWeek({length: "short"})
9420 	 * <p>
9421 	 * The options parameter may contain any of the following properties:
9422 	 * 
9423 	 * <ul>
9424 	 * <li><i>length</i> - length of the names of the months being sought. This may be one of
9425 	 * "short", "medium", "long", or "full"
9426 	 * </ul>
9427 	 * @param  {Object=} options an object-literal that contains one key 
9428 	 *                   "length" with the standard length strings
9429 	 * @return {Array} an array of all of the names of the days of the week
9430 	 */
9431 	getDaysOfWeek: function(options) {
9432 		var length = (options && options.length) || this.getLength(),
9433 			template = DateFmt.weekDayLenMap[length],
9434 			days = [];
9435 		for (var i = 0; i < 7; i++) {
9436 			days[i] = this.sysres.getString(this._getTemplate(template + i, this.cal.getType())).toString();
9437 		}
9438 		return days;
9439 	},
9440 
9441 	
9442 	/**
9443 	 * Convert this formatter to a string representation by returning the
9444 	 * format template. This method delegates to getTemplate.
9445 	 * 
9446 	 * @return {string} the format template
9447 	 */
9448 	toString: function() {
9449 		return this.getTemplate();
9450 	},
9451 		
9452 	/**
9453 	 * @private
9454 	 * Format a date according to a sequence of components. 
9455 	 * @param {IDate} date a date/time object to format
9456 	 * @param {Array.<string>} templateArr an array of components to format
9457 	 * @return {string} the formatted date
9458 	 */
9459 	_formatTemplate: function (date, templateArr) {
9460 		var i, key, temp, tz, str = "";
9461 		for (i = 0; i < templateArr.length; i++) {
9462 			switch (templateArr[i]) {
9463 				case 'd':
9464 					str += (date.day || 1);
9465 					break;
9466 				case 'dd':
9467 					str += JSUtils.pad(date.day || "1", 2);
9468 					break;
9469 				case 'yy':
9470 					temp = "" + ((date.year || 0) % 100);
9471 					str += JSUtils.pad(temp, 2);
9472 					break;
9473 				case 'yyyy':
9474 					str += JSUtils.pad(date.year || "0", 4);
9475 					break;
9476 				case 'M':
9477 					str += (date.month || 1);
9478 					break;
9479 				case 'MM':
9480 					str += JSUtils.pad(date.month || "1", 2);
9481 					break;
9482 				case 'h':
9483 					temp = (date.hour || 0) % 12;
9484 					if (temp == 0) {
9485 						temp = "12";
9486 					}
9487 					str += temp; 
9488 					break;
9489 				case 'hh':
9490 					temp = (date.hour || 0) % 12;
9491 					if (temp == 0) {
9492 						temp = "12";
9493 					}
9494 					str += JSUtils.pad(temp, 2);
9495 					break;
9496 				/*
9497 				case 'j':
9498 					temp = (date.hour || 0) % 12 + 1;
9499 					str += temp; 
9500 					break;
9501 				case 'jj':
9502 					temp = (date.hour || 0) % 12 + 1;
9503 					str += JSUtils.pad(temp, 2);
9504 					break;
9505 				*/
9506 				case 'K':
9507 					temp = (date.hour || 0) % 12;
9508 					str += temp; 
9509 					break;
9510 				case 'KK':
9511 					temp = (date.hour || 0) % 12;
9512 					str += JSUtils.pad(temp, 2);
9513 					break;
9514 
9515 				case 'H':
9516 					str += (date.hour || "0");
9517 					break;
9518 				case 'HH':
9519 					str += JSUtils.pad(date.hour || "0", 2);
9520 					break;
9521 				case 'k':
9522 					str += (date.hour == 0 ? "24" : date.hour);
9523 					break;
9524 				case 'kk':
9525 					temp = (date.hour == 0 ? "24" : date.hour);
9526 					str += JSUtils.pad(temp, 2);
9527 					break;
9528 
9529 				case 'm':
9530 					str += (date.minute || "0");
9531 					break;
9532 				case 'mm':
9533 					str += JSUtils.pad(date.minute || "0", 2);
9534 					break;
9535 				case 's':
9536 					str += (date.minute || "0");
9537 					break;
9538 				case 'ss':
9539 					str += JSUtils.pad(date.second || "0", 2);
9540 					break;
9541 				case 'S':
9542 					str += (date.millisecond || "0");
9543 					break;
9544 				case 'SSS':
9545 					str += JSUtils.pad(date.millisecond || "0", 3);
9546 					break;
9547 
9548 				case 'N':
9549 				case 'NN':
9550 				case 'MMM':
9551 				case 'MMMM':
9552 				case 'L':
9553 				case 'LL':
9554 				case 'LLL':
9555 				case 'LLLL':
9556 					key = templateArr[i] + (date.month || 1);
9557 					str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key));
9558 					break;
9559 					
9560 				case 'E':
9561 				case 'EE':
9562 				case 'EEE':
9563 				case 'EEEE':
9564 				case 'c':
9565 				case 'cc':
9566 				case 'ccc':
9567 				case 'cccc':
9568 					key = templateArr[i] + date.getDayOfWeek();
9569 					//console.log("finding " + key + " in the resources");
9570 					str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key));
9571 					break;
9572 
9573 				case 'a':
9574 					switch (this.meridiems) {
9575 					case "chinese":
9576 						if (date.hour < 6) {
9577 							key = "azh0";	// before dawn
9578 						} else if (date.hour < 9) {
9579 							key = "azh1";	// morning
9580 						} else if (date.hour < 12) {
9581 							key = "azh2";	// late morning/day before noon
9582 						} else if (date.hour < 13) {
9583 							key = "azh3";	// noon hour/midday
9584 						} else if (date.hour < 18) {
9585 							key = "azh4";	// afternoon
9586 						} else if (date.hour < 21) {
9587 							key = "azh5";	// evening time/dusk
9588 						} else {
9589 							key = "azh6";	// night time
9590 						}
9591 						break;
9592 					case "ethiopic":
9593 						if (date.hour < 6) {
9594 							key = "a0-ethiopic";	// morning
9595 						} else if (date.hour === 6 && date.minute === 0) {
9596 							key = "a1-ethiopic";	// noon
9597 						} else if (date.hour >= 6 && date.hour < 12) {
9598 							key = "a2-ethiopic";	// afternoon
9599 						} else if (date.hour >= 12 && date.hour < 18) {
9600 							key = "a3-ethiopic";	// evening
9601 						} else if (date.hour >= 18) {
9602 							key = "a4-ethiopic";	// night
9603 						}
9604 						break;
9605 					default:
9606 						key = date.hour < 12 ? "a0" : "a1";
9607 						break;
9608 					}
9609 					//console.log("finding " + key + " in the resources");
9610 					str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key));
9611 					break;
9612 					
9613 				case 'w':
9614 					str += date.getWeekOfYear();
9615 					break;
9616 				case 'ww':
9617 					str += JSUtils.pad(date.getWeekOfYear(), 2);
9618 					break;
9619 
9620 				case 'D':
9621 					str += date.getDayOfYear();
9622 					break;
9623 				case 'DD':
9624 					str += JSUtils.pad(date.getDayOfYear(), 2);
9625 					break;
9626 				case 'DDD':
9627 					str += JSUtils.pad(date.getDayOfYear(), 3);
9628 					break;
9629 				case 'W':
9630 					str += date.getWeekOfMonth(this.locale);
9631 					break;
9632 
9633 				case 'G':
9634 					key = "G" + date.getEra();
9635 					str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key));
9636 					break;
9637 
9638 				case 'O':
9639 					temp = this.sysres.getString("1#1st|2#2nd|3#3rd|21#21st|22#22nd|23#23rd|31#31st|#{num}th", "ordinalChoice");
9640 					str += temp.formatChoice(date.day, {num: date.day});
9641 					break;
9642 					
9643 				case 'z': // general time zone
9644 					tz = this.getTimeZone(); // lazy-load the tz
9645 					str += tz.getDisplayName(date, "standard");
9646 					break;
9647 				case 'Z': // RFC 822 time zone
9648 					tz = this.getTimeZone(); // lazy-load the tz
9649 					str += tz.getDisplayName(date, "rfc822");
9650 					break;
9651 
9652 				default:
9653 					str += templateArr[i].replace(/'/g, "");
9654 					break;
9655 			}
9656 		}
9657 
9658 		if (this.digits) {
9659 			str = JSUtils.mapString(str, this.digits);
9660 		}
9661 		return str;
9662 	},
9663 	
9664 	/**
9665 	 * Format a particular date instance according to the settings of this
9666 	 * formatter object. The type of the date instance being formatted must 
9667 	 * correspond exactly to the calendar type with which this formatter was 
9668 	 * constructed. If the types are not compatible, this formatter will
9669 	 * produce bogus results.
9670 	 * 
9671 	 * @param {IDate|Number|String|Date|JulianDay|null|undefined} dateLike a date-like object to format
9672 	 * @return {string} the formatted version of the given date instance
9673 	 */
9674 	format: function (dateLike) {
9675 		var thisZoneName = this.tz && this.tz.getId() || "local";
9676 
9677 		var date = DateFactory._dateToIlib(dateLike, thisZoneName, this.locale);
9678 		
9679 		if (!date.getCalendar || !(date instanceof IDate)) {
9680 			throw "Wrong date type passed to DateFmt.format()";
9681 		}
9682 		
9683 		var dateZoneName = date.timezone || "local";
9684 		
9685 		// convert to the time zone of this formatter before formatting
9686 		if (dateZoneName !== thisZoneName || date.getCalendar() !== this.calName) {
9687 			// console.log("Differing time zones date: " + dateZoneName + " and fmt: " + thisZoneName + ". Converting...");
9688 			// this will recalculate the date components based on the new time zone
9689 			// and/or convert a date in another calendar to the current calendar before formatting it
9690 			var newDate = DateFactory({
9691 				type: this.calName,
9692 				timezone: thisZoneName,
9693 				julianday: date.getJulianDay()
9694 			});
9695 			
9696 			date = newDate;
9697 		}
9698 		return this._formatTemplate(date, this.templateArr);
9699 	},
9700 	
9701 	/**
9702 	 * Return a string that describes a date relative to the given 
9703 	 * reference date. The string returned is text that for the locale that
9704 	 * was specified when the formatter instance was constructed.<p>
9705 	 * 
9706 	 * The date can be in the future relative to the reference date or in
9707 	 * the past, and the formatter will generate the appropriate string.<p>
9708 	 * 
9709 	 * The text used to describe the relative reference depends on the length
9710 	 * of time between the date and the reference. If the time was in the
9711 	 * past, it will use the "ago" phrase, and in the future, it will use
9712 	 * the "in" phrase. Examples:<p>
9713 	 * 
9714 	 * <ul>
9715 	 * <li>within a minute: either "X seconds ago" or "in X seconds"
9716 	 * <li>within an hour: either "X minutes ago" or "in X minutes"
9717 	 * <li>within a day: either "X hours ago" or "in X hours"
9718 	 * <li>within 2 weeks: either "X days ago" or "in X days"
9719 	 * <li>within 12 weeks (~3 months): either "X weeks ago" or "in X weeks"
9720 	 * <li>within two years: either "X months ago" or "in X months"
9721 	 * <li>longer than 2 years: "X years ago" or "in X years"
9722 	 * </ul>
9723 	 * 
9724 	 * @param {IDate|Number|String|Date|JulianDay|null|undefined} reference a date that the date parameter should be relative to
9725 	 * @param {IDate|Number|String|Date|JulianDay|null|undefined} date a date being formatted
9726 	 * @throws "Wrong calendar type" when the start or end dates are not the same
9727 	 * calendar type as the formatter itself
9728 	 * @return {string} the formatted relative date
9729 	 */
9730 	formatRelative: function(reference, date) {
9731 		reference = DateFactory._dateToIlib(reference);
9732 		date = DateFactory._dateToIlib(date);
9733 		
9734 		var referenceRd, dateRd, fmt, time, diff, num;
9735 		
9736 		if (typeof(reference) !== 'object' || !reference.getCalendar || reference.getCalendar() !== this.calName ||
9737 			typeof(date) !== 'object' || !date.getCalendar || date.getCalendar() !== this.calName) {
9738 			throw "Wrong calendar type";
9739 		}
9740 		
9741 		referenceRd = reference.getRataDie();
9742 		dateRd = date.getRataDie();
9743 		
9744 		if (dateRd < referenceRd) {
9745 			diff = referenceRd - dateRd;
9746 			fmt = this.sysres.getString("{duration} ago");
9747 		} else {
9748 			diff = dateRd - referenceRd;
9749 			fmt = this.sysres.getString("in {duration}");
9750 		}
9751 		
9752 		if (diff < 0.000694444) {
9753 			num = Math.round(diff * 86400);
9754 			switch (this.length) {
9755 				case 's':
9756 					time = this.sysres.getString("#{num}s");
9757 					break;
9758 				case 'm':
9759 					time = this.sysres.getString("1#1 se|#{num} sec");
9760 					break;
9761 				case 'l':
9762 					time = this.sysres.getString("1#1 sec|#{num} sec");
9763 					break;
9764 				default:
9765 				case 'f':
9766 					time = this.sysres.getString("1#1 second|#{num} seconds");
9767 					break;
9768 			}
9769 		} else if (diff < 0.041666667) {
9770 			num = Math.round(diff * 1440);
9771 			switch (this.length) {
9772 				case 's':
9773 					time = this.sysres.getString("#{num}m", "durationShortMinutes");
9774 					break;
9775 				case 'm':
9776 					time = this.sysres.getString("1#1 mi|#{num} min");
9777 					break;
9778 				case 'l':
9779 					time = this.sysres.getString("1#1 min|#{num} min");
9780 					break;
9781 				default:
9782 				case 'f':
9783 					time = this.sysres.getString("1#1 minute|#{num} minutes");
9784 					break;
9785 			}
9786 		} else if (diff < 1) {
9787 			num = Math.round(diff * 24);
9788 			switch (this.length) {
9789 				case 's':
9790 					time = this.sysres.getString("#{num}h");
9791 					break;
9792 				case 'm':
9793 					time = this.sysres.getString("1#1 hr|#{num} hrs", "durationMediumHours");
9794 					break;
9795 				case 'l':
9796 					time = this.sysres.getString("1#1 hr|#{num} hrs");
9797 					break;
9798 				default:
9799 				case 'f':
9800 					time = this.sysres.getString("1#1 hour|#{num} hours");
9801 					break;
9802 			}
9803 		} else if (diff < 14) {
9804 			num = Math.round(diff);
9805 			switch (this.length) {
9806 				case 's':
9807 					time = this.sysres.getString("#{num}d");
9808 					break;
9809 				case 'm':
9810 					time = this.sysres.getString("1#1 dy|#{num} dys");
9811 					break;
9812 				case 'l':
9813 					time = this.sysres.getString("1#1 day|#{num} days", "durationLongDays");
9814 					break;
9815 				default:
9816 				case 'f':
9817 					time = this.sysres.getString("1#1 day|#{num} days");
9818 					break;
9819 			}
9820 		} else if (diff < 84) {
9821 			num = Math.round(diff/7);
9822 			switch (this.length) {
9823 				case 's':
9824 					time = this.sysres.getString("#{num}w");
9825 					break;
9826 				case 'm':
9827 					time = this.sysres.getString("1#1 wk|#{num} wks", "durationMediumWeeks");
9828 					break;
9829 				case 'l':
9830 					time = this.sysres.getString("1#1 wk|#{num} wks");
9831 					break;
9832 				default:
9833 				case 'f':
9834 					time = this.sysres.getString("1#1 week|#{num} weeks");
9835 					break;
9836 			}
9837 		} else if (diff < 730) {
9838 			num = Math.round(diff/30.4);
9839 			switch (this.length) {
9840 				case 's':
9841 					time = this.sysres.getString("#{num}m", "durationShortMonths");
9842 					break;
9843 				case 'm':
9844 					time = this.sysres.getString("1#1 mo|#{num} mos");
9845 					break;
9846 				case 'l':
9847 					time = this.sysres.getString("1#1 mon|#{num} mons");
9848 					break;
9849 				default:
9850 				case 'f':
9851 					time = this.sysres.getString("1#1 month|#{num} months");
9852 					break;
9853 			}
9854 		} else {
9855 			num = Math.round(diff/365);
9856 			switch (this.length) {
9857 				case 's':
9858 					time = this.sysres.getString("#{num}y");
9859 					break;
9860 				case 'm':
9861 					time = this.sysres.getString("1#1 yr|#{num} yrs", "durationMediumYears");
9862 					break;
9863 				case 'l':
9864 					time = this.sysres.getString("1#1 yr|#{num} yrs");
9865 					break;
9866 				default:
9867 				case 'f':
9868 					time = this.sysres.getString("1#1 year|#{num} years");
9869 					break;
9870 			}
9871 		}
9872 		return fmt.format({duration: time.formatChoice(num, {num: num})});
9873 	}
9874 };
9875 
9876 
9877 
9878 /*< DateRngFmt.js */
9879 /*
9880  * DateFmt.js - Date formatter definition
9881  * 
9882  * Copyright © 2012-2015, JEDLSoft
9883  *
9884  * Licensed under the Apache License, Version 2.0 (the "License");
9885  * you may not use this file except in compliance with the License.
9886  * You may obtain a copy of the License at
9887  *
9888  *     http://www.apache.org/licenses/LICENSE-2.0
9889  *
9890  * Unless required by applicable law or agreed to in writing, software
9891  * distributed under the License is distributed on an "AS IS" BASIS,
9892  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9893  *
9894  * See the License for the specific language governing permissions and
9895  * limitations under the License.
9896  */
9897 
9898 /*
9899 !depends 
9900 ilib.js 
9901 Locale.js 
9902 IDate.js 
9903 IString.js 
9904 CalendarFactory.js
9905 LocaleInfo.js
9906 TimeZone.js
9907 DateFmt.js
9908 GregorianCal.js
9909 JSUtils.js
9910 Utils.js
9911 */
9912 
9913 // !data dateformats sysres
9914 
9915 
9916 
9917 
9918 
9919 /**
9920  * @class
9921  * Create a new date range formatter instance. The date range formatter is immutable once
9922  * it is created, but can format as many different date ranges as needed with the same
9923  * options. Create different date range formatter instances for different purposes
9924  * and then keep them cached for use later if you have more than one range to
9925  * format.<p>
9926  * 
9927  * The options may contain any of the following properties:
9928  * 
9929  * <ul>
9930  * <li><i>locale</i> - locale to use when formatting the date/times in the range. If the 
9931  * locale is not specified, then the default locale of the app or web page will be used.
9932  * 
9933  * <li><i>calendar</i> - the type of calendar to use for this format. The value should
9934  * be a sting containing the name of the calendar. Currently, the supported
9935  * types are "gregorian", "julian", "arabic", "hebrew", or "chinese". If the
9936  * calendar is not specified, then the default calendar for the locale is used. When the
9937  * calendar type is specified, then the format method must be called with an instance of
9938  * the appropriate date type. (eg. Gregorian calendar means that the format method must 
9939  * be called with a GregDate instance.)
9940  *  
9941  * <li><i>timezone</i> - time zone to use when formatting times. This may be a time zone
9942  * instance or a time zone specifier string in RFC 822 format. If not specified, the
9943  * default time zone for the locale is used.
9944  * 
9945  * <li><i>length</i> - Specify the length of the format to use as a string. The length 
9946  * is the approximate size of the formatted string.
9947  * 
9948  * <ul>
9949  * <li><i>short</i> - use a short representation of the time. This is the most compact format possible for the locale.
9950  * <li><i>medium</i> - use a medium length representation of the time. This is a slightly longer format.
9951  * <li><i>long</i> - use a long representation of the time. This is a fully specified format, but some of the textual 
9952  * components may still be abbreviated. (eg. "Tue" instead of "Tuesday")
9953  * <li><i>full</i> - use a full representation of the time. This is a fully specified format where all the textual 
9954  * components are spelled out completely.
9955  * </ul>
9956  * 
9957  * eg. The "short" format for an en_US range may be "MM/yy - MM/yy", whereas the long format might be 
9958  * "MMM, yyyy - MMM, yyyy". In the long format, the month name is textual instead of numeric 
9959  * and is longer, the year is 4 digits instead of 2, and the format contains slightly more 
9960  * spaces and formatting characters.<p>
9961  * 
9962  * Note that the length parameter does not specify which components are to be formatted. The
9963  * components that are formatted depend on the length of time in the range.
9964  * 
9965  * <li><i>clock</i> - specify that formatted times should use a 12 or 24 hour clock if the
9966  * format happens to include times. Valid values are "12" and "24".<p>
9967  * 
9968  * In some locales, both clocks are used. For example, in en_US, the general populace uses
9969  * a 12 hour clock with am/pm, but in the US military or in nautical or aeronautical or 
9970  * scientific writing, it is more common to use a 24 hour clock. This property allows you to
9971  * construct a formatter that overrides the default for the locale.<p>
9972  * 
9973  * If this property is not specified, the default is to use the most widely used convention
9974  * for the locale.
9975  * <li>onLoad - a callback function to call when the date range format object is fully 
9976  * loaded. When the onLoad option is given, the DateRngFmt object will attempt to
9977  * load any missing locale data using the ilib loader callback.
9978  * When the constructor is done (even if the data is already preassembled), the 
9979  * onLoad function is called with the current instance as a parameter, so this
9980  * callback can be used with preassembled or dynamic loading or a mix of the two. 
9981  * 
9982  * <li>sync - tell whether to load any missing locale data synchronously or 
9983  * asynchronously. If this option is given as "false", then the "onLoad"
9984  * callback must be given, as the instance returned from this constructor will
9985  * not be usable for a while.
9986  *  
9987  * <li><i>loadParams</i> - an object containing parameters to pass to the 
9988  * loader callback function when locale data is missing. The parameters are not
9989  * interpretted or modified in any way. They are simply passed along. The object 
9990  * may contain any property/value pairs as long as the calling code is in
9991  * agreement with the loader callback function as to what those parameters mean.
9992  * </ul>
9993  * <p>
9994  * 
9995  * 
9996  * @constructor
9997  * @param {Object} options options governing the way this date range formatter instance works
9998  */
9999 var DateRngFmt = function(options) {
10000 	var sync = true;
10001 	var loadParams = undefined;
10002 	this.locale = new Locale();
10003 	this.length = "s";
10004 	
10005 	if (options) {
10006 		if (options.locale) {
10007 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
10008 		}
10009 		
10010 		if (options.calendar) {
10011 			this.calName = options.calendar;
10012 		}
10013 		
10014 		if (options.length) {
10015 			if (options.length === 'short' ||
10016 				options.length === 'medium' ||
10017 				options.length === 'long' ||
10018 				options.length === 'full') {
10019 				// only use the first char to save space in the json files
10020 				this.length = options.length.charAt(0);
10021 			}
10022 		}
10023 		if (typeof(options.sync) !== 'undefined') {
10024 			sync = (options.sync == true);
10025 		}
10026 		
10027 		loadParams = options.loadParams;
10028 	}
10029 	
10030 	var opts = {};
10031 	JSUtils.shallowCopy(options, opts);
10032 	opts.sync = sync;
10033 	opts.loadParams = loadParams;
10034 	
10035 	/**
10036 	 * @private
10037 	 */
10038 	opts.onLoad = ilib.bind(this, function (fmt) {
10039 		this.dateFmt = fmt;
10040 		if (fmt) {
10041 			this.locinfo = this.dateFmt.locinfo;
10042 
10043 			// get the default calendar name from the locale, and if the locale doesn't define
10044 			// one, use the hard-coded gregorian as the last resort
10045 			this.calName = this.calName || this.locinfo.getCalendar() || "gregorian";
10046 			this.cal = CalendarFactory({
10047 				type: this.calName
10048 			});
10049 			if (!this.cal) {
10050 				this.cal = new GregorianCal();
10051 			}
10052 			
10053 			this.timeTemplate = this.dateFmt._getFormat(this.dateFmt.formats.time[this.dateFmt.clock], this.dateFmt.timeComponents, this.length) || "hh:mm";
10054 			this.timeTemplateArr = this.dateFmt._tokenize(this.timeTemplate);
10055 			
10056 			if (options && typeof(options.onLoad) === 'function') {
10057 				options.onLoad(this);
10058 			}
10059 		}
10060 	});
10061 
10062 	// delegate a bunch of the formatting to this formatter
10063 	new DateFmt(opts);
10064 };
10065 
10066 DateRngFmt.prototype = {
10067 	/**
10068 	 * Return the locale used with this formatter instance.
10069 	 * @return {Locale} the Locale instance for this formatter
10070 	 */
10071 	getLocale: function() {
10072 		return this.locale;
10073 	},
10074 	
10075 	/**
10076 	 * Return the name of the calendar used to format date/times for this
10077 	 * formatter instance.
10078 	 * @return {string} the name of the calendar used by this formatter
10079 	 */
10080 	getCalendar: function () {
10081 		return this.dateFmt.getCalendar();
10082 	},
10083 	
10084 	/**
10085 	 * Return the length used to format date/times in this formatter. This is either the
10086 	 * value of the length option to the constructor, or the default value.
10087 	 * 
10088 	 * @return {string} the length of formats this formatter returns
10089 	 */
10090 	getLength: function () {
10091 		return DateFmt.lenmap[this.length] || "";
10092 	},
10093 	
10094 	/**
10095 	 * Return the time zone used to format date/times for this formatter
10096 	 * instance.
10097 	 * @return {TimeZone} a string naming the time zone
10098 	 */
10099 	getTimeZone: function () {
10100 		return this.dateFmt.getTimeZone();
10101 	},
10102 	
10103 	/**
10104 	 * Return the clock option set in the constructor. If the clock option was
10105 	 * not given, the default from the locale is returned instead.
10106 	 * @return {string} "12" or "24" depending on whether this formatter uses
10107 	 * the 12-hour or 24-hour clock
10108 	 */
10109 	getClock: function () {
10110 		return this.dateFmt.getClock();
10111 	},
10112 	
10113 	/**
10114 	 * Format a date/time range according to the settings of the current
10115 	 * formatter. The range is specified as being from the "start" date until
10116 	 * the "end" date. <p>
10117 	 * 
10118 	 * The template that the date/time range uses depends on the
10119 	 * length of time between the dates, on the premise that a long date range
10120 	 * which is too specific is not useful. For example, when giving
10121 	 * the dates of the 100 Years War, in most situations it would be more 
10122 	 * appropriate to format the range as "1337 - 1453" than to format it as 
10123 	 * "10:37am November 9, 1337 - 4:37pm July 17, 1453", as the latter format 
10124 	 * is much too specific given the length of time that the range represents.
10125 	 * If a very specific, but long, date range really is needed, the caller 
10126 	 * should format two specific dates separately and put them 
10127 	 * together as you might with other normal strings.<p>
10128 	 * 
10129 	 * The format used for a date range contains the following date components,
10130 	 * where the order of those components is rearranged and the component values 
10131 	 * are translated according to each locale:
10132 	 * 
10133 	 * <ul>
10134 	 * <li>within 3 days: the times of day, dates, months, and years
10135 	 * <li>within 730 days (2 years): the dates, months, and years
10136 	 * <li>within 3650 days (10 years): the months and years
10137 	 * <li>longer than 10 years: the years only 
10138 	 * </ul>
10139 	 * 
10140 	 * In general, if any of the date components share a value between the
10141 	 * start and end date, that component is only given once. For example,
10142 	 * if the range is from November 15, 2011 to November 26, 2011, the 
10143 	 * start and end dates both share the same month and year. The 
10144 	 * range would then be formatted as "November 15-26, 2011". <p>
10145 	 * 
10146 	 * If you want to format a length of time instead of a particular range of
10147 	 * time (for example, the length of an event rather than the specific start time
10148 	 * and end time of that event), then use a duration formatter instance 
10149 	 * (DurationFmt) instead. The formatRange method will make sure that each component 
10150 	 * of the date/time is within the normal range for that component. For example, 
10151 	 * the minutes will always be between 0 and 59, no matter what is specified in 
10152 	 * the date to format, because that is the normal range for minutes. A duration 
10153 	 * format will allow the number of minutes to exceed 59. For example, if you 
10154 	 * were displaying the length of a movie that is 198 minutes long, the minutes
10155 	 * component of a duration could be 198.<p>
10156 	 * 
10157 	 * @param {IDate} start the starting date/time of the range. This must be of 
10158 	 * the same calendar type as the formatter itself. 
10159 	 * @param {IDate} end the ending date/time of the range. This must be of the 
10160 	 * same calendar type as the formatter itself.
10161 	 * @throws "Wrong calendar type" when the start or end dates are not the same
10162 	 * calendar type as the formatter itself
10163 	 * @return {string} a date range formatted for the locale
10164 	 */
10165 	format: function (start, end) {
10166 		var startRd, endRd, fmt = "", yearTemplate, monthTemplate, dayTemplate, formats;
10167 		
10168 		if (typeof(start) !== 'object' || !start.getCalendar || start.getCalendar() !== this.calName ||
10169 			typeof(end) !== 'object' || !end.getCalendar || end.getCalendar() !== this.calName) {
10170 			throw "Wrong calendar type";
10171 		}
10172 		
10173 		startRd = start.getRataDie();
10174 		endRd = end.getRataDie();
10175 		
10176 		// 
10177 		// legend:
10178 		// c00 - difference is less than 3 days. Year, month, and date are same, but time is different
10179 		// c01 - difference is less than 3 days. Year and month are same but date and time are different
10180 		// c02 - difference is less than 3 days. Year is same but month, date, and time are different. (ie. it straddles a month boundary)
10181 		// c03 - difference is less than 3 days. Year, month, date, and time are all different. (ie. it straddles a year boundary)
10182 		// c10 - difference is less than 2 years. Year and month are the same, but date is different.
10183 		// c11 - difference is less than 2 years. Year is the same, but month, date, and time are different.
10184 		// c12 - difference is less than 2 years. All fields are different. (ie. straddles a year boundary)
10185 		// c20 - difference is less than 10 years. All fields are different.
10186 		// c30 - difference is more than 10 years. All fields are different.
10187 		//
10188 		
10189 		if (endRd - startRd < 3) {
10190 			if (start.year === end.year) {
10191 				if (start.month === end.month) {
10192 					if (start.day === end.day) {
10193 						fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c00", this.length));
10194 					} else {
10195 						fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c01", this.length));
10196 					}
10197 				} else {
10198 					fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c02", this.length));
10199 				}
10200 			} else {
10201 				fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c03", this.length));
10202 			}
10203 		} else if (endRd - startRd < 730) {
10204 			if (start.year === end.year) {
10205 				if (start.month === end.month) {
10206 					fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c10", this.length));
10207 				} else {
10208 					fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c11", this.length));
10209 				}
10210 			} else {
10211 				fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c12", this.length));
10212 			}
10213 		} else if (endRd - startRd < 3650) {
10214 			fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c20", this.length));
10215 		} else {
10216 			fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c30", this.length));
10217 		}
10218 
10219 		formats = this.dateFmt.formats.date;
10220 		yearTemplate = this.dateFmt._tokenize(this.dateFmt._getFormatInternal(formats, "y", this.length) || "yyyy");
10221 		monthTemplate = this.dateFmt._tokenize(this.dateFmt._getFormatInternal(formats, "m", this.length) || "MM");
10222 		dayTemplate = this.dateFmt._tokenize(this.dateFmt._getFormatInternal(formats, "d", this.length) || "dd");
10223 		
10224 		/*
10225 		console.log("fmt is " + fmt.toString());
10226 		console.log("year template is " + yearTemplate);
10227 		console.log("month template is " + monthTemplate);
10228 		console.log("day template is " + dayTemplate);
10229 		*/
10230 		
10231 		return fmt.format({
10232 			sy: this.dateFmt._formatTemplate(start, yearTemplate),
10233 			sm: this.dateFmt._formatTemplate(start, monthTemplate),
10234 			sd: this.dateFmt._formatTemplate(start, dayTemplate),
10235 			st: this.dateFmt._formatTemplate(start, this.timeTemplateArr),
10236 			ey: this.dateFmt._formatTemplate(end, yearTemplate),
10237 			em: this.dateFmt._formatTemplate(end, monthTemplate),
10238 			ed: this.dateFmt._formatTemplate(end, dayTemplate),
10239 			et: this.dateFmt._formatTemplate(end, this.timeTemplateArr)
10240 		});
10241 	}
10242 };
10243 
10244 
10245 /*< HebrewCal.js */
10246 /*
10247  * hebrew.js - Represent a Hebrew calendar object.
10248  * 
10249  * Copyright © 2012-2015, JEDLSoft
10250  *
10251  * Licensed under the Apache License, Version 2.0 (the "License");
10252  * you may not use this file except in compliance with the License.
10253  * You may obtain a copy of the License at
10254  *
10255  *     http://www.apache.org/licenses/LICENSE-2.0
10256  *
10257  * Unless required by applicable law or agreed to in writing, software
10258  * distributed under the License is distributed on an "AS IS" BASIS,
10259  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10260  *
10261  * See the License for the specific language governing permissions and
10262  * limitations under the License.
10263  */
10264 
10265 
10266 /* !depends ilib.js Calendar.js MathUtils.js */
10267 
10268 
10269 /**
10270  * @class
10271  * Construct a new Hebrew calendar object. This class encodes information about
10272  * the Hebrew (Jewish) calendar. The Hebrew calendar is a tabular hebrew 
10273  * calendar where the dates are calculated by arithmetic rules. This differs from 
10274  * the religious Hebrew calendar which is used to mark the beginning of particular 
10275  * holidays. The religious calendar depends on the first sighting of the new 
10276  * crescent moon to determine the first day of the new month. Because humans and 
10277  * weather are both involved, the actual time of sighting varies, so it is not 
10278  * really possible to precalculate the religious calendar. Certain groups, such 
10279  * as the Hebrew Society of North America, decreed in in 2007 that they will use
10280  * a calendar based on calculations rather than observations to determine the 
10281  * beginning of lunar months, and therefore the dates of holidays.<p>
10282  * 
10283  * 
10284  * @constructor
10285  * @extends Calendar
10286  */
10287 var HebrewCal = function() {
10288 	this.type = "hebrew";
10289 };
10290 
10291 /**
10292  * Return the number of days elapsed in the Hebrew calendar before the
10293  * given year starts.
10294  * @private
10295  * @param {number} year the year for which the number of days is sought
10296  * @return {number} the number of days elapsed in the Hebrew calendar before the
10297  * given year starts
10298  */
10299 HebrewCal.elapsedDays = function(year) {
10300 	var months = Math.floor(((235*year) - 234)/19);
10301 	var parts = 204 + 793 * MathUtils.mod(months, 1080);
10302 	var hours = 11 + 12 * months + 793 * Math.floor(months/1080) + 
10303 		Math.floor(parts/1080);
10304 	var days = 29 * months + Math.floor(hours/24);
10305 	return (MathUtils.mod(3 * (days + 1), 7) < 3) ? days + 1 : days;
10306 };
10307 
10308 /**
10309  * Return the number of days that the New Year's (Rosh HaShanah) in the Hebrew 
10310  * calendar will be corrected for the given year. Corrections are caused because New 
10311  * Year's is not allowed to start on certain days of the week. To deal with 
10312  * it, the start of the new year is corrected for the next year by adding a 
10313  * day to the 8th month (Heshvan) and/or the 9th month (Kislev) in the current
10314  * year to make them 30 days long instead of 29.
10315  * 
10316  * @private
10317  * @param {number} year the year for which the correction is sought
10318  * @param {number} elapsed number of days elapsed up to this year
10319  * @return {number} the number of days correction in the current year to make sure
10320  * Rosh HaShanah does not fall on undesirable days of the week
10321  */
10322 HebrewCal.newYearsCorrection = function(year, elapsed) {
10323 	var lastYear = HebrewCal.elapsedDays(year-1),
10324 		thisYear = elapsed,
10325 		nextYear = HebrewCal.elapsedDays(year+1);
10326 	
10327 	return (nextYear - thisYear) == 356 ? 2 : ((thisYear - lastYear) == 382 ? 1 : 0);
10328 };
10329 
10330 /**
10331  * Return the rata die date of the new year for the given hebrew year.
10332  * @private
10333  * @param {number} year the year for which the new year is needed
10334  * @return {number} the rata die date of the new year
10335  */
10336 HebrewCal.newYear = function(year) {
10337 	var elapsed = HebrewCal.elapsedDays(year); 
10338 	
10339 	return elapsed + HebrewCal.newYearsCorrection(year, elapsed);
10340 };
10341 
10342 /**
10343  * Return the number of days in the given year. Years contain a variable number of
10344  * days because the date of Rosh HaShanah (New Year's) changes so that it doesn't
10345  * fall on particular days of the week. Days are added to the months of Heshvan
10346  * and/or Kislev in the previous year in order to prevent the current year's New
10347  * Year from being on Sunday, Wednesday, or Friday.
10348  * 
10349  * @param {number} year the year for which the length is sought
10350  * @return {number} number of days in the given year
10351  */
10352 HebrewCal.daysInYear = function(year) {
10353 	return HebrewCal.newYear(year+1) - HebrewCal.newYear(year);
10354 };
10355 
10356 /**
10357  * Return true if the given year contains a long month of Heshvan. That is,
10358  * it is 30 days instead of 29.
10359  * 
10360  * @private
10361  * @param {number} year the year in which that month is questioned
10362  * @return {boolean} true if the given year contains a long month of Heshvan
10363  */
10364 HebrewCal.longHeshvan = function(year) {
10365 	return MathUtils.mod(HebrewCal.daysInYear(year), 10) === 5;
10366 };
10367 
10368 /**
10369  * Return true if the given year contains a long month of Kislev. That is,
10370  * it is 30 days instead of 29.
10371  * 
10372  * @private
10373  * @param {number} year the year in which that month is questioned
10374  * @return {boolean} true if the given year contains a short month of Kislev
10375  */
10376 HebrewCal.longKislev = function(year) {
10377 	return MathUtils.mod(HebrewCal.daysInYear(year), 10) !== 3;
10378 };
10379 
10380 /**
10381  * Return the date of the last day of the month for the given year. The date of
10382  * the last day of the month is variable because a number of months gain an extra 
10383  * day in leap years, and it is variable which months gain a day for each leap 
10384  * year and which do not.
10385  * 
10386  * @param {number} month the month for which the number of days is sought
10387  * @param {number} year the year in which that month is
10388  * @return {number} the number of days in the given month and year
10389  */
10390 HebrewCal.prototype.lastDayOfMonth = function(month, year) {
10391 	switch (month) {
10392 		case 2: 
10393 		case 4: 
10394 		case 6: 
10395 		case 10: 
10396 			return 29;
10397 		case 13:
10398 			return this.isLeapYear(year) ? 29 : 0;
10399 		case 8:
10400 			return HebrewCal.longHeshvan(year) ? 30 : 29;
10401 		case 9:
10402 			return HebrewCal.longKislev(year) ? 30 : 29;
10403 		case 12:
10404 		case 1:
10405 		case 3:
10406 		case 5:
10407 		case 7:
10408 		case 11:
10409 			return 30;
10410 		default:
10411 			return 0;
10412 	}
10413 };
10414 
10415 /**
10416  * Return the number of months in the given year. The number of months in a year varies
10417  * for luni-solar calendars because in some years, an extra month is needed to extend the 
10418  * days in a year to an entire solar year. The month is represented as a 1-based number
10419  * where 1=first month, 2=second month, etc.
10420  * 
10421  * @param {number} year a year for which the number of months is sought
10422  */
10423 HebrewCal.prototype.getNumMonths = function(year) {
10424 	return this.isLeapYear(year) ? 13 : 12;
10425 };
10426 
10427 /**
10428  * Return the number of days in a particular month in a particular year. This function
10429  * can return a different number for a month depending on the year because of leap years.
10430  *
10431  * @param {number} month the month for which the length is sought
10432  * @param {number} year the year within which that month can be found
10433  * @returns {number} the number of days within the given month in the given year, or
10434  * 0 for an invalid month in the year
10435  */
10436 HebrewCal.prototype.getMonLength = function(month, year) {
10437 	if (month < 1 || month > 13 || (month == 13 && !this.isLeapYear(year))) {
10438 		return 0;
10439 	}
10440 	return this.lastDayOfMonth(month, year);
10441 };
10442 
10443 /**
10444  * Return true if the given year is a leap year in the Hebrew calendar.
10445  * The year parameter may be given as a number, or as a HebrewDate object.
10446  * @param {number|Object} year the year for which the leap year information is being sought
10447  * @returns {boolean} true if the given year is a leap year
10448  */
10449 HebrewCal.prototype.isLeapYear = function(year) {
10450 	var y = (typeof(year) == 'number') ? year : year.year;
10451 	return (MathUtils.mod(1 + 7 * y, 19) < 7);
10452 };
10453 
10454 /**
10455  * Return the type of this calendar.
10456  * 
10457  * @returns {string} the name of the type of this calendar 
10458  */
10459 HebrewCal.prototype.getType = function() {
10460 	return this.type;
10461 };
10462 
10463 /**
10464  * Return a date instance for this calendar type using the given
10465  * options.
10466  * @param {Object} options options controlling the construction of 
10467  * the date instance
10468  * @returns {HebrewDate} a date appropriate for this calendar type
10469  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
10470  */
10471 HebrewCal.prototype.newDateInstance = function (options) {
10472 		return new HebrewDate(options);
10473 };
10474 
10475 /*register this calendar for the factory method */
10476 Calendar._constructors["hebrew"] = HebrewCal;
10477 
10478 
10479 
10480 /*< HebrewRataDie.js */
10481 /*
10482  * HebrewRataDie.js - Represent an RD date in the Hebrew calendar
10483  * 
10484  * Copyright © 2012-2015, JEDLSoft
10485  *
10486  * Licensed under the Apache License, Version 2.0 (the "License");
10487  * you may not use this file except in compliance with the License.
10488  * You may obtain a copy of the License at
10489  *
10490  *     http://www.apache.org/licenses/LICENSE-2.0
10491  *
10492  * Unless required by applicable law or agreed to in writing, software
10493  * distributed under the License is distributed on an "AS IS" BASIS,
10494  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10495  *
10496  * See the License for the specific language governing permissions and
10497  * limitations under the License.
10498  */
10499 
10500 /* !depends 
10501 MathUtils.js
10502 HebrewCal.js
10503 RataDie.js
10504 */
10505 
10506 
10507 /**
10508  * @class
10509  * Construct a new Hebrew RD date number object. The constructor parameters can 
10510  * contain any of the following properties:
10511  * 
10512  * <ul>
10513  * <li><i>unixtime<i> - sets the time of this instance according to the given 
10514  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
10515  * 
10516  * <li><i>julianday</i> - sets the time of this instance according to the given
10517  * Julian Day instance or the Julian Day given as a float
10518  * 
10519  * <li><i>year</i> - any integer, including 0
10520  * 
10521  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
10522  * 
10523  * <li><i>day</i> - 1 to 31
10524  * 
10525  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
10526  * is always done with an unambiguous 24 hour representation
10527  * 
10528  * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify 
10529  * the parts or specify the minutes, seconds, and milliseconds, but not both. 
10530  * 
10531  * <li><i>minute</i> - 0 to 59
10532  * 
10533  * <li><i>second</i> - 0 to 59
10534  * 
10535  * <li><i>millisecond</i> - 0 to 999
10536  * 
10537  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
10538  * </ul>
10539  *
10540  * If the constructor is called with another Hebrew date instance instead of
10541  * a parameter block, the other instance acts as a parameter block and its
10542  * settings are copied into the current instance.<p>
10543  * 
10544  * If the constructor is called with no arguments at all or if none of the 
10545  * properties listed above are present, then the RD is calculate based on 
10546  * the current date at the time of instantiation. <p>
10547  * 
10548  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
10549  * specified in the params, it is assumed that they have the smallest possible
10550  * value in the range for the property (zero or one).<p>
10551  * 
10552  * 
10553  * @private
10554  * @constructor
10555  * @extends RataDie
10556  * @param {Object=} params parameters that govern the settings and behaviour of this Hebrew RD date
10557  */
10558 var HebrewRataDie = function(params) {
10559 	this.cal = params && params.cal || new HebrewCal();
10560 	this.rd = NaN;
10561 	RataDie.call(this, params);
10562 };
10563 
10564 HebrewRataDie.prototype = new RataDie();
10565 HebrewRataDie.prototype.parent = RataDie;
10566 HebrewRataDie.prototype.constructor = HebrewRataDie;
10567 
10568 /**
10569  * The difference between a zero Julian day and the first day of the Hebrew 
10570  * calendar: sunset on Monday, Tishri 1, 1 = September 7, 3760 BC Gregorian = JD 347997.25
10571  * @private
10572  * @type number
10573  */
10574 HebrewRataDie.prototype.epoch = 347997.25;
10575 
10576 /**
10577  * the cumulative lengths of each month for a non-leap year, without new years corrections
10578  * @private
10579  * @const
10580  * @type Array.<number>
10581  */
10582 HebrewRataDie.cumMonthLengths = [
10583 	176,  /* Nisan */
10584 	206,  /* Iyyar */
10585 	235,  /* Sivan */
10586 	265,  /* Tammuz */
10587 	294,  /* Av */
10588 	324,  /* Elul */
10589 	0,    /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
10590 	30,   /* Heshvan */
10591 	59,   /* Kislev */
10592 	88,   /* Teveth */
10593 	117,  /* Shevat */
10594 	147   /* Adar I */
10595 ];
10596 
10597 /**
10598  * the cumulative lengths of each month for a leap year, without new years corrections 
10599  * @private
10600  * @const
10601  * @type Array.<number>
10602  */
10603 HebrewRataDie.cumMonthLengthsLeap = [
10604 	206,  /* Nisan */
10605 	236,  /* Iyyar */
10606 	265,  /* Sivan */
10607 	295,  /* Tammuz */
10608 	324,  /* Av */
10609 	354,  /* Elul */
10610 	0,    /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
10611 	30,   /* Heshvan */
10612 	59,   /* Kislev */
10613 	88,   /* Teveth */
10614 	117,  /* Shevat */
10615 	147,  /* Adar I */
10616 	177   /* Adar II */
10617 ];
10618 
10619 /**
10620  * Calculate the Rata Die (fixed day) number of the given date from the
10621  * date components.
10622  * 
10623  * @private
10624  * @param {Object} date the date components to calculate the RD from
10625  */
10626 HebrewRataDie.prototype._setDateComponents = function(date) {
10627 	var elapsed = HebrewCal.elapsedDays(date.year);
10628 	var days = elapsed +
10629 		HebrewCal.newYearsCorrection(date.year, elapsed) +
10630 		date.day - 1;
10631 	var sum = 0, table;
10632 	
10633 	//console.log("getRataDie: converting " +  JSON.stringify(date));
10634 	//console.log("getRataDie: days is " +  days);
10635 	//console.log("getRataDie: new years correction is " +  HebrewCal.newYearsCorrection(date.year, elapsed));
10636 	
10637 	table = this.cal.isLeapYear(date.year) ? 
10638 		HebrewRataDie.cumMonthLengthsLeap :
10639 		HebrewRataDie.cumMonthLengths;
10640 	sum = table[date.month-1];
10641 	
10642 	// gets cumulative without correction, so now add in the correction
10643 	if ((date.month < 7 || date.month > 8) && HebrewCal.longHeshvan(date.year)) {
10644 		sum++;
10645 	}
10646 	if ((date.month < 7 || date.month > 9) && HebrewCal.longKislev(date.year)) {
10647 		sum++;
10648 	}
10649 	// console.log("getRataDie: cum days is now " +  sum);
10650 	
10651 	days += sum;
10652 	
10653 	// the date starts at sunset, which we take as 18:00, so the hours from
10654 	// midnight to 18:00 are on the current Gregorian day, and the hours from
10655 	// 18:00 to midnight are on the previous Gregorian day. So to calculate the 
10656 	// number of hours into the current day that this time represents, we have
10657 	// to count from 18:00 to midnight first, and add in 6 hours if the time is
10658 	// less than 18:00
10659 	var minute, second, millisecond;
10660 	
10661 	if (typeof(date.parts) !== 'undefined') {
10662 		// The parts (halaqim) of the hour. This can be a number from 0 to 1079.
10663 		var parts = parseInt(date.parts, 10);
10664 		var seconds = parseInt(parts, 10) * 3.333333333333;
10665 		minute = Math.floor(seconds / 60);
10666 		seconds -= minute * 60;
10667 		second = Math.floor(seconds);
10668 		millisecond = (seconds - second);	
10669 	} else {
10670 		minute = parseInt(date.minute, 10) || 0;
10671 		second = parseInt(date.second, 10) || 0;
10672 		millisecond = parseInt(date.millisecond, 10) || 0;
10673 	}
10674 		
10675 	var time;
10676 	if (date.hour >= 18) {
10677 		time = ((date.hour - 18 || 0) * 3600000 +
10678 			(minute || 0) * 60000 +
10679 			(second || 0) * 1000 +
10680 			(millisecond || 0)) / 
10681 			86400000;
10682 	} else {
10683 		time = 0.25 +	// 6 hours from 18:00 to midnight on the previous gregorian day
10684 				((date.hour || 0) * 3600000 +
10685 				(minute || 0) * 60000 +
10686 				(second || 0) * 1000 +
10687 				(millisecond || 0)) / 
10688 				86400000;
10689 	}
10690 	
10691 	//console.log("getRataDie: rd is " +  (days + time));
10692 	this.rd = days + time;
10693 };
10694 	
10695 /**
10696  * Return the rd number of the particular day of the week on or before the 
10697  * given rd. eg. The Sunday on or before the given rd.
10698  * @private
10699  * @param {number} rd the rata die date of the reference date
10700  * @param {number} dayOfWeek the day of the week that is being sought relative 
10701  * to the current date
10702  * @return {number} the rd of the day of the week
10703  */
10704 HebrewRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
10705 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek + 1, 7);
10706 };
10707 
10708 
10709 
10710 /*< HebrewDate.js */
10711 /*
10712  * HebrewDate.js - Represent a date in the Hebrew calendar
10713  * 
10714  * Copyright © 2012-2015, JEDLSoft
10715  *
10716  * Licensed under the Apache License, Version 2.0 (the "License");
10717  * you may not use this file except in compliance with the License.
10718  * You may obtain a copy of the License at
10719  *
10720  *     http://www.apache.org/licenses/LICENSE-2.0
10721  *
10722  * Unless required by applicable law or agreed to in writing, software
10723  * distributed under the License is distributed on an "AS IS" BASIS,
10724  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10725  *
10726  * See the License for the specific language governing permissions and
10727  * limitations under the License.
10728  */
10729 
10730 /* !depends 
10731 ilib.js
10732 Locale.js
10733 LocaleInfo.js
10734 TimeZone.js
10735 IDate.js
10736 MathUtils.js
10737 Calendar.js
10738 HebrewCal.js
10739 HebrewRataDie.js
10740 */
10741 
10742 
10743 
10744 
10745 /**
10746  * @class
10747  * Construct a new civil Hebrew date object. The constructor can be called
10748  * with a params object that can contain the following properties:<p>
10749  * 
10750  * <ul>
10751  * <li><i>julianday</i> - the Julian Day to set into this date
10752  * <li><i>year</i> - any integer except 0. Years go from -1 (BCE) to 1 (CE), skipping the zero year
10753  * <li><i>month</i> - 1 to 12, where 1 means Nisan, 2 means Iyyar, etc.
10754  * <li><i>day</i> - 1 to 30
10755  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
10756  * is always done with an unambiguous 24 hour representation
10757  * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify 
10758  * the parts or specify the minutes, seconds, and milliseconds, but not both. 
10759  * <li><i>minute</i> - 0 to 59
10760  * <li><i>second</i> - 0 to 59
10761  * <li><i>millisecond</i> - 0 to 999
10762  * <li><i>locale</i> - the TimeZone instance or time zone name as a string 
10763  * of this julian date. The date/time is kept in the local time. The time zone
10764  * is used later if this date is formatted according to a different time zone and
10765  * the difference has to be calculated, or when the date format has a time zone
10766  * component in it.
10767  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 
10768  * given, it can be inferred from this locale. For locales that span multiple
10769  * time zones, the one with the largest population is chosen as the one that 
10770  * represents the locale. 
10771  * 
10772  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
10773  * </ul>
10774  * 
10775  * If called with another Hebrew date argument, the date components of the given
10776  * date are copied into the current one.<p>
10777  * 
10778  * If the constructor is called with no arguments at all or if none of the 
10779  * properties listed above 
10780  * from <i>julianday</i> through <i>millisecond</i> are present, then the date 
10781  * components are 
10782  * filled in with the current date at the time of instantiation. Note that if
10783  * you do not give the time zone when defaulting to the current time and the 
10784  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
10785  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
10786  * Mean Time").<p>
10787  * 
10788  * 
10789  * @constructor
10790  * @extends IDate
10791  * @param {Object=} params parameters that govern the settings and behaviour of this Hebrew date
10792  */
10793 var HebrewDate = function(params) {
10794 	this.cal = new HebrewCal();
10795 	
10796 	if (params) {
10797 		if (params.timezone) {
10798 			this.timezone = params.timezone;
10799 		}
10800 		if (params.locale) {
10801 			this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
10802 			if (!this.timezone) {
10803 				var li = new LocaleInfo(this.locale);
10804 				this.timezone = li.getTimeZone(); 
10805 			}
10806 		}
10807 
10808 		if (params.year || params.month || params.day || params.hour ||
10809 				params.minute || params.second || params.millisecond || params.parts ) {
10810 			/**
10811 			 * Year in the Hebrew calendar.
10812 			 * @type number
10813 			 */
10814 			this.year = parseInt(params.year, 10) || 0;
10815 
10816 			/**
10817 			 * The month number, ranging from 1 to 13.
10818 			 * @type number
10819 			 */
10820 			this.month = parseInt(params.month, 10) || 1;
10821 
10822 			/**
10823 			 * The day of the month. This ranges from 1 to 30.
10824 			 * @type number
10825 			 */
10826 			this.day = parseInt(params.day, 10) || 1;
10827 			
10828 			/**
10829 			 * The hour of the day. This can be a number from 0 to 23, as times are
10830 			 * stored unambiguously in the 24-hour clock.
10831 			 * @type number
10832 			 */
10833 			this.hour = parseInt(params.hour, 10) || 0;
10834 
10835 			if (typeof(params.parts) !== 'undefined') {
10836 				/**
10837 				 * The parts (halaqim) of the hour. This can be a number from 0 to 1079.
10838 				 * @type number
10839 				 */
10840 				this.parts = parseInt(params.parts, 10);
10841 				var seconds = parseInt(params.parts, 10) * 3.333333333333;
10842 				this.minute = Math.floor(seconds / 60);
10843 				seconds -= this.minute * 60;
10844 				this.second = Math.floor(seconds);
10845 				this.millisecond = (seconds - this.second);	
10846 			} else {
10847 				/**
10848 				 * The minute of the hours. Ranges from 0 to 59.
10849 				 * @type number
10850 				 */
10851 				this.minute = parseInt(params.minute, 10) || 0;
10852 	
10853 				/**
10854 				 * The second of the minute. Ranges from 0 to 59.
10855 				 * @type number
10856 				 */
10857 				this.second = parseInt(params.second, 10) || 0;
10858 	
10859 				/**
10860 				 * The millisecond of the second. Ranges from 0 to 999.
10861 				 * @type number
10862 				 */
10863 				this.millisecond = parseInt(params.millisecond, 10) || 0;
10864 			}
10865 				
10866 			/**
10867 			 * The day of the year. Ranges from 1 to 383.
10868 			 * @type number
10869 			 */
10870 			this.dayOfYear = parseInt(params.dayOfYear, 10);
10871 			
10872 			if (typeof(params.dst) === 'boolean') {
10873 				this.dst = params.dst;
10874 			}
10875 			
10876 			this.rd = this.newRd(this);
10877 			
10878 			// add the time zone offset to the rd to convert to UTC
10879 			if (!this.tz) {
10880 				this.tz = new TimeZone({id: this.timezone});
10881 			}
10882 			// getOffsetMillis requires that this.year, this.rd, and this.dst 
10883 			// are set in order to figure out which time zone rules apply and 
10884 			// what the offset is at that point in the year
10885 			this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
10886 			if (this.offset !== 0) {
10887 				this.rd = this.newRd({
10888 					rd: this.rd.getRataDie() - this.offset
10889 				});
10890 			}
10891 		}
10892 	} 
10893 	
10894 	if (!this.rd) {
10895 		this.rd = this.newRd(params);
10896 		this._calcDateComponents();
10897 	}
10898 };
10899 
10900 HebrewDate.prototype = new IDate({noinstance: true});
10901 HebrewDate.prototype.parent = IDate;
10902 HebrewDate.prototype.constructor = HebrewDate;
10903 
10904 /**
10905  * the cumulative lengths of each month for a non-leap year, without new years corrections,
10906  * that can be used in reverse to map days to months
10907  * @private
10908  * @const
10909  * @type Array.<number>
10910  */
10911 HebrewDate.cumMonthLengthsReverse = [
10912 //  [days, monthnumber],                                                
10913 	[0,   7],  /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
10914 	[30,  8],  /* Heshvan */
10915 	[59,  9],  /* Kislev */
10916 	[88,  10], /* Teveth */
10917 	[117, 11], /* Shevat */
10918 	[147, 12], /* Adar I */
10919 	[176, 1],  /* Nisan */
10920 	[206, 2],  /* Iyyar */
10921 	[235, 3],  /* Sivan */
10922 	[265, 4],  /* Tammuz */
10923 	[294, 5],  /* Av */
10924 	[324, 6],  /* Elul */
10925 	[354, 7]   /* end of year sentinel value */
10926 ];
10927 
10928 /**
10929  * the cumulative lengths of each month for a leap year, without new years corrections
10930  * that can be used in reverse to map days to months 
10931  * 
10932  * @private
10933  * @const
10934  * @type Array.<number>
10935  */
10936 HebrewDate.cumMonthLengthsLeapReverse = [
10937 //  [days, monthnumber],                                                
10938 	[0,   7],  /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
10939 	[30,  8],  /* Heshvan */
10940 	[59,  9],  /* Kislev */
10941 	[88,  10], /* Teveth */
10942 	[117, 11], /* Shevat */
10943 	[147, 12], /* Adar I */
10944 	[177, 13], /* Adar II */
10945 	[206, 1],  /* Nisan */
10946 	[236, 2],  /* Iyyar */
10947 	[265, 3],  /* Sivan */
10948 	[295, 4],  /* Tammuz */
10949 	[324, 5],  /* Av */
10950 	[354, 6],  /* Elul */
10951 	[384, 7]   /* end of year sentinel value */
10952 ];
10953 
10954 /**
10955  * Number of days difference between RD 0 of the Hebrew calendar 
10956  * (Jan 1, 1 Gregorian = JD 1721057.5) and RD 0 of the Hebrew calendar
10957  * (September 7, -3760 Gregorian = JD 347997.25)
10958  * @private
10959  * @const
10960  * @type number
10961  */
10962 HebrewDate.GregorianDiff = 1373060.25;
10963 
10964 /**
10965  * Return a new RD for this date type using the given params.
10966  * @private
10967  * @param {Object=} params the parameters used to create this rata die instance
10968  * @returns {RataDie} the new RD instance for the given params
10969  */
10970 HebrewDate.prototype.newRd = function (params) {
10971 	return new HebrewRataDie(params);
10972 };
10973 
10974 /**
10975  * Return the year for the given RD
10976  * @protected
10977  * @param {number} rd RD to calculate from 
10978  * @returns {number} the year for the RD
10979  */
10980 HebrewDate.prototype._calcYear = function(rd) {
10981 	var year, approximation, nextNewYear;
10982 	
10983 	// divide by the average number of days per year in the Hebrew calendar
10984 	// to approximate the year, then tweak it to get the real year
10985 	approximation = Math.floor(rd / 365.246822206) + 1;
10986 	
10987 	// console.log("HebrewDate._calcYear: approx is " + approximation);
10988 	
10989 	// search forward from approximation-1 for the year that actually contains this rd
10990 	year = approximation;
10991 	nextNewYear = HebrewCal.newYear(year);
10992 	while (rd >= nextNewYear) {
10993 		year++;
10994 		nextNewYear = HebrewCal.newYear(year);
10995 	}
10996 	return year - 1;
10997 };
10998 
10999 /**
11000  * Calculate date components for the given RD date.
11001  * @protected
11002  */
11003 HebrewDate.prototype._calcDateComponents = function () {
11004 	var remainder,
11005 		i,
11006 		table,
11007 		target,
11008 		rd = this.rd.getRataDie();
11009 	
11010 	// console.log("HebrewDate.calcComponents: calculating for rd " + rd);
11011 
11012 	if (typeof(this.offset) === "undefined") {
11013 		this.year = this._calcYear(rd);
11014 		
11015 		// now offset the RD by the time zone, then recalculate in case we were 
11016 		// near the year boundary
11017 		if (!this.tz) {
11018 			this.tz = new TimeZone({id: this.timezone});
11019 		}
11020 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
11021 	}
11022 
11023 	if (this.offset !== 0) {
11024 		rd += this.offset;
11025 		this.year = this._calcYear(rd);
11026 	}
11027 	
11028 	// console.log("HebrewDate.calcComponents: year is " + this.year + " with starting rd " + thisNewYear);
11029 	
11030 	remainder = rd - HebrewCal.newYear(this.year);
11031 	// console.log("HebrewDate.calcComponents: remainder is " + remainder);
11032 
11033 	// take out new years corrections so we get the right month when we look it up in the table
11034 	if (remainder >= 59) {
11035 		if (remainder >= 88) {
11036 			if (HebrewCal.longKislev(this.year)) {
11037 				remainder--;
11038 			}
11039 		}
11040 		if (HebrewCal.longHeshvan(this.year)) {
11041 			remainder--;
11042 		}
11043 	}
11044 	
11045 	// console.log("HebrewDate.calcComponents: after new years corrections, remainder is " + remainder);
11046 	
11047 	table = this.cal.isLeapYear(this.year) ? 
11048 			HebrewDate.cumMonthLengthsLeapReverse :
11049 			HebrewDate.cumMonthLengthsReverse;
11050 	
11051 	i = 0;
11052 	target = Math.floor(remainder);
11053 	while (i+1 < table.length && target >= table[i+1][0]) {
11054 		i++;
11055 	}
11056 	
11057 	this.month = table[i][1];
11058 	// console.log("HebrewDate.calcComponents: remainder is " + remainder);
11059 	remainder -= table[i][0];
11060 	
11061 	// console.log("HebrewDate.calcComponents: month is " + this.month + " and remainder is " + remainder);
11062 	
11063 	this.day = Math.floor(remainder);
11064 	remainder -= this.day;
11065 	this.day++; // days are 1-based
11066 	
11067 	// console.log("HebrewDate.calcComponents: day is " + this.day + " and remainder is " + remainder);
11068 
11069 	// now convert to milliseconds for the rest of the calculation
11070 	remainder = Math.round(remainder * 86400000);
11071 	
11072 	this.hour = Math.floor(remainder/3600000);
11073 	remainder -= this.hour * 3600000;
11074 	
11075 	// the hours from 0 to 6 are actually 18:00 to midnight of the previous
11076 	// gregorian day, so we have to adjust for that
11077 	if (this.hour >= 6) {
11078 		this.hour -= 6;
11079 	} else {
11080 		this.hour += 18;
11081 	}
11082 		
11083 	this.minute = Math.floor(remainder/60000);
11084 	remainder -= this.minute * 60000;
11085 	
11086 	this.second = Math.floor(remainder/1000);
11087 	remainder -= this.second * 1000;
11088 	
11089 	this.millisecond = Math.floor(remainder);
11090 };
11091 
11092 /**
11093  * Return the day of the week of this date. The day of the week is encoded
11094  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
11095  * 
11096  * @return {number} the day of the week
11097  */
11098 HebrewDate.prototype.getDayOfWeek = function() {
11099 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
11100 	return MathUtils.mod(rd+1, 7);
11101 };
11102 
11103 /**
11104  * Get the Halaqim (parts) of an hour. There are 1080 parts in an hour, which means
11105  * each part is 3.33333333 seconds long. This means the number returned may not
11106  * be an integer.
11107  * 
11108  * @return {number} the halaqim parts of the current hour
11109  */
11110 HebrewDate.prototype.getHalaqim = function() {
11111 	if (this.parts < 0) {
11112 		// convert to ms first, then to parts
11113 		var h = this.minute * 60000 + this.second * 1000 + this.millisecond;
11114 		this.parts = (h * 0.0003);
11115 	}
11116 	return this.parts;
11117 };
11118 
11119 /**
11120  * Return the rd number of the first Sunday of the given ISO year.
11121  * @protected
11122  * @return the rd of the first Sunday of the ISO year
11123  */
11124 HebrewDate.prototype.firstSunday = function (year) {
11125 	var tishri1 = this.newRd({
11126 		year: year,
11127 		month: 7,
11128 		day: 1,
11129 		hour: 18,
11130 		minute: 0,
11131 		second: 0,
11132 		millisecond: 0,
11133 		cal: this.cal
11134 	});
11135 	var firstThu = this.newRd({
11136 		rd: tishri1.onOrAfter(4),
11137 		cal: this.cal
11138 	});
11139 	return firstThu.before(0);
11140 };
11141 
11142 /**
11143  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 
11144  * 385, regardless of months or weeks, etc. That is, Tishri 1st is day 1, and 
11145  * Elul 29 is 385 for a leap year with a long Heshvan and long Kislev.
11146  * @return {number} the ordinal day of the year
11147  */
11148 HebrewDate.prototype.getDayOfYear = function() {
11149 	var table = this.cal.isLeapYear(this.year) ? 
11150 				HebrewRataDie.cumMonthLengthsLeap : 
11151 				HebrewRataDie.cumMonthLengths;
11152 	var days = table[this.month-1];
11153 	if ((this.month < 7 || this.month > 8) && HebrewCal.longHeshvan(this.year)) {
11154 		days++;
11155 	}
11156 	if ((this.month < 7 || this.month > 9) && HebrewCal.longKislev(this.year)) {
11157 		days++;
11158 	}
11159 
11160 	return days + this.day;
11161 };
11162 
11163 /**
11164  * Return the ordinal number of the week within the month. The first week of a month is
11165  * the first one that contains 4 or more days in that month. If any days precede this
11166  * first week, they are marked as being in week 0. This function returns values from 0
11167  * through 6.<p>
11168  * 
11169  * The locale is a required parameter because different locales that use the same 
11170  * Hebrew calendar consider different days of the week to be the beginning of
11171  * the week. This can affect the week of the month in which some days are located.
11172  * 
11173  * @param {Locale|string} locale the locale or locale spec to use when figuring out 
11174  * the first day of the week
11175  * @return {number} the ordinal number of the week within the current month
11176  */
11177 HebrewDate.prototype.getWeekOfMonth = function(locale) {
11178 	var li = new LocaleInfo(locale),
11179 		first = this.newRd({
11180 			year: this.year,
11181 			month: this.month,
11182 			day: 1,
11183 			hour: 18,
11184 			minute: 0,
11185 			second: 0,
11186 			millisecond: 0
11187 		}),
11188 		rd = this.rd.getRataDie(),
11189 		weekStart = first.onOrAfter(li.getFirstDayOfWeek());
11190 	
11191 	if (weekStart - first.getRataDie() > 3) {
11192 		// if the first week has 4 or more days in it of the current month, then consider
11193 		// that week 1. Otherwise, it is week 0. To make it week 1, move the week start
11194 		// one week earlier.
11195 		weekStart -= 7;
11196 	}
11197 	return (rd < weekStart) ? 0 : Math.floor((rd - weekStart) / 7) + 1;
11198 };
11199 
11200 /**
11201  * Return the era for this date as a number. The value for the era for Hebrew 
11202  * calendars is -1 for "before the Hebrew era" and 1 for "the Hebrew era". 
11203  * Hebrew era dates are any date after Tishri 1, 1, which is the same as
11204  * September 7, 3760 BC in the Gregorian calendar. 
11205  * 
11206  * @return {number} 1 if this date is in the Hebrew era, -1 if it is before the 
11207  * Hebrew era 
11208  */
11209 HebrewDate.prototype.getEra = function() {
11210 	return (this.year < 1) ? -1 : 1;
11211 };
11212 
11213 /**
11214  * Return the name of the calendar that governs this date.
11215  * 
11216  * @return {string} a string giving the name of the calendar
11217  */
11218 HebrewDate.prototype.getCalendar = function() {
11219 	return "hebrew";
11220 };
11221 
11222 // register with the factory method
11223 IDate._constructors["hebrew"] = HebrewDate;
11224 
11225 
11226 
11227 /*< IslamicCal.js */
11228 /*
11229  * islamic.js - Represent a Islamic calendar object.
11230  * 
11231  * Copyright © 2012-2015, JEDLSoft
11232  *
11233  * Licensed under the Apache License, Version 2.0 (the "License");
11234  * you may not use this file except in compliance with the License.
11235  * You may obtain a copy of the License at
11236  *
11237  *     http://www.apache.org/licenses/LICENSE-2.0
11238  *
11239  * Unless required by applicable law or agreed to in writing, software
11240  * distributed under the License is distributed on an "AS IS" BASIS,
11241  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11242  *
11243  * See the License for the specific language governing permissions and
11244  * limitations under the License.
11245  */
11246 
11247 
11248 /* !depends 
11249 ilib.js
11250 Calendar.js 
11251 MathUtils.js 
11252 */
11253 
11254 
11255 /**
11256  * @class
11257  * Construct a new Islamic calendar object. This class encodes information about
11258  * the civil Islamic calendar. The civil Islamic calendar is a tabular islamic 
11259  * calendar where the dates are calculated by arithmetic rules. This differs from 
11260  * the religious Islamic calendar which is used to mark the beginning of particular 
11261  * holidays. The religious calendar depends on the first sighting of the new 
11262  * crescent moon to determine the first day of the new month. Because humans and 
11263  * weather are both involved, the actual time of sighting varies, so it is not 
11264  * really possible to precalculate the religious calendar. Certain groups, such 
11265  * as the Islamic Society of North America, decreed in in 2007 that they will use
11266  * a calendar based on calculations rather than observations to determine the 
11267  * beginning of lunar months, and therefore the dates of holidays.<p>
11268  * 
11269  * 
11270  * @constructor
11271  * @extends Calendar
11272  */
11273 var IslamicCal = function() {
11274 	this.type = "islamic";
11275 };
11276 
11277 /**
11278  * the lengths of each month 
11279  * @private
11280  * @const
11281  * @type Array.<number>
11282  */
11283 IslamicCal.monthLengths = [
11284 	30,  /* Muharram */
11285 	29,  /* Saffar */
11286 	30,  /* Rabi'I */
11287 	29,  /* Rabi'II */
11288 	30,  /* Jumada I */
11289 	29,  /* Jumada II */
11290 	30,  /* Rajab */
11291 	29,  /* Sha'ban */
11292 	30,  /* Ramadan */
11293 	29,  /* Shawwal */
11294 	30,  /* Dhu al-Qa'da */
11295 	29   /* Dhu al-Hijja */
11296 ];
11297 
11298 
11299 /**
11300  * Return the number of months in the given year. The number of months in a year varies
11301  * for luni-solar calendars because in some years, an extra month is needed to extend the 
11302  * days in a year to an entire solar year. The month is represented as a 1-based number
11303  * where 1=first month, 2=second month, etc.
11304  * 
11305  * @param {number} year a year for which the number of months is sought
11306  */
11307 IslamicCal.prototype.getNumMonths = function(year) {
11308 	return 12;
11309 };
11310 
11311 /**
11312  * Return the number of days in a particular month in a particular year. This function
11313  * can return a different number for a month depending on the year because of things
11314  * like leap years.
11315  *
11316  * @param {number} month the month for which the length is sought
11317  * @param {number} year the year within which that month can be found
11318  * @return {number} the number of days within the given month in the given year
11319  */
11320 IslamicCal.prototype.getMonLength = function(month, year) {
11321 	if (month !== 12) {
11322 		return IslamicCal.monthLengths[month-1];
11323 	} else {
11324 		return this.isLeapYear(year) ? 30 : 29;
11325 	}
11326 };
11327 
11328 /**
11329  * Return true if the given year is a leap year in the Islamic calendar.
11330  * The year parameter may be given as a number, or as a IslamicDate object.
11331  * @param {number} year the year for which the leap year information is being sought
11332  * @return {boolean} true if the given year is a leap year
11333  */
11334 IslamicCal.prototype.isLeapYear = function(year) {
11335 	return (MathUtils.mod((14 + 11 * year), 30) < 11);
11336 };
11337 
11338 /**
11339  * Return the type of this calendar.
11340  * 
11341  * @return {string} the name of the type of this calendar 
11342  */
11343 IslamicCal.prototype.getType = function() {
11344 	return this.type;
11345 };
11346 
11347 /**
11348  * Return a date instance for this calendar type using the given
11349  * options.
11350  * @param {Object} options options controlling the construction of 
11351  * the date instance
11352  * @return {IslamicDate} a date appropriate for this calendar type
11353  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
11354  */
11355 IslamicCal.prototype.newDateInstance = function (options) {
11356 		return new IslamicDate(options);
11357 };
11358 
11359 /*register this calendar for the factory method */
11360 Calendar._constructors["islamic"] = IslamicCal;
11361 
11362 
11363 /*< IslamicRataDie.js */
11364 /*
11365  * IslamicRataDie.js - Represent an RD date in the Islamic calendar
11366  * 
11367  * Copyright © 2012-2015, JEDLSoft
11368  *
11369  * Licensed under the Apache License, Version 2.0 (the "License");
11370  * you may not use this file except in compliance with the License.
11371  * You may obtain a copy of the License at
11372  *
11373  *     http://www.apache.org/licenses/LICENSE-2.0
11374  *
11375  * Unless required by applicable law or agreed to in writing, software
11376  * distributed under the License is distributed on an "AS IS" BASIS,
11377  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11378  *
11379  * See the License for the specific language governing permissions and
11380  * limitations under the License.
11381  */
11382 
11383 /* !depends 
11384 IslamicCal.js
11385 RataDie.js
11386 */
11387 
11388 
11389 /**
11390  * @class
11391  * Construct a new Islamic RD date number object. The constructor parameters can 
11392  * contain any of the following properties:
11393  * 
11394  * <ul>
11395  * <li><i>unixtime<i> - sets the time of this instance according to the given 
11396  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
11397  * 
11398  * <li><i>julianday</i> - sets the time of this instance according to the given
11399  * Julian Day instance or the Julian Day given as a float
11400  * 
11401  * <li><i>year</i> - any integer, including 0
11402  * 
11403  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
11404  * 
11405  * <li><i>day</i> - 1 to 31
11406  * 
11407  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
11408  * is always done with an unambiguous 24 hour representation
11409  * 
11410  * <li><i>minute</i> - 0 to 59
11411  * 
11412  * <li><i>second</i> - 0 to 59
11413  * 
11414  * <li><i>millisecond</i> - 0 to 999
11415  * 
11416  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
11417  * </ul>
11418  *
11419  * If the constructor is called with another Islamic date instance instead of
11420  * a parameter block, the other instance acts as a parameter block and its
11421  * settings are copied into the current instance.<p>
11422  * 
11423  * If the constructor is called with no arguments at all or if none of the 
11424  * properties listed above are present, then the RD is calculate based on 
11425  * the current date at the time of instantiation. <p>
11426  * 
11427  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
11428  * specified in the params, it is assumed that they have the smallest possible
11429  * value in the range for the property (zero or one).<p>
11430  * 
11431  * 
11432  * @private
11433  * @constructor
11434  * @extends RataDie
11435  * @param {Object=} params parameters that govern the settings and behaviour of this Islamic RD date
11436  */
11437 var IslamicRataDie = function(params) {
11438 	this.cal = params && params.cal || new IslamicCal();
11439 	this.rd = NaN;
11440 	RataDie.call(this, params);
11441 };
11442 
11443 IslamicRataDie.prototype = new RataDie();
11444 IslamicRataDie.prototype.parent = RataDie;
11445 IslamicRataDie.prototype.constructor = IslamicRataDie;
11446 
11447 /**
11448  * The difference between a zero Julian day and the first Islamic date
11449  * of Friday, July 16, 622 CE Julian. 
11450  * @private
11451  * @type number
11452  */
11453 IslamicRataDie.prototype.epoch = 1948439.5;
11454 
11455 /**
11456  * Calculate the Rata Die (fixed day) number of the given date from the
11457  * date components.
11458  *
11459  * @protected
11460  * @param {Object} date the date components to calculate the RD from
11461  */
11462 IslamicRataDie.prototype._setDateComponents = function(date) {
11463 	var days = (date.year - 1) * 354 +
11464 		Math.ceil(29.5 * (date.month - 1)) +
11465 		date.day +
11466 		Math.floor((3 + 11 * date.year) / 30) - 1;
11467 	var time = (date.hour * 3600000 +
11468 		date.minute * 60000 +
11469 		date.second * 1000 +
11470 		date.millisecond) / 
11471 		86400000; 
11472 	
11473 	//console.log("getRataDie: converting " +  JSON.stringify(date));
11474 	//console.log("getRataDie: days is " +  days);
11475 	//console.log("getRataDie: time is " +  time);
11476 	//console.log("getRataDie: rd is " +  (days + time));
11477 
11478 	this.rd = days + time;
11479 };
11480 	
11481 
11482 /*< IslamicDate.js */
11483 /*
11484  * islamicDate.js - Represent a date in the Islamic calendar
11485  * 
11486  * Copyright © 2012-2015, JEDLSoft
11487  *
11488  * Licensed under the Apache License, Version 2.0 (the "License");
11489  * you may not use this file except in compliance with the License.
11490  * You may obtain a copy of the License at
11491  *
11492  *     http://www.apache.org/licenses/LICENSE-2.0
11493  *
11494  * Unless required by applicable law or agreed to in writing, software
11495  * distributed under the License is distributed on an "AS IS" BASIS,
11496  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11497  *
11498  * See the License for the specific language governing permissions and
11499  * limitations under the License.
11500  */
11501 
11502 /* !depends 
11503 ilib.js
11504 Locale.js
11505 LocaleInfo.js
11506 TimeZone.js
11507 IDate.js
11508 MathUtils.js
11509 SearchUtils.js
11510 Calendar.js
11511 IslamicCal.js
11512 IslamicRataDie.js
11513 */
11514 
11515 
11516 
11517 
11518 /**
11519  * @class
11520  * Construct a new civil Islamic date object. The constructor can be called
11521  * with a params object that can contain the following properties:<p>
11522  * 
11523  * <ul>
11524  * <li><i>julianday</i> - the Julian Day to set into this date
11525  * <li><i>year</i> - any integer except 0. Years go from -1 (BCE) to 1 (CE), skipping the zero year
11526  * <li><i>month</i> - 1 to 12, where 1 means Muharram, 2 means Saffar, etc.
11527  * <li><i>day</i> - 1 to 30
11528  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
11529  * is always done with an unambiguous 24 hour representation
11530  * <li><i>minute</i> - 0 to 59
11531  * <li><i>second</i> - 0 to 59
11532  * <li><i>millisecond</i> - 0 to 999
11533  * <li><i>locale</i> - the TimeZone instance or time zone name as a string 
11534  * of this julian date. The date/time is kept in the local time. The time zone
11535  * is used later if this date is formatted according to a different time zone and
11536  * the difference has to be calculated, or when the date format has a time zone
11537  * component in it.
11538  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 
11539  * given, it can be inferred from this locale. For locales that span multiple
11540  * time zones, the one with the largest population is chosen as the one that 
11541  * represents the locale. 
11542  * 
11543  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
11544  * </ul>
11545  * 
11546  * If called with another Islamic date argument, the date components of the given
11547  * date are copied into the current one.<p>
11548  * 
11549  * If the constructor is called with no arguments at all or if none of the 
11550  * properties listed above 
11551  * from <i>julianday</i> through <i>millisecond</i> are present, then the date 
11552  * components are 
11553  * filled in with the current date at the time of instantiation. Note that if
11554  * you do not give the time zone when defaulting to the current time and the 
11555  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
11556  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
11557  * Mean Time").<p>
11558  * 
11559  * 
11560  * @constructor
11561  * @extends IDate
11562  * @param {Object=} params parameters that govern the settings and behaviour of this Islamic date
11563  */
11564 var IslamicDate = function(params) {
11565 	this.cal = new IslamicCal();
11566 	
11567 	if (params) {
11568 		if (params.locale) {
11569 			this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
11570 			var li = new LocaleInfo(this.locale);
11571 			this.timezone = li.getTimeZone(); 
11572 		}
11573 		if (params.timezone) {
11574 			this.timezone = params.timezone;
11575 		}
11576 		
11577 		if (params.year || params.month || params.day || params.hour ||
11578 				params.minute || params.second || params.millisecond ) {
11579 			/**
11580 			 * Year in the Islamic calendar.
11581 			 * @type number
11582 			 */
11583 			this.year = parseInt(params.year, 10) || 0;
11584 
11585 			/**
11586 			 * The month number, ranging from 1 to 12 (December).
11587 			 * @type number
11588 			 */
11589 			this.month = parseInt(params.month, 10) || 1;
11590 
11591 			/**
11592 			 * The day of the month. This ranges from 1 to 30.
11593 			 * @type number
11594 			 */
11595 			this.day = parseInt(params.day, 10) || 1;
11596 			
11597 			/**
11598 			 * The hour of the day. This can be a number from 0 to 23, as times are
11599 			 * stored unambiguously in the 24-hour clock.
11600 			 * @type number
11601 			 */
11602 			this.hour = parseInt(params.hour, 10) || 0;
11603 
11604 			/**
11605 			 * The minute of the hours. Ranges from 0 to 59.
11606 			 * @type number
11607 			 */
11608 			this.minute = parseInt(params.minute, 10) || 0;
11609 
11610 			/**
11611 			 * The second of the minute. Ranges from 0 to 59.
11612 			 * @type number
11613 			 */
11614 			this.second = parseInt(params.second, 10) || 0;
11615 
11616 			/**
11617 			 * The millisecond of the second. Ranges from 0 to 999.
11618 			 * @type number
11619 			 */
11620 			this.millisecond = parseInt(params.millisecond, 10) || 0;
11621 			
11622 			/**
11623 			 * The day of the year. Ranges from 1 to 355.
11624 			 * @type number
11625 			 */
11626 			this.dayOfYear = parseInt(params.dayOfYear, 10);
11627 
11628 			if (typeof(params.dst) === 'boolean') {
11629 				this.dst = params.dst;
11630 			}
11631 			
11632 			this.rd = this.newRd(this);
11633 			
11634 			// add the time zone offset to the rd to convert to UTC
11635 			if (!this.tz) {
11636 				this.tz = new TimeZone({id: this.timezone});
11637 			}
11638 			// getOffsetMillis requires that this.year, this.rd, and this.dst 
11639 			// are set in order to figure out which time zone rules apply and 
11640 			// what the offset is at that point in the year
11641 			this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
11642 			if (this.offset !== 0) {
11643 				this.rd = this.newRd({
11644 					rd: this.rd.getRataDie() - this.offset
11645 				});
11646 			}
11647 		}
11648 	}
11649 
11650 	if (!this.rd) {
11651 		this.rd = this.newRd(params);
11652 		this._calcDateComponents();
11653 	}
11654 };
11655 
11656 IslamicDate.prototype = new IDate({noinstance: true});
11657 IslamicDate.prototype.parent = IDate;
11658 IslamicDate.prototype.constructor = IslamicDate;
11659 
11660 /**
11661  * the cumulative lengths of each month, for a non-leap year 
11662  * @private
11663  * @const
11664  * @type Array.<number>
11665  */
11666 IslamicDate.cumMonthLengths = [
11667 	0,  /* Muharram */
11668 	30,  /* Saffar */
11669 	59,  /* Rabi'I */
11670 	89,  /* Rabi'II */
11671 	118,  /* Jumada I */
11672 	148,  /* Jumada II */
11673 	177,  /* Rajab */
11674 	207,  /* Sha'ban */
11675 	236,  /* Ramadan */
11676 	266,  /* Shawwal */
11677 	295,  /* Dhu al-Qa'da */
11678 	325,  /* Dhu al-Hijja */
11679 	354
11680 ];
11681 
11682 /**
11683  * Number of days difference between RD 0 of the Gregorian calendar and
11684  * RD 0 of the Islamic calendar. 
11685  * @private
11686  * @const
11687  * @type number
11688  */
11689 IslamicDate.GregorianDiff = 227015;
11690 
11691 /**
11692  * Return a new RD for this date type using the given params.
11693  * @protected
11694  * @param {Object=} params the parameters used to create this rata die instance
11695  * @returns {RataDie} the new RD instance for the given params
11696  */
11697 IslamicDate.prototype.newRd = function (params) {
11698 	return new IslamicRataDie(params);
11699 };
11700 
11701 /**
11702  * Return the year for the given RD
11703  * @protected
11704  * @param {number} rd RD to calculate from 
11705  * @returns {number} the year for the RD
11706  */
11707 IslamicDate.prototype._calcYear = function(rd) {
11708 	return Math.floor((30 * rd + 10646) / 10631);
11709 };
11710 
11711 /**
11712  * Calculate date components for the given RD date.
11713  * @protected
11714  */
11715 IslamicDate.prototype._calcDateComponents = function () {
11716 	var remainder,
11717 		rd = this.rd.getRataDie();
11718 	
11719 	this.year = this._calcYear(rd);
11720 
11721 	if (typeof(this.offset) === "undefined") {
11722 		this.year = this._calcYear(rd);
11723 		
11724 		// now offset the RD by the time zone, then recalculate in case we were 
11725 		// near the year boundary
11726 		if (!this.tz) {
11727 			this.tz = new TimeZone({id: this.timezone});
11728 		}
11729 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
11730 	}
11731 
11732 	if (this.offset !== 0) {
11733 		rd += this.offset;
11734 		this.year = this._calcYear(rd);
11735 	}
11736 
11737 	//console.log("IslamicDate.calcComponent: calculating for rd " + rd);
11738 	//console.log("IslamicDate.calcComponent: year is " + ret.year);
11739 	var yearStart = this.newRd({
11740 		year: this.year,
11741 		month: 1,
11742 		day: 1,
11743 		hour: 0,
11744 		minute: 0,
11745 		second: 0,
11746 		millisecond: 0
11747 	});
11748 	remainder = rd - yearStart.getRataDie() + 1;
11749 	
11750 	this.dayOfYear = remainder;
11751 	
11752 	//console.log("IslamicDate.calcComponent: remainder is " + remainder);
11753 	
11754 	this.month = SearchUtils.bsearch(remainder, IslamicDate.cumMonthLengths);
11755 	remainder -= IslamicDate.cumMonthLengths[this.month-1];
11756 
11757 	//console.log("IslamicDate.calcComponent: month is " + this.month + " and remainder is " + remainder);
11758 	
11759 	this.day = Math.floor(remainder);
11760 	remainder -= this.day;
11761 
11762 	//console.log("IslamicDate.calcComponent: day is " + this.day + " and remainder is " + remainder);
11763 
11764 	// now convert to milliseconds for the rest of the calculation
11765 	remainder = Math.round(remainder * 86400000);
11766 	
11767 	this.hour = Math.floor(remainder/3600000);
11768 	remainder -= this.hour * 3600000;
11769 	
11770 	this.minute = Math.floor(remainder/60000);
11771 	remainder -= this.minute * 60000;
11772 	
11773 	this.second = Math.floor(remainder/1000);
11774 	remainder -= this.second * 1000;
11775 	
11776 	this.millisecond = remainder;
11777 };
11778 
11779 /**
11780  * Return the day of the week of this date. The day of the week is encoded
11781  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
11782  * 
11783  * @return {number} the day of the week
11784  */
11785 IslamicDate.prototype.getDayOfWeek = function() {
11786 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
11787 	return MathUtils.mod(rd-2, 7);
11788 };
11789 
11790 /**
11791  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 
11792  * 354 or 355, regardless of months or weeks, etc. That is, Muharran 1st is day 1, and 
11793  * Dhu al-Hijja 29 is 354.
11794  * @return {number} the ordinal day of the year
11795  */
11796 IslamicDate.prototype.getDayOfYear = function() {
11797 	return IslamicDate.cumMonthLengths[this.month-1] + this.day;
11798 };
11799 
11800 /**
11801  * Return the era for this date as a number. The value for the era for Islamic 
11802  * calendars is -1 for "before the Islamic era" and 1 for "the Islamic era". 
11803  * Islamic era dates are any date after Muharran 1, 1, which is the same as
11804  * July 16, 622 CE in the Gregorian calendar. 
11805  * 
11806  * @return {number} 1 if this date is in the common era, -1 if it is before the 
11807  * common era 
11808  */
11809 IslamicDate.prototype.getEra = function() {
11810 	return (this.year < 1) ? -1 : 1;
11811 };
11812 
11813 /**
11814  * Return the name of the calendar that governs this date.
11815  * 
11816  * @return {string} a string giving the name of the calendar
11817  */
11818 IslamicDate.prototype.getCalendar = function() {
11819 	return "islamic";
11820 };
11821 
11822 //register with the factory method
11823 IDate._constructors["islamic"] = IslamicDate;
11824 
11825 
11826 /*< JulianCal.js */
11827 /*
11828  * julian.js - Represent a Julian calendar object.
11829  * 
11830  * Copyright © 2012-2015, JEDLSoft
11831  *
11832  * Licensed under the Apache License, Version 2.0 (the "License");
11833  * you may not use this file except in compliance with the License.
11834  * You may obtain a copy of the License at
11835  *
11836  *     http://www.apache.org/licenses/LICENSE-2.0
11837  *
11838  * Unless required by applicable law or agreed to in writing, software
11839  * distributed under the License is distributed on an "AS IS" BASIS,
11840  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11841  *
11842  * See the License for the specific language governing permissions and
11843  * limitations under the License.
11844  */
11845 
11846 
11847 /* !depends ilib.js Calendar.js MathUtils.js */
11848 
11849 
11850 /**
11851  * @class
11852  * Construct a new Julian calendar object. This class encodes information about
11853  * a Julian calendar.<p>
11854  * 
11855  * 
11856  * @constructor
11857  * @extends Calendar
11858  */
11859 var JulianCal = function() {
11860 	this.type = "julian";
11861 };
11862 
11863 /* the lengths of each month */
11864 JulianCal.monthLengths = [
11865 	31,  /* Jan */
11866 	28,  /* Feb */
11867 	31,  /* Mar */
11868 	30,  /* Apr */
11869 	31,  /* May */
11870 	30,  /* Jun */
11871 	31,  /* Jul */
11872 	31,  /* Aug */
11873 	30,  /* Sep */
11874 	31,  /* Oct */
11875 	30,  /* Nov */
11876 	31   /* Dec */
11877 ];
11878 
11879 /**
11880  * the cumulative lengths of each month, for a non-leap year 
11881  * @private
11882  * @const
11883  * @type Array.<number>
11884  */
11885 JulianCal.cumMonthLengths = [
11886     0,   /* Jan */
11887 	31,  /* Feb */
11888 	59,  /* Mar */
11889 	90,  /* Apr */
11890 	120, /* May */
11891 	151, /* Jun */
11892 	181, /* Jul */
11893 	212, /* Aug */
11894 	243, /* Sep */
11895 	273, /* Oct */
11896 	304, /* Nov */
11897 	334, /* Dec */
11898 	365
11899 ];
11900 
11901 /**
11902  * the cumulative lengths of each month, for a leap year 
11903  * @private
11904  * @const
11905  * @type Array.<number>
11906  */
11907 JulianCal.cumMonthLengthsLeap = [
11908 	0,   /* Jan */
11909 	31,  /* Feb */
11910 	60,  /* Mar */
11911 	91,  /* Apr */
11912 	121, /* May */
11913 	152, /* Jun */
11914 	182, /* Jul */
11915 	213, /* Aug */
11916 	244, /* Sep */
11917 	274, /* Oct */
11918 	305, /* Nov */
11919 	335, /* Dec */
11920 	366
11921 ];
11922 
11923 /**
11924  * Return the number of months in the given year. The number of months in a year varies
11925  * for lunar calendars because in some years, an extra month is needed to extend the 
11926  * days in a year to an entire solar year. The month is represented as a 1-based number
11927  * where 1=Jaunary, 2=February, etc. until 12=December.
11928  * 
11929  * @param {number} year a year for which the number of months is sought
11930  */
11931 JulianCal.prototype.getNumMonths = function(year) {
11932 	return 12;
11933 };
11934 
11935 /**
11936  * Return the number of days in a particular month in a particular year. This function
11937  * can return a different number for a month depending on the year because of things
11938  * like leap years.
11939  * 
11940  * @param {number} month the month for which the length is sought
11941  * @param {number} year the year within which that month can be found
11942  * @return {number} the number of days within the given month in the given year
11943  */
11944 JulianCal.prototype.getMonLength = function(month, year) {
11945 	if (month !== 2 || !this.isLeapYear(year)) {
11946 		return JulianCal.monthLengths[month-1];
11947 	} else {
11948 		return 29;
11949 	}
11950 };
11951 
11952 /**
11953  * Return true if the given year is a leap year in the Julian calendar.
11954  * The year parameter may be given as a number, or as a JulDate object.
11955  * @param {number|JulianDate} year the year for which the leap year information is being sought
11956  * @return {boolean} true if the given year is a leap year
11957  */
11958 JulianCal.prototype.isLeapYear = function(year) {
11959 	var y = (typeof(year) === 'number' ? year : year.year);
11960 	return MathUtils.mod(y, 4) === ((year > 0) ? 0 : 3);
11961 };
11962 
11963 /**
11964  * Return the type of this calendar.
11965  * 
11966  * @return {string} the name of the type of this calendar 
11967  */
11968 JulianCal.prototype.getType = function() {
11969 	return this.type;
11970 };
11971 
11972 /**
11973  * Return a date instance for this calendar type using the given
11974  * options.
11975  * @param {Object} options options controlling the construction of 
11976  * the date instance
11977  * @return {IDate} a date appropriate for this calendar type
11978  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
11979  */
11980 JulianCal.prototype.newDateInstance = function (options) {
11981 		return new JulianDate(options);
11982 };
11983 
11984 /* register this calendar for the factory method */
11985 Calendar._constructors["julian"] = JulianCal;
11986 
11987 
11988 /*< JulianRataDie.js */
11989 /*
11990  * julianDate.js - Represent a date in the Julian calendar
11991  * 
11992  * Copyright © 2012-2015, JEDLSoft
11993  *
11994  * Licensed under the Apache License, Version 2.0 (the "License");
11995  * you may not use this file except in compliance with the License.
11996  * You may obtain a copy of the License at
11997  *
11998  *     http://www.apache.org/licenses/LICENSE-2.0
11999  *
12000  * Unless required by applicable law or agreed to in writing, software
12001  * distributed under the License is distributed on an "AS IS" BASIS,
12002  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12003  *
12004  * See the License for the specific language governing permissions and
12005  * limitations under the License.
12006  */
12007 
12008 /* !depends 
12009 JulianCal.js 
12010 RataDie.js
12011 */
12012 
12013 
12014 /**
12015  * @class
12016  * Construct a new Julian RD date number object. The constructor parameters can 
12017  * contain any of the following properties:
12018  * 
12019  * <ul>
12020  * <li><i>unixtime<i> - sets the time of this instance according to the given 
12021  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
12022  * 
12023  * <li><i>julianday</i> - sets the time of this instance according to the given
12024  * Julian Day instance or the Julian Day given as a float
12025  * 
12026  * <li><i>year</i> - any integer, including 0
12027  * 
12028  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
12029  * 
12030  * <li><i>day</i> - 1 to 31
12031  * 
12032  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
12033  * is always done with an unambiguous 24 hour representation
12034  * 
12035  * <li><i>minute</i> - 0 to 59
12036  * 
12037  * <li><i>second</i> - 0 to 59
12038  * 
12039  * <li><i>millisecond</i> - 0 to 999
12040  * 
12041  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
12042  * </ul>
12043  *
12044  * If the constructor is called with another Julian date instance instead of
12045  * a parameter block, the other instance acts as a parameter block and its
12046  * settings are copied into the current instance.<p>
12047  * 
12048  * If the constructor is called with no arguments at all or if none of the 
12049  * properties listed above are present, then the RD is calculate based on 
12050  * the current date at the time of instantiation. <p>
12051  * 
12052  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
12053  * specified in the params, it is assumed that they have the smallest possible
12054  * value in the range for the property (zero or one).<p>
12055  * 
12056  * 
12057  * @private
12058  * @constructor
12059  * @extends RataDie
12060  * @param {Object=} params parameters that govern the settings and behaviour of this Julian RD date
12061  */
12062 var JulianRataDie = function(params) {
12063 	this.cal = params && params.cal || new JulianCal();
12064 	this.rd = NaN;
12065 	RataDie.call(this, params);
12066 };
12067 
12068 JulianRataDie.prototype = new RataDie();
12069 JulianRataDie.prototype.parent = RataDie;
12070 JulianRataDie.prototype.constructor = JulianRataDie;
12071 
12072 /**
12073  * The difference between a zero Julian day and the first Julian date
12074  * of Friday, July 16, 622 CE Julian. 
12075  * @private
12076  * @type number
12077  */
12078 JulianRataDie.prototype.epoch = 1721422.5;
12079 
12080 /**
12081  * Calculate the Rata Die (fixed day) number of the given date from the
12082  * date components.
12083  * 
12084  * @protected
12085  * @param {Object} date the date components to calculate the RD from
12086  */
12087 JulianRataDie.prototype._setDateComponents = function(date) {
12088 	var year = date.year + ((date.year < 0) ? 1 : 0);
12089 	var years = 365 * (year - 1) + Math.floor((year-1)/4);
12090 	var dayInYear = (date.month > 1 ? JulianCal.cumMonthLengths[date.month-1] : 0) +
12091 		date.day +
12092 		(this.cal.isLeapYear(date.year) && date.month > 2 ? 1 : 0);
12093 	var rdtime = (date.hour * 3600000 +
12094 		date.minute * 60000 +
12095 		date.second * 1000 +
12096 		date.millisecond) / 
12097 		86400000;
12098 	
12099 	/*
12100 	console.log("calcRataDie: converting " +  JSON.stringify(parts));
12101 	console.log("getRataDie: year is " +  years);
12102 	console.log("getRataDie: day in year is " +  dayInYear);
12103 	console.log("getRataDie: rdtime is " +  rdtime);
12104 	console.log("getRataDie: rd is " +  (years + dayInYear + rdtime));
12105 	*/
12106 	
12107 	this.rd = years + dayInYear + rdtime;
12108 };
12109 
12110 
12111 /*< JulianDate.js */
12112 /*
12113  * JulianDate.js - Represent a date in the Julian calendar
12114  * 
12115  * Copyright © 2012-2015, JEDLSoft
12116  *
12117  * Licensed under the Apache License, Version 2.0 (the "License");
12118  * you may not use this file except in compliance with the License.
12119  * You may obtain a copy of the License at
12120  *
12121  *     http://www.apache.org/licenses/LICENSE-2.0
12122  *
12123  * Unless required by applicable law or agreed to in writing, software
12124  * distributed under the License is distributed on an "AS IS" BASIS,
12125  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12126  *
12127  * See the License for the specific language governing permissions and
12128  * limitations under the License.
12129  */
12130 
12131 /* !depends 
12132 ilib.js
12133 Locale.js
12134 IDate.js 
12135 TimeZone.js
12136 Calendar.js 
12137 JulianCal.js 
12138 SearchUtils.js 
12139 MathUtils.js
12140 LocaleInfo.js 
12141 JulianRataDie.js
12142 */
12143 
12144 
12145 
12146 
12147 /**
12148  * @class
12149  * Construct a new date object for the Julian Calendar. The constructor can be called
12150  * with a parameter object that contains any of the following properties:
12151  * 
12152  * <ul>
12153  * <li><i>unixtime<i> - sets the time of this instance according to the given 
12154  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970 (Gregorian).
12155  * <li><i>julianday</i> - the Julian Day to set into this date
12156  * <li><i>year</i> - any integer except 0. Years go from -1 (BCE) to 1 (CE), skipping the zero 
12157  * year which doesn't exist in the Julian calendar
12158  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
12159  * <li><i>day</i> - 1 to 31
12160  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
12161  * is always done with an unambiguous 24 hour representation
12162  * <li><i>minute</i> - 0 to 59
12163  * <li><i>second</i> - 0 to 59
12164  * <li><i>millisecond<i> - 0 to 999
12165  * <li><i>locale</i> - the TimeZone instance or time zone name as a string 
12166  * of this julian date. The date/time is kept in the local time. The time zone
12167  * is used later if this date is formatted according to a different time zone and
12168  * the difference has to be calculated, or when the date format has a time zone
12169  * component in it.
12170  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 
12171  * given, it can be inferred from this locale. For locales that span multiple
12172  * time zones, the one with the largest population is chosen as the one that 
12173  * represents the locale. 
12174  * 
12175  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
12176  * </ul>
12177  * 
12178  * NB. The <a href="http://en.wikipedia.org/wiki/Julian_date">Julian Day</a> 
12179  * (JulianDay) object is a <i>different</i> object than a 
12180  * <a href="http://en.wikipedia.org/wiki/Julian_calendar">date in
12181  * the Julian calendar</a> and the two are not to be confused. The Julian Day 
12182  * object represents time as a number of whole and fractional days since the 
12183  * beginning of the epoch, whereas a date in the Julian 
12184  * calendar is a regular date that signifies year, month, day, etc. using the rules
12185  * of the Julian calendar. The naming of Julian Days and the Julian calendar are
12186  * unfortunately close, and come from history.<p>
12187  *  
12188  * If called with another Julian date argument, the date components of the given
12189  * date are copied into the current one.<p>
12190  * 
12191  * If the constructor is called with no arguments at all or if none of the 
12192  * properties listed above 
12193  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
12194  * components are 
12195  * filled in with the current date at the time of instantiation. Note that if
12196  * you do not give the time zone when defaulting to the current time and the 
12197  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
12198  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
12199  * Mean Time").<p>
12200  * 
12201  * 
12202  * @constructor
12203  * @extends IDate
12204  * @param {Object=} params parameters that govern the settings and behaviour of this Julian date
12205  */
12206 var JulianDate = function(params) {
12207 	this.cal = new JulianCal();
12208 	
12209 	if (params) {
12210 		if (params.locale) {
12211 			this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
12212 			var li = new LocaleInfo(this.locale);
12213 			this.timezone = li.getTimeZone(); 
12214 		}
12215 		if (params.timezone) {
12216 			this.timezone = params.timezone;
12217 		}
12218 		
12219 		if (params.year || params.month || params.day || params.hour ||
12220 				params.minute || params.second || params.millisecond ) {
12221 			/**
12222 			 * Year in the Julian calendar.
12223 			 * @type number
12224 			 */
12225 			this.year = parseInt(params.year, 10) || 0;
12226 			/**
12227 			 * The month number, ranging from 1 (January) to 12 (December).
12228 			 * @type number
12229 			 */
12230 			this.month = parseInt(params.month, 10) || 1;
12231 			/**
12232 			 * The day of the month. This ranges from 1 to 31.
12233 			 * @type number
12234 			 */
12235 			this.day = parseInt(params.day, 10) || 1;
12236 			/**
12237 			 * The hour of the day. This can be a number from 0 to 23, as times are
12238 			 * stored unambiguously in the 24-hour clock.
12239 			 * @type number
12240 			 */
12241 			this.hour = parseInt(params.hour, 10) || 0;
12242 			/**
12243 			 * The minute of the hours. Ranges from 0 to 59.
12244 			 * @type number
12245 			 */
12246 			this.minute = parseInt(params.minute, 10) || 0;
12247 			/**
12248 			 * The second of the minute. Ranges from 0 to 59.
12249 			 * @type number
12250 			 */
12251 			this.second = parseInt(params.second, 10) || 0;
12252 			/**
12253 			 * The millisecond of the second. Ranges from 0 to 999.
12254 			 * @type number
12255 			 */
12256 			this.millisecond = parseInt(params.millisecond, 10) || 0;
12257 			
12258 			/**
12259 			 * The day of the year. Ranges from 1 to 383.
12260 			 * @type number
12261 			 */
12262 			this.dayOfYear = parseInt(params.dayOfYear, 10);
12263 			
12264 			if (typeof(params.dst) === 'boolean') {
12265 				this.dst = params.dst;
12266 			}
12267 			
12268 			this.rd = this.newRd(this);
12269 			
12270 			// add the time zone offset to the rd to convert to UTC
12271 			if (!this.tz) {
12272 				this.tz = new TimeZone({id: this.timezone});
12273 			}
12274 			// getOffsetMillis requires that this.year, this.rd, and this.dst 
12275 			// are set in order to figure out which time zone rules apply and 
12276 			// what the offset is at that point in the year
12277 			this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
12278 			if (this.offset !== 0) {
12279 				this.rd = this.newRd({
12280 					rd: this.rd.getRataDie() - this.offset
12281 				});
12282 			}
12283 		}
12284 	}
12285 	
12286 	if (!this.rd) {
12287 		this.rd = this.newRd(params);
12288 		this._calcDateComponents();
12289 	}
12290 };
12291 
12292 JulianDate.prototype = new IDate({noinstance: true});
12293 JulianDate.prototype.parent = IDate;
12294 JulianDate.prototype.constructor = JulianDate;
12295 
12296 /**
12297  * Return a new RD for this date type using the given params.
12298  * @protected
12299  * @param {Object=} params the parameters used to create this rata die instance
12300  * @returns {RataDie} the new RD instance for the given params
12301  */
12302 JulianDate.prototype.newRd = function (params) {
12303 	return new JulianRataDie(params);
12304 };
12305 
12306 /**
12307  * Return the year for the given RD
12308  * @protected
12309  * @param {number} rd RD to calculate from 
12310  * @returns {number} the year for the RD
12311  */
12312 JulianDate.prototype._calcYear = function(rd) {
12313 	var year = Math.floor((4*(Math.floor(rd)-1) + 1464)/1461);
12314 	
12315 	return (year <= 0) ? year - 1 : year;
12316 };
12317 
12318 /**
12319  * Calculate date components for the given RD date.
12320  * @protected
12321  */
12322 JulianDate.prototype._calcDateComponents = function () {
12323 	var remainder,
12324 		cumulative,
12325 		rd = this.rd.getRataDie();
12326 	
12327 	this.year = this._calcYear(rd);
12328 
12329 	if (typeof(this.offset) === "undefined") {
12330 		this.year = this._calcYear(rd);
12331 		
12332 		// now offset the RD by the time zone, then recalculate in case we were 
12333 		// near the year boundary
12334 		if (!this.tz) {
12335 			this.tz = new TimeZone({id: this.timezone});
12336 		}
12337 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
12338 	}
12339 
12340 	if (this.offset !== 0) {
12341 		rd += this.offset;
12342 		this.year = this._calcYear(rd);
12343 	}
12344 	
12345 	var jan1 = this.newRd({
12346 		year: this.year,
12347 		month: 1,
12348 		day: 1,
12349 		hour: 0,
12350 		minute: 0,
12351 		second: 0,
12352 		millisecond: 0
12353 	});
12354 	remainder = rd + 1 - jan1.getRataDie();
12355 	
12356 	cumulative = this.cal.isLeapYear(this.year) ? 
12357 		JulianCal.cumMonthLengthsLeap : 
12358 		JulianCal.cumMonthLengths; 
12359 	
12360 	this.month = SearchUtils.bsearch(Math.floor(remainder), cumulative);
12361 	remainder = remainder - cumulative[this.month-1];
12362 	
12363 	this.day = Math.floor(remainder);
12364 	remainder -= this.day;
12365 	// now convert to milliseconds for the rest of the calculation
12366 	remainder = Math.round(remainder * 86400000);
12367 	
12368 	this.hour = Math.floor(remainder/3600000);
12369 	remainder -= this.hour * 3600000;
12370 	
12371 	this.minute = Math.floor(remainder/60000);
12372 	remainder -= this.minute * 60000;
12373 	
12374 	this.second = Math.floor(remainder/1000);
12375 	remainder -= this.second * 1000;
12376 	
12377 	this.millisecond = remainder;
12378 };
12379 
12380 /**
12381  * Return the day of the week of this date. The day of the week is encoded
12382  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
12383  * 
12384  * @return {number} the day of the week
12385  */
12386 JulianDate.prototype.getDayOfWeek = function() {
12387 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
12388 	return MathUtils.mod(rd-2, 7);
12389 };
12390 
12391 /**
12392  * Return the name of the calendar that governs this date.
12393  * 
12394  * @return {string} a string giving the name of the calendar
12395  */
12396 JulianDate.prototype.getCalendar = function() {
12397 	return "julian";
12398 };
12399 
12400 //register with the factory method
12401 IDate._constructors["julian"] = JulianDate;
12402 
12403 
12404 /*< ThaiSolarCal.js */
12405 /*
12406  * thaisolar.js - Represent a Thai solar calendar object.
12407  *
12408  * Copyright © 2013-2015, JEDLSoft
12409  *
12410  * Licensed under the Apache License, Version 2.0 (the "License");
12411  * you may not use this file except in compliance with the License.
12412  * You may obtain a copy of the License at
12413  *
12414  *     http://www.apache.org/licenses/LICENSE-2.0
12415  *
12416  * Unless required by applicable law or agreed to in writing, software
12417  * distributed under the License is distributed on an "AS IS" BASIS,
12418  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12419  *
12420  * See the License for the specific language governing permissions and
12421  * limitations under the License.
12422  */
12423 
12424 
12425 /* !depends ilib.js Calendar.js GregorianCal.js MathUtils.js */
12426 
12427 
12428 /**
12429  * @class
12430  * Construct a new Thai solar calendar object. This class encodes information about
12431  * a Thai solar calendar.<p>
12432  *
12433  *
12434  * @constructor
12435  * @extends Calendar
12436  */
12437 var ThaiSolarCal = function() {
12438 	this.type = "thaisolar";
12439 };
12440 
12441 ThaiSolarCal.prototype = new GregorianCal({noinstance: true});
12442 ThaiSolarCal.prototype.parent = GregorianCal;
12443 ThaiSolarCal.prototype.constructor = ThaiSolarCal;
12444 
12445 /**
12446  * Return true if the given year is a leap year in the Thai solar calendar.
12447  * The year parameter may be given as a number, or as a ThaiSolarDate object.
12448  * @param {number|ThaiSolarDate} year the year for which the leap year information is being sought
12449  * @return {boolean} true if the given year is a leap year
12450  */
12451 ThaiSolarCal.prototype.isLeapYear = function(year) {
12452 	var y = (typeof(year) === 'number' ? year : year.getYears());
12453 	y -= 543;
12454 	var centuries = MathUtils.mod(y, 400);
12455 	return (MathUtils.mod(y, 4) === 0 && centuries !== 100 && centuries !== 200 && centuries !== 300);
12456 };
12457 
12458 /**
12459  * Return a date instance for this calendar type using the given
12460  * options.
12461  * @param {Object} options options controlling the construction of
12462  * the date instance
12463  * @return {IDate} a date appropriate for this calendar type
12464  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
12465  */
12466 ThaiSolarCal.prototype.newDateInstance = function (options) {
12467 		return new ThaiSolarDate(options);
12468 };
12469 
12470 /* register this calendar for the factory method */
12471 Calendar._constructors["thaisolar"] = ThaiSolarCal;
12472 
12473 
12474 /*< ThaiSolarDate.js */
12475 /*
12476  * ThaiSolarDate.js - Represent a date in the ThaiSolar calendar
12477  * 
12478  * Copyright © 2013-2015, JEDLSoft
12479  *
12480  * Licensed under the Apache License, Version 2.0 (the "License");
12481  * you may not use this file except in compliance with the License.
12482  * You may obtain a copy of the License at
12483  *
12484  *     http://www.apache.org/licenses/LICENSE-2.0
12485  *
12486  * Unless required by applicable law or agreed to in writing, software
12487  * distributed under the License is distributed on an "AS IS" BASIS,
12488  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12489  *
12490  * See the License for the specific language governing permissions and
12491  * limitations under the License.
12492  */
12493 
12494 /* !depends 
12495 ilib.js
12496 IDate.js 
12497 JSUtils.js
12498 GregorianDate.js
12499 ThaiSolarCal.js
12500 */
12501 
12502 
12503 
12504 
12505 /**
12506  * @class
12507  * Construct a new Thai solar date object. The constructor parameters can 
12508  * contain any of the following properties:
12509  * 
12510  * <ul>
12511  * <li><i>unixtime<i> - sets the time of this instance according to the given 
12512  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
12513  * 
12514  * <li><i>julianday</i> - sets the time of this instance according to the given
12515  * Julian Day instance or the Julian Day given as a float
12516  * 
12517  * <li><i>year</i> - any integer, including 0
12518  * 
12519  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
12520  * 
12521  * <li><i>day</i> - 1 to 31
12522  * 
12523  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
12524  * is always done with an unambiguous 24 hour representation
12525  * 
12526  * <li><i>minute</i> - 0 to 59
12527  * 
12528  * <li><i>second</i> - 0 to 59
12529  * 
12530  * <li><i>millisecond</i> - 0 to 999
12531  * 
12532  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string 
12533  * of this Thai solar date. The date/time is kept in the local time. The time zone
12534  * is used later if this date is formatted according to a different time zone and
12535  * the difference has to be calculated, or when the date format has a time zone
12536  * component in it.
12537  * 
12538  * <li><i>locale</i> - locale for this Thai solar date. If the time zone is not 
12539  * given, it can be inferred from this locale. For locales that span multiple
12540  * time zones, the one with the largest population is chosen as the one that 
12541  * represents the locale. 
12542  * </ul>
12543  *
12544  * If the constructor is called with another Thai solar date instance instead of
12545  * a parameter block, the other instance acts as a parameter block and its
12546  * settings are copied into the current instance.<p>
12547  * 
12548  * If the constructor is called with no arguments at all or if none of the 
12549  * properties listed above 
12550  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
12551  * components are 
12552  * filled in with the current date at the time of instantiation. Note that if
12553  * you do not give the time zone when defaulting to the current time and the 
12554  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
12555  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
12556  * Mean Time").<p>
12557  * 
12558  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
12559  * specified in the params, it is assumed that they have the smallest possible
12560  * value in the range for the property (zero or one).<p>
12561  * 
12562  * 
12563  * @constructor
12564  * @extends GregorianDate
12565  * @param {Object=} params parameters that govern the settings and behaviour of this Thai solar date
12566  */
12567 var ThaiSolarDate = function(params) {
12568 	var p = params;
12569 	if (params) {
12570 		// there is 198327 days difference between the Thai solar and 
12571 		// Gregorian epochs which is equivalent to 543 years
12572 		p = {};
12573 		JSUtils.shallowCopy(params, p);
12574 		if (typeof(p.year) !== 'undefined') {
12575 			p.year -= 543;	
12576 		}
12577 		if (typeof(p.rd) !== 'undefined') {
12578 			p.rd -= 198327;
12579 		}
12580 	}
12581 	this.rd = NaN; // clear these out so that the GregorianDate constructor can set it
12582 	this.offset = undefined;
12583 	//console.log("ThaiSolarDate.constructor: date is " + JSON.stringify(this) + " parent is " + JSON.stringify(this.parent) + " and parent.parent is " + JSON.stringify(this.parent.parent));
12584 	GregorianDate.call(this, p);
12585 	this.cal = new ThaiSolarCal();
12586 	// make sure the year is set correctly
12587 	if (params && typeof(params.year) !== 'undefined') {
12588 		this.year = parseInt(params.year, 10);
12589 	}
12590 };
12591 
12592 ThaiSolarDate.prototype = new GregorianDate({noinstance: true});
12593 ThaiSolarDate.prototype.parent = GregorianDate.prototype;
12594 ThaiSolarDate.prototype.constructor = ThaiSolarDate;
12595 
12596 /**
12597  * the difference between a zero Julian day and the zero Thai Solar date.
12598  * This is some 543 years before the start of the Gregorian epoch. 
12599  * @private
12600  * @type number
12601  */
12602 ThaiSolarDate.epoch = 1523097.5;
12603 
12604 /**
12605  * Calculate the date components for the current time zone
12606  * @protected
12607  */
12608 ThaiSolarDate.prototype._calcDateComponents = function () {
12609 	// there is 198327 days difference between the Thai solar and 
12610 	// Gregorian epochs which is equivalent to 543 years
12611 	// console.log("ThaiSolarDate._calcDateComponents: date is " + JSON.stringify(this) + " parent is " + JSON.stringify(this.parent) + " and parent.parent is " + JSON.stringify(this.parent.parent));
12612 	this.parent._calcDateComponents.call(this);
12613 	this.year += 543;
12614 };
12615 
12616 /**
12617  * Return the Rata Die (fixed day) number of this date.
12618  * 
12619  * @protected
12620  * @return {number} the rd date as a number
12621  */
12622 ThaiSolarDate.prototype.getRataDie = function() {
12623 	// there is 198327 days difference between the Thai solar and 
12624 	// Gregorian epochs which is equivalent to 543 years
12625 	return this.rd.getRataDie() + 198327;
12626 };
12627 
12628 /**
12629  * Return a new Gregorian date instance that represents the first instance of the 
12630  * given day of the week before the current date. The day of the week is encoded
12631  * as a number where 0 = Sunday, 1 = Monday, etc.
12632  * 
12633  * @param {number} dow the day of the week before the current date that is being sought
12634  * @return {IDate} the date being sought
12635  */
12636 ThaiSolarDate.prototype.before = function (dow) {
12637 	return new ThaiSolarDate({
12638 		rd: this.rd.before(dow, this.offset) + 198327,
12639 		timezone: this.timezone
12640 	});
12641 };
12642 
12643 /**
12644  * Return a new Gregorian date instance that represents the first instance of the 
12645  * given day of the week after the current date. The day of the week is encoded
12646  * as a number where 0 = Sunday, 1 = Monday, etc.
12647  * 
12648  * @param {number} dow the day of the week after the current date that is being sought
12649  * @return {IDate} the date being sought
12650  */
12651 ThaiSolarDate.prototype.after = function (dow) {
12652 	return new ThaiSolarDate({
12653 		rd: this.rd.after(dow, this.offset) + 198327,
12654 		timezone: this.timezone
12655 	});
12656 };
12657 
12658 /**
12659  * Return a new Gregorian date instance that represents the first instance of the 
12660  * given day of the week on or before the current date. The day of the week is encoded
12661  * as a number where 0 = Sunday, 1 = Monday, etc.
12662  * 
12663  * @param {number} dow the day of the week on or before the current date that is being sought
12664  * @return {IDate} the date being sought
12665  */
12666 ThaiSolarDate.prototype.onOrBefore = function (dow) {
12667 	return new ThaiSolarDate({
12668 		rd: this.rd.onOrBefore(dow, this.offset) + 198327,
12669 		timezone: this.timezone
12670 	});
12671 };
12672 
12673 /**
12674  * Return a new Gregorian date instance that represents the first instance of the 
12675  * given day of the week on or after the current date. The day of the week is encoded
12676  * as a number where 0 = Sunday, 1 = Monday, etc.
12677  * 
12678  * @param {number} dow the day of the week on or after the current date that is being sought
12679  * @return {IDate} the date being sought
12680  */
12681 ThaiSolarDate.prototype.onOrAfter = function (dow) {
12682 	return new ThaiSolarDate({
12683 		rd: this.rd.onOrAfter(dow, this.offset) + 198327,
12684 		timezone: this.timezone
12685 	});
12686 };
12687 
12688 /**
12689  * Return the name of the calendar that governs this date.
12690  * 
12691  * @return {string} a string giving the name of the calendar
12692  */
12693 ThaiSolarDate.prototype.getCalendar = function() {
12694 	return "thaisolar";
12695 };
12696 
12697 //register with the factory method
12698 IDate._constructors["thaisolar"] = ThaiSolarDate;
12699 
12700 
12701 
12702 /*< Astro.js */
12703 /*
12704  * astro.js - Static functions to support astronomical calculations
12705  * 
12706  * Copyright © 2014-2015, JEDLSoft
12707  *
12708  * Licensed under the Apache License, Version 2.0 (the "License");
12709  * you may not use this file except in compliance with the License.
12710  * You may obtain a copy of the License at
12711  *
12712  *     http://www.apache.org/licenses/LICENSE-2.0
12713  *
12714  * Unless required by applicable law or agreed to in writing, software
12715  * distributed under the License is distributed on an "AS IS" BASIS,
12716  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12717  *
12718  * See the License for the specific language governing permissions and
12719  * limitations under the License.
12720  */
12721 
12722 /* !depends
12723 ilib.js
12724 IDate.js
12725 Utils.js
12726 MathUtils.js
12727 SearchUtils.js
12728 GregorianDate.js
12729 GregRataDie.js
12730 */
12731 
12732 // !data astro
12733 
12734 /*
12735  * These routines were derived from a public domain set of JavaScript 
12736  * functions for positional astronomy by John Walker of Fourmilab, 
12737  * September 1999.
12738  */
12739 
12740 
12741 
12742 var Astro = {};
12743 
12744 /**
12745  * Load in all the data needed for astrological calculations.
12746  * 
12747  * @private
12748  * @param {boolean} sync
12749  * @param {*} loadParams
12750  * @param {function(*)|undefined} callback
12751  */
12752 Astro.initAstro = function(sync, loadParams, callback) {
12753 	if (!ilib.data.astro) {
12754 		Utils.loadData({
12755 			name: "astro.json", // countries in their own language 
12756 			locale: "-", // only need to load the root file 
12757 			nonLocale: true,
12758 			sync: sync, 
12759 			loadParams: loadParams, 
12760 			callback: ilib.bind(this, function(astroData) {
12761 				/** 
12762 				 * @type {{
12763 				 *  	_EquinoxpTerms:Array.<number>, 
12764 				 *  	_JDE0tab1000:Array.<number>, 
12765 				 *  	_JDE0tab2000:Array.<number>, 
12766 				 *  	_deltaTtab:Array.<number>,
12767 				 *  	_oterms:Array.<number>,
12768 				 *  	_nutArgMult:Array.<number>, 
12769 				 *  	_nutArgCoeff:Array.<number>, 
12770 				 *  	_nutCoeffA:Array.<number>,
12771 				 *  	_nutCoeffB:Array.<number>,
12772 				 *  	_coeff19th:Array.<number>,
12773 				 *  	_coeff18th:Array.<number>,
12774 				 *  	_solarLongCoeff:Array.<number>, 
12775 				 *  	_solarLongMultipliers:Array.<number>, 
12776 				 *  	_solarLongAddends:Array.<number>, 
12777 				 *  	_meanMoonCoeff:Array.<number>,
12778 				 *  	_elongationCoeff:Array.<number>,
12779 				 *  	_solarAnomalyCoeff:Array.<number>,
12780 				 *  	_lunarAnomalyCoeff:Array.<number>,
12781 				 *  	_moonFromNodeCoeff:Array.<number>,
12782 				 *  	_eCoeff:Array.<number>,
12783 				 *  	_lunarElongationLongCoeff:Array.<number>,
12784 				 *  	_solarAnomalyLongCoeff:Array.<number>,
12785 				 *  	_lunarAnomalyLongCoeff:Array.<number>,
12786 				 *  	_moonFromNodeLongCoeff:Array.<number>,
12787 				 *  	_sineCoeff:Array.<number>,
12788 				 *  	_nmApproxCoeff:Array.<number>,
12789 				 *  	_nmCapECoeff:Array.<number>,
12790 				 *  	_nmSolarAnomalyCoeff:Array.<number>,
12791 				 *  	_nmLunarAnomalyCoeff:Array.<number>,
12792 				 *  	_nmMoonArgumentCoeff:Array.<number>,
12793 				 *  	_nmCapOmegaCoeff:Array.<number>,
12794 				 *  	_nmEFactor:Array.<number>,
12795 				 *  	_nmSolarCoeff:Array.<number>,
12796 				 *  	_nmLunarCoeff:Array.<number>,
12797 				 *  	_nmMoonCoeff:Array.<number>,
12798 				 *  	_nmSineCoeff:Array.<number>,
12799 				 *  	_nmAddConst:Array.<number>,
12800 				 *  	_nmAddCoeff:Array.<number>,
12801 				 *  	_nmAddFactor:Array.<number>,
12802 				 *  	_nmExtra:Array.<number>
12803 				 *  }}
12804 				 */ 	
12805 			 	ilib.data.astro = astroData;
12806 				if (callback && typeof(callback) === 'function') {
12807 					callback(astroData);
12808 				}
12809 			})
12810 		});
12811 	} else {
12812 		if (callback && typeof(callback) === 'function') {
12813 			callback(ilib.data.astro);
12814 		}
12815 	}
12816 };
12817 
12818 /**
12819  * Convert degrees to radians.
12820  * 
12821  * @static
12822  * @protected
12823  * @param {number} d angle in degrees
12824  * @return {number} angle in radians 
12825  */
12826 Astro._dtr = function(d) {
12827 	return (d * Math.PI) / 180.0;
12828 };
12829 
12830 /**
12831  * Convert radians to degrees.
12832  * 
12833  * @static
12834  * @protected
12835  * @param {number} r angle in radians
12836  * @return {number} angle in degrees 
12837  */
12838 Astro._rtd = function(r) {
12839 	return (r * 180.0) / Math.PI;
12840 };
12841 
12842 /**
12843  * Return the cosine of an angle given in degrees.
12844  * @static
12845  * @protected
12846  * @param {number} d angle in degrees
12847  * @return {number} cosine of the angle.
12848  */  
12849 Astro._dcos = function(d) {
12850 	return Math.cos(Astro._dtr(d));
12851 };
12852 
12853 /**
12854  * Return the sine of an angle given in degrees.
12855  * @static
12856  * @protected
12857  * @param {number} d angle in degrees
12858  * @return {number} sine of the angle.
12859  */  
12860 Astro._dsin = function(d) {
12861 	return Math.sin(Astro._dtr(d));
12862 };
12863 
12864 /**
12865  * Return the tan of an angle given in degrees.
12866  * @static
12867  * @protected
12868  * @param {number} d angle in degrees
12869  * @return {number} tan of the angle.
12870  */  
12871 Astro._dtan = function(d) {
12872 	return Math.tan(Astro._dtr(d));
12873 };
12874 
12875 /**
12876  * Range reduce angle in degrees.
12877  * 
12878  * @static
12879  * @param {number} a angle to reduce
12880  * @return {number} the reduced angle  
12881  */
12882 Astro._fixangle = function(a) {
12883 	return a - 360.0 * (Math.floor(a / 360.0));
12884 };
12885 
12886 /**
12887  * Range reduce angle in radians.
12888  * 
12889  * @static
12890  * @protected
12891  * @param {number} a angle to reduce
12892  * @return {number} the reduced angle  
12893  */
12894 Astro._fixangr = function(a) {
12895 	return a - (2 * Math.PI) * (Math.floor(a / (2 * Math.PI)));
12896 };
12897 
12898 /**
12899  * Determine the Julian Ephemeris Day of an equinox or solstice.  The "which" 
12900  * argument selects the item to be computed:
12901  * 
12902  * <ul>
12903  * <li>0   March equinox
12904  * <li>1   June solstice
12905  * <li>2   September equinox
12906  * <li>3   December solstice
12907  * </ul>
12908  * 
12909  * @static
12910  * @protected
12911  * @param {number} year Gregorian year to calculate for
12912  * @param {number} which Which equinox or solstice to calculate
12913  */
12914 Astro._equinox = function(year, which) {
12915 	var deltaL, i, j, JDE0, JDE, JDE0tab, S, T, W, Y;
12916 
12917 	/*  Initialize terms for mean equinox and solstices.  We
12918 	    have two sets: one for years prior to 1000 and a second
12919 	    for subsequent years.  */
12920 
12921 	if (year < 1000) {
12922 		JDE0tab = ilib.data.astro._JDE0tab1000;
12923 		Y = year / 1000;
12924 	} else {
12925 		JDE0tab = ilib.data.astro._JDE0tab2000;
12926 		Y = (year - 2000) / 1000;
12927 	}
12928 
12929 	JDE0 = JDE0tab[which][0] + (JDE0tab[which][1] * Y)
12930 			+ (JDE0tab[which][2] * Y * Y) + (JDE0tab[which][3] * Y * Y * Y)
12931 			+ (JDE0tab[which][4] * Y * Y * Y * Y);
12932 
12933 	//document.debug.log.value += "JDE0 = " + JDE0 + "\n";
12934 
12935 	T = (JDE0 - 2451545.0) / 36525;
12936 	//document.debug.log.value += "T = " + T + "\n";
12937 	W = (35999.373 * T) - 2.47;
12938 	//document.debug.log.value += "W = " + W + "\n";
12939 	deltaL = 1 + (0.0334 * Astro._dcos(W)) + (0.0007 * Astro._dcos(2 * W));
12940 	//document.debug.log.value += "deltaL = " + deltaL + "\n";
12941 
12942 	//  Sum the periodic terms for time T
12943 
12944 	S = 0;
12945 	j = 0;
12946 	for (i = 0; i < 24; i++) {
12947 		S += ilib.data.astro._EquinoxpTerms[j]
12948 				* Astro._dcos(ilib.data.astro._EquinoxpTerms[j + 1] + (ilib.data.astro._EquinoxpTerms[j + 2] * T));
12949 		j += 3;
12950 	}
12951 
12952 	//document.debug.log.value += "S = " + S + "\n";
12953 	//document.debug.log.value += "Corr = " + ((S * 0.00001) / deltaL) + "\n";
12954 
12955 	JDE = JDE0 + ((S * 0.00001) / deltaL);
12956 
12957 	return JDE;
12958 };
12959 
12960 /* 
12961  * The table of observed Delta T values at the beginning of
12962  * years from 1620 through 2014 as found in astro.json is taken from
12963  * http://www.staff.science.uu.nl/~gent0113/deltat/deltat.htm
12964  * and
12965  * ftp://maia.usno.navy.mil/ser7/deltat.data
12966  */
12967 
12968 /**  
12969  * Determine the difference, in seconds, between dynamical time and universal time.
12970  * 
12971  * @static
12972  * @protected
12973  * @param {number} year to calculate the difference for
12974  * @return {number} difference in seconds between dynamical time and universal time  
12975  */
12976 Astro._deltat = function (year) {
12977 	var dt, f, i, t;
12978 
12979 	if ((year >= 1620) && (year <= 2014)) {
12980 		i = Math.floor(year - 1620);
12981 		f = (year - 1620) - i; /* Fractional part of year */
12982 		dt = ilib.data.astro._deltaTtab[i] + ((ilib.data.astro._deltaTtab[i + 1] - ilib.data.astro._deltaTtab[i]) * f);
12983 	} else {
12984 		t = (year - 2000) / 100;
12985 		if (year < 948) {
12986 			dt = 2177 + (497 * t) + (44.1 * t * t);
12987 		} else {
12988 			dt = 102 + (102 * t) + (25.3 * t * t);
12989 			if ((year > 2000) && (year < 2100)) {
12990 				dt += 0.37 * (year - 2100);
12991 			}
12992 		}
12993 	}
12994 	return dt;
12995 };
12996 
12997 /**
12998  * Calculate the obliquity of the ecliptic for a given
12999  * Julian date.  This uses Laskar's tenth-degree
13000  * polynomial fit (J. Laskar, Astronomy and
13001  * Astrophysics, Vol. 157, page 68 [1986]) which is
13002  * accurate to within 0.01 arc second between AD 1000
13003  * and AD 3000, and within a few seconds of arc for
13004  * +/-10000 years around AD 2000.  If we're outside the
13005  * range in which this fit is valid (deep time) we
13006  * simply return the J2000 value of the obliquity, which
13007  * happens to be almost precisely the mean.
13008  * 
13009  * @static
13010  * @protected
13011  * @param {number} jd Julian Day to calculate the obliquity for
13012  * @return {number} the obliquity
13013  */
13014 Astro._obliqeq = function (jd) {
13015 	var eps, u, v, i;
13016 
13017  	v = u = (jd - 2451545.0) / 3652500.0;
13018 
13019  	eps = 23 + (26 / 60.0) + (21.448 / 3600.0);
13020 
13021  	if (Math.abs(u) < 1.0) {
13022  		for (i = 0; i < 10; i++) {
13023  			eps += (ilib.data.astro._oterms[i] / 3600.0) * v;
13024  			v *= u;
13025  		}
13026  	}
13027  	return eps;
13028 };
13029 
13030 /**
13031  * Return the position of the sun.  We return
13032  * intermediate values because they are useful in a
13033  * variety of other contexts.
13034  * @static
13035  * @protected
13036  * @param {number} jd find the position of sun on this Julian Day
13037  * @return {Object} the position of the sun and many intermediate
13038  * values
13039  */
13040 Astro._sunpos = function(jd) {
13041 	var ret = {}, 
13042 		T, T2, T3, Omega, epsilon, epsilon0;
13043 
13044 	T = (jd - 2451545.0) / 36525.0;
13045 	//document.debug.log.value += "Sunpos.  T = " + T + "\n";
13046 	T2 = T * T;
13047 	T3 = T * T2;
13048 	ret.meanLongitude = Astro._fixangle(280.46646 + 36000.76983 * T + 0.0003032 * T2);
13049 	//document.debug.log.value += "ret.meanLongitude = " + ret.meanLongitude + "\n";
13050 	ret.meanAnomaly = Astro._fixangle(357.52911 + (35999.05029 * T) - 0.0001537 * T2 - 0.00000048 * T3);
13051 	//document.debug.log.value += "ret.meanAnomaly = " + ret.meanAnomaly + "\n";
13052 	ret.eccentricity = 0.016708634 - 0.000042037 * T - 0.0000001267 * T2;
13053 	//document.debug.log.value += "e = " + e + "\n";
13054 	ret.equationOfCenter = ((1.914602 - 0.004817 * T - 0.000014 * T2) * Astro._dsin(ret.meanAnomaly))
13055 			+ ((0.019993 - 0.000101 * T) * Astro._dsin(2 * ret.meanAnomaly))
13056 			+ (0.000289 * Astro._dsin(3 * ret.meanAnomaly));
13057 	//document.debug.log.value += "ret.equationOfCenter = " + ret.equationOfCenter + "\n";
13058 	ret.sunLongitude = ret.meanLongitude + ret.equationOfCenter;
13059 	//document.debug.log.value += "ret.sunLongitude = " + ret.sunLongitude + "\n";
13060 	//ret.sunAnomaly = ret.meanAnomaly + ret.equationOfCenter;
13061 	//document.debug.log.value += "ret.sunAnomaly = " + ret.sunAnomaly + "\n";
13062 	// ret.sunRadius = (1.000001018 * (1 - (ret.eccentricity * ret.eccentricity))) / (1 + (ret.eccentricity * Astro._dcos(ret.sunAnomaly)));
13063 	//document.debug.log.value += "ret.sunRadius = " + ret.sunRadius + "\n";
13064 	Omega = 125.04 - (1934.136 * T);
13065 	//document.debug.log.value += "Omega = " + Omega + "\n";
13066 	ret.apparentLong = ret.sunLongitude + (-0.00569) + (-0.00478 * Astro._dsin(Omega));
13067 	//document.debug.log.value += "ret.apparentLong = " + ret.apparentLong + "\n";
13068 	epsilon0 = Astro._obliqeq(jd);
13069 	//document.debug.log.value += "epsilon0 = " + epsilon0 + "\n";
13070 	epsilon = epsilon0 + (0.00256 * Astro._dcos(Omega));
13071 	//document.debug.log.value += "epsilon = " + epsilon + "\n";
13072 	//ret.rightAscension = Astro._fixangle(Astro._rtd(Math.atan2(Astro._dcos(epsilon0) * Astro._dsin(ret.sunLongitude), Astro._dcos(ret.sunLongitude))));
13073 	//document.debug.log.value += "ret.rightAscension = " + ret.rightAscension + "\n";
13074 	// ret.declination = Astro._rtd(Math.asin(Astro._dsin(epsilon0) * Astro._dsin(ret.sunLongitude)));
13075 	////document.debug.log.value += "ret.declination = " + ret.declination + "\n";
13076 	ret.inclination = Astro._fixangle(23.4392911 - 0.013004167 * T - 0.00000016389 * T2 + 0.0000005036 * T3);
13077 	ret.apparentRightAscension = Astro._fixangle(Astro._rtd(Math.atan2(Astro._dcos(epsilon) * Astro._dsin(ret.apparentLong), Astro._dcos(ret.apparentLong))));
13078 	//document.debug.log.value += "ret.apparentRightAscension = " + ret.apparentRightAscension + "\n";
13079 	//ret.apparentDeclination = Astro._rtd(Math.asin(Astro._dsin(epsilon) * Astro._dsin(ret.apparentLong)));
13080 	//document.debug.log.value += "ret.apparentDecliation = " + ret.apparentDecliation + "\n";
13081 
13082 	// Angular quantities are expressed in decimal degrees
13083 	return ret;
13084 };
13085 
13086 /**
13087  * Calculate the nutation in longitude, deltaPsi, and obliquity, 
13088  * deltaEpsilon for a given Julian date jd. Results are returned as an object
13089  * giving deltaPsi and deltaEpsilon in degrees.
13090  * 
13091  * @static
13092  * @protected
13093  * @param {number} jd calculate the nutation of this Julian Day
13094  * @return {Object} the deltaPsi and deltaEpsilon of the nutation
13095  */
13096 Astro._nutation = function(jd) {
13097 	var i, j, 
13098 		t = (jd - 2451545.0) / 36525.0, 
13099 		t2, t3, to10, 
13100 		ta = [], 
13101 		dp = 0, 
13102 		de = 0, 
13103 		ang,
13104 		ret = {};
13105 
13106 	t3 = t * (t2 = t * t);
13107 
13108 	/*
13109 	 * Calculate angles. The correspondence between the elements of our array
13110 	 * and the terms cited in Meeus are:
13111 	 * 
13112 	 * ta[0] = D ta[0] = M ta[2] = M' ta[3] = F ta[4] = \Omega
13113 	 * 
13114 	 */
13115 
13116 	ta[0] = Astro._dtr(297.850363 + 445267.11148 * t - 0.0019142 * t2 + t3 / 189474.0);
13117 	ta[1] = Astro._dtr(357.52772 + 35999.05034 * t - 0.0001603 * t2 - t3 / 300000.0);
13118 	ta[2] = Astro._dtr(134.96298 + 477198.867398 * t + 0.0086972 * t2 + t3 / 56250.0);
13119 	ta[3] = Astro._dtr(93.27191 + 483202.017538 * t - 0.0036825 * t2 + t3 / 327270);
13120 	ta[4] = Astro._dtr(125.04452 - 1934.136261 * t + 0.0020708 * t2 + t3 / 450000.0);
13121 
13122 	/*
13123 	 * Range reduce the angles in case the sine and cosine functions don't do it
13124 	 * as accurately or quickly.
13125 	 */
13126 
13127 	for (i = 0; i < 5; i++) {
13128 		ta[i] = Astro._fixangr(ta[i]);
13129 	}
13130 
13131 	to10 = t / 10.0;
13132 	for (i = 0; i < 63; i++) {
13133 		ang = 0;
13134 		for (j = 0; j < 5; j++) {
13135 			if (ilib.data.astro._nutArgMult[(i * 5) + j] != 0) {
13136 				ang += ilib.data.astro._nutArgMult[(i * 5) + j] * ta[j];
13137 			}
13138 		}
13139 		dp += (ilib.data.astro._nutArgCoeff[(i * 4) + 0] + ilib.data.astro._nutArgCoeff[(i * 4) + 1] * to10) * Math.sin(ang);
13140 		de += (ilib.data.astro._nutArgCoeff[(i * 4) + 2] + ilib.data.astro._nutArgCoeff[(i * 4) + 3] * to10) * Math.cos(ang);
13141 	}
13142 
13143 	/*
13144 	 * Return the result, converting from ten thousandths of arc seconds to
13145 	 * radians in the process.
13146 	 */
13147 
13148 	ret.deltaPsi = dp / (3600.0 * 10000.0);
13149 	ret.deltaEpsilon = de / (3600.0 * 10000.0);
13150 
13151 	return ret;
13152 };
13153 
13154 /**
13155  * Returns the equation of time as a fraction of a day.
13156  * 
13157  * @static
13158  * @protected
13159  * @param {number} jd the Julian Day of the day to calculate for
13160  * @return {number} the equation of time for the given day  
13161  */
13162 Astro._equationOfTime = function(jd) {
13163 	var alpha, deltaPsi, E, epsilon, L0, tau, pos;
13164 
13165 	// 2451545.0 is the Julian day of J2000 epoch
13166 	// 365250.0 is the number of days in a Julian millenium
13167 	tau = (jd - 2451545.0) / 365250.0;
13168 	//console.log("equationOfTime.  tau = " + tau);
13169 	L0 = 280.4664567 + (360007.6982779 * tau) + (0.03032028 * tau * tau)
13170 			+ ((tau * tau * tau) / 49931)
13171 			+ (-((tau * tau * tau * tau) / 15300))
13172 			+ (-((tau * tau * tau * tau * tau) / 2000000));
13173 	//console.log("L0 = " + L0);
13174 	L0 = Astro._fixangle(L0);
13175 	//console.log("L0 = " + L0);
13176 	pos = Astro._sunpos(jd);
13177 	alpha = pos.apparentRightAscension;
13178 	//console.log("alpha = " + alpha);
13179 	var nut = Astro._nutation(jd);
13180 	deltaPsi = nut.deltaPsi;
13181 	//console.log("deltaPsi = " + deltaPsi);
13182 	epsilon = Astro._obliqeq(jd) + nut.deltaEpsilon;
13183 	//console.log("epsilon = " + epsilon);
13184 	//console.log("L0 - 0.0057183 = " + (L0 - 0.0057183));
13185 	//console.log("L0 - 0.0057183 - alpha = " + (L0 - 0.0057183 - alpha));
13186 	//console.log("deltaPsi * cos(epsilon) = " + deltaPsi * Astro._dcos(epsilon));
13187 	
13188 	E = L0 - 0.0057183 - alpha + deltaPsi * Astro._dcos(epsilon);
13189 	// if alpha and L0 are in different quadrants, then renormalize
13190 	// so that the difference between them is in the right range
13191 	if (E > 180) {
13192 		E -= 360;
13193 	}
13194 	//console.log("E = " + E);
13195 	// E = E - 20.0 * (Math.floor(E / 20.0));
13196 	E = E * 4;
13197 	//console.log("Efixed = " + E);
13198 	E = E / (24 * 60);
13199 	//console.log("Eday = " + E);
13200 
13201 	return E;
13202 };
13203 
13204 /**
13205  * @private
13206  * @static
13207  */
13208 Astro._poly = function(x, coefficients) {
13209 	var result = coefficients[0];
13210 	var xpow = x;
13211 	for (var i = 1; i < coefficients.length; i++) {
13212 		result += coefficients[i] * xpow;
13213 		xpow *= x;
13214 	}
13215 	return result;
13216 };
13217 
13218 /**
13219  * Calculate the UTC RD from the local RD given "zone" number of minutes
13220  * worth of offset.
13221  * 
13222  * @static
13223  * @protected
13224  * @param {number} local RD of the locale time, given in any calendar
13225  * @param {number} zone number of minutes of offset from UTC for the time zone 
13226  * @return {number} the UTC equivalent of the local RD
13227  */
13228 Astro._universalFromLocal = function(local, zone) {
13229 	return local - zone / 1440;
13230 };
13231 
13232 /**
13233  * Calculate the local RD from the UTC RD given "zone" number of minutes
13234  * worth of offset.
13235  * 
13236  * @static
13237  * @protected
13238  * @param {number} local RD of the locale time, given in any calendar
13239  * @param {number} zone number of minutes of offset from UTC for the time zone 
13240  * @return {number} the UTC equivalent of the local RD
13241  */
13242 Astro._localFromUniversal = function(local, zone) {
13243 	return local + zone / 1440;
13244 };
13245 
13246 /**
13247  * @private
13248  * @static
13249  * @param {number} c julian centuries of the date to calculate
13250  * @return {number} the aberration
13251  */
13252 Astro._aberration = function(c) {
13253 	return 9.74e-05 * Astro._dcos(177.63 + 35999.01847999999 * c) - 0.005575;
13254 };
13255 
13256 /**
13257  * @private
13258  *
13259 ilib.data.astro._nutCoeffA = [124.90, -1934.134, 0.002063];
13260 ilib.data.astro._nutCoeffB q= [201.11, 72001.5377, 0.00057];
13261 */
13262 
13263 /**
13264  * @private
13265  * @static
13266  * @param {number} c julian centuries of the date to calculate
13267  * @return {number} the nutation for the given julian century in radians
13268  */
13269 Astro._nutation2 = function(c) {
13270 	var a = Astro._poly(c, ilib.data.astro._nutCoeffA);
13271 	var b = Astro._poly(c, ilib.data.astro._nutCoeffB);
13272 	// return -0.0000834 * Astro._dsin(a) - 0.0000064 * Astro._dsin(b);
13273 	return -0.004778 * Astro._dsin(a) - 0.0003667 * Astro._dsin(b);
13274 };
13275 
13276 /**
13277  * @static
13278  * @private
13279  */
13280 Astro._ephemerisCorrection = function(jd) {
13281 	var year = GregorianDate._calcYear(jd - 1721424.5);
13282 	
13283 	if (1988 <= year && year <= 2019) {
13284 		return (year - 1933) / 86400;
13285 	}
13286 	
13287 	if (1800 <= year && year <= 1987) {
13288 		var jul1 = new GregRataDie({
13289 			year: year,
13290 			month: 7,
13291 			day: 1,
13292 			hour: 0,
13293 			minute: 0,
13294 			second: 0
13295 		});
13296 		// 693596 is the rd of Jan 1, 1900
13297 		var theta = (jul1.getRataDie() - 693596) / 36525;
13298 		return Astro._poly(theta, (1900 <= year) ? ilib.data.astro._coeff19th : ilib.data.astro._coeff18th);
13299 	}
13300 	
13301 	if (1620 <= year && year <= 1799) {
13302 		year -= 1600;
13303 		return (196.58333 - 4.0675 * year + 0.0219167 * year * year) / 86400;
13304 	}
13305 	
13306 	// 660724 is the rd of Jan 1, 1810
13307 	var jan1 = new GregRataDie({
13308 		year: year,
13309 		month: 1,
13310 		day: 1,
13311 		hour: 0,
13312 		minute: 0,
13313 		second: 0
13314 	});
13315 	// var x = 0.5 + (jan1.getRataDie() - 660724);
13316 	var x = 0.5 + (jan1.getRataDie() - 660724);
13317 	
13318 	return ((x * x / 41048480) - 15) / 86400;
13319 };
13320 
13321 /**
13322  * @static
13323  * @private
13324  */
13325 Astro._ephemerisFromUniversal = function(jd) {
13326 	return jd + Astro._ephemerisCorrection(jd);
13327 };
13328 
13329 /**
13330  * @static
13331  * @private
13332  */
13333 Astro._universalFromEphemeris = function(jd) {
13334 	return jd - Astro._ephemerisCorrection(jd);
13335 };
13336 
13337 /**
13338  * @static
13339  * @private
13340  */
13341 Astro._julianCenturies = function(jd) {
13342 	// 2451545.0 is the Julian day of J2000 epoch
13343 	// 730119.5 is the Gregorian RD of J2000 epoch
13344 	// 36525.0 is the number of days in a Julian century
13345 	return (Astro._ephemerisFromUniversal(jd) - 2451545.0) / 36525.0;
13346 };
13347 
13348 /**
13349  * Calculate the solar longitude
13350  * 
13351  * @static
13352  * @protected
13353  * @param {number} jd julian day of the date to calculate the longitude for 
13354  * @return {number} the solar longitude in degrees
13355  */
13356 Astro._solarLongitude = function(jd) {
13357 	var c = Astro._julianCenturies(jd),
13358 		longitude = 0,
13359 		len = ilib.data.astro._solarLongCoeff.length,
13360 		row;
13361 	
13362 	for (var i = 0; i < len; i++) {
13363 		longitude += ilib.data.astro._solarLongCoeff[i] * 
13364 			Astro._dsin(ilib.data.astro._solarLongAddends[i] + ilib.data.astro._solarLongMultipliers[i] * c);
13365 	}
13366 	longitude *= 5.729577951308232e-06;
13367 	longitude += 282.77718340000001 + 36000.769537439999 * c;
13368 	longitude += Astro._aberration(c) + Astro._nutation2(c);
13369 	return Astro._fixangle(longitude);
13370 };
13371 
13372 /**
13373  * @static
13374  * @protected
13375  * @param {number} jd
13376  * @return {number}
13377  */
13378 Astro._lunarLongitude = function (jd) {
13379 	var c = Astro._julianCenturies(jd),
13380 	    meanMoon = Astro._fixangle(Astro._poly(c, ilib.data.astro._meanMoonCoeff)),
13381 	    elongation = Astro._fixangle(Astro._poly(c, ilib.data.astro._elongationCoeff)),
13382 	    solarAnomaly = Astro._fixangle(Astro._poly(c, ilib.data.astro._solarAnomalyCoeff)),
13383 	    lunarAnomaly = Astro._fixangle(Astro._poly(c, ilib.data.astro._lunarAnomalyCoeff)),
13384 	    moonNode = Astro._fixangle(Astro._poly(c, ilib.data.astro._moonFromNodeCoeff)),
13385 	    e = Astro._poly(c, ilib.data.astro._eCoeff);
13386 	
13387 	var sum = 0;
13388 	for (var i = 0; i < ilib.data.astro._lunarElongationLongCoeff.length; i++) {
13389 		var x = ilib.data.astro._solarAnomalyLongCoeff[i];
13390 
13391 		sum += ilib.data.astro._sineCoeff[i] * Math.pow(e, Math.abs(x)) * 
13392 			Astro._dsin(ilib.data.astro._lunarElongationLongCoeff[i] * elongation + x * solarAnomaly + 
13393 				ilib.data.astro._lunarAnomalyLongCoeff[i] * lunarAnomaly + 
13394 				ilib.data.astro._moonFromNodeLongCoeff[i] * moonNode);
13395 	}
13396 	var longitude = sum / 1000000;
13397 	var venus = 3958.0 / 1000000 * Astro._dsin(119.75 + c * 131.84899999999999);
13398 	var jupiter = 318.0 / 1000000 * Astro._dsin(53.090000000000003 + c * 479264.28999999998);
13399 	var flatEarth = 1962.0 / 1000000 * Astro._dsin(meanMoon - moonNode);
13400 	
13401 	return Astro._fixangle(meanMoon + longitude + venus + jupiter + flatEarth + Astro._nutation2(c));
13402 };
13403 
13404 /**
13405  * @static
13406  * @protected
13407  * @param {number} n
13408  * @return {number} julian day of the n'th new moon
13409  */
13410 Astro._newMoonTime = function(n) {
13411 	var k = n - 24724;
13412 	var c = k / 1236.8499999999999;
13413 	var approx = Astro._poly(c, ilib.data.astro._nmApproxCoeff);
13414 	var capE = Astro._poly(c, ilib.data.astro._nmCapECoeff);
13415 	var solarAnomaly = Astro._poly(c, ilib.data.astro._nmSolarAnomalyCoeff);
13416 	var lunarAnomaly = Astro._poly(c, ilib.data.astro._nmLunarAnomalyCoeff);
13417 	var moonArgument = Astro._poly(c, ilib.data.astro._nmMoonArgumentCoeff);
13418 	var capOmega = Astro._poly(c, ilib.data.astro._nmCapOmegaCoeff);
13419 	var correction = -0.00017 * Astro._dsin(capOmega);
13420 	for (var i = 0; i < ilib.data.astro._nmSineCoeff.length; i++) {
13421 		correction = correction + ilib.data.astro._nmSineCoeff[i] * Math.pow(capE, ilib.data.astro._nmEFactor[i]) * 
13422 		Astro._dsin(ilib.data.astro._nmSolarCoeff[i] * solarAnomaly + 
13423 				ilib.data.astro._nmLunarCoeff[i] * lunarAnomaly + 
13424 				ilib.data.astro._nmMoonCoeff[i] * moonArgument);
13425 	}
13426 	var additional = 0;
13427 	for (var i = 0; i < ilib.data.astro._nmAddConst.length; i++) {
13428 		additional = additional + ilib.data.astro._nmAddFactor[i] * 
13429 		Astro._dsin(ilib.data.astro._nmAddConst[i] + ilib.data.astro._nmAddCoeff[i] * k);
13430 	}
13431 	var extra = 0.000325 * Astro._dsin(Astro._poly(c, ilib.data.astro._nmExtra));
13432 	return Astro._universalFromEphemeris(approx + correction + extra + additional + RataDie.gregorianEpoch);
13433 };
13434 
13435 /**
13436  * @static
13437  * @protected
13438  * @param {number} jd
13439  * @return {number}
13440  */
13441 Astro._lunarSolarAngle = function(jd) {
13442 	var lunar = Astro._lunarLongitude(jd);
13443 	var solar = Astro._solarLongitude(jd)
13444 	return Astro._fixangle(lunar - solar);
13445 };
13446 
13447 /**
13448  * @static
13449  * @protected
13450  * @param {number} jd
13451  * @return {number}
13452  */
13453 Astro._newMoonBefore = function (jd) {
13454 	var phase = Astro._lunarSolarAngle(jd);
13455 	// 11.450086114414322 is the julian day of the 0th full moon
13456 	// 29.530588853000001 is the average length of a month
13457 	var guess = Math.round((jd - 11.450086114414322 - RataDie.gregorianEpoch) / 29.530588853000001 - phase / 360) - 1;
13458 	var current, last;
13459 	current = last = Astro._newMoonTime(guess);
13460 	while (current < jd) {
13461 		guess++;
13462 		last = current;
13463 		current = Astro._newMoonTime(guess);
13464 	}
13465 	return last;
13466 };
13467 
13468 /**
13469  * @static
13470  * @protected
13471  * @param {number} jd
13472  * @return {number}
13473  */
13474 Astro._newMoonAtOrAfter = function (jd) {
13475 	var phase = Astro._lunarSolarAngle(jd);
13476 	// 11.450086114414322 is the julian day of the 0th full moon
13477 	// 29.530588853000001 is the average length of a month
13478 	var guess = Math.round((jd - 11.450086114414322 - RataDie.gregorianEpoch) / 29.530588853000001 - phase / 360);
13479 	var current;
13480 	while ((current = Astro._newMoonTime(guess)) < jd) {
13481 		guess++;
13482 	}
13483 	return current;
13484 };
13485 
13486 /**
13487  * @static
13488  * @protected
13489  * @param {number} jd JD to calculate from
13490  * @param {number} longitude longitude to seek 
13491  * @returns {number} the JD of the next time that the solar longitude 
13492  * is a multiple of the given longitude
13493  */
13494 Astro._nextSolarLongitude = function(jd, longitude) {
13495 	var rate = 365.242189 / 360.0;
13496 	var tau = jd + rate * Astro._fixangle(longitude - Astro._solarLongitude(jd));
13497 	var start = Math.max(jd, tau - 5.0);
13498 	var end = tau + 5.0;
13499 	
13500 	return SearchUtils.bisectionSearch(0, start, end, 1e-6, function (l) {
13501 		return 180 - Astro._fixangle(Astro._solarLongitude(l) - longitude);
13502 	});
13503 };
13504 
13505 /**
13506  * Floor the julian day to midnight of the current julian day.
13507  * 
13508  * @static
13509  * @protected
13510  * @param {number} jd the julian to round
13511  * @return {number} the jd floored to the midnight of the julian day
13512  */
13513 Astro._floorToJD = function(jd) {
13514 	return Math.floor(jd - 0.5) + 0.5;
13515 };
13516 
13517 /**
13518  * Floor the julian day to midnight of the current julian day.
13519  * 
13520  * @static
13521  * @protected
13522  * @param {number} jd the julian to round
13523  * @return {number} the jd floored to the midnight of the julian day
13524  */
13525 Astro._ceilToJD = function(jd) {
13526 	return Math.ceil(jd + 0.5) - 0.5;
13527 };
13528 
13529 
13530 
13531 /*< PersRataDie.js */
13532 /*
13533  * persratadie.js - Represent a rata die date in the Persian calendar
13534  * 
13535  * Copyright © 2014-2015, JEDLSoft
13536  *
13537  * Licensed under the Apache License, Version 2.0 (the "License");
13538  * you may not use this file except in compliance with the License.
13539  * You may obtain a copy of the License at
13540  *
13541  *     http://www.apache.org/licenses/LICENSE-2.0
13542  *
13543  * Unless required by applicable law or agreed to in writing, software
13544  * distributed under the License is distributed on an "AS IS" BASIS,
13545  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13546  *
13547  * See the License for the specific language governing permissions and
13548  * limitations under the License.
13549  */
13550 
13551 /* !depends 
13552 ilib.js
13553 MathUtils.js
13554 RataDie.js
13555 Astro.js
13556 GregorianDate.js
13557 */
13558 
13559 
13560 
13561 
13562 /**
13563  * @class
13564  * Construct a new Persian RD date number object. The constructor parameters can 
13565  * contain any of the following properties:
13566  * 
13567  * <ul>
13568  * <li><i>unixtime<i> - sets the time of this instance according to the given 
13569  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
13570  * 
13571  * <li><i>julianday</i> - sets the time of this instance according to the given
13572  * Julian Day instance or the Julian Day given as a float
13573  * 
13574  * <li><i>year</i> - any integer, including 0
13575  * 
13576  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
13577  * 
13578  * <li><i>day</i> - 1 to 31
13579  * 
13580  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
13581  * is always done with an unambiguous 24 hour representation
13582  * 
13583  * <li><i>minute</i> - 0 to 59
13584  * 
13585  * <li><i>second</i> - 0 to 59
13586  * 
13587  * <li><i>millisecond</i> - 0 to 999
13588  * 
13589  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
13590  * </ul>
13591  *
13592  * If the constructor is called with another Persian date instance instead of
13593  * a parameter block, the other instance acts as a parameter block and its
13594  * settings are copied into the current instance.<p>
13595  * 
13596  * If the constructor is called with no arguments at all or if none of the 
13597  * properties listed above are present, then the RD is calculate based on 
13598  * the current date at the time of instantiation. <p>
13599  * 
13600  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
13601  * specified in the params, it is assumed that they have the smallest possible
13602  * value in the range for the property (zero or one).<p>
13603  * 
13604  * 
13605  * @private
13606  * @constructor
13607  * @extends RataDie
13608  * @param {Object=} params parameters that govern the settings and behaviour of this Persian RD date
13609  */
13610 var PersRataDie = function(params) {
13611 	this.rd = NaN;
13612 	Astro.initAstro(
13613 		params && typeof(params.sync) === 'boolean' ? params.sync : true,
13614 		params && params.loadParams,
13615 		ilib.bind(this, function (x) {
13616 			RataDie.call(this, params);
13617 			if (params && typeof(params.callback) === 'function') {
13618 				params.callback(this);
13619 			}
13620 		})
13621 	);
13622 };
13623 
13624 PersRataDie.prototype = new RataDie();
13625 PersRataDie.prototype.parent = RataDie;
13626 PersRataDie.prototype.constructor = PersRataDie;
13627 
13628 /**
13629  * The difference between a zero Julian day and the first Persian date
13630  * @private
13631  * @type number
13632  */
13633 PersRataDie.prototype.epoch = 1948319.5;
13634 
13635 /**
13636  * @protected 
13637  */
13638 PersRataDie.prototype._tehranEquinox = function(year) {
13639     var equJED, equJD, equAPP, equTehran, dtTehran, eot;
13640 
13641     //  March equinox in dynamical time
13642     equJED = Astro._equinox(year, 0);
13643 
13644     //  Correct for delta T to obtain Universal time
13645     equJD = equJED - (Astro._deltat(year) / (24 * 60 * 60));
13646 
13647     //  Apply the equation of time to yield the apparent time at Greenwich
13648     eot = Astro._equationOfTime(equJED) * 360;
13649     eot = (eot - 20 * Math.floor(eot/20)) / 360;
13650     equAPP = equJD + eot;
13651 
13652     /*  
13653      * Finally, we must correct for the constant difference between
13654      * the Greenwich meridian and the time zone standard for Iran 
13655      * Standard time, 52 degrees 30 minutes to the East.
13656      */
13657 
13658     dtTehran = 52.5 / 360;
13659     equTehran = equAPP + dtTehran;
13660 
13661     return equTehran;
13662 };
13663 
13664 /**
13665  * Calculate the year based on the given Julian day.
13666  * @protected
13667  * @param {number} jd the Julian day to get the year for
13668  * @return {{year:number,equinox:number}} the year and the last equinox
13669  */
13670 PersRataDie.prototype._getYear = function(jd) {
13671 	var gd = new GregorianDate({julianday: jd});
13672     var guess = gd.getYears() - 2,
13673     	nexteq,
13674     	ret = {};
13675 
13676     //ret.equinox = Math.floor(this._tehranEquinox(guess));
13677     ret.equinox = this._tehranEquinox(guess);
13678 	while (ret.equinox > jd) {
13679 	    guess--;
13680 	    // ret.equinox = Math.floor(this._tehranEquinox(guess));
13681 	    ret.equinox = this._tehranEquinox(guess);
13682 	}
13683 	nexteq = ret.equinox - 1;
13684 	// if the equinox falls after noon, then the day after that is the start of the 
13685 	// next year, so truncate the JD to get the noon of the day before the day with 
13686 	//the equinox on it, then add 0.5 to get the midnight of that day 
13687 	while (!(Math.floor(ret.equinox) + 0.5 <= jd && jd < Math.floor(nexteq) + 0.5)) {
13688 	    ret.equinox = nexteq;
13689 	    guess++;
13690 	    // nexteq = Math.floor(this._tehranEquinox(guess));
13691 	    nexteq = this._tehranEquinox(guess);
13692 	}
13693 	
13694 	// Mean solar tropical year is 365.24219878 days
13695 	ret.year = Math.round((ret.equinox - this.epoch - 1) / 365.24219878) + 1;
13696 	
13697 	return ret;
13698 };
13699 
13700 /**
13701  * Calculate the Rata Die (fixed day) number of the given date from the
13702  * date components.
13703  *
13704  * @protected
13705  * @param {Object} date the date components to calculate the RD from
13706  */
13707 PersRataDie.prototype._setDateComponents = function(date) {
13708     var adr, guess, jd;
13709 
13710     // Mean solar tropical year is 365.24219878 days 
13711     guess = this.epoch + 1 + 365.24219878 * (date.year - 2);
13712     adr = {year: date.year - 1, equinox: 0};
13713 
13714     while (adr.year < date.year) {
13715         adr = this._getYear(guess);
13716         guess = adr.equinox + (365.24219878 + 2);
13717     }
13718 
13719     jd = Math.floor(adr.equinox) +
13720             ((date.month <= 7) ?
13721                 ((date.month - 1) * 31) :
13722                 (((date.month - 1) * 30) + 6)
13723             ) +
13724     	    (date.day - 1 + 0.5); // add 0.5 so that we convert JDs, which start at noon to RDs which start at midnight
13725     
13726 	jd += (date.hour * 3600000 +
13727 			date.minute * 60000 +
13728 			date.second * 1000 +
13729 			date.millisecond) /
13730 			86400000;
13731 
13732     this.rd = jd - this.epoch;
13733 };
13734 
13735 /**
13736  * Return the rd number of the particular day of the week on or before the 
13737  * given rd. eg. The Sunday on or before the given rd.
13738  * @private
13739  * @param {number} rd the rata die date of the reference date
13740  * @param {number} dayOfWeek the day of the week that is being sought relative 
13741  * to the current date
13742  * @return {number} the rd of the day of the week
13743  */
13744 PersRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
13745 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek - 3, 7);
13746 };
13747 
13748 
13749 /*< PersianCal.js */
13750 /*
13751  * persianastro.js - Represent a Persian astronomical (Hijjri) calendar object.
13752  * 
13753  * Copyright © 2014-2015, JEDLSoft
13754  *
13755  * Licensed under the Apache License, Version 2.0 (the "License");
13756  * you may not use this file except in compliance with the License.
13757  * You may obtain a copy of the License at
13758  *
13759  *     http://www.apache.org/licenses/LICENSE-2.0
13760  *
13761  * Unless required by applicable law or agreed to in writing, software
13762  * distributed under the License is distributed on an "AS IS" BASIS,
13763  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13764  *
13765  * See the License for the specific language governing permissions and
13766  * limitations under the License.
13767  */
13768 
13769 
13770 /* !depends 
13771 Calendar.js 
13772 PersRataDie.js 
13773 ilib.js
13774 MathUtils.js
13775 */
13776 
13777 
13778 
13779 
13780 /**
13781  * @class
13782  * Construct a new Persian astronomical (Hijjri) calendar object. This class encodes 
13783  * information about a Persian calendar. This class differs from the 
13784  * Persian calendar in that the leap years are calculated based on the
13785  * astronomical observations of the sun in Teheran, instead of calculating
13786  * the leap years based on a regular cyclical rhythm algorithm.<p>
13787  * 
13788  * 
13789  * @constructor
13790  * @extends Calendar
13791  */
13792 var PersianCal = function() {
13793 	this.type = "persian";
13794 };
13795 
13796 /**
13797  * @private
13798  * @const
13799  * @type Array.<number> 
13800  * the lengths of each month 
13801  */
13802 PersianCal.monthLengths = [
13803 	31,  // Farvardin
13804 	31,  // Ordibehesht
13805 	31,  // Khordad
13806 	31,  // Tir
13807 	31,  // Mordad
13808 	31,  // Shahrivar
13809 	30,  // Mehr
13810 	30,  // Aban
13811 	30,  // Azar
13812 	30,  // Dey
13813 	30,  // Bahman
13814 	29   // Esfand
13815 ];
13816 
13817 /**
13818  * Return the number of months in the given year. The number of months in a year varies
13819  * for some luni-solar calendars because in some years, an extra month is needed to extend the 
13820  * days in a year to an entire solar year. The month is represented as a 1-based number
13821  * where 1=first month, 2=second month, etc.
13822  * 
13823  * @param {number} year a year for which the number of months is sought
13824  * @return {number} The number of months in the given year
13825  */
13826 PersianCal.prototype.getNumMonths = function(year) {
13827 	return 12;
13828 };
13829 
13830 /**
13831  * Return the number of days in a particular month in a particular year. This function
13832  * can return a different number for a month depending on the year because of things
13833  * like leap years.
13834  * 
13835  * @param {number} month the month for which the length is sought
13836  * @param {number} year the year within which that month can be found
13837  * @return {number} the number of days within the given month in the given year
13838  */
13839 PersianCal.prototype.getMonLength = function(month, year) {
13840 	if (month !== 12 || !this.isLeapYear(year)) {
13841 		return PersianCal.monthLengths[month-1];
13842 	} else {
13843 		// Month 12, Esfand, has 30 days instead of 29 in leap years
13844 		return 30;
13845 	}
13846 };
13847 
13848 /**
13849  * Return true if the given year is a leap year in the Persian astronomical calendar.
13850  * @param {number} year the year for which the leap year information is being sought
13851  * @return {boolean} true if the given year is a leap year
13852  */
13853 PersianCal.prototype.isLeapYear = function(year) {
13854 	var rdNextYear = new PersRataDie({
13855 		cal: this,
13856 		year: year + 1,
13857 		month: 1,
13858 		day: 1,
13859 		hour: 0,
13860 		minute: 0,
13861 		second: 0,
13862 		millisecond: 0
13863 	});
13864 	var rdThisYear = new PersRataDie({
13865 		cal: this,
13866 		year: year,
13867 		month: 1,
13868 		day: 1,
13869 		hour: 0,
13870 		minute: 0,
13871 		second: 0,
13872 		millisecond: 0
13873 	}); 
13874     return (rdNextYear.getRataDie() - rdThisYear.getRataDie()) > 365;
13875 };
13876 
13877 /**
13878  * Return the type of this calendar.
13879  * 
13880  * @return {string} the name of the type of this calendar 
13881  */
13882 PersianCal.prototype.getType = function() {
13883 	return this.type;
13884 };
13885 
13886 /**
13887  * Return a date instance for this calendar type using the given
13888  * options.
13889  * @param {Object} options options controlling the construction of 
13890  * the date instance
13891  * @return {IDate} a date appropriate for this calendar type
13892  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
13893  */
13894 PersianCal.prototype.newDateInstance = function (options) {
13895 		return new PersianDate(options);
13896 };
13897 
13898 /* register this calendar for the factory method */
13899 Calendar._constructors["persian"] = PersianCal;
13900 
13901 
13902 /*< PersianDate.js */
13903 /*
13904  * PersianDate.js - Represent a date in the Persian astronomical (Hijjri) calendar
13905  * 
13906  * Copyright © 2014-2015, JEDLSoft
13907  *
13908  * Licensed under the Apache License, Version 2.0 (the "License");
13909  * you may not use this file except in compliance with the License.
13910  * You may obtain a copy of the License at
13911  *
13912  *     http://www.apache.org/licenses/LICENSE-2.0
13913  *
13914  * Unless required by applicable law or agreed to in writing, software
13915  * distributed under the License is distributed on an "AS IS" BASIS,
13916  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13917  *
13918  * See the License for the specific language governing permissions and
13919  * limitations under the License.
13920  */
13921 
13922 /* !depends 
13923 ilib.js
13924 Locale.js
13925 TimeZone.js
13926 IDate.js
13927 PersRataDie.js
13928 PersianCal.js 
13929 SearchUtils.js
13930 MathUtils.js
13931 LocaleInfo.js 
13932 Astro.js
13933 */
13934 
13935 // !data astro
13936 
13937 
13938 
13939 
13940 /**
13941  * @class
13942  * 
13943  * Construct a new Persian astronomical date object. The constructor parameters can 
13944  * contain any of the following properties:
13945  * 
13946  * <ul>
13947  * <li><i>unixtime<i> - sets the time of this instance according to the given 
13948  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
13949  * 
13950  * <li><i>julianday</i> - sets the time of this instance according to the given
13951  * Julian Day instance or the Julian Day given as a float
13952  * 
13953  * <li><i>year</i> - any integer, including 0
13954  * 
13955  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
13956  * 
13957  * <li><i>day</i> - 1 to 31
13958  * 
13959  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
13960  * is always done with an unambiguous 24 hour representation
13961  * 
13962  * <li><i>minute</i> - 0 to 59
13963  * 
13964  * <li><i>second</i> - 0 to 59
13965  * 
13966  * <li><i>millisecond</i> - 0 to 999
13967  * 
13968  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string 
13969  * of this persian date. The date/time is kept in the local time. The time zone
13970  * is used later if this date is formatted according to a different time zone and
13971  * the difference has to be calculated, or when the date format has a time zone
13972  * component in it.
13973  * 
13974  * <li><i>locale</i> - locale for this persian date. If the time zone is not 
13975  * given, it can be inferred from this locale. For locales that span multiple
13976  * time zones, the one with the largest population is chosen as the one that 
13977  * represents the locale.
13978  * 
13979  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
13980  * </ul>
13981  *
13982  * If the constructor is called with another Persian date instance instead of
13983  * a parameter block, the other instance acts as a parameter block and its
13984  * settings are copied into the current instance.<p>
13985  * 
13986  * If the constructor is called with no arguments at all or if none of the 
13987  * properties listed above 
13988  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
13989  * components are 
13990  * filled in with the current date at the time of instantiation. Note that if
13991  * you do not give the time zone when defaulting to the current time and the 
13992  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
13993  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
13994  * Mean Time").<p>
13995  * 
13996  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
13997  * specified in the params, it is assumed that they have the smallest possible
13998  * value in the range for the property (zero or one).<p>
13999  * 
14000  * 
14001  * @constructor
14002  * @extends IDate
14003  * @param {Object=} params parameters that govern the settings and behaviour of this Persian date
14004  */
14005 var PersianDate = function(params) {
14006 	this.cal = new PersianCal();
14007 	this.timezone = "local";
14008 	
14009 	if (params) {
14010 		if (params.locale) {
14011 			this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
14012 			var li = new LocaleInfo(this.locale);
14013 			this.timezone = li.getTimeZone(); 
14014 		}
14015 		if (params.timezone) {
14016 			this.timezone = params.timezone;
14017 		}
14018 	}
14019 	
14020 	Astro.initAstro(
14021 		params && typeof(params.sync) === 'boolean' ? params.sync : true,
14022 		params && params.loadParams,
14023 		ilib.bind(this, function (x) {
14024 			if (params && (params.year || params.month || params.day || params.hour ||
14025 					params.minute || params.second || params.millisecond)) {
14026 				/**
14027 				 * Year in the Persian calendar.
14028 				 * @type number
14029 				 */
14030 				this.year = parseInt(params.year, 10) || 0;
14031 
14032 				/**
14033 				 * The month number, ranging from 1 to 12
14034 				 * @type number
14035 				 */
14036 				this.month = parseInt(params.month, 10) || 1;
14037 
14038 				/**
14039 				 * The day of the month. This ranges from 1 to 31.
14040 				 * @type number
14041 				 */
14042 				this.day = parseInt(params.day, 10) || 1;
14043 				
14044 				/**
14045 				 * The hour of the day. This can be a number from 0 to 23, as times are
14046 				 * stored unambiguously in the 24-hour clock.
14047 				 * @type number
14048 				 */
14049 				this.hour = parseInt(params.hour, 10) || 0;
14050 
14051 				/**
14052 				 * The minute of the hours. Ranges from 0 to 59.
14053 				 * @type number
14054 				 */
14055 				this.minute = parseInt(params.minute, 10) || 0;
14056 
14057 				/**
14058 				 * The second of the minute. Ranges from 0 to 59.
14059 				 * @type number
14060 				 */
14061 				this.second = parseInt(params.second, 10) || 0;
14062 
14063 				/**
14064 				 * The millisecond of the second. Ranges from 0 to 999.
14065 				 * @type number
14066 				 */
14067 				this.millisecond = parseInt(params.millisecond, 10) || 0;
14068 				
14069 				/**
14070 				 * The day of the year. Ranges from 1 to 366.
14071 				 * @type number
14072 				 */
14073 				this.dayOfYear = parseInt(params.dayOfYear, 10);
14074 
14075 				if (typeof(params.dst) === 'boolean') {
14076 					this.dst = params.dst;
14077 				}
14078 				
14079 				this.rd = this.newRd(this);
14080 				
14081 				// add the time zone offset to the rd to convert to UTC
14082 				if (!this.tz) {
14083 					this.tz = new TimeZone({id: this.timezone});
14084 				}
14085 				// getOffsetMillis requires that this.year, this.rd, and this.dst 
14086 				// are set in order to figure out which time zone rules apply and 
14087 				// what the offset is at that point in the year
14088 				this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
14089 				if (this.offset !== 0) {
14090 					this.rd = this.newRd({
14091 						rd: this.rd.getRataDie() - this.offset
14092 					});
14093 				}
14094 			}
14095 			
14096 			if (!this.rd) {
14097 				this.rd = this.newRd(params);
14098 				this._calcDateComponents();
14099 			}
14100 			
14101 			if (params && typeof(params.onLoad) === 'function') {
14102 				params.onLoad(this);
14103 			}
14104 		})
14105 	);
14106 };
14107 
14108 PersianDate.prototype = new IDate({noinstance: true});
14109 PersianDate.prototype.parent = IDate;
14110 PersianDate.prototype.constructor = PersianDate;
14111 
14112 /**
14113  * @private
14114  * @const
14115  * @type Array.<number>
14116  * the cumulative lengths of each month, for a non-leap year 
14117  */
14118 PersianDate.cumMonthLengths = [
14119     0,    // Farvardin
14120 	31,   // Ordibehesht
14121 	62,   // Khordad
14122 	93,   // Tir
14123 	124,  // Mordad
14124 	155,  // Shahrivar
14125 	186,  // Mehr
14126 	216,  // Aban
14127 	246,  // Azar
14128 	276,  // Dey
14129 	306,  // Bahman
14130 	336,  // Esfand
14131 	366
14132 ];
14133 
14134 /**
14135  * Return a new RD for this date type using the given params.
14136  * @protected
14137  * @param {Object=} params the parameters used to create this rata die instance
14138  * @returns {RataDie} the new RD instance for the given params
14139  */
14140 PersianDate.prototype.newRd = function (params) {
14141 	return new PersRataDie(params);
14142 };
14143 
14144 /**
14145  * Return the year for the given RD
14146  * @protected
14147  * @param {number} rd RD to calculate from 
14148  * @returns {number} the year for the RD
14149  */
14150 PersianDate.prototype._calcYear = function(rd) {
14151 	var julianday = rd + this.rd.epoch;
14152 	return this.rd._getYear(julianday).year;
14153 };
14154 
14155 /**
14156  * @private
14157  * Calculate date components for the given RD date.
14158  */
14159 PersianDate.prototype._calcDateComponents = function () {
14160 	var remainder,
14161 		rd = this.rd.getRataDie();
14162 	
14163 	this.year = this._calcYear(rd);
14164 	
14165 	if (typeof(this.offset) === "undefined") {
14166 		// now offset the RD by the time zone, then recalculate in case we were 
14167 		// near the year boundary
14168 		if (!this.tz) {
14169 			this.tz = new TimeZone({id: this.timezone});
14170 		}
14171 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
14172 	}
14173 	
14174 	if (this.offset !== 0) {
14175 		rd += this.offset;
14176 		this.year = this._calcYear(rd);
14177 	}
14178 	
14179 	//console.log("PersDate.calcComponent: calculating for rd " + rd);
14180 	//console.log("PersDate.calcComponent: year is " + ret.year);
14181 	var yearStart = this.newRd({
14182 		year: this.year,
14183 		month: 1,
14184 		day: 1,
14185 		hour: 0,
14186 		minute: 0,
14187 		second: 0,
14188 		millisecond: 0
14189 	});
14190 	remainder = rd - yearStart.getRataDie() + 1;
14191 	
14192 	this.dayOfYear = remainder;
14193 	
14194 	//console.log("PersDate.calcComponent: remainder is " + remainder);
14195 	
14196 	this.month = SearchUtils.bsearch(Math.floor(remainder), PersianDate.cumMonthLengths);
14197 	remainder -= PersianDate.cumMonthLengths[this.month-1];
14198 	
14199 	//console.log("PersDate.calcComponent: month is " + this.month + " and remainder is " + remainder);
14200 	
14201 	this.day = Math.floor(remainder);
14202 	remainder -= this.day;
14203 	
14204 	//console.log("PersDate.calcComponent: day is " + this.day + " and remainder is " + remainder);
14205 	
14206 	// now convert to milliseconds for the rest of the calculation
14207 	remainder = Math.round(remainder * 86400000);
14208 	
14209 	this.hour = Math.floor(remainder/3600000);
14210 	remainder -= this.hour * 3600000;
14211 	
14212 	this.minute = Math.floor(remainder/60000);
14213 	remainder -= this.minute * 60000;
14214 	
14215 	this.second = Math.floor(remainder/1000);
14216 	remainder -= this.second * 1000;
14217 	
14218 	this.millisecond = remainder;
14219 };
14220 
14221 /**
14222  * Return the day of the week of this date. The day of the week is encoded
14223  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
14224  * 
14225  * @return {number} the day of the week
14226  */
14227 PersianDate.prototype.getDayOfWeek = function() {
14228 	var rd = Math.floor(this.getRataDie());
14229 	return MathUtils.mod(rd-3, 7);
14230 };
14231 
14232 /**
14233  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 
14234  * 365, regardless of months or weeks, etc. That is, Farvardin 1st is day 1, and 
14235  * December 31st is 365 in regular years, or 366 in leap years.
14236  * @return {number} the ordinal day of the year
14237  */
14238 PersianDate.prototype.getDayOfYear = function() {
14239 	return PersianDate.cumMonthLengths[this.month-1] + this.day;
14240 };
14241 
14242 /**
14243  * Return the era for this date as a number. The value for the era for Persian 
14244  * calendars is -1 for "before the persian era" (BP) and 1 for "the persian era" (anno 
14245  * persico or AP). 
14246  * BP dates are any date before Farvardin 1, 1 AP. In the proleptic Persian calendar, 
14247  * there is a year 0, so any years that are negative or zero are BP.
14248  * @return {number} 1 if this date is in the common era, -1 if it is before the 
14249  * common era 
14250  */
14251 PersianDate.prototype.getEra = function() {
14252 	return (this.year < 1) ? -1 : 1;
14253 };
14254 
14255 /**
14256  * Return the name of the calendar that governs this date.
14257  * 
14258  * @return {string} a string giving the name of the calendar
14259  */
14260 PersianDate.prototype.getCalendar = function() {
14261 	return "persian";
14262 };
14263 
14264 // register with the factory method
14265 IDate._constructors["persian"] = PersianDate;
14266 
14267 
14268 /*< PersianAlgoCal.js */
14269 /*
14270  * persian.js - Represent a Persian algorithmic calendar object.
14271  * 
14272  * Copyright © 2014-2015, JEDLSoft
14273  *
14274  * Licensed under the Apache License, Version 2.0 (the "License");
14275  * you may not use this file except in compliance with the License.
14276  * You may obtain a copy of the License at
14277  *
14278  *     http://www.apache.org/licenses/LICENSE-2.0
14279  *
14280  * Unless required by applicable law or agreed to in writing, software
14281  * distributed under the License is distributed on an "AS IS" BASIS,
14282  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14283  *
14284  * See the License for the specific language governing permissions and
14285  * limitations under the License.
14286  */
14287 
14288 
14289 /* !depends ilib.js Calendar.js MathUtils.js */
14290 
14291 
14292 /**
14293  * @class
14294  * Construct a new Persian algorithmic calendar object. This class encodes information about
14295  * a Persian algorithmic calendar.<p>
14296  * 
14297  * 
14298  * @constructor
14299  * @extends Calendar
14300  */
14301 var PersianAlgoCal = function() {
14302 	this.type = "persian-algo";
14303 };
14304 
14305 /**
14306  * @private
14307  * @const
14308  * @type Array.<number> 
14309  * the lengths of each month 
14310  */
14311 PersianAlgoCal.monthLengths = [
14312 	31,  // Farvardin
14313 	31,  // Ordibehesht
14314 	31,  // Khordad
14315 	31,  // Tir
14316 	31,  // Mordad
14317 	31,  // Shahrivar
14318 	30,  // Mehr
14319 	30,  // Aban
14320 	30,  // Azar
14321 	30,  // Dey
14322 	30,  // Bahman
14323 	29   // Esfand
14324 ];
14325 
14326 /**
14327  * Return the number of months in the given year. The number of months in a year varies
14328  * for some luni-solar calendars because in some years, an extra month is needed to extend the 
14329  * days in a year to an entire solar year. The month is represented as a 1-based number
14330  * where 1=first month, 2=second month, etc.
14331  * 
14332  * @param {number} year a year for which the number of months is sought
14333  * @return {number} The number of months in the given year
14334  */
14335 PersianAlgoCal.prototype.getNumMonths = function(year) {
14336 	return 12;
14337 };
14338 
14339 /**
14340  * Return the number of days in a particular month in a particular year. This function
14341  * can return a different number for a month depending on the year because of things
14342  * like leap years.
14343  * 
14344  * @param {number} month the month for which the length is sought
14345  * @param {number} year the year within which that month can be found
14346  * @return {number} the number of days within the given month in the given year
14347  */
14348 PersianAlgoCal.prototype.getMonLength = function(month, year) {
14349 	if (month !== 12 || !this.isLeapYear(year)) {
14350 		return PersianAlgoCal.monthLengths[month-1];
14351 	} else {
14352 		// Month 12, Esfand, has 30 days instead of 29 in leap years
14353 		return 30;
14354 	}
14355 };
14356 
14357 /**
14358  * Return the equivalent year in the 2820 year cycle that begins on 
14359  * Far 1, 474. This particular cycle obeys the cycle-of-years formula 
14360  * whereas the others do not specifically. This cycle can be used as
14361  * a proxy for other years outside of the cycle by shifting them into 
14362  * the cycle.   
14363  * @param {number} year year to find the equivalent cycle year for
14364  * @returns {number} the equivalent cycle year
14365  */
14366 PersianAlgoCal.prototype.equivalentCycleYear = function(year) {
14367 	var y = year - (year >= 0 ? 474 : 473);
14368 	return MathUtils.mod(y, 2820) + 474;
14369 };
14370 
14371 /**
14372  * Return true if the given year is a leap year in the Persian calendar.
14373  * The year parameter may be given as a number, or as a PersAlgoDate object.
14374  * @param {number} year the year for which the leap year information is being sought
14375  * @return {boolean} true if the given year is a leap year
14376  */
14377 PersianAlgoCal.prototype.isLeapYear = function(year) {
14378 	return (MathUtils.mod((this.equivalentCycleYear(year) + 38) * 682, 2816) < 682);
14379 };
14380 
14381 /**
14382  * Return the type of this calendar.
14383  * 
14384  * @return {string} the name of the type of this calendar 
14385  */
14386 PersianAlgoCal.prototype.getType = function() {
14387 	return this.type;
14388 };
14389 
14390 /**
14391  * Return a date instance for this calendar type using the given
14392  * options.
14393  * @param {Object} options options controlling the construction of 
14394  * the date instance
14395  * @return {IDate} a date appropriate for this calendar type
14396  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
14397  */
14398 PersianAlgoCal.prototype.newDateInstance = function (options) {
14399 		return new PersianAlgoDate(options);
14400 };
14401 
14402 /* register this calendar for the factory method */
14403 Calendar._constructors["persian-algo"] = PersianAlgoCal;
14404 
14405 
14406 /*< PersAlgoRataDie.js */
14407 /*
14408  * PersAlsoRataDie.js - Represent an RD date in the Persian algorithmic calendar
14409  * 
14410  * Copyright © 2014-2015, JEDLSoft
14411  *
14412  * Licensed under the Apache License, Version 2.0 (the "License");
14413  * you may not use this file except in compliance with the License.
14414  * You may obtain a copy of the License at
14415  *
14416  *     http://www.apache.org/licenses/LICENSE-2.0
14417  *
14418  * Unless required by applicable law or agreed to in writing, software
14419  * distributed under the License is distributed on an "AS IS" BASIS,
14420  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14421  *
14422  * See the License for the specific language governing permissions and
14423  * limitations under the License.
14424  */
14425 
14426 /* !depends 
14427 PersianAlgoCal.js 
14428 MathUtils.js
14429 RataDie.js
14430 */
14431 
14432 
14433 /**
14434  * @class
14435  * Construct a new Persian RD date number object. The constructor parameters can 
14436  * contain any of the following properties:
14437  * 
14438  * <ul>
14439  * <li><i>unixtime<i> - sets the time of this instance according to the given 
14440  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
14441  * 
14442  * <li><i>julianday</i> - sets the time of this instance according to the given
14443  * Julian Day instance or the Julian Day given as a float
14444  * 
14445  * <li><i>year</i> - any integer, including 0
14446  * 
14447  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
14448  * 
14449  * <li><i>day</i> - 1 to 31
14450  * 
14451  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
14452  * is always done with an unambiguous 24 hour representation
14453  * 
14454  * <li><i>minute</i> - 0 to 59
14455  * 
14456  * <li><i>second</i> - 0 to 59
14457  * 
14458  * <li><i>millisecond</i> - 0 to 999
14459  * 
14460  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
14461  * </ul>
14462  *
14463  * If the constructor is called with another Persian date instance instead of
14464  * a parameter block, the other instance acts as a parameter block and its
14465  * settings are copied into the current instance.<p>
14466  * 
14467  * If the constructor is called with no arguments at all or if none of the 
14468  * properties listed above are present, then the RD is calculate based on 
14469  * the current date at the time of instantiation. <p>
14470  * 
14471  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
14472  * specified in the params, it is assumed that they have the smallest possible
14473  * value in the range for the property (zero or one).<p>
14474  * 
14475  * 
14476  * @private
14477  * @constructor
14478  * @extends RataDie
14479  * @param {Object=} params parameters that govern the settings and behaviour of this Persian RD date
14480  */
14481 var PersAlgoRataDie = function(params) {
14482 	this.cal = params && params.cal || new PersianAlgoCal();
14483 	this.rd = NaN;
14484 	RataDie.call(this, params);
14485 };
14486 
14487 PersAlgoRataDie.prototype = new RataDie();
14488 PersAlgoRataDie.prototype.parent = RataDie;
14489 PersAlgoRataDie.prototype.constructor = PersAlgoRataDie;
14490 
14491 /**
14492  * The difference between a zero Julian day and the first Persian date
14493  * @private
14494  * @type number
14495  */
14496 PersAlgoRataDie.prototype.epoch = 1948319.5;
14497 
14498 /**
14499  * @private
14500  * @const
14501  * @type Array.<number>
14502  * the cumulative lengths of each month, for a non-leap year 
14503  */
14504 PersAlgoRataDie.cumMonthLengths = [
14505     0,    // Farvardin
14506 	31,   // Ordibehesht
14507 	62,   // Khordad
14508 	93,   // Tir
14509 	124,  // Mordad
14510 	155,  // Shahrivar
14511 	186,  // Mehr
14512 	216,  // Aban
14513 	246,  // Azar
14514 	276,  // Dey
14515 	306,  // Bahman
14516 	336,  // Esfand
14517 	365
14518 ];
14519 
14520 /**
14521  * Calculate the Rata Die (fixed day) number of the given date from the
14522  * date components.
14523  *
14524  * @protected
14525  * @param {Object} date the date components to calculate the RD from
14526  */
14527 PersAlgoRataDie.prototype._setDateComponents = function(date) {
14528 	var year = this.cal.equivalentCycleYear(date.year);
14529 	var y = date.year - (date.year >= 0 ? 474 : 473);
14530 	var rdOfYears = 1029983 * Math.floor(y/2820) + 365 * (year - 1) + Math.floor((682 * year - 110) / 2816);
14531 	var dayInYear = (date.month > 1 ? PersAlgoRataDie.cumMonthLengths[date.month-1] : 0) + date.day;
14532 	var rdtime = (date.hour * 3600000 +
14533 		date.minute * 60000 +
14534 		date.second * 1000 +
14535 		date.millisecond) /
14536 		86400000;
14537 	
14538 	/*
14539 	// console.log("getRataDie: converting " +  JSON.stringify(this));
14540 	console.log("getRataDie: year is " +  year);
14541 	console.log("getRataDie: rd of years is " +  rdOfYears);
14542 	console.log("getRataDie: day in year is " +  dayInYear);
14543 	console.log("getRataDie: rdtime is " +  rdtime);
14544 	console.log("getRataDie: rd is " +  (rdOfYears + dayInYear + rdtime));
14545 	*/
14546 	
14547 	this.rd = rdOfYears + dayInYear + rdtime;
14548 };
14549 
14550 /**
14551  * Return the rd number of the particular day of the week on or before the 
14552  * given rd. eg. The Sunday on or before the given rd.
14553  * @private
14554  * @param {number} rd the rata die date of the reference date
14555  * @param {number} dayOfWeek the day of the week that is being sought relative 
14556  * to the current date
14557  * @return {number} the rd of the day of the week
14558  */
14559 PersAlgoRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
14560 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek - 3, 7);
14561 };
14562 
14563 
14564 /*< PersianAlgoDate.js */
14565 /*
14566  * PersianAlgoDate.js - Represent a date in the Persian algorithmic calendar
14567  * 
14568  * Copyright © 2014-2015, JEDLSoft
14569  *
14570  * Licensed under the Apache License, Version 2.0 (the "License");
14571  * you may not use this file except in compliance with the License.
14572  * You may obtain a copy of the License at
14573  *
14574  *     http://www.apache.org/licenses/LICENSE-2.0
14575  *
14576  * Unless required by applicable law or agreed to in writing, software
14577  * distributed under the License is distributed on an "AS IS" BASIS,
14578  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14579  *
14580  * See the License for the specific language governing permissions and
14581  * limitations under the License.
14582  */
14583 
14584 /* !depends 
14585 ilib.js
14586 Locale.js
14587 LocaleInfo.js
14588 TimeZone.js
14589 IDate.js
14590 PersianAlgoCal.js 
14591 SearchUtils.js
14592 MathUtils.js
14593 PersAlgoRataDie.js
14594 */
14595 
14596 
14597 
14598 
14599 /**
14600  * @class
14601  * 
14602  * Construct a new Persian date object. The constructor parameters can 
14603  * contain any of the following properties:
14604  * 
14605  * <ul>
14606  * <li><i>unixtime<i> - sets the time of this instance according to the given 
14607  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
14608  * 
14609  * <li><i>julianday</i> - sets the time of this instance according to the given
14610  * Julian Day instance or the Julian Day given as a float
14611  * 
14612  * <li><i>year</i> - any integer, including 0
14613  * 
14614  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
14615  * 
14616  * <li><i>day</i> - 1 to 31
14617  * 
14618  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
14619  * is always done with an unambiguous 24 hour representation
14620  * 
14621  * <li><i>minute</i> - 0 to 59
14622  * 
14623  * <li><i>second</i> - 0 to 59
14624  * 
14625  * <li><i>millisecond</i> - 0 to 999
14626  * 
14627  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string 
14628  * of this persian date. The date/time is kept in the local time. The time zone
14629  * is used later if this date is formatted according to a different time zone and
14630  * the difference has to be calculated, or when the date format has a time zone
14631  * component in it.
14632  * 
14633  * <li><i>locale</i> - locale for this persian date. If the time zone is not 
14634  * given, it can be inferred from this locale. For locales that span multiple
14635  * time zones, the one with the largest population is chosen as the one that 
14636  * represents the locale.
14637  * 
14638  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
14639  * </ul>
14640  *
14641  * If the constructor is called with another Persian date instance instead of
14642  * a parameter block, the other instance acts as a parameter block and its
14643  * settings are copied into the current instance.<p>
14644  * 
14645  * If the constructor is called with no arguments at all or if none of the 
14646  * properties listed above 
14647  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
14648  * components are 
14649  * filled in with the current date at the time of instantiation. Note that if
14650  * you do not give the time zone when defaulting to the current time and the 
14651  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
14652  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
14653  * Mean Time").<p>
14654  * 
14655  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
14656  * specified in the params, it is assumed that they have the smallest possible
14657  * value in the range for the property (zero or one).<p>
14658  * 
14659  * 
14660  * @constructor
14661  * @extends IDate
14662  * @param {Object=} params parameters that govern the settings and behaviour of this Persian date
14663  */
14664 var PersianAlgoDate = function(params) {
14665 	this.cal = new PersianAlgoCal();
14666 	this.timezone = "local";
14667 	
14668 	if (params) {
14669 		if (params.locale) {
14670 			this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
14671 			var li = new LocaleInfo(this.locale);
14672 			this.timezone = li.getTimeZone(); 
14673 		}
14674 		if (params.timezone) {
14675 			this.timezone = params.timezone;
14676 		}
14677 		
14678 		if (params.year || params.month || params.day || params.hour ||
14679 				params.minute || params.second || params.millisecond ) {
14680 			/**
14681 			 * Year in the Persian calendar.
14682 			 * @type number
14683 			 */
14684 			this.year = parseInt(params.year, 10) || 0;
14685 
14686 			/**
14687 			 * The month number, ranging from 1 to 12
14688 			 * @type number
14689 			 */
14690 			this.month = parseInt(params.month, 10) || 1;
14691 
14692 			/**
14693 			 * The day of the month. This ranges from 1 to 31.
14694 			 * @type number
14695 			 */
14696 			this.day = parseInt(params.day, 10) || 1;
14697 			
14698 			/**
14699 			 * The hour of the day. This can be a number from 0 to 23, as times are
14700 			 * stored unambiguously in the 24-hour clock.
14701 			 * @type number
14702 			 */
14703 			this.hour = parseInt(params.hour, 10) || 0;
14704 
14705 			/**
14706 			 * The minute of the hours. Ranges from 0 to 59.
14707 			 * @type number
14708 			 */
14709 			this.minute = parseInt(params.minute, 10) || 0;
14710 
14711 			/**
14712 			 * The second of the minute. Ranges from 0 to 59.
14713 			 * @type number
14714 			 */
14715 			this.second = parseInt(params.second, 10) || 0;
14716 
14717 			/**
14718 			 * The millisecond of the second. Ranges from 0 to 999.
14719 			 * @type number
14720 			 */
14721 			this.millisecond = parseInt(params.millisecond, 10) || 0;
14722 			
14723 			/**
14724 			 * The day of the year. Ranges from 1 to 366.
14725 			 * @type number
14726 			 */
14727 			this.dayOfYear = parseInt(params.dayOfYear, 10);
14728 
14729 			if (typeof(params.dst) === 'boolean') {
14730 				this.dst = params.dst;
14731 			}
14732 			
14733 			this.rd = this.newRd(this);
14734 			
14735 			// add the time zone offset to the rd to convert to UTC
14736 			if (!this.tz) {
14737 				this.tz = new TimeZone({id: this.timezone});
14738 			}
14739 			// getOffsetMillis requires that this.year, this.rd, and this.dst 
14740 			// are set in order to figure out which time zone rules apply and 
14741 			// what the offset is at that point in the year
14742 			this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
14743 			if (this.offset !== 0) {
14744 				this.rd = this.newRd({
14745 					rd: this.rd.getRataDie() - this.offset
14746 				});
14747 			}
14748 		}
14749 	}
14750 
14751 	if (!this.rd) {
14752 		this.rd = this.newRd(params);
14753 		this._calcDateComponents();
14754 	}
14755 };
14756 
14757 PersianAlgoDate.prototype = new IDate({noinstance: true});
14758 PersianAlgoDate.prototype.parent = IDate;
14759 PersianAlgoDate.prototype.constructor = PersianAlgoDate;
14760 
14761 /**
14762  * Return a new RD for this date type using the given params.
14763  * @protected
14764  * @param {Object=} params the parameters used to create this rata die instance
14765  * @returns {RataDie} the new RD instance for the given params
14766  */
14767 PersianAlgoDate.prototype.newRd = function (params) {
14768 	return new PersAlgoRataDie(params);
14769 };
14770 
14771 /**
14772  * Return the year for the given RD
14773  * @protected
14774  * @param {number} rd RD to calculate from 
14775  * @returns {number} the year for the RD
14776  */
14777 PersianAlgoDate.prototype._calcYear = function(rd) {
14778 	var shiftedRd = rd - 173126;
14779 	var numberOfCycles = Math.floor(shiftedRd / 1029983);
14780 	var shiftedDayInCycle = MathUtils.mod(shiftedRd, 1029983);
14781 	var yearInCycle = (shiftedDayInCycle === 1029982) ? 2820 : Math.floor((2816 * shiftedDayInCycle + 1031337) / 1028522);
14782 	var year = 474 + 2820 * numberOfCycles + yearInCycle;
14783 	return (year > 0) ? year : year - 1;
14784 };
14785 
14786 /**
14787  * @private
14788  * Calculate date components for the given RD date.
14789  */
14790 PersianAlgoDate.prototype._calcDateComponents = function () {
14791 	var remainder,
14792 		rd = this.rd.getRataDie();
14793 	
14794 	this.year = this._calcYear(rd);
14795 	
14796 	if (typeof(this.offset) === "undefined") {
14797 		// now offset the RD by the time zone, then recalculate in case we were 
14798 		// near the year boundary
14799 		if (!this.tz) {
14800 			this.tz = new TimeZone({id: this.timezone});
14801 		}
14802 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
14803 	}
14804 	
14805 	if (this.offset !== 0) {
14806 		rd += this.offset;
14807 		this.year = this._calcYear(rd);
14808 	}
14809 	
14810 	//console.log("PersAlgoDate.calcComponent: calculating for rd " + rd);
14811 	//console.log("PersAlgoDate.calcComponent: year is " + ret.year);
14812 	var yearStart = this.newRd({
14813 		year: this.year,
14814 		month: 1,
14815 		day: 1,
14816 		hour: 0,
14817 		minute: 0,
14818 		second: 0,
14819 		millisecond: 0
14820 	});
14821 	remainder = rd - yearStart.getRataDie() + 1;
14822 	
14823 	this.dayOfYear = remainder;
14824 	
14825 	//console.log("PersAlgoDate.calcComponent: remainder is " + remainder);
14826 	
14827 	this.month = SearchUtils.bsearch(remainder, PersAlgoRataDie.cumMonthLengths);
14828 	remainder -= PersAlgoRataDie.cumMonthLengths[this.month-1];
14829 	
14830 	//console.log("PersAlgoDate.calcComponent: month is " + this.month + " and remainder is " + remainder);
14831 	
14832 	this.day = Math.floor(remainder);
14833 	remainder -= this.day;
14834 	
14835 	//console.log("PersAlgoDate.calcComponent: day is " + this.day + " and remainder is " + remainder);
14836 	
14837 	// now convert to milliseconds for the rest of the calculation
14838 	remainder = Math.round(remainder * 86400000);
14839 	
14840 	this.hour = Math.floor(remainder/3600000);
14841 	remainder -= this.hour * 3600000;
14842 	
14843 	this.minute = Math.floor(remainder/60000);
14844 	remainder -= this.minute * 60000;
14845 	
14846 	this.second = Math.floor(remainder/1000);
14847 	remainder -= this.second * 1000;
14848 	
14849 	this.millisecond = remainder;
14850 };
14851 
14852 /**
14853  * Return the day of the week of this date. The day of the week is encoded
14854  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
14855  * 
14856  * @return {number} the day of the week
14857  */
14858 PersianAlgoDate.prototype.getDayOfWeek = function() {
14859 	var rd = Math.floor(this.getRataDie());
14860 	return MathUtils.mod(rd-3, 7);
14861 };
14862 
14863 /**
14864  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 
14865  * 365, regardless of months or weeks, etc. That is, Farvardin 1st is day 1, and 
14866  * December 31st is 365 in regular years, or 366 in leap years.
14867  * @return {number} the ordinal day of the year
14868  */
14869 PersianAlgoDate.prototype.getDayOfYear = function() {
14870 	return PersAlgoRataDie.cumMonthLengths[this.month-1] + this.day;
14871 };
14872 
14873 /**
14874  * Return the era for this date as a number. The value for the era for Persian 
14875  * calendars is -1 for "before the persian era" (BP) and 1 for "the persian era" (anno 
14876  * persico or AP). 
14877  * BP dates are any date before Farvardin 1, 1 AP. In the proleptic Persian calendar, 
14878  * there is a year 0, so any years that are negative or zero are BP.
14879  * @return {number} 1 if this date is in the common era, -1 if it is before the 
14880  * common era 
14881  */
14882 PersianAlgoDate.prototype.getEra = function() {
14883 	return (this.year < 1) ? -1 : 1;
14884 };
14885 
14886 /**
14887  * Return the name of the calendar that governs this date.
14888  * 
14889  * @return {string} a string giving the name of the calendar
14890  */
14891 PersianAlgoDate.prototype.getCalendar = function() {
14892 	return "persian-algo";
14893 };
14894 
14895 // register with the factory method
14896 IDate._constructors["persian-algo"] = PersianAlgoDate;
14897 
14898 
14899 /*< HanCal.js */
14900 /*
14901  * han.js - Represent a Han Chinese Lunar calendar object.
14902  * 
14903  * Copyright © 2014-2015, JEDLSoft
14904  *
14905  * Licensed under the Apache License, Version 2.0 (the "License");
14906  * you may not use this file except in compliance with the License.
14907  * You may obtain a copy of the License at
14908  *
14909  *     http://www.apache.org/licenses/LICENSE-2.0
14910  *
14911  * Unless required by applicable law or agreed to in writing, software
14912  * distributed under the License is distributed on an "AS IS" BASIS,
14913  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14914  *
14915  * See the License for the specific language governing permissions and
14916  * limitations under the License.
14917  */
14918 
14919 /* !depends 
14920 ilib.js 
14921 Calendar.js 
14922 MathUtils.js 
14923 Astro.js
14924 GregorianDate.js
14925 GregRataDie.js
14926 RataDie.js
14927 */
14928 
14929 
14930 
14931 
14932 /**
14933  * @class
14934  * Construct a new Han algorithmic calendar object. This class encodes information about
14935  * a Han algorithmic calendar.<p>
14936  * 
14937  * 
14938  * @constructor
14939  * @param {Object=} params optional parameters to load the calendrical data
14940  * @extends Calendar
14941  */
14942 var HanCal = function(params) {
14943 	this.type = "han";
14944 	var sync = params && typeof(params.sync) === 'boolean' ? params.sync : true;
14945 	
14946 	Astro.initAstro(sync, params && params.loadParams, ilib.bind(this, function (x) {
14947 		if (params && typeof(params.callback) === 'function') {
14948 			params.callback(this);
14949 		}
14950 	}));
14951 };
14952 
14953 /**
14954  * @protected
14955  * @static
14956  * @param {number} year
14957  * @param {number=} cycle
14958  * @return {number}
14959  */
14960 HanCal._getElapsedYear = function(year, cycle) {
14961 	var elapsedYear = year || 0;
14962 	if (typeof(year) !== 'undefined' && year < 61 && typeof(cycle) !== 'undefined') {
14963 		elapsedYear = 60 * cycle + year;
14964 	}
14965 	return elapsedYear;
14966 };
14967 
14968 /**
14969  * @protected
14970  * @static
14971  * @param {number} jd julian day to calculate from
14972  * @param {number} longitude longitude to seek 
14973  * @returns {number} the julian day of the next time that the solar longitude 
14974  * is a multiple of the given longitude
14975  */
14976 HanCal._hanNextSolarLongitude = function(jd, longitude) {
14977 	var tz = HanCal._chineseTZ(jd);
14978 	var uni = Astro._universalFromLocal(jd, tz);
14979 	var sol = Astro._nextSolarLongitude(uni, longitude);
14980 	return Astro._localFromUniversal(sol, tz);
14981 };
14982 
14983 /**
14984  * @protected
14985  * @static
14986  * @param {number} jd julian day to calculate from 
14987  * @returns {number} the major solar term for the julian day
14988  */
14989 HanCal._majorSTOnOrAfter = function(jd) {
14990 	var tz = HanCal._chineseTZ(jd);
14991 	var uni = Astro._universalFromLocal(jd, tz);
14992 	var next = Astro._fixangle(30 * Math.ceil(Astro._solarLongitude(uni)/30));
14993 	return HanCal._hanNextSolarLongitude(jd, next);
14994 };
14995 
14996 /**
14997  * @protected
14998  * @static
14999  * @param {number} year the year for which the leap year information is being sought
15000  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15001  * cycle is not given, then the year should be given as elapsed years since the beginning
15002  * of the epoch
15003  */
15004 HanCal._solsticeBefore = function (year, cycle) {
15005 	var elapsedYear = HanCal._getElapsedYear(year, cycle);
15006 	var gregyear = elapsedYear - 2697;
15007 	var rd = new GregRataDie({
15008 		year: gregyear-1, 
15009 		month: 12, 
15010 		day: 15, 
15011 		hour: 0, 
15012 		minute: 0, 
15013 		second: 0, 
15014 		millisecond: 0
15015 	});
15016 	return HanCal._majorSTOnOrAfter(rd.getRataDie() + RataDie.gregorianEpoch);
15017 };
15018 
15019 /**
15020  * @protected
15021  * @static
15022  * @param {number} jd julian day to calculate from
15023  * @returns {number} the current major solar term
15024  */
15025 HanCal._chineseTZ = function(jd) {
15026 	var year = GregorianDate._calcYear(jd - RataDie.gregorianEpoch);
15027 	return year < 1929 ? 465.6666666666666666 : 480;
15028 };
15029 
15030 /**
15031  * @protected
15032  * @static
15033  * @param {number} jd julian day to calculate from 
15034  * @returns {number} the julian day of next new moon on or after the given julian day date
15035  */
15036 HanCal._newMoonOnOrAfter = function(jd) {
15037 	var tz = HanCal._chineseTZ(jd);
15038 	var uni = Astro._universalFromLocal(jd, tz);
15039 	var moon = Astro._newMoonAtOrAfter(uni);
15040 	// floor to the start of the julian day
15041 	return Astro._floorToJD(Astro._localFromUniversal(moon, tz)); 
15042 };
15043 
15044 /**
15045  * @protected
15046  * @static
15047  * @param {number} jd julian day to calculate from 
15048  * @returns {number} the julian day of previous new moon before the given julian day date
15049  */
15050 HanCal._newMoonBefore = function(jd) {
15051 	var tz = HanCal._chineseTZ(jd);
15052 	var uni = Astro._universalFromLocal(jd, tz);
15053 	var moon = Astro._newMoonBefore(uni);
15054 	// floor to the start of the julian day
15055 	return Astro._floorToJD(Astro._localFromUniversal(moon, tz));
15056 };
15057 
15058 /**
15059  * @static
15060  * @protected
15061  * @param {number} year the year for which the leap year information is being sought
15062  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15063  * cycle is not given, then the year should be given as elapsed years since the beginning
15064  * of the epoch
15065  */
15066 HanCal._leapYearCalc = function(year, cycle) {
15067 	var ret = {
15068 		elapsedYear: HanCal._getElapsedYear(year, cycle)
15069 	};
15070 	ret.solstice1 = HanCal._solsticeBefore(ret.elapsedYear);
15071 	ret.solstice2 = HanCal._solsticeBefore(ret.elapsedYear+1);
15072 	// ceil to the end of the julian day
15073 	ret.m1 = HanCal._newMoonOnOrAfter(Astro._ceilToJD(ret.solstice1));
15074 	ret.m2 = HanCal._newMoonBefore(Astro._ceilToJD(ret.solstice2));
15075 	
15076 	return ret;
15077 };
15078 
15079 /**
15080  * @protected
15081  * @static
15082  * @param {number} jd julian day to calculate from
15083  * @returns {number} the current major solar term
15084  */
15085 HanCal._currentMajorST = function(jd) {
15086 	var s = Astro._solarLongitude(Astro._universalFromLocal(jd, HanCal._chineseTZ(jd)));
15087 	return MathUtils.amod(2 + Math.floor(s/30), 12);
15088 };
15089 
15090 /**
15091  * @protected
15092  * @static
15093  * @param {number} jd julian day to calculate from
15094  * @returns {boolean} true if there is no major solar term in the same year
15095  */
15096 HanCal._noMajorST = function(jd) {
15097 	return HanCal._currentMajorST(jd) === HanCal._currentMajorST(HanCal._newMoonOnOrAfter(jd+1));
15098 };
15099 
15100 /**
15101  * Return the number of months in the given year. The number of months in a year varies
15102  * for some luni-solar calendars because in some years, an extra month is needed to extend the 
15103  * days in a year to an entire solar year. The month is represented as a 1-based number
15104  * where 1=first month, 2=second month, etc.
15105  * 
15106  * @param {number} year a year for which the number of months is sought
15107  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15108  * cycle is not given, then the year should be given as elapsed years since the beginning
15109  * of the epoch
15110  * @return {number} The number of months in the given year
15111  */
15112 HanCal.prototype.getNumMonths = function(year, cycle) {
15113 	return this.isLeapYear(year, cycle) ? 13 : 12;
15114 };
15115 
15116 /**
15117  * Return the number of days in a particular month in a particular year. This function
15118  * can return a different number for a month depending on the year because of things
15119  * like leap years.
15120  * 
15121  * @param {number} month the elapsed month for which the length is sought
15122  * @param {number} year the elapsed year within which that month can be found
15123  * @return {number} the number of days within the given month in the given year
15124  */
15125 HanCal.prototype.getMonLength = function(month, year) {
15126 	// distance between two new moons in Nanjing China
15127 	var calc = HanCal._leapYearCalc(year);
15128 	var priorNewMoon = HanCal._newMoonOnOrAfter(calc.m1 + month * 29);
15129 	var postNewMoon = HanCal._newMoonOnOrAfter(priorNewMoon + 1);
15130 	return postNewMoon - priorNewMoon;
15131 };
15132 
15133 /**
15134  * Return the equivalent year in the 2820 year cycle that begins on 
15135  * Far 1, 474. This particular cycle obeys the cycle-of-years formula 
15136  * whereas the others do not specifically. This cycle can be used as
15137  * a proxy for other years outside of the cycle by shifting them into 
15138  * the cycle.   
15139  * @param {number} year year to find the equivalent cycle year for
15140  * @returns {number} the equivalent cycle year
15141  */
15142 HanCal.prototype.equivalentCycleYear = function(year) {
15143 	var y = year - (year >= 0 ? 474 : 473);
15144 	return MathUtils.mod(y, 2820) + 474;
15145 };
15146 
15147 /**
15148  * Return true if the given year is a leap year in the Han calendar.
15149  * If the year is given as a year/cycle combination, then the year should be in the 
15150  * range [1,60] and the given cycle is the cycle in which the year is located. If 
15151  * the year is greater than 60, then
15152  * it represents the total number of years elapsed in the proleptic calendar since
15153  * the beginning of the Chinese epoch in on 15 Feb, -2636 (Gregorian). In this 
15154  * case, the cycle parameter is ignored.
15155  * 
15156  * @param {number} year the year for which the leap year information is being sought
15157  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15158  * cycle is not given, then the year should be given as elapsed years since the beginning
15159  * of the epoch
15160  * @return {boolean} true if the given year is a leap year
15161  */
15162 HanCal.prototype.isLeapYear = function(year, cycle) {
15163 	var calc = HanCal._leapYearCalc(year, cycle);
15164 	return Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12;
15165 };
15166 
15167 /**
15168  * Return the month of the year that is the leap month. If the given year is
15169  * not a leap year, then this method will return -1.
15170  * 
15171  * @param {number} year the year for which the leap year information is being sought
15172  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15173  * cycle is not given, then the year should be given as elapsed years since the beginning
15174  * of the epoch
15175  * @return {number} the number of the month that is doubled in this leap year, or -1
15176  * if this is not a leap year
15177  */
15178 HanCal.prototype.getLeapMonth = function(year, cycle) {
15179 	var calc = HanCal._leapYearCalc(year, cycle);
15180 	
15181 	if (Math.round((calc.m2 - calc.m1) / 29.530588853000001) != 12) {
15182 		return -1; // no leap month
15183 	}
15184 	
15185 	// search between rd1 and rd2 for the first month with no major solar term. That is our leap month.
15186 	var month = 0;
15187 	var m = HanCal._newMoonOnOrAfter(calc.m1+1);
15188 	while (!HanCal._noMajorST(m)) {
15189 		month++;
15190 		m = HanCal._newMoonOnOrAfter(m+1);
15191 	}
15192 	
15193 	// return the number of the month that is doubled
15194 	return month; 
15195 };
15196 
15197 /**
15198  * Return the date of Chinese New Years in the given calendar year.
15199  * 
15200  * @param {number} year the Chinese year for which the new year information is being sought
15201  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15202  * cycle is not given, then the year should be given as elapsed years since the beginning
15203  * of the epoch
15204  * @return {number} the julian day of the beginning of the given year 
15205  */
15206 HanCal.prototype.newYears = function(year, cycle) {
15207 	var calc = HanCal._leapYearCalc(year, cycle);
15208 	var m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
15209 	if (Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12 &&
15210 			(HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2)) ) {
15211 		return HanCal._newMoonOnOrAfter(m2+1);
15212 	}
15213 	return m2;
15214 };
15215 
15216 /**
15217  * Return the type of this calendar.
15218  * 
15219  * @return {string} the name of the type of this calendar 
15220  */
15221 HanCal.prototype.getType = function() {
15222 	return this.type;
15223 };
15224 
15225 /**
15226  * Return a date instance for this calendar type using the given
15227  * options.
15228  * @param {Object} options options controlling the construction of 
15229  * the date instance
15230  * @return {HanDate} a date appropriate for this calendar type
15231  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
15232  */
15233 HanCal.prototype.newDateInstance = function (options) {
15234 		return new HanDate(options);
15235 };
15236 
15237 /* register this calendar for the factory method */
15238 Calendar._constructors["han"] = HanCal;
15239 
15240 
15241 /*< HanRataDie.js */
15242 /*
15243  * HanDate.js - Represent a date in the Han algorithmic calendar
15244  * 
15245  * Copyright © 2014-2015, JEDLSoft
15246  *
15247  * Licensed under the Apache License, Version 2.0 (the "License");
15248  * you may not use this file except in compliance with the License.
15249  * You may obtain a copy of the License at
15250  *
15251  *     http://www.apache.org/licenses/LICENSE-2.0
15252  *
15253  * Unless required by applicable law or agreed to in writing, software
15254  * distributed under the License is distributed on an "AS IS" BASIS,
15255  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15256  *
15257  * See the License for the specific language governing permissions and
15258  * limitations under the License.
15259  */
15260 
15261 /* !depends
15262 ilib.js
15263 HanCal.js
15264 MathUtils.js
15265 RataDie.js
15266 */
15267 
15268 
15269 /**
15270  * Construct a new Han RD date number object. The constructor parameters can 
15271  * contain any of the following properties:
15272  * 
15273  * <ul>
15274  * <li><i>unixtime<i> - sets the time of this instance according to the given 
15275  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
15276  * 
15277  * <li><i>julianday</i> - sets the time of this instance according to the given
15278  * Julian Day instance or the Julian Day given as a float
15279  * 
15280  * <li><i>cycle</i> - any integer giving the number of 60-year cycle in which the date is located.
15281  * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious 
15282  * linear count of years since the beginning of the epoch, much like other calendars. This linear
15283  * count is never used. If both the cycle and year are given, the year is wrapped to the range 0 
15284  * to 60 and treated as if it were a year in the regular 60-year cycle.
15285  * 
15286  * <li><i>year</i> - any integer, including 0
15287  * 
15288  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
15289  * 
15290  * <li><i>day</i> - 1 to 31
15291  * 
15292  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
15293  * is always done with an unambiguous 24 hour representation
15294  * 
15295  * <li><i>minute</i> - 0 to 59
15296  * 
15297  * <li><i>second</i> - 0 to 59
15298  * 
15299  * <li><i>millisecond</i> - 0 to 999
15300  * 
15301  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
15302  * </ul>
15303  *
15304  * If the constructor is called with another Han date instance instead of
15305  * a parameter block, the other instance acts as a parameter block and its
15306  * settings are copied into the current instance.<p>
15307  * 
15308  * If the constructor is called with no arguments at all or if none of the 
15309  * properties listed above are present, then the RD is calculate based on 
15310  * the current date at the time of instantiation. <p>
15311  * 
15312  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
15313  * specified in the params, it is assumed that they have the smallest possible
15314  * value in the range for the property (zero or one).<p>
15315  * 
15316  * 
15317  * @private
15318  * @class
15319  * @constructor
15320  * @extends RataDie
15321  * @param {Object=} params parameters that govern the settings and behaviour of this Han RD date
15322  */
15323 var HanRataDie = function(params) {
15324 	this.rd = NaN;
15325 	if (params && params.cal) {
15326 		this.cal = params.cal;
15327 		RataDie.call(this, params);
15328 		if (params && typeof(params.callback) === 'function') {
15329 			params.callback(this);
15330 		}
15331 	} else {
15332 		new HanCal({
15333 			sync: params && params.sync,
15334 			loadParams: params && params.loadParams,
15335 			callback: ilib.bind(this, function(c) {
15336 				this.cal = c;
15337 				RataDie.call(this, params);
15338 				if (params && typeof(params.callback) === 'function') {
15339 					params.callback(this);
15340 				}
15341 			})
15342 		});
15343 	}
15344 };
15345 
15346 HanRataDie.prototype = new RataDie();
15347 HanRataDie.prototype.parent = RataDie;
15348 HanRataDie.prototype.constructor = HanRataDie;
15349 
15350 /**
15351  * The difference between a zero Julian day and the first Han date
15352  * which is February 15, -2636 (Gregorian).
15353  * @private
15354  * @type number
15355  */
15356 HanRataDie.epoch = 758325.5;
15357 
15358 /**
15359  * Calculate the Rata Die (fixed day) number of the given date from the
15360  * date components.
15361  *
15362  * @protected
15363  * @param {Object} date the date components to calculate the RD from
15364  */
15365 HanRataDie.prototype._setDateComponents = function(date) {
15366 	var calc = HanCal._leapYearCalc(date.year, date.cycle);
15367 	var m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
15368 	var newYears;
15369 	this.leapYear = (Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12);
15370 	if (this.leapYear && (HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2)) ) {
15371 		newYears = HanCal._newMoonOnOrAfter(m2+1);
15372 	} else {
15373 		newYears = m2;
15374 	}
15375 
15376 	var priorNewMoon = HanCal._newMoonOnOrAfter(calc.m1 + date.month * 29); // this is a julian day
15377 	this.priorLeapMonth = HanRataDie._priorLeapMonth(newYears, HanCal._newMoonBefore(priorNewMoon));
15378 	this.leapMonth = (this.leapYear && HanCal._noMajorST(priorNewMoon) && !this.priorLeapMonth);
15379 
15380 	var rdtime = (date.hour * 3600000 +
15381 		date.minute * 60000 +
15382 		date.second * 1000 +
15383 		date.millisecond) /
15384 		86400000;
15385 	
15386 	/*
15387 	console.log("getRataDie: converting " +  JSON.stringify(date) + " to an RD");
15388 	console.log("getRataDie: year is " +  date.year + " plus cycle " + date.cycle);
15389 	console.log("getRataDie: isLeapYear is " +  this.leapYear);
15390 	console.log("getRataDie: priorNewMoon is " +  priorNewMoon);
15391 	console.log("getRataDie: day in month is " +  date.day);
15392 	console.log("getRataDie: rdtime is " +  rdtime);
15393 	console.log("getRataDie: rd is " +  (priorNewMoon + date.day - 1 + rdtime));
15394 	*/
15395 	
15396 	this.rd = priorNewMoon + date.day - 1 + rdtime - RataDie.gregorianEpoch;
15397 };
15398 
15399 /**
15400  * Return the rd number of the particular day of the week on or before the 
15401  * given rd. eg. The Sunday on or before the given rd.
15402  * @private
15403  * @param {number} rd the rata die date of the reference date
15404  * @param {number} dayOfWeek the day of the week that is being sought relative 
15405  * to the current date
15406  * @return {number} the rd of the day of the week
15407  */
15408 HanRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
15409 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek, 7);
15410 };
15411 
15412 /**
15413  * @protected
15414  * @static
15415  * @param {number} jd1 first julian day
15416  * @param {number} jd2 second julian day
15417  * @returns {boolean} true if there is a leap month earlier in the same year 
15418  * as the given months 
15419  */
15420 HanRataDie._priorLeapMonth = function(jd1, jd2) {
15421 	return jd2 >= jd1 &&
15422 		(HanRataDie._priorLeapMonth(jd1, HanCal._newMoonBefore(jd2)) ||
15423 				HanCal._noMajorST(jd2));
15424 };
15425 
15426 
15427 
15428 /*< HanDate.js */
15429 /*
15430  * HanDate.js - Represent a date in the Han algorithmic calendar
15431  * 
15432  * Copyright © 2014-2015, JEDLSoft
15433  *
15434  * Licensed under the Apache License, Version 2.0 (the "License");
15435  * you may not use this file except in compliance with the License.
15436  * You may obtain a copy of the License at
15437  *
15438  *     http://www.apache.org/licenses/LICENSE-2.0
15439  *
15440  * Unless required by applicable law or agreed to in writing, software
15441  * distributed under the License is distributed on an "AS IS" BASIS,
15442  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15443  *
15444  * See the License for the specific language governing permissions and
15445  * limitations under the License.
15446  */
15447 
15448 /* !depends
15449 ilib.js
15450 IDate.js
15451 GregorianDate.js 
15452 HanCal.js
15453 Astro.js 
15454 JSUtils.js
15455 MathUtils.js
15456 LocaleInfo.js 
15457 Locale.js
15458 TimeZone.js
15459 HanRataDie.js
15460 RataDie.js
15461 */
15462 
15463 
15464 
15465 
15466 /**
15467  * @class
15468  * 
15469  * Construct a new Han date object. The constructor parameters can 
15470  * contain any of the following properties:
15471  * 
15472  * <ul>
15473  * <li><i>unixtime<i> - sets the time of this instance according to the given 
15474  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
15475  * 
15476  * <li><i>julianday</i> - sets the time of this instance according to the given
15477  * Julian Day instance or the Julian Day given as a float
15478  * 
15479  * <li><i>cycle</i> - any integer giving the number of 60-year cycle in which the date is located.
15480  * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious 
15481  * linear count of years since the beginning of the epoch, much like other calendars. This linear
15482  * count is never used. If both the cycle and year are given, the year is wrapped to the range 0 
15483  * to 60 and treated as if it were a year in the regular 60-year cycle.
15484  * 
15485  * <li><i>year</i> - any integer, including 0
15486  * 
15487  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
15488  * 
15489  * <li><i>day</i> - 1 to 31
15490  * 
15491  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
15492  * is always done with an unambiguous 24 hour representation
15493  * 
15494  * <li><i>minute</i> - 0 to 59
15495  * 
15496  * <li><i>second</i> - 0 to 59
15497  * 
15498  * <li><i>millisecond</i> - 0 to 999
15499  * 
15500  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string 
15501  * of this han date. The date/time is kept in the local time. The time zone
15502  * is used later if this date is formatted according to a different time zone and
15503  * the difference has to be calculated, or when the date format has a time zone
15504  * component in it.
15505  * 
15506  * <li><i>locale</i> - locale for this han date. If the time zone is not 
15507  * given, it can be inferred from this locale. For locales that span multiple
15508  * time zones, the one with the largest population is chosen as the one that 
15509  * represents the locale.
15510  * 
15511  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
15512  * </ul>
15513  *
15514  * If the constructor is called with another Han date instance instead of
15515  * a parameter block, the other instance acts as a parameter block and its
15516  * settings are copied into the current instance.<p>
15517  * 
15518  * If the constructor is called with no arguments at all or if none of the 
15519  * properties listed above 
15520  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
15521  * components are 
15522  * filled in with the current date at the time of instantiation. Note that if
15523  * you do not give the time zone when defaulting to the current time and the 
15524  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
15525  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
15526  * Mean Time").<p>
15527  * 
15528  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
15529  * specified in the params, it is assumed that they have the smallest possible
15530  * value in the range for the property (zero or one).<p>
15531  * 
15532  * 
15533  * @constructor
15534  * @extends Date
15535  * @param {Object=} params parameters that govern the settings and behaviour of this Han date
15536  */
15537 var HanDate = function(params) {
15538 	this.timezone = "local";
15539 	if (params) {
15540 		if (params.locale) {
15541 			this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
15542 			var li = new LocaleInfo(this.locale);
15543 			this.timezone = li.getTimeZone(); 
15544 		}
15545 		if (params.timezone) {
15546 			this.timezone = params.timezone;
15547 		}
15548 	}
15549 	
15550 	new HanCal({
15551 		sync: params && typeof(params) === 'boolean' ? params.sync : true,
15552 		loadParams: params && params.loadParams,
15553 		callback: ilib.bind(this, function (cal) {
15554 			this.cal = cal;
15555 	
15556 			if (params && (params.year || params.month || params.day || params.hour ||
15557 				params.minute || params.second || params.millisecond || params.cycle || params.cycleYear)) {
15558 				if (typeof(params.cycle) !== 'undefined') {
15559 					/**
15560 					 * Cycle number in the Han calendar.
15561 					 * @type number
15562 					 */
15563 					this.cycle = parseInt(params.cycle, 10) || 0;
15564 					
15565 					var year = (typeof(params.year) !== 'undefined' ? parseInt(params.year, 10) : parseInt(params.cycleYear, 10)) || 0;
15566 					
15567 					/**
15568 					 * Year in the Han calendar.
15569 					 * @type number
15570 					 */
15571 					this.year = HanCal._getElapsedYear(year, this.cycle);
15572 				} else {
15573 					if (typeof(params.year) !== 'undefined') {
15574 						this.year = parseInt(params.year, 10) || 0;
15575 						this.cycle = Math.floor((this.year - 1) / 60);
15576 					} else {
15577 						this.year = this.cycle = 0;
15578 					}
15579 				}	
15580 				
15581 				/**
15582 				 * The month number, ranging from 1 to 13
15583 				 * @type number
15584 				 */
15585 				this.month = parseInt(params.month, 10) || 1;
15586 	
15587 				/**
15588 				 * The day of the month. This ranges from 1 to 30.
15589 				 * @type number
15590 				 */
15591 				this.day = parseInt(params.day, 10) || 1;
15592 				
15593 				/**
15594 				 * The hour of the day. This can be a number from 0 to 23, as times are
15595 				 * stored unambiguously in the 24-hour clock.
15596 				 * @type number
15597 				 */
15598 				this.hour = parseInt(params.hour, 10) || 0;
15599 	
15600 				/**
15601 				 * The minute of the hours. Ranges from 0 to 59.
15602 				 * @type number
15603 				 */
15604 				this.minute = parseInt(params.minute, 10) || 0;
15605 	
15606 				/**
15607 				 * The second of the minute. Ranges from 0 to 59.
15608 				 * @type number
15609 				 */
15610 				this.second = parseInt(params.second, 10) || 0;
15611 	
15612 				/**
15613 				 * The millisecond of the second. Ranges from 0 to 999.
15614 				 * @type number
15615 				 */
15616 				this.millisecond = parseInt(params.millisecond, 10) || 0;
15617 			
15618 				// derived properties
15619 				
15620 				/**
15621 				 * Year in the cycle of the Han calendar
15622 				 * @type number
15623 				 */
15624 				this.cycleYear = MathUtils.amod(this.year, 60); 
15625 
15626 				/**
15627 				 * The day of the year. Ranges from 1 to 384.
15628 				 * @type number
15629 				 */
15630 				this.dayOfYear = parseInt(params.dayOfYear, 10);
15631 	
15632 				if (typeof(params.dst) === 'boolean') {
15633 					this.dst = params.dst;
15634 				}
15635 				
15636 				this.newRd({
15637 					cal: this.cal,
15638 					cycle: this.cycle,
15639 					year: this.year,
15640 					month: this.month,
15641 					day: this.day,
15642 					hour: this.hour,
15643 					minute: this.minute,
15644 					second: this.second,
15645 					millisecond: this.millisecond,
15646 					sync: params && typeof(params.sync) === 'boolean' ? params.sync : true,
15647 					loadParams: params && params.loadParams,
15648 					callback: ilib.bind(this, function (rd) {
15649 						if (rd) {
15650 							this.rd = rd;
15651 							
15652 							// add the time zone offset to the rd to convert to UTC
15653 							if (!this.tz) {
15654 								this.tz = new TimeZone({id: this.timezone});
15655 							}
15656 							// getOffsetMillis requires that this.year, this.rd, and this.dst 
15657 							// are set in order to figure out which time zone rules apply and 
15658 							// what the offset is at that point in the year
15659 							this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
15660 							if (this.offset !== 0) {
15661 								this.rd = this.newRd({
15662 									cal: this.cal,
15663 									rd: this.rd.getRataDie() - this.offset
15664 								});
15665 								this._calcLeap();
15666 							} else {
15667 								// re-use the derived properties from the RD calculations
15668 								this.leapMonth = this.rd.leapMonth;
15669 								this.priorLeapMonth = this.rd.priorLeapMonth;
15670 								this.leapYear = this.rd.leapYear;
15671 							}
15672 						}
15673 						
15674 						if (!this.rd) {
15675 							this.rd = this.newRd(JSUtils.merge(params || {}, {
15676 								cal: this.cal
15677 							}));
15678 							this._calcDateComponents();
15679 						}
15680 						
15681 						if (params && typeof(params.onLoad) === 'function') {
15682 							params.onLoad(this);
15683 						}
15684 					})
15685 				});
15686 			} else {
15687 				if (!this.rd) {
15688 					this.rd = this.newRd(JSUtils.merge(params || {}, {
15689 						cal: this.cal
15690 					}));
15691 					this._calcDateComponents();
15692 				}
15693 				
15694 				if (params && typeof(params.onLoad) === 'function') {
15695 					params.onLoad(this);
15696 				}
15697 			}
15698 		})
15699 	});
15700 
15701 };
15702 
15703 HanDate.prototype = new IDate({noinstance: true});
15704 HanDate.prototype.parent = IDate;
15705 HanDate.prototype.constructor = HanDate;
15706 
15707 /**
15708  * Return a new RD for this date type using the given params.
15709  * @protected
15710  * @param {Object=} params the parameters used to create this rata die instance
15711  * @returns {RataDie} the new RD instance for the given params
15712  */
15713 HanDate.prototype.newRd = function (params) {
15714 	return new HanRataDie(params);
15715 };
15716 
15717 /**
15718  * Return the year for the given RD
15719  * @protected
15720  * @param {number} rd RD to calculate from 
15721  * @returns {number} the year for the RD
15722  */
15723 HanDate.prototype._calcYear = function(rd) {
15724 	var gregdate = new GregorianDate({
15725 		rd: rd,
15726 		timezone: this.timezone
15727 	});
15728 	var hanyear = gregdate.year + 2697;
15729 	var newYears = this.cal.newYears(hanyear);
15730 	return hanyear - ((rd + RataDie.gregorianEpoch < newYears) ? 1 : 0);
15731 };
15732 
15733 /** 
15734  * @private 
15735  * Calculate the leap year and months from the RD.
15736  */
15737 HanDate.prototype._calcLeap = function() {
15738 	var jd = this.rd.getRataDie() + RataDie.gregorianEpoch;
15739 	
15740 	var calc = HanCal._leapYearCalc(this.year);
15741 	var m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
15742 	this.leapYear = Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12;
15743 	
15744 	var newYears = (this.leapYear &&
15745 		(HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2))) ?
15746 				HanCal._newMoonOnOrAfter(m2+1) : m2;
15747 	
15748 	var m = HanCal._newMoonBefore(jd + 1);
15749 	this.priorLeapMonth = HanRataDie._priorLeapMonth(newYears, HanCal._newMoonBefore(m));
15750 	this.leapMonth = (this.leapYear && HanCal._noMajorST(m) && !this.priorLeapMonth);
15751 };
15752 
15753 /**
15754  * @private
15755  * Calculate date components for the given RD date.
15756  */
15757 HanDate.prototype._calcDateComponents = function () {
15758 	var remainder,
15759 		jd = this.rd.getRataDie() + RataDie.gregorianEpoch;
15760 
15761 	// console.log("HanDate._calcDateComponents: calculating for jd " + jd);
15762 
15763 	if (typeof(this.offset) === "undefined") {
15764 		// now offset the jd by the time zone, then recalculate in case we were 
15765 		// near the year boundary
15766 		if (!this.tz) {
15767 			this.tz = new TimeZone({id: this.timezone});
15768 		}
15769 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
15770 	}
15771 	
15772 	if (this.offset !== 0) {
15773 		jd += this.offset;
15774 	}
15775 
15776 	// use the Gregorian calendar objects as a convenient way to short-cut some
15777 	// of the date calculations
15778 	
15779 	var gregyear = GregorianDate._calcYear(this.rd.getRataDie());
15780 	this.year = gregyear + 2697;
15781 	var calc = HanCal._leapYearCalc(this.year);
15782 	var m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
15783 	this.leapYear = Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12;
15784 	var newYears = (this.leapYear &&
15785 		(HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2))) ?
15786 				HanCal._newMoonOnOrAfter(m2+1) : m2;
15787 	
15788 	// See if it's between Jan 1 and the Chinese new years of that Gregorian year. If
15789 	// so, then the Han year is actually the previous one
15790 	if (jd < newYears) {
15791 		this.year--;
15792 		calc = HanCal._leapYearCalc(this.year);
15793 		m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
15794 		this.leapYear = Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12;
15795 		newYears = (this.leapYear &&
15796 			(HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2))) ?
15797 					HanCal._newMoonOnOrAfter(m2+1) : m2;
15798 	}
15799 	// month is elapsed month, not the month number + leap month boolean
15800 	var m = HanCal._newMoonBefore(jd + 1);
15801 	this.month = Math.round((m - calc.m1) / 29.530588853000001);
15802 	
15803 	this.priorLeapMonth = HanRataDie._priorLeapMonth(newYears, HanCal._newMoonBefore(m));
15804 	this.leapMonth = (this.leapYear && HanCal._noMajorST(m) && !this.priorLeapMonth);
15805 	
15806 	this.cycle = Math.floor((this.year - 1) / 60);
15807 	this.cycleYear = MathUtils.amod(this.year, 60);
15808 	this.day = Astro._floorToJD(jd) - m + 1;
15809 
15810 	/*
15811 	console.log("HanDate._calcDateComponents: year is " + this.year);
15812 	console.log("HanDate._calcDateComponents: isLeapYear is " + this.leapYear);
15813 	console.log("HanDate._calcDateComponents: cycle is " + this.cycle);
15814 	console.log("HanDate._calcDateComponents: cycleYear is " + this.cycleYear);
15815 	console.log("HanDate._calcDateComponents: month is " + this.month);
15816 	console.log("HanDate._calcDateComponents: isLeapMonth is " + this.leapMonth);
15817 	console.log("HanDate._calcDateComponents: day is " + this.day);
15818 	*/
15819 
15820 	// floor to the start of the julian day
15821 	remainder = jd - Astro._floorToJD(jd);
15822 	
15823 	// console.log("HanDate._calcDateComponents: time remainder is " + remainder);
15824 	
15825 	// now convert to milliseconds for the rest of the calculation
15826 	remainder = Math.round(remainder * 86400000);
15827 	
15828 	this.hour = Math.floor(remainder/3600000);
15829 	remainder -= this.hour * 3600000;
15830 	
15831 	this.minute = Math.floor(remainder/60000);
15832 	remainder -= this.minute * 60000;
15833 	
15834 	this.second = Math.floor(remainder/1000);
15835 	remainder -= this.second * 1000;
15836 	
15837 	this.millisecond = remainder;
15838 };
15839 
15840 /**
15841  * Return the year within the Chinese cycle of this date. Cycles are 60 
15842  * years long, and the value returned from this method is the number of the year 
15843  * within this cycle. The year returned from getYear() is the total elapsed 
15844  * years since the beginning of the Chinese epoch and does not include 
15845  * the cycles. 
15846  * 
15847  * @return {number} the year within the current Chinese cycle
15848  */
15849 HanDate.prototype.getCycleYears = function() {
15850 	return this.cycleYear;
15851 };
15852 
15853 /**
15854  * Return the Chinese cycle number of this date. Cycles are 60 years long,
15855  * and the value returned from getCycleYear() is the number of the year 
15856  * within this cycle. The year returned from getYear() is the total elapsed 
15857  * years since the beginning of the Chinese epoch and does not include 
15858  * the cycles. 
15859  * 
15860  * @return {number} the current Chinese cycle
15861  */
15862 HanDate.prototype.getCycles = function() {
15863 	return this.cycle;
15864 };
15865 
15866 /**
15867  * Return whether the year of this date is a leap year in the Chinese Han 
15868  * calendar. 
15869  * 
15870  * @return {boolean} true if the year of this date is a leap year in the 
15871  * Chinese Han calendar. 
15872  */
15873 HanDate.prototype.isLeapYear = function() {
15874 	return this.leapYear;
15875 };
15876 
15877 /**
15878  * Return whether the month of this date is a leap month in the Chinese Han 
15879  * calendar.
15880  * 
15881  * @return {boolean} true if the month of this date is a leap month in the 
15882  * Chinese Han calendar.
15883  */
15884 HanDate.prototype.isLeapMonth = function() {
15885 	return this.leapMonth;
15886 };
15887 
15888 /**
15889  * Return the day of the week of this date. The day of the week is encoded
15890  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
15891  * 
15892  * @return {number} the day of the week
15893  */
15894 HanDate.prototype.getDayOfWeek = function() {
15895 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
15896 	return MathUtils.mod(rd, 7);
15897 };
15898 
15899 /**
15900  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 
15901  * 365, regardless of months or weeks, etc. That is, Farvardin 1st is day 1, and 
15902  * December 31st is 365 in regular years, or 366 in leap years.
15903  * @return {number} the ordinal day of the year
15904  */
15905 HanDate.prototype.getDayOfYear = function() {
15906 	var newYears = this.cal.newYears(this.year);
15907 	var priorNewMoon = HanCal._newMoonOnOrAfter(newYears + (this.month -1) * 29);
15908 	return priorNewMoon - newYears + this.day;
15909 };
15910 
15911 /**
15912  * Return the era for this date as a number. The value for the era for Han 
15913  * calendars is -1 for "before the han era" (BP) and 1 for "the han era" (anno 
15914  * persico or AP). 
15915  * BP dates are any date before Farvardin 1, 1 AP. In the proleptic Han calendar, 
15916  * there is a year 0, so any years that are negative or zero are BP.
15917  * @return {number} 1 if this date is in the common era, -1 if it is before the 
15918  * common era 
15919  */
15920 HanDate.prototype.getEra = function() {
15921 	return (this.year < 1) ? -1 : 1;
15922 };
15923 
15924 /**
15925  * Return the name of the calendar that governs this date.
15926  * 
15927  * @return {string} a string giving the name of the calendar
15928  */
15929 HanDate.prototype.getCalendar = function() {
15930 	return "han";
15931 };
15932 
15933 // register with the factory method
15934 IDate._constructors["han"] = HanDate;
15935 
15936 
15937 /*< EthiopicCal.js */
15938 /*
15939  * ethiopic.js - Represent a Ethiopic calendar object.
15940  * 
15941  * Copyright © 2015, JEDLSoft
15942  *
15943  * Licensed under the Apache License, Version 2.0 (the "License");
15944  * you may not use this file except in compliance with the License.
15945  * You may obtain a copy of the License at
15946  *
15947  *     http://www.apache.org/licenses/LICENSE-2.0
15948  *
15949  * Unless required by applicable law or agreed to in writing, software
15950  * distributed under the License is distributed on an "AS IS" BASIS,
15951  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15952  *
15953  * See the License for the specific language governing permissions and
15954  * limitations under the License.
15955  */
15956 
15957 /* !depends ilib.js Calendar.js Utils.js MathUtils.js */
15958 
15959 
15960 
15961 /**
15962  * @class
15963  * Construct a new Ethiopic calendar object. This class encodes information about
15964  * a Ethiopic calendar.<p>
15965  * 
15966  * 
15967  * @constructor
15968  * @extends Calendar
15969  */
15970 var EthiopicCal = function() {
15971 	this.type = "ethiopic";
15972 };
15973 
15974 /**
15975  * Return the number of months in the given year. The number of months in a year varies
15976  * for lunar calendars because in some years, an extra month is needed to extend the 
15977  * days in a year to an entire solar year. The month is represented as a 1-based number
15978  * where 1=Maskaram, 2=Teqemt, etc. until 13=Paguemen.
15979  * 
15980  * @param {number} year a year for which the number of months is sought
15981  */
15982 EthiopicCal.prototype.getNumMonths = function(year) {
15983 	return 13;
15984 };
15985 
15986 /**
15987  * Return the number of days in a particular month in a particular year. This function
15988  * can return a different number for a month depending on the year because of things
15989  * like leap years.
15990  * 
15991  * @param {number|string} month the month for which the length is sought
15992  * @param {number} year the year within which that month can be found
15993  * @return {number} the number of days within the given month in the given year
15994  */
15995 EthiopicCal.prototype.getMonLength = function(month, year) {
15996 	var m = month;
15997 	switch (typeof(m)) {
15998         case "string": 
15999             m = parseInt(m, 10); 
16000             break;
16001         case "function":
16002         case "object":
16003         case "undefined":
16004             return 30;
16005             break;
16006     }    
16007 	if (m < 13) {
16008 		return 30;
16009 	} else {
16010 		return this.isLeapYear(year) ? 6 : 5;
16011 	}
16012 };
16013 
16014 /**
16015  * Return true if the given year is a leap year in the Ethiopic calendar.
16016  * The year parameter may be given as a number, or as a JulDate object.
16017  * @param {number|EthiopicDate|string} year the year for which the leap year information is being sought
16018  * @return {boolean} true if the given year is a leap year
16019  */
16020 EthiopicCal.prototype.isLeapYear = function(year) {
16021 	var y = year;
16022 	 switch (typeof(y)) {
16023         case "string":
16024             y = parseInt(y, 10);
16025             break;
16026         case "object":
16027             if (typeof(y.year) !== "number") { // in case it is an ilib.Date object
16028                 return false;
16029             }
16030             y = y.year;
16031             break;
16032         case "function":
16033         case "undefined":
16034             return false;
16035             break;
16036     }
16037 	return MathUtils.mod(y, 4) === 3;
16038 };
16039 
16040 /**
16041  * Return the type of this calendar.
16042  * 
16043  * @return {string} the name of the type of this calendar 
16044  */
16045 EthiopicCal.prototype.getType = function() {
16046 	return this.type;
16047 };
16048 
16049 /**
16050  * Return a date instance for this calendar type using the given
16051  * options.
16052  * @param {Object} options options controlling the construction of 
16053  * the date instance
16054  * @return {IDate} a date appropriate for this calendar type
16055  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
16056  */
16057 EthiopicCal.prototype.newDateInstance = function (options) {
16058 		return new EthiopicDate(options);
16059 };
16060 
16061 /* register this calendar for the factory method */
16062 Calendar._constructors["ethiopic"] = EthiopicCal;
16063 
16064 
16065 /*< EthiopicRataDie.js */
16066 /*
16067  * EthiopicRataDie.js - Represent an RD date in the Ethiopic calendar
16068  * 
16069  * Copyright © 2015, JEDLSoft
16070  *
16071  * Licensed under the Apache License, Version 2.0 (the "License");
16072  * you may not use this file except in compliance with the License.
16073  * You may obtain a copy of the License at
16074  *
16075  *     http://www.apache.org/licenses/LICENSE-2.0
16076  *
16077  * Unless required by applicable law or agreed to in writing, software
16078  * distributed under the License is distributed on an "AS IS" BASIS,
16079  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16080  *
16081  * See the License for the specific language governing permissions and
16082  * limitations under the License.
16083  */
16084 
16085 /* !depends 
16086 ilib.js
16087 EthiopicCal.js
16088 RataDie.js
16089 */
16090 
16091 
16092 /**
16093  * @class
16094  * Construct a new Ethiopic RD date number object. The constructor parameters can 
16095  * contain any of the following properties:
16096  * 
16097  * <ul>
16098  * <li><i>unixtime<i> - sets the time of this instance according to the given 
16099  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
16100  * 
16101  * <li><i>julianday</i> - sets the time of this instance according to the given
16102  * Julian Day instance or the Julian Day given as a float
16103  * 
16104  * <li><i>year</i> - any integer, including 0
16105  * 
16106  * <li><i>month</i> - 1 to 12, where 1 means Maskaram, 2 means Teqemt, etc., and 13 means Paguemen
16107  * 
16108  * <li><i>day</i> - 1 to 30
16109  * 
16110  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
16111  * is always done with an unambiguous 24 hour representation
16112  * 
16113  * <li><i>minute</i> - 0 to 59
16114  * 
16115  * <li><i>second</i> - 0 to 59
16116  * 
16117  * <li><i>millisecond</i> - 0 to 999
16118  * 
16119  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
16120  * </ul>
16121  *
16122  * If the constructor is called with another Ethiopic date instance instead of
16123  * a parameter block, the other instance acts as a parameter block and its
16124  * settings are copied into the current instance.<p>
16125  * 
16126  * If the constructor is called with no arguments at all or if none of the 
16127  * properties listed above are present, then the RD is calculate based on 
16128  * the current date at the time of instantiation. <p>
16129  * 
16130  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
16131  * specified in the params, it is assumed that they have the smallest possible
16132  * value in the range for the property (zero or one).<p>
16133  * 
16134  * 
16135  * @private
16136  * @constructor
16137  * @extends RataDie
16138  * @param {Object=} params parameters that govern the settings and behaviour of this Ethiopic RD date
16139  */
16140 var EthiopicRataDie = function(params) {
16141 	this.cal = params && params.cal || new EthiopicCal();
16142 	this.rd = NaN;
16143 	RataDie.call(this, params);
16144 };
16145 
16146 EthiopicRataDie.prototype = new RataDie();
16147 EthiopicRataDie.prototype.parent = RataDie;
16148 EthiopicRataDie.prototype.constructor = EthiopicRataDie;
16149 
16150 /**
16151  * The difference between the zero Julian day and the first Ethiopic date
16152  * of Friday, August 29, 8 CE Julian at 6:00am UTC.<p> 
16153  * 
16154  * See <a href="http://us.wow.com/wiki/Time_in_Ethiopia?s_chn=90&s_pt=aolsem&v_t=aolsem"
16155  * Time in Ethiopia</a> for information about how time is handled in Ethiopia.
16156  * 
16157  * @protected
16158  * @type number
16159  */
16160 EthiopicRataDie.prototype.epoch = 1724219.75;
16161 
16162 /**
16163  * Calculate the Rata Die (fixed day) number of the given date from the
16164  * date components.
16165  * 
16166  * @protected
16167  * @param {Object} date the date components to calculate the RD from
16168  */
16169 EthiopicRataDie.prototype._setDateComponents = function(date) {
16170 	var year = date.year;
16171 	var years = 365 * (year - 1) + Math.floor(year/4);
16172 	var dayInYear = (date.month-1) * 30 + date.day;
16173 	var rdtime = (date.hour * 3600000 +
16174 		date.minute * 60000 +
16175 		date.second * 1000 +
16176 		date.millisecond) / 
16177 		86400000;
16178 	
16179 	/*
16180 	console.log("calcRataDie: converting " +  JSON.stringify(parts));
16181 	console.log("getRataDie: year is " +  years);
16182 	console.log("getRataDie: day in year is " +  dayInYear);
16183 	console.log("getRataDie: rdtime is " +  rdtime);
16184 	console.log("getRataDie: rd is " +  (years + dayInYear + rdtime));
16185 	*/
16186 	
16187 	this.rd = years + dayInYear + rdtime;
16188 };
16189 
16190 
16191 
16192 /*< EthiopicDate.js */
16193 /*
16194  * EthiopicDate.js - Represent a date in the Ethiopic calendar
16195  * 
16196  * Copyright © 2015, JEDLSoft
16197  *
16198  * Licensed under the Apache License, Version 2.0 (the "License");
16199  * you may not use this file except in compliance with the License.
16200  * You may obtain a copy of the License at
16201  *
16202  *     http://www.apache.org/licenses/LICENSE-2.0
16203  *
16204  * Unless required by applicable law or agreed to in writing, software
16205  * distributed under the License is distributed on an "AS IS" BASIS,
16206  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16207  *
16208  * See the License for the specific language governing permissions and
16209  * limitations under the License.
16210  */
16211 
16212 /* !depends 
16213 ilib.js
16214 IDate.js 
16215 EthiopicCal.js 
16216 MathUtils.js
16217 Locale.js
16218 LocaleInfo.js 
16219 TimeZone.js
16220 EthiopicRataDie.js
16221 */
16222 
16223 
16224 
16225 /**
16226  * @class
16227  * Construct a new date object for the Ethiopic Calendar. The constructor can be called
16228  * with a parameter object that contains any of the following properties:
16229  * 
16230  * <ul>
16231  * <li><i>unixtime<i> - sets the time of this instance according to the given 
16232  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970 (Gregorian).
16233  * <li><i>julianday</i> - the Julian Day to set into this date
16234  * <li><i>year</i> - any integer
16235  * <li><i>month</i> - 1 to 13, where 1 means Maskaram, 2 means Teqemt, etc., and 13 means Paguemen
16236  * <li><i>day</i> - 1 to 30
16237  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
16238  * is always done with an unambiguous 24 hour representation
16239  * <li><i>minute</i> - 0 to 59
16240  * <li><i>second</i> - 0 to 59
16241  * <li><i>millisecond<i> - 0 to 999
16242  * <li><i>locale</i> - the TimeZone instance or time zone name as a string 
16243  * of this ethiopic date. The date/time is kept in the local time. The time zone
16244  * is used later if this date is formatted according to a different time zone and
16245  * the difference has to be calculated, or when the date format has a time zone
16246  * component in it.
16247  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 
16248  * given, it can be inferred from this locale. For locales that span multiple
16249  * time zones, the one with the largest population is chosen as the one that 
16250  * represents the locale. 
16251  * 
16252  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
16253  * </ul>
16254  *  
16255  * If called with another Ethiopic date argument, the date components of the given
16256  * date are copied into the current one.<p>
16257  * 
16258  * If the constructor is called with no arguments at all or if none of the 
16259  * properties listed above 
16260  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
16261  * components are 
16262  * filled in with the current date at the time of instantiation. Note that if
16263  * you do not give the time zone when defaulting to the current time and the 
16264  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
16265  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
16266  * Mean Time").<p>
16267  * 
16268  * 
16269  * @constructor
16270  * @extends IDate
16271  * @param {Object=} params parameters that govern the settings and behaviour of this Ethiopic date
16272  */
16273 var EthiopicDate = function(params) {
16274 	this.cal = new EthiopicCal();
16275 	
16276 	if (params) {
16277 		if (typeof(params.noinstance) === 'boolean' && params.noinstance) {
16278 			// for doing inheritance, so don't need to fill in the data. The inheriting class only wants the methods.
16279 			return;
16280 		}
16281 		if (params.locale) {
16282 			this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
16283 			var li = new LocaleInfo(this.locale);
16284 			this.timezone = li.getTimeZone(); 
16285 		}
16286 		if (params.timezone) {
16287 			this.timezone = params.timezone;
16288 		}
16289 		
16290 		if (params.year || params.month || params.day || params.hour ||
16291 				params.minute || params.second || params.millisecond ) {
16292 			/**
16293 			 * Year in the Ethiopic calendar.
16294 			 * @type number
16295 			 */
16296 			this.year = parseInt(params.year, 10) || 0;
16297 			/**
16298 			 * The month number, ranging from 1 (Maskaram) to 13 (Paguemen).
16299 			 * @type number
16300 			 */
16301 			this.month = parseInt(params.month, 10) || 1;
16302 			/**
16303 			 * The day of the month. This ranges from 1 to 30.
16304 			 * @type number
16305 			 */
16306 			this.day = parseInt(params.day, 10) || 1;
16307 			/**
16308 			 * The hour of the day. This can be a number from 0 to 23, as times are
16309 			 * stored unambiguously in the 24-hour clock.
16310 			 * @type number
16311 			 */
16312 			this.hour = parseInt(params.hour, 10) || 0;
16313 			/**
16314 			 * The minute of the hours. Ranges from 0 to 59.
16315 			 * @type number
16316 			 */
16317 			this.minute = parseInt(params.minute, 10) || 0;
16318 			/**
16319 			 * The second of the minute. Ranges from 0 to 59.
16320 			 * @type number
16321 			 */
16322 			this.second = parseInt(params.second, 10) || 0;
16323 			/**
16324 			 * The millisecond of the second. Ranges from 0 to 999.
16325 			 * @type number
16326 			 */
16327 			this.millisecond = parseInt(params.millisecond, 10) || 0;
16328 			
16329 			/**
16330 			 * The day of the year. Ranges from 1 to 366.
16331 			 * @type number
16332 			 */
16333 			this.dayOfYear = parseInt(params.dayOfYear, 10);
16334 			
16335 			if (typeof(params.dst) === 'boolean') {
16336 				this.dst = params.dst;
16337 			}
16338 			
16339 			this.rd = this.newRd(this);
16340 			
16341 			// add the time zone offset to the rd to convert to UTC
16342 			if (!this.tz) {
16343 				this.tz = new TimeZone({id: this.timezone});
16344 			}
16345 			// getOffsetMillis requires that this.year, this.rd, and this.dst 
16346 			// are set in order to figure out which time zone rules apply and 
16347 			// what the offset is at that point in the year
16348 			this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
16349 			if (this.offset !== 0) {
16350 				this.rd = this.newRd({
16351 					rd: this.rd.getRataDie() - this.offset
16352 				});
16353 			}
16354 		}
16355 	}
16356 	
16357 	if (!this.rd) {
16358 		this.rd = this.newRd(params);
16359 		this._calcDateComponents();
16360 	}
16361 };
16362 
16363 EthiopicDate.prototype = new IDate({ noinstance: true });
16364 EthiopicDate.prototype.parent = IDate;
16365 EthiopicDate.prototype.constructor = EthiopicDate;
16366 
16367 /**
16368  * Return a new RD for this date type using the given params.
16369  * @protected
16370  * @param {Object=} params the parameters used to create this rata die instance
16371  * @returns {RataDie} the new RD instance for the given params
16372  */
16373 EthiopicDate.prototype.newRd = function (params) {
16374 	return new EthiopicRataDie(params);
16375 };
16376 
16377 /**
16378  * Return the year for the given RD
16379  * @protected
16380  * @param {number} rd RD to calculate from 
16381  * @returns {number} the year for the RD
16382  */
16383 EthiopicDate.prototype._calcYear = function(rd) {
16384 	var year = Math.floor((4*(Math.floor(rd)-1) + 1463)/1461);
16385 	
16386 	return year;
16387 };
16388 
16389 /**
16390  * Calculate date components for the given RD date.
16391  * @protected
16392  */
16393 EthiopicDate.prototype._calcDateComponents = function () {
16394 	var remainder,
16395 		cumulative,
16396 		rd = this.rd.getRataDie();
16397 	
16398 	this.year = this._calcYear(rd);
16399 
16400 	if (typeof(this.offset) === "undefined") {
16401 		this.year = this._calcYear(rd);
16402 		
16403 		// now offset the RD by the time zone, then recalculate in case we were 
16404 		// near the year boundary
16405 		if (!this.tz) {
16406 			this.tz = new TimeZone({id: this.timezone});
16407 		}
16408 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
16409 	}
16410 
16411 	if (this.offset !== 0) {
16412 		rd += this.offset;
16413 		this.year = this._calcYear(rd);
16414 	}
16415 	
16416 	var jan1 = this.newRd({
16417 		year: this.year,
16418 		month: 1,
16419 		day: 1,
16420 		hour: 0,
16421 		minute: 0,
16422 		second: 0,
16423 		millisecond: 0
16424 	});
16425 	remainder = rd + 1 - jan1.getRataDie();
16426 	
16427 	this.month = Math.floor((remainder-1)/30) + 1;
16428 	remainder = remainder - (this.month-1) * 30;
16429 	
16430 	this.day = Math.floor(remainder);
16431 	remainder -= this.day;
16432 	// now convert to milliseconds for the rest of the calculation
16433 	remainder = Math.round(remainder * 86400000);
16434 	
16435 	this.hour = Math.floor(remainder/3600000);
16436 	remainder -= this.hour * 3600000;
16437 	
16438 	this.minute = Math.floor(remainder/60000);
16439 	remainder -= this.minute * 60000;
16440 	
16441 	this.second = Math.floor(remainder/1000);
16442 	remainder -= this.second * 1000;
16443 	
16444 	this.millisecond = remainder;
16445 };
16446 
16447 /**
16448  * Return the day of the week of this date. The day of the week is encoded
16449  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
16450  * 
16451  * @return {number} the day of the week
16452  */
16453 EthiopicDate.prototype.getDayOfWeek = function() {
16454 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
16455 	return MathUtils.mod(rd-4, 7);
16456 };
16457 
16458 /**
16459  * Return the name of the calendar that governs this date.
16460  * 
16461  * @return {string} a string giving the name of the calendar
16462  */
16463 EthiopicDate.prototype.getCalendar = function() {
16464 	return "ethiopic";
16465 };
16466 
16467 //register with the factory method
16468 IDate._constructors["ethiopic"] = EthiopicDate;
16469 
16470 
16471 
16472 /*< CopticCal.js */
16473 /*
16474  * coptic.js - Represent a Coptic calendar object.
16475  * 
16476  * Copyright © 2015, JEDLSoft
16477  *
16478  * Licensed under the Apache License, Version 2.0 (the "License");
16479  * you may not use this file except in compliance with the License.
16480  * You may obtain a copy of the License at
16481  *
16482  *     http://www.apache.org/licenses/LICENSE-2.0
16483  *
16484  * Unless required by applicable law or agreed to in writing, software
16485  * distributed under the License is distributed on an "AS IS" BASIS,
16486  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16487  *
16488  * See the License for the specific language governing permissions and
16489  * limitations under the License.
16490  */
16491 
16492 
16493 /* !depends ilib.js Calendar.js Locale.js Utils.js EthiopicCal.js */
16494 
16495 
16496 /**
16497  * @class
16498  * Construct a new Coptic calendar object. This class encodes information about
16499  * a Coptic calendar.<p>
16500  * 
16501  * 
16502  * @constructor
16503  * @extends EthiopicCal
16504  */
16505 var CopticCal = function() {
16506 	this.type = "coptic";
16507 };
16508 
16509 CopticCal.prototype = new EthiopicCal();
16510 CopticCal.prototype.parent = EthiopicCal;
16511 CopticCal.prototype.constructor = CopticCal;
16512 
16513 /**
16514  * Return a date instance for this calendar type using the given
16515  * options.
16516  * @param {Object} options options controlling the construction of 
16517  * the date instance
16518  * @return {IDate} a date appropriate for this calendar type
16519  * @deprecated Since 11.0.5. Use DateFactory({calendar: cal.getType(), ...}) instead
16520  */
16521 CopticCal.prototype.newDateInstance = function (options) {
16522 		return new CopticDate(options);
16523 };
16524 
16525 /* register this calendar for the factory method */
16526 Calendar._constructors["coptic"] = CopticCal;
16527 
16528 
16529 /*< CopticRataDie.js */
16530 /*
16531  * CopticRataDie.js - Represent an RD date in the Coptic calendar
16532  * 
16533  * Copyright © 2015, JEDLSoft
16534  *
16535  * Licensed under the Apache License, Version 2.0 (the "License");
16536  * you may not use this file except in compliance with the License.
16537  * You may obtain a copy of the License at
16538  *
16539  *     http://www.apache.org/licenses/LICENSE-2.0
16540  *
16541  * Unless required by applicable law or agreed to in writing, software
16542  * distributed under the License is distributed on an "AS IS" BASIS,
16543  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16544  *
16545  * See the License for the specific language governing permissions and
16546  * limitations under the License.
16547  */
16548 
16549 /* !depends 
16550 ilib.js
16551 CopticCal.js 
16552 JSUtils.js
16553 EthiopicRataDie.js
16554 */
16555 
16556 
16557 /**
16558  * @class
16559  * Construct a new Coptic RD date number object. The constructor parameters can 
16560  * contain any of the following properties:
16561  * 
16562  * <ul>
16563  * <li><i>unixtime<i> - sets the time of this instance according to the given 
16564  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
16565  * 
16566  * <li><i>julianday</i> - sets the time of this instance according to the given
16567  * Julian Day instance or the Julian Day given as a float
16568  * 
16569  * <li><i>year</i> - any integer, including 0
16570  * 
16571  * <li><i>month</i> - 1 to 13, where 1 means Thoout, 2 means Paope, etc., and 13 means Epagomene
16572  * 
16573  * <li><i>day</i> - 1 to 30
16574  * 
16575  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
16576  * is always done with an unambiguous 24 hour representation
16577  * 
16578  * <li><i>minute</i> - 0 to 59
16579  * 
16580  * <li><i>second</i> - 0 to 59
16581  * 
16582  * <li><i>millisecond</i> - 0 to 999
16583  * 
16584  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
16585  * </ul>
16586  *
16587  * If the constructor is called with another Coptic date instance instead of
16588  * a parameter block, the other instance acts as a parameter block and its
16589  * settings are copied into the current instance.<p>
16590  * 
16591  * If the constructor is called with no arguments at all or if none of the 
16592  * properties listed above are present, then the RD is calculate based on 
16593  * the current date at the time of instantiation. <p>
16594  * 
16595  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
16596  * specified in the params, it is assumed that they have the smallest possible
16597  * value in the range for the property (zero or one).<p>
16598  * 
16599  * 
16600  * @private
16601  * @constructor
16602  * @extends EthiopicRataDie
16603  * @param {Object=} params parameters that govern the settings and behaviour of this Coptic RD date
16604  */
16605 var CopticRataDie = function(params) {
16606 	this.cal = params && params.cal || new CopticCal();
16607 	this.rd = NaN;
16608 	/**
16609 	 * The difference between the zero Julian day and the first Coptic date
16610 	 * of Friday, August 29, 284 CE Julian at 7:00am UTC. 
16611 	 * @private
16612 	 * @type number
16613 	 */
16614 	this.epoch = 1825028.5;
16615 
16616 	var tmp = {};
16617 	if (params) {
16618 		JSUtils.shallowCopy(params, tmp);
16619 	}
16620 	tmp.cal = this.cal; // override the cal parameter that may be passed in
16621 	EthiopicRataDie.call(this, tmp);
16622 };
16623 
16624 CopticRataDie.prototype = new EthiopicRataDie();
16625 CopticRataDie.prototype.parent = EthiopicRataDie;
16626 CopticRataDie.prototype.constructor = CopticRataDie;
16627 
16628 
16629 /*< CopticDate.js */
16630 /*
16631  * CopticDate.js - Represent a date in the Coptic calendar
16632  * 
16633  * Copyright © 2015, JEDLSoft
16634  *
16635  * Licensed under the Apache License, Version 2.0 (the "License");
16636  * you may not use this file except in compliance with the License.
16637  * You may obtain a copy of the License at
16638  *
16639  *     http://www.apache.org/licenses/LICENSE-2.0
16640  *
16641  * Unless required by applicable law or agreed to in writing, software
16642  * distributed under the License is distributed on an "AS IS" BASIS,
16643  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16644  *
16645  * See the License for the specific language governing permissions and
16646  * limitations under the License.
16647  */
16648 
16649 /* !depends 
16650 ilib.js
16651 IDate.js 
16652 CopticCal.js 
16653 MathUtils.js
16654 JSUtils.js
16655 Locale.js
16656 LocaleInfo.js 
16657 TimeZone.js
16658 EthiopicDate.js
16659 CopticRataDie.js
16660 */
16661 
16662 
16663 
16664 
16665 /**
16666  * @class
16667  * Construct a new date object for the Coptic Calendar. The constructor can be called
16668  * with a parameter object that contains any of the following properties:
16669  * 
16670  * <ul>
16671  * <li><i>unixtime<i> - sets the time of this instance according to the given 
16672  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970 (Gregorian).
16673  * <li><i>julianday</i> - the Julian Day to set into this date
16674  * <li><i>year</i> - any integer
16675  * <li><i>month</i> - 1 to 13, where 1 means Thoout, 2 means Paope, etc., and 13 means Epagomene
16676  * <li><i>day</i> - 1 to 30
16677  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
16678  * is always done with an unambiguous 24 hour representation
16679  * <li><i>minute</i> - 0 to 59
16680  * <li><i>second</i> - 0 to 59
16681  * <li><i>millisecond<i> - 0 to 999
16682  * <li><i>locale</i> - the TimeZone instance or time zone name as a string 
16683  * of this coptic date. The date/time is kept in the local time. The time zone
16684  * is used later if this date is formatted according to a different time zone and
16685  * the difference has to be calculated, or when the date format has a time zone
16686  * component in it.
16687  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 
16688  * given, it can be inferred from this locale. For locales that span multiple
16689  * time zones, the one with the largest population is chosen as the one that 
16690  * represents the locale. 
16691  * 
16692  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
16693  * </ul>
16694  *  
16695  * If called with another Coptic date argument, the date components of the given
16696  * date are copied into the current one.<p>
16697  * 
16698  * If the constructor is called with no arguments at all or if none of the 
16699  * properties listed above 
16700  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
16701  * components are 
16702  * filled in with the current date at the time of instantiation. Note that if
16703  * you do not give the time zone when defaulting to the current time and the 
16704  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
16705  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
16706  * Mean Time").<p>
16707  * 
16708  * 
16709  * @constructor
16710  * @extends EthiopicDate
16711  * @param {Object=} params parameters that govern the settings and behaviour of this Coptic date
16712  */
16713 var CopticDate = function(params) {
16714 	this.rd = NaN; // clear these out so that the EthiopicDate constructor can set it
16715 	EthiopicDate.call(this, params);
16716 	this.cal = new CopticCal();
16717 };
16718 
16719 CopticDate.prototype = new EthiopicDate({noinstance: true});
16720 CopticDate.prototype.parent = EthiopicDate.prototype;
16721 CopticDate.prototype.constructor = CopticDate;
16722 
16723 /**
16724  * Return a new RD for this date type using the given params.
16725  * @protected
16726  * @param {Object=} params the parameters used to create this rata die instance
16727  * @returns {RataDie} the new RD instance for the given params
16728  */
16729 CopticDate.prototype.newRd = function (params) {
16730 	return new CopticRataDie(params);
16731 };
16732 
16733 /**
16734  * Return the day of the week of this date. The day of the week is encoded
16735  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
16736  * 
16737  * @return {number} the day of the week
16738  */
16739 CopticDate.prototype.getDayOfWeek = function() {
16740 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
16741 	return MathUtils.mod(rd-3, 7);
16742 };
16743 
16744 /**
16745  * Return the name of the calendar that governs this date.
16746  * 
16747  * @return {string} a string giving the name of the calendar
16748  */
16749 CopticDate.prototype.getCalendar = function() {
16750 	return "coptic";
16751 };
16752 
16753 //register with the factory method
16754 IDate._constructors["coptic"] = CopticDate;
16755 
16756 
16757 /*< CType.js */
16758 /*
16759  * CType.js - Character type definitions
16760  * 
16761  * Copyright © 2012-2015, JEDLSoft
16762  *
16763  * Licensed under the Apache License, Version 2.0 (the "License");
16764  * you may not use this file except in compliance with the License.
16765  * You may obtain a copy of the License at
16766  *
16767  *     http://www.apache.org/licenses/LICENSE-2.0
16768  *
16769  * Unless required by applicable law or agreed to in writing, software
16770  * distributed under the License is distributed on an "AS IS" BASIS,
16771  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16772  *
16773  * See the License for the specific language governing permissions and
16774  * limitations under the License.
16775  */
16776 
16777 // !depends ilib.js Locale.js SearchUtils.js Utils.js IString.js
16778 
16779 // !data ctype
16780 
16781 
16782 /**
16783  * Provides a set of static routines that return information about characters.
16784  * These routines emulate the C-library ctype functions. The characters must be 
16785  * encoded in utf-16, as no other charsets are currently supported. Only the first
16786  * character of the given string is tested.
16787  * @namespace
16788  */
16789 var CType = {};
16790 
16791 
16792 /**
16793  * Actual implementation for withinRange. Searches the given object for ranges.
16794  * The range names are taken from the Unicode range names in 
16795  * http://www.unicode.org/Public/UNIDATA/extracted/DerivedGeneralCategory.txt
16796  * 
16797  * <ul>
16798  * <li>Cn - Unassigned
16799  * <li>Lu - Uppercase_Letter
16800  * <li>Ll - Lowercase_Letter
16801  * <li>Lt - Titlecase_Letter
16802  * <li>Lm - Modifier_Letter
16803  * <li>Lo - Other_Letter
16804  * <li>Mn - Nonspacing_Mark
16805  * <li>Me - Enclosing_Mark
16806  * <li>Mc - Spacing_Mark
16807  * <li>Nd - Decimal_Number
16808  * <li>Nl - Letter_Number
16809  * <li>No - Other_Number
16810  * <li>Zs - Space_Separator
16811  * <li>Zl - Line_Separator
16812  * <li>Zp - Paragraph_Separator
16813  * <li>Cc - Control
16814  * <li>Cf - Format
16815  * <li>Co - Private_Use
16816  * <li>Cs - Surrogate
16817  * <li>Pd - Dash_Punctuation
16818  * <li>Ps - Open_Punctuation
16819  * <li>Pe - Close_Punctuation
16820  * <li>Pc - Connector_Punctuation
16821  * <li>Po - Other_Punctuation
16822  * <li>Sm - Math_Symbol
16823  * <li>Sc - Currency_Symbol
16824  * <li>Sk - Modifier_Symbol
16825  * <li>So - Other_Symbol
16826  * <li>Pi - Initial_Punctuation
16827  * <li>Pf - Final_Punctuation
16828  * </ul>
16829  * 
16830  * @protected
16831  * @param {number} num code point of the character to examine
16832  * @param {string} rangeName the name of the range to check
16833  * @param {Object} obj object containing the character range data
16834  * @return {boolean} true if the first character is within the named
16835  * range
16836  */
16837 CType._inRange = function(num, rangeName, obj) {
16838 	var range, i;
16839 	if (num < 0 || !rangeName || !obj) {
16840 		return false;
16841 	}
16842 	
16843 	range = obj[rangeName];
16844 	if (!range) {
16845 		return false;
16846 	}
16847 	
16848 	var compare = function(singlerange, target) {
16849 		if (singlerange.length === 1) {
16850 			return singlerange[0] - target;
16851 		} else {
16852 			return target < singlerange[0] ? singlerange[0] - target :
16853 				(target > singlerange[1] ? singlerange[1] - target : 0);
16854 		}
16855 	};
16856 	var result = SearchUtils.bsearch(num, range, compare);
16857 	return result < range.length && compare(range[result], num) === 0;
16858 };
16859 
16860 /**
16861  * Return whether or not the first character is within the named range
16862  * of Unicode characters. The valid list of range names are taken from 
16863  * the Unicode 6.0 spec. Characters in all ranges of Unicode are supported,
16864  * including those supported in Javascript via UTF-16. Currently, this method 
16865  * supports the following range names:
16866  * 
16867  * <ul>
16868  * <li><i>ascii</i> - basic ASCII
16869  * <li><i>latin</i> - Latin, Latin Extended Additional, Latin-1 supplement, Latin Extended-C, Latin Extended-D, Latin Extended-E
16870  * <li><i>armenian</i>
16871  * <li><i>greek</i> - Greek, Greek Extended
16872  * <li><i>cyrillic</i> - Cyrillic, Cyrillic Extended-A, Cyrillic Extended-B, Cyrillic Supplement
16873  * <li><i>georgian</i> - Georgian, Georgian Supplement
16874  * <li><i>glagolitic</i>
16875  * <li><i>gothic</i>
16876  * <li><i>ogham</i>
16877  * <li><i>oldpersian</i>
16878  * <li><i>runic</i>
16879  * <li><i>ipa</i> - IPA, Phonetic Extensions, Phonetic Extensions Supplement
16880  * <li><i>phonetic</i>
16881  * <li><i>modifiertone</i> - Modifier Tone Letters
16882  * <li><i>spacing</i>
16883  * <li><i>diacritics</i>
16884  * <li><i>halfmarks</i> - Combining Half Marks
16885  * <li><i>small</i> - Small Form Variants
16886  * <li><i>bamum</i> - Bamum, Bamum Supplement
16887  * <li><i>ethiopic</i> - Ethiopic, Ethiopic Extended, Ethiopic Extended-A
16888  * <li><i>nko</i>
16889  * <li><i>osmanya</i>
16890  * <li><i>tifinagh</i>
16891  * <li><i>val</i>
16892  * <li><i>arabic</i> - Arabic, Arabic Supplement, Arabic Presentation Forms-A, 
16893  * Arabic Presentation Forms-B, Arabic Mathematical Alphabetic Symbols
16894  * <li><i>carlan</i>
16895  * <li><i>hebrew</i>
16896  * <li><i>mandaic</i>
16897  * <li><i>samaritan</i>
16898  * <li><i>syriac</i>
16899  * <li><i>mongolian</i>
16900  * <li><i>phagspa</i>
16901  * <li><i>tibetan</i>
16902  * <li><i>bengali</i>
16903  * <li><i>devanagari</i> - Devanagari, Devanagari Extended
16904  * <li><i>gujarati</i>
16905  * <li><i>gurmukhi</i>
16906  * <li><i>kannada</i>
16907  * <li><i>lepcha</i>
16908  * <li><i>limbu</i>
16909  * <li><i>malayalam</i>
16910  * <li><i>meetaimayek</i>
16911  * <li><i>olchiki</i>
16912  * <li><i>oriya</i>
16913  * <li><i>saurashtra</i>
16914  * <li><i>sinhala</i>
16915  * <li><i>sylotinagri</i> - Syloti Nagri
16916  * <li><i>tamil</i>
16917  * <li><i>telugu</i>
16918  * <li><i>thaana</i>
16919  * <li><i>vedic</i>
16920  * <li><i>batak</i>
16921  * <li><i>balinese</i>
16922  * <li><i>buginese</i>
16923  * <li><i>cham</i>
16924  * <li><i>javanese</i>
16925  * <li><i>kayahli</i>
16926  * <li><i>khmer</i>
16927  * <li><i>lao</i>
16928  * <li><i>myanmar</i> - Myanmar, Myanmar Extended-A, Myanmar Extended-B
16929  * <li><i>newtailue</i>
16930  * <li><i>rejang</i>
16931  * <li><i>sundanese</i> - Sundanese, Sundanese Supplement
16932  * <li><i>taile</i>
16933  * <li><i>taitham</i>
16934  * <li><i>taiviet</i>
16935  * <li><i>thai</i>
16936  * <li><i>buhld</i>
16937  * <li><i>hanunoo</i>
16938  * <li><i>tagalog</i>
16939  * <li><i>tagbanwa</i>
16940  * <li><i>bopomofo</i> - Bopomofo, Bopomofo Extended
16941  * <li><i>cjk</i> - the CJK unified ideographs (Han), CJK Unified Ideographs
16942  *  Extension A, CJK Unified Ideographs Extension B, CJK Unified Ideographs 
16943  *  Extension C, CJK Unified Ideographs Extension D, Ideographic Description 
16944  *  Characters (=isIdeo())
16945  * <li><i>cjkcompatibility</i> - CJK Compatibility, CJK Compatibility 
16946  * Ideographs, CJK Compatibility Forms, CJK Compatibility Ideographs Supplement
16947  * <li><i>cjkradicals</i> - the CJK radicals, KangXi radicals
16948  * <li><i>hangul</i> - Hangul Jamo, Hangul Syllables, Hangul Jamo Extended-A, 
16949  * Hangul Jamo Extended-B, Hangul Compatibility Jamo
16950  * <li><i>cjkpunct</i> - CJK symbols and punctuation
16951  * <li><i>cjkstrokes</i> - CJK strokes
16952  * <li><i>hiragana</i>
16953  * <li><i>katakana</i> - Katakana, Katakana Phonetic Extensions, Kana Supplement
16954  * <li><i>kanbun</i>
16955  * <li><i>lisu</i>
16956  * <li><i>yi</i> - Yi Syllables, Yi Radicals
16957  * <li><i>cherokee</i>
16958  * <li><i>canadian</i> - Unified Canadian Aboriginal Syllabics, Unified Canadian 
16959  * Aboriginal Syllabics Extended
16960  * <li><i>presentation</i> - Alphabetic presentation forms
16961  * <li><i>vertical</i> - Vertical Forms
16962  * <li><i>width</i> - Halfwidth and Fullwidth Forms
16963  * <li><i>punctuation</i> - General punctuation, Supplemental Punctuation
16964  * <li><i>box</i> - Box Drawing
16965  * <li><i>block</i> - Block Elements
16966  * <li><i>letterlike</i> - Letterlike symbols
16967  * <li><i>mathematical</i> - Mathematical alphanumeric symbols, Miscellaneous 
16968  * Mathematical Symbols-A, Miscellaneous Mathematical Symbols-B
16969  * <li><i>enclosedalpha</i> - Enclosed alphanumerics, Enclosed Alphanumeric Supplement
16970  * <li><i>enclosedcjk</i> - Enclosed CJK letters and months, Enclosed Ideographic Supplement
16971  * <li><i>cjkcompatibility</i> - CJK compatibility
16972  * <li><i>apl</i> - APL symbols
16973  * <li><i>controlpictures</i> - Control pictures
16974  * <li><i>misc</i> - Miscellaneous technical
16975  * <li><i>ocr</i> - Optical character recognition (OCR)
16976  * <li><i>combining</i> - Combining Diacritical Marks, Combining Diacritical Marks 
16977  * for Symbols, Combining Diacritical Marks Supplement, Combining Diacritical Marks Extended
16978  * <li><i>digits</i> - ASCII digits (=isDigit())
16979  * <li><i>indicnumber</i> - Common Indic Number Forms
16980  * <li><i>numbers</i> - Number forms
16981  * <li><i>supersub</i> - Superscripts and Subscripts
16982  * <li><i>arrows</i> - Arrows, Miscellaneous Symbols and Arrows, Supplemental Arrows-A,
16983  * Supplemental Arrows-B, Supplemental Arrows-C
16984  * <li><i>operators</i> - Mathematical operators, supplemental 
16985  * mathematical operators 
16986  * <li><i>geometric</i> - Geometric shapes, Geometric shapes extended
16987  * <li><i>ancient</i> - Ancient symbols
16988  * <li><i>braille</i> - Braille patterns
16989  * <li><i>currency</i> - Currency symbols
16990  * <li><i>dingbats</i>
16991  * <li><i>gamesymbols</i>
16992  * <li><i>yijing</i> - Yijing Hexagram Symbols
16993  * <li><i>specials</i>
16994  * <li><i>variations</i> - Variation Selectors, Variation Selectors Supplement
16995  * <li><i>privateuse</i> - Private Use Area, Supplementary Private Use Area-A, 
16996  * Supplementary Private Use Area-B
16997  * <li><i>supplementarya</i> - Supplementary private use area-A
16998  * <li><i>supplementaryb</i> - Supplementary private use area-B
16999  * <li><i>highsurrogates</i> - High Surrogates, High Private Use Surrogates
17000  * <li><i>lowsurrogates</i>
17001  * <li><i>reserved</i>
17002  * <li><i>noncharacters</i>
17003  * <li><i>copticnumber</i> - coptic epact numbers
17004  * <li><i>oldpermic</i> - old permic
17005  * <li><i>albanian</i> - albanian
17006  * <li><i>lineara</i> - linear a
17007  * <li><i>meroitic</i> - meroitic cursive
17008  * <li><i>oldnortharabian</i> - old north arabian
17009  * <li><i>oldhungarian</i> - Supplementary private use area-A
17010  * <li><i>sorasompeng</i> - sora sompeng
17011  * <li><i>warangciti</i> - warang citi
17012  * <li><i>paucinhau</i> - pau cin hau
17013  * <li><i>bassavah</i> - bassa vah
17014  * <li><i>pahawhhmong</i> - pahawh hmong
17015  * <li><i>shorthandformat</i> - shorthand format controls
17016  * <li><i>suttonsignwriting</i> - sutton signwriting
17017  * <li><i>pictographs</i> - miscellaneous symbols and pictographs, supplemental symbols and pictographs
17018  * <li><i>ornamentaldingbats</i> - ornamental dingbats
17019  * </ul><p>
17020  * 
17021  * 
17022  * @protected
17023  * @param {string|IString|number} ch character or code point to examine
17024  * @param {string} rangeName the name of the range to check
17025  * @return {boolean} true if the first character is within the named
17026  * range
17027  */
17028 CType.withinRange = function(ch, rangeName) {
17029 	if (!rangeName) {
17030 		return false;
17031 	}
17032 	var num;
17033 	switch (typeof(ch)) {
17034 		case 'number':
17035 			num = ch;
17036 			break;
17037 		case 'string':
17038 			num = IString.toCodePoint(ch, 0);
17039 			break;
17040 		case 'undefined':
17041 			return false;
17042 		default:
17043 			num = ch._toCodePoint(0);
17044 			break;
17045 	}
17046 
17047 	return CType._inRange(num, rangeName.toLowerCase(), ilib.data.ctype);
17048 };
17049 
17050 /**
17051  * @protected
17052  * @param {boolean} sync
17053  * @param {Object|undefined} loadParams
17054  * @param {function(*)|undefined} onLoad
17055  */
17056 CType._init = function(sync, loadParams, onLoad) {
17057 	CType._load("ctype", sync, loadParams, onLoad);
17058 };
17059 
17060 /**
17061  * @protected
17062  * @param {string} name
17063  * @param {boolean} sync
17064  * @param {Object|undefined} loadParams
17065  * @param {function(*)|undefined} onLoad
17066  */
17067 CType._load = function (name, sync, loadParams, onLoad) {
17068 	if (!ilib.data[name]) {
17069 		var loadName = name ? name + ".json" : "CType.json";
17070 		Utils.loadData({
17071 			name: loadName,
17072 			locale: "-",
17073 			nonlocale: true,
17074 			sync: sync,
17075 			loadParams: loadParams, 
17076 			callback: ilib.bind(this, function(ct) {
17077 				ilib.data[name] = ct;
17078 				if (onLoad && typeof(onLoad) === 'function') {
17079 					onLoad(ilib.data[name]);
17080 				}
17081 			})
17082 		});
17083 	} else {
17084 		if (onLoad && typeof(onLoad) === 'function') {
17085 			onLoad(ilib.data[name]);
17086 		}
17087 	}
17088 };
17089 
17090 
17091 
17092 /*< isDigit.js */
17093 /*
17094  * isDigit.js - Character type is digit
17095  * 
17096  * Copyright © 2012-2015, JEDLSoft
17097  *
17098  * Licensed under the Apache License, Version 2.0 (the "License");
17099  * you may not use this file except in compliance with the License.
17100  * You may obtain a copy of the License at
17101  *
17102  *     http://www.apache.org/licenses/LICENSE-2.0
17103  *
17104  * Unless required by applicable law or agreed to in writing, software
17105  * distributed under the License is distributed on an "AS IS" BASIS,
17106  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17107  *
17108  * See the License for the specific language governing permissions and
17109  * limitations under the License.
17110  */
17111 
17112 // !depends CType.js IString.js ilib.js
17113 
17114 // !data ctype
17115 
17116 
17117 /**
17118  * Return whether or not the first character is a digit character in the
17119  * Latin script.<p>
17120  * 
17121  * @static
17122  * @param {string|IString|number} ch character or code point to examine
17123  * @return {boolean} true if the first character is a digit character in the
17124  * Latin script. 
17125  */
17126 var isDigit = function (ch) {
17127 	var num;
17128 	switch (typeof(ch)) {
17129 		case 'number':
17130 			num = ch;
17131 			break;
17132 		case 'string':
17133 			num = IString.toCodePoint(ch, 0);
17134 			break;
17135 		case 'undefined':
17136 			return false;
17137 		default:
17138 			num = ch._toCodePoint(0);
17139 			break;
17140 	}
17141 	return CType._inRange(num, 'digit', ilib.data.ctype);
17142 };
17143 
17144 /**
17145  * @protected
17146  * @param {boolean} sync
17147  * @param {Object|undefined} loadParams
17148  * @param {function(*)|undefined} onLoad
17149  */
17150 isDigit._init = function (sync, loadParams, onLoad) {
17151 	CType._init(sync, loadParams, onLoad);
17152 };
17153 
17154 
17155 
17156 /*< isSpace.js */
17157 /*
17158  * isSpace.js - Character type is space char
17159  * 
17160  * Copyright © 2012-2015, JEDLSoft
17161  *
17162  * Licensed under the Apache License, Version 2.0 (the "License");
17163  * you may not use this file except in compliance with the License.
17164  * You may obtain a copy of the License at
17165  *
17166  *     http://www.apache.org/licenses/LICENSE-2.0
17167  *
17168  * Unless required by applicable law or agreed to in writing, software
17169  * distributed under the License is distributed on an "AS IS" BASIS,
17170  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17171  *
17172  * See the License for the specific language governing permissions and
17173  * limitations under the License.
17174  */
17175 
17176 // !depends CType.js IString.js
17177 
17178 // !data ctype ctype_z
17179 
17180 
17181 
17182 /**
17183  * Return whether or not the first character is a whitespace character.<p>
17184  * 
17185  * @static
17186  * @param {string|IString|number} ch character or code point to examine
17187  * @return {boolean} true if the first character is a whitespace character.
17188  */
17189 var isSpace = function (ch) {
17190 	var num;
17191 	switch (typeof(ch)) {
17192 		case 'number':
17193 			num = ch;
17194 			break;
17195 		case 'string':
17196 			num = IString.toCodePoint(ch, 0);
17197 			break;
17198 		case 'undefined':
17199 			return false;
17200 		default:
17201 			num = ch._toCodePoint(0);
17202 			break;
17203 	}
17204 
17205 	return CType._inRange(num, 'space', ilib.data.ctype) ||
17206 		CType._inRange(num, 'Zs', ilib.data.ctype_z) ||
17207 		CType._inRange(num, 'Zl', ilib.data.ctype_z) ||
17208 		CType._inRange(num, 'Zp', ilib.data.ctype_z);
17209 };
17210 
17211 /**
17212  * @protected
17213  * @param {boolean} sync
17214  * @param {Object|undefined} loadParams
17215  * @param {function(*)|undefined} onLoad
17216  */
17217 isSpace._init = function (sync, loadParams, onLoad) {
17218 	CType._load("ctype_z", sync, loadParams, function () {
17219 		CType._init(sync, loadParams, onLoad);
17220 	});
17221 };
17222 
17223 
17224 /*< Currency.js */
17225 /*
17226  * Currency.js - Currency definition
17227  * 
17228  * Copyright © 2012-2015, JEDLSoft
17229  *
17230  * Licensed under the Apache License, Version 2.0 (the "License");
17231  * you may not use this file except in compliance with the License.
17232  * You may obtain a copy of the License at
17233  *
17234  *     http://www.apache.org/licenses/LICENSE-2.0
17235  *
17236  * Unless required by applicable law or agreed to in writing, software
17237  * distributed under the License is distributed on an "AS IS" BASIS,
17238  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17239  *
17240  * See the License for the specific language governing permissions and
17241  * limitations under the License.
17242  */
17243 
17244 // !depends ilib.js Utils.js Locale.js LocaleInfo.js
17245 
17246 // !data currency
17247 
17248 
17249 /**
17250  * @class
17251  * Create a new currency information instance. Instances of this class encode 
17252  * information about a particular currency.<p>
17253  * 
17254  * Note: that if you are looking to format currency for display, please see
17255  * the number formatting class {NumFmt}. This class only gives information
17256  * about currencies.<p> 
17257  * 
17258  * The options can contain any of the following properties:
17259  * 
17260  * <ul>
17261  * <li><i>locale</i> - specify the locale for this instance
17262  * <li><i>code</i> - find info on a specific currency with the given ISO 4217 code 
17263  * <li><i>sign</i> - search for a currency that uses this sign
17264  * <li><i>onLoad</i> - a callback function to call when the currency data is fully 
17265  * loaded. When the onLoad option is given, this class will attempt to
17266  * load any missing locale data using the ilib loader callback.
17267  * When the constructor is done (even if the data is already preassembled), the 
17268  * onLoad function is called with the current instance as a parameter, so this
17269  * callback can be used with preassembled or dynamic loading or a mix of the two. 
17270  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
17271  * asynchronously. If this option is given as "false", then the "onLoad"
17272  * callback must be given, as the instance returned from this constructor will
17273  * not be usable for a while.
17274  * <li><i>loadParams</i> - an object containing parameters to pass to the 
17275  * loader callback function when locale data is missing. The parameters are not
17276  * interpretted or modified in any way. They are simply passed along. The object 
17277  * may contain any property/value pairs as long as the calling code is in
17278  * agreement with the loader callback function as to what those parameters mean.
17279  * </ul>
17280  * 
17281  * When searching for a currency by its sign, this class cannot guarantee 
17282  * that it will return info about a specific currency. The reason is that currency 
17283  * signs are sometimes shared between different currencies and the sign is 
17284  * therefore ambiguous. If you need a 
17285  * guarantee, find the currency using the code instead.<p>
17286  * 
17287  * The way this class finds a currency by sign is the following. If the sign is 
17288  * unambiguous, then
17289  * the currency is returned. If there are multiple currencies that use the same
17290  * sign, and the current locale uses that sign, then the default currency for
17291  * the current locale is returned. If there are multiple, but the current locale
17292  * does not use that sign, then the currency with the largest circulation is
17293  * returned. For example, if you are in the en-GB locale, and the sign is "$",
17294  * then this class will notice that there are multiple currencies with that
17295  * sign (USD, CAD, AUD, HKD, MXP, etc.) Since "$" is not used in en-GB, it will 
17296  * pick the one with the largest circulation, which in this case is the US Dollar
17297  * (USD).<p>
17298  * 
17299  * If neither the code or sign property is set, the currency that is most common 
17300  * for the locale
17301  * will be used instead. If the locale is not set, the default locale will be used.
17302  * If the code is given, but it is not found in the list of known currencies, this
17303  * constructor will throw an exception. If the sign is given, but it is not found,
17304  * this constructor will default to the currency for the current locale. If both
17305  * the code and sign properties are given, then the sign property will be ignored
17306  * and only the code property used. If the locale is given, but it is not a known
17307  * locale, this class will default to the default locale instead.<p>
17308  * 
17309  * 
17310  * @constructor
17311  * @param options {Object} a set of properties to govern how this instance is constructed.
17312  * @throws "currency xxx is unknown" when the given currency code is not in the list of 
17313  * known currencies. xxx is replaced with the requested code.
17314  */
17315 var Currency = function (options) {
17316 	this.sync = true;
17317 	
17318 	if (options) {
17319 		if (options.code) {
17320 			this.code = options.code;
17321 		}
17322 		if (options.locale) {
17323 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
17324 		}
17325 		if (options.sign) {
17326 			this.sign = options.sign;
17327 		}
17328 		if (typeof(options.sync) !== 'undefined') {
17329 			this.sync = options.sync;
17330 		}
17331 		if (options.loadParams) {
17332 			this.loadParams = options.loadParams;
17333 		}
17334 	}
17335 	
17336 	this.locale = this.locale || new Locale();
17337 	if (typeof(ilib.data.currency) === 'undefined') {
17338 		Utils.loadData({
17339 			name: "currency.json",
17340 			object: Currency, 
17341 			locale: "-",
17342 			sync: this.sync, 
17343 			loadParams: this.loadParams, 
17344 			callback: ilib.bind(this, function(currency) {
17345 				ilib.data.currency = currency;
17346 				this._loadLocinfo(options && options.onLoad);
17347 			})
17348 		});
17349 	} else {
17350 		this._loadLocinfo(options && options.onLoad);
17351 	}
17352 };
17353 
17354 /**
17355  * Return an array of the ids for all ISO 4217 currencies that
17356  * this copy of ilib knows about.
17357  * 
17358  * @static
17359  * @return {Array.<string>} an array of currency ids that this copy of ilib knows about.
17360  */
17361 Currency.getAvailableCurrencies = function() {
17362 	var ret = [],
17363 		cur,
17364 		currencies = new ResBundle({
17365 			name: "currency"
17366 		}).getResObj();
17367 	
17368 	for (cur in currencies) {
17369 		if (cur && currencies[cur]) {
17370 			ret.push(cur);
17371 		}
17372 	}
17373 	
17374 	return ret;
17375 };
17376 
17377 Currency.prototype = {
17378 	/**
17379 	 * @private
17380 	 */
17381 	_loadLocinfo: function(onLoad) {
17382 		new LocaleInfo(this.locale, {
17383 			onLoad: ilib.bind(this, function (li) {
17384 				var currInfo;
17385 				
17386 				this.locinfo = li;
17387 		    	if (this.code) {
17388 		    		currInfo = ilib.data.currency[this.code];
17389 		    		if (!currInfo) {
17390 		    			throw "currency " + this.code + " is unknown";
17391 		    		}
17392 		    	} else if (this.sign) {
17393 		    		currInfo = ilib.data.currency[this.sign]; // maybe it is really a code...
17394 		    		if (typeof(currInfo) !== 'undefined') {
17395 		    			this.code = this.sign;
17396 		    		} else {
17397 		    			this.code = this.locinfo.getCurrency();
17398 		    			currInfo = ilib.data.currency[this.code];
17399 		    			if (currInfo.sign !== this.sign) {
17400 		    				// current locale does not use the sign, so search for it
17401 		    				for (var cur in ilib.data.currency) {
17402 		    					if (cur && ilib.data.currency[cur]) {
17403 		    						currInfo = ilib.data.currency[cur];
17404 		    						if (currInfo.sign === this.sign) {
17405 		    							// currency data is already ordered so that the currency with the
17406 		    							// largest circulation is at the beginning, so all we have to do
17407 		    							// is take the first one in the list that matches
17408 		    							this.code = cur;
17409 		    							break;
17410 		    						}
17411 		    					}
17412 		    				}
17413 		    			}
17414 		    		}
17415 		    	}
17416 		    	
17417 		    	if (!currInfo || !this.code) {
17418 		    		this.code = this.locinfo.getCurrency();
17419 		    		currInfo = ilib.data.currency[this.code];
17420 		    	}
17421 		    	
17422 		    	this.name = currInfo.name;
17423 		    	this.fractionDigits = currInfo.decimals;
17424 		    	this.sign = currInfo.sign;
17425 		    	
17426 				if (typeof(onLoad) === 'function') {
17427 					onLoad(this);
17428 				}
17429 			})
17430 		});
17431 	},
17432 	
17433 	/**
17434 	 * Return the ISO 4217 currency code for this instance.
17435 	 * @return {string} the ISO 4217 currency code for this instance
17436 	 */
17437 	getCode: function () {
17438 		return this.code;
17439 	},
17440 	
17441 	/**
17442 	 * Return the default number of fraction digits that is typically used
17443 	 * with this type of currency.
17444 	 * @return {number} the number of fraction digits for this currency
17445 	 */
17446 	getFractionDigits: function () {
17447 		return this.fractionDigits;
17448 	},
17449 	
17450 	/**
17451 	 * Return the sign commonly used to represent this currency.
17452 	 * @return {string} the sign commonly used to represent this currency
17453 	 */
17454 	getSign: function () {
17455 		return this.sign;
17456 	},
17457 	
17458 	/**
17459 	 * Return the name of the currency in English.
17460 	 * @return {string} the name of the currency in English
17461 	 */
17462 	getName: function () {
17463 		return this.name;
17464 	},
17465 	
17466 	/**
17467 	 * Return the locale for this currency. If the options to the constructor 
17468 	 * included a locale property in order to find the currency that is appropriate
17469 	 * for that locale, then the locale is returned here. If the options did not
17470 	 * include a locale, then this method returns undefined.
17471 	 * @return {Locale} the locale used in the constructor of this instance,
17472 	 * or undefined if no locale was given in the constructor
17473 	 */
17474 	getLocale: function () {
17475 		return this.locale;
17476 	}
17477 };
17478 
17479 
17480 
17481 /*< INumber.js */
17482 /*
17483  * INumber.js - Parse a number in any locale
17484  * 
17485  * Copyright © 2012-2015, JEDLSoft
17486  *
17487  * Licensed under the Apache License, Version 2.0 (the "License");
17488  * you may not use this file except in compliance with the License.
17489  * You may obtain a copy of the License at
17490  *
17491  *     http://www.apache.org/licenses/LICENSE-2.0
17492  *
17493  * Unless required by applicable law or agreed to in writing, software
17494  * distributed under the License is distributed on an "AS IS" BASIS,
17495  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17496  *
17497  * See the License for the specific language governing permissions and
17498  * limitations under the License.
17499  */
17500 
17501 /*
17502 !depends 
17503 ilib.js 
17504 Locale.js 
17505 isDigit.js 
17506 isSpace.js
17507 LocaleInfo.js
17508 Utils.js
17509 Currency.js
17510 */
17511 
17512 
17513 
17514 
17515 
17516 
17517 /**
17518  * @class
17519  * Parse a string as a number, ignoring all locale-specific formatting.<p>
17520  * 
17521  * This class is different from the standard Javascript parseInt() and parseFloat() 
17522  * functions in that the number to be parsed can have formatting characters in it 
17523  * that are not supported by those two
17524  * functions, and it handles numbers written in other locales properly. For example, 
17525  * if you pass the string "203,231.23" to the parseFloat() function in Javascript, it 
17526  * will return you the number 203. The INumber class will parse it correctly and 
17527  * the value() function will return the number 203231.23. If you pass parseFloat() the 
17528  * string "203.231,23" with the locale set to de-DE, it will return you 203 again. This
17529  * class will return the correct number 203231.23 again.<p>
17530  * 
17531  * The options object may contain any of the following properties:
17532  * 
17533  * <ul>
17534  * <li><i>locale</i> - specify the locale of the string to parse. This is used to
17535  * figure out what the decimal point character is. If not specified, the default locale
17536  * for the app or browser is used.
17537  * <li><i>type</i> - specify whether this string should be interpretted as a number,
17538  * currency, or percentage amount. When the number is interpretted as a currency
17539  * amount, the getCurrency() method will return something useful, otherwise it will
17540  * return undefined. If
17541  * the number is to be interpretted as percentage amount and there is a percentage sign
17542  * in the string, then the number will be returned
17543  * as a fraction from the valueOf() method. If there is no percentage sign, then the 
17544  * number will be returned as a regular number. That is "58.3%" will be returned as the 
17545  * number 0.583 but "58.3" will be returned as 58.3. Valid values for this property 
17546  * are "number", "currency", and "percentage". Default if this is not specified is
17547  * "number".
17548  * <li><i>onLoad</i> - a callback function to call when the locale data is fully 
17549  * loaded. When the onLoad option is given, this class will attempt to
17550  * load any missing locale data using the ilib loader callback.
17551  * When the constructor is done (even if the data is already preassembled), the 
17552  * onLoad function is called with the current instance as a parameter, so this
17553  * callback can be used with preassembled or dynamic loading or a mix of the two. 
17554  * 
17555  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
17556  * asynchronously. If this option is given as "false", then the "onLoad"
17557  * callback must be given, as the instance returned from this constructor will
17558  * not be usable for a while.
17559  *  
17560  * <li><i>loadParams</i> - an object containing parameters to pass to the 
17561  * loader callback function when locale data is missing. The parameters are not
17562  * interpretted or modified in any way. They are simply passed along. The object 
17563  * may contain any property/value pairs as long as the calling code is in
17564  * agreement with the loader callback function as to what those parameters mean.
17565  * </ul>
17566  * <p>
17567  * 
17568  * This class is named INumber ("ilib number") so as not to conflict with the 
17569  * built-in Javascript Number class.
17570  * 
17571  * @constructor
17572  * @param {string|number|INumber|Number|undefined} str a string to parse as a number, or a number value
17573  * @param {Object=} options Options controlling how the instance should be created 
17574  */
17575 var INumber = function (str, options) {
17576 	var i, stripped = "", 
17577 		sync = true,
17578 		loadParams,
17579 		onLoad;
17580 	
17581 	this.locale = new Locale();
17582 	this.type = "number";
17583 	
17584 	if (options) {
17585 		if (options.locale) {
17586 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
17587 		}
17588 		if (options.type) {
17589 			switch (options.type) {
17590 				case "number":
17591 				case "currency":
17592 				case "percentage":
17593 					this.type = options.type;
17594 					break;
17595 				default:
17596 					break;
17597 			}
17598 		}
17599 		if (typeof(options.sync) !== 'undefined') {
17600 			sync = (options.sync == true);
17601 		}
17602 		loadParams = options.loadParams;
17603 		onLoad = options.onLoad;
17604 	}
17605 	
17606 	isDigit._init(sync, loadParams, ilib.bind(this, function() {
17607 		isSpace._init(sync, loadParams, ilib.bind(this, function() {
17608 			new LocaleInfo(this.locale, {
17609 				sync: sync,
17610 				onLoad: ilib.bind(this, function (li) {
17611 					this.decimal = li.getDecimalSeparator();
17612 					
17613 					switch (typeof(str)) {
17614 					case 'string':
17615 						// stripping should work for all locales, because you just ignore all the
17616 						// formatting except the decimal char
17617 						var unary = true; // looking for the unary minus still?
17618 						var lastNumericChar = 0;
17619 						this.str = str || "0";
17620 						i = 0;
17621 						for (i = 0; i < this.str.length; i++) {
17622 							if (unary && this.str.charAt(i) === '-') {
17623 								unary = false;
17624 								stripped += this.str.charAt(i);
17625 								lastNumericChar = i;
17626 							} else if (isDigit(this.str.charAt(i))) {
17627 								stripped += this.str.charAt(i);
17628 								unary = false;
17629 								lastNumericChar = i;
17630 							} else if (this.str.charAt(i) === this.decimal) {
17631 								stripped += "."; // always convert to period
17632 								unary = false;
17633 								lastNumericChar = i;
17634 							} // else ignore
17635 						}
17636 						// record what we actually parsed
17637 						this.parsed = this.str.substring(0, lastNumericChar+1);
17638 						/** @type {number} */
17639 						this.value = parseFloat(stripped);
17640 						break;
17641 					case 'number':
17642 						this.str = "" + str;
17643 						this.value = str;
17644 						break;
17645 						
17646 					case 'object':
17647 						// call parseFloat to coerse the type to number
17648 						this.value = parseFloat(str.valueOf());
17649     					this.str = "" + this.value;
17650 						break;
17651 						
17652 					case 'undefined':
17653 						this.value = 0;
17654 						this.str = "0";
17655 						break;
17656 					}
17657 					
17658 					switch (this.type) {
17659 						default:
17660 							// don't need to do anything special for other types
17661 							break;
17662 						case "percentage":
17663 							if (this.str.indexOf(li.getPercentageSymbol()) !== -1) {
17664 								this.value /= 100;
17665 							}
17666 							break;
17667 						case "currency":
17668 							stripped = "";
17669 							i = 0;
17670 							while (i < this.str.length &&
17671 								   !isDigit(this.str.charAt(i)) &&
17672 								   !isSpace(this.str.charAt(i))) {
17673 								stripped += this.str.charAt(i++);
17674 							}
17675 							if (stripped.length === 0) {
17676 								while (i < this.str.length && 
17677 									   isDigit(this.str.charAt(i)) ||
17678 									   isSpace(this.str.charAt(i)) ||
17679 									   this.str.charAt(i) === '.' ||
17680 									   this.str.charAt(i) === ',' ) {
17681 									i++;
17682 								}
17683 								while (i < this.str.length && 
17684 									   !isDigit(this.str.charAt(i)) &&
17685 									   !isSpace(this.str.charAt(i))) {
17686 									stripped += this.str.charAt(i++);
17687 								}
17688 							}
17689 							new Currency({
17690 								locale: this.locale, 
17691 								sign: stripped,
17692 								sync: sync,
17693 								onLoad: ilib.bind(this, function (cur) {
17694 									this.currency = cur;
17695 									if (options && typeof(options.onLoad) === 'function') {
17696 										options.onLoad(this);
17697 									}				
17698 								})
17699 							});
17700 							return;
17701 					}
17702 					
17703 					if (options && typeof(options.onLoad) === 'function') {
17704 						options.onLoad(this);
17705 					}
17706 				})
17707 			});
17708 		}));
17709 	}));
17710 };
17711 
17712 INumber.prototype = {
17713 	/**
17714 	 * Return the locale for this formatter instance.
17715 	 * @return {Locale} the locale instance for this formatter
17716 	 */
17717 	getLocale: function () {
17718 		return this.locale;
17719 	},
17720 	
17721 	/**
17722 	 * Return the original string that this number instance was created with.
17723 	 * @return {string} the original string
17724 	 */
17725 	toString: function () {
17726 		return this.str;
17727 	},
17728 	
17729 	/**
17730 	 * If the type of this INumber instance is "currency", then the parser will attempt
17731 	 * to figure out which currency this amount represents. The amount can be written
17732 	 * with any of the currency signs or ISO 4217 codes that are currently
17733 	 * recognized by ilib, and the currency signs may occur before or after the
17734 	 * numeric portion of the string. If no currency can be recognized, then the 
17735 	 * default currency for the locale is returned. If multiple currencies can be
17736 	 * recognized (for example if the currency sign is "$"), then this method 
17737 	 * will prefer the one for the current locale. If multiple currencies can be
17738 	 * recognized, but none are used in the current locale, then the first currency
17739 	 * encountered will be used. This may produce random results, though the larger
17740 	 * currencies occur earlier in the list. For example, if the sign found in the
17741 	 * string is "$" and that is not the sign of the currency of the current locale
17742 	 * then the US dollar will be recognized, as it is the largest currency that uses
17743 	 * the "$" as its sign.
17744 	 * 
17745 	 * @return {Currency|undefined} the currency instance for this amount, or 
17746 	 * undefined if this INumber object is not of type currency
17747 	 */
17748 	getCurrency: function () {
17749 		return this.currency;
17750 	},
17751 	
17752 	/**
17753 	 * Return the value of this INumber object as a primitive number instance.
17754 	 * @return {number} the value of this number instance
17755 	 */
17756 	valueOf: function () {
17757 		return this.value;
17758 	}
17759 };
17760 
17761 
17762 /*< NumFmt.js */
17763 /*
17764  * NumFmt.js - Number formatter definition
17765  *
17766  * Copyright © 2012-2015, JEDLSoft
17767  *
17768  * Licensed under the Apache License, Version 2.0 (the "License");
17769  * you may not use this file except in compliance with the License.
17770  * You may obtain a copy of the License at
17771  *
17772  *     http://www.apache.org/licenses/LICENSE-2.0
17773  *
17774  * Unless required by applicable law or agreed to in writing, software
17775  * distributed under the License is distributed on an "AS IS" BASIS,
17776  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17777  *
17778  * See the License for the specific language governing permissions and
17779  * limitations under the License.
17780  */
17781 
17782 /*
17783 !depends 
17784 ilib.js 
17785 Locale.js
17786 LocaleInfo.js
17787 Utils.js
17788 MathUtils.js
17789 Currency.js
17790 IString.js
17791 JSUtils.js
17792 INumber.js
17793 */
17794 
17795 // !data localeinfo currency
17796 
17797 
17798 
17799 /**
17800  * @class
17801  * Create a new number formatter instance. Locales differ in the way that digits
17802  * in a formatted number are grouped, in the way the decimal character is represented,
17803  * etc. Use this formatter to get it right for any locale.<p>
17804  *
17805  * This formatter can format plain numbers, currency amounts, and percentage amounts.<p>
17806  *
17807  * As with all formatters, the recommended
17808  * practice is to create one formatter and use it multiple times to format various
17809  * numbers.<p>
17810  *
17811  * The options can contain any of the following properties:
17812  *
17813  * <ul>
17814  * <li><i>locale</i> - use the conventions of the specified locale when figuring out how to
17815  * format a number.
17816  * <li><i>type</i> - the type of this formatter. Valid values are "number", "currency", or
17817  * "percentage". If this property is not specified, the default is "number".
17818  * <li><i>currency</i> - the ISO 4217 3-letter currency code to use when the formatter type
17819  * is "currency". This property is required for currency formatting. If the type property
17820  * is "currency" and the currency property is not specified, the constructor will throw a
17821  * an exception.
17822  * <li><i>maxFractionDigits</i> - the maximum number of digits that should appear in the
17823  * formatted output after the decimal. A value of -1 means unlimited, and 0 means only print
17824  * the integral part of the number.
17825  * <li><i>minFractionDigits</i> - the minimum number of fractional digits that should
17826  * appear in the formatted output. If the number does not have enough fractional digits
17827  * to reach this minimum, the number will be zero-padded at the end to get to the limit.
17828  * If the type of the formatter is "currency" and this
17829  * property is not specified, then the minimum fraction digits is set to the normal number
17830  * of digits used with that currency, which is almost always 0, 2, or 3 digits.
17831  * <li><i>useNative</i> - the flag used to determaine whether to use the native script settings
17832  * for formatting the numbers .
17833  * <li><i>roundingMode</i> - When the maxFractionDigits or maxIntegerDigits is specified,
17834  * this property governs how the least significant digits are rounded to conform to that
17835  * maximum. The value of this property is a string with one of the following values:
17836  * <ul>
17837  *   <li><i>up</i> - round away from zero
17838  *   <li><i>down</i> - round towards zero. This has the effect of truncating the number
17839  *   <li><i>ceiling</i> - round towards positive infinity
17840  *   <li><i>floor</i> - round towards negative infinity
17841  *   <li><i>halfup</i> - round towards nearest neighbour. If equidistant, round up.
17842  *   <li><i>halfdown</i> - round towards nearest neighbour. If equidistant, round down.
17843  *   <li><i>halfeven</i> - round towards nearest neighbour. If equidistant, round towards the even neighbour
17844  *   <li><i>halfodd</i> - round towards nearest neighbour. If equidistant, round towards the odd neighbour
17845  * </ul>
17846  * When the type of the formatter is "currency" and the <i>roundingMode</i> property is not
17847  * set, then the standard legal rounding rules for the locale are followed. If the type
17848  * is "number" or "percentage" and the <i>roundingMode</i> property is not set, then the
17849  * default mode is "halfdown".</i>.
17850  *
17851  * <li><i>style</i> - When the type of this formatter is "currency", the currency amount
17852  * can be formatted in the following styles: "common" and "iso". The common style is the
17853  * one commonly used in every day writing where the currency unit is represented using a
17854  * symbol. eg. "$57.35" for fifty-seven dollars and thirty five cents. The iso style is
17855  * the international style where the currency unit is represented using the ISO 4217 code.
17856  * eg. "USD 57.35" for the same amount. The default is "common" style if the style is
17857  * not specified.<p>
17858  *
17859  * When the type of this formatter is "number", the style can be one of the following:
17860  * <ul>
17861  *   <li><i>standard - format a fully specified floating point number properly for the locale
17862  *   <li><i>scientific</i> - use scientific notation for all numbers. That is, 1 integral 
17863  *   digit, followed by a number of fractional digits, followed by an "e" which denotes 
17864  *   exponentiation, followed digits which give the power of 10 in the exponent. 
17865  *   <li><i>native</i> - format a floating point number using the native digits and 
17866  *   formatting symbols for the script of the locale. 
17867  *   <li><i>nogrouping</i> - format a floating point number without grouping digits for
17868  *   the integral portion of the number
17869  * </ul>
17870  * Note that if you specify a maximum number
17871  * of integral digits, the formatter with a standard style will give you standard
17872  * formatting for smaller numbers and scientific notation for larger numbers. The default
17873  * is standard style if this is not specified.
17874  *
17875  * <li><i>onLoad</i> - a callback function to call when the format data is fully
17876  * loaded. When the onLoad option is given, this class will attempt to
17877  * load any missing locale data using the ilib loader callback.
17878  * When the constructor is done (even if the data is already preassembled), the
17879  * onLoad function is called with the current instance as a parameter, so this
17880  * callback can be used with preassembled or dynamic loading or a mix of the two.
17881  *
17882  * <li>sync - tell whether to load any missing locale data synchronously or
17883  * asynchronously. If this option is given as "false", then the "onLoad"
17884  * callback must be given, as the instance returned from this constructor will
17885  * not be usable for a while.
17886  *
17887  * <li><i>loadParams</i> - an object containing parameters to pass to the
17888  * loader callback function when locale data is missing. The parameters are not
17889  * interpretted or modified in any way. They are simply passed along. The object
17890  * may contain any property/value pairs as long as the calling code is in
17891  * agreement with the loader callback function as to what those parameters mean.
17892  * </ul>
17893  * <p>
17894  *
17895  *
17896  * @constructor
17897  * @param {Object.<string,*>} options A set of options that govern how the formatter will behave
17898  */
17899 var NumFmt = function (options) {
17900 	var sync = true;
17901 	this.locale = new Locale();
17902 	/** 
17903 	 * @private
17904 	 * @type {string} 
17905 	 */
17906 	this.type = "number";
17907 	var loadParams = undefined;
17908 
17909 	if (options) {
17910 		if (options.locale) {
17911 			this.locale = (typeof (options.locale) === 'string') ? new Locale(options.locale) : options.locale;
17912 		}
17913 
17914 		if (options.type) {
17915 			if (options.type === 'number' ||
17916 				options.type === 'currency' ||
17917 				options.type === 'percentage') {
17918 				this.type = options.type;
17919 			}
17920 		}
17921 
17922 		if (options.currency) {
17923 			/** 
17924 			 * @private 
17925 			 * @type {string} 
17926 			 */
17927 			this.currency = options.currency;
17928 		}
17929 
17930 		if (typeof (options.maxFractionDigits) === 'number') {
17931 			/** 
17932 			 * @private 
17933 			 * @type {number|undefined} 
17934 			 */
17935 			this.maxFractionDigits = this._toPrimitive(options.maxFractionDigits);
17936 		}
17937 		if (typeof (options.minFractionDigits) === 'number') {
17938 			/** 
17939 			 * @private 
17940 			 * @type {number|undefined} 
17941 			 */
17942 			this.minFractionDigits = this._toPrimitive(options.minFractionDigits);
17943 			// enforce the limits to avoid JS exceptions
17944 			if (this.minFractionDigits < 0) {
17945 				this.minFractionDigits = 0;
17946 			}
17947 			if (this.minFractionDigits > 20) {
17948 				this.minFractionDigits = 20;
17949 			}
17950 		}
17951 		if (options.style) {
17952 			/** 
17953 			 * @private 
17954 			 * @type {string} 
17955 			 */
17956 			this.style = options.style;
17957 		}
17958 		if (typeof(options.useNative) === 'boolean') {
17959 			/** 
17960 			 * @private 
17961 			 * @type {boolean} 
17962 			 * */
17963 			this.useNative = options.useNative;
17964 		}
17965 		/** 
17966 		 * @private 
17967 		 * @type {string} 
17968 		 */
17969 		this.roundingMode = options.roundingMode;
17970 
17971 		if (typeof(options.sync) === 'boolean') {
17972 			sync = options.sync;
17973 		}
17974 		
17975 		loadParams = options.loadParams;
17976 	}
17977 
17978 	/** 
17979 	 * @private 
17980 	 * @type {LocaleInfo|undefined} 
17981 	 */
17982 	this.localeInfo = undefined;
17983 	
17984 	new LocaleInfo(this.locale, {
17985 		sync: sync,
17986 		loadParams: loadParams,
17987 		onLoad: ilib.bind(this, function (li) {
17988 			/** 
17989 			 * @private 
17990 			 * @type {LocaleInfo|undefined} 
17991 			 */
17992 			this.localeInfo = li;
17993 
17994 			if (this.type === "number") {
17995 				this.templateNegative = new IString(this.localeInfo.getNegativeNumberFormat() || "-{n}");
17996 			} else if (this.type === "currency") {
17997 				var templates;
17998 
17999 				if (!this.currency || typeof (this.currency) != 'string') {
18000 					throw "A currency property is required in the options to the number formatter constructor when the type property is set to currency.";
18001 				}
18002 
18003 				new Currency({
18004 					locale: this.locale,
18005 					code: this.currency,
18006 					sync: sync,
18007 					loadParams: loadParams,
18008 					onLoad: ilib.bind(this, function (cur) {
18009 						this.currencyInfo = cur;
18010 						if (this.style !== "common" && this.style !== "iso") {
18011 							this.style = "common";
18012 						}
18013 						
18014 						if (typeof(this.maxFractionDigits) !== 'number' && typeof(this.minFractionDigits) !== 'number') {
18015 							this.minFractionDigits = this.maxFractionDigits = this.currencyInfo.getFractionDigits();
18016 						}
18017 
18018 						templates = this.localeInfo.getCurrencyFormats();
18019 						this.template = new IString(templates[this.style] || templates.common);
18020 						this.templateNegative = new IString(templates[this.style + "Negative"] || templates["commonNegative"]);
18021 						this.sign = (this.style === "iso") ? this.currencyInfo.getCode() : this.currencyInfo.getSign();
18022 						
18023 						if (!this.roundingMode) {
18024 							this.roundingMode = this.currencyInfo && this.currencyInfo.roundingMode;
18025 						}
18026 
18027 						this._init();
18028 
18029 						if (options && typeof (options.onLoad) === 'function') {
18030 							options.onLoad(this);
18031 						}
18032 					})
18033 				});
18034 				return;
18035 			} else if (this.type === "percentage") {
18036 				this.template =  new IString(this.localeInfo.getPercentageFormat() || "{n}%");
18037 				this.templateNegative = new IString(this.localeInfo.getNegativePercentageFormat() || this.localeInfo.getNegativeNumberFormat() + "%");
18038 			}
18039 
18040 			this._init();
18041 
18042 			if (options && typeof (options.onLoad) === 'function') {
18043 				options.onLoad(this);
18044 			}
18045 		})
18046 	});
18047 };
18048 
18049 /**
18050  * Return an array of available locales that this formatter can format
18051  * @static
18052  * @return {Array.<Locale>|undefined} an array of available locales
18053  */
18054 NumFmt.getAvailableLocales = function () {
18055 	return undefined;
18056 };
18057 
18058 /**
18059  * @private
18060  * @const
18061  * @type string
18062  */
18063 NumFmt.zeros = "0000000000000000000000000000000000000000000000000000000000000000000000";
18064 
18065 NumFmt.prototype = {
18066 	/**
18067 	 * Return true if this formatter uses native digits to format the number. If the useNative
18068 	 * option is given to the constructor, then this flag will be honoured. If the useNative
18069 	 * option is not given to the constructor, this this formatter will use native digits if
18070 	 * the locale typically uses native digits.
18071 	 * 
18072 	 *  @return {boolean} true if this formatter will format with native digits, false otherwise
18073 	 */
18074 	getUseNative: function() {
18075 		if (typeof(this.useNative) === "boolean") {
18076 			return this.useNative;
18077 		} 
18078 		return (this.localeInfo.getDigitsStyle() === "native");
18079 	},
18080 	
18081 	/**
18082 	 * @private
18083 	 */
18084 	_init: function () {
18085 		if (this.maxFractionDigits < this.minFractionDigits) {
18086 			this.minFractionDigits = this.maxFractionDigits;
18087 		}
18088 
18089 		if (!this.roundingMode) {
18090 			this.roundingMode = this.localeInfo.getRoundingMode();
18091 		}
18092 
18093 		if (!this.roundingMode) {
18094 			this.roundingMode = "halfdown";
18095 		}
18096 
18097 		// set up the function, so we only have to figure it out once
18098 		// and not every time we do format()
18099 		this.round = MathUtils[this.roundingMode];
18100 		if (!this.round) {
18101 			this.roundingMode = "halfdown";
18102 			this.round = MathUtils[this.roundingMode];
18103 		}
18104 		
18105 		if (this.style === "nogrouping") {
18106 			this.prigroupSize = this.secgroupSize = 0;
18107 		} else {
18108 			this.prigroupSize = this.localeInfo.getPrimaryGroupingDigits();
18109 			this.secgroupSize = this.localeInfo.getSecondaryGroupingDigits();
18110 			this.groupingSeparator = this.getUseNative() ? this.localeInfo.getNativeGroupingSeparator() : this.localeInfo.getGroupingSeparator();
18111 		} 
18112 		this.decimalSeparator = this.getUseNative() ? this.localeInfo.getNativeDecimalSeparator() : this.localeInfo.getDecimalSeparator();
18113 		
18114 		if (this.getUseNative()) {
18115 			var nd = this.localeInfo.getNativeDigits() || this.localeInfo.getDigits();
18116 			if (nd) {
18117 				this.digits = nd.split("");
18118 			}
18119 		}
18120 		
18121 		this.exponentSymbol = this.localeInfo.getExponential() || "e";
18122 	},
18123 
18124 	/**
18125 	 * @private
18126 	 * @param {INumber|Number|string|number} num object, string, or number to convert to a primitive number
18127 	 * @return {number} the primitive number equivalent of the argument
18128 	 */
18129 	_toPrimitive: function (num) {
18130 		var n = 0;
18131 
18132 		switch (typeof (num)) {
18133 		case 'number':
18134 			n = num;
18135 			break;
18136 		case 'string':
18137 			n = parseFloat(num);
18138 			break;
18139 		case 'object':
18140 			// call parseFloat to coerse the type to number 
18141 			n = parseFloat(num.valueOf());
18142 			break;
18143 		}
18144 
18145 		return n;
18146 	},
18147 
18148 	/**
18149 	 * Format the number using scientific notation as a positive number. Negative
18150 	 * formatting to be applied later.
18151 	 * @private
18152 	 * @param {number} num the number to format
18153 	 * @return {string} the formatted number
18154 	 */
18155 	_formatScientific: function (num) {
18156 		var n = new Number(num);
18157 		var formatted;
18158 		
18159 		var factor,
18160 			str = n.toExponential(),
18161 			parts = str.split("e"),
18162 			significant = parts[0],
18163 			exponent = parts[1],
18164 			numparts,
18165 			integral,
18166 			fraction;
18167 
18168 		if (this.maxFractionDigits > 0) {
18169 			// if there is a max fraction digits setting, round the fraction to 
18170 			// the right length first by dividing or multiplying by powers of 10. 
18171 			// manipulate the fraction digits so as to
18172 			// avoid the rounding errors of floating point numbers
18173 			factor = Math.pow(10, this.maxFractionDigits);
18174 			significant = this.round(significant * factor) / factor;
18175 		}
18176 		numparts = ("" + significant).split(".");
18177 		integral = numparts[0];
18178 		fraction = numparts[1];
18179 		
18180 		if (typeof(this.maxFractionDigits) !== 'undefined') {
18181 			fraction = fraction.substring(0, this.maxFractionDigits);
18182 		}
18183 		if (typeof(this.minFractionDigits) !== 'undefined') {
18184 			fraction = JSUtils.pad(fraction || "", this.minFractionDigits, true);
18185 		}
18186 		formatted = integral;
18187 		if (fraction.length) {
18188 			formatted += this.decimalSeparator + fraction;	
18189 		} 
18190 		formatted += this.exponentSymbol + exponent;
18191 		return formatted;
18192 	},
18193 
18194 	/**
18195 	 * Formats the number as a positive number. Negative formatting to be applied later.
18196 	 * @private
18197 	 * @param {number} num the number to format
18198 	 * @return {string} the formatted number
18199 	 */
18200 	_formatStandard: function (num) {
18201 		var i;
18202 		var k;
18203 		
18204 		if (typeof(this.maxFractionDigits) !== 'undefined' && this.maxFractionDigits > -1) {
18205 			var factor = Math.pow(10, this.maxFractionDigits);
18206 			num = this.round(num * factor) / factor;
18207 		}
18208 
18209 		num = Math.abs(num);
18210 
18211 		var parts = ("" + num).split("."),
18212 			integral = parts[0],
18213 			fraction = parts[1],
18214 			cycle,
18215 			formatted;
18216 		
18217 		integral = integral.toString();
18218 
18219 		if (this.minFractionDigits > 0) {
18220 			fraction = JSUtils.pad(fraction || "", this.minFractionDigits, true);
18221 		}
18222 
18223 		if (this.secgroupSize > 0) {
18224 			if (integral.length > this.prigroupSize) {
18225 				var size1 = this.prigroupSize;
18226 				var size2 = integral.length;
18227 				var size3 = size2 - size1;
18228 				integral = integral.slice(0, size3) + this.groupingSeparator + integral.slice(size3);
18229 				var num_sec = integral.substring(0, integral.indexOf(this.groupingSeparator));
18230 				k = num_sec.length;
18231 				while (k > this.secgroupSize) {
18232 					var secsize1 = this.secgroupSize;
18233 					var secsize2 = num_sec.length;
18234 					var secsize3 = secsize2 - secsize1;
18235 					integral = integral.slice(0, secsize3) + this.groupingSeparator + integral.slice(secsize3);
18236 					num_sec = integral.substring(0, integral.indexOf(this.groupingSeparator));
18237 					k = num_sec.length;
18238 				}
18239 			}
18240 
18241 			formatted = integral;
18242 		} else if (this.prigroupSize !== 0) {
18243 			cycle = MathUtils.mod(integral.length - 1, this.prigroupSize);
18244 
18245 			formatted = "";
18246 
18247 			for (i = 0; i < integral.length - 1; i++) {
18248 				formatted += integral.charAt(i);
18249 				if (cycle === 0) {
18250 					formatted += this.groupingSeparator;
18251 				}
18252 				cycle = MathUtils.mod(cycle - 1, this.prigroupSize);
18253 			}
18254 			formatted += integral.charAt(integral.length - 1);
18255 		} else {
18256 			formatted = integral;
18257 		}
18258 
18259 		if (fraction && (typeof(this.maxFractionDigits) === 'undefined' || this.maxFractionDigits > 0)) {
18260 			formatted += this.decimalSeparator;
18261 			formatted += fraction;
18262 		}
18263 		
18264 		if (this.digits) {
18265 			formatted = JSUtils.mapString(formatted, this.digits);
18266 		}
18267 		
18268 		return formatted;
18269 	},
18270 
18271 	/**
18272 	 * Format a number according to the settings of this number formatter instance.
18273 	 * @param num {number|string|INumber|Number} a floating point number to format
18274 	 * @return {string} a string containing the formatted number
18275 	 */
18276 	format: function (num) {
18277 		var formatted, n;
18278 
18279 		if (typeof (num) === 'undefined') {
18280 			return "";
18281 		}
18282 
18283 		// convert to a real primitive number type
18284 		n = this._toPrimitive(num);
18285 
18286 		if (this.type === "number") {
18287 			formatted = (this.style === "scientific") ?
18288 				this._formatScientific(n) :
18289 				this._formatStandard(n);
18290 
18291 			if (num < 0) {
18292 				formatted = this.templateNegative.format({n: formatted});
18293 			}
18294 		} else {
18295 			formatted = this._formatStandard(n);
18296 			var template = (n < 0) ? this.templateNegative : this.template;
18297 			formatted = template.format({
18298 				n: formatted,
18299 				s: this.sign
18300 			});
18301 		}
18302 
18303 		return formatted;
18304 	},
18305 
18306 	/**
18307 	 * Return the type of formatter. Valid values are "number", "currency", and
18308 	 * "percentage".
18309 	 *
18310 	 * @return {string} the type of formatter
18311 	 */
18312 	getType: function () {
18313 		return this.type;
18314 	},
18315 
18316 	/**
18317 	 * Return the locale for this formatter instance.
18318 	 * @return {Locale} the locale instance for this formatter
18319 	 */
18320 	getLocale: function () {
18321 		return this.locale;
18322 	},
18323 
18324 	/**
18325 	 * Returns true if this formatter groups together digits in the integral
18326 	 * portion of a number, based on the options set up in the constructor. In
18327 	 * most western European cultures, this means separating every 3 digits
18328 	 * of the integral portion of a number with a particular character.
18329 	 *
18330 	 * @return {boolean} true if this formatter groups digits in the integral
18331 	 * portion of the number
18332 	 */
18333 	isGroupingUsed: function () {
18334 		return (this.groupingSeparator !== 'undefined' && this.groupingSeparator.length > 0);
18335 	},
18336 
18337 	/**
18338 	 * Returns the maximum fraction digits set up in the constructor.
18339 	 *
18340 	 * @return {number} the maximum number of fractional digits this
18341 	 * formatter will format, or -1 for no maximum
18342 	 */
18343 	getMaxFractionDigits: function () {
18344 		return typeof (this.maxFractionDigits) !== 'undefined' ? this.maxFractionDigits : -1;
18345 	},
18346 
18347 	/**
18348 	 * Returns the minimum fraction digits set up in the constructor. If
18349 	 * the formatter has the type "currency", then the minimum fraction
18350 	 * digits is the amount of digits that is standard for the currency
18351 	 * in question unless overridden in the options to the constructor.
18352 	 *
18353 	 * @return {number} the minimum number of fractional digits this
18354 	 * formatter will format, or -1 for no minimum
18355 	 */
18356 	getMinFractionDigits: function () {
18357 		return typeof (this.minFractionDigits) !== 'undefined' ? this.minFractionDigits : -1;
18358 	},
18359 
18360 	/**
18361 	 * Returns the ISO 4217 code for the currency that this formatter formats.
18362 	 * IF the typeof this formatter is not "currency", then this method will
18363 	 * return undefined.
18364 	 *
18365 	 * @return {string} the ISO 4217 code for the currency that this formatter
18366 	 * formats, or undefined if this not a currency formatter
18367 	 */
18368 	getCurrency: function () {
18369 		return this.currencyInfo && this.currencyInfo.getCode();
18370 	},
18371 
18372 	/**
18373 	 * Returns the rounding mode set up in the constructor. The rounding mode
18374 	 * controls how numbers are rounded when the integral or fraction digits
18375 	 * of a number are limited.
18376 	 *
18377 	 * @return {string} the name of the rounding mode used in this formatter
18378 	 */
18379 	getRoundingMode: function () {
18380 		return this.roundingMode;
18381 	},
18382 
18383 	/**
18384 	 * If this formatter is a currency formatter, then the style determines how the
18385 	 * currency is denoted in the formatted output. This method returns the style
18386 	 * that this formatter will produce. (See the constructor comment for more about
18387 	 * the styles.)
18388 	 * @return {string} the name of the style this formatter will use to format
18389 	 * currency amounts, or "undefined" if this formatter is not a currency formatter
18390 	 */
18391 	getStyle: function () {
18392 		return this.style;
18393 	}
18394 };
18395 
18396 
18397 /*< DurationFmt.js */
18398 /*
18399  * DurFmt.js - Date formatter definition
18400  * 
18401  * Copyright © 2012-2015, JEDLSoft
18402  *
18403  * Licensed under the Apache License, Version 2.0 (the "License");
18404  * you may not use this file except in compliance with the License.
18405  * You may obtain a copy of the License at
18406  *
18407  *     http://www.apache.org/licenses/LICENSE-2.0
18408  *
18409  * Unless required by applicable law or agreed to in writing, software
18410  * distributed under the License is distributed on an "AS IS" BASIS,
18411  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18412  *
18413  * See the License for the specific language governing permissions and
18414  * limitations under the License.
18415  */
18416 
18417 /*
18418 !depends 
18419 ilib.js 
18420 Locale.js 
18421 DateFmt.js
18422 IString.js 
18423 ResBundle.js 
18424 LocaleInfo.js
18425 JSUtils.js
18426 Utils.js
18427 */
18428 
18429 // !data dateformats sysres
18430 // !resbundle sysres
18431 
18432 
18433 /**
18434  * @class
18435  * Create a new duration formatter instance. The duration formatter is immutable once
18436  * it is created, but can format as many different durations as needed with the same
18437  * options. Create different duration formatter instances for different purposes
18438  * and then keep them cached for use later if you have more than one duration to
18439  * format.<p>
18440  * 
18441  * Duration formatters format lengths of time. The duration formatter is meant to format 
18442  * durations of such things as the length of a song or a movie or a meeting, or the 
18443  * current position in that song or movie while playing it. If you wish to format a 
18444  * period of time that has a specific start and end date/time, then use a
18445  * [DateRngFmt] instance instead and call its format method.<p>
18446  *  
18447  * The options may contain any of the following properties:
18448  * 
18449  * <ul>
18450  * <li><i>locale</i> - locale to use when formatting the duration. If the locale is
18451  * not specified, then the default locale of the app or web page will be used.
18452  * 
18453  * <li><i>length</i> - Specify the length of the format to use. The length is the approximate size of the 
18454  * formatted string.
18455  * 
18456  * <ul>
18457  * <li><i>short</i> - use a short representation of the duration. This is the most compact format possible for the locale. eg. 1y 1m 1w 1d 1:01:01
18458  * <li><i>medium</i> - use a medium length representation of the duration. This is a slightly longer format. eg. 1 yr 1 mo 1 wk 1 dy 1 hr 1 mi 1 se
18459  * <li><i>long</i> - use a long representation of the duration. This is a fully specified format, but some of the textual 
18460  * parts may still be abbreviated. eg. 1 yr 1 mo 1 wk 1 day 1 hr 1 min 1 sec
18461  * <li><i>full</i> - use a full representation of the duration. This is a fully specified format where all the textual 
18462  * parts are spelled out completely. eg. 1 year, 1 month, 1 week, 1 day, 1 hour, 1 minute and 1 second
18463  * </ul>
18464  * 
18465  * <li><i>style<i> - whether hours, minutes, and seconds should be formatted as a text string
18466  * or as a regular time as on a clock. eg. text is "1 hour, 15 minutes", whereas clock is "1:15:00". Valid
18467  * values for this property are "text" or "clock". Default if this property is not specified
18468  * is "text".
18469  * 
18470  *<li><i>useNative</i> - the flag used to determaine whether to use the native script settings 
18471  * for formatting the numbers .
18472  * 
18473  * <li><i>onLoad</i> - a callback function to call when the format data is fully 
18474  * loaded. When the onLoad option is given, this class will attempt to
18475  * load any missing locale data using the ilib loader callback.
18476  * When the constructor is done (even if the data is already preassembled), the 
18477  * onLoad function is called with the current instance as a parameter, so this
18478  * callback can be used with preassembled or dynamic loading or a mix of the two. 
18479  * 
18480  * <li>sync - tell whether to load any missing locale data synchronously or 
18481  * asynchronously. If this option is given as "false", then the "onLoad"
18482  * callback must be given, as the instance returned from this constructor will
18483  * not be usable for a while.
18484  *  
18485  * <li><i>loadParams</i> - an object containing parameters to pass to the 
18486  * loader callback function when locale data is missing. The parameters are not
18487  * interpretted or modified in any way. They are simply passed along. The object 
18488  * may contain any property/value pairs as long as the calling code is in
18489  * agreement with the loader callback function as to what those parameters mean.
18490  * </ul>
18491  * <p>
18492  * 
18493  * 
18494  * @constructor
18495  * @param {?Object} options options governing the way this date formatter instance works
18496  */
18497 var DurationFmt = function(options) {
18498 	var sync = true;
18499 	var loadParams = undefined;
18500 	
18501 	this.locale = new Locale();
18502 	this.length = "short";
18503 	this.style = "text";
18504 	
18505 	if (options) {
18506 		if (options.locale) {
18507 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
18508 		}
18509 		
18510 		if (options.length) {
18511 			if (options.length === 'short' ||
18512 				options.length === 'medium' ||
18513 				options.length === 'long' ||
18514 				options.length === 'full') {
18515 				this.length = options.length;
18516 			}
18517 		}
18518 		
18519 		if (options.style) {
18520 			if (options.style === 'text' || options.style === 'clock') {
18521 				this.style = options.style;
18522 			}
18523 		}
18524 		
18525 		if (typeof(options.sync) !== 'undefined') {
18526 			sync = (options.sync == true);
18527 		}
18528 		
18529 		if (typeof(options.useNative) === 'boolean') {
18530 			this.useNative = options.useNative;
18531 		}
18532 		
18533 		loadParams = options.loadParams;
18534 	}
18535 	
18536 	new ResBundle({
18537 		locale: this.locale,
18538 		name: "sysres",
18539 		sync: sync,
18540 		loadParams: loadParams,
18541 		onLoad: ilib.bind(this, function (sysres) {
18542 			switch (this.length) {
18543 				case 'short':
18544 					this.components = {
18545 						year: sysres.getString("#{num}y"),
18546 						month: sysres.getString("#{num}m", "durationShortMonths"),
18547 						week: sysres.getString("#{num}w"),
18548 						day: sysres.getString("#{num}d"),
18549 						hour: sysres.getString("#{num}h"),
18550 						minute: sysres.getString("#{num}m", "durationShortMinutes"),
18551 						second: sysres.getString("#{num}s"),
18552 						millisecond: sysres.getString("#{num}m", "durationShortMillis"),
18553 						separator: sysres.getString(" ", "separatorShort"),
18554 						finalSeparator: "" // not used at this length
18555 					};
18556 					break;
18557 					
18558 				case 'medium':
18559 					this.components = {
18560 						year: sysres.getString("1#1 yr|#{num} yrs", "durationMediumYears"),
18561 						month: sysres.getString("1#1 mo|#{num} mos"),
18562 						week: sysres.getString("1#1 wk|#{num} wks", "durationMediumWeeks"),
18563 						day: sysres.getString("1#1 dy|#{num} dys"),
18564 						hour: sysres.getString("1#1 hr|#{num} hrs", "durationMediumHours"),
18565 						minute: sysres.getString("1#1 mi|#{num} min"),
18566 						second: sysres.getString("1#1 se|#{num} sec"),
18567 						millisecond: sysres.getString("#{num} ms", "durationMediumMillis"),
18568 						separator: sysres.getString(" ", "separatorMedium"),
18569 						finalSeparator: "" // not used at this length
18570 					};
18571 					break;
18572 					
18573 				case 'long':
18574 					this.components = {
18575 						year: sysres.getString("1#1 yr|#{num} yrs"),
18576 						month: sysres.getString("1#1 mon|#{num} mons"),
18577 						week: sysres.getString("1#1 wk|#{num} wks"),
18578 						day: sysres.getString("1#1 day|#{num} days", "durationLongDays"),
18579 						hour: sysres.getString("1#1 hr|#{num} hrs"),
18580 						minute: sysres.getString("1#1 min|#{num} min"),
18581 						second: sysres.getString("1#1 sec|#{num} sec"),
18582 						millisecond: sysres.getString("#{num} ms"),
18583 						separator: sysres.getString(", ", "separatorLong"),
18584 						finalSeparator: "" // not used at this length
18585 					};
18586 					break;
18587 					
18588 				case 'full':
18589 					this.components = {
18590 						year: sysres.getString("1#1 year|#{num} years"),
18591 						month: sysres.getString("1#1 month|#{num} months"),
18592 						week: sysres.getString("1#1 week|#{num} weeks"),
18593 						day: sysres.getString("1#1 day|#{num} days"),
18594 						hour: sysres.getString("1#1 hour|#{num} hours"),
18595 						minute: sysres.getString("1#1 minute|#{num} minutes"),
18596 						second: sysres.getString("1#1 second|#{num} seconds"),
18597 						millisecond: sysres.getString("1#1 millisecond|#{num} milliseconds"),
18598 						separator: sysres.getString(", ", "separatorFull"),
18599 						finalSeparator: sysres.getString(" and ", "finalSeparatorFull")
18600 					};
18601 					break;
18602 			}
18603 			
18604 			if (this.style === 'clock') {
18605 				new DateFmt({
18606 					locale: this.locale,
18607 					calendar: "gregorian",
18608 					type: "time",
18609 					time: "ms",
18610 					sync: sync,
18611 					loadParams: loadParams,
18612 					useNative: this.useNative,
18613 					onLoad: ilib.bind(this, function (fmtMS) {
18614 						this.timeFmtMS = fmtMS;
18615 						new DateFmt({
18616 							locale: this.locale,
18617 							calendar: "gregorian",
18618 							type: "time",
18619 							time: "hm",
18620 							sync: sync,
18621 							loadParams: loadParams,
18622 							useNative: this.useNative,
18623 							onLoad: ilib.bind(this, function (fmtHM) {
18624 								this.timeFmtHM = fmtHM;		
18625 								new DateFmt({
18626 									locale: this.locale,
18627 									calendar: "gregorian",
18628 									type: "time",
18629 									time: "hms",
18630 									sync: sync,
18631 									loadParams: loadParams,
18632 									useNative: this.useNative,
18633 									onLoad: ilib.bind(this, function (fmtHMS) {
18634 										this.timeFmtHMS = fmtHMS;		
18635 
18636 										// munge with the template to make sure that the hours are not formatted mod 12
18637 										this.timeFmtHM.template = this.timeFmtHM.template.replace(/hh?/, 'H');
18638 										this.timeFmtHM.templateArr = this.timeFmtHM._tokenize(this.timeFmtHM.template);
18639 										this.timeFmtHMS.template = this.timeFmtHMS.template.replace(/hh?/, 'H');
18640 										this.timeFmtHMS.templateArr = this.timeFmtHMS._tokenize(this.timeFmtHMS.template);
18641 										
18642 										this._init(this.timeFmtHM.locinfo, options && options.onLoad);
18643 									})
18644 								});
18645 							})
18646 						});
18647 					})
18648 				});
18649 				return;
18650 			}
18651 
18652 			new LocaleInfo(this.locale, {
18653 				sync: sync,
18654 				loadParams: loadParams,
18655 				onLoad: ilib.bind(this, function (li) {
18656 					this._init(li, options && options.onLoad);
18657 				})
18658 			});
18659 		})
18660 	});
18661 };
18662 
18663 /**
18664  * @private
18665  * @static
18666  */
18667 DurationFmt.complist = {
18668 	"text": ["year", "month", "week", "day", "hour", "minute", "second", "millisecond"],
18669 	"clock": ["year", "month", "week", "day"]
18670 };
18671 
18672 /**
18673  * @private
18674  */
18675 DurationFmt.prototype._mapDigits = function(str) {
18676 	if (this.useNative && this.digits) {
18677 		return JSUtils.mapString(str.toString(), this.digits);
18678 	}
18679 	return str;
18680 };
18681 
18682 /**
18683  * @private
18684  * @param {LocaleInfo} locinfo
18685  * @param {function(DurationFmt)|undefined} onLoad
18686  */
18687 DurationFmt.prototype._init = function(locinfo, onLoad) {
18688 	var digits;
18689 	var scriptInfo = new ScriptInfo(locinfo.getScript());
18690 	this.scriptDirection = scriptInfo.getScriptDirection();
18691 
18692 	if (typeof(this.useNative) === 'boolean') {
18693 		// if the caller explicitly said to use native or not, honour that despite what the locale data says...
18694 		if (this.useNative) {
18695 			digits = locinfo.getNativeDigits();
18696 			if (digits) {
18697 				this.digits = digits;
18698 			}
18699 		}
18700 	} else if (locinfo.getDigitsStyle() === "native") {
18701 		// else if the locale usually uses native digits, then use them 
18702 		digits = locinfo.getNativeDigits();
18703 		if (digits) {
18704 			this.useNative = true;
18705 			this.digits = digits;
18706 		}
18707 	} // else use western digits always
18708 
18709 	if (typeof(onLoad) === 'function') {
18710 		onLoad(this);
18711 	}
18712 };
18713 
18714 /**
18715  * Format a duration according to the format template of this formatter instance.<p>
18716  * 
18717  * The components parameter should be an object that contains any or all of these 
18718  * numeric properties:
18719  * 
18720  * <ul>
18721  * <li>year
18722  * <li>month
18723  * <li>week
18724  * <li>day
18725  * <li>hour
18726  * <li>minute
18727  * <li>second
18728  * </ul>
18729  * <p>
18730  *
18731  * When a property is left out of the components parameter or has a value of 0, it will not
18732  * be formatted into the output string, except for times that include 0 minutes and 0 seconds.
18733  * 
18734  * This formatter will not ensure that numbers for each component property is within the
18735  * valid range for that component. This allows you to format durations that are longer
18736  * than normal range. For example, you could format a duration has being "33 hours" rather
18737  * than "1 day, 9 hours".
18738  * 
18739  * @param {Object} components date/time components to be formatted into a duration string
18740  * @return {IString} a string with the duration formatted according to the style and 
18741  * locale set up for this formatter instance. If the components parameter is empty or 
18742  * undefined, an empty string is returned.
18743  */
18744 DurationFmt.prototype.format = function (components) {
18745 	var i, list, temp, fmt, secondlast = true, str = "";
18746 	
18747 	list = DurationFmt.complist[this.style];
18748 	//for (i = 0; i < list.length; i++) {
18749 	for (i = list.length-1; i >= 0; i--) {
18750 		//console.log("Now dealing with " + list[i]);
18751 		if (typeof(components[list[i]]) !== 'undefined' && components[list[i]] != 0) {
18752 			if (str.length > 0) {
18753 				str = ((this.length === 'full' && secondlast) ? this.components.finalSeparator : this.components.separator) + str;
18754 				secondlast = false;
18755 			}
18756 			str = this.components[list[i]].formatChoice(components[list[i]], {num: this._mapDigits(components[list[i]])}) + str;
18757 		}
18758 	}
18759 
18760 	if (this.style === 'clock') {
18761 		if (typeof(components.hour) !== 'undefined') {
18762 			fmt = (typeof(components.second) !== 'undefined') ? this.timeFmtHMS : this.timeFmtHM;
18763 		} else {
18764 			fmt = this.timeFmtMS;
18765 		}
18766 				
18767 		if (str.length > 0) {
18768 			str += this.components.separator;
18769 		}
18770 		str += fmt._formatTemplate(components, fmt.templateArr);
18771 	}
18772 
18773 	if (this.scriptDirection === 'rtl') {
18774 		str = "\u200F" + str;
18775 	}
18776 	return new IString(str);
18777 };
18778 
18779 /**
18780  * Return the locale that was used to construct this duration formatter object. If the
18781  * locale was not given as parameter to the constructor, this method returns the default
18782  * locale of the system.
18783  * 
18784  * @return {Locale} locale that this duration formatter was constructed with
18785  */
18786 DurationFmt.prototype.getLocale = function () {
18787 	return this.locale;
18788 };
18789 
18790 /**
18791  * Return the length that was used to construct this duration formatter object. If the
18792  * length was not given as parameter to the constructor, this method returns the default
18793  * length. Valid values are "short", "medium", "long", and "full".
18794  * 
18795  * @return {string} length that this duration formatter was constructed with
18796  */
18797 DurationFmt.prototype.getLength = function () {
18798 	return this.length;
18799 };
18800 
18801 /**
18802  * Return the style that was used to construct this duration formatter object. Returns
18803  * one of "text" or "clock".
18804  * 
18805  * @return {string} style that this duration formatter was constructed with
18806  */
18807 DurationFmt.prototype.getStyle = function () {
18808 	return this.style;
18809 };
18810 
18811 
18812 
18813 /*< isAlpha.js */
18814 /*
18815  * ctype.islpha.js - Character type is alphabetic
18816  * 
18817  * Copyright © 2012-2015, JEDLSoft
18818  *
18819  * Licensed under the Apache License, Version 2.0 (the "License");
18820  * you may not use this file except in compliance with the License.
18821  * You may obtain a copy of the License at
18822  *
18823  *     http://www.apache.org/licenses/LICENSE-2.0
18824  *
18825  * Unless required by applicable law or agreed to in writing, software
18826  * distributed under the License is distributed on an "AS IS" BASIS,
18827  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18828  *
18829  * See the License for the specific language governing permissions and
18830  * limitations under the License.
18831  */
18832 
18833 // !depends CType.js IString.js ilib.js
18834 
18835 // !data ctype_l
18836 
18837 
18838 /**
18839  * Return whether or not the first character is alphabetic.<p>
18840  * 
18841  * @static
18842  * @param {string|IString|number} ch character or code point to examine
18843  * @return {boolean} true if the first character is alphabetic.
18844  */
18845 var isAlpha = function (ch) {
18846 	var num;
18847 	switch (typeof(ch)) {
18848 		case 'number':
18849 			num = ch;
18850 			break;
18851 		case 'string':
18852 			num = IString.toCodePoint(ch, 0);
18853 			break;
18854 		case 'undefined':
18855 			return false;
18856 		default:
18857 			num = ch._toCodePoint(0);
18858 			break;
18859 	}
18860 	return CType._inRange(num, 'Lu', ilib.data.ctype_l) ||
18861 		CType._inRange(num, 'Ll', ilib.data.ctype_l) ||
18862 		CType._inRange(num, 'Lt', ilib.data.ctype_l) ||
18863 		CType._inRange(num, 'Lm', ilib.data.ctype_l) ||
18864 		CType._inRange(num, 'Lo', ilib.data.ctype_l);
18865 };
18866 
18867 /**
18868  * @protected
18869  * @param {boolean} sync
18870  * @param {Object|undefined} loadParams
18871  * @param {function(*)|undefined} onLoad
18872  */
18873 isAlpha._init = function (sync, loadParams, onLoad) {
18874 	CType._load("ctype_l", sync, loadParams, onLoad);
18875 };
18876 
18877 
18878 /*< isAlnum.js */
18879 /*
18880  * isAlnum.js - Character type is alphanumeric
18881  * 
18882  * Copyright © 2012-2015, JEDLSoft
18883  *
18884  * Licensed under the Apache License, Version 2.0 (the "License");
18885  * you may not use this file except in compliance with the License.
18886  * You may obtain a copy of the License at
18887  *
18888  *     http://www.apache.org/licenses/LICENSE-2.0
18889  *
18890  * Unless required by applicable law or agreed to in writing, software
18891  * distributed under the License is distributed on an "AS IS" BASIS,
18892  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18893  *
18894  * See the License for the specific language governing permissions and
18895  * limitations under the License.
18896  */
18897 
18898 // !depends CType.js IString.js isAlpha.js isDigit.js
18899 
18900 
18901 /**
18902  * Return whether or not the first character is alphabetic or numeric.<p>
18903  * 
18904  * @static
18905  * @param {string|IString|number} ch character or code point to examine
18906  * @return {boolean} true if the first character is alphabetic or numeric
18907  */
18908 var isAlnum = function (ch) {
18909 	var num;
18910 	switch (typeof(ch)) {
18911 		case 'number':
18912 			num = ch;
18913 			break;
18914 		case 'string':
18915 			num = IString.toCodePoint(ch, 0);
18916 			break;
18917 		case 'undefined':
18918 			return false;
18919 		default:
18920 			num = ch._toCodePoint(0);
18921 			break;
18922 	}
18923 	return isAlpha(num) || isDigit(num);
18924 };
18925 
18926 /**
18927  * @protected
18928  * @param {boolean} sync
18929  * @param {Object|undefined} loadParams
18930  * @param {function(*)|undefined} onLoad
18931  */
18932 isAlnum._init = function (sync, loadParams, onLoad) {
18933 	isAlpha._init(sync, loadParams, function () {
18934 		isDigit._init(sync, loadParams, onLoad);
18935 	});
18936 };
18937 
18938 
18939 
18940 /*< isAscii.js */
18941 /*
18942  * isAscii.js - Character type is ASCII
18943  * 
18944  * Copyright © 2012-2015, JEDLSoft
18945  *
18946  * Licensed under the Apache License, Version 2.0 (the "License");
18947  * you may not use this file except in compliance with the License.
18948  * You may obtain a copy of the License at
18949  *
18950  *     http://www.apache.org/licenses/LICENSE-2.0
18951  *
18952  * Unless required by applicable law or agreed to in writing, software
18953  * distributed under the License is distributed on an "AS IS" BASIS,
18954  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18955  *
18956  * See the License for the specific language governing permissions and
18957  * limitations under the License.
18958  */
18959 
18960 // !depends CType.js IString.js ilib.js
18961 
18962 // !data ctype
18963 
18964 
18965 /**
18966  * Return whether or not the first character is in the ASCII range.<p>
18967  * 
18968  * @static
18969  * @param {string|IString|number} ch character or code point to examine
18970  * @return {boolean} true if the first character is in the ASCII range.
18971  */
18972 var isAscii = function (ch) {
18973 	var num;
18974 	switch (typeof(ch)) {
18975 		case 'number':
18976 			num = ch;
18977 			break;
18978 		case 'string':
18979 			num = IString.toCodePoint(ch, 0);
18980 			break;
18981 		case 'undefined':
18982 			return false;
18983 		default:
18984 			num = ch._toCodePoint(0);
18985 			break;
18986 	}
18987 	return CType._inRange(num, 'ascii', ilib.data.ctype);
18988 };
18989 
18990 /**
18991  * @protected
18992  * @param {boolean} sync
18993  * @param {Object|undefined} loadParams
18994  * @param {function(*)|undefined} onLoad
18995  */
18996 isAscii._init = function (sync, loadParams, onLoad) {
18997 	CType._init(sync, loadParams, onLoad);
18998 };
18999 
19000 
19001 /*< isBlank.js */
19002 /*
19003  * isBlank.js - Character type is blank
19004  * 
19005  * Copyright © 2012-2015, JEDLSoft
19006  *
19007  * Licensed under the Apache License, Version 2.0 (the "License");
19008  * you may not use this file except in compliance with the License.
19009  * You may obtain a copy of the License at
19010  *
19011  *     http://www.apache.org/licenses/LICENSE-2.0
19012  *
19013  * Unless required by applicable law or agreed to in writing, software
19014  * distributed under the License is distributed on an "AS IS" BASIS,
19015  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19016  *
19017  * See the License for the specific language governing permissions and
19018  * limitations under the License.
19019  */
19020 
19021 // !depends CType.js IString.js ilib.js
19022 
19023 // !data ctype
19024 
19025 
19026 /**
19027  * Return whether or not the first character is a blank character.<p>
19028  * 
19029  * @static
19030  * ie. a space or a tab.
19031  * @param {string|IString|number} ch character or code point to examine
19032  * @return {boolean} true if the first character is a blank character.
19033  */
19034 var isBlank = function (ch) {
19035 	var num;
19036 	switch (typeof(ch)) {
19037 		case 'number':
19038 			num = ch;
19039 			break;
19040 		case 'string':
19041 			num = IString.toCodePoint(ch, 0);
19042 			break;
19043 		case 'undefined':
19044 			return false;
19045 		default:
19046 			num = ch._toCodePoint(0);
19047 			break;
19048 	}
19049 	return CType._inRange(num, 'blank', ilib.data.ctype);
19050 };
19051 
19052 /**
19053  * @protected
19054  * @param {boolean} sync
19055  * @param {Object|undefined} loadParams
19056  * @param {function(*)|undefined} onLoad
19057  */
19058 isBlank._init = function (sync, loadParams, onLoad) {
19059 	CType._init(sync, loadParams, onLoad);
19060 };
19061 
19062 
19063 /*< isCntrl.js */
19064 /*
19065  * isCntrl.js - Character type is control character
19066  * 
19067  * Copyright © 2012-2015, JEDLSoft
19068  *
19069  * Licensed under the Apache License, Version 2.0 (the "License");
19070  * you may not use this file except in compliance with the License.
19071  * You may obtain a copy of the License at
19072  *
19073  *     http://www.apache.org/licenses/LICENSE-2.0
19074  *
19075  * Unless required by applicable law or agreed to in writing, software
19076  * distributed under the License is distributed on an "AS IS" BASIS,
19077  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19078  *
19079  * See the License for the specific language governing permissions and
19080  * limitations under the License.
19081  */
19082 
19083 // !depends CType.js IString.js ilib.js
19084 
19085 // !data ctype_c
19086 
19087 
19088 /**
19089  * Return whether or not the first character is a control character.<p>
19090  * 
19091  * @static
19092  * @param {string|IString|number} ch character or code point to examine
19093  * @return {boolean} true if the first character is a control character.
19094  */
19095 var isCntrl = function (ch) {
19096 	var num;
19097 	switch (typeof(ch)) {
19098 		case 'number':
19099 			num = ch;
19100 			break;
19101 		case 'string':
19102 			num = IString.toCodePoint(ch, 0);
19103 			break;
19104 		case 'undefined':
19105 			return false;
19106 		default:
19107 			num = ch._toCodePoint(0);
19108 			break;
19109 	}
19110 	return CType._inRange(num, 'Cc', ilib.data.ctype_c);
19111 };
19112 
19113 /**
19114  * @protected
19115  * @param {boolean} sync
19116  * @param {Object|undefined} loadParams
19117  * @param {function(*)|undefined} onLoad
19118  */
19119 isCntrl._init = function (sync, loadParams, onLoad) {
19120 	CType._load("ctype_c", sync, loadParams, onLoad);
19121 };
19122 
19123 
19124 /*< isGraph.js */
19125 /*
19126  * isGraph.js - Character type is graph char
19127  * 
19128  * Copyright © 2012-2015, JEDLSoft
19129  *
19130  * Licensed under the Apache License, Version 2.0 (the "License");
19131  * you may not use this file except in compliance with the License.
19132  * You may obtain a copy of the License at
19133  *
19134  *     http://www.apache.org/licenses/LICENSE-2.0
19135  *
19136  * Unless required by applicable law or agreed to in writing, software
19137  * distributed under the License is distributed on an "AS IS" BASIS,
19138  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19139  *
19140  * See the License for the specific language governing permissions and
19141  * limitations under the License.
19142  */
19143 
19144 // !depends IString.js isSpace.js isCntrl.js ilib.js
19145 
19146 
19147 /**
19148  * Return whether or not the first character is any printable character
19149  * other than space.<p>
19150  * 
19151  * @static
19152  * @param {string|IString|number} ch character or code point to examine
19153  * @return {boolean} true if the first character is any printable character
19154  * other than space. 
19155  */
19156 var isGraph = function (ch) {
19157 	var num;
19158 	switch (typeof(ch)) {
19159 		case 'number':
19160 			num = ch;
19161 			break;
19162 		case 'string':
19163 			num = IString.toCodePoint(ch, 0);
19164 			break;
19165 		case 'undefined':
19166 			return false;
19167 		default:
19168 			num = ch._toCodePoint(0);
19169 			break;
19170 	}
19171 	return typeof(ch) !== 'undefined' && ch.length > 0 && !isSpace(num) && !isCntrl(num);
19172 };
19173 
19174 /**
19175  * @protected
19176  * @param {boolean} sync
19177  * @param {Object|undefined} loadParams
19178  * @param {function(*)|undefined} onLoad
19179  */
19180 isGraph._init = function (sync, loadParams, onLoad) {
19181 	isSpace._init(sync, loadParams, function () {
19182 		isCntrl._init(sync, loadParams, onLoad);
19183 	});
19184 };
19185 
19186 
19187 
19188 /*< isIdeo.js */
19189 /*
19190  * CType.js - Character type definitions
19191  * 
19192  * Copyright © 2012-2015, JEDLSoft
19193  *
19194  * Licensed under the Apache License, Version 2.0 (the "License");
19195  * you may not use this file except in compliance with the License.
19196  * You may obtain a copy of the License at
19197  *
19198  *     http://www.apache.org/licenses/LICENSE-2.0
19199  *
19200  * Unless required by applicable law or agreed to in writing, software
19201  * distributed under the License is distributed on an "AS IS" BASIS,
19202  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19203  *
19204  * See the License for the specific language governing permissions and
19205  * limitations under the License.
19206  */
19207 
19208 // !depends CType.js IString.js
19209 
19210 // !data ctype
19211 
19212 
19213 /**
19214  * Return whether or not the first character is an ideographic character.<p>
19215  * 
19216  * @static
19217  * @param {string|IString|number} ch character or code point to examine
19218  * @return {boolean} true if the first character is an ideographic character.
19219  */
19220 var isIdeo = function (ch) {
19221 	var num;
19222 	switch (typeof(ch)) {
19223 		case 'number':
19224 			num = ch;
19225 			break;
19226 		case 'string':
19227 			num = IString.toCodePoint(ch, 0);
19228 			break;
19229 		case 'undefined':
19230 			return false;
19231 		default:
19232 			num = ch._toCodePoint(0);
19233 			break;
19234 	}
19235 
19236 	return CType._inRange(num, 'cjk', ilib.data.ctype) ||
19237 		CType._inRange(num, 'cjkradicals', ilib.data.ctype) ||
19238 		CType._inRange(num, 'enclosedcjk', ilib.data.ctype) ||
19239 		CType._inRange(num, 'cjkpunct', ilib.data.ctype) ||
19240 		CType._inRange(num, 'cjkcompatibility', ilib.data.ctype);
19241 };
19242 
19243 /**
19244  * @protected
19245  * @param {boolean} sync
19246  * @param {Object|undefined} loadParams
19247  * @param {function(*)|undefined} onLoad
19248  */
19249 isIdeo._init = function (sync, loadParams, onLoad) {
19250 	CType._init(sync, loadParams, onLoad);
19251 };
19252 
19253 
19254 /*< isLower.js */
19255 /*
19256  * isLower.js - Character type is lower case letter
19257  * 
19258  * Copyright © 2012-2015, JEDLSoft
19259  *
19260  * Licensed under the Apache License, Version 2.0 (the "License");
19261  * you may not use this file except in compliance with the License.
19262  * You may obtain a copy of the License at
19263  *
19264  *     http://www.apache.org/licenses/LICENSE-2.0
19265  *
19266  * Unless required by applicable law or agreed to in writing, software
19267  * distributed under the License is distributed on an "AS IS" BASIS,
19268  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19269  *
19270  * See the License for the specific language governing permissions and
19271  * limitations under the License.
19272  */
19273 
19274 // !depends CType.js IString.js
19275 
19276 // !data ctype_l
19277 
19278 
19279 
19280 /**
19281  * Return whether or not the first character is lower-case. For alphabetic
19282  * characters in scripts that do not make a distinction between upper- and 
19283  * lower-case, this function always returns true.<p>
19284  * 
19285  * @static
19286  * @param {string|IString|number} ch character or code point to examine
19287  * @return {boolean} true if the first character is lower-case.
19288  */
19289 var isLower = function (ch) {
19290 	var num;
19291 	switch (typeof(ch)) {
19292 		case 'number':
19293 			num = ch;
19294 			break;
19295 		case 'string':
19296 			num = IString.toCodePoint(ch, 0);
19297 			break;
19298 		case 'undefined':
19299 			return false;
19300 		default:
19301 			num = ch._toCodePoint(0);
19302 			break;
19303 	}
19304 
19305 	return CType._inRange(num, 'Ll', ilib.data.ctype_l);
19306 };
19307 
19308 /**
19309  * @protected
19310  * @param {boolean} sync
19311  * @param {Object|undefined} loadParams
19312  * @param {function(*)|undefined} onLoad
19313  */
19314 isLower._init = function (sync, loadParams, onLoad) {
19315 	CType._load("ctype_l", sync, loadParams, onLoad);
19316 };
19317 
19318 
19319 /*< isPrint.js */
19320 /*
19321  * isPrint.js - Character type is printable char
19322  * 
19323  * Copyright © 2012-2015, JEDLSoft
19324  *
19325  * Licensed under the Apache License, Version 2.0 (the "License");
19326  * you may not use this file except in compliance with the License.
19327  * You may obtain a copy of the License at
19328  *
19329  *     http://www.apache.org/licenses/LICENSE-2.0
19330  *
19331  * Unless required by applicable law or agreed to in writing, software
19332  * distributed under the License is distributed on an "AS IS" BASIS,
19333  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19334  *
19335  * See the License for the specific language governing permissions and
19336  * limitations under the License.
19337  */
19338 
19339 // !depends CType.js isCntrl.js
19340 
19341 
19342 /**
19343  * Return whether or not the first character is any printable character,
19344  * including space.<p>
19345  * 
19346  * @static
19347  * @param {string|IString|number} ch character or code point to examine
19348  * @return {boolean} true if the first character is printable.
19349  */
19350 var isPrint = function (ch) {
19351 	return typeof(ch) !== 'undefined' && ch.length > 0 && !isCntrl(ch);
19352 };
19353 
19354 /**
19355  * @protected
19356  * @param {boolean} sync
19357  * @param {Object|undefined} loadParams
19358  * @param {function(*)|undefined} onLoad
19359  */
19360 isPrint._init = function (sync, loadParams, onLoad) {
19361 	isCntrl._init(sync, loadParams, onLoad);
19362 };
19363 
19364 
19365 /*< isPunct.js */
19366 /*
19367  * isPunct.js - Character type is punctuation
19368  * 
19369  * Copyright © 2012-2015, JEDLSoft
19370  *
19371  * Licensed under the Apache License, Version 2.0 (the "License");
19372  * you may not use this file except in compliance with the License.
19373  * You may obtain a copy of the License at
19374  *
19375  *     http://www.apache.org/licenses/LICENSE-2.0
19376  *
19377  * Unless required by applicable law or agreed to in writing, software
19378  * distributed under the License is distributed on an "AS IS" BASIS,
19379  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19380  *
19381  * See the License for the specific language governing permissions and
19382  * limitations under the License.
19383  */
19384 
19385 // !depends CType.js IString.js
19386 
19387 // !data ctype_p
19388 
19389 
19390 
19391 /**
19392  * Return whether or not the first character is punctuation.<p>
19393  * 
19394  * @static
19395  * @param {string|IString|number} ch character or code point to examine
19396  * @return {boolean} true if the first character is punctuation.
19397  */
19398 var isPunct = function (ch) {
19399 	var num;
19400 	switch (typeof(ch)) {
19401 		case 'number':
19402 			num = ch;
19403 			break;
19404 		case 'string':
19405 			num = IString.toCodePoint(ch, 0);
19406 			break;
19407 		case 'undefined':
19408 			return false;
19409 		default:
19410 			num = ch._toCodePoint(0);
19411 			break;
19412 	}
19413 
19414 	return CType._inRange(num, 'Pd', ilib.data.ctype_p) ||
19415 		CType._inRange(num, 'Ps', ilib.data.ctype_p) ||
19416 		CType._inRange(num, 'Pe', ilib.data.ctype_p) ||
19417 		CType._inRange(num, 'Pc', ilib.data.ctype_p) ||
19418 		CType._inRange(num, 'Po', ilib.data.ctype_p) ||
19419 		CType._inRange(num, 'Pi', ilib.data.ctype_p) ||
19420 		CType._inRange(num, 'Pf', ilib.data.ctype_p);
19421 };
19422 
19423 /**
19424  * @protected
19425  * @param {boolean} sync
19426  * @param {Object|undefined} loadParams
19427  * @param {function(*)|undefined} onLoad
19428  */
19429 isPunct._init = function (sync, loadParams, onLoad) {
19430 	CType._load("ctype_p", sync, loadParams, onLoad);
19431 };
19432 
19433 
19434 
19435 /*< isUpper.js */
19436 /*
19437  * isUpper.js - Character type is upper-case letter
19438  * 
19439  * Copyright © 2012-2015, JEDLSoft
19440  *
19441  * Licensed under the Apache License, Version 2.0 (the "License");
19442  * you may not use this file except in compliance with the License.
19443  * You may obtain a copy of the License at
19444  *
19445  *     http://www.apache.org/licenses/LICENSE-2.0
19446  *
19447  * Unless required by applicable law or agreed to in writing, software
19448  * distributed under the License is distributed on an "AS IS" BASIS,
19449  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19450  *
19451  * See the License for the specific language governing permissions and
19452  * limitations under the License.
19453  */
19454 
19455 // !depends CType.js IString.js
19456 
19457 // !data ctype_l
19458 
19459 
19460 
19461 /**
19462  * Return whether or not the first character is upper-case. For alphabetic
19463  * characters in scripts that do not make a distinction between upper- and 
19464  * lower-case, this function always returns true.<p>
19465  * 
19466  * @static
19467  * @param {string|IString|number} ch character or code point to examine
19468  * @return {boolean} true if the first character is upper-case.
19469  */
19470 var isUpper = function (ch) {
19471 	var num;
19472 	switch (typeof(ch)) {
19473 		case 'number':
19474 			num = ch;
19475 			break;
19476 		case 'string':
19477 			num = IString.toCodePoint(ch, 0);
19478 			break;
19479 		case 'undefined':
19480 			return false;
19481 		default:
19482 			num = ch._toCodePoint(0);
19483 			break;
19484 	}
19485 
19486 	return CType._inRange(num, 'Lu', ilib.data.ctype_l);
19487 };
19488 
19489 /**
19490  * @protected
19491  * @param {boolean} sync
19492  * @param {Object|undefined} loadParams
19493  * @param {function(*)|undefined} onLoad
19494  */
19495 isUpper._init = function (sync, loadParams, onLoad) {
19496 	CType._load("ctype_l", sync, loadParams, onLoad);
19497 };
19498 
19499 
19500 /*< isXdigit.js */
19501 /*
19502  * isXdigit.js - Character type is hex digit
19503  * 
19504  * Copyright © 2012-2015, JEDLSoft
19505  *
19506  * Licensed under the Apache License, Version 2.0 (the "License");
19507  * you may not use this file except in compliance with the License.
19508  * You may obtain a copy of the License at
19509  *
19510  *     http://www.apache.org/licenses/LICENSE-2.0
19511  *
19512  * Unless required by applicable law or agreed to in writing, software
19513  * distributed under the License is distributed on an "AS IS" BASIS,
19514  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19515  *
19516  * See the License for the specific language governing permissions and
19517  * limitations under the License.
19518  */
19519 
19520 // !depends CType.js IString.js
19521 
19522 // !data ctype
19523 
19524 
19525 
19526 /**
19527  * Return whether or not the first character is a hexadecimal digit written
19528  * in the Latin script. (0-9 or A-F)<p>
19529  * 
19530  * @static
19531  * @param {string|IString|number} ch character or code point to examine
19532  * @return {boolean} true if the first character is a hexadecimal digit written
19533  * in the Latin script.
19534  */
19535 var isXdigit = function (ch) {
19536 	var num;
19537 	switch (typeof(ch)) {
19538 		case 'number':
19539 			num = ch;
19540 			break;
19541 		case 'string':
19542 			num = IString.toCodePoint(ch, 0);
19543 			break;
19544 		case 'undefined':
19545 			return false;
19546 		default:
19547 			num = ch._toCodePoint(0);
19548 			break;
19549 	}
19550 
19551 	return CType._inRange(num, 'xdigit', ilib.data.ctype);
19552 };
19553 
19554 /**
19555  * @protected
19556  * @param {boolean} sync
19557  * @param {Object|undefined} loadParams
19558  * @param {function(*)|undefined} onLoad
19559  */
19560 isXdigit._init = function (sync, loadParams, onLoad) {
19561 	CType._init(sync, loadParams, onLoad);
19562 };
19563 
19564 
19565 /*< isScript.js */
19566 /*
19567  * isScript.js - Character type is script
19568  * 
19569  * Copyright © 2012-2015, JEDLSoft
19570  *
19571  * Licensed under the Apache License, Version 2.0 (the "License");
19572  * you may not use this file except in compliance with the License.
19573  * You may obtain a copy of the License at
19574  *
19575  *     http://www.apache.org/licenses/LICENSE-2.0
19576  *
19577  * Unless required by applicable law or agreed to in writing, software
19578  * distributed under the License is distributed on an "AS IS" BASIS,
19579  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19580  *
19581  * See the License for the specific language governing permissions and
19582  * limitations under the License.
19583  */
19584 
19585 // !depends CType.js IString.js
19586 
19587 // !data scriptToRange
19588 
19589 
19590 
19591 /**
19592  * Return whether or not the first character in the given string is 
19593  * in the given script. The script is given as the 4-letter ISO
19594  * 15924 script code.<p>
19595  * 
19596  * @static
19597  * @param {string|IString|number} ch character or code point to examine
19598  * @param {string} script the 4-letter ISO 15924 to query against
19599  * @return {boolean} true if the first character is in the given script, and
19600  * false otherwise
19601  */
19602 var isScript = function (ch, script) {
19603 	var num;
19604 	switch (typeof(ch)) {
19605 		case 'number':
19606 			num = ch;
19607 			break;
19608 		case 'string':
19609 			num = IString.toCodePoint(ch, 0);
19610 			break;
19611 		case 'undefined':
19612 			return false;
19613 		default:
19614 			num = ch._toCodePoint(0);
19615 			break;
19616 	}
19617 
19618 	return CType._inRange(num, script, ilib.data.scriptToRange);
19619 };
19620 
19621 /**
19622  * @protected
19623  * @param {boolean} sync
19624  * @param {Object|undefined} loadParams
19625  * @param {function(*)|undefined} onLoad
19626  */
19627 isScript._init = function (sync, loadParams, onLoad) {
19628 	CType._load("scriptToRange", sync, loadParams, onLoad);
19629 };
19630 
19631 
19632 /*< ScriptInfo.js */
19633 /*
19634  * ScriptInfo.js - information about scripts
19635  * 
19636  * Copyright © 2012-2017, JEDLSoft
19637  *
19638  * Licensed under the Apache License, Version 2.0 (the "License");
19639  * you may not use this file except in compliance with the License.
19640  * You may obtain a copy of the License at
19641  *
19642  *     http://www.apache.org/licenses/LICENSE-2.0
19643  *
19644  * Unless required by applicable law or agreed to in writing, software
19645  * distributed under the License is distributed on an "AS IS" BASIS,
19646  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19647  *
19648  * See the License for the specific language governing permissions and
19649  * limitations under the License.
19650  */
19651 
19652 // !depends ilib.js Utils.js
19653 
19654 // !data scripts
19655 
19656 
19657 /**
19658  * @class
19659  * Create a new script info instance. This class encodes information about
19660  * scripts, which are sets of characters used in a writing system.<p>
19661  * 
19662  * The options object may contain any of the following properties:
19663  * 
19664  * <ul>
19665  * <li><i>onLoad</i> - a callback function to call when the script info object is fully 
19666  * loaded. When the onLoad option is given, the script info object will attempt to
19667  * load any missing locale data using the ilib loader callback.
19668  * When the constructor is done (even if the data is already preassembled), the 
19669  * onLoad function is called with the current instance as a parameter, so this
19670  * callback can be used with preassembled or dynamic loading or a mix of the two.
19671  * 
19672  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
19673  * asynchronously. If this option is given as "false", then the "onLoad"
19674  * callback must be given, as the instance returned from this constructor will
19675  * not be usable for a while. 
19676  *
19677  * <li><i>loadParams</i> - an object containing parameters to pass to the 
19678  * loader callback function when locale data is missing. The parameters are not
19679  * interpretted or modified in any way. They are simply passed along. The object 
19680  * may contain any property/value pairs as long as the calling code is in
19681  * agreement with the loader callback function as to what those parameters mean.
19682  * </ul>
19683  * 
19684  * 
19685  * @constructor
19686  * @param {string} script The ISO 15924 4-letter identifier for the script
19687  * @param {Object=} options parameters to initialize this instance 
19688  */
19689 var ScriptInfo = function(script, options) {
19690 	var sync = true,
19691 	    loadParams = undefined;
19692 	
19693 	this.script = script;
19694 	
19695 	if (options) {
19696 		if (typeof(options.sync) !== 'undefined') {
19697 			sync = (options.sync == true);
19698 		}
19699 		
19700 		if (typeof(options.loadParams) !== 'undefined') {
19701 			loadParams = options.loadParams;
19702 		}
19703 	}
19704 
19705 	if (!ScriptInfo.cache) {
19706 		ScriptInfo.cache = {};
19707 	}
19708 
19709 	if (!ilib.data.scripts) {
19710 		Utils.loadData({
19711 			object: ScriptInfo, 
19712 			locale: "-", 
19713 			name: "scripts.json", 
19714 			sync: sync, 
19715 			loadParams: loadParams, 
19716 			callback: ilib.bind(this, function (info) {
19717 				if (!info) {
19718 					info = {"Latn":{"nb":215,"nm":"Latin","lid":"Latin","rtl":false,"ime":false,"casing":true}};
19719 					var spec = this.locale.getSpec().replace(/-/g, "_");
19720 					ScriptInfo.cache[spec] = info;
19721 				}
19722 				ilib.data.scripts = info;
19723 				this.info = script && ilib.data.scripts[script];
19724 				if (options && typeof(options.onLoad) === 'function') {
19725 					options.onLoad(this);
19726 				}
19727 			})
19728 		});
19729 	} else {
19730 		this.info = ilib.data.scripts[script];
19731 	}
19732 
19733 };
19734 
19735 /**
19736  * @private
19737  */
19738 ScriptInfo._getScriptsArray = function() {
19739 	var ret = [],
19740 		script = undefined,
19741 		scripts = ilib.data.scripts;
19742 
19743 	for (script in scripts) {
19744 		if (script && scripts[script]) {
19745 			ret.push(script);
19746 		}
19747 	}
19748 	
19749 	return ret;
19750 };
19751 
19752 /**
19753  * Return an array of all ISO 15924 4-letter identifier script identifiers that
19754  * this copy of ilib knows about.
19755  * @static
19756  * @param {boolean} sync whether to find the available ids synchronously (true) or asynchronously (false)
19757  * @param {Object} loadParams arbitrary object full of properties to pass to the loader
19758  * @param {function(Array.<string>)} onLoad callback function to call when the data is finished loading
19759  * @return {Array.<string>} an array of all script identifiers that this copy of
19760  * ilib knows about
19761  */
19762 ScriptInfo.getAllScripts = function(sync, loadParams, onLoad) {
19763 	if (!ilib.data.scripts) {
19764 		Utils.loadData({
19765 			object: ScriptInfo, 
19766 			locale: "-", 
19767 			name: "scripts.json", 
19768 			sync: sync, 
19769 			loadParams: loadParams, 
19770 			callback: ilib.bind(this, function (info) {
19771 				ilib.data.scripts = info;
19772 				
19773 				if (typeof(onLoad) === 'function') {
19774 					onLoad(ScriptInfo._getScriptsArray());
19775 				}
19776 			})
19777 		});
19778 	}
19779 	
19780 	return ScriptInfo._getScriptsArray();
19781 };
19782 
19783 ScriptInfo.prototype = {
19784 	/**
19785 	 * Return the 4-letter ISO 15924 identifier associated
19786 	 * with this script.
19787 	 * @return {string} the 4-letter ISO code for this script
19788 	 */
19789 	getCode: function () {
19790 		return this.info && this.script;
19791 	},
19792 	
19793 	/**
19794 	 * Get the ISO 15924 code number associated with this
19795 	 * script.
19796 	 * 
19797 	 * @return {number} the ISO 15924 code number
19798 	 */
19799 	getCodeNumber: function () {
19800 		return this.info && this.info.nb || 0;
19801 	},
19802 	
19803 	/**
19804 	 * Get the name of this script in English.
19805 	 * 
19806 	 * @return {string} the name of this script in English
19807 	 */
19808 	getName: function () {
19809 		return this.info && this.info.nm;
19810 	},
19811 	
19812 	/**
19813 	 * Get the long identifier assciated with this script.
19814 	 * 
19815 	 * @return {string} the long identifier of this script
19816 	 */
19817 	getLongCode: function () {
19818 		return this.info && this.info.lid;
19819 	},
19820 	
19821 	/**
19822 	 * Return the usual direction that text in this script is written
19823 	 * in. Possible return values are "rtl" for right-to-left,
19824 	 * "ltr" for left-to-right, and "ttb" for top-to-bottom.
19825 	 * 
19826 	 * @return {string} the usual direction that text in this script is
19827 	 * written in
19828 	 */
19829 	getScriptDirection: function() {
19830 		return (this.info && typeof(this.info.rtl) !== 'undefined' && this.info.rtl) ? "rtl" : "ltr";
19831 	},
19832 	
19833 	/**
19834 	 * Return true if this script typically requires an input method engine
19835 	 * to enter its characters.
19836 	 * 
19837 	 * @return {boolean} true if this script typically requires an IME
19838 	 */
19839 	getNeedsIME: function () {
19840 		return this.info && this.info.ime ? true : false; // converts undefined to false
19841 	},
19842 	
19843 	/**
19844 	 * Return true if this script uses lower- and upper-case characters.
19845 	 * 
19846 	 * @return {boolean} true if this script uses letter case
19847 	 */
19848 	getCasing: function () {
19849 		return this.info && this.info.casing ? true : false; // converts undefined to false
19850 	}
19851 };
19852 
19853 
19854 /*< Name.js */
19855 /*
19856  * Name.js - Person name parser
19857  *
19858  * Copyright © 2013-2015, JEDLSoft
19859  *
19860  * Licensed under the Apache License, Version 2.0 (the "License");
19861  * you may not use this file except in compliance with the License.
19862  * You may obtain a copy of the License at
19863  *
19864  *     http://www.apache.org/licenses/LICENSE-2.0
19865  *
19866  * Unless required by applicable law or agreed to in writing, software
19867  * distributed under the License is distributed on an "AS IS" BASIS,
19868  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19869  *
19870  * See the License for the specific language governing permissions and
19871  * limitations under the License.
19872  */
19873 
19874 /* !depends 
19875 ilib.js 
19876 Locale.js
19877 Utils.js 
19878 isAlpha.js 
19879 isIdeo.js 
19880 isPunct.js 
19881 isSpace.js
19882 JSUtils.js 
19883 IString.js
19884 */
19885 
19886 // !data name
19887 
19888 // notes:
19889 // icelandic given names: http://en.wiktionary.org/wiki/Appendix:Icelandic_given_names
19890 // danish approved given names: http://www.familiestyrelsen.dk/samliv/navne/
19891 // http://www.mentalfloss.com/blogs/archives/59277
19892 // other countries with first name restrictions: Norway, China, New Zealand, Japan, Sweden, Germany, Hungary
19893 
19894 
19895 
19896 /**
19897  * @class
19898  * A class to parse names of people. Different locales have different conventions when it
19899  * comes to naming people.<p>
19900  *
19901  * The options can contain any of the following properties:
19902  *
19903  * <ul>
19904  * <li><i>locale</i> - use the rules and conventions of the given locale in order to parse
19905  * the name
19906  * <li><i>style</i> - explicitly use the named style to parse the name. Valid values so
19907  * far are "western" and "asian". If this property is not specified, then the style will
19908  * be gleaned from the name itself. This class will count the total number of Latin or Asian
19909  * characters. If the majority of the characters are in one style, that style will be
19910  * used to parse the whole name.
19911  * <li><i>order</i> - explicitly use the given order for names. In some locales, such
19912  * as Russian, names may be written equally validly as "givenName familyName" or "familyName
19913  * givenName". This option tells the parser which order to prefer, and overrides the
19914  * default order for the locale. Valid values are "gf" (given-family) or "fg" (family-given).
19915  * <li><i>useSpaces</i> - explicitly specifies whether to use spaces or not between the given name , middle name
19916  * and family name.
19917  * <li>onLoad - a callback function to call when the name info is fully
19918  * loaded and the name has been parsed. When the onLoad option is given, the name object
19919  * will attempt to load any missing locale data using the ilib loader callback.
19920  * When the constructor is done (even if the data is already preassembled), the
19921  * onLoad function is called with the current instance as a parameter, so this
19922  * callback can be used with preassembled or dynamic loading or a mix of the two.
19923  *
19924  * <li>sync - tell whether to load any missing locale data synchronously or
19925  * asynchronously. If this option is given as "false", then the "onLoad"
19926  * callback must be given, as the instance returned from this constructor will
19927  * not be usable for a while.
19928  *
19929  * <li><i>loadParams</i> - an object containing parameters to pass to the
19930  * loader callback function when locale data is missing. The parameters are not
19931  * interpretted or modified in any way. They are simply passed along. The object
19932  * may contain any property/value pairs as long as the calling code is in
19933  * agreement with the loader callback function as to what those parameters mean.
19934  * </ul>
19935  *
19936  * When the parser has completed its parsing, it fills in the fields listed below.<p>
19937  *
19938  * For names that include auxilliary words, such as the family name "van der Heijden", all
19939  * of the auxilliary words ("van der") will be included in the field.<p>
19940  *
19941  * For names in Spanish locales, it is assumed that the family name is doubled. That is,
19942  * a person may have a paternal family name followed by a maternal family name. All
19943  * family names will be listed in the familyName field as normal, separated by spaces.
19944  * When formatting the short version of such names, only the paternal family name will
19945  * be used.
19946  *
19947  *
19948  * @constructor
19949  * @param {string|Name=} name the name to parse
19950  * @param {Object=} options Options governing the construction of this name instance
19951  */
19952 var Name = function (name, options) {
19953     var sync = true;
19954 
19955     if (!name || name.length === 0) {
19956     	return;
19957     }
19958 
19959     this.loadParams = {};
19960 
19961     if (options) {
19962         if (options.locale) {
19963             this.locale = (typeof (options.locale) === 'string') ? new Locale(options.locale) : options.locale;
19964         }
19965 
19966         if (options.style && (options.style === "asian" || options.style === "western")) {
19967             this.style = options.style;
19968         }
19969 
19970         if (options.order && (options.order === "gmf" || options.order === "fmg" || options.order === "fgm")) {
19971             this.order = options.order;
19972         }
19973 
19974         if (typeof (options.sync) !== 'undefined') {
19975             sync = (options.sync == true);
19976         }
19977 
19978         if (typeof (options.loadParams) !== 'undefined') {
19979             this.loadParams = options.loadParams;
19980         }
19981     }
19982 
19983     if (!Name.cache) {
19984         Name.cache = {};
19985     }
19986 
19987 	this.locale = this.locale || new Locale();
19988 	
19989 	isAlpha._init(sync, this.loadParams, ilib.bind(this, function() {
19990 		isIdeo._init(sync, this.loadParams, ilib.bind(this, function() {
19991 			isPunct._init(sync, this.loadParams, ilib.bind(this, function() {
19992 				isSpace._init(sync, this.loadParams, ilib.bind(this, function() {
19993 					Utils.loadData({
19994 						object: Name, 
19995 						locale: this.locale, 
19996 						name: "name.json", 
19997 						sync: sync, 
19998 						loadParams: this.loadParams, 
19999 						callback: ilib.bind(this, function (info) {
20000 							if (!info) {
20001 								info = Name.defaultInfo;
20002 								var spec = this.locale.getSpec().replace(/-/g, "_");
20003 								Name.cache[spec] = info;
20004 							}
20005                             if (typeof (name) === 'object') {
20006     							// copy constructor
20007 							    /**
20008 							     * The prefixes for this name
20009 							     * @type {string|Array.<string>}
20010 							     */
20011 							    this.prefix = name.prefix;
20012 							    /**
20013 							     * The given (personal) name in this name.
20014 							     * @type {string|Array.<string>}
20015 							     */
20016 							    this.givenName = name.givenName;
20017 							    /**
20018 							     * The middle names used in this name. If there are multiple middle names, they all
20019 							     * appear in this field separated by spaces.
20020 							     * @type {string|Array.<string>}
20021 							     */
20022 							    this.middleName = name.middleName;
20023 							    /**
20024 							     * The family names in this name. If there are multiple family names, they all
20025 							     * appear in this field separated by spaces.
20026 							     * @type {string|Array.<string>}
20027 							     */
20028 							    this.familyName = name.familyName;
20029 							    /**
20030 							     * The suffixes for this name. If there are multiple suffixes, they all
20031 							     * appear in this field separated by spaces.
20032 							     * @type {string|Array.<string>}
20033 							     */
20034 							    this.suffix = name.suffix;
20035 
20036 							    // private properties
20037 							    this.locale = name.locale;
20038 							    this.style = name.style;
20039 							    this.order = name.order;
20040 							    this.useSpaces = name.useSpaces;
20041 							    this.isAsianName = name.isAsianName;
20042 					    	    return;
20043 						    }
20044 							/** 
20045 							 * @type {{
20046 							 *   nameStyle:string,
20047 							 *   order:string,
20048 							 *   prefixes:Array.<string>,
20049 							 *   suffixes:Array.<string>,
20050 							 *   auxillaries:Array.<string>,
20051 							 *   honorifics:Array.<string>,
20052 							 *   knownFamilyNames:Array.<string>,
20053 							 *   noCompoundFamilyNames:boolean,
20054 							 *   sortByHeadWord:boolean
20055 							 * }} */
20056 							this.info = info;
20057 							this._init(name);
20058 							if (options && typeof(options.onLoad) === 'function') {
20059 								options.onLoad(this);
20060 							}
20061 						})
20062 					});					
20063 				}));
20064 			}));
20065 		}));
20066 	}));
20067 };
20068 
20069 Name.defaultInfo = ilib.data.name ||  {
20070 	"components": {
20071 		"short": {
20072 			"g": 1,
20073 			"f": 1
20074 		},
20075 		"medium": {
20076 			"g": 1,
20077 			"m": 1,
20078 			"f": 1
20079 		},
20080 		"long": {
20081 			"p": 1,
20082 			"g": 1,
20083 			"m": 1,
20084 			"f": 1
20085 		},
20086 		"full": {
20087 			"p": 1,
20088 			"g": 1,
20089 			"m": 1,
20090 			"f": 1,
20091 			"s": 1
20092 		}
20093 	},
20094 	"format": "{prefix} {givenName} {middleName} {familyName}{suffix}",
20095 	"sortByHeadWord": false,
20096 	"nameStyle": "western",
20097 	"conjunctions": {
20098 		"and1": "and",
20099 		"and2": "and",
20100 		"or1": "or",
20101 		"or2": "or"
20102 	},
20103 	"auxillaries": {
20104 		"von": 1,
20105 		"von der": 1,
20106 		"von den": 1,
20107 		"van": 1,
20108 		"van der": 1,
20109 		"van de": 1,
20110 		"van den": 1,
20111 		"de": 1,
20112 		"di": 1,
20113 		"la": 1,
20114 		"lo": 1,
20115 		"des": 1,
20116 		"le": 1,
20117 		"les": 1,
20118 		"du": 1,
20119 		"de la": 1,
20120 		"del": 1,
20121 		"de los": 1,
20122 		"de las": 1
20123 	},
20124 	"prefixes": [
20125         "doctor",
20126         "dr",
20127         "mr",
20128         "mrs",
20129         "ms",
20130         "mister",
20131         "madame",
20132         "madamoiselle",
20133         "miss",
20134         "monsieur",
20135         "señor",
20136         "señora",
20137         "señorita"
20138 	],
20139 	"suffixes": [
20140         ",",
20141         "junior",
20142         "jr",
20143         "senior",
20144         "sr",
20145         "i",
20146         "ii",
20147         "iii",
20148         "esq",
20149         "phd",
20150         "md"
20151 	],
20152     "patronymicName":[ ],
20153     "familyNames":[ ]
20154 };
20155 
20156 /**
20157  * Return true if the given character is in the range of the Han, Hangul, or kana
20158  * scripts.
20159  * @static
20160  * @protected
20161  */
20162 Name._isAsianChar = function(c) {
20163 	return isIdeo(c) ||
20164 		CType.withinRange(c, "hangul") ||
20165 		CType.withinRange(c, "hiragana") ||
20166 		CType.withinRange(c, "katakana");
20167 };
20168 
20169 
20170 /**
20171  * @static
20172  * @protected
20173  */
20174 Name._isAsianName = function (name, language) {
20175     // the idea is to count the number of asian chars and the number
20176     // of latin chars. If one is greater than the other, choose
20177     // that style.
20178     var asian = 0,
20179         latin = 0,
20180         i;
20181 
20182     if (name && name.length > 0) {
20183         for (i = 0; i < name.length; i++) {
20184         	var c = name.charAt(i);
20185 
20186             if (Name._isAsianChar(c)) {
20187                 if (language =="ko" || language =="ja" || language =="zh") {
20188                     return true;
20189                 }
20190                 asian++;
20191             } else if (isAlpha(c)) {
20192                 if (!language =="ko" || !language =="ja" || !language =="zh") {
20193                     return false;
20194                 }
20195                 latin++;
20196             }
20197         }
20198 
20199         return latin < asian;
20200     }
20201 
20202     return false;
20203 };
20204 
20205 /**
20206  * Return true if any Latin letters are found in the string. Return
20207  * false if all the characters are non-Latin.
20208  * @static
20209  * @protected
20210  */
20211 Name._isEuroName = function (name, language) {
20212     var c,
20213         n = new IString(name),
20214         it = n.charIterator();
20215 
20216     while (it.hasNext()) {
20217         c = it.next();
20218 
20219         if (!Name._isAsianChar(c) && !isPunct(c) && !isSpace(c)) {
20220             return true;
20221         } else if (Name._isAsianChar(c) && (language =="ko" || language =="ja" || language =="zh")) {
20222             return false;
20223         }
20224     }
20225     return false;
20226 };
20227 
20228 Name.prototype = {
20229     /**
20230      * @protected
20231      */
20232     _init: function (name) {
20233         var parts, prefixArray, prefix, prefixLower,
20234             suffixArray, suffix, suffixLower,
20235             i, info, hpSuffix;
20236         var currentLanguage = this.locale.getLanguage();
20237 
20238         if (name) {
20239             // for DFISH-12905, pick off the part that the LDAP server automatically adds to our names in HP emails
20240             i = name.search(/\s*[,\/\(\[\{<]/);
20241             if (i !== -1) {
20242                 hpSuffix = name.substring(i);
20243                 hpSuffix = hpSuffix.replace(/\s+/g, ' '); // compress multiple whitespaces
20244                 suffixArray = hpSuffix.split(" ");
20245                 var conjunctionIndex = this._findLastConjunction(suffixArray);
20246                 if (conjunctionIndex > -1) {
20247                     // it's got conjunctions in it, so this is not really a suffix
20248                     hpSuffix = undefined;
20249                 } else {
20250                     name = name.substring(0, i);
20251                 }
20252             }
20253 
20254             this.isAsianName = Name._isAsianName(name, currentLanguage);
20255             if (this.info.nameStyle === "asian" || this.info.order === "fmg" || this.info.order === "fgm") {
20256                 info = this.isAsianName ? this.info : ilib.data.name;
20257             } else {
20258                 info = this.isAsianName ? ilib.data.name : this.info;
20259             }
20260 
20261             if (this.isAsianName) {
20262                 // all-asian names
20263                 if (this.useSpaces == false) {
20264                     name = name.replace(/\s+/g, ''); // eliminate all whitespaces
20265                 }
20266                 parts = name.trim().split('');
20267             }
20268             //} 
20269             else {
20270                 name = name.replace(/, /g, ' , ');
20271                 name = name.replace(/\s+/g, ' '); // compress multiple whitespaces
20272                 parts = name.trim().split(' ');
20273             }
20274 
20275             // check for prefixes
20276             if (parts.length > 1) {
20277                 for (i = parts.length; i > 0; i--) {
20278                     prefixArray = parts.slice(0, i);
20279                     prefix = prefixArray.join(this.isAsianName ? '' : ' ');
20280                     prefixLower = prefix.toLowerCase();
20281                     prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods
20282                     if (ilib.isArray(this.info.prefixes) &&
20283                         (JSUtils.indexOf(this.info.prefixes, prefixLower) > -1 || this._isConjunction(prefixLower))) {
20284                         if (this.prefix) {
20285                             if (!this.isAsianName) {
20286                                 this.prefix += ' ';
20287                             }
20288                             this.prefix += prefix;
20289                         } else {
20290                             this.prefix = prefix;
20291                         }
20292                         parts = parts.slice(i);
20293                         i = parts.length;
20294                     }
20295                 }
20296             }
20297             // check for suffixes
20298             if (parts.length > 1) {
20299                 for (i = parts.length; i > 0; i--) {
20300                     suffixArray = parts.slice(-i);
20301                     suffix = suffixArray.join(this.isAsianName ? '' : ' ');
20302                     suffixLower = suffix.toLowerCase();
20303                     suffixLower = suffixLower.replace(/[\.]/g, ''); // ignore periods
20304                     if (ilib.isArray(this.info.suffixes) && JSUtils.indexOf(this.info.suffixes, suffixLower) > -1) {
20305                         if (this.suffix) {
20306                             if (!this.isAsianName && !isPunct(this.suffix.charAt(0))) {
20307                                 this.suffix = ' ' + this.suffix;
20308                             }
20309                             this.suffix = suffix + this.suffix;
20310                         } else {
20311                             this.suffix = suffix;
20312                         }
20313                         parts = parts.slice(0, parts.length - i);
20314                         i = parts.length;
20315                     }
20316                 }
20317             }
20318 
20319             if (hpSuffix) {
20320                 this.suffix = (this.suffix && this.suffix + hpSuffix) || hpSuffix;
20321             }
20322 
20323             // adjoin auxillary words to their headwords
20324             if (parts.length > 1 && !this.isAsianName) {
20325                 parts = this._joinAuxillaries(parts, this.isAsianName);
20326             }
20327 
20328             if (this.isAsianName) {
20329                 this._parseAsianName(parts, currentLanguage);
20330             } else {
20331                 this._parseWesternName(parts);
20332             }
20333 
20334             this._joinNameArrays();
20335         }
20336     },
20337 
20338     /**
20339 	 * @return {number} 
20340 	 *
20341 	_findSequence: function(parts, hash, isAsian) {
20342 		var sequence, sequenceLower, sequenceArray, aux = [], i, ret = {};
20343 		
20344 		if (parts.length > 0 && hash) {
20345 			//console.info("_findSequence: finding sequences");
20346 			for (var start = 0; start < parts.length-1; start++) {
20347 				for ( i = parts.length; i > start; i-- ) {
20348 					sequenceArray = parts.slice(start, i);
20349 					sequence = sequenceArray.join(isAsian ? '' : ' ');
20350 					sequenceLower = sequence.toLowerCase();
20351 					sequenceLower = sequenceLower.replace(/[,\.]/g, '');  // ignore commas and periods
20352 					
20353 					//console.info("_findSequence: checking sequence: '" + sequenceLower + "'");
20354 					
20355 					if ( sequenceLower in hash ) {
20356 						ret.match = sequenceArray;
20357 						ret.start = start;
20358 						ret.end = i;
20359 						return ret;
20360 						//console.info("_findSequence: Found sequence '" + sequence + "' New parts list is " + JSON.stringify(parts));
20361 					}
20362 				}
20363 			}
20364 		}
20365 	
20366 		return undefined;
20367 	},
20368 	*/
20369 
20370     /**
20371      * @protected
20372      * @param {Array} parts
20373      * @param {Array} names
20374      * @param {boolean} isAsian
20375      * @param {boolean=} noCompoundPrefix
20376      */
20377     _findPrefix: function (parts, names, isAsian, noCompoundPrefix) {
20378         var i, prefix, prefixLower, prefixArray, aux = [];
20379 
20380         if (parts.length > 0 && names) {
20381             for (i = parts.length; i > 0; i--) {
20382                 prefixArray = parts.slice(0, i);
20383                 prefix = prefixArray.join(isAsian ? '' : ' ');
20384                 prefixLower = prefix.toLowerCase();
20385                 prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods
20386 
20387                 if (prefixLower in names) {
20388                     aux = aux.concat(isAsian ? prefix : prefixArray);
20389                     if (noCompoundPrefix) {
20390                     	// don't need to parse further. Just return it as is.
20391                     	return aux;
20392                     }
20393                     parts = parts.slice(i);
20394                     i = parts.length + 1;
20395                 }
20396             }
20397         }
20398 
20399         return aux;
20400     },
20401 
20402     /**
20403      * @protected
20404      */
20405     _findSuffix: function (parts, names, isAsian) {
20406         var i, j, seq = "";
20407 
20408         for (i = 0; i < names.length; i++) {
20409             if (parts.length >= names[i].length) {
20410                 j = 0;
20411                 while (j < names[i].length && parts[parts.length - j] === names[i][names[i].length - j]) {
20412                     j++;
20413                 }
20414                 if (j >= names[i].length) {
20415                     seq = parts.slice(parts.length - j).join(isAsian ? "" : " ") + (isAsian ? "" : " ") + seq;
20416                     parts = parts.slice(0, parts.length - j);
20417                     i = -1; // restart the search
20418                 }
20419             }
20420         }
20421 
20422         this.suffix = seq;
20423         return parts;
20424     },
20425 
20426     /**
20427      * @protected
20428      * Tell whether or not the given word is a conjunction in this language.
20429      * @param {string} word the word to test
20430      * @return {boolean} true if the word is a conjunction
20431      */
20432     _isConjunction: function _isConjunction(word) {
20433         return (this.info.conjunctions.and1 === word ||
20434             this.info.conjunctions.and2 === word ||
20435             this.info.conjunctions.or1 === word ||
20436             this.info.conjunctions.or2 === word ||
20437             ("&" === word) ||
20438             ("+" === word));
20439     },
20440 
20441     /**
20442      * Find the last instance of 'and' in the name
20443      * @protected
20444      * @param {Array.<string>} parts
20445      * @return {number}
20446      */
20447     _findLastConjunction: function _findLastConjunction(parts) {
20448         var conjunctionIndex = -1,
20449             index, part;
20450 
20451         for (index = 0; index < parts.length; index++) {
20452             part = parts[index];
20453             if (typeof (part) === 'string') {
20454                 part = part.toLowerCase();
20455                 // also recognize English
20456                 if ("and" === part || "or" === part || "&" === part || "+" === part) {
20457                     conjunctionIndex = index;
20458                 }
20459                 if (this._isConjunction(part)) {
20460                     conjunctionIndex = index;
20461                 }
20462             }
20463         }
20464         return conjunctionIndex;
20465     },
20466 
20467     /**
20468      * @protected
20469      * @param {Array.<string>} parts the current array of name parts
20470      * @param {boolean} isAsian true if the name is being parsed as an Asian name
20471      * @return {Array.<string>} the remaining parts after the prefixes have been removed
20472      */
20473     _extractPrefixes: function (parts, isAsian) {
20474         var i = this._findPrefix(parts, this.info.prefixes, isAsian);
20475         if (i > 0) {
20476             this.prefix = parts.slice(0, i).join(isAsian ? "" : " ");
20477             return parts.slice(i);
20478         }
20479         // prefixes not found, so just return the array unmodified
20480         return parts;
20481     },
20482 
20483     /**
20484      * @protected
20485      * @param {Array.<string>} parts the current array of name parts
20486      * @param {boolean} isAsian true if the name is being parsed as an Asian name
20487      * @return {Array.<string>} the remaining parts after the suffices have been removed
20488      */
20489     _extractSuffixes: function (parts, isAsian) {
20490         var i = this._findSuffix(parts, this.info.suffixes, isAsian);
20491         if (i > 0) {
20492             this.suffix = parts.slice(i).join(isAsian ? "" : " ");
20493             return parts.slice(0, i);
20494         }
20495         // suffices not found, so just return the array unmodified
20496         return parts;
20497     },
20498 
20499     /**
20500      * Adjoin auxillary words to their head words.
20501      * @protected
20502      * @param {Array.<string>} parts the current array of name parts
20503      * @param {boolean} isAsian true if the name is being parsed as an Asian name
20504      * @return {Array.<string>} the parts after the auxillary words have been plucked onto their head word
20505      */
20506     _joinAuxillaries: function (parts, isAsian) {
20507         var start, i, prefixArray, prefix, prefixLower;
20508 
20509         if (this.info.auxillaries && (parts.length > 2 || this.prefix)) {
20510             for (start = 0; start < parts.length - 1; start++) {
20511                 for (i = parts.length; i > start; i--) {
20512                     prefixArray = parts.slice(start, i);
20513                     prefix = prefixArray.join(' ');
20514                     prefixLower = prefix.toLowerCase();
20515                     prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods
20516 
20517                     if (prefixLower in this.info.auxillaries) {
20518                         parts.splice(start, i + 1 - start, prefixArray.concat(parts[i]));
20519                         i = start;
20520                     }
20521                 }
20522             }
20523         }
20524 
20525         return parts;
20526     },
20527 
20528     /**
20529      * Recursively join an array or string into a long string.
20530      * @protected
20531      */
20532     _joinArrayOrString: function _joinArrayOrString(part) {
20533         var i;
20534         if (typeof (part) === 'object') {
20535             for (i = 0; i < part.length; i++) {
20536                 part[i] = this._joinArrayOrString(part[i]);
20537             }
20538             var ret = "";
20539             part.forEach(function (segment) {
20540                 if (ret.length > 0 && !isPunct(segment.charAt(0))) {
20541                     ret += ' ';
20542                 }
20543                 ret += segment;
20544             });
20545 
20546             return ret;
20547         }
20548 
20549         return part;
20550     },
20551 
20552     /**
20553      * @protected
20554      */
20555     _joinNameArrays: function _joinNameArrays() {
20556         var prop;
20557         for (prop in this) {
20558 
20559             if (this[prop] !== undefined && typeof (this[prop]) === 'object' && ilib.isArray(this[prop])) {
20560 
20561                 this[prop] = this._joinArrayOrString(this[prop]);
20562             }
20563         }
20564     },
20565 
20566     /**
20567      * @protected
20568      */
20569     _parseAsianName: function (parts, language) {
20570         var familyNameArray = this._findPrefix(parts, this.info.knownFamilyNames, true, this.info.noCompoundFamilyNames);
20571         var tempFullName = parts.join('');
20572 
20573         if (familyNameArray && familyNameArray.length > 0) {
20574             this.familyName = familyNameArray.join('');
20575             this.givenName = parts.slice(this.familyName.length).join('');
20576             
20577             //Overide parsing rules if spaces are found in korean
20578             if (language === "ko" && tempFullName.search(/\s*[/\s]/) > -1 && !this.suffix) {
20579                 this._parseKoreanName(tempFullName);
20580             }
20581         } else if (this.locale.getLanguage() === "ja") {
20582             this._parseJapaneseName(parts);
20583         } else if (this.suffix || this.prefix) {
20584             this.familyName = parts.join('');
20585         } else {
20586             this.givenName = parts.join('');
20587         }
20588     },
20589 
20590     /**
20591      * @protected
20592      */
20593     _parseKoreanName: function (name) {
20594         var tempName = name;
20595 
20596         var spaceSplit = tempName.split(" ");
20597         var spceCount = spaceSplit.length;
20598         var fistSpaceIndex = tempName.indexOf(" ");
20599         var lastSpaceIndex = tempName.lastIndexOf(" ");
20600 
20601         if (spceCount === 2) {
20602             this.familyName = spaceSplit[0];
20603             this.givenName = tempName.slice(fistSpaceIndex, tempName.length);
20604         } else {
20605             this.familyName = spaceSplit[0];
20606             this.middleName = tempName.slice(fistSpaceIndex, lastSpaceIndex);
20607             this.givenName = tempName.slice(lastSpaceIndex, tempName.length);
20608         }
20609         
20610     },
20611 
20612     /**
20613      * @protected
20614      */
20615     _parseJapaneseName: function (parts) {
20616     	if (this.suffix && this.suffix.length > 1 && this.info.honorifics.indexOf(this.suffix)>-1) {
20617     		if (parts.length === 1) {
20618     			if (CType.withinRange(parts[0], "cjk")) {
20619     				this.familyName = parts[0];
20620     			} else {
20621     				this.givenName = parts[0];
20622     			}
20623     			return;
20624     		} else if (parts.length === 2) {
20625     			this.familyName = parts.slice(0,parts.length).join("")
20626     			return;
20627     		}
20628     	}
20629     	if (parts.length > 1) {
20630     		var fn = "";                                                                    
20631     		for (var i = 0; i < parts.length; i++) {
20632     			if (CType.withinRange(parts[i], "cjk")) {
20633     				fn += parts[i];
20634     			} else if (fn.length > 1 && CType.withinRange(parts[i], "hiragana")) {
20635     				this.familyName = fn;
20636     				this.givenName = parts.slice(i,parts.length).join("");
20637     				return;
20638     			} else {
20639     				break;
20640     			}
20641     		}
20642     	}
20643     	if (parts.length === 1) {
20644     		this.familyName = parts[0];
20645     	} else if (parts.length === 2) {
20646     		this.familyName = parts[0];
20647     		this.givenName = parts[1];
20648     	} else if (parts.length === 3) {
20649     		this.familyName = parts[0];
20650     		this.givenName = parts.slice(1,parts.length).join("");
20651     	} else if (parts.length > 3) {
20652     		this.familyName = parts.slice(0,2).join("")
20653     		this.givenName = parts.slice(2,parts.length).join("");
20654     	}      
20655     },
20656 
20657     /**
20658      * @protected
20659      */
20660     _parseSpanishName: function (parts) {
20661         var conjunctionIndex;
20662 
20663         if (parts.length === 1) {
20664             if (this.prefix || typeof (parts[0]) === 'object') {
20665                 this.familyName = parts[0];
20666             } else {
20667                 this.givenName = parts[0];
20668             }
20669         } else if (parts.length === 2) {
20670             // we do G F
20671             this.givenName = parts[0];
20672             this.familyName = parts[1];
20673         } else if (parts.length === 3) {
20674             conjunctionIndex = this._findLastConjunction(parts);
20675             // if there's an 'and' in the middle spot, put everything in the first name
20676             if (conjunctionIndex === 1) {
20677                 this.givenName = parts;
20678             } else {
20679                 // else, do G F F
20680                 this.givenName = parts[0];
20681                 this.familyName = parts.slice(1);
20682             }
20683         } else if (parts.length > 3) {
20684             //there are at least 4 parts to this name
20685 
20686             conjunctionIndex = this._findLastConjunction(parts);
20687             ////console.log("@@@@@@@@@@@@@@@@"+conjunctionIndex)
20688             if (conjunctionIndex > 0) {
20689                 // if there's a conjunction that's not the first token, put everything up to and 
20690                 // including the token after it into the first name, the last 2 tokens into
20691                 // the family name (if they exist) and everything else in to the middle name
20692                 // 0 1 2 3 4 5
20693                 // G A G
20694                 // G A G F
20695                 // G G A G
20696                 // G A G F F
20697                 // G G A G F
20698                 // G G G A G
20699                 // G A G M F F
20700                 // G G A G F F
20701                 // G G G A G F
20702                 // G G G G A G
20703                 this.givenName = parts.splice(0, conjunctionIndex + 2);
20704                 if (parts.length > 1) {
20705                     this.familyName = parts.splice(parts.length - 2, 2);
20706                     if (parts.length > 0) {
20707                         this.middleName = parts;
20708                     }
20709                 } else if (parts.length === 1) {
20710                     this.familyName = parts[0];
20711                 }
20712             } else {
20713                 this.givenName = parts.splice(0, 1);
20714                 this.familyName = parts.splice(parts.length - 2, 2);
20715                 this.middleName = parts;
20716             }
20717         }
20718     },
20719 
20720     /**
20721      * @protected
20722      */
20723     _parseIndonesianName: function (parts) {
20724         var conjunctionIndex;
20725 
20726         if (parts.length === 1) {
20727             //if (this.prefix || typeof(parts[0]) === 'object') {
20728             //this.familyName = parts[0];
20729             //} else {
20730             this.givenName = parts[0];
20731             //}
20732             //} else if (parts.length === 2) {
20733             // we do G F
20734             //this.givenName = parts[0];
20735             //this.familyName = parts[1];
20736         } else if (parts.length >= 2) {
20737             //there are at least 3 parts to this name
20738 
20739             conjunctionIndex = this._findLastConjunction(parts);
20740             if (conjunctionIndex > 0) {
20741                 // if there's a conjunction that's not the first token, put everything up to and 
20742                 // including the token after it into the first name, the last 2 tokens into
20743                 // the family name (if they exist) and everything else in to the middle name
20744                 // 0 1 2 3 4 5
20745                 // G A G
20746                 // G A G F
20747                 // G G A G
20748                 // G A G F F
20749                 // G G A G F
20750                 // G G G A G
20751                 // G A G M F F
20752                 // G G A G F F
20753                 // G G G A G F
20754                 // G G G G A G
20755                 this.givenName = parts.splice(0, conjunctionIndex + 2);
20756                 if (parts.length > 1) {
20757                     //this.familyName = parts.splice(parts.length-2, 2);
20758                     //if ( parts.length > 0 ) {
20759                     this.middleName = parts;
20760                 }
20761                 //} else if (parts.length === 1) {
20762                 //	this.familyName = parts[0];
20763                 //}
20764             } else {
20765                 this.givenName = parts.splice(0, 1);
20766                 //this.familyName = parts.splice(parts.length-2, 2);
20767                 this.middleName = parts;
20768             }
20769         }
20770     },
20771     
20772     /**
20773      * @protected
20774      */
20775     _parseGenericWesternName: function (parts) {
20776         /* Western names are parsed as follows, and rules are applied in this 
20777          * order:
20778          *
20779          * G
20780          * G F
20781          * G M F
20782          * G M M F
20783          * P F
20784          * P G F
20785          */
20786         var conjunctionIndex;
20787 
20788         if (parts.length === 1) {
20789             if (this.prefix || typeof (parts[0]) === 'object') {
20790                 // already has a prefix, so assume it goes with the family name like "Dr. Roberts" or
20791                 // it is a name with auxillaries, which is almost always a family name
20792                 this.familyName = parts[0];
20793             } else {
20794                 this.givenName = parts[0];
20795             }
20796         } else if (parts.length === 2) {
20797             // we do G F
20798             if (this.info.order == 'fgm') {
20799                 this.givenName = parts[1];
20800                 this.familyName = parts[0];
20801             } else if (this.info.order == "gmf" || typeof (this.info.order) == 'undefined') {
20802                 this.givenName = parts[0];
20803                 this.familyName = parts[1];
20804             }
20805         } else if (parts.length >= 3) {
20806             //find the first instance of 'and' in the name
20807             conjunctionIndex = this._findLastConjunction(parts);
20808 
20809             if (conjunctionIndex > 0) {
20810                 // if there's a conjunction that's not the first token, put everything up to and 
20811                 // including the token after it into the first name, the last token into
20812                 // the family name (if it exists) and everything else in to the middle name
20813                 // 0 1 2 3 4 5
20814                 // G A G M M F
20815                 // G G A G M F
20816                 // G G G A G F
20817                 // G G G G A G
20818                 //if(this.order == "gmf") {
20819                 this.givenName = parts.slice(0, conjunctionIndex + 2);
20820 
20821                 if (conjunctionIndex + 1 < parts.length - 1) {
20822                     this.familyName = parts.splice(parts.length - 1, 1);
20823                     ////console.log(this.familyName);
20824                     if (conjunctionIndex + 2 < parts.length - 1) {
20825                         this.middleName = parts.slice(conjunctionIndex + 2, parts.length - conjunctionIndex - 3);
20826                     }
20827                 } else if (this.order == "fgm") {
20828                     this.familyName = parts.slice(0, conjunctionIndex + 2);
20829                     if (conjunctionIndex + 1 < parts.length - 1) {
20830                         this.middleName = parts.splice(parts.length - 1, 1);
20831                         if (conjunctionIndex + 2 < parts.length - 1) {
20832                             this.givenName = parts.slice(conjunctionIndex + 2, parts.length - conjunctionIndex - 3);
20833                         }
20834                     }
20835                 }
20836             } else {
20837                 this.givenName = parts[0];
20838 
20839                 this.middleName = parts.slice(1, parts.length - 1);
20840 
20841                 this.familyName = parts[parts.length - 1];
20842             }
20843         }
20844     },
20845     
20846      /**
20847      * parse patrinomic name from the russian names 
20848      * @protected
20849      * @param {Array.<string>} parts the current array of name parts
20850      * @return number  index of the part which contains patronymic name
20851      */
20852     _findPatronymicName: function(parts) {
20853     	var index, part;
20854     	for (index = 0; index < parts.length; index++) {
20855     		part = parts[index];
20856     		if (typeof (part) === 'string') {
20857     			part = part.toLowerCase();
20858 
20859     			var subLength = this.info.patronymicName.length;
20860     			while(subLength--) {
20861     				if(part.indexOf(this.info.patronymicName[subLength])!== -1 )
20862     					return index;
20863     			}
20864     		}
20865     	}
20866     	return -1;
20867     },
20868 
20869     /**
20870 	 * find if the given part is patronymic name
20871 	 * 
20872 	 * @protected
20873 	 * @param {string} part string from name parts @
20874 	 * @return number index of the part which contains familyName
20875 	 */
20876     _isPatronymicName: function(part) {
20877 	    var pName;
20878 	    if ( typeof (part) === 'string') {
20879 		    pName = part.toLowerCase();
20880 
20881 		    var subLength = this.info.patronymicName.length;
20882 		    while (subLength--) {
20883 			    if (pName.indexOf(this.info.patronymicName[subLength]) !== -1)
20884 				    return true;
20885 		    }
20886 	    }
20887 	    return false;
20888     },
20889 
20890     /**
20891 	 * find family name from the russian name
20892 	 * 
20893 	 * @protected
20894 	 * @param {Array.<string>} parts the current array of name parts
20895 	 * @return boolean true if patronymic, false otherwise
20896 	 */
20897     _findFamilyName: function(parts) {
20898 	    var index, part, substring;
20899 	    for (index = 0; index < parts.length; index++) {
20900 		    part = parts[index];
20901 
20902 		    if ( typeof (part) === 'string') {
20903 			    part = part.toLowerCase();
20904 			    var length = part.length - 1;
20905 
20906 			    if (this.info.familyName.indexOf(part) !== -1) {
20907 				    return index;
20908 			    } else if (part[length] === 'в' || part[length] === 'н' ||
20909 			        part[length] === 'й') {
20910 				    substring = part.slice(0, -1);
20911 				    if (this.info.familyName.indexOf(substring) !== -1) {
20912 					    return index;
20913 				    }
20914 			    } else if ((part[length - 1] === 'в' && part[length] === 'а') ||
20915 			        (part[length - 1] === 'н' && part[length] === 'а') ||
20916 			        (part[length - 1] === 'а' && part[length] === 'я')) {
20917 				    substring = part.slice(0, -2);
20918 				    if (this.info.familyName.indexOf(substring) !== -1) {
20919 					    return index;
20920 				    }
20921 			    }
20922 		    }
20923 	    }
20924 	    return -1;
20925     },
20926 
20927     /**
20928 	 * parse russian name
20929 	 * 
20930 	 * @protected
20931 	 * @param {Array.<string>} parts the current array of name parts
20932 	 * @return
20933 	 */
20934     _parseRussianName: function(parts) {
20935 	    var conjunctionIndex, familyIndex = -1;
20936 
20937 	    if (parts.length === 1) {
20938 		    if (this.prefix || typeof (parts[0]) === 'object') {
20939 			    // already has a prefix, so assume it goes with the family name
20940 				// like "Dr. Roberts" or
20941 			    // it is a name with auxillaries, which is almost always a
20942 				// family name
20943 			    this.familyName = parts[0];
20944 		    } else {
20945 			    this.givenName = parts[0];
20946 		    }
20947 	    } else if (parts.length === 2) {
20948 		    // we do G F
20949 		    if (this.info.order === 'fgm') {
20950 			    this.givenName = parts[1];
20951 			    this.familyName = parts[0];
20952 		    } else if (this.info.order === "gmf") {
20953 			    this.givenName = parts[0];
20954 			    this.familyName = parts[1];
20955 		    } else if ( typeof (this.info.order) === 'undefined') {
20956 			    if (this._isPatronymicName(parts[1]) === true) {
20957 				    this.middleName = parts[1];
20958 				    this.givenName = parts[0];
20959 			    } else if ((familyIndex = this._findFamilyName(parts)) !== -1) {
20960 				    if (familyIndex === 1) {
20961 					    this.givenName = parts[0];
20962 					    this.familyName = parts[1];
20963 				    } else {
20964 					    this.familyName = parts[0];
20965 					    this.givenName = parts[1];
20966 				    }
20967 
20968 			    } else {
20969 				    this.givenName = parts[0];
20970 				    this.familyName = parts[1];
20971 			    }
20972 
20973 		    }
20974 	    } else if (parts.length >= 3) {
20975 		    // find the first instance of 'and' in the name
20976 		    conjunctionIndex = this._findLastConjunction(parts);
20977 		    var patronymicNameIndex = this._findPatronymicName(parts);
20978 		    if (conjunctionIndex > 0) {
20979 			    // if there's a conjunction that's not the first token, put
20980 				// everything up to and
20981 			    // including the token after it into the first name, the last
20982 				// token into
20983 			    // the family name (if it exists) and everything else in to the
20984 				// middle name
20985 			    // 0 1 2 3 4 5
20986 			    // G A G M M F
20987 			    // G G A G M F
20988 			    // G G G A G F
20989 			    // G G G G A G
20990 			    // if(this.order == "gmf") {
20991 			    this.givenName = parts.slice(0, conjunctionIndex + 2);
20992 
20993 			    if (conjunctionIndex + 1 < parts.length - 1) {
20994 				    this.familyName = parts.splice(parts.length - 1, 1);
20995 				    // //console.log(this.familyName);
20996 				    if (conjunctionIndex + 2 < parts.length - 1) {
20997 					    this.middleName = parts.slice(conjunctionIndex + 2,
20998 					        parts.length - conjunctionIndex - 3);
20999 				    }
21000 			    } else if (this.order == "fgm") {
21001 				    this.familyName = parts.slice(0, conjunctionIndex + 2);
21002 				    if (conjunctionIndex + 1 < parts.length - 1) {
21003 					    this.middleName = parts.splice(parts.length - 1, 1);
21004 					    if (conjunctionIndex + 2 < parts.length - 1) {
21005 						    this.givenName = parts.slice(conjunctionIndex + 2,
21006 						        parts.length - conjunctionIndex - 3);
21007 					    }
21008 				    }
21009 			    }
21010 		    } else if (patronymicNameIndex !== -1) {
21011 			    this.middleName = parts[patronymicNameIndex];
21012 
21013 			    if (patronymicNameIndex === (parts.length - 1)) {
21014 				    this.familyName = parts[0];
21015 				    this.givenName = parts.slice(1, patronymicNameIndex);
21016 			    } else {
21017 				    this.givenName = parts.slice(0, patronymicNameIndex);
21018 
21019 				    this.familyName = parts[parts.length - 1];
21020 			    }
21021 		    } else {
21022 			    this.givenName = parts[0];
21023 
21024 			    this.middleName = parts.slice(1, parts.length - 1);
21025 
21026 			    this.familyName = parts[parts.length - 1];
21027 		    }
21028 	    }
21029     },
21030     
21031     
21032     /**
21033      * @protected
21034      */
21035     _parseWesternName: function (parts) {
21036 
21037         if (this.locale.getLanguage() === "es" || this.locale.getLanguage() === "pt") {
21038             // in spain and mexico and portugal, we parse names differently than in the rest of the world 
21039             // because of the double family names
21040             this._parseSpanishName(parts);
21041         } else if (this.locale.getLanguage() === "ru") {
21042             /*
21043              * In Russian, names can be given equally validly as given-family
21044              * or family-given. Use the value of the "order" property of the
21045              * constructor options to give the default when the order is ambiguous.
21046              */
21047             this._parseRussianName(parts);
21048         } else if (this.locale.getLanguage() === "id") {
21049             // in indonesia, we parse names differently than in the rest of the world 
21050             // because names don't have family names usually.
21051             this._parseIndonesianName(parts);
21052         } else {
21053         	this._parseGenericWesternName(parts);
21054         }
21055     },
21056 
21057     /**
21058      * When sorting names with auxiliary words (like "van der" or "de los"), determine
21059      * which is the "head word" and return a string that can be easily sorted by head
21060      * word. In English, names are always sorted by initial characters. In places like
21061      * the Netherlands or Germany, family names are sorted by the head word of a list
21062      * of names rather than the first element of that name.
21063      * @return {string|undefined} a string containing the family name[s] to be used for sorting
21064      * in the current locale, or undefined if there is no family name in this object
21065      */
21066     getSortFamilyName: function () {
21067         var name,
21068             auxillaries,
21069             auxString,
21070             parts,
21071             i;
21072 
21073         // no name to sort by
21074         if (!this.familyName) {
21075             return undefined;
21076         }
21077 
21078         // first break the name into parts
21079         if (this.info) {
21080             if (this.info.sortByHeadWord) {
21081                 if (typeof (this.familyName) === 'string') {
21082                     name = this.familyName.replace(/\s+/g, ' '); // compress multiple whitespaces
21083                     parts = name.trim().split(' ');
21084                 } else {
21085                     // already split
21086                     parts = this.familyName;
21087                 }
21088 
21089                 auxillaries = this._findPrefix(parts, this.info.auxillaries, false);
21090                 if (auxillaries && auxillaries.length > 0) {
21091                     if (typeof (this.familyName) === 'string') {
21092                         auxString = auxillaries.join(' ');
21093                         name = this.familyName.substring(auxString.length + 1) + ', ' + auxString;
21094                     } else {
21095                         name = parts.slice(auxillaries.length).join(' ') +
21096                             ', ' +
21097                             parts.slice(0, auxillaries.length).join(' ');
21098                     }
21099                 }
21100             } else if (this.info.knownFamilyNames && this.familyName) {
21101                 parts = this.familyName.split('');
21102                 var familyNameArray = this._findPrefix(parts, this.info.knownFamilyNames, true, this.info.noCompoundFamilyNames);
21103                 name = "";
21104                 for (i = 0; i < familyNameArray.length; i++) {
21105                     name += (this.info.knownFamilyNames[familyNameArray[i]] || "");
21106                 }
21107             }
21108         }
21109 
21110         return name || this.familyName;
21111     },
21112 
21113     getHeadFamilyName: function () {},
21114 
21115     /** 
21116      * @protected
21117      * Return a shallow copy of the current instance.
21118      */
21119     clone: function () {
21120         return new Name(this);
21121     }
21122 };
21123 
21124 
21125 /*< NameFmt.js */
21126 /*
21127  * NameFmt.js - Format person names for display
21128  * 
21129  * Copyright © 2013-2015, JEDLSoft
21130  *
21131  * Licensed under the Apache License, Version 2.0 (the "License");
21132  * you may not use this file except in compliance with the License.
21133  * You may obtain a copy of the License at
21134  *
21135  *     http://www.apache.org/licenses/LICENSE-2.0
21136  *
21137  * Unless required by applicable law or agreed to in writing, software
21138  * distributed under the License is distributed on an "AS IS" BASIS,
21139  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21140  *
21141  * See the License for the specific language governing permissions and
21142  * limitations under the License.
21143  */
21144 
21145 /* !depends 
21146 ilib.js
21147 Locale.js
21148 IString.js
21149 Name.js
21150 isPunct.js
21151 Utils.js
21152 */
21153 
21154 // !data name
21155 
21156 
21157 
21158 
21159 /**
21160  * @class
21161  * Creates a formatter that can format person name instances (Name) for display to
21162  * a user. The options may contain the following properties:
21163  * 
21164  * <ul>
21165  * <li><i>locale</i> - Use the conventions of the given locale to construct the name format. 
21166  * <li><i>style</i> - Format the name with the given style. The value of this property
21167  * should be one of the following strings: 
21168  *   <ul>
21169  *     <li><i>short</i> - Format a short name with just the given and family names.
21170  *     <li><i>medium</i> - Format a medium-length name with the given, middle, and family names.
21171  *     <li><i>long</i> - Format a long name with all names available in the given name object, including
21172  *     prefixes.
21173  *     <li><i>full</i> - Format a long name with all names available in the given name object, including
21174  *     prefixes and suffixes.
21175  *   </ul>
21176  * <li><i>components</i> - Format the name with the given components in the correct
21177  * order for those components. Components are encoded as a string of letters representing
21178  * the desired components:
21179  *   <ul>
21180  *     <li><i>p</i> - prefixes
21181  *     <li><i>g</i> - given name
21182  *     <li><i>m</i> - middle names
21183  *     <li><i>f</i> - family name
21184  *     <li><i>s</i> - suffixes
21185  *   </ul>
21186  * <p>
21187  * 
21188  * For example, the string "pf" would mean to only format any prefixes and family names 
21189  * together and leave out all the other parts of the name.<p>
21190  * 
21191  * The components can be listed in any order in the string. The <i>components</i> option 
21192  * overrides the <i>style</i> option if both are specified.
21193  *
21194  * <li>onLoad - a callback function to call when the locale info object is fully 
21195  * loaded. When the onLoad option is given, the localeinfo object will attempt to
21196  * load any missing locale data using the ilib loader callback.
21197  * When the constructor is done (even if the data is already preassembled), the 
21198  * onLoad function is called with the current instance as a parameter, so this
21199  * callback can be used with preassembled or dynamic loading or a mix of the two.
21200  * 
21201  * <li>sync - tell whether to load any missing locale data synchronously or 
21202  * asynchronously. If this option is given as "false", then the "onLoad"
21203  * callback must be given, as the instance returned from this constructor will
21204  * not be usable for a while. 
21205  *
21206  * <li><i>loadParams</i> - an object containing parameters to pass to the 
21207  * loader callback function when locale data is missing. The parameters are not
21208  * interpretted or modified in any way. They are simply passed along. The object 
21209  * may contain any property/value pairs as long as the calling code is in
21210  * agreement with the loader callback function as to what those parameters mean.
21211  * </ul>
21212  * 
21213  * Formatting names is a locale-dependent function, as the order of the components 
21214  * depends on the locale. The following explains some of the details:<p>
21215  * 
21216  * <ul>
21217  * <li>In Western countries, the given name comes first, followed by a space, followed 
21218  * by the family name. In Asian countries, the family name comes first, followed immediately
21219  * by the given name with no space. But, that format is only used with Asian names written
21220  * in ideographic characters. In Asian countries, especially ones where both an Asian and 
21221  * a Western language are used (Hong Kong, Singapore, etc.), the convention is often to 
21222  * follow the language of the name. That is, Asian names are written in Asian style, and 
21223  * Western names are written in Western style. This class follows that convention as
21224  * well. 
21225  * <li>In other Asian countries, Asian names
21226  * written in Latin script are written with Asian ordering. eg. "Xu Ping-an" instead
21227  * of the more Western order "Ping-an Xu", as the order is thought to go with the style
21228  * that is appropriate for the name rather than the style for the language being written.
21229  * <li>In some Spanish speaking countries, people often take both their maternal and
21230  * paternal last names as their own family name. When formatting a short or medium style
21231  * of that family name, only the paternal name is used. In the long style, all the names
21232  * are used. eg. "Juan Julio Raul Lopez Ortiz" took the name "Lopez" from his father and 
21233  * the name "Ortiz" from his mother. His family name would be "Lopez Ortiz". The formatted
21234  * short style of his name would be simply "Juan Lopez" which only uses his paternal
21235  * family name of "Lopez".
21236  * <li>In many Western languages, it is common to use auxillary words in family names. For
21237  * example, the family name of "Ludwig von Beethoven" in German is "von Beethoven", not 
21238  * "Beethoven". This class ensures that the family name is formatted correctly with 
21239  * all auxillary words.   
21240  * </ul>
21241  * 
21242  * 
21243  * @constructor
21244  * @param {Object} options A set of options that govern how the formatter will behave
21245  */
21246 var NameFmt = function(options) {
21247 	var sync = true;
21248 	
21249 	this.style = "short";
21250 	this.loadParams = {};
21251 	
21252 	if (options) {
21253 		if (options.locale) {
21254 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
21255 		}
21256 		
21257 		if (options.style) {
21258 			this.style = options.style;
21259 		}
21260 		
21261 		if (options.components) {
21262 			this.components = options.components;
21263 		}
21264 		
21265 		if (typeof(options.sync) !== 'undefined') {
21266 			sync = (options.sync == true);
21267 		}
21268 		
21269 		if (typeof(options.loadParams) !== 'undefined') {
21270 			this.loadParams = options.loadParams;
21271 		}
21272 	}
21273 	
21274 	// set up defaults in case we need them
21275 	this.defaultEuroTemplate = new IString("{prefix} {givenName} {middleName} {familyName}{suffix}");
21276 	this.defaultAsianTemplate = new IString("{prefix}{familyName}{givenName}{middleName}{suffix}");
21277 	this.useFirstFamilyName = false;
21278 
21279 	switch (this.style) {
21280 		default:
21281 		case "s":
21282 		case "short":
21283 			this.style = "short";
21284 			break;
21285 		case "m":
21286 		case "medium":
21287 			this.style = "medium";
21288 			break;
21289 		case "l":
21290 		case "long":
21291 			this.style = "long";
21292 			break;
21293 		case "f":
21294 		case "full":
21295 			this.style = "full";
21296 			break;
21297 	}
21298 
21299 	if (!Name.cache) {
21300 		Name.cache = {};
21301 	}
21302 
21303 	this.locale = this.locale || new Locale();
21304 	
21305 	isPunct._init(sync, this.loadParams, ilib.bind(this, function() {
21306 		Utils.loadData({
21307 			object: Name, 
21308 			locale: this.locale, 
21309 			name: "name.json", 
21310 			sync: sync, 
21311 			loadParams: this.loadParams, 
21312 			callback: ilib.bind(this, function (info) {
21313 				if (!info) {
21314 					info = Name.defaultInfo;
21315 					var spec = this.locale.getSpec().replace(/-/g, "_");
21316 					Name.cache[spec] = info;
21317 				}
21318 				this.info = info;
21319 				this._init();
21320 				if (options && typeof(options.onLoad) === 'function') {
21321 					options.onLoad(this);
21322 				}
21323 			})
21324 		});
21325 	}));
21326 };
21327 
21328 NameFmt.prototype = {
21329 	/**                          
21330 	 * @protected
21331 	 */
21332 	_init: function() {
21333 		if (this.components) {
21334 			var valids = {"p":1,"g":1,"m":1,"f":1,"s":1},
21335 				arr = this.components.split("");
21336 			this.comps = {};
21337 			for (var i = 0; i < arr.length; i++) {
21338 				if (valids[arr[i].toLowerCase()]) {
21339 					this.comps[arr[i].toLowerCase()] = true;
21340 				}
21341 			}
21342 		} else {
21343 			this.comps = this.info.components[this.style];
21344 		}
21345 
21346 		this.template = new IString(this.info.format);
21347 		
21348 		if (this.locale.language === "es" && (this.style !== "long" && this.style !== "full")) {
21349 			this.useFirstFamilyName = true;	// in spanish, they have 2 family names, the maternal and paternal
21350 		}
21351 
21352 		this.isAsianLocale = (this.info.nameStyle === "asian");
21353 	},
21354 
21355 	/**
21356 	 * adjoin auxillary words to their head words
21357 	 * @protected
21358 	 */
21359 	_adjoinAuxillaries: function (parts, namePrefix) {
21360 		var start, i, prefixArray, prefix, prefixLower;
21361 		
21362 		//console.info("_adjoinAuxillaries: finding and adjoining aux words in " + parts.join(' '));
21363 		
21364 		if ( this.info.auxillaries && (parts.length > 2 || namePrefix) ) {
21365 			for ( start = 0; start < parts.length-1; start++ ) {
21366 				for ( i = parts.length; i > start; i-- ) {
21367 					prefixArray = parts.slice(start, i);
21368 					prefix = prefixArray.join(' ');
21369 					prefixLower = prefix.toLowerCase();
21370 					prefixLower = prefixLower.replace(/[,\.]/g, '');  // ignore commas and periods
21371 					
21372 					//console.info("_adjoinAuxillaries: checking aux prefix: '" + prefixLower + "' which is " + start + " to " + i);
21373 					
21374 					if ( prefixLower in this.info.auxillaries ) {
21375 						//console.info("Found! Old parts list is " + JSON.stringify(parts));
21376 						parts.splice(start, i+1-start, prefixArray.concat(parts[i]));
21377 						//console.info("_adjoinAuxillaries: Found! New parts list is " + JSON.stringify(parts));
21378 						i = start;
21379 					}
21380 				}
21381 			}
21382 		}
21383 		
21384 		//console.info("_adjoinAuxillaries: done. Result is " + JSON.stringify(parts));
21385 
21386 		return parts;
21387 	},
21388 
21389 	/**
21390 	 * Return the locale for this formatter instance.
21391 	 * @return {Locale} the locale instance for this formatter
21392 	 */
21393 	getLocale: function () {
21394 		return this.locale;
21395 	},
21396 	
21397 	/**
21398 	 * Return the style of names returned by this formatter
21399 	 * @return {string} the style of names returned by this formatter
21400 	 */
21401 	getStyle: function () {
21402 		return this.style;
21403 	},
21404 	
21405 	/**
21406 	 * Return the list of components used to format names in this formatter
21407 	 * @return {string} the list of components
21408 	 */
21409 	getComponents: function () {
21410 		return this.components;
21411 	},
21412 	
21413 	/**
21414 	 * Format the name for display in the current locale with the options set up
21415 	 * in the constructor of this formatter instance.<p>
21416 	 * 
21417 	 * If the name does not contain all the parts required for the style, those parts
21418 	 * will be left blank.<p>
21419 	 * 
21420 	 * There are two basic styles of formatting: European, and Asian. If this formatter object
21421 	 * is set for European style, but an Asian name is passed to the format method, then this
21422 	 * method will format the Asian name with a generic Asian template. Similarly, if the
21423 	 * formatter is set for an Asian style, and a European name is passed to the format method,
21424 	 * the formatter will use a generic European template.<p>
21425 	 * 
21426 	 * This means it is always safe to format any name with a formatter for any locale. You should
21427 	 * always get something at least reasonable as output.<p>
21428 	 * 
21429 	 * @param {Name} name the name to format
21430 	 * @return {string|undefined} the name formatted according to the style of this formatter instance
21431 	 */
21432 	format: function(name) {
21433 		var formatted, temp, modified, isAsianName;
21434 		var currentLanguage = this.locale.getLanguage();
21435 		 
21436 		if (!name || typeof(name) !== 'object') {
21437 			return undefined;
21438 		}
21439 		
21440 		if ((typeof(name.isAsianName) === 'boolean' && !name.isAsianName) ||
21441 				Name._isEuroName([name.givenName, name.middleName, name.familyName].join(""), currentLanguage)) {
21442 			isAsianName = false;	// this is a euro name, even if the locale is asian
21443 			modified = name.clone();
21444 			
21445 			// handle the case where there is no space if there is punctuation in the suffix like ", Phd". 
21446 			// Otherwise, put a space in to transform "PhD" to " PhD"
21447 			/*
21448 			console.log("suffix is " + modified.suffix);
21449 			if ( modified.suffix ) {
21450 				console.log("first char is " + modified.suffix.charAt(0));
21451 				console.log("isPunct(modified.suffix.charAt(0)) is " + isPunct(modified.suffix.charAt(0)));
21452 			}
21453 			*/
21454 			if (modified.suffix && isPunct(modified.suffix.charAt(0)) === false) {
21455 				modified.suffix = ' ' + modified.suffix; 
21456 			}
21457 			
21458 			if (this.useFirstFamilyName && name.familyName) {
21459 				var familyNameParts = modified.familyName.trim().split(' ');
21460 				if (familyNameParts.length > 1) {
21461 					familyNameParts = this._adjoinAuxillaries(familyNameParts, name.prefix);
21462 				}	//in spain and mexico, we parse names differently than in the rest of the world
21463 	
21464 				modified.familyName = familyNameParts[0];
21465 			}
21466 		
21467 			modified._joinNameArrays();
21468 		} else {
21469 			isAsianName = true;
21470 			modified = name;
21471 			if (modified.suffix && currentLanguage === "ko" && this.info.honorifics.indexOf(name.suffix) == -1) {
21472 				modified.suffix = ' ' + modified.suffix; 
21473 			}
21474 		}
21475 		
21476 		if (!this.template || isAsianName !== this.isAsianLocale) {
21477 			temp = isAsianName ? this.defaultAsianTemplate : this.defaultEuroTemplate;
21478 		} else {
21479 			temp = this.template;
21480 		}
21481 		
21482 		var parts = {
21483 			prefix: this.comps["p"] && modified.prefix || "",
21484 			givenName: this.comps["g"] && modified.givenName || "",
21485 			middleName: this.comps["m"] && modified.middleName || "",
21486 			familyName: this.comps["f"] && modified.familyName || "",
21487 			suffix: this.comps["s"] && modified.suffix || ""
21488 		};
21489 		
21490 		formatted = temp.format(parts);
21491 		return formatted.replace(/\s+/g, ' ').trim();
21492 	}
21493 };
21494 
21495 
21496 /*< Address.js */
21497 /*
21498  * Address.js - Represent a mailing address
21499  * 
21500  * Copyright © 2013-2015, JEDLSoft
21501  *
21502  * Licensed under the Apache License, Version 2.0 (the "License");
21503  * you may not use this file except in compliance with the License.
21504  * You may obtain a copy of the License at
21505  *
21506  *     http://www.apache.org/licenses/LICENSE-2.0
21507  *
21508  * Unless required by applicable law or agreed to in writing, software
21509  * distributed under the License is distributed on an "AS IS" BASIS,
21510  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21511  *
21512  * See the License for the specific language governing permissions and
21513  * limitations under the License.
21514  */
21515 
21516 /*globals console RegExp */
21517 
21518 /* !depends 
21519 ilib.js
21520 Utils.js
21521 JSUtils.js
21522 Locale.js 
21523 isIdeo.js 
21524 isAscii.js
21525 isDigit.js
21526 IString.js
21527 */
21528 
21529 // !data address countries nativecountries ctrynames
21530 
21531 
21532 /**
21533  * @class
21534  * Create a new Address instance and parse a physical address.<p>
21535  * 
21536  * This function parses a physical address written in a free-form string. 
21537  * It returns an object with a number of properties from the list below 
21538  * that it may have extracted from that address.<p>
21539  * 
21540  * The following is a list of properties that the algorithm will return:<p>
21541  * 
21542  * <ul>
21543  * <li><i>streetAddress</i>: The street address, including house numbers and all.
21544  * <li><i>locality</i>: The locality of this address (usually a city or town). 
21545  * <li><i>region</i>: The region where the locality is located. In the US, this
21546  * corresponds to states. In other countries, this may be provinces,
21547  * cantons, prefectures, etc. In some smaller countries, there are no
21548  * such divisions.
21549  * <li><i>postalCode</i>: Country-specific code for expediting mail. In the US, 
21550  * this is the zip code.
21551  * <li><i>country</i>: The country of the address.
21552  * <li><i>countryCode</i>: The ISO 3166 2-letter region code for the destination
21553  * country in this address.
21554  * </ul> 
21555  * 
21556  * The above properties will not necessarily appear in the instance. For 
21557  * any individual property, if the free-form address does not contain 
21558  * that property or it cannot be parsed out, the it is left out.<p>
21559  * 
21560  * The options parameter may contain any of the following properties:
21561  * 
21562  * <ul>
21563  * <li><i>locale</i> - locale or localeSpec to use to parse the address. If not 
21564  * specified, this function will use the current ilib locale
21565  * 
21566  * <li><i>onLoad</i> - a callback function to call when the address info for the
21567  * locale is fully loaded and the address has been parsed. When the onLoad 
21568  * option is given, the address object 
21569  * will attempt to load any missing locale data using the ilib loader callback.
21570  * When the constructor is done (even if the data is already preassembled), the 
21571  * onLoad function is called with the current instance as a parameter, so this
21572  * callback can be used with preassembled or dynamic loading or a mix of the two. 
21573  * 
21574  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
21575  * asynchronously. If this option is given as "false", then the "onLoad"
21576  * callback must be given, as the instance returned from this constructor will
21577  * not be usable for a while. 
21578  *
21579  * <li><i>loadParams</i> - an object containing parameters to pass to the 
21580  * loader callback function when locale data is missing. The parameters are not
21581  * interpretted or modified in any way. They are simply passed along. The object 
21582  * may contain any property/value pairs as long as the calling code is in
21583  * agreement with the loader callback function as to what those parameters mean.
21584  * </ul>
21585  * 
21586  * When an address cannot be parsed properly, the entire address will be placed
21587  * into the streetAddress property.<p>
21588  * 
21589  * When the freeformAddress is another Address, this will act like a copy
21590  * constructor.<p>
21591  * 
21592  * 
21593  * @constructor
21594  * @param {string|Address} freeformAddress free-form address to parse, or a
21595  * javascript object containing the fields
21596  * @param {Object} options options to the parser
21597  */
21598 var Address = function (freeformAddress, options) {
21599 	var address;
21600 
21601 	if (!freeformAddress) {
21602 		return undefined;
21603 	}
21604 
21605 	this.sync = true;
21606 	this.loadParams = {};
21607 	
21608 	if (options) {
21609 		if (options.locale) {
21610 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
21611 		}
21612 		
21613 		if (typeof(options.sync) !== 'undefined') {
21614 			this.sync = (options.sync == true);
21615 		}
21616 		
21617 		if (options.loadParams) {
21618 			this.loadParams = options.loadParams;
21619 		}
21620 	}
21621 
21622 	this.locale = this.locale || new Locale();
21623 	// initialize from an already parsed object
21624 	if (typeof(freeformAddress) === 'object') {
21625 		/**
21626 		 * The street address, including house numbers and all.
21627 		 * @type {string|undefined} 
21628 		 */
21629 		this.streetAddress = freeformAddress.streetAddress;
21630 		/**
21631 		 * The locality of this address (usually a city or town).
21632 		 * @type {string|undefined} 
21633 		 */
21634 		this.locality = freeformAddress.locality;
21635 		/**
21636 		 * The region (province, canton, prefecture, state, etc.) where the address is located.
21637 		 * @type {string|undefined} 
21638 		 */
21639 		this.region = freeformAddress.region;
21640 		/**
21641 		 * Country-specific code for expediting mail. In the US, this is the zip code.
21642 		 * @type {string|undefined} 
21643 		 */
21644 		this.postalCode = freeformAddress.postalCode;
21645 		/**
21646 		 * Optional city-specific code for a particular post office, used to expidite
21647 		 * delivery.
21648 		 * @type {string|undefined} 
21649 		 */
21650 		this.postOffice = freeformAddress.postOffice;
21651 		/**
21652 		 * The country of the address.
21653 		 * @type {string|undefined}
21654 		 */
21655 		this.country = freeformAddress.country;
21656 		if (freeformAddress.countryCode) {
21657 			/**
21658 			 * The 2 or 3 letter ISO 3166 region code for the destination country in this address.
21659 			 * @type {string} 
21660 			 */
21661 			this.countryCode = freeformAddress.countryCode;
21662 		}
21663 		if (freeformAddress.format) {
21664 			/**
21665 			 * private
21666 			 * @type {string}
21667 			 */
21668 			this.format = freeformAddress.format;
21669 		}
21670 		return this;
21671 	}
21672 
21673 	address = freeformAddress.replace(/[ \t\r]+/g, " ").trim();
21674 	address = address.replace(/[\s\n]+$/, "");
21675 	address = address.replace(/^[\s\n]+/, "");
21676 	//console.log("\n\n-------------\nAddress is '" + address + "'");
21677 	
21678 	this.lines = address.split(/[,,\n]/g);
21679 	this.removeEmptyLines(this.lines);
21680 	
21681 	isAscii._init(this.sync, this.loadParams, ilib.bind(this, function() {
21682 		isIdeo._init(this.sync, this.loadParams, ilib.bind(this, function() {
21683 			isDigit._init(this.sync, this.loadParams, ilib.bind(this, function() {
21684 				if (typeof(ilib.data.nativecountries) === 'undefined') {
21685 					Utils.loadData({
21686 						object: Address,
21687 						name: "nativecountries.json", // countries in their own language 
21688 						locale: "-", // only need to load the root file 
21689 						nonlocale: true,
21690 						sync: this.sync, 
21691 						loadParams: this.loadParams, 
21692 						callback: ilib.bind(this, function(nativecountries) {
21693 							ilib.data.nativecountries = nativecountries;
21694 							this._loadCountries(options && options.onLoad);
21695 						})
21696 					});
21697 				} else {
21698 					this._loadCountries(options && options.onLoad);
21699 				}
21700 			}));
21701 		}));
21702 	}));
21703 };
21704 
21705 /** @protected */
21706 Address.prototype = {
21707 	/**
21708 	 * @private
21709 	 */
21710 	_loadCountries: function(onLoad) {
21711 		if (typeof(ilib.data.countries) === 'undefined') {
21712 			Utils.loadData({
21713 				object: Address,
21714 				name: "countries.json", // countries in English
21715 				locale: "-", // only need to load the root file
21716 				nonlocale: true,
21717 				sync: this.sync, 
21718 				loadParams: this.loadParams, 
21719 				callback: ilib.bind(this, function(countries) {
21720 					ilib.data.countries = countries;
21721 					this._loadCtrynames(onLoad);
21722 				})
21723 			});
21724 		} else {
21725 			this._loadCtrynames(onLoad);
21726 		}
21727 	},
21728 
21729 	/**
21730 	 * @private
21731 	 */
21732 	_loadCtrynames: function(onLoad) {
21733 		Utils.loadData({
21734 			name: "ctrynames.json", 
21735 			object: Address, 
21736 			locale: this.locale,
21737 			sync: this.sync, 
21738 			loadParams: this.loadParams, 
21739 			callback: ilib.bind(this, function(ctrynames) {
21740 				this._determineDest(ctrynames, onLoad);
21741 			})
21742 		});
21743 	},
21744 	
21745 	/**
21746 	 * @private
21747 	 * @param {Object?} ctrynames
21748 	 */
21749 	_findDest: function (ctrynames) {
21750 		var match;
21751 		
21752 		for (var countryName in ctrynames) {
21753 			if (countryName && countryName !== "generated") {
21754 				// find the longest match in the current table
21755 				// ctrynames contains the country names mapped to region code
21756 				// for efficiency, only test for things longer than the current match
21757 				if (!match || match.text.length < countryName.length) {
21758 					var temp = this._findCountry(countryName);
21759 					if (temp) {
21760 						match = temp;
21761 						this.country = match.text;
21762 						this.countryCode = ctrynames[countryName];
21763 					}
21764 				}
21765 			}
21766 		}
21767 		return match;
21768 	},
21769 	
21770 	/**
21771 	 * @private
21772 	 * @param {Object?} localizedCountries
21773 	 * @param {function(Address):undefined} callback
21774 	 */
21775 	_determineDest: function (localizedCountries, callback) {
21776 		var match;
21777 		
21778 		/*
21779 		 * First, find the name of the destination country, as that determines how to parse
21780 		 * the rest of the address. For any address, there are three possible ways 
21781 		 * that the name of the country could be written:
21782 		 * 1. In the current language
21783 		 * 2. In its own native language
21784 		 * 3. In English
21785 		 * We'll try all three.
21786 		 */
21787 		var tables = [];
21788 		if (localizedCountries) {
21789 			tables.push(localizedCountries);
21790 		}
21791 		tables.push(ilib.data.nativecountries);
21792 		tables.push(ilib.data.countries);
21793 		
21794 		for (var i = 0; i < tables.length; i++) {
21795 			match = this._findDest(tables[i]);
21796 			
21797 			if (match) {
21798 				this.lines[match.line] = this.lines[match.line].substring(0, match.start) + this.lines[match.line].substring(match.start + match.text.length);
21799 
21800 				this._init(callback);
21801 				return;
21802 			}
21803 		}
21804 		
21805 		// no country, so try parsing it as if we were in the same country
21806 		this.country = undefined;
21807 		this.countryCode = this.locale.getRegion();
21808 		this._init(callback);
21809 	},
21810 
21811 	/**
21812 	 * @private
21813 	 * @param {function(Address):undefined} callback
21814 	 */
21815 	_init: function(callback) {
21816 		Utils.loadData({
21817 			object: Address, 
21818 			locale: new Locale(this.countryCode), 
21819 			name: "address.json", 
21820 			sync: this.sync, 
21821 			loadParams: this.loadParams,
21822 			callback: ilib.bind(this, function(info) {
21823 				if (!info || JSUtils.isEmpty(info)) {
21824 					// load the "unknown" locale instead
21825 					Utils.loadData({
21826 						object: Address, 
21827 						locale: new Locale("XX"), 
21828 						name: "address.json", 
21829 						sync: this.sync, 
21830 						loadParams: this.loadParams,
21831 						callback: ilib.bind(this, function(info) {
21832 							this.info = info;
21833 							this._parseAddress();
21834 							if (typeof(callback) === 'function') {
21835 								callback(this);
21836 							}	
21837 						})
21838 					});
21839 				} else {
21840 					this.info = info;
21841 					this._parseAddress();
21842 					if (typeof(callback) === 'function') {
21843 						callback(this);
21844 					}
21845 				}
21846 			})
21847 		});
21848 	},
21849 
21850 	/**
21851 	 * @private
21852 	 */
21853 	_parseAddress: function() {
21854 		// clean it up first
21855 		var i, 
21856 			asianChars = 0, 
21857 			latinChars = 0,
21858 			startAt,
21859 			infoFields,
21860 			field,
21861 			pattern,
21862 			matchFunction,
21863 			match,
21864 			fieldNumber;
21865 		
21866 		// for locales that support both latin and asian character addresses, 
21867 		// decide if we are parsing an asian or latin script address
21868 		if (this.info && this.info.multiformat) {
21869 			for (var j = 0; j < this.lines.length; j++) {
21870 				var line = new IString(this.lines[j]);
21871 				var it = line.charIterator();
21872 				while (it.hasNext()) {
21873 					var c = it.next();
21874 					if (isIdeo(c) || CType.withinRange(c, "Hangul")) {
21875 						asianChars++;
21876 					} else if (isAscii(c) && !isDigit(c)) {
21877 						latinChars++;
21878 					}
21879 				}
21880 			}
21881 			
21882 			this.format = (asianChars >= latinChars) ? "asian" : "latin";
21883 			startAt = this.info.startAt[this.format];
21884 			infoFields = this.info.fields[this.format];
21885 			// //console.log("multiformat locale: format is now " + this.format);
21886 		} else {
21887 			startAt = (this.info && this.info.startAt) || "end";
21888 			infoFields = this.info.fields;
21889 		}
21890 		this.compare = (startAt === "end") ? this.endsWith : this.startsWith;
21891 		
21892 		//console.log("this.lines is: " + JSON.stringify(this.lines));
21893 		
21894 		for (i = 0; i < infoFields.length && this.lines.length > 0; i++) {
21895 			field = infoFields[i];
21896 			this.removeEmptyLines(this.lines);
21897 			//console.log("Searching for field " + field.name);
21898 			if (field.pattern) {
21899 				if (typeof(field.pattern) === 'string') {
21900 					pattern = new RegExp(field.pattern, "img");
21901 					matchFunction = this.matchRegExp;
21902 				} else {
21903 					pattern = field.pattern;
21904 					matchFunction = this.matchPattern;
21905 				}
21906 					
21907 				switch (field.line) {
21908 				case 'startAtFirst':
21909 					for (fieldNumber = 0; fieldNumber < this.lines.length; fieldNumber++) {
21910 						match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt);
21911 						if (match) {
21912 							break;
21913 						}
21914 					}
21915 					break;
21916 				case 'startAtLast':
21917 					for (fieldNumber = this.lines.length-1; fieldNumber >= 0; fieldNumber--) {
21918 						match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt);
21919 						if (match) {
21920 							break;
21921 						}
21922 					}
21923 					break;
21924 				case 'first':
21925 					fieldNumber = 0;
21926 					match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt);
21927 					break;
21928 				case 'last':
21929 				default:
21930 					fieldNumber = this.lines.length - 1;
21931 					match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt);
21932 					break;
21933 				}
21934 				if (match) {
21935 					// //console.log("found match for " + field.name + ": " + JSON.stringify(match));
21936 					// //console.log("remaining line is " + match.line);
21937 					this.lines[fieldNumber] = match.line;
21938 					this[field.name] = match.match;
21939 				}
21940 			} else {
21941 				// if nothing is given, default to taking the whole field
21942 				this[field.name] = this.lines.splice(fieldNumber,1)[0].trim();
21943 				//console.log("typeof(this[field.name]) is " + typeof(this[field.name]) + " and value is " + JSON.stringify(this[field.name]));
21944 			}
21945 		}
21946 			
21947 		// all the left overs go in the street address field
21948 		this.removeEmptyLines(this.lines);
21949 		if (this.lines.length > 0) {
21950 			//console.log("this.lines is " + JSON.stringify(this.lines) + " and splicing to get streetAddress");
21951 			// Korea uses spaces between words, despite being an "asian" locale
21952 			var joinString = (this.info.joinString && this.info.joinString[this.format]) || ((this.format && this.format === "asian") ? "" : ", ");
21953 			this.streetAddress = this.lines.join(joinString).trim();
21954 		}
21955 		
21956 		this.lines = undefined;
21957 		//console.log("final result is " + JSON.stringify(this));
21958 	},
21959 	
21960 	/**
21961 	 * @protected
21962 	 * Find the named country either at the end or the beginning of the address.
21963 	 */
21964 	_findCountry: function(name) {
21965 		var start = -1, match, line = 0;
21966 		
21967 		if (this.lines.length > 0) {
21968 			start = this.startsWith(this.lines[line], name);
21969 			if (start === -1) {
21970 				line = this.lines.length-1;
21971 				start = this.endsWith(this.lines[line], name);
21972 			}
21973 			if (start !== -1) {
21974 				match = {
21975 					text: this.lines[line].substring(start, start + name.length),
21976 					line: line,
21977 					start: start
21978 				};
21979 			}
21980 		}
21981 		
21982 		return match;
21983 	},
21984 	
21985 	endsWith: function (subject, query) {
21986 		var start = subject.length-query.length,
21987 			i,
21988 			pat;
21989 		//console.log("endsWith: checking " + query + " against " + subject);
21990 		for (i = 0; i < query.length; i++) {
21991 			// TODO: use case mapper instead of toLowerCase()
21992 			if (subject.charAt(start+i).toLowerCase() !== query.charAt(i).toLowerCase()) {
21993 				return -1;
21994 			}
21995 		}
21996 		if (start > 0) {
21997 			pat = /\s/;
21998 			if (!pat.test(subject.charAt(start-1))) {
21999 				// make sure if we are not at the beginning of the string, that the match is 
22000 				// not the end of some other word
22001 				return -1;
22002 			}
22003 		}
22004 		return start;
22005 	},
22006 	
22007 	startsWith: function (subject, query) {
22008 		var i;
22009 		// //console.log("startsWith: checking " + query + " against " + subject);
22010 		for (i = 0; i < query.length; i++) {
22011 			// TODO: use case mapper instead of toLowerCase()
22012 			if (subject.charAt(i).toLowerCase() !== query.charAt(i).toLowerCase()) {
22013 				return -1;
22014 			}
22015 		}
22016 		return 0;
22017 	},
22018 	
22019 	removeEmptyLines: function (arr) {
22020 		var i = 0;
22021 		
22022 		while (i < arr.length) {
22023 			if (arr[i]) {
22024 				arr[i] = arr[i].trim();
22025 				if (arr[i].length === 0) {
22026 					arr.splice(i,1);
22027 				} else {
22028 					i++;
22029 				}
22030 			} else {
22031 				arr.splice(i,1);
22032 			}
22033 		}
22034 	},
22035 	
22036 	matchRegExp: function(address, line, expression, matchGroup, startAt) {
22037 		var lastMatch,
22038 			match,
22039 			ret = {},
22040 			last;
22041 		
22042 		//console.log("searching for regexp " + expression.source + " in line " + line);
22043 		
22044 		match = expression.exec(line);
22045 		if (startAt === 'end') {
22046 			while (match !== null && match.length > 0) {
22047 				//console.log("found matches " + JSON.stringify(match));
22048 				lastMatch = match;
22049 				match = expression.exec(line);
22050 			}
22051 			match = lastMatch;
22052 		}
22053 		
22054 		if (match && match !== null) {
22055 			//console.log("found matches " + JSON.stringify(match));
22056 			matchGroup = matchGroup || 0;
22057 			if (match[matchGroup] !== undefined) {
22058 				ret.match = match[matchGroup].trim();
22059 				ret.match = ret.match.replace(/^\-|\-+$/, '');
22060 				ret.match = ret.match.replace(/\s+$/, '');
22061 				last = (startAt === 'end') ? line.lastIndexOf(match[matchGroup]) : line.indexOf(match[matchGroup]); 
22062 				//console.log("last is " + last);
22063 				ret.line = line.slice(0,last);
22064 				if (address.format !== "asian") {
22065 					ret.line += " ";
22066 				}
22067 				ret.line += line.slice(last+match[matchGroup].length);
22068 				ret.line = ret.line.trim();
22069 				//console.log("found match " + ret.match + " from matchgroup " + matchGroup + " and rest of line is " + ret.line);
22070 				return ret;
22071 			}
22072 		//} else {
22073 			//console.log("no match");
22074 		}
22075 		
22076 		return undefined;
22077 	},
22078 	
22079 	matchPattern: function(address, line, pattern, matchGroup) {
22080 		var start,
22081 			j,
22082 			ret = {};
22083 		
22084 		//console.log("searching in line " + line);
22085 		
22086 		// search an array of possible fixed strings
22087 		//console.log("Using fixed set of strings.");
22088 		for (j = 0; j < pattern.length; j++) {
22089 			start = address.compare(line, pattern[j]); 
22090 			if (start !== -1) {
22091                             ret.match = line.substring(start, start+pattern[j].length);
22092                             if (start !== 0) {
22093                                 ret.line = line.substring(0,start).trim();
22094                             } else {
22095                                 ret.line = line.substring(pattern[j].length).trim();
22096                             }
22097 				//console.log("found match " + ret.match + " and rest of line is " + ret.line);
22098                             return ret;
22099 			}
22100 		}
22101 		
22102 		return undefined;
22103 	}
22104 };
22105 
22106 
22107 
22108 /*< AddressFmt.js */
22109 /*
22110  * AddressFmt.js - Format an address
22111  * 
22112  * Copyright © 2013-2015, JEDLSoft
22113  *
22114  * Licensed under the Apache License, Version 2.0 (the "License");
22115  * you may not use this file except in compliance with the License.
22116  * You may obtain a copy of the License at
22117  *
22118  *     http://www.apache.org/licenses/LICENSE-2.0
22119  *
22120  * Unless required by applicable law or agreed to in writing, software
22121  * distributed under the License is distributed on an "AS IS" BASIS,
22122  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22123  *
22124  * See the License for the specific language governing permissions and
22125  * limitations under the License.
22126  */
22127 
22128 /* !depends 
22129 ilib.js 
22130 Locale.js
22131 Address.js
22132 IString.js
22133 Utils.js
22134 JSUtils.js
22135 */
22136 
22137 // !data address
22138 
22139 
22140 
22141 /**
22142  * @class
22143  * Create a new formatter object to format physical addresses in a particular way.
22144  *
22145  * The options object may contain the following properties, both of which are optional:
22146  *
22147  * <ul>
22148  * <li><i>locale</i> - the locale to use to format this address. If not specified, it uses the default locale
22149  * 
22150  * <li><i>style</i> - the style of this address. The default style for each country usually includes all valid 
22151  * fields for that country.
22152  * 
22153  * <li><i>onLoad</i> - a callback function to call when the address info for the
22154  * locale is fully loaded and the address has been parsed. When the onLoad 
22155  * option is given, the address formatter object 
22156  * will attempt to load any missing locale data using the ilib loader callback.
22157  * When the constructor is done (even if the data is already preassembled), the 
22158  * onLoad function is called with the current instance as a parameter, so this
22159  * callback can be used with preassembled or dynamic loading or a mix of the two. 
22160  * 
22161  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
22162  * asynchronously. If this option is given as "false", then the "onLoad"
22163  * callback must be given, as the instance returned from this constructor will
22164  * not be usable for a while. 
22165  *
22166  * <li><i>loadParams</i> - an object containing parameters to pass to the 
22167  * loader callback function when locale data is missing. The parameters are not
22168  * interpretted or modified in any way. They are simply passed along. The object 
22169  * may contain any property/value pairs as long as the calling code is in
22170  * agreement with the loader callback function as to what those parameters mean.
22171  * </ul>
22172  * 
22173  * 
22174  * @constructor
22175  * @param {Object} options options that configure how this formatter should work
22176  * Returns a formatter instance that can format multiple addresses.
22177  */
22178 var AddressFmt = function(options) {
22179 	this.sync = true;
22180 	this.styleName = 'default';
22181 	this.loadParams = {};
22182 	this.locale = new Locale();
22183 	
22184 	if (options) {
22185 		if (options.locale) {
22186 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
22187 		}
22188 		
22189 		if (typeof(options.sync) !== 'undefined') {
22190 			this.sync = (options.sync == true);
22191 		}
22192 		
22193 		if (options.style) {
22194 			this.styleName = options.style;
22195 		}
22196 		
22197 		if (options.loadParams) {
22198 			this.loadParams = options.loadParams;
22199 		}
22200 	}
22201 	
22202 	// console.log("Creating formatter for region: " + this.locale.region);
22203 	Utils.loadData({
22204 		name: "address.json",
22205 		object: AddressFmt, 
22206 		locale: this.locale,
22207 		sync: this.sync, 
22208 		loadParams: this.loadParams, 
22209 		callback: ilib.bind(this, function(info) {
22210 			if (!info || JSUtils.isEmpty(info)) {
22211 				// load the "unknown" locale instead
22212 				Utils.loadData({
22213 					name: "address.json",
22214 					object: AddressFmt, 
22215 					locale: new Locale("XX"),
22216 					sync: this.sync, 
22217 					loadParams: this.loadParams, 
22218 					callback: ilib.bind(this, function(info) {
22219 						this.info = info;
22220 						this._init();
22221 						if (options && typeof(options.onLoad) === 'function') {
22222 							options.onLoad(this);
22223 						}
22224 					})
22225 				});
22226 			} else {
22227 				this.info = info;
22228 				this._init();
22229 				if (options && typeof(options.onLoad) === 'function') {
22230 					options.onLoad(this);
22231 				}
22232 			}
22233 		})
22234 	});
22235 };
22236 
22237 /**
22238  * @private
22239  */
22240 AddressFmt.prototype._init = function () {
22241 	this.style = this.info && this.info.formats && this.info.formats[this.styleName];
22242 	
22243 	// use generic default -- should not happen, but just in case...
22244 	this.style = this.style || (this.info && this.info.formats["default"]) || "{streetAddress}\n{locality} {region} {postalCode}\n{country}";
22245 };
22246 
22247 /**
22248  * This function formats a physical address (Address instance) for display. 
22249  * Whitespace is trimmed from the beginning and end of final resulting string, and 
22250  * multiple consecutive whitespace characters in the middle of the string are 
22251  * compressed down to 1 space character.
22252  * 
22253  * If the Address instance is for a locale that is different than the locale for this
22254  * formatter, then a hybrid address is produced. The country name is located in the
22255  * correct spot for the current formatter's locale, but the rest of the fields are
22256  * formatted according to the default style of the locale of the actual address.
22257  * 
22258  * Example: a mailing address in China, but formatted for the US might produce the words
22259  * "People's Republic of China" in English at the last line of the address, and the 
22260  * Chinese-style address will appear in the first line of the address. In the US, the
22261  * country is on the last line, but in China the country is usually on the first line.
22262  *
22263  * @param {Address} address Address to format
22264  * @returns {string} Returns a string containing the formatted address
22265  */
22266 AddressFmt.prototype.format = function (address) {
22267 	var ret, template, other, format;
22268 	
22269 	if (!address) {
22270 		return "";
22271 	}
22272 	// console.log("formatting address: " + JSON.stringify(address));
22273 	if (address.countryCode && 
22274 			address.countryCode !== this.locale.region && 
22275 			Locale._isRegionCode(this.locale.region) && 
22276 			this.locale.region !== "XX") {
22277 		// we are formatting an address that is sent from this country to another country,
22278 		// so only the country should be in this locale, and the rest should be in the other
22279 		// locale
22280 		// console.log("formatting for another locale. Loading in its settings: " + address.countryCode);
22281 		other = new AddressFmt({
22282 			locale: new Locale(address.countryCode), 
22283 			style: this.styleName
22284 		});
22285 		return other.format(address);
22286 	}
22287 	
22288 	if (typeof(this.style) === 'object') {
22289 		format = this.style[address.format || "latin"];
22290 	} else {
22291 		format = this.style;
22292 	}
22293 	
22294 	// console.log("Using format: " + format);
22295 	// make sure we have a blank string for any missing parts so that
22296 	// those template parts get blanked out
22297 	var params = {
22298 		country: address.country || "",
22299 		region: address.region || "",
22300 		locality: address.locality || "",
22301 		streetAddress: address.streetAddress || "",
22302 		postalCode: address.postalCode || "",
22303 		postOffice: address.postOffice || ""
22304 	};
22305 	template = new IString(format);
22306 	ret = template.format(params);
22307 	ret = ret.replace(/[ \t]+/g, ' ');
22308 	ret = ret.replace("\n ", "\n");
22309 	ret = ret.replace(" \n", "\n");
22310 	return ret.replace(/\n+/g, '\n').trim();
22311 };
22312 
22313 
22314 
22315 /*< GlyphString.js */
22316 /*
22317  * GlyphString.js - ilib string subclass that allows you to access 
22318  * whole glyphs at a time
22319  * 
22320  * Copyright © 2015-2017, JEDLSoft
22321  *
22322  * Licensed under the Apache License, Version 2.0 (the "License");
22323  * you may not use this file except in compliance with the License.
22324  * You may obtain a copy of the License at
22325  *
22326  *     http://www.apache.org/licenses/LICENSE-2.0
22327  *
22328  * Unless required by applicable law or agreed to in writing, software
22329  * distributed under the License is distributed on an "AS IS" BASIS,
22330  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22331  *
22332  * See the License for the specific language governing permissions and
22333  * limitations under the License.
22334  */
22335 
22336 // !depends IString.js CType.js Utils.js JSUtils.js
22337 // !data normdata ctype_m
22338 
22339 
22340 
22341 /**
22342  * @class
22343  * Create a new glyph string instance. This string inherits from 
22344  * the IString class, and adds methods that allow you to access
22345  * whole glyphs at a time. <p>
22346  * 
22347  * In Unicode, various accented characters can be created by using
22348  * a base character and one or more combining characters following
22349  * it. These appear on the screen to the user as a single glyph.
22350  * For example, the Latin character "a" (U+0061) followed by the
22351  * combining diaresis character "¨" (U+0308) combine together to
22352  * form the "a with diaresis" glyph "ä", which looks like a single
22353  * character on the screen.<p>
22354  * 
22355  * The big problem with combining characters for web developers is
22356  * that many CSS engines do not ellipsize text between glyphs. They
22357  * only deal with single Unicode characters. So if a particular space 
22358  * only allows for 4 characters, the CSS engine will truncate a
22359  * string at 4 Unicode characters and then add the ellipsis (...)
22360  * character. What if the fourth Unicode character is the "a" and
22361  * the fifth one is the diaresis? Then a string like "xxxäxxx" that
22362  * is ellipsized at 4 characters will appear as "xxxa..." on the 
22363  * screen instead of "xxxä...".<p>
22364  * 
22365  * In the Latin script as it is commonly used, it is not so common
22366  * to form accented characters using combining accents, so the above
22367  * example is mostly for illustrative purposes. It is not unheard of
22368  * however. The situation is much, much worse in scripts such as Thai and 
22369  * Devanagari that normally make very heavy use of combining characters.
22370  * These scripts do so because Unicode does not include pre-composed 
22371  * versions of the accented characters like it does for Latin, so 
22372  * combining accents are the only way to create these accented and 
22373  * combined versions of the characters.<p>
22374  * 
22375  * The solution to this problem is not to use the the CSS property 
22376  * "text-overflow: ellipsis" in your web site, ever. Instead, use
22377  * a glyph string to truncate text between glyphs dynamically,
22378  * rather than truncating between Unicode characters using CSS.<p>
22379  * 
22380  * Glyph strings are also useful for truncation, hyphenation, and 
22381  * line wrapping, as all of these should be done between glyphs instead
22382  * of between characters.<p>
22383  * 
22384  * The options parameter is optional, and may contain any combination
22385  * of the following properties:<p>
22386  * 
22387  * <ul>
22388  * <li><i>onLoad</i> - a callback function to call when the locale data are
22389  * fully loaded. When the onLoad option is given, this object will attempt to
22390  * load any missing locale data using the ilib loader callback.
22391  * When the constructor is done (even if the data is already preassembled), the 
22392  * onLoad function is called with the current instance as a parameter, so this
22393  * callback can be used with preassembled or dynamic loading or a mix of the two.
22394  * 
22395  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
22396  * asynchronously. If this option is given as "false", then the "onLoad"
22397  * callback must be given, as the instance returned from this constructor will
22398  * not be usable for a while.
22399  *  
22400  * <li><i>loadParams</i> - an object containing parameters to pass to the 
22401  * loader callback function when locale data is missing. The parameters are not
22402  * interpretted or modified in any way. They are simply passed along. The object 
22403  * may contain any property/value pairs as long as the calling code is in
22404  * agreement with the loader callback function as to what those parameters mean.
22405  * </ul>
22406  * 
22407  * @constructor
22408  * @extends IString
22409  * @param {string|IString=} str initialize this instance with this string 
22410  * @param {Object=} options options governing the way this instance works
22411  */
22412 var GlyphString = function (str, options) {
22413 	if (options && options.noinstance) {
22414 		return;
22415 	}
22416 	
22417 	IString.call(this, str);
22418 	
22419 	var sync = true;
22420 	var loadParams = {};
22421 	if (options) {
22422 		if (typeof(options.sync) === 'boolean') {
22423 			sync = options.sync;
22424 		}
22425 		if (options.loadParams) {
22426 			loadParams = options.loadParams;
22427 		}
22428 	}
22429 	
22430 	CType._load("ctype_m", sync, loadParams, function() {
22431 		if (!ilib.data.norm || JSUtils.isEmpty(ilib.data.norm.ccc)) {
22432 			Utils.loadData({
22433 				object: GlyphString, 
22434 				locale: "-", 
22435 				name: "normdata.json",
22436 				nonlocale: true,
22437 				sync: sync, 
22438 				loadParams: loadParams, 
22439 				callback: ilib.bind(this, function (norm) {
22440 					ilib.extend(ilib.data.norm, norm);
22441 					if (options && typeof(options.onLoad) === 'function') {
22442 						options.onLoad(this);
22443 					}
22444 				})
22445 			});
22446 		} else {
22447 			if (options && typeof(options.onLoad) === 'function') {
22448 				options.onLoad(this);
22449 			}
22450 		}
22451 	});
22452 };
22453 
22454 GlyphString.prototype = new IString(undefined);
22455 GlyphString.prototype.parent = IString;
22456 GlyphString.prototype.constructor = GlyphString;
22457 
22458 /**
22459  * Return true if the given character is a leading Jamo (Choseong) character.
22460  * 
22461  * @private
22462  * @static
22463  * @param {number} n code point to check
22464  * @return {boolean} true if the character is a leading Jamo character, 
22465  * false otherwise
22466  */
22467 GlyphString._isJamoL = function (n) {
22468 	return (n >= 0x1100 && n <= 0x1112);
22469 };
22470 
22471 /**
22472  * Return true if the given character is a vowel Jamo (Jungseong) character.
22473  * 
22474  * @private
22475  * @static
22476  * @param {number} n code point to check
22477  * @return {boolean} true if the character is a vowel Jamo character, 
22478  * false otherwise
22479  */
22480 GlyphString._isJamoV = function (n) {
22481 	return (n >= 0x1161 && n <= 0x1175);
22482 };
22483 
22484 /**
22485  * Return true if the given character is a trailing Jamo (Jongseong) character.
22486  * 
22487  * @private
22488  * @static
22489  * @param {number} n code point to check
22490  * @return {boolean} true if the character is a trailing Jamo character, 
22491  * false otherwise
22492  */
22493 GlyphString._isJamoT = function (n) {
22494 	return (n >= 0x11A8 && n <= 0x11C2);
22495 };
22496 
22497 /**
22498  * Return true if the given character is a LV Jamo character.
22499  * LV Jamo character is a precomposed Hangul character with LV sequence.
22500  * 
22501  * @private
22502  * @static
22503  * @param {number} n code point to check
22504  * @return {boolean} true if the character is a LV Jamo character,
22505  * false otherwise
22506  */
22507 GlyphString._isJamoLV = function (n) {
22508 	var syllableBase = 0xAC00;
22509 	var leadingJamoCount = 19;
22510 	var vowelJamoCount = 21;
22511 	var trailingJamoCount = 28;
22512 	var syllableCount = leadingJamoCount * vowelJamoCount * trailingJamoCount;
22513 	var syllableIndex = n - syllableBase;
22514 	// Check if n is a precomposed Hangul
22515 	if (0 <= syllableIndex && syllableIndex < syllableCount) {
22516 	// Check if n is a LV Jamo character
22517 		if((syllableIndex % trailingJamoCount) == 0) {
22518 			return true;
22519 		}
22520 	}
22521 	return false;
22522 };
22523 
22524 /**
22525  * Return true if the given character is a precomposed Hangul character.
22526  * The precomposed Hangul character may be a LV Jamo character or a LVT Jamo Character.
22527  * 
22528  * @private
22529  * @static
22530  * @param {number} n code point to check
22531  * @return {boolean} true if the character is a precomposed Hangul character, 
22532  * false otherwise
22533  */
22534 GlyphString._isHangul = function (n) {
22535 	return (n >= 0xAC00 && n <= 0xD7A3);
22536 };
22537 
22538 /**
22539  * Algorithmically compose an L and a V combining Jamo characters into
22540  * a precomposed Korean syllabic Hangul character. Both should already
22541  * be in the proper ranges for L and V characters. 
22542  * 
22543  * @private
22544  * @static
22545  * @param {number} lead the code point of the lead Jamo character to compose
22546  * @param {number} trail the code point of the trailing Jamo character to compose
22547  * @return {string} the composed Hangul character
22548  */
22549 GlyphString._composeJamoLV = function (lead, trail) {
22550 	var lindex = lead - 0x1100;
22551 	var vindex = trail - 0x1161;
22552 	return IString.fromCodePoint(0xAC00 + (lindex * 21 + vindex) * 28);
22553 };
22554 
22555 /**
22556  * Algorithmically compose a Hangul LV and a combining Jamo T character 
22557  * into a precomposed Korean syllabic Hangul character. 
22558  * 
22559  * @private
22560  * @static
22561  * @param {number} lead the code point of the lead Hangul character to compose
22562  * @param {number} trail the code point of the trailing Jamo T character to compose
22563  * @return {string} the composed Hangul character
22564  */
22565 GlyphString._composeJamoLVT = function (lead, trail) {
22566 	return IString.fromCodePoint(lead + (trail - 0x11A7));
22567 };
22568 
22569 /**
22570  * Compose one character out of a leading character and a 
22571  * trailing character. If the characters are Korean Jamo, they
22572  * will be composed algorithmically. If they are any other
22573  * characters, they will be looked up in the nfc tables.
22574  * 
22575  * @private
22576  * @static
22577  * @param {string} lead leading character to compose
22578  * @param {string} trail the trailing character to compose
22579  * @return {string|null} the fully composed character, or undefined if
22580  * there is no composition for those two characters
22581  */
22582 GlyphString._compose = function (lead, trail) {
22583 	var first = lead.charCodeAt(0);
22584 	var last = trail.charCodeAt(0);
22585 	if (GlyphString._isJamoLV(first) && GlyphString._isJamoT(last)) {
22586 		return GlyphString._composeJamoLVT(first, last);
22587 	} else if (GlyphString._isJamoL(first) && GlyphString._isJamoV(last)) {
22588 		return GlyphString._composeJamoLV(first, last);
22589 	}
22590 
22591 	var c = lead + trail;
22592 	return (ilib.data.norm.nfc && ilib.data.norm.nfc[c]);
22593 };
22594 
22595 /**
22596  * Return an iterator that will step through all of the characters
22597  * in the string one at a time, taking care to step through decomposed 
22598  * characters and through surrogate pairs in the UTF-16 encoding 
22599  * as single characters. <p>
22600  * 
22601  * The GlyphString class will return decomposed Unicode characters
22602  * as a single unit that a user might see on the screen as a single
22603  * glyph. If the 
22604  * next character in the iteration is a base character and it is 
22605  * followed by combining characters, the base and all its following 
22606  * combining characters are returned as a single unit.<p>
22607  * 
22608  * The standard Javascript String's charAt() method only
22609  * returns information about a particular 16-bit character in the 
22610  * UTF-16 encoding scheme.
22611  * If the index is pointing to a low- or high-surrogate character,
22612  * it will return that surrogate character rather 
22613  * than the surrogate pair which represents a character 
22614  * in the supplementary planes.<p>
22615  * 
22616  * The iterator instance returned has two methods, hasNext() which
22617  * returns true if the iterator has more characters to iterate through,
22618  * and next() which returns the next character.<p>
22619  * 
22620  * @override
22621  * @return {Object} an iterator 
22622  * that iterates through all the characters in the string
22623  */
22624 GlyphString.prototype.charIterator = function() {
22625 	var it = IString.prototype.charIterator.call(this);
22626 	
22627 	/**
22628 	 * @constructor
22629 	 */
22630 	function _chiterator (istring) {
22631 		this.index = 0;
22632 		this.spacingCombining = false;
22633 		this.hasNext = function () {
22634 			return !!this.nextChar || it.hasNext();
22635 		};
22636 		this.next = function () {
22637 			var ch = this.nextChar || it.next(),
22638 				prevCcc = ilib.data.norm.ccc[ch],
22639 				nextCcc,
22640 				composed = ch;
22641 			
22642 			this.nextChar = undefined;
22643 			this.spacingCombining = false;
22644 			
22645 			if (ilib.data.norm.ccc && 
22646 					(typeof(ilib.data.norm.ccc[ch]) === 'undefined' || ilib.data.norm.ccc[ch] === 0)) {
22647 				// found a starter... find all the non-starters until the next starter. Must include
22648 				// the next starter because under some odd circumstances, two starters sometimes recompose 
22649 				// together to form another character
22650 				var notdone = true;
22651 				while (it.hasNext() && notdone) {
22652 					this.nextChar = it.next();
22653 					nextCcc = ilib.data.norm.ccc[this.nextChar];
22654 					var codePoint = IString.toCodePoint(this.nextChar, 0);
22655 					// Mn characters are Marks that are non-spacing. These do not take more room than an accent, so they should be 
22656 					// considered part of the on-screen glyph, even if they are non-combining. Mc are marks that are spacing
22657 					// and combining, which means they are part of the glyph, but they cause the glyph to use up more space than
22658 					// just the base character alone.
22659 					var isMn = CType._inRange(codePoint, "Mn", ilib.data.ctype_m);
22660 					var isMc = CType._inRange(codePoint, "Mc", ilib.data.ctype_m);
22661 					if (isMn || isMc || (typeof(nextCcc) !== 'undefined' && nextCcc !== 0)) {
22662 						if (isMc) {
22663 							this.spacingCombining = true;
22664 						}
22665 						ch += this.nextChar;
22666 						this.nextChar = undefined;
22667 					} else {
22668 						// found the next starter. See if this can be composed with the previous starter
22669 						var testChar = GlyphString._compose(composed, this.nextChar);
22670 						if (prevCcc === 0 && typeof(testChar) !== 'undefined') { 
22671 							// not blocked and there is a mapping 
22672 							composed = testChar;
22673 							ch += this.nextChar;
22674 							this.nextChar = undefined;
22675 						} else {
22676 							// finished iterating, leave this.nextChar for the next next() call 
22677 							notdone = false;
22678 						}
22679 					}
22680 					prevCcc = nextCcc;
22681 				}
22682 			}
22683 			return ch;
22684 		};
22685 		// Returns true if the last character returned by the "next" method included
22686 		// spacing combining characters. If it does, then the character was wider than
22687 		// just the base character alone, and the truncation code will not add it.
22688 		this.wasSpacingCombining = function() {
22689 			return this.spacingCombining;
22690 		};
22691 	};
22692 	return new _chiterator(this);
22693 };
22694 
22695 /**
22696  * Truncate the current string at the given number of whole glyphs and return
22697  * the resulting string.
22698  * 
22699  * @param {number} length the number of whole glyphs to keep in the string
22700  * @return {string} a string truncated to the requested number of glyphs
22701  */
22702 GlyphString.prototype.truncate = function(length) {
22703 	var it = this.charIterator();
22704 	var tr = "";
22705 	for (var i = 0; i < length-1 && it.hasNext(); i++) {
22706 		tr += it.next();
22707 	}
22708 	
22709 	/*
22710 	 * handle the last character separately. If it contains spacing combining
22711 	 * accents, then we must assume that it uses up more horizontal space on
22712 	 * the screen than just the base character by itself, and therefore this
22713 	 * method will not truncate enough characters to fit in the given length.
22714 	 * In this case, we have to chop off not only the combining characters, 
22715 	 * but also the base character as well because the base without the
22716 	 * combining accents is considered a different character.
22717 	 */
22718 	if (i < length && it.hasNext()) {
22719 		var c = it.next();
22720 		if (!it.wasSpacingCombining()) {
22721 			tr += c;
22722 		}
22723 	}
22724 	return tr;
22725 };
22726 
22727 /**
22728  * Truncate the current string at the given number of glyphs and add an ellipsis
22729  * to indicate that is more to the string. The ellipsis forms the last character
22730  * in the string, so the string is actually truncated at length-1 glyphs.
22731  * 
22732  * @param {number} length the number of whole glyphs to keep in the string 
22733  * including the ellipsis
22734  * @return {string} a string truncated to the requested number of glyphs
22735  * with an ellipsis
22736  */
22737 GlyphString.prototype.ellipsize = function(length) {
22738 	return this.truncate(length > 0 ? length-1 : 0) + "…";
22739 };
22740 
22741 
22742 
22743 /*< NormString.js */
22744 /*
22745  * NormString.js - ilib normalized string subclass definition
22746  * 
22747  * Copyright © 2013-2015, JEDLSoft
22748  *
22749  * Licensed under the Apache License, Version 2.0 (the "License");
22750  * you may not use this file except in compliance with the License.
22751  * You may obtain a copy of the License at
22752  *
22753  *     http://www.apache.org/licenses/LICENSE-2.0
22754  *
22755  * Unless required by applicable law or agreed to in writing, software
22756  * distributed under the License is distributed on an "AS IS" BASIS,
22757  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22758  *
22759  * See the License for the specific language governing permissions and
22760  * limitations under the License.
22761  */
22762 
22763 // !depends IString.js GlyphString.js Utils.js
22764 
22765 
22766 
22767 /**
22768  * @class
22769  * Create a new normalized string instance. This string inherits from 
22770  * the GlyphString class, and adds the normalize method. It can be
22771  * used anywhere that a normal Javascript string is used. <p>
22772  * 
22773  * 
22774  * @constructor
22775  * @extends GlyphString
22776  * @param {string|IString=} str initialize this instance with this string 
22777  */
22778 var NormString = function (str) {
22779 	GlyphString.call(this, str);
22780 };
22781 
22782 NormString.prototype = new GlyphString("", {noinstance:true});
22783 NormString.prototype.parent = GlyphString;
22784 NormString.prototype.constructor = NormString;
22785 
22786 /**
22787  * Initialize the normalized string routines statically. This
22788  * is intended to be called in a dynamic-load version of ilib
22789  * to load the data need to normalize strings before any instances
22790  * of NormString are created.<p>
22791  * 
22792  * The options parameter may contain any of the following properties:
22793  * 
22794  * <ul>
22795  * <li><i>form</i> - {string} the normalization form to load
22796  * <li><i>script</i> - {string} load the normalization for this script. If the 
22797  * script is given as "all" then the normalization data for all scripts
22798  * is loaded at the same time
22799  * <li><i>sync</i> - {boolean} whether to load the files synchronously or not
22800  * <li><i>loadParams</i> - {Object} parameters to the loader function
22801  * <li><i>onLoad</i> - {function()} a function to call when the 
22802  * files are done being loaded
22803  * </ul>
22804  * 
22805  * @param {Object} options an object containing properties that govern 
22806  * how to initialize the data
22807  */
22808 NormString.init = function(options) {
22809 	if (!ilib._load || (typeof(ilib._load) !== 'function' && typeof(ilib._load.loadFiles) !== 'function')) {
22810 		// can't do anything
22811 		return;
22812 	}
22813 	var form = "nfkc";
22814 	var script = "all";
22815 	var sync = true;
22816 	var onLoad = undefined;
22817 	var loadParams = undefined;
22818 	if (options) {
22819 		form = options.form || "nfkc";
22820 		script = options.script || "all";
22821 		sync = typeof(options.sync) !== 'undefined' ? options.sync : true;
22822 		onLoad = typeof(options.onLoad) === 'function' ? options.onLoad : undefined;
22823 		if (options.loadParams) {
22824 			loadParams = options.loadParams;
22825 		}
22826 	}
22827 	var formDependencies = {
22828 		"nfd": ["nfd"],
22829 		"nfc": ["nfd"],
22830 		"nfkd": ["nfkd", "nfd"],
22831 		"nfkc": ["nfkd", "nfd"]
22832 	};
22833 	var files = ["normdata.json"];
22834 	var forms = formDependencies[form];
22835 	for (var f in forms) {
22836 		files.push(forms[f] + "/" + script + ".json");
22837 	}
22838 	
22839 	if (JSUtils.isEmpty(ilib.data.norm.ccc) || JSUtils.isEmpty(ilib.data.norm.nfd) || JSUtils.isEmpty(ilib.data.norm.nfkd)) {
22840 		//console.log("loading files " + JSON.stringify(files));
22841 		Utils._callLoadData(files, sync, loadParams, function(arr) {
22842 			ilib.extend(ilib.data.norm, arr[0]);
22843 			for (var i = 1; i < arr.length; i++) {
22844 				if (typeof(arr[i]) !== 'undefined') {
22845 					ilib.extend(ilib.data.norm[forms[i-1]], arr[i]);
22846 				}
22847 			}
22848 			
22849 			if (onLoad) {
22850 				onLoad(arr);
22851 			}
22852 		});
22853 	}
22854 };
22855 
22856 /**
22857  * Algorithmically decompose a precomposed Korean syllabic Hangul 
22858  * character into its individual combining Jamo characters. The given 
22859  * character must be in the range of Hangul characters U+AC00 to U+D7A3.
22860  * 
22861  * @private
22862  * @static
22863  * @param {number} cp code point of a Korean Hangul character to decompose
22864  * @return {string} the decomposed string of Jamo characters
22865  */
22866 NormString._decomposeHangul = function (cp) {
22867 	var sindex = cp - 0xAC00;
22868 	var result = String.fromCharCode(0x1100 + sindex / 588) + 
22869 			String.fromCharCode(0x1161 + (sindex % 588) / 28);
22870 	var t = sindex % 28;
22871 	if (t !== 0) {
22872 		result += String.fromCharCode(0x11A7 + t);
22873 	}
22874 	return result;
22875 };
22876 
22877 /**
22878  * Expand one character according to the given canonical and 
22879  * compatibility mappings.
22880  *
22881  * @private
22882  * @static
22883  * @param {string} ch character to map
22884  * @param {Object} canon the canonical mappings to apply
22885  * @param {Object=} compat the compatibility mappings to apply, or undefined
22886  * if only the canonical mappings are needed
22887  * @return {string} the mapped character
22888  */
22889 NormString._expand = function (ch, canon, compat) {
22890 	var i, 
22891 		expansion = "",
22892 		n = ch.charCodeAt(0);
22893 	if (GlyphString._isHangul(n)) {
22894 		expansion = NormString._decomposeHangul(n);
22895 	} else {
22896 		var result = canon[ch];
22897 		if (!result && compat) {
22898 			result = compat[ch];
22899 		}
22900 		if (result && result !== ch) {
22901 			for (i = 0; i < result.length; i++) {
22902 				expansion += NormString._expand(result[i], canon, compat);
22903 			}
22904 		} else {
22905 			expansion = ch;
22906 		}
22907 	}
22908 	return expansion;
22909 };
22910 
22911 /**
22912  * Perform the Unicode Normalization Algorithm upon the string and return 
22913  * the resulting new string. The current string is not modified.
22914  * 
22915  * <h2>Forms</h2>
22916  * 
22917  * The forms of possible normalizations are defined by the <a 
22918  * href="http://www.unicode.org/reports/tr15/">Unicode Standard
22919  * Annex (UAX) 15</a>. The form parameter is a string that may have one 
22920  * of the following values:
22921  * 
22922  * <ul>
22923  * <li>nfd - Canonical decomposition. This decomposes characters into
22924  * their exactly equivalent forms. For example, "ü" would decompose
22925  * into a "u" followed by the combining diaeresis character. 
22926  * <li>nfc - Canonical decomposition followed by canonical composition.
22927  * This decomposes and then recomposes character into their shortest
22928  * exactly equivalent forms by recomposing as many combining characters
22929  * as possible. For example, "ü" followed by a combining 
22930  * macron character would decompose into a "u" followed by the combining 
22931  * macron characters the combining diaeresis character, and then be recomposed into
22932  * the u with macron and diaeresis "ṻ" character. The reason that
22933  * the "nfc" form decomposes and then recomposes is that combining characters
22934  * have a specific order under the Unicode Normalization Algorithm, and
22935  * partly composed characters such as the "ü" followed by combining
22936  * marks may change the order of the combining marks when decomposed and
22937  * recomposed.
22938  * <li>nfkd - Compatibility decomposition. This decomposes characters
22939  * into compatible forms that may not be exactly equivalent semantically,
22940  * as well as performing canonical decomposition as well.
22941  * For example, the "œ" ligature character decomposes to the two
22942  * characters "oe" because they are compatible even though they are not 
22943  * exactly the same semantically. 
22944  * <li>nfkc - Compatibility decomposition followed by canonical composition.
22945  * This decomposes characters into compatible forms, then recomposes
22946  * characters using the canonical composition. That is, it breaks down
22947  * characters into the compatible forms, and then recombines all combining
22948  * marks it can with their base characters. For example, the character
22949  * "ǽ" would be normalized to "aé" by first decomposing
22950  * the character into "a" followed by "e" followed by the combining acute accent
22951  * combining mark, and then recomposed to an "a" followed by the "e"
22952  * with acute accent.
22953  * </ul>
22954  * 
22955  * <h2>Operation</h2>
22956  * 
22957  * Two strings a and b can be said to be canonically equivalent if 
22958  * normalize(a) = normalize(b)
22959  * under the nfc normalization form. Two strings can be said to be compatible if
22960  * normalize(a) = normalize(b) under the nfkc normalization form.<p>
22961  * 
22962  * The canonical normalization is often used to see if strings are 
22963  * equivalent to each other, and thus is useful when implementing parsing 
22964  * algorithms or exact matching algorithms. It can also be used to ensure
22965  * that any string output produces a predictable sequence of characters.<p>
22966  * 
22967  * Compatibility normalization 
22968  * does not always preserve the semantic meaning of all the characters, 
22969  * although this is sometimes the behaviour that you are after. It is useful, 
22970  * for example, when doing searches of user-input against text in documents 
22971  * where the matches are supposed to "fuzzy". In this case, both the query
22972  * string and the document string would be mapped to their compatibility 
22973  * normalized forms, and then compared.<p>
22974  * 
22975  * Compatibility normalization also does not guarantee round-trip conversion
22976  * to and from legacy character sets as the normalization is "lossy". It is 
22977  * akin to doing a lower- or upper-case conversion on text -- after casing,
22978  * you cannot tell what case each character is in the original string. It is 
22979  * good for matching and searching, but it rarely good for output because some 
22980  * distinctions or meanings in the original text have been lost.<p>
22981  * 
22982  * Note that W3C normalization for HTML also escapes and unescapes
22983  * HTML character entities such as "&uuml;" for u with diaeresis. This
22984  * method does not do such escaping or unescaping. If normalization is required
22985  * for HTML strings with entities, unescaping should be performed on the string 
22986  * prior to calling this method.<p>
22987  * 
22988  * <h2>Data</h2>
22989  * 
22990  * Normalization requires a fair amount of mapping data, much of which you may 
22991  * not need for the characters expected in your texts. It is possible to assemble
22992  * a copy of ilib that saves space by only including normalization data for 
22993  * those scripts that you expect to encounter in your data.<p>
22994  * 
22995  * The normalization data is organized by normalization form and within there
22996  * by script. To include the normalization data for a particular script with
22997  * a particular normalization form, use the directive:
22998  * 
22999  * <pre><code>
23000  * !depends <form>/<script>.js
23001  * </code></pre>
23002  * 
23003  * Where <form> is the normalization form ("nfd", "nfc", "nfkd", or "nfkc"), and
23004  * <script> is the ISO 15924 code for the script you would like to
23005  * support. Example: to load in the NFC data for Cyrillic, you would use:
23006  * 
23007  * <pre><code>
23008  * !depends nfc/Cyrl.js
23009  * </code></pre>
23010  * 
23011  * Note that because certain normalization forms include others in their algorithm, 
23012  * their data also depends on the data for the other forms. For example, if you 
23013  * include the "nfc" data for a script, you will automatically get the "nfd" data 
23014  * for that same script as well because the NFC algorithm does NFD normalization 
23015  * first. Here are the dependencies:<p>
23016  * 
23017  * <ul>
23018  * <li>NFD -> no dependencies
23019  * <li>NFC -> NFD
23020  * <li>NFKD -> NFD
23021  * <li>NFKC -> NFKD, NFD, NFC
23022  * </ul>
23023  * 
23024  * A special value for the script dependency is "all" which will cause the data for 
23025  * all scripts
23026  * to be loaded for that normalization form. This would be useful if you know that
23027  * you are going to normalize a lot of multilingual text or cannot predict which scripts
23028  * will appear in the input. Because the NFKC form depends on all others, you can 
23029  * get all of the data for all forms automatically by depending on "nfkc/all.js".
23030  * Note that the normalization data for practically all script automatically depend
23031  * on data for the Common script (code "Zyyy") which contains all of the characters
23032  * that are commonly used in many different scripts. Examples of characters in the
23033  * Common script are the ASCII punctuation characters, or the ASCII Arabic 
23034  * numerals "0" through "9".<p>
23035  * 
23036  * By default, none of the data for normalization is automatically 
23037  * included in the preassembled iliball.js file. 
23038  * If you would like to normalize strings, you must assemble
23039  * your own copy of ilib and explicitly include the normalization data
23040  * for those scripts as per the instructions above. This normalization method will 
23041  * produce output, even without the normalization data. However, the output will be 
23042  * simply the same thing as its input for all scripts 
23043  * except Korean Hangul and Jamo, which are decomposed and recomposed 
23044  * algorithmically and therefore do not rely on data.<p>
23045  * 
23046  * If characters are encountered for which there are no normalization data, they
23047  * will be passed through to the output string unmodified.
23048  * 
23049  * @param {string} form The normalization form requested
23050  * @return {IString} a new instance of an IString that has been normalized
23051  * according to the requested form. The current instance is not modified.
23052  */
23053 NormString.prototype.normalize = function (form) {
23054 	var i;
23055 	
23056 	if (typeof(form) !== 'string' || this.str.length === 0) {
23057 		return new IString(this.str);
23058 	}
23059 	
23060 	var nfc = false,
23061 		nfkd = false;
23062 	
23063 	switch (form) {
23064 	default:
23065 		break;
23066 		
23067 	case "nfc":
23068 		nfc = true;
23069 		break;
23070 		
23071 	case "nfkd":
23072 		nfkd = true;
23073 		break;
23074 		
23075 	case "nfkc":
23076 		nfkd = true;
23077 		nfc = true;
23078 		break;
23079 	}
23080 
23081 	// decompose
23082 	var decomp = "";
23083 	
23084 	if (nfkd) {
23085 		var ch, it = IString.prototype.charIterator.call(this);
23086 		while (it.hasNext()) {
23087 			ch = it.next();
23088 			decomp += NormString._expand(ch, ilib.data.norm.nfd, ilib.data.norm.nfkd);
23089 		}
23090 	} else {
23091 		var ch, it = IString.prototype.charIterator.call(this);
23092 		while (it.hasNext()) {
23093 			ch = it.next();
23094 			decomp += NormString._expand(ch, ilib.data.norm.nfd);
23095 		}
23096 	}
23097 
23098 	// now put the combining marks in a fixed order by 
23099 	// sorting on the combining class
23100 	function compareByCCC(left, right) {
23101 		return ilib.data.norm.ccc[left] - ilib.data.norm.ccc[right]; 
23102 	}
23103 	
23104 	function ccc(c) {
23105 		return ilib.data.norm.ccc[c] || 0;
23106 	}
23107 
23108 	function sortChars(arr, comp) {
23109 		// qt/qml's Javascript engine re-arranges entries that are equal to
23110 		// each other. Technically, that is a correct behaviour, but it is
23111 		// not desirable. All the other engines leave equivalent entries
23112 		// where they are. This bubblesort emulates what the other engines
23113 		// do. Fortunately, the arrays we are sorting are a max of 5 or 6
23114 		// entries, so performance is not a big deal here.
23115 		if (ilib._getPlatform() === "qt") {
23116 			var tmp;
23117 			for (var i = arr.length-1; i > 0; i--) {
23118 				for (var j = 0; j < i; j++) {
23119 					if (comp(arr[j], arr[j+1]) > 0) {
23120 						tmp = arr[j];
23121 						arr[j] = arr[j+1];
23122 						arr[j+1] = tmp;
23123 					}
23124 				}
23125 			}
23126 			return arr;
23127 		} else {
23128 			return arr.sort(comp);
23129 		}
23130 	}
23131 		
23132 	var dstr = new IString(decomp);
23133 	var it = dstr.charIterator();
23134 	var cpArray = [];
23135 
23136 	// easier to deal with as an array of chars
23137 	while (it.hasNext()) {
23138 		cpArray.push(it.next());
23139 	}
23140 	
23141 	i = 0;
23142 	while (i < cpArray.length) {
23143 		if (typeof(ilib.data.norm.ccc[cpArray[i]]) !== 'undefined' && ccc(cpArray[i]) !== 0) {
23144 			// found a non-starter... rearrange all the non-starters until the next starter
23145 			var end = i+1;
23146 			while (end < cpArray.length &&
23147 					typeof(ilib.data.norm.ccc[cpArray[end]]) !== 'undefined' && 
23148 					ccc(cpArray[end]) !== 0) {
23149 				end++;
23150 			}
23151 			
23152 			// simple sort of the non-starter chars
23153 			if (end - i > 1) {
23154 				cpArray = cpArray.slice(0,i).concat(sortChars(cpArray.slice(i, end), compareByCCC), cpArray.slice(end));
23155 			}
23156 		}
23157 		i++;
23158 	}
23159 	
23160 	if (nfc) {
23161 		i = 0;
23162 		while (i < cpArray.length) {
23163 			if (typeof(ilib.data.norm.ccc[cpArray[i]]) === 'undefined' || ilib.data.norm.ccc[cpArray[i]] === 0) {
23164 				// found a starter... find all the non-starters until the next starter. Must include
23165 				// the next starter because under some odd circumstances, two starters sometimes recompose 
23166 				// together to form another character
23167 				var end = i+1;
23168 				var notdone = true;
23169 				while (end < cpArray.length && notdone) {
23170 					if (typeof(ilib.data.norm.ccc[cpArray[end]]) !== 'undefined' && 
23171 						ilib.data.norm.ccc[cpArray[end]] !== 0) {
23172 						if (ccc(cpArray[end-1]) < ccc(cpArray[end])) { 
23173 							// not blocked 
23174 							var testChar = GlyphString._compose(cpArray[i], cpArray[end]);
23175 							if (typeof(testChar) !== 'undefined') {
23176 								cpArray[i] = testChar;
23177 								
23178 								// delete the combining char
23179 								cpArray.splice(end,1);	
23180 								
23181 								// restart the iteration, just in case there is more to recompose with the new char
23182 								end = i;
23183 							}
23184 						}
23185 						end++;
23186 					} else {
23187 						// found the next starter. See if this can be composed with the previous starter
23188 						var testChar = GlyphString._compose(cpArray[i], cpArray[end]);
23189 						if (ccc(cpArray[end-1]) === 0 && typeof(testChar) !== 'undefined') { 
23190 							// not blocked and there is a mapping 
23191 							cpArray[i] = testChar;
23192 							
23193 							// delete the combining char
23194 							cpArray.splice(end,1);
23195 							
23196 							// restart the iteration, just in case there is more to recompose with the new char
23197 							end = i+1;
23198 						} else {
23199 							// finished iterating 
23200 							notdone = false;
23201 						}
23202 					}
23203 				}
23204 			}
23205 			i++;
23206 		}
23207 	}
23208 	
23209 	return new IString(cpArray.length > 0 ? cpArray.join("") : "");
23210 };
23211 	
23212 /**
23213  * @override
23214  * Return an iterator that will step through all of the characters
23215  * in the string one at a time, taking care to step through decomposed 
23216  * characters and through surrogate pairs in UTF-16 encoding 
23217  * properly. <p>
23218  * 
23219  * The NormString class will return decomposed Unicode characters
23220  * as a single unit that a user might see on the screen. If the 
23221  * next character in the iteration is a base character and it is 
23222  * followed by combining characters, the base and all its following 
23223  * combining characters are returned as a single unit.<p>
23224  * 
23225  * The standard Javascript String's charAt() method only
23226  * returns information about a particular 16-bit character in the 
23227  * UTF-16 encoding scheme.
23228  * If the index is pointing to a low- or high-surrogate character,
23229  * it will return that surrogate character rather 
23230  * than the surrogate pair which represents a character 
23231  * in the supplementary planes.<p>
23232  * 
23233  * The iterator instance returned has two methods, hasNext() which
23234  * returns true if the iterator has more characters to iterate through,
23235  * and next() which returns the next character.<p>
23236  * 
23237  * @return {Object} an iterator 
23238  * that iterates through all the characters in the string
23239  */
23240 NormString.prototype.charIterator = function() {
23241 	var it = IString.prototype.charIterator.call(this);
23242 	
23243 	/**
23244 	 * @constructor
23245 	 */
23246 	function _chiterator (istring) {
23247 		/**
23248 		 * @private
23249 		 */
23250 		var ccc = function(c) {
23251 			return ilib.data.norm.ccc[c] || 0;
23252 		};
23253 
23254 		this.index = 0;
23255 		this.hasNext = function () {
23256 			return !!this.nextChar || it.hasNext();
23257 		};
23258 		this.next = function () {
23259 			var ch = this.nextChar || it.next(),
23260 				prevCcc = ccc(ch),
23261 				nextCcc,
23262 				composed = ch;
23263 			
23264 			this.nextChar = undefined;
23265 			
23266 			if (ilib.data.norm.ccc && 
23267 					(typeof(ilib.data.norm.ccc[ch]) === 'undefined' || ccc(ch) === 0)) {
23268 				// found a starter... find all the non-starters until the next starter. Must include
23269 				// the next starter because under some odd circumstances, two starters sometimes recompose 
23270 				// together to form another character
23271 				var notdone = true;
23272 				while (it.hasNext() && notdone) {
23273 					this.nextChar = it.next();
23274 					nextCcc = ccc(this.nextChar);
23275 					if (typeof(ilib.data.norm.ccc[this.nextChar]) !== 'undefined' && nextCcc !== 0) {
23276 						ch += this.nextChar;
23277 						this.nextChar = undefined;
23278 					} else {
23279 						// found the next starter. See if this can be composed with the previous starter
23280 						var testChar = GlyphString._compose(composed, this.nextChar);
23281 						if (prevCcc === 0 && typeof(testChar) !== 'undefined') { 
23282 							// not blocked and there is a mapping 
23283 							composed = testChar;
23284 							ch += this.nextChar;
23285 							this.nextChar = undefined;
23286 						} else {
23287 							// finished iterating, leave this.nextChar for the next next() call 
23288 							notdone = false;
23289 						}
23290 					}
23291 					prevCcc = nextCcc;
23292 				}
23293 			}
23294 			return ch;
23295 		};
23296 	};
23297 	return new _chiterator(this);
23298 };
23299 
23300 
23301 /*< CodePointSource.js */
23302 /*
23303  * CodePointSource.js - Source of code points from a string
23304  * 
23305  * Copyright © 2013-2015, JEDLSoft
23306  *
23307  * Licensed under the Apache License, Version 2.0 (the "License");
23308  * you may not use this file except in compliance with the License.
23309  * You may obtain a copy of the License at
23310  *
23311  *     http://www.apache.org/licenses/LICENSE-2.0
23312  *
23313  * Unless required by applicable law or agreed to in writing, software
23314  * distributed under the License is distributed on an "AS IS" BASIS,
23315  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23316  *
23317  * See the License for the specific language governing permissions and
23318  * limitations under the License.
23319  */
23320 
23321 // !depends isPunct.js NormString.js
23322 
23323 
23324 /**
23325  * @class
23326  * Represents a buffered source of code points. The input string is first
23327  * normalized so that combining characters come out in a standardized order.
23328  * If the "ignorePunctuation" flag is turned on, then punctuation 
23329  * characters are skipped.
23330  * 
23331  * @constructor
23332  * @private
23333  * @param {NormString|string} str a string to get code points from
23334  * @param {boolean} ignorePunctuation whether or not to ignore punctuation
23335  * characters
23336  */
23337 var CodePointSource = function(str, ignorePunctuation) {
23338 	this.chars = [];
23339 	// first convert the string to a normalized sequence of characters
23340 	var s = (typeof(str) === "string") ? new NormString(str) : str;
23341 	this.it = s.charIterator();
23342 	this.ignorePunctuation = typeof(ignorePunctuation) === "boolean" && ignorePunctuation;
23343 };
23344 
23345 /**
23346  * Return the first num code points in the source without advancing the
23347  * source pointer. If there are not enough code points left in the
23348  * string to satisfy the request, this method will return undefined. 
23349  * 
23350  * @param {number} num the number of characters to peek ahead
23351  * @return {string|undefined} a string formed out of up to num code points from
23352  * the start of the string, or undefined if there are not enough character left
23353  * in the source to complete the request
23354  */
23355 CodePointSource.prototype.peek = function(num) {
23356 	if (num < 1) {
23357 		return undefined;
23358 	}
23359 	if (this.chars.length < num && this.it.hasNext()) {
23360 		for (var i = 0; this.chars.length < 4 && this.it.hasNext(); i++) {
23361 			var c = this.it.next();
23362 			if (c && !this.ignorePunctuation || !isPunct(c)) {
23363 				this.chars.push(c);
23364 			}
23365 		}
23366 	}
23367 	if (this.chars.length < num) {
23368 		return undefined;
23369 	}
23370 	return this.chars.slice(0, num).join("");
23371 };
23372 /**
23373  * Advance the source pointer by the given number of code points.
23374  * @param {number} num number of code points to advance
23375  */
23376 CodePointSource.prototype.consume = function(num) {
23377 	if (num > 0) {
23378 		this.peek(num); // for the iterator to go forward if needed
23379 		if (num < this.chars.length) {
23380 			this.chars = this.chars.slice(num);
23381 		} else {
23382 			this.chars = [];
23383 		}
23384 	}
23385 };
23386 
23387 
23388 
23389 /*< ElementIterator.js */
23390 /*
23391  * ElementIterator.js - Iterate through a list of collation elements
23392  * 
23393  * Copyright © 2013-2015, JEDLSoft
23394  *
23395  * Licensed under the Apache License, Version 2.0 (the "License");
23396  * you may not use this file except in compliance with the License.
23397  * You may obtain a copy of the License at
23398  *
23399  *     http://www.apache.org/licenses/LICENSE-2.0
23400  *
23401  * Unless required by applicable law or agreed to in writing, software
23402  * distributed under the License is distributed on an "AS IS" BASIS,
23403  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23404  *
23405  * See the License for the specific language governing permissions and
23406  * limitations under the License.
23407  */
23408 
23409 /**
23410  * @class
23411  * An iterator through a sequence of collation elements. This
23412  * iterator takes a source of code points, converts them into
23413  * collation elements, and allows the caller to get single
23414  * elements at a time.
23415  * 
23416  * @constructor
23417  * @private
23418  * @param {CodePointSource} source source of code points to 
23419  * convert to collation elements
23420  * @param {Object} map mapping from sequences of code points to
23421  * collation elements
23422  * @param {number} keysize size in bits of the collation elements
23423  */
23424 var ElementIterator = function (source, map, keysize) {
23425 	this.elements = [];
23426 	this.source = source;
23427 	this.map = map;
23428 	this.keysize = keysize;
23429 };
23430 
23431 /**
23432  * @private
23433  */
23434 ElementIterator.prototype._fillBuffer = function () {
23435 	var str = undefined;
23436 	
23437 	// peek ahead by up to 4 characters, which may combine
23438 	// into 1 or more collation elements
23439 	for (var i = 4; i > 0; i--) {
23440 		str = this.source.peek(i);
23441 		if (str && this.map[str]) {
23442 			this.elements = this.elements.concat(this.map[str]);
23443 			this.source.consume(i);
23444 			return;
23445 		}
23446 	}
23447 	
23448 	if (str) {
23449 		// no mappings for the first code point, so just use its
23450 		// Unicode code point as a proxy for its sort order. Shift
23451 		// it by the key size so that everything unknown sorts
23452 		// after things that have mappings
23453 		this.elements.push(str.charCodeAt(0) << this.keysize);
23454 		this.source.consume(1);
23455 	} else {
23456 		// end of the string
23457 		return undefined;
23458 	}
23459 };
23460 
23461 /**
23462  * Return true if there are more collation elements left to
23463  * iterate through.
23464  * @returns {boolean} true if there are more elements left to
23465  * iterate through, and false otherwise
23466  */
23467 ElementIterator.prototype.hasNext = function () {
23468 	if (this.elements.length < 1) {
23469 		this._fillBuffer();
23470 	}
23471 	return !!this.elements.length;
23472 };
23473 
23474 /**
23475  * Return the next collation element. If more than one collation 
23476  * element is generated from a sequence of code points 
23477  * (ie. an "expansion"), then this class will buffer the
23478  * other elements and return them on subsequent calls to 
23479  * this method.
23480  * 
23481  * @returns {number|undefined} the next collation element or
23482  * undefined for no more collation elements
23483  */
23484 ElementIterator.prototype.next = function () {
23485 	if (this.elements.length < 1) {
23486 		this._fillBuffer();
23487 	}
23488 	var ret = this.elements[0];
23489 	this.elements = this.elements.slice(1);
23490 	return ret;
23491 };
23492 
23493 
23494 
23495 /*< Collator.js */
23496 /*
23497  * Collator.js - Collation routines
23498  * 
23499  * Copyright © 2013-2015, JEDLSoft
23500  *
23501  * Licensed under the Apache License, Version 2.0 (the "License");
23502  * you may not use this file except in compliance with the License.
23503  * You may obtain a copy of the License at
23504  *
23505  *     http://www.apache.org/licenses/LICENSE-2.0
23506  *
23507  * Unless required by applicable law or agreed to in writing, software
23508  * distributed under the License is distributed on an "AS IS" BASIS,
23509  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23510  *
23511  * See the License for the specific language governing permissions and
23512  * limitations under the License.
23513  */
23514 
23515 /* !depends 
23516 Locale.js 
23517 ilib.js 
23518 INumber.js 
23519 isPunct.js 
23520 NormString.js 
23521 MathUtils.js 
23522 Utils.js
23523 JSUtils.js
23524 LocaleInfo.js 
23525 CodePointSource.js
23526 ElementIterator.js
23527 */
23528 
23529 // !data collation
23530 
23531 
23532 /**
23533  * @class
23534  * A class that implements a locale-sensitive comparator function 
23535  * for use with sorting function. The comparator function
23536  * assumes that the strings it is comparing contain Unicode characters
23537  * encoded in UTF-16.<p>
23538  * 
23539  * Collations usually depend only on the language, because most collation orders 
23540  * are shared between locales that speak the same language. There are, however, a
23541  * number of instances where a locale collates differently than other locales
23542  * that share the same language. There are also a number of instances where a
23543  * locale collates differently based on the script used. This object can handle
23544  * these cases automatically if a full locale is specified in the options rather
23545  * than just a language code.<p>
23546  * 
23547  * <h2>Options</h2>
23548  * 
23549  * The options parameter can contain any of the following properties:
23550  * 
23551  * <ul>
23552  * <li><i>locale</i> - String|Locale. The locale which the comparator function 
23553  * will collate with. Default: the current iLib locale.
23554  * 
23555  * <li><i>sensitivity</i> - String. Sensitivity or strength of collator. This is one of 
23556  * "primary", "base", "secondary", "accent", "tertiary", "case", "quaternary", or 
23557  * "variant". Default: "primary"
23558  *   <ol>
23559  *   <li>base or primary - Only the primary distinctions between characters are significant.
23560  *   Another way of saying that is that the collator will be case-, accent-, and 
23561  *   variation-insensitive, and only distinguish between the base characters
23562  *   <li>case or secondary - Both the primary and secondary distinctions between characters
23563  *   are significant. That is, the collator will be accent- and variation-insensitive
23564  *   and will distinguish between base characters and character case.
23565  *   <li>accent or tertiary - The primary, secondary, and tertiary distinctions between
23566  *   characters are all significant. That is, the collator will be 
23567  *   variation-insensitive, but accent-, case-, and base-character-sensitive. 
23568  *   <li>variant or quaternary - All distinctions between characters are significant. That is,
23569  *   the algorithm is base character-, case-, accent-, and variation-sensitive.
23570  *   </ol>
23571  *   
23572  * <li><i>upperFirst</i> - boolean. When collating case-sensitively in a script that
23573  * has the concept of case, put upper-case
23574  * characters first, otherwise lower-case will come first. Warning: some browsers do
23575  * not implement this feature or at least do not implement it properly, so if you are 
23576  * using the native collator with this option, you may get different results in different
23577  * browsers. To guarantee the same results, set useNative to false to use the ilib 
23578  * collator implementation. This of course will be somewhat slower, but more 
23579  * predictable. Default: true
23580  * 
23581  * <li><i>reverse</i> - boolean. Return the list sorted in reverse order. When the
23582  * upperFirst option is also set to true, upper-case characters would then come at 
23583  * the end of the list. Default: false.
23584  * 
23585  * <li><i>scriptOrder</i> - string. When collating strings in multiple scripts,
23586  * this property specifies what order those scripts should be sorted. The default
23587  * Unicode Collation Algorithm (UCA) already has a default order for scripts, but
23588  * this can be tailored via this property. The value of this option is a 
23589  * space-separated list of ISO 15924 scripts codes. If a code is specified in this
23590  * property, its default data must be included using the JS assembly tool. If the
23591  * data is not included, the ordering for the script will be ignored. Default:
23592  * the default order defined by the UCA. 
23593  * 
23594  * <li><i>style</i> - The value of the style parameter is dependent on the locale.
23595  * For some locales, there are different styles of collating strings depending
23596  * on what kind of strings are being collated or what the preference of the user 
23597  * is. For example, in German, there is a phonebook order and a dictionary ordering
23598  * that sort the same array of strings slightly differently.
23599  * The static method {@link Collator#getAvailableStyles} will return a list of styles that ilib
23600  * currently knows about for any given locale. If the value of the style option is 
23601  * not recognized for a locale, it will be ignored. Default style is "standard".<p>
23602  * 
23603  * <li><i>usage</i> - Whether this collator will be used for searching or sorting.
23604  * Valid values are simply the strings "sort" or "search". When used for sorting,
23605  * it is good idea if a collator produces a stable sort. That is, the order of the 
23606  * sorted array of strings should not depend on the order of the strings in the
23607  * input array. As such, when a collator is supposed to act case insensitively, 
23608  * it nonetheless still distinguishes between case after all other criteria
23609  * are satisfied so that strings that are distinguished only by case do not sort
23610  * randomly. For searching, we would like to match two strings that different only 
23611  * by case, so the collator must return equals in that situation instead of 
23612  * further distinguishing by case. Default is "sort".
23613  * 
23614  * <li><i>numeric</i> - Treat the left and right strings as if they started with
23615  * numbers and sort them numerically rather than lexically.
23616  * 
23617  * <li><i>ignorePunctuation</i> - Skip punctuation characters when comparing the
23618  * strings.
23619  *  
23620  * <li>onLoad - a callback function to call when the collator object is fully 
23621  * loaded. When the onLoad option is given, the collator object will attempt to
23622  * load any missing locale data using the ilib loader callback.
23623  * When the constructor is done (even if the data is already preassembled), the 
23624  * onLoad function is called with the current instance as a parameter, so this
23625  * callback can be used with preassembled or dynamic loading or a mix of the two.
23626  * 
23627  * <li>sync - tell whether to load any missing locale data synchronously or 
23628  * asynchronously. If this option is given as "false", then the "onLoad"
23629  * callback must be given, as the instance returned from this constructor will
23630  * not be usable for a while. 
23631  *
23632  * <li><i>loadParams</i> - an object containing parameters to pass to the 
23633  * loader callback function when locale data is missing. The parameters are not
23634  * interpretted or modified in any way. They are simply passed along. The object 
23635  * may contain any property/value pairs as long as the calling code is in
23636  * agreement with the loader callback function as to what those parameters mean.
23637  * 
23638  * <li><i>useNative</i> - when this option is true, use the native Intl object
23639  * provided by the Javascript engine, if it exists, to implement this class. If
23640  * it doesn't exist, or if this parameter is false, then this class uses a pure 
23641  * Javascript implementation, which is slower and uses a lot more memory, but 
23642  * works everywhere that ilib works. Default is "true".
23643  * </ul>
23644  * 
23645  * <h2>Operation</h2>
23646  * 
23647  * The Collator constructor returns a collator object tailored with the above 
23648  * options. The object contains an internal compare() method which compares two 
23649  * strings according to those options. This can be used directly to compare
23650  * two strings, but is not useful for passing to the javascript sort function
23651  * because then it will not have its collation data available. Instead, use the 
23652  * getComparator() method to retrieve a function that is bound to the collator
23653  * object. (You could also bind it yourself using ilib.bind()). The bound function 
23654  * can be used with the standard Javascript array sorting algorithm, or as a 
23655  * comparator with your own sorting algorithm.<p>
23656  * 
23657  * Example using the standard Javascript array sorting call with the bound
23658  * function:<p>
23659  * 
23660  * <code>
23661  * <pre>
23662  * var arr = ["ö", "oe", "ü", "o", "a", "ae", "u", "ß", "ä"];
23663  * var collator = new Collator({locale: 'de-DE', style: "dictionary"});
23664  * arr.sort(collator.getComparator());
23665  * console.log(JSON.stringify(arr));
23666  * </pre>
23667  * </code>
23668  * <p>
23669  * 
23670  * Would give the output:<p>
23671  * 
23672  * <code>
23673  * <pre>
23674  * ["a", "ae", "ä", "o", "oe", "ö", "ß", "u", "ü"]
23675  * </pre>
23676  * </code>
23677  * 
23678  * When sorting an array of Javascript objects according to one of the 
23679  * string properties of the objects, wrap the collator's compare function 
23680  * in your own comparator function that knows the structure of the objects
23681  * being sorted:<p>
23682  * 
23683  * <code>
23684  * <pre>
23685  * var collator = new Collator({locale: 'de-DE'});
23686  * var myComparator = function (collator) {
23687  *   var comparator = collator.getComparator();
23688  *   // left and right are your own objects
23689  *   return function (left, right) {
23690  *   	return comparator(left.x.y.textProperty, right.x.y.textProperty);
23691  *   };
23692  * };
23693  * arr.sort(myComparator(collator));
23694  * </pre>
23695  * </code>
23696  * <p>
23697  * 
23698  * <h2>Sort Keys</h2>
23699  * 
23700  * The collator class also has a method to retrieve the sort key for a
23701  * string. The sort key is an array of values that represent how each  
23702  * character in the string should be collated according to the characteristics
23703  * of the collation algorithm and the given options. Thus, sort keys can be 
23704  * compared directly value-for-value with other sort keys that were generated 
23705  * by the same collator, and the resulting ordering is guaranteed to be the 
23706  * same as if the original strings were compared by the collator.
23707  * Sort keys generated by different collators are not guaranteed to give
23708  * any reasonable results when compared together unless the two collators 
23709  * were constructed with 
23710  * exactly the same options and therefore end up representing the exact same 
23711  * collation sequence.<p>
23712  * 
23713  * A good rule of thumb is that you would use a sort key if you had 10 or more
23714  * items to sort or if your array might be resorted arbitrarily. For example, if your 
23715  * user interface was displaying a table with 100 rows in it, and each row had
23716  * 4 sortable text columns which could be sorted in acending or descending order,
23717  * the recommended practice would be to generate a sort key for each of the 4
23718  * sortable fields in each row and store that in the Javascript representation of the
23719  * table data. Then, when the user clicks on a column header to resort the
23720  * table according to that column, the resorting would be relatively quick 
23721  * because it would only be comparing arrays of values, and not recalculating 
23722  * the collation values for each character in each string for every comparison.<p>
23723  * 
23724  * For tables that are large, it is usually a better idea to do the sorting
23725  * on the server side, especially if the table is the result of a database
23726  * query. In this case, the table is usually a view of the cursor of a large
23727  * results set, and only a few entries are sent to the front end at a time.
23728  * In order to sort the set efficiently, it should be done on the database
23729  * level instead.
23730  * 
23731  * <h2>Data</h2>
23732  * 
23733  * Doing correct collation entails a huge amount of mapping data, much of which is
23734  * not necessary when collating in one language with one script, which is the most
23735  * common case. Thus, ilib implements a number of ways to include the data you
23736  * need or leave out the data you don't need using the JS assembly tool:
23737  * 
23738  * <ol>
23739  * <li>Full multilingual data - if you are sorting multilingual data and need to collate 
23740  * text written in multiple scripts, you can use the directive "!data collation/ducet" to 
23741  * load in the full collation data.  This allows the collator to perform the entire 
23742  * Unicode Collation Algorithm (UCA) based on the Default Unicode Collation Element 
23743  * Table (DUCET). The data is very large, on the order of multiple megabytes, but 
23744  * sometimes it is necessary.
23745  * <li>A few scripts - if you are sorting text written in only a few scripts, you may 
23746  * want to include only the data for those scripts. Each ISO 15924 script code has its
23747  * own data available in a separate file, so you can use the data directive to include
23748  * only the data for the scripts you need. For example, use  
23749  * "!data collation/Latn" to retrieve the collation information for the Latin script.
23750  * Because the "ducet" table mentioned in the previous point is a superset of the 
23751  * tables for all other scripts, you do not need to include explicitly the data for 
23752  * any particular script when using "ducet". That is, you either include "ducet" or 
23753  * you include a specific list of scripts.
23754  * <li>Only one script - if you are sorting text written only in one script, you can
23755  * either include the data directly as in the previous point, or you can rely on the 
23756  * locale to include the correct data for you. In this case, you can use the directive
23757  * "!data collate" to load in the locale's collation data for its most common script.
23758  * </ol>
23759  *   
23760  * With any of the above ways of including the data, the collator will only perform the
23761  * correct language-sensitive sorting for the given locale. All other scripts will be
23762  * sorted in the default manner according to the UCA. For example, if you include the
23763  * "ducet" data and pass in "de-DE" (German for Germany) as the locale spec, then
23764  * only the Latin script (the default script for German) will be sorted according to
23765  * German rules. All other scripts in the DUCET, such as Japanese or Arabic, will use 
23766  * the default UCA collation rules.<p>
23767  * 
23768  * If this collator encounters a character for which it has no collation data, it will
23769  * sort those characters by pure Unicode value after all characters for which it does have
23770  * collation data. For example, if you only loaded in the German collation data (ie. the
23771  * data for the Latin script tailored to German) to sort a list of person names, but that
23772  * list happens to include the names of a few Japanese people written in Japanese 
23773  * characters, the Japanese names will sort at the end of the list after all German names,
23774  * and will sort according to the Unicode values of the characters.
23775  * 
23776  * @constructor
23777  * @param {Object} options options governing how the resulting comparator 
23778  * function will operate
23779  */
23780 var Collator = function(options) {
23781 	var sync = true,
23782 		loadParams = undefined,
23783 		useNative = true;
23784 
23785 	// defaults
23786 	/** 
23787 	 * @private
23788 	 * @type {Locale} 
23789 	 */
23790 	this.locale = new Locale(ilib.getLocale());
23791 	
23792 	/** @private */
23793 	this.caseFirst = "upper";
23794 	/** @private */
23795 	this.sensitivity = "variant";
23796 	/** @private */
23797 	this.level = 4;
23798 	/** @private */
23799 	this.usage = "sort";
23800 	/** @private */
23801 	this.reverse = false;
23802 	/** @private */
23803 	this.numeric = false;
23804 	/** @private */
23805 	this.style = "default";
23806 	/** @private */
23807 	this.ignorePunctuation = false;
23808 	
23809 	if (options) {
23810 		if (options.locale) {
23811 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
23812 		}
23813 		if (options.sensitivity) {
23814 			switch (options.sensitivity) {
23815 				case 'primary':
23816 				case 'base':
23817 					this.sensitivity = "base";
23818 					this.level = 1;
23819 					break;
23820 				case 'secondary':
23821 				case 'accent':
23822 					this.sensitivity = "accent";
23823 					this.level = 2;
23824 					break;
23825 				case 'tertiary':
23826 				case 'case':
23827 					this.sensitivity = "case";
23828 					this.level = 3;
23829 					break;
23830 				case 'quaternary':
23831 				case 'variant':
23832 					this.sensitivity = "variant";
23833 					this.level = 4;
23834 					break;
23835 			}
23836 		}
23837 		if (typeof(options.upperFirst) !== 'undefined') {
23838 			this.caseFirst = options.upperFirst ? "upper" : "lower"; 
23839 		}
23840 		
23841 		if (typeof(options.ignorePunctuation) !== 'undefined') {
23842 			this.ignorePunctuation = options.ignorePunctuation;
23843 		}
23844 		if (typeof(options.sync) !== 'undefined') {
23845 			sync = (options.sync == true);
23846 		}
23847 		
23848 		loadParams = options.loadParams;
23849 		if (typeof(options.useNative) !== 'undefined') {
23850 			useNative = options.useNative;
23851 		}
23852 		
23853 		if (options.usage === "sort" || options.usage === "search") {
23854 			this.usage = options.usage;
23855 		}
23856 		
23857 		if (typeof(options.reverse) === 'boolean') {
23858 			this.reverse = options.reverse;
23859 		}
23860 
23861 		if (typeof(options.numeric) === 'boolean') {
23862 			this.numeric = options.numeric;
23863 		}
23864 		
23865 		if (typeof(options.style) === 'string') {
23866 			this.style = options.style;
23867 		}
23868 	}
23869 
23870 	if (this.usage === "sort") {
23871 		// produces a stable sort
23872 		this.level = 4;
23873 	}
23874 
23875 	if (useNative && typeof(Intl) !== 'undefined' && Intl) {
23876 		// this engine is modern and supports the new Intl object!
23877 		//console.log("implemented natively");
23878 		/** 
23879 		 * @private
23880 		 * @type {{compare:function(string,string)}} 
23881 		 */
23882 		this.collator = new Intl.Collator(this.locale.getSpec(), {
23883 			sensitivity: this.sensitivity,
23884 			caseFirst: this.caseFirst,
23885 			ignorePunctuation: this.ignorePunctuation,
23886 			numeric: this.numeric,
23887 			usage: this.usage
23888 		});
23889 		
23890 		if (options && typeof(options.onLoad) === 'function') {
23891 			options.onLoad(this);
23892 		}
23893 	} else {
23894 		//console.log("implemented in pure JS");
23895 		if (!Collator.cache) {
23896 			Collator.cache = {};
23897 		}
23898 
23899 		// else implement in pure Javascript
23900 		Utils.loadData({
23901 			object: Collator, 
23902 			locale: this.locale, 
23903 			name: "collation.json",
23904 			sync: sync,
23905 			loadParams: loadParams, 
23906 			callback: ilib.bind(this, function (collation) {
23907 				if (!collation) {
23908 					collation = ilib.data.collation;
23909 					var spec = this.locale.getSpec().replace(/-/g, '_');
23910 					Collator.cache[spec] = collation;
23911 				}
23912 				this._init(collation);
23913 				new LocaleInfo(this.locale, {
23914 					sync: sync,
23915 					loadParams: loadParams,
23916 					onLoad: ilib.bind(this, function(li) {
23917 						this.li = li;
23918 						if (this.ignorePunctuation) {
23919 			    			isPunct._init(sync, loadParams, ilib.bind(this, function() {
23920 								if (options && typeof(options.onLoad) === 'function') {
23921 									options.onLoad(this);
23922 								}
23923 			    			}));
23924 		    			} else {
23925 							if (options && typeof(options.onLoad) === 'function') {
23926 								options.onLoad(this);
23927 							}
23928 		    			}
23929 		    		})
23930 				});
23931 			})
23932 		});
23933 	}
23934 };
23935 
23936 Collator.prototype = {
23937 	/**
23938 	 * @private
23939 	 * Bit pack an array of values into a single number
23940 	 * @param {number|null|Array.<number>} arr array of values to bit pack
23941 	 * @param {number} offset offset for the start of this map
23942 	 */
23943 	_pack: function (arr, offset) {
23944 		var value = 0;
23945 		if (arr) {
23946 			if (typeof(arr) === 'number') {
23947 				arr = [ arr ];
23948 			}
23949 			for (var i = 0; i < this.level; i++) {
23950 				var thisLevel = (typeof(arr[i]) !== "undefined" ? arr[i] : 0);
23951 				if (i === 0) {
23952 					thisLevel += offset;
23953 				}
23954 				if (i > 0) {
23955 					value <<= this.collation.bits[i];	
23956 				}
23957 				if (i === 2 && this.caseFirst === "lower") {
23958 					// sort the lower case first instead of upper
23959 					value = value | (1 - thisLevel);
23960 				} else {
23961 					value = value | thisLevel;
23962 				}
23963 			}
23964 		}
23965 		return value;
23966 	},
23967 	
23968 	/**
23969 	 * @private
23970 	 * Return the rule packed into an array of collation elements.
23971 	 * @param {Array.<number|null|Array.<number>>} rule
23972 	 * @param {number} offset
23973 	 * @return {Array.<number>} a bit-packed array of numbers
23974 	 */
23975 	_packRule: function(rule, offset) {
23976 		if (ilib.isArray(rule[0])) {
23977 			var ret = [];
23978 			for (var i = 0; i < rule.length; i++) {
23979 				ret.push(this._pack(rule[i], offset));
23980 			}
23981 			return ret;
23982 		} else {
23983 			return [ this._pack(rule, offset) ];
23984 		}
23985 	},
23986     
23987 	/**
23988 	 * @private
23989 	 */
23990 	_addChars: function (str, offset) {
23991 		var gs = new GlyphString(str);
23992 		var it = gs.charIterator();
23993 		var c;
23994 		
23995 		while (it.hasNext()) {
23996 			c = it.next();
23997 			if (c === "'") {
23998 				// escape a sequence of chars as one collation element
23999 				c = "";
24000 				var x = "";
24001 				while (it.hasNext() && x !== "'") {
24002 					c += x;
24003 					x = it.next();
24004 				}
24005 			}
24006 			this.lastMap++;
24007 			this.map[c] = this._packRule([this.lastMap], offset);
24008 		}
24009 	},
24010 	
24011 	/**
24012 	 * @private
24013 	 */
24014 	_addRules: function(rules, start) {
24015 		var p;
24016     	for (var r in rules.map) {
24017     		if (r) {
24018     			this.map[r] = this._packRule(rules.map[r], start);
24019     			p = typeof(rules.map[r][0]) === 'number' ? rules.map[r][0] : rules.map[r][0][0]; 
24020     			this.lastMap = Math.max(p + start, this.lastMap);
24021     		}
24022     	}
24023     	
24024     	if (typeof(rules.ranges) !== 'undefined') {
24025     		// for each range, everything in the range goes in primary sequence from the start
24026     		for (var i = 0; i < rules.ranges.length; i++) {
24027     			var range = rules.ranges[i];
24028     			
24029     			this.lastMap = range.start;
24030     			if (typeof(range.chars) === "string") {
24031     				this._addChars(range.chars, start);
24032     			} else {
24033     				for (var k = 0; k < range.chars.length; k++) {
24034     					this._addChars(range.chars[k], start);
24035     				}
24036     			}
24037     		}
24038     	}
24039 	},
24040 	
24041 	/**
24042      * @private
24043      */
24044     _init: function(rules) {
24045     	var rule = this.style;
24046     	while (typeof(rule) === 'string') {
24047     		rule = rules[rule];
24048     	}
24049     	if (!rule) {
24050     		rule = "default";
24051         	while (typeof(rule) === 'string') {
24052         		rule = rules[rule];
24053         	}
24054     	}
24055     	if (!rule) {
24056     		this.map = {};
24057     		return;
24058     	}
24059     	
24060     	/** 
24061     	 * @private
24062     	 * @type {{scripts:Array.<string>,bits:Array.<number>,maxes:Array.<number>,bases:Array.<number>,map:Object.<string,Array.<number|null|Array.<number>>>}}
24063     	 */
24064     	this.collation = rule;
24065     	this.map = {};
24066     	this.lastMap = -1;
24067     	this.keysize = this.collation.keysize[this.level-1];
24068     	
24069     	if (typeof(this.collation.inherit) !== 'undefined') {
24070     		for (var i = 0; i < this.collation.inherit.length; i++) {
24071     			var col = this.collation.inherit[i];
24072     			rule = typeof(col) === 'object' ? col.name : col;
24073     			if (rules[rule]) {
24074     				this._addRules(rules[rule], col.start || this.lastMap+1);
24075     			}
24076     		}
24077     	}
24078     	this._addRules(this.collation, this.lastMap+1);
24079     },
24080     
24081     /**
24082      * @private
24083      */
24084     _basicCompare: function(left, right) {
24085 		var l = (left instanceof NormString) ? left : new NormString(left),
24086 			r = (right instanceof NormString) ? right : new NormString(right),
24087 			lchar, 
24088 			rchar,
24089 			lelements,
24090 			relements;
24091 		
24092 		if (this.numeric) {
24093 			var lvalue = new INumber(left, {locale: this.locale});
24094 			var rvalue = new INumber(right, {locale: this.locale});
24095 			if (!isNaN(lvalue.valueOf()) && !isNaN(rvalue.valueOf())) {
24096 				var diff = lvalue.valueOf() - rvalue.valueOf();
24097 				if (diff) {
24098 					return diff;
24099 				} else {
24100 					// skip the numeric part and compare the rest lexically
24101 					l = new NormString(left.substring(lvalue.parsed.length));
24102 					r = new NormString(right.substring(rvalue.parsed.length));
24103 				}
24104 			}
24105 			// else if they aren't both numbers, then let the code below take care of the lexical comparison instead
24106 		}
24107 			
24108 		lelements = new ElementIterator(new CodePointSource(l, this.ignorePunctuation), this.map, this.keysize);
24109 		relements = new ElementIterator(new CodePointSource(r, this.ignorePunctuation), this.map, this.keysize);
24110 		
24111 		while (lelements.hasNext() && relements.hasNext()) {
24112 			var diff = lelements.next() - relements.next();
24113 			if (diff) {
24114 				return diff;
24115 			}
24116 		}
24117 		if (!lelements.hasNext() && !relements.hasNext()) {
24118 			return 0;
24119 		} else if (lelements.hasNext()) {
24120 			return 1;
24121 		} else {
24122 			return -1;
24123 		}
24124     },
24125     
24126 	/**
24127 	 * Compare two strings together according to the rules of this 
24128 	 * collator instance. Do not use this function directly with 
24129 	 * Array.sort, as it will not have its collation data available
24130 	 * and therefore will not function properly. Use the function
24131 	 * returned by getComparator() instead.
24132 	 * 
24133 	 * @param {string} left the left string to compare
24134 	 * @param {string} right the right string to compare
24135 	 * @return {number} a negative number if left comes before right, a
24136 	 * positive number if right comes before left, and zero if left and 
24137 	 * right are equivalent according to this collator
24138 	 */
24139 	compare: function (left, right) {
24140 		// last resort: use the "C" locale
24141 		if (this.collator) {
24142 			// implemented by the core engine
24143 			return this.collator.compare(left, right);
24144 		}
24145 
24146 		var ret = this._basicCompare(left, right);
24147 		return this.reverse ? -ret : ret;
24148 	},
24149 	
24150 	/**
24151 	 * Return a comparator function that can compare two strings together
24152 	 * according to the rules of this collator instance. The function 
24153 	 * returns a negative number if the left 
24154 	 * string comes before right, a positive number if the right string comes 
24155 	 * before the left, and zero if left and right are equivalent. If the
24156 	 * reverse property was given as true to the collator constructor, this 
24157 	 * function will
24158 	 * switch the sign of those values to cause sorting to happen in the
24159 	 * reverse order.
24160 	 * 
24161 	 * @return {function(...)|undefined} a comparator function that 
24162 	 * can compare two strings together according to the rules of this 
24163 	 * collator instance
24164 	 */
24165 	getComparator: function() {
24166 		// bind the function to this instance so that we have the collation
24167 		// rules available to do the work
24168 		if (this.collator) {
24169 			// implemented by the core engine
24170 			return this.collator.compare;
24171 		}
24172 		
24173 		return ilib.bind(this, this.compare);
24174 	},
24175 	
24176 	/**
24177 	 * Return a sort key string for the given string. The sort key
24178 	 * string is a list of values that represent each character 
24179 	 * in the original string. The sort key
24180 	 * values for any particular character consists of 3 numbers that
24181 	 * encode the primary, secondary, and tertiary characteristics
24182 	 * of that character. The values of each characteristic are 
24183 	 * modified according to the strength of this collator instance 
24184 	 * to give the correct collation order. The idea is that this
24185 	 * sort key string is directly comparable byte-for-byte to 
24186 	 * other sort key strings generated by this collator without
24187 	 * any further knowledge of the collation rules for the locale.
24188 	 * More formally, if a < b according to the rules of this collation, 
24189 	 * then it is guaranteed that sortkey(a) < sortkey(b) when compared
24190 	 * byte-for-byte. The sort key string can therefore be used
24191 	 * without the collator to sort an array of strings efficiently
24192 	 * because the work of determining the applicability of various
24193 	 * collation rules is done once up-front when generating 
24194 	 * the sort key.<p>
24195 	 * 
24196 	 * The sort key string can be treated as a regular, albeit somewhat
24197 	 * odd-looking, string. That is, it can be pass to regular 
24198 	 * Javascript functions without problems.  
24199 	 * 
24200 	 * @param {string} str the original string to generate the sort key for
24201 	 * @return {string} a sort key string for the given string
24202 	 */
24203 	sortKey: function (str) {
24204 		if (!str) {
24205 			return "";
24206 		}
24207 		
24208 		if (this.collator) {
24209 			// native, no sort keys available
24210 			return str;
24211 		}
24212 		
24213 		if (this.numeric) {
24214 			var v = new INumber(str, {locale: this.locale});
24215 			var s = isNaN(v.valueOf()) ? "" : v.valueOf().toString(16);
24216 			return JSUtils.pad(s, 16);	
24217 		} else {
24218 			var n = (typeof(str) === "string") ? new NormString(str) : str,
24219 				ret = "",
24220 				lelements = new ElementIterator(new CodePointSource(n, this.ignorePunctuation), this.map, this.keysize),
24221 				element;
24222 			
24223 			while (lelements.hasNext()) {
24224 				element = lelements.next();
24225 				if (this.reverse) {
24226 					// for reverse, take the bitwise inverse
24227 					element = (1 << this.keysize) - element;
24228 				}
24229 				ret += JSUtils.pad(element.toString(16), this.keysize/4);	
24230 			}
24231 		}
24232 		return ret;
24233 	}
24234 };
24235 
24236 /**
24237  * Retrieve the list of collation style names that are available for the 
24238  * given locale. This list varies depending on the locale, and depending
24239  * on whether or not the data for that locale was assembled into this copy
24240  * of ilib.
24241  * 
24242  * @param {Locale|string=} locale The locale for which the available
24243  * styles are being sought
24244  * @return Array.<string> an array of style names that are available for
24245  * the given locale
24246  */
24247 Collator.getAvailableStyles = function (locale) {
24248 	return [ "standard" ];
24249 };
24250 
24251 /**
24252  * Retrieve the list of ISO 15924 script codes that are available in this
24253  * copy of ilib. This list varies depending on whether or not the data for 
24254  * various scripts was assembled into this copy of ilib. If the "ducet"
24255  * data is assembled into this copy of ilib, this method will report the
24256  * entire list of scripts as being available. If a collator instance is
24257  * instantiated with a script code that is not on the list returned by this
24258  * function, it will be ignored and text in that script will be sorted by
24259  * numeric Unicode values of the characters.
24260  * 
24261  * @return Array.<string> an array of ISO 15924 script codes that are 
24262  * available
24263  */
24264 Collator.getAvailableScripts = function () {
24265 	return [ "Latn" ];
24266 };
24267 
24268 
24269 
24270 /*< nfd/all.js */
24271 /*
24272  * all.js - include file for normalization data for a particular script
24273  * 
24274  * Copyright © 2013-2015, JEDLSoft
24275  *
24276  * Licensed under the Apache License, Version 2.0 (the "License");
24277  * you may not use this file except in compliance with the License.
24278  * You may obtain a copy of the License at
24279  *
24280  *     http://www.apache.org/licenses/LICENSE-2.0
24281  *
24282  * Unless required by applicable law or agreed to in writing, software
24283  * distributed under the License is distributed on an "AS IS" BASIS,
24284  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24285  *
24286  * See the License for the specific language governing permissions and
24287  * limitations under the License.
24288  */
24289 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */
24290 // !data normdata nfd/all
24291 ilib.extend(ilib.data.norm, ilib.data.normdata);
24292 ilib.extend(ilib.data.norm.nfd, ilib.data.nfd_all);
24293 ilib.data.normdata = undefined;
24294 ilib.data.nfd_all = undefined;
24295 /*< nfkd/all.js */
24296 /*
24297  * all.js - include file for normalization data for a particular script
24298  * 
24299  * Copyright © 2013-2015, JEDLSoft
24300  *
24301  * Licensed under the Apache License, Version 2.0 (the "License");
24302  * you may not use this file except in compliance with the License.
24303  * You may obtain a copy of the License at
24304  *
24305  *     http://www.apache.org/licenses/LICENSE-2.0
24306  *
24307  * Unless required by applicable law or agreed to in writing, software
24308  * distributed under the License is distributed on an "AS IS" BASIS,
24309  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24310  *
24311  * See the License for the specific language governing permissions and
24312  * limitations under the License.
24313  */
24314 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */
24315 // !depends nfd/all.js
24316 // !data normdata nfkd/all
24317 ilib.extend(ilib.data.norm, ilib.data.normdata);
24318 ilib.extend(ilib.data.norm.nfkd, ilib.data.nfkd_all);
24319 ilib.data.normdata = undefined;
24320 ilib.data.nfkd_all = undefined;
24321 /*< nfkc/all.js */
24322 /*
24323  * all.js - include file for normalization data for a particular script
24324  * 
24325  * Copyright © 2013-2015, JEDLSoft
24326  *
24327  * Licensed under the Apache License, Version 2.0 (the "License");
24328  * you may not use this file except in compliance with the License.
24329  * You may obtain a copy of the License at
24330  *
24331  *     http://www.apache.org/licenses/LICENSE-2.0
24332  *
24333  * Unless required by applicable law or agreed to in writing, software
24334  * distributed under the License is distributed on an "AS IS" BASIS,
24335  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24336  *
24337  * See the License for the specific language governing permissions and
24338  * limitations under the License.
24339  */
24340 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */
24341 // !depends nfd/all.js nfkd/all.js
24342 // !data norm
24343 ilib.extend(ilib.data.norm, ilib.data.normdata);
24344 ilib.data.normdata = undefined;
24345 
24346 /*< LocaleMatcher.js */
24347 /*
24348  * LocaleMatcher.js - Locale matcher definition
24349  * 
24350  * Copyright © 2013-2015, JEDLSoft
24351  *
24352  * Licensed under the Apache License, Version 2.0 (the "License");
24353  * you may not use this file except in compliance with the License.
24354  * You may obtain a copy of the License at
24355  *
24356  *     http://www.apache.org/licenses/LICENSE-2.0
24357  *
24358  * Unless required by applicable law or agreed to in writing, software
24359  * distributed under the License is distributed on an "AS IS" BASIS,
24360  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24361  *
24362  * See the License for the specific language governing permissions and
24363  * limitations under the License.
24364  */
24365 
24366 // !depends ilib.js Locale.js Utils.js
24367 // !data likelylocales
24368 
24369 
24370 /**
24371  * @class
24372  * Create a new locale matcher instance. This is used
24373  * to see which locales can be matched with each other in
24374  * various ways.<p>
24375  * 
24376  * The options object may contain any of the following properties:
24377  * 
24378  * <ul>
24379  * <li><i>locale</i> - the locale to match
24380  * 
24381  * <li><i>onLoad</i> - a callback function to call when the locale matcher object is fully 
24382  * loaded. When the onLoad option is given, the locale matcher object will attempt to
24383  * load any missing locale data using the ilib loader callback.
24384  * When the constructor is done (even if the data is already preassembled), the 
24385  * onLoad function is called with the current instance as a parameter, so this
24386  * callback can be used with preassembled or dynamic loading or a mix of the two.
24387  * 
24388  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
24389  * asynchronously. If this option is given as "false", then the "onLoad"
24390  * callback must be given, as the instance returned from this constructor will
24391  * not be usable for a while. 
24392  *
24393  * <li><i>loadParams</i> - an object containing parameters to pass to the 
24394  * loader callback function when locale data is missing. The parameters are not
24395  * interpretted or modified in any way. They are simply passed along. The object 
24396  * may contain any property/value pairs as long as the calling code is in
24397  * agreement with the loader callback function as to what those parameters mean.
24398  * </ul>
24399  * 
24400  * 
24401  * @constructor
24402  * @param {Object} options parameters to initialize this matcher 
24403  */
24404 var LocaleMatcher = function(options) {
24405 	var sync = true,
24406 	    loadParams = undefined;
24407 	
24408 	this.locale = new Locale();
24409 	
24410 	if (options) {
24411 		if (typeof(options.locale) !== 'undefined') {
24412 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
24413 		}
24414 		
24415 		if (typeof(options.sync) !== 'undefined') {
24416 			sync = (options.sync == true);
24417 		}
24418 		
24419 		if (typeof(options.loadParams) !== 'undefined') {
24420 			loadParams = options.loadParams;
24421 		}
24422 	}
24423 
24424 	if (!LocaleMatcher.cache) {
24425 		LocaleMatcher.cache = {};
24426 	}
24427 
24428 	if (typeof(ilib.data.likelylocales) === 'undefined') {
24429 		Utils.loadData({
24430 			object: LocaleMatcher, 
24431 			locale: "-", 
24432 			name: "likelylocales.json", 
24433 			sync: sync, 
24434 			loadParams: loadParams, 
24435 			callback: ilib.bind(this, function (info) {
24436 				if (!info) {
24437 					info = {};
24438 					var spec = this.locale.getSpec().replace(/-/g, "_");
24439 					LocaleMatcher.cache[spec] = info;
24440 				}
24441 				/** @type {Object.<string,string>} */
24442 				this.info = info;
24443 				if (options && typeof(options.onLoad) === 'function') {
24444 					options.onLoad(this);
24445 				}
24446 			})
24447 		});
24448 	} else {
24449 		this.info = ilib.data.likelylocales;
24450 	}
24451 };
24452 
24453 
24454 LocaleMatcher.prototype = {
24455 	/**
24456 	 * Return the locale used to construct this instance. 
24457 	 * @return {Locale|undefined} the locale for this matcher
24458 	 */
24459 	getLocale: function() {
24460 		return this.locale;
24461 	},
24462 	
24463 	/**
24464 	 * Return an Locale instance that is fully specified based on partial information
24465 	 * given to the constructor of this locale matcher instance. For example, if the locale
24466 	 * spec given to this locale matcher instance is simply "ru" (for the Russian language), 
24467 	 * then it will fill in the missing region and script tags and return a locale with 
24468 	 * the specifier "ru-Cyrl-RU". (ie. Russian language, Cyrillic, Russian Federation).
24469 	 * Any one or two of the language, script, or region parts may be left unspecified,
24470 	 * and the other one or two parts will be filled in automatically. If this
24471 	 * class has no information about the given locale, then the locale of this
24472 	 * locale matcher instance is returned unchanged.
24473 	 * 
24474 	 * @returns {Locale} the most likely completion of the partial locale given
24475 	 * to the constructor of this locale matcher instance
24476 	 */
24477 	getLikelyLocale: function () {
24478 		if (typeof(this.info[this.locale.getSpec()]) === 'undefined') {
24479 			return this.locale;
24480 		}
24481 		
24482 		return new Locale(this.info[this.locale.getSpec()]);
24483 	}
24484 };
24485 
24486 
24487 /*< CaseMapper.js */
24488 /*
24489  * caseMapper.js - define upper- and lower-case mapper
24490  * 
24491  * Copyright © 2014-2015, JEDLSoft
24492  *
24493  * Licensed under the Apache License, Version 2.0 (the "License");
24494  * you may not use this file except in compliance with the License.
24495  * You may obtain a copy of the License at
24496  *
24497  *     http://www.apache.org/licenses/LICENSE-2.0
24498  *
24499  * Unless required by applicable law or agreed to in writing, software
24500  * distributed under the License is distributed on an "AS IS" BASIS,
24501  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24502  *
24503  * See the License for the specific language governing permissions and
24504  * limitations under the License.
24505  */
24506 
24507 // !depends Locale.js IString.js
24508 
24509 
24510 
24511 /**
24512  * @class
24513  * Create a new string mapper instance that maps strings to upper or
24514  * lower case. This mapping will work for any string as characters 
24515  * that have no case will be returned unchanged.<p>
24516  * 
24517  * The options may contain any of the following properties:
24518  * 
24519  * <ul>
24520  * <li><i>locale</i> - locale to use when loading the mapper. Some maps are 
24521  * locale-dependent, and this locale selects the right one. Default if this is
24522  * not specified is the current locale.
24523  * 
24524  * <li><i>direction</i> - "toupper" for upper-casing, or "tolower" for lower-casing.
24525  * Default if not specified is "toupper".
24526  * </ul>
24527  * 
24528  * 
24529  * @constructor
24530  * @param {Object=} options options to initialize this mapper 
24531  */
24532 var CaseMapper = function (options) {
24533 	this.up = true;
24534 	this.locale = new Locale();
24535 	
24536 	if (options) {
24537 		if (typeof(options.locale) !== 'undefined') {
24538 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
24539 		}
24540 		
24541 		this.up = (!options.direction || options.direction === "toupper");
24542 	}
24543 
24544 	this.mapData = this.up ? {
24545 		"ß": "SS",		// German
24546 		'ΐ': 'Ι',		// Greek
24547 		'ά': 'Α',
24548 		'έ': 'Ε',
24549 		'ή': 'Η',
24550 		'ί': 'Ι',
24551 		'ΰ': 'Υ',
24552 		'ϊ': 'Ι',
24553 		'ϋ': 'Υ',
24554 		'ό': 'Ο',
24555 		'ύ': 'Υ',
24556 		'ώ': 'Ω',
24557 		'Ӏ': 'Ӏ',		// Russian and slavic languages
24558 		'ӏ': 'Ӏ'
24559 	} : {
24560 		'Ӏ': 'Ӏ'		// Russian and slavic languages
24561 	};
24562 
24563 	switch (this.locale.getLanguage()) {
24564 		case "az":
24565 		case "tr":
24566 		case "crh":
24567 		case "kk":
24568 		case "krc":
24569 		case "tt":
24570 			var lower = "iı";
24571 			var upper = "İI";
24572 			this._setUpMap(lower, upper);
24573 			break;
24574 		case "fr":
24575 			if (this.up && this.locale.getRegion() !== "CA") {
24576 				this._setUpMap("àáâãäçèéêëìíîïñòóôöùúûü", "AAAAACEEEEIIIINOOOOUUUU");
24577 			}
24578 			break;
24579 	}
24580 	
24581 	if (ilib._getBrowser() === "ie" || ilib._getBrowser() === "Edge") {
24582 		// IE is missing these mappings for some reason
24583 		if (this.up) {
24584 			this.mapData['ς'] = 'Σ';
24585 		}
24586 		this._setUpMap("ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⳁⳉⳋ", "ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⳀⳈⳊ"); // Coptic
24587 		// Georgian Nuskhuri <-> Asomtavruli
24588 		this._setUpMap("ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥ", "ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ");	
24589 	}
24590 };
24591 
24592 CaseMapper.prototype = {
24593 	/** 
24594 	 * @private 
24595 	 */
24596 	_charMapper: function(string) {
24597 		if (!string) {
24598 			return string;
24599 		}
24600 		var input = (typeof(string) === 'string') ? new IString(string) : string.toString();
24601 		var ret = "";
24602 		var it = input.charIterator();
24603 		var c;
24604 		
24605 		while (it.hasNext()) {
24606 			c = it.next();
24607 			if (!this.up && c === 'Σ') {
24608 				if (it.hasNext()) {
24609 					c = it.next();
24610 					var code = c.charCodeAt(0);
24611 					// if the next char is not a greek letter, this is the end of the word so use the
24612 					// final form of sigma. Otherwise, use the mid-word form.
24613 					ret += ((code < 0x0388 && code !== 0x0386) || code > 0x03CE) ? 'ς' : 'σ';
24614 					ret += c.toLowerCase();
24615 				} else {
24616 					// no next char means this is the end of the word, so use the final form of sigma
24617 					ret += 'ς';
24618 				}
24619 			} else {
24620 				if (this.mapData[c]) {
24621 					ret += this.mapData[c];
24622 				} else {
24623 					ret += this.up ? c.toUpperCase() : c.toLowerCase();
24624 				}
24625 			}
24626 		}
24627 		
24628 		return ret;
24629 	},
24630 
24631 	/** @private */
24632 	_setUpMap: function(lower, upper) {
24633 		var from, to;
24634 		if (this.up) {
24635 			from = lower;
24636 			to = upper;
24637 		} else {
24638 			from = upper;
24639 			to = lower;
24640 		}
24641 		for (var i = 0; i < upper.length; i++) {
24642 			this.mapData[from[i]] = to[i];
24643 		}
24644 	},
24645 
24646 	/**
24647 	 * Return the locale that this mapper was constructed with. 
24648 	 * @returns {Locale} the locale that this mapper was constructed with
24649 	 */
24650 	getLocale: function () {
24651 		return this.locale;
24652 	},
24653 		
24654 	/**
24655 	 * Map a string to lower case in a locale-sensitive manner.
24656 	 * 
24657 	 * @param {string|undefined} string
24658 	 * @return {string|undefined}
24659 	 */
24660 	map: function (string) {
24661 		return this._charMapper(string);
24662 	}
24663 };
24664 
24665 
24666 
24667 /*< NumberingPlan.js */
24668 /*
24669  * NumPlan.js - Represent a phone numbering plan.
24670  * 
24671  * Copyright © 2014-2015, JEDLSoft
24672  *
24673  * Licensed under the Apache License, Version 2.0 (the "License");
24674  * you may not use this file except in compliance with the License.
24675  * You may obtain a copy of the License at
24676  *
24677  *     http://www.apache.org/licenses/LICENSE-2.0
24678  *
24679  * Unless required by applicable law or agreed to in writing, software
24680  * distributed under the License is distributed on an "AS IS" BASIS,
24681  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24682  *
24683  * See the License for the specific language governing permissions and
24684  * limitations under the License.
24685  */
24686 
24687 /*
24688 !depends 
24689 ilib.js 
24690 Locale.js 
24691 Utils.js
24692 JSUtils.js
24693 */
24694 
24695 // !data numplan
24696 
24697 
24698 /**
24699  * @class
24700  * Create a numbering plan information instance for a particular country's plan.<p>
24701  * 
24702  * The options may contain any of the following properties:
24703  * 
24704  * <ul>
24705  * <li><i>locale</i> - locale for which the numbering plan is sought. This locale
24706  * will be mapped to the actual numbering plan, which may be shared amongst a
24707  * number of countries.
24708  *
24709  * <li>onLoad - a callback function to call when the date format object is fully 
24710  * loaded. When the onLoad option is given, the DateFmt object will attempt to
24711  * load any missing locale data using the ilib loader callback.
24712  * When the constructor is done (even if the data is already preassembled), the 
24713  * onLoad function is called with the current instance as a parameter, so this
24714  * callback can be used with preassembled or dynamic loading or a mix of the two.
24715  * 
24716  * <li>sync - tell whether to load any missing locale data synchronously or 
24717  * asynchronously. If this option is given as "false", then the "onLoad"
24718  * callback must be given, as the instance returned from this constructor will
24719  * not be usable for a while.
24720  *  
24721  * <li><i>loadParams</i> - an object containing parameters to pass to the 
24722  * loader callback function when locale data is missing. The parameters are not
24723  * interpretted or modified in any way. They are simply passed along. The object 
24724  * may contain any property/value pairs as long as the calling code is in
24725  * agreement with the loader callback function as to what those parameters mean.
24726  * </ul>
24727  * 
24728  * @private
24729  * @constructor
24730  * @param {Object} options options governing the way this plan is loaded
24731  */
24732 var NumberingPlan = function (options) {
24733 	var sync = true,
24734 	    loadParams = {};
24735 	
24736 	this.locale = new Locale();
24737 
24738 	if (options) {
24739 		if (options.locale) {
24740 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
24741 		}
24742 		
24743 		if (typeof(options.sync) !== 'undefined') {
24744 			sync = (options.sync == true);
24745 		}
24746 		
24747 		if (options.loadParams) {
24748 			loadParams = options.loadParams;
24749 		}
24750 	}	
24751 
24752 	Utils.loadData({
24753 		name: "numplan.json",
24754 		object: NumberingPlan,
24755 		locale: this.locale,
24756 		sync: sync, 
24757 		loadParams: loadParams, 
24758 		callback: ilib.bind(this, function (npdata) {
24759 			if (!npdata) {
24760 				npdata = {
24761 					"region": "XX",
24762 					"skipTrunk": false,
24763 					"trunkCode": "0",
24764 					"iddCode": "00",
24765 					"dialingPlan": "closed",
24766 					"commonFormatChars": " ()-./",
24767 					"fieldLengths": {
24768 						"areaCode": 0,
24769 						"cic": 0,
24770 						"mobilePrefix": 0,
24771 						"serviceCode": 0
24772 					}
24773 				};
24774 			}
24775 
24776 			/** 
24777 			 * @type {{
24778 			 *   region:string,
24779 			 *   skipTrunk:boolean,
24780 			 *   trunkCode:string,
24781 			 *   iddCode:string,
24782 			 *   dialingPlan:string,
24783 			 *   commonFormatChars:string,
24784 			 *   fieldLengths:Object.<string,number>,
24785 			 *   contextFree:boolean,
24786 			 *   findExtensions:boolean,
24787 			 *   trunkRequired:boolean,
24788 			 *   extendedAreaCodes:boolean
24789 			 * }}
24790 			 */
24791 			this.npdata = npdata;
24792 			if (options && typeof(options.onLoad) === 'function') {
24793 				options.onLoad(this);
24794 			}
24795 		})
24796 	});
24797 };
24798 
24799 NumberingPlan.prototype = {
24800 	/**
24801 	 * Return the name of this plan. This may be different than the 
24802 	 * name of the region because sometimes multiple countries share 
24803 	 * the same plan.
24804 	 * @return {string} the name of the plan
24805 	 */
24806 	getName: function() {
24807 		return this.npdata.region;
24808 	},
24809 
24810 	/**
24811 	 * Return the trunk code of the current plan as a string.
24812 	 * @return {string|undefined} the trunk code of the plan or
24813 	 * undefined if there is no trunk code in this plan
24814 	 */
24815 	getTrunkCode: function() {
24816 		return this.npdata.trunkCode;
24817 	},
24818 	
24819 	/**
24820 	 * Return the international direct dialing code of this plan.
24821 	 * @return {string} the IDD code of this plan
24822 	 */
24823 	getIDDCode: function() {
24824 		return this.npdata.iddCode;	
24825 	},
24826 	
24827 	/**
24828 	 * Return the plan style for this plan. The plan style may be
24829 	 * one of:
24830 	 * 
24831 	 * <ul>
24832 	 * <li>"open" - area codes may be left off if the caller is 
24833 	 * dialing to another number within the same area code
24834 	 * <li>"closed" - the area code must always be specified, even
24835 	 * if calling another number within the same area code
24836 	 * </ul>
24837 	 * 
24838 	 * @return {string} the plan style, "open" or "closed"
24839 	 */
24840 	getPlanStyle: function() {	
24841 		return this.npdata.dialingPlan;
24842 	},
24843 	/** [Need Comment]
24844 	 * Return a contextFree
24845 	 *
24846 	 * @return {boolean}
24847 	 */
24848 	getContextFree: function() {
24849 		return this.npdata.contextFree;
24850 	},
24851 	/** [Need Comment]
24852 	 * Return a findExtensions
24853 	 * 
24854 	 * @return {boolean}
24855 	 */
24856 	getFindExtensions: function() {
24857 		return this.npdata.findExtensions;
24858 	},
24859 	/** [Need Comment]
24860 	 * Return a skipTrunk
24861 	 * 
24862 	 * @return {boolean}
24863 	 */
24864 	getSkipTrunk: function() {
24865 		return this.npdata.skipTrunk;
24866 	},
24867 	/** [Need Comment]
24868 	 * Return a skipTrunk
24869 	 * 
24870 	 * @return {boolean}
24871 	 */
24872 	getTrunkRequired: function() {
24873 		return this.npdata.trunkRequired;
24874 	},
24875 	/**
24876 	 * Return true if this plan uses extended area codes.
24877 	 * @return {boolean} true if the plan uses extended area codes
24878 	 */
24879 	getExtendedAreaCode: function() {
24880 		return this.npdata.extendedAreaCodes;
24881 	},
24882 	/**
24883 	 * Return a string containing all of the common format characters
24884 	 * used to format numbers.
24885 	 * @return {string} the common format characters fused in this locale
24886 	 */
24887 	getCommonFormatChars: function() {
24888 		return this.npdata.commonFormatChars;
24889 	},
24890 	
24891 	/**
24892 	 * Return the length of the field with the given name. If the length
24893 	 * is returned as 0, this means it is variable length.
24894 	 * 
24895 	 * @param {string} field name of the field for which the length is 
24896 	 * being sought
24897 	 * @return {number} if positive, this gives the length of the given 
24898 	 * field. If zero, the field is variable length. If negative, the
24899 	 * field is not known.
24900 	 */
24901 	getFieldLength: function (field) {
24902 		var dataField = this.npdata.fieldLengths;
24903 		
24904 		return dataField[field];
24905 	}
24906 };
24907 
24908 
24909 /*< PhoneLocale.js */
24910 /*
24911  * phoneloc.js - Represent a phone locale object.
24912  * 
24913  * Copyright © 2014-2015, JEDLSoft
24914  *
24915  * Licensed under the Apache License, Version 2.0 (the "License");
24916  * you may not use this file except in compliance with the License.
24917  * You may obtain a copy of the License at
24918  *
24919  *     http://www.apache.org/licenses/LICENSE-2.0
24920  *
24921  * Unless required by applicable law or agreed to in writing, software
24922  * distributed under the License is distributed on an "AS IS" BASIS,
24923  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24924  *
24925  * See the License for the specific language governing permissions and
24926  * limitations under the License.
24927  */
24928 
24929 /*
24930 !depends 
24931 ilib.js 
24932 Locale.js
24933 Utils.js
24934 */
24935 
24936 // !data phoneloc
24937 
24938 
24939 /**
24940  * @class
24941  * Extension of the locale class that has extra methods to map various numbers
24942  * related to phone number parsing.
24943  *
24944  * @param {Object} options Options that govern how this phone locale works
24945  * 
24946  * @private
24947  * @constructor
24948  * @extends Locale
24949  */
24950 var PhoneLocale = function(options) {
24951 	var region,
24952 		mcc,
24953 		cc,
24954 		sync = true,
24955 		loadParams = {},
24956 		locale;
24957 	
24958 	locale = (options && options.locale) || ilib.getLocale();
24959 
24960 	this.parent.call(this, locale);
24961 	
24962 	region = this.region;
24963 	
24964 	if (options) {
24965 		if (typeof(options.mcc) !== 'undefined') {
24966 			mcc = options.mcc;
24967 		}
24968 		
24969 		if (typeof(options.countryCode) !== 'undefined') {
24970 			cc = options.countryCode;
24971 		}
24972 
24973 		if (typeof(options.sync) !== 'undefined') {
24974 			sync = (options.sync == true);
24975 		}
24976 		
24977 		if (options.loadParams) {
24978 			loadParams = options.loadParams;
24979 		}
24980 	}
24981 
24982 	Utils.loadData({
24983 		name: "phoneloc.json",
24984 		object: PhoneLocale,
24985 		nonlocale: true,
24986 		sync: sync, 
24987 		loadParams: loadParams, 
24988 		callback: ilib.bind(this, function (data) {
24989 			/** @type {{mcc2reg:Object.<string,string>,cc2reg:Object.<string,string>,reg2cc:Object.<string,string>,area2reg:Object.<string,string>}} */
24990 			this.mappings = data;
24991 			
24992 			if (typeof(mcc) !== 'undefined') {
24993 				region = this.mappings.mcc2reg[mcc];	
24994 			}
24995 
24996 			if (typeof(cc) !== 'undefined') {
24997 				region = this.mappings.cc2reg[cc];
24998 			}
24999 
25000 			if (!region) {
25001 				region = "XX";
25002 			}
25003 
25004 			this.region = this._normPhoneReg(region);
25005 			this._genSpec();
25006 
25007 			if (options && typeof(options.onLoad) === 'function') {
25008 				options.onLoad(this);
25009 			}									
25010 		})
25011 	});
25012 };
25013 
25014 PhoneLocale.prototype = new Locale();
25015 PhoneLocale.prototype.parent = Locale;
25016 PhoneLocale.prototype.constructor = PhoneLocale;
25017 
25018 /**
25019  * Map a mobile carrier code to a region code.
25020  *
25021  * @static
25022  * @package
25023  * @param {string|undefined} mcc the MCC to map
25024  * @return {string|undefined} the region code
25025  */
25026 
25027 PhoneLocale.prototype._mapMCCtoRegion = function(mcc) {
25028 	if (!mcc) {
25029 		return undefined;
25030 	}
25031 	return this.mappings.mcc2reg && this.mappings.mcc2reg[mcc] || "XX";
25032 };
25033 
25034 /**
25035  * Map a country code to a region code.
25036  *
25037  * @static
25038  * @package
25039  * @param {string|undefined} cc the country code to map
25040  * @return {string|undefined} the region code
25041  */
25042 PhoneLocale.prototype._mapCCtoRegion = function(cc) {
25043 	if (!cc) {
25044 		return undefined;
25045 	}
25046 	return this.mappings.cc2reg && this.mappings.cc2reg[cc] || "XX";
25047 };
25048 
25049 /**
25050  * Map a region code to a country code.
25051  *
25052  * @static
25053  * @package
25054  * @param {string|undefined} region the region code to map
25055  * @return {string|undefined} the country code
25056  */
25057 PhoneLocale.prototype._mapRegiontoCC = function(region) {
25058 	if (!region) {
25059 		return undefined;
25060 	}
25061 	return this.mappings.reg2cc && this.mappings.reg2cc[region] || "0";
25062 };
25063 
25064 /**
25065  * Map a country code to a region code.
25066  *
25067  * @static
25068  * @package
25069  * @param {string|undefined} cc the country code to map
25070  * @param {string|undefined} area the area code within the country code's numbering plan
25071  * @return {string|undefined} the region code
25072  */
25073 PhoneLocale.prototype._mapAreatoRegion = function(cc, area) {
25074 	if (!cc) {
25075 		return undefined;
25076 	}
25077 	if (cc in this.mappings.area2reg) {
25078 		return this.mappings.area2reg[cc][area] || this.mappings.area2reg[cc]["default"];
25079 	} else {
25080 		return this.mappings.cc2reg[cc];
25081 	}
25082 };
25083 
25084 /**
25085  * Return the region that controls the dialing plan in the given
25086  * region. (ie. the "normalized phone region".)
25087  * 
25088  * @static
25089  * @package
25090  * @param {string} region the region code to normalize
25091  * @return {string} the normalized region code
25092  */
25093 PhoneLocale.prototype._normPhoneReg = function(region) {
25094 	var norm;
25095 	
25096 	// Map all NANP regions to the right region, so that they get parsed using the 
25097 	// correct state table
25098 	switch (region) {
25099 		case "US": // usa
25100 		case "CA": // canada
25101 		case "AG": // antigua and barbuda
25102 		case "BS": // bahamas
25103 		case "BB": // barbados
25104 		case "DM": // dominica
25105 		case "DO": // dominican republic
25106 		case "GD": // grenada
25107 		case "JM": // jamaica
25108 		case "KN": // st. kitts and nevis
25109 		case "LC": // st. lucia
25110 		case "VC": // st. vincent and the grenadines
25111 		case "TT": // trinidad and tobago
25112 		case "AI": // anguilla
25113 		case "BM": // bermuda
25114 		case "VG": // british virgin islands
25115 		case "KY": // cayman islands
25116 		case "MS": // montserrat
25117 		case "TC": // turks and caicos
25118 		case "AS": // American Samoa 
25119 		case "VI": // Virgin Islands, U.S.
25120 		case "PR": // Puerto Rico
25121 		case "MP": // Northern Mariana Islands
25122 		case "T:": // East Timor
25123 		case "GU": // Guam
25124 			norm = "US";
25125 			break;
25126 		
25127 		// these all use the Italian dialling plan
25128 		case "IT": // italy
25129 		case "SM": // san marino
25130 		case "VA": // vatican city
25131 			norm = "IT";
25132 			break;
25133 		
25134 		// all the French dependencies are on the French dialling plan
25135 		case "FR": // france
25136 		case "GF": // french guiana
25137 		case "MQ": // martinique
25138 		case "GP": // guadeloupe, 
25139 		case "BL": // saint barthélemy
25140 		case "MF": // saint martin
25141 		case "RE": // réunion, mayotte
25142 			norm = "FR";
25143 			break;
25144 		default:
25145 			norm = region;
25146 			break;
25147 	}	
25148 	return norm;
25149 };
25150 
25151 
25152 /*< PhoneHandlerFactory.js */
25153 /*
25154  * handler.js - Handle phone number parse states
25155  * 
25156  * Copyright © 2014-2015, JEDLSoft
25157  *
25158  * Licensed under the Apache License, Version 2.0 (the "License");
25159  * you may not use this file except in compliance with the License.
25160  * You may obtain a copy of the License at
25161  *
25162  *     http://www.apache.org/licenses/LICENSE-2.0
25163  *
25164  * Unless required by applicable law or agreed to in writing, software
25165  * distributed under the License is distributed on an "AS IS" BASIS,
25166  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25167  *
25168  * See the License for the specific language governing permissions and
25169  * limitations under the License.
25170  */
25171 
25172 
25173 /**
25174  * @class
25175  * @private
25176  * @constructor
25177  */
25178 var PhoneHandler = function () {
25179 	return this;
25180 };
25181 
25182 PhoneHandler.prototype = {
25183 	/**
25184 	 * @private
25185 	 * @param {string} number phone number
25186 	 * @param {Object} fields the fields that have been extracted so far
25187 	 * @param {Object} regionSettings settings used to parse the rest of the number
25188 	 */
25189 	processSubscriberNumber: function(number, fields, regionSettings) {
25190 		var last;
25191 		
25192 		last = number.search(/[xwtp,;]/i);	// last digit of the local number
25193 
25194 		if (last > -1) {
25195 			if (last > 0) {
25196 				fields.subscriberNumber = number.substring(0, last);
25197 			}
25198 			// strip x's which are there to indicate a break between the local subscriber number and the extension, but
25199 			// are not themselves a dialable character
25200 			fields.extension = number.substring(last).replace('x', '');
25201 		} else {
25202 			if (number.length) {
25203 				fields.subscriberNumber = number;
25204 			}
25205 		}
25206 		
25207 		if (regionSettings.plan.getFieldLength('maxLocalLength') &&
25208 				fields.subscriberNumber &&
25209 				fields.subscriberNumber.length > regionSettings.plan.getFieldLength('maxLocalLength')) {
25210 			fields.invalid = true;
25211 		}
25212 	},
25213 	/**
25214 	 * @private
25215 	 * @param {string} fieldName 
25216 	 * @param {number} length length of phone number
25217 	 * @param {string} number phone number
25218 	 * @param {number} currentChar currentChar to be parsed
25219 	 * @param {Object} fields the fields that have been extracted so far
25220 	 * @param {Object} regionSettings settings used to parse the rest of the number
25221 	 * @param {boolean} noExtractTrunk 
25222 	 */
25223 	processFieldWithSubscriberNumber: function(fieldName, length, number, currentChar, fields, regionSettings, noExtractTrunk) {
25224 		var ret, end;
25225 		
25226 		if (length !== undefined && length > 0) {
25227 			// fixed length
25228 			end = length;
25229 			if (regionSettings.plan.getTrunkCode() === "0" && number.charAt(0) === "0") {
25230 				end += regionSettings.plan.getTrunkCode().length;  // also extract the trunk access code
25231 			}
25232 		} else {
25233 			// variable length
25234 			// the setting is the negative of the length to add, so subtract to make it positive
25235 			end = currentChar + 1 - length;
25236 		}
25237 		
25238 		if (fields[fieldName] !== undefined) {
25239 			// we have a spurious recognition, because this number already contains that field! So, just put
25240 			// everything into the subscriberNumber as the default
25241 			this.processSubscriberNumber(number, fields, regionSettings);
25242 		} else {
25243 			fields[fieldName] = number.substring(0, end);
25244 			if (number.length > end) {
25245 				this.processSubscriberNumber(number.substring(end), fields, regionSettings);
25246 			}
25247 		}
25248 		
25249 		ret = {
25250 			number: ""
25251 		};
25252 
25253 		return ret;
25254 	},
25255 	/**
25256 	 * @private
25257 	 * @param {string} fieldName 
25258 	 * @param {number} length length of phone number
25259 	 * @param {string} number phone number
25260 	 * @param {number} currentChar currentChar to be parsed
25261 	 * @param {Object} fields the fields that have been extracted so far
25262 	 * @param {Object} regionSettings settings used to parse the rest of the number
25263 	 */
25264 	processField: function(fieldName, length, number, currentChar, fields, regionSettings) {
25265 		var ret = {}, end;
25266 		
25267 		if (length !== undefined && length > 0) {
25268 			// fixed length
25269 			end = length;
25270 			if (regionSettings.plan.getTrunkCode() === "0" && number.charAt(0) === "0") {
25271 				end += regionSettings.plan.getTrunkCode().length;  // also extract the trunk access code
25272 			}
25273 		} else {
25274 			// variable length
25275 			// the setting is the negative of the length to add, so subtract to make it positive
25276 			end = currentChar + 1 - length;
25277 		}
25278 		
25279 		if (fields[fieldName] !== undefined) {
25280 			// we have a spurious recognition, because this number already contains that field! So, just put
25281 			// everything into the subscriberNumber as the default
25282 			this.processSubscriberNumber(number, fields, regionSettings);
25283 			ret.number = "";
25284 		} else {
25285 			fields[fieldName] = number.substring(0, end);			
25286 			ret.number = (number.length > end) ? number.substring(end) : "";
25287 		}
25288 		
25289 		return ret;
25290 	},
25291 	/**
25292 	 * @private
25293 	 * @param {string} number phone number
25294 	 * @param {number} currentChar currentChar to be parsed
25295 	 * @param {Object} fields the fields that have been extracted so far
25296 	 * @param {Object} regionSettings settings used to parse the rest of the number
25297 	 */
25298 	trunk: function(number, currentChar, fields, regionSettings) {
25299 		var ret, trunkLength;
25300 		
25301 		if (fields.trunkAccess !== undefined) {
25302 			// What? We already have one? Okay, put the rest of this in the subscriber number as the default behaviour then.
25303 			this.processSubscriberNumber(number, fields, regionSettings);
25304 			number = "";
25305 		} else {
25306 			trunkLength = regionSettings.plan.getTrunkCode().length;
25307 			fields.trunkAccess = number.substring(0, trunkLength);
25308 			number = (number.length > trunkLength) ? number.substring(trunkLength) : "";
25309 		}
25310 		
25311 		ret = {
25312 			number: number
25313 		};
25314 		
25315 		return ret;
25316 	},
25317 	/**
25318 	 * @private
25319 	 * @param {string} number phone number
25320 	 * @param {number} currentChar currentChar to be parsed
25321 	 * @param {Object} fields the fields that have been extracted so far
25322 	 * @param {Object} regionSettings settings used to parse the rest of the number
25323 	 */
25324 	plus: function(number, currentChar, fields, regionSettings) {
25325 		var ret = {};
25326 		
25327 		if (fields.iddPrefix !== undefined) {
25328 			// What? We already have one? Okay, put the rest of this in the subscriber number as the default behaviour then.
25329 			this.processSubscriberNumber(number, fields, regionSettings);
25330 			ret.number = "";
25331 		} else {
25332 			// found the idd prefix, so save it and cause the function to parse the next part
25333 			// of the number with the idd table
25334 			fields.iddPrefix = number.substring(0, 1);
25335 	
25336 			ret = {
25337 				number: number.substring(1),
25338 				table: 'idd'    // shared subtable that parses the country code
25339 			};
25340 		}		
25341 		return ret;
25342 	},
25343 	/**
25344 	 * @private
25345 	 * @param {string} number phone number
25346 	 * @param {number} currentChar currentChar to be parsed
25347 	 * @param {Object} fields the fields that have been extracted so far
25348 	 * @param {Object} regionSettings settings used to parse the rest of the number
25349 	 */
25350 	idd: function(number, currentChar, fields, regionSettings) {
25351 		var ret = {};
25352 		
25353 		if (fields.iddPrefix !== undefined) {
25354 			// What? We already have one? Okay, put the rest of this in the subscriber number as the default behaviour then.
25355 			this.processSubscriberNumber(number, fields, regionSettings);
25356 			ret.number = "";
25357 		} else {
25358 			// found the idd prefix, so save it and cause the function to parse the next part
25359 			// of the number with the idd table
25360 			fields.iddPrefix = number.substring(0, currentChar+1);
25361 	
25362 			ret = {
25363 				number: number.substring(currentChar+1),
25364 				table: 'idd'    // shared subtable that parses the country code
25365 			};
25366 		}
25367 		
25368 		return ret;
25369 	},
25370 	/**
25371 	 * @private
25372 	 * @param {string} number phone number
25373 	 * @param {number} currentChar currentChar to be parsed
25374 	 * @param {Object} fields the fields that have been extracted so far
25375 	 * @param {Object} regionSettings settings used to parse the rest of the number
25376 	 */	
25377 	country: function(number, currentChar, fields, regionSettings) {
25378 		var ret, cc;
25379 		
25380 		// found the country code of an IDD number, so save it and cause the function to 
25381 		// parse the rest of the number with the regular table for this locale
25382 		fields.countryCode = number.substring(0, currentChar+1);
25383 		cc = fields.countryCode.replace(/[wWpPtT\+#\*]/g, ''); // fix for NOV-108200
25384 		// console.log("Found country code " + fields.countryCode + ". Switching to country " + locale.region + " to parse the rest of the number");
25385 		
25386 		ret = {
25387 			number: number.substring(currentChar+1),
25388 			countryCode: cc
25389 		};
25390 		
25391 		return ret;
25392 	},
25393 	/**
25394 	 * @private
25395 	 * @param {string} number phone number
25396 	 * @param {number} currentChar currentChar to be parsed
25397 	 * @param {Object} fields the fields that have been extracted so far
25398 	 * @param {Object} regionSettings settings used to parse the rest of the number
25399 	 */
25400 	cic: function(number, currentChar, fields, regionSettings) {
25401 		return this.processField('cic', regionSettings.plan.getFieldLength('cic'), number, currentChar, fields, regionSettings);
25402 	},
25403 	/**
25404 	 * @private
25405 	 * @param {string} number phone number
25406 	 * @param {number} currentChar currentChar to be parsed
25407 	 * @param {Object} fields the fields that have been extracted so far
25408 	 * @param {Object} regionSettings settings used to parse the rest of the number
25409 	 */
25410 	service: function(number, currentChar, fields, regionSettings) {
25411 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('serviceCode'), number, currentChar, fields, regionSettings, false);
25412 	},
25413 	/**
25414 	 * @private
25415 	 * @param {string} number phone number
25416 	 * @param {number} currentChar currentChar to be parsed
25417 	 * @param {Object} fields the fields that have been extracted so far
25418 	 * @param {Object} regionSettings settings used to parse the rest of the number
25419 	 */
25420 	area: function(number, currentChar, fields, regionSettings) {
25421 		var ret, last, end, localLength;
25422 		
25423 		last = number.search(/[xwtp]/i);	// last digit of the local number
25424 		localLength = (last > -1) ? last : number.length;
25425 
25426 		if (regionSettings.plan.getFieldLength('areaCode') > 0) {
25427 			// fixed length
25428 			end = regionSettings.plan.getFieldLength('areaCode');
25429 			if (regionSettings.plan.getTrunkCode() === number.charAt(0)) {
25430 				end += regionSettings.plan.getTrunkCode().length;  // also extract the trunk access code
25431 				localLength -= regionSettings.plan.getTrunkCode().length;
25432 			}
25433 		} else {
25434 			// variable length
25435 			// the setting is the negative of the length to add, so subtract to make it positive
25436 			end = currentChar + 1 - regionSettings.plan.getFieldLength('areaCode');
25437 		}
25438 		
25439 		// substring() extracts the part of the string up to but not including the end character,
25440 		// so add one to compensate
25441 		if (regionSettings.plan.getFieldLength('maxLocalLength') !== undefined) {
25442 			if (fields.trunkAccess !== undefined || fields.mobilePrefix !== undefined ||
25443 					fields.countryCode !== undefined ||
25444 					localLength > regionSettings.plan.getFieldLength('maxLocalLength')) {
25445 				// too long for a local number by itself, or a different final state already parsed out the trunk
25446 				// or mobile prefix, then consider the rest of this number to be an area code + part of the subscriber number
25447 				fields.areaCode = number.substring(0, end);
25448 				if (number.length > end) {
25449 					this.processSubscriberNumber(number.substring(end), fields, regionSettings);
25450 				}
25451 			} else {
25452 				// shorter than the length needed for a local number, so just consider it a local number
25453 				this.processSubscriberNumber(number, fields, regionSettings);
25454 			}
25455 		} else {
25456 			fields.areaCode = number.substring(0, end);
25457 			if (number.length > end) {
25458 				this.processSubscriberNumber(number.substring(end), fields, regionSettings);
25459 			}
25460 		}
25461 		
25462 		// extensions are separated from the number by a dash in Germany
25463 		if (regionSettings.plan.getFindExtensions() !== undefined && fields.subscriberNumber !== undefined) {
25464 			var dash = fields.subscriberNumber.indexOf("-");
25465 			if (dash > -1) {
25466 				fields.subscriberNumber = fields.subscriberNumber.substring(0, dash);
25467 				fields.extension = fields.subscriberNumber.substring(dash+1);
25468 			}
25469 		}
25470 
25471 		ret = {
25472 			number: ""
25473 		};
25474 
25475 		return ret;
25476 	},
25477 	/**
25478 	 * @private
25479 	 * @param {string} number phone number
25480 	 * @param {number} currentChar currentChar to be parsed
25481 	 * @param {Object} fields the fields that have been extracted so far
25482 	 * @param {Object} regionSettings settings used to parse the rest of the number
25483 	 */
25484 	none: function(number, currentChar, fields, regionSettings) {
25485 		var ret;
25486 		
25487 		// this is a last resort function that is called when nothing is recognized.
25488 		// When this happens, just put the whole stripped number into the subscriber number
25489 			
25490 		if (number.length > 0) {
25491 			this.processSubscriberNumber(number, fields, regionSettings);
25492 			if (currentChar > 0 && currentChar < number.length) {
25493 				// if we were part-way through parsing, and we hit an invalid digit,
25494 				// indicate that the number could not be parsed properly
25495 				fields.invalid = true;
25496 			}
25497 		}
25498 		
25499 		ret = {
25500 			number:""
25501 		};
25502 		
25503 		return ret;
25504 	},
25505 	/**
25506 	 * @private
25507 	 * @param {string} number phone number
25508 	 * @param {number} currentChar currentChar to be parsed
25509 	 * @param {Object} fields the fields that have been extracted so far
25510 	 * @param {Object} regionSettings settings used to parse the rest of the number
25511 	 */
25512 	vsc: function(number, currentChar, fields, regionSettings) {
25513 		var ret, length, end;
25514 
25515 		if (fields.vsc === undefined) {
25516 			length = regionSettings.plan.getFieldLength('vsc') || 0;
25517 			if (length !== undefined && length > 0) {
25518 				// fixed length
25519 				end = length;
25520 			} else {
25521 				// variable length
25522 				// the setting is the negative of the length to add, so subtract to make it positive
25523 				end = currentChar + 1 - length;
25524 			}
25525 			
25526 			// found a VSC code (ie. a "star code"), so save it and cause the function to 
25527 			// parse the rest of the number with the same table for this locale
25528 			fields.vsc = number.substring(0, end);
25529 			number = (number.length > end) ? "^" + number.substring(end) : "";
25530 		} else {
25531 			// got it twice??? Okay, this is a bogus number then. Just put everything else into the subscriber number as the default
25532 			this.processSubscriberNumber(number, fields, regionSettings);
25533 			number = "";
25534 		}
25535 
25536 		// treat the rest of the number as if it were a completely new number
25537 		ret = {
25538 			number: number
25539 		};
25540 
25541 		return ret;
25542 	},
25543 	/**
25544 	 * @private
25545 	 * @param {string} number phone number
25546 	 * @param {number} currentChar currentChar to be parsed
25547 	 * @param {Object} fields the fields that have been extracted so far
25548 	 * @param {Object} regionSettings settings used to parse the rest of the number
25549 	 */
25550 	cell: function(number, currentChar, fields, regionSettings) {
25551 		return this.processFieldWithSubscriberNumber('mobilePrefix', regionSettings.plan.getFieldLength('mobilePrefix'), number, currentChar, fields, regionSettings, false);
25552 	},
25553 	/**
25554 	 * @private
25555 	 * @param {string} number phone number
25556 	 * @param {number} currentChar currentChar to be parsed
25557 	 * @param {Object} fields the fields that have been extracted so far
25558 	 * @param {Object} regionSettings settings used to parse the rest of the number
25559 	 */
25560 	personal: function(number, currentChar, fields, regionSettings) {
25561 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('personal'), number, currentChar, fields, regionSettings, false);
25562 	},
25563 	/**
25564 	 * @private
25565 	 * @param {string} number phone number
25566 	 * @param {number} currentChar currentChar to be parsed
25567 	 * @param {Object} fields the fields that have been extracted so far
25568 	 * @param {Object} regionSettings settings used to parse the rest of the number
25569 	 */
25570 	emergency: function(number, currentChar, fields, regionSettings) {
25571 		return this.processFieldWithSubscriberNumber('emergency', regionSettings.plan.getFieldLength('emergency'), number, currentChar, fields, regionSettings, true);
25572 	},
25573 	/**
25574 	 * @private
25575 	 * @param {string} number phone number
25576 	 * @param {number} currentChar currentChar to be parsed
25577 	 * @param {Object} fields the fields that have been extracted so far
25578 	 * @param {Object} regionSettings settings used to parse the rest of the number
25579 	 */
25580 	premium: function(number, currentChar, fields, regionSettings) {
25581 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('premium'), number, currentChar, fields, regionSettings, false);
25582 	},
25583 	/**
25584 	 * @private
25585 	 * @param {string} number phone number
25586 	 * @param {number} currentChar currentChar to be parsed
25587 	 * @param {Object} fields the fields that have been extracted so far
25588 	 * @param {Object} regionSettings settings used to parse the rest of the number
25589 	 */
25590 	special: function(number, currentChar, fields, regionSettings) {
25591 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('special'), number, currentChar, fields, regionSettings, false);
25592 	},
25593 	/**
25594 	 * @private
25595 	 * @param {string} number phone number
25596 	 * @param {number} currentChar currentChar to be parsed
25597 	 * @param {Object} fields the fields that have been extracted so far
25598 	 * @param {Object} regionSettings settings used to parse the rest of the number
25599 	 */
25600 	service2: function(number, currentChar, fields, regionSettings) {
25601 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('service2'), number, currentChar, fields, regionSettings, false);
25602 	},
25603 	/**
25604 	 * @private
25605 	 * @param {string} number phone number
25606 	 * @param {number} currentChar currentChar to be parsed
25607 	 * @param {Object} fields the fields that have been extracted so far
25608 	 * @param {Object} regionSettings settings used to parse the rest of the number
25609 	 */
25610 	service3: function(number, currentChar, fields, regionSettings) {
25611 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('service3'), number, currentChar, fields, regionSettings, false);
25612 	},
25613 	/**
25614 	 * @private
25615 	 * @param {string} number phone number
25616 	 * @param {number} currentChar currentChar to be parsed
25617 	 * @param {Object} fields the fields that have been extracted so far
25618 	 * @param {Object} regionSettings settings used to parse the rest of the number
25619 	 */
25620 	service4: function(number, currentChar, fields, regionSettings) {
25621 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('service4'), number, currentChar, fields, regionSettings, false);
25622 	},
25623 	/**
25624 	 * @private
25625 	 * @param {string} number phone number
25626 	 * @param {number} currentChar currentChar to be parsed
25627 	 * @param {Object} fields the fields that have been extracted so far
25628 	 * @param {Object} regionSettings settings used to parse the rest of the number
25629 	 */
25630 	cic2: function(number, currentChar, fields, regionSettings) {
25631 		return this.processField('cic', regionSettings.plan.getFieldLength('cic2'), number, currentChar, fields, regionSettings);
25632 	},
25633 	/**
25634 	 * @private
25635 	 * @param {string} number phone number
25636 	 * @param {number} currentChar currentChar to be parsed
25637 	 * @param {Object} fields the fields that have been extracted so far
25638 	 * @param {Object} regionSettings settings used to parse the rest of the number
25639 	 */
25640 	cic3: function(number, currentChar, fields, regionSettings) {
25641 		return this.processField('cic', regionSettings.plan.getFieldLength('cic3'), number, currentChar, fields, regionSettings);
25642 	},
25643 	/**
25644 	 * @private
25645 	 * @param {string} number phone number
25646 	 * @param {number} currentChar currentChar to be parsed
25647 	 * @param {Object} fields the fields that have been extracted so far
25648 	 * @param {Object} regionSettings settings used to parse the rest of the number
25649 	 */
25650 	start: function(number, currentChar, fields, regionSettings) {
25651 		// don't do anything except transition to the next state
25652 		return {
25653 			number: number
25654 		};
25655 	},
25656 	/**
25657 	 * @private
25658 	 * @param {string} number phone number
25659 	 * @param {number} currentChar currentChar to be parsed
25660 	 * @param {Object} fields the fields that have been extracted so far
25661 	 * @param {Object} regionSettings settings used to parse the rest of the number
25662 	 */
25663 	local: function(number, currentChar, fields, regionSettings) {
25664 		// in open dialling plans, we can tell that this number is a local subscriber number because it
25665 		// starts with a digit that indicates as such
25666 		this.processSubscriberNumber(number, fields, regionSettings);
25667 		return {
25668 			number: ""
25669 		};
25670 	}
25671 };
25672 
25673 // context-sensitive handler
25674 /**
25675  * @class
25676  * @private
25677  * @extends PhoneHandler
25678  * @constructor
25679  */
25680 var CSStateHandler = function () {
25681 	return this;
25682 };
25683 
25684 CSStateHandler.prototype = new PhoneHandler();
25685 CSStateHandler.prototype.special = function (number, currentChar, fields, regionSettings) {
25686 	var ret;
25687 	
25688 	// found a special area code that is both a node and a leaf. In
25689 	// this state, we have found the leaf, so chop off the end 
25690 	// character to make it a leaf.
25691 	if (number.charAt(0) === "0") {
25692 		fields.trunkAccess = number.charAt(0);
25693 		fields.areaCode = number.substring(1, currentChar);
25694 	} else {
25695 		fields.areaCode = number.substring(0, currentChar);
25696 	}
25697 	this.processSubscriberNumber(number.substring(currentChar), fields, regionSettings);
25698 	
25699 	ret = {
25700 		number: ""
25701 	};
25702 	
25703 	return ret;
25704 };
25705 
25706 /**
25707  * @class
25708  * @private
25709  * @extends PhoneHandler
25710  * @constructor
25711  */
25712 var USStateHandler = function () {
25713 	return this;
25714 };
25715 
25716 USStateHandler.prototype = new PhoneHandler();
25717 USStateHandler.prototype.vsc = function (number, currentChar, fields, regionSettings) {
25718 	var ret;
25719 
25720 	// found a VSC code (ie. a "star code")
25721 	fields.vsc = number;
25722 
25723 	// treat the rest of the number as if it were a completely new number
25724 	ret = {
25725 		number: ""
25726 	};
25727 
25728 	return ret;
25729 };
25730 
25731 /**
25732  * Creates a phone handler instance that is correct for the locale. Phone handlers are
25733  * used to handle parsing of the various fields in a phone number.
25734  * 
25735  * @protected
25736  * @static
25737  * @return {PhoneHandler} the correct phone handler for the locale
25738  */
25739 var PhoneHandlerFactory = function (locale, plan) {
25740 	if (plan.getContextFree() !== undefined && typeof(plan.getContextFree()) === 'boolean' && plan.getContextFree() === false) {
25741 		return new CSStateHandler();
25742 	}
25743 	var region = (locale && locale.getRegion()) || "ZZ";
25744 	switch (region) {
25745 	case 'US':
25746 		return new USStateHandler();
25747 		break;
25748 	default:
25749 		return new PhoneHandler();
25750 	}
25751 };
25752 
25753 
25754 /*< PhoneNumber.js */
25755 /*
25756  * phonenum.js - Represent a phone number.
25757  * 
25758  * Copyright © 2014-2015, JEDLSoft
25759  *
25760  * Licensed under the Apache License, Version 2.0 (the "License");
25761  * you may not use this file except in compliance with the License.
25762  * You may obtain a copy of the License at
25763  *
25764  *     http://www.apache.org/licenses/LICENSE-2.0
25765  *
25766  * Unless required by applicable law or agreed to in writing, software
25767  * distributed under the License is distributed on an "AS IS" BASIS,
25768  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25769  *
25770  * See the License for the specific language governing permissions and
25771  * limitations under the License.
25772  */
25773 
25774 /*
25775 !depends 
25776 ilib.js
25777 NumberingPlan.js
25778 PhoneLocale.js
25779 PhoneHandlerFactory.js
25780 Utils.js
25781 JSUtils.js
25782 */
25783 
25784 // !data states idd mnc
25785 
25786 
25787 /**
25788  * @class
25789  * Create a new phone number instance that parses the phone number parameter for its 
25790  * constituent parts, and store them as separate fields in the returned object.
25791  * 
25792  * The options object may include any of these properties:
25793  * 
25794  * <ul>
25795  * <li><i>locale</i> The locale with which to parse the number. This gives a clue as to which
25796  * numbering plan to use.
25797  * <li><i>mcc</i> The mobile carrier code (MCC) associated with the carrier that the phone is 
25798  * currently connected to, if known. This also can give a clue as to which numbering plan to
25799  * use
25800  * <li>onLoad - a callback function to call when this instance is fully 
25801  * loaded. When the onLoad option is given, this class will attempt to
25802  * load any missing locale data using the ilib loader callback.
25803  * When the constructor is done (even if the data is already preassembled), the 
25804  * onLoad function is called with the current instance as a parameter, so this
25805  * callback can be used with preassembled or dynamic loading or a mix of the two.
25806  * <li>sync - tell whether to load any missing locale data synchronously or 
25807  * asynchronously. If this option is given as "false", then the "onLoad"
25808  * callback must be given, as the instance returned from this constructor will
25809  * not be usable for a while.
25810  * <li><i>loadParams</i> - an object containing parameters to pass to the 
25811  * loader callback function when locale data is missing. The parameters are not
25812  * interpretted or modified in any way. They are simply passed along. The object 
25813  * may contain any property/value pairs as long as the calling code is in
25814  * agreement with the loader callback function as to what those parameters mean.
25815  * </ul>
25816  * 
25817  * This function is locale-sensitive, and will assume any number passed to it is
25818  * appropriate for the given locale. If the MCC is given, this method will assume
25819  * that numbers without an explicit country code have been dialled within the country
25820  * given by the MCC. This affects how things like area codes are parsed. If the MCC
25821  * is not given, this method will use the given locale to determine the country
25822  * code. If the locale is not explicitly given either, then this function uses the 
25823  * region of current locale as the default.<p>
25824  * 
25825  * The input number may contain any formatting characters for the given locale. Each 
25826  * field that is returned in the json object is a simple string of digits with
25827  * all formatting and whitespace characters removed.<p>
25828  * 
25829  * The number is decomposed into its parts, regardless if the number
25830  * contains formatting characters. If a particular part cannot be extracted from given 
25831  * number, the field will not be returned as a field in the object. If no fields can be
25832  * extracted from the number at all, then all digits found in the string will be 
25833  * returned in the subscriberNumber field. If the number parameter contains no 
25834  * digits, an empty object is returned.<p>
25835  * 
25836  * This instance can contain any of the following fields after parsing is done:
25837  * 
25838  * <ul>
25839  * <li>vsc - if this number starts with a VSC (Vertical Service Code, or "star code"), this field will contain the star and the code together
25840  * <li>iddPrefix - the prefix for international direct dialing. This can either be in the form of a plus character or the IDD access code for the given locale
25841  * <li>countryCode - if this number is an international direct dial number, this is the country code
25842  * <li>cic - for "dial-around" services (access to other carriers), this is the prefix used as the carrier identification code
25843  * <li>emergency - an emergency services number
25844  * <li>mobilePrefix - prefix that introduces a mobile phone number
25845  * <li>trunkAccess - trunk access code (long-distance access)
25846  * <li>serviceCode - like a geographic area code, but it is a required prefix for various services
25847  * <li>areaCode - geographic area codes
25848  * <li>subscriberNumber - the unique number of the person or company that pays for this phone line
25849  * <li>extension - in some countries, extensions are dialed directly without going through an operator or a voice prompt system. If the number includes an extension, it is given in this field.
25850  * <li>invalid - this property is added and set to true if the parser found that the number is invalid in the numbering plan for the country. This method will make its best effort at parsing, but any digits after the error will go into the subscriberNumber field
25851  * </ul>
25852  * 
25853  * The following rules determine how the number is parsed:
25854  * 
25855  * <ol>
25856  * <li>If the number starts with a character that is alphabetic instead of numeric, do
25857  * not parse the number at all. There is a good chance that it is not really a phone number.
25858  * In this case, an empty instance will be returned.
25859  * <li>If the phone number uses the plus notation or explicitly uses the international direct
25860  * dialing prefix for the given locale, then the country code is identified in 
25861  * the number. The rules of given locale are used to parse the IDD prefix, and then the rules
25862  * of the country in the prefix are used to parse the rest of the number.
25863  * <li>If a country code is provided as an argument to the function call, use that country's
25864  * parsing rules for the number. This is intended for programs like a Contacts application that 
25865  * know what the country is of the person that owns the phone number and can pass that on as 
25866  * a hint.
25867  * <li>If the appropriate locale cannot be easily determined, default to using the rules 
25868  * for the current user's region.
25869  * </ol>
25870  * 
25871  * Example: parsing the number "+49 02101345345-78" will give the following properties in the
25872  * resulting phone number instance:
25873  * 
25874  * <pre>
25875  *      {
25876  *        iddPrefix: "+",
25877  *        countryCode: "49",
25878  *        areaCode: "02101",
25879  *        subscriberNumber: "345345",
25880  *        extension: "78"
25881  *      }
25882  * </pre>
25883  *  
25884  * Note that in this example, because international direct dialing is explicitly used 
25885  * in the number, the part of this number after the IDD prefix and country code will be 
25886  * parsed exactly the same way in all locales with German rules (country code 49).
25887  *  
25888  * Regions currently supported are:
25889  *  
25890  * <ul>
25891  * <li>NANP (North American Numbering Plan) countries - USA, Canada, Bermuda, various Caribbean nations
25892  * <li>UK
25893  * <li>Republic of Ireland
25894  * <li>Germany
25895  * <li>France
25896  * <li>Spain
25897  * <li>Italy
25898  * <li>Mexico
25899  * <li>India
25900  * <li>People's Republic of China
25901  * <li>Netherlands
25902  * <li>Belgium
25903  * <li>Luxembourg
25904  * <li>Australia
25905  * <li>New Zealand
25906  * <li>Singapore
25907  * <li>Korea
25908  * <li>Japan
25909  * <li>Russia
25910  * <li>Brazil
25911  * </ul>
25912  * 
25913  * @constructor
25914  * @param {!string|PhoneNumber} number A free-form phone number to be parsed, or another phone
25915  * number instance to copy
25916  * @param {Object=} options options that guide the parser in parsing the number
25917  */
25918 var PhoneNumber = function(number, options) {
25919 	var stateData,
25920 		regionSettings;
25921 
25922 	this.sync = true;
25923 	this.loadParams = {};
25924 	
25925 	if (!number || (typeof number === "string" && number.length === 0)) {
25926 		return this;
25927 	}
25928 
25929 	if (options) {
25930 		if (typeof(options.sync) === 'boolean') {
25931 			this.sync = options.sync;
25932 		}
25933 
25934 		if (options.loadParams) {
25935 			this.loadParams = options.loadParams;
25936 		}
25937 
25938 		if (typeof(options.onLoad) === 'function') {
25939 			/** 
25940 			 * @private
25941 			 * @type {function(PhoneNumber)} 
25942 			 */
25943 			this.onLoad = options.onLoad;
25944 		}
25945 	}
25946 
25947 	if (typeof number === "object") {
25948 		/**
25949 		 * The vertical service code. These are codes that typically
25950 		 * start with a star or hash, like "*69" for "dial back the 
25951 		 * last number that called me".
25952 		 * @type {string|undefined} 
25953 		 */
25954 		this.vsc = number.vsc;
25955 
25956 		/**
25957 		 * The international direct dialing prefix. This is always
25958 		 * followed by the country code. 
25959 		 * @type {string} 
25960 		 */
25961 		this.iddPrefix = number.iddPrefix;
25962 		
25963 		/**
25964 		 * The unique IDD country code for the country where the
25965 		 * phone number is serviced. 
25966 		 * @type {string|undefined} 
25967 		 */
25968 		this.countryCode = number.countryCode;
25969 		
25970 		/**
25971 		 * The digits required to access the trunk. 
25972 		 * @type {string|undefined} 
25973 		 */
25974 		this.trunkAccess = number.trunkAccess;
25975 		
25976 		/**
25977 		 * The carrier identification code used to identify 
25978 		 * alternate long distance or international carriers. 
25979 		 * @type {string|undefined} 
25980 		 */
25981 		this.cic = number.cic;
25982 		
25983 		/**
25984 		 * Identifies an emergency number that is typically
25985 		 * short, such as "911" in North America or "112" in
25986 		 * many other places in the world. 
25987 		 * @type {string|undefined} 
25988 		 */
25989 		this.emergency = number.emergency;
25990 		
25991 		/**
25992 		 * The prefix of the subscriber number that indicates
25993 		 * that this is the number of a mobile phone. 
25994 		 * @type {string|undefined} 
25995 		 */
25996 		this.mobilePrefix = number.mobilePrefix;
25997 		
25998 		/**
25999 		 * The prefix that identifies this number as commercial
26000 		 * service number. 
26001 		 * @type {string|undefined} 
26002 		 */
26003 		this.serviceCode = number.serviceCode;
26004 		
26005 		/**
26006 		 * The area code prefix of a land line number. 
26007 		 * @type {string|undefined} 
26008 		 */
26009 		this.areaCode = number.areaCode;
26010 		
26011 		/**
26012 		 * The unique number associated with the subscriber
26013 		 * of this phone. 
26014 		 * @type {string|undefined} 
26015 		 */
26016 		this.subscriberNumber = number.subscriberNumber;
26017 		
26018 		/**
26019 		 * The direct dial extension number. 
26020 		 * @type {string|undefined} 
26021 		 */
26022 		this.extension = number.extension;
26023 		
26024 		/**
26025 		 * @private
26026 		 * @type {boolean} 
26027 		 */
26028 		this.invalid = number.invalid;
26029 
26030 		if (number.plan && number.locale) {
26031 			/** 
26032 			 * @private
26033 			 * @type {NumberingPlan} 
26034 			 */
26035 			this.plan = number.plan;
26036 			
26037 			/** 
26038 			 * @private
26039 			 * @type {PhoneLocale} 
26040 			 */
26041 			this.locale = number.locale;
26042 	
26043 			/** 
26044 			 * @private
26045 			 * @type {NumberingPlan} 
26046 			 */
26047 			this.destinationPlan = number.destinationPlan;
26048 			
26049 			/** 
26050 			 * @private
26051 			 * @type {PhoneLocale} 
26052 			 */
26053 			this.destinationLocale = number.destinationLocale;
26054 	
26055 			if (options && typeof(options.onLoad) === 'function') {
26056 				options.onLoad(this);
26057 			}
26058 			return;
26059 		}
26060 	}
26061 
26062 	new PhoneLocale({
26063 		locale: options && options.locale,
26064 		mcc: options && options.mcc,
26065 		sync: this.sync,
26066 		loadParams: this.loadParams,
26067 		onLoad: ilib.bind(this, function(loc) {
26068 			this.locale = this.destinationLocale = loc;
26069 			new NumberingPlan({
26070 				locale: this.locale,
26071 				sync: this.sync,
26072 				loadParms: this.loadParams,
26073 				onLoad: ilib.bind(this, function (plan) {
26074 					this.plan = this.destinationPlan = plan;
26075 			
26076 					if (typeof number === "object") {
26077 						// the copy constructor code above did not find the locale 
26078 						// or plan before, but now they are loaded, so we can return 
26079 						// already without going further
26080 						return;
26081 					}
26082 					Utils.loadData({
26083 						name: "states.json",
26084 						object: PhoneNumber,
26085 						locale: this.locale,
26086 						sync: this.sync,
26087 						loadParams: JSUtils.merge(this.loadParams, {
26088 							returnOne: true
26089 						}),
26090 						callback: ilib.bind(this, function (stdata) {
26091 							if (!stdata) {
26092 								stdata = PhoneNumber._defaultStates;
26093 							}
26094 		
26095 							stateData = stdata;
26096 
26097 							regionSettings = {
26098 								stateData: stateData,
26099 								plan: plan,
26100 								handler: PhoneHandlerFactory(this.locale, plan)
26101 							};
26102 							
26103 							// use ^ to indicate the beginning of the number, because certain things only match at the beginning
26104 							number = "^" + number.replace(/\^/g, '');
26105 							number = PhoneNumber._stripFormatting(number);
26106 
26107 							this._parseNumber(number, regionSettings, options);
26108 						})
26109 					});
26110 				})
26111 			});
26112 		})
26113 	});
26114 };
26115 
26116 /**
26117  * Parse an International Mobile Subscriber Identity (IMSI) number into its 3 constituent parts:
26118  * 
26119  * <ol>
26120  * <li>mcc - Mobile Country Code, which identifies the country where the phone is currently receiving 
26121  * service.
26122  * <li>mnc - Mobile Network Code, which identifies the carrier which is currently providing service to the phone 
26123  * <li>msin - Mobile Subscription Identifier Number. This is a unique number identifying the mobile phone on 
26124  * the network, which usually maps to an account/subscriber in the carrier's database.
26125  * </ol>
26126  * 
26127  * Because this function may need to load data to identify the above parts, you can pass an options
26128  * object that controls how the data is loaded. The options may contain any of the following properties:
26129  *
26130  * <ul>
26131  * <li>onLoad - a callback function to call when the parsing is done. When the onLoad option is given, 
26132  * this method will attempt to load the locale data using the ilib loader callback. When it is done
26133  * (even if the data is already preassembled), the onLoad function is called with the parsing results
26134  * as a parameter, so this callback can be used with preassembled or dynamic, synchronous or 
26135  * asynchronous loading or a mix of the above.
26136  * <li>sync - tell whether to load any missing locale data synchronously or asynchronously. If this 
26137  * option is given as "false", then the "onLoad" callback must be given, as the results returned from 
26138  * this constructor will not be usable for a while.
26139  * <li><i>loadParams</i> - an object containing parameters to pass to the loader callback function 
26140  * when locale data is missing. The parameters are not interpretted or modified in any way. They are 
26141  * simply passed along. The object may contain any property/value pairs as long as the calling code is in
26142  * agreement with the loader callback function as to what those parameters mean.
26143  * </ul>
26144  *
26145  * @static
26146  * @param {string} imsi IMSI number to parse
26147  * @param {Object} options options controlling the loading of the locale data
26148  * @return {{mcc:string,mnc:string,msin:string}|undefined} components of the IMSI number, when the locale data
26149  * is loaded synchronously, or undefined if asynchronous
26150  */
26151 PhoneNumber.parseImsi = function(imsi, options) {
26152 	var sync = true,
26153 		loadParams = {},
26154 		fields = {};
26155 	
26156 	if (!imsi) {
26157 		return undefined;
26158 	}
26159 
26160 	if (options) {
26161 		if (typeof(options.sync) !== 'undefined') {
26162 			sync = (options.sync == true);
26163 		}
26164 		
26165 		if (options.loadParams) {
26166 			loadParams = options.loadParams;
26167 		}
26168 	}	
26169 
26170 	if (ilib.data.mnc) {
26171 		fields = PhoneNumber._parseImsi(ilib.data.mnc, imsi);
26172 		
26173 		if (options && typeof(options.onLoad) === 'function') {
26174 			options.onLoad(fields);
26175 		}
26176 	} else {
26177 		Utils.loadData({
26178 			name: "mnc.json", 
26179 			object: PhoneNumber, 
26180 			nonlocale: true, 
26181 			sync: sync, 
26182 			loadParams: loadParams, 
26183 			callback: ilib.bind(this, function(data) {
26184 				ilib.data.mnc = data;
26185 				fields = PhoneNumber._parseImsi(data, imsi);
26186 				
26187 				if (options && typeof(options.onLoad) === 'function') {
26188 					options.onLoad(fields);
26189 				}
26190 			})
26191 		});
26192 	}
26193 	return fields;
26194 };
26195 
26196 
26197 /**
26198  * @static
26199  * @protected
26200  */
26201 PhoneNumber._parseImsi = function(data, imsi) {
26202 	var ch, 
26203 		i,
26204 		currentState, 
26205 		end, 
26206 		handlerMethod,
26207 		state = 0,
26208 		newState,
26209 		fields = {},
26210 		lastLeaf,
26211 		consumed = 0;
26212 	
26213 	currentState = data;
26214 	if (!currentState) {
26215 		// can't parse anything
26216 		return undefined;
26217 	}
26218 	
26219 	i = 0;
26220 	while (i < imsi.length) {
26221 		ch = PhoneNumber._getCharacterCode(imsi.charAt(i));
26222 		// console.info("parsing char " + imsi.charAt(i) + " code: " + ch);
26223 		if (ch >= 0) {
26224 			newState = currentState.s && currentState.s[ch];
26225 			
26226 			if (typeof(newState) === 'object') {
26227 				if (typeof(newState.l) !== 'undefined') {
26228 					// save for latter if needed
26229 					lastLeaf = newState;
26230 					consumed = i;
26231 				}
26232 				// console.info("recognized digit " + ch + " continuing...");
26233 				// recognized digit, so continue parsing
26234 				currentState = newState;
26235 				i++;
26236 			} else {
26237 				if ((typeof(newState) === 'undefined' || newState === 0 ||
26238 					(typeof(newState) === 'object' && typeof(newState.l) === 'undefined')) &&
26239 					 lastLeaf) {
26240 					// this is possibly a look-ahead and it didn't work... 
26241 					// so fall back to the last leaf and use that as the
26242 					// final state
26243 					newState = lastLeaf;
26244 					i = consumed;
26245 				}
26246 				
26247 				if ((typeof(newState) === 'number' && newState) ||
26248 					(typeof(newState) === 'object' && typeof(newState.l) !== 'undefined')) {
26249 					// final state
26250 					var stateNumber = typeof(newState) === 'number' ? newState : newState.l;
26251 					handlerMethod = PhoneNumber._states[stateNumber];
26252 
26253 					// console.info("reached final state " + newState + " handler method is " + handlerMethod + " and i is " + i);
26254 	
26255 					// deal with syntactic ambiguity by using the "special" end state instead of "area"
26256 					if ( handlerMethod === "area" ) {
26257 						end = i+1;
26258 					} else {
26259 						// unrecognized imsi, so just assume the mnc is 3 digits
26260 						end = 6;
26261 					}
26262 					
26263 					fields.mcc = imsi.substring(0,3);
26264 					fields.mnc = imsi.substring(3,end);
26265 					fields.msin = imsi.substring(end);
26266 	
26267 					return fields;
26268 				} else {
26269 					// parse error
26270 					if (imsi.length >= 6) {
26271 						fields.mcc = imsi.substring(0,3);
26272 						fields.mnc = imsi.substring(3,6);
26273 						fields.msin = imsi.substring(6);
26274 					}
26275 					return fields;
26276 				}
26277 			}
26278 		} else if (ch === -1) {
26279 			// non-transition character, continue parsing in the same state
26280 			i++;
26281 		} else {
26282 			// should not happen
26283 			// console.info("skipping character " + ch);
26284 			// not a digit, plus, pound, or star, so this is probably a formatting char. Skip it.
26285 			i++;
26286 		}
26287 	}
26288 		
26289 	if (i >= imsi.length && imsi.length >= 6) {
26290 		// we reached the end of the imsi, but did not finish recognizing anything. 
26291 		// Default to last resort and assume 3 digit mnc
26292 		fields.mcc = imsi.substring(0,3);
26293 		fields.mnc = imsi.substring(3,6);
26294 		fields.msin = imsi.substring(6);
26295 	} else {
26296 		// unknown or not enough characters for a real imsi 
26297 		fields = undefined;
26298 	}
26299 		
26300 	// console.info("Globalization.Phone.parseImsi: final result is: " + JSON.stringify(fields));
26301 	return fields;
26302 };
26303 
26304 /**
26305  * @static
26306  * @private
26307  */
26308 PhoneNumber._stripFormatting = function(str) {
26309 	var ret = "";
26310 	var i;
26311 
26312 	for (i = 0; i < str.length; i++) {
26313 		if (PhoneNumber._getCharacterCode(str.charAt(i)) >= -1) {
26314 			ret += str.charAt(i);
26315 		}
26316 	}
26317 	return ret;
26318 };
26319 
26320 /**
26321  * @static
26322  * @protected
26323  */
26324 PhoneNumber._getCharacterCode = function(ch) {
26325 	if (ch >= '0' && ch <= '9') {
26326 			return ch - '0';
26327 		}
26328 	switch (ch) {
26329 	case '+':
26330 		return 10;
26331 	case '*':
26332 		return 11;
26333 	case '#':
26334 		return 12;
26335 	case '^':
26336 		return 13;
26337 	case 'p':		// pause chars
26338 	case 'P':
26339 	case 't':
26340 	case 'T':
26341 	case 'w':
26342 	case 'W':
26343 		return -1;
26344 	case 'x':
26345 	case 'X':
26346 	case ',':
26347 	case ';':		// extension char
26348 		return -1;
26349 	}
26350 	return -2;
26351 };
26352 
26353 /**
26354  * @private
26355  */
26356 PhoneNumber._states = [
26357 	"none",
26358 	"unknown",
26359 	"plus",
26360 	"idd",
26361 	"cic",
26362 	"service",
26363 	"cell",
26364 	"area",
26365 	"vsc",
26366 	"country",
26367 	"personal",
26368 	"special",
26369 	"trunk",
26370 	"premium",
26371 	"emergency",
26372 	"service2",
26373 	"service3",
26374 	"service4",
26375 	"cic2",
26376 	"cic3",
26377 	"start",
26378 	"local"
26379 ];
26380 
26381 /**
26382  * @private
26383  */
26384 PhoneNumber._fieldOrder = [
26385 	"vsc",
26386 	"iddPrefix",
26387 	"countryCode",
26388 	"trunkAccess",
26389 	"cic",
26390 	"emergency",
26391 	"mobilePrefix",
26392 	"serviceCode",
26393 	"areaCode",
26394 	"subscriberNumber",
26395 	"extension"
26396 ];
26397 
26398 PhoneNumber._defaultStates = {
26399 	"s": [
26400         0,
26401 		21,  // 1 -> local
26402         21,  // 2 -> local
26403         21,  // 3 -> local
26404         21,  // 4 -> local
26405         21,  // 5 -> local
26406         21,  // 6 -> local
26407         21,  // 7 -> local
26408         21,  // 8 -> local
26409         21,  // 9 -> local
26410         0,0,0,
26411 	    { // ^
26412 	    	"s": [
26413 				{ // ^0
26414 					"s": [3], // ^00 -> idd
26415 					"l": 12   // ^0  -> trunk
26416 				},
26417 				21,  // ^1 -> local
26418 	            21,  // ^2 -> local
26419 	            21,  // ^3 -> local
26420 	            21,  // ^4 -> local
26421 	            21,  // ^5 -> local
26422 	            21,  // ^6 -> local
26423 	            21,  // ^7 -> local
26424 	            21,  // ^8 -> local
26425 	            21,  // ^9 -> local
26426 	            2    // ^+ -> plus
26427 	        ]
26428 	    }
26429 	]
26430 };
26431 
26432 PhoneNumber.prototype = {
26433 	/**
26434 	 * @protected
26435 	 * @param {string} number
26436 	 * @param {Object} regionData
26437 	 * @param {Object} options
26438 	 * @param {string} countryCode
26439 	 */
26440 	_parseOtherCountry: function(number, regionData, options, countryCode) {
26441 		new PhoneLocale({
26442 			locale: this.locale,
26443 			countryCode: countryCode,
26444 			sync: this.sync,
26445 			loadParms: this.loadParams,
26446 			onLoad: ilib.bind(this, function (loc) {
26447 				/*
26448 				 * this.locale is the locale where this number is being parsed,
26449 				 * and is used to parse the IDD prefix, if any, and this.destinationLocale is 
26450 				 * the locale of the rest of this number after the IDD prefix.
26451 				 */
26452 				/** @type {PhoneLocale} */
26453 				this.destinationLocale = loc;
26454 
26455 				Utils.loadData({
26456 					name: "states.json",
26457 					object: PhoneNumber,
26458 					locale: this.destinationLocale,
26459 					sync: this.sync,
26460 					loadParams: JSUtils.merge(this.loadParams, {
26461 						returnOne: true
26462 					}),
26463 					callback: ilib.bind(this, function (stateData) {
26464 						if (!stateData) {
26465 							stateData = PhoneNumber._defaultStates;
26466 						}
26467 						
26468 						new NumberingPlan({
26469 							locale: this.destinationLocale,
26470 							sync: this.sync,
26471 							loadParms: this.loadParams,
26472 							onLoad: ilib.bind(this, function (plan) {
26473 								/*
26474 								 * this.plan is the plan where this number is being parsed,
26475 								 * and is used to parse the IDD prefix, if any, and this.destinationPlan is 
26476 								 * the plan of the rest of this number after the IDD prefix in the 
26477 								 * destination locale.
26478 								 */
26479 								/** @type {NumberingPlan} */
26480 								this.destinationPlan = plan;
26481 
26482 								var regionSettings = {
26483 									stateData: stateData,
26484 									plan: plan,
26485 									handler: PhoneHandlerFactory(this.destinationLocale, plan)
26486 								};
26487 								
26488 								// for plans that do not skip the trunk code when dialing from
26489 								// abroad, we need to treat the number from here on in as if it 
26490 								// were parsing a local number from scratch. That way, the parser
26491 								// does not get confused between parts of the number at the
26492 								// beginning of the number, and parts in the middle.
26493 								if (!plan.getSkipTrunk()) {
26494 									number = '^' + number;
26495 								}
26496 									
26497 								// recursively call the parser with the new states data
26498 								// to finish the parsing
26499 								this._parseNumber(number, regionSettings, options);
26500 							})
26501 						});
26502 					})
26503 				});
26504 			})
26505 		});
26506 	},
26507 	
26508 	/**
26509 	 * @protected
26510 	 * @param {string} number
26511 	 * @param {Object} regionData
26512 	 * @param {Object} options
26513 	 */
26514 	_parseNumber: function(number, regionData, options) {
26515 		var i, ch,
26516 			regionSettings,
26517 			newState,
26518 			dot,
26519 			handlerMethod,
26520 			result,
26521 			currentState = regionData.stateData,
26522 			lastLeaf = undefined,
26523 			consumed = 0;
26524 
26525 		regionSettings = regionData;
26526 		dot = 14; // special transition which matches all characters. See AreaCodeTableMaker.java
26527 
26528 		i = 0;
26529 		while (i < number.length) {
26530 			ch = PhoneNumber._getCharacterCode(number.charAt(i));
26531 			if (ch >= 0) {
26532 				// newState = stateData.states[state][ch];
26533 				newState = currentState.s && currentState.s[ch];
26534 				
26535 				if (!newState && currentState.s && currentState.s[dot]) {
26536 					newState = currentState.s[dot];
26537 				}
26538 				
26539 				if (typeof(newState) === 'object' && i+1 < number.length) {
26540 					if (typeof(newState.l) !== 'undefined') {
26541 						// this is a leaf node, so save that for later if needed
26542 						lastLeaf = newState;
26543 						consumed = i;
26544 					}
26545 					// console.info("recognized digit " + ch + " continuing...");
26546 					// recognized digit, so continue parsing
26547 					currentState = newState;
26548 					i++;
26549 				} else {
26550 					if ((typeof(newState) === 'undefined' || newState === 0 ||
26551 						(typeof(newState) === 'object' && typeof(newState.l) === 'undefined')) &&
26552 						 lastLeaf) {
26553 						// this is possibly a look-ahead and it didn't work... 
26554 						// so fall back to the last leaf and use that as the
26555 						// final state
26556 						newState = lastLeaf;
26557 						i = consumed;
26558 						consumed = 0;
26559 						lastLeaf = undefined;
26560 					}
26561 					
26562 					if ((typeof(newState) === 'number' && newState) ||
26563 						(typeof(newState) === 'object' && typeof(newState.l) !== 'undefined')) {
26564 						// final state
26565 						var stateNumber = typeof(newState) === 'number' ? newState : newState.l;
26566 						handlerMethod = PhoneNumber._states[stateNumber];
26567 						
26568 						if (number.charAt(0) === '^') {
26569 							result = regionSettings.handler[handlerMethod](number.slice(1), i-1, this, regionSettings);
26570 						} else {
26571 							result = regionSettings.handler[handlerMethod](number, i, this, regionSettings);
26572 						}
26573 		
26574 						// reparse whatever is left
26575 						number = result.number;
26576 						i = consumed = 0;
26577 						lastLeaf = undefined;
26578 						
26579 						//console.log("reparsing with new number: " +  number);
26580 						currentState = regionSettings.stateData;
26581 						// if the handler requested a special sub-table, use it for this round of parsing,
26582 						// otherwise, set it back to the regular table to continue parsing
26583 	
26584 						if (result.countryCode !== undefined) {
26585 							this._parseOtherCountry(number, regionData, options, result.countryCode);
26586 							// don't process any further -- let the work be done in the onLoad callbacks
26587 							return;
26588 						} else if (result.table !== undefined) {
26589 							Utils.loadData({
26590 								name: result.table + ".json",
26591 								object: PhoneNumber,
26592 								nonlocale: true,
26593 								sync: this.sync,
26594 								loadParams: this.loadParams,
26595 								callback: ilib.bind(this, function (data) {
26596 									if (!data) {
26597 										data = PhoneNumber._defaultStates;
26598 									}
26599 	
26600 									regionSettings = {
26601 										stateData: data,
26602 										plan: regionSettings.plan,
26603 										handler: regionSettings.handler
26604 									};
26605 									
26606 									// recursively call the parser with the new states data
26607 									// to finish the parsing
26608 									this._parseNumber(number, regionSettings, options);
26609 								})
26610 							});
26611 							// don't process any further -- let the work be done in the onLoad callbacks
26612 							return;
26613 						} else if (result.skipTrunk !== undefined) {
26614 							ch = PhoneNumber._getCharacterCode(regionSettings.plan.getTrunkCode());
26615 							currentState = currentState.s && currentState.s[ch];
26616 						}
26617 					} else {
26618 						handlerMethod = (typeof(newState) === 'number') ? "none" : "local";
26619 						// failed parse. Either no last leaf to fall back to, or there was an explicit
26620 						// zero in the table. Put everything else in the subscriberNumber field as the
26621 						// default place
26622 						if (number.charAt(0) === '^') {
26623 							result = regionSettings.handler[handlerMethod](number.slice(1), i-1, this, regionSettings);
26624 						} else {
26625 							result = regionSettings.handler[handlerMethod](number, i, this, regionSettings);
26626 						}
26627 						break;
26628 					}
26629 				}
26630 			} else if (ch === -1) {
26631 				// non-transition character, continue parsing in the same state
26632 				i++;
26633 			} else {
26634 				// should not happen
26635 				// console.info("skipping character " + ch);
26636 				// not a digit, plus, pound, or star, so this is probably a formatting char. Skip it.
26637 				i++;
26638 			}
26639 		}
26640 		if (i >= number.length && currentState !== regionData.stateData) {
26641 			handlerMethod = (typeof(currentState.l) === 'undefined' || currentState === 0) ? "none" : "local";
26642 			// we reached the end of the phone number, but did not finish recognizing anything. 
26643 			// Default to last resort and put everything that is left into the subscriber number
26644 			//console.log("Reached end of number before parsing was complete. Using handler for method none.")
26645 			if (number.charAt(0) === '^') {
26646 				result = regionSettings.handler[handlerMethod](number.slice(1), i-1, this, regionSettings);
26647 			} else {
26648 				result = regionSettings.handler[handlerMethod](number, i, this, regionSettings);
26649 			}
26650 		}
26651 
26652 		// let the caller know we are done parsing
26653 		if (this.onLoad) {
26654 			this.onLoad(this);
26655 		}
26656 	},
26657 	/**
26658 	 * @protected
26659 	 */
26660 	_getPrefix: function() {
26661 		return this.areaCode || this.serviceCode || this.mobilePrefix || "";
26662 	},
26663 	
26664 	/**
26665 	 * @protected
26666 	 */
26667 	_hasPrefix: function() {
26668 		return (this._getPrefix() !== "");
26669 	},
26670 	
26671 	/**
26672 	 * Exclusive or -- return true, if one is defined and the other isn't
26673 	 * @protected
26674 	 */
26675 	_xor : function(left, right) {
26676 		if ((left === undefined && right === undefined ) || (left !== undefined && right !== undefined)) {
26677 			return false;
26678 		} else {
26679 			return true;
26680 		}
26681 	},
26682 	
26683 	/**
26684 	 * return a version of the phone number that contains only the dialable digits in the correct order 
26685 	 * @protected
26686 	 */
26687 	_join: function () {
26688 		var fieldName, formatted = "";
26689 		
26690 		try {
26691 			for (var field in PhoneNumber._fieldOrder) {
26692 				if (typeof field === 'string' && typeof PhoneNumber._fieldOrder[field] === 'string') {
26693 					fieldName = PhoneNumber._fieldOrder[field];
26694 					// console.info("normalize: formatting field " + fieldName);
26695 					if (this[fieldName] !== undefined) {
26696 						formatted += this[fieldName];
26697 					}
26698 				}
26699 			}
26700 		} catch ( e ) {
26701 			//console.warn("caught exception in _join: " + e);
26702 			throw e;
26703 		}
26704 		return formatted;
26705 	},
26706 
26707 	/**
26708 	 * This routine will compare the two phone numbers in an locale-sensitive
26709 	 * manner to see if they possibly reference the same phone number.<p>
26710 	 * 
26711 	 * In many places,
26712 	 * there are multiple ways to reach the same phone number. In North America for 
26713 	 * example, you might have a number with the trunk access code of "1" and another
26714 	 * without, and they reference the exact same phone number. This is considered a
26715 	 * strong match. For a different pair of numbers, one may be a local number and
26716 	 * the other a full phone number with area code, which may reference the same 
26717 	 * phone number if the local number happens to be located in that area code. 
26718 	 * However, you cannot say for sure if it is in that area code, so it will 
26719 	 * be considered a somewhat weaker match.<p>
26720 	 *  
26721 	 * Similarly, in other countries, there are sometimes different ways of 
26722 	 * reaching the same destination, and the way that numbers
26723 	 * match depends on the locale.<p>
26724 	 * 
26725 	 * The various phone number fields are handled differently for matches. There
26726 	 * are various fields that do not need to match at all. For example, you may
26727 	 * type equally enter "00" or "+" into your phone to start international direct
26728 	 * dialling, so the iddPrefix field does not need to match at all.<p> 
26729 	 * 
26730 	 * Typically, fields that require matches need to match exactly if both sides have a value 
26731 	 * for that field. If both sides specify a value and those values differ, that is
26732 	 * a strong non-match. If one side does not have a value and the other does, that 
26733 	 * causes a partial match, because the number with the missing field may possibly
26734 	 * have an implied value that matches the other number. For example, the numbers
26735 	 * "650-555-1234" and "555-1234" have a partial match as the local number "555-1234"
26736 	 * might possibly have the same 650 area code as the first number, and might possibly
26737 	 * not. If both side do not specify a value for a particular field, that field is 
26738 	 * considered matching.<p>
26739 	 *  
26740 	 * The values of following fields are ignored when performing matches:
26741 	 * 
26742 	 * <ul>
26743 	 * <li>vsc
26744 	 * <li>iddPrefix
26745 	 * <li>cic
26746 	 * <li>trunkAccess
26747 	 * </ul>
26748 	 * 
26749 	 * The values of the following fields matter if they do not match:
26750 	 *   
26751 	 * <ul>
26752 	 * <li>countryCode - A difference causes a moderately strong problem except for 
26753 	 * certain countries where there is a way to access the same subscriber via IDD 
26754 	 * and via intranetwork dialling
26755 	 * <li>mobilePrefix - A difference causes a possible non-match
26756 	 * <li>serviceCode - A difference causes a possible non-match
26757 	 * <li>areaCode - A difference causes a possible non-match
26758 	 * <li>subscriberNumber - A difference causes a very strong non-match
26759 	 * <li>extension - A difference causes a minor non-match
26760 	 * </ul>
26761 	 *  
26762 	 * @param {string|PhoneNumber} other other phone number to compare this one to
26763 	 * @return {number} non-negative integer describing the percentage quality of the 
26764 	 * match. 100 means a very strong match (100%), and lower numbers are less and 
26765 	 * less strong, down to 0 meaning not at all a match. 
26766 	 */
26767 	compare: function (other) {
26768 		var match = 100,
26769 			FRdepartments = {"590":1, "594":1, "596":1, "262":1},
26770 			ITcountries = {"378":1, "379":1},
26771 			thisPrefix,
26772 			otherPrefix,
26773 			currentCountryCode = 0;
26774 
26775 		if (typeof this.locale.region === "string") {
26776 			currentCountryCode = this.locale._mapRegiontoCC(this.locale.region);
26777 		}
26778 		
26779 		// subscriber number must be present and must match
26780 		if (!this.subscriberNumber || !other.subscriberNumber || this.subscriberNumber !== other.subscriberNumber) {
26781 			return 0;
26782 		}
26783 
26784 		// extension must match if it is present
26785 		if (this._xor(this.extension, other.extension) || this.extension !== other.extension) {
26786 			return 0;
26787 		}
26788 
26789 		if (this._xor(this.countryCode, other.countryCode)) {
26790 			// if one doesn't have a country code, give it some demerit points, but if the
26791 			// one that has the country code has something other than the current country
26792 			// add even more. Ignore the special cases where you can dial the same number internationally or via 
26793 			// the local numbering system
26794 			switch (this.locale.getRegion()) {
26795 			case 'FR':
26796 				if (this.countryCode in FRdepartments || other.countryCode in FRdepartments) {
26797 					if (this.areaCode !== other.areaCode || this.mobilePrefix !== other.mobilePrefix) {
26798 						match -= 100;
26799 					}
26800 				} else {
26801 					match -= 16;
26802 				}
26803 				break;
26804 			case 'IT':
26805 				if (this.countryCode in ITcountries || other.countryCode in ITcountries) { 
26806 					if (this.areaCode !== other.areaCode) {
26807 						match -= 100;
26808 					}
26809 				} else {
26810 					match -= 16;
26811 				}
26812 				break;
26813 			default:
26814 				match -= 16;
26815 				if ((this.countryCode !== undefined && this.countryCode !== currentCountryCode) || 
26816 					(other.countryCode !== undefined && other.countryCode !== currentCountryCode)) {
26817 					match -= 16;
26818 				}
26819 			}
26820 		} else if (this.countryCode !== other.countryCode) {
26821 			// ignore the special cases where you can dial the same number internationally or via 
26822 			// the local numbering system
26823 			if (other.countryCode === '33' || this.countryCode === '33') {
26824 				// france
26825 				if (this.countryCode in FRdepartments || other.countryCode in FRdepartments) {
26826 					if (this.areaCode !== other.areaCode || this.mobilePrefix !== other.mobilePrefix) {
26827 						match -= 100;
26828 					}
26829 				} else {
26830 					match -= 100;
26831 				}
26832 			} else if (this.countryCode === '39' || other.countryCode === '39') {
26833 				// italy
26834 				if (this.countryCode in ITcountries || other.countryCode in ITcountries) { 
26835 					if (this.areaCode !== other.areaCode) {
26836 						match -= 100;
26837 					}
26838 				} else {
26839 					match -= 100;
26840 				}
26841 			} else {
26842 				match -= 100;
26843 			}
26844 		}
26845 
26846 		if (this._xor(this.serviceCode, other.serviceCode)) {
26847 			match -= 20;
26848 		} else if (this.serviceCode !== other.serviceCode) {
26849 			match -= 100;
26850 		}
26851 
26852 		if (this._xor(this.mobilePrefix, other.mobilePrefix)) {
26853 			match -= 20;
26854 		} else if (this.mobilePrefix !== other.mobilePrefix) {
26855 			match -= 100;
26856 		}
26857 
26858 		if (this._xor(this.areaCode, other.areaCode)) {
26859 			// one has an area code, the other doesn't, so dock some points. It could be a match if the local
26860 			// number in the one number has the same implied area code as the explicit area code in the other number.
26861 			match -= 12;
26862 		} else if (this.areaCode !== other.areaCode) {
26863 			match -= 100;
26864 		}
26865 
26866 		thisPrefix = this._getPrefix();
26867 		otherPrefix = other._getPrefix();
26868 		
26869 		if (thisPrefix && otherPrefix && thisPrefix !== otherPrefix) {
26870 			match -= 100;
26871 		}
26872 		
26873 		// make sure we are between 0 and 100
26874 		if (match < 0) {
26875 			match = 0;	
26876 		} else if (match > 100) {
26877 			match = 100;
26878 		}
26879 
26880 		return match;
26881 	},
26882 	
26883 	/**
26884 	 * Determine whether or not the other phone number is exactly equal to the current one.<p>
26885 	 *  
26886 	 * The difference between the compare method and the equals method is that the compare 
26887 	 * method compares normalized numbers with each other and returns the degree of match,
26888 	 * whereas the equals operator returns true iff the two numbers contain the same fields
26889 	 * and the fields are exactly the same. Functions and other non-phone number properties
26890 	 * are not compared.
26891 	 * @param {string|PhoneNumber} other another phone number to compare to this one
26892 	 * @return {boolean} true if the numbers are the same, false otherwise
26893 	 */
26894 	equals: function equals(other) {
26895 		if (other.locale && this.locale && !this.locale.equals(other.locale) && (!this.countryCode || !other.countryCode)) {
26896 			return false;
26897 		}
26898 		
26899 		for (var p in other) {
26900 			if (p !== undefined && this[p] !== undefined && typeof(this[p]) !== 'object') {
26901 				if (other[p] === undefined) {
26902 					/*console.error("PhoneNumber.equals: other is missing property " + p + " which has the value " + this[p] + " in this");
26903 					console.error("this is : " + JSON.stringify(this));
26904 					console.error("other is: " + JSON.stringify(other));*/
26905 					return false;
26906 				}
26907 				if (this[p] !== other[p]) {
26908 					/*console.error("PhoneNumber.equals: difference in property " + p);
26909 					console.error("this is : " + JSON.stringify(this));
26910 					console.error("other is: " + JSON.stringify(other));*/
26911 					return false;
26912 				}
26913 			}
26914 		}
26915 		for (var p in other) {
26916 			if (p !== undefined && other[p] !== undefined && typeof(other[p]) !== 'object') {
26917 				if (this[p] === undefined) {
26918 					/*console.error("PhoneNumber.equals: this is missing property " + p + " which has the value " + other[p] + " in the other");
26919 					console.error("this is : " + JSON.stringify(this));
26920 					console.error("other is: " + JSON.stringify(other));*/
26921 					return false;
26922 				}
26923 				if (this[p] !== other[p]) {
26924 					/*console.error("PhoneNumber.equals: difference in property " + p);
26925 					console.error("this is : " + JSON.stringify(this));
26926 					console.error("other is: " + JSON.stringify(other));*/
26927 					return false;
26928 				}
26929 			}
26930 		}
26931 		return true;
26932 	},
26933 	
26934 
26935 	/**
26936 	 * @private
26937 	 * @param {{
26938 	 *   mcc:string,
26939 	 *   defaultAreaCode:string,
26940 	 *   country:string,
26941 	 *   networkType:string,
26942 	 *   assistedDialing:boolean,
26943 	 *   sms:boolean,
26944 	 *   manualDialing:boolean
26945 	 * }} options an object containing options to help in normalizing. 
26946 	 * @param {PhoneNumber} norm
26947 	 * @param {PhoneLocale} homeLocale
26948 	 * @param {PhoneLocale} currentLocale
26949 	 * @param {NumberingPlan} currentPlan
26950 	 * @param {PhoneLocale} destinationLocale
26951 	 * @param {NumberingPlan} destinationPlan
26952 	 * @param {boolean} sync
26953 	 * @param {Object|undefined} loadParams
26954 	 */
26955 	_doNormalize: function(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams) {
26956 		var formatted = "";
26957 		
26958 		if (!norm.invalid && options && options.assistedDialing) {
26959 			// don't normalize things that don't have subscriber numbers. Also, don't normalize
26960 			// manually dialed local numbers. Do normalize local numbers in contact entries.
26961 			if (norm.subscriberNumber && 
26962 					(!options.manualDialing ||
26963 					 norm.iddPrefix ||
26964 					 norm.countryCode ||
26965 					 norm.trunkAccess)) {
26966 				// console.log("normalize: assisted dialling normalization of " + JSON.stringify(norm));
26967 				if (currentLocale.getRegion() !== destinationLocale.getRegion()) {
26968 					// we are currently calling internationally
26969 					if (!norm._hasPrefix() && 
26970 							options.defaultAreaCode && 
26971 							destinationLocale.getRegion() === homeLocale.getRegion() &&
26972 							(!destinationPlan.getFieldLength("minLocalLength") || 
26973 								norm.subscriberNumber.length >= destinationPlan.getFieldLength("minLocalLength"))) {
26974 						// area code is required when dialling from international, but only add it if we are dialing
26975 						// to our home area. Otherwise, the default area code is not valid!
26976 						norm.areaCode = options.defaultAreaCode;
26977 						if (!destinationPlan.getSkipTrunk() && destinationPlan.getTrunkCode()) {
26978 							// some phone systems require the trunk access code, even when dialling from international
26979 							norm.trunkAccess = destinationPlan.getTrunkCode();
26980 						}
26981 					}
26982 					
26983 					if (norm.trunkAccess && destinationPlan.getSkipTrunk()) {
26984 						// on some phone systems, the trunk access code is dropped when dialling from international
26985 						delete norm.trunkAccess;
26986 					}
26987 					
26988 					// make sure to get the country code for the destination region, not the current region!
26989 					if (options.sms) {
26990 						if (homeLocale.getRegion() === "US" && currentLocale.getRegion() !== "US") {
26991 							if (destinationLocale.getRegion() !== "US") {
26992 								norm.iddPrefix = "011"; // non-standard code to make it go through the US first
26993 								norm.countryCode = norm.countryCode || homeLocale._mapRegiontoCC(destinationLocale.getRegion());
26994 							} else if (options.networkType === "cdma") {
26995 								delete norm.iddPrefix;
26996 								delete norm.countryCode;
26997 								if (norm.areaCode) {
26998 									norm.trunkAccess = "1";
26999 								}
27000 							} else if (norm.areaCode) {
27001 								norm.iddPrefix = "+";
27002 								norm.countryCode = "1";
27003 								delete norm.trunkAccess;
27004 							}
27005 						} else {
27006 							norm.iddPrefix = (options.networkType === "cdma") ? currentPlan.getIDDCode() : "+";
27007 							norm.countryCode = norm.countryCode || homeLocale._mapRegiontoCC(destinationLocale.region);
27008 						}
27009 					} else if (norm._hasPrefix() && !norm.countryCode) {
27010 						norm.countryCode = homeLocale._mapRegiontoCC(destinationLocale.region);
27011 					}
27012 
27013 					if (norm.countryCode && !options.sms) {
27014 						// for CDMA, make sure to get the international dialling access code for the current region, not the destination region
27015 						// all umts carriers support plus dialing
27016 						norm.iddPrefix = (options.networkType === "cdma") ? currentPlan.getIDDCode() : "+";
27017 					}
27018 				} else {
27019 					// console.log("normalize: dialing within the country");
27020 					if (options.defaultAreaCode) {
27021 						if (destinationPlan.getPlanStyle() === "open") {
27022 							if (!norm.trunkAccess && norm._hasPrefix() && destinationPlan.getTrunkCode()) {
27023 								// call is not local to this area code, so you have to dial the trunk code and the area code
27024 								norm.trunkAccess = destinationPlan.getTrunkCode();
27025 							}
27026 						} else {
27027 							// In closed plans, you always have to dial the area code, even if the call is local.
27028 							if (!norm._hasPrefix()) {
27029 								if (destinationLocale.getRegion() === homeLocale.getRegion()) {
27030 									norm.areaCode = options.defaultAreaCode;
27031 									if (destinationPlan.getTrunkRequired() && destinationPlan.getTrunkCode()) {
27032 										norm.trunkAccess = norm.trunkAccess || destinationPlan.getTrunkCode();
27033 									}
27034 								}
27035 							} else {
27036 								if (destinationPlan.getTrunkRequired() && destinationPlan.getTrunkCode()) {
27037 									norm.trunkAccess = norm.trunkAccess || destinationPlan.getTrunkCode();
27038 								}
27039 							}
27040 						}
27041 					}
27042 					
27043 					if (options.sms &&
27044 							homeLocale.getRegion() === "US" && 
27045 							currentLocale.getRegion() !== "US") {
27046 						norm.iddPrefix = "011"; // make it go through the US first
27047 						if (destinationPlan.getSkipTrunk() && norm.trunkAccess) {
27048 							delete norm.trunkAccess;
27049 						}
27050 					} else if (norm.iddPrefix || norm.countryCode) {
27051 						// we are in our destination country, so strip the international dialling prefixes
27052 						delete norm.iddPrefix;
27053 						delete norm.countryCode;
27054 						
27055 						if ((destinationPlan.getPlanStyle() === "open" || destinationPlan.getTrunkRequired()) && destinationPlan.getTrunkCode()) {
27056 							norm.trunkAccess = destinationPlan.getTrunkCode();
27057 						}
27058 					}
27059 				}
27060 			}
27061 		} else if (!norm.invalid) {
27062 			// console.log("normalize: non-assisted normalization");
27063 			if (!norm._hasPrefix() && options && options.defaultAreaCode && destinationLocale.getRegion() === homeLocale.region) {
27064 				norm.areaCode = options.defaultAreaCode;
27065 			}
27066 			
27067 			if (!norm.countryCode && norm._hasPrefix()) {
27068 				norm.countryCode = homeLocale._mapRegiontoCC(destinationLocale.getRegion());
27069 			}
27070 
27071 			if (norm.countryCode) {
27072 				if (options && options.networkType && options.networkType === "cdma") {
27073 					norm.iddPrefix = currentPlan.getIDDCode(); 
27074 				} else {
27075 					// all umts carriers support plus dialing
27076 					norm.iddPrefix = "+";
27077 				}
27078 		
27079 				if (destinationPlan.getSkipTrunk() && norm.trunkAccess) {
27080 					delete norm.trunkAccess;
27081 				} else if (!destinationPlan.getSkipTrunk() && !norm.trunkAccess && destinationPlan.getTrunkCode()) {
27082 					norm.trunkAccess = destinationPlan.getTrunkCode();
27083 				}
27084 			}
27085 		}
27086 		
27087 		// console.info("normalize: after normalization, the normalized phone number is: " + JSON.stringify(norm));
27088 		formatted = norm._join();
27089 
27090 		return formatted;
27091 	},
27092 	
27093 	/**
27094 	 * @private
27095 	 * @param {{
27096 	 *   mcc:string,
27097 	 *   defaultAreaCode:string,
27098 	 *   country:string,
27099 	 *   networkType:string,
27100 	 *   assistedDialing:boolean,
27101 	 *   sms:boolean,
27102 	 *   manualDialing:boolean
27103 	 * }} options an object containing options to help in normalizing. 
27104 	 * @param {PhoneNumber} norm
27105 	 * @param {PhoneLocale} homeLocale
27106 	 * @param {PhoneLocale} currentLocale
27107 	 * @param {NumberingPlan} currentPlan
27108 	 * @param {PhoneLocale} destinationLocale
27109 	 * @param {NumberingPlan} destinationPlan
27110 	 * @param {boolean} sync
27111 	 * @param {Object|undefined} loadParams
27112 	 * @param {function(string)} callback
27113 	 */
27114 	_doReparse: function(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams, callback) {
27115 		var formatted, 
27116 			tempRegion;
27117 		
27118 		if (options &&
27119 				options.assistedDialing &&
27120 				!norm.trunkAccess && 
27121 				!norm.iddPrefix &&
27122 				norm.subscriberNumber && 
27123 				norm.subscriberNumber.length > destinationPlan.getFieldLength("maxLocalLength")) {
27124 
27125 			// numbers that are too long are sometimes international direct dialed numbers that
27126 			// are missing the IDD prefix. So, try reparsing it using a plus in front to see if that works.
27127 			new PhoneNumber("+" + this._join(), {
27128 				locale: this.locale,
27129 				sync: sync,
27130 				loadParms: loadParams,
27131 				onLoad: ilib.bind(this, function (data) {
27132 					tempRegion = (data.countryCode && data.locale._mapCCtoRegion(data.countryCode));
27133 
27134 					if (tempRegion && tempRegion !== "unknown" && tempRegion !== "SG") {
27135 						// only use it if it is a recognized country code. Singapore (SG) is a special case.
27136 						norm = data;
27137 						destinationLocale = data.destinationLocale;
27138 						destinationPlan = data.destinationPlan;
27139 					}
27140 					
27141 					formatted = this._doNormalize(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams);
27142 					if (typeof(callback) === 'function') {
27143 						callback(formatted);
27144 					}
27145 				})
27146 			});
27147 		} else if (options && options.assistedDialing && norm.invalid && currentLocale.region !== norm.locale.region) {
27148 			// if this number is not valid for the locale it was parsed with, try again with the current locale
27149 			// console.log("norm is invalid. Attempting to reparse with the current locale");
27150 
27151 			new PhoneNumber(this._join(), {
27152 				locale: currentLocale,
27153 				sync: sync,
27154 				loadParms: loadParams,
27155 				onLoad: ilib.bind(this, function (data) {
27156 					if (data && !data.invalid) {
27157 						norm = data;
27158 					}
27159 					
27160 					formatted = this._doNormalize(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams);
27161 					if (typeof(callback) === 'function') {
27162 						callback(formatted);
27163 					}
27164 				})
27165 			});
27166 		} else {
27167 			formatted = this._doNormalize(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams);
27168 			if (typeof(callback) === 'function') {
27169 				callback(formatted);
27170 			}
27171 		}
27172 	},
27173 	
27174 	/**
27175 	 * This function normalizes the current phone number to a canonical format and returns a
27176 	 * string with that phone number. If parts are missing, this function attempts to fill in 
27177 	 * those parts.<p>
27178 	 * 	  
27179 	 * The options object contains a set of properties that can possibly help normalize
27180 	 * this number by providing "extra" information to the algorithm. The options
27181 	 * parameter may be null or an empty object if no hints can be determined before
27182 	 * this call is made. If any particular hint is not
27183 	 * available, it does not need to be present in the options object.<p>
27184 	 * 
27185 	 * The following is a list of hints that the algorithm will look for in the options
27186 	 * object:
27187 	 * 
27188 	 * <ul>
27189 	 * <li><i>mcc</i> the mobile carrier code of the current network upon which this 
27190 	 * phone is operating. This is translated into an IDD country code. This is 
27191 	 * useful if the number being normalized comes from CNAP (callerid) and the
27192 	 * MCC is known.
27193 	 * <li><i>defaultAreaCode</i> the area code of the phone number of the current
27194 	 * device, if available. Local numbers in a person's contact list are most 
27195 	 * probably in this same area code.
27196 	 * <li><i>country</i> the 2 letter ISO 3166 code of the country if it is
27197 	 * known from some other means such as parsing the physical address of the
27198 	 * person associated with the phone number, or the from the domain name 
27199 	 * of the person's email address
27200 	 * <li><i>networkType</i> specifies whether the phone is currently connected to a
27201 	 * CDMA network or a UMTS network. Valid values are the strings "cdma" and "umts".
27202 	 * If one of those two strings are not specified, or if this property is left off
27203 	 * completely, this method will assume UMTS.
27204 	 * </ul>
27205 	 * 
27206 	 * The following are a list of options that control the behaviour of the normalization:
27207 	 * 
27208 	 * <ul>
27209 	 * <li><i>assistedDialing</i> if this is set to true, the number will be normalized
27210 	 * so that it can dialled directly on the type of network this phone is 
27211 	 * currently connected to. This allows customers to dial numbers or use numbers 
27212 	 * in their contact list that are specific to their "home" region when they are 
27213 	 * roaming and those numbers would not otherwise work with the current roaming 
27214 	 * carrier as they are. The home region is 
27215 	 * specified as the phoneRegion system preference that is settable in the 
27216 	 * regional settings app. With assisted dialling, this method will add or 
27217 	 * remove international direct dialling prefixes and country codes, as well as
27218 	 * national trunk access codes, as required by the current roaming carrier and the
27219 	 * home region in order to dial the number properly. If it is not possible to 
27220 	 * construct a full international dialling sequence from the options and hints given,
27221 	 * this function will not modify the phone number, and will return "undefined".
27222 	 * If assisted dialling is false or not specified, then this method will attempt
27223 	 * to add all the information it can to the number so that it is as fully
27224 	 * specified as possible. This allows two numbers to be compared more easily when
27225 	 * those two numbers were otherwise only partially specified.
27226 	 * <li><i>sms</i> set this option to true for the following conditions: 
27227 	 *   <ul>
27228 	 *   <li>assisted dialing is turned on
27229 	 *   <li>the phone number represents the destination of an SMS message
27230 	 *   <li>the phone is UMTS 
27231 	 *   <li>the phone is SIM-locked to its carrier
27232 	 *   </ul> 
27233 	 * This enables special international direct dialling codes to route the SMS message to
27234 	 * the correct carrier. If assisted dialling is not turned on, this option has no
27235 	 * affect.
27236 	 * <li><i>manualDialing</i> set this option to true if the user is entering this number on
27237 	 * the keypad directly, and false when the number comes from a stored location like a 
27238 	 * contact entry or a call log entry. When true, this option causes the normalizer to 
27239 	 * not perform any normalization on numbers that look like local numbers in the home 
27240 	 * country. If false, all numbers go through normalization. This option only has an effect
27241 	 * when the assistedDialing option is true as well, otherwise it is ignored.
27242 	 * </ul> 
27243 	 * 
27244 	 * If both a set of options and a locale are given, and they offer conflicting
27245 	 * information, the options will take precedence. The idea is that the locale
27246 	 * tells you the region setting that the user has chosen (probably in 
27247 	 * firstuse), whereas the the hints are more current information such as
27248 	 * where the phone is currently operating (the MCC).<p> 
27249 	 * 
27250 	 * This function performs the following types of normalizations with assisted
27251 	 * dialling turned on:
27252 	 * 
27253 	 * <ol>
27254 	 * <li>If the current location of the phone matches the home country, this is a
27255 	 * domestic call.
27256 	 *   <ul> 
27257 	 *   <li>Remove any iddPrefix and countryCode fields, as they are not needed
27258 	 *   <li>Add in a trunkAccess field that may be necessary to call a domestic numbers 
27259 	 *     in the home country
27260 	 *   </ul>
27261 	 * <li> If the current location of the phone does not match the home country,
27262 	 * attempt to form a whole international number.
27263 	 *   <ul>
27264 	 *   <li>Add in the area code if it is missing from the phone number and the area code
27265 	 *     of the current phone is available in the hints
27266 	 *   <li>Add the country dialling code for the home country if it is missing from the 
27267 	 *     phone number
27268 	 *   <li>Add or replace the iddPrefix with the correct one for the current country. The
27269 	 *     phone number will have been parsed with the settings for the home country, so
27270 	 *     the iddPrefix may be incorrect for the
27271 	 *     current country. The iddPrefix for the current country can be "+" if the phone 
27272 	 *     is connected to a UMTS network, and either a "+" or a country-dependent 
27273 	 *     sequences of digits for CDMA networks.
27274 	 *   </ul>
27275 	 * </ol>
27276 	 * 
27277 	 * This function performs the following types of normalization with assisted
27278 	 * dialling turned off:
27279 	 * 
27280 	 * <ul>
27281 	 * <li>Normalize the international direct dialing prefix to be a plus or the
27282 	 * international direct dialling access code for the current country, depending
27283 	 * on the network type.
27284 	 * <li>If a number is a local number (ie. it is missing its area code), 
27285 	 * use a default area code from the hints if available. CDMA phones always know their area 
27286 	 * code, and GSM/UMTS phones know their area code in many instances, but not always 
27287 	 * (ie. not on Vodaphone or Telcel phones). If the default area code is not available, 
27288 	 * do not add it.
27289 	 * <li>In assisted dialling mode, if a number is missing its country code, 
27290 	 * use the current MCC number if
27291 	 * it is available to figure out the current country code, and prepend that 
27292 	 * to the number. If it is not available, leave it off. Also, use that 
27293 	 * country's settings to parse the number instead of the current format 
27294 	 * locale.
27295 	 * <li>For North American numbers with an area code but no trunk access 
27296 	 * code, add in the trunk access code.
27297 	 * <li>For other countries, if the country code is added in step 3, remove the 
27298 	 * trunk access code when required by that country's conventions for 
27299 	 * international calls. If the country requires a trunk access code for 
27300 	 * international calls and it doesn't exist, add one.
27301 	 * </ul>
27302 	 *  
27303 	 * This method modifies the current object, and also returns a string 
27304 	 * containing the normalized phone number that can be compared directly against
27305 	 * other normalized numbers. The canonical format for phone numbers that is 
27306 	 * returned from thhomeLocaleis method is simply an uninterrupted and unformatted string 
27307 	 * of dialable digits.
27308 	 * 
27309 	 * @param {{
27310 	 *   mcc:string,
27311 	 *   defaultAreaCode:string,
27312 	 *   country:string,
27313 	 *   networkType:string,
27314 	 *   assistedDialing:boolean,
27315 	 *   sms:boolean,
27316 	 *   manualDialing:boolean
27317 	 * }} options an object containing options to help in normalizing. 
27318 	 * @return {string|undefined} the normalized string, or undefined if the number
27319 	 * could not be normalized
27320 	 */
27321 	normalize: function(options) {
27322 		var norm,
27323 			sync = true,
27324 			loadParams = {};
27325 			
27326 
27327 		if (options) {
27328 			if (typeof(options.sync) !== 'undefined') {
27329 				sync = (options.sync == true);
27330 			}
27331 			
27332 			if (options.loadParams) {
27333 				loadParams = options.loadParams;
27334 			}
27335 		}
27336 		
27337 		// Clone this number, so we don't mess with the original.
27338 		// No need to do this asynchronously because it's a copy constructor which doesn't 
27339 		// load any extra files.
27340 		norm = new PhoneNumber(this);
27341 
27342 		var normalized;
27343 		
27344 		if (options && (typeof(options.mcc) !== 'undefined' || typeof(options.country) !== 'undefined')) {
27345 			new PhoneLocale({
27346 				mcc: options.mcc,
27347 				countryCode: options.countryCode,
27348 				locale: this.locale,
27349 				sync: sync,
27350 				loadParams: loadParams,
27351 				onLoad: ilib.bind(this, function(loc) {
27352 					new NumberingPlan({
27353 						locale: loc,
27354 						sync: sync,
27355 						loadParms: loadParams,
27356 						onLoad: ilib.bind(this, function (plan) {
27357 							this._doReparse(options, norm, this.locale, loc, plan, this.destinationLocale, this.destinationPlan, sync, loadParams, function (fmt) {
27358 								normalized = fmt;
27359 								
27360 								if (options && typeof(options.onLoad) === 'function') {
27361 									options.onLoad(fmt);
27362 								}
27363 							});
27364 						})
27365 					});
27366 				})
27367 			});
27368 		} else {
27369 			this._doReparse(options, norm, this.locale, this.locale, this.plan, this.destinationLocale, this.destinationPlan, sync, loadParams, function (fmt) {
27370 				normalized = fmt;
27371 				
27372 				if (options && typeof(options.onLoad) === 'function') {
27373 					options.onLoad(fmt);
27374 				}
27375 			});
27376 		}
27377 
27378 		// return the value for the synchronous case
27379 		return normalized;
27380 	}
27381 };
27382 
27383 
27384 /*< PhoneFmt.js */
27385 /*
27386  * phonefmt.js - Represent a phone number formatter.
27387  * 
27388  * Copyright © 2014-2015, JEDLSoft
27389  *
27390  * Licensed under the Apache License, Version 2.0 (the "License");
27391  * you may not use this file except in compliance with the License.
27392  * You may obtain a copy of the License at
27393  *
27394  *     http://www.apache.org/licenses/LICENSE-2.0
27395  *
27396  * Unless required by applicable law or agreed to in writing, software
27397  * distributed under the License is distributed on an "AS IS" BASIS,
27398  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27399  *
27400  * See the License for the specific language governing permissions and
27401  * limitations under the License.
27402  */
27403 
27404 /*
27405 !depends 
27406 ilib.js 
27407 Locale.js 
27408 NumberingPlan.js
27409 PhoneNumber.js
27410 PhoneLocale.js
27411 Utils.js
27412 JSUtils.js
27413 */
27414 
27415 // !data phonefmt
27416 
27417 
27418 /**
27419  * @class
27420  * Create a new phone number formatter object that formats numbers according to the parameters.<p>
27421  * 
27422  * The options object can contain zero or more of the following parameters:
27423  *
27424  * <ul>
27425  * <li><i>locale</i> locale to use to format this number, or undefined to use the default locale
27426  * <li><i>style</i> the name of style to use to format numbers, or undefined to use the default style
27427  * <li><i>mcc</i> the MCC of the country to use if the number is a local number and the country code is not known
27428  *
27429  * <li><i>onLoad</i> - a callback function to call when the locale data is fully loaded and the address has been 
27430  * parsed. When the onLoad option is given, the address formatter object 
27431  * will attempt to load any missing locale data using the ilib loader callback.
27432  * When the constructor is done (even if the data is already preassembled), the 
27433  * onLoad function is called with the current instance as a parameter, so this
27434  * callback can be used with preassembled or dynamic loading or a mix of the two. 
27435  * 
27436  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
27437  * asynchronously. If this option is given as "false", then the "onLoad"
27438  * callback must be given, as the instance returned from this constructor will
27439  * not be usable for a while. 
27440  *
27441  * <li><i>loadParams</i> - an object containing parameters to pass to the 
27442  * loader callback function when locale data is missing. The parameters are not
27443  * interpretted or modified in any way. They are simply passed along. The object 
27444  * may contain any property/value pairs as long as the calling code is in
27445  * agreement with the loader callback function as to what those parameters mean.
27446  * </ul>
27447  *
27448  * Some regions have more than one style of formatting, and the style parameter
27449  * selects which style the user prefers. An array of style names that this locale
27450  * supports can be found by calling {@link PhoneFmt.getAvailableStyles}. 
27451  * Example phone numbers can be retrieved for each style by calling 
27452  * {@link PhoneFmt.getStyleExample}.
27453  * <p>
27454  *
27455  * If the MCC is given, numbers will be formatted in the manner of the country
27456  * specified by the MCC. If it is not given, but the locale is, the manner of
27457  * the country in the locale will be used. If neither the locale or MCC are not given,
27458  * then the country of the current ilib locale is used. 
27459  *
27460  * @constructor
27461  * @param {Object} options properties that control how this formatter behaves
27462  */
27463 var PhoneFmt = function(options) {
27464 	this.sync = true;
27465 	this.styleName = 'default',
27466 	this.loadParams = {};
27467 
27468 	var locale = new Locale();
27469 
27470 	if (options) {
27471 		if (options.locale) {
27472 			locale = options.locale;
27473 		}
27474 
27475 		if (typeof(options.sync) !== 'undefined') {
27476 			this.sync = (options.sync == true);
27477 		}
27478 
27479 		if (options.loadParams) {
27480 			this.loadParams = options.loadParams;
27481 		}
27482 
27483 		if (options.style) {
27484 			this.style = options.style;
27485 		}
27486 	}
27487 
27488 	new PhoneLocale({
27489 		locale: locale,
27490 		mcc: options && options.mcc,
27491 		countryCode: options && options.countryCode,
27492 		onLoad: ilib.bind(this, function (data) {
27493 			/** @type {PhoneLocale} */
27494 			this.locale = data;
27495 
27496 			new NumberingPlan({
27497 				locale: this.locale,
27498 				sync: this.sync,
27499 				loadParms: this.loadParams,
27500 				onLoad: ilib.bind(this, function (plan) {
27501 					/** @type {NumberingPlan} */
27502 					this.plan = plan;
27503 
27504 					Utils.loadData({
27505 						name: "phonefmt.json",
27506 						object: PhoneFmt,
27507 						locale: this.locale, 
27508 						sync: this.sync,
27509 						loadParams: JSUtils.merge(this.loadParams, {
27510 							returnOne: true
27511 						}),
27512 						callback: ilib.bind(this, function (fmtdata) {
27513 							this.fmtdata = fmtdata;
27514 							
27515 							if (options && typeof(options.onLoad) === 'function') {
27516 								options.onLoad(this);
27517 							}
27518 						})
27519 					});
27520 				})
27521 			});
27522 		})
27523 	});
27524 };
27525 
27526 PhoneFmt.prototype = {
27527 	/**
27528 	 * 
27529 	 * @protected
27530 	 * @param {string} part
27531 	 * @param {Object} formats
27532 	 * @param {boolean} mustUseAll
27533 	 */
27534 	_substituteDigits: function(part, formats, mustUseAll) {
27535 		var formatString,
27536 			formatted = "",
27537 			partIndex = 0,
27538 			templates,
27539 			i;
27540 
27541 		// console.info("Globalization.Phone._substituteDigits: typeof(formats) is " + typeof(formats));
27542 		if (!part) {
27543 			return formatted;
27544 		}
27545 
27546 		if (typeof(formats) === "object") {
27547 			templates = (typeof(formats.template) !== 'undefined') ? formats.template : formats;
27548 			if (part.length > templates.length) {
27549 				// too big, so just use last resort rule.
27550 				throw "part " + part + " is too big. We do not have a format template to format it.";
27551 			}
27552 			// use the format in this array that corresponds to the digit length of this
27553 			// part of the phone number
27554 			formatString =  templates[part.length-1];
27555 			// console.info("Globalization.Phone._substituteDigits: formats is an Array: " + JSON.stringify(formats));
27556 		} else {
27557 			formatString = formats;
27558 		}
27559 
27560 		for (i = 0; i < formatString.length; i++) {
27561 			if (formatString.charAt(i) === "X") {
27562 				formatted += part.charAt(partIndex);
27563 				partIndex++;
27564 			} else {
27565 				formatted += formatString.charAt(i);
27566 			}
27567 		}
27568 		
27569 		if (mustUseAll && partIndex < part.length-1) {
27570 			// didn't use the whole thing in this format? Hmm... go to last resort rule
27571 			throw "too many digits in " + part + " for format " + formatString;
27572 		}
27573 		
27574 		return formatted;
27575 	},
27576 	
27577 	/**
27578 	 * Returns the style with the given name, or the default style if there
27579 	 * is no style with that name.
27580 	 * @protected
27581 	 * @return {{example:string,whole:Object.<string,string>,partial:Object.<string,string>}|Object.<string,string>}
27582 	 */
27583 	_getStyle: function (name, fmtdata) {
27584 		return fmtdata[name] || fmtdata["default"];
27585 	},
27586 
27587 	/**
27588 	 * Do the actual work of formatting the phone number starting at the given
27589 	 * field in the regular field order.
27590 	 * 
27591 	 * @param {!PhoneNumber} number
27592 	 * @param {{
27593 	 *   partial:boolean,
27594 	 *   style:string,
27595 	 *   mcc:string,
27596 	 *   locale:(string|Locale),
27597 	 *   sync:boolean,
27598 	 *   loadParams:Object,
27599 	 *   onLoad:function(string)
27600 	 * }} options Parameters which control how to format the number
27601 	 * @param {number} startField
27602 	 */
27603 	_doFormat: function(number, options, startField, locale, fmtdata, callback) {
27604 		var sync = true,
27605 			loadParams = {},
27606 			temp, 
27607 			templates, 
27608 			fieldName, 
27609 			countryCode, 
27610 			isWhole, 
27611 			style,
27612 			formatted = "",
27613 			styleTemplates,
27614 			lastFieldName;
27615 	
27616 		if (options) {
27617 			if (typeof(options.sync) !== 'undefined') {
27618 				sync = (options.sync == true);				
27619 			}
27620 		
27621 			if (options.loadParams) {
27622 				loadParams = options.loadParams;
27623 			}
27624 		}
27625 	
27626 		style = this.style; // default style for this formatter
27627 
27628 		// figure out what style to use for this type of number
27629 		if (number.countryCode) {
27630 			// dialing from outside the country
27631 			// check to see if it to a mobile number because they are often formatted differently
27632 			style = (number.mobilePrefix) ? "internationalmobile" : "international";
27633 		} else if (number.mobilePrefix !== undefined) {
27634 			style = "mobile";
27635 		} else if (number.serviceCode !== undefined && typeof(fmtdata["service"]) !== 'undefined') {
27636 			// if there is a special format for service numbers, then use it
27637 			style = "service";
27638 		}
27639 
27640 		isWhole = (!options || !options.partial);
27641 		styleTemplates = this._getStyle(style, fmtdata);
27642 		
27643 		// console.log("Style ends up being " + style + " and using subtype " + (isWhole ? "whole" : "partial"));
27644 		styleTemplates = (isWhole ? styleTemplates.whole : styleTemplates.partial) || styleTemplates;
27645 
27646 		for (var i = startField; i < PhoneNumber._fieldOrder.length; i++) {
27647 			fieldName = PhoneNumber._fieldOrder[i];
27648 			// console.info("format: formatting field " + fieldName + " value: " + number[fieldName]);
27649 			if (number[fieldName] !== undefined) {
27650 				if (styleTemplates[fieldName] !== undefined) {
27651 					templates = styleTemplates[fieldName];
27652 					if (fieldName === "trunkAccess") {
27653 						if (number.areaCode === undefined && number.serviceCode === undefined && number.mobilePrefix === undefined) {
27654 							templates = "X";
27655 						}
27656 					}
27657 					if (lastFieldName && typeof(styleTemplates[lastFieldName].suffix) !== 'undefined') {
27658 						if (fieldName !== "extension" && number[fieldName].search(/[xwtp,;]/i) <= -1) {
27659 							formatted += styleTemplates[lastFieldName].suffix;	
27660 						}
27661 					}
27662 					lastFieldName = fieldName;
27663 					
27664 					// console.info("format: formatting field " + fieldName + " with templates " + JSON.stringify(templates));
27665 					temp = this._substituteDigits(number[fieldName], templates, (fieldName === "subscriberNumber"));
27666 					// console.info("format: formatted is: " + temp);
27667 					formatted += temp;
27668 	
27669 					if (fieldName === "countryCode") {
27670 						// switch to the new country to format the rest of the number
27671 						countryCode = number.countryCode.replace(/[wWpPtT\+#\*]/g, '');	// fix for NOV-108200
27672 
27673 						new PhoneLocale({
27674 							locale: this.locale,
27675 							sync: sync,							
27676 							loadParms: loadParams,
27677 							countryCode: countryCode,
27678 							onLoad: ilib.bind(this, function (locale) {
27679 								Utils.loadData({
27680 									name: "phonefmt.json",
27681 									object: PhoneFmt,
27682 									locale: locale,
27683 									sync: sync,
27684 									loadParams: JSUtils.merge(loadParams, {
27685 										returnOne: true
27686 									}),
27687 									callback: ilib.bind(this, function (fmtdata) {
27688 										// console.info("format: switching to region " + locale.region + " and style " + style + " to format the rest of the number ");
27689 										
27690 										var subfmt = "";
27691 
27692 										this._doFormat(number, options, i+1, locale, fmtdata, function (subformat) {
27693 											subfmt = subformat;
27694 											if (typeof(callback) === 'function') {
27695 												callback(formatted + subformat);
27696 											}
27697 										});
27698 										
27699 										formatted += subfmt;
27700 									})
27701 								});
27702 							})
27703 						});
27704 						return formatted;
27705 					}
27706 				} else {
27707 					//console.warn("PhoneFmt.format: cannot find format template for field " + fieldName + ", region " + locale.region + ", style " + style);
27708 					// use default of "minimal formatting" so we don't miss parts because of bugs in the format templates
27709 					formatted += number[fieldName];
27710 				}
27711 			}
27712 		}
27713 		
27714 		if (typeof(callback) === 'function') {
27715 			callback(formatted);
27716 		}
27717 
27718 		return formatted;
27719 	},
27720 	
27721 	/**
27722 	 * Format the parts of a phone number appropriately according to the settings in 
27723 	 * this formatter instance.
27724 	 *  
27725 	 * The options can contain zero or more of these properties:
27726 	 * 
27727 	 * <ul>
27728 	 * <li><i>partial</i> boolean which tells whether or not this phone number 
27729 	 * represents a partial number or not. The default is false, which means the number 
27730 	 * represents a whole number. 
27731 	 * <li><i>style</i> style to use to format the number, if different from the 
27732 	 * default style or the style specified in the constructor
27733 	 * <li><i>locale</i> The locale with which to parse the number. This gives a clue as to which
27734      * numbering plan to use.
27735      * <li><i>mcc</i> The mobile carrier code (MCC) associated with the carrier that the phone is 
27736      * currently connected to, if known. This also can give a clue as to which numbering plan to
27737      * use
27738      * <li><i>onLoad</i> - a callback function to call when the date format object is fully 
27739      * loaded. When the onLoad option is given, the DateFmt object will attempt to
27740      * load any missing locale data using the ilib loader callback.
27741      * When the constructor is done (even if the data is already preassembled), the 
27742      * onLoad function is called with the current instance as a parameter, so this
27743      * callback can be used with preassembled or dynamic loading or a mix of the two.
27744      * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
27745      * asynchronously. If this option is given as "false", then the "onLoad"
27746      * callback must be given, as the instance returned from this constructor will
27747      * not be usable for a while.
27748      * <li><i>loadParams</i> - an object containing parameters to pass to the 
27749      * loader callback function when locale data is missing. The parameters are not
27750      * interpretted or modified in any way. They are simply passed along. The object 
27751      * may contain any property/value pairs as long as the calling code is in
27752      * agreement with the loader callback function as to what those parameters mean.
27753 	 * </ul>
27754 	 *      
27755 	 * The partial parameter specifies whether or not the phone number contains
27756 	 * a partial phone number or if it is a whole phone number. A partial 
27757 	 * number is usually a number as the user is entering it with a dial pad. The
27758 	 * reason is that certain types of phone numbers should be formatted differently
27759 	 * depending on whether or not it represents a whole number. Specifically, SMS
27760 	 * short codes are formatted differently.<p>
27761 	 * 
27762 	 * Example: a subscriber number of "48773" in the US would get formatted as:
27763 	 * 
27764 	 * <ul>
27765 	 * <li>partial: 487-73  (perhaps the user is in the process of typing a whole phone 
27766 	 * number such as 487-7379)
27767 	 * <li>whole:   48773   (this is the entire SMS short code)
27768 	 * </ul>
27769 	 * 
27770 	 * Any place in the UI where the user types in phone numbers, such as the keypad in 
27771 	 * the phone app, should pass in partial: true to this formatting routine. All other 
27772 	 * places, such as the call log in the phone app, should pass in partial: false, or 
27773 	 * leave the partial flag out of the parameters entirely. 
27774 	 * 
27775 	 * @param {!PhoneNumber} number object containing the phone number to format
27776 	 * @param {{
27777 	 *   partial:boolean,
27778 	 *   style:string,
27779 	 *   mcc:string,
27780 	 *   locale:(string|Locale),
27781 	 *   sync:boolean,
27782 	 *   loadParams:Object,
27783 	 *   onLoad:function(string)
27784 	 * }} options Parameters which control how to format the number
27785 	 * @return {string} Returns the formatted phone number as a string.
27786 	 */
27787 	format: function (number, options) {
27788 		var formatted = "",
27789 		    callback;
27790 
27791 		callback = options && options.onLoad;
27792 
27793 		try {
27794 			this._doFormat(number, options, 0, this.locale, this.fmtdata, function (fmt) {
27795 				formatted = fmt;
27796 				
27797 				if (typeof(callback) === 'function') {
27798 					callback(fmt);
27799 				}
27800 			});
27801 		} catch (e) {
27802 			if (typeof(e) === 'string') { 
27803 				// console.warn("caught exception: " + e + ". Using last resort rule.");
27804 				// if there was some exception, use this last resort rule
27805 				formatted = "";
27806 				for (var field in PhoneNumber._fieldOrder) {
27807 					if (typeof field === 'string' && typeof PhoneNumber._fieldOrder[field] === 'string' && number[PhoneNumber._fieldOrder[field]] !== undefined) {
27808 						// just concatenate without any formatting
27809 						formatted += number[PhoneNumber._fieldOrder[field]];
27810 						if (PhoneNumber._fieldOrder[field] === 'countryCode') {
27811 							formatted += ' ';		// fix for NOV-107894
27812 						}
27813 					}
27814 				}
27815 			} else {
27816 				throw e;
27817 			}
27818 			
27819 			if (typeof(callback) === 'function') {
27820 				callback(formatted);
27821 			}
27822 		}
27823 		return formatted;
27824 	},
27825 	
27826 	/**
27827 	 * Return an array of names of all available styles that can be used with the current 
27828 	 * formatter.
27829 	 * @return {Array.<string>} an array of names of styles that are supported by this formatter
27830 	 */
27831 	getAvailableStyles: function () {
27832 		var ret = [],
27833 			style;
27834 
27835 		if (this.fmtdata) {
27836 			for (style in this.fmtdata) {
27837 				if (this.fmtdata[style].example) {
27838 					ret.push(style);
27839 				}
27840 			}
27841 		}
27842 		return ret;
27843 	},
27844 	
27845 	/**
27846 	 * Return an example phone number formatted with the given style.
27847 	 * 
27848 	 * @param {string|undefined} style style to get an example of, or undefined to use
27849 	 * the current default style for this formatter
27850 	 * @return {string|undefined} an example phone number formatted according to the 
27851 	 * given style, or undefined if the style is not recognized or does not have an 
27852 	 * example 
27853 	 */
27854 	getStyleExample: function (style) {
27855 		return this.fmtdata[style].example || undefined;
27856 	}
27857 };
27858 
27859 
27860 /*< PhoneGeoLocator.js */
27861 /*
27862  * phonegeo.js - Represent a phone number geolocator object.
27863  * 
27864  * Copyright © 2014-2015, JEDLSoft
27865  *
27866  * Licensed under the Apache License, Version 2.0 (the "License");
27867  * you may not use this file except in compliance with the License.
27868  * You may obtain a copy of the License at
27869  *
27870  *     http://www.apache.org/licenses/LICENSE-2.0
27871  *
27872  * Unless required by applicable law or agreed to in writing, software
27873  * distributed under the License is distributed on an "AS IS" BASIS,
27874  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27875  *
27876  * See the License for the specific language governing permissions and
27877  * limitations under the License.
27878  */
27879 
27880 /*
27881 !depends 
27882 ilib.js 
27883 NumberingPlan.js
27884 PhoneLocale.js
27885 PhoneNumber.js
27886 Utils.js
27887 JSUtils.js
27888 ResBundle.js
27889 */
27890 
27891 // !data iddarea area extarea extstates phoneres
27892 
27893 
27894 
27895 /**
27896  * @class
27897  * Create an instance that can geographically locate a phone number.<p>
27898  * 
27899  * The location of the number is calculated according to the following rules:
27900  * 
27901  * <ol>
27902  * <li>If the areaCode property is undefined or empty, or if the number specifies a 
27903  * country code for which we do not have information, then the area property may be 
27904  * missing from the returned object. In this case, only the country object will be returned.
27905  * 
27906  * <li>If there is no area code, but there is a mobile prefix, service code, or emergency 
27907  * code, then a fixed string indicating the type of number will be returned.
27908  * 
27909  * <li>The country object is filled out according to the countryCode property of the phone
27910  * number. 
27911  * 
27912  * <li>If the phone number does not have an explicit country code, the MCC will be used if
27913  * it is available. The country code can be gleaned directly from the MCC. If the MCC 
27914  * of the carrier to which the phone is currently connected is available, it should be 
27915  * passed in so that local phone numbers will look correct.
27916  * 
27917  * <li>If the country's dialling plan mandates a fixed length for phone numbers, and a 
27918  * particular number exceeds that length, then the area code will not be given on the
27919  * assumption that the number has problems in the first place and we cannot guess
27920  * correctly.
27921  * </ol>
27922  * 
27923  * The returned area property varies in specificity according
27924  * to the locale. In North America, the area is no finer than large parts of states
27925  * or provinces. In Germany and the UK, the area can be as fine as small towns.<p>
27926  * 
27927  * If the number passed in is invalid, no geolocation will be performed. If the location
27928  * information about the country where the phone number is located is not available,
27929  * then the area information will be missing and only the country will be available.<p>
27930  * 
27931  * The options parameter can contain any one of the following properties:
27932  * 
27933  * <ul>
27934  * <li><i>locale</i> The locale parameter is used to load translations of the names of regions and
27935  * areas if available. For example, if the locale property is given as "en-US" (English for USA), 
27936  * but the phone number being geolocated is in Germany, then this class would return the the names
27937  * of the country (Germany) and region inside of Germany in English instead of German. That is, a 
27938  * phone number in Munich and return the country "Germany" and the area code "Munich"
27939  * instead of "Deutschland" and "München". The default display locale is the current ilib locale. 
27940  * If translations are not available, the region and area names are given in English, which should 
27941  * always be available.
27942  * <li><i>mcc</i> The mcc of the current mobile carrier, if known.
27943  * 
27944  * <li><i>onLoad</i> - a callback function to call when the data for the
27945  * locale is fully loaded. When the onLoad option is given, this object 
27946  * will attempt to load any missing locale data using the ilib loader callback.
27947  * When the constructor is done (even if the data is already preassembled), the 
27948  * onLoad function is called with the current instance as a parameter, so this
27949  * callback can be used with preassembled or dynamic loading or a mix of the two. 
27950  * 
27951  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
27952  * asynchronously. If this option is given as "false", then the "onLoad"
27953  * callback must be given, as the instance returned from this constructor will
27954  * not be usable for a while. 
27955  *
27956  * <li><i>loadParams</i> - an object containing parameters to pass to the 
27957  * loader callback function when locale data is missing. The parameters are not
27958  * interpretted or modified in any way. They are simply passed along. The object 
27959  * may contain any property/value pairs as long as the calling code is in
27960  * agreement with the loader callback function as to what those parameters mean.
27961  * </ul>
27962  * 
27963  * @constructor
27964  * @param {Object} options parameters controlling the geolocation of the phone number.
27965  */
27966 var PhoneGeoLocator = function(options) {
27967 	var sync = true,
27968 		loadParams = {},
27969 		locale = ilib.getLocale();
27970 
27971 	if (options) {
27972 		if (options.locale) {
27973 			locale = options.locale;
27974 		}
27975 
27976 		if (typeof(options.sync) === 'boolean') {
27977 			sync = options.sync;
27978 		}
27979 		
27980 		if (options.loadParams) {
27981 			loadParams = options.loadParams;
27982 		}
27983 	}
27984 	
27985 	new PhoneLocale({
27986 		locale: locale,
27987 		mcc: options && options.mcc,
27988 		countryCode: options && options.countryCode,
27989 		sync: sync,
27990 		loadParams: loadParams,
27991 		onLoad: ilib.bind(this, function (loc) {
27992 			this.locale = loc;
27993 			new NumberingPlan({
27994 				locale: this.locale,
27995 				sync: sync,
27996 				loadParams: loadParams,
27997 				onLoad: ilib.bind(this, function (plan) {
27998 					this.plan = plan;
27999 					
28000 					new ResBundle({
28001 						locale: this.locale,
28002 						name: "phoneres",
28003 						sync: sync,
28004 						loadParams: loadParams,
28005 						onLoad: ilib.bind(this, function (rb) {
28006 							this.rb = rb;
28007 							
28008 							Utils.loadData({
28009 								name: "iddarea.json",
28010 								object: PhoneGeoLocator,
28011 								nonlocale: true,
28012 								sync: sync,
28013 								loadParams: loadParams,
28014 								callback: ilib.bind(this, function (data) {
28015 									this.regiondata = data;
28016 									Utils.loadData({
28017 										name: "area.json",
28018 										object: PhoneGeoLocator,
28019 										locale: this.locale,
28020 										sync: sync,
28021 										loadParams: JSUtils.merge(loadParams, {
28022 											returnOne: true
28023 										}),
28024 										callback: ilib.bind(this, function (areadata) {
28025 											this.areadata = areadata;
28026 		
28027 											if (options && typeof(options.onLoad) === 'function') {
28028 												options.onLoad(this);
28029 											}
28030 										})
28031 									});
28032 								})
28033 							});
28034 						})
28035 					});
28036 				})
28037 			});
28038 		})
28039 	});
28040 };
28041 
28042 PhoneGeoLocator.prototype = {
28043 	/**
28044 	 * @private
28045 	 * 
28046 	 * Used for locales where the area code is very general, and you need to add in
28047 	 * the initial digits of the subscriber number in order to get the area
28048 	 * 
28049 	 * @param {string} number
28050 	 * @param {Object} stateTable
28051 	 */
28052 	_parseAreaAndSubscriber: function (number, stateTable) {
28053 		var ch,
28054 			i,
28055 			handlerMethod,
28056 			newState,
28057 			prefix = "",
28058 			consumed,
28059 			lastLeaf,
28060 			currentState,
28061 			dot = 14;	// special transition which matches all characters. See AreaCodeTableMaker.java
28062 
28063 		if (!number || !stateTable) {
28064 			// can't parse anything
28065 			return undefined;
28066 		}
28067 
28068 		//console.log("GeoLocator._parseAreaAndSubscriber: parsing number " + number);
28069 
28070 		currentState = stateTable;
28071 		i = 0;
28072 		while (i < number.length) {
28073 			ch = PhoneNumber._getCharacterCode(number.charAt(i));
28074 			if (ch >= 0) {
28075 				// newState = stateData.states[state][ch];
28076 				newState = currentState.s && currentState.s[ch];
28077 				
28078 				if (!newState && currentState.s && currentState.s[dot]) {
28079 					newState = currentState.s[dot];
28080 				}
28081 				
28082 				if (typeof(newState) === 'object') {
28083 					if (typeof(newState.l) !== 'undefined') {
28084 						// save for latter if needed
28085 						lastLeaf = newState;
28086 						consumed = i;
28087 					}
28088 					// console.info("recognized digit " + ch + " continuing...");
28089 					// recognized digit, so continue parsing
28090 					currentState = newState;
28091 					i++;
28092 				} else {
28093 					if (typeof(newState) === 'undefined' || newState === 0) {
28094 						// this is possibly a look-ahead and it didn't work... 
28095 						// so fall back to the last leaf and use that as the
28096 						// final state
28097 						newState = lastLeaf;
28098 						i = consumed;
28099 					}
28100 					
28101 					if ((typeof(newState) === 'number' && newState) ||
28102 						(typeof(newState) === 'object' && typeof(newState.l) !== 'undefined')) {
28103 						// final state
28104 						var stateNumber = typeof(newState) === 'number' ? newState : newState.l;
28105 						handlerMethod = PhoneNumber._states[stateNumber];
28106 
28107 						//console.info("reached final state " + newState + " handler method is " + handlerMethod + " and i is " + i);
28108 	
28109 						return (handlerMethod === "area") ? number.substring(0, i+1) : undefined;
28110 					} else {
28111 						// failed parse. Either no last leaf to fall back to, or there was an explicit
28112 						// zero in the table
28113 						break;
28114 					}
28115 				}
28116 			} else if (ch === -1) {
28117 				// non-transition character, continue parsing in the same state
28118 				i++;
28119 			} else {
28120 				// should not happen
28121 				// console.info("skipping character " + ch);
28122 				// not a digit, plus, pound, or star, so this is probably a formatting char. Skip it.
28123 				i++;
28124 			}
28125 		}
28126 		return undefined;
28127 	},
28128 	/**
28129 	 * @private
28130 	 * @param prefix
28131 	 * @param table
28132 	 * @returns
28133 	 */
28134 	_matchPrefix: function(prefix, table)  {
28135 		var i, matchedDot, matchesWithDots = [];
28136 
28137 		if (table[prefix]) {
28138 			return table[prefix];
28139 		}
28140 		for (var entry in table) {
28141 			if (entry && typeof(entry) === 'string') {
28142 				i = 0;
28143 				matchedDot = false;
28144 				while (i < entry.length && (entry.charAt(i) === prefix.charAt(i) || entry.charAt(i) === '.')) {
28145 					if (entry.charAt(i) === '.') {
28146 						matchedDot = true;
28147 					}
28148 					i++;
28149 				}
28150 				if (i >= entry.length) {
28151 					if (matchedDot) {
28152 						matchesWithDots.push(entry);
28153 					} else {
28154 						return table[entry];
28155 					}
28156 				}
28157 			}
28158 		}
28159 
28160 		// match entries with dots last, so sort the matches so that the entry with the 
28161 		// most dots sorts last. The entry that ends up at the beginning of the list is
28162 		// the best match because it has the fewest dots
28163 		if (matchesWithDots.length > 0) {
28164 			matchesWithDots.sort(function (left, right) {
28165 				return (right < left) ? -1 : ((left < right) ? 1 : 0);
28166 			});
28167 			return table[matchesWithDots[0]];
28168 		}
28169 		
28170 		return undefined;
28171 	},
28172 	/**
28173 	 * @private
28174 	 * @param number
28175 	 * @param data
28176 	 * @param locale
28177 	 * @param plan
28178 	 * @param options
28179 	 * @returns {Object}
28180 	 */
28181 	_getAreaInfo: function(number, data, locale, plan, options) {
28182 		var sync = true,
28183 			ret = {}, 
28184 			countryCode, 
28185 			areaInfo, 
28186 			temp, 
28187 			areaCode, 
28188 			geoTable, 
28189 			tempNumber, 
28190 			prefix;
28191 
28192 		if (options && typeof(options.sync) === 'boolean') {
28193 			sync = options.sync;
28194 		}
28195 
28196 		prefix = number.areaCode || number.serviceCode;
28197 		geoTable = data;
28198 		
28199 		if (prefix !== undefined) {
28200 			if (plan.getExtendedAreaCode()) {
28201 				// for countries where the area code is very general and large, and you need a few initial
28202 				// digits of the subscriber number in order find the actual area
28203 				tempNumber = prefix + number.subscriberNumber;
28204 				tempNumber = tempNumber.replace(/[wWpPtT\+#\*]/g, '');	// fix for NOV-108200
28205 		
28206 				Utils.loadData({
28207 					name: "extarea.json",
28208 					object: PhoneGeoLocator, 
28209 					locale: locale,
28210 					sync: sync,
28211 					loadParams: JSUtils.merge((options && options.loadParams) || {}, {returnOne: true}),
28212 					callback: ilib.bind(this, function (data) {
28213 						this.extarea = data;
28214 						Utils.loadData({
28215 							name: "extstates.json",
28216 							object: PhoneGeoLocator, 
28217 							locale: locale,
28218 							sync: sync,
28219 							loadParams: JSUtils.merge((options && options.loadParams) || {}, {returnOne: true}),
28220 							callback: ilib.bind(this, function (data) {
28221 								this.extstates = data;
28222 								geoTable = this.extarea;
28223 								if (this.extarea && this.extstates) {
28224 									prefix = this._parseAreaAndSubscriber(tempNumber, this.extstates);
28225 								}
28226 								
28227 								if (!prefix) {
28228 									// not a recognized prefix, so now try the general table
28229 									geoTable = this.areadata;
28230 									prefix = number.areaCode || number.serviceCode;					
28231 								}
28232 
28233 								if ((!plan.fieldLengths || 
28234 								  plan.getFieldLength('maxLocalLength') === undefined ||
28235 								  !number.subscriberNumber ||
28236 								 	number.subscriberNumber.length <= plan.fieldLengths('maxLocalLength'))) {
28237 								  	areaInfo = this._matchPrefix(prefix, geoTable);
28238 									if (areaInfo && areaInfo.sn && areaInfo.ln) {
28239 										//console.log("Found areaInfo " + JSON.stringify(areaInfo));
28240 										ret.area = {
28241 											sn: this.rb.getString(areaInfo.sn).toString(),
28242 											ln: this.rb.getString(areaInfo.ln).toString()
28243 										};
28244 									}
28245 								}		
28246 							})
28247 						});
28248 					})
28249 				});
28250 
28251 			} else if (!plan || 
28252 					plan.getFieldLength('maxLocalLength') === undefined || 
28253 					!number.subscriberNumber ||
28254 					number.subscriberNumber.length <= plan.getFieldLength('maxLocalLength')) {
28255 				if (geoTable) {
28256 					areaCode = prefix.replace(/[wWpPtT\+#\*]/g, '');
28257 					areaInfo = this._matchPrefix(areaCode, geoTable);
28258 
28259 					if (areaInfo && areaInfo.sn && areaInfo.ln) {
28260 						ret.area = {
28261 							sn: this.rb.getString(areaInfo.sn).toString(),
28262 							ln: this.rb.getString(areaInfo.ln).toString()
28263 						};
28264 					} else if (number.serviceCode) {
28265 						ret.area = {
28266 							sn: this.rb.getString("Service Number").toString(),
28267 							ln: this.rb.getString("Service Number").toString()
28268 						};
28269 					}
28270 				} else {
28271 					countryCode = number.locale._mapRegiontoCC(this.locale.getRegion());
28272 					if (countryCode !== "0" && this.regiondata) {
28273 						temp = this.regiondata[countryCode];
28274 						if (temp && temp.sn) {
28275 							ret.country = {
28276 								sn: this.rb.getString(temp.sn).toString(),
28277 								ln: this.rb.getString(temp.ln).toString(),
28278 								code: this.locale.getRegion()
28279 							};
28280 						}
28281 					}
28282 				}
28283 			} else {
28284 				countryCode = number.locale._mapRegiontoCC(this.locale.getRegion());
28285 				if (countryCode !== "0" && this.regiondata) {
28286 					temp = this.regiondata[countryCode];
28287 					if (temp && temp.sn) {
28288 						ret.country = {
28289 							sn: this.rb.getString(temp.sn).toString(),
28290 							ln: this.rb.getString(temp.ln).toString(),
28291 							code: this.locale.getRegion()
28292 						};
28293 					}
28294 				}
28295 			}
28296 
28297 		} else if (number.mobilePrefix) {
28298 			ret.area = {
28299 				sn: this.rb.getString("Mobile Number").toString(),
28300 				ln: this.rb.getString("Mobile Number").toString()
28301 			};
28302 		} else if (number.emergency) {
28303 			ret.area = {
28304 				sn: this.rb.getString("Emergency Services Number").toString(),
28305 				ln: this.rb.getString("Emergency Services Number").toString()
28306 			};
28307 		}
28308 
28309 		return ret;
28310 	},
28311 	/**
28312 	 * Returns a the location of the given phone number, if known. 
28313 	 * The returned object has 2 properties, each of which has an sn (short name) 
28314 	 * and an ln (long name) string. Additionally, the country code, if given,
28315 	 * includes the 2 letter ISO code for the recognized country.
28316 	 *	 	{
28317 	 *			"country": {
28318 	 *	        	"sn": "North America",
28319 	 *            	"ln": "North America and the Caribbean Islands",
28320 	 *				"code": "us"
28321 	 *         	 },
28322 	 *         	 "area": {
28323 	 *       	    "sn": "California",
28324 	 *          	 "ln": "Central California: San Jose, Los Gatos, Milpitas, Sunnyvale, Cupertino, Gilroy"
28325 	 *         	 }
28326 	 *    	 }
28327 	 * 
28328 	 * The location name is subject to the following rules:
28329 	 *
28330 	 * If the areaCode property is undefined or empty, or if the number specifies a 
28331 	 * country code for which we do not have information, then the area property may be 
28332 	 * missing from the returned object. In this case, only the country object will be returned.
28333 	 *
28334 	 * If there is no area code, but there is a mobile prefix, service code, or emergency 
28335 	 * code, then a fixed string indicating the type of number will be returned.
28336 	 * 
28337 	 * The country object is filled out according to the countryCode property of the phone
28338 	 * number. 
28339 	 * 
28340 	 * If the phone number does not have an explicit country code, the MCC will be used if
28341 	 * it is available. The country code can be gleaned directly from the MCC. If the MCC 
28342 	 * of the carrier to which the phone is currently connected is available, it should be 
28343 	 * passed in so that local phone numbers will look correct.
28344 	 * 
28345 	 * If the country's dialling plan mandates a fixed length for phone numbers, and a 
28346 	 * particular number exceeds that length, then the area code will not be given on the
28347 	 * assumption that the number has problems in the first place and we cannot guess
28348 	 * correctly.
28349 	 *
28350 	 * The returned area property varies in specificity according
28351 	 * to the locale. In North America, the area is no finer than large parts of states
28352 	 * or provinces. In Germany and the UK, the area can be as fine as small towns.
28353 	 *
28354 	 * The strings returned from this function are already localized to the 
28355 	 * given locale, and thus are ready for display to the user.
28356 	 *
28357 	 * If the number passed in is invalid, an empty object is returned. If the location
28358 	 * information about the country where the phone number is located is not available,
28359 	 * then the area information will be missing and only the country will be returned.
28360      *
28361 	 * The options parameter can contain any one of the following properties:
28362  	 * 
28363  	 * <ul>
28364  	 * <li><i>locale</i> The locale parameter is used to load translations of the names of regions and
28365  	 * areas if available. For example, if the locale property is given as "en-US" (English for USA), 
28366  	 * but the phone number being geolocated is in Germany, then this class would return the the names
28367  	 * of the country (Germany) and region inside of Germany in English instead of German. That is, a 
28368  	 * phone number in Munich and return the country "Germany" and the area code "Munich"
28369  	 * instead of "Deutschland" and "München". The default display locale is the current ilib locale. 
28370  	 * If translations are not available, the region and area names are given in English, which should 
28371  	 * always be available.
28372  	 * <li><i>mcc</i> The mcc of the current mobile carrier, if known.
28373  	 * 
28374  	 * <li><i>onLoad</i> - a callback function to call when the data for the
28375  	 * locale is fully loaded. When the onLoad option is given, this object 
28376  	 * will attempt to load any missing locale data using the ilib loader callback.
28377  	 * When the constructor is done (even if the data is already preassembled), the 
28378  	 * onLoad function is called with the current instance as a parameter, so this
28379  	 * callback can be used with preassembled or dynamic loading or a mix of the two. 
28380  	 * 
28381  	 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
28382  	 * asynchronously. If this option is given as "false", then the "onLoad"
28383  	 * callback must be given, as the instance returned from this constructor will
28384  	 * not be usable for a while. 
28385  	 *
28386  	 * <li><i>loadParams</i> - an object containing parameters to pass to the 
28387  	 * loader callback function when locale data is missing. The parameters are not
28388  	 * interpretted or modified in any way. They are simply passed along. The object 
28389  	 * may contain any property/value pairs as long as the calling code is in
28390  	 * agreement with the loader callback function as to what those parameters mean.
28391  	 * </ul>
28392 	 * 
28393 	 * @param {PhoneNumber} number phone number to locate
28394 	 * @param {Object} options options governing the way this ares is loaded
28395 	 * @return {Object} an object  
28396 	 * that describes the country and the area in that country corresponding to this
28397 	 * phone number. Each of the country and area contain a short name (sn) and long
28398 	 * name (ln) that describes the location.
28399 	 */
28400 	locate: function(number, options) {
28401 		var loadParams = {},
28402 			ret = {}, 
28403 			region, 
28404 			countryCode, 
28405 			temp, 
28406 			plan,
28407 			areaResult,
28408 			phoneLoc = this.locale,
28409 			sync = true;
28410 
28411 		if (number === undefined || typeof(number) !== 'object' || !(number instanceof PhoneNumber)) {
28412 			return ret;
28413 		}
28414 
28415 		if (options) {
28416 			if (typeof(options.sync) !== 'undefined') {
28417 				sync = (options.sync == true);
28418 			}
28419 		
28420 			if (options.loadParams) {
28421 				loadParams = options.loadParams;
28422 			}
28423 		}
28424 
28425 		// console.log("GeoLocator.locate: looking for geo for number " + JSON.stringify(number));
28426 		region = this.locale.getRegion();
28427 		if (number.countryCode !== undefined && this.regiondata) {
28428 			countryCode = number.countryCode.replace(/[wWpPtT\+#\*]/g, '');
28429 			temp = this.regiondata[countryCode];
28430 			phoneLoc = number.destinationLocale;
28431 			plan = number.destinationPlan;
28432 			ret.country = {
28433 				sn: this.rb.getString(temp.sn).toString(),
28434 				ln: this.rb.getString(temp.ln).toString(),
28435 				code: phoneLoc.getRegion()
28436 			};
28437 		}
28438 		
28439 		if (!plan) {
28440 			plan = this.plan;
28441 		}
28442 		
28443 		Utils.loadData({
28444 			name: "area.json",
28445 			object: PhoneGeoLocator,
28446 			locale: phoneLoc,
28447 			sync: sync,
28448 			loadParams: JSUtils.merge(loadParams, {
28449 				returnOne: true
28450 			}),
28451 			callback: ilib.bind(this, function (areadata) {
28452 				if (areadata) {
28453 					this.areadata = areadata;	
28454 				}
28455 				areaResult = this._getAreaInfo(number, this.areadata, phoneLoc, plan, options);
28456 				ret = JSUtils.merge(ret, areaResult);
28457 
28458 				if (ret.country === undefined) {
28459 					countryCode = number.locale._mapRegiontoCC(region);
28460 					
28461 					if (countryCode !== "0" && this.regiondata) {
28462 						temp = this.regiondata[countryCode];
28463 						if (temp && temp.sn) {
28464 							ret.country = {
28465 								sn: this.rb.getString(temp.sn).toString(),
28466 								ln: this.rb.getString(temp.ln).toString(),
28467 								code: this.locale.getRegion()
28468 							};
28469 						}
28470 					}
28471 				}
28472 			})
28473 		});
28474 		
28475 		return ret;
28476 	},
28477 	
28478 	/**
28479 	 * Returns a string that describes the ISO-3166-2 country code of the given phone
28480 	 * number.<p> 
28481 	 * 
28482 	 * If the phone number is a local phone number and does not contain
28483 	 * any country information, this routine will return the region for the current
28484 	 * formatter instance.
28485      *
28486 	 * @param {PhoneNumber} number An PhoneNumber instance
28487 	 * @return {string}
28488 	 */
28489 	country: function(number) {
28490 		var countryCode,
28491 			region,
28492 			phoneLoc;
28493 
28494 		if (!number || !(number instanceof PhoneNumber)) {
28495 			return "";
28496 		}
28497 
28498 		phoneLoc = number.locale;
28499 
28500 		region = (number.countryCode && phoneLoc._mapCCtoRegion(number.countryCode)) ||
28501 			(number.locale && number.locale.region) || 
28502 			phoneLoc.locale.getRegion() ||
28503 			this.locale.getRegion();
28504 
28505 		countryCode = number.countryCode || phoneLoc._mapRegiontoCC(region);
28506 		
28507 		if (number.areaCode) {
28508 			region = phoneLoc._mapAreatoRegion(countryCode, number.areaCode);
28509 		} else if (countryCode === "33" && number.serviceCode) {
28510 			// french departments are in the service code, not the area code
28511 			region = phoneLoc._mapAreatoRegion(countryCode, number.serviceCode);
28512 		}		
28513 		return region;
28514 	}
28515 };
28516 
28517 
28518 /*< Measurement.js */
28519 /*
28520  * Measurement.js - Measurement unit superclass
28521  * 
28522  * Copyright © 2014-2015, JEDLSoft
28523  *
28524  * Licensed under the Apache License, Version 2.0 (the "License");
28525  * you may not use this file except in compliance with the License.
28526  * You may obtain a copy of the License at
28527  *
28528  *     http://www.apache.org/licenses/LICENSE-2.0
28529  *
28530  * Unless required by applicable law or agreed to in writing, software
28531  * distributed under the License is distributed on an "AS IS" BASIS,
28532  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28533  *
28534  * See the License for the specific language governing permissions and
28535  * limitations under the License.
28536  */
28537 
28538 /**
28539  * @class
28540  * Superclass for measurement instances that contains shared functionality
28541  * and defines the interface. <p>
28542  * 
28543  * This class is never instantiated on its own. Instead, measurements should
28544  * be created using the {@link MeasurementFactory} function, which creates the
28545  * correct subclass based on the given parameters.<p>
28546  * 
28547  * @private
28548  * @constructor 
28549  */
28550 var Measurement = function() {
28551 };
28552 
28553 /**
28554  * @private
28555  */
28556 Measurement._constructors = {};
28557 
28558 Measurement.prototype = {
28559 	/**
28560 	 * Return the normalized name of the given units. If the units are
28561 	 * not recognized, this method returns its parameter unmodified.<p>
28562 	 * 
28563 	 * Examples:
28564 	 * 
28565 	 * <ui>
28566 	 * <li>"metres" gets normalized to "meter"<br>
28567 	 * <li>"ml" gets normalized to "milliliter"<br>
28568 	 * <li>"foobar" gets normalized to "foobar" (no change because it is not recognized)
28569 	 * </ul>
28570 	 *  
28571 	 * @param {string} name name of the units to normalize. 
28572 	 * @returns {string} normalized name of the units
28573 	 */
28574 	normalizeUnits: function(name) {
28575 		return this.aliases[name] || name;
28576 	},
28577 
28578 	/**
28579 	 * Return the normalized units used in this measurement.
28580 	 * @return {string} name of the unit of measurement 
28581 	 */
28582 	getUnit: function() {
28583 		return this.unit;
28584 	},
28585      
28586 	/**
28587 	 * Return the units originally used to construct this measurement
28588 	 * before it was normalized.
28589 	 * @return {string} name of the unit of measurement 
28590 	 */
28591 	getOriginalUnit: function() {
28592 		return this.originalUnit;
28593 	},
28594 	
28595 	/**
28596 	 * Return the numeric amount of this measurement.
28597 	 * @return {number} the numeric amount of this measurement
28598 	 */
28599 	getAmount: function() {
28600 		return this.amount;
28601 	},
28602 	
28603 	/**
28604 	 * Return the type of this measurement. Examples are "mass",
28605 	 * "length", "speed", etc. Measurements can only be converted
28606 	 * to measurements of the same type.<p>
28607 	 * 
28608 	 * The type of the units is determined automatically from the 
28609 	 * units. For example, the unit "grams" is type "mass". Use the 
28610 	 * static call {@link Measurement.getAvailableUnits}
28611 	 * to find out what units this version of ilib supports.
28612 	 * 
28613 	 * @return {string} the name of the type of this measurement
28614 	 */
28615 	getMeasure: function() {},
28616 	
28617 	/**
28618 	 * Return a new measurement instance that is converted to a new
28619 	 * measurement unit. Measurements can only be converted
28620 	 * to measurements of the same type.<p>
28621 	 * 
28622 	 * @param {string} to The name of the units to convert to
28623 	 * @return {Measurement|undefined} the converted measurement
28624 	 * or undefined if the requested units are for a different
28625 	 * measurement type
28626 	 */
28627 	convert: function(to) {},     
28628         
28629     /**
28630 	 * Scale the measurement unit to an acceptable level. The scaling
28631 	 * happens so that the integer part of the amount is as small as
28632 	 * possible without being below zero. This will result in the 
28633 	 * largest units that can represent this measurement without
28634 	 * fractions. Measurements can only be scaled to other measurements 
28635 	 * of the same type.
28636 	 * 
28637 	 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
28638 	 * or undefined if the system can be inferred from the current measure
28639 	 * @return {Measurement} a new instance that is scaled to the 
28640 	 * right level
28641 	 */
28642 	scale: function(measurementsystem) {},
28643         
28644 	/**
28645 	 * Localize the measurement to the commonly used measurement in that locale, for example
28646 	 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
28647 	 * the formatted number should be automatically converted to the most appropriate 
28648 	 * measure in the other system, in this case, mph. The formatted result should
28649 	 * appear as "37.3 mph". 
28650 	 * 
28651 	 * @param {string} locale current locale string
28652 	 * @returns {Measurement} a new instance that is converted to locale
28653 	 */
28654 	localize: function(locale) {}
28655 };
28656 
28657 
28658 
28659 /*< UnknownUnit.js */
28660 /*
28661  * Unknown.js - Dummy unit conversions for unknown types
28662  * 
28663  * Copyright © 2014-2015, JEDLSoft
28664  *
28665  * Licensed under the Apache License, Version 2.0 (the "License");
28666  * you may not use this file except in compliance with the License.
28667  * You may obtain a copy of the License at
28668  *
28669  *     http://www.apache.org/licenses/LICENSE-2.0
28670  *
28671  * Unless required by applicable law or agreed to in writing, software
28672  * distributed under the License is distributed on an "AS IS" BASIS,
28673  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28674  *
28675  * See the License for the specific language governing permissions and
28676  * limitations under the License.
28677  */
28678 
28679 // !depends Measurement.js
28680 
28681 
28682 /**
28683  * @class
28684  * Create a new unknown measurement instance.
28685  * 
28686  * @constructor
28687  * @extends Measurement
28688  * @param options {{unit:string,amount:number|string|undefined}} Options controlling 
28689  * the construction of this instance
28690  */
28691 var UnknownUnit = function (options) {
28692 	if (options) {
28693 		this.unit = options.unit;
28694 		this.amount = options.amount;
28695 	}
28696 };
28697 
28698 UnknownUnit.prototype = new Measurement();
28699 UnknownUnit.prototype.parent = Measurement;
28700 UnknownUnit.prototype.constructor = UnknownUnit;
28701 
28702 UnknownUnit.aliases = {
28703 	"unknown":"unknown"
28704 };
28705 
28706 /**
28707  * Return the type of this measurement. Examples are "mass",
28708  * "length", "speed", etc. Measurements can only be converted
28709  * to measurements of the same type.<p>
28710  * 
28711  * The type of the units is determined automatically from the 
28712  * units. For example, the unit "grams" is type "mass". Use the 
28713  * static call {@link Measurement.getAvailableUnits}
28714  * to find out what units this version of ilib supports.
28715  *  
28716  * @return {string} the name of the type of this measurement
28717  */
28718 UnknownUnit.prototype.getMeasure = function() {
28719 	return "unknown";
28720 };
28721 
28722 /**
28723  * Return a new measurement instance that is converted to a new
28724  * measurement unit. Measurements can only be converted
28725  * to measurements of the same type.<p>
28726  *  
28727  * @param {string} to The name of the units to convert to
28728  * @return {Measurement|undefined} the converted measurement
28729  * or undefined if the requested units are for a different
28730  * measurement type 
28731  */
28732 UnknownUnit.prototype.convert = function(to) {
28733 	return undefined;
28734 };
28735 
28736 /**
28737  * Convert a unknown to another measure.
28738  * @static
28739  * @param {string} to unit to convert to
28740  * @param {string} from unit to convert from
28741  * @param {number} unknown amount to be convert
28742  * @returns {number|undefined} the converted amount
28743  */
28744 UnknownUnit.convert = function(to, from, unknown) {
28745     return undefined;
28746 };
28747 
28748 /**
28749  * Localize the measurement to the commonly used measurement in that locale. For example
28750  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
28751  * the formatted number should be automatically converted to the most appropriate 
28752  * measure in the other system, in this case, mph. The formatted result should
28753  * appear as "37.3 mph". 
28754  * 
28755  * @param {string} locale current locale string
28756  * @returns {Measurement} a new instance that is converted to locale
28757  */
28758 UnknownUnit.prototype.localize = function(locale) {
28759     return new UnknownUnit({
28760         unit: this.unit,
28761         amount: this.amount
28762     });
28763 };
28764 
28765 /**
28766  * Scale the measurement unit to an acceptable level. The scaling
28767  * happens so that the integer part of the amount is as small as
28768  * possible without being below zero. This will result in the 
28769  * largest units that can represent this measurement without
28770  * fractions. Measurements can only be scaled to other measurements 
28771  * of the same type.
28772  * 
28773  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
28774  * or undefined if the system can be inferred from the current measure
28775  * @return {Measurement} a new instance that is scaled to the 
28776  * right level
28777  */
28778 UnknownUnit.prototype.scale = function(measurementsystem) {
28779     return new UnknownUnit({
28780         unit: this.unit,
28781         amount: this.amount
28782     }); 
28783 };
28784 
28785 /**
28786  * @private
28787  * @static
28788  */
28789 UnknownUnit.getMeasures = function () {
28790 	return [];
28791 };
28792 
28793 
28794 /*< AreaUnit.js */
28795 /*
28796  * area.js - Unit conversions for Area
28797  * 
28798  * Copyright © 2014-2015, JEDLSoft
28799  *
28800  * Licensed under the Apache License, Version 2.0 (the "License");
28801  * you may not use this file except in compliance with the License.
28802  * You may obtain a copy of the License at
28803  *
28804  *     http://www.apache.org/licenses/LICENSE-2.0
28805  *
28806  * Unless required by applicable law or agreed to in writing, software
28807  * distributed under the License is distributed on an "AS IS" BASIS,
28808  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28809  *
28810  * See the License for the specific language governing permissions and
28811  * limitations under the License.
28812  */
28813 
28814 /*
28815 !depends 
28816 Measurement.js
28817 */
28818 
28819 
28820 /**
28821  * @class
28822  * Create a new area measurement instance.
28823  * @constructor
28824  * @extends Measurement
28825  * @param options {{unit:string,amount:number|string|undefined}} Options controlling 
28826  * the construction of this instance
28827  */
28828 var AreaUnit = function (options) {
28829 	this.unit = "square meter";
28830 	this.amount = 0;
28831 	this.aliases = AreaUnit.aliases; // share this table in all instances
28832 	
28833 	if (options) {
28834 		if (typeof(options.unit) !== 'undefined') {
28835 			this.originalUnit = options.unit;
28836 			this.unit = this.aliases[options.unit] || options.unit;
28837 		}
28838 		
28839 		if (typeof(options.amount) === 'object') {
28840 			if (options.amount.getMeasure() === "area") {
28841 				this.amount = AreaUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
28842 			} else {
28843 				throw "Cannot convert unit " + options.amount.unit + " to area";
28844 			}
28845 		} else if (typeof(options.amount) !== 'undefined') {
28846 			this.amount = parseFloat(options.amount);
28847 		}
28848 	}
28849 	
28850 	if (typeof(AreaUnit.ratios[this.unit]) === 'undefined') {
28851 		throw "Unknown unit: " + options.unit;
28852 	}
28853 };
28854 
28855 AreaUnit.prototype = new Measurement();
28856 AreaUnit.prototype.parent = Measurement;
28857 AreaUnit.prototype.constructor = AreaUnit;
28858 
28859 AreaUnit.ratios = {
28860     /*               index		square cm,		square meter,   hectare,   	square km, 	, square inch 	square foot, 		square yard, 	  	  		acre,			square mile			        */
28861     "square centimeter":[1,   	1,				0.0001,			1e-8,	    1e-10,        0.15500031,	0.00107639104,		0.000119599005,			2.47105381e-8,		3.86102159e-11 		],
28862     "square meter": 	[2,   	10000,			1,              1e-4,       1e-6,         1550,    	 	10.7639,    	  	1.19599,   				0.000247105,		3.861e-7     	    ],
28863     "hectare":      	[3,	 	100000000,  	10000,          1,          0.01,         1.55e+7, 	  	107639,     	 	11959.9,   				2.47105	,			0.00386102    	    ],
28864     "square km":    	[4,	  	10000000000, 	1e+6,          	100,        1,	          1.55e+9, 	  	1.076e+7,   	 	1.196e+6,  				247.105 ,   		0.386102     	    ],
28865     "square inch":  	[5,	  	6.4516,			0.00064516,     6.4516e-8,  6.4516e-10,   1,			0.000771605,	  	0.0007716051, 			1.5942e-7,			2.491e-10    	    ],
28866     "square foot":  	[6,		929.0304,		0.092903,       9.2903e-6,  9.2903e-8,    144,			1,          	  	0.111111,  				2.2957e-5,			3.587e-8    		],
28867     "square yard":  	[7,		8361.2736,		0.836127,       8.3613e-5,  8.3613e-7,    1296,    	  	9,          	  	1,         				0.000206612,		3.2283e-7    	    ],
28868     "acre":         	[8,		40468564.2,		4046.86,        0.404686,   0.00404686,   6.273e+6,	  	43560,      	  	4840,      				1,		    		0.0015625    	    ],
28869     "square mile":  	[9,	   	2.58998811e+10,	2.59e+6,        258.999,    2.58999,      4.014e+9,	 	2.788e+7,   	  	3.098e+6,  				640,     			1   	     		]
28870 }
28871 
28872 /**
28873  * Return the type of this measurement. Examples are "mass",
28874  * "length", "speed", etc. Measurements can only be converted
28875  * to measurements of the same type.<p>
28876  * 
28877  * The type of the units is determined automatically from the 
28878  * units. For example, the unit "grams" is type "mass". Use the 
28879  * static call {@link Measurement.getAvailableUnits}
28880  * to find out what units this version of ilib supports.
28881  *  
28882  * @return {string} the name of the type of this measurement
28883  */
28884 AreaUnit.prototype.getMeasure = function() {
28885 	return "area";
28886 }; 
28887 
28888 /**
28889  * Return a new measurement instance that is converted to a new
28890  * measurement unit. Measurements can only be converted
28891  * to measurements of the same type.<p>
28892  *  
28893  * @param {string} to The name of the units to convert to
28894  * @return {Measurement|undefined} the converted measurement
28895  * or undefined if the requested units are for a different
28896  * measurement type
28897  * 
28898  */
28899 AreaUnit.prototype.convert = function(to) {
28900 	if (!to || typeof(AreaUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
28901 		return undefined;
28902 	}
28903 	return new AreaUnit({
28904 		unit: to, 
28905 		amount: this
28906 	});
28907 };
28908 
28909 AreaUnit.aliases = {
28910     "square centimeter":"square centimeter",
28911     "square cm":"square centimeter",
28912     "sq cm":"square centimeter",
28913     "Square Cm":"square centimeter",
28914     "square Centimeters":"square centimeter",
28915     "square Centimeter":"square centimeter",
28916     "square Centimetre":"square centimeter",
28917     "square Centimetres":"square centimeter",
28918     "square centimeters":"square centimeter",
28919     "Square km": "square km",
28920 	"Square kilometre":"square km",
28921 	"square kilometer":"square km",
28922 	"square kilometre":"square km",
28923 	"square kilometers":"square km",
28924 	"square kilometres":"square km",
28925     "square km":"square km",
28926 	"sq km":"square km",
28927 	"km2":"square km",
28928 	"Hectare":"hectare",
28929 	"hectare":"hectare",
28930 	"ha":"hectare",
28931 	"Square meter": "square meter",
28932 	"Square meters":"square meter",
28933 	"square meter": "square meter",
28934 	"square meters":"square meter",
28935 	"Square metre": "square meter",
28936 	"Square metres":"square meter",
28937 	"square metres": "square meter",
28938 	"Square Metres":"square meter",
28939 	"sqm":"square meter",
28940 	"m2": "square meter",
28941 	"Square mile":"square mile",
28942 	"Square miles":"square mile",
28943 	"square mile":"square mile",
28944 	"square miles":"square mile",
28945 	"square mi":"square mile",
28946 	"Square mi":"square mile",
28947 	"sq mi":"square mile",
28948 	"mi2":"square mile",
28949 	"Acre": "acre",
28950 	"acre": "acre",
28951 	"Acres":"acre",
28952 	"acres":"acre",
28953 	"Square yard": "square yard",
28954 	"Square yards":"square yard",
28955 	"square yard": "square yard",
28956 	"square yards":"square yard",
28957 	"yd2":"square yard",
28958 	"Square foot": "square foot",
28959 	"square foot": "square foot",
28960 	"Square feet": "square foot",
28961 	"Square Feet": "square foot",
28962 	"sq ft":"square foot",
28963 	"ft2":"square foot",
28964 	"Square inch":"square inch",
28965 	"square inch":"square inch",
28966 	"Square inches":"square inch",
28967 	"square inches":"square inch",
28968 	"in2":"square inch"
28969 };
28970 
28971 /**
28972  * Convert a Area to another measure.
28973  * @static
28974  * @param to {string} unit to convert to
28975  * @param from {string} unit to convert from
28976  * @param area {number} amount to be convert
28977  * @returns {number|undefined} the converted amount
28978  */
28979 AreaUnit.convert = function(to, from, area) {
28980     from = AreaUnit.aliases[from] || from;
28981     to = AreaUnit.aliases[to] || to;
28982 	var fromRow = AreaUnit.ratios[from];
28983 	var toRow = AreaUnit.ratios[to];
28984 	if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
28985 		return undefined;
28986 	}
28987 	return area* fromRow[toRow[0]];
28988 };
28989 
28990 /**
28991  * @private
28992  * @static
28993  */
28994 AreaUnit.getMeasures = function () {
28995 	var ret = [];
28996 	for (var m in AreaUnit.ratios) {
28997 		ret.push(m);
28998 	}
28999 	return ret;
29000 };
29001 
29002 AreaUnit.metricSystem = {
29003 	"square centimeter" : 1,
29004 	"square meter" : 2,
29005 	"hectare" : 3,
29006 	"square km" : 4
29007 };
29008 AreaUnit.imperialSystem = {
29009 	"square inch" : 5,
29010 	"square foot" : 6,
29011 	"square yard" : 7,
29012 	"acre" : 8,
29013 	"square mile" : 9
29014 };
29015 AreaUnit.uscustomarySystem = {
29016 	"square inch" : 5,
29017 	"square foot" : 6,
29018 	"square yard" : 7,
29019 	"acre" : 8,
29020 	"square mile" : 9
29021 };
29022 
29023 AreaUnit.metricToUScustomary = {
29024 	"square centimeter" : "square inch",
29025 	"square meter" : "square yard",
29026 	"hectare" : "acre",
29027 	"square km" : "square mile"
29028 };
29029 AreaUnit.usCustomaryToMetric = {
29030 	"square inch" : "square centimeter",
29031 	"square foot" : "square meter",
29032 	"square yard" : "square meter",
29033 	"acre" : "hectare",
29034 	"square mile" : "square km"
29035 };
29036 
29037 
29038 /**
29039  * Scale the measurement unit to an acceptable level. The scaling
29040  * happens so that the integer part of the amount is as small as
29041  * possible without being below zero. This will result in the 
29042  * largest units that can represent this measurement without
29043  * fractions. Measurements can only be scaled to other measurements 
29044  * of the same type.
29045  * 
29046  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
29047  * or undefined if the system can be inferred from the current measure
29048  * @return {Measurement} a new instance that is scaled to the 
29049  * right level
29050  */
29051 AreaUnit.prototype.scale = function(measurementsystem) {
29052     var fromRow = AreaUnit.ratios[this.unit];
29053     var mSystem;
29054 
29055     if (measurementsystem === "metric" || (typeof(measurementsystem) === 'undefined'
29056         && typeof(AreaUnit.metricSystem[this.unit]) !== 'undefined')) {
29057         mSystem = AreaUnit.metricSystem;
29058     } else if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined'
29059         && typeof(AreaUnit.uscustomarySystem[this.unit]) !== 'undefined')) {
29060         mSystem = AreaUnit.uscustomarySystem;
29061     } else if (measurementsystem === "imperial" || (typeof(measurementsystem) === 'undefined'
29062         && typeof(AreaUnit.imperialSystem[this.unit]) !== 'undefined')) {
29063         mSystem = AreaUnit.imperialSystem;
29064     }
29065 
29066     var area = this.amount;
29067     var munit = this.unit;
29068 
29069     area = 18446744073709551999;
29070     
29071     for (var m in mSystem) {
29072         var tmp = this.amount * fromRow[mSystem[m]];
29073         if (tmp >= 1 && tmp < area) {
29074 	        area = tmp;
29075 	        munit = m;
29076         }
29077     }
29078 
29079     return new AreaUnit({
29080         unit: munit,
29081         amount: area
29082     });
29083 };
29084 
29085 /**
29086  * Localize the measurement to the commonly used measurement in that locale. For example
29087  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
29088  * the formatted number should be automatically converted to the most appropriate 
29089  * measure in the other system, in this case, mph. The formatted result should
29090  * appear as "37.3 mph". 
29091  * 
29092  * @param {string} locale current locale string
29093  * @returns {Measurement} a new instance that is converted to locale
29094  */
29095 AreaUnit.prototype.localize = function(locale) {
29096     var to;
29097     if (locale === "en-US" || locale === "en-GB") {
29098         to = AreaUnit.metricToUScustomary[this.unit] || this.unit;
29099     } else {
29100         to = AreaUnit.usCustomaryToMetric[this.unit] || this.unit;
29101     }
29102     return new AreaUnit({
29103         unit: to,
29104         amount: this
29105     });
29106 };
29107 
29108 
29109 //register with the factory method
29110 Measurement._constructors["area"] = AreaUnit;
29111 
29112 
29113 /*< DigitalStorageUnit.js */
29114 /*
29115  * digitalStorage.js - Unit conversions for Digital Storage
29116  * 
29117  * Copyright © 2014-2015, JEDLSoft
29118  *
29119  * Licensed under the Apache License, Version 2.0 (the "License");
29120  * you may not use this file except in compliance with the License.
29121  * You may obtain a copy of the License at
29122  *
29123  *     http://www.apache.org/licenses/LICENSE-2.0
29124  *
29125  * Unless required by applicable law or agreed to in writing, software
29126  * distributed under the License is distributed on an "AS IS" BASIS,
29127  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29128  *
29129  * See the License for the specific language governing permissions and
29130  * limitations under the License.
29131  */
29132 
29133 /*
29134 !depends 
29135 Measurement.js
29136 */
29137 
29138 
29139 /**
29140  * @class
29141  * Create a new DigitalStorage measurement instance.
29142  *  
29143  * @constructor
29144  * @extends Measurement
29145  * @param options {{unit:string,amount:number|string|undefined}} Options controlling 
29146  * the construction of this instance
29147  */
29148 var DigitalStorageUnit = function (options) {
29149 	this.unit = "byte";
29150 	this.amount = 0;
29151 	this.aliases = DigitalStorageUnit.aliases; // share this table in all instances
29152 	
29153 	if (options) {
29154 		if (typeof(options.unit) !== 'undefined') {
29155 			this.originalUnit = options.unit;
29156 			this.unit = this.aliases[options.unit] || options.unit;
29157 		}
29158 		
29159 		if (typeof(options.amount) === 'object') {
29160 			if (options.amount.getMeasure() === "digitalStorage") {
29161 				this.amount = DigitalStorageUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
29162 			} else {
29163 				throw "Cannot convert unit " + options.amount.unit + " to a digitalStorage";
29164 			}
29165 		} else if (typeof(options.amount) !== 'undefined') {
29166 			this.amount = parseFloat(options.amount);
29167 		}
29168 	}
29169 	
29170 	if (typeof(DigitalStorageUnit.ratios[this.unit]) === 'undefined') {
29171 		throw "Unknown unit: " + options.unit;
29172 	}
29173 };
29174 
29175 DigitalStorageUnit.prototype = new Measurement();
29176 DigitalStorageUnit.prototype.parent = Measurement;
29177 DigitalStorageUnit.prototype.constructor = DigitalStorageUnit;
29178 
29179 DigitalStorageUnit.ratios = {
29180     /*            #    bit             byte            kb              kB              mb              mB              gb               gB               tb               tB               pb               pB   */           
29181 	"bit":      [ 1,   1,              0.125,          0.0009765625,   1.220703125e-4, 9.536743164e-7, 1.192092896e-7, 9.313225746e-10, 1.164153218e-10, 9.094947017e-13, 1.136868377e-13, 8.881784197e-16, 1.110223025e-16 ],
29182     "byte":     [ 2,   8,              1,              0.0078125,      0.0009765625,   7.629394531e-6, 9.536743164e-7, 7.450580597e-9,  9.313225746e-10, 7.275957614e-12, 9.094947017e-13, 7.105427358e-15, 8.881784197e-16 ],
29183     "kilobit":  [ 3,   1024,           128,            1,              0.125,          0.0009765625,   1.220703125e-4, 9.536743164e-7,  1.192092896e-7,  9.313225746e-10, 1.164153218e-10, 9.094947017e-13, 1.136868377e-13 ],
29184     "kilobyte": [ 4,   8192,           1024,           8,              1,              0.0078125,      0.0009765625,   7.629394531e-6,  9.536743164e-7,  7.450580597e-9,  9.313225746e-10, 7.275957614e-12, 9.094947017e-13 ],
29185     "megabit":  [ 5,   1048576,        131072,         1024,           128,            1,              0.125,          0.0009765625,    1.220703125e-4,  9.536743164e-7,  1.192092896e-7,  9.313225746e-10, 1.164153218e-10 ],
29186     "megabyte": [ 6,   8388608,        1048576,        8192,           1024,           8,              1,              0.0078125,       0.0009765625,    7.629394531e-6,  9.536743164e-7,  7.450580597e-9,  9.313225746e-10 ],
29187     "gigabit":  [ 7,   1073741824,     134217728,      1048576,        131072,         1024,           128,            1,               0.125,           0.0009765625,    1.220703125e-4,  9.536743164e-7,  1.192092896e-7  ],
29188     "gigabyte": [ 8,   8589934592,     1073741824,     8388608,        1048576,        8192,           1024,           8,               1,               0.0078125,       0.0009765625,    7.629394531e-6,  9.536743164e-7  ],
29189     "terabit":  [ 9,   1.099511628e12, 137438953472,   1073741824,     134217728,      1048576,        131072,         1024,            128,             1,               0.125,           0.0009765625,    1.220703125e-4  ],
29190     "terabyte": [ 10,  8.796093022e12, 1.099511628e12, 8589934592,     1073741824,     8388608,        1048576,        8192,            1024,            8,               1,               0.0078125,       0.0009765625    ],
29191     "petabit":  [ 11,  1.125899907e15, 1.407374884e14, 1.099511628e12, 137438953472,   1073741824,     134217728,      1048576,         131072,          1024,            128,             1,               0.125           ],
29192     "petabyte": [ 12,  9.007199255e15, 1.125899907e15, 8.796093022e12, 1.099511628e12, 8589934592,     1073741824,     8388608,         1048576,         8192,            1024,            8,               1               ]
29193 };
29194 
29195 DigitalStorageUnit.bitSystem = {
29196     "bit":      1,
29197     "kilobit":  3,
29198     "megabit":  5,
29199     "gigabit":  7,
29200     "terabit":  9,
29201     "petabit":  11
29202 };
29203 DigitalStorageUnit.byteSystem = {
29204     "byte":     2,
29205     "kilobyte": 4,
29206     "megabyte": 6,
29207     "gigabyte": 8,
29208     "terabyte": 10,
29209     "petabyte": 12
29210 };
29211 
29212 /**
29213  * Return the type of this measurement. Examples are "mass",
29214  * "length", "speed", etc. Measurements can only be converted
29215  * to measurements of the same type.<p>
29216  * 
29217  * The type of the units is determined automatically from the 
29218  * units. For example, the unit "grams" is type "mass". Use the 
29219  * static call {@link Measurement.getAvailableUnits}
29220  * to find out what units this version of ilib supports.
29221  *  
29222  * @return {string} the name of the type of this measurement
29223  */
29224 DigitalStorageUnit.prototype.getMeasure = function() {
29225 	return "digitalStorage";
29226 };
29227 
29228 /**
29229  * Return a new measurement instance that is converted to a new
29230  * measurement unit. Measurements can only be converted
29231  * to measurements of the same type.<p>
29232  *  
29233  * @param {string} to The name of the units to convert to
29234  * @return {Measurement|undefined} the converted measurement
29235  * or undefined if the requested units are for a different
29236  * measurement type
29237  * 
29238  */
29239 DigitalStorageUnit.prototype.convert = function(to) {
29240 	if (!to || typeof(DigitalStorageUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
29241 		return undefined;
29242 	}
29243 	return new DigitalStorageUnit({
29244 		unit: to,
29245 		amount: this
29246 	});
29247 };
29248 
29249 /**
29250  * Localize the measurement to the commonly used measurement in that locale. For example
29251  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
29252  * the formatted number should be automatically converted to the most appropriate 
29253  * measure in the other system, in this case, mph. The formatted result should
29254  * appear as "37.3 mph". 
29255  * 
29256  * @param {string} locale current locale string
29257  * @returns {Measurement} a new instance that is converted to locale
29258  */
29259 DigitalStorageUnit.prototype.localize = function(locale) {
29260     return new DigitalStorageUnit({
29261         unit: this.unit,
29262         amount: this.amount
29263     });
29264 };
29265 
29266 /**
29267  * Scale the measurement unit to an acceptable level. The scaling
29268  * happens so that the integer part of the amount is as small as
29269  * possible without being below zero. This will result in the 
29270  * largest units that can represent this measurement without
29271  * fractions. Measurements can only be scaled to other measurements 
29272  * of the same type.
29273  * 
29274  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
29275  * or undefined if the system can be inferred from the current measure
29276  * @return {Measurement} a new instance that is scaled to the 
29277  * right level
29278  */
29279 DigitalStorageUnit.prototype.scale = function(measurementsystem) {
29280     var mSystem;
29281     if (this.unit in DigitalStorageUnit.bitSystem) {
29282     	mSystem = DigitalStorageUnit.bitSystem;
29283     } else {
29284     	mSystem = DigitalStorageUnit.byteSystem;
29285     }
29286     
29287     var dStorage = this.amount;
29288     var munit = this.unit;
29289     var fromRow = DigitalStorageUnit.ratios[this.unit];
29290     
29291     dStorage = 18446744073709551999;
29292     for (var m in mSystem) {
29293     	var tmp = this.amount * fromRow[mSystem[m]];
29294         if (tmp >= 1 && tmp < dStorage) {
29295         	dStorage = tmp;
29296 	        munit = m;
29297         }
29298     }
29299     
29300     return new DigitalStorageUnit({
29301 		unit: munit,
29302 		amount: dStorage
29303     });
29304 };
29305 
29306 DigitalStorageUnit.aliases = {
29307     "bits": "bit",
29308     "bit": "bit",
29309     "Bits": "bit",
29310     "Bit": "bit",
29311     "byte": "byte",
29312     "bytes": "byte",
29313     "Byte": "byte",
29314     "Bytes": "byte",
29315     "kilobits": "kilobit",
29316     "Kilobits": "kilobit",
29317     "KiloBits": "kilobit",
29318     "kiloBits": "kilobit",
29319     "kilobit": "kilobit",
29320     "Kilobit": "kilobit",
29321     "kiloBit": "kilobit",
29322     "KiloBit": "kilobit",
29323     "kb": "kilobit",
29324     "Kb": "kilobit",
29325     "kilobyte": "kilobyte",
29326     "Kilobyte": "kilobyte",
29327     "kiloByte": "kilobyte",
29328     "KiloByte": "kilobyte",
29329     "kilobytes": "kilobyte",
29330     "Kilobytes": "kilobyte",
29331     "kiloBytes": "kilobyte",
29332     "KiloBytes": "kilobyte",
29333     "kB": "kilobyte",
29334     "KB": "kilobyte",
29335     "megabit": "megabit",
29336     "Megabit": "megabit",
29337     "megaBit": "megabit",
29338     "MegaBit": "megabit",
29339     "megabits": "megabit",
29340     "Megabits": "megabit",
29341     "megaBits": "megabit",
29342     "MegaBits": "megabit",
29343     "Mb": "megabit",
29344     "mb": "megabit",
29345     "megabyte": "megabyte",
29346     "Megabyte": "megabyte",
29347     "megaByte": "megabyte",
29348     "MegaByte": "megabyte",
29349     "megabytes": "megabyte",
29350     "Megabytes": "megabyte",
29351     "megaBytes": "megabyte",
29352     "MegaBytes": "megabyte",
29353     "MB": "megabyte",
29354     "mB": "megabyte",
29355     "gigabit": "gigabit",
29356     "Gigabit": "gigabit",
29357     "gigaBit": "gigabit",
29358     "GigaBit": "gigabit",
29359     "gigabits": "gigabit",
29360     "Gigabits": "gigabit",
29361     "gigaBits": "gigabyte",
29362     "GigaBits": "gigabit",
29363     "Gb": "gigabit",
29364     "gb": "gigabit",
29365     "gigabyte": "gigabyte",
29366     "Gigabyte": "gigabyte",
29367     "gigaByte": "gigabyte",
29368     "GigaByte": "gigabyte",
29369     "gigabytes": "gigabyte",
29370     "Gigabytes": "gigabyte",
29371     "gigaBytes": "gigabyte",
29372     "GigaBytes": "gigabyte",
29373     "GB": "gigabyte",
29374     "gB": "gigabyte",
29375     "terabit": "terabit",
29376     "Terabit": "terabit",
29377     "teraBit": "terabit",
29378     "TeraBit": "terabit",
29379     "terabits": "terabit",
29380     "Terabits": "terabit",
29381     "teraBits": "terabit",
29382     "TeraBits": "terabit",
29383     "tb": "terabit",
29384     "Tb": "terabit",
29385     "terabyte": "terabyte",
29386     "Terabyte": "terabyte",
29387     "teraByte": "terabyte",
29388     "TeraByte": "terabyte",
29389     "terabytes": "terabyte",
29390     "Terabytes": "terabyte",
29391     "teraBytes": "terabyte",
29392     "TeraBytes": "terabyte",
29393     "TB": "terabyte",
29394     "tB": "terabyte",
29395     "petabit": "petabit",
29396     "Petabit": "petabit",
29397     "petaBit": "petabit",
29398     "PetaBit": "petabit",
29399     "petabits": "petabit",
29400     "Petabits": "petabit",
29401     "petaBits": "petabit",
29402     "PetaBits": "petabit",
29403     "pb": "petabit",
29404     "Pb": "petabit",
29405     "petabyte": "petabyte",
29406     "Petabyte": "petabyte",
29407     "petaByte": "petabyte",
29408     "PetaByte": "petabyte",
29409     "petabytes": "petabyte",
29410     "Petabytes": "petabyte",
29411     "petaBytes": "petabyte",
29412     "PetaBytes": "petabyte",
29413     "PB": "petabyte",
29414     "pB": "petabyte"
29415 };
29416 
29417 /**
29418  * Convert a digitalStorage to another measure.
29419  * @static
29420  * @param to {string} unit to convert to
29421  * @param from {string} unit to convert from
29422  * @param digitalStorage {number} amount to be convert
29423  * @returns {number|undefined} the converted amount
29424  */
29425 DigitalStorageUnit.convert = function(to, from, digitalStorage) {
29426     from = DigitalStorageUnit.aliases[from] || from;
29427     to = DigitalStorageUnit.aliases[to] || to;
29428 	var fromRow = DigitalStorageUnit.ratios[from];
29429 	var toRow = DigitalStorageUnit.ratios[to];
29430 	if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
29431 		return undefined;
29432 	}	
29433 	var result = digitalStorage * fromRow[toRow[0]];
29434     return result;
29435 };
29436 
29437 /**
29438  * @private
29439  * @static
29440  */
29441 DigitalStorageUnit.getMeasures = function () {
29442 	var ret = [];
29443 	for (var m in DigitalStorageUnit.ratios) {
29444 		ret.push(m);
29445 	}
29446 	return ret;
29447 };
29448 
29449 //register with the factory method
29450 Measurement._constructors["digitalStorage"] = DigitalStorageUnit;
29451 
29452 
29453 /*< EnergyUnit.js */
29454 /*
29455  * Energy.js - Unit conversions for Energys/energys
29456  *
29457  * Copyright © 2014-2015, JEDLSoft
29458  *
29459  * Licensed under the Apache License, Version 2.0 (the "License");
29460  * you may not use this file except in compliance with the License.
29461  * You may obtain a copy of the License at
29462  *
29463  *     http://www.apache.org/licenses/LICENSE-2.0
29464  *
29465  * Unless required by applicable law or agreed to in writing, software
29466  * distributed under the License is distributed on an "AS IS" BASIS,
29467  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29468  *
29469  * See the License for the specific language governing permissions and
29470  * limitations under the License.
29471  */
29472 
29473 /*
29474 !depends
29475 Measurement.js
29476 */
29477 
29478 
29479 /**
29480  * @class
29481  * Create a new energy measurement instance.
29482  * 
29483  * @constructor
29484  * @extends Measurement
29485  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
29486  * the construction of this instance
29487  */
29488 var EnergyUnit = function (options) {
29489 	this.unit = "joule";
29490 	this.amount = 0;
29491 	this.aliases = EnergyUnit.aliases; // share this table in all instances
29492 
29493 	if (options) {
29494 		if (typeof(options.unit) !== 'undefined') {
29495 			this.originalUnit = options.unit;
29496 			this.unit = this.aliases[options.unit] || options.unit;
29497 		}
29498 
29499 		if (typeof(options.amount) === 'object') {
29500 			if (options.amount.getMeasure() === "energy") {
29501 				this.amount = EnergyUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
29502 			} else {
29503 				throw "Cannot convert units " + options.amount.unit + " to a energy";
29504 			}
29505 		} else if (typeof(options.amount) !== 'undefined') {
29506 			this.amount = parseFloat(options.amount);
29507 		}
29508 	}
29509 
29510 	if (typeof(EnergyUnit.ratios[this.unit]) === 'undefined') {
29511 		throw "Unknown unit: " + options.unit;
29512 	}
29513 };
29514 
29515 EnergyUnit.prototype = new Measurement();
29516 EnergyUnit.prototype.parent = Measurement;
29517 EnergyUnit.prototype.constructor = EnergyUnit;
29518 
29519 EnergyUnit.ratios = {
29520    /*                index mJ          J           BTU               kJ          Wh                Cal               MJ             kWh                gJ             MWh                 GWh         */
29521     "millijoule":   [ 1,   1,          0.001,      9.4781707775e-7,  1e-6,       2.7777777778e-7,  2.3884589663e-7,  1.0e-9,        2.7777777778e-10,  1.0e-12,       2.7777777778e-13,   2.7777777778e-16  ],
29522     "joule":        [ 2,   1000,       1,          9.4781707775e-4,  0.001,      2.7777777778e-4,  2.3884589663e-4,  1.0e-6,        2.7777777778e-7,   1.0e-9,        2.7777777778e-10,   2.7777777778e-13  ],
29523     "BTU":          [ 3,   1055055.9,  1055.0559,  1,                1.0550559,  0.29307108333,    0.25199577243,    1.0550559e-3,  2.9307108333e-4,   1.0550559e-6,  2.9307108333e-7,    2.9307108333e-10  ],
29524     "kilojoule":    [ 4,   1000000,    1000,       0.94781707775,    1,          0.27777777778,    0.23884589663,    0.001,         2.7777777778e-4,   1.0e-6,        2.7777777778e-7,    2.7777777778e-10  ],
29525     "watt hour":    [ 5,   3.6e+6,     3600,       3.4121414799,     3.6,        1,                0.85984522786,    0.0036,        0.001,             3.6e-6,        1.0e-6,             1.0e-9            ],
29526     "calorie":      [ 6,   4.868e+5,   4186.8,     3.9683205411,     4.1868,     1.163,            1,                4.1868e-3,     1.163e-3,          4.1868e-6,     1.163e-6,           1.163e-9          ],
29527     "megajoule":    [ 7,   1e+9,       1e+6,       947.81707775,     1000,       277.77777778,     238.84589663,     1,             0.27777777778,     0.001,         2.7777777778e-4,    2.7777777778e-7   ],
29528     "kilowatt hour":[ 8,   3.6e+9,     3.6e+6,     3412.1414799,     3600,       1000,             859.84522786,     3.6,           1,                 3.6e-3,        0.001,              1e-6              ],
29529     "gigajoule":    [ 9,   1e+12,      1e+9,       947817.07775,     1e+6,       277777.77778,     238845.89663,     1000,          277.77777778,      1,             0.27777777778,      2.7777777778e-4   ],
29530     "megawatt hour":[ 10,  3.6e+12,    3.6e+9,     3412141.4799,     3.6e+6,     1e+6,             859845.22786,     3600,          1000,              3.6,           1,                  0.001             ],
29531     "gigawatt hour":[ 11,  3.6e+15,    3.6e+12,    3412141479.9,     3.6e+9,     1e+9,             859845227.86,     3.6e+6,        1e+6,              3600,          1000,               1                 ]
29532 };
29533 
29534 /**
29535  * Return the type of this measurement. Examples are "mass",
29536  * "length", "speed", etc. Measurements can only be converted
29537  * to measurements of the same type.<p>
29538  * 
29539  * The type of the units is determined automatically from the 
29540  * units. For example, the unit "grams" is type "mass". Use the 
29541  * static call {@link Measurement.getAvailableUnits}
29542  * to find out what units this version of ilib supports.
29543  *  
29544  * @return {string} the name of the type of this measurement
29545  */
29546 EnergyUnit.prototype.getMeasure = function() {
29547 	return "energy";
29548 };
29549 
29550 /**
29551  * Return a new measurement instance that is converted to a new
29552  * measurement unit. Measurements can only be converted
29553  * to measurements of the same type.<p>
29554  *  
29555  * @param {string} to The name of the units to convert to
29556  * @return {Measurement|undefined} the converted measurement
29557  * or undefined if the requested units are for a different
29558  * measurement type 
29559  */
29560 EnergyUnit.prototype.convert = function(to) {
29561 	if (!to || typeof(EnergyUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
29562 		return undefined;
29563 	}
29564 	return new EnergyUnit({
29565 		unit: to,
29566 		amount: this
29567 	});
29568 };
29569 
29570 EnergyUnit.aliases = {
29571     "milli joule": "millijoule",
29572     "millijoule": "millijoule",
29573     "MilliJoule": "millijoule",
29574     "milliJ": "millijoule",
29575     "joule": "joule",
29576     "J": "joule",
29577     "j": "joule",
29578     "Joule": "joule",
29579     "Joules": "joule",
29580     "joules": "joule",
29581     "BTU": "BTU",
29582     "btu": "BTU",
29583     "British thermal unit": "BTU",
29584     "british thermal unit": "BTU",
29585     "kilo joule": "kilojoule",
29586     "kJ": "kilojoule",
29587     "kj": "kilojoule",
29588     "Kj": "kilojoule",
29589     "kiloJoule": "kilojoule",
29590     "kilojoule": "kilojoule",
29591     "kjoule": "kilojoule",
29592     "watt hour": "watt hour",
29593     "Wh": "watt hour",
29594     "wh": "watt hour",
29595     "watt-hour": "watt hour",
29596     "calorie": "calorie",
29597     "Cal": "calorie",
29598     "cal": "calorie",
29599     "Calorie": "calorie",
29600     "calories": "calorie",
29601     "mega joule": "megajoule",
29602     "MJ": "megajoule",
29603     "megajoule": "megajoule",
29604     "megajoules": "megajoule",
29605     "Megajoules": "megajoule",
29606     "megaJoules": "megajoule",
29607     "MegaJoules": "megajoule",
29608     "megaJoule": "megajoule",
29609     "MegaJoule": "megajoule",
29610     "kilo Watt hour": "kilowatt hour",
29611     "kWh": "kilowatt hour",
29612     "kiloWh": "kilowatt hour",
29613     "KiloWh": "kilowatt hour",
29614     "KiloWatt-hour": "kilowatt hour",
29615     "kilowatt hour": "kilowatt hour",
29616     "kilowatt-hour": "kilowatt hour",
29617     "KiloWatt-hours": "kilowatt hour",
29618     "kilowatt-hours": "kilowatt hour",
29619     "Kilo Watt-hour": "kilowatt hour",
29620     "Kilo Watt-hours": "kilowatt hour",
29621     "giga joule": "gigajoule",
29622     "gJ": "gigajoule",
29623     "GJ": "gigajoule",
29624     "GigaJoule": "gigajoule",
29625     "gigaJoule": "gigajoule",
29626     "gigajoule": "gigajoule",   
29627     "GigaJoules": "gigajoule",
29628     "gigaJoules": "gigajoule",
29629     "Gigajoules": "gigajoule",
29630     "gigajoules": "gigajoule",
29631     "mega watt hour": "megawatt hour",
29632     "MWh": "megawatt hour",
29633     "MegaWh": "megawatt hour",
29634     "megaWh": "megawatt hour",
29635     "megaWatthour": "megawatt hour",
29636     "megaWatt-hour": "megawatt hour",
29637     "mega Watt-hour": "megawatt hour",
29638     "megaWatt hour": "megawatt hour",
29639     "megawatt hour": "megawatt hour",
29640     "mega Watt hour": "megawatt hour",
29641     "giga watt hour": "gigawatt hour",
29642     "gWh": "gigawatt hour",
29643     "GWh": "gigawatt hour",
29644     "gigaWh": "gigawatt hour",
29645     "gigaWatt-hour": "gigawatt hour",
29646     "gigawatt-hour": "gigawatt hour",
29647     "gigaWatt hour": "gigawatt hour",
29648     "gigawatt hour": "gigawatt hour",
29649     "gigawatthour": "gigawatt hour"
29650 };
29651 
29652 /**
29653  * Convert a energy to another measure.
29654  * @static
29655  * @param to {string} unit to convert to
29656  * @param from {string} unit to convert from
29657  * @param energy {number} amount to be convert
29658  * @returns {number|undefined} the converted amount
29659  */
29660 EnergyUnit.convert = function(to, from, energy) {
29661     from = EnergyUnit.aliases[from] || from;
29662     to = EnergyUnit.aliases[to] || to;
29663     var fromRow = EnergyUnit.ratios[from];
29664     var toRow = EnergyUnit.ratios[to];
29665     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
29666         return undefined;
29667     }
29668     return energy * fromRow[toRow[0]];
29669 };
29670 
29671 /**
29672  * @private
29673  * @static
29674  */
29675 EnergyUnit.getMeasures = function () {
29676 	var ret = [];
29677 	for (var m in EnergyUnit.ratios) {
29678 		ret.push(m);
29679 	}
29680 	return ret;
29681 };
29682 
29683 EnergyUnit.metricJouleSystem = {
29684     "millijoule": 1,
29685     "joule": 2,
29686     "kilojoule": 4,
29687     "megajoule": 7,
29688     "gigajoule": 9
29689 };
29690 EnergyUnit.metricWattHourSystem = {
29691     "watt hour": 5,
29692     "kilowatt hour": 8,
29693     "megawatt hour": 10,
29694     "gigawatt hour": 11
29695 };
29696 
29697 EnergyUnit.imperialSystem = {
29698 	"BTU": 3
29699 };
29700 EnergyUnit.uscustomarySystem = {
29701 	"calorie": 6
29702 };
29703 
29704 EnergyUnit.metricToImperial = {
29705     "millijoule": "BTU",
29706     "joule": "BTU",
29707     "kilojoule": "BTU",
29708     "megajoule": "BTU",
29709     "gigajoule": "BTU"
29710 };
29711 EnergyUnit.imperialToMetric = {
29712 	"BTU": "joule"
29713 };
29714 
29715 /**
29716  * Localize the measurement to the commonly used measurement in that locale. For example
29717  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
29718  * the formatted number should be automatically converted to the most appropriate 
29719  * measure in the other system, in this case, mph. The formatted result should
29720  * appear as "37.3 mph". 
29721  * 
29722  * @param {string} locale current locale string
29723  * @returns {Measurement} a new instance that is converted to locale
29724  */
29725 EnergyUnit.prototype.localize = function(locale) {
29726 	var to;
29727 	if (locale === "en-GB") {
29728 		to = EnergyUnit.metricToImperial[this.unit] || this.unit;
29729 	} else {
29730 		to = EnergyUnit.imperialToMetric[this.unit] || this.unit;
29731 	}
29732 
29733 	return new EnergyUnit({
29734 	    unit: to,
29735 	    amount: this
29736 	});
29737 };
29738 
29739 /**
29740  * Scale the measurement unit to an acceptable level. The scaling
29741  * happens so that the integer part of the amount is as small as
29742  * possible without being below zero. This will result in the 
29743  * largest units that can represent this measurement without
29744  * fractions. Measurements can only be scaled to other measurements 
29745  * of the same type.
29746  * 
29747  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
29748  * or undefined if the system can be inferred from the current measure
29749  * @return {Measurement} a new instance that is scaled to the 
29750  * right level
29751  */
29752 EnergyUnit.prototype.scale = function(measurementsystem) {
29753     var fromRow = EnergyUnit.ratios[this.unit];
29754     var mSystem;
29755 
29756     if ((measurementsystem === "metric" && typeof(EnergyUnit.metricJouleSystem[this.unit]) !== 'undefined')|| (typeof(measurementsystem) === 'undefined'
29757         && typeof(EnergyUnit.metricJouleSystem[this.unit]) !== 'undefined')) {
29758         mSystem = EnergyUnit.metricJouleSystem;
29759     }
29760     else if ((measurementsystem === "metric" && typeof(EnergyUnit.metricWattHourSystem[this.unit]) !== 'undefined')|| (typeof(measurementsystem) === 'undefined'
29761         && typeof(EnergyUnit.metricWattHourSystem[this.unit]) !== 'undefined')) {
29762         mSystem = EnergyUnit.metricWattHourSystem;
29763     }
29764 
29765     else  if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined'
29766         && typeof(EnergyUnit.uscustomarySystem[this.unit]) !== 'undefined')) {
29767         mSystem = EnergyUnit.uscustomarySystem;
29768     }
29769     else if (measurementsystem === "imperial"|| (typeof(measurementsystem) === 'undefined'
29770         && typeof(EnergyUnit.imperialSystem[this.unit]) !== 'undefined')) {
29771         mSystem = EnergyUnit.imperialSystem;
29772     }
29773 
29774     var energy = this.amount;
29775     var munit = this.unit;
29776 
29777     energy = 18446744073709551999;
29778     
29779     for (var m in mSystem) {
29780         var tmp = this.amount * fromRow[mSystem[m]];
29781         if (tmp >= 1 && tmp < energy) {
29782 	        energy = tmp;
29783 	        munit = m;
29784         }
29785     }
29786 
29787     return new EnergyUnit({
29788         unit: munit,
29789         amount: energy
29790     });
29791 };
29792 //register with the factory method
29793 Measurement._constructors["energy"] = EnergyUnit;
29794 
29795 
29796 /*< FuelConsumptionUnit.js */
29797 /*
29798  * fuelconsumption.js - Unit conversions for FuelConsumption
29799  *
29800  * Copyright © 2014-2015, JEDLSoft
29801  *
29802  * Licensed under the Apache License, Version 2.0 (the "License");
29803  * you may not use this file except in compliance with the License.
29804  * You may obtain a copy of the License at
29805  *
29806  *     http://www.apache.org/licenses/LICENSE-2.0
29807  *
29808  * Unless required by applicable law or agreed to in writing, software
29809  * distributed under the License is distributed on an "AS IS" BASIS,
29810  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29811  *
29812  * See the License for the specific language governing permissions and
29813  * limitations under the License.
29814  */
29815 
29816 /*
29817 !depends 
29818 Measurement.js 
29819 */
29820 
29821 
29822 /**
29823  * @class
29824  * Create a new fuelconsumption measurement instance.
29825  * 
29826  * @constructor
29827  * @extends Measurement
29828  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
29829  * the construction of this instance
29830  */
29831 var FuelConsumptionUnit = function(options) {
29832     this.unit = "km/liter";
29833     this.amount = 0;
29834     this.aliases = FuelConsumptionUnit.aliases; // share this table in all instances
29835 
29836     if (options) {
29837         if (typeof(options.unit) !== 'undefined') {
29838             this.originalUnit = options.unit;
29839             this.unit = this.aliases[options.unit] || options.unit;
29840         }
29841 
29842         if (typeof(options.amount) === 'object') {
29843             if (options.amount.getMeasure() === "fuelconsumption") {
29844                 this.amount = FuelConsumptionUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
29845             } else {
29846                 throw "Cannot convert unit " + options.amount.unit + " to fuelconsumption";
29847             }
29848         } else if (typeof(options.amount) !== 'undefined') {
29849             this.amount = parseFloat(options.amount);
29850         }
29851     }
29852 };
29853 
29854 FuelConsumptionUnit.prototype = new Measurement();
29855 FuelConsumptionUnit.prototype.parent = Measurement;
29856 FuelConsumptionUnit.prototype.constructor = FuelConsumptionUnit;
29857 
29858 FuelConsumptionUnit.ratios = [
29859     "km/liter",
29860     "liter/100km",
29861     "mpg",
29862     "mpg(imp)"
29863 ];
29864 
29865 /**
29866  * Return the type of this measurement. Examples are "mass",
29867  * "length", "speed", etc. Measurements can only be converted
29868  * to measurements of the same type.<p>
29869  * 
29870  * The type of the units is determined automatically from the 
29871  * units. For example, the unit "grams" is type "mass". Use the 
29872  * static call {@link Measurement.getAvailableUnits}
29873  * to find out what units this version of ilib supports.
29874  *  
29875  * @return {string} the name of the type of this measurement
29876  */
29877 FuelConsumptionUnit.prototype.getMeasure = function() {
29878     return "fuelconsumption";
29879 };
29880 
29881 /**
29882  * Return a new measurement instance that is converted to a new
29883  * measurement unit. Measurements can only be converted
29884  * to measurements of the same type.<p>
29885  *  
29886  * @param {string} to The name of the units to convert to
29887  * @return {Measurement|undefined} the converted measurement
29888  * or undefined if the requested units are for a different
29889  * measurement type 
29890  */
29891 FuelConsumptionUnit.prototype.convert = function(to) {
29892     if (!to || typeof(FuelConsumptionUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
29893         return undefined;
29894     }
29895     return new FuelConsumptionUnit({
29896         unit: to,
29897         amount: this
29898     });
29899 };
29900 /*["km/liter", "liter/100km", "mpg", "mpg(imp)"*/
29901 FuelConsumptionUnit.aliases = {
29902     "Km/liter": "km/liter",
29903     "KM/Liter": "km/liter",
29904     "KM/L": "km/liter",
29905     "Kilometers Per Liter": "km/liter",
29906     "kilometers per liter": "km/liter",
29907     "km/l": "km/liter",
29908     "Kilometers/Liter": "km/liter",
29909     "Kilometer/Liter": "km/liter",
29910     "kilometers/liter": "km/liter",
29911     "kilometer/liter": "km/liter",
29912     "km/liter": "km/liter",
29913     "Liter/100km": "liter/100km",
29914     "Liters/100km": "liter/100km",
29915     "Liter/100kms": "liter/100km",
29916     "Liters/100kms": "liter/100km",
29917     "liter/100km": "liter/100km",
29918     "liters/100kms": "liter/100km",
29919     "liters/100km": "liter/100km",
29920     "liter/100kms": "liter/100km",
29921     "Liter/100KM": "liter/100km",
29922     "Liters/100KM": "liter/100km",
29923     "L/100km": "liter/100km",
29924     "L/100KM": "liter/100km",
29925     "l/100KM": "liter/100km",
29926     "l/100km": "liter/100km",
29927     "l/100kms": "liter/100km",
29928     "MPG(US)": "mpg",
29929     "USMPG ": "mpg",
29930     "mpg": "mpg",
29931     "mpgUS": "mpg",
29932     "mpg(US)": "mpg",
29933     "mpg(us)": "mpg",
29934     "mpg-us": "mpg",
29935     "mpg Imp": "mpg(imp)",
29936     "MPG(imp)": "mpg(imp)",
29937     "mpg(imp)": "mpg(imp)",
29938     "mpg-imp": "mpg(imp)"
29939 };
29940 
29941 FuelConsumptionUnit.metricToUScustomary = {
29942     "km/liter": "mpg",
29943     "liter/100km": "mpg"
29944 };
29945 FuelConsumptionUnit.metricToImperial = {
29946     "km/liter": "mpg(imp)",
29947     "liter/100km": "mpg(imp)"
29948 };
29949 
29950 FuelConsumptionUnit.imperialToMetric = {
29951 	"mpg(imp)": "km/liter"
29952 };
29953 FuelConsumptionUnit.imperialToUScustomary = {
29954 	"mpg(imp)": "mpg"
29955 };
29956 
29957 FuelConsumptionUnit.uScustomaryToImperial = {
29958 	"mpg": "mpg(imp)"
29959 };
29960 FuelConsumptionUnit.uScustomarylToMetric = {
29961 	"mpg": "km/liter"
29962 };
29963 
29964 /**
29965  * Localize the measurement to the commonly used measurement in that locale. For example
29966  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
29967  * the formatted number should be automatically converted to the most appropriate 
29968  * measure in the other system, in this case, mph. The formatted result should
29969  * appear as "37.3 mph". 
29970  * 
29971  * @param {string} locale current locale string
29972  * @returns {Measurement} a new instance that is converted to locale
29973  */
29974 FuelConsumptionUnit.prototype.localize = function(locale) {
29975 	var to;
29976 	if (locale === "en-US") {
29977 		to = FuelConsumptionUnit.metricToUScustomary[this.unit] ||
29978 		    FuelConsumptionUnit.imperialToUScustomary[this.unit] ||
29979 		    this.unit;
29980 	} else if (locale === "en-GB") {
29981 		to = FuelConsumptionUnit.metricToImperial[this.unit] ||
29982 		    FuelConsumptionUnit.uScustomaryToImperial[this.unit] ||
29983 		    this.unit;
29984 	} else {
29985 		to = FuelConsumptionUnit.uScustomarylToMetric[this.unit] ||
29986 		    FuelConsumptionUnit.imperialToUScustomary[this.unit] ||
29987 		    this.unit;
29988 	}
29989 	return new FuelConsumptionUnit({
29990 	    unit: to,
29991 	    amount: this
29992 	});
29993 };
29994 
29995 /**
29996  * Convert a FuelConsumption to another measure.
29997  * 
29998  * @static
29999  * @param to {string} unit to convert to
30000  * @param from {string} unit to convert from
30001  * @param fuelConsumption {number} amount to be convert
30002  * @returns {number|undefined} the converted amount
30003  */
30004 FuelConsumptionUnit.convert = function(to, from, fuelConsumption) {
30005     from = FuelConsumptionUnit.aliases[from] || from;
30006     to = FuelConsumptionUnit.aliases[to] || to;
30007     var returnValue = 0;
30008 
30009     switch (from) {
30010         case "km/liter":
30011             switch (to) {
30012                 case "km/liter":
30013                     returnValue = fuelConsumption * 1;
30014                     break;
30015                 case "liter/100km":
30016                     returnValue = 100 / fuelConsumption;
30017                     break;
30018                 case "mpg":
30019                     returnValue = fuelConsumption * 2.35215;
30020                     break;
30021                 case "mpg(imp)":
30022                     returnValue = fuelConsumption * 2.82481;
30023                     break;
30024             }
30025             break;
30026         case "liter/100km":
30027             switch (to) {
30028                 case "km/liter":
30029                     returnValue = 100 / fuelConsumption;
30030                     break;
30031                 case "liter/100km":
30032                     returnValue = fuelConsumption * 1;
30033                     break;
30034                 case "mpg":
30035                     returnValue = 235.215 / fuelConsumption;
30036                     break;
30037                 case "mpg(imp)":
30038                     returnValue = 282.481 / fuelConsumption;
30039                     break;
30040             }
30041             break;
30042         case "mpg":
30043             switch (to) {
30044                 case "km/liter":
30045                     returnValue = fuelConsumption * 0.425144;
30046                     break;
30047                 case "liter/100km":
30048                     returnValue = 235.215 / fuelConsumption;
30049                     break;
30050                 case "mpg":
30051                     returnValue = 1 * fuelConsumption;
30052                     break;
30053                 case "mpg(imp)":
30054                     returnValue = 1.20095 * fuelConsumption;
30055                     break;
30056             }
30057             break;
30058         case "mpg(imp)":
30059             switch (to) {
30060                 case "km/liter":
30061                     returnValue = fuelConsumption * 0.354006;
30062                     break;
30063                 case "liter/100km":
30064                     returnValue = 282.481 / fuelConsumption;
30065                     break;
30066                 case "mpg":
30067                     returnValue = 0.832674 * fuelConsumption;
30068                     break;
30069                 case "mpg(imp)":
30070                     returnValue = 1 * fuelConsumption;
30071                     break;
30072             }
30073             break;
30074     }
30075     return returnValue;
30076 };
30077 
30078 /**
30079  * Scale the measurement unit to an acceptable level. The scaling
30080  * happens so that the integer part of the amount is as small as
30081  * possible without being below zero. This will result in the 
30082  * largest units that can represent this measurement without
30083  * fractions. Measurements can only be scaled to other measurements 
30084  * of the same type.
30085  * 
30086  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
30087  * or undefined if the system can be inferred from the current measure
30088  * @return {Measurement} a new instance that is scaled to the 
30089  * right level
30090  */
30091 FuelConsumptionUnit.prototype.scale = function(measurementsystem) {
30092     return new FuelConsumptionUnit({
30093         unit: this.unit,
30094         amount: this.amount
30095     }); 
30096 };
30097 
30098 /**
30099  * @private
30100  * @static
30101  */
30102 FuelConsumptionUnit.getMeasures = function() {
30103     var ret = [];
30104     ret.push("km/liter");
30105     ret.push("liter/100km");
30106     ret.push("mpg");
30107     ret.push("mpg(imp)");
30108     
30109     return ret;
30110 };
30111 
30112 //register with the factory method
30113 Measurement._constructors["fuelconsumption"] = FuelConsumptionUnit;
30114 
30115 
30116 /*< LengthUnit.js */
30117 /*
30118  * LengthUnit.js - Unit conversions for Lengths/lengths
30119  * 
30120  * Copyright © 2014-2015, JEDLSoft
30121  *
30122  * Licensed under the Apache License, Version 2.0 (the "License");
30123  * you may not use this file except in compliance with the License.
30124  * You may obtain a copy of the License at
30125  *
30126  *     http://www.apache.org/licenses/LICENSE-2.0
30127  *
30128  * Unless required by applicable law or agreed to in writing, software
30129  * distributed under the License is distributed on an "AS IS" BASIS,
30130  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30131  *
30132  * See the License for the specific language governing permissions and
30133  * limitations under the License.
30134  */
30135 
30136 /*
30137 !depends 
30138 Measurement.js
30139 */
30140 
30141 
30142 /**
30143  * @class
30144  * Create a new length measurement instance.
30145  *  
30146  * @constructor
30147  * @extends Measurement
30148  * @param options {{unit:string,amount:number|string|undefined}} Options controlling 
30149  * the construction of this instance
30150  */
30151 var LengthUnit = function (options) {
30152 	this.unit = "meter";
30153 	this.amount = 0;
30154 	this.aliases = LengthUnit.aliases; // share this table in all instances
30155 	
30156 	if (options) {
30157 		if (typeof(options.unit) !== 'undefined') {
30158 			this.originalUnit = options.unit;
30159 			this.unit = this.aliases[options.unit] || options.unit;
30160 		}
30161 		
30162 		if (typeof(options.amount) === 'object') {
30163 			if (options.amount.getMeasure() === "length") {
30164 				this.amount = LengthUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
30165 			} else {
30166 				throw "Cannot convert unit " + options.amount.unit + " to a length";
30167 			}
30168 		} else if (typeof(options.amount) !== 'undefined') {
30169 			this.amount = parseFloat(options.amount);
30170 		}
30171 	}
30172 	
30173 	if (typeof(LengthUnit.ratios[this.unit]) === 'undefined') {
30174 		throw "Unknown unit: " + options.unit;
30175 	}
30176 };
30177 
30178 LengthUnit.prototype = new Measurement();
30179 LengthUnit.prototype.parent = Measurement;
30180 LengthUnit.prototype.constructor = LengthUnit;
30181 
30182 LengthUnit.ratios = {
30183 	/*              index, µm           mm           cm           inch         dm           foot          yard          m             dam            hm              km              mile            nm            Mm             Gm             */ 
30184 	"micrometer":   [ 1,   1,           1e-3,        1e-4,        3.93701e-5,  1e-5,        3.28084e-6,   1.09361e-6,   1e-6,         1e-7,          1e-8,           1e-9,           6.21373e-10,  5.39957e-10,  1e-12,          1e-15           ],
30185 	"millimeter":   [ 2,   1000,        1,           0.1,         0.0393701,   0.01,        0.00328084,   1.09361e-3,   0.001,        1e-4,          1e-5,           1e-6,           6.21373e-7,   5.39957e-7,   1e-9,           1e-12           ],
30186 	"centimeter":   [ 3,   1e4,         10,          1,           0.393701,    0.1,         0.0328084,    0.0109361,    0.01,         0.001,         1e-4,           1e-5,           6.21373e-6,   5.39957e-6,   1e-8,           1e-9            ],
30187     "inch":         [ 4,   25399.986,   25.399986,   2.5399986,   1,           0.25399986,  0.083333333,  0.027777778,  0.025399986,  2.5399986e-3,  2.5399986e-4,   2.5399986e-5,   1.5783e-5,    1.3715e-5,    2.5399986e-8,   2.5399986e-11   ],
30188     "decimeter":    [ 5,   1e5,         100,         10,          3.93701,     1,           0.328084,     0.109361,     0.1,          0.01,          0.001,          1e-4,           6.21373e-5,   5.39957e-5,   1e-7,           1e-8            ],
30189     "foot":         [ 6,   304799.99,   304.79999,   30.479999,   12,          3.0479999,   1,            0.33333333,   0.30479999,   0.030479999,   3.0479999e-3,   3.0479999e-4,   1.89394e-4,   1.64579e-4,   3.0479999e-7,   3.0479999e-10   ],
30190     "yard":         [ 7,   914402.758,  914.402758,  91.4402758,  36,          9.14402758,  3,            1,            0.914402758,  0.0914402758,  9.14402758e-3,  9.14402758e-4,  5.68182e-4,   4.93737e-4,   9.14402758e-7,  9.14402758e-10  ],
30191 	"meter":        [ 8,   1e6,         1000,        100,         39.3701,     10,          3.28084,      1.09361,      1,            0.1,           0.01,           0.001,          6.213712e-4,  5.39957e-4,   1e-6,           1e-7            ],
30192 	"decameter":    [ 9,   1e7,         1e4,         1000,        393.701,     100,         32.8084,      10.9361,      10,           1,             0.1,            0.01,           6.21373e-3,   5.39957e-3,   1e-5,           1e-6            ],
30193 	"hectometer":   [ 10,  1e8,         1e5,         1e4,         3937.01,     1000,        328.084,      109.361,      100,          10,            1,              0.1,            0.0621373,    0.0539957,    1e-4,           1e-5            ],
30194 	"kilometer":    [ 11,  1e9,         1e6,         1e5,         39370.1,     1e4,         3280.84,      1093.61,      1000,         100,           10,             1,              0.621373,     0.539957,     0.001,          1e-4            ],
30195     "mile":         [ 12,  1.60934e9,   1.60934e6,   1.60934e5,   63360,       1.60934e4,   5280,         1760,         1609.34,      160.934,       16.0934,        1.60934,        1,            0.868976,     1.60934e-3,     1.60934e-6      ],
30196     "nauticalmile": [ 13,  1.852e9,     1.852e6,     1.852e5,     72913.4,     1.852e4,     6076.12,      2025.37,      1852,         185.2,         18.52,          1.852,          1.15078,      1,            1.852e-3,       1.852e-6        ],
30197 	"megameter":    [ 14,  1e12,        1e9,         1e6,         3.93701e7,   1e5,         3.28084e6,    1.09361e6,    1e4,          1000,          100,            10,             621.373,      539.957,      1,              0.001           ],        
30198     "gigameter":    [ 15,  1e15,        1e12,        1e9,         3.93701e10,  1e8,         3.28084e9,    1.09361e9,    1e7,          1e6,           1e5,            1e4,            621373.0,     539957.0,     1000,           1               ]	
30199 };
30200 
30201 LengthUnit.metricSystem = {
30202     "micrometer": 1,
30203     "millimeter": 2,
30204     "centimeter": 3,
30205     "decimeter": 5,
30206     "meter": 8,
30207     "decameter": 9,
30208     "hectometer": 10,
30209     "kilometer": 11,
30210     "megameter": 14,
30211     "gigameter": 15
30212 };
30213 LengthUnit.imperialSystem = {
30214     "inch": 4,
30215     "foot": 6,
30216     "yard": 7,
30217     "mile": 12,
30218     "nauticalmile": 13
30219 };
30220 LengthUnit.uscustomarySystem = {
30221     "inch": 4,
30222     "foot": 6,
30223     "yard": 7,
30224     "mile": 12,
30225     "nauticalmile": 13
30226 };
30227 
30228 LengthUnit.metricToUScustomary = {
30229     "micrometer": "inch",
30230     "millimeter": "inch",
30231     "centimeter": "inch",
30232     "decimeter": "inch",
30233     "meter": "yard",
30234     "decameter": "yard",
30235     "hectometer": "mile",
30236     "kilometer": "mile",
30237     "megameter": "nauticalmile",
30238     "gigameter": "nauticalmile"
30239 };
30240 LengthUnit.usCustomaryToMetric = {
30241     "inch": "centimeter",
30242     "foot": "centimeter",
30243     "yard": "meter",
30244     "mile": "kilometer",
30245     "nauticalmile": "kilometer"
30246 };
30247 
30248 /**
30249  * Return the type of this measurement. Examples are "mass",
30250  * "length", "speed", etc. Measurements can only be converted
30251  * to measurements of the same type.<p>
30252  * 
30253  * The type of the units is determined automatically from the 
30254  * units. For example, the unit "grams" is type "mass". Use the 
30255  * static call {@link Measurement.getAvailableUnits}
30256  * to find out what units this version of ilib supports.
30257  *  
30258  * @return {string} the name of the type of this measurement
30259  */
30260 LengthUnit.prototype.getMeasure = function() {
30261 	return "length";
30262 };
30263 
30264 /**
30265  * Localize the measurement to the commonly used measurement in that locale. For example
30266  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
30267  * the formatted number should be automatically converted to the most appropriate 
30268  * measure in the other system, in this case, mph. The formatted result should
30269  * appear as "37.3 mph". 
30270  * 
30271  * @param {string} locale current locale string
30272  * @returns {Measurement} a new instance that is converted to locale
30273  */
30274 LengthUnit.prototype.localize = function(locale) {
30275     var to;
30276     if (locale === "en-US" || locale === "en-GB") {
30277         to = LengthUnit.metricToUScustomary[this.unit] || this.unit;
30278     } else {
30279         to = LengthUnit.usCustomaryToMetric[this.unit] || this.unit;
30280     }
30281     return new LengthUnit({
30282         unit: to,
30283         amount: this
30284     });
30285 };
30286 
30287 /**
30288  * Return a new measurement instance that is converted to a new
30289  * measurement unit. Measurements can only be converted
30290  * to measurements of the same type.<p>
30291  *  
30292  * @param {string} to The name of the units to convert to
30293  * @return {Measurement|undefined} the converted measurement
30294  * or undefined if the requested units are for a different
30295  * measurement type 
30296  */
30297 LengthUnit.prototype.convert = function(to) {
30298 	if (!to || typeof(LengthUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
30299 		return undefined;
30300 	}
30301 	return new LengthUnit({
30302 		unit: to,
30303 		amount: this
30304 	});
30305 };
30306 
30307 /**
30308  * Scale the measurement unit to an acceptable level. The scaling
30309  * happens so that the integer part of the amount is as small as
30310  * possible without being below zero. This will result in the 
30311  * largest units that can represent this measurement without
30312  * fractions. Measurements can only be scaled to other measurements 
30313  * of the same type.
30314  * 
30315  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
30316  * or undefined if the system can be inferred from the current measure
30317  * @return {Measurement} a new instance that is scaled to the 
30318  * right level
30319  */
30320 LengthUnit.prototype.scale = function(measurementsystem) {
30321     var mSystem;    
30322     if (measurementsystem === "metric" || (typeof(measurementsystem) === 'undefined' 
30323             && typeof(LengthUnit.metricSystem[this.unit]) !== 'undefined')) {
30324         mSystem = LengthUnit.metricSystem;
30325     } else if (measurementsystem === "imperial" || (typeof(measurementsystem) === 'undefined' 
30326             && typeof(LengthUnit.imperialSystem[this.unit]) !== 'undefined')) {
30327         mSystem = LengthUnit.imperialSystem;
30328     } else if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined' 
30329             && typeof(LengthUnit.uscustomarySystem[this.unit]) !== 'undefined')) {
30330         mSystem = LengthUnit.uscustomarySystem;
30331     } else {
30332         return new LengthUnit({
30333 			unit: this.unit,
30334 			amount: this.amount
30335 		});
30336     }    
30337     
30338     var length = this.amount;
30339     var munit = this.unit;
30340     var fromRow = LengthUnit.ratios[this.unit];
30341     
30342     length = 18446744073709551999;
30343     for (var m in mSystem) {
30344     	var tmp = this.amount * fromRow[mSystem[m]];
30345         if (tmp >= 1 && tmp < length) {
30346 	        length = tmp;
30347 	        munit = m;
30348         }
30349     }
30350     
30351     return new LengthUnit({
30352 		unit: munit,
30353 		amount: length
30354     });
30355 };
30356 
30357 LengthUnit.aliases = {
30358 	"miles": "mile",
30359 	"mile":"mile",
30360 	"nauticalmiles": "nauticalmile",
30361 	"nautical mile": "nauticalmile",
30362 	"nautical miles": "nauticalmile",
30363 	"nauticalmile":"nauticalmile",
30364 	"yards": "yard",
30365 	"yard": "yard",
30366 	"feet": "foot",
30367 	"foot": "foot",
30368 	"inches": "inch",
30369 	"inch": "inch",
30370 	"meters": "meter",
30371 	"metre": "meter",
30372 	"metres": "meter",
30373 	"m": "meter",
30374 	"meter": "meter",        
30375 	"micrometers": "micrometer",
30376 	"micrometres": "micrometer",
30377 	"micrometre": "micrometer",
30378 	"µm": "micrometer",
30379 	"micrometer": "micrometer",
30380 	"millimeters": "millimeter",
30381 	"millimetres": "millimeter",
30382 	"millimetre": "millimeter",
30383 	"mm": "millimeter",
30384 	"millimeter": "millimeter",
30385 	"centimeters": "centimeter",
30386 	"centimetres": "centimeter",
30387 	"centimetre": "centimeter",
30388 	"cm": "centimeter",
30389 	"centimeter": "centimeter",
30390 	"decimeters": "decimeter",
30391 	"decimetres": "decimeter",
30392 	"decimetre": "decimeter",
30393 	"dm": "decimeter",
30394 	"decimeter": "decimeter",
30395 	"decameters": "decameter",
30396 	"decametres": "decameter",
30397 	"decametre": "decameter",
30398 	"dam": "decameter",
30399 	"decameter": "decameter",
30400 	"hectometers": "hectometer",
30401 	"hectometres": "hectometer",
30402 	"hectometre": "hectometer",
30403 	"hm": "hectometer",
30404 	"hectometer": "hectometer",
30405 	"kilometers": "kilometer",
30406 	"kilometres": "kilometer",
30407 	"kilometre": "kilometer",
30408 	"km": "kilometer",
30409 	"kilometer": "kilometer",
30410 	"megameters": "megameter",
30411 	"megametres": "megameter",
30412 	"megametre": "megameter",
30413 	"Mm": "megameter",
30414 	"megameter": "megameter",
30415 	"gigameters": "gigameter",
30416 	"gigametres": "gigameter",
30417 	"gigametre": "gigameter",
30418 	"Gm": "gigameter",
30419 	"gigameter": "gigameter"
30420 };
30421 
30422 /**
30423  * Convert a length to another measure.
30424  * @static
30425  * @param to {string} unit to convert to
30426  * @param from {string} unit to convert from
30427  * @param length {number} amount to be convert
30428  * @returns {number|undefined} the converted amount
30429  */
30430 LengthUnit.convert = function(to, from, length) {
30431     from = LengthUnit.aliases[from] || from;
30432     to = LengthUnit.aliases[to] || to;
30433 	var fromRow = LengthUnit.ratios[from];
30434 	var toRow = LengthUnit.ratios[to];
30435 	if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
30436 		return undefined;
30437 	}
30438 	return length * fromRow[toRow[0]];
30439 };
30440 
30441 /**
30442  * @private
30443  * @static
30444  */
30445 LengthUnit.getMeasures = function () {
30446 	var ret = [];
30447 	for (var m in LengthUnit.ratios) {
30448 		ret.push(m);
30449 	}
30450 	return ret;
30451 };
30452 
30453 //register with the factory method
30454 Measurement._constructors["length"] = LengthUnit;
30455 
30456 
30457 /*< MassUnit.js */
30458 /*
30459  * MassUnit.js - Unit conversions for Mass/mass
30460  * 
30461  * Copyright © 2014-2015, JEDLSoft
30462  *
30463  * Licensed under the Apache License, Version 2.0 (the "License");
30464  * you may not use this file except in compliance with the License.
30465  * You may obtain a copy of the License at
30466  *
30467  *     http://www.apache.org/licenses/LICENSE-2.0
30468  *
30469  * Unless required by applicable law or agreed to in writing, software
30470  * distributed under the License is distributed on an "AS IS" BASIS,
30471  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30472  *
30473  * See the License for the specific language governing permissions and
30474  * limitations under the License.
30475  */
30476 
30477 /*
30478 !depends 
30479 Measurement.js
30480 */
30481 
30482 
30483 /**
30484  * @class
30485  * Create a new mass measurement instance.
30486  *
30487  * @constructor
30488  * @extends Measurement
30489  * @param options {{unit:string,amount:number|string|undefined}} Options controlling 
30490  * the construction of this instance
30491  */
30492 var MassUnit = function (options) {
30493 	this.unit = "gram";
30494 	this.amount = 0;
30495 	this.aliases = MassUnit.aliases; // share this table in all instances
30496 	
30497 	if (options) {
30498 		if (typeof(options.unit) !== 'undefined') {
30499 			this.originalUnit = options.unit;
30500 			this.unit = this.aliases[options.unit] || options.unit;
30501 		}
30502 		
30503 		if (typeof(options.amount) === 'object') {
30504 			if (options.amount.getMeasure() === "mass") {
30505 				this.amount = MassUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
30506 			} else {
30507 				throw "Cannot convert units " + options.amount.unit + " to a mass";
30508 			}
30509 		} else if (typeof(options.amount) !== 'undefined') {
30510 			this.amount = parseFloat(options.amount);
30511 		}
30512 	}
30513 	
30514 	if (typeof(MassUnit.ratios[this.unit]) === 'undefined') {
30515 		throw "Unknown unit: " + options.unit;
30516 	}
30517 };
30518 
30519 MassUnit.prototype = new Measurement();
30520 MassUnit.prototype.parent = Measurement;
30521 MassUnit.prototype.constructor = MassUnit;
30522 
30523 MassUnit.ratios = {
30524 	/*             index  µg          mg         g          oz          lp           kg          st            sh ton       mt ton        ln ton      */           
30525 	"microgram":   [ 1,   1,          0.001,     1e-6,      3.5274e-8,  2.2046e-9,   1e-9,       1.5747e-10,   1.1023e-12,  1e-12,        9.8421e-13   ],  
30526 	"milligram":   [ 2,   1000,       1,         0.001,     3.5274e-5,  2.2046e-6,   1e-6,       1.5747e-7,    1.1023e-9,   1e-9,         9.8421e-10   ],  
30527 	"gram":        [ 3,   1e+6,       1000,      1,         0.035274,   0.00220462,  0.001,      0.000157473,  1.1023e-6,   1e-6,         9.8421e-7    ],
30528 	"ounce":       [ 4,   2.835e+7,   28349.5,   28.3495,   1,          0.0625,      0.0283495,  0.00446429,   3.125e-5,    2.835e-5,     2.7902e-5    ],
30529 	"pound":       [ 5,   4.536e+8,   453592,    453.592,   16,         1,           0.453592,   0.0714286,    0.0005,      0.000453592,  0.000446429  ],
30530     "kilogram":    [ 6,   1e+9,       1e+6,      1000,      35.274,     2.20462,     1,          0.157473,     0.00110231,  0.001,        0.000984207  ],
30531     "stone":       [ 7,   6.35e+9,    6.35e+6,   6350.29,   224,        14,          6.35029,    1,            0.007,       0.00635029,   0.00625      ],
30532     "short ton":   [ 8,   9.072e+11,  9.072e+8,  907185,    32000,      2000,        907.185,    142.857,      1,           0.907185,     0.892857     ],
30533     "metric ton":  [ 9,   1e+12,      1e+9,      1e+6,      35274,      2204.62,     1000,       157.473,      1.10231,     1,            0.984207     ],
30534     "long ton":    [ 10,  1.016e+12,  1.016e+9,  1.016e+6,  35840,      2240,        1016.05,    160,          1.12,        1.01605,      1            ]
30535 };
30536 
30537 MassUnit.metricSystem = {
30538     "microgram": 1,
30539     "milligram": 2,
30540     "gram": 3,
30541     "kilogram": 6,
30542     "metric ton": 9
30543 };
30544 MassUnit.imperialSystem = {
30545     "ounce": 4,
30546     "pound": 5,
30547     "stone": 7,
30548     "long ton": 10
30549 };
30550 MassUnit.uscustomarySystem = {
30551     "ounce": 4,
30552     "pound": 5,
30553     "short ton": 8
30554 };
30555 
30556 MassUnit.metricToUScustomary = {
30557     "microgram": "ounce",
30558     "milligram": "ounce",
30559     "gram": "ounce",
30560     "kilogram": "pound",
30561     "metric ton": "long ton"
30562 };
30563 MassUnit.metricToImperial = {
30564     "microgram": "ounce",
30565     "milligram": "ounce",
30566     "gram": "ounce",
30567     "kilogram": "pound",
30568     "metric ton": "short ton"
30569 };
30570 
30571 MassUnit.imperialToMetric = {
30572     "ounce": "gram",
30573     "pound": "kilogram",
30574     "stone": "kilogram",
30575     "short ton": "metric ton"
30576 };
30577 MassUnit.imperialToUScustomary = {
30578     "ounce": "ounce",
30579     "pound": "pound",
30580     "stone": "stone",
30581     "short ton": "long ton"
30582 };
30583 
30584 MassUnit.uScustomaryToImperial = {
30585     "ounce": "ounce",
30586     "pound": "pound",
30587     "stone": "stone",
30588     "long ton": "short ton"
30589 };
30590 MassUnit.uScustomarylToMetric = {
30591     "ounce": "gram",
30592     "pound": "kilogram",
30593     "stone": "kilogram",
30594     "long ton": "metric ton"
30595 };
30596 
30597 /**
30598  * Localize the measurement to the commonly used measurement in that locale. For example
30599  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
30600  * the formatted number should be automatically converted to the most appropriate 
30601  * measure in the other system, in this case, mph. The formatted result should
30602  * appear as "37.3 mph". 
30603  * 
30604  * @param {string} locale current locale string
30605  * @returns {Measurement} a new instance that is converted to locale
30606  */
30607 MassUnit.prototype.localize = function(locale) {
30608 	var to;
30609 	if (locale === "en-US") {
30610 		to = MassUnit.metricToUScustomary[this.unit] ||
30611 		    MassUnit.imperialToUScustomary[this.unit] || this.unit;
30612 	} else if (locale === "en-GB") {
30613 		to = MassUnit.metricToImperial[this.unit] ||
30614 		    MassUnit.uScustomaryToImperial[this.unit] || this.unit;
30615 	} else {
30616 		to = MassUnit.uScustomarylToMetric[this.unit] ||
30617 		    MassUnit.imperialToUScustomary[this.unit] || this.unit;
30618 	}
30619 	return new MassUnit({
30620 	    unit: to,
30621 	    amount: this
30622 	});
30623 };
30624 
30625 /**
30626  * Return the type of this measurement. Examples are "mass",
30627  * "length", "speed", etc. Measurements can only be converted
30628  * to measurements of the same type.<p>
30629  * 
30630  * The type of the units is determined automatically from the 
30631  * units. For example, the unit "grams" is type "mass". Use the 
30632  * static call {@link Measurement.getAvailableUnits}
30633  * to find out what units this version of ilib supports.
30634  *  
30635  * @return {string} the name of the type of this measurement
30636  */
30637 MassUnit.prototype.getMeasure = function() {
30638 	return "mass";
30639 };
30640 
30641 /**
30642  * Return a new measurement instance that is converted to a new
30643  * measurement unit. Measurements can only be converted
30644  * to measurements of the same type.<p>
30645  *  
30646  * @param {string} to The name of the units to convert to
30647  * @return {Measurement|undefined} the converted measurement
30648  * or undefined if the requested units are for a different
30649  * measurement type 
30650  */
30651 MassUnit.prototype.convert = function(to) {
30652 	if (!to || typeof(MassUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
30653 		return undefined;
30654 	}
30655 	return new MassUnit({
30656 		unit: to,
30657 		amount: this
30658 	});
30659 };
30660 
30661 MassUnit.aliases = {
30662     "µg":"microgram",
30663     "microgram":"microgram",
30664     "mcg":"microgram",  
30665     "milligram":"milligram",
30666     "mg":"milligram",
30667     "milligrams":"milligram",
30668     "Milligram":"milligram",
30669     "Milligrams":"milligram",
30670     "MilliGram":"milligram",
30671     "MilliGrams":"milligram",
30672     "g":"gram",
30673     "gram":"gram",
30674     "grams":"gram",
30675     "Gram":"gram",
30676     "Grams":"gram",
30677     "ounce":"ounce",
30678     "oz":"ounce",
30679     "Ounce":"ounce",
30680     "℥":"ounce",
30681     "pound":"pound",
30682     "poundm":"pound",
30683     "℔":"pound",
30684     "lb":"pound",
30685     "pounds":"pound",
30686     "Pound":"pound",
30687     "Pounds":"pound",
30688     "kilogram":"kilogram",
30689     "kg":"kilogram",
30690     "kilograms":"kilogram",
30691     "kilo grams":"kilogram",
30692     "kilo gram":"kilogram",
30693     "Kilogram":"kilogram",    
30694     "Kilograms":"kilogram",
30695     "KiloGram":"kilogram",
30696     "KiloGrams":"kilogram",
30697     "Kilo gram":"kilogram",
30698     "Kilo grams":"kilogram",
30699     "Kilo Gram":"kilogram",
30700     "Kilo Grams":"kilogram",
30701     "stone":"stone",
30702     "st":"stone",
30703     "stones":"stone",
30704     "Stone":"stone",
30705     "short ton":"short ton",
30706     "Short ton":"short ton",
30707     "Short Ton":"short ton",
30708     "metric ton":"metric ton",
30709     "metricton":"metric ton",
30710     "t":"metric ton",
30711     "tonne":"metric ton",
30712     "Tonne":"metric ton",
30713     "Metric Ton":"metric ton",
30714     "MetricTon":"metric ton",    
30715     "long ton":"long ton",
30716     "longton":"long ton",
30717     "Longton":"long ton",
30718     "Long ton":"long ton",
30719     "Long Ton":"long ton",
30720     "ton":"long ton",
30721     "Ton":"long ton"
30722 };
30723 
30724 /**
30725  * Convert a mass to another measure.
30726  * @static
30727  * @param to {string} unit to convert to
30728  * @param from {string} unit to convert from
30729  * @param mass {number} amount to be convert
30730  * @returns {number|undefined} the converted amount
30731  */
30732 MassUnit.convert = function(to, from, mass) {
30733     from = MassUnit.aliases[from] || from;
30734     to = MassUnit.aliases[to] || to;
30735     var fromRow = MassUnit.ratios[from];
30736     var toRow = MassUnit.ratios[to];
30737     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
30738         return undefined;
30739     }	
30740     return mass * fromRow[toRow[0]];    
30741 };
30742 
30743 /**
30744  * Scale the measurement unit to an acceptable level. The scaling
30745  * happens so that the integer part of the amount is as small as
30746  * possible without being below zero. This will result in the 
30747  * largest units that can represent this measurement without
30748  * fractions. Measurements can only be scaled to other measurements 
30749  * of the same type.
30750  * 
30751  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
30752  * or undefined if the system can be inferred from the current measure
30753  * @return {Measurement} a new instance that is scaled to the 
30754  * right level
30755  */
30756 MassUnit.prototype.scale = function(measurementsystem) {
30757     var mSystem;    
30758     if (measurementsystem === "metric" || (typeof(measurementsystem) === 'undefined' 
30759             && typeof(MassUnit.metricSystem[this.unit]) !== 'undefined')) {
30760         mSystem = MassUnit.metricSystem;
30761     } else if (measurementsystem === "imperial" || (typeof(measurementsystem) === 'undefined' 
30762             && typeof(MassUnit.imperialSystem[this.unit]) !== 'undefined')) {
30763         mSystem = MassUnit.imperialSystem;
30764     } else if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined' 
30765             && typeof(MassUnit.uscustomarySystem[this.unit]) !== 'undefined')) {
30766         mSystem = MassUnit.uscustomarySystem;
30767     } else {
30768         return new MassUnit({
30769 			unit: this.unit,
30770 			amount: this.amount
30771 		});
30772     }    
30773     
30774     var mass = this.amount;
30775     var munit = this.amount;
30776     var fromRow = MassUnit.ratios[this.unit];
30777     
30778     mass = 18446744073709551999;
30779     
30780     for (var m in mSystem) {
30781         var tmp = this.amount * fromRow[mSystem[m]];
30782         if (tmp >= 1 && tmp < mass) {
30783         	mass = tmp;
30784             munit = m;
30785         }
30786     }
30787     
30788     return new MassUnit({
30789 		unit: munit,
30790 		amount: mass
30791     });
30792 };
30793 
30794 /**
30795  * @private
30796  * @static
30797  */
30798 MassUnit.getMeasures = function () {
30799 	var ret = [];
30800 	for (var m in MassUnit.ratios) {
30801 		ret.push(m);
30802 	}
30803 	return ret;
30804 };
30805 
30806 //register with the factory method
30807 Measurement._constructors["mass"] = MassUnit;
30808 
30809 
30810 /*< TemperatureUnit.js */
30811 /*
30812  * temperature.js - Unit conversions for Temperature/temperature
30813  * 
30814  * Copyright © 2014-2015, JEDLSoft
30815  *
30816  * Licensed under the Apache License, Version 2.0 (the "License");
30817  * you may not use this file except in compliance with the License.
30818  * You may obtain a copy of the License at
30819  *
30820  *     http://www.apache.org/licenses/LICENSE-2.0
30821  *
30822  * Unless required by applicable law or agreed to in writing, software
30823  * distributed under the License is distributed on an "AS IS" BASIS,
30824  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30825  *
30826  * See the License for the specific language governing permissions and
30827  * limitations under the License.
30828  */
30829 
30830 /*
30831 !depends 
30832 Measurement.js
30833 */
30834 
30835 
30836 /**
30837  * @class
30838  * Create a new Temperature measurement instance.
30839  *  
30840  * @constructor
30841  * @extends Measurement
30842  * @param options {{unit:string,amount:number|string|undefined}} Options controlling 
30843  * the construction of this instance
30844  */
30845 var TemperatureUnit = function (options) {
30846 	this.unit = "celsius";
30847 	this.amount = 0;
30848 	this.aliases = TemperatureUnit.aliases; // share this table in all instances
30849 
30850 	if (options) {
30851 		if (typeof(options.unit) !== 'undefined') {
30852 			this.originalUnit = options.unit;
30853 			this.unit = this.aliases[options.unit] || options.unit;
30854 		}
30855 
30856 		if (typeof(options.amount) === 'object') {
30857 			if (options.amount.getMeasure() === "temperature") {
30858 				this.amount = TemperatureUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
30859 			} else {
30860 				throw "Cannot convert unit " + options.amount.unit + " to a temperature";
30861 			}
30862 		} else if (typeof(options.amount) !== 'undefined') {
30863 			this.amount = parseFloat(options.amount);
30864 		}
30865 	}
30866 };
30867 
30868 TemperatureUnit.prototype = new Measurement();
30869 TemperatureUnit.prototype.parent = Measurement;
30870 TemperatureUnit.prototype.constructor = TemperatureUnit;
30871 
30872 /**
30873  * Return the type of this measurement. Examples are "mass",
30874  * "length", "speed", etc. Measurements can only be converted
30875  * to measurements of the same type.<p>
30876  * 
30877  * The type of the units is determined automatically from the 
30878  * units. For example, the unit "grams" is type "mass". Use the 
30879  * static call {@link Measurement.getAvailableUnits}
30880  * to find out what units this version of ilib supports.
30881  *  
30882  * @return {string} the name of the type of this measurement
30883  */
30884 TemperatureUnit.prototype.getMeasure = function() {
30885 	return "temperature";
30886 };
30887 
30888 TemperatureUnit.aliases = {
30889     "Celsius": "celsius",
30890     "celsius": "celsius",
30891     "C": "celsius",
30892     "centegrade": "celsius",
30893     "Centegrade": "celsius",
30894     "centigrade": "celsius",
30895     "Centigrade": "celsius",
30896     "fahrenheit": "fahrenheit",
30897     "Fahrenheit": "fahrenheit",
30898     "F": "fahrenheit",
30899     "kelvin": "kelvin",
30900     "K": "kelvin",
30901     "Kelvin": "kelvin",
30902     "°F": "fahrenheit",
30903     "℉": "fahrenheit",
30904     "℃": "celsius",
30905     "°C": "celsius"
30906 };
30907 
30908 /**
30909  * Return a new measurement instance that is converted to a new
30910  * measurement unit. Measurements can only be converted
30911  * to measurements of the same type.<p>
30912  *  
30913  * @param {string} to The name of the units to convert to
30914  * @return {Measurement|undefined} the converted measurement
30915  * or undefined if the requested units are for a different
30916  * measurement type 
30917  */
30918 TemperatureUnit.prototype.convert = function(to) {
30919 	if (!to || typeof(TemperatureUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
30920 		return undefined;
30921 	}
30922 	return new TemperatureUnit({
30923 		unit: to,
30924 		amount: this
30925 	});
30926 };
30927 
30928 /**
30929  * Convert a temperature to another measure.
30930  * @static
30931  * @param to {string} unit to convert to
30932  * @param from {string} unit to convert from
30933  * @param temperature {number} amount to be convert
30934  * @returns {number|undefined} the converted amount
30935  */
30936 TemperatureUnit.convert = function(to, from, temperature) {
30937 	var result = 0;
30938 	from = TemperatureUnit.aliases[from] || from;
30939 	to = TemperatureUnit.aliases[to] || to;
30940 	if (from === to)
30941 		return temperature;
30942 
30943 	else if (from === "celsius") {
30944 		if (to === "fahrenheit") {
30945 			result = ((temperature * 9 / 5) + 32);
30946 		} else if (to === "kelvin") {
30947 			result = (temperature + 273.15);
30948 		}
30949 
30950 	} else if (from === "fahrenheit") {
30951 		if (to === "celsius") {
30952 			result = ((5 / 9 * (temperature - 32)));
30953 		} else if (to === "kelvin") {
30954 			result = ((temperature + 459.67) * 5 / 9);
30955 		}
30956 	} else if (from === "kelvin") {
30957 		if (to === "celsius") {
30958 			result = (temperature - 273.15);
30959 		} else if (to === "fahrenheit") {
30960 			result = ((temperature * 9 / 5) - 459.67);
30961 		}
30962 	}
30963 
30964 	return result;
30965 };
30966 
30967 /**
30968  * Scale the measurement unit to an acceptable level. The scaling
30969  * happens so that the integer part of the amount is as small as
30970  * possible without being below zero. This will result in the 
30971  * largest units that can represent this measurement without
30972  * fractions. Measurements can only be scaled to other measurements 
30973  * of the same type.
30974  * 
30975  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
30976  * or undefined if the system can be inferred from the current measure
30977  * @return {Measurement} a new instance that is scaled to the 
30978  * right level
30979  */
30980 TemperatureUnit.prototype.scale = function(measurementsystem) {
30981     return new TemperatureUnit({
30982         unit: this.unit,
30983         amount: this.amount
30984     }); 
30985 };
30986 
30987 /**
30988  * @private
30989  * @static
30990  */
30991 TemperatureUnit.getMeasures = function () {
30992 	return ["celsius", "kelvin", "fahrenheit"];
30993 };
30994 TemperatureUnit.metricToUScustomary = {
30995 	"celsius": "fahrenheit"
30996 };
30997 TemperatureUnit.usCustomaryToMetric = {
30998 	"fahrenheit": "celsius"
30999 };
31000 
31001 /**
31002  * Localize the measurement to the commonly used measurement in that locale. For example
31003  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
31004  * the formatted number should be automatically converted to the most appropriate 
31005  * measure in the other system, in this case, mph. The formatted result should
31006  * appear as "37.3 mph". 
31007  * 
31008  * @param {string} locale current locale string
31009  * @returns {Measurement} a new instance that is converted to locale
31010  */
31011 TemperatureUnit.prototype.localize = function(locale) {
31012     var to;
31013     if (locale === "en-US" ) {
31014         to = TemperatureUnit.metricToUScustomary[this.unit] || this.unit;
31015     } else {
31016         to = TemperatureUnit.usCustomaryToMetric[this.unit] || this.unit;
31017     }
31018     return new TemperatureUnit({
31019         unit: to,
31020         amount: this
31021     });
31022 };
31023 //register with the factory method
31024 Measurement._constructors["temperature"] = TemperatureUnit;
31025 
31026 
31027 /*< TimeUnit.js */
31028 /*
31029  * Time.js - Unit conversions for Times/times
31030  * 
31031  * Copyright © 2014-2015, JEDLSoft
31032  *
31033  * Licensed under the Apache License, Version 2.0 (the "License");
31034  * you may not use this file except in compliance with the License.
31035  * You may obtain a copy of the License at
31036  *
31037  *     http://www.apache.org/licenses/LICENSE-2.0
31038  *
31039  * Unless required by applicable law or agreed to in writing, software
31040  * distributed under the License is distributed on an "AS IS" BASIS,
31041  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31042  *
31043  * See the License for the specific language governing permissions and
31044  * limitations under the License.
31045  */
31046 
31047 /*
31048 !depends 
31049 Measurement.js
31050 */
31051 
31052 
31053 /**
31054  * @class
31055  * Create a new time measurement instance.
31056  * 
31057  * @constructor
31058  * @extends Measurement
31059  * @param options {{unit:string,amount:number|string|undefined}} Options controlling 
31060  * the construction of this instance
31061  */
31062 var TimeUnit = function (options) {
31063 	this.unit = "second";
31064 	this.amount = 0;
31065 	this.aliases = TimeUnit.aliases; // share this table in all instances
31066 	
31067 	if (options) {
31068 		if (typeof(options.unit) !== 'undefined') {
31069 			this.originalUnit = options.unit;
31070 			this.unit = this.aliases[options.unit] || options.unit;
31071 		}
31072 		
31073 		if (typeof(options.amount) === 'object') {
31074 			if (options.amount.getMeasure() === "time") {
31075 				this.amount = TimeUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
31076 			} else {
31077 				throw "Cannot convert units " + options.amount.unit + " to a time";
31078 			}
31079 		} else if (typeof(options.amount) !== 'undefined') {
31080 			this.amount = parseFloat(options.amount);
31081 		}
31082 	}
31083 	
31084 	if (typeof(TimeUnit.ratios[this.unit]) === 'undefined') {
31085 		throw "Unknown unit: " + options.unit;
31086 	}
31087 };
31088 
31089 TimeUnit.prototype = new Measurement();
31090 TimeUnit.prototype.parent = Measurement;
31091 TimeUnit.prototype.constructor = TimeUnit;
31092 
31093 TimeUnit.ratios = {
31094 	/*              index  nsec        msec        mlsec       sec        min          hour          day           week         month        year         decade        century    */           
31095 	"nanosecond":   [ 1,   1,          0.001,      1e-6,       1e-9,      1.6667e-11,  2.7778e-13,   1.1574e-14,   1.6534e-15,  3.8027e-16,  3.1689e-17,  3.1689e-18,   3.1689e-19  ],  
31096 	"microsecond":  [ 2,   1000,       1,          0.001,      1e-6,      1.6667e-8,   2.7778e-10,   1.1574e-11,   1.6534e-12,  3.8027e-13,  3.1689e-14,  3.1689e-15,   3.1689e-16  ],  
31097 	"millisecond":  [ 3,   1e+6,       1000,       1,          0.001,     1.6667e-5,   2.7778e-7,    1.1574e-8,    1.6534e-9,   3.8027e-10,  3.1689e-11,  3.1689e-12,   3.1689e-13  ],
31098 	"second":       [ 4,   1e+9,       1e+6,       1000,       1,         0.0166667,   0.000277778,  1.1574e-5,    1.6534e-6,   3.8027e-7,   3.1689e-8,   3.1689e-9,    3.1689e-10  ],
31099 	"minute":       [ 5,   6e+10,      6e+7,       60000,      60,        1,           0.0166667,    0.000694444,  9.9206e-5,   2.2816e-5,   1.9013e-6,   1.9013e-7,    1.9013e-8   ],
31100     "hour":         [ 6,   3.6e+12,    3.6e+9,     3.6e+6,     3600,      60,          1,            0.0416667,    0.00595238,  0.00136895,  0.00011408,  1.1408e-5,    1.1408e-6   ],
31101     "day":          [ 7,   8.64e+13,   8.64e+10,   8.64e+7,    86400,     1440,        24,           1,            0.142857,    0.0328549,   0.00273791,  0.000273791,  2.7379e-5   ],
31102     "week":         [ 8,   6.048e+14,  6.048e+11,  6.048e+8,   604800,    10080,       168,          7,            1,           0.229984,    0.0191654,   0.00191654,   0.000191654 ],
31103     "month":        [ 9,   2.63e+15,   2.63e+12,   2.63e+9,    2.63e+6,   43829.1,     730.484,      30.4368,      4.34812,     1,           0.0833333,   0.00833333,   0.000833333 ],
31104     "year":         [ 10,  3.156e+16,  3.156e+13,  3.156e+10,  3.156e+7,  525949,      8765.81,      365.242,      52.1775,     12,          1,           0.1,          0.01        ],
31105     "decade":       [ 11,  3.156e+17,  3.156e+14,  3.156e+11,  3.156e+8,  5.259e+6,    87658.1,      3652.42,      521.775,     120,         10,          1,            0.1         ],
31106     "century":      [ 12,  3.156e+18,  3.156e+18,  3.156e+12,  3.156e+9,  5.259e+7,    876581,       36524.2,      5217.75,     1200,        100,         10,           1           ]
31107 };
31108 
31109 /**
31110  * Return the type of this measurement. Examples are "mass",
31111  * "length", "speed", etc. Measurements can only be converted
31112  * to measurements of the same type.<p>
31113  * 
31114  * The type of the units is determined automatically from the 
31115  * units. For example, the unit "grams" is type "mass". Use the 
31116  * static call {@link Measurement.getAvailableUnits}
31117  * to find out what units this version of ilib supports.
31118  *  
31119  * @return {string} the name of the type of this measurement
31120  */
31121 TimeUnit.prototype.getMeasure = function() {
31122 	return "time";
31123 };
31124 
31125 /**
31126  * Return a new measurement instance that is converted to a new
31127  * measurement unit. Measurements can only be converted
31128  * to measurements of the same type.<p>
31129  *  
31130  * @param {string} to The name of the units to convert to
31131  * @return {Measurement|undefined} the converted measurement
31132  * or undefined if the requested units are for a different
31133  * measurement type 
31134  */
31135 TimeUnit.prototype.convert = function(to) {
31136 	if (!to || typeof(TimeUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
31137 		return undefined;
31138 	}
31139 	return new TimeUnit({
31140 		unit: to,
31141 		amount: this
31142 	});
31143 };
31144 
31145 TimeUnit.aliases = {
31146     "ns": "nanosecond",
31147     "NS": "nanosecond",
31148     "nS": "nanosecond",
31149     "Ns": "nanosecond",
31150     "Nanosecond": "nanosecond",
31151     "Nanoseconds": "nanosecond",
31152     "nanosecond": "nanosecond",
31153     "nanoseconds": "nanosecond",
31154     "NanoSecond": "nanosecond",
31155     "NanoSeconds": "nanosecond",
31156     "μs": "microsecond",
31157     "μS": "microsecond",
31158     "microsecond": "microsecond",
31159     "microseconds": "microsecond",
31160     "Microsecond": "microsecond",
31161     "Microseconds": "microsecond",
31162     "MicroSecond": "microsecond",
31163     "MicroSeconds": "microsecond",
31164     "ms": "millisecond",
31165     "MS": "millisecond",
31166     "mS": "millisecond",
31167     "Ms": "millisecond",
31168     "millisecond": "millisecond",
31169     "milliseconds": "millisecond",
31170     "Millisecond": "millisecond",
31171     "Milliseconds": "millisecond",
31172     "MilliSecond": "millisecond",
31173     "MilliSeconds": "millisecond",
31174     "s": "second",
31175     "S": "second",
31176     "sec": "second",
31177     "second": "second",
31178     "seconds": "second",
31179     "Second": "second",
31180     "Seconds": "second",
31181     "min": "minute",
31182     "Min": "minute",
31183     "minute": "minute",
31184     "minutes": "minute",
31185     "Minute": "minute",
31186     "Minutes": "minute",
31187     "h": "hour",
31188     "H": "hour",
31189     "hr": "hour",
31190     "Hr": "hour",
31191     "hR": "hour",
31192     "HR": "hour",
31193     "hour": "hour",
31194     "hours": "hour",
31195     "Hour": "hour",
31196     "Hours": "hour",
31197     "Hrs": "hour",
31198     "hrs": "hour",
31199     "day": "day",
31200     "days": "day",
31201     "Day": "day",
31202     "Days": "day",
31203     "week": "week",
31204     "weeks": "week",
31205     "Week": "week",
31206     "Weeks": "week",
31207     "month": "month",
31208     "Month": "month",
31209     "months": "month",
31210     "Months": "month",
31211     "year": "year",
31212     "years": "year",
31213     "Year": "year",
31214     "Years": "year",
31215     "yr": "year",
31216     "Yr": "year",
31217     "yrs": "year",
31218     "Yrs": "year",
31219     "decade": "decade",
31220     "decades": "decade",
31221     "Decade": "decade",
31222     "Decades": "decade",
31223     "century": "century",
31224     "centuries": "century",
31225     "Century": "century",
31226     "Centuries": "century"
31227 };
31228 
31229 /**
31230  * Convert a time to another measure.
31231  * @static
31232  * @param to {string} unit to convert to
31233  * @param from {string} unit to convert from
31234  * @param time {number} amount to be convert
31235  * @returns {number|undefined} the converted amount
31236  */
31237 TimeUnit.convert = function(to, from, time) {
31238     from = TimeUnit.aliases[from] || from;
31239     to = TimeUnit.aliases[to] || to;
31240     var fromRow = TimeUnit.ratios[from];
31241     var toRow = TimeUnit.ratios[to];
31242     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
31243         return undefined;
31244     }	
31245     return time * fromRow[toRow[0]];
31246 };
31247 
31248 /**
31249  * Localize the measurement to the commonly used measurement in that locale. For example
31250  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
31251  * the formatted number should be automatically converted to the most appropriate 
31252  * measure in the other system, in this case, mph. The formatted result should
31253  * appear as "37.3 mph". 
31254  * 
31255  * @param {string} locale current locale string
31256  * @returns {Measurement} a new instance that is converted to locale
31257  */
31258 TimeUnit.prototype.localize = function(locale) {
31259     return new TimeUnit({
31260         unit: this.unit,
31261         amount: this.amount
31262     });
31263 };
31264 
31265 /**
31266  * Scale the measurement unit to an acceptable level. The scaling
31267  * happens so that the integer part of the amount is as small as
31268  * possible without being below zero. This will result in the 
31269  * largest units that can represent this measurement without
31270  * fractions. Measurements can only be scaled to other measurements 
31271  * of the same type.
31272  * 
31273  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
31274  * or undefined if the system can be inferred from the current measure
31275  * @return {Measurement} a new instance that is scaled to the 
31276  * right level
31277  */
31278 TimeUnit.prototype.scale = function(measurementsystem) {
31279 
31280     var fromRow = TimeUnit.ratios[this.unit];
31281     var time = this.amount;
31282     var munit = this.unit;
31283     var i;
31284 
31285     time = 18446744073709551999;
31286     for (var m in TimeUnit.ratios) {
31287     	i = TimeUnit.ratios[m][0];
31288     	var tmp = this.amount * fromRow[i];
31289     	if (tmp >= 1 && tmp < time) {
31290 	        time = tmp;
31291 	        munit = m;
31292     	}
31293     }
31294 
31295     return new TimeUnit({
31296         unit: munit,
31297         amount: time
31298     });
31299 };
31300 /**
31301  * @private
31302  * @static
31303  */
31304 TimeUnit.getMeasures = function () {
31305 	var ret = [];
31306 	for (var m in TimeUnit.ratios) {
31307 		ret.push(m);
31308 	}
31309 	return ret;
31310 };
31311 
31312 //register with the factory method
31313 Measurement._constructors["time"] = TimeUnit;
31314 
31315 
31316 /*< VelocityUnit.js */
31317 /*
31318  * VelocityUnit.js - Unit conversions for VelocityUnits/speeds
31319  * 
31320  * Copyright © 2014-2015, JEDLSoft
31321  *
31322  * Licensed under the Apache License, Version 2.0 (the "License");
31323  * you may not use this file except in compliance with the License.
31324  * You may obtain a copy of the License at
31325  *
31326  *     http://www.apache.org/licenses/LICENSE-2.0
31327  *
31328  * Unless required by applicable law or agreed to in writing, software
31329  * distributed under the License is distributed on an "AS IS" BASIS,
31330  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31331  *
31332  * See the License for the specific language governing permissions and
31333  * limitations under the License.
31334  */
31335 
31336 /*
31337 !depends 
31338 Measurement.js
31339 */
31340 
31341 
31342 /**
31343  * @class
31344  * Create a new speed measurement instance.
31345  * 
31346  * @constructor
31347  * @extends Measurement
31348  * @param options {{unit:string,amount:number|string|undefined}} Options controlling 
31349  * the construction of this instance
31350  */
31351 var VelocityUnit = function (options) {
31352 	this.unit = "meters/second";
31353 	this.amount = 0;
31354 	this.aliases = VelocityUnit.aliases; // share this table in all instances
31355 	
31356 	if (options) {
31357 		if (typeof(options.unit) !== 'undefined') {
31358 			this.originalUnit = options.unit;
31359 			this.unit = this.aliases[options.unit] || options.unit;
31360 		}
31361 		
31362 		if (typeof(options.amount) === 'object') {
31363 			if (options.amount.getMeasure() === "speed") {
31364 				this.amount = VelocityUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
31365 			} else {
31366 				throw "Cannot convert units " + options.amount.unit + " to a speed";
31367 			}
31368 		} else if (typeof(options.amount) !== 'undefined') {
31369 			this.amount = parseFloat(options.amount);
31370 		}
31371 	}
31372 	
31373 	if (typeof(VelocityUnit.ratios[this.unit]) === 'undefined') {
31374 		throw "Unknown unit: " + options.unit;
31375 	}
31376 };
31377 
31378 VelocityUnit.prototype = new Measurement();
31379 VelocityUnit.prototype.parent = Measurement;
31380 VelocityUnit.prototype.constructor = VelocityUnit;
31381 
31382 VelocityUnit.ratios = {
31383 	/*                 index, k/h         f/s         miles/h      knot         m/s        km/s         miles/s */
31384     "kilometer/hour":   [ 1,  1,          0.911344,   0.621371,    0.539957,    0.277778,  2.77778e-4,  1.72603109e-4 ],
31385 	"feet/second":      [ 2,  1.09728,    1,          0.681818,    0.592484,    0.3048,    3.048e-4,    1.89393939e-4 ],  
31386     "miles/hour":       [ 3,  1.60934,    1.46667,    1,           0.868976,    0.44704,   4.4704e-4,   2.77777778e-4 ],
31387     "knot":             [ 4,  1.852,      1.68781,    1.15078,     1,           0.514444,  5.14444e-4,  3.19660958e-4 ],
31388   	"meters/second":    [ 5,  3.6,        3.28084,    2.236936,    1.94384,     1,         0.001,       6.21371192e-4 ],	
31389     "kilometer/second": [ 6,  3600,       3280.8399,  2236.93629,  1943.84449,  1000,      1,           0.621371192   ],
31390     "miles/second":     [ 7,  5793.6384,  5280,       3600,        3128.31447,  1609.344,  1.609344,    1             ]
31391 };
31392 
31393 VelocityUnit.metricSystem = {
31394     "kilometer/hour": 1,
31395     "meters/second": 5,
31396     "kilometer/second": 6
31397 };
31398 VelocityUnit.imperialSystem = {
31399     "feet/second": 2,
31400     "miles/hour": 3,
31401     "knot": 4,
31402     "miles/second": 7
31403 };
31404 VelocityUnit.uscustomarySystem = {
31405     "feet/second": 2,
31406     "miles/hour": 3,
31407     "knot": 4,
31408     "miles/second": 7
31409 };
31410 
31411 VelocityUnit.metricToUScustomary = {
31412     "kilometer/hour": "miles/hour",
31413     "meters/second": "feet/second",
31414     "kilometer/second": "miles/second"
31415 };
31416 VelocityUnit.UScustomaryTometric = {
31417     "miles/hour": "kilometer/hour",
31418     "feet/second": "meters/second",
31419     "miles/second": "kilometer/second",
31420     "knot": "kilometer/hour"
31421 };
31422 
31423 /**
31424  * Return the type of this measurement. Examples are "mass",
31425  * "length", "speed", etc. Measurements can only be converted
31426  * to measurements of the same type.<p>
31427  * 
31428  * The type of the units is determined automatically from the 
31429  * units. For example, the unit "grams" is type "mass". Use the 
31430  * static call {@link Measurement.getAvailableUnits}
31431  * to find out what units this version of ilib supports.
31432  *  
31433  * @return {string} the name of the type of this measurement
31434  */
31435 VelocityUnit.prototype.getMeasure = function() {
31436 	return "speed";
31437 };
31438 
31439 /**
31440  * Return a new measurement instance that is converted to a new
31441  * measurement unit. Measurements can only be converted
31442  * to measurements of the same type.<p>
31443  *  
31444  * @param {string} to The name of the units to convert to
31445  * @return {Measurement|undefined} the converted measurement
31446  * or undefined if the requested units are for a different
31447  * measurement type 
31448  */
31449 VelocityUnit.prototype.convert = function(to) {
31450 	if (!to || typeof(VelocityUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
31451 		return undefined;
31452 	}
31453 	return new VelocityUnit({
31454 		unit: to,
31455 		amount: this
31456 	});
31457 };
31458 
31459 /**
31460  * Scale the measurement unit to an acceptable level. The scaling
31461  * happens so that the integer part of the amount is as small as
31462  * possible without being below zero. This will result in the 
31463  * largest units that can represent this measurement without
31464  * fractions. Measurements can only be scaled to other measurements 
31465  * of the same type.
31466  * 
31467  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
31468  * or undefined if the system can be inferred from the current measure
31469  * @return {Measurement} a new instance that is scaled to the 
31470  * right level
31471  */
31472 VelocityUnit.prototype.scale = function(measurementsystem) {
31473 	var mSystem;
31474 	if (measurementsystem === "metric" ||
31475 	    (typeof (measurementsystem) === 'undefined' && typeof (VelocityUnit.metricSystem[this.unit]) !== 'undefined')) {
31476 		mSystem = VelocityUnit.metricSystem;
31477 	} else if (measurementsystem === "imperial" ||
31478 	    (typeof (measurementsystem) === 'undefined' && typeof (VelocityUnit.imperialSystem[this.unit]) !== 'undefined')) {
31479 		mSystem = VelocityUnit.imperialSystem;
31480 	} else if (measurementsystem === "uscustomary" ||
31481 	    (typeof (measurementsystem) === 'undefined' && typeof (VelocityUnit.uscustomarySystem[this.unit]) !== 'undefined')) {
31482 		mSystem = VelocityUnit.uscustomarySystem;
31483 	} else {
31484 		return new VelocityUnit({
31485 		    unit: this.unit,
31486 		    amount: this.amount
31487 		});
31488 	}
31489 
31490 	var speed = this.amount;
31491 	var munit = this.unit;
31492 	var fromRow = VelocityUnit.ratios[this.unit];
31493 
31494 	speed = 18446744073709551999;
31495 	
31496     for ( var m in mSystem) {
31497 		var tmp = this.amount * fromRow[mSystem[m]];
31498 		if (tmp >= 1 && tmp < speed) {
31499 			speed = tmp;
31500 			munit = m;
31501 		}
31502 	}
31503 
31504 	return new VelocityUnit({
31505 	    unit: munit,
31506 	    amount: speed
31507 	});
31508 };
31509 
31510 /**
31511  * Localize the measurement to the commonly used measurement in that locale. For example
31512  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
31513  * the formatted number should be automatically converted to the most appropriate 
31514  * measure in the other system, in this case, mph. The formatted result should
31515  * appear as "37.3 mph". 
31516  * 
31517  * @param {string} locale current locale string
31518  * @returns {Measurement} a new instance that is converted to locale
31519  */
31520 VelocityUnit.prototype.localize = function(locale) {
31521     var to;
31522     if (locale === "en-US" || locale === "en-GB") {
31523         to = VelocityUnit.metricToUScustomary[this.unit] || this.unit;
31524     } else {
31525         to = VelocityUnit.UScustomaryTometric[this.unit] || this.unit;
31526     }
31527     return new VelocityUnit({
31528 		unit: to,
31529 		amount: this
31530     });
31531 };
31532 
31533 VelocityUnit.aliases = {
31534     "foot/sec": "feet/second",
31535     "foot/s": "feet/second",
31536     "feet/s": "feet/second",
31537     "f/s": "feet/second",
31538     "feet/second": "feet/second",
31539     "feet/sec": "feet/second",
31540     "meter/sec": "meters/second",
31541     "meter/s": "meters/second",
31542     "meters/s": "meters/second",
31543     "metre/sec": "meters/second",
31544     "metre/s": "meters/second",
31545     "metres/s": "meters/second",
31546     "mt/sec": "meters/second",
31547     "m/sec": "meters/second",
31548     "mt/s": "meters/second",
31549     "m/s": "meters/second",
31550     "mps": "meters/second",
31551     "meters/second": "meters/second",
31552     "meters/sec": "meters/second",
31553     "kilometer/hour": "kilometer/hour",
31554     "km/hour": "kilometer/hour",
31555     "kilometers/hour": "kilometer/hour",
31556     "kmh": "kilometer/hour",
31557     "km/h": "kilometer/hour",
31558     "kilometer/h": "kilometer/hour",
31559     "kilometers/h": "kilometer/hour",
31560     "km/hr": "kilometer/hour",
31561     "kilometer/hr": "kilometer/hour",
31562     "kilometers/hr": "kilometer/hour",
31563     "kilometre/hour": "kilometer/hour",
31564     "mph": "miles/hour",
31565     "mile/hour": "miles/hour",
31566     "mile/hr": "miles/hour",
31567     "mile/h": "miles/hour",
31568     "miles/h": "miles/hour",
31569     "miles/hr": "miles/hour",
31570     "miles/hour": "miles/hour",
31571     "kn": "knot",
31572     "kt": "knot",
31573     "kts": "knot",
31574     "knots": "knot",
31575     "nm/h": "knot",
31576     "nm/hr": "knot",
31577     "nauticalmile/h": "knot",
31578     "nauticalmile/hr": "knot",
31579     "nauticalmile/hour": "knot",
31580     "nauticalmiles/hr": "knot",
31581     "nauticalmiles/hour": "knot",
31582     "knot": "knot",
31583     "kilometer/second": "kilometer/second",
31584     "kilometer/sec": "kilometer/second",
31585     "kilometre/sec": "kilometer/second",
31586     "Kilometre/sec": "kilometer/second",
31587     "kilometers/second": "kilometer/second",
31588     "kilometers/sec": "kilometer/second",
31589     "kilometres/sec": "kilometer/second",
31590     "Kilometres/sec": "kilometer/second",
31591     "km/sec": "kilometer/second",
31592     "Km/s": "kilometer/second",
31593     "km/s": "kilometer/second",
31594     "miles/second": "miles/second",
31595     "miles/sec": "miles/second",
31596     "miles/s": "miles/second",
31597     "mile/s": "miles/second",
31598     "mile/sec": "miles/second",
31599     "Mile/s": "miles/second"
31600 };
31601 
31602 /**
31603  * Convert a speed to another measure.
31604  * @static
31605  * @param to {string} unit to convert to
31606  * @param from {string} unit to convert from
31607  * @param speed {number} amount to be convert
31608  * @returns {number|undefined} the converted amount
31609  */
31610 VelocityUnit.convert = function(to, from, speed) {
31611     from = VelocityUnit.aliases[from] || from;
31612     to = VelocityUnit.aliases[to] || to;
31613 	var fromRow = VelocityUnit.ratios[from];
31614 	var toRow = VelocityUnit.ratios[to];
31615 	if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
31616 		return undefined;
31617 	}	
31618 	var result = speed * fromRow[toRow[0]];
31619     return result;
31620 };
31621 
31622 /**
31623  * @private
31624  * @static
31625  */
31626 VelocityUnit.getMeasures = function () {
31627 	var ret = [];
31628 	for (var m in VelocityUnit.ratios) {
31629 		ret.push(m);
31630 	}
31631 	return ret;
31632 };
31633 
31634 //register with the factory method
31635 Measurement._constructors["speed"] = VelocityUnit;
31636 Measurement._constructors["velocity"] = VelocityUnit;
31637 
31638 
31639 /*< VolumeUnit.js */
31640 /*
31641  * volume.js - Unit conversions for volume
31642  * 
31643  * Copyright © 2014-2015, JEDLSoft
31644  *
31645  * Licensed under the Apache License, Version 2.0 (the "License");
31646  * you may not use this file except in compliance with the License.
31647  * You may obtain a copy of the License at
31648  *
31649  *     http://www.apache.org/licenses/LICENSE-2.0
31650  *
31651  *
31652  * Unless required by applicable law or agreed to in writing, software
31653  * distributed under the License is distributed on an "AS IS" BASIS,
31654  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31655  *
31656  * See the License for the specific language governing permissions and
31657  * limitations under the License.
31658  */
31659 
31660 /*
31661 !depends 
31662 Measurement.js
31663 */
31664 
31665 
31666 /**
31667  * @class
31668  * Create a new Volume measurement instance.
31669  * 
31670  * @constructor
31671  * @extends Measurement
31672  * @param options {{unit:string,amount:number|string|undefined}} Options controlling 
31673  * the construction of this instance
31674  */
31675 var VolumeUnit = function (options) {
31676 	this.unit = "cubic meter";
31677 	this.amount = 0;
31678 	this.aliases = VolumeUnit.aliases; // share this table in all instances
31679 	
31680 	if (options) {
31681 		if (typeof(options.unit) !== 'undefined') {
31682 			this.originalUnit = options.unit;
31683 			this.unit = this.aliases[options.unit] || options.unit;
31684 		}
31685 		
31686 		if (typeof(options.amount) === 'object') {
31687 			if (options.amount.getMeasure() === "volume") {
31688 				this.amount = VolumeUnit.convert(this.unit, options.amount.getUnit(), options.amount.getAmount());
31689 			} else {
31690 				throw "Cannot convert unit " + options.amount.unit + " to a volume";
31691 			}
31692 		} else if (typeof(options.amount) !== 'undefined') {
31693 			this.amount = parseFloat(options.amount);
31694 		}
31695 	}
31696 	
31697 	if (typeof(VolumeUnit.ratios[this.unit]) === 'undefined') {
31698 		throw "Unknown unit: " + options.unit;
31699 	}
31700 };
31701 
31702 VolumeUnit.prototype = new Measurement();
31703 VolumeUnit.prototype.parent = Measurement;
31704 VolumeUnit.prototype.constructor = VolumeUnit;
31705 
31706 VolumeUnit.ratios = {
31707     /*                 index, tsp,      tbsp,          cubic inch  us ounce, cup,        pint,       quart,      gallon,      cubic foot,  milliliter  liter,      cubic meter, imperial tsp,  imperial tbsp, imperial ounce,  imperial pint,  imperial quart, imperial gal, */
31708     "tsp" :            [1,    1,        0.333333,      0.300781,   0.166667, 0.0208333,  0.0104167,  0.00520833, 0.00130208,  0.000174063, 4.92892,    0.00492892, 4.9289e-6,   0.832674,      0.277558,      0.173474,        0.00867369,     0.00433684,     0.00108421          ],
31709     "tbsp":            [2,    3,        1,             0.902344,   0.5,      0.0625,     0.0312,     0.015625,   0.00390625,  0.00052219,  14.7868,    0.0147868,  1.4787e-5,   2.49802,       0.832674,      0.520421,        0.0260211,      0.0130105,      0.00325263          ],
31710     "cubic inch":      [3,    3.32468,  1.10823,       1,          0.554113, 0.0692641,  0.034632,   0.017316,   0.004329,    0.000578704, 16.3871,    0.0163871,  1.6387e-5,   2.76837,       0.92279,       0.576744,        0.0288372,      0.0144186,      0.00360465          ],
31711     "us ounce":        [4,    6,        2,             1.80469,    1,        0.125,      0.0625,     0.0078125,  0.0078125,   0.00104438,  29.5735,    0.0295735,  2.9574e-5,   4.99604,       1.04084,       1.04084,         0.0520421,      0.0260211,      0.00650526          ],
31712     "cup":             [5,    48,       16,            14.4375,    8,        1,          0.5,        0.25,       0.0625,      0.00835503,  236.588,    0.236588,   0.000236588, 39.9683,       13.3228,       8.32674,         0.416337,       0.208168,       0.0520421           ],
31713     "pint":            [6,    96,       32,            28.875,     16,       2,          1,          0.5,        0.125,       0.0167101,   473.176,    0.473176,   0.000473176, 79.9367,       26.6456,       16.6535,         0.832674,       0.416337,       0.104084            ],
31714     "quart":           [7,    192,      64,            57.75,      32,       4,          2,          1,          0.25,        0.0334201,   946.353,    0.946353,   0.000946353, 159.873,       53.2911,       33.307,          1.66535,        0.832674,       0.208168            ],
31715     "gallon":          [8,    768,      256,           231,        128,      16,         8,          4,          1,           0.133681,    3785.41,    3.78541,    0.00378541,  639.494,       213.165,       133.228,         6.66139,        3.3307,         0.832674            ],
31716     "cubic foot":      [9,    5745.04,  1915.01,       1728,       957.506,  119.688,    59.8442,    29.9221,    7.48052,     1,           28316.8,    28.3168,    0.0283168,   4783.74,       1594.58,       996.613,         49.8307,        24.9153,        6.22883             ],
31717     "milliliter":      [10,   0.202884, 0.067628,      0.0610237,  0.033814, 0.00422675, 0.00211338, 0.00105669, 0.000264172, 3.5315e-5,   1,          0.001,      1e-6,        0.168936,      0.0563121,     0.0351951,       0.00175975,     0.000879877,    0.000219969         ],
31718     "liter":           [11,   202.884,  67.628,        61.0237,    33.814,   4.22675,    2.11338,    1.05669,    0.264172,    0.0353147,   1000,       1,          0.001,       56.3121,       56.3121,       35.191,          1.75975,        0.879877,       0.219969            ],
31719     "cubic meter":     [12,   202884,   67628,         61023.7,    33814,    4226.75,    2113.38,    1056.69,    264.172,     35.3147,     1e+6,       1000,       1,           168936,        56312.1,       35195.1,         1759.75,        879.877,        219.969             ],
31720     "imperial tsp":    [13,   1.20095,  0.200158,      0.361223,   0.600475, 0.0250198,  0.0125099,  0.00625495, 0.00156374,  0.000209041, 5.91939,    0.00591939, 5.9194e-6,   1,             0.333333,      0.208333,        0.0104167,      0.00520833,     0.00130208          ],
31721     "imperial tbsp":   [14,   3.60285,  1.20095,       1.08367,    0.600475, 0.0750594,  0.0375297,  0.0187649,  0.00469121,  0.000627124, 17.7582,    0.0177582,  1.7758e-5,   3,             1,             0.625,           0.03125,        0.015625,       0.00390625          ],
31722     "imperial ounce":  [15,   5.76456,  1.92152,       1.73387,    0.96076,  0.120095,   0.0600475,  0.0300238,  0.00750594,  0.0010034,   28.4131,    0.0284131,  2.8413e-5,   4.8,           1.6,           1,               0.05,           0.025,          0.00625             ],
31723     "imperial pint":   [16,   115.291,  38.4304,       34.6774,    19.2152,  2.4019,     1.20095,    0.600475,   0.150119,    0.020068,    568.261,    0.568261,   0.000568261, 96,            32,            20,              1,              0.5,            0.125               ],
31724     "imperial quart":  [17,   230.582,  76.8608,       69.3549,    38.4304,  4.8038,     2.4019,     1.20095,    0.300238,    0.0401359,   1136.52,    1.13652,    0.00113652,  192,           64,            40,              2,              1,              0.25                ],
31725     "imperial gallon": [18,   922.33,   307.443,       277.42,     153.722,  19.2152,    9.6076,     4.8038,     1.20095,     0.160544,    4546.09,    4.54609,    0.00454609,  768,           256,           160,             8,              4,              1                   ]
31726 };
31727 
31728 /**
31729  * Return the type of this measurement. Examples are "mass",
31730  * "length", "speed", etc. Measurements can only be converted
31731  * to measurements of the same type.<p>
31732  * 
31733  * The type of the units is determined automatically from the 
31734  * units. For example, the unit "grams" is type "mass". Use the 
31735  * static call {@link Measurement.getAvailableUnits}
31736  * to find out what units this version of ilib supports.
31737  *  
31738  * @return {string} the name of the type of this measurement
31739  */
31740 VolumeUnit.prototype.getMeasure = function() {
31741 	return "volume";
31742 };
31743 
31744 /**
31745  * Return a new measurement instance that is converted to a new
31746  * measurement unit. Measurements can only be converted
31747  * to measurements of the same type.<p>
31748  *  
31749  * @param {string} to The name of the units to convert to
31750  * @return {Measurement|undefined} the converted measurement
31751  * or undefined if the requested units are for a different
31752  * measurement type 
31753  */
31754 VolumeUnit.prototype.convert = function(to) {
31755 	if (!to || typeof(VolumeUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
31756 		return undefined;
31757 	}
31758 	return new VolumeUnit({
31759 		unit: to,
31760 		amount: this
31761 	});
31762 };
31763 
31764 VolumeUnit.aliases = {
31765     "US gal": "gallon",
31766     "US gallon": "gallon",
31767     "US Gal": "gallon",
31768     "US Gallons": "gallon",
31769     "Gal(US)": "gallon",
31770     "gal(US)": "gallon",
31771     "gallon": "gallon",
31772     "quart": "quart",
31773     "US quart": "quart",
31774     "US quarts": "quart",
31775     "US Quart": "quart",
31776     "US Quarts": "quart",
31777     "US qt": "quart",
31778     "Qt(US)": "quart",
31779     "qt(US)": "quart",
31780     "US pint": "pint",
31781     "US Pint": "pint",
31782     "pint": "pint",
31783     "pint(US)": "pint",
31784     "Pint(US)": "pint",
31785     "US cup": "cup",
31786     "US Cup": "cup",
31787     "cup(US)": "cup",
31788     "Cup(US)": "cup",
31789     "cup": "cup",
31790     "us ounce": "us ounce",
31791     "US ounce": "us ounce",
31792     "℥": "us ounce",
31793     "US Oz": "us ounce",
31794     "oz(US)": "us ounce",
31795     "Oz(US)": "us ounce",
31796     "US tbsp": "tbsp",
31797     "tbsp": "tbsp",
31798     "tbsp(US)": "tbsp",
31799     "US tablespoon": "tbsp",
31800     "US tsp": "tsp",
31801     "tsp(US)": "tsp",
31802     "tsp": "tsp",
31803     "Cubic meter": "cubic meter",
31804     "cubic meter": "cubic meter",
31805     "Cubic metre": "cubic meter",
31806     "cubic metre": "cubic meter",
31807     "m3": "cubic meter",
31808     "Liter": "liter",
31809     "Liters": "liter",
31810     "liter": "liter",
31811     "L": "liter",
31812     "l": "liter",
31813     "Milliliter": "milliliter",
31814     "ML": "milliliter",
31815     "ml": "milliliter",
31816     "milliliter": "milliliter",
31817     "mL": "milliliter",
31818     "Imperial gal": "imperial gallon",
31819     "imperial gallon": "imperial gallon",
31820     "Imperial gallon": "imperial gallon",
31821     "gallon(imperial)": "imperial gallon",
31822     "gal(imperial)": "imperial gallon",
31823     "Imperial quart": "imperial quart",
31824     "imperial quart": "imperial quart",
31825     "Imperial Quart": "imperial quart",
31826     "IMperial qt": "imperial quart",
31827     "qt(Imperial)": "imperial quart",
31828     "quart(imperial)": "imperial quart",
31829     "Imperial pint": "imperial pint",
31830     "imperial pint": "imperial pint",
31831     "pint(Imperial)": "imperial pint",
31832     "imperial oz": "imperial ounce",
31833     "imperial ounce": "imperial ounce",
31834     "Imperial Ounce": "imperial ounce",
31835     "Imperial tbsp": "imperial tbsp",
31836     "imperial tbsp": "imperial tbsp",
31837     "tbsp(Imperial)": "imperial tbsp",
31838     "Imperial tsp": "imperial tsp",
31839     "imperial tsp": "imperial tsp",
31840     "tsp(Imperial)": "imperial tsp",
31841     "Cubic foot": "cubic foot",
31842     "cubic foot": "cubic foot",
31843     "Cubic Foot": "cubic foot",
31844     "Cubic feet": "cubic foot",
31845     "cubic Feet": "cubic foot",
31846     "cubic ft": "cubic foot",
31847     "ft3": "cubic foot",
31848     "Cubic inch": "cubic inch",
31849     "Cubic inches": "cubic inch",
31850     "cubic inches": "cubic inch",
31851     "cubic inch": "cubic inch",
31852     "cubic in": "cubic inch",
31853     "cu in": "cubic inch",
31854     "cu inch": "cubic inch",
31855     "inch³": "cubic inch",
31856     "in³": "cubic inch",
31857     "inch^3": "cubic inch",
31858     "in^3": "cubic inch",
31859     "c.i": "cubic inch",
31860     "CI": "cubic inch",
31861     "cui": "cubic inch"
31862 };
31863 
31864 /**
31865  * Convert a volume to another measure.
31866  * @static
31867  * @param to {string} unit to convert to
31868  * @param from {string} unit to convert from
31869  * @param volume {number} amount to be convert
31870  * @returns {number|undefined} the converted amount
31871  */
31872 VolumeUnit.convert = function(to, from, volume) {
31873     from = VolumeUnit.aliases[from] || from;
31874     to = VolumeUnit.aliases[to] || to;
31875 	var fromRow = VolumeUnit.ratios[from];
31876 	var toRow = VolumeUnit.ratios[to];
31877 	if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
31878 		return undefined;
31879 	}	
31880 	var result = volume * fromRow[toRow[0]];
31881     return result;
31882 };
31883 
31884 /**
31885  * @private
31886  * @static
31887  */
31888 VolumeUnit.getMeasures = function () {
31889 	var ret = [];
31890 	for (var m in VolumeUnit.ratios) {
31891 		ret.push(m);
31892 	}
31893 	return ret;
31894 };
31895 VolumeUnit.metricSystem = {
31896     "milliliter": 10,
31897     "liter": 11,
31898     "cubic meter": 12
31899 };
31900 VolumeUnit.imperialSystem = {
31901     "imperial tsp": 13,
31902     "imperial tbsp": 14,
31903     "imperial ounce": 15,
31904     "imperial pint": 16,
31905     "imperial quart": 17,
31906     "imperial gallon": 18
31907 };
31908 VolumeUnit.uscustomarySystem = {
31909     "tsp": 1,
31910     "tbsp": 2,
31911     "cubic inch": 3,
31912     "us ounce": 4,
31913     "cup": 5,
31914     "pint": 6,
31915     "quart": 7,
31916     "gallon": 8,
31917     "cubic foot": 9
31918 };
31919 
31920 VolumeUnit.metricToUScustomary = {
31921     "milliliter": "tsp",
31922     "liter": "quart",
31923     "cubic meter": "cubic foot"
31924 };
31925 VolumeUnit.metricToImperial = {
31926     "milliliter": "imperial tsp",
31927     "liter": "imperial quart",
31928     "cubic meter": "imperial gallon"
31929 };
31930 
31931 VolumeUnit.imperialToMetric = {
31932     "imperial tsp": "milliliter",
31933     "imperial tbsp": "milliliter",
31934     "imperial ounce": "milliliter",
31935     "imperial pint": "liter",
31936     "imperial quart": "liter",
31937     "imperial gallon": "cubic meter"
31938 };
31939 VolumeUnit.imperialToUScustomary = {
31940     "imperial tsp": "tsp",
31941     "imperial tbsp": "tbsp",
31942     "imperial ounce": "us ounce",
31943     "imperial pint": "pint",
31944     "imperial quart": "quart",
31945     "imperial gallon": "gallon"
31946 };
31947 
31948 VolumeUnit.uScustomaryToImperial = {
31949     "tsp": "imperial tsp",
31950     "tbsp": "imperial tbsp",
31951     "cubic inch": "imperial tbsp",
31952     "us ounce": "imperial ounce",
31953     "cup": "imperial ounce",
31954     "pint": "imperial pint",
31955     "quart": "imperial quart",
31956     "gallon": "imperial gallon",
31957     "cubic foot": "imperial gallon"
31958 };
31959 VolumeUnit.uScustomarylToMetric = {
31960     "tsp": "milliliter",
31961     "tbsp": "milliliter",
31962     "cubic inch": "milliliter",
31963     "us ounce": "milliliter",
31964     "cup": "milliliter",
31965     "pint": "liter",
31966     "quart": "liter",
31967     "gallon": "cubic meter",
31968     "cubic foot": "cubic meter"
31969 };
31970 
31971 /**
31972  * Localize the measurement to the commonly used measurement in that locale. For example
31973  * If a user's locale is "en-US" and the measurement is given as "60 kmh", 
31974  * the formatted number should be automatically converted to the most appropriate 
31975  * measure in the other system, in this case, mph. The formatted result should
31976  * appear as "37.3 mph". 
31977  * 
31978  * @param {string} locale current locale string
31979  * @returns {Measurement} a new instance that is converted to locale
31980  */
31981 VolumeUnit.prototype.localize = function(locale) {
31982 	var to;
31983 	if (locale === "en-US") {
31984 		to = VolumeUnit.metricToUScustomary[this.unit] ||
31985 		    VolumeUnit.imperialToUScustomary[this.unit] ||
31986 		    this.unit;
31987 	} else if (locale === "en-GB") {
31988 		to = VolumeUnit.metricToImperial[this.unit] ||
31989 		    VolumeUnit.uScustomaryToImperial[this.unit] ||
31990 		    this.unit;
31991 	} else {
31992 		to = VolumeUnit.uScustomarylToMetric[this.unit] ||
31993 		    VolumeUnit.imperialToUScustomary[this.unit] ||
31994 		    this.unit;
31995 	}
31996 	return new VolumeUnit({
31997 	    unit: to,
31998 	    amount: this
31999 	});
32000 };
32001 
32002 /**
32003  * Scale the measurement unit to an acceptable level. The scaling
32004  * happens so that the integer part of the amount is as small as
32005  * possible without being below zero. This will result in the 
32006  * largest units that can represent this measurement without
32007  * fractions. Measurements can only be scaled to other measurements 
32008  * of the same type.
32009  * 
32010  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
32011  * or undefined if the system can be inferred from the current measure
32012  * @return {Measurement} a new instance that is scaled to the 
32013  * right level
32014  */
32015 VolumeUnit.prototype.scale = function(measurementsystem) {
32016     var fromRow = VolumeUnit.ratios[this.unit];
32017     var mSystem;
32018 
32019     if (measurementsystem === "metric"|| (typeof(measurementsystem) === 'undefined'
32020         && typeof(VolumeUnit.metricSystem[this.unit]) !== 'undefined')) {
32021         mSystem = VolumeUnit.metricSystem;
32022     } else if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined'
32023         && typeof(VolumeUnit.uscustomarySystem[this.unit]) !== 'undefined')) {
32024         mSystem = VolumeUnit.uscustomarySystem;
32025     } else if (measurementsystem === "imperial"|| (typeof(measurementsystem) === 'undefined'
32026         && typeof(VolumeUnit.imperialSystem[this.unit]) !== 'undefined')) {
32027         mSystem = VolumeUnit.imperialSystem;
32028     }
32029 
32030     var volume = this.amount;
32031     var munit = this.unit;
32032 
32033     volume = 18446744073709551999;
32034     
32035     for (var m in mSystem) {
32036     	var tmp = this.amount * fromRow[mSystem[m]];
32037         if (tmp >= 1 && tmp < volume) {
32038         	volume = tmp;
32039 	        munit = m;
32040         }
32041     }
32042     
32043     return new VolumeUnit({
32044         unit: munit,
32045         amount: volume
32046     });
32047 };
32048 
32049 //register with the factory method
32050 Measurement._constructors["volume"] = VolumeUnit;
32051 
32052 
32053 
32054 /*< MeasurementFactory.js */
32055 /*
32056  * MeasurementFactory.js - Function to instantiate the appropriate subclasses of
32057  * the Measurement class.
32058  *
32059  * Copyright © 2015, JEDLSoft
32060  *
32061  * Licensed under the Apache License, Version 2.0 (the "License");
32062  * you may not use this file except in compliance with the License.
32063  * You may obtain a copy of the License at
32064  *
32065  *     http://www.apache.org/licenses/LICENSE-2.0
32066  *
32067  * Unless required by applicable law or agreed to in writing, software
32068  * distributed under the License is distributed on an "AS IS" BASIS,
32069  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32070  *
32071  * See the License for the specific language governing permissions and
32072  * limitations under the License.
32073  */
32074 
32075 /*
32076 !depends
32077 UnknownUnit.js
32078 AreaUnit.js
32079 DigitalStorageUnit.js
32080 EnergyUnit.js
32081 FuelConsumptionUnit.js
32082 LengthUnit.js
32083 MassUnit.js
32084 TemperatureUnit.js
32085 TimeUnit.js
32086 VelocityUnit.js
32087 VolumeUnit.js
32088 Measurement.js
32089 */
32090 
32091 // TODO: make these dependencies dynamic or at least generate them in the build
32092 // These will each add themselves to Measurement._constructors[]
32093 
32094 
32095 /**
32096  * Create a measurement subclass instance based on a particular measure
32097  * required. The measurement is immutable once
32098  * it is created, but it can be converted to other measurements later.<p>
32099  *
32100  * The options may contain any of the following properties:
32101  *
32102  * <ul>
32103  * <li><i>amount</i> - either a numeric amount for this measurement given
32104  * as a number of the specified units, or another Measurement instance
32105  * to convert to the requested units. If converting to new units, the type
32106  * of measure between the other instance's units and the current units
32107  * must be the same. That is, you can only convert one unit of mass to
32108  * another. You cannot convert a unit of mass into a unit of length.
32109  *
32110  * <li><i>unit</i> - units of this measurement. Use the
32111  * static call {@link MeasurementFactory.getAvailableUnits}
32112  * to find out what units this version of ilib supports. If the given unit
32113  * is not a base unit, the amount will be normalized to the number of base units
32114  * and stored as that number of base units.
32115  * For example, if an instance is constructed with 1 kg, this will be converted
32116  * automatically into 1000 g, as grams are the base unit and kg is merely a
32117  * commonly used scale of grams. 
32118  * </ul>
32119  *
32120  * Here are some examples of converting a length into new units. 
32121  * The first method is via this factory function by passing the old measurement 
32122  * in as the "amount" property.<p>
32123  * 
32124  * <pre>
32125  * var measurement1 = MeasurementFactory({
32126  *   amount: 5,
32127  *   units: "kilometers"
32128  * });
32129  * var measurement2 = MeasurementFactory({
32130  *   amount: measurement1,
32131  *   units: "miles"
32132  * });
32133  * </pre>
32134  * 
32135  * The value in measurement2 will end up being about 3.125 miles.<p>
32136  * 
32137  * The second method uses the convert method.<p>
32138  * 
32139  * <pre>
32140  * var measurement1 = MeasurementFactory({
32141  *   amount: 5,
32142  *   units: "kilometers"
32143  * });
32144  * var measurement2 = measurement1.convert("miles");
32145  * });
32146  * </pre>
32147  *
32148  * The value in measurement2 will again end up being about 3.125 miles.
32149  * 
32150  * @static
32151  * @param {Object=} options options that control the construction of this instance
32152  */
32153 var MeasurementFactory = function(options) {
32154 	if (!options || typeof(options.unit) === 'undefined') {
32155 		return undefined;
32156 	}
32157 
32158 	var measure = undefined;
32159 
32160 	for (var c in Measurement._constructors) {
32161 		var measurement = Measurement._constructors[c];
32162 		if (typeof(measurement.aliases[options.unit]) !== 'undefined') {
32163 			measure = c;
32164 			break;
32165 		}
32166 	}
32167 
32168 	if (!measure || typeof(measure) === 'undefined') {
32169 		return new UnknownUnit({
32170 			unit: options.unit,
32171 			amount: options.amount
32172 		});
32173 	} else {
32174 		return new Measurement._constructors[measure](options);
32175 	}
32176 };
32177 
32178 /**
32179  * Return a list of all possible units that this version of ilib supports.
32180  * Typically, the units are given as their full names in English. Unit names
32181  * are case-insensitive.
32182  *
32183  * @static
32184  * @return {Array.<string>} an array of strings containing names of measurement 
32185  * units available
32186  */
32187 MeasurementFactory.getAvailableUnits = function () {
32188 	var units = [];
32189 	for (var c in Measurement._constructors) {
32190 		var measure = Measurement._constructors[c];
32191 		units = units.concat(measure.getMeasures());
32192 	}
32193 	return units;
32194 };
32195 
32196 
32197 
32198 /*< UnitFmt.js */
32199 /*
32200  * UnitFmt.js - Unit formatter class
32201  * 
32202  * Copyright © 2014-2015, JEDLSoft
32203  *
32204  * Licensed under the Apache License, Version 2.0 (the "License");
32205  * you may not use this file except in compliance with the License.
32206  * You may obtain a copy of the License at
32207  *
32208  *     http://www.apache.org/licenses/LICENSE-2.0
32209  *
32210  * Unless required by applicable law or agreed to in writing, software
32211  * distributed under the License is distributed on an "AS IS" BASIS,
32212  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32213  *
32214  * See the License for the specific language governing permissions and
32215  * limitations under the License.
32216  */
32217 
32218 /*
32219 !depends 
32220 ilib.js 
32221 Locale.js 
32222 ResBundle.js 
32223 LocaleInfo.js
32224 IString.js
32225 NumFmt.js
32226 Utils.js
32227 */
32228 
32229 // !data unitfmt
32230 
32231 
32232 
32233 /**
32234  * @class
32235  * Create a new unit formatter instance. The unit formatter is immutable once
32236  * it is created, but can format as many different strings with different values
32237  * as needed with the same options. Create different unit formatter instances 
32238  * for different purposes and then keep them cached for use later if you have 
32239  * more than one unit string to format.<p>
32240  * 
32241  * The options may contain any of the following properties:
32242  * 
32243  * <ul>
32244  * <li><i>locale</i> - locale to use when formatting the units. The locale also
32245  * controls the translation of the names of the units. If the locale is
32246  * not specified, then the default locale of the app or web page will be used.
32247  * 
32248  * <li><i>autoScale</i> - when true, automatically scale the amount to get the smallest
32249  * number greater than 1, where possible, possibly by converting units within the locale's
32250  * measurement system. For example, if the current locale is "en-US", and we have
32251  * a measurement containing 278 fluid ounces, then the number "278" can be scaled down
32252  * by converting the units to a larger one such as gallons. The scaled size would be
32253  * 2.17188 gallons. Since iLib does not have a US customary measure larger than gallons,
32254  * it cannot scale it down any further. If the amount is less than the smallest measure
32255  * already, it cannot be scaled down any further and no autoscaling will be applied.
32256  * Default for the autoScale property is "true", so it only needs to be specified when
32257  * you want to turn off autoscaling.
32258  * 
32259  * <li><i>autoConvert</i> - automatically convert the units to the nearest appropriate
32260  * measure of the same type in the measurement system used by the locale. For example, 
32261  * if a measurement of length is given in meters, but the current locale is "en-US" 
32262  * which uses the US Customary system, then the nearest appropriate measure would be 
32263  * "yards", and the amount would be converted from meters to yards automatically before
32264  * being formatted. Default for the autoConvert property is "true", so it only needs to 
32265  * be specified when you want to turn off autoconversion.
32266  * 
32267  * <li><i>maxFractionDigits</i> - the maximum number of digits that should appear in the
32268  * formatted output after the decimal. A value of -1 means unlimited, and 0 means only print
32269  * the integral part of the number.
32270  * 
32271  * <li><i>minFractionDigits</i> - the minimum number of fractional digits that should
32272  * appear in the formatted output. If the number does not have enough fractional digits
32273  * to reach this minimum, the number will be zero-padded at the end to get to the limit.
32274  * 
32275  * <li><i>roundingMode</i> - When the maxFractionDigits or maxIntegerDigits is specified,
32276  * this property governs how the least significant digits are rounded to conform to that
32277  * maximum. The value of this property is a string with one of the following values:
32278  * <ul>
32279  *   <li><i>up</i> - round away from zero
32280  *   <li><i>down</i> - round towards zero. This has the effect of truncating the number
32281  *   <li><i>ceiling</i> - round towards positive infinity
32282  *   <li><i>floor</i> - round towards negative infinity
32283  *   <li><i>halfup</i> - round towards nearest neighbour. If equidistant, round up.
32284  *   <li><i>halfdown</i> - round towards nearest neighbour. If equidistant, round down.
32285  *   <li><i>halfeven</i> - round towards nearest neighbour. If equidistant, round towards the even neighbour
32286  *   <li><i>halfodd</i> - round towards nearest neighbour. If equidistant, round towards the odd neighbour
32287  * </ul>
32288  * 
32289  * <li><i>onLoad</i> - a callback function to call when the date format object is fully 
32290  * loaded. When the onLoad option is given, the UnitFmt object will attempt to
32291  * load any missing locale data using the ilib loader callback.
32292  * When the constructor is done (even if the data is already preassembled), the 
32293  * onLoad function is called with the current instance as a parameter, so this
32294  * callback can be used with preassembled or dynamic loading or a mix of the two.
32295  * 
32296  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
32297  * asynchronously. If this option is given as "false", then the "onLoad"
32298  * callback must be given, as the instance returned from this constructor will
32299  * not be usable for a while.
32300  *  
32301  * <li><i>loadParams</i> - an object containing parameters to pass to the 
32302  * loader callback function when locale data is missing. The parameters are not
32303  * interpretted or modified in any way. They are simply passed along. The object 
32304  * may contain any property/value pairs as long as the calling code is in
32305  * agreement with the loader callback function as to what those parameters mean.
32306  * </ul>
32307  * 
32308  * Here is an example of how you might use the unit formatter to format a string with
32309  * the correct units.<p>
32310  * 
32311  *  
32312  * @constructor
32313  * @param {Object} options options governing the way this date formatter instance works
32314  */
32315 var UnitFmt = function(options) {
32316 	var sync = true, 
32317 		loadParams = undefined;
32318 	
32319     this.length = "long";
32320     this.scale  = true;
32321     this.measurementType = 'undefined';
32322     this.convert = true;
32323 	this.locale = new Locale();
32324 
32325     if (options) {
32326     	if (options.locale) {
32327     		this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
32328     	}
32329 
32330     	if (typeof(options.sync) === 'boolean') {
32331     		sync = options.sync;
32332     	}
32333 
32334     	if (typeof(options.loadParams) !== 'undefined') {
32335     		loadParams = options.loadParams;
32336     	}
32337 
32338     	if (options.length) {
32339     		this.length = options.length;
32340     	}
32341 
32342     	if (typeof(options.autoScale) === 'boolean') {
32343     		this.scale = options.autoScale;
32344     	}
32345 
32346     	if (typeof(options.autoConvert) === 'boolean') {
32347     		this.convert = options.autoConvert;
32348     	}
32349         
32350         if (typeof(options.useNative) === 'boolean') {
32351     		this.useNative = options.useNative;
32352     	}
32353 
32354     	if (options.measurementSystem) {
32355     		this.measurementSystem = options.measurementSystem;
32356     	}
32357         
32358         if (typeof (options.maxFractionDigits) === 'number') {
32359             /** 
32360              * @private
32361              * @type {number|undefined} 
32362              */
32363             this.maxFractionDigits = options.maxFractionDigits;
32364         }
32365         if (typeof (options.minFractionDigits) === 'number') {
32366             /** 
32367              * @private
32368              * @type {number|undefined} 
32369              */
32370             this.minFractionDigits = options.minFractionDigits;
32371         }
32372         /** 
32373          * @private
32374          * @type {string} 
32375          */
32376         this.roundingMode = options.roundingMode;
32377     }
32378 
32379     if (!UnitFmt.cache) {
32380     	UnitFmt.cache = {};
32381     }
32382 
32383 	Utils.loadData({
32384 		object: UnitFmt, 
32385 		locale: this.locale, 
32386 		name: "unitfmt.json", 
32387 		sync: sync, 
32388 		loadParams: loadParams, 
32389 		callback: ilib.bind(this, function (format) {                      
32390 			var formatted = format;
32391 			this.template = formatted["unitfmt"][this.length];
32392 			
32393 			new NumFmt({
32394 	    		locale: this.locale,
32395 	    		useNative: this.useNative,
32396 	            maxFractionDigits: this.maxFractionDigits,
32397 	            minFractionDigits: this.minFractionDigits,
32398 	            roundingMode: this.roundingMode,
32399 	            sync: sync,
32400 	            loadParams: loadParams,
32401 	            onLoad: ilib.bind(this, function (numfmt) {
32402 	            	this.numFmt = numfmt;
32403 	            	
32404 	    			if (options && typeof(options.onLoad) === 'function') {
32405 	    				options.onLoad(this);
32406 	    			}
32407 	            })
32408 	    	});
32409 		})
32410 	});
32411 };
32412 
32413 UnitFmt.prototype = {
32414 	
32415 	/**
32416 	 * Return the locale used with this formatter instance.
32417 	 * @return {Locale} the Locale instance for this formatter
32418 	 */
32419 	getLocale: function() {
32420 		return this.locale;
32421 	},
32422 	
32423 	/**
32424 	 * Return the template string that is used to format date/times for this
32425 	 * formatter instance. This will work, even when the template property is not explicitly 
32426 	 * given in the options to the constructor. Without the template option, the constructor 
32427 	 * will build the appropriate template according to the options and use that template
32428 	 * in the format method. 
32429 	 * 
32430 	 * @return {string} the format template for this formatter
32431 	 */
32432 	getTemplate: function() {
32433 		return this.template;
32434 	},
32435 	
32436 	/**
32437 	 * Convert this formatter to a string representation by returning the
32438 	 * format template. This method delegates to getTemplate.
32439 	 * 
32440 	 * @return {string} the format template
32441 	 */
32442 	toString: function() {
32443 		return this.getTemplate();
32444 	},
32445     
32446 	/**
32447 	 * Return whether or not this formatter will auto-scale the units while formatting.
32448 	 * @returns {boolean} true if auto-scaling is turned on
32449 	 */
32450     getScale: function() {
32451         return this.scale;
32452     },
32453 
32454     /**
32455      * Return the measurement system that is used for this formatter.
32456      * @returns {string} the measurement system used in this formatter
32457      */
32458     getMeasurementSystem: function() {
32459         return this.measurementSystem;
32460     },
32461 
32462 	/**
32463 	 * Format a particular unit instance according to the settings of this
32464 	 * formatter object.
32465 	 * 
32466 	 * @param {Measurement} measurement measurement to format	 
32467 	 * @return {string} the formatted version of the given date instance
32468 	 */
32469     format: function (measurement) {
32470     	var u = this.convert ? measurement.localize(this.locale.getSpec()) : measurement;
32471     	u = this.scale ? u.scale(this.measurementSystem) : u;
32472     	var formatted = new IString(this.template[u.getUnit()]);
32473     	// make sure to use the right plural rules
32474     	formatted.setLocale(this.locale, true, undefined, undefined);
32475     	formatted = formatted.formatChoice(u.amount,{n:this.numFmt.format(u.amount)});
32476     	return formatted.length > 0 ? formatted : u.amount +" " + u.unit;
32477     }
32478 };
32479 
32480 
32481 /*< Charset.js */
32482 /*
32483  * Charset.js - Return information about a particular character set
32484  * 
32485  * Copyright © 2014-2015, JEDLSoft
32486  *
32487  * Licensed under the Apache License, Version 2.0 (the "License");
32488  * you may not use this file except in compliance with the License.
32489  * You may obtain a copy of the License at
32490  *
32491  *     http://www.apache.org/licenses/LICENSE-2.0
32492  *
32493  * Unless required by applicable law or agreed to in writing, software
32494  * distributed under the License is distributed on an "AS IS" BASIS,
32495  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32496  *
32497  * See the License for the specific language governing permissions and
32498  * limitations under the License.
32499  */
32500 
32501 // !depends ilib.js Utils.js
32502 // !data charsetaliases charset/ISO-8859-1 charset/ISO-8859-15 charset/UTF-8
32503 
32504 
32505 /**
32506  * @class
32507  * Create a new character set info instance. Charset instances give information about
32508  * a particular character set, such as whether or not it is single byte or multibyte,
32509  * and which languages commonly use that charset.<p>
32510  * 
32511  * The optional options object holds extra parameters if they are necessary. The
32512  * current list of supported options are:
32513  * 
32514  * <ul>
32515  * <li><i>name</i> - the name of the charset. This can be given as any commonly
32516  * used name for the character set, which is normalized to a standard IANA name 
32517  * before its info is loaded. If a name is not given,
32518  * this class will return information about the base character set of Javascript,
32519  * which is currently Unicode as encoded in UTF-16.
32520  * 
32521  * <li><i>onLoad</i> - a callback function to call when this object is fully 
32522  * loaded. When the onLoad option is given, this class will attempt to
32523  * load any missing data using the ilib loader callback.
32524  * When the constructor is done (even if the data is already preassembled), the 
32525  * onLoad function is called with the current instance as a parameter, so this
32526  * callback can be used with preassembled or dynamic loading or a mix of the two.
32527  * 
32528  * <li><i>sync</i> - tell whether to load any missing data synchronously or 
32529  * asynchronously. If this option is given as "false", then the "onLoad"
32530  * callback must be given, because the instance returned from this constructor will
32531  * not be usable for a while.
32532  *
32533  * <li><i>loadParams</i> - an object containing parameters to pass to the 
32534  * loader callback function when data is missing. The parameters are not
32535  * interpretted or modified in any way. They are simply passed along. The object 
32536  * may contain any property/value pairs as long as the calling code is in
32537  * agreement with the loader callback function as to what those parameters mean.
32538  * </ul>
32539  * 
32540  * If this copy of ilib is pre-assembled and all the data is already available, 
32541  * or if the data was already previously loaded, then this constructor will call
32542  * the onLoad callback immediately when the initialization is done. 
32543  * If the onLoad option is not given, this class will only attempt to load any
32544  * missing data synchronously.
32545  * 
32546  * Depends directive: !depends charset.js
32547  * 
32548  * @constructor
32549  * @see {ilib.setLoaderCallback} for information about registering a loader callback instance
32550  * @param {Object=} options options which govern the construction of this instance
32551  */
32552 var Charset = function(options) {
32553 	var sync = true,
32554 	    loadParams = undefined;
32555 	this.originalName = "UTF-8";
32556 	
32557 	if (options) {
32558 		if (typeof(options.name) !== 'undefined') {
32559 			this.originalName = options.name;
32560 		}
32561 		
32562 		if (typeof(options.sync) !== 'undefined') {
32563 			sync = (options.sync == true);
32564 		}
32565 		
32566 		if (typeof(options.loadParams) !== 'undefined') {
32567 			loadParams = options.loadParams;
32568 		}
32569 	}
32570 
32571 	if (!Charset.cache) {
32572 		Charset.cache = {};
32573 	}
32574 	
32575 	// default data. A majority of charsets use this info
32576 	this.info = {
32577 		description: "default",
32578 		min: 1,
32579 		max: 1,
32580 		bigendian: true,
32581 		scripts: ["Latn"],
32582 		locales: ["*"]
32583 	};
32584 
32585 	Utils.loadData({
32586 		object: Charset, 
32587 		locale: "-",
32588 		nonlocale: true,
32589 		name: "charsetaliases.json", 
32590 		sync: sync,
32591 		loadParams: loadParams, 
32592 		callback: ilib.bind(this, function (info) {
32593 			// first map the given original name to one of the standardized IANA names
32594 			if (info) {
32595 				// recognize better by getting rid of extraneous crap and upper-casing
32596 				// it so that the match is case-insensitive
32597 				var n = this.originalName.replace(/[-_,:\+\.\(\)]/g, '').toUpperCase();
32598 				this.name = info[n];
32599 			}
32600 			if (!this.name) {
32601 				this.name = this.originalName;
32602 			}
32603 			Utils.loadData({
32604 				object: Charset, 
32605 				locale: "-",
32606 				nonlocale: true,
32607 				name: "charset/" + this.name + ".json", 
32608 				sync: sync, 
32609 				loadParams: loadParams, 
32610 				callback: ilib.bind(this, function (info) {
32611 					if (info) {
32612 						ilib.extend(this.info, info);	
32613 					}
32614 					if (options && typeof(options.onLoad) === 'function') {
32615 						options.onLoad(this);
32616 					}
32617 				})
32618 			});
32619 		})
32620 	});
32621 };
32622 
32623 Charset.prototype = {
32624     /**
32625      * Return the standard normalized name of this charset.  The list of standard names 
32626      * comes from the IANA registry of character set names at 
32627      * <a href="http://www.iana.org/assignments/character-sets/character-sets.xhtml">http://www.iana.org/assignments/character-sets/character-sets.xhtml</a>.
32628      * 
32629      * @returns {string} the name of the charset
32630      */
32631     getName: function () {
32632     	return this.name;	
32633     },
32634     
32635     /**
32636      * Return the original name that this instance was constructed with before it was
32637      * normalized to the standard name returned by {@link #getName}.
32638      * 
32639      * @returns {String} the original name that this instance was constructed with
32640      */
32641     getOriginalName: function() {
32642     	return this.originalName;
32643     },
32644     
32645     /**
32646      * Return a short description of the character set.
32647      * 
32648      * @returns {string} a description of the character set
32649      */
32650     getDescription: function() {
32651     	return this.info.description || this.getName();
32652     },
32653     
32654     /**
32655      * Return the smallest number of bytes that a single character in this charset
32656      * could use. For most charsets, this is 1, but for some charsets such as Unicode
32657      * encoded in UTF-16, this may be 2 or more.
32658      * @returns {number} the smallest number of bytes that a single character in
32659      * this charset uses
32660      */
32661     getMinCharWidth: function () {
32662     	return this.info.min;
32663     },
32664     
32665     /**
32666      * Return the largest number of bytes that a single character in this charset
32667      * could use.
32668      * @returns {number} the largest number of bytes that a single character in
32669      * this charset uses
32670      */
32671     getMaxCharWidth: function () {
32672     	return this.info.max;
32673     },
32674     
32675     /**
32676      * Return true if this is a multibyte character set, or false for a fixed
32677      * width character set. A multibyte character set is one in which the characters
32678      * have a variable width. That is, one character may use 1 byte and a different
32679      * character might use 2 or 3 bytes.
32680      * 
32681      * @returns {boolean} true if this is a multibyte charset, or false otherwise
32682      */
32683     isMultibyte: function() {
32684     	return this.getMaxCharWidth() > this.getMinCharWidth();
32685     },
32686     
32687     /**
32688      * Return whether or not characters larger than 1 byte use the big endian order
32689      * or little endian.
32690      * 
32691      * @returns {boolean} true if this character set uses big endian order, or false
32692      * otherwise
32693      */
32694     isBigEndian: function() {
32695     	return this.info.bigendian;
32696     },
32697     
32698     /**
32699      * Return an array of ISO script codes whose characters can be encoded with this 
32700      * character set.
32701      * 
32702      * @returns {Array.<string>} an array of ISO script codes supported by this charset
32703      */
32704     getScripts: function() {
32705     	return this.info.scripts;
32706     }
32707 };
32708 
32709 
32710 /*< Charmap.js */
32711 /*
32712  * Charmap.js - A character set mapping class
32713  * 
32714  * Copyright © 2014-2015, JEDLSoft
32715  *
32716  * Licensed under the Apache License, Version 2.0 (the "License");
32717  * you may not use this file except in compliance with the License.
32718  * You may obtain a copy of the License at
32719  *
32720  *     http://www.apache.org/licenses/LICENSE-2.0
32721  *
32722  * Unless required by applicable law or agreed to in writing, software
32723  * distributed under the License is distributed on an "AS IS" BASIS,
32724  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32725  *
32726  * See the License for the specific language governing permissions and
32727  * limitations under the License.
32728  */
32729 
32730 // !depends ilib.js Utils.js Charset.js JSUtils.js IString.js
32731 
32732 // !data charset/US-ASCII charset/ISO-10646-UCS-2 charset/ISO-10646-UCS-4 charset/ISO-10646-Unicode-Latin1
32733 
32734 
32735 /**
32736  * @class
32737  * Create a new default character set mapping instance. This class is the parent
32738  * class of all of the charmapping subclasses, and only implements basic US-ASCII
32739  * mapping. The subclasses implement all other charsets, some algorithmically, and
32740  * some in a table-based way. Use {@link CharmapFactory} to create the correct
32741  * subclass instance for the desired charmap.<p>
32742  * 
32743  * All mappings are done to or from Unicode in the UTF-16 encoding, which is the base
32744  * character set and encoding used by Javascript itself. In order to convert 
32745  * between two non-Unicode character sets, you must chain two charmap instances together 
32746  * to first map to Unicode and then back to the second charset. <p>
32747  * 
32748  * The options parameter controls which mapping is constructed and its behaviours. The 
32749  * current list of supported options are:
32750  * 
32751  * <ul>
32752  * <li><i>missing</i> - specify what to do if a mapping is missing for a particular
32753  * character. For example, if you are mapping Unicode characters to a particular native
32754  * character set that does not support particular Unicode characters, the mapper will
32755  * follow the behaviour specified in this property. Valid values are:
32756  * <ul>
32757  * <li><i>skip</i> - skip any characters that do not exist in the target charset
32758  * <li><i>placeholder</i> - put a static placeholder character in the output string 
32759  * wherever there is an unknown character in the input string. Use the <i>placeholder</i> 
32760  * parameter to specify which character to use in this case
32761  * <li><i>escape</i> - use an escape sequence to represent the unknown character 
32762  * </ul>
32763  * The default value for the missing property if not otherwise specified is "escape"
32764  * so that information is not lost.
32765  * 
32766  * <li><i>placeholder</i> - specify the placeholder character to use when the 
32767  * mapper cannot map a particular input character to the output string. If this
32768  * option is not specified, then the '?' (question mark) character is used where 
32769  * possible.
32770  * 
32771  * <li><i>escapeStyle</i> - what style of escape sequences should be used to
32772  * escape unknown characters in the input when mapping to native, and what
32773  * style of espcae sequences should be parsed when mapping to Unicode. Valid 
32774  * values are:
32775  * <ul>
32776  * <li><i>html</i> - Escape the characters as HTML entities. This would use
32777  * the standard HTML 5.0 (or later) entity names where possible, and numeric
32778  * entities in all other cases. Eg. an "e" with an acute accent would be 
32779  * "é"
32780  * <li><i>js</i> - Use the Javascript escape style. Eg. an "e" with an acute
32781  * accent would be "\u00E9". This can also be specified as "c#" as
32782  * it uses a similar escape syntax.
32783  * <li><i>c</i> - Use the C/C++ escape style, which is similar to the the
32784  * Javascript style, but uses an "x" in place of the "u". Eg. an "e" with an 
32785  * acute accent would be "\x00E9". This can also be specified as "c++".
32786  * <li><i>java</i> - Use the Java escape style. This is very similar to the
32787  * the Javascript style, but the backslash has to be escaped twice. Eg. an
32788  * "e" with an acute accent would be "\\u00E9". This can also be specified
32789  * as "ruby", as Ruby uses a similar escape syntax with double backslashes.
32790  * <li><i>perl</i> - Use the Perl escape style. Eg. an "e" with an acute
32791  * accent would be "\N{U+00E9}"
32792  * </ul>
32793  * The default if this style is not specified is "js" for Javascript.
32794  * </ul>
32795  * 
32796  * If this copy of ilib is pre-assembled and all the data is already available, 
32797  * or if the data was already previously loaded, then this constructor will call
32798  * the onLoad callback immediately when the initialization is done. 
32799  * If the onLoad option is not given, this class will only attempt to load any
32800  * missing data synchronously.
32801  * 
32802  * @constructor
32803  * @param {Object=} options options which govern the construction of this instance
32804  */
32805 var Charmap = function(options) {
32806 	var sync = true,
32807 	    loadParams = undefined;
32808 	
32809 	this.charset = new Charset({name: "US-ASCII"});
32810 	this.missing = "placeholder";
32811 	this.placeholder = "?";
32812 	this.escapeStyle = "js";
32813 	this.expansionFactor = 1;
32814 	
32815 	if (options) {
32816 		if (typeof(options.placeholder) !== 'undefined') {
32817 			this.placeholder = options.placeholder;
32818 		}
32819 
32820 		var escapes = {
32821 			"html": "html",
32822 			"js": "js",
32823 			"c#": "js",
32824 			"c": "c",
32825 			"c++": "c",
32826 			"java": "java",
32827 			"ruby": "java",
32828 			"perl": "perl"
32829 		};
32830 		
32831 		if (typeof(options.escapeStyle) !== 'undefined') {
32832 			if (typeof(escapes[options.escapeStyle]) !== 'undefined') {
32833 				this.escapeStyle = escapes[options.escapeStyle];
32834 			}
32835 		}
32836 
32837 		if (typeof(options.missing) !== 'undefined') {
32838 			if (options.missing === "skip" || options.missing === "placeholder" || options.missing === "escape") {
32839 				this.missing = options.missing;
32840 			}
32841 		}
32842 	}
32843 	
32844 	this._calcExpansionFactor();
32845 };
32846 
32847 /**
32848  * A place for the algorithmic conversions to register themselves as 
32849  * they are defined.
32850  * 
32851  * @static
32852  * @private
32853  */
32854 Charmap._algorithms = {};
32855 
32856 Charmap.prototype = {
32857     /**
32858      * Return the standard name of this charmap. All charmaps map from
32859      * Unicode to the native charset, so the name returned from this
32860      * function corresponds to the native charset.
32861      * 
32862      * @returns {string} the name of the locale's language in English
32863      */
32864     getName: function () {
32865     	return this.charset.getName();	
32866     },
32867     
32868     /**
32869      * @private
32870      */
32871     writeNative: function (array, start, value) {
32872     	// console.log("Charmap.writeNative: start " + start + " adding " + JSON.stringify(value));
32873     	if (ilib.isArray(value)) { 
32874 	    	for (var i = 0; i < value.length; i++) {
32875 	    		array[start+i] = value[i];
32876 	    	}
32877 	    	
32878 	    	return value.length;
32879     	} else {
32880     		array[start] = value;
32881     		return 1;
32882     	}
32883     },
32884     
32885     /**
32886      * @private
32887      */
32888     writeNativeString: function (array, start, string) {
32889     	// console.log("Charmap.writeNativeString: start " + start + " adding " + JSON.stringify(string));
32890     	for (var i = 0; i < string.length; i++) {
32891     		array[start+i] = string.charCodeAt(i);
32892     	}
32893     	return string.length;
32894     },
32895     
32896     /**
32897      * @private
32898      */
32899     _calcExpansionFactor: function() {
32900     	var factor = 1;
32901     	factor = Math.max(factor, this.charset.getMaxCharWidth());
32902     	switch (this.missing) {
32903     	case "placeholder":
32904     		if (this.placeholder) {
32905     			factor = Math.max(factor, this.placeholder.length);
32906     		}
32907     		break;
32908     	case "escape":
32909     		switch (this.escapeStyle) {
32910 			case "html":
32911 				factor = Math.max(factor, 8); // &#xHHHH;
32912 				break;
32913 			case "c":
32914 				factor = Math.max(factor, 6); // \xHHHH
32915 				break;
32916 			case "perl":
32917 				factor = Math.max(factor, 10); // \N{U+HHHH}
32918 				break;
32919 				
32920 			default:
32921 				factor = Math.max(factor, 6); // \uHHHH
32922 				break;
32923     		}
32924     		break;
32925 		default:
32926 			break;
32927     	}
32928     	
32929     	this.expansionFactor = factor;
32930     },
32931     
32932     /**
32933      * @private
32934      */
32935     dealWithMissingChar: function(c) {
32936     	var seq = "";
32937     	
32938 		switch (this.missing) {
32939 			case "skip":
32940 				// do nothing
32941 				break;
32942 				
32943 			case "escape":
32944 				var num = (typeof(c) === 'string') ? c.charCodeAt(0) : c;
32945 				var bigc = JSUtils.pad(num.toString(16), 4).toUpperCase();
32946 				switch (this.escapeStyle) {
32947 					case "html":
32948 						seq = "&#x" + bigc + ";";
32949 						break;
32950 					case "c":
32951 						seq = "\\x" + bigc;
32952 						break;
32953 					case "java":
32954 						seq = "\\\\u" + bigc;
32955 						break;
32956 					case "perl":
32957 						seq = "\\N{U+" + bigc + "}";
32958 						break;
32959 						
32960 					default:
32961 					case "js":
32962 						seq = "\\u" + bigc;
32963 						break;
32964 				}
32965 				break;
32966 				
32967 			default:
32968 			case "placeholder":
32969 				seq = this.placeholder;
32970 				break;
32971 		}
32972 		
32973 		return seq;
32974     },
32975     
32976     /**
32977      * Map a string to the native character set. This string may be 
32978      * given as an intrinsic Javascript string object or an IString 
32979      * object.
32980      * 
32981      * @param {string|IString} string string to map to a different 
32982      * character set. 
32983      * @return {Uint8Array} An array of bytes representing the string 
32984      * in the native character set
32985      */
32986     mapToNative: function(string) {
32987     	if (!string) {
32988     		return new Uint8Array(0);
32989     	}
32990     	
32991     	if (this.algorithm) {
32992     		return this.algorithm.mapToNative(string);
32993     	}
32994     	
32995     	// the default algorithm is plain old ASCII
32996     	var str = (string instanceof IString) ? string : new IString(string);
32997     	
32998     	// use IString's iterator so that we take care of walking through
32999     	// the code points correctly, including the surrogate pairs
33000     	var c, i = 0, j = 0, it = str.iterator();
33001     	var ret = new Uint8Array(str.length * this.expansionFactor);
33002     	
33003     	while (it.hasNext() && i < ret.length) {
33004     		c = it.next();
33005     		if (c < 127) {
33006     			ret[i++] = c;
33007     		} else {
33008     			i += this.writeNativeString(ret, i, this.dealWithMissingChar(c));
33009     		}
33010     	}
33011 
33012     	return ret;
33013     },
33014     
33015     /**
33016      * Map a native string to the standard Javascript charset of UTF-16. 
33017      * This string may be given as an array of numbers where each number 
33018      * represents a code point in the "from" charset, or as a Uint8Array 
33019      * array of bytes representing the bytes of the string in order.
33020      * 
33021      * @param {Array.<number>|Uint8Array} bytes bytes to map to 
33022      * a Unicode string
33023      * @return {string} A string in the standard Javascript charset UTF-16
33024      */
33025     mapToUnicode: function(bytes) {
33026     	var ret = "";
33027     	var c, i = 0;
33028     	
33029     	while (i < bytes.length) {
33030     		c = bytes[i];
33031     		
33032     		// the default algorithm is plain old ASCII
33033         	if (c < 128) {
33034     			ret += String.fromCharCode(c);
33035     		} else {
33036     			// The byte at "i" wasn't ASCII
33037 				ret += this.dealWithMissingChar(bytes[i++]);
33038     		}
33039     	}
33040 
33041     	return ret;
33042     }
33043 };
33044 
33045 
33046 /*< CharmapTable.js */
33047 /*
33048  * CharmapTable.js - A character set mapping class that maps using trie table
33049  * 
33050  * Copyright © 2014-2015, JEDLSoft
33051  *
33052  * Licensed under the Apache License, Version 2.0 (the "License");
33053  * you may not use this file except in compliance with the License.
33054  * You may obtain a copy of the License at
33055  *
33056  *     http://www.apache.org/licenses/LICENSE-2.0
33057  *
33058  * Unless required by applicable law or agreed to in writing, software
33059  * distributed under the License is distributed on an "AS IS" BASIS,
33060  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33061  *
33062  * See the License for the specific language governing permissions and
33063  * limitations under the License.
33064  */
33065 
33066 // !depends ilib.js Utils.js Charset.js Charmap.js IString.js
33067 
33068 // !data charmaps/ISO-8859-1 charset/ISO-8859-1
33069 
33070 
33071 /**
33072  * @class
33073  * Create a new character set mapping instance using based on a trie table. Charmap 
33074  * instances map strings to 
33075  * other character sets. The charsets can be of any type, single-byte, multi-byte,
33076  * shifting, etc. <p>
33077  * 
33078  * All mappings are done to or from Unicode in the UTF-16 encoding, which is the base
33079  * character set and encoding used by Javascript itself. In order to convert 
33080  * between two non-Unicode character sets, you must chain two charmap instances together 
33081  * to first map to Unicode and then back to the second charset. <p>
33082  * 
33083  * The options parameter controls which mapping is constructed and its behaviours. The 
33084  * current list of supported options are:
33085  * 
33086  * <ul>
33087  * <li><i>charset</i> - the name of the native charset to map to or from. This can be 
33088  * given as an {@link Charset} instance or as a string that contains any commonly used name 
33089  * for the character set, which is normalized to a standard IANA name. 
33090  * If a name is not given, this class will default to the Western European character 
33091  * set called ISO-8859-15.
33092  * 
33093  * <li><i>missing</i> - specify what to do if a mapping is missing for a particular
33094  * character. For example, if you are mapping Unicode characters to a particular native
33095  * character set that does not support particular Unicode characters, the mapper will
33096  * follow the behaviour specified in this property. Valid values are:
33097  * <ul>
33098  * <li><i>skip</i> - skip any characters that do not exist in the target charset
33099  * <li><i>placeholder</i> - put a static placeholder character in the output string 
33100  * wherever there is an unknown character in the input string. Use the <i>placeholder</i> 
33101  * parameter to specify which character to use in this case
33102  * <li><i>escape</i> - use an escape sequence to represent the unknown character 
33103  * </ul>
33104  * The default value for the missing property if not otherwise specified is "escape"
33105  * so that information is not lost.
33106  * 
33107  * <li><i>placeholder</i> - specify the placeholder character to use when the 
33108  * mapper cannot map a particular input character to the output string. If this
33109  * option is not specified, then the '?' (question mark) character is used where 
33110  * possible.
33111  * 
33112  * <li><i>escapeStyle</i> - what style of escape sequences should be used to
33113  * escape unknown characters in the input when mapping to native, and what
33114  * style of espcae sequences should be parsed when mapping to Unicode. Valid 
33115  * values are:
33116  * <ul>
33117  * <li><i>html</i> - Escape the characters as HTML entities. This would use
33118  * the standard HTML 5.0 (or later) entity names where possible, and numeric
33119  * entities in all other cases. Eg. an "e" with an acute accent would be 
33120  * "é"
33121  * <li><i>js</i> - Use the Javascript escape style. Eg. an "e" with an acute
33122  * accent would be "\u00E9". This can also be specified as "c#" as
33123  * it uses a similar escape syntax.
33124  * <li><i>c</i> - Use the C/C++ escape style, which is similar to the the
33125  * Javascript style, but uses an "x" in place of the "u". Eg. an "e" with an 
33126  * acute accent would be "\x00E9". This can also be specified as "c++".
33127  * <li><i>java</i> - Use the Java escape style. This is very similar to the
33128  * the Javascript style, but the backslash has to be escaped twice. Eg. an
33129  * "e" with an acute accent would be "\\u00E9". This can also be specified
33130  * as "ruby", as Ruby uses a similar escape syntax with double backslashes.
33131  * <li><i>perl</i> - Use the Perl escape style. Eg. an "e" with an acute
33132  * accent would be "\N{U+00E9}"
33133  * </ul>
33134  * The default if this style is not specified is "js" for Javascript.
33135  * 
33136  * <li><i>onLoad</i> - a callback function to call when this object is fully 
33137  * loaded. When the onLoad option is given, this class will attempt to
33138  * load any missing data using the ilib loader callback.
33139  * When the constructor is done (even if the data is already preassembled), the 
33140  * onLoad function is called with the current instance as a parameter, so this
33141  * callback can be used with preassembled or dynamic loading or a mix of the two.
33142  * 
33143  * <li><i>sync</i> - tell whether to load any missing data synchronously or 
33144  * asynchronously. If this option is given as "false", then the "onLoad"
33145  * callback must be given, because the instance returned from this constructor will
33146  * not be usable for a while.
33147  *
33148  * <li><i>loadParams</i> - an object containing parameters to pass to the 
33149  * loader callback function when data is missing. The parameters are not
33150  * interpretted or modified in any way. They are simply passed along. The object 
33151  * may contain any property/value pairs as long as the calling code is in
33152  * agreement with the loader callback function as to what those parameters mean.
33153  * </ul>
33154  * 
33155  * If this copy of ilib is pre-assembled and all the data is already available, 
33156  * or if the data was already previously loaded, then this constructor will call
33157  * the onLoad callback immediately when the initialization is done. 
33158  * If the onLoad option is not given, this class will only attempt to load any
33159  * missing data synchronously.
33160  * 
33161  * @constructor
33162  * @see {ilib.setLoaderCallback} for information about registering a loader callback instance
33163  * @extends Charmap
33164  * @param {Object=} options options which govern the construction of this instance
33165  */
33166 var CharmapTable = function(options) {
33167 	var sync = true,
33168 	    loadParams = undefined;
33169 	
33170 	// console.log("CharmapTable: constructor with options: " + JSON.stringify(options));
33171 	
33172 	this.parent.call(this, options);
33173 	
33174 	if (options) {
33175 		if (typeof(options.charset) === "object") {
33176 			this.charset = options.charset;
33177 		} else if (typeof(options.name) !== 'undefined') {
33178 			this.charset = new Charset({name: options.name});
33179 		}
33180 		
33181 		if (typeof(options.sync) !== 'undefined') {
33182 			sync = (options.sync == true);
33183 		}
33184 		
33185 		if (typeof(options.loadParams) !== 'undefined') {
33186 			loadParams = options.loadParams;
33187 		}
33188 	}
33189 
33190 	if (!this.charset) {
33191 		this.charset = new Charset({name: "ISO-8859-15"});
33192 	}
33193 
33194 	this._calcExpansionFactor();
33195 	
33196 	if (!Charmap.cache) {
33197 		Charmap.cache = {};
33198 	}
33199 
33200 	Utils.loadData({
33201 		object: Charmap, 
33202 		locale: "-",
33203 		nonlocale: true,
33204 		name: "charmaps/" + this.charset.getName() + ".json", 
33205 		sync: sync, 
33206 		loadParams: loadParams, 
33207 		callback: ilib.bind(this, function (mapping) {
33208 			if (!mapping) {
33209 				throw "No mapping found for " + this.charset.getName();
33210 			}
33211 
33212 			/** @type {{from:Object,to:Object}} */
33213 			this.map = mapping;
33214 			if (options && typeof(options.onLoad) === 'function') {
33215 				options.onLoad(this);
33216 			}
33217 		})
33218 	});
33219 };
33220 
33221 CharmapTable.prototype = new Charmap();
33222 CharmapTable.prototype.parent = Charmap;
33223 CharmapTable.prototype.constructor = CharmapTable;
33224 
33225 /**
33226  * Walk a trie to find the value for the current position in the given array.
33227  * @private
33228  */
33229 CharmapTable.prototype._trieWalk = function(trie, array, start) {
33230 	function isValue(node) {
33231 		return (typeof(node) === 'string' || typeof(node) === 'number' ||
33232 			(typeof(node) === 'object' && ilib.isArray(node)));
33233 	}
33234 	
33235 	var lastLeaf = undefined,
33236 		i = start,
33237 		trienode = trie;
33238 	
33239 	while (i < array.length) {
33240 		if (typeof(trienode.__leaf) !== 'undefined') {
33241 			lastLeaf = {
33242 				consumed: i - start + 1,
33243 				value: trienode.__leaf
33244 			};
33245 		}
33246 		if (array[i] === 0) {
33247 			// null-terminator, so end the mapping.
33248 			return {
33249 				consumed: 1,
33250 				value: 0
33251 			};
33252 		} else if (typeof(trienode[array[i]]) !== 'undefined') {
33253 			// we have a mapping
33254 			if (isValue(trienode[array[i]])) {
33255 				// it is a leaf node
33256 				return {
33257 					consumed: i - start + 1,
33258 					value: trienode[array[i]]
33259 				};
33260 			} else {
33261 				// it is an intermediate node
33262     			trienode = trienode[array[i++]];
33263     		}
33264 		} else {
33265 			// no mapping for this array element, so return the last known
33266 			// leaf. If none, this will return undefined.
33267 			return lastLeaf;
33268 		}
33269 	}
33270 
33271 	return undefined;
33272 };
33273     
33274 /**
33275  * Map a string to the native character set. This string may be 
33276  * given as an intrinsic Javascript string object or an IString 
33277  * object.
33278  * 
33279  * @param {string|IString} string string to map to a different 
33280  * character set. 
33281  * @return {Uint8Array} An array of bytes representing the string 
33282  * in the native character set
33283  */
33284 CharmapTable.prototype.mapToNative = function(string) {
33285 	if (!string) {
33286 		return new Uint8Array(0);
33287 	}
33288 	
33289 	var str = (string instanceof IString) ? string : new IString(string);
33290 	
33291 	// use IString's iterator so that we take care of walking through
33292 	// the code points correctly, including the surrogate pairs
33293 	// var c, i = 0, it = str.charIterator();
33294 	var ret = new Uint8Array(str.length * this.expansionFactor);
33295 	
33296 	var i = 0, j = 0;
33297 	
33298 	while (i < string.length) {
33299 		var result = this._trieWalk(this.map.from, string, i);
33300 		if (result) {
33301 			if (result.value) {
33302     			i += result.consumed;
33303     			j += this.writeNative(ret, j, result.value);
33304 			} else {
33305 				// null-termination
33306 				i = string.length;
33307 				this.writeNative(ret, j, [result.value]);
33308 			}
33309 		} else {
33310 			// The unicode char at "i" didn't have any mapping, so
33311 			// deal with the missing char
33312 			j += this.writeNativeString(ret, j, this.dealWithMissingChar(string[i++]));
33313 		}
33314 	}
33315 
33316 	return ret.subarray(0, j);
33317 };
33318 
33319 /**
33320  * Map a native string to the standard Javascript charset of UTF-16. 
33321  * This string may be given as an array of numbers where each number 
33322  * represents a code point in the "from" charset, or as a Uint8Array 
33323  * array of bytes representing the bytes of the string in order.
33324  * 
33325  * @param {Array.<number>|Uint8Array} bytes bytes to map to 
33326  * a Unicode string
33327  * @return {string} A string in the standard Javascript charset UTF-16
33328  */
33329 CharmapTable.prototype.mapToUnicode = function(bytes) {
33330 	var ret = "";
33331 	var i = 0;
33332 	
33333 	while (i < bytes.length) {
33334 		var result = this._trieWalk(this.map.to, bytes, i);
33335 		if (result) {
33336 			if (result.value) {
33337     			i += result.consumed;
33338     			if (typeof(result.value) === 'string') {
33339         			ret += result.value;
33340         		} else if (ilib.isArray(result.value)) {
33341         			for (var j = 0; j < result.value.length; j++) {
33342         				ret += result.value[j];
33343         			}
33344         		} // else error in charmap file??
33345 			} else {
33346 				// null-termination
33347 				i = bytes.length;
33348 			}
33349 		} else {
33350 			// The byte at "i" wasn't a lead byte, so start again at the 
33351 			// next byte instead. This may synchronize the rest 
33352 			// of the string.
33353 			ret += this.dealWithMissingChar(bytes[i++]);
33354 		}
33355 	}
33356 
33357 	return ret;
33358 };
33359 
33360 Charmap._algorithms["CharmapTable"] = CharmapTable;
33361 
33362 
33363 /*< CharmapFactory.js */
33364 /*
33365  * CharmapFactory.js - Factory class to create the right subclasses of a charmap for any 
33366  * given chararacter set.
33367  * 
33368  * Copyright © 2015, JEDLSoft
33369  *
33370  * Licensed under the Apache License, Version 2.0 (the "License");
33371  * you may not use this file except in compliance with the License.
33372  * You may obtain a copy of the License at
33373  *
33374  *     http://www.apache.org/licenses/LICENSE-2.0
33375  *
33376  * Unless required by applicable law or agreed to in writing, software
33377  * distributed under the License is distributed on an "AS IS" BASIS,
33378  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33379  *
33380  * See the License for the specific language governing permissions and
33381  * limitations under the License.
33382  */
33383 
33384 /* !depends ilib.js JSUtils.js Charmap.js CharmapTable.js */
33385 // !data charset/ISO-8859-15 charmaps/ISO-8859-15
33386 
33387 
33388 
33389 /**
33390  * Factory method to create a new instance of a character set mapping (charmap) 
33391  * subclass that is appropriate for the requested charset. Charmap instances map strings to 
33392  * other character sets. The charsets can be of any type, single-byte, multi-byte,
33393  * shifting, etc. <p>
33394  * 
33395  * All mappings are done to or from Unicode in the UTF-16 encoding, which is the base
33396  * character set and encoding used by Javascript itself. In order to convert 
33397  * between two non-Unicode character sets, you must chain two charmap instances together 
33398  * to first map to Unicode and then back to the second charset. <p>
33399  * 
33400  * The options parameter controls which mapping is constructed and its behaviours. The 
33401  * current list of supported options are:
33402  * 
33403  * <ul>
33404  * <li><i>name</i> - the name of the native charset to map to or from. This can be 
33405  * given as an {@link Charset} instance or as a string that contains any commonly used name 
33406  * for the character set, which is normalized to a standard IANA name. 
33407  * If a name is not given, this class will default to the Western European character 
33408  * set called ISO-8859-15.
33409  * 
33410  * <li><i>missing</i> - specify what to do if a mapping is missing for a particular
33411  * character. For example, if you are mapping Unicode characters to a particular native
33412  * character set that does not support particular Unicode characters, the mapper will
33413  * follow the behaviour specified in this property. Valid values are:
33414  * <ul>
33415  * <li><i>skip</i> - skip any characters that do not exist in the target charset
33416  * <li><i>placeholder</i> - put a static placeholder character in the output string 
33417  * wherever there is an unknown character in the input string. Use the <i>placeholder</i> 
33418  * parameter to specify which character to use in this case
33419  * <li><i>escape</i> - use an escape sequence to represent the unknown character 
33420  * </ul>
33421  * The default value for the missing property if not otherwise specified is "escape"
33422  * so that information is not lost.
33423  * 
33424  * <li><i>placeholder</i> - specify the placeholder character to use when the 
33425  * mapper cannot map a particular input character to the output string. If this
33426  * option is not specified, then the '?' (question mark) character is used where 
33427  * possible.
33428  * 
33429  * <li><i>escapeStyle</i> - what style of escape sequences should be used to
33430  * escape unknown characters in the input when mapping to native, and what
33431  * style of espcae sequences should be parsed when mapping to Unicode. Valid 
33432  * values are:
33433  * <ul>
33434  * <li><i>html</i> - Escape the characters as HTML entities. This would use
33435  * the standard HTML 5.0 (or later) entity names where possible, and numeric
33436  * entities in all other cases. Eg. an "e" with an acute accent would be 
33437  * "é"
33438  * <li><i>js</i> - Use the Javascript escape style. Eg. an "e" with an acute
33439  * accent would be "\u00E9". This can also be specified as "c#" as
33440  * it uses a similar escape syntax.
33441  * <li><i>c</i> - Use the C/C++ escape style, which is similar to the the
33442  * Javascript style, but uses an "x" in place of the "u". Eg. an "e" with an 
33443  * acute accent would be "\x00E9". This can also be specified as "c++".
33444  * <li><i>java</i> - Use the Java escape style. This is very similar to the
33445  * the Javascript style, but the backslash has to be escaped twice. Eg. an
33446  * "e" with an acute accent would be "\\u00E9". This can also be specified
33447  * as "ruby", as Ruby uses a similar escape syntax with double backslashes.
33448  * <li><i>perl</i> - Use the Perl escape style. Eg. an "e" with an acute
33449  * accent would be "\N{U+00E9}"
33450  * </ul>
33451  * The default if this style is not specified is "js" for Javascript.
33452  * 
33453  * <li><i>onLoad</i> - a callback function to call when this object is fully 
33454  * loaded. When the onLoad option is given, this class will attempt to
33455  * load any missing data using the ilib loader callback.
33456  * When the constructor is done (even if the data is already preassembled), the 
33457  * onLoad function is called with the current instance as a parameter, so this
33458  * callback can be used with preassembled or dynamic loading or a mix of the two.
33459  * 
33460  * <li><i>sync</i> - tell whether to load any missing data synchronously or 
33461  * asynchronously. If this option is given as "false", then the "onLoad"
33462  * callback must be given, because the instance returned from this constructor will
33463  * not be usable for a while.
33464  *
33465  * <li><i>loadParams</i> - an object containing parameters to pass to the 
33466  * loader callback function when data is missing. The parameters are not
33467  * interpretted or modified in any way. They are simply passed along. The object 
33468  * may contain any property/value pairs as long as the calling code is in
33469  * agreement with the loader callback function as to what those parameters mean.
33470  * </ul>
33471  * 
33472  * If this copy of ilib is pre-assembled and all the data is already available, 
33473  * or if the data was already previously loaded, then this constructor will call
33474  * the onLoad callback immediately when the initialization is done. 
33475  * If the onLoad option is not given, this class will only attempt to load any
33476  * missing data synchronously.
33477  * 
33478  * @static
33479  * @param {Object=} options options controlling the construction of this instance, or
33480  * undefined to use the default options
33481  * @return {Charmap|undefined} an instance of a character set mapping class appropriate for
33482  * the requested charset, or undefined if no mapper could be found that supports the
33483  * requested charset
33484  */
33485 var CharmapFactory = function(options) {
33486 	var charsetName = (options && options.name) || "ISO-8859-15";
33487 	var sync = true;
33488 	
33489 	// console.log("CharmapFactory: called with options: " + JSON.stringify(options));
33490 	
33491 	if (options) {
33492 		if (typeof(options.sync) === 'boolean') {
33493 			sync = options.sync;
33494 		}
33495 	}
33496 
33497 	var instance;
33498 	
33499 	new Charset({
33500 		name: charsetName,
33501 		sync: sync,
33502 		loadParams: options && options.loadParams,
33503 		onLoad: function (charset) {
33504 			// name will be normalized already
33505 			var cons, name = charset.getName();
33506 	
33507 			// console.log("CharmapFactory: normalized charset name: " + name);
33508 			
33509 			if (!Charmap._algorithms[name] && ilib.isDynCode()) {
33510 				// console.log("CharmapFactory: isDynCode. Doing require");
33511 				var entry = CharmapFactory._dynMap[name] || "CharmapTable";
33512 				cons = Charmap._algorithms[name] = require("./" + entry + ".js");
33513 			}
33514 			
33515 			if (!cons) {
33516 				cons = Charmap._algorithms[name] || Charmap._algorithms["CharmapTable"];
33517 			}
33518 			
33519 			// console.log("CharmapFactory: cons is "); console.dir(cons);
33520 			
33521 			// pass the same options through to the constructor so the subclass
33522 			// has the ability to do something with if it needs to
33523 			instance = cons && new cons(JSUtils.merge(options || {}, {charset: charset}));
33524 		}
33525 	});
33526 	
33527 	return instance;
33528 };
33529 
33530 
33531 /**
33532  * Map standardized charset names to classes to initialize in the dynamic code model.
33533  * These classes implement algorithmic mappings instead of table-based ones.
33534  * TODO: Need to figure out some way that this doesn't have to be updated by hand.
33535  * @private
33536  */
33537 CharmapFactory._dynMap = {
33538 	"UTF-8":      "UTF8",
33539 	"UTF-16":     "UTF16LE",
33540 	"UTF-16LE":   "UTF16LE",
33541 	"UTF-16BE":   "UTF16BE",
33542 	"US-ASCII":   "Charmap"
33543 	/*
33544 	not implemented yet
33545 	"ISO-2022-JP": "ISO2022",
33546 	"ISO-2022-JP-1": "ISO2022",
33547 	"ISO-2022-JP-2": "ISO2022",
33548 	"ISO-2022-JP-3": "ISO2022",
33549 	"ISO-2022-JP-2004": "ISO2022",
33550 	"ISO-2022-CN": "ISO2022",
33551 	"ISO-2022-CN-EXT": "ISO2022",
33552 	"ISO-2022-KR": "ISO2022"
33553 	*/
33554 };
33555 
33556 
33557 /*< UTF8.js */
33558 /*
33559  * UTF8.js - Implement Unicode Transformation Format 8-bit mappings
33560  * 
33561  * Copyright © 2014-2015, JEDLSoft
33562  *
33563  * Licensed under the Apache License, Version 2.0 (the "License");
33564  * you may not use this file except in compliance with the License.
33565  * You may obtain a copy of the License at
33566  *
33567  *     http://www.apache.org/licenses/LICENSE-2.0
33568  *
33569  * Unless required by applicable law or agreed to in writing, software
33570  * distributed under the License is distributed on an "AS IS" BASIS,
33571  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33572  *
33573  * See the License for the specific language governing permissions and
33574  * limitations under the License.
33575  */
33576 
33577 // !depends Charmap.js IString.js
33578 
33579 // !data charset/UTF-8
33580 
33581 
33582 /**
33583  * @class
33584  * Create a new UTF-8 mapping instance
33585  * @constructor
33586  * @extends Charmap
33587  */
33588 var UTF8 = function (options) {
33589 	this.charset = new Charset({name: "UTF-8"});
33590 };
33591 
33592 UTF8.prototype = new Charmap();
33593 UTF8.prototype.parent = Charmap;
33594 UTF8.prototype.constructor = UTF8;
33595 
33596 UTF8.prototype.validate = function(bytes) {
33597 	var i = 0;
33598 	while (i < bytes.length) {
33599 		if ((bytes[i] & 0x80) === 0) {
33600 			i++;
33601 		} else {
33602 			var len;
33603 			if ((bytes[i] & 0xC0) === 0xC0) {
33604 				len = 2;
33605 			} else if ((bytes[i] & 0xE0) === 0xE0) {
33606 				len = 3;
33607 			} else if ((bytes[i] & 0xF0) === 0xF0) {
33608 				len = 4;
33609 			} else {
33610 				// invalid lead byte
33611 				return false;
33612 			}
33613 			if (i + len > bytes.length) {
33614 				// not enough trailing bytes
33615 				return false;
33616 			}
33617 			for (var j = 1; j < len; j++) {
33618 				// check each trailing byte to see if it has the correct form
33619 				if ((bytes[i+j] & 0x80) !== 0x80) {
33620 					return false;
33621 				}
33622 			}
33623 			i += len;
33624 		}
33625 	}
33626 	
33627 	return true;
33628 };
33629 	
33630 UTF8.prototype.mapToUnicode = function (bytes) {
33631 	if (typeof(Buffer) !== "undefined") {
33632 		// nodejs can convert it quickly in native code
33633 		var b = new Buffer(bytes);
33634 		return b.toString("utf8");
33635 	}
33636 	// otherwise we have to implement it in pure JS
33637 	var ret = "";
33638 	var i = 0;
33639 	while (i < bytes.length) {
33640 		if (bytes[i] === 0) {
33641 			// null-terminator
33642 			i = bytes.length;
33643 		} else if ((bytes[i] & 0x80) === 0) {
33644 			// 1 byte char
33645 			ret += String.fromCharCode(bytes[i++]);
33646 		} else if ((bytes[i] & 0xE0) === 0xC0) {
33647 			// 2 byte char
33648 			if (i + 1 >= bytes.length || (bytes[i+1] & 0x80) !== 0x80) {
33649 				throw "invalid utf-8 bytes";
33650 			}
33651 			// xxx xxyyyyyy
33652 			ret += String.fromCharCode((bytes[i] & 0x1F) << 6 | (bytes[i+1] & 0x3F));
33653 			i += 2;
33654 		} else if ((bytes[i] & 0xF0) === 0xE0) {
33655 			// 3 byte char
33656 			if (i + 2 >= bytes.length || (bytes[i+1] & 0x80) !== 0x80 || (bytes[i+2] & 0x80) !== 0x80) {
33657 				throw "invalid utf-8 bytes";
33658 			}
33659 			// xxxxyyyy yyzzzzzz
33660 			ret += String.fromCharCode((bytes[i] & 0xF) << 12 | (bytes[i+1] & 0x3F) << 6 | (bytes[i+2] & 0x3F));
33661 			i += 3;
33662 		} else if ((bytes[i] & 0xF8) === 0xF0) {
33663 			// 4 byte char
33664 			if (i + 3 >= bytes.length || (bytes[i+1] & 0x80) !== 0x80 || (bytes[i+2] & 0x80) !== 0x80 || (bytes[i+3] & 0x80) !== 0x80) {
33665 				throw "invalid utf-8 bytes";
33666 			}
33667 			// wwwxx xxxxyyyy yyzzzzzz
33668 			ret += IString.fromCodePoint((bytes[i] & 0x7) << 18 | (bytes[i+1] & 0x3F) << 12 | (bytes[i+2] & 0x3F) << 6 | (bytes[i+3] & 0x3F));
33669 			i += 4;
33670 		} else {
33671 			throw "invalid utf-8 bytes";
33672 		}
33673 	}
33674 	
33675 	return ret;
33676 };
33677 	
33678 UTF8.prototype.mapToNative = function(str) {
33679 	if (typeof(Buffer) !== "undefined") {
33680 		// nodejs can convert it quickly in native code
33681 		var b = new Buffer(str, "utf8");
33682 		return new Uint8Array(b);
33683 	}
33684 	// otherwise we have to implement it in pure JS
33685 	var istr = (str instanceof IString) ? str : new IString(str);
33686 	
33687 	// step through the surrogate pairs as single code points by using
33688 	// IString's iterator 
33689 	var it = istr.iterator();
33690 	
33691 	// multiply by 4 because the max size of a UTF-8 char is 4 bytes, so
33692 	// this will at least get us enough room to encode everything. Add 1
33693 	// for the null terminator
33694 	var ret = new Uint8Array(istr.length * 4 + 1);
33695 	var i = 0;
33696 	
33697 	while (it.hasNext()) {
33698 		var c = it.next();
33699 		if (c > 0x7F) {
33700 			if (c > 0x7FF) {
33701 				if (c > 0xFFFF) {
33702 					// astral planes char
33703 					ret[i]   = 0xF0 | ((c >> 18) & 0x3);
33704 					ret[i+1] = 0x80 | ((c >> 12) & 0x3F);
33705 					ret[i+2] = 0x80 | ((c >> 6) & 0x3F);
33706 					ret[i+3] = 0x80 | (c & 0x3F);
33707 					
33708 					i += 4;
33709 				} else {
33710 					ret[i]   = 0xE0 | ((c >> 12) & 0xF);
33711 					ret[i+1] = 0x80 | ((c >> 6) & 0x3F);
33712 					ret[i+2] = 0x80 | (c & 0x3F);
33713 					
33714 					i += 3;
33715 				}
33716 			} else {
33717 				ret[i]   = 0xC0 | ((c >> 6) & 0x1F);
33718 				ret[i+1] = 0x80 | (c & 0x3F);
33719 				
33720 				i += 2;
33721 			}
33722 		} else {
33723 			ret[i++] = (c & 0x7F);
33724 		}
33725 	}
33726 	ret[i] = 0; // null-terminate it
33727 	
33728 	return ret;
33729 };
33730 
33731 Charmap._algorithms["UTF-8"] = UTF8;
33732 
33733 
33734 /*< UTF16BE.js */
33735 /*
33736  * UTF16BE.js - Implement Unicode Transformation Format 16-bit,
33737  * Big Endian mappings
33738  * 
33739  * Copyright © 2014-2015, JEDLSoft
33740  *
33741  * Licensed under the Apache License, Version 2.0 (the "License");
33742  * you may not use this file except in compliance with the License.
33743  * You may obtain a copy of the License at
33744  *
33745  *     http://www.apache.org/licenses/LICENSE-2.0
33746  *
33747  * Unless required by applicable law or agreed to in writing, software
33748  * distributed under the License is distributed on an "AS IS" BASIS,
33749  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33750  *
33751  * See the License for the specific language governing permissions and
33752  * limitations under the License.
33753  */
33754 
33755 // !depends Charmap.js
33756 
33757 // !data charset/UTF-16 charset/UTF-16BE
33758 
33759 
33760 /**
33761  * @class
33762  * Create a new UTF-16BE mapping instance
33763  * @constructor
33764  * @extends Charmap
33765  */
33766 var UTF16BE = function (options) {
33767 	this.charset = new Charset({name: "UTF-16BE"});
33768 };
33769 
33770 UTF16BE.prototype = new Charmap();
33771 UTF16BE.prototype.parent = Charmap;
33772 UTF16BE.prototype.constructor = UTF16BE;
33773 
33774 UTF16BE.prototype.mapToUnicode = function (bytes) {
33775 	// nodejs can't convert big-endian in native code,
33776 	// so we would have to flip each Uint16 ourselves.
33777 	// At that point, it's just quicker to convert 
33778 	// in JS code anyways
33779 	var ret = "";
33780 	for (var i = 0; i < bytes.length; i += 2) {
33781 		ret += String.fromCharCode(bytes[i] << 8 | bytes[i+1]);
33782 	}
33783 	
33784 	return ret;
33785 };
33786 	
33787 UTF16BE.prototype.mapToNative = function(str) {
33788 	// nodejs can't convert big-endian in native code,
33789 	// so we would have to flip each Uint16 ourselves.
33790 	// At that point, it's just quicker to convert 
33791 	// in JS code anyways
33792 	var ret = new Uint8Array(str.length * 2 + 2);
33793 	var c;
33794 	for (var i = 0; i < str.length; i++) {
33795 		c = str.charCodeAt(i);
33796 		ret[i*2] = (c >> 8) & 0xFF;
33797 		ret[i*2+1] = c & 0xFF;
33798 	}
33799 	// double null terminate it, just in case
33800 	ret[i*2+1] = 0;
33801 	ret[i*2+2] = 0;
33802 	
33803 	return ret;
33804 };
33805 
33806 Charmap._algorithms["UTF-16BE"] = UTF16BE;
33807 
33808 
33809 /*< UTF16LE.js */
33810 /*
33811  * UTF16LE.js - Implement Unicode Transformation Format 16 bit, 
33812  * Little Endian mappings
33813  * 
33814  * Copyright © 2014-2015, JEDLSoft
33815  *
33816  * Licensed under the Apache License, Version 2.0 (the "License");
33817  * you may not use this file except in compliance with the License.
33818  * You may obtain a copy of the License at
33819  *
33820  *     http://www.apache.org/licenses/LICENSE-2.0
33821  *
33822  * Unless required by applicable law or agreed to in writing, software
33823  * distributed under the License is distributed on an "AS IS" BASIS,
33824  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33825  *
33826  * See the License for the specific language governing permissions and
33827  * limitations under the License.
33828  */
33829 
33830 // !depends Charmap.js
33831 
33832 // !data charset/UTF-16 charset/UTF-16LE
33833 
33834 
33835 /**
33836  * @class
33837  * Create a new UTF-16LE mapping instance
33838  * @constructor
33839  * @extends Charmap
33840  */
33841 var UTF16LE = function (options) {
33842 	this.charset = new Charset({name: "UTF-16LE"});
33843 };
33844 
33845 UTF16LE.prototype = new Charmap();
33846 UTF16LE.prototype.parent = Charmap;
33847 UTF16LE.prototype.constructor = UTF16LE;
33848 
33849 UTF16LE.prototype.mapToUnicode = function (bytes) {
33850 	if (typeof(Buffer) !== "undefined") {
33851 		// nodejs can convert it quickly in native code
33852 		var b = new Buffer(bytes);
33853 		return b.toString("utf16le");
33854 	}
33855 	// otherwise we have to implement it in pure JS
33856 	var ret = "";
33857 	for (var i = 0; i < bytes.length; i += 2) {
33858 		ret += String.fromCharCode(bytes[i+1] << 8 | bytes[i]);
33859 	}
33860 	
33861 	return ret;
33862 };
33863 	
33864 UTF16LE.prototype.mapToNative =  function(str) {
33865 	if (typeof(Buffer) !== "undefined") {
33866 		// nodejs can convert it quickly in native code
33867 		var b = new Buffer(str, "utf16le");
33868 		return new Uint8Array(b);
33869 	}
33870 	// otherwise we have to implement it in pure JS
33871 	var ret = new Uint8Array(str.length * 2 + 2);
33872 	var c;
33873 	for (var i = 0; i < str.length; i++) {
33874 		c = str.charCodeAt(i);
33875 		ret[i*2] = c & 0xFF;
33876 		ret[i*2+1] = (c >> 8) & 0xFF;
33877 	}
33878 	// double null terminate it, just in case
33879 	ret[i*2+1] = 0;
33880 	ret[i*2+2] = 0;
33881 	
33882 	return ret;
33883 };
33884 
33885 Charmap._algorithms["UTF-16"] = UTF16LE;
33886 Charmap._algorithms["UTF-16LE"] = UTF16LE;
33887 
33888 
33889 /*< /mnt/Terasaur/root/home/edwin/ht/ilib/js/lib/ilib-full-inc.js */
33890 /**
33891  * @license
33892  * Copyright © 2012-2015, JEDLSoft
33893  *
33894  * Licensed under the Apache License, Version 2.0 (the "License");
33895  * you may not use this file except in compliance with the License.
33896  * You may obtain a copy of the License at
33897  *
33898  *     http://www.apache.org/licenses/LICENSE-2.0
33899  *
33900  * Unless required by applicable law or agreed to in writing, software
33901  * distributed under the License is distributed on an "AS IS" BASIS,
33902  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33903  *
33904  * See the License for the specific language governing permissions and
33905  * limitations under the License.
33906  */
33907 
33908 /*
33909  * ilib-full-inc.js - metafile that includes all other js files
33910  */
33911 
33912 /* !depends
33913 ilib.js
33914 DateRngFmt.js
33915 IDate.js
33916 DateFactory.js
33917 HebrewDate.js
33918 HebrewCal.js
33919 IslamicCal.js
33920 IslamicDate.js
33921 JulianCal.js
33922 JulianDate.js
33923 GregorianCal.js
33924 GregorianDate.js
33925 ThaiSolarCal.js
33926 ThaiSolarDate.js
33927 PersianCal.js
33928 PersianDate.js
33929 PersianAlgoCal.js
33930 PersianAlgoDate.js
33931 HanCal.js
33932 HanDate.js
33933 EthiopicCal.js
33934 EthiopicDate.js
33935 CopticCal.js
33936 CopticDate.js
33937 INumber.js
33938 NumFmt.js
33939 JulianDay.js
33940 DateFmt.js
33941 Calendar.js
33942 CalendarFactory.js
33943 Utils.js
33944 Locale.js
33945 IString.js
33946 DurationFmt.js
33947 ResBundle.js
33948 CType.js
33949 LocaleInfo.js
33950 DateRngFmt.js
33951 isAlnum.js
33952 isAlpha.js
33953 isAscii.js
33954 isBlank.js
33955 isCntrl.js
33956 isDigit.js
33957 isGraph.js
33958 isIdeo.js
33959 isLower.js
33960 isPrint.js
33961 isPunct.js
33962 isSpace.js
33963 isUpper.js
33964 isXdigit.js
33965 isScript.js
33966 ScriptInfo.js
33967 Name.js
33968 NameFmt.js
33969 Address.js
33970 AddressFmt.js
33971 Collator.js
33972 nfkc/all.js
33973 LocaleMatcher.js
33974 NormString.js
33975 CaseMapper.js
33976 GlyphString.js
33977 PhoneFmt.js
33978 PhoneGeoLocator.js
33979 PhoneNumber.js
33980 Measurement.js
33981 MeasurementFactory.js
33982 UnitFmt.js
33983 LengthUnit.js
33984 VelocityUnit.js
33985 DigitalStorageUnit.js
33986 TemperatureUnit.js
33987 UnknownUnit.js
33988 TimeUnit.js
33989 MassUnit.js
33990 AreaUnit.js
33991 FuelConsumptionUnit.js
33992 VolumeUnit.js	
33993 EnergyUnit.js
33994 Charset.js
33995 Charmap.js
33996 CharmapFactory.js
33997 CharmapTable.js
33998 UTF8.js
33999 UTF16BE.js
34000 UTF16LE.js
34001 */
34002 
34003