1 /* 2 * ilibglobal.js - define the ilib name space 3 * 4 * Copyright © 2012-2014, JEDLSoft 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 /** 21 * @namespace The global namespace that contains all ilib functions and classes. 22 */ 23 var ilib = ilib || {}; 24 25 /** 26 * Return the current version of ilib. 27 * 28 * @static 29 * @return {string} a version string for this instance of ilib 30 */ 31 ilib.getVersion = function () { 32 // increment this for each release 33 return "10.0" 34 ; 35 }; 36 37 /** 38 * Place where resources and such are eventually assigned. 39 */ 40 ilib.data = { 41 norm: { 42 nfc: {}, 43 nfd: {}, 44 nfkd: {}, 45 ccc: {} 46 }, 47 zoneinfo: { 48 "Etc/UTC":{"o":"0:0","f":"UTC"}, 49 "local":{"f":"local"} 50 }, 51 /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype: null, 52 /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_c: null, 53 /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_l: null, 54 /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_m: null, 55 /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_p: null, 56 /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_z: null, 57 /** @type {null|Object.<string,Array.<Array.<number>>>} */ scriptToRange: null, 58 /** @type {null|Object.<string,string|Object.<string|Object.<string,string>>>} */ dateformats: null 59 }; 60 61 if (typeof(window) !== 'undefined') { 62 window["ilib"] = ilib; 63 } 64 65 // export ilib for use as a module in nodejs 66 if (typeof(exports) !== 'undefined') { 67 exports.ilib = ilib; 68 } 69 70 ilib.pseudoLocales = ["zxx-XX"]; 71 72 /** 73 * Sets the pseudo locale. Pseudolocalization (or pseudo-localization) is used for testing 74 * internationalization aspects of software. Instead of translating the text of the software 75 * into a foreign language, as in the process of localization, the textual elements of an application 76 * are replaced with an altered version of the original language.These specific alterations make 77 * the original words appear readable, but include the most problematic characteristics of 78 * the world's languages: varying length of text or characters, language direction, and so on. 79 * Regular Latin pseudo locale: eu-ES and RTL pseudo locale: ps-AF 80 * 81 * @param {string|undefined|null} localename the locale specifier for the pseudo locale 82 */ 83 ilib.setAsPseudoLocale = function (localename) { 84 if (localename) { 85 ilib.pseudoLocales.push(localename) 86 } 87 }; 88 89 /** 90 * Reset the list of pseudo locales back to the default single locale of zxx-XX. 91 */ 92 ilib.clearPseudoLocales = function() { 93 ilib.pseudoLocales = ["zxx-XX"]; 94 }; 95 96 /** 97 * Return the name of the platform 98 * @private 99 * @static 100 * @return {string} string naming the platform 101 */ 102 ilib._getPlatform = function () { 103 if (!ilib._platform) { 104 if (typeof(environment) !== 'undefined') { 105 ilib._platform = "rhino"; 106 } else if (typeof(process) !== 'undefined' || typeof(require) !== 'undefined') { 107 ilib._platform = "nodejs"; 108 } else if (typeof(window) !== 'undefined') { 109 ilib._platform = (typeof(PalmSystem) !== 'undefined') ? "webos" : "browser"; 110 } else { 111 ilib._platform = "unknown"; 112 } 113 } 114 return ilib._platform; 115 }; 116 117 /** 118 * If this ilib is running in a browser, return the name of that browser. 119 * @private 120 * @static 121 * @return {string|undefined} the name of the browser that this is running in ("firefox", "chrome", "ie", 122 * "safari", or "opera"), or undefined if this is not running in a browser or if 123 * the browser name could not be determined 124 */ 125 ilib._getBrowser = function () { 126 var browser = undefined; 127 if (ilib._getPlatform() === "browser") { 128 if (navigator && navigator.userAgent) { 129 if (navigator.userAgent.indexOf("Firefox") > -1) { 130 browser = "firefox"; 131 } 132 if (navigator.userAgent.indexOf("Opera") > -1) { 133 browser = "opera"; 134 } 135 if (navigator.userAgent.indexOf("Chrome") > -1) { 136 browser = "chrome"; 137 } 138 if (navigator.userAgent.indexOf(" .NET") > -1) { 139 browser = "ie"; 140 } 141 if (navigator.userAgent.indexOf("Safari") > -1) { 142 // chrome also has the string Safari in its userAgent, but the chrome case is 143 // already taken care of above 144 browser = "safari"; 145 } 146 } 147 } 148 return browser; 149 }; 150 151 /** 152 * Return true if the global variable is defined on this platform. 153 * @private 154 * @static 155 * @return {boolean} true if the global variable is defined on this platform, false otherwise 156 */ 157 ilib._isGlobal = function(name) { 158 switch (ilib._getPlatform()) { 159 case "rhino": 160 var top = (function() { 161 return (typeof global === 'object') ? global : this; 162 })(); 163 return typeof(top[name]) !== undefined; 164 case "nodejs": 165 var root = typeof(global) !== 'undefined' ? global : this; 166 return root && typeof(root[name]) !== undefined; 167 168 default: 169 return typeof(window[name]) !== undefined; 170 } 171 }; 172 173 /** 174 * Sets the default locale for all of ilib. This locale will be used 175 * when no explicit locale is passed to any ilib class. If the default 176 * locale is not set, ilib will attempt to use the locale of the 177 * environment it is running in, if it can find that. If not, it will 178 * default to the locale "en-US". If a type of parameter is string, 179 * ilib will take only well-formed BCP-47 tag <p> 180 * 181 * Depends directive: !depends ilibglobal.js 182 * 183 * @static 184 * @param {string|undefined|null} spec the locale specifier for the default locale 185 */ 186 ilib.setLocale = function (spec) { 187 if (typeof(spec) === 'string' || !spec) { 188 ilib.locale = spec; 189 } 190 // else ignore other data types, as we don't have the dependencies 191 // to look into them to find a locale 192 }; 193 194 /** 195 * Return the default locale for all of ilib if one has been set. This 196 * locale will be used when no explicit locale is passed to any ilib 197 * class. If the default 198 * locale is not set, ilib will attempt to use the locale of the 199 * environment it is running in, if it can find that. If not, it will 200 * default to the locale "en-US".<p> 201 * 202 * Depends directive: !depends ilibglobal.js 203 * 204 * @static 205 * @return {string} the locale specifier for the default locale 206 */ 207 ilib.getLocale = function () { 208 if (typeof(ilib.locale) !== 'string') { 209 if (typeof(navigator) !== 'undefined' && typeof(navigator.language) !== 'undefined') { 210 // running in a browser 211 ilib.locale = navigator.language.substring(0,3) + navigator.language.substring(3,5).toUpperCase(); // FF/Opera/Chrome/Webkit 212 if (!ilib.locale) { 213 // IE on Windows 214 var lang = typeof(navigator.browserLanguage) !== 'undefined' ? 215 navigator.browserLanguage : 216 (typeof(navigator.userLanguage) !== 'undefined' ? 217 navigator.userLanguage : 218 (typeof(navigator.systemLanguage) !== 'undefined' ? 219 navigator.systemLanguage : 220 undefined)); 221 if (typeof(lang) !== 'undefined' && lang) { 222 // for some reason, MS uses lower case region tags 223 ilib.locale = lang.substring(0,3) + lang.substring(3,5).toUpperCase(); 224 } 225 } 226 } else if (typeof(PalmSystem) !== 'undefined' && typeof(PalmSystem.locales) !== 'undefined') { 227 // webOS 228 if (typeof(PalmSystem.locales.UI) != 'undefined' && PalmSystem.locales.UI.length > 0) { 229 ilib.locale = PalmSystem.locales.UI; 230 } 231 } else if (typeof(environment) !== 'undefined' && typeof(environment.user) !== 'undefined') { 232 // running under rhino 233 if (typeof(environment.user.language) === 'string' && environment.user.language.length > 0) { 234 ilib.locale = environment.user.language; 235 if (typeof(environment.user.country) === 'string' && environment.user.country.length > 0) { 236 ilib.locale += '-' + environment.user.country; 237 } 238 } 239 } else if (typeof(process) !== 'undefined' && typeof(process.env) !== 'undefined') { 240 // running under nodejs 241 var lang = process.env.LANG || process.env.LC_ALL; 242 // the LANG variable on unix is in the form "lang_REGION.CHARSET" 243 // where language and region are the correct ISO codes separated by 244 // an underscore. This translate it back to the BCP-47 form. 245 if (lang && lang !== 'undefined') { 246 ilib.locale = lang.substring(0,2).toLowerCase() + '-' + lang.substring(3,5).toUpperCase(); 247 } 248 } 249 250 ilib.locale = typeof(ilib.locale) === 'string' ? ilib.locale : 'en-US'; 251 } 252 return ilib.locale; 253 }; 254 255 /** 256 * Sets the default time zone for all of ilib. This time zone will be used when 257 * no explicit time zone is passed to any ilib class. If the default time zone 258 * is not set, ilib will attempt to use the time zone of the 259 * environment it is running in, if it can find that. If not, it will 260 * default to the the UTC zone "Etc/UTC".<p> 261 * 262 * Depends directive: !depends ilibglobal.js 263 * 264 * @static 265 * @param {string} tz the name of the time zone to set as the default time zone 266 */ 267 ilib.setTimeZone = function (tz) { 268 ilib.tz = tz || ilib.tz; 269 }; 270 271 /** 272 * Return the default time zone for all of ilib if one has been set. This 273 * time zone will be used when no explicit time zone is passed to any ilib 274 * class. If the default time zone 275 * is not set, ilib will attempt to use the locale of the 276 * environment it is running in, if it can find that. If not, it will 277 * default to the the zone "local".<p> 278 * 279 * Depends directive: !depends ilibglobal.js 280 * 281 * @static 282 * @return {string} the default time zone for ilib 283 */ 284 ilib.getTimeZone = function() { 285 if (typeof(ilib.tz) === 'undefined') { 286 if (typeof(navigator) !== 'undefined' && typeof(navigator.timezone) !== 'undefined') { 287 // running in a browser 288 if (navigator.timezone.length > 0) { 289 ilib.tz = navigator.timezone; 290 } 291 } else if (typeof(PalmSystem) !== 'undefined' && typeof(PalmSystem.timezone) !== 'undefined') { 292 // running in webkit on webOS 293 if (PalmSystem.timezone.length > 0) { 294 ilib.tz = PalmSystem.timezone; 295 } 296 } else if (typeof(environment) !== 'undefined' && typeof(environment.user) !== 'undefined') { 297 // running under rhino 298 if (typeof(environment.user.timezone) !== 'undefined' && environment.user.timezone.length > 0) { 299 ilib.tz = environment.user.timezone; 300 } 301 } else if (typeof(process) !== 'undefined' && typeof(process.env) !== 'undefined') { 302 // running in nodejs 303 if (process.env.TZ && process.env.TZ !== "undefined") { 304 ilib.tz = process.env.TZ; 305 } 306 } 307 308 ilib.tz = ilib.tz || "local"; 309 } 310 311 return ilib.tz; 312 }; 313 314 /** 315 * @class 316 * Defines the interface for the loader class for ilib. The main method of the 317 * loader object is loadFiles(), which loads a set of requested locale data files 318 * from where-ever it is stored. 319 * @interface 320 */ 321 ilib.Loader = function() {}; 322 323 /** 324 * Load a set of files from where-ever it is stored.<p> 325 * 326 * This is the main function define a callback function for loading missing locale 327 * data or resources. 328 * If this copy of ilib is assembled without including the required locale data 329 * or resources, then that data can be lazy loaded dynamically when it is 330 * needed by calling this method. Each ilib class will first 331 * check for the existence of data under ilib.data, and if it is not there, 332 * it will attempt to load it by calling this method of the laoder, and then place 333 * it there.<p> 334 * 335 * Suggested implementations of this method might load files 336 * directly from disk under nodejs or rhino, or within web pages, to load 337 * files from the server with XHR calls.<p> 338 * 339 * The first parameter to this method, paths, is an array of relative paths within 340 * the ilib dir structure for the 341 * requested data. These paths will already have the locale spec integrated 342 * into them, so no further tweaking needs to happen to load the data. Simply 343 * load the named files. The second 344 * parameter tells the loader whether to load the files synchronously or asynchronously. 345 * If the sync parameters is false, then the onLoad function must also be specified. 346 * The third parameter gives extra parameters to the loader passed from the calling 347 * code. This may contain any property/value pairs. The last parameter, callback, 348 * is a callback function to call when all of the data is finishing loading. Make 349 * sure to call the callback with the context of "this" so that the caller has their 350 * context back again.<p> 351 * 352 * The loader function must be able to operate either synchronously or asychronously. 353 * If the loader function is called with an undefined callback function, it is 354 * expected to load the data synchronously, convert it to javascript 355 * objects, and return the array of json objects as the return value of the 356 * function. If the loader 357 * function is called with a callback function, it may load the data 358 * synchronously or asynchronously (doesn't matter which) as long as it calls 359 * the callback function with the data converted to a javascript objects 360 * when it becomes available. If a particular file could not be loaded, the 361 * loader function should put undefined into the corresponding entry in the 362 * results array. 363 * Note that it is important that all the data is loaded before the callback 364 * is called.<p> 365 * 366 * An example implementation for nodejs might be: 367 * 368 * <pre> 369 * var fs = require("fs"); 370 * 371 * var myLoader = function() {}; 372 * myLoader.prototype = new ilib.Loader(); 373 * myLoader.prototype.constructor = myLoader; 374 * myLoader.prototype.loadFiles = function(paths, sync, params, callback) { 375 * if (sync) { 376 * var ret = []; 377 * // synchronous load -- just return the result 378 * paths.forEach(function (path) { 379 * var json = fs.readFileSync(path, "utf-8"); 380 * ret.push(json ? JSON.parse(json) : undefined); 381 * }); 382 * 383 * return ret; 384 * } 385 * this.callback = callback; 386 * 387 * // asynchronous 388 * this.results = []; 389 * this._loadFilesAsync(paths); 390 * } 391 * myLoader.prototype._loadFilesAsync = function (paths) { 392 * if (paths.length > 0) { 393 * var file = paths.shift(); 394 * fs.readFile(file, "utf-8", function(err, json) { 395 * this.results.push(err ? undefined : JSON.parse(json)); 396 * // call self recursively so that the callback is only called at the end 397 * // when all the files are loaded sequentially 398 * if (paths.length > 0) { 399 * this._loadFilesAsync(paths); 400 * } else { 401 * this.callback(this.results); 402 * } 403 * }); 404 * } 405 * } 406 * 407 * // bind to "this" so that "this" is relative to your own instance 408 * ilib.setLoaderCallback(new myLoader()); 409 * </pre> 410 411 * @param {Array.<string>} paths An array of paths to load from wherever the files are stored 412 * @param {Boolean} sync if true, load the files synchronously, and false means asynchronously 413 * @param {Object} params an object with any extra parameters for the loader. These can be 414 * anything. The caller of the ilib class passes these parameters in. Presumably, the code that 415 * calls ilib and the code that provides the loader are together and can have a private 416 * agreement between them about what the parameters should contain. 417 * @param {function(Object)} callback function to call when the files are all loaded. The 418 * parameter of the callback function is the contents of the files. 419 */ 420 ilib.Loader.prototype.loadFiles = function (paths, sync, params, callback) {}; 421 422 /** 423 * Return all files available for loading using this loader instance. 424 * This method returns an object where the properties are the paths to 425 * directories where files are loaded from and the values are an array 426 * of strings containing the relative paths under the directory of each 427 * file that can be loaded.<p> 428 * 429 * Example: 430 * <pre> 431 * { 432 * "/usr/share/javascript/ilib/locale": [ 433 * "dateformats.json", 434 * "aa/dateformats.json", 435 * "af/dateformats.json", 436 * "agq/dateformats.json", 437 * "ak/dateformats.json", 438 * ... 439 * "zxx/dateformats.json" 440 * ] 441 * } 442 * </pre> 443 * @returns {Object} a hash containing directory names and 444 * paths to file that can be loaded by this loader 445 */ 446 ilib.Loader.prototype.listAvailableFiles = function() {}; 447 448 /** 449 * Return true if the file in the named path is available for loading using 450 * this loader. The path may be given as an absolute path, in which case 451 * only that file is checked, or as a relative path, in which case, the 452 * relative path may appear underneath any of the directories that the loader 453 * knows about. 454 * @returns {boolean} true if the file in the named path is available for loading, and 455 * false otherwise 456 */ 457 ilib.Loader.prototype.isAvailable = function(path) {}; 458 459 /** 460 * Set the custom loader used to load ilib's locale data in your environment. 461 * The instance passed in must implement the ilib.Loader interface. See the 462 * ilib.Loader class documentation for more information about loaders. 463 * 464 * @static 465 * @param {ilib.Loader} loader class to call to access the requested data. 466 * @return {boolean} true if the loader was installed correctly, or false 467 * if not 468 */ 469 ilib.setLoaderCallback = function(loader) { 470 // only a basic check 471 if ((typeof(loader) === 'object' && loader instanceof ilib.Loader) || 472 typeof(loader) === 'function' || typeof(loader) === 'undefined') { 473 // console.log("setting callback loader to " + (loader ? loader.name : "undefined")); 474 ilib._load = loader; 475 return true; 476 } 477 return false; 478 }; 479 480 /* 481 * locale.js - Locale specifier definition 482 * 483 * Copyright © 2012-2014, JEDLSoft 484 * 485 * Licensed under the Apache License, Version 2.0 (the "License"); 486 * you may not use this file except in compliance with the License. 487 * You may obtain a copy of the License at 488 * 489 * http://www.apache.org/licenses/LICENSE-2.0 490 * 491 * Unless required by applicable law or agreed to in writing, software 492 * distributed under the License is distributed on an "AS IS" BASIS, 493 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 494 * 495 * See the License for the specific language governing permissions and 496 * limitations under the License. 497 */ 498 499 // !depends ilibglobal.js 500 501 /** 502 * @class 503 * Create a new locale instance. Locales are specified either with a specifier string 504 * that follows the BCP-47 convention (roughly: "language-region-script-variant") or 505 * with 4 parameters that specify the language, region, variant, and script individually.<p> 506 * 507 * The language is given as an ISO 639-1 two-letter, lower-case language code. You 508 * can find a full list of these codes at 509 * <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> 510 * 511 * The region is given as an ISO 3166-1 two-letter, upper-case region code. You can 512 * find a full list of these codes at 513 * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2</a>.<p> 514 * 515 * The variant is any string that does not contain a dash which further differentiates 516 * locales from each other.<p> 517 * 518 * The script is given as the ISO 15924 four-letter script code. In some locales, 519 * text may be validly written in more than one script. For example, Serbian is often 520 * written in both Latin and Cyrillic, though not usually mixed together. You can find a 521 * full list of these codes at 522 * <a href="http://en.wikipedia.org/wiki/ISO_15924#List_of_codes">http://en.wikipedia.org/wiki/ISO_15924#List_of_codes</a>.<p> 523 * 524 * As an example in ilib, the script can be used in the date formatter. Dates formatted 525 * in Serbian could have day-of-week names or month names written in the Latin 526 * or Cyrillic script. Often one script is default such that sr-SR-Latn is the same 527 * as sr-SR so the script code "Latn" can be left off of the locale spec.<p> 528 * 529 * Each part is optional, and an empty string in the specifier before or after a 530 * dash or as a parameter to the constructor denotes an unspecified value. In this 531 * case, many of the ilib functions will treat the locale as generic. For example 532 * the locale "en-" is equivalent to "en" and to "en--" and denotes a locale 533 * of "English" with an unspecified region and variant, which typically matches 534 * any region or variant.<p> 535 * 536 * Without any arguments to the constructor, this function returns the locale of 537 * the host Javascript engine.<p> 538 * 539 * Depends directive: !depends locale.js 540 * 541 * @constructor 542 * @param {?string|ilib.Locale=} language the ISO 639 2-letter code for the language, or a full 543 * locale spec in BCP-47 format, or another ilib.Locale instance to copy from 544 * @param {string=} region the ISO 3166 2-letter code for the region 545 * @param {string=} variant the name of the variant of this locale, if any 546 * @param {string=} script the ISO 15924 code of the script for this locale, if any 547 */ 548 ilib.Locale = function(language, region, variant, script) { 549 if (typeof(region) === 'undefined') { 550 var spec = language || ilib.getLocale(); 551 552 if (typeof(spec) === 'string') { 553 var parts = spec.split('-'); 554 555 556 for ( var i = 0; i < parts.length; i++ ) { 557 if (ilib.Locale._isLanguageCode(parts[i])) { 558 /** 559 * @private 560 * @type {string|undefined} 561 */ 562 this.language = parts[i]; 563 } else if (ilib.Locale._isRegionCode(parts[i])) { 564 /** 565 * @private 566 * @type {string|undefined} 567 */ 568 this.region = parts[i]; 569 } else if (ilib.Locale._isScriptCode(parts[i])) { 570 /** 571 * @private 572 * @type {string|undefined} 573 */ 574 this.script = parts[i]; 575 } else { 576 /** 577 * @private 578 * @type {string|undefined} 579 */ 580 this.variant = parts[i]; 581 } 582 } 583 this.language = this.language || undefined; 584 this.region = this.region || undefined; 585 this.script = this.script || undefined; 586 this.variant = this.variant || undefined; 587 } else if (typeof(spec) === 'object') { 588 this.language = spec.language || undefined; 589 this.region = spec.region || undefined; 590 this.script = spec.script || undefined; 591 this.variant = spec.variant || undefined; 592 } 593 } else { 594 if (language) { 595 language = language.trim(); 596 this.language = language.length > 0 ? language.toLowerCase() : undefined; 597 } else { 598 this.language = undefined; 599 } 600 if (region) { 601 region = region.trim(); 602 this.region = region.length > 0 ? region.toUpperCase() : undefined; 603 } else { 604 this.region = undefined; 605 } 606 if (variant) { 607 variant = variant.trim(); 608 this.variant = variant.length > 0 ? variant : undefined; 609 } else { 610 this.variant = undefined; 611 } 612 if (script) { 613 script = script.trim(); 614 this.script = script.length > 0 ? script : undefined; 615 } else { 616 this.script = undefined; 617 } 618 } 619 this._genSpec(); 620 }; 621 622 // from http://en.wikipedia.org/wiki/ISO_3166-1 623 ilib.Locale.a2toa3regmap = { 624 "AF": "AFG", 625 "AX": "ALA", 626 "AL": "ALB", 627 "DZ": "DZA", 628 "AS": "ASM", 629 "AD": "AND", 630 "AO": "AGO", 631 "AI": "AIA", 632 "AQ": "ATA", 633 "AG": "ATG", 634 "AR": "ARG", 635 "AM": "ARM", 636 "AW": "ABW", 637 "AU": "AUS", 638 "AT": "AUT", 639 "AZ": "AZE", 640 "BS": "BHS", 641 "BH": "BHR", 642 "BD": "BGD", 643 "BB": "BRB", 644 "BY": "BLR", 645 "BE": "BEL", 646 "BZ": "BLZ", 647 "BJ": "BEN", 648 "BM": "BMU", 649 "BT": "BTN", 650 "BO": "BOL", 651 "BQ": "BES", 652 "BA": "BIH", 653 "BW": "BWA", 654 "BV": "BVT", 655 "BR": "BRA", 656 "IO": "IOT", 657 "BN": "BRN", 658 "BG": "BGR", 659 "BF": "BFA", 660 "BI": "BDI", 661 "KH": "KHM", 662 "CM": "CMR", 663 "CA": "CAN", 664 "CV": "CPV", 665 "KY": "CYM", 666 "CF": "CAF", 667 "TD": "TCD", 668 "CL": "CHL", 669 "CN": "CHN", 670 "CX": "CXR", 671 "CC": "CCK", 672 "CO": "COL", 673 "KM": "COM", 674 "CG": "COG", 675 "CD": "COD", 676 "CK": "COK", 677 "CR": "CRI", 678 "CI": "CIV", 679 "HR": "HRV", 680 "CU": "CUB", 681 "CW": "CUW", 682 "CY": "CYP", 683 "CZ": "CZE", 684 "DK": "DNK", 685 "DJ": "DJI", 686 "DM": "DMA", 687 "DO": "DOM", 688 "EC": "ECU", 689 "EG": "EGY", 690 "SV": "SLV", 691 "GQ": "GNQ", 692 "ER": "ERI", 693 "EE": "EST", 694 "ET": "ETH", 695 "FK": "FLK", 696 "FO": "FRO", 697 "FJ": "FJI", 698 "FI": "FIN", 699 "FR": "FRA", 700 "GF": "GUF", 701 "PF": "PYF", 702 "TF": "ATF", 703 "GA": "GAB", 704 "GM": "GMB", 705 "GE": "GEO", 706 "DE": "DEU", 707 "GH": "GHA", 708 "GI": "GIB", 709 "GR": "GRC", 710 "GL": "GRL", 711 "GD": "GRD", 712 "GP": "GLP", 713 "GU": "GUM", 714 "GT": "GTM", 715 "GG": "GGY", 716 "GN": "GIN", 717 "GW": "GNB", 718 "GY": "GUY", 719 "HT": "HTI", 720 "HM": "HMD", 721 "VA": "VAT", 722 "HN": "HND", 723 "HK": "HKG", 724 "HU": "HUN", 725 "IS": "ISL", 726 "IN": "IND", 727 "ID": "IDN", 728 "IR": "IRN", 729 "IQ": "IRQ", 730 "IE": "IRL", 731 "IM": "IMN", 732 "IL": "ISR", 733 "IT": "ITA", 734 "JM": "JAM", 735 "JP": "JPN", 736 "JE": "JEY", 737 "JO": "JOR", 738 "KZ": "KAZ", 739 "KE": "KEN", 740 "KI": "KIR", 741 "KP": "PRK", 742 "KR": "KOR", 743 "KW": "KWT", 744 "KG": "KGZ", 745 "LA": "LAO", 746 "LV": "LVA", 747 "LB": "LBN", 748 "LS": "LSO", 749 "LR": "LBR", 750 "LY": "LBY", 751 "LI": "LIE", 752 "LT": "LTU", 753 "LU": "LUX", 754 "MO": "MAC", 755 "MK": "MKD", 756 "MG": "MDG", 757 "MW": "MWI", 758 "MY": "MYS", 759 "MV": "MDV", 760 "ML": "MLI", 761 "MT": "MLT", 762 "MH": "MHL", 763 "MQ": "MTQ", 764 "MR": "MRT", 765 "MU": "MUS", 766 "YT": "MYT", 767 "MX": "MEX", 768 "FM": "FSM", 769 "MD": "MDA", 770 "MC": "MCO", 771 "MN": "MNG", 772 "ME": "MNE", 773 "MS": "MSR", 774 "MA": "MAR", 775 "MZ": "MOZ", 776 "MM": "MMR", 777 "NA": "NAM", 778 "NR": "NRU", 779 "NP": "NPL", 780 "NL": "NLD", 781 "NC": "NCL", 782 "NZ": "NZL", 783 "NI": "NIC", 784 "NE": "NER", 785 "NG": "NGA", 786 "NU": "NIU", 787 "NF": "NFK", 788 "MP": "MNP", 789 "NO": "NOR", 790 "OM": "OMN", 791 "PK": "PAK", 792 "PW": "PLW", 793 "PS": "PSE", 794 "PA": "PAN", 795 "PG": "PNG", 796 "PY": "PRY", 797 "PE": "PER", 798 "PH": "PHL", 799 "PN": "PCN", 800 "PL": "POL", 801 "PT": "PRT", 802 "PR": "PRI", 803 "QA": "QAT", 804 "RE": "REU", 805 "RO": "ROU", 806 "RU": "RUS", 807 "RW": "RWA", 808 "BL": "BLM", 809 "SH": "SHN", 810 "KN": "KNA", 811 "LC": "LCA", 812 "MF": "MAF", 813 "PM": "SPM", 814 "VC": "VCT", 815 "WS": "WSM", 816 "SM": "SMR", 817 "ST": "STP", 818 "SA": "SAU", 819 "SN": "SEN", 820 "RS": "SRB", 821 "SC": "SYC", 822 "SL": "SLE", 823 "SG": "SGP", 824 "SX": "SXM", 825 "SK": "SVK", 826 "SI": "SVN", 827 "SB": "SLB", 828 "SO": "SOM", 829 "ZA": "ZAF", 830 "GS": "SGS", 831 "SS": "SSD", 832 "ES": "ESP", 833 "LK": "LKA", 834 "SD": "SDN", 835 "SR": "SUR", 836 "SJ": "SJM", 837 "SZ": "SWZ", 838 "SE": "SWE", 839 "CH": "CHE", 840 "SY": "SYR", 841 "TW": "TWN", 842 "TJ": "TJK", 843 "TZ": "TZA", 844 "TH": "THA", 845 "TL": "TLS", 846 "TG": "TGO", 847 "TK": "TKL", 848 "TO": "TON", 849 "TT": "TTO", 850 "TN": "TUN", 851 "TR": "TUR", 852 "TM": "TKM", 853 "TC": "TCA", 854 "TV": "TUV", 855 "UG": "UGA", 856 "UA": "UKR", 857 "AE": "ARE", 858 "GB": "GBR", 859 "US": "USA", 860 "UM": "UMI", 861 "UY": "URY", 862 "UZ": "UZB", 863 "VU": "VUT", 864 "VE": "VEN", 865 "VN": "VNM", 866 "VG": "VGB", 867 "VI": "VIR", 868 "WF": "WLF", 869 "EH": "ESH", 870 "YE": "YEM", 871 "ZM": "ZMB", 872 "ZW": "ZWE" 873 }; 874 875 876 ilib.Locale.a1toa3langmap = { 877 "ab": "abk", 878 "aa": "aar", 879 "af": "afr", 880 "ak": "aka", 881 "sq": "sqi", 882 "am": "amh", 883 "ar": "ara", 884 "an": "arg", 885 "hy": "hye", 886 "as": "asm", 887 "av": "ava", 888 "ae": "ave", 889 "ay": "aym", 890 "az": "aze", 891 "bm": "bam", 892 "ba": "bak", 893 "eu": "eus", 894 "be": "bel", 895 "bn": "ben", 896 "bh": "bih", 897 "bi": "bis", 898 "bs": "bos", 899 "br": "bre", 900 "bg": "bul", 901 "my": "mya", 902 "ca": "cat", 903 "ch": "cha", 904 "ce": "che", 905 "ny": "nya", 906 "zh": "zho", 907 "cv": "chv", 908 "kw": "cor", 909 "co": "cos", 910 "cr": "cre", 911 "hr": "hrv", 912 "cs": "ces", 913 "da": "dan", 914 "dv": "div", 915 "nl": "nld", 916 "dz": "dzo", 917 "en": "eng", 918 "eo": "epo", 919 "et": "est", 920 "ee": "ewe", 921 "fo": "fao", 922 "fj": "fij", 923 "fi": "fin", 924 "fr": "fra", 925 "ff": "ful", 926 "gl": "glg", 927 "ka": "kat", 928 "de": "deu", 929 "el": "ell", 930 "gn": "grn", 931 "gu": "guj", 932 "ht": "hat", 933 "ha": "hau", 934 "he": "heb", 935 "hz": "her", 936 "hi": "hin", 937 "ho": "hmo", 938 "hu": "hun", 939 "ia": "ina", 940 "id": "ind", 941 "ie": "ile", 942 "ga": "gle", 943 "ig": "ibo", 944 "ik": "ipk", 945 "io": "ido", 946 "is": "isl", 947 "it": "ita", 948 "iu": "iku", 949 "ja": "jpn", 950 "jv": "jav", 951 "kl": "kal", 952 "kn": "kan", 953 "kr": "kau", 954 "ks": "kas", 955 "kk": "kaz", 956 "km": "khm", 957 "ki": "kik", 958 "rw": "kin", 959 "ky": "kir", 960 "kv": "kom", 961 "kg": "kon", 962 "ko": "kor", 963 "ku": "kur", 964 "kj": "kua", 965 "la": "lat", 966 "lb": "ltz", 967 "lg": "lug", 968 "li": "lim", 969 "ln": "lin", 970 "lo": "lao", 971 "lt": "lit", 972 "lu": "lub", 973 "lv": "lav", 974 "gv": "glv", 975 "mk": "mkd", 976 "mg": "mlg", 977 "ms": "msa", 978 "ml": "mal", 979 "mt": "mlt", 980 "mi": "mri", 981 "mr": "mar", 982 "mh": "mah", 983 "mn": "mon", 984 "na": "nau", 985 "nv": "nav", 986 "nb": "nob", 987 "nd": "nde", 988 "ne": "nep", 989 "ng": "ndo", 990 "nn": "nno", 991 "no": "nor", 992 "ii": "iii", 993 "nr": "nbl", 994 "oc": "oci", 995 "oj": "oji", 996 "cu": "chu", 997 "om": "orm", 998 "or": "ori", 999 "os": "oss", 1000 "pa": "pan", 1001 "pi": "pli", 1002 "fa": "fas", 1003 "pl": "pol", 1004 "ps": "pus", 1005 "pt": "por", 1006 "qu": "que", 1007 "rm": "roh", 1008 "rn": "run", 1009 "ro": "ron", 1010 "ru": "rus", 1011 "sa": "san", 1012 "sc": "srd", 1013 "sd": "snd", 1014 "se": "sme", 1015 "sm": "smo", 1016 "sg": "sag", 1017 "sr": "srp", 1018 "gd": "gla", 1019 "sn": "sna", 1020 "si": "sin", 1021 "sk": "slk", 1022 "sl": "slv", 1023 "so": "som", 1024 "st": "sot", 1025 "az": "azb", 1026 "es": "spa", 1027 "su": "sun", 1028 "sw": "swa", 1029 "ss": "ssw", 1030 "sv": "swe", 1031 "ta": "tam", 1032 "te": "tel", 1033 "tg": "tgk", 1034 "th": "tha", 1035 "ti": "tir", 1036 "bo": "bod", 1037 "tk": "tuk", 1038 "tl": "tgl", 1039 "tn": "tsn", 1040 "to": "ton", 1041 "tr": "tur", 1042 "ts": "tso", 1043 "tt": "tat", 1044 "tw": "twi", 1045 "ty": "tah", 1046 "ug": "uig", 1047 "uk": "ukr", 1048 "ur": "urd", 1049 "uz": "uzb", 1050 "ve": "ven", 1051 "vi": "vie", 1052 "vo": "vol", 1053 "wa": "wln", 1054 "cy": "cym", 1055 "wo": "wol", 1056 "fy": "fry", 1057 "xh": "xho", 1058 "yi": "yid", 1059 "yo": "yor", 1060 "za": "zha", 1061 "zu": "zul" 1062 }; 1063 1064 /** 1065 * Tell whether or not the str does not start with a lower case ASCII char. 1066 * @private 1067 * @param {string} str the char to check 1068 * @return {boolean} true if the char is not a lower case ASCII char 1069 */ 1070 ilib.Locale._notLower = function(str) { 1071 // do this with ASCII only so we don't have to depend on the CType functions 1072 var ch = str.charCodeAt(0); 1073 return ch < 97 || ch > 122; 1074 }; 1075 1076 /** 1077 * Tell whether or not the str does not start with an upper case ASCII char. 1078 * @private 1079 * @param {string} str the char to check 1080 * @return {boolean} true if the char is a not an upper case ASCII char 1081 */ 1082 ilib.Locale._notUpper = function(str) { 1083 // do this with ASCII only so we don't have to depend on the CType functions 1084 var ch = str.charCodeAt(0); 1085 return ch < 65 || ch > 90; 1086 }; 1087 1088 /** 1089 * Tell whether or not the str does not start with a digit char. 1090 * @private 1091 * @param {string} str the char to check 1092 * @return {boolean} true if the char is a not an upper case ASCII char 1093 */ 1094 ilib.Locale._notDigit = function(str) { 1095 // do this with ASCII only so we don't have to depend on the CType functions 1096 var ch = str.charCodeAt(0); 1097 return ch < 48 || ch > 57; 1098 }; 1099 1100 /** 1101 * Tell whether or not the given string has the correct syntax to be 1102 * an ISO 639 language code. 1103 * 1104 * @private 1105 * @param {string} str the string to parse 1106 * @return {boolean} true if the string could syntactically be a language code. 1107 */ 1108 ilib.Locale._isLanguageCode = function(str) { 1109 if (typeof(str) === 'undefined' || str.length < 2 || str.length > 3) { 1110 return false; 1111 } 1112 1113 for (var i = 0; i < str.length; i++) { 1114 if (ilib.Locale._notLower(str.charAt(i))) { 1115 return false; 1116 } 1117 } 1118 1119 return true; 1120 }; 1121 1122 /** 1123 * Tell whether or not the given string has the correct syntax to be 1124 * an ISO 3166 2-letter region code or M.49 3-digit region code. 1125 * 1126 * @private 1127 * @param {string} str the string to parse 1128 * @return {boolean} true if the string could syntactically be a language code. 1129 */ 1130 ilib.Locale._isRegionCode = function (str) { 1131 if (typeof(str) === 'undefined' || str.length < 2 || str.length > 3) { 1132 return false; 1133 } 1134 1135 if (str.length === 2) { 1136 for (var i = 0; i < str.length; i++) { 1137 if (ilib.Locale._notUpper(str.charAt(i))) { 1138 return false; 1139 } 1140 } 1141 } else { 1142 for (var i = 0; i < str.length; i++) { 1143 if (ilib.Locale._notDigit(str.charAt(i))) { 1144 return false; 1145 } 1146 } 1147 } 1148 1149 return true; 1150 }; 1151 1152 /** 1153 * Tell whether or not the given string has the correct syntax to be 1154 * an ISO 639 language code. 1155 * 1156 * @private 1157 * @param {string} str the string to parse 1158 * @return {boolean} true if the string could syntactically be a language code. 1159 */ 1160 ilib.Locale._isScriptCode = function(str) 1161 { 1162 if (typeof(str) === 'undefined' || str.length !== 4 || ilib.Locale._notUpper(str.charAt(0))) { 1163 return false; 1164 } 1165 1166 for (var i = 1; i < 4; i++) { 1167 if (ilib.Locale._notLower(str.charAt(i))) { 1168 return false; 1169 } 1170 } 1171 1172 return true; 1173 }; 1174 1175 /** 1176 * Return the ISO-3166 alpha3 equivalent region code for the given ISO 3166 alpha2 1177 * region code. If the given alpha2 code is not found, this function returns its 1178 * argument unchanged. 1179 * @static 1180 * @param {string|undefined} alpha2 the alpha2 code to map 1181 * @return {string|undefined} the alpha3 equivalent of the given alpha2 code, or the alpha2 1182 * parameter if the alpha2 value is not found 1183 */ 1184 ilib.Locale.regionAlpha2ToAlpha3 = function(alpha2) { 1185 return ilib.Locale.a2toa3regmap[alpha2] || alpha2; 1186 }; 1187 1188 /** 1189 * Return the ISO-639 alpha3 equivalent language code for the given ISO 639 alpha1 1190 * language code. If the given alpha1 code is not found, this function returns its 1191 * argument unchanged. 1192 * @static 1193 * @param {string|undefined} alpha1 the alpha1 code to map 1194 * @return {string|undefined} the alpha3 equivalent of the given alpha1 code, or the alpha1 1195 * parameter if the alpha1 value is not found 1196 */ 1197 ilib.Locale.languageAlpha1ToAlpha3 = function(alpha1) { 1198 return ilib.Locale.a1toa3langmap[alpha1] || alpha1; 1199 }; 1200 1201 ilib.Locale.prototype = { 1202 /** 1203 * @private 1204 */ 1205 _genSpec: function () { 1206 this.spec = this.language || ""; 1207 1208 if (this.script) { 1209 if (this.spec.length > 0) { 1210 this.spec += "-"; 1211 } 1212 this.spec += this.script; 1213 } 1214 1215 if (this.region) { 1216 if (this.spec.length > 0) { 1217 this.spec += "-"; 1218 } 1219 this.spec += this.region; 1220 } 1221 1222 if (this.variant) { 1223 if (this.spec.length > 0) { 1224 this.spec += "-"; 1225 } 1226 this.spec += this.variant; 1227 } 1228 }, 1229 1230 /** 1231 * Return the ISO 639 language code for this locale. 1232 * @return {string|undefined} the language code for this locale 1233 */ 1234 getLanguage: function() { 1235 return this.language; 1236 }, 1237 1238 /** 1239 * Return the language of this locale as an ISO-639-alpha3 language code 1240 * @return {string|undefined} the alpha3 language code of this locale 1241 */ 1242 getLanguageAlpha3: function() { 1243 return ilib.Locale.languageAlpha1ToAlpha3(this.language); 1244 }, 1245 1246 /** 1247 * Return the ISO 3166 region code for this locale. 1248 * @return {string|undefined} the region code of this locale 1249 */ 1250 getRegion: function() { 1251 return this.region; 1252 }, 1253 1254 /** 1255 * Return the region of this locale as an ISO-3166-alpha3 region code 1256 * @return {string|undefined} the alpha3 region code of this locale 1257 */ 1258 getRegionAlpha3: function() { 1259 return ilib.Locale.regionAlpha2ToAlpha3(this.region); 1260 }, 1261 1262 /** 1263 * Return the ISO 15924 script code for this locale 1264 * @return {string|undefined} the script code of this locale 1265 */ 1266 getScript: function () { 1267 return this.script; 1268 }, 1269 1270 /** 1271 * Return the variant code for this locale 1272 * @return {string|undefined} the variant code of this locale, if any 1273 */ 1274 getVariant: function() { 1275 return this.variant; 1276 }, 1277 1278 /** 1279 * Return the whole locale specifier as a string. 1280 * @return {string} the locale specifier 1281 */ 1282 getSpec: function() { 1283 return this.spec; 1284 }, 1285 1286 /** 1287 * Express this locale object as a string. Currently, this simply calls the getSpec 1288 * function to represent the locale as its specifier. 1289 * 1290 * @return {string} the locale specifier 1291 */ 1292 toString: function() { 1293 return this.getSpec(); 1294 }, 1295 1296 /** 1297 * Return true if the the other locale is exactly equal to the current one. 1298 * @return {boolean} whether or not the other locale is equal to the current one 1299 */ 1300 equals: function(other) { 1301 return this.language === other.language && 1302 this.region === other.region && 1303 this.script === other.script && 1304 this.variant === other.variant; 1305 }, 1306 1307 /** 1308 * Return true if the current locale is the special pseudo locale. 1309 * @return {boolean} true if the current locale is the special pseudo locale 1310 */ 1311 isPseudo: function () { 1312 var localeName = this.language + "-" + this.region; 1313 return ilib.pseudoLocales.indexOf(localeName) > -1; 1314 } 1315 }; 1316 1317 // static functions 1318 /** 1319 * @private 1320 */ 1321 ilib.Locale.locales = [ 1322 1323 ]; 1324 1325 /** 1326 * Return the list of available locales that this iLib file was assembled 1327 * with. The list that this file was assembled with may be much smaller 1328 * than the list of all available locales in the iLib repository. The 1329 * assembly tool will automatically fill in the list. 1330 * 1331 * @return {Array.<string>} this is an array of locale specs for which 1332 * this iLib file has locale data for 1333 */ 1334 ilib.Locale.getAvailableLocales = function () { 1335 return ilib.Locale.locales; 1336 }; 1337 1338 /* 1339 * localeinfo.js - Encode locale-specific defaults 1340 * 1341 * Copyright © 2012-2014, JEDLSoft 1342 * 1343 * Licensed under the Apache License, Version 2.0 (the "License"); 1344 * you may not use this file except in compliance with the License. 1345 * You may obtain a copy of the License at 1346 * 1347 * http://www.apache.org/licenses/LICENSE-2.0 1348 * 1349 * Unless required by applicable law or agreed to in writing, software 1350 * distributed under the License is distributed on an "AS IS" BASIS, 1351 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1352 * 1353 * See the License for the specific language governing permissions and 1354 * limitations under the License. 1355 */ 1356 1357 // !depends ilibglobal.js locale.js 1358 1359 // !data localeinfo 1360 1361 /** 1362 * @class 1363 * Create a new locale info instance. Locale info instances give information about 1364 * the default settings for a particular locale. These settings may be overridden 1365 * by various parts of the code, and should be used as a fall-back setting of last 1366 * resort. <p> 1367 * 1368 * The optional options object holds extra parameters if they are necessary. The 1369 * current list of supported options are: 1370 * 1371 * <ul> 1372 * <li><i>onLoad</i> - a callback function to call when the locale info object is fully 1373 * loaded. When the onLoad option is given, the localeinfo object will attempt to 1374 * load any missing locale data using the ilib loader callback. 1375 * When the constructor is done (even if the data is already preassembled), the 1376 * onLoad function is called with the current instance as a parameter, so this 1377 * callback can be used with preassembled or dynamic loading or a mix of the two. 1378 * 1379 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 1380 * asynchronously. If this option is given as "false", then the "onLoad" 1381 * callback must be given, as the instance returned from this constructor will 1382 * not be usable for a while. 1383 * 1384 * <li><i>loadParams</i> - an object containing parameters to pass to the 1385 * loader callback function when locale data is missing. The parameters are not 1386 * interpretted or modified in any way. They are simply passed along. The object 1387 * may contain any property/value pairs as long as the calling code is in 1388 * agreement with the loader callback function as to what those parameters mean. 1389 * </ul> 1390 * 1391 * If this copy of ilib is pre-assembled and all the data is already available, 1392 * or if the data was already previously loaded, then this constructor will call 1393 * the onLoad callback immediately when the initialization is done. 1394 * If the onLoad option is not given, this class will only attempt to load any 1395 * missing locale data synchronously. 1396 * 1397 * Depends directive: !depends localeinfo.js 1398 * 1399 * @constructor 1400 * @see {ilib.setLoaderCallback} for information about registering a loader callback 1401 * function 1402 * @param {ilib.Locale|string=} locale the locale for which the info is sought, or undefined for 1403 * @param {Object=} options the locale for which the info is sought, or undefined for 1404 * the current locale 1405 */ 1406 ilib.LocaleInfo = function(locale, options) { 1407 var sync = true, 1408 loadParams = undefined; 1409 1410 /* these are all the defaults. Essentially, en-US */ 1411 /** 1412 @private 1413 @type {{ 1414 scripts:Array.<string>, 1415 timezone:string, 1416 units:string, 1417 calendar:string, 1418 clock:string, 1419 currency:string, 1420 firstDayOfWeek:number, 1421 weekendStart:number, 1422 weekendEnd:number, 1423 meridiems:string, 1424 unitfmt: {long:string,short:string}, 1425 numfmt:Object.<{ 1426 currencyFormats:Object.<{common:string,commonNegative:string,iso:string,isoNegative:string}>, 1427 script:string, 1428 decimalChar:string, 1429 groupChar:string, 1430 prigroupSize:number, 1431 secgroupSize:number, 1432 negativenumFmt:string, 1433 pctFmt:string, 1434 negativepctFmt:string, 1435 pctChar:string, 1436 roundingMode:string, 1437 exponential:string, 1438 digits:string 1439 }> 1440 }} 1441 */ 1442 this.info = ilib.LocaleInfo.defaultInfo; 1443 1444 switch (typeof(locale)) { 1445 case "string": 1446 this.locale = new ilib.Locale(locale); 1447 break; 1448 default: 1449 case "undefined": 1450 this.locale = new ilib.Locale(); 1451 break; 1452 case "object": 1453 this.locale = locale; 1454 break; 1455 } 1456 1457 if (options) { 1458 if (typeof(options.sync) !== 'undefined') { 1459 sync = (options.sync == true); 1460 } 1461 1462 if (typeof(options.loadParams) !== 'undefined') { 1463 loadParams = options.loadParams; 1464 } 1465 } 1466 1467 if (!ilib.LocaleInfo.cache) { 1468 ilib.LocaleInfo.cache = {}; 1469 } 1470 1471 ilib.loadData({ 1472 object: ilib.LocaleInfo, 1473 locale: this.locale, 1474 name: "localeinfo.json", 1475 sync: sync, 1476 loadParams: loadParams, 1477 callback: ilib.bind(this, function (info) { 1478 if (!info) { 1479 info = ilib.LocaleInfo.defaultInfo; 1480 var spec = this.locale.getSpec().replace(/-/g, "_"); 1481 ilib.LocaleInfo.cache[spec] = info; 1482 } 1483 this.info = info; 1484 if (options && typeof(options.onLoad) === 'function') { 1485 options.onLoad(this); 1486 } 1487 }) 1488 }); 1489 }; 1490 1491 ilib.LocaleInfo.defaultInfo = /** @type {{ 1492 scripts:Array.<string>, 1493 timezone:string, 1494 units:string, 1495 calendar:string, 1496 clock:string, 1497 currency:string, 1498 firstDayOfWeek:number, 1499 weekendStart:number, 1500 weekendEnd:number, 1501 meridiems:string, 1502 unitfmt: {long:string,short:string}, 1503 numfmt:Object.<{ 1504 currencyFormats:Object.<{ 1505 common:string, 1506 commonNegative:string, 1507 iso:string, 1508 isoNegative:string 1509 }>, 1510 script:string, 1511 decimalChar:string, 1512 groupChar:string, 1513 prigroupSize:number, 1514 secgroupSize:number, 1515 negativenumFmt:string, 1516 pctFmt:string, 1517 negativepctFmt:string, 1518 pctChar:string, 1519 roundingMode:string, 1520 exponential:string, 1521 digits:string 1522 }> 1523 }}*/ ilib.data.localeinfo; 1524 ilib.LocaleInfo.defaultInfo = ilib.LocaleInfo.defaultInfo || { 1525 "scripts": ["Latn"], 1526 "timezone": "Etc/UTC", 1527 "units": "metric", 1528 "calendar": "gregorian", 1529 "clock": "24", 1530 "currency": "USD", 1531 "firstDayOfWeek": 1, 1532 "meridiems": "gregorian", 1533 "numfmt": { 1534 "currencyFormats": { 1535 "common": "{s}{n}", 1536 "commonNegative": "{s}-{n}", 1537 "iso": "{s}{n}", 1538 "isoNegative": "{s}-{n}" 1539 }, 1540 "script": "Latn", 1541 "decimalChar": ",", 1542 "groupChar": ".", 1543 "prigroupSize": 3, 1544 "secgroupSize": 0, 1545 "pctFmt": "{n}%", 1546 "negativepctFmt": "-{n}%", 1547 "pctChar": "%", 1548 "roundingMode": "halfdown", 1549 "exponential": "e", 1550 "digits": "" 1551 } 1552 }; 1553 1554 ilib.LocaleInfo.prototype = { 1555 /** 1556 * Return the name of the locale's language in English. 1557 * @returns {string} the name of the locale's language in English 1558 */ 1559 getLanguageName: function () { 1560 return this.info["language.name"]; 1561 }, 1562 1563 /** 1564 * Return the name of the locale's region in English. If the locale 1565 * has no region, this returns undefined. 1566 * 1567 * @returns {string|undefined} the name of the locale's region in English 1568 */ 1569 getRegionName: function () { 1570 return this.info["region.name"]; 1571 }, 1572 1573 /** 1574 * Return whether this locale commonly uses the 12- or the 24-hour clock. 1575 * 1576 * @returns {string} "12" if the locale commonly uses a 12-hour clock, or "24" 1577 * if the locale commonly uses a 24-hour clock. 1578 */ 1579 getClock: function() { 1580 return this.info.clock; 1581 }, 1582 1583 /** 1584 * Return the locale that this info object was created with. 1585 * @returns {ilib.Locale} The locale spec of the locale used to construct this info instance 1586 */ 1587 getLocale: function () { 1588 return this.locale; 1589 }, 1590 1591 /** 1592 * Return the name of the measuring system that is commonly used in the given locale. 1593 * Valid values are "uscustomary", "imperial", and "metric". 1594 * 1595 * @returns {string} The name of the measuring system commonly used in the locale 1596 */ 1597 getUnits: function () { 1598 return this.info.units; 1599 }, 1600 1601 getUnitFormat: function () { 1602 return this.info.unitfmt; 1603 }, 1604 1605 /** 1606 * Return the name of the calendar that is commonly used in the given locale. 1607 * 1608 * @returns {string} The name of the calendar commonly used in the locale 1609 */ 1610 getCalendar: function () { 1611 return this.info.calendar; 1612 }, 1613 1614 /** 1615 * Return the day of week that starts weeks in the current locale. Days are still 1616 * numbered the standard way with 0 for Sunday through 6 for Saturday, but calendars 1617 * should be displayed and weeks calculated with the day of week returned from this 1618 * function as the first day of the week. 1619 * 1620 * @returns {number} the day of the week that starts weeks in the current locale. 1621 */ 1622 getFirstDayOfWeek: function () { 1623 return this.info.firstDayOfWeek; 1624 }, 1625 1626 /** 1627 * Return the day of week that starts weekend in the current locale. Days are still 1628 * numbered the standard way with 0 for Sunday through 6 for Saturday. 1629 * 1630 * @returns {number} the day of the week that starts weeks in the current locale. 1631 */ 1632 getWeekEndStart: function () { 1633 return this.info.weekendStart; 1634 }, 1635 1636 /** 1637 * Return the day of week that starts weekend in the current locale. Days are still 1638 * numbered the standard way with 0 for Sunday through 6 for Saturday. 1639 * 1640 * @returns {number} the day of the week that starts weeks in the current locale. 1641 */ 1642 getWeekEndEnd: function () { 1643 return this.info.weekendEnd; 1644 }, 1645 1646 /** 1647 * Return the default time zone for this locale. Many locales span across multiple 1648 * time zones. In this case, the time zone with the largest population is chosen 1649 * to represent the locale. This is obviously not that accurate, but then again, 1650 * this method's return value should only be used as a default anyways. 1651 * @returns {string} the default time zone for this locale. 1652 */ 1653 getTimeZone: function () { 1654 return this.info.timezone; 1655 }, 1656 1657 /** 1658 * Return the decimal separator for formatted numbers in this locale. 1659 * @returns {string} the decimal separator char 1660 */ 1661 getDecimalSeparator: function () { 1662 return this.info.numfmt.decimalChar; 1663 }, 1664 1665 /** 1666 * Return the decimal separator for formatted numbers in this locale for native script. 1667 * @returns {string} the decimal separator char 1668 */ 1669 getNativeDecimalSeparator: function () { 1670 return (this.info.native_numfmt && this.info.native_numfmt.decimalChar) || this.info.numfmt.decimalChar; 1671 }, 1672 1673 /** 1674 * Return the separator character used to separate groups of digits on the 1675 * integer side of the decimal character. 1676 * @returns {string} the grouping separator char 1677 */ 1678 getGroupingSeparator: function () { 1679 return this.info.numfmt.groupChar; 1680 }, 1681 1682 /** 1683 * Return the separator character used to separate groups of digits on the 1684 * integer side of the decimal character for the native script if present other than the default script. 1685 * @returns {string} the grouping separator char 1686 */ 1687 getNativeGroupingSeparator: function () { 1688 return (this.info.native_numfmt && this.info.native_numfmt.groupChar) || this.info.numfmt.groupChar; 1689 }, 1690 1691 /** 1692 * Return the minimum number of digits grouped together on the integer side 1693 * for the first (primary) group. 1694 * In western European cultures, groupings are in 1000s, so the number of digits 1695 * is 3. 1696 * @returns {number} the number of digits in a primary grouping, or 0 for no grouping 1697 */ 1698 getPrimaryGroupingDigits: function () { 1699 return (typeof(this.info.numfmt.prigroupSize) !== 'undefined' && this.info.numfmt.prigroupSize) || 0; 1700 }, 1701 1702 /** 1703 * Return the minimum number of digits grouped together on the integer side 1704 * for the second or more (secondary) group.<p> 1705 * 1706 * In western European cultures, all groupings are by 1000s, so the secondary 1707 * size should be 0 because there is no secondary size. In general, if this 1708 * method returns 0, then all groupings are of the primary size.<p> 1709 * 1710 * For some other cultures, the first grouping (primary) 1711 * is 3 and any subsequent groupings (secondary) are two. So, 100000 would be 1712 * written as: "1,00,000". 1713 * 1714 * @returns {number} the number of digits in a secondary grouping, or 0 for no 1715 * secondary grouping. 1716 */ 1717 getSecondaryGroupingDigits: function () { 1718 return this.info.numfmt.secgroupSize || 0; 1719 }, 1720 1721 /** 1722 * Return the format template used to format percentages in this locale. 1723 * @returns {string} the format template for formatting percentages 1724 */ 1725 getPercentageFormat: function () { 1726 return this.info.numfmt.pctFmt; 1727 }, 1728 1729 /** 1730 * Return the format template used to format percentages in this locale 1731 * with negative amounts. 1732 * @returns {string} the format template for formatting percentages 1733 */ 1734 getNegativePercentageFormat: function () { 1735 return this.info.numfmt.negativepctFmt; 1736 }, 1737 1738 /** 1739 * Return the symbol used for percentages in this locale. 1740 * @returns {string} the symbol used for percentages in this locale 1741 */ 1742 getPercentageSymbol: function () { 1743 return this.info.numfmt.pctChar || "%"; 1744 }, 1745 1746 /** 1747 * Return the symbol used for exponential in this locale. 1748 * @returns {string} the symbol used for exponential in this locale 1749 */ 1750 getExponential: function () { 1751 return this.info.numfmt.exponential; 1752 }, 1753 1754 /** 1755 * Return the symbol used for exponential in this locale for native script. 1756 * @returns {string} the symbol used for exponential in this locale for native script 1757 */ 1758 getNativeExponential: function () { 1759 return (this.info.native_numfmt && this.info.native_numfmt.exponential) || this.info.numfmt.exponential; 1760 }, 1761 1762 /** 1763 * Return the symbol used for percentages in this locale for native script. 1764 * @returns {string} the symbol used for percentages in this locale for native script 1765 */ 1766 getNativePercentageSymbol: function () { 1767 return (this.info.native_numfmt && this.info.native_numfmt.pctChar) || this.info.numfmt.pctChar || "%"; 1768 1769 }, 1770 /** 1771 * Return the format template used to format negative numbers in this locale. 1772 * @returns {string} the format template for formatting negative numbers 1773 */ 1774 getNegativeNumberFormat: function () { 1775 return this.info.numfmt.negativenumFmt; 1776 }, 1777 1778 /** 1779 * Return an object containing the format templates for formatting currencies 1780 * in this locale. The object has a number of properties in it that each are 1781 * a particular style of format. Normally, this contains a "common" and an "iso" 1782 * style, but may contain others in the future. 1783 * @returns {Object} an object containing the format templates for currencies 1784 */ 1785 getCurrencyFormats: function () { 1786 return this.info.numfmt.currencyFormats; 1787 }, 1788 1789 /** 1790 * Return the currency that is legal in the locale, or which is most commonly 1791 * used in regular commerce. 1792 * @returns {string} the ISO 4217 code for the currency of this locale 1793 */ 1794 getCurrency: function () { 1795 return this.info.currency; 1796 }, 1797 1798 /** 1799 * Return a string that describes the style of digits used by this locale. 1800 * Possible return values are: 1801 * <ul> 1802 * <li><i>western</i> - uses the regular western 10-based digits 0 through 9 1803 * <li><i>optional</i> - native 10-based digits exist, but in modern usage, 1804 * this locale most often uses western digits 1805 * <li><i>native</i> - native 10-based native digits exist and are used 1806 * regularly by this locale 1807 * <li><i>custom</i> - uses native digits by default that are not 10-based 1808 * </ul> 1809 * @returns {string} string that describes the style of digits used in this locale 1810 */ 1811 getDigitsStyle: function () { 1812 if (this.info.numfmt.useNative) { 1813 return "native"; 1814 } 1815 if (typeof(this.info.native_numfmt) !== 'undefined') { 1816 return "optional"; 1817 } 1818 return "western"; 1819 }, 1820 1821 /** 1822 * Return the digits of the default script if they are defined. 1823 * If not defined, the default should be the regular "Arabic numerals" 1824 * used in the Latin script. (0-9) 1825 * @returns {string|undefined} the digits used in the default script 1826 */ 1827 getDigits: function () { 1828 return this.info.numfmt.digits; 1829 }, 1830 1831 /** 1832 * Return the digits of the native script if they are defined. 1833 * @returns {string|undefined} the digits used in the default script 1834 */ 1835 getNativeDigits: function () { 1836 return (this.info.numfmt.useNative && this.info.numfmt.digits) || (this.info.native_numfmt && this.info.native_numfmt.digits); 1837 }, 1838 1839 /** 1840 * If this locale typically uses a different type of rounding for numeric 1841 * formatting other than halfdown, especially for currency, then it can be 1842 * specified in the localeinfo. If the locale uses the default, then this 1843 * method returns undefined. The locale's rounding method overrides the 1844 * rounding method for the currency itself, which can sometimes shared 1845 * between various locales so it is less specific. 1846 * @returns {string} the name of the rounding mode typically used in this 1847 * locale, or "halfdown" if the locale does not override the default 1848 */ 1849 getRoundingMode: function () { 1850 return this.info.numfmt.roundingMode; 1851 }, 1852 1853 /** 1854 * Return the default script used to write text in the language of this 1855 * locale. Text for most languages is written in only one script, but there 1856 * are some languages where the text can be written in a number of scripts, 1857 * depending on a variety of things such as the region, ethnicity, religion, 1858 * etc. of the author. This method returns the default script for the 1859 * locale, in which the language is most commonly written.<p> 1860 * 1861 * The script is returned as an ISO 15924 4-letter code. 1862 * 1863 * @returns {string} the ISO 15924 code for the default script used to write 1864 * text in this locale 1865 */ 1866 getDefaultScript: function() { 1867 return (this.info.scripts) ? this.info.scripts[0] : "Latn"; 1868 }, 1869 1870 /** 1871 * Return the script used for the current locale. If the current locale 1872 * explicitly defines a script, then this script is returned. If not, then 1873 * the default script for the locale is returned. 1874 * 1875 * @see ilib.LocaleInfo.getDefaultScript 1876 * @returns {string} the ISO 15924 code for the script used to write 1877 * text in this locale 1878 */ 1879 getScript: function() { 1880 return this.locale.getScript() || this.getDefaultScript(); 1881 }, 1882 1883 /** 1884 * Return an array of script codes which are used to write text in the current 1885 * language. Text for most languages is written in only one script, but there 1886 * are some languages where the text can be written in a number of scripts, 1887 * depending on a variety of things such as the region, ethnicity, religion, 1888 * etc. of the author. This method returns an array of script codes in which 1889 * the language is commonly written. 1890 * 1891 * @returns {Array.<string>} an array of ISO 15924 codes for the scripts used 1892 * to write text in this language 1893 */ 1894 getAllScripts: function() { 1895 return this.info.scripts || ["Latn"]; 1896 }, 1897 1898 /** 1899 * Return the default style of meridiems used in this locale. Meridiems are 1900 * times of day like AM/PM. In a few locales with some calendars, for example 1901 * Amharic/Ethiopia using the Ethiopic calendar, the times of day may be 1902 * split into different segments than simple AM/PM as in the Gregorian 1903 * calendar. Only a few locales are like that. For most locales, formatting 1904 * a Gregorian date will use the regular Gregorian AM/PM meridiems. 1905 * 1906 * @returns {string} the default meridiems style used in this locale. Possible 1907 * values are "gregorian", "chinese", and "ethiopic" 1908 */ 1909 getMeridiemsStyle: function () { 1910 return this.info.meridiems || "gregorian"; 1911 } 1912 }; 1913 1914 /* 1915 * date.js - Represent a date in any calendar. This class is subclassed for each calendar. 1916 * 1917 * Copyright © 2012-2014, JEDLSoft 1918 * 1919 * Licensed under the Apache License, Version 2.0 (the "License"); 1920 * you may not use this file except in compliance with the License. 1921 * You may obtain a copy of the License at 1922 * 1923 * http://www.apache.org/licenses/LICENSE-2.0 1924 * 1925 * Unless required by applicable law or agreed to in writing, software 1926 * distributed under the License is distributed on an "AS IS" BASIS, 1927 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1928 * 1929 * See the License for the specific language governing permissions and 1930 * limitations under the License. 1931 */ 1932 1933 /* !depends ilibglobal.js localeinfo.js */ 1934 1935 /** 1936 * @class 1937 * Construct a new date object. Each parameter is a numeric value, but its 1938 * accepted range can vary depending on the subclass of this date. For example, 1939 * Gregorian months can be from 1 to 12, whereas months in the Hebrew calendar 1940 * can be from 1 to 13.<p> 1941 * 1942 * Note that this really calls the newInstance factory method underneath in 1943 * order to instantiate the correct subclass of ilib.Date. 1944 * 1945 * Depends directive: !depends date.js 1946 * 1947 * @constructor 1948 * @param {Object=} options The date components to initialize this date with 1949 */ 1950 ilib.Date = function(options) { 1951 if (!options || typeof(options.noinstance) === 'undefined') { 1952 return ilib.Date.newInstance(options); 1953 } 1954 }; 1955 1956 /** 1957 * Factory method to create a new instance of a date subclass.<p> 1958 * 1959 * The options parameter can be an object that contains the following 1960 * properties: 1961 * 1962 * <ul> 1963 * <li><i>type</i> - specify the type/calendar of the date desired. The 1964 * list of valid values changes depending on which calendars are 1965 * defined. When assembling your iliball.js, include those date type 1966 * you wish to use in your program or web page, and they will register 1967 * themselves with this factory method. The "gregorian", 1968 * and "julian" calendars are all included by default, as they are the 1969 * standard calendars for much of the world. If not specified, the type 1970 * of the date returned is the one that is appropriate for the locale. 1971 * This property may also be given as "calendar" instead of "type". 1972 * </ul> 1973 * 1974 * The options object is also passed down to the date constructor, and 1975 * thus can contain the the properties as the date object being instantiated. 1976 * See the documentation for {@link ilib.Date.GregDate}, and other 1977 * subclasses for more details on other parameter that may be passed in.<p> 1978 * 1979 * Please note that if you do not give the type parameter, this factory 1980 * method will create a date object that is appropriate for the calendar 1981 * that is most commonly used in the specified or current ilib locale. 1982 * For example, in Thailand, the most common calendar is the Thai solar 1983 * calendar. If the current locale is "th-TH" (Thai for Thailand) and you 1984 * use this factory method to construct a new date without specifying the 1985 * type, it will automatically give you back an instance of 1986 * {@link ilib.Date.ThaiSolarDate}. This is convenient because you do not 1987 * need to know which locales use which types of dates. In fact, you 1988 * should always use this factory method to make new date instances unless 1989 * you know that you specifically need a date in a particular calendar.<p> 1990 * 1991 * Also note that when you pass in the date components such as year, month, 1992 * day, etc., these components should be appropriate for the given date 1993 * being instantiated. That is, in our Thai example in the previous 1994 * paragraph, the year and such should be given as a Thai solar year, not 1995 * the Gregorian year that you get from the Javascript Date class. In 1996 * order to initialize a date instance when you don't know what subclass 1997 * will be instantiated for the locale, use a parameter such as "unixtime" 1998 * or "julianday" which are unambiguous and based on UTC time, instead of 1999 * the year/month/date date components. The date components for that UTC 2000 * time will be calculated and the time zone offset will be automatically 2001 * factored in. 2002 * 2003 * @param {Object=} options options controlling the construction of this instance, or 2004 * undefined to use the default options 2005 * @return {ilib.Date} an instance of a calendar object of the appropriate type 2006 */ 2007 ilib.Date.newInstance = function(options) { 2008 var locale = options && options.locale, 2009 type = options && (options.type || options.calendar), 2010 cons; 2011 2012 if (!locale) { 2013 locale = new ilib.Locale(); // default locale 2014 } 2015 2016 if (!type) { 2017 var info = new ilib.LocaleInfo(locale); 2018 type = info.getCalendar(); 2019 } 2020 2021 cons = ilib.Date._constructors[type]; 2022 2023 // pass the same options through to the constructor so the subclass 2024 // has the ability to do something with if it needs to 2025 return cons && new cons(options); 2026 }; 2027 2028 /** 2029 * Convert JavaScript Date objects and other types into native ilib Dates. This accepts any 2030 * string or number that can be translated by the JavaScript Date class, 2031 * (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) 2032 * any JavaScript Date classed object, any ilib.Date subclass, an ilib.JulianDay object, an object 2033 * containing the normal options to initialize an ilib.Date instance, or null (will 2034 * return null or undefined if input is null or undefined). Normal output is 2035 * a standard native subclass of the ilib Date object as appropriate for the locale. 2036 * 2037 * @static 2038 * @private 2039 * @param {ilib.Date|Object|ilib.JulianDay|Date|string|number=} inDate The input date object, string or Number. 2040 * @param {ilib.String|string=} timezone timezone to use if a new date object is created 2041 * @return {ilib.Date|null|undefined} an ilib.Date subclass equivalent to the given inDate 2042 */ 2043 ilib.Date._dateToIlib = function(inDate, timezone) { 2044 if (typeof(inDate) === 'undefined' || inDate === null) { 2045 return inDate; 2046 } 2047 if (inDate instanceof ilib.Date) { 2048 return inDate; 2049 } 2050 if (inDate instanceof Date) { 2051 return ilib.Date.newInstance({ 2052 unixtime: inDate.getTime(), 2053 timezone: timezone 2054 }); 2055 } 2056 if (inDate instanceof ilib.JulianDay) { 2057 return ilib.Date.newInstance({ 2058 jd: inDate, 2059 timezone: timezone 2060 }); 2061 } 2062 if (typeof(inDate) === 'number') { 2063 return ilib.Date.newInstance({ 2064 unixtime: inDate, 2065 timezone: timezone 2066 }); 2067 } 2068 if (typeof(inDate) === 'object') { 2069 return ilib.Date.newInstance(inDate); 2070 } 2071 if (typeof(inDate) === 'string') { 2072 inDate = new Date(inDate); 2073 } 2074 return ilib.Date.newInstance({ 2075 unixtime: inDate.getTime(), 2076 timezone: timezone 2077 }); 2078 }; 2079 2080 /* place for the subclasses to put their constructors so that the factory method 2081 * can find them. Do this to add your date after it's defined: 2082 * ilib.Date._constructors["mytype"] = ilib.Date.MyTypeConstructor; 2083 */ 2084 ilib.Date._constructors = {}; 2085 2086 ilib.Date.prototype = { 2087 getType: function() { 2088 return "ilib.Date"; 2089 }, 2090 2091 /** 2092 * Return the unix time equivalent to this date instance. Unix time is 2093 * the number of milliseconds since midnight on Jan 1, 1970 UTC (Gregorian). This 2094 * method only returns a valid number for dates between midnight, 2095 * Jan 1, 1970 UTC (Gregorian) and Jan 19, 2038 at 3:14:07am UTC (Gregorian) when 2096 * the unix time runs out. If this instance encodes a date outside of that range, 2097 * this method will return -1. For date types that are not Gregorian, the point 2098 * in time represented by this date object will only give a return value if it 2099 * is in the correct range in the Gregorian calendar as given previously. 2100 * 2101 * @return {number} a number giving the unix time, or -1 if the date is outside the 2102 * valid unix time range 2103 */ 2104 getTime: function() { 2105 return this.rd.getTime(); 2106 }, 2107 2108 /** 2109 * Return the extended unix time equivalent to this Gregorian date instance. Unix time is 2110 * the number of milliseconds since midnight on Jan 1, 1970 UTC. Traditionally unix time 2111 * (or the type "time_t" in C/C++) is only encoded with an unsigned 32 bit integer, and thus 2112 * runs out on Jan 19, 2038. However, most Javascript engines encode numbers well above 2113 * 32 bits and the Date object allows you to encode up to 100 million days worth of time 2114 * after Jan 1, 1970, and even more interestingly, 100 million days worth of time before 2115 * Jan 1, 1970 as well. This method returns the number of milliseconds in that extended 2116 * range. If this instance encodes a date outside of that range, this method will return 2117 * NaN. 2118 * 2119 * @return {number} a number giving the extended unix time, or Nan if the date is outside 2120 * the valid extended unix time range 2121 */ 2122 getTimeExtended: function() { 2123 return this.rd.getTimeExtended(); 2124 }, 2125 2126 /** 2127 * Set the time of this instance according to the given unix time. Unix time is 2128 * the number of milliseconds since midnight on Jan 1, 1970. 2129 * 2130 * @param {number} millis the unix time to set this date to in milliseconds 2131 */ 2132 setTime: function(millis) { 2133 this.rd = this.newRd({ 2134 unixtime: millis, 2135 cal: this.cal 2136 }); 2137 this._calcDateComponents(); 2138 }, 2139 2140 getDays: function() { 2141 return this.day; 2142 }, 2143 getMonths: function() { 2144 return this.month; 2145 }, 2146 getYears: function() { 2147 return this.year; 2148 }, 2149 getHours: function() { 2150 return this.hour; 2151 }, 2152 getMinutes: function() { 2153 return this.minute; 2154 }, 2155 getSeconds: function() { 2156 return this.second; 2157 }, 2158 getMilliseconds: function() { 2159 return this.millisecond; 2160 }, 2161 2162 setDays: function(day) { 2163 this.day = parseInt(day, 10) || 1; 2164 this.rd._setDateComponents(this); 2165 }, 2166 setMonths: function(month) { 2167 this.month = parseInt(month, 10) || 1; 2168 this.rd._setDateComponents(this); 2169 }, 2170 setYears: function(year) { 2171 this.year = parseInt(year, 10) || 0; 2172 this.rd._setDateComponents(this); 2173 }, 2174 2175 setHours: function(hour) { 2176 this.hour = parseInt(hour, 10) || 0; 2177 this.rd._setDateComponents(this); 2178 }, 2179 setMinutes: function(minute) { 2180 this.minute = parseInt(minute, 10) || 0; 2181 this.rd._setDateComponents(this); 2182 }, 2183 setSeconds: function(second) { 2184 this.second = parseInt(second, 10) || 0; 2185 this.rd._setDateComponents(this); 2186 }, 2187 setMilliseconds: function(milli) { 2188 this.millisecond = parseInt(milli, 10) || 0; 2189 this.rd._setDateComponents(this); 2190 }, 2191 2192 /** 2193 * Return a new date instance in the current calendar that represents the first instance 2194 * of the given day of the week before the current date. The day of the week is encoded 2195 * as a number where 0 = Sunday, 1 = Monday, etc. 2196 * 2197 * @param {number} dow the day of the week before the current date that is being sought 2198 * @return {ilib.Date} the date being sought 2199 */ 2200 before: function (dow) { 2201 return this.cal.newDateInstance({ 2202 rd: this.rd.before(dow, this.offset), 2203 timezone: this.timezone 2204 }); 2205 }, 2206 2207 /** 2208 * Return a new date instance in the current calendar that represents the first instance 2209 * of the given day of the week after the current date. The day of the week is encoded 2210 * as a number where 0 = Sunday, 1 = Monday, etc. 2211 * 2212 * @param {number} dow the day of the week after the current date that is being sought 2213 * @return {ilib.Date} the date being sought 2214 */ 2215 after: function (dow) { 2216 return this.cal.newDateInstance({ 2217 rd: this.rd.after(dow, this.offset), 2218 timezone: this.timezone 2219 }); 2220 }, 2221 2222 /** 2223 * Return a new Gregorian date instance that represents the first instance of the 2224 * given day of the week on or before the current date. The day of the week is encoded 2225 * as a number where 0 = Sunday, 1 = Monday, etc. 2226 * 2227 * @param {number} dow the day of the week on or before the current date that is being sought 2228 * @return {ilib.Date} the date being sought 2229 */ 2230 onOrBefore: function (dow) { 2231 return this.cal.newDateInstance({ 2232 rd: this.rd.onOrBefore(dow, this.offset), 2233 timezone: this.timezone 2234 }); 2235 }, 2236 2237 /** 2238 * Return a new Gregorian date instance that represents the first instance of the 2239 * given day of the week on or after the current date. The day of the week is encoded 2240 * as a number where 0 = Sunday, 1 = Monday, etc. 2241 * 2242 * @param {number} dow the day of the week on or after the current date that is being sought 2243 * @return {ilib.Date} the date being sought 2244 */ 2245 onOrAfter: function (dow) { 2246 return this.cal.newDateInstance({ 2247 rd: this.rd.onOrAfter(dow, this.offset), 2248 timezone: this.timezone 2249 }); 2250 }, 2251 2252 /** 2253 * Return a Javascript Date object that is equivalent to this date 2254 * object. 2255 * 2256 * @return {Date|undefined} a javascript Date object 2257 */ 2258 getJSDate: function() { 2259 var unix = this.rd.getTimeExtended(); 2260 return isNaN(unix) ? undefined : new Date(unix); 2261 }, 2262 2263 /** 2264 * Return the Rata Die (fixed day) number of this date. 2265 * 2266 * @protected 2267 * @return {number} the rd date as a number 2268 */ 2269 getRataDie: function() { 2270 return this.rd.getRataDie(); 2271 }, 2272 2273 /** 2274 * Set the date components of this instance based on the given rd. 2275 * @protected 2276 * @param {number} rd the rata die date to set 2277 */ 2278 setRd: function (rd) { 2279 this.rd = this.newRd({ 2280 rd: rd, 2281 cal: this.cal 2282 }); 2283 this._calcDateComponents(); 2284 }, 2285 2286 /** 2287 * Return the Julian Day equivalent to this calendar date as a number. 2288 * 2289 * @return {number} the julian date equivalent of this date 2290 */ 2291 getJulianDay: function() { 2292 return this.rd.getJulianDay(); 2293 }, 2294 2295 /** 2296 * Set the date of this instance using a Julian Day. 2297 * @param {number|ilib.JulianDay} date the Julian Day to use to set this date 2298 */ 2299 setJulianDay: function (date) { 2300 this.rd = this.newRd({ 2301 julianday: (typeof(date) === 'object') ? date.getDate() : date, 2302 cal: this.cal 2303 }); 2304 this._calcDateComponents(); 2305 }, 2306 2307 /** 2308 * Return the time zone associated with this date, or 2309 * undefined if none was specified in the constructor. 2310 * 2311 * @return {string|undefined} the name of the time zone for this date instance 2312 */ 2313 getTimeZone: function() { 2314 return this.timezone || "local"; 2315 }, 2316 2317 /** 2318 * Set the time zone associated with this date. 2319 * @param {string=} tzName the name of the time zone to set into this date instance, 2320 * or "undefined" to unset the time zone 2321 */ 2322 setTimeZone: function (tzName) { 2323 if (!tzName || tzName === "") { 2324 // same as undefining it 2325 this.timezone = undefined; 2326 this.tz = undefined; 2327 } else if (typeof(tzName) === 'string') { 2328 this.timezone = tzName; 2329 this.tz = undefined; 2330 // assuming the same UTC time, but a new time zone, now we have to 2331 // recalculate what the date components are 2332 this._calcDateComponents(); 2333 } 2334 }, 2335 2336 /** 2337 * Return the rd number of the first Sunday of the given ISO year. 2338 * @protected 2339 * @param {number} year the year for which the first Sunday is being sought 2340 * @return {number} the rd of the first Sunday of the ISO year 2341 */ 2342 firstSunday: function (year) { 2343 var firstDay = this.newRd({ 2344 year: year, 2345 month: 1, 2346 day: 1, 2347 hour: 0, 2348 minute: 0, 2349 second: 0, 2350 millisecond: 0, 2351 cal: this.cal 2352 }); 2353 var firstThu = this.newRd({ 2354 rd: firstDay.onOrAfter(4), 2355 cal: this.cal 2356 }); 2357 return firstThu.before(0); 2358 }, 2359 2360 /** 2361 * Return the ISO 8601 week number in the current year for the current date. The week 2362 * number ranges from 0 to 55, as some years have 55 weeks assigned to them in some 2363 * calendars. 2364 * 2365 * @return {number} the week number for the current date 2366 */ 2367 getWeekOfYear: function() { 2368 var rd = Math.floor(this.rd.getRataDie()); 2369 var year = this._calcYear(rd + this.offset); 2370 var yearStart = this.firstSunday(year); 2371 var nextYear; 2372 2373 // if we have a January date, it may be in this ISO year or the previous year 2374 if (rd < yearStart) { 2375 yearStart = this.firstSunday(year-1); 2376 } else { 2377 // if we have a late December date, it may be in this ISO year, or the next year 2378 nextYear = this.firstSunday(year+1); 2379 if (rd >= nextYear) { 2380 yearStart = nextYear; 2381 } 2382 } 2383 2384 return Math.floor((rd-yearStart)/7) + 1; 2385 }, 2386 2387 /** 2388 * Return the ordinal number of the week within the month. The first week of a month is 2389 * the first one that contains 4 or more days in that month. If any days precede this 2390 * first week, they are marked as being in week 0. This function returns values from 0 2391 * through 6.<p> 2392 * 2393 * The locale is a required parameter because different locales that use the same 2394 * Gregorian calendar consider different days of the week to be the beginning of 2395 * the week. This can affect the week of the month in which some days are located. 2396 * 2397 * @param {ilib.Locale|string} locale the locale or locale spec to use when figuring out 2398 * the first day of the week 2399 * @return {number} the ordinal number of the week within the current month 2400 */ 2401 getWeekOfMonth: function(locale) { 2402 var li = new ilib.LocaleInfo(locale); 2403 2404 var first = this.newRd({ 2405 year: this._calcYear(this.rd.getRataDie()+this.offset), 2406 month: this.getMonths(), 2407 day: 1, 2408 hour: 0, 2409 minute: 0, 2410 second: 0, 2411 millisecond: 0, 2412 cal: this.cal 2413 }); 2414 var weekStart = first.onOrAfter(li.getFirstDayOfWeek()); 2415 2416 if (weekStart - first.getRataDie() > 3) { 2417 // if the first week has 4 or more days in it of the current month, then consider 2418 // that week 1. Otherwise, it is week 0. To make it week 1, move the week start 2419 // one week earlier. 2420 weekStart -= 7; 2421 } 2422 return Math.floor((this.rd.getRataDie() - weekStart) / 7) + 1; 2423 } 2424 }; 2425 2426 /* 2427 * util/utils.js - Core utility routines 2428 * 2429 * Copyright © 2012-2014, JEDLSoft 2430 * 2431 * Licensed under the Apache License, Version 2.0 (the "License"); 2432 * you may not use this file except in compliance with the License. 2433 * You may obtain a copy of the License at 2434 * 2435 * http://www.apache.org/licenses/LICENSE-2.0 2436 * 2437 * Unless required by applicable law or agreed to in writing, software 2438 * distributed under the License is distributed on an "AS IS" BASIS, 2439 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2440 * 2441 * See the License for the specific language governing permissions and 2442 * limitations under the License. 2443 */ 2444 2445 // !depends ilibglobal.js 2446 2447 /** 2448 * If Function.prototype.bind does not exist in this JS engine, this 2449 * function reimplements it in terms of older JS functions. 2450 * bind() doesn't exist in many older browsers. 2451 * 2452 * @param {Object} scope object that the method should operate on 2453 * @param {function(...)} method method to call 2454 * @return {function(...)|undefined} function that calls the given method 2455 * in the given scope with all of its arguments properly attached, or 2456 * undefined if there was a problem with the arguments 2457 */ 2458 ilib.bind = function(scope, method/*, bound arguments*/){ 2459 if (!scope || !method) { 2460 return undefined; 2461 } 2462 2463 /** @protected 2464 * @param {Arguments} inArrayLike 2465 * @param {number=} inOffset 2466 */ 2467 function cloneArray(inArrayLike, inOffset) { 2468 var arr = []; 2469 for(var i = inOffset || 0, l = inArrayLike.length; i<l; i++){ 2470 arr.push(inArrayLike[i]); 2471 } 2472 return arr; 2473 } 2474 2475 if (typeof(method) === 'function') { 2476 var func, args = cloneArray(arguments, 2); 2477 if (typeof(method.bind) === 'function') { 2478 func = method.bind.apply(method, [scope].concat(args)); 2479 } else { 2480 func = function() { 2481 var nargs = cloneArray(arguments); 2482 // invoke with collected args 2483 return method.apply(scope, args.concat(nargs)); 2484 }; 2485 } 2486 return func; 2487 } 2488 return undefined; 2489 }; 2490 2491 /** 2492 * Merge the properties of object2 into object1 in a deep manner and return a merged 2493 * object. If the property exists in both objects, the value in object2 will overwrite 2494 * the value in object1. If a property exists in object1, but not in object2, its value 2495 * will not be touched. If a property exists in object2, but not in object1, it will be 2496 * added to the merged result.<p> 2497 * 2498 * Name1 and name2 are for creating debug output only. They are not necessary.<p> 2499 * 2500 * Depends directive: !depends utils.js 2501 * 2502 * @param {*} object1 the object to merge into 2503 * @param {*} object2 the object to merge 2504 * @param {boolean=} replace if true, replace the array elements in object1 with those in object2. 2505 * If false, concatenate array elements in object1 with items in object2. 2506 * @param {string=} name1 name of the object being merged into 2507 * @param {string=} name2 name of the object being merged in 2508 * @return {Object} the merged object 2509 */ 2510 ilib.merge = function (object1, object2, replace, name1, name2) { 2511 var prop = undefined, 2512 newObj = {}; 2513 for (prop in object1) { 2514 if (prop && typeof(object1[prop]) !== 'undefined') { 2515 newObj[prop] = object1[prop]; 2516 } 2517 } 2518 for (prop in object2) { 2519 if (prop && typeof(object2[prop]) !== 'undefined') { 2520 if (object1[prop] instanceof Array && object2[prop] instanceof Array) { 2521 if (typeof(replace) !== 'boolean' || !replace) { 2522 newObj[prop] = new Array(); 2523 newObj[prop] = newObj[prop].concat(object1[prop]); 2524 newObj[prop] = newObj[prop].concat(object2[prop]); 2525 } else { 2526 newObj[prop] = object2[prop]; 2527 } 2528 } else if (typeof(object1[prop]) === 'object' && typeof(object2[prop]) === 'object') { 2529 newObj[prop] = ilib.merge(object1[prop], object2[prop], replace); 2530 } else { 2531 // for debugging. Used to determine whether or not json files are overriding their parents unnecessarily 2532 if (name1 && name2 && newObj[prop] == object2[prop]) { 2533 console.log("Property " + prop + " in " + name1 + " is being overridden by the same value in " + name2); 2534 } 2535 newObj[prop] = object2[prop]; 2536 } 2537 } 2538 } 2539 return newObj; 2540 }; 2541 2542 /** 2543 * Find and merge all the locale data for a particular prefix in the given locale 2544 * and return it as a single javascript object. This merges the data in the 2545 * correct order: 2546 * 2547 * <ol> 2548 * <li>shared data (usually English) 2549 * <li>data for language 2550 * <li>data for language + region 2551 * <li>data for language + region + script 2552 * <li>data for language + region + script + variant 2553 * </ol> 2554 * 2555 * It is okay for any of the above to be missing. This function will just skip the 2556 * missing data. However, if everything except the shared data is missing, this 2557 * function returns undefined, allowing the caller to go and dynamically load the 2558 * data instead. 2559 * 2560 * @param {string} prefix prefix under ilib.data of the data to merge 2561 * @param {ilib.Locale} locale locale of the data being sought 2562 * @param {boolean=} replaceArrays if true, replace the array elements in object1 with those in object2. 2563 * If false, concatenate array elements in object1 with items in object2. 2564 * @param {boolean=} returnOne if true, only return the most locale-specific data. If false, 2565 * merge all the relevant locale data together. 2566 * @return {Object?} the merged locale data 2567 */ 2568 ilib.mergeLocData = function (prefix, locale, replaceArrays, returnOne) { 2569 var data = undefined; 2570 var loc = locale || new ilib.Locale(); 2571 var foundLocaleData = false; 2572 var property = prefix; 2573 var mostSpecific; 2574 2575 data = ilib.data[prefix] || {}; 2576 2577 mostSpecific = data; 2578 2579 if (loc.getLanguage()) { 2580 property = prefix + '_' + loc.getLanguage(); 2581 if (ilib.data[property]) { 2582 foundLocaleData = true; 2583 data = ilib.merge(data, ilib.data[property], replaceArrays); 2584 mostSpecific = ilib.data[property]; 2585 } 2586 } 2587 2588 if (loc.getRegion()) { 2589 property = prefix + '_' + loc.getRegion(); 2590 if (ilib.data[property]) { 2591 foundLocaleData = true; 2592 data = ilib.merge(data, ilib.data[property], replaceArrays); 2593 mostSpecific = ilib.data[property]; 2594 } 2595 } 2596 2597 if (loc.getLanguage()) { 2598 property = prefix + '_' + loc.getLanguage(); 2599 2600 if (loc.getScript()) { 2601 property = prefix + '_' + loc.getLanguage() + '_' + loc.getScript(); 2602 if (ilib.data[property]) { 2603 foundLocaleData = true; 2604 data = ilib.merge(data, ilib.data[property], replaceArrays); 2605 mostSpecific = ilib.data[property]; 2606 } 2607 } 2608 2609 if (loc.getRegion()) { 2610 property = prefix + '_' + loc.getLanguage() + '_' + loc.getRegion(); 2611 if (ilib.data[property]) { 2612 foundLocaleData = true; 2613 data = ilib.merge(data, ilib.data[property], replaceArrays); 2614 mostSpecific = ilib.data[property]; 2615 } 2616 } 2617 } 2618 2619 if (loc.getRegion() && loc.getVariant()) { 2620 property = prefix + '_' + loc.getLanguage() + '_' + loc.getVariant(); 2621 if (ilib.data[property]) { 2622 foundLocaleData = true; 2623 data = ilib.merge(data, ilib.data[property], replaceArrays); 2624 mostSpecific = ilib.data[property]; 2625 } 2626 } 2627 2628 if (loc.getLanguage() && loc.getScript() && loc.getRegion()) { 2629 property = prefix + '_' + loc.getLanguage() + '_' + loc.getScript() + '_' + loc.getRegion(); 2630 if (ilib.data[property]) { 2631 foundLocaleData = true; 2632 data = ilib.merge(data, ilib.data[property], replaceArrays); 2633 mostSpecific = ilib.data[property]; 2634 } 2635 } 2636 2637 if (loc.getLanguage() && loc.getRegion() && loc.getVariant()) { 2638 property = prefix + '_' + loc.getLanguage() + '_' + loc.getRegion() + '_' + loc.getVariant(); 2639 if (ilib.data[property]) { 2640 foundLocaleData = true; 2641 data = ilib.merge(data, ilib.data[property], replaceArrays); 2642 mostSpecific = ilib.data[property]; 2643 } 2644 } 2645 2646 if (loc.getLanguage() && loc.getScript() && loc.getRegion() && loc.getVariant()) { 2647 property = prefix + '_' + loc.getLanguage() + '_' + loc.getScript() + '_' + loc.getRegion() + '_' + loc.getVariant(); 2648 if (ilib.data[property]) { 2649 foundLocaleData = true; 2650 data = ilib.merge(data, ilib.data[property], replaceArrays); 2651 mostSpecific = ilib.data[property]; 2652 } 2653 } 2654 2655 return foundLocaleData ? (returnOne ? mostSpecific : data) : undefined; 2656 }; 2657 2658 /** 2659 * Return an array of relative path names for the 2660 * files that represent the data for the given locale.<p> 2661 * 2662 * Note that to prevent the situation where a directory for 2663 * a language exists next to the directory for a region where 2664 * the language code and region code differ only by case, the 2665 * plain region directories are located under the special 2666 * "undefined" language directory which has the ISO code "und". 2667 * The reason is that some platforms have case-insensitive 2668 * file systems, and you cannot have 2 directories with the 2669 * same name which only differ by case. For example, "es" is 2670 * the ISO 639 code for the language "Spanish" and "ES" is 2671 * the ISO 3166 code for the region "Spain", so both the 2672 * directories cannot exist underneath "locale". The region 2673 * therefore will be loaded from "und/ES" instead.<p> 2674 * 2675 * <h4>Variations</h4> 2676 * 2677 * With only language and region specified, the following 2678 * sequence of paths will be generated:<p> 2679 * 2680 * <pre> 2681 * language 2682 * und/region 2683 * language/region 2684 * </pre> 2685 * 2686 * With only language and script specified:<p> 2687 * 2688 * <pre> 2689 * language 2690 * language/script 2691 * </pre> 2692 * 2693 * With only script and region specified:<p> 2694 * 2695 * <pre> 2696 * und/region 2697 * </pre> 2698 * 2699 * With only region and variant specified:<p> 2700 * 2701 * <pre> 2702 * und/region 2703 * region/variant 2704 * </pre> 2705 * 2706 * With only language, script, and region specified:<p> 2707 * 2708 * <pre> 2709 * language 2710 * und/region 2711 * language/script 2712 * language/region 2713 * language/script/region 2714 * </pre> 2715 * 2716 * With only language, region, and variant specified:<p> 2717 * 2718 * <pre> 2719 * language 2720 * und/region 2721 * language/region 2722 * region/variant 2723 * language/region/variant 2724 * </pre> 2725 * 2726 * With all parts specified:<p> 2727 * 2728 * <pre> 2729 * language 2730 * und/region 2731 * language/script 2732 * language/region 2733 * region/variant 2734 * language/script/region 2735 * language/region/variant 2736 * language/script/region/variant 2737 * </pre> 2738 * 2739 * @param {ilib.Locale} locale load the files for this locale 2740 * @param {string?} name the file name of each file to load without 2741 * any path 2742 * @return {Array.<string>} An array of relative path names 2743 * for the files that contain the locale data 2744 */ 2745 ilib.getLocFiles = function(locale, name) { 2746 var dir = ""; 2747 var files = []; 2748 var filename = name || "resources.json"; 2749 var loc = locale || new ilib.Locale(); 2750 2751 var language = loc.getLanguage(); 2752 var region = loc.getRegion(); 2753 var script = loc.getScript(); 2754 var variant = loc.getVariant(); 2755 2756 files.push(filename); // generic shared file 2757 2758 if (language) { 2759 dir = language + "/"; 2760 files.push(dir + filename); 2761 } 2762 2763 if (region) { 2764 dir = "und/" + region + "/"; 2765 files.push(dir + filename); 2766 } 2767 2768 if (language) { 2769 if (script) { 2770 dir = language + "/" + script + "/"; 2771 files.push(dir + filename); 2772 } 2773 if (region) { 2774 dir = language + "/" + region + "/"; 2775 files.push(dir + filename); 2776 } 2777 } 2778 2779 if (region && variant) { 2780 dir = "und/" + region + "/" + variant + "/"; 2781 files.push(dir + filename); 2782 } 2783 2784 if (language && script && region) { 2785 dir = language + "/" + script + "/" + region + "/"; 2786 files.push(dir + filename); 2787 } 2788 2789 if (language && region && variant) { 2790 dir = language + "/" + region + "/" + variant + "/"; 2791 files.push(dir + filename); 2792 } 2793 2794 if (language && script && region && variant) { 2795 dir = language + "/" + script + "/" + region + "/" + variant + "/"; 2796 files.push(dir + filename); 2797 } 2798 2799 return files; 2800 }; 2801 2802 /** 2803 * Return true if the given object has no properties.<p> 2804 * 2805 * Depends directive: !depends utils.js 2806 * 2807 * @param {Object} obj the object to check 2808 * @return {boolean} true if the given object has no properties, false otherwise 2809 */ 2810 ilib.isEmpty = function (obj) { 2811 var prop = undefined; 2812 2813 if (!obj) { 2814 return true; 2815 } 2816 2817 for (prop in obj) { 2818 if (prop && typeof(obj[prop]) !== 'undefined') { 2819 return false; 2820 } 2821 } 2822 return true; 2823 }; 2824 2825 2826 /** 2827 * @private 2828 */ 2829 ilib.hashCode = function(obj) { 2830 var hash = 0; 2831 2832 function addHash(hash, newValue) { 2833 // co-prime numbers creates a nicely distributed hash 2834 hash *= 65543; 2835 hash += newValue; 2836 hash %= 2147483647; 2837 return hash; 2838 } 2839 2840 function stringHash(str) { 2841 var hash = 0; 2842 for (var i = 0; i < str.length; i++) { 2843 hash = addHash(hash, str.charCodeAt(i)); 2844 } 2845 return hash; 2846 } 2847 2848 switch (typeof(obj)) { 2849 case 'undefined': 2850 hash = 0; 2851 break; 2852 case 'string': 2853 hash = stringHash(obj); 2854 break; 2855 case 'function': 2856 case 'number': 2857 case 'xml': 2858 hash = stringHash(String(obj)); 2859 break; 2860 case 'boolean': 2861 hash = obj ? 1 : 0; 2862 break; 2863 case 'object': 2864 var props = []; 2865 for (var p in obj) { 2866 if (obj.hasOwnProperty(p)) { 2867 props.push(p); 2868 } 2869 } 2870 // make sure the order of the properties doesn't matter 2871 props.sort(); 2872 for (var i = 0; i < props.length; i++) { 2873 hash = addHash(hash, stringHash(props[i])); 2874 hash = addHash(hash, ilib.hashCode(obj[props[i]])); 2875 } 2876 break; 2877 } 2878 2879 return hash; 2880 }; 2881 2882 2883 /** 2884 * Load data using the new loader object or via the old function callback. 2885 * @private 2886 */ 2887 ilib._callLoadData = function (files, sync, params, callback) { 2888 // console.log("ilib._callLoadData called"); 2889 if (typeof(ilib._load) === 'function') { 2890 // console.log("ilib._callLoadData: calling as a regular function"); 2891 return ilib._load(files, sync, params, callback); 2892 } else if (typeof(ilib._load) === 'object' && ilib._load instanceof ilib.Loader) { 2893 // console.log("ilib._callLoadData: calling as an object"); 2894 return ilib._load.loadFiles(files, sync, params, callback); 2895 } 2896 2897 // console.log("ilib._callLoadData: not calling. Type is " + typeof(ilib._load) + " and instanceof says " + (ilib._load instanceof ilib.Loader)); 2898 return undefined; 2899 }; 2900 2901 /** 2902 * Find locale data or load it in. If the data with the given name is preassembled, it will 2903 * find the data in ilib.data. If the data is not preassembled but there is a loader function, 2904 * this function will call it to load the data. Otherwise, the callback will be called with 2905 * undefined as the data. This function will create a cache under the given class object. 2906 * If data was successfully loaded, it will be set into the cache so that future access to 2907 * the same data for the same locale is much quicker.<p> 2908 * 2909 * The parameters can specify any of the following properties:<p> 2910 * 2911 * <ul> 2912 * <li><i>name</i> - String. The name of the file being loaded. Default: resources.json 2913 * <li><i>object</i> - Object. The class attempting to load data. The cache is stored inside of here. 2914 * <li><i>locale</i> - ilib.Locale. The locale for which data is loaded. Default is the current locale. 2915 * <li><i>nonlocale</i> - boolean. If true, the data being loaded is not locale-specific. 2916 * <li><i>type</i> - String. Type of file to load. This can be "json" or "other" type. Default: "json" 2917 * <li><i>replace</i> - boolean. When merging json objects, this parameter controls whether to merge arrays 2918 * or have arrays replace each other. If true, arrays in child objects replace the arrays in parent 2919 * objects. When false, the arrays in child objects are concatenated with the arrays in parent objects. 2920 * <li><i>loadParams</i> - Object. An object with parameters to pass to the loader function 2921 * <li><i>sync</i> - boolean. Whether or not to load the data synchronously 2922 * <li><i>callback</i> - function(?)=. callback Call back function to call when the data is available. 2923 * Data is not returned from this method, so a callback function is mandatory. 2924 * </ul> 2925 * 2926 * @param {Object} params Parameters configuring how to load the files (see above) 2927 */ 2928 ilib.loadData = function(params) { 2929 var name = "resources.json", 2930 object = undefined, 2931 locale = new ilib.Locale(ilib.getLocale()), 2932 sync = false, 2933 type = undefined, 2934 loadParams = {}, 2935 callback = undefined, 2936 nonlocale = false, 2937 replace = false, 2938 basename; 2939 2940 if (!params || typeof(params.callback) !== 'function') { 2941 return; 2942 } 2943 2944 if (params.name) { 2945 name = params.name; 2946 } 2947 if (params.object) { 2948 object = params.object; 2949 } 2950 if (params.locale) { 2951 locale = (typeof(params.locale) === 'string') ? new ilib.Locale(params.locale) : params.locale; 2952 } 2953 if (params.type) { 2954 type = params.type; 2955 } 2956 if (params.loadParams) { 2957 loadParams = params.loadParams; 2958 } 2959 if (params.sync) { 2960 sync = params.sync; 2961 } 2962 if (params.nonlocale) { 2963 nonlocale = !!params.nonlocale; 2964 } 2965 if (typeof(params.replace) === 'boolean') { 2966 replace = params.replace; 2967 } 2968 2969 callback = params.callback; 2970 2971 if (object && !object.cache) { 2972 object.cache = {}; 2973 } 2974 2975 if (!type) { 2976 var dot = name.lastIndexOf("."); 2977 type = (dot !== -1) ? name.substring(dot+1) : "text"; 2978 } 2979 2980 var spec = ((!nonlocale && locale.getSpec().replace(/-/g, '_')) || "root") + "," + name + "," + String(ilib.hashCode(loadParams)); 2981 if (!object || typeof(object.cache[spec]) === 'undefined') { 2982 var data, returnOne = (loadParams && loadParams.returnOne); 2983 2984 if (type === "json") { 2985 // console.log("type is json"); 2986 basename = name.substring(0, name.lastIndexOf(".")); 2987 if (nonlocale) { 2988 basename = basename.replace(/\//g, '.').replace(/[\\\+\-]/g, "_"); 2989 data = ilib.data[basename]; 2990 } else { 2991 data = ilib.mergeLocData(basename, locale, replace, returnOne); 2992 } 2993 if (data) { 2994 // console.log("found assembled data"); 2995 if (object) { 2996 object.cache[spec] = data; 2997 } 2998 callback(data); 2999 return; 3000 } 3001 } 3002 3003 // console.log("ilib._load is " + typeof(ilib._load)); 3004 if (typeof(ilib._load) !== 'undefined') { 3005 // the data is not preassembled, so attempt to load it dynamically 3006 var files = nonlocale ? [ name || "resources.json" ] : ilib.getLocFiles(locale, name); 3007 if (type !== "json") { 3008 loadParams.returnOne = true; 3009 } 3010 3011 ilib._callLoadData(files, sync, loadParams, ilib.bind(this, function(arr) { 3012 if (type === "json") { 3013 data = ilib.data[basename] || {}; 3014 for (var i = 0; i < arr.length; i++) { 3015 if (typeof(arr[i]) !== 'undefined') { 3016 data = loadParams.returnOne ? arr[i] : ilib.merge(data, arr[i], replace); 3017 } 3018 } 3019 3020 if (object) { 3021 object.cache[spec] = data; 3022 } 3023 callback(data); 3024 } else { 3025 var i = arr.length-1; 3026 while (i > -1 && !arr[i]) { 3027 i--; 3028 } 3029 if (i > -1) { 3030 if (object) { 3031 object.cache[spec] = arr[i]; 3032 } 3033 callback(arr[i]); 3034 } else { 3035 callback(undefined); 3036 } 3037 } 3038 })); 3039 } else { 3040 // no data other than the generic shared data 3041 if (type === "json") { 3042 data = ilib.data[basename]; 3043 } 3044 if (object && data) { 3045 object.cache[spec] = data; 3046 } 3047 callback(data); 3048 } 3049 } else { 3050 callback(object.cache[spec]); 3051 } 3052 }; 3053 3054 /* 3055 * util/math.js - Misc math utility routines 3056 * 3057 * Copyright © 2013, JEDLSoft 3058 * 3059 * Licensed under the Apache License, Version 2.0 (the "License"); 3060 * you may not use this file except in compliance with the License. 3061 * You may obtain a copy of the License at 3062 * 3063 * http://www.apache.org/licenses/LICENSE-2.0 3064 * 3065 * Unless required by applicable law or agreed to in writing, software 3066 * distributed under the License is distributed on an "AS IS" BASIS, 3067 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3068 * 3069 * See the License for the specific language governing permissions and 3070 * limitations under the License. 3071 */ 3072 3073 // !depends ilibglobal.js 3074 3075 /** 3076 * Return the sign of the given number. If the sign is negative, this function 3077 * returns -1. If the sign is positive or zero, this function returns 1. 3078 * @static 3079 * @param {number} num the number to test 3080 * @return {number} -1 if the number is negative, and 1 otherwise 3081 */ 3082 ilib.signum = function (num) { 3083 var n = num; 3084 if (typeof(num) === 'string') { 3085 n = parseInt(num, 10); 3086 } else if (typeof(num) !== 'number') { 3087 return 1; 3088 } 3089 return (n < 0) ? -1 : 1; 3090 }; 3091 3092 3093 /** 3094 * @protected 3095 */ 3096 ilib._roundFnc = { 3097 /** 3098 * @static 3099 * @protected 3100 * @param {number} num number to round 3101 * @return {number} rounded number 3102 */ 3103 floor: function (num) { 3104 return Math.floor(num); 3105 }, 3106 3107 /** 3108 * @static 3109 * @protected 3110 * @param {number} num number to round 3111 * @return {number} rounded number 3112 */ 3113 ceiling: function (num) { 3114 return Math.ceil(num); 3115 }, 3116 3117 /** 3118 * @static 3119 * @protected 3120 * @param {number} num number to round 3121 * @return {number} rounded number 3122 */ 3123 down: function (num) { 3124 return (num < 0) ? Math.ceil(num) : Math.floor(num); 3125 }, 3126 3127 /** 3128 * @static 3129 * @protected 3130 * @param {number} num number to round 3131 * @return {number} rounded number 3132 */ 3133 up: function (num) { 3134 return (num < 0) ? Math.floor(num) : Math.ceil(num); 3135 }, 3136 3137 /** 3138 * @static 3139 * @protected 3140 * @param {number} num number to round 3141 * @return {number} rounded number 3142 */ 3143 halfup: function (num) { 3144 return (num < 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5); 3145 }, 3146 3147 /** 3148 * @static 3149 * @protected 3150 * @param {number} num number to round 3151 * @return {number} rounded number 3152 */ 3153 halfdown: function (num) { 3154 return (num < 0) ? Math.floor(num + 0.5) : Math.ceil(num - 0.5); 3155 }, 3156 3157 /** 3158 * @static 3159 * @protected 3160 * @param {number} num number to round 3161 * @return {number} rounded number 3162 */ 3163 halfeven: function (num) { 3164 return (Math.floor(num) % 2 === 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5); 3165 }, 3166 3167 /** 3168 * @static 3169 * @protected 3170 * @param {number} num number to round 3171 * @return {number} rounded number 3172 */ 3173 halfodd: function (num) { 3174 return (Math.floor(num) % 2 !== 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5); 3175 } 3176 }; 3177 3178 /** 3179 * Do a proper modulo function. The Javascript % operator will give the truncated 3180 * division algorithm, but for calendrical calculations, we need the Euclidean 3181 * division algorithm where the remainder of any division, whether the dividend 3182 * is negative or not, is always a positive number in the range [0, modulus).<p> 3183 * 3184 * Depends directive: !depends utils.js 3185 * 3186 * @param {number} dividend the number being divided 3187 * @param {number} modulus the number dividing the dividend. This should always be a positive number. 3188 * @return the remainder of dividing the dividend by the modulus. 3189 */ 3190 ilib.mod = function (dividend, modulus) { 3191 if (modulus == 0) { 3192 return 0; 3193 } 3194 var x = dividend % modulus; 3195 return (x < 0) ? x + modulus : x; 3196 }; 3197 3198 /** 3199 * Do a proper adjusted modulo function. The Javascript % operator will give the truncated 3200 * division algorithm, but for calendrical calculations, we need the Euclidean 3201 * division algorithm where the remainder of any division, whether the dividend 3202 * is negative or not, is always a positive number in the range (0, modulus]. The adjusted 3203 * modulo function differs from the regular modulo function in that when the remainder is 3204 * zero, the modulus should be returned instead.<p> 3205 * 3206 * Depends directive: !depends utils.js 3207 * 3208 * @param {number} dividend the number being divided 3209 * @param {number} modulus the number dividing the dividend. This should always be a positive number. 3210 * @return the remainder of dividing the dividend by the modulus. 3211 */ 3212 ilib.amod = function (dividend, modulus) { 3213 if (modulus == 0) { 3214 return 0; 3215 } 3216 var x = dividend % modulus; 3217 return (x <= 0) ? x + modulus : x; 3218 }; 3219 3220 /* 3221 * strings.js - ilib string subclass definition 3222 * 3223 * Copyright © 2012-2014, JEDLSoft 3224 * 3225 * Licensed under the Apache License, Version 2.0 (the "License"); 3226 * you may not use this file except in compliance with the License. 3227 * You may obtain a copy of the License at 3228 * 3229 * http://www.apache.org/licenses/LICENSE-2.0 3230 * 3231 * Unless required by applicable law or agreed to in writing, software 3232 * distributed under the License is distributed on an "AS IS" BASIS, 3233 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3234 * 3235 * See the License for the specific language governing permissions and 3236 * limitations under the License. 3237 */ 3238 3239 // !depends ilibglobal.js util/utils.js locale.js util/math.js 3240 3241 // !data plurals 3242 3243 /** 3244 * @class 3245 * Create a new string instance. This string inherits from the Javascript 3246 * String class, and adds two more methods, fmt and fmtChoice. It can be 3247 * used anywhere that a normal Javascript string is used. The formatting 3248 * methods are of course most useful when localizing strings in an app 3249 * or web site in combination with the ilib.ResBundle class.<p> 3250 * 3251 * Depends directive: !depends strings.js 3252 * 3253 * @constructor 3254 * @param {string|ilib.String=} string initialize this instance with this string 3255 */ 3256 ilib.String = function (string) { 3257 if (typeof(string) === 'object') { 3258 if (string instanceof ilib.String) { 3259 this.str = string.str; 3260 } else { 3261 this.str = string.toString(); 3262 } 3263 } else if (typeof(string) === 'string') { 3264 this.str = new String(string); 3265 } else { 3266 this.str = ""; 3267 } 3268 this.length = this.str.length; 3269 this.cpLength = -1; 3270 this.localeSpec = ilib.getLocale(); 3271 }; 3272 3273 /** 3274 * Return true if the given character is a Unicode surrogate character, 3275 * either high or low. 3276 * 3277 * @private 3278 * @static 3279 * @param {string} ch character to check 3280 * @return {boolean} true if the character is a surrogate 3281 */ 3282 ilib.String._isSurrogate = function (ch) { 3283 var n = ch.charCodeAt(0); 3284 return ((n >= 0xDC00 && n <= 0xDFFF) || (n >= 0xD800 && n <= 0xDBFF)); 3285 }; 3286 3287 /** 3288 * Convert a UCS-4 code point to a Javascript string. The codepoint can be any valid 3289 * UCS-4 Unicode character, including supplementary characters. Standard Javascript 3290 * only supports supplementary characters using the UTF-16 encoding, which has 3291 * values in the range 0x0000-0xFFFF. String.fromCharCode() will only 3292 * give you a string containing 16-bit characters, and will not properly convert 3293 * the code point for a supplementary character (which has a value > 0xFFFF) into 3294 * two UTF-16 surrogate characters. Instead, it will just just give you whatever 3295 * single character happens to be the same as your code point modulo 0x10000, which 3296 * is almost never what you want.<p> 3297 * 3298 * Similarly, that means if you use String.charCodeAt() 3299 * you will only retrieve a 16-bit value, which may possibly be a single 3300 * surrogate character that is part of a surrogate pair representing a character 3301 * in the supplementary plane. It will not give you a code point. Use 3302 * ilib.String.codePointAt() to access code points in a string, or use 3303 * an iterator to walk through the code points in a string. 3304 * 3305 * @static 3306 * @param {number} codepoint UCS-4 code point to convert to a character 3307 * @return {string} a string containing the character represented by the codepoint 3308 */ 3309 ilib.String.fromCodePoint = function (codepoint) { 3310 if (codepoint < 0x10000) { 3311 return String.fromCharCode(codepoint); 3312 } else { 3313 var high = Math.floor(codepoint / 0x10000) - 1; 3314 var low = codepoint & 0xFFFF; 3315 3316 return String.fromCharCode(0xD800 | ((high & 0x000F) << 6) | ((low & 0xFC00) >> 10)) + 3317 String.fromCharCode(0xDC00 | (low & 0x3FF)); 3318 } 3319 }; 3320 3321 /** 3322 * Convert the character or the surrogate pair at the given 3323 * index into the intrinsic Javascript string to a Unicode 3324 * UCS-4 code point. 3325 * 3326 * @param {string} str string to get the code point from 3327 * @param {number} index index into the string 3328 * @return {number} code point of the character at the 3329 * given index into the string 3330 */ 3331 ilib.String.toCodePoint = function(str, index) { 3332 if (!str || str.length === 0) { 3333 return -1; 3334 } 3335 var code = -1, high = str.charCodeAt(index); 3336 if (high >= 0xD800 && high <= 0xDBFF) { 3337 if (str.length > index+1) { 3338 var low = str.charCodeAt(index+1); 3339 if (low >= 0xDC00 && low <= 0xDFFF) { 3340 code = (((high & 0x3C0) >> 6) + 1) << 16 | 3341 (((high & 0x3F) << 10) | (low & 0x3FF)); 3342 } 3343 } 3344 } else { 3345 code = high; 3346 } 3347 3348 return code; 3349 }; 3350 3351 /** 3352 * Load the plural the definitions of plurals for the locale. 3353 * @param {boolean=} sync 3354 * @param {ilib.Locale|string=} locale 3355 * @param {Object=} loadParams 3356 * @param {function(*)=} onLoad 3357 */ 3358 ilib.String.loadPlurals = function (sync, locale, loadParams, onLoad) { 3359 var loc; 3360 if (locale) { 3361 loc = (typeof(locale) === 'string') ? new ilib.Locale(locale) : locale; 3362 } else { 3363 loc = new ilib.Locale(ilib.getLocale()); 3364 } 3365 var spec = loc.getLanguage(); 3366 if (!ilib.data["plurals_" + spec]) { 3367 ilib.loadData({ 3368 name: "plurals.json", 3369 object: ilib.String, 3370 locale: loc, 3371 sync: sync, 3372 loadParams: loadParams, 3373 callback: /** @type function(Object=):undefined */ ilib.bind(this, /** @type function() */ function(plurals) { 3374 if (!plurals) { 3375 ilib.String.cache[spec] = {}; 3376 } 3377 ilib.data["plurals_" + spec] = plurals || {}; 3378 if (onLoad && typeof(onLoad) === 'function') { 3379 onLoad(ilib.data["plurals_" + spec]); 3380 } 3381 }) 3382 }); 3383 } else { 3384 if (onLoad && typeof(onLoad) === 'function') { 3385 onLoad(ilib.data["plurals_" + spec]); 3386 } 3387 } 3388 }; 3389 3390 /** 3391 * @private 3392 * @static 3393 */ 3394 ilib.String._fncs = { 3395 /** 3396 * @private 3397 * @param {Object} obj 3398 * @return {string|undefined} 3399 */ 3400 firstProp: function (obj) { 3401 for (var p in obj) { 3402 if (p && obj[p]) { 3403 return p; 3404 } 3405 } 3406 return undefined; // should never get here 3407 }, 3408 3409 /** 3410 * @private 3411 * @param {Object} obj 3412 * @param {number} n 3413 * @return {?} 3414 */ 3415 getValue: function (obj, n) { 3416 if (typeof(obj) === 'object') { 3417 var subrule = ilib.String._fncs.firstProp(obj); 3418 return ilib.String._fncs[subrule](obj[subrule], n); 3419 } else if (typeof(obj) === 'string') { 3420 return n; 3421 } else { 3422 return obj; 3423 } 3424 }, 3425 3426 /** 3427 * @private 3428 * @param {number} n 3429 * @param {Array.<number|Array.<number>>} range 3430 * @return {boolean} 3431 */ 3432 matchRangeContinuous: function(n, range) { 3433 for (var num in range) { 3434 if (typeof(num) !== 'undefined' && typeof(range[num]) !== 'undefined') { 3435 var obj = /** @type {Object|null|undefined} */ range[num]; 3436 if (typeof(obj) === 'number') { 3437 if (n === range[num]) { 3438 return true; 3439 } 3440 } else if (Object.prototype.toString.call(obj) === '[object Array]') { 3441 if (n >= obj[0] && n <= obj[1]) { 3442 return true; 3443 } 3444 } 3445 } 3446 } 3447 return false; 3448 }, 3449 3450 /** 3451 * @private 3452 * @param {number} n 3453 * @param {Array.<number|Array.<number>>} range 3454 * @return {boolean} 3455 */ 3456 matchRange: function(n, range) { 3457 if (Math.floor(n) !== n) { 3458 return false; 3459 } 3460 return ilib.String._fncs.matchRangeContinuous(n, range); 3461 }, 3462 3463 /** 3464 * @private 3465 * @param {Object} rule 3466 * @param {number} n 3467 * @return {boolean} 3468 */ 3469 is: function(rule, n) { 3470 var left = ilib.String._fncs.getValue(rule[0], n); 3471 var right = ilib.String._fncs.getValue(rule[1], n); 3472 return left == right; 3473 // return ilib.String._fncs.getValue(rule[0]) == ilib.String._fncs.getValue(rule[1]); 3474 }, 3475 3476 /** 3477 * @private 3478 * @param {Object} rule 3479 * @param {number} n 3480 * @return {boolean} 3481 */ 3482 isnot: function(rule, n) { 3483 return ilib.String._fncs.getValue(rule[0], n) != ilib.String._fncs.getValue(rule[1], n); 3484 }, 3485 3486 /** 3487 * @private 3488 * @param {Object} rule 3489 * @param {number} n 3490 * @return {boolean} 3491 */ 3492 inrange: function(rule, n) { 3493 return ilib.String._fncs.matchRange(ilib.String._fncs.getValue(rule[0], n), rule[1]); 3494 }, 3495 3496 /** 3497 * @private 3498 * @param {Object} rule 3499 * @param {number} n 3500 * @return {boolean} 3501 */ 3502 notin: function(rule, n) { 3503 return !ilib.String._fncs.matchRange(ilib.String._fncs.getValue(rule[0], n), rule[1]); 3504 }, 3505 3506 /** 3507 * @private 3508 * @param {Object} rule 3509 * @param {number} n 3510 * @return {boolean} 3511 */ 3512 within: function(rule, n) { 3513 return ilib.String._fncs.matchRangeContinuous(ilib.String._fncs.getValue(rule[0], n), rule[1]); 3514 }, 3515 3516 /** 3517 * @private 3518 * @param {Object} rule 3519 * @param {number} n 3520 * @return {number} 3521 */ 3522 mod: function(rule, n) { 3523 return ilib.mod(ilib.String._fncs.getValue(rule[0], n), ilib.String._fncs.getValue(rule[1], n)); 3524 }, 3525 3526 /** 3527 * @private 3528 * @param {Object} rule 3529 * @param {number} n 3530 * @return {number} 3531 */ 3532 n: function(rule, n) { 3533 return n; 3534 }, 3535 3536 /** 3537 * @private 3538 * @param {Object} rule 3539 * @param {number} n 3540 * @return {boolean} 3541 */ 3542 or: function(rule, n) { 3543 return ilib.String._fncs.getValue(rule[0], n) || ilib.String._fncs.getValue(rule[1], n); 3544 }, 3545 3546 /** 3547 * @private 3548 * @param {Object} rule 3549 * @param {number} n 3550 * @return {boolean} 3551 */ 3552 and: function(rule, n) { 3553 return ilib.String._fncs.getValue(rule[0], n) && ilib.String._fncs.getValue(rule[1], n); 3554 } 3555 }; 3556 3557 ilib.String.prototype = { 3558 /** 3559 * Return the length of this string in characters. This function defers to the regular 3560 * Javascript string class in order to perform the length function. Please note that this 3561 * method is a real method, whereas the length property of Javascript strings is 3562 * implemented by native code and appears as a property.<p> 3563 * 3564 * Example: 3565 * 3566 * <pre> 3567 * var str = new ilib.String("this is a string"); 3568 * console.log("String is " + str._length() + " characters long."); 3569 * </pre> 3570 * @private 3571 */ 3572 _length: function () { 3573 return this.str.length; 3574 }, 3575 3576 /** 3577 * Format this string instance as a message, replacing the parameters with 3578 * the given values.<p> 3579 * 3580 * The string can contain any text that a regular Javascript string can 3581 * contain. Replacement parameters have the syntax: 3582 * 3583 * <pre> 3584 * {name} 3585 * </pre> 3586 * 3587 * Where "name" can be any string surrounded by curly brackets. The value of 3588 * "name" is taken from the parameters argument.<p> 3589 * 3590 * Example: 3591 * 3592 * <pre> 3593 * var str = new ilib.String("There are {num} objects."); 3594 * console.log(str.format({ 3595 * num: 12 3596 * }); 3597 * </pre> 3598 * 3599 * Would give the output: 3600 * 3601 * <pre> 3602 * There are 12 objects. 3603 * </pre> 3604 * 3605 * If a property is missing from the parameter block, the replacement 3606 * parameter substring is left untouched in the string, and a different 3607 * set of parameters may be applied a second time. This way, different 3608 * parts of the code may format different parts of the message that they 3609 * happen to know about.<p> 3610 * 3611 * Example: 3612 * 3613 * <pre> 3614 * var str = new ilib.String("There are {num} objects in the {container}."); 3615 * console.log(str.format({ 3616 * num: 12 3617 * }); 3618 * </pre> 3619 * 3620 * Would give the output:<p> 3621 * 3622 * <pre> 3623 * There are 12 objects in the {container}. 3624 * </pre> 3625 * 3626 * The result can then be formatted again with a different parameter block that 3627 * specifies a value for the container property. 3628 * 3629 * @param params a Javascript object containing values for the replacement 3630 * parameters in the current string 3631 * @return a new ilib.String instance with as many replacement parameters filled 3632 * out as possible with real values. 3633 */ 3634 format: function (params) { 3635 var formatted = this.str; 3636 if (params) { 3637 var regex; 3638 for (var p in params) { 3639 if (typeof(params[p]) !== 'undefined') { 3640 regex = new RegExp("\{"+p+"\}", "g"); 3641 formatted = formatted.replace(regex, params[p]); 3642 } 3643 } 3644 } 3645 return formatted.toString(); 3646 }, 3647 3648 /** 3649 * Format a string as one of a choice of strings dependent on the value of 3650 * a particular argument index.<p> 3651 * 3652 * The syntax of the choice string is as follows. The string contains a 3653 * series of choices separated by a vertical bar character "|". Each choice 3654 * has a value or range of values to match followed by a hash character "#" 3655 * followed by the string to use if the variable matches the criteria.<p> 3656 * 3657 * Example string: 3658 * 3659 * <pre> 3660 * var num = 2; 3661 * var str = new ilib.String("0#There are no objects.|1#There is one object.|2#There are {number} objects."); 3662 * console.log(str.formatChoice(num, { 3663 * number: num 3664 * })); 3665 * </pre> 3666 * 3667 * Gives the output: 3668 * 3669 * <pre> 3670 * "There are 2 objects." 3671 * </pre> 3672 * 3673 * The strings to format may contain replacement variables that will be formatted 3674 * using the format() method above and the params argument as a source of values 3675 * to use while formatting those variables.<p> 3676 * 3677 * If the criterion for a particular choice is empty, that choice will be used 3678 * as the default one for use when none of the other choice's criteria match.<p> 3679 * 3680 * Example string: 3681 * 3682 * <pre> 3683 * var num = 22; 3684 * var str = new ilib.String("0#There are no objects.|1#There is one object.|#There are {number} objects."); 3685 * console.log(str.formatChoice(num, { 3686 * number: num 3687 * })); 3688 * </pre> 3689 * 3690 * Gives the output: 3691 * 3692 * <pre> 3693 * "There are 22 objects." 3694 * </pre> 3695 * 3696 * If multiple choice patterns can match a given argument index, the first one 3697 * encountered in the string will be used. If no choice patterns match the 3698 * argument index, then the default choice will be used. If there is no default 3699 * choice defined, then this method will return an empty string.<p> 3700 * 3701 * <b>Special Syntax</b><p> 3702 * 3703 * For any choice format string, all of the patterns in the string should be 3704 * of a single type: numeric, boolean, or string/regexp. The type of the 3705 * patterns is determined by the type of the argument index parameter.<p> 3706 * 3707 * If the argument index is numeric, then some special syntax can be used 3708 * in the patterns to match numeric ranges.<p> 3709 * 3710 * <ul> 3711 * <li><i>>x</i> - match any number that is greater than x 3712 * <li><i>>=x</i> - match any number that is greater than or equal to x 3713 * <li><i><x</i> - match any number that is less than x 3714 * <li><i><=x</i> - match any number that is less than or equal to x 3715 * <li><i>start-end</i> - match any number in the range [start,end) 3716 * <li><i>zero</i> - match any number in the class "zero". (See below for 3717 * a description of number classes.) 3718 * <li><i>one</i> - match any number in the class "one" 3719 * <li><i>two</i> - match any number in the class "two" 3720 * <li><i>few</i> - match any number in the class "few" 3721 * <li><i>many</i> - match any number in the class "many" 3722 * </ul> 3723 * 3724 * A number class defines a set of numbers that receive a particular syntax 3725 * in the strings. For example, in Slovenian, integers ending in the digit 3726 * "1" are in the "one" class, including 1, 21, 31, ... 101, 111, etc. 3727 * Similarly, integers ending in the digit "2" are in the "two" class. 3728 * Integers ending in the digits "3" or "4" are in the "few" class, and 3729 * every other integer is handled by the default string.<p> 3730 * 3731 * The definition of what numbers are included in a class is locale-dependent. 3732 * They are defined in the data file plurals.json. If your string is in a 3733 * different locale than the default for ilib, you should call the setLocale() 3734 * method of the string instance before calling this method.<p> 3735 * 3736 * <b>Other Pattern Types</b><p> 3737 * 3738 * If the argument index is a boolean, the string values "true" and "false" 3739 * may appear as the choice patterns.<p> 3740 * 3741 * If the argument index is of type string, then the choice patterns may contain 3742 * regular expressions, or static strings as degenerate regexps. 3743 * 3744 * @param {*} argIndex The index into the choice array of the current parameter 3745 * @param {Object} params The hash of parameter values that replace the replacement 3746 * variables in the string 3747 * @throws "syntax error in choice format pattern: " if there is a syntax error 3748 * @return {string} the formatted string 3749 */ 3750 formatChoice: function(argIndex, params) { 3751 var choices = this.str.split("|"); 3752 var type = typeof(argIndex); 3753 var limits = []; 3754 var strings = []; 3755 var i; 3756 var parts; 3757 var limit; 3758 var arg; 3759 var result = undefined; 3760 var defaultCase = ""; 3761 3762 if (this.str.length === 0) { 3763 // nothing to do 3764 return ""; 3765 } 3766 3767 // first parse all the choices 3768 for (i = 0; i < choices.length; i++) { 3769 parts = choices[i].split("#"); 3770 if (parts.length > 2) { 3771 limits[i] = parts[0]; 3772 parts = parts.shift(); 3773 strings[i] = parts.join("#"); 3774 } else if (parts.length === 2) { 3775 limits[i] = parts[0]; 3776 strings[i] = parts[1]; 3777 } else { 3778 // syntax error 3779 throw "syntax error in choice format pattern: " + choices[i]; 3780 } 3781 } 3782 3783 // then apply the argument index 3784 for (i = 0; i < limits.length; i++) { 3785 if (limits[i].length === 0) { 3786 // this is default case 3787 defaultCase = new ilib.String(strings[i]); 3788 } else { 3789 switch (type) { 3790 case 'number': 3791 arg = parseInt(argIndex, 10); 3792 3793 if (limits[i].substring(0,2) === "<=") { 3794 limit = parseFloat(limits[i].substring(2)); 3795 if (arg <= limit) { 3796 result = new ilib.String(strings[i]); 3797 i = limits.length; 3798 } 3799 } else if (limits[i].substring(0,2) === ">=") { 3800 limit = parseFloat(limits[i].substring(2)); 3801 if (arg >= limit) { 3802 result = new ilib.String(strings[i]); 3803 i = limits.length; 3804 } 3805 } else if (limits[i].charAt(0) === "<") { 3806 limit = parseFloat(limits[i].substring(1)); 3807 if (arg < limit) { 3808 result = new ilib.String(strings[i]); 3809 i = limits.length; 3810 } 3811 } else if (limits[i].charAt(0) === ">") { 3812 limit = parseFloat(limits[i].substring(1)); 3813 if (arg > limit) { 3814 result = new ilib.String(strings[i]); 3815 i = limits.length; 3816 } 3817 } else { 3818 this.locale = this.locale || new ilib.Locale(this.localeSpec); 3819 switch (limits[i]) { 3820 case "zero": 3821 case "one": 3822 case "two": 3823 case "few": 3824 case "many": 3825 // CLDR locale-dependent number classes 3826 var ruleset = ilib.data["plurals_" + this.locale.getLanguage()]; 3827 if (ruleset) { 3828 var rule = ruleset[limits[i]]; 3829 if (ilib.String._fncs.getValue(rule, arg)) { 3830 result = new ilib.String(strings[i]); 3831 i = limits.length; 3832 } 3833 } 3834 break; 3835 default: 3836 var dash = limits[i].indexOf("-"); 3837 if (dash !== -1) { 3838 // range 3839 var start = limits[i].substring(0, dash); 3840 var end = limits[i].substring(dash+1); 3841 if (arg >= parseInt(start, 10) && arg <= parseInt(end, 10)) { 3842 result = new ilib.String(strings[i]); 3843 i = limits.length; 3844 } 3845 } else if (arg === parseInt(limits[i], 10)) { 3846 // exact amount 3847 result = new ilib.String(strings[i]); 3848 i = limits.length; 3849 } 3850 break; 3851 } 3852 } 3853 break; 3854 case 'boolean': 3855 if (limits[i] === "true" && argIndex === true) { 3856 result = new ilib.String(strings[i]); 3857 i = limits.length; 3858 } else if (limits[i] === "false" && argIndex === false) { 3859 result = new ilib.String(strings[i]); 3860 i = limits.length; 3861 } 3862 break; 3863 case 'string': 3864 var regexp = new RegExp(limits[i], "i"); 3865 if (regexp.test(argIndex)) { 3866 result = new ilib.String(strings[i]); 3867 i = limits.length; 3868 } 3869 break; 3870 case 'object': 3871 throw "syntax error: fmtChoice parameter for the argument index cannot be an object"; 3872 } 3873 } 3874 } 3875 3876 if (!result) { 3877 result = defaultCase || new ilib.String(""); 3878 } 3879 3880 result = result.format(params); 3881 3882 return result.toString(); 3883 }, 3884 3885 // delegates 3886 /** 3887 * Same as String.toString() 3888 * @return {string} this instance as regular Javascript string 3889 */ 3890 toString: function () { 3891 return this.str.toString(); 3892 }, 3893 3894 /** 3895 * Same as String.valueOf() 3896 * @return {string} this instance as a regular Javascript string 3897 */ 3898 valueOf: function () { 3899 return this.str.valueOf(); 3900 }, 3901 3902 /** 3903 * Same as String.charAt() 3904 * @param {number} index the index of the character being sought 3905 * @return {ilib.String} the character at the given index 3906 */ 3907 charAt: function(index) { 3908 return new ilib.String(this.str.charAt(index)); 3909 }, 3910 3911 /** 3912 * Same as String.charCodeAt(). This only reports on 3913 * 2-byte UCS-2 Unicode values, and does not take into 3914 * account supplementary characters encoded in UTF-16. 3915 * If you would like to take account of those characters, 3916 * use codePointAt() instead. 3917 * @param {number} index the index of the character being sought 3918 * @return {number} the character code of the character at the 3919 * given index in the string 3920 */ 3921 charCodeAt: function(index) { 3922 return this.str.charCodeAt(index); 3923 }, 3924 3925 /** 3926 * Same as String.concat() 3927 * @param {string} strings strings to concatenate to the current one 3928 * @return {ilib.String} a concatenation of the given strings 3929 */ 3930 concat: function(strings) { 3931 return new ilib.String(this.str.concat(strings)); 3932 }, 3933 3934 /** 3935 * Same as String.indexOf() 3936 * @param {string} searchValue string to search for 3937 * @param {number} start index into the string to start searching, or 3938 * undefined to search the entire string 3939 * @return {number} index into the string of the string being sought, 3940 * or -1 if the string is not found 3941 */ 3942 indexOf: function(searchValue, start) { 3943 return this.str.indexOf(searchValue, start); 3944 }, 3945 3946 /** 3947 * Same as String.lastIndexOf() 3948 * @param {string} searchValue string to search for 3949 * @param {number} start index into the string to start searching, or 3950 * undefined to search the entire string 3951 * @return {number} index into the string of the string being sought, 3952 * or -1 if the string is not found 3953 */ 3954 lastIndexOf: function(searchValue, start) { 3955 return this.str.lastIndexOf(searchValue, start); 3956 }, 3957 3958 /** 3959 * Same as String.match() 3960 * @param {string} regexp the regular expression to match 3961 * @return {Array.<string>} an array of matches 3962 */ 3963 match: function(regexp) { 3964 return this.str.match(regexp); 3965 }, 3966 3967 /** 3968 * Same as String.replace() 3969 * @param {string} searchValue a regular expression to search for 3970 * @param {string} newValue the string to replace the matches with 3971 * @return {ilib.String} a new string with all the matches replaced 3972 * with the new value 3973 */ 3974 replace: function(searchValue, newValue) { 3975 return new ilib.String(this.str.replace(searchValue, newValue)); 3976 }, 3977 3978 /** 3979 * Same as String.search() 3980 * @param {string} regexp the regular expression to search for 3981 * @return {number} position of the match, or -1 for no match 3982 */ 3983 search: function(regexp) { 3984 return this.str.search(regexp); 3985 }, 3986 3987 /** 3988 * Same as String.slice() 3989 * @param {number} start first character to include in the string 3990 * @param {number} end include all characters up to, but not including 3991 * the end character 3992 * @return {ilib.String} a slice of the current string 3993 */ 3994 slice: function(start, end) { 3995 return new ilib.String(this.str.slice(start, end)); 3996 }, 3997 3998 /** 3999 * Same as String.split() 4000 * @param {string} separator regular expression to match to find 4001 * separations between the parts of the text 4002 * @param {number} limit maximum number of items in the final 4003 * output array. Any items beyond that limit will be ignored. 4004 * @return {Array.<string>} the parts of the current string split 4005 * by the separator 4006 */ 4007 split: function(separator, limit) { 4008 return this.str.split(separator, limit); 4009 }, 4010 4011 /** 4012 * Same as String.substr() 4013 * @param {number} start the index of the character that should 4014 * begin the returned substring 4015 * @param {number} length the number of characters to return after 4016 * the start character. 4017 * @return {ilib.String} the requested substring 4018 */ 4019 substr: function(start, length) { 4020 return new ilib.String(this.str.substr(start, length)); 4021 }, 4022 4023 /** 4024 * Same as String.substring() 4025 * @param {number} from the index of the character that should 4026 * begin the returned substring 4027 * @param {number} to the index where to stop the extraction. If 4028 * omitted, extracts the rest of the string 4029 * @return {ilib.String} the requested substring 4030 */ 4031 substring: function(from, to) { 4032 return this.str.substring(from, to); 4033 }, 4034 4035 /** 4036 * Same as String.toLowerCase(). Note that this method is 4037 * not locale-sensitive. 4038 * @return {ilib.String} a string with the first character 4039 * lower-cased 4040 */ 4041 toLowerCase: function() { 4042 return this.str.toLowerCase(); 4043 }, 4044 4045 /** 4046 * Same as String.toUpperCase(). Note that this method is 4047 * not locale-sensitive. Use toLocaleUpperCase() instead 4048 * to get locale-sensitive behaviour. 4049 * @return {ilib.String} a string with the first character 4050 * upper-cased 4051 */ 4052 toUpperCase: function() { 4053 return this.str.toUpperCase(); 4054 }, 4055 4056 /** 4057 * Convert the character or the surrogate pair at the given 4058 * index into the string to a Unicode UCS-4 code point. 4059 * @protected 4060 * @param {number} index index into the string 4061 * @return {number} code point of the character at the 4062 * given index into the string 4063 */ 4064 _toCodePoint: function (index) { 4065 return ilib.String.toCodePoint(this.str, index); 4066 }, 4067 4068 /** 4069 * Call the callback with each character in the string one at 4070 * a time, taking care to step through the surrogate pairs in 4071 * the UTF-16 encoding properly.<p> 4072 * 4073 * The standard Javascript String's charAt() method only 4074 * returns a particular 16-bit character in the 4075 * UTF-16 encoding scheme. 4076 * If the index to charAt() is pointing to a low- or 4077 * high-surrogate character, 4078 * it will return the surrogate character rather 4079 * than the the character 4080 * in the supplementary planes that the two surrogates together 4081 * encode. This function will call the callback with the full 4082 * character, making sure to join two 4083 * surrogates into one character in the supplementary planes 4084 * where necessary.<p> 4085 * 4086 * @param {function(string)} callback a callback function to call with each 4087 * full character in the current string 4088 */ 4089 forEach: function(callback) { 4090 if (typeof(callback) === 'function') { 4091 var it = this.charIterator(); 4092 while (it.hasNext()) { 4093 callback(it.next()); 4094 } 4095 } 4096 }, 4097 4098 /** 4099 * Call the callback with each numeric code point in the string one at 4100 * a time, taking care to step through the surrogate pairs in 4101 * the UTF-16 encoding properly.<p> 4102 * 4103 * The standard Javascript String's charCodeAt() method only 4104 * returns information about a particular 16-bit character in the 4105 * UTF-16 encoding scheme. 4106 * If the index to charCodeAt() is pointing to a low- or 4107 * high-surrogate character, 4108 * it will return the code point of the surrogate character rather 4109 * than the code point of the character 4110 * in the supplementary planes that the two surrogates together 4111 * encode. This function will call the callback with the full 4112 * code point of each character, making sure to join two 4113 * surrogates into one code point in the supplementary planes.<p> 4114 * 4115 * @param {function(string)} callback a callback function to call with each 4116 * code point in the current string 4117 */ 4118 forEachCodePoint: function(callback) { 4119 if (typeof(callback) === 'function') { 4120 var it = this.iterator(); 4121 while (it.hasNext()) { 4122 callback(it.next()); 4123 } 4124 } 4125 }, 4126 4127 /** 4128 * Return an iterator that will step through all of the characters 4129 * in the string one at a time and return their code points, taking 4130 * care to step through the surrogate pairs in UTF-16 encoding 4131 * properly.<p> 4132 * 4133 * The standard Javascript String's charCodeAt() method only 4134 * returns information about a particular 16-bit character in the 4135 * UTF-16 encoding scheme. 4136 * If the index is pointing to a low- or high-surrogate character, 4137 * it will return a code point of the surrogate character rather 4138 * than the code point of the character 4139 * in the supplementary planes that the two surrogates together 4140 * encode.<p> 4141 * 4142 * The iterator instance returned has two methods, hasNext() which 4143 * returns true if the iterator has more code points to iterate through, 4144 * and next() which returns the next code point as a number.<p> 4145 * 4146 * @return {Object} an iterator 4147 * that iterates through all the code points in the string 4148 */ 4149 iterator: function() { 4150 /** 4151 * @constructor 4152 */ 4153 function _iterator (istring) { 4154 this.index = 0; 4155 this.hasNext = function () { 4156 return (this.index < istring.str.length); 4157 }; 4158 this.next = function () { 4159 if (this.index < istring.str.length) { 4160 var num = istring._toCodePoint(this.index); 4161 this.index += ((num > 0xFFFF) ? 2 : 1); 4162 } else { 4163 num = -1; 4164 } 4165 return num; 4166 }; 4167 }; 4168 return new _iterator(this); 4169 }, 4170 4171 /** 4172 * Return an iterator that will step through all of the characters 4173 * in the string one at a time, taking 4174 * care to step through the surrogate pairs in UTF-16 encoding 4175 * properly.<p> 4176 * 4177 * The standard Javascript String's charAt() method only 4178 * returns information about a particular 16-bit character in the 4179 * UTF-16 encoding scheme. 4180 * If the index is pointing to a low- or high-surrogate character, 4181 * it will return that surrogate character rather 4182 * than the surrogate pair which represents a character 4183 * in the supplementary planes.<p> 4184 * 4185 * The iterator instance returned has two methods, hasNext() which 4186 * returns true if the iterator has more characters to iterate through, 4187 * and next() which returns the next character.<p> 4188 * 4189 * @return {Object} an iterator 4190 * that iterates through all the characters in the string 4191 */ 4192 charIterator: function() { 4193 /** 4194 * @constructor 4195 */ 4196 function _chiterator (istring) { 4197 this.index = 0; 4198 this.hasNext = function () { 4199 return (this.index < istring.str.length); 4200 }; 4201 this.next = function () { 4202 var ch; 4203 if (this.index < istring.str.length) { 4204 ch = istring.str.charAt(this.index); 4205 if (ilib.String._isSurrogate(ch) && 4206 this.index+1 < istring.str.length && 4207 ilib.String._isSurrogate(istring.str.charAt(this.index+1))) { 4208 this.index++; 4209 ch += istring.str.charAt(this.index); 4210 } 4211 this.index++; 4212 } 4213 return ch; 4214 }; 4215 }; 4216 return new _chiterator(this); 4217 }, 4218 4219 /** 4220 * Return the code point at the given index when the string is viewed 4221 * as an array of code points. If the index is beyond the end of the 4222 * array of code points or if the index is negative, -1 is returned. 4223 * @param {number} index index of the code point 4224 * @return {number} code point of the character at the given index into 4225 * the string 4226 */ 4227 codePointAt: function (index) { 4228 if (index < 0) { 4229 return -1; 4230 } 4231 var count, 4232 it = this.iterator(), 4233 ch; 4234 for (count = index; count >= 0 && it.hasNext(); count--) { 4235 ch = it.next(); 4236 } 4237 return (count < 0) ? ch : -1; 4238 }, 4239 4240 /** 4241 * Set the locale to use when processing choice formats. The locale 4242 * affects how number classes are interpretted. In some cultures, 4243 * the limit "few" maps to "any integer that ends in the digits 2 to 9" and 4244 * in yet others, "few" maps to "any integer that ends in the digits 4245 * 3 or 4". 4246 * @param {ilib.Locale|string} locale locale to use when processing choice 4247 * formats with this string 4248 * @param {boolean=} sync [optional] whether to load the locale data synchronously 4249 * or not 4250 * @param {Object=} loadParams [optional] parameters to pass to the loader function 4251 * @param {function(*)=} onLoad [optional] function to call when the loading is done 4252 */ 4253 setLocale: function (locale, sync, loadParams, onLoad) { 4254 if (typeof(locale) === 'object') { 4255 this.locale = locale; 4256 } else { 4257 this.localeSpec = locale; 4258 this.locale = new ilib.Locale(locale); 4259 } 4260 4261 ilib.String.loadPlurals(typeof(sync) !== 'undefined' ? sync : true, this.locale, loadParams, onLoad); 4262 }, 4263 4264 /** 4265 * Return the locale to use when processing choice formats. The locale 4266 * affects how number classes are interpretted. In some cultures, 4267 * the limit "few" maps to "any integer that ends in the digits 2 to 9" and 4268 * in yet others, "few" maps to "any integer that ends in the digits 4269 * 3 or 4". 4270 * @return {string} localespec to use when processing choice 4271 * formats with this string 4272 */ 4273 getLocale: function () { 4274 return (this.locale ? this.locale.getSpec() : this.localeSpec) || ilib.getLocale(); 4275 }, 4276 4277 /** 4278 * Return the number of code points in this string. This may be different 4279 * than the number of characters, as the UTF-16 encoding that Javascript 4280 * uses for its basis returns surrogate pairs separately. Two 2-byte 4281 * surrogate characters together make up one character/code point in 4282 * the supplementary character planes. If your string contains no 4283 * characters in the supplementary planes, this method will return the 4284 * same thing as the length() method. 4285 * @return {number} the number of code points in this string 4286 */ 4287 codePointLength: function () { 4288 if (this.cpLength === -1) { 4289 var it = this.iterator(); 4290 this.cpLength = 0; 4291 while (it.hasNext()) { 4292 this.cpLength++; 4293 it.next(); 4294 }; 4295 } 4296 return this.cpLength; 4297 } 4298 }; 4299 /* 4300 * calendar.js - Represent a calendar object. 4301 * 4302 * Copyright © 2012, JEDLSoft 4303 * 4304 * Licensed under the Apache License, Version 2.0 (the "License"); 4305 * you may not use this file except in compliance with the License. 4306 * You may obtain a copy of the License at 4307 * 4308 * http://www.apache.org/licenses/LICENSE-2.0 4309 * 4310 * Unless required by applicable law or agreed to in writing, software 4311 * distributed under the License is distributed on an "AS IS" BASIS, 4312 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4313 * 4314 * See the License for the specific language governing permissions and 4315 * limitations under the License. 4316 */ 4317 4318 /* !depends 4319 ilibglobal.js 4320 locale.js 4321 localeinfo.js 4322 */ 4323 4324 /** 4325 * Interface that all calendars must implement. 4326 * 4327 * Depends directive: !depends calendar.js 4328 * 4329 * @interface 4330 * @protected 4331 */ 4332 ilib.Cal = function() { 4333 }; 4334 4335 /** 4336 * Factory method to create a new instance of a calendar subclass.<p> 4337 * 4338 * The options parameter can be an object that contains the following 4339 * properties: 4340 * 4341 * <ul> 4342 * <li><i>type</i> - specify the type of the calendar desired. The 4343 * list of valid values changes depending on which calendars are 4344 * defined. When assembling your iliball.js, include those calendars 4345 * you wish to use in your program or web page, and they will register 4346 * themselves with this factory method. The "official", "gregorian", 4347 * and "julian" calendars are all included by default, as they are the 4348 * standard calendars for much of the world. 4349 * <li><i>locale</i> - some calendars vary depending on the locale. 4350 * For example, the "official" calendar transitions from a Julian-style 4351 * calendar to a Gregorian-style calendar on a different date for 4352 * each country, as the governments of those countries decided to 4353 * adopt the Gregorian calendar at different times. 4354 * </ul> 4355 * 4356 * If a locale is specified, but no type, then the calendar that is default for 4357 * the locale will be instantiated and returned. If neither the type nor 4358 * the locale are specified, then the calendar for the default locale will 4359 * be used. 4360 * 4361 * @param {Object=} options options controlling the construction of this instance, or 4362 * undefined to use the default options 4363 * @return {ilib.Cal} an instance of a calendar object of the appropriate type 4364 */ 4365 ilib.Cal.newInstance = function (options) { 4366 var locale = options && options.locale, 4367 type = options && options.type, 4368 cons; 4369 4370 if (!locale) { 4371 locale = new ilib.Locale(); // default locale 4372 } 4373 4374 if (!type) { 4375 var info = new ilib.LocaleInfo(locale); 4376 type = info.getCalendar(); 4377 } 4378 4379 cons = ilib.Cal._constructors[type]; 4380 4381 // pass the same options through to the constructor so the subclass 4382 // has the ability to do something with if it needs to 4383 return cons && new cons(options); 4384 }; 4385 4386 /* place for the subclasses to put their constructors so that the factory method 4387 * can find them. Do this to add your calendar after it's defined: 4388 * ilib.Cal._constructors["mytype"] = ilib.Cal.MyTypeConstructor; 4389 */ 4390 ilib.Cal._constructors = {}; 4391 4392 /** 4393 * Return an array of known calendar types that the factory method can instantiate. 4394 * 4395 * @return {Array.<string>} an array of calendar types 4396 */ 4397 ilib.Cal.getCalendars = function () { 4398 var arr = [], 4399 c; 4400 4401 for (c in ilib.Cal._constructors) { 4402 if (c && ilib.Cal._constructors[c]) { 4403 arr.push(c); // code like a pirate 4404 } 4405 } 4406 4407 return arr; 4408 }; 4409 4410 ilib.Cal.prototype = { 4411 /** 4412 * Return the type of this calendar. 4413 * 4414 * @return {string} the name of the type of this calendar 4415 */ 4416 getType: function() { 4417 throw "Cannot call methods of abstract class ilib.Cal"; 4418 }, 4419 4420 /** 4421 * Return the number of months in the given year. The number of months in a year varies 4422 * for some luni-solar calendars because in some years, an extra month is needed to extend the 4423 * days in a year to an entire solar year. The month is represented as a 1-based number 4424 * where 1=first month, 2=second month, etc. 4425 * 4426 * @param {number} year a year for which the number of months is sought 4427 * @return {number} The number of months in the given year 4428 */ 4429 getNumMonths: function(year) { 4430 throw "Cannot call methods of abstract class ilib.Cal"; 4431 }, 4432 4433 /** 4434 * Return the number of days in a particular month in a particular year. This function 4435 * can return a different number for a month depending on the year because of things 4436 * like leap years. 4437 * 4438 * @param {number} month the month for which the length is sought 4439 * @param {number} year the year within which that month can be found 4440 * @return {number} the number of days within the given month in the given year 4441 */ 4442 getMonLength: function(month, year) { 4443 throw "Cannot call methods of abstract class ilib.Cal"; 4444 }, 4445 4446 /** 4447 * Return true if the given year is a leap year in this calendar. 4448 * The year parameter may be given as a number. 4449 * 4450 * @param {number} year the year for which the leap year information is being sought 4451 * @return {boolean} true if the given year is a leap year 4452 */ 4453 isLeapYear: function(year) { 4454 throw "Cannot call methods of abstract class ilib.Cal"; 4455 } 4456 }; 4457 4458 /* 4459 * julianday.js - A Julian date object. 4460 * 4461 * Copyright © 2012-2014, JEDLSoft 4462 * 4463 * Licensed under the Apache License, Version 2.0 (the "License"); 4464 * you may not use this file except in compliance with the License. 4465 * You may obtain a copy of the License at 4466 * 4467 * http://www.apache.org/licenses/LICENSE-2.0 4468 * 4469 * Unless required by applicable law or agreed to in writing, software 4470 * distributed under the License is distributed on an "AS IS" BASIS, 4471 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4472 * 4473 * See the License for the specific language governing permissions and 4474 * limitations under the License. 4475 */ 4476 4477 /* !depends locale.js */ 4478 4479 /** 4480 * @class 4481 * A Julian Day class. A Julian Day is a date based on the Julian Day count 4482 * of time invented by Joseph Scaliger in 1583 for use with astronomical calculations. 4483 * Do not confuse it with a date in the Julian calendar, which it has very 4484 * little in common with. The naming is unfortunately close, and comes from history.<p> 4485 * 4486 * Depends directive: !depends julianday.js 4487 * 4488 * @constructor 4489 * @param {number} num the Julian Day expressed as a floating point number 4490 */ 4491 ilib.JulianDay = function(num) { 4492 this.jd = num; 4493 this.days = Math.floor(this.jd); 4494 this.frac = num - this.days; 4495 }; 4496 4497 ilib.JulianDay.prototype = { 4498 /** 4499 * Return the integral portion of this Julian Day instance. This corresponds to 4500 * the number of days since the beginning of the epoch. 4501 * 4502 * @return {number} the integral portion of this Julian Day 4503 */ 4504 getDays: function() { 4505 return this.days; 4506 }, 4507 4508 /** 4509 * Set the date of this Julian Day instance. 4510 * 4511 * @param {number} days the julian date expressed as a floating point number 4512 */ 4513 setDays: function(days) { 4514 this.days = Math.floor(days); 4515 this.jd = this.days + this.frac; 4516 }, 4517 4518 /** 4519 * Return the fractional portion of this Julian Day instance. This portion 4520 * corresponds to the time of day for the instance. 4521 */ 4522 getDayFraction: function() { 4523 return this.frac; 4524 }, 4525 4526 /** 4527 * Set the fractional part of the Julian Day. The fractional part represents 4528 * the portion of a fully day. Julian dates start at noon, and proceed until 4529 * noon of the next day. That would mean midnight is represented as a fractional 4530 * part of 0.5. 4531 * 4532 * @param {number} fraction The fractional part of the Julian date 4533 */ 4534 setDayFraction: function(fraction) { 4535 var t = Math.floor(fraction); 4536 this.frac = fraction - t; 4537 this.jd = this.days + this.frac; 4538 }, 4539 4540 /** 4541 * Return the Julian Day expressed as a floating point number. 4542 * @return {number} the Julian Day as a number 4543 */ 4544 getDate: function () { 4545 return this.jd; 4546 }, 4547 4548 /** 4549 * Set the date of this Julian Day instance. 4550 * 4551 * @param {number} num the numeric Julian Day to set into this instance 4552 */ 4553 setDate: function (num) { 4554 this.jd = num; 4555 }, 4556 4557 /** 4558 * Add an offset to the current date instance. The offset should be expressed in 4559 * terms of Julian days. That is, each integral unit represents one day of time, and 4560 * fractional part represents a fraction of a regular 24-hour day. 4561 * 4562 * @param {number} offset an amount to add (or subtract) to the current result instance. 4563 */ 4564 addDate: function(offset) { 4565 if (typeof(offset) === 'number') { 4566 this.jd += offset; 4567 this.days = Math.floor(this.jd); 4568 this.frac = this.jd - this.days; 4569 } 4570 } 4571 }; 4572 4573 /* 4574 * gregorian.js - Represent a Gregorian calendar object. 4575 * 4576 * Copyright © 2012-2014, JEDLSoft 4577 * 4578 * Licensed under the Apache License, Version 2.0 (the "License"); 4579 * you may not use this file except in compliance with the License. 4580 * You may obtain a copy of the License at 4581 * 4582 * http://www.apache.org/licenses/LICENSE-2.0 4583 * 4584 * Unless required by applicable law or agreed to in writing, software 4585 * distributed under the License is distributed on an "AS IS" BASIS, 4586 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4587 * 4588 * See the License for the specific language governing permissions and 4589 * limitations under the License. 4590 */ 4591 4592 4593 /* !depends calendar.js locale.js date.js julianday.js util/utils.js util/math.js */ 4594 4595 /** 4596 * @class 4597 * Construct a new Gregorian calendar object. This class encodes information about 4598 * a Gregorian calendar.<p> 4599 * 4600 * Depends directive: !depends gregorian.js 4601 * 4602 * @constructor 4603 * @implements ilib.Cal 4604 */ 4605 ilib.Cal.Gregorian = function() { 4606 this.type = "gregorian"; 4607 }; 4608 4609 /** 4610 * the lengths of each month 4611 * @private 4612 * @const 4613 * @type Array.<number> 4614 */ 4615 ilib.Cal.Gregorian.monthLengths = [ 4616 31, /* Jan */ 4617 28, /* Feb */ 4618 31, /* Mar */ 4619 30, /* Apr */ 4620 31, /* May */ 4621 30, /* Jun */ 4622 31, /* Jul */ 4623 31, /* Aug */ 4624 30, /* Sep */ 4625 31, /* Oct */ 4626 30, /* Nov */ 4627 31 /* Dec */ 4628 ]; 4629 4630 /** 4631 * Return the number of months in the given year. The number of months in a year varies 4632 * for some luni-solar calendars because in some years, an extra month is needed to extend the 4633 * days in a year to an entire solar year. The month is represented as a 1-based number 4634 * where 1=first month, 2=second month, etc. 4635 * 4636 * @param {number} year a year for which the number of months is sought 4637 * @return {number} The number of months in the given year 4638 */ 4639 ilib.Cal.Gregorian.prototype.getNumMonths = function(year) { 4640 return 12; 4641 }; 4642 4643 /** 4644 * Return the number of days in a particular month in a particular year. This function 4645 * can return a different number for a month depending on the year because of things 4646 * like leap years. 4647 * 4648 * @param {number} month the month for which the length is sought 4649 * @param {number} year the year within which that month can be found 4650 * @return {number} the number of days within the given month in the given year 4651 */ 4652 ilib.Cal.Gregorian.prototype.getMonLength = function(month, year) { 4653 if (month !== 2 || !this.isLeapYear(year)) { 4654 return ilib.Cal.Gregorian.monthLengths[month-1]; 4655 } else { 4656 return 29; 4657 } 4658 }; 4659 4660 /** 4661 * Return true if the given year is a leap year in the Gregorian calendar. 4662 * The year parameter may be given as a number, or as a GregDate object. 4663 * @param {number|ilib.Date.GregDate} year the year for which the leap year information is being sought 4664 * @return {boolean} true if the given year is a leap year 4665 */ 4666 ilib.Cal.Gregorian.prototype.isLeapYear = function(year) { 4667 var y = (typeof(year) === 'number' ? year : year.getYears()); 4668 var centuries = ilib.mod(y, 400); 4669 return (ilib.mod(y, 4) === 0 && centuries !== 100 && centuries !== 200 && centuries !== 300); 4670 }; 4671 4672 /** 4673 * Return the type of this calendar. 4674 * 4675 * @return {string} the name of the type of this calendar 4676 */ 4677 ilib.Cal.Gregorian.prototype.getType = function() { 4678 return this.type; 4679 }; 4680 4681 /** 4682 * Return a date instance for this calendar type using the given 4683 * options. 4684 * @param {Object} options options controlling the construction of 4685 * the date instance 4686 * @return {ilib.Date} a date appropriate for this calendar type 4687 */ 4688 ilib.Cal.Gregorian.prototype.newDateInstance = function (options) { 4689 return new ilib.Date.GregDate(options); 4690 }; 4691 4692 /* register this calendar for the factory method */ 4693 ilib.Cal._constructors["gregorian"] = ilib.Cal.Gregorian; 4694 4695 /* 4696 * ratadie.js - Represent the RD date number in the calendar 4697 * 4698 * Copyright © 2014, JEDLSoft 4699 * 4700 * Licensed under the Apache License, Version 2.0 (the "License"); 4701 * you may not use this file except in compliance with the License. 4702 * You may obtain a copy of the License at 4703 * 4704 * http://www.apache.org/licenses/LICENSE-2.0 4705 * 4706 * Unless required by applicable law or agreed to in writing, software 4707 * distributed under the License is distributed on an "AS IS" BASIS, 4708 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4709 * 4710 * See the License for the specific language governing permissions and 4711 * limitations under the License. 4712 */ 4713 4714 /* !depends 4715 util/utils.js 4716 util/math.js 4717 julianday.js 4718 */ 4719 4720 /** 4721 * @class 4722 * Construct a new RD date number object. The constructor parameters can 4723 * contain any of the following properties: 4724 * 4725 * <ul> 4726 * <li><i>unixtime<i> - sets the time of this instance according to the given 4727 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970. 4728 * 4729 * <li><i>julianday</i> - sets the time of this instance according to the given 4730 * Julian Day instance or the Julian Day given as a float 4731 * 4732 * <li><i>cycle</i> - any integer giving the number of 60-year cycle in which the date is located. 4733 * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious 4734 * linear count of years since the beginning of the epoch, much like other calendars. This linear 4735 * count is never used. If both the cycle and year are given, the year is wrapped to the range 0 4736 * to 60 and treated as if it were a year in the regular 60-year cycle. 4737 * 4738 * <li><i>year</i> - any integer, including 0 4739 * 4740 * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc. 4741 * 4742 * <li><i>day</i> - 1 to 31 4743 * 4744 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 4745 * is always done with an unambiguous 24 hour representation 4746 * 4747 * <li><i>minute</i> - 0 to 59 4748 * 4749 * <li><i>second</i> - 0 to 59 4750 * 4751 * <li><i>millisecond</i> - 0 to 999 4752 * 4753 * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify 4754 * the parts or specify the minutes, seconds, and milliseconds, but not both. This is only used 4755 * in the Hebrew calendar. 4756 * 4757 * <li><i>minute</i> - 0 to 59 4758 * 4759 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 4760 * </ul> 4761 * 4762 * If the constructor is called with another date instance instead of 4763 * a parameter block, the other instance acts as a parameter block and its 4764 * settings are copied into the current instance.<p> 4765 * 4766 * If the constructor is called with no arguments at all or if none of the 4767 * properties listed above are present, then the RD is calculate based on 4768 * the current date at the time of instantiation. <p> 4769 * 4770 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 4771 * specified in the params, it is assumed that they have the smallest possible 4772 * value in the range for the property (zero or one).<p> 4773 * 4774 * Depends directive: !depends ratadie.js 4775 * 4776 * @private 4777 * @constructor 4778 * @param {Object=} params parameters that govern the settings and behaviour of this RD date 4779 */ 4780 ilib.Date.RataDie = function(params) { 4781 if (params) { 4782 if (typeof(params.date) !== 'undefined') { 4783 // accept JS Date classes or strings 4784 var date = params.date; 4785 if (!(date instanceof Date)) { 4786 date = new Date(date); // maybe a string initializer? 4787 } 4788 this._setTime(date.getTime()); 4789 } else if (typeof(params.unixtime) !== 'undefined') { 4790 this._setTime(parseInt(params.unixtime, 10)); 4791 } else if (typeof(params.julianday) !== 'undefined') { 4792 // JD time is defined to be UTC 4793 this._setJulianDay(parseFloat(params.julianday)); 4794 } else if (params.year || params.month || params.day || params.hour || 4795 params.minute || params.second || params.millisecond || params.parts || params.cycle) { 4796 this._setDateComponents(params); 4797 } else if (typeof(params.rd) !== 'undefined') { 4798 this.rd = (typeof(params.rd) === 'object' && params.rd instanceof ilib.Date.RataDie) ? params.rd.rd : params.rd; 4799 } 4800 } 4801 4802 /** 4803 * @type {number} the Rata Die number of this date for this calendar type 4804 */ 4805 if (typeof(this.rd) === 'undefined') { 4806 var now = new Date(); 4807 this._setTime(now.getTime()); 4808 } 4809 }; 4810 4811 /** 4812 * @private 4813 * @const 4814 * @type {number} 4815 */ 4816 ilib.Date.RataDie.gregorianEpoch = 1721424.5; 4817 4818 ilib.Date.RataDie.prototype = { 4819 /** 4820 * @protected 4821 * @const 4822 * @type {number} 4823 * the difference between a zero Julian day and the zero Gregorian date. 4824 */ 4825 epoch: ilib.Date.RataDie.gregorianEpoch, 4826 4827 /** 4828 * Set the RD of this instance according to the given unix time. Unix time is 4829 * the number of milliseconds since midnight on Jan 1, 1970. 4830 * 4831 * @protected 4832 * @param {number} millis the unix time to set this date to in milliseconds 4833 */ 4834 _setTime: function(millis) { 4835 // 2440587.5 is the julian day of midnight Jan 1, 1970, UTC (Gregorian) 4836 this._setJulianDay(2440587.5 + millis / 86400000); 4837 }, 4838 4839 /** 4840 * Set the date of this instance using a Julian Day. 4841 * @protected 4842 * @param {number} date the Julian Day to use to set this date 4843 */ 4844 _setJulianDay: function (date) { 4845 var jd = (typeof(date) === 'number') ? new ilib.JulianDay(date) : date; 4846 // round to the nearest millisecond 4847 this.rd = ilib._roundFnc.halfup((jd.getDate() - this.epoch) * 86400000) / 86400000; 4848 }, 4849 4850 /** 4851 * Return the rd number of the particular day of the week on or before the 4852 * given rd. eg. The Sunday on or before the given rd. 4853 * @protected 4854 * @param {number} rd the rata die date of the reference date 4855 * @param {number} dayOfWeek the day of the week that is being sought relative 4856 * to the current date 4857 * @return {number} the rd of the day of the week 4858 */ 4859 _onOrBefore: function(rd, dayOfWeek) { 4860 return rd - ilib.mod(Math.floor(rd) - dayOfWeek - 2, 7); 4861 }, 4862 4863 /** 4864 * Return the rd number of the particular day of the week on or before the current rd. 4865 * eg. The Sunday on or before the current rd. If the offset is given, the calculation 4866 * happens in wall time instead of UTC. UTC time may be a day before or day behind 4867 * wall time, so it it would give the wrong day of the week if this calculation was 4868 * done in UTC time when the caller really wanted wall time. Even though the calculation 4869 * may be done in wall time, the return value is nonetheless always given in UTC. 4870 * @param {number} dayOfWeek the day of the week that is being sought relative 4871 * to the current date 4872 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is 4873 * not given 4874 * @return {number} the rd of the day of the week 4875 */ 4876 onOrBefore: function(dayOfWeek, offset) { 4877 offset = offset || 0; 4878 return this._onOrBefore(this.rd + offset, dayOfWeek) - offset; 4879 }, 4880 4881 /** 4882 * Return the rd number of the particular day of the week on or before the current rd. 4883 * eg. The Sunday on or before the current rd. If the offset is given, the calculation 4884 * happens in wall time instead of UTC. UTC time may be a day before or day behind 4885 * wall time, so it it would give the wrong day of the week if this calculation was 4886 * done in UTC time when the caller really wanted wall time. Even though the calculation 4887 * may be done in wall time, the return value is nonetheless always given in UTC. 4888 * @param {number} dayOfWeek the day of the week that is being sought relative 4889 * to the reference date 4890 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is 4891 * not given 4892 * @return {number} the day of the week 4893 */ 4894 onOrAfter: function(dayOfWeek, offset) { 4895 offset = offset || 0; 4896 return this._onOrBefore(this.rd+6+offset, dayOfWeek) - offset; 4897 }, 4898 4899 /** 4900 * Return the rd number of the particular day of the week before the current rd. 4901 * eg. The Sunday before the current rd. If the offset is given, the calculation 4902 * happens in wall time instead of UTC. UTC time may be a day before or day behind 4903 * wall time, so it it would give the wrong day of the week if this calculation was 4904 * done in UTC time when the caller really wanted wall time. Even though the calculation 4905 * may be done in wall time, the return value is nonetheless always given in UTC. 4906 * @param {number} dayOfWeek the day of the week that is being sought relative 4907 * to the reference date 4908 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is 4909 * not given 4910 * @return {number} the day of the week 4911 */ 4912 before: function(dayOfWeek, offset) { 4913 offset = offset || 0; 4914 return this._onOrBefore(this.rd-1+offset, dayOfWeek) - offset; 4915 }, 4916 4917 /** 4918 * Return the rd number of the particular day of the week after the current rd. 4919 * eg. The Sunday after the current rd. If the offset is given, the calculation 4920 * happens in wall time instead of UTC. UTC time may be a day before or day behind 4921 * wall time, so it it would give the wrong day of the week if this calculation was 4922 * done in UTC time when the caller really wanted wall time. Even though the calculation 4923 * may be done in wall time, the return value is nonetheless always given in UTC. 4924 * @param {number} dayOfWeek the day of the week that is being sought relative 4925 * to the reference date 4926 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is 4927 * not given 4928 * @return {number} the day of the week 4929 */ 4930 after: function(dayOfWeek, offset) { 4931 offset = offset || 0; 4932 return this._onOrBefore(this.rd+7+offset, dayOfWeek) - offset; 4933 }, 4934 4935 /** 4936 * Return the unix time equivalent to this Gregorian date instance. Unix time is 4937 * the number of milliseconds since midnight on Jan 1, 1970 UTC. This method only 4938 * returns a valid number for dates between midnight, Jan 1, 1970 and 4939 * Jan 19, 2038 at 3:14:07am when the unix time runs out. If this instance 4940 * encodes a date outside of that range, this method will return -1. 4941 * 4942 * @return {number} a number giving the unix time, or -1 if the date is outside the 4943 * valid unix time range 4944 */ 4945 getTime: function() { 4946 // earlier than Jan 1, 1970 4947 // or later than Jan 19, 2038 at 3:14:07am 4948 var jd = this.getJulianDay(); 4949 if (jd < 2440587.5 || jd > 2465442.634803241) { 4950 return -1; 4951 } 4952 4953 // avoid the rounding errors in the floating point math by only using 4954 // the whole days from the rd, and then calculating the milliseconds directly 4955 return Math.round((jd - 2440587.5) * 86400000); 4956 }, 4957 4958 /** 4959 * Return the extended unix time equivalent to this Gregorian date instance. Unix time is 4960 * the number of milliseconds since midnight on Jan 1, 1970 UTC. Traditionally unix time 4961 * (or the type "time_t" in C/C++) is only encoded with a unsigned 32 bit integer, and thus 4962 * runs out on Jan 19, 2038. However, most Javascript engines encode numbers well above 4963 * 32 bits and the Date object allows you to encode up to 100 million days worth of time 4964 * after Jan 1, 1970, and even more interestingly 100 million days worth of time before 4965 * Jan 1, 1970 as well. This method returns the number of milliseconds in that extended 4966 * range. If this instance encodes a date outside of that range, this method will return 4967 * NaN. 4968 * 4969 * @return {number} a number giving the extended unix time, or NaN if the date is outside 4970 * the valid extended unix time range 4971 */ 4972 getTimeExtended: function() { 4973 var jd = this.getJulianDay(); 4974 4975 // test if earlier than Jan 1, 1970 - 100 million days 4976 // or later than Jan 1, 1970 + 100 million days 4977 if (jd < -97559412.5 || jd > 102440587.5) { 4978 return NaN; 4979 } 4980 4981 // avoid the rounding errors in the floating point math by only using 4982 // the whole days from the rd, and then calculating the milliseconds directly 4983 return Math.round((jd - 2440587.5) * 86400000); 4984 }, 4985 4986 /** 4987 * Return the Julian Day equivalent to this calendar date as a number. 4988 * This returns the julian day in UTC. 4989 * 4990 * @return {number} the julian date equivalent of this date 4991 */ 4992 getJulianDay: function() { 4993 return this.rd + this.epoch; 4994 }, 4995 4996 /** 4997 * Return the Rata Die (fixed day) number of this RD date. 4998 * 4999 * @return {number} the rd date as a number 5000 */ 5001 getRataDie: function() { 5002 return this.rd; 5003 } 5004 }; 5005 5006 /* 5007 * gregratadie.js - Represent the RD date number in the Gregorian calendar 5008 * 5009 * Copyright © 2014, JEDLSoft 5010 * 5011 * Licensed under the Apache License, Version 2.0 (the "License"); 5012 * you may not use this file except in compliance with the License. 5013 * You may obtain a copy of the License at 5014 * 5015 * http://www.apache.org/licenses/LICENSE-2.0 5016 * 5017 * Unless required by applicable law or agreed to in writing, software 5018 * distributed under the License is distributed on an "AS IS" BASIS, 5019 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5020 * 5021 * See the License for the specific language governing permissions and 5022 * limitations under the License. 5023 */ 5024 5025 /* !depends 5026 date.js 5027 calendar/gregorian.js 5028 calendar/ratadie.js 5029 util/utils.js 5030 util/math.js 5031 julianday.js 5032 */ 5033 5034 /** 5035 * @class 5036 * Construct a new Gregorian RD date number object. The constructor parameters can 5037 * contain any of the following properties: 5038 * 5039 * <ul> 5040 * <li><i>unixtime<i> - sets the time of this instance according to the given 5041 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970. 5042 * 5043 * <li><i>julianday</i> - sets the time of this instance according to the given 5044 * Julian Day instance or the Julian Day given as a float 5045 * 5046 * <li><i>year</i> - any integer, including 0 5047 * 5048 * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc. 5049 * 5050 * <li><i>day</i> - 1 to 31 5051 * 5052 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 5053 * is always done with an unambiguous 24 hour representation 5054 * 5055 * <li><i>minute</i> - 0 to 59 5056 * 5057 * <li><i>second</i> - 0 to 59 5058 * 5059 * <li><i>millisecond</i> - 0 to 999 5060 * 5061 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 5062 * </ul> 5063 * 5064 * If the constructor is called with another Gregorian date instance instead of 5065 * a parameter block, the other instance acts as a parameter block and its 5066 * settings are copied into the current instance.<p> 5067 * 5068 * If the constructor is called with no arguments at all or if none of the 5069 * properties listed above are present, then the RD is calculate based on 5070 * the current date at the time of instantiation. <p> 5071 * 5072 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 5073 * specified in the params, it is assumed that they have the smallest possible 5074 * value in the range for the property (zero or one).<p> 5075 * 5076 * Depends directive: !depends gregratadie.js 5077 * 5078 * @private 5079 * @constructor 5080 * @extends ilib.Date.RataDie 5081 * @param {Object=} params parameters that govern the settings and behaviour of this Gregorian RD date 5082 */ 5083 ilib.Date.GregRataDie = function(params) { 5084 this.cal = params && params.cal || new ilib.Cal.Gregorian(); 5085 /** @type {number|undefined} */ 5086 this.rd = undefined; 5087 ilib.Date.RataDie.call(this, params); 5088 }; 5089 5090 ilib.Date.GregRataDie.prototype = new ilib.Date.RataDie(); 5091 ilib.Date.GregRataDie.prototype.parent = ilib.Date.RataDie; 5092 ilib.Date.GregRataDie.prototype.constructor = ilib.Date.GregRataDie; 5093 5094 /** 5095 * the cumulative lengths of each month, for a non-leap year 5096 * @private 5097 * @const 5098 * @type Array.<number> 5099 */ 5100 ilib.Date.GregRataDie.cumMonthLengths = [ 5101 0, /* Jan */ 5102 31, /* Feb */ 5103 59, /* Mar */ 5104 90, /* Apr */ 5105 120, /* May */ 5106 151, /* Jun */ 5107 181, /* Jul */ 5108 212, /* Aug */ 5109 243, /* Sep */ 5110 273, /* Oct */ 5111 304, /* Nov */ 5112 334, /* Dec */ 5113 365 5114 ]; 5115 5116 /** 5117 * the cumulative lengths of each month, for a leap year 5118 * @private 5119 * @const 5120 * @type Array.<number> 5121 */ 5122 ilib.Date.GregRataDie.cumMonthLengthsLeap = [ 5123 0, /* Jan */ 5124 31, /* Feb */ 5125 60, /* Mar */ 5126 91, /* Apr */ 5127 121, /* May */ 5128 152, /* Jun */ 5129 182, /* Jul */ 5130 213, /* Aug */ 5131 244, /* Sep */ 5132 274, /* Oct */ 5133 305, /* Nov */ 5134 335, /* Dec */ 5135 366 5136 ]; 5137 5138 /** 5139 * Calculate the Rata Die (fixed day) number of the given date. 5140 * 5141 * @private 5142 * @param {Object} date the date components to calculate the RD from 5143 */ 5144 ilib.Date.GregRataDie.prototype._setDateComponents = function(date) { 5145 var year = parseInt(date.year, 10) || 0; 5146 var month = parseInt(date.month, 10) || 1; 5147 var day = parseInt(date.day, 10) || 1; 5148 var hour = parseInt(date.hour, 10) || 0; 5149 var minute = parseInt(date.minute, 10) || 0; 5150 var second = parseInt(date.second, 10) || 0; 5151 var millisecond = parseInt(date.millisecond, 10) || 0; 5152 5153 var years = 365 * (year - 1) + 5154 Math.floor((year-1)/4) - 5155 Math.floor((year-1)/100) + 5156 Math.floor((year-1)/400); 5157 5158 var dayInYear = (month > 1 ? ilib.Date.GregRataDie.cumMonthLengths[month-1] : 0) + 5159 day + 5160 (ilib.Cal.Gregorian.prototype.isLeapYear.call(this.cal, year) && month > 2 ? 1 : 0); 5161 var rdtime = (hour * 3600000 + 5162 minute * 60000 + 5163 second * 1000 + 5164 millisecond) / 5165 86400000; 5166 /* 5167 debug("getRataDie: converting " + JSON.stringify(this)); 5168 debug("getRataDie: year is " + years); 5169 debug("getRataDie: day in year is " + dayInYear); 5170 debug("getRataDie: rdtime is " + rdtime); 5171 debug("getRataDie: rd is " + (years + dayInYear + rdtime)); 5172 */ 5173 5174 /** 5175 * @type {number|undefined} the RD number of this Gregorian date 5176 */ 5177 this.rd = years + dayInYear + rdtime; 5178 }; 5179 5180 /** 5181 * Return the rd number of the particular day of the week on or before the 5182 * given rd. eg. The Sunday on or before the given rd. 5183 * @private 5184 * @param {number} rd the rata die date of the reference date 5185 * @param {number} dayOfWeek the day of the week that is being sought relative 5186 * to the current date 5187 * @return {number} the rd of the day of the week 5188 */ 5189 ilib.Date.GregRataDie.prototype._onOrBefore = function(rd, dayOfWeek) { 5190 return rd - ilib.mod(Math.floor(rd) - dayOfWeek, 7); 5191 }; 5192 5193 /* 5194 * timezone.js - Definition of a time zone class 5195 * 5196 * Copyright © 2012-2014, JEDLSoft 5197 * 5198 * Licensed under the Apache License, Version 2.0 (the "License"); 5199 * you may not use this file except in compliance with the License. 5200 * You may obtain a copy of the License at 5201 * 5202 * http://www.apache.org/licenses/LICENSE-2.0 5203 * 5204 * Unless required by applicable law or agreed to in writing, software 5205 * distributed under the License is distributed on an "AS IS" BASIS, 5206 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5207 * 5208 * See the License for the specific language governing permissions and 5209 * limitations under the License. 5210 */ 5211 5212 /* 5213 !depends 5214 ilibglobal.js 5215 locale.js 5216 localeinfo.js 5217 util/utils.js 5218 util/math.js 5219 calendar/gregratadie.js 5220 */ 5221 5222 // !data localeinfo zoneinfo 5223 5224 /** 5225 * @class 5226 * Create a time zone instance. 5227 * 5228 * This class reports and transforms 5229 * information about particular time zones.<p> 5230 * 5231 * The options parameter may contain any of the following properties: 5232 * 5233 * <ul> 5234 * <li><i>id</i> - The id of the requested time zone such as "Europe/London" or 5235 * "America/Los_Angeles". These are taken from the IANA time zone database. (See 5236 * http://www.iana.org/time-zones for more information.) <p> 5237 * 5238 * There is one special 5239 * time zone that is not taken from the IANA database called simply "local". In 5240 * this case, this class will attempt to discover the current time zone and 5241 * daylight savings time settings by calling standard Javascript classes to 5242 * determine the offsets from UTC. 5243 * 5244 * <li><i>locale</i> - The locale for this time zone. 5245 * 5246 * <li><i>offset</i> - Choose the time zone based on the offset from UTC given in 5247 * number of minutes (negative is west, positive is east). 5248 * 5249 * <li><i>onLoad</i> - a callback function to call when the data is fully 5250 * loaded. When the onLoad option is given, this class will attempt to 5251 * load any missing locale data using the ilib loader callback. 5252 * When the data is loaded, the onLoad function is called with the current 5253 * instance as a parameter. 5254 * 5255 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 5256 * asynchronously. If this option is given as "false", then the "onLoad" 5257 * callback must be given, as the instance returned from this constructor will 5258 * not be usable for a while. 5259 * 5260 * <li><i>loadParams</i> - an object containing parameters to pass to the 5261 * loader callback function when locale data is missing. The parameters are not 5262 * interpretted or modified in any way. They are simply passed along. The object 5263 * may contain any property/value pairs as long as the calling code is in 5264 * agreement with the loader callback function as to what those parameters mean. 5265 * </ul> 5266 * 5267 * There is currently no way in the ECMAscript 5268 * standard to tell which exact time zone is currently in use. Choosing the 5269 * id "locale" or specifying an explicit offset will not give a specific time zone, 5270 * as it is impossible to tell with certainty which zone the offsets 5271 * match.<p> 5272 * 5273 * When the id "local" is given or the offset option is specified, this class will 5274 * have the following behaviours: 5275 * <ul> 5276 * <li>The display name will always be given as the RFC822 style, no matter what 5277 * style is requested 5278 * <li>The id will also be returned as the RFC822 style display name 5279 * <li>When the offset is explicitly given, this class will assume the time zone 5280 * does not support daylight savings time, and the offsets will be calculated 5281 * the same way year round. 5282 * <li>When the offset is explicitly given, the inDaylightSavings() method will 5283 * always return false. 5284 * <li>When the id "local" is given, this class will attempt to determine the 5285 * daylight savings time settings by examining the offset from UTC on Jan 1 5286 * and June 1 of the current year. If they are different, this class assumes 5287 * that the local time zone uses DST. When the offset for a particular date is 5288 * requested, it will use the built-in Javascript support to determine the 5289 * offset for that date. 5290 * </ul> 5291 * 5292 * If a more specific time zone is 5293 * needed with display names and known start/stop times for DST, use the "id" 5294 * property instead to specify the time zone exactly. You can perhaps ask the 5295 * user which time zone they prefer so that your app does not need to guess.<p> 5296 * 5297 * If the id and the offset are both not given, the default time zone for the 5298 * locale is retrieved from 5299 * the locale info. If the locale is not specified, the default locale for the 5300 * library is used.<p> 5301 * 5302 * Because this class was designed for use in web sites, and the vast majority 5303 * of dates and times being formatted are recent date/times, this class is simplified 5304 * by not implementing historical time zones. That is, when governments change the 5305 * time zone rules for a particular zone, only the latest such rule is implemented 5306 * in this class. That means that determining the offset for a date that is prior 5307 * to the last change may give the wrong result. Historical time zone calculations 5308 * may be implemented in a later version of iLib if there is enough demand for it, 5309 * but it would entail a much larger set of time zone data that would have to be 5310 * loaded. 5311 * 5312 * Depends directive: !depends timezone.js 5313 * 5314 * @constructor 5315 * @param {Object} options Options guiding the construction of this time zone instance 5316 */ 5317 ilib.TimeZone = function(options) { 5318 this.sync = true; 5319 this.locale = new ilib.Locale(); 5320 this.isLocal = false; 5321 5322 if (options) { 5323 if (options.locale) { 5324 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 5325 } 5326 5327 if (options.id) { 5328 var id = options.id.toString(); 5329 if (id === 'local') { 5330 this.isLocal = true; 5331 5332 // use standard Javascript Date to figure out the time zone offsets 5333 var now = new Date(), 5334 jan1 = new Date(now.getFullYear(), 0, 1), // months in std JS Date object are 0-based 5335 jun1 = new Date(now.getFullYear(), 5, 1); 5336 5337 // Javascript's method returns the offset backwards, so we have to 5338 // take the negative to get the correct offset 5339 this.offsetJan1 = -jan1.getTimezoneOffset(); 5340 this.offsetJun1 = -jun1.getTimezoneOffset(); 5341 // the offset of the standard time for the time zone is always the one that is closest 5342 // to negative infinity of the two, no matter whether you are in the northern or southern 5343 // hemisphere, east or west 5344 this.offset = Math.min(this.offsetJan1, this.offsetJun1); 5345 } 5346 this.id = id; 5347 } else if (options.offset) { 5348 this.offset = (typeof(options.offset) === 'string') ? parseInt(options.offset, 10) : options.offset; 5349 this.id = this.getDisplayName(undefined, undefined); 5350 } 5351 5352 if (typeof(options.sync) !== 'undefined') { 5353 this.sync = !!options.sync; 5354 } 5355 5356 this.loadParams = options.loadParams; 5357 this.onLoad = options.onLoad; 5358 } 5359 5360 //console.log("timezone: locale is " + this.locale); 5361 5362 if (!this.id) { 5363 new ilib.LocaleInfo(this.locale, { 5364 sync: this.sync, 5365 onLoad: ilib.bind(this, function (li) { 5366 this.id = li.getTimeZone() || "Etc/UTC"; 5367 this._loadtzdata(); 5368 }) 5369 }); 5370 } else { 5371 this._loadtzdata(); 5372 } 5373 5374 //console.log("localeinfo is: " + JSON.stringify(this.locinfo)); 5375 //console.log("id is: " + JSON.stringify(this.id)); 5376 }; 5377 5378 /* 5379 * Explanation of the compressed time zone info properties. 5380 * { 5381 * "o": "8:0", // offset from UTC 5382 * "f": "W{c}T", // standard abbreviation. For time zones that observe DST, the {c} replacement is replaced with the 5383 * // letter in the e.c or s.c properties below 5384 * "e": { // info about the end of DST 5385 * "j": 78322.5 // Julian day when the transition happens. Either specify the "j" property or all of the "m", "r", and 5386 * // "t" properties, but not both sets. 5387 * "m": 3, // month that it ends 5388 * "r": "l0", // rule for the day it ends "l" = "last", numbers are Sun=0 through Sat=6. Other syntax is "0>7". 5389 * // This means the 0-day (Sun) after the 7th of the month. Other possible operators are <, >, <=, >= 5390 * "t": "2:0", // time of day that the DST turns off, hours:minutes 5391 * "c": "S" // character to replace into the abbreviation for standard time 5392 * }, 5393 * "s": { // info about the start of DST 5394 * "j": 78189.5 // Julian day when the transition happens. Either specify the "j" property or all of the "m", "r", and 5395 * // "t" properties, but not both sets. 5396 * "m": 10, // month that it starts 5397 * "r": "l0", // rule for the day it starts "l" = "last", numbers are Sun=0 through Sat=6. Other syntax is "0>7". 5398 * // This means the 0-day (Sun) after the 7th of the month. Other possible operators are <, >, <=, >= 5399 * "t": "2:0", // time of day that the DST turns on, hours:minutes 5400 * "v": "1:0", // amount of time saved in hours:minutes 5401 * "c": "D" // character to replace into the abbreviation for daylight time 5402 * }, 5403 * "c": "AU", // ISO code for the country that contains this time zone 5404 * "n": "W. Australia {c} Time" 5405 * // long English name of the zone. The {c} replacement is for the word "Standard" or "Daylight" as appropriate 5406 * } 5407 */ 5408 ilib.TimeZone.prototype._loadtzdata = function () { 5409 // console.log("id is: " + JSON.stringify(this.id)); 5410 // console.log("zoneinfo is: " + JSON.stringify(ilib.data.zoneinfo[this.id])); 5411 if (!ilib.data.zoneinfo[this.id] && typeof(this.offset) === 'undefined') { 5412 ilib.loadData({ 5413 object: ilib.TimeZone, 5414 nonlocale: true, // locale independent 5415 name: "zoneinfo/" + this.id + ".json", 5416 sync: this.sync, 5417 loadParams: this.loadParams, 5418 callback: ilib.bind(this, function (tzdata) { 5419 if (tzdata && !ilib.isEmpty(tzdata)) { 5420 ilib.data.zoneinfo[this.id] = tzdata; 5421 } 5422 this._initZone(); 5423 }) 5424 }); 5425 } else { 5426 this._initZone(); 5427 } 5428 }; 5429 5430 ilib.TimeZone.prototype._initZone = function() { 5431 /** 5432 * @private 5433 * @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}} 5434 */ 5435 this.zone = ilib.data.zoneinfo[this.id]; 5436 if (!this.zone && typeof(this.offset) === 'undefined') { 5437 this.id = "Etc/UTC"; 5438 this.zone = ilib.data.zoneinfo[this.id]; 5439 } 5440 5441 this._calcDSTSavings(); 5442 5443 if (typeof(this.offset) === 'undefined' && this.zone.o) { 5444 var offsetParts = this._offsetStringToObj(this.zone.o); 5445 /** 5446 * @private 5447 * @type {number} raw offset from UTC without DST, in minutes 5448 */ 5449 this.offset = (Math.abs(offsetParts.h || 0) * 60 + (offsetParts.m || 0)) * ilib.signum(offsetParts.h || 0); 5450 } 5451 5452 if (this.onLoad && typeof(this.onLoad) === 'function') { 5453 this.onLoad(this); 5454 } 5455 }; 5456 5457 ilib.data.timezone = {}; 5458 5459 /** 5460 * Return an array of available zone ids that the constructor knows about. 5461 * The country parameter is optional. If it is not given, all time zones will 5462 * be returned. If it specifies a country code, then only time zones for that 5463 * country will be returned. 5464 * 5465 * @param {string} country country code for which time zones are being sought 5466 * @return {Array.<string>} an array of zone id strings 5467 */ 5468 ilib.TimeZone.getAvailableIds = function (country) { 5469 var tz, ids = []; 5470 5471 if (!ilib.data.timezone.list) { 5472 ilib.data.timezone.list = []; 5473 if (ilib._load instanceof ilib.Loader) { 5474 var hash = ilib._load.listAvailableFiles(); 5475 for (var dir in hash) { 5476 var files = hash[dir]; 5477 if (typeof(files) === 'object' && files instanceof Array) { 5478 files.forEach(function (filename) { 5479 if (filename && filename.match(/^zoneinfo/)) { 5480 ilib.data.timezone.list.push(filename.replace(/^zoneinfo\//, "").replace(/\.json$/, "")); 5481 } 5482 }); 5483 } 5484 } 5485 } else { 5486 for (tz in ilib.data.zoneinfo) { 5487 if (ilib.data.zoneinfo[tz]) { 5488 ilib.data.timezone.list.push(tz); 5489 } 5490 } 5491 } 5492 } 5493 5494 if (!country) { 5495 // special zone meaning "the local time zone according to the JS engine we are running upon" 5496 ids.push("local"); 5497 for (tz in ilib.data.timezone.list) { 5498 if (ilib.data.timezone.list[tz]) { 5499 ids.push(ilib.data.timezone.list[tz]); 5500 } 5501 } 5502 } else { 5503 if (!ilib.data.zoneinfo.zonetab) { 5504 ilib.loadData({ 5505 object: ilib.TimeZone, 5506 nonlocale: true, // locale independent 5507 name: "zoneinfo/zonetab.json", 5508 sync: true, 5509 callback: ilib.bind(this, function (tzdata) { 5510 if (tzdata) { 5511 ilib.data.zoneinfo.zonetab = tzdata; 5512 } 5513 }) 5514 }); 5515 } 5516 ids = ilib.data.zoneinfo.zonetab[country]; 5517 } 5518 5519 return ids; 5520 }; 5521 5522 /** 5523 * Return the id used to uniquely identify this time zone. 5524 * @return {string} a unique id for this time zone 5525 */ 5526 ilib.TimeZone.prototype.getId = function () { 5527 return this.id.toString(); 5528 }; 5529 5530 /** 5531 * Return the abbreviation that is used for the current time zone on the given date. 5532 * The date may be in DST or during standard time, and many zone names have different 5533 * abbreviations depending on whether or not the date is falls within DST.<p> 5534 * 5535 * There are two styles that are supported: 5536 * 5537 * <ol> 5538 * <li>standard - returns the 3 to 5 letter abbreviation of the time zone name such 5539 * as "CET" for "Central European Time" or "PDT" for "Pacific Daylight Time" 5540 * <li>rfc822 - returns an RFC 822 style time zone specifier, which specifies more 5541 * explicitly what the offset is from UTC 5542 * <li>long - returns the long name of the zone in English 5543 * </ol> 5544 * 5545 * @param {ilib.Date=} date a date to determine if it is in daylight time or standard time 5546 * @param {string=} style one of "standard" or "rfc822". Default if not specified is "standard" 5547 * @return {string} the name of the time zone, abbreviated according to the style 5548 */ 5549 ilib.TimeZone.prototype.getDisplayName = function (date, style) { 5550 style = (this.isLocal || typeof(this.zone) === 'undefined') ? "rfc822" : (style || "standard"); 5551 switch (style) { 5552 default: 5553 case 'standard': 5554 if (this.zone.f && this.zone.f !== "zzz") { 5555 if (this.zone.f.indexOf("{c}") !== -1) { 5556 var letter = ""; 5557 letter = this.inDaylightTime(date) ? this.zone.s && this.zone.s.c : this.zone.e && this.zone.e.c; 5558 var temp = new ilib.String(this.zone.f); 5559 return temp.format({c: letter || ""}); 5560 } 5561 return this.zone.f; 5562 } 5563 var temp = "GMT" + this.zone.o; 5564 if (this.inDaylightTime(date)) { 5565 temp += "+" + this.zone.s.v; 5566 } 5567 return temp; 5568 break; 5569 case 'rfc822': 5570 var offset = this.getOffset(date), // includes the DST if applicable 5571 ret = "UTC", 5572 hour = offset.h || 0, 5573 minute = offset.m || 0; 5574 5575 if (hour !== 0) { 5576 ret += (hour > 0) ? "+" : "-"; 5577 if (Math.abs(hour) < 10) { 5578 ret += "0"; 5579 } 5580 ret += (hour < 0) ? -hour : hour; 5581 if (minute < 10) { 5582 ret += "0"; 5583 } 5584 ret += minute; 5585 } 5586 return ret; 5587 case 'long': 5588 if (this.zone.n) { 5589 if (this.zone.n.indexOf("{c}") !== -1) { 5590 var str = this.inDaylightTime(date) ? "Daylight" : "Standard"; 5591 var temp = new ilib.String(this.zone.n); 5592 return temp.format({c: str || ""}); 5593 } 5594 return this.zone.n; 5595 } 5596 var temp = "GMT" + this.zone.o; 5597 if (this.inDaylightTime(date)) { 5598 temp += "+" + this.zone.s.v; 5599 } 5600 return temp; 5601 break; 5602 } 5603 }; 5604 5605 /** 5606 * Convert the offset string to an object with an h, m, and possibly s property 5607 * to indicate the hours, minutes, and seconds. 5608 * 5609 * @private 5610 * @param {string} str the offset string to convert to an object 5611 * @return {Object.<{h:number,m:number,s:number}>} an object giving the offset for the zone at 5612 * the given date/time, in hours, minutes, and seconds 5613 */ 5614 ilib.TimeZone.prototype._offsetStringToObj = function (str) { 5615 var offsetParts = (typeof(str) === 'string') ? str.split(":") : [], 5616 ret = {h:0}, 5617 temp; 5618 5619 if (offsetParts.length > 0) { 5620 ret.h = parseInt(offsetParts[0], 10); 5621 if (offsetParts.length > 1) { 5622 temp = parseInt(offsetParts[1], 10); 5623 if (temp) { 5624 ret.m = temp; 5625 } 5626 if (offsetParts.length > 2) { 5627 temp = parseInt(offsetParts[2], 10); 5628 if (temp) { 5629 ret.s = temp; 5630 } 5631 } 5632 } 5633 } 5634 5635 return ret; 5636 }; 5637 5638 /** 5639 * Returns the offset of this time zone from UTC at the given date/time. If daylight saving 5640 * time is in effect at the given date/time, this method will return the offset value 5641 * adjusted by the amount of daylight saving. 5642 * @param {ilib.Date=} date the date for which the offset is needed 5643 * @return {Object.<{h:number,m:number}>} an object giving the offset for the zone at 5644 * the given date/time, in hours, minutes, and seconds 5645 */ 5646 ilib.TimeZone.prototype.getOffset = function (date) { 5647 if (!date) { 5648 return this.getRawOffset(); 5649 } 5650 var offset = this.getOffsetMillis(date)/60000; 5651 5652 var hours = ilib._roundFnc.down(offset/60), 5653 minutes = Math.abs(offset) - Math.abs(hours)*60; 5654 5655 var ret = { 5656 h: hours 5657 }; 5658 if (minutes != 0) { 5659 ret.m = minutes; 5660 } 5661 return ret; 5662 }; 5663 5664 /** 5665 * Returns the offset of this time zone from UTC at the given date/time expressed in 5666 * milliseconds. If daylight saving 5667 * time is in effect at the given date/time, this method will return the offset value 5668 * adjusted by the amount of daylight saving. Negative numbers indicate offsets west 5669 * of UTC and conversely, positive numbers indicate offset east of UTC. 5670 * 5671 * @param {ilib.Date=} date the date for which the offset is needed, or null for the 5672 * present date 5673 * @return {number} the number of milliseconds of offset from UTC that the given date is 5674 */ 5675 ilib.TimeZone.prototype.getOffsetMillis = function (date) { 5676 var ret; 5677 5678 // check if the dst property is defined -- the intrinsic JS Date object doesn't work so 5679 // well if we are in the overlap time at the end of DST 5680 if (this.isLocal && typeof(date.dst) === 'undefined') { 5681 var d = (!date) ? new Date() : new Date(date.getTimeExtended()); 5682 return -d.getTimezoneOffset() * 60000; 5683 } 5684 5685 ret = this.offset; 5686 5687 if (date && this.inDaylightTime(date)) { 5688 ret += this.dstSavings; 5689 } 5690 5691 return ret * 60000; 5692 }; 5693 5694 /** 5695 * Return the offset in milliseconds when the date has an RD number in wall 5696 * time rather than in UTC time. 5697 * @protected 5698 * @param date the date to check in wall time 5699 * @returns {number} the number of milliseconds of offset from UTC that the given date is 5700 */ 5701 ilib.TimeZone.prototype._getOffsetMillisWallTime = function (date) { 5702 var ret; 5703 5704 ret = this.offset; 5705 5706 if (date && this.inDaylightTime(date, true)) { 5707 ret += this.dstSavings; 5708 } 5709 5710 return ret * 60000; 5711 }; 5712 5713 /** 5714 * Returns the offset of this time zone from UTC at the given date/time. If daylight saving 5715 * time is in effect at the given date/time, this method will return the offset value 5716 * adjusted by the amount of daylight saving. 5717 * @param {ilib.Date=} date the date for which the offset is needed 5718 * @return {string} the offset for the zone at the given date/time as a string in the 5719 * format "h:m:s" 5720 */ 5721 ilib.TimeZone.prototype.getOffsetStr = function (date) { 5722 var offset = this.getOffset(date), 5723 ret; 5724 5725 ret = offset.h; 5726 if (typeof(offset.m) !== 'undefined') { 5727 ret += ":" + offset.m; 5728 if (typeof(offset.s) !== 'undefined') { 5729 ret += ":" + offset.s; 5730 } 5731 } else { 5732 ret += ":0"; 5733 } 5734 5735 return ret; 5736 }; 5737 5738 /** 5739 * Gets the offset from UTC for this time zone. 5740 * @return {Object.<{h:number,m:number,s:number}>} an object giving the offset from 5741 * UTC for this time zone, in hours, minutes, and seconds 5742 */ 5743 ilib.TimeZone.prototype.getRawOffset = function () { 5744 var hours = ilib._roundFnc.down(this.offset/60), 5745 minutes = Math.abs(this.offset) - Math.abs(hours)*60; 5746 5747 var ret = { 5748 h: hours 5749 }; 5750 if (minutes != 0) { 5751 ret.m = minutes; 5752 } 5753 return ret; 5754 }; 5755 5756 /** 5757 * Gets the offset from UTC for this time zone expressed in milliseconds. Negative numbers 5758 * indicate zones west of UTC, and positive numbers indicate zones east of UTC. 5759 * 5760 * @return {number} an number giving the offset from 5761 * UTC for this time zone in milliseconds 5762 */ 5763 ilib.TimeZone.prototype.getRawOffsetMillis = function () { 5764 return this.offset * 60000; 5765 }; 5766 5767 /** 5768 * Gets the offset from UTC for this time zone without DST savings. 5769 * @return {string} the offset from UTC for this time zone, in the format "h:m:s" 5770 */ 5771 ilib.TimeZone.prototype.getRawOffsetStr = function () { 5772 var off = this.getRawOffset(); 5773 return off.h + ":" + (off.m || "0"); 5774 }; 5775 5776 /** 5777 * Return the amount of time in hours:minutes that the clock is advanced during 5778 * daylight savings time. 5779 * @return {Object.<{h:number,m:number,s:number}>} the amount of time that the 5780 * clock advances for DST in hours, minutes, and seconds 5781 */ 5782 ilib.TimeZone.prototype.getDSTSavings = function () { 5783 if (this.isLocal) { 5784 // take the absolute because the difference in the offsets may be positive or 5785 // negative, depending on the hemisphere 5786 var savings = Math.abs(this.offsetJan1 - this.offsetJun1); 5787 var hours = ilib._roundFnc.down(savings/60), 5788 minutes = savings - hours*60; 5789 return { 5790 h: hours, 5791 m: minutes 5792 }; 5793 } else if (this.zone && this.zone.s) { 5794 return this._offsetStringToObj(this.zone.s.v); // this.zone.start.savings 5795 } 5796 return {h:0}; 5797 }; 5798 5799 /** 5800 * Return the amount of time in hours:minutes that the clock is advanced during 5801 * daylight savings time. 5802 * @return {string} the amount of time that the clock advances for DST in the 5803 * format "h:m:s" 5804 */ 5805 ilib.TimeZone.prototype.getDSTSavingsStr = function () { 5806 if (this.isLocal) { 5807 var savings = this.getDSTSavings(); 5808 return savings.h + ":" + savings.m; 5809 } else if (typeof(this.offset) !== 'undefined' && this.zone && this.zone.s) { 5810 return this.zone.s.v; // this.zone.start.savings 5811 } 5812 return "0:0"; 5813 }; 5814 5815 /** 5816 * return the rd of the start of DST transition for the given year 5817 * @protected 5818 * @param {Object} rule set of rules 5819 * @param {number} year year to check 5820 * @return {number} the rd of the start of DST for the year 5821 */ 5822 ilib.TimeZone.prototype._calcRuleStart = function (rule, year) { 5823 var type = "=", 5824 weekday = 0, 5825 day, 5826 refDay, 5827 cal, 5828 hour = 0, 5829 minute = 0, 5830 second = 0, 5831 time, 5832 i; 5833 5834 if (typeof(rule.j) !== 'undefined') { 5835 refDay = new ilib.Date.GregRataDie({ 5836 julianday: rule.j 5837 }); 5838 } else { 5839 if (rule.r.charAt(0) == 'l' || rule.r.charAt(0) == 'f') { 5840 cal = ilib.Cal.newInstance({type: "gregorian"}); 5841 type = rule.r.charAt(0); 5842 weekday = parseInt(rule.r.substring(1), 10); 5843 day = (type === 'l') ? cal.getMonLength(rule.m, year) : 1; 5844 //console.log("_calcRuleStart: Calculating the " + 5845 // (rule.r.charAt(0) == 'f' ? "first " : "last ") + weekday + 5846 // " of month " + rule.m); 5847 } else { 5848 i = rule.r.indexOf('<'); 5849 if (i == -1) { 5850 i = rule.r.indexOf('>'); 5851 } 5852 5853 if (i != -1) { 5854 type = rule.r.charAt(i); 5855 weekday = parseInt(rule.r.substring(0, i), 10); 5856 day = parseInt(rule.r.substring(i+1), 10); 5857 //console.log("_calcRuleStart: Calculating the " + weekday + 5858 // type + day + " of month " + rule.m); 5859 } else { 5860 day = parseInt(rule.r, 10); 5861 //console.log("_calcRuleStart: Calculating the " + day + " of month " + rule.m); 5862 } 5863 } 5864 5865 if (rule.t) { 5866 time = rule.t.split(":"); 5867 hour = parseInt(time[0], 10); 5868 if (time.length > 1) { 5869 minute = parseInt(time[1], 10); 5870 if (time.length > 2) { 5871 second = parseInt(time[2], 10); 5872 } 5873 } 5874 } 5875 //console.log("calculating rd of " + year + "/" + rule.m + "/" + day); 5876 refDay = new ilib.Date.GregRataDie({ 5877 year: year, 5878 month: rule.m, 5879 day: day, 5880 hour: hour, 5881 minute: minute, 5882 second: second 5883 }); 5884 } 5885 //console.log("refDay is " + JSON.stringify(refDay)); 5886 var d = refDay.getRataDie(); 5887 5888 switch (type) { 5889 case 'l': 5890 case '<': 5891 //console.log("returning " + refDay.onOrBefore(rd, weekday)); 5892 d = refDay.onOrBefore(weekday); 5893 break; 5894 case 'f': 5895 case '>': 5896 //console.log("returning " + refDay.onOrAfterRd(rd, weekday)); 5897 d = refDay.onOrAfter(weekday); 5898 break; 5899 } 5900 return d; 5901 }; 5902 5903 /** 5904 * @private 5905 */ 5906 ilib.TimeZone.prototype._calcDSTSavings = function () { 5907 var saveParts = this.getDSTSavings(); 5908 5909 /** 5910 * @private 5911 * @type {number} savings in minutes when DST is in effect 5912 */ 5913 this.dstSavings = (Math.abs(saveParts.h || 0) * 60 + (saveParts.m || 0)) * ilib.signum(saveParts.h || 0); 5914 }; 5915 5916 /** 5917 * @private 5918 */ 5919 ilib.TimeZone.prototype._getDSTStartRule = function (year) { 5920 // TODO: update this when historic/future zones are supported 5921 return this.zone.s; 5922 }; 5923 5924 /** 5925 * @private 5926 */ 5927 ilib.TimeZone.prototype._getDSTEndRule = function (year) { 5928 // TODO: update this when historic/future zones are supported 5929 return this.zone.e; 5930 }; 5931 5932 /** 5933 * Returns whether or not the given date is in daylight saving time for the current 5934 * zone. Note that daylight savings time is observed for the summer. Because 5935 * the seasons are reversed, daylight savings time in the southern hemisphere usually 5936 * runs from the end of the year through New Years into the first few months of the 5937 * next year. This method will correctly calculate the start and end of DST for any 5938 * location. 5939 * 5940 * @param {ilib.Date=} date a date for which the info about daylight time is being sought, 5941 * or undefined to tell whether we are currently in daylight savings time 5942 * @param {boolean=} wallTime if true, then the given date is in wall time. If false or 5943 * undefined, it is in the usual UTC time. 5944 * @return {boolean} true if the given date is in DST for the current zone, and false 5945 * otherwise. 5946 */ 5947 ilib.TimeZone.prototype.inDaylightTime = function (date, wallTime) { 5948 var rd, startRd, endRd; 5949 5950 if (this.isLocal) { 5951 // check if the dst property is defined -- the intrinsic JS Date object doesn't work so 5952 // well if we are in the overlap time at the end of DST, so we have to work around that 5953 // problem by adding in the savings ourselves 5954 var offset = 0; 5955 if (typeof(date.dst) !== 'undefined' && !date.dst) { 5956 offset = this.dstSavings * 60000; 5957 } 5958 5959 var d = new Date(date ? date.getTimeExtended() + offset: undefined); 5960 // the DST offset is always the one that is closest to positive infinity, no matter 5961 // if you are in the northern or southern hemisphere, east or west 5962 var dst = Math.max(this.offsetJan1, this.offsetJun1); 5963 return (-d.getTimezoneOffset() === dst); 5964 } 5965 5966 if (!date) { 5967 date = new ilib.Date.GregDate(); // right now 5968 } else if (!(date instanceof ilib.Date.GregDate)) { 5969 // convert to Gregorian so that we can tell if it is in DST or not 5970 date = new ilib.Date.GregDate({ 5971 julianday: date.getJulianDay(), 5972 timezone: date.getTimeZone() 5973 }); 5974 } 5975 5976 // if we aren't using daylight time in this zone for the given year, then we are 5977 // not in daylight time 5978 if (!this.useDaylightTime(date.year)) { 5979 return false; 5980 } 5981 5982 // this should be a Gregorian RD number now, in UTC 5983 rd = date.rd.getRataDie(); 5984 5985 // these calculate the start/end in local wall time 5986 var startrule = this._getDSTStartRule(date.year); 5987 var endrule = this._getDSTEndRule(date.year); 5988 startRd = this._calcRuleStart(startrule, date.year); 5989 endRd = this._calcRuleStart(endrule, date.year); 5990 5991 if (wallTime) { 5992 // rd is in wall time, so we have to make sure to skip the missing time 5993 // at the start of DST when standard time ends and daylight time begins 5994 startRd += this.dstSavings/1440; 5995 } else { 5996 // rd is in UTC, so we have to convert the start/end to UTC time so 5997 // that they can be compared directly to the UTC rd number of the date 5998 5999 // when DST starts, time is standard time already, so we only have 6000 // to subtract the offset to get to UTC and not worry about the DST savings 6001 startRd -= this.offset/1440; 6002 6003 // when DST ends, time is in daylight time already, so we have to 6004 // subtract the DST savings to get back to standard time, then the 6005 // offset to get to UTC 6006 endRd -= (this.offset + this.dstSavings)/1440; 6007 } 6008 6009 // In the northern hemisphere, the start comes first some time in spring (Feb-Apr), 6010 // then the end some time in the fall (Sept-Nov). In the southern 6011 // hemisphere, it is the other way around because the seasons are reversed. Standard 6012 // time is still in the winter, but the winter months are May-Aug, and daylight 6013 // savings time usually starts Aug-Oct of one year and runs through Mar-May of the 6014 // next year. 6015 if (rd < endRd && endRd - rd <= this.dstSavings/1440 && typeof(date.dst) === 'boolean') { 6016 // take care of the magic overlap time at the end of DST 6017 return date.dst; 6018 } 6019 if (startRd < endRd) { 6020 // northern hemisphere 6021 return (rd >= startRd && rd < endRd) ? true : false; 6022 } 6023 // southern hemisphere 6024 return (rd >= startRd || rd < endRd) ? true : false; 6025 }; 6026 6027 /** 6028 * Returns true if this time zone switches to daylight savings time at some point 6029 * in the year, and false otherwise. 6030 * @param {number} year Whether or not the time zone uses daylight time in the given year. If 6031 * this parameter is not given, the current year is assumed. 6032 * @return {boolean} true if the time zone uses daylight savings time 6033 */ 6034 ilib.TimeZone.prototype.useDaylightTime = function (year) { 6035 6036 // this zone uses daylight savings time iff there is a rule defining when to start 6037 // and when to stop the DST 6038 return (this.isLocal && this.offsetJan1 !== this.offsetJun1) || 6039 (typeof(this.zone) !== 'undefined' && 6040 typeof(this.zone.s) !== 'undefined' && 6041 typeof(this.zone.e) !== 'undefined'); 6042 }; 6043 6044 /** 6045 * Returns the ISO 3166 code of the country for which this time zone is defined. 6046 * @return {string} the ISO 3166 code of the country for this zone 6047 */ 6048 ilib.TimeZone.prototype.getCountry = function () { 6049 return this.zone.c; 6050 }; 6051 /* 6052 * resources.js - Resource bundle definition 6053 * 6054 * Copyright © 2012-2014, JEDLSoft 6055 * 6056 * Licensed under the Apache License, Version 2.0 (the "License"); 6057 * you may not use this file except in compliance with the License. 6058 * You may obtain a copy of the License at 6059 * 6060 * http://www.apache.org/licenses/LICENSE-2.0 6061 * 6062 * Unless required by applicable law or agreed to in writing, software 6063 * distributed under the License is distributed on an "AS IS" BASIS, 6064 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6065 * 6066 * See the License for the specific language governing permissions and 6067 * limitations under the License. 6068 */ 6069 6070 // !depends ilibglobal.js locale.js localeinfo.js strings.js util/utils.js 6071 6072 // !data pseudomap 6073 6074 /** 6075 * @class 6076 * Create a new resource bundle instance. The resource bundle loads strings 6077 * appropriate for a particular locale and provides them via the getString 6078 * method.<p> 6079 * 6080 * The options object may contain any (or none) of the following properties: 6081 * 6082 * <ul> 6083 * <li><i>locale</i> - The locale of the strings to load. If not specified, the default 6084 * locale is the the default for the web page or app in which the bundle is 6085 * being loaded. 6086 * 6087 * <li><i>name</i> - Base name of the resource bundle to load. If not specified the default 6088 * base name is "resources". 6089 * 6090 * <li><i>type</i> - Name the type of strings this bundle contains. Valid values are 6091 * "xml", "html", "text", or "raw". The default is "text". If the type is "xml" or "html", 6092 * then XML/HTML entities and tags are not pseudo-translated. During a real translation, 6093 * HTML character entities are translated to their corresponding characters in a source 6094 * string before looking that string up in the translations. Also, the characters "<", ">", 6095 * and "&" are converted to entities again in the output, but characters are left as they 6096 * are. If the type is "xml", "html", or "text" types, then the replacement parameter names 6097 * are not pseudo-translated as well so that the output can be used for formatting with 6098 * the ilib.String class. If the type is raw, all characters are pseudo-translated, 6099 * including replacement parameters as well as XML/HTML tags and entities. 6100 * 6101 * <li><i>lengthen</i> - when pseudo-translating the string, tell whether or not to 6102 * automatically lengthen the string to simulate "long" languages such as German 6103 * or French. This is a boolean value. Default is false. 6104 * 6105 * <li><i>missing</i> - what to do when a resource is missing. The choices are: 6106 * <ul> 6107 * <li><i>source</i> - return the source string unchanged 6108 * <li><i>pseudo</i> - return the pseudo-translated source string, translated to the 6109 * script of the locale if the mapping is available, or just the default Latin 6110 * pseudo-translation if not 6111 * <li><i>empty</i> - return the empty string 6112 * </ul> 6113 * The default behaviour is the same as before, which is to return the source string 6114 * unchanged. 6115 * 6116 * <li><i>onLoad</i> - a callback function to call when the resources are fully 6117 * loaded. When the onLoad option is given, this class will attempt to 6118 * load any missing locale data using the ilib loader callback. 6119 * When the constructor is done (even if the data is already preassembled), the 6120 * onLoad function is called with the current instance as a parameter, so this 6121 * callback can be used with preassembled or dynamic loading or a mix of the two. 6122 * 6123 * <li>sync - tell whether to load any missing locale data synchronously or 6124 * asynchronously. If this option is given as "false", then the "onLoad" 6125 * callback must be given, as the instance returned from this constructor will 6126 * not be usable for a while. 6127 * 6128 * <li><i>loadParams</i> - an object containing parameters to pass to the 6129 * loader callback function when locale data is missing. The parameters are not 6130 * interpretted or modified in any way. They are simply passed along. The object 6131 * may contain any property/value pairs as long as the calling code is in 6132 * agreement with the loader callback function as to what those parameters mean. 6133 * </ul> 6134 * 6135 * The locale option may be given as a locale spec string or as an 6136 * ilib.Locale object. If the locale option is not specified, then strings for 6137 * the default locale will be loaded.<p> 6138 * 6139 * The name option can be used to put groups of strings together in a 6140 * single bundle. The strings will then appear together in a JS object in 6141 * a JS file that can be included before the ilib.<p> 6142 * 6143 * A resource bundle with a particular name is actually a set of bundles 6144 * that are each specific to a language, a language plus a region, etc. 6145 * All bundles with the same base name should 6146 * contain the same set of source strings, but with different translations for 6147 * the given locale. The user of the bundle does not need to be aware of 6148 * the locale of the bundle, as long as it contains values for the strings 6149 * it needs.<p> 6150 * 6151 * Strings in bundles for a particular locale are inherited from parent bundles 6152 * that are more generic. In general, the hierarchy is as follows (from 6153 * least locale-specific to most locale-specific): 6154 * 6155 * <ol> 6156 * <li> language 6157 * <li> region 6158 * <li> language_script 6159 * <li> language_region 6160 * <li> region_variant 6161 * <li> language_script_region 6162 * <li> language_region_variant 6163 * <li> language_script_region_variant 6164 * </ol> 6165 * 6166 * That is, if the translation for a string does not exist in the current 6167 * locale, the more-generic parent locale is searched for the string. In the 6168 * worst case scenario, the string is not found in the base locale's strings. 6169 * In this case, the missing option guides this class on what to do. If 6170 * the missing option is "source", then the original source is returned as 6171 * the translation. If it is "empty", the empty string is returned. If it 6172 * is "pseudo", then the pseudo-translated string that is appropriate for 6173 * the default script of the locale is returned.<p> 6174 * 6175 * This allows developers to create code with new or changed strings in it and check in that 6176 * code without waiting for the translations to be done first. The translated 6177 * version of the app or web site will still function properly, but will show 6178 * a spurious untranslated string here and there until the translations are 6179 * done and also checked in.<p> 6180 * 6181 * The base is whatever language your developers use to code in. For 6182 * a German web site, strings in the source code may be written in German 6183 * for example. Often this base is English, as many web sites are coded in 6184 * English, but that is not required.<p> 6185 * 6186 * The strings can be extracted with the ilib localization tool (which will be 6187 * shipped at some future time.) Once the strings 6188 * have been translated, the set of translated files can be generated with the 6189 * same tool. The output from the tool can be used as input to the ResBundle 6190 * object. It is up to the web page or app to make sure the JS file that defines 6191 * the bundle is included before creating the ResBundle instance.<p> 6192 * 6193 * A special locale "zxx-XX" is used as the pseudo-translation locale because 6194 * zxx means "no linguistic information" in the ISO 639 standard, and the region 6195 * code XX is defined to be user-defined in the ISO 3166 standard. 6196 * Pseudo-translation is a locale where the translations are generated on 6197 * the fly based on the contents of the source string. Characters in the source 6198 * string are replaced with other characters and returned. 6199 * 6200 * Example. If the source string is: 6201 * 6202 * <pre> 6203 * "This is a string" 6204 * </pre> 6205 * 6206 * then the pseudo-translated version might look something like this: 6207 * 6208 * <pre> 6209 * "Ţħïş ïş á şţřïñĝ" 6210 * </pre> 6211 * <p> 6212 * 6213 * Pseudo-translation can be used to test that your app or web site is translatable 6214 * before an actual translation has happened. These bugs can then be fixed 6215 * before the translation starts, avoiding an explosion of bugs later when 6216 * each language's tester registers the same bug complaining that the same 6217 * string is not translated. When pseudo-localizing with 6218 * the Latin script, this allows the strings to be readable in the UI in the 6219 * source language (if somewhat funky-looking), 6220 * so that a tester can easily verify that the string is properly externalized 6221 * and loaded from a resource bundle without the need to be able to read a 6222 * foreign language.<p> 6223 * 6224 * If one of a list of script tags is given in the pseudo-locale specifier, then the 6225 * pseudo-localization can map characters to very rough transliterations of 6226 * characters in the given script. For example, zxx-Hebr-XX maps strings to 6227 * Hebrew characters, which can be used to test your UI in a right-to-left 6228 * language to catch bidi bugs before a translation is done. Currently, the 6229 * list of target scripts includes Hebrew (Hebr), Chinese Simplified Han (Hans), 6230 * and Cyrillic (Cyrl) with more to be added later. If no script is explicitly 6231 * specified in the locale spec, or if the script is not supported, 6232 * then the default mapping maps Latin base characters to accented versions of 6233 * those Latin characters as in the example above. 6234 * 6235 * When the "lengthen" property is set to true in the options, the 6236 * pseudotranslation code will add digits to the end of the string to simulate 6237 * the lengthening that occurs when translating to other languages. The above 6238 * example will come out like this: 6239 * 6240 * <pre> 6241 * "Ţħïş ïş á şţřïñĝ76543210" 6242 * </pre> 6243 * 6244 * The string is lengthened according to the length of the source string. If 6245 * the source string is less than 20 characters long, the string is lengthened 6246 * by 50%. If the source string is 20-40 6247 * characters long, the string is lengthened by 33%. If te string is greater 6248 * than 40 characters long, the string is lengthened by 20%.<p> 6249 * 6250 * The pseudotranslation always ends a string with the digit "0". If you do 6251 * not see the digit "0" in the UI for your app, you know that truncation 6252 * has occurred, and the number you see at the end of the string tells you 6253 * how many characters were truncated.<p> 6254 * 6255 * Depends directive: !depends resources.js 6256 * 6257 * @constructor 6258 * @param {?Object} options Options controlling how the bundle is created 6259 */ 6260 ilib.ResBundle = function (options) { 6261 var lookupLocale, spec; 6262 6263 this.locale = new ilib.Locale(); // use the default locale 6264 this.baseName = "strings"; 6265 this.type = "text"; 6266 this.loadParams = {}; 6267 this.missing = "source"; 6268 this.sync = true; 6269 6270 if (options) { 6271 if (options.locale) { 6272 this.locale = (typeof(options.locale) === 'string') ? 6273 new ilib.Locale(options.locale) : 6274 options.locale; 6275 } 6276 if (options.name) { 6277 this.baseName = options.name; 6278 } 6279 if (options.type) { 6280 this.type = options.type; 6281 } 6282 this.lengthen = options.lengthen || false; 6283 6284 if (typeof(options.sync) !== 'undefined') { 6285 this.sync = (options.sync == true); 6286 } 6287 6288 if (typeof(options.loadParams) !== 'undefined') { 6289 this.loadParams = options.loadParams; 6290 } 6291 if (typeof(options.missing) !== 'undefined') { 6292 if (options.missing === "pseudo" || options.missing === "empty") { 6293 this.missing = options.missing; 6294 } 6295 } 6296 } 6297 6298 this.map = {}; 6299 6300 if (!ilib.ResBundle[this.baseName]) { 6301 ilib.ResBundle[this.baseName] = {}; 6302 } 6303 6304 lookupLocale = this.locale.isPseudo() ? new ilib.Locale("en-US") : this.locale; 6305 6306 ilib.loadData({ 6307 object: ilib.ResBundle[this.baseName], 6308 locale: lookupLocale, 6309 name: this.baseName + ".json", 6310 sync: this.sync, 6311 loadParams: this.loadParams, 6312 callback: ilib.bind(this, function (map) { 6313 if (!map) { 6314 map = ilib.data[this.baseName] || {}; 6315 spec = lookupLocale.getSpec().replace(/-/g, '_'); 6316 ilib.ResBundle[this.baseName].cache[spec] = map; 6317 } 6318 this.map = map; 6319 if (this.locale.isPseudo()) { 6320 if (!ilib.ResBundle.pseudomap) { 6321 ilib.ResBundle.pseudomap = {}; 6322 } 6323 6324 this._loadPseudo(this.locale, options.onLoad); 6325 } else if (this.missing === "pseudo") { 6326 if (!ilib.ResBundle.pseudomap) { 6327 ilib.ResBundle.pseudomap = {}; 6328 } 6329 6330 new ilib.LocaleInfo(this.locale, { 6331 sync: this.sync, 6332 loadParams: this.loadParams, 6333 onLoad: ilib.bind(this, function (li) { 6334 var pseudoLocale = new ilib.Locale("zxx", "XX", undefined, li.getDefaultScript()); 6335 this._loadPseudo(pseudoLocale, options.onLoad); 6336 }) 6337 }); 6338 } else { 6339 if (options && typeof(options.onLoad) === 'function') { 6340 options.onLoad(this); 6341 } 6342 } 6343 }) 6344 }); 6345 6346 // console.log("Merged resources " + this.locale.toString() + " are: " + JSON.stringify(this.map)); 6347 //if (!this.locale.isPseudo() && ilib.isEmpty(this.map)) { 6348 // console.log("Resources for bundle " + this.baseName + " locale " + this.locale.toString() + " are not available."); 6349 //} 6350 }; 6351 6352 ilib.ResBundle.defaultPseudo = ilib.data.pseudomap || { 6353 "a": "à", 6354 "e": "ë", 6355 "i": "í", 6356 "o": "õ", 6357 "u": "ü", 6358 "y": "ÿ", 6359 "A": "Ã", 6360 "E": "Ë", 6361 "I": "Ï", 6362 "O": "Ø", 6363 "U": "Ú", 6364 "Y": "Ŷ" 6365 }; 6366 6367 ilib.ResBundle.prototype = { 6368 /** 6369 * @protected 6370 */ 6371 _loadPseudo: function (pseudoLocale, onLoad) { 6372 ilib.loadData({ 6373 object: ilib.ResBundle.pseudomap, 6374 locale: pseudoLocale, 6375 name: "pseudomap.json", 6376 sync: this.sync, 6377 loadParams: this.loadParams, 6378 callback: ilib.bind(this, function (map) { 6379 if (!map || ilib.isEmpty(map)) { 6380 map = ilib.ResBundle.defaultPseudo; 6381 var spec = pseudoLocale.getSpec().replace(/-/g, '_'); 6382 ilib.ResBundle.pseudomap.cache[spec] = map; 6383 } 6384 this.pseudomap = map; 6385 if (typeof(onLoad) === 'function') { 6386 onLoad(this); 6387 } 6388 }) 6389 }); 6390 }, 6391 6392 /** 6393 * Return the locale of this resource bundle. 6394 * @return {ilib.Locale} the locale of this resource bundle object 6395 */ 6396 getLocale: function () { 6397 return this.locale; 6398 }, 6399 6400 /** 6401 * Return the name of this resource bundle. This corresponds to the name option 6402 * given to the constructor. 6403 * @return {string} name of the the current instance 6404 */ 6405 getName: function () { 6406 return this.baseName; 6407 }, 6408 6409 /** 6410 * Return the type of this resource bundle. This corresponds to the type option 6411 * given to the constructor. 6412 * @return {string} type of the the current instance 6413 */ 6414 getType: function () { 6415 return this.type; 6416 }, 6417 6418 /* 6419 * @private 6420 * Pseudo-translate a string 6421 */ 6422 pseudo: function (str) { 6423 if (!str) { 6424 return undefined; 6425 } 6426 var ret = "", i; 6427 for (i = 0; i < str.length; i++) { 6428 if (this.type !== "raw") { 6429 if (this.type === "html" || this.type === "xml") { 6430 if (str.charAt(i) === '<') { 6431 ret += str.charAt(i++); 6432 while (i < str.length && str.charAt(i) !== '>') { 6433 ret += str.charAt(i++); 6434 } 6435 if (i < str.length) { 6436 ret += str.charAt(i++); 6437 } 6438 } else if (str.charAt(i) === '&') { 6439 ret += str.charAt(i++); 6440 while (i < str.length && str.charAt(i) !== ';' && str.charAt(i) !== ' ') { 6441 ret += str.charAt(i++); 6442 } 6443 if (i < str.length) { 6444 ret += str.charAt(i++); 6445 } 6446 } 6447 } 6448 if (i < str.length) { 6449 if (str.charAt(i) === '{') { 6450 ret += str.charAt(i++); 6451 while (i < str.length && str.charAt(i) !== '}') { 6452 ret += str.charAt(i++); 6453 } 6454 if (i < str.length) { 6455 ret += str.charAt(i); 6456 } 6457 } else { 6458 ret += this.pseudomap[str.charAt(i)] || str.charAt(i); 6459 } 6460 } 6461 } else { 6462 ret += this.pseudomap[str.charAt(i)] || str.charAt(i); 6463 } 6464 } 6465 if (this.lengthen) { 6466 var add; 6467 if (ret.length <= 20) { 6468 add = Math.round(ret.length / 2); 6469 } else if (ret.length > 20 && ret.length <= 40) { 6470 add = Math.round(ret.length / 3); 6471 } else { 6472 add = Math.round(ret.length / 5); 6473 } 6474 for (i = add-1; i >= 0; i--) { 6475 ret += (i % 10); 6476 } 6477 } 6478 if (this.locale.getScript() === "Hans" || this.locale.getScript() === "Hant" || 6479 this.locale.getScript() === "Hani" || 6480 this.locale.getScript() === "Hrkt" || this.locale.getScript() === "Jpan" || 6481 this.locale.getScript() === "Hira" || this.locale.getScript() === "Kana" ) { 6482 // simulate Asian languages by getting rid of all the spaces 6483 ret = ret.replace(/ /g, ""); 6484 } 6485 return ret; 6486 }, 6487 6488 /* 6489 * @private 6490 * Escape html characters in the output. 6491 */ 6492 escapeXml: function (str) { 6493 str = str.replace(/&/g, '&'); 6494 str = str.replace(/</g, '<'); 6495 str = str.replace(/>/g, '>'); 6496 return str; 6497 }, 6498 6499 /* 6500 * @private 6501 * @param {string} str the string to unescape 6502 */ 6503 unescapeXml: function (str) { 6504 str = str.replace(/&/g, '&'); 6505 str = str.replace(/</g, '<'); 6506 str = str.replace(/>/g, '>'); 6507 return str; 6508 }, 6509 6510 /* 6511 * @private 6512 * Create a key name out of a source string. All this does so far is 6513 * compress sequences of white space into a single space on the assumption 6514 * that this doesn't really change the meaning of the string, and therefore 6515 * all such strings that compress to the same thing should share the same 6516 * translation. 6517 * @param {string} source the source string to make a key out of 6518 */ 6519 makeKey: function (source) { 6520 var key = source.replace(/\s+/gm, ' '); 6521 return (this.type === "xml" || this.type === "html") ? this.unescapeXml(key) : key; 6522 }, 6523 6524 /** 6525 * Return a localized string. If the string is not found in the loaded set of 6526 * resources, the original source string is returned. If the key is not given, 6527 * then the source string itself is used as the key. In the case where the 6528 * source string is used as the key, the whitespace is compressed down to 1 space 6529 * each, and the whitespace at the beginning and end of the string is trimmed.<p> 6530 * 6531 * The escape mode specifies what type of output you are escaping the returned 6532 * string for. Modes are similar to the types: 6533 * 6534 * <ul> 6535 * <li>"html" -- prevents HTML injection by escaping the characters < > and & 6536 * <li>"xml" -- currently same as "html" mode 6537 * <li>"js" -- prevents breaking Javascript syntax by backslash escaping all quote and 6538 * double-quote characters 6539 * <li>"attribute" -- meant for HTML attribute values. Currently this is the same as 6540 * "js" escape mode. 6541 * <li>"default" -- use the type parameter from the constructor as the escape mode as well 6542 * <li>"none" or undefined -- no escaping at all. 6543 * </ul> 6544 * 6545 * The type parameter of the constructor specifies what type of strings this bundle 6546 * is operating upon. This allows pseudo-translation and automatic key generation 6547 * to happen properly by telling this class how to parse the string. The escape mode 6548 * for this method is different in that it specifies how this string will be used in 6549 * the calling code and therefore how to escape it properly.<p> 6550 * 6551 * For example, a section of Javascript code may be constructing an HTML snippet in a 6552 * string to add to the web page. In this case, the type parameter in the constructor should 6553 * be "html" so that the source string can be parsed properly, but the escape mode should 6554 * be "js" so that the output string can be used in Javascript without causing syntax 6555 * errors. 6556 * 6557 * @param {?string=} source the source string to translate 6558 * @param {?string=} key optional name of the key, if any 6559 * @param {?string=} escapeMode escape mode, if any 6560 * @return {ilib.String|undefined} the translation of the given source/key or undefined 6561 * if the translation is not found and the source is undefined 6562 */ 6563 getString: function (source, key, escapeMode) { 6564 if (!source && !key) return new ilib.String(""); 6565 6566 var trans; 6567 if (this.locale.isPseudo()) { 6568 var str = source ? source : this.map[key]; 6569 trans = this.pseudo(str || key); 6570 } else { 6571 var keyName = key || this.makeKey(source); 6572 if (typeof(this.map[keyName]) !== 'undefined') { 6573 trans = this.map[keyName]; 6574 } else if (this.missing === "pseudo") { 6575 trans = this.pseudo(source || key); 6576 } else if (this.missing === "empty") { 6577 trans = ""; 6578 } else { 6579 trans = source; 6580 } 6581 } 6582 6583 if (escapeMode && escapeMode !== "none") { 6584 if (escapeMode == "default") { 6585 escapeMode = this.type; 6586 } 6587 if (escapeMode === "xml" || escapeMode === "html") { 6588 trans = this.escapeXml(trans); 6589 } else if (escapeMode == "js" || escapeMode === "attribute") { 6590 trans = trans.replace(/'/g, "\\\'").replace(/"/g, "\\\""); 6591 } 6592 } 6593 if (trans === undefined) { 6594 return undefined; 6595 } else { 6596 var ret = new ilib.String(trans); 6597 ret.setLocale(this.locale.getSpec(), true, this.loadParams); // no callback 6598 return ret; 6599 } 6600 }, 6601 6602 /** 6603 * Return a localized string as a Javascript object. This does the same thing as 6604 * the getString() method, but it returns a regular Javascript string instead of 6605 * and ilib.String instance. This means it cannot be formatted with the format() 6606 * method without being wrapped in an ilib.String instance first. 6607 * 6608 * @param {?string=} source the source string to translate 6609 * @param {?string=} key optional name of the key, if any 6610 * @param {?string=} escapeMode escape mode, if any 6611 * @return {string|undefined} the translation of the given source/key or undefined 6612 * if the translation is not found and the source is undefined 6613 */ 6614 getStringJS: function(source, key, escapeMode) { 6615 return this.getString(source, key, escapeMode).toString(); 6616 }, 6617 6618 /** 6619 * Return true if the current bundle contains a translation for the given key and 6620 * source. The 6621 * getString method will always return a string for any given key and source 6622 * combination, so it cannot be used to tell if a translation exists. Either one 6623 * or both of the source and key must be specified. If both are not specified, 6624 * this method will return false. 6625 * 6626 * @param {?string=} source source string to look up 6627 * @param {?string=} key key to look up 6628 * @return {boolean} true if this bundle contains a translation for the key, and 6629 * false otherwise 6630 */ 6631 containsKey: function(source, key) { 6632 if (typeof(source) === 'undefined' && typeof(key) === 'undefined') { 6633 return false; 6634 } 6635 6636 var keyName = key || this.makeKey(source); 6637 return typeof(this.map[keyName]) !== 'undefined'; 6638 }, 6639 6640 /** 6641 * Return the merged resources as an entire object. When loading resources for a 6642 * locale that are not just a set of translated strings, but instead an entire 6643 * structured javascript object, you can gain access to that object via this call. This method 6644 * will ensure that all the of the parts of the object are correct for the locale.<p> 6645 * 6646 * For pre-assembled data, it starts by loading <i>ilib.data[name]</i>, where 6647 * <i>name</i> is the base name for this set of resources. Then, it successively 6648 * merges objects in the base data using progressively more locale-specific data. 6649 * It loads it in this order from <i>ilib.data</i>: 6650 * 6651 * <ol> 6652 * <li> language 6653 * <li> region 6654 * <li> language_script 6655 * <li> language_region 6656 * <li> region_variant 6657 * <li> language_script_region 6658 * <li> language_region_variant 6659 * <li> language_script_region_variant 6660 * </ol> 6661 * 6662 * For dynamically loaded data, the code attempts to load the same sequence as 6663 * above, but with slash path separators instead of underscores.<p> 6664 * 6665 * Loading the resources this way allows the program to share resources between all 6666 * locales that share a common language, region, or script. As a 6667 * general rule-of-thumb, resources should be as generic as possible in order to 6668 * cover as many locales as possible. 6669 * 6670 * @return {Object} returns the object that is the basis for this resources instance 6671 */ 6672 getResObj: function () { 6673 return this.map; 6674 } 6675 }; 6676 6677 /* 6678 * util/jsutils.js - Misc utilities to work around Javascript engine differences 6679 * 6680 * Copyright © 2013-2014, JEDLSoft 6681 * 6682 * Licensed under the Apache License, Version 2.0 (the "License"); 6683 * you may not use this file except in compliance with the License. 6684 * You may obtain a copy of the License at 6685 * 6686 * http://www.apache.org/licenses/LICENSE-2.0 6687 * 6688 * Unless required by applicable law or agreed to in writing, software 6689 * distributed under the License is distributed on an "AS IS" BASIS, 6690 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6691 * 6692 * See the License for the specific language governing permissions and 6693 * limitations under the License. 6694 */ 6695 6696 // !depends ilibglobal.js 6697 6698 /** 6699 * Perform a shallow copy of the source object to the target object. This only 6700 * copies the assignments of the source properties to the target properties, 6701 * but not recursively from there.<p> 6702 * 6703 * Depends directive: !depends utils.js 6704 * 6705 * @static 6706 * @param {Object} source the source object to copy properties from 6707 * @param {Object} target the target object to copy properties into 6708 */ 6709 ilib.shallowCopy = function (source, target) { 6710 var prop = undefined; 6711 if (source && target) { 6712 for (prop in source) { 6713 if (prop !== undefined && typeof(source[prop]) !== 'undefined') { 6714 target[prop] = source[prop]; 6715 } 6716 } 6717 } 6718 }; 6719 6720 /** [Need Comment] 6721 * 6722 */ 6723 ilib.deepCopy = function(from, to) { 6724 var prop; 6725 6726 for (prop in from) { 6727 if (prop) { 6728 if (typeof(from[prop]) === 'object') { 6729 to[prop] ={}; 6730 ilib.deepCopy(from[prop], to[prop]); 6731 } else { 6732 to[prop] = from[prop]; 6733 } 6734 } 6735 } 6736 return to; 6737 }; 6738 6739 /** 6740 * Map a string to the given set of alternate characters. If the target set 6741 * does not contain a particular character in the input string, then that 6742 * character will be copied to the output unmapped. 6743 * 6744 * @static 6745 * @param {string} str a string to map to an alternate set of characters 6746 * @param {Array.<string>|Object} map a mapping to alternate characters 6747 * @return {string} the source string where each character is mapped to alternate characters 6748 */ 6749 ilib.mapString = function (str, map) { 6750 var mapped = ""; 6751 if (map && str) { 6752 for (var i = 0; i < str.length; i++) { 6753 var c = str.charAt(i); // TODO use a char iterator? 6754 mapped += map[c] || c; 6755 } 6756 } else { 6757 mapped = str; 6758 } 6759 return mapped; 6760 }; 6761 6762 /** 6763 * Check if an object is a member of the given array. If this javascript engine 6764 * support indexOf, it is used directly. Otherwise, this function implements it 6765 * itself. The idea is to make sure that you can use the quick indexOf if it is 6766 * available, but use a slower implementation in older engines as well. 6767 * 6768 * @static 6769 * @param {Array.<Object>} array array to search 6770 * @param {Object} obj object being sought. This should be of the same type as the 6771 * members of the array being searched. If not, this function will not return 6772 * any results. 6773 * @return {number} index of the object in the array, or -1 if it is not in the array. 6774 */ 6775 ilib.indexOf = function(array, obj) { 6776 if (!array || !obj) { 6777 return -1; 6778 } 6779 if (typeof(array.indexOf) === 'function') { 6780 return array.indexOf(obj); 6781 } else { 6782 for (var i = 0; i < array.length; i++) { 6783 if (array[i] === obj) { 6784 return i; 6785 } 6786 } 6787 return -1; 6788 } 6789 }; 6790 6791 /** 6792 * @static 6793 * Convert a string into the hexadecimal representation 6794 * of the Unicode characters in that string. 6795 * 6796 * @param {string} string The string to convert 6797 * @param {number=} limit the number of digits to use to represent the character (1 to 8) 6798 * @return {string} a hexadecimal representation of the 6799 * Unicode characters in the input string 6800 */ 6801 ilib.toHexString = function(string, limit) { 6802 var i, 6803 result = "", 6804 lim = (limit && limit < 9) ? limit : 4; 6805 6806 if (!string) { 6807 return ""; 6808 } 6809 for (i = 0; i < string.length; i++) { 6810 var ch = string.charCodeAt(i).toString(16); 6811 result += "00000000".substring(0, lim-ch.length) + ch; 6812 } 6813 return result.toUpperCase(); 6814 }; 6815 6816 /* 6817 * datefmt.js - Date formatter definition 6818 * 6819 * Copyright © 2012-2014, JEDLSoft 6820 * 6821 * Licensed under the Apache License, Version 2.0 (the "License"); 6822 * you may not use this file except in compliance with the License. 6823 * You may obtain a copy of the License at 6824 * 6825 * http://www.apache.org/licenses/LICENSE-2.0 6826 * 6827 * Unless required by applicable law or agreed to in writing, software 6828 * distributed under the License is distributed on an "AS IS" BASIS, 6829 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6830 * 6831 * See the License for the specific language governing permissions and 6832 * limitations under the License. 6833 */ 6834 6835 /* 6836 !depends 6837 ilibglobal.js 6838 locale.js 6839 date.js 6840 strings.js 6841 resources.js 6842 calendar.js 6843 localeinfo.js 6844 timezone.js 6845 calendar/gregorian.js 6846 util/jsutils.js 6847 */ 6848 6849 // !data dateformats sysres 6850 6851 /** 6852 * @class 6853 * Create a new date formatter instance. The date formatter is immutable once 6854 * it is created, but can format as many different dates as needed with the same 6855 * options. Create different date formatter instances for different purposes 6856 * and then keep them cached for use later if you have more than one date to 6857 * format.<p> 6858 * 6859 * The options may contain any of the following properties: 6860 * 6861 * <ul> 6862 * <li><i>locale</i> - locale to use when formatting the date/time. If the locale is 6863 * not specified, then the default locale of the app or web page will be used. 6864 * 6865 * <li><i>calendar</i> - the type of calendar to use for this format. The value should 6866 * be a sting containing the name of the calendar. Currently, the supported 6867 * types are "gregorian", "julian", "arabic", "hebrew", or "chinese". If the 6868 * calendar is not specified, then the default calendar for the locale is used. When the 6869 * calendar type is specified, then the format method must be called with an instance of 6870 * the appropriate date type. (eg. Gregorian calendar means that the format method must 6871 * be called with a GregDate instance.) 6872 * 6873 * <li><i>timezone</i> - time zone to use when formatting times. This may be a time zone 6874 * instance or a time zone specifier from the IANA list of time zone database names 6875 * (eg. "America/Los_Angeles"), 6876 * the string "local", or a string specifying the offset in RFC 822 format. The IANA 6877 * list of time zone names can be viewed at 6878 * <a href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">this page</a>. 6879 * If the time zone is given as "local", the offset from UTC as given by 6880 * the Javascript system is used. If the offset is given as an RFC 822 style offset 6881 * specifier, it will parse that string and use the resulting offset. If the time zone 6882 * is not specified, the 6883 * default time zone for the locale is used. If both the date object and this formatter 6884 * instance contain time zones and those time zones are different from each other, the 6885 * formatter will calculate the offset between the time zones and subtract it from the 6886 * date before formatting the result for the current time zone. The theory is that a date 6887 * object that contains a time zone specifies a specific instant in time that is valid 6888 * around the world, whereas a date object without one is a local time and can only be 6889 * used for doing things in the local time zone of the user. 6890 * 6891 * <li><i>type</i> - Specify whether this formatter should format times only, dates only, or 6892 * both times and dates together. Valid values are "time", "date", and "datetime". Note that 6893 * in some locales, the standard format uses the order "time followed by date" and in others, 6894 * the order is exactly opposite, so it is better to create a single "datetime" formatter 6895 * than it is to create a time formatter and a date formatter separately and concatenate the 6896 * results. A "datetime" formatter will get the order correct for the locale.<p> 6897 * 6898 * The default type if none is specified in with the type option is "date". 6899 * 6900 * <li><i>length</i> - Specify the length of the format to use. The length is the approximate size of the 6901 * formatted string. 6902 * 6903 * <ul> 6904 * <li><i>short</i> - use a short representation of the time. This is the most compact format possible for the locale. 6905 * <li><i>medium</i> - use a medium length representation of the time. This is a slightly longer format. 6906 * <li><i>long</i> - use a long representation of the time. This is a fully specified format, but some of the textual 6907 * components may still be abbreviated 6908 * <li><i>full</i> - use a full representation of the time. This is a fully specified format where all the textual 6909 * components are spelled out completely 6910 * </ul> 6911 * 6912 * 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 6913 * format, the month name is textual instead of numeric and is longer, the year is 4 digits instead of 2, and the format 6914 * contains slightly more spaces and formatting characters.<p> 6915 * 6916 * Note that the length parameter does not specify which components are to be formatted. Use the "date" and the "time" 6917 * properties to specify the components. Also, very few of the components of a time format differ according to the length, 6918 * so this property has little to no affect on time formatting. 6919 * 6920 * <li><i>date</i> - This property tells 6921 * which components of a date format to use. For example, 6922 * sometimes you may wish to format a date that only contains the month and date 6923 * without the year, such as when displaying a person's yearly birthday. The value 6924 * of this property allows you to specify only those components you want to see in the 6925 * final output, ordered correctly for the locale. <p> 6926 * 6927 * Valid values are: 6928 * 6929 * <ul> 6930 * <li><i>dmwy</i> - format all components, weekday, date, month, and year 6931 * <li><i>dmy</i> - format only date, month, and year 6932 * <li><i>dmw</i> - format only weekday, date, and month 6933 * <li><i>dm</i> - format only date and month 6934 * <li><i>my</i> - format only month and year 6935 * <li><i>dw</i> - format only the weekday and date 6936 * <li><i>d</i> - format only the date 6937 * <li><i>m</i> - format only the month, in numbers for shorter lengths, and letters for 6938 * longer lengths 6939 * <li><i>n</i> - format only the month, in letters only for all lengths 6940 * <li><i>y</i> - format only the year 6941 * </ul> 6942 * Default components, if this property is not specified, is "dmy". This property may be specified 6943 * but has no affect if the current formatter is for times only. 6944 * 6945 * <li><i>time</i> - This property gives which components of a time format to use. The time will be formatted 6946 * correctly for the locale with only the time components requested. For example, a clock might only display 6947 * the hour and minute and not need the seconds or the am/pm component. In this case, the time property should be set 6948 * to "hm". <p> 6949 * 6950 * Valid values for this property are: 6951 * 6952 * <ul> 6953 * <li><i>ahmsz</i> - format the hours, minutes, seconds, am/pm (if using a 12 hour clock), and the time zone 6954 * <li><i>ahms</i> - format the hours, minutes, seconds, and am/pm (if using a 12 hour clock) 6955 * <li><i>hmsz</i> - format the hours, minutes, seconds, and the time zone 6956 * <li><i>hms</i> - format the hours, minutes, and seconds 6957 * <li><i>ahmz</i> - format the hours, minutes, am/pm (if using a 12 hour clock), and the time zone 6958 * <li><i>ahm</i> - format the hours, minutes, and am/pm (if using a 12 hour clock) 6959 * <li><i>hmz</i> - format the hours, minutes, and the time zone 6960 * <li><i>ah</i> - format only the hours and am/pm if using a 12 hour clock 6961 * <li><i>hm</i> - format only the hours and minutes 6962 * <li><i>ms</i> - format only the minutes and seconds 6963 * <li><i>h</i> - format only the hours 6964 * <li><i>m</i> - format only the minutes 6965 * <li><i>s</i> - format only the seconds 6966 * </ul> 6967 * 6968 * If you want to format a length of time instead of a particular instant 6969 * in time, use the duration formatter object (ilib.DurFmt) instead because this 6970 * formatter is geared towards instants. A date formatter will make sure that each component of the 6971 * time is within the normal range 6972 * for that component. That is, the minutes will always be between 0 and 59, no matter 6973 * what is specified in the date to format. A duration format will allow the number 6974 * of minutes to exceed 59 if, for example, you were displaying the length of 6975 * a movie of 198 minutes.<p> 6976 * 6977 * Default value if this property is not specified is "hma". 6978 * 6979 * <li><i>clock</i> - specify that the time formatter should use a 12 or 24 hour clock. 6980 * Valid values are "12" and "24".<p> 6981 * 6982 * In some locales, both clocks are used. For example, in en_US, the general populace uses 6983 * a 12 hour clock with am/pm, but in the US military or in nautical or aeronautical or 6984 * scientific writing, it is more common to use a 24 hour clock. This property allows you to 6985 * construct a formatter that overrides the default for the locale.<p> 6986 * 6987 * If this property is not specified, the default is to use the most widely used convention 6988 * for the locale. 6989 * 6990 * <li><i>template</i> - use the given template string as a fixed format when formatting 6991 * the date/time. Valid codes to use in a template string are as follows: 6992 * 6993 * <ul> 6994 * <li><i>a</i> - am/pm marker 6995 * <li><i>d</i> - 1 or 2 digit date of month, not padded 6996 * <li><i>dd</i> - 1 or 2 digit date of month, 0 padded to 2 digits 6997 * <li><i>O</i> - ordinal representation of the date of month (eg. "1st", "2nd", etc.) 6998 * <li><i>D</i> - 1 to 3 digit day of year 6999 * <li><i>DD</i> - 1 to 3 digit day of year, 0 padded to 2 digits 7000 * <li><i>DDD</i> - 1 to 3 digit day of year, 0 padded to 3 digits 7001 * <li><i>M</i> - 1 or 2 digit month number, not padded 7002 * <li><i>MM</i> - 1 or 2 digit month number, 0 padded to 2 digits 7003 * <li><i>N</i> - 1 character month name abbreviation 7004 * <li><i>NN</i> - 2 character month name abbreviation 7005 * <li><i>MMM</i> - 3 character month month name abbreviation 7006 * <li><i>MMMM</i> - fully spelled out month name 7007 * <li><i>yy</i> - 2 digit year 7008 * <li><i>yyyy</i> - 4 digit year 7009 * <li><i>E</i> - day-of-week name, abbreviated to a single character 7010 * <li><i>EE</i> - day-of-week name, abbreviated to a max of 2 characters 7011 * <li><i>EEE</i> - day-of-week name, abbreviated to a max of 3 characters 7012 * <li><i>EEEE</i> - day-of-week name fully spelled out 7013 * <li><i>G</i> - era designator 7014 * <li><i>w</i> - week number in year 7015 * <li><i>ww</i> - week number in year, 0 padded to 2 digits 7016 * <li><i>W</i> - week in month 7017 * <li><i>h</i> - hour (12 followed by 1 to 11) 7018 * <li><i>hh</i> - hour (12, followed by 1 to 11), 0 padded to 2 digits 7019 * <li><i>k</i> - hour (1 to 24) 7020 * <li><i>kk</i> - hour (1 to 24), 0 padded to 2 digits 7021 * <li><i>H</i> - hour (0 to 23) 7022 * <li><i>HH</i> - hour (0 to 23), 0 padded to 2 digits 7023 * <li><i>K</i> - hour (0 to 11) 7024 * <li><i>KK</i> - hour (0 to 11), 0 padded to 2 digits 7025 * <li><i>m</i> - minute in hour 7026 * <li><i>mm</i> - minute in hour, 0 padded to 2 digits 7027 * <li><i>s</i> - second in minute 7028 * <li><i>ss</i> - second in minute, 0 padded to 2 digits 7029 * <li><i>S</i> - millisecond (1 to 3 digits) 7030 * <li><i>SSS</i> - millisecond, 0 padded to 3 digits 7031 * <li><i>z</i> - general time zone 7032 * <li><i>Z</i> - RFC 822 time zone 7033 * </ul> 7034 * 7035 * <li><i>useNative</i> - the flag used to determine whether to use the native script settings 7036 * for formatting the numbers. 7037 * 7038 * <li><i>meridiems</i> - string that specifies what style of meridiems to use with this 7039 * format. The choices are "default", "gregorian", "ethiopic", and "chinese". The "default" 7040 * style is often the simple Gregorian AM/PM, but the actual style is chosen by the locale. 7041 * (For almost all locales, the Gregorian AM/PM style is most frequently used.) 7042 * The "ethiopic" style uses 5 different meridiems for "morning", "noon", "afternoon", 7043 * "evening", and "night". The "chinese" style uses 7 different meridiems corresponding 7044 * to the various parts of the day. N.B. Even for the Chinese locales, the default is "gregorian" 7045 * when formatting dates in the Gregorian calendar. 7046 * 7047 * <li><i>onLoad</i> - a callback function to call when the date format object is fully 7048 * loaded. When the onLoad option is given, the DateFmt object will attempt to 7049 * load any missing locale data using the ilib loader callback. 7050 * When the constructor is done (even if the data is already preassembled), the 7051 * onLoad function is called with the current instance as a parameter, so this 7052 * callback can be used with preassembled or dynamic loading or a mix of the two. 7053 * 7054 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 7055 * asynchronously. If this option is given as "false", then the "onLoad" 7056 * callback must be given, as the instance returned from this constructor will 7057 * not be usable for a while. 7058 * 7059 * <li><i>loadParams</i> - an object containing parameters to pass to the 7060 * loader callback function when locale data is missing. The parameters are not 7061 * interpretted or modified in any way. They are simply passed along. The object 7062 * may contain any property/value pairs as long as the calling code is in 7063 * agreement with the loader callback function as to what those parameters mean. 7064 * </ul> 7065 * 7066 * Any substring containing letters within single or double quotes will be used 7067 * as-is in the final output and will not be interpretted for codes as above.<p> 7068 * 7069 * Example: a date format in Spanish might be given as: "'El' d. 'de' MMMM", where 7070 * the 'El' and the 'de' are left as-is in the output because they are quoted. Typical 7071 * output for this example template might be, "El 5. de Mayo". 7072 * 7073 * The following options will be used when formatting a date/time with an explicit 7074 * template: 7075 * 7076 * <ul> 7077 * <li>locale - the locale is only used for 7078 * translations of things like month names or day-of-week names. 7079 * <li>calendar - used to translate a date instance into date/time component values 7080 * that can be formatted into the template 7081 * <li>timezone - used to figure out the offset to add or subtract from the time to 7082 * get the final time component values 7083 * <li>clock - used to figure out whether to format times with a 12 or 24 hour clock. 7084 * If this option is specified, it will override the hours portion of a time format. 7085 * That is, "hh" is switched with "HH" and "kk" is switched with "KK" as appropriate. 7086 * If this option is not specified, the 12/24 code in the template will dictate whether 7087 * to use the 12 or 24 clock, and the 12/24 default in the locale will be ignored. 7088 * </ul> 7089 * 7090 * All other options will be ignored and their corresponding getter methods will 7091 * return the empty string.<p> 7092 * 7093 * Depends directive: !depends datefmt.js 7094 * 7095 * @constructor 7096 * @param {Object} options options governing the way this date formatter instance works 7097 */ 7098 ilib.DateFmt = function(options) { 7099 var arr, i, bad, 7100 sync = true, 7101 loadParams = undefined; 7102 7103 this.locale = new ilib.Locale(); 7104 this.type = "date"; 7105 this.length = "s"; 7106 this.dateComponents = "dmy"; 7107 this.timeComponents = "ahm"; 7108 this.meridiems = "default"; 7109 7110 if (options) { 7111 if (options.locale) { 7112 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 7113 } 7114 7115 if (options.type) { 7116 if (options.type === 'date' || options.type === 'time' || options.type === 'datetime') { 7117 this.type = options.type; 7118 } 7119 } 7120 7121 if (options.calendar) { 7122 this.calName = options.calendar; 7123 } 7124 7125 if (options.length) { 7126 if (options.length === 'short' || 7127 options.length === 'medium' || 7128 options.length === 'long' || 7129 options.length === 'full') { 7130 // only use the first char to save space in the json files 7131 this.length = options.length.charAt(0); 7132 } 7133 } 7134 7135 if (options.date) { 7136 arr = options.date.split(""); 7137 arr.sort(function (left, right) { 7138 return (left < right) ? -1 : ((right < left) ? 1 : 0); 7139 }); 7140 bad = false; 7141 for (i = 0; i < arr.length; i++) { 7142 if (arr[i] !== 'd' && arr[i] !== 'm' && arr[i] !== 'y' && arr[i] !== 'w' && arr[i] !== 'n') { 7143 bad = true; 7144 break; 7145 } 7146 } 7147 if (!bad) { 7148 this.dateComponents = arr.join(""); 7149 } 7150 } 7151 7152 if (options.time) { 7153 arr = options.time.split(""); 7154 arr.sort(function (left, right) { 7155 return (left < right) ? -1 : ((right < left) ? 1 : 0); 7156 }); 7157 this.badTime = false; 7158 for (i = 0; i < arr.length; i++) { 7159 if (arr[i] !== 'h' && arr[i] !== 'm' && arr[i] !== 's' && arr[i] !== 'a' && arr[i] !== 'z') { 7160 this.badTime = true; 7161 break; 7162 } 7163 } 7164 if (!this.badTime) { 7165 this.timeComponents = arr.join(""); 7166 } 7167 } 7168 7169 if (options.clock && (options.clock === '12' || options.clock === '24')) { 7170 this.clock = options.clock; 7171 } 7172 7173 if (options.template) { 7174 // many options are not useful when specifying the template directly, so zero 7175 // them out. 7176 this.type = ""; 7177 this.length = ""; 7178 this.dateComponents = ""; 7179 this.timeComponents = ""; 7180 7181 this.template = options.template; 7182 } 7183 7184 if (options.timezone) { 7185 if (options.timezone instanceof ilib.TimeZone) { 7186 this.tz = options.timezone; 7187 } else { 7188 this.tz = new ilib.TimeZone({ 7189 locale: this.locale, 7190 id: options.timezone 7191 }); 7192 } 7193 } else if (options.locale) { 7194 // if an explicit locale was given, then get the time zone for that locale 7195 this.tz = new ilib.TimeZone({ 7196 locale: this.locale 7197 }); 7198 } // else just assume time zone "local" 7199 7200 if (typeof(options.useNative) === 'boolean') { 7201 this.useNative = options.useNative; 7202 } 7203 7204 if (typeof(options.meridiems) !== 'undefined' && 7205 (options.meridiems === "chinese" || 7206 options.meridiems === "gregorian" || 7207 options.meridiems === "ethiopic")) { 7208 this.meridiems = options.meridiems; 7209 } 7210 7211 if (typeof(options.sync) !== 'undefined') { 7212 sync = (options.sync === true); 7213 } 7214 7215 loadParams = options.loadParams; 7216 } 7217 7218 if (!ilib.DateFmt.cache) { 7219 ilib.DateFmt.cache = {}; 7220 } 7221 7222 new ilib.LocaleInfo(this.locale, { 7223 sync: sync, 7224 loadParams: loadParams, 7225 onLoad: ilib.bind(this, function (li) { 7226 this.locinfo = li; 7227 7228 // get the default calendar name from the locale, and if the locale doesn't define 7229 // one, use the hard-coded gregorian as the last resort 7230 this.calName = this.calName || this.locinfo.getCalendar() || "gregorian"; 7231 this.cal = ilib.Cal.newInstance({ 7232 type: this.calName 7233 }); 7234 if (!this.cal) { 7235 this.cal = new ilib.Cal.Gregorian(); 7236 } 7237 7238 if (this.meridiems === "default") { 7239 this.meridiems = li.getMeridiemsStyle(); 7240 } 7241 7242 /* 7243 if (this.timeComponents && 7244 (this.clock === '24' || 7245 (!this.clock && this.locinfo.getClock() === "24"))) { 7246 // make sure we don't have am/pm in 24 hour mode unless the user specifically 7247 // requested it in the time component option 7248 this.timeComponents = this.timeComponents.replace("a", ""); 7249 } 7250 */ 7251 7252 // load the strings used to translate the components 7253 new ilib.ResBundle({ 7254 locale: this.locale, 7255 name: "sysres", 7256 sync: sync, 7257 loadParams: loadParams, 7258 onLoad: ilib.bind(this, function (rb) { 7259 this.sysres = rb; 7260 7261 if (!this.template) { 7262 ilib.loadData({ 7263 object: ilib.DateFmt, 7264 locale: this.locale, 7265 name: "dateformats.json", 7266 sync: sync, 7267 loadParams: loadParams, 7268 callback: ilib.bind(this, function (formats) { 7269 if (!formats) { 7270 formats = ilib.data.dateformats || ilib.DateFmt.defaultFmt; 7271 var spec = this.locale.getSpec().replace(/-/g, '_'); 7272 ilib.DateFmt.cache[spec] = formats; 7273 } 7274 if (typeof(this.clock) === 'undefined') { 7275 // default to the locale instead 7276 this.clock = this.locinfo.getClock(); 7277 } 7278 this._initTemplate(formats); 7279 this._massageTemplate(); 7280 if (options && typeof(options.onLoad) === 'function') { 7281 options.onLoad(this); 7282 } 7283 }) 7284 }); 7285 } else { 7286 this._massageTemplate(); 7287 if (options && typeof(options.onLoad) === 'function') { 7288 options.onLoad(this); 7289 } 7290 } 7291 }) 7292 }); 7293 }) 7294 }); 7295 }; 7296 7297 // used in getLength 7298 ilib.DateFmt.lenmap = { 7299 "s": "short", 7300 "m": "medium", 7301 "l": "long", 7302 "f": "full" 7303 }; 7304 7305 ilib.DateFmt.zeros = "0000"; 7306 7307 ilib.DateFmt.defaultFmt = { 7308 "gregorian": { 7309 "order": "{date} {time}", 7310 "date": { 7311 "dmwy": "EEE d/MM/yyyy", 7312 "dmy": "d/MM/yyyy", 7313 "dmw": "EEE d/MM", 7314 "dm": "d/MM", 7315 "my": "MM/yyyy", 7316 "dw": "EEE d", 7317 "d": "dd", 7318 "m": "MM", 7319 "y": "yyyy", 7320 "n": "NN", 7321 "w": "EEE" 7322 }, 7323 "time": { 7324 "12": "h:mm:ssa", 7325 "24": "H:mm:ss" 7326 }, 7327 "range": { 7328 "c00": "{st} - {et}, {sd}/{sm}/{sy}", 7329 "c01": "{sd}/{sm} {st} - {ed}/{em} {et}, {sy}", 7330 "c02": "{sd}/{sm} {st} - {ed}/{em} {et}, {sy}", 7331 "c03": "{sd}/{sm}/{sy} {st} - {ed}/{em}/{ey} {et}", 7332 "c10": "{sd}-{ed}/{sm}/{sy}", 7333 "c11": "{sd}/{sm} - {ed}/{em} {sy}", 7334 "c12": "{sd}/{sm}/{sy} - {ed}/{em}/{ey}", 7335 "c20": "{sm}/{sy} - {em}/{ey}", 7336 "c30": "{sy} - {ey}" 7337 } 7338 }, 7339 "islamic": "gregorian", 7340 "hebrew": "gregorian", 7341 "julian": "gregorian", 7342 "buddhist": "gregorian", 7343 "persian": "gregorian", 7344 "persian-algo": "gregorian", 7345 "han": "gregorian" 7346 }; 7347 7348 /** 7349 * @static 7350 * @private 7351 */ 7352 ilib.DateFmt.monthNameLenMap = { 7353 "short" : "N", 7354 "medium": "NN", 7355 "long": "MMM", 7356 "full": "MMMM" 7357 }; 7358 7359 /** 7360 * @static 7361 * @private 7362 */ 7363 ilib.DateFmt.weekDayLenMap = { 7364 "short" : "E", 7365 "medium": "EE", 7366 "long": "EEE", 7367 "full": "EEEE" 7368 }; 7369 7370 /** 7371 * @protected 7372 * @param {Object.<string, (string|{s:string,m:string,l:string,f:string})>} obj Object to search 7373 * @param {string} components Format components to search 7374 * @param {string} length Length of the requested format 7375 * @return {string|undefined} the requested format 7376 */ 7377 7378 /** 7379 * @static 7380 * @public 7381 * The options may contain any of the following properties: 7382 * 7383 * <ul> 7384 * <li><i>locale</i> - locale to use when formatting the date/time. If the locale is 7385 * not specified, then the default locale of the app or web page will be used. 7386 * 7387 * <li><i>meridiems</i> - string that specifies what style of meridiems to use with this 7388 * format. The choices are "default", "gregorian", "ethiopic", and "chinese". The "default" 7389 * style is often the simple Gregorian AM/PM, but the actual style is chosen by the locale. 7390 * (For almost all locales, the Gregorian AM/PM style is most frequently used.) 7391 * The "ethiopic" style uses 5 different meridiems for "morning", "noon", "afternoon", 7392 * "evening", and "night". The "chinese" style uses 7 different meridiems corresponding 7393 * to the various parts of the day. N.B. Even for the Chinese locales, the default is "gregorian" 7394 * when formatting dates in the Gregorian calendar. 7395 * </ul> 7396 * 7397 * @param {Object} options options governing the way this date formatter instance works for getting meridiems range 7398 * @return {Array.<{name:string,start:string,end:string}>} 7399 */ 7400 ilib.DateFmt.getMeridiemsRange = function (options) { 7401 options = options || {}; 7402 var args = {}; 7403 if (options.locale) { 7404 args.locale = options.locale; 7405 } 7406 7407 if (options.meridiems) { 7408 args.meridiems = options.meridiems; 7409 } 7410 7411 var fmt = new ilib.DateFmt(args); 7412 7413 return fmt.getMeridiemsRange(); 7414 }; 7415 7416 ilib.DateFmt.prototype = { 7417 /** 7418 * @protected 7419 */ 7420 _initTemplate: function (formats) { 7421 if (formats[this.calName]) { 7422 /** 7423 * @private 7424 * @type {{order:(string|{s:string,m:string,l:string,f:string}),date:Object.<string, (string|{s:string,m:string,l:string,f:string})>,time:Object.<string,(string|{s:string,m:string,l:string,f:string})>,range:Object.<string, (string|{s:string,m:string,l:string,f:string})>}} 7425 */ 7426 this.formats = formats[this.calName]; 7427 if (typeof(this.formats) === "string") { 7428 // alias to another calendar type 7429 this.formats = formats[this.formats]; 7430 } 7431 7432 this.template = ""; 7433 7434 switch (this.type) { 7435 case "datetime": 7436 this.template = (this.formats && this._getLengthFormat(this.formats.order, this.length)) || "{date} {time}"; 7437 this.template = this.template.replace("{date}", this._getFormat(this.formats.date, this.dateComponents, this.length) || ""); 7438 this.template = this.template.replace("{time}", this._getFormat(this.formats.time[this.clock], this.timeComponents, this.length) || ""); 7439 break; 7440 case "date": 7441 this.template = this._getFormat(this.formats.date, this.dateComponents, this.length); 7442 break; 7443 case "time": 7444 this.template = this._getFormat(this.formats.time[this.clock], this.timeComponents, this.length); 7445 break; 7446 } 7447 } else { 7448 throw "No formats available for calendar " + this.calName + " in locale " + this.locale.toString(); 7449 } 7450 }, 7451 7452 /** 7453 * @protected 7454 */ 7455 _massageTemplate: function () { 7456 var i; 7457 7458 if (this.clock && this.template) { 7459 // explicitly set the hours to the requested type 7460 var temp = ""; 7461 switch (this.clock) { 7462 case "24": 7463 for (i = 0; i < this.template.length; i++) { 7464 if (this.template.charAt(i) == "'") { 7465 temp += this.template.charAt(i++); 7466 while (i < this.template.length && this.template.charAt(i) !== "'") { 7467 temp += this.template.charAt(i++); 7468 } 7469 if (i < this.template.length) { 7470 temp += this.template.charAt(i); 7471 } 7472 } else if (this.template.charAt(i) == 'K') { 7473 temp += 'k'; 7474 } else if (this.template.charAt(i) == 'h') { 7475 temp += 'H'; 7476 } else { 7477 temp += this.template.charAt(i); 7478 } 7479 } 7480 this.template = temp; 7481 break; 7482 case "12": 7483 for (i = 0; i < this.template.length; i++) { 7484 if (this.template.charAt(i) == "'") { 7485 temp += this.template.charAt(i++); 7486 while (i < this.template.length && this.template.charAt(i) !== "'") { 7487 temp += this.template.charAt(i++); 7488 } 7489 if (i < this.template.length) { 7490 temp += this.template.charAt(i); 7491 } 7492 } else if (this.template.charAt(i) == 'k') { 7493 temp += 'K'; 7494 } else if (this.template.charAt(i) == 'H') { 7495 temp += 'h'; 7496 } else { 7497 temp += this.template.charAt(i); 7498 } 7499 } 7500 this.template = temp; 7501 break; 7502 } 7503 } 7504 7505 // tokenize it now for easy formatting 7506 this.templateArr = this._tokenize(this.template); 7507 7508 var digits; 7509 // set up the mapping to native or alternate digits if necessary 7510 if (typeof(this.useNative) === "boolean") { 7511 if (this.useNative) { 7512 digits = this.locinfo.getNativeDigits(); 7513 if (digits) { 7514 this.digits = digits; 7515 } 7516 } 7517 } else if (this.locinfo.getDigitsStyle() === "native") { 7518 digits = this.locinfo.getNativeDigits(); 7519 if (digits) { 7520 this.useNative = true; 7521 this.digits = digits; 7522 } 7523 } 7524 }, 7525 7526 /** 7527 * Convert the template into an array of date components separated by formatting chars. 7528 * @protected 7529 * @param {string} template Format template to tokenize into components 7530 * @return {Array.<string>} a tokenized array of date format components 7531 */ 7532 _tokenize: function (template) { 7533 var i = 0, start, ch, letter, arr = []; 7534 7535 // console.log("_tokenize: tokenizing template " + template); 7536 if (template) { 7537 while (i < template.length) { 7538 ch = template.charAt(i); 7539 start = i; 7540 if (ch === "'") { 7541 // console.log("found quoted string"); 7542 i++; 7543 // escaped string - push as-is, then dequote later 7544 while (i < template.length && template.charAt(i) !== "'") { 7545 i++; 7546 } 7547 if (i < template.length) { 7548 i++; // grab the other quote too 7549 } 7550 } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { 7551 letter = template.charAt(i); 7552 // console.log("found letters " + letter); 7553 while (i < template.length && ch === letter) { 7554 ch = template.charAt(++i); 7555 } 7556 } else { 7557 // console.log("found other"); 7558 while (i < template.length && ch !== "'" && (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')) { 7559 ch = template.charAt(++i); 7560 } 7561 } 7562 arr.push(template.substring(start,i)); 7563 // console.log("start is " + start + " i is " + i + " and substr is " + template.substring(start,i)); 7564 } 7565 } 7566 return arr; 7567 }, 7568 7569 /** 7570 * @protected 7571 * @param {Object.<string, (string|{s:string,m:string,l:string,f:string})>} obj Object to search 7572 * @param {string} components Format components to search 7573 * @param {string} length Length of the requested format 7574 * @return {string|undefined} the requested format 7575 */ 7576 _getFormat: function getFormat(obj, components, length) { 7577 if (typeof(components) !== 'undefined' && obj && obj[components]) { 7578 return this._getLengthFormat(obj[components], length); 7579 } 7580 return undefined; 7581 }, 7582 7583 /** 7584 * @protected 7585 * @param {(string|{s:string,m:string,l:string,f:string})} obj Object to search 7586 * @param {string} length Length of the requested format 7587 * @return {(string|undefined)} the requested format 7588 */ 7589 _getLengthFormat: function getLengthFormat(obj, length) { 7590 if (typeof(obj) === 'string') { 7591 return obj; 7592 } else if (obj[length]) { 7593 return obj[length]; 7594 } 7595 return undefined; 7596 }, 7597 7598 /** 7599 * Return the locale used with this formatter instance. 7600 * @return {ilib.Locale} the ilib.Locale instance for this formatter 7601 */ 7602 getLocale: function() { 7603 return this.locale; 7604 }, 7605 7606 /** 7607 * Return the template string that is used to format date/times for this 7608 * formatter instance. This will work, even when the template property is not explicitly 7609 * given in the options to the constructor. Without the template option, the constructor 7610 * will build the appropriate template according to the options and use that template 7611 * in the format method. 7612 * 7613 * @return {string} the format template for this formatter 7614 */ 7615 getTemplate: function() { 7616 return this.template; 7617 }, 7618 7619 /** 7620 * Return the type of this formatter. The type is a string that has one of the following 7621 * values: "time", "date", "datetime". 7622 * @return {string} the type of the formatter 7623 */ 7624 getType: function() { 7625 return this.type; 7626 }, 7627 7628 /** 7629 * Return the name of the calendar used to format date/times for this 7630 * formatter instance. 7631 * @return {string} the name of the calendar used by this formatter 7632 */ 7633 getCalendar: function () { 7634 return this.cal.getType(); 7635 }, 7636 7637 /** 7638 * Return the length used to format date/times in this formatter. This is either the 7639 * value of the length option to the constructor, or the default value. 7640 * 7641 * @return {string} the length of formats this formatter returns 7642 */ 7643 getLength: function () { 7644 return ilib.DateFmt.lenmap[this.length] || ""; 7645 }, 7646 7647 /** 7648 * Return the date components that this formatter formats. This is either the 7649 * value of the date option to the constructor, or the default value. If this 7650 * formatter is a time-only formatter, this method will return the empty 7651 * string. The date component letters may be specified in any order in the 7652 * constructor, but this method will reorder the given components to a standard 7653 * order. 7654 * 7655 * @return {string} the date components that this formatter formats 7656 */ 7657 getDateComponents: function () { 7658 return this.dateComponents || ""; 7659 }, 7660 7661 /** 7662 * Return the time components that this formatter formats. This is either the 7663 * value of the time option to the constructor, or the default value. If this 7664 * formatter is a date-only formatter, this method will return the empty 7665 * string. The time component letters may be specified in any order in the 7666 * constructor, but this method will reorder the given components to a standard 7667 * order. 7668 * 7669 * @return {string} the time components that this formatter formats 7670 */ 7671 getTimeComponents: function () { 7672 return this.timeComponents || ""; 7673 }, 7674 7675 /** 7676 * Return the time zone used to format date/times for this formatter 7677 * instance. 7678 * @return a string naming the time zone 7679 */ 7680 getTimeZone: function () { 7681 // Lazy load the time zone. If it wasn't explicitly set up before, set 7682 // it up now, but use the 7683 // default TZ for the locale. This way, if the caller never uses the 7684 // time zone in their format, we never have to load up a TimeZone 7685 // instance into this formatter. 7686 if (!this.tz) { 7687 this.tz = new ilib.TimeZone({id: ilib.getTimeZone()}); 7688 } 7689 return this.tz; 7690 }, 7691 /** 7692 * Return the clock option set in the constructor. If the clock option was 7693 * not given, the default from the locale is returned instead. 7694 * @return {string} "12" or "24" depending on whether this formatter uses 7695 * the 12-hour or 24-hour clock 7696 */ 7697 getClock: function () { 7698 return this.clock || this.locinfo.getClock(); 7699 }, 7700 /** 7701 * Return the meridiems range in current locale. 7702 * @return {Array.<{name:string,start:string,end:string}>} 7703 */ 7704 getMeridiemsRange: function () { 7705 var result; 7706 var _getSysString = function (key) { 7707 return (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key)).toString(); 7708 }; 7709 7710 switch (this.meridiems) { 7711 case "chinese": 7712 result = [ 7713 { 7714 name: _getSysString.call(this, "azh0"), 7715 start: "00:00", 7716 end: "05:59" 7717 }, 7718 { 7719 name: _getSysString.call(this, "azh1"), 7720 start: "06:00", 7721 end: "08:59" 7722 }, 7723 { 7724 name: _getSysString.call(this, "azh2"), 7725 start: "09:00", 7726 end: "11:59" 7727 }, 7728 { 7729 name: _getSysString.call(this, "azh3"), 7730 start: "12:00", 7731 end: "12:59" 7732 }, 7733 { 7734 name: _getSysString.call(this, "azh4"), 7735 start: "13:00", 7736 end: "17:59" 7737 }, 7738 { 7739 name: _getSysString.call(this, "azh5"), 7740 start: "18:00", 7741 end: "20:59" 7742 }, 7743 { 7744 name: _getSysString.call(this, "azh6"), 7745 start: "21:00", 7746 end: "23:59" 7747 } 7748 ]; 7749 break; 7750 case "ethiopic": 7751 result = [ 7752 { 7753 name: _getSysString.call(this, "a0-ethiopic"), 7754 start: "00:00", 7755 end: "05:59" 7756 }, 7757 { 7758 name: _getSysString.call(this, "a1-ethiopic"), 7759 start: "06:00", 7760 end: "06:00" 7761 }, 7762 { 7763 name: _getSysString.call(this, "a2-ethiopic"), 7764 start: "06:01", 7765 end: "11:59" 7766 }, 7767 { 7768 name: _getSysString.call(this, "a3-ethiopic"), 7769 start: "12:00", 7770 end: "17:59" 7771 }, 7772 { 7773 name: _getSysString.call(this, "a4-ethiopic"), 7774 start: "18:00", 7775 end: "23:59" 7776 } 7777 ]; 7778 break; 7779 default: 7780 result = [ 7781 { 7782 name: _getSysString.call(this, "a0"), 7783 start: "00:00", 7784 end: "11:59" 7785 }, 7786 { 7787 name: _getSysString.call(this, "a1"), 7788 start: "12:00", 7789 end: "23:59" 7790 } 7791 ]; 7792 break; 7793 } 7794 7795 return result; 7796 }, 7797 7798 /** 7799 * @private 7800 */ 7801 _getTemplate: function (prefix, calendar) { 7802 if (calendar !== "gregorian") { 7803 return prefix + "-" + calendar; 7804 } 7805 return prefix; 7806 }, 7807 7808 /** 7809 * Returns an array of the months of the year, formatted to the optional length specified. 7810 * i.e. ...getMonthsOfYear() OR ...getMonthsOfYear({length: "short"}) 7811 * <p> 7812 * The options parameter may contain any of the following properties: 7813 * 7814 * <ul> 7815 * <li><i>length</i> - length of the names of the months being sought. This may be one of 7816 * "short", "medium", "long", or "full" 7817 * <li><i>date</i> - retrieve the names of the months in the date of the given date 7818 * <li><i>year</i> - retrieve the names of the months in the given year. In some calendars, 7819 * the months have different names depending if that year is a leap year or not. 7820 * </ul> 7821 * 7822 * @param {Object=} options an object-literal that contains any of the above properties 7823 * @return {Array} an array of the names of all of the months of the year in the current calendar 7824 */ 7825 getMonthsOfYear: function(options) { 7826 var length = (options && options.length) || this.getLength(), 7827 template = ilib.DateFmt.monthNameLenMap[length], 7828 months = [undefined], 7829 date, 7830 monthCount; 7831 7832 if (options) { 7833 if (options.date) { 7834 date = ilib.Date._dateToIlib(options.date); 7835 } 7836 7837 if (options.year) { 7838 date = ilib.Date.newInstance({year: options.year, month: 1, day: 1, type: this.cal.getType()}); 7839 } 7840 } 7841 7842 if (!date) { 7843 date = this.cal.newDateInstance(); 7844 } 7845 7846 monthCount = this.cal.getNumMonths(date.getYears()); 7847 for (var i = 1; i <= monthCount; i++) { 7848 months[i] = this.sysres.getString(this._getTemplate(template + i, this.cal.getType())).toString(); 7849 } 7850 return months; 7851 }, 7852 7853 /** 7854 * Returns an array of the days of the week, formatted to the optional length specified. 7855 * i.e. ...getDaysOfWeek() OR ...getDaysOfWeek({length: "short"}) 7856 * <p> 7857 * The options parameter may contain any of the following properties: 7858 * 7859 * <ul> 7860 * <li><i>length</i> - length of the names of the months being sought. This may be one of 7861 * "short", "medium", "long", or "full" 7862 * </ul> 7863 * @param {Object=} options an object-literal that contains one key 7864 * "length" with the standard length strings 7865 * @return {Array} an array of all of the names of the days of the week 7866 */ 7867 getDaysOfWeek: function(options) { 7868 var length = (options && options.length) || this.getLength(), 7869 template = ilib.DateFmt.weekDayLenMap[length], 7870 days = []; 7871 for (var i = 0; i < 7; i++) { 7872 days[i] = this.sysres.getString(this._getTemplate(template + i, this.cal.getType())).toString(); 7873 } 7874 return days; 7875 }, 7876 7877 7878 /** 7879 * Convert this formatter to a string representation by returning the 7880 * format template. This method delegates to getTemplate. 7881 * 7882 * @return {string} the format template 7883 */ 7884 toString: function() { 7885 return this.getTemplate(); 7886 }, 7887 7888 /* 7889 * @private 7890 * Left pad the str to the given length of digits with zeros 7891 * @param {string} str the string to pad 7892 * @param {number} length the desired total length of the output string, padded 7893 */ 7894 _pad: function (str, length) { 7895 if (typeof(str) !== 'string') { 7896 str = "" + str; 7897 } 7898 var start = 0; 7899 if (str.charAt(0) === '-') { 7900 start++; 7901 } 7902 return (str.length >= length+start) ? str : str.substring(0, start) + ilib.DateFmt.zeros.substring(0,length-str.length+start) + str.substring(start); 7903 }, 7904 7905 /* 7906 * @private 7907 * Format a date according to a sequence of components. 7908 * @param {ilib.Date} date a date/time object to format 7909 * @param {Array.<string>} templateArr an array of components to format 7910 * @return {string} the formatted date 7911 */ 7912 _formatTemplate: function (date, templateArr) { 7913 var i, key, temp, tz, str = ""; 7914 for (i = 0; i < templateArr.length; i++) { 7915 switch (templateArr[i]) { 7916 case 'd': 7917 str += (date.day || 1); 7918 break; 7919 case 'dd': 7920 str += this._pad(date.day || "1", 2); 7921 break; 7922 case 'yy': 7923 temp = "" + ((date.year || 0) % 100); 7924 str += this._pad(temp, 2); 7925 break; 7926 case 'yyyy': 7927 str += this._pad(date.year || "0", 4); 7928 break; 7929 case 'M': 7930 str += (date.month || 1); 7931 break; 7932 case 'MM': 7933 str += this._pad(date.month || "1", 2); 7934 break; 7935 case 'h': 7936 temp = (date.hour || 0) % 12; 7937 if (temp == 0) { 7938 temp = "12"; 7939 } 7940 str += temp; 7941 break; 7942 case 'hh': 7943 temp = (date.hour || 0) % 12; 7944 if (temp == 0) { 7945 temp = "12"; 7946 } 7947 str += this._pad(temp, 2); 7948 break; 7949 /* 7950 case 'j': 7951 temp = (date.hour || 0) % 12 + 1; 7952 str += temp; 7953 break; 7954 case 'jj': 7955 temp = (date.hour || 0) % 12 + 1; 7956 str += this._pad(temp, 2); 7957 break; 7958 */ 7959 case 'K': 7960 temp = (date.hour || 0) % 12; 7961 str += temp; 7962 break; 7963 case 'KK': 7964 temp = (date.hour || 0) % 12; 7965 str += this._pad(temp, 2); 7966 break; 7967 7968 case 'H': 7969 str += (date.hour || "0"); 7970 break; 7971 case 'HH': 7972 str += this._pad(date.hour || "0", 2); 7973 break; 7974 case 'k': 7975 str += (date.hour == 0 ? "24" : date.hour); 7976 break; 7977 case 'kk': 7978 temp = (date.hour == 0 ? "24" : date.hour); 7979 str += this._pad(temp, 2); 7980 break; 7981 7982 case 'm': 7983 str += (date.minute || "0"); 7984 break; 7985 case 'mm': 7986 str += this._pad(date.minute || "0", 2); 7987 break; 7988 case 's': 7989 str += (date.minute || "0"); 7990 break; 7991 case 'ss': 7992 str += this._pad(date.second || "0", 2); 7993 break; 7994 case 'S': 7995 str += (date.millisecond || "0"); 7996 break; 7997 case 'SSS': 7998 str += this._pad(date.millisecond || "0", 3); 7999 break; 8000 8001 case 'N': 8002 case 'NN': 8003 case 'MMM': 8004 case 'MMMM': 8005 key = templateArr[i] + (date.month || 1); 8006 str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key)); 8007 break; 8008 8009 case 'E': 8010 case 'EE': 8011 case 'EEE': 8012 case 'EEEE': 8013 key = templateArr[i] + date.getDayOfWeek(); 8014 //console.log("finding " + key + " in the resources"); 8015 str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key)); 8016 break; 8017 8018 case 'a': 8019 switch (this.meridiems) { 8020 case "chinese": 8021 if (date.hour < 6) { 8022 key = "azh0"; // before dawn 8023 } else if (date.hour < 9) { 8024 key = "azh1"; // morning 8025 } else if (date.hour < 12) { 8026 key = "azh2"; // late morning/day before noon 8027 } else if (date.hour < 13) { 8028 key = "azh3"; // noon hour/midday 8029 } else if (date.hour < 18) { 8030 key = "azh4"; // afternoon 8031 } else if (date.hour < 21) { 8032 key = "azh5"; // evening time/dusk 8033 } else { 8034 key = "azh6"; // night time 8035 } 8036 break; 8037 case "ethiopic": 8038 if (date.hour < 6) { 8039 key = "a0-ethiopic"; // morning 8040 } else if (date.hour === 6 && date.minute === 0) { 8041 key = "a1-ethiopic"; // noon 8042 } else if (date.hour >= 6 && date.hour < 12) { 8043 key = "a2-ethiopic"; // afternoon 8044 } else if (date.hour >= 12 && date.hour < 18) { 8045 key = "a3-ethiopic"; // evening 8046 } else if (date.hour >= 18) { 8047 key = "a4-ethiopic"; // night 8048 } 8049 break; 8050 default: 8051 key = date.hour < 12 ? "a0" : "a1"; 8052 break; 8053 } 8054 //console.log("finding " + key + " in the resources"); 8055 str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key)); 8056 break; 8057 8058 case 'w': 8059 str += date.getWeekOfYear(); 8060 break; 8061 case 'ww': 8062 str += this._pad(date.getWeekOfYear(), 2); 8063 break; 8064 8065 case 'D': 8066 str += date.getDayOfYear(); 8067 break; 8068 case 'DD': 8069 str += this._pad(date.getDayOfYear(), 2); 8070 break; 8071 case 'DDD': 8072 str += this._pad(date.getDayOfYear(), 3); 8073 break; 8074 case 'W': 8075 str += date.getWeekOfMonth(this.locale); 8076 break; 8077 8078 case 'G': 8079 key = "G" + date.getEra(); 8080 str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key)); 8081 break; 8082 8083 case 'O': 8084 temp = this.sysres.getString("1#1st|2#2nd|3#3rd|21#21st|22#22nd|23#23rd|31#31st|#{num}th", "ordinalChoice"); 8085 str += temp.formatChoice(date.day, {num: date.day}); 8086 break; 8087 8088 case 'z': // general time zone 8089 tz = this.getTimeZone(); // lazy-load the tz 8090 str += tz.getDisplayName(date, "standard"); 8091 break; 8092 case 'Z': // RFC 822 time zone 8093 tz = this.getTimeZone(); // lazy-load the tz 8094 str += tz.getDisplayName(date, "rfc822"); 8095 break; 8096 8097 default: 8098 str += templateArr[i].replace(/'/g, ""); 8099 break; 8100 } 8101 } 8102 8103 if (this.digits) { 8104 str = ilib.mapString(str, this.digits); 8105 } 8106 return str; 8107 }, 8108 8109 /** 8110 * Format a particular date instance according to the settings of this 8111 * formatter object. The type of the date instance being formatted must 8112 * correspond exactly to the calendar type with which this formatter was 8113 * constructed. If the types are not compatible, this formatter will 8114 * produce bogus results. 8115 * 8116 * @param {Date|Number|String|ilib.Date|ilib.JulianDay|null|undefined} dateLike a date-like object to format 8117 * @return {string} the formatted version of the given date instance 8118 */ 8119 format: function (dateLike) { 8120 var thisZoneName = this.tz && this.tz.getId() || "local"; 8121 8122 var date = ilib.Date._dateToIlib(dateLike, thisZoneName); 8123 8124 if (!date.getCalendar || !(date instanceof ilib.Date)) { 8125 throw "Wrong date type passed to ilib.DateFmt.format()"; 8126 } 8127 8128 var dateZoneName = date.timezone || "local"; 8129 8130 // convert to the time zone of this formatter before formatting 8131 if (dateZoneName !== thisZoneName || date.getCalendar() !== this.calName) { 8132 // console.log("Differing time zones date: " + dateZoneName + " and fmt: " + thisZoneName + ". Converting..."); 8133 // this will recalculate the date components based on the new time zone 8134 // and/or convert a date in another calendar to the current calendar before formatting it 8135 var newDate = ilib.Date.newInstance({ 8136 type: this.calName, 8137 timezone: thisZoneName, 8138 julianday: date.getJulianDay() 8139 }); 8140 8141 date = newDate; 8142 } 8143 return this._formatTemplate(date, this.templateArr); 8144 }, 8145 8146 /** 8147 * Return a string that describes a date relative to the given 8148 * reference date. The string returned is text that for the locale that 8149 * was specified when the formatter instance was constructed.<p> 8150 * 8151 * The date can be in the future relative to the reference date or in 8152 * the past, and the formatter will generate the appropriate string.<p> 8153 * 8154 * The text used to describe the relative reference depends on the length 8155 * of time between the date and the reference. If the time was in the 8156 * past, it will use the "ago" phrase, and in the future, it will use 8157 * the "in" phrase. Examples:<p> 8158 * 8159 * <ul> 8160 * <li>within a minute: either "X seconds ago" or "in X seconds" 8161 * <li>within an hour: either "X minutes ago" or "in X minutes" 8162 * <li>within a day: either "X hours ago" or "in X hours" 8163 * <li>within 2 weeks: either "X days ago" or "in X days" 8164 * <li>within 12 weeks (~3 months): either "X weeks ago" or "in X weeks" 8165 * <li>within two years: either "X months ago" or "in X months" 8166 * <li>longer than 2 years: "X years ago" or "in X years" 8167 * </ul> 8168 * 8169 * @param {Date|Number|String|ilib.Date|ilib.JulianDay|null|undefined} reference a date that the date parameter should be relative to 8170 * @param {Date|Number|String|ilib.Date|ilib.JulianDay|null|undefined} date a date being formatted 8171 * @throws "Wrong calendar type" when the start or end dates are not the same 8172 * calendar type as the formatter itself 8173 * @return {string} the formatted relative date 8174 */ 8175 formatRelative: function(reference, date) { 8176 reference = ilib.Date._dateToIlib(reference); 8177 date = ilib.Date._dateToIlib(date); 8178 8179 var referenceRd, dateRd, fmt, time, diff, num; 8180 8181 if (typeof(reference) !== 'object' || !reference.getCalendar || reference.getCalendar() !== this.calName || 8182 typeof(date) !== 'object' || !date.getCalendar || date.getCalendar() !== this.calName) { 8183 throw "Wrong calendar type"; 8184 } 8185 8186 referenceRd = reference.getRataDie(); 8187 dateRd = date.getRataDie(); 8188 8189 if (dateRd < referenceRd) { 8190 diff = referenceRd - dateRd; 8191 fmt = this.sysres.getString("{duration} ago"); 8192 } else { 8193 diff = dateRd - referenceRd; 8194 fmt = this.sysres.getString("in {duration}"); 8195 } 8196 8197 if (diff < 0.000694444) { 8198 num = Math.round(diff * 86400); 8199 switch (this.length) { 8200 case 's': 8201 time = this.sysres.getString("#{num}s"); 8202 break; 8203 case 'm': 8204 time = this.sysres.getString("1#1 se|#{num} sec"); 8205 break; 8206 case 'l': 8207 time = this.sysres.getString("1#1 sec|#{num} sec"); 8208 break; 8209 default: 8210 case 'f': 8211 time = this.sysres.getString("1#1 second|#{num} seconds"); 8212 break; 8213 } 8214 } else if (diff < 0.041666667) { 8215 num = Math.round(diff * 1440); 8216 switch (this.length) { 8217 case 's': 8218 time = this.sysres.getString("#{num}m", "durationShortMinutes"); 8219 break; 8220 case 'm': 8221 time = this.sysres.getString("1#1 mi|#{num} min"); 8222 break; 8223 case 'l': 8224 time = this.sysres.getString("1#1 min|#{num} min"); 8225 break; 8226 default: 8227 case 'f': 8228 time = this.sysres.getString("1#1 minute|#{num} minutes"); 8229 break; 8230 } 8231 } else if (diff < 1) { 8232 num = Math.round(diff * 24); 8233 switch (this.length) { 8234 case 's': 8235 time = this.sysres.getString("#{num}h"); 8236 break; 8237 case 'm': 8238 time = this.sysres.getString("1#1 hr|#{num} hrs", "durationMediumHours"); 8239 break; 8240 case 'l': 8241 time = this.sysres.getString("1#1 hr|#{num} hrs"); 8242 break; 8243 default: 8244 case 'f': 8245 time = this.sysres.getString("1#1 hour|#{num} hours"); 8246 break; 8247 } 8248 } else if (diff < 14) { 8249 num = Math.round(diff); 8250 switch (this.length) { 8251 case 's': 8252 time = this.sysres.getString("#{num}d"); 8253 break; 8254 case 'm': 8255 time = this.sysres.getString("1#1 dy|#{num} dys"); 8256 break; 8257 case 'l': 8258 time = this.sysres.getString("1#1 day|#{num} days", "durationLongDays"); 8259 break; 8260 default: 8261 case 'f': 8262 time = this.sysres.getString("1#1 day|#{num} days"); 8263 break; 8264 } 8265 } else if (diff < 84) { 8266 num = Math.round(diff/7); 8267 switch (this.length) { 8268 case 's': 8269 time = this.sysres.getString("#{num}w"); 8270 break; 8271 case 'm': 8272 time = this.sysres.getString("1#1 wk|#{num} wks", "durationMediumWeeks"); 8273 break; 8274 case 'l': 8275 time = this.sysres.getString("1#1 wk|#{num} wks"); 8276 break; 8277 default: 8278 case 'f': 8279 time = this.sysres.getString("1#1 week|#{num} weeks"); 8280 break; 8281 } 8282 } else if (diff < 730) { 8283 num = Math.round(diff/30.4); 8284 switch (this.length) { 8285 case 's': 8286 time = this.sysres.getString("#{num}m", "durationShortMonths"); 8287 break; 8288 case 'm': 8289 time = this.sysres.getString("1#1 mo|#{num} mos"); 8290 break; 8291 case 'l': 8292 time = this.sysres.getString("1#1 mon|#{num} mons"); 8293 break; 8294 default: 8295 case 'f': 8296 time = this.sysres.getString("1#1 month|#{num} months"); 8297 break; 8298 } 8299 } else { 8300 num = Math.round(diff/365); 8301 switch (this.length) { 8302 case 's': 8303 time = this.sysres.getString("#{num}y"); 8304 break; 8305 case 'm': 8306 time = this.sysres.getString("1#1 yr|#{num} yrs", "durationMediumYears"); 8307 break; 8308 case 'l': 8309 time = this.sysres.getString("1#1 yr|#{num} yrs"); 8310 break; 8311 default: 8312 case 'f': 8313 time = this.sysres.getString("1#1 year|#{num} years"); 8314 break; 8315 } 8316 } 8317 return fmt.format({duration: time.formatChoice(num, {num: num})}); 8318 } 8319 }; 8320 8321 /* 8322 * datefmt.js - Date formatter definition 8323 * 8324 * Copyright © 2012-2014, JEDLSoft 8325 * 8326 * Licensed under the Apache License, Version 2.0 (the "License"); 8327 * you may not use this file except in compliance with the License. 8328 * You may obtain a copy of the License at 8329 * 8330 * http://www.apache.org/licenses/LICENSE-2.0 8331 * 8332 * Unless required by applicable law or agreed to in writing, software 8333 * distributed under the License is distributed on an "AS IS" BASIS, 8334 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8335 * 8336 * See the License for the specific language governing permissions and 8337 * limitations under the License. 8338 */ 8339 8340 /* 8341 !depends 8342 ilibglobal.js 8343 locale.js 8344 date.js 8345 strings.js 8346 calendar.js 8347 localeinfo.js 8348 timezone.js 8349 datefmt.js 8350 calendar/gregorian.js 8351 util/jsutils.js 8352 */ 8353 8354 // !data dateformats sysres 8355 8356 /** 8357 * @class 8358 * Create a new date range formatter instance. The date range formatter is immutable once 8359 * it is created, but can format as many different date ranges as needed with the same 8360 * options. Create different date range formatter instances for different purposes 8361 * and then keep them cached for use later if you have more than one range to 8362 * format.<p> 8363 * 8364 * The options may contain any of the following properties: 8365 * 8366 * <ul> 8367 * <li><i>locale</i> - locale to use when formatting the date/times in the range. If the 8368 * locale is not specified, then the default locale of the app or web page will be used. 8369 * 8370 * <li><i>calendar</i> - the type of calendar to use for this format. The value should 8371 * be a sting containing the name of the calendar. Currently, the supported 8372 * types are "gregorian", "julian", "arabic", "hebrew", or "chinese". If the 8373 * calendar is not specified, then the default calendar for the locale is used. When the 8374 * calendar type is specified, then the format method must be called with an instance of 8375 * the appropriate date type. (eg. Gregorian calendar means that the format method must 8376 * be called with a GregDate instance.) 8377 * 8378 * <li><i>timezone</i> - time zone to use when formatting times. This may be a time zone 8379 * instance or a time zone specifier string in RFC 822 format. If not specified, the 8380 * default time zone for the locale is used. 8381 * 8382 * <li><i>length</i> - Specify the length of the format to use as a string. The length 8383 * is the approximate size of the formatted string. 8384 * 8385 * <ul> 8386 * <li><i>short</i> - use a short representation of the time. This is the most compact format possible for the locale. 8387 * <li><i>medium</i> - use a medium length representation of the time. This is a slightly longer format. 8388 * <li><i>long</i> - use a long representation of the time. This is a fully specified format, but some of the textual 8389 * components may still be abbreviated. (eg. "Tue" instead of "Tuesday") 8390 * <li><i>full</i> - use a full representation of the time. This is a fully specified format where all the textual 8391 * components are spelled out completely. 8392 * </ul> 8393 * 8394 * eg. The "short" format for an en_US range may be "MM/yy - MM/yy", whereas the long format might be 8395 * "MMM, yyyy - MMM, yyyy". In the long format, the month name is textual instead of numeric 8396 * and is longer, the year is 4 digits instead of 2, and the format contains slightly more 8397 * spaces and formatting characters.<p> 8398 * 8399 * Note that the length parameter does not specify which components are to be formatted. The 8400 * components that are formatted depend on the length of time in the range. 8401 * 8402 * <li><i>clock</i> - specify that formatted times should use a 12 or 24 hour clock if the 8403 * format happens to include times. Valid values are "12" and "24".<p> 8404 * 8405 * In some locales, both clocks are used. For example, in en_US, the general populace uses 8406 * a 12 hour clock with am/pm, but in the US military or in nautical or aeronautical or 8407 * scientific writing, it is more common to use a 24 hour clock. This property allows you to 8408 * construct a formatter that overrides the default for the locale.<p> 8409 * 8410 * If this property is not specified, the default is to use the most widely used convention 8411 * for the locale. 8412 * <li>onLoad - a callback function to call when the date range format object is fully 8413 * loaded. When the onLoad option is given, the DateRngFmt object will attempt to 8414 * load any missing locale data using the ilib loader callback. 8415 * When the constructor is done (even if the data is already preassembled), the 8416 * onLoad function is called with the current instance as a parameter, so this 8417 * callback can be used with preassembled or dynamic loading or a mix of the two. 8418 * 8419 * <li>sync - tell whether to load any missing locale data synchronously or 8420 * asynchronously. If this option is given as "false", then the "onLoad" 8421 * callback must be given, as the instance returned from this constructor will 8422 * not be usable for a while. 8423 * 8424 * <li><i>loadParams</i> - an object containing parameters to pass to the 8425 * loader callback function when locale data is missing. The parameters are not 8426 * interpretted or modified in any way. They are simply passed along. The object 8427 * may contain any property/value pairs as long as the calling code is in 8428 * agreement with the loader callback function as to what those parameters mean. 8429 * </ul> 8430 * <p> 8431 * 8432 * Depends directive: !depends daterangefmt.js 8433 * 8434 * @constructor 8435 * @param {Object} options options governing the way this date range formatter instance works 8436 */ 8437 ilib.DateRngFmt = function(options) { 8438 var sync = true; 8439 var loadParams = undefined; 8440 this.locale = new ilib.Locale(); 8441 this.length = "s"; 8442 8443 if (options) { 8444 if (options.locale) { 8445 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 8446 } 8447 8448 if (options.calendar) { 8449 this.calName = options.calendar; 8450 } 8451 8452 if (options.length) { 8453 if (options.length === 'short' || 8454 options.length === 'medium' || 8455 options.length === 'long' || 8456 options.length === 'full') { 8457 // only use the first char to save space in the json files 8458 this.length = options.length.charAt(0); 8459 } 8460 } 8461 if (typeof(options.sync) !== 'undefined') { 8462 sync = (options.sync == true); 8463 } 8464 8465 loadParams = options.loadParams; 8466 } 8467 8468 var opts = {}; 8469 ilib.shallowCopy(options, opts); 8470 opts.sync = sync; 8471 opts.loadParams = loadParams; 8472 8473 /** 8474 * @private 8475 */ 8476 opts.onLoad = ilib.bind(this, function (fmt) { 8477 this.dateFmt = fmt; 8478 if (fmt) { 8479 this.locinfo = this.dateFmt.locinfo; 8480 8481 // get the default calendar name from the locale, and if the locale doesn't define 8482 // one, use the hard-coded gregorian as the last resort 8483 this.calName = this.calName || this.locinfo.getCalendar() || "gregorian"; 8484 this.cal = ilib.Cal.newInstance({ 8485 type: this.calName 8486 }); 8487 if (!this.cal) { 8488 this.cal = new ilib.Cal.Gregorian(); 8489 } 8490 8491 this.timeTemplate = this.dateFmt._getFormat(this.dateFmt.formats.time[this.dateFmt.clock], this.dateFmt.timeComponents, this.length) || "hh:mm"; 8492 this.timeTemplateArr = this.dateFmt._tokenize(this.timeTemplate); 8493 8494 if (options && typeof(options.onLoad) === 'function') { 8495 options.onLoad(this); 8496 } 8497 } 8498 }); 8499 8500 // delegate a bunch of the formatting to this formatter 8501 new ilib.DateFmt(opts); 8502 }; 8503 8504 ilib.DateRngFmt.prototype = { 8505 /** 8506 * Return the locale used with this formatter instance. 8507 * @return {ilib.Locale} the ilib.Locale instance for this formatter 8508 */ 8509 getLocale: function() { 8510 return this.locale; 8511 }, 8512 8513 /** 8514 * Return the name of the calendar used to format date/times for this 8515 * formatter instance. 8516 * @return {string} the name of the calendar used by this formatter 8517 */ 8518 getCalendar: function () { 8519 return this.dateFmt.getCalendar(); 8520 }, 8521 8522 /** 8523 * Return the length used to format date/times in this formatter. This is either the 8524 * value of the length option to the constructor, or the default value. 8525 * 8526 * @return {string} the length of formats this formatter returns 8527 */ 8528 getLength: function () { 8529 return ilib.DateFmt.lenmap[this.length] || ""; 8530 }, 8531 8532 /** 8533 * Return the time zone used to format date/times for this formatter 8534 * instance. 8535 * @return {ilib.TimeZone} a string naming the time zone 8536 */ 8537 getTimeZone: function () { 8538 return this.dateFmt.getTimeZone(); 8539 }, 8540 8541 /** 8542 * Return the clock option set in the constructor. If the clock option was 8543 * not given, the default from the locale is returned instead. 8544 * @return {string} "12" or "24" depending on whether this formatter uses 8545 * the 12-hour or 24-hour clock 8546 */ 8547 getClock: function () { 8548 return this.dateFmt.getClock(); 8549 }, 8550 8551 /** 8552 * Format a date/time range according to the settings of the current 8553 * formatter. The range is specified as being from the "start" date until 8554 * the "end" date. <p> 8555 * 8556 * The template that the date/time range uses depends on the 8557 * length of time between the dates, on the premise that a long date range 8558 * which is too specific is not useful. For example, when giving 8559 * the dates of the 100 Years War, in most situations it would be more 8560 * appropriate to format the range as "1337 - 1453" than to format it as 8561 * "10:37am November 9, 1337 - 4:37pm July 17, 1453", as the latter format 8562 * is much too specific given the length of time that the range represents. 8563 * If a very specific, but long, date range really is needed, the caller 8564 * should format two specific dates separately and put them 8565 * together as you might with other normal strings.<p> 8566 * 8567 * The format used for a date range contains the following date components, 8568 * where the order of those components is rearranged and the component values 8569 * are translated according to each locale: 8570 * 8571 * <ul> 8572 * <li>within 3 days: the times of day, dates, months, and years 8573 * <li>within 730 days (2 years): the dates, months, and years 8574 * <li>within 3650 days (10 years): the months and years 8575 * <li>longer than 10 years: the years only 8576 * </ul> 8577 * 8578 * In general, if any of the date components share a value between the 8579 * start and end date, that component is only given once. For example, 8580 * if the range is from November 15, 2011 to November 26, 2011, the 8581 * start and end dates both share the same month and year. The 8582 * range would then be formatted as "November 15-26, 2011". <p> 8583 * 8584 * If you want to format a length of time instead of a particular range of 8585 * time (for example, the length of an event rather than the specific start time 8586 * and end time of that event), then use a duration formatter instance 8587 * (ilib.DurFmt) instead. The formatRange method will make sure that each component 8588 * of the date/time is within the normal range for that component. For example, 8589 * the minutes will always be between 0 and 59, no matter what is specified in 8590 * the date to format, because that is the normal range for minutes. A duration 8591 * format will allow the number of minutes to exceed 59. For example, if you 8592 * were displaying the length of a movie that is 198 minutes long, the minutes 8593 * component of a duration could be 198.<p> 8594 * 8595 * @param {ilib.Date} start the starting date/time of the range. This must be of 8596 * the same calendar type as the formatter itself. 8597 * @param {ilib.Date} end the ending date/time of the range. This must be of the 8598 * same calendar type as the formatter itself. 8599 * @throws "Wrong calendar type" when the start or end dates are not the same 8600 * calendar type as the formatter itself 8601 * @return {string} a date range formatted for the locale 8602 */ 8603 format: function (start, end) { 8604 var startRd, endRd, fmt = "", yearTemplate, monthTemplate, dayTemplate; 8605 8606 if (typeof(start) !== 'object' || !start.getCalendar || start.getCalendar() !== this.calName || 8607 typeof(end) !== 'object' || !end.getCalendar || end.getCalendar() !== this.calName) { 8608 throw "Wrong calendar type"; 8609 } 8610 8611 startRd = start.getRataDie(); 8612 endRd = end.getRataDie(); 8613 8614 // 8615 // legend: 8616 // c00 - difference is less than 3 days. Year, month, and date are same, but time is different 8617 // c01 - difference is less than 3 days. Year and month are same but date and time are different 8618 // c02 - difference is less than 3 days. Year is same but month, date, and time are different. (ie. it straddles a month boundary) 8619 // c03 - difference is less than 3 days. Year, month, date, and time are all different. (ie. it straddles a year boundary) 8620 // c10 - difference is less than 2 years. Year and month are the same, but date is different. 8621 // c11 - difference is less than 2 years. Year is the same, but month, date, and time are different. 8622 // c12 - difference is less than 2 years. All fields are different. (ie. straddles a year boundary) 8623 // c20 - difference is less than 10 years. All fields are different. 8624 // c30 - difference is more than 10 years. All fields are different. 8625 // 8626 8627 if (endRd - startRd < 3) { 8628 if (start.year === end.year) { 8629 if (start.month === end.month) { 8630 if (start.day === end.day) { 8631 fmt = new ilib.String(this.dateFmt._getFormat(this.dateFmt.formats.range, "c00", this.length)); 8632 } else { 8633 fmt = new ilib.String(this.dateFmt._getFormat(this.dateFmt.formats.range, "c01", this.length)); 8634 } 8635 } else { 8636 fmt = new ilib.String(this.dateFmt._getFormat(this.dateFmt.formats.range, "c02", this.length)); 8637 } 8638 } else { 8639 fmt = new ilib.String(this.dateFmt._getFormat(this.dateFmt.formats.range, "c03", this.length)); 8640 } 8641 } else if (endRd - startRd < 730) { 8642 if (start.year === end.year) { 8643 if (start.month === end.month) { 8644 fmt = new ilib.String(this.dateFmt._getFormat(this.dateFmt.formats.range, "c10", this.length)); 8645 } else { 8646 fmt = new ilib.String(this.dateFmt._getFormat(this.dateFmt.formats.range, "c11", this.length)); 8647 } 8648 } else { 8649 fmt = new ilib.String(this.dateFmt._getFormat(this.dateFmt.formats.range, "c12", this.length)); 8650 } 8651 } else if (endRd - startRd < 3650) { 8652 fmt = new ilib.String(this.dateFmt._getFormat(this.dateFmt.formats.range, "c20", this.length)); 8653 } else { 8654 fmt = new ilib.String(this.dateFmt._getFormat(this.dateFmt.formats.range, "c30", this.length)); 8655 } 8656 8657 yearTemplate = this.dateFmt._tokenize(this.dateFmt._getFormat(this.dateFmt.formats.date, "y", this.length) || "yyyy"); 8658 monthTemplate = this.dateFmt._tokenize(this.dateFmt._getFormat(this.dateFmt.formats.date, "m", this.length) || "MM"); 8659 dayTemplate = this.dateFmt._tokenize(this.dateFmt._getFormat(this.dateFmt.formats.date, "d", this.length) || "dd"); 8660 8661 /* 8662 console.log("fmt is " + fmt.toString()); 8663 console.log("year template is " + yearTemplate); 8664 console.log("month template is " + monthTemplate); 8665 console.log("day template is " + dayTemplate); 8666 */ 8667 8668 return fmt.format({ 8669 sy: this.dateFmt._formatTemplate(start, yearTemplate), 8670 sm: this.dateFmt._formatTemplate(start, monthTemplate), 8671 sd: this.dateFmt._formatTemplate(start, dayTemplate), 8672 st: this.dateFmt._formatTemplate(start, this.timeTemplateArr), 8673 ey: this.dateFmt._formatTemplate(end, yearTemplate), 8674 em: this.dateFmt._formatTemplate(end, monthTemplate), 8675 ed: this.dateFmt._formatTemplate(end, dayTemplate), 8676 et: this.dateFmt._formatTemplate(end, this.timeTemplateArr) 8677 }); 8678 } 8679 }; 8680 8681 /* 8682 * hebrew.js - Represent a Hebrew calendar object. 8683 * 8684 * Copyright © 2012-2014, JEDLSoft 8685 * 8686 * Licensed under the Apache License, Version 2.0 (the "License"); 8687 * you may not use this file except in compliance with the License. 8688 * You may obtain a copy of the License at 8689 * 8690 * http://www.apache.org/licenses/LICENSE-2.0 8691 * 8692 * Unless required by applicable law or agreed to in writing, software 8693 * distributed under the License is distributed on an "AS IS" BASIS, 8694 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8695 * 8696 * See the License for the specific language governing permissions and 8697 * limitations under the License. 8698 */ 8699 8700 8701 /* !depends calendar.js locale.js date.js julianday.js util/utils.js util/math.js */ 8702 8703 /** 8704 * @class 8705 * Construct a new Hebrew calendar object. This class encodes information about 8706 * the Hebrew (Jewish) calendar. The Hebrew calendar is a tabular hebrew 8707 * calendar where the dates are calculated by arithmetic rules. This differs from 8708 * the religious Hebrew calendar which is used to mark the beginning of particular 8709 * holidays. The religious calendar depends on the first sighting of the new 8710 * crescent moon to determine the first day of the new month. Because humans and 8711 * weather are both involved, the actual time of sighting varies, so it is not 8712 * really possible to precalculate the religious calendar. Certain groups, such 8713 * as the Hebrew Society of North America, decreed in in 2007 that they will use 8714 * a calendar based on calculations rather than observations to determine the 8715 * beginning of lunar months, and therefore the dates of holidays.<p> 8716 * 8717 * Depends directive: !depends hebrew.js 8718 * 8719 * @constructor 8720 * @implements ilib.Cal 8721 */ 8722 ilib.Cal.Hebrew = function() { 8723 this.type = "hebrew"; 8724 }; 8725 8726 8727 /** 8728 * Return the number of days elapsed in the Hebrew calendar before the 8729 * given year starts. 8730 * @private 8731 * @param {number} year the year for which the number of days is sought 8732 * @return {number} the number of days elapsed in the Hebrew calendar before the 8733 * given year starts 8734 */ 8735 ilib.Cal.Hebrew.elapsedDays = function(year) { 8736 var months = Math.floor(((235*year) - 234)/19); 8737 var parts = 204 + 793 * ilib.mod(months, 1080); 8738 var hours = 11 + 12 * months + 793 * Math.floor(months/1080) + 8739 Math.floor(parts/1080); 8740 var days = 29 * months + Math.floor(hours/24); 8741 return (ilib.mod(3 * (days + 1), 7) < 3) ? days + 1 : days; 8742 }; 8743 8744 /** 8745 * Return the number of days that the New Year's (Rosh HaShanah) in the Hebrew 8746 * calendar will be corrected for the given year. Corrections are caused because New 8747 * Year's is not allowed to start on certain days of the week. To deal with 8748 * it, the start of the new year is corrected for the next year by adding a 8749 * day to the 8th month (Heshvan) and/or the 9th month (Kislev) in the current 8750 * year to make them 30 days long instead of 29. 8751 * 8752 * @private 8753 * @param {number} year the year for which the correction is sought 8754 * @param {number} elapsed number of days elapsed up to this year 8755 * @return {number} the number of days correction in the current year to make sure 8756 * Rosh HaShanah does not fall on undesirable days of the week 8757 */ 8758 ilib.Cal.Hebrew.newYearsCorrection = function(year, elapsed) { 8759 var lastYear = ilib.Cal.Hebrew.elapsedDays(year-1), 8760 thisYear = elapsed, 8761 nextYear = ilib.Cal.Hebrew.elapsedDays(year+1); 8762 8763 return (nextYear - thisYear) == 356 ? 2 : ((thisYear - lastYear) == 382 ? 1 : 0); 8764 }; 8765 8766 /** 8767 * Return the rata die date of the new year for the given hebrew year. 8768 * @private 8769 * @param {number} year the year for which the new year is needed 8770 * @return {number} the rata die date of the new year 8771 */ 8772 ilib.Cal.Hebrew.newYear = function(year) { 8773 var elapsed = ilib.Cal.Hebrew.elapsedDays(year); 8774 8775 return elapsed + ilib.Cal.Hebrew.newYearsCorrection(year, elapsed); 8776 }; 8777 8778 /** 8779 * Return the number of days in the given year. Years contain a variable number of 8780 * days because the date of Rosh HaShanah (New Year's) changes so that it doesn't 8781 * fall on particular days of the week. Days are added to the months of Heshvan 8782 * and/or Kislev in the previous year in order to prevent the current year's New 8783 * Year from being on Sunday, Wednesday, or Friday. 8784 * 8785 * @param {number} year the year for which the length is sought 8786 * @return {number} number of days in the given year 8787 */ 8788 ilib.Cal.Hebrew.daysInYear = function(year) { 8789 return ilib.Cal.Hebrew.newYear(year+1) - ilib.Cal.Hebrew.newYear(year); 8790 }; 8791 8792 /** 8793 * Return true if the given year contains a long month of Heshvan. That is, 8794 * it is 30 days instead of 29. 8795 * 8796 * @private 8797 * @param {number} year the year in which that month is questioned 8798 * @return {boolean} true if the given year contains a long month of Heshvan 8799 */ 8800 ilib.Cal.Hebrew.longHeshvan = function(year) { 8801 return ilib.mod(ilib.Cal.Hebrew.daysInYear(year), 10) === 5; 8802 }; 8803 8804 /** 8805 * Return true if the given year contains a long month of Kislev. That is, 8806 * it is 30 days instead of 29. 8807 * 8808 * @private 8809 * @param {number} year the year in which that month is questioned 8810 * @return {boolean} true if the given year contains a short month of Kislev 8811 */ 8812 ilib.Cal.Hebrew.longKislev = function(year) { 8813 return ilib.mod(ilib.Cal.Hebrew.daysInYear(year), 10) !== 3; 8814 }; 8815 8816 /** 8817 * Return the date of the last day of the month for the given year. The date of 8818 * the last day of the month is variable because a number of months gain an extra 8819 * day in leap years, and it is variable which months gain a day for each leap 8820 * year and which do not. 8821 * 8822 * @param {number} month the month for which the number of days is sought 8823 * @param {number} year the year in which that month is 8824 * @return {number} the number of days in the given month and year 8825 */ 8826 ilib.Cal.Hebrew.prototype.lastDayOfMonth = function(month, year) { 8827 switch (month) { 8828 case 2: 8829 case 4: 8830 case 6: 8831 case 10: 8832 return 29; 8833 case 13: 8834 return this.isLeapYear(year) ? 29 : 0; 8835 case 8: 8836 return ilib.Cal.Hebrew.longHeshvan(year) ? 30 : 29; 8837 case 9: 8838 return ilib.Cal.Hebrew.longKislev(year) ? 30 : 29; 8839 case 12: 8840 case 1: 8841 case 3: 8842 case 5: 8843 case 7: 8844 case 11: 8845 return 30; 8846 default: 8847 return 0; 8848 } 8849 }; 8850 8851 /** 8852 * Return the number of months in the given year. The number of months in a year varies 8853 * for luni-solar calendars because in some years, an extra month is needed to extend the 8854 * days in a year to an entire solar year. The month is represented as a 1-based number 8855 * where 1=first month, 2=second month, etc. 8856 * 8857 * @param {number} year a year for which the number of months is sought 8858 */ 8859 ilib.Cal.Hebrew.prototype.getNumMonths = function(year) { 8860 return this.isLeapYear(year) ? 13 : 12; 8861 }; 8862 8863 /** 8864 * Return the number of days in a particular month in a particular year. This function 8865 * can return a different number for a month depending on the year because of leap years. 8866 * 8867 * @param {number} month the month for which the length is sought 8868 * @param {number} year the year within which that month can be found 8869 * @returns {number} the number of days within the given month in the given year, or 8870 * 0 for an invalid month in the year 8871 */ 8872 ilib.Cal.Hebrew.prototype.getMonLength = function(month, year) { 8873 if (month < 1 || month > 13 || (month == 13 && !this.isLeapYear(year))) { 8874 return 0; 8875 } 8876 return this.lastDayOfMonth(month, year); 8877 }; 8878 8879 /** 8880 * Return true if the given year is a leap year in the Hebrew calendar. 8881 * The year parameter may be given as a number, or as a HebrewDate object. 8882 * @param {number|Object} year the year for which the leap year information is being sought 8883 * @returns {boolean} true if the given year is a leap year 8884 */ 8885 ilib.Cal.Hebrew.prototype.isLeapYear = function(year) { 8886 var y = (typeof(year) == 'number') ? year : year.year; 8887 return (ilib.mod(1 + 7 * y, 19) < 7); 8888 }; 8889 8890 /** 8891 * Return the type of this calendar. 8892 * 8893 * @returns {string} the name of the type of this calendar 8894 */ 8895 ilib.Cal.Hebrew.prototype.getType = function() { 8896 return this.type; 8897 }; 8898 8899 /** 8900 * Return a date instance for this calendar type using the given 8901 * options. 8902 * @param {Object} options options controlling the construction of 8903 * the date instance 8904 * @returns {ilib.Date} a date appropriate for this calendar type 8905 */ 8906 ilib.Cal.Hebrew.prototype.newDateInstance = function (options) { 8907 return new ilib.Date.HebrewDate(options); 8908 }; 8909 8910 /*register this calendar for the factory method */ 8911 ilib.Cal._constructors["hebrew"] = ilib.Cal.Hebrew; 8912 8913 /* 8914 * hebrewdate.js - Represent a date in the Hebrew calendar 8915 * 8916 * Copyright © 2012-2014, JEDLSoft 8917 * 8918 * Licensed under the Apache License, Version 2.0 (the "License"); 8919 * you may not use this file except in compliance with the License. 8920 * You may obtain a copy of the License at 8921 * 8922 * http://www.apache.org/licenses/LICENSE-2.0 8923 * 8924 * Unless required by applicable law or agreed to in writing, software 8925 * distributed under the License is distributed on an "AS IS" BASIS, 8926 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8927 * 8928 * See the License for the specific language governing permissions and 8929 * limitations under the License. 8930 */ 8931 8932 /* !depends 8933 date.js 8934 calendar/hebrew.js 8935 calendar/ratadie.js 8936 util/utils.js 8937 util/math.js 8938 julianday.js 8939 */ 8940 8941 /** 8942 * @class 8943 * Construct a new Hebrew RD date number object. The constructor parameters can 8944 * contain any of the following properties: 8945 * 8946 * <ul> 8947 * <li><i>unixtime<i> - sets the time of this instance according to the given 8948 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970. 8949 * 8950 * <li><i>julianday</i> - sets the time of this instance according to the given 8951 * Julian Day instance or the Julian Day given as a float 8952 * 8953 * <li><i>year</i> - any integer, including 0 8954 * 8955 * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc. 8956 * 8957 * <li><i>day</i> - 1 to 31 8958 * 8959 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 8960 * is always done with an unambiguous 24 hour representation 8961 * 8962 * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify 8963 * the parts or specify the minutes, seconds, and milliseconds, but not both. 8964 * 8965 * <li><i>minute</i> - 0 to 59 8966 * 8967 * <li><i>second</i> - 0 to 59 8968 * 8969 * <li><i>millisecond</i> - 0 to 999 8970 * 8971 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 8972 * </ul> 8973 * 8974 * If the constructor is called with another Hebrew date instance instead of 8975 * a parameter block, the other instance acts as a parameter block and its 8976 * settings are copied into the current instance.<p> 8977 * 8978 * If the constructor is called with no arguments at all or if none of the 8979 * properties listed above are present, then the RD is calculate based on 8980 * the current date at the time of instantiation. <p> 8981 * 8982 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 8983 * specified in the params, it is assumed that they have the smallest possible 8984 * value in the range for the property (zero or one).<p> 8985 * 8986 * Depends directive: !depends hebrewdate.js 8987 * 8988 * @private 8989 * @constructor 8990 * @extends ilib.Date.RataDie 8991 * @param {Object=} params parameters that govern the settings and behaviour of this Hebrew RD date 8992 */ 8993 ilib.Date.HebrewRataDie = function(params) { 8994 this.cal = params && params.cal || new ilib.Cal.Hebrew(); 8995 this.rd = undefined; 8996 ilib.Date.RataDie.call(this, params); 8997 }; 8998 8999 ilib.Date.HebrewRataDie.prototype = new ilib.Date.RataDie(); 9000 ilib.Date.HebrewRataDie.prototype.parent = ilib.Date.RataDie; 9001 ilib.Date.HebrewRataDie.prototype.constructor = ilib.Date.HebrewRataDie; 9002 9003 /** 9004 * The difference between a zero Julian day and the first day of the Hebrew 9005 * calendar: sunset on Monday, Tishri 1, 1 = September 7, 3760 BC Gregorian = JD 347997.25 9006 * @private 9007 * @const 9008 * @type number 9009 */ 9010 ilib.Date.HebrewRataDie.prototype.epoch = 347997.25; 9011 9012 /** 9013 * Calculate the Rata Die (fixed day) number of the given date from the 9014 * date components. 9015 * 9016 * @private 9017 * @param {Object} date the date components to calculate the RD from 9018 */ 9019 ilib.Date.HebrewRataDie.prototype._setDateComponents = function(date) { 9020 var elapsed = ilib.Cal.Hebrew.elapsedDays(date.year); 9021 var days = elapsed + 9022 ilib.Cal.Hebrew.newYearsCorrection(date.year, elapsed) + 9023 date.day - 1; 9024 var sum = 0, table; 9025 9026 //console.log("getRataDie: converting " + JSON.stringify(date)); 9027 //console.log("getRataDie: days is " + days); 9028 //console.log("getRataDie: new years correction is " + ilib.Cal.Hebrew.newYearsCorrection(date.year, elapsed)); 9029 9030 table = this.cal.isLeapYear(date.year) ? 9031 ilib.Date.HebrewDate.cumMonthLengthsLeap : 9032 ilib.Date.HebrewDate.cumMonthLengths; 9033 sum = table[date.month-1]; 9034 9035 // gets cumulative without correction, so now add in the correction 9036 if ((date.month < 7 || date.month > 8) && ilib.Cal.Hebrew.longHeshvan(date.year)) { 9037 sum++; 9038 } 9039 if ((date.month < 7 || date.month > 9) && ilib.Cal.Hebrew.longKislev(date.year)) { 9040 sum++; 9041 } 9042 // console.log("getRataDie: cum days is now " + sum); 9043 9044 days += sum; 9045 9046 // the date starts at sunset, which we take as 18:00, so the hours from 9047 // midnight to 18:00 are on the current Gregorian day, and the hours from 9048 // 18:00 to midnight are on the previous Gregorian day. So to calculate the 9049 // number of hours into the current day that this time represents, we have 9050 // to count from 18:00 to midnight first, and add in 6 hours if the time is 9051 // less than 18:00 9052 var minute, second, millisecond; 9053 9054 if (typeof(date.parts) !== 'undefined') { 9055 // The parts (halaqim) of the hour. This can be a number from 0 to 1079. 9056 var parts = parseInt(date.parts, 10); 9057 var seconds = parseInt(parts, 10) * 3.333333333333; 9058 minute = Math.floor(seconds / 60); 9059 seconds -= minute * 60; 9060 second = Math.floor(seconds); 9061 millisecond = (seconds - second); 9062 } else { 9063 minute = parseInt(date.minute, 10) || 0; 9064 second = parseInt(date.second, 10) || 0; 9065 millisecond = parseInt(date.millisecond, 10) || 0; 9066 } 9067 9068 var time; 9069 if (date.hour >= 18) { 9070 time = ((date.hour - 18 || 0) * 3600000 + 9071 (minute || 0) * 60000 + 9072 (second || 0) * 1000 + 9073 (millisecond || 0)) / 9074 86400000; 9075 } else { 9076 time = 0.25 + // 6 hours from 18:00 to midnight on the previous gregorian day 9077 ((date.hour || 0) * 3600000 + 9078 (minute || 0) * 60000 + 9079 (second || 0) * 1000 + 9080 (millisecond || 0)) / 9081 86400000; 9082 } 9083 9084 //console.log("getRataDie: rd is " + (days + time)); 9085 this.rd = days + time; 9086 }; 9087 9088 /** 9089 * Return the rd number of the particular day of the week on or before the 9090 * given rd. eg. The Sunday on or before the given rd. 9091 * @private 9092 * @param {number} rd the rata die date of the reference date 9093 * @param {number} dayOfWeek the day of the week that is being sought relative 9094 * to the current date 9095 * @return {number} the rd of the day of the week 9096 */ 9097 ilib.Date.HebrewRataDie.prototype._onOrBefore = function(rd, dayOfWeek) { 9098 return rd - ilib.mod(Math.floor(rd) - dayOfWeek + 1, 7); 9099 }; 9100 9101 /** 9102 * @class 9103 * Construct a new civil Hebrew date object. The constructor can be called 9104 * with a params object that can contain the following properties:<p> 9105 * 9106 * <ul> 9107 * <li><i>julianday</i> - the Julian Day to set into this date 9108 * <li><i>year</i> - any integer except 0. Years go from -1 (BCE) to 1 (CE), skipping the zero year 9109 * <li><i>month</i> - 1 to 12, where 1 means Nisan, 2 means Iyyar, etc. 9110 * <li><i>day</i> - 1 to 30 9111 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 9112 * is always done with an unambiguous 24 hour representation 9113 * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify 9114 * the parts or specify the minutes, seconds, and milliseconds, but not both. 9115 * <li><i>minute</i> - 0 to 59 9116 * <li><i>second</i> - 0 to 59 9117 * <li><i>millisecond</i> - 0 to 999 9118 * <li><i>locale</i> - the ilib.TimeZone instance or time zone name as a string 9119 * of this julian date. The date/time is kept in the local time. The time zone 9120 * is used later if this date is formatted according to a different time zone and 9121 * the difference has to be calculated, or when the date format has a time zone 9122 * component in it. 9123 * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 9124 * given, it can be inferred from this locale. For locales that span multiple 9125 * time zones, the one with the largest population is chosen as the one that 9126 * represents the locale. 9127 * 9128 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 9129 * </ul> 9130 * 9131 * If called with another Hebrew date argument, the date components of the given 9132 * date are copied into the current one.<p> 9133 * 9134 * If the constructor is called with no arguments at all or if none of the 9135 * properties listed above 9136 * from <i>julianday</i> through <i>millisecond</i> are present, then the date 9137 * components are 9138 * filled in with the current date at the time of instantiation. Note that if 9139 * you do not give the time zone when defaulting to the current time and the 9140 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 9141 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 9142 * Mean Time").<p> 9143 * 9144 * Depends directive: !depends hebrewdate.js 9145 * 9146 * @constructor 9147 * @extends ilib.Date 9148 * @param {Object=} params parameters that govern the settings and behaviour of this Hebrew date 9149 */ 9150 ilib.Date.HebrewDate = function(params) { 9151 this.cal = new ilib.Cal.Hebrew(); 9152 9153 if (params) { 9154 if (params.timezone) { 9155 this.timezone = params.timezone; 9156 } 9157 if (params.locale) { 9158 this.locale = (typeof(params.locale) === 'string') ? new ilib.Locale(params.locale) : params.locale; 9159 if (!this.timezone) { 9160 var li = new ilib.LocaleInfo(this.locale); 9161 this.timezone = li.getTimeZone(); 9162 } 9163 } 9164 9165 if (params.year || params.month || params.day || params.hour || 9166 params.minute || params.second || params.millisecond || params.parts ) { 9167 /** 9168 * Year in the Hebrew calendar. 9169 * @type number 9170 */ 9171 this.year = parseInt(params.year, 10) || 0; 9172 9173 /** 9174 * The month number, ranging from 1 to 13. 9175 * @type number 9176 */ 9177 this.month = parseInt(params.month, 10) || 1; 9178 9179 /** 9180 * The day of the month. This ranges from 1 to 30. 9181 * @type number 9182 */ 9183 this.day = parseInt(params.day, 10) || 1; 9184 9185 /** 9186 * The hour of the day. This can be a number from 0 to 23, as times are 9187 * stored unambiguously in the 24-hour clock. 9188 * @type number 9189 */ 9190 this.hour = parseInt(params.hour, 10) || 0; 9191 9192 if (typeof(params.parts) !== 'undefined') { 9193 /** 9194 * The parts (halaqim) of the hour. This can be a number from 0 to 1079. 9195 * @type number 9196 */ 9197 this.parts = parseInt(params.parts, 10); 9198 var seconds = parseInt(params.parts, 10) * 3.333333333333; 9199 this.minute = Math.floor(seconds / 60); 9200 seconds -= this.minute * 60; 9201 this.second = Math.floor(seconds); 9202 this.millisecond = (seconds - this.second); 9203 } else { 9204 /** 9205 * The minute of the hours. Ranges from 0 to 59. 9206 * @type number 9207 */ 9208 this.minute = parseInt(params.minute, 10) || 0; 9209 9210 /** 9211 * The second of the minute. Ranges from 0 to 59. 9212 * @type number 9213 */ 9214 this.second = parseInt(params.second, 10) || 0; 9215 9216 /** 9217 * The millisecond of the second. Ranges from 0 to 999. 9218 * @type number 9219 */ 9220 this.millisecond = parseInt(params.millisecond, 10) || 0; 9221 } 9222 9223 /** 9224 * The day of the year. Ranges from 1 to 383. 9225 * @type number 9226 */ 9227 this.dayOfYear = parseInt(params.dayOfYear, 10); 9228 9229 if (typeof(params.dst) === 'boolean') { 9230 this.dst = params.dst; 9231 } 9232 9233 this.rd = this.newRd(this); 9234 9235 // add the time zone offset to the rd to convert to UTC 9236 if (!this.tz) { 9237 this.tz = new ilib.TimeZone({id: this.timezone}); 9238 } 9239 // getOffsetMillis requires that this.year, this.rd, and this.dst 9240 // are set in order to figure out which time zone rules apply and 9241 // what the offset is at that point in the year 9242 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000; 9243 if (this.offset !== 0) { 9244 this.rd = this.newRd({ 9245 rd: this.rd.getRataDie() - this.offset 9246 }); 9247 } 9248 } 9249 } 9250 9251 if (!this.rd) { 9252 this.rd = this.newRd(params); 9253 this._calcDateComponents(); 9254 } 9255 }; 9256 9257 ilib.Date.HebrewDate.prototype = new ilib.Date({noinstance: true}); 9258 ilib.Date.HebrewDate.prototype.parent = ilib.Date; 9259 ilib.Date.HebrewDate.prototype.constructor = ilib.Date.HebrewDate; 9260 9261 /** 9262 * the cumulative lengths of each month for a non-leap year, without new years corrections 9263 * @private 9264 * @const 9265 * @type Array.<number> 9266 */ 9267 ilib.Date.HebrewDate.cumMonthLengths = [ 9268 176, /* Nisan */ 9269 206, /* Iyyar */ 9270 235, /* Sivan */ 9271 265, /* Tammuz */ 9272 294, /* Av */ 9273 324, /* Elul */ 9274 0, /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */ 9275 30, /* Heshvan */ 9276 59, /* Kislev */ 9277 88, /* Teveth */ 9278 117, /* Shevat */ 9279 147 /* Adar I */ 9280 ]; 9281 9282 /** 9283 * the cumulative lengths of each month for a non-leap year, without new years corrections, 9284 * that can be used in reverse to map days to months 9285 * @private 9286 * @const 9287 * @type Array.<number> 9288 */ 9289 ilib.Date.HebrewDate.cumMonthLengthsReverse = [ 9290 // [days, monthnumber], 9291 [0, 7], /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */ 9292 [30, 8], /* Heshvan */ 9293 [59, 9], /* Kislev */ 9294 [88, 10], /* Teveth */ 9295 [117, 11], /* Shevat */ 9296 [147, 12], /* Adar I */ 9297 [176, 1], /* Nisan */ 9298 [206, 2], /* Iyyar */ 9299 [235, 3], /* Sivan */ 9300 [265, 4], /* Tammuz */ 9301 [294, 5], /* Av */ 9302 [324, 6], /* Elul */ 9303 [354, 7] /* end of year sentinel value */ 9304 ]; 9305 9306 /** 9307 * the cumulative lengths of each month for a leap year, without new years corrections 9308 * @private 9309 * @const 9310 * @type Array.<number> 9311 */ 9312 ilib.Date.HebrewDate.cumMonthLengthsLeap = [ 9313 206, /* Nisan */ 9314 236, /* Iyyar */ 9315 265, /* Sivan */ 9316 295, /* Tammuz */ 9317 324, /* Av */ 9318 354, /* Elul */ 9319 0, /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */ 9320 30, /* Heshvan */ 9321 59, /* Kislev */ 9322 88, /* Teveth */ 9323 117, /* Shevat */ 9324 147, /* Adar I */ 9325 177 /* Adar II */ 9326 ]; 9327 9328 /** 9329 * the cumulative lengths of each month for a leap year, without new years corrections 9330 * that can be used in reverse to map days to months 9331 * 9332 * @private 9333 * @const 9334 * @type Array.<number> 9335 */ 9336 ilib.Date.HebrewDate.cumMonthLengthsLeapReverse = [ 9337 // [days, monthnumber], 9338 [0, 7], /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */ 9339 [30, 8], /* Heshvan */ 9340 [59, 9], /* Kislev */ 9341 [88, 10], /* Teveth */ 9342 [117, 11], /* Shevat */ 9343 [147, 12], /* Adar I */ 9344 [177, 13], /* Adar II */ 9345 [206, 1], /* Nisan */ 9346 [236, 2], /* Iyyar */ 9347 [265, 3], /* Sivan */ 9348 [295, 4], /* Tammuz */ 9349 [324, 5], /* Av */ 9350 [354, 6], /* Elul */ 9351 [384, 7] /* end of year sentinel value */ 9352 ]; 9353 9354 /** 9355 * Number of days difference between RD 0 of the Hebrew calendar 9356 * (Jan 1, 1 Gregorian = JD 1721057.5) and RD 0 of the Hebrew calendar 9357 * (September 7, -3760 Gregorian = JD 347997.25) 9358 * @private 9359 * @const 9360 * @type number 9361 */ 9362 ilib.Date.HebrewDate.GregorianDiff = 1373060.25; 9363 9364 /** 9365 * Return a new RD for this date type using the given params. 9366 * @private 9367 * @param {Object=} params the parameters used to create this rata die instance 9368 * @returns {ilib.Date.RataDie} the new RD instance for the given params 9369 */ 9370 ilib.Date.HebrewDate.prototype.newRd = function (params) { 9371 return new ilib.Date.HebrewRataDie(params); 9372 }; 9373 9374 /** 9375 * Return the year for the given RD 9376 * @protected 9377 * @param {number} rd RD to calculate from 9378 * @returns {number} the year for the RD 9379 */ 9380 ilib.Date.HebrewDate.prototype._calcYear = function(rd) { 9381 var year, approximation, nextNewYear; 9382 9383 // divide by the average number of days per year in the Hebrew calendar 9384 // to approximate the year, then tweak it to get the real year 9385 approximation = Math.floor(rd / 365.246822206) + 1; 9386 9387 // console.log("HebrewDate._calcYear: approx is " + approximation); 9388 9389 // search forward from approximation-1 for the year that actually contains this rd 9390 year = approximation; 9391 nextNewYear = ilib.Cal.Hebrew.newYear(year); 9392 while (rd >= nextNewYear) { 9393 year++; 9394 nextNewYear = ilib.Cal.Hebrew.newYear(year); 9395 } 9396 return year - 1; 9397 }; 9398 9399 /** 9400 * Calculate date components for the given RD date. 9401 * @protected 9402 */ 9403 ilib.Date.HebrewDate.prototype._calcDateComponents = function () { 9404 var remainder, 9405 i, 9406 table, 9407 target, 9408 rd = this.rd.getRataDie(); 9409 9410 // console.log("HebrewDate.calcComponents: calculating for rd " + rd); 9411 9412 if (typeof(this.offset) === "undefined") { 9413 this.year = this._calcYear(rd); 9414 9415 // now offset the RD by the time zone, then recalculate in case we were 9416 // near the year boundary 9417 if (!this.tz) { 9418 this.tz = new ilib.TimeZone({id: this.timezone}); 9419 } 9420 this.offset = this.tz.getOffsetMillis(this) / 86400000; 9421 } 9422 9423 if (this.offset !== 0) { 9424 rd += this.offset; 9425 this.year = this._calcYear(rd); 9426 } 9427 9428 // console.log("HebrewDate.calcComponents: year is " + this.year + " with starting rd " + thisNewYear); 9429 9430 remainder = rd - ilib.Cal.Hebrew.newYear(this.year); 9431 // console.log("HebrewDate.calcComponents: remainder is " + remainder); 9432 9433 // take out new years corrections so we get the right month when we look it up in the table 9434 if (remainder >= 59) { 9435 if (remainder >= 88) { 9436 if (ilib.Cal.Hebrew.longKislev(this.year)) { 9437 remainder--; 9438 } 9439 } 9440 if (ilib.Cal.Hebrew.longHeshvan(this.year)) { 9441 remainder--; 9442 } 9443 } 9444 9445 // console.log("HebrewDate.calcComponents: after new years corrections, remainder is " + remainder); 9446 9447 table = this.cal.isLeapYear(this.year) ? 9448 ilib.Date.HebrewDate.cumMonthLengthsLeapReverse : 9449 ilib.Date.HebrewDate.cumMonthLengthsReverse; 9450 9451 i = 0; 9452 target = Math.floor(remainder); 9453 while (i+1 < table.length && target >= table[i+1][0]) { 9454 i++; 9455 } 9456 9457 this.month = table[i][1]; 9458 // console.log("HebrewDate.calcComponents: remainder is " + remainder); 9459 remainder -= table[i][0]; 9460 9461 // console.log("HebrewDate.calcComponents: month is " + this.month + " and remainder is " + remainder); 9462 9463 this.day = Math.floor(remainder); 9464 remainder -= this.day; 9465 this.day++; // days are 1-based 9466 9467 // console.log("HebrewDate.calcComponents: day is " + this.day + " and remainder is " + remainder); 9468 9469 // now convert to milliseconds for the rest of the calculation 9470 remainder = Math.round(remainder * 86400000); 9471 9472 this.hour = Math.floor(remainder/3600000); 9473 remainder -= this.hour * 3600000; 9474 9475 // the hours from 0 to 6 are actually 18:00 to midnight of the previous 9476 // gregorian day, so we have to adjust for that 9477 if (this.hour >= 6) { 9478 this.hour -= 6; 9479 } else { 9480 this.hour += 18; 9481 } 9482 9483 this.minute = Math.floor(remainder/60000); 9484 remainder -= this.minute * 60000; 9485 9486 this.second = Math.floor(remainder/1000); 9487 remainder -= this.second * 1000; 9488 9489 this.millisecond = Math.floor(remainder); 9490 }; 9491 9492 /** 9493 * Return the day of the week of this date. The day of the week is encoded 9494 * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday. 9495 * 9496 * @return {number} the day of the week 9497 */ 9498 ilib.Date.HebrewDate.prototype.getDayOfWeek = function() { 9499 var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0)); 9500 return ilib.mod(rd+1, 7); 9501 }; 9502 9503 /** 9504 * Get the Halaqim (parts) of an hour. There are 1080 parts in an hour, which means 9505 * each part is 3.33333333 seconds long. This means the number returned may not 9506 * be an integer. 9507 * 9508 * @return {number} the halaqim parts of the current hour 9509 */ 9510 ilib.Date.HebrewDate.prototype.getHalaqim = function() { 9511 if (this.parts < 0) { 9512 // convert to ms first, then to parts 9513 var h = this.minute * 60000 + this.second * 1000 + this.millisecond; 9514 this.parts = (h * 0.0003); 9515 } 9516 return this.parts; 9517 }; 9518 9519 /** 9520 * Return the rd number of the first Sunday of the given ISO year. 9521 * @protected 9522 * @return the rd of the first Sunday of the ISO year 9523 */ 9524 ilib.Date.HebrewDate.prototype.firstSunday = function (year) { 9525 var tishri1 = this.newRd({ 9526 year: year, 9527 month: 7, 9528 day: 1, 9529 hour: 18, 9530 minute: 0, 9531 second: 0, 9532 millisecond: 0, 9533 cal: this.cal 9534 }); 9535 var firstThu = this.newRd({ 9536 rd: tishri1.onOrAfter(4), 9537 cal: this.cal 9538 }); 9539 return firstThu.before(0); 9540 }; 9541 9542 /** 9543 * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 9544 * 385, regardless of months or weeks, etc. That is, Tishri 1st is day 1, and 9545 * Elul 29 is 385 for a leap year with a long Heshvan and long Kislev. 9546 * @return {number} the ordinal day of the year 9547 */ 9548 ilib.Date.HebrewDate.prototype.getDayOfYear = function() { 9549 var table = this.cal.isLeapYear(this.year) ? 9550 ilib.Date.HebrewDate.cumMonthLengthsLeap : 9551 ilib.Date.HebrewDate.cumMonthLengths; 9552 var days = table[this.month-1]; 9553 if ((this.month < 7 || this.month > 8) && ilib.Cal.Hebrew.longHeshvan(this.year)) { 9554 days++; 9555 } 9556 if ((this.month < 7 || this.month > 9) && ilib.Cal.Hebrew.longKislev(this.year)) { 9557 days++; 9558 } 9559 9560 return days + this.day; 9561 }; 9562 9563 /** 9564 * Return the ordinal number of the week within the month. The first week of a month is 9565 * the first one that contains 4 or more days in that month. If any days precede this 9566 * first week, they are marked as being in week 0. This function returns values from 0 9567 * through 6.<p> 9568 * 9569 * The locale is a required parameter because different locales that use the same 9570 * Hebrew calendar consider different days of the week to be the beginning of 9571 * the week. This can affect the week of the month in which some days are located. 9572 * 9573 * @param {ilib.Locale|string} locale the locale or locale spec to use when figuring out 9574 * the first day of the week 9575 * @return {number} the ordinal number of the week within the current month 9576 */ 9577 ilib.Date.HebrewDate.prototype.getWeekOfMonth = function(locale) { 9578 var li = new ilib.LocaleInfo(locale), 9579 first = this.newRd({ 9580 year: this.year, 9581 month: this.month, 9582 day: 1, 9583 hour: 18, 9584 minute: 0, 9585 second: 0, 9586 millisecond: 0 9587 }), 9588 rd = this.rd.getRataDie(), 9589 weekStart = first.onOrAfter(li.getFirstDayOfWeek()); 9590 9591 if (weekStart - first.getRataDie() > 3) { 9592 // if the first week has 4 or more days in it of the current month, then consider 9593 // that week 1. Otherwise, it is week 0. To make it week 1, move the week start 9594 // one week earlier. 9595 weekStart -= 7; 9596 } 9597 return (rd < weekStart) ? 0 : Math.floor((rd - weekStart) / 7) + 1; 9598 }; 9599 9600 /** 9601 * Return the era for this date as a number. The value for the era for Hebrew 9602 * calendars is -1 for "before the Hebrew era" and 1 for "the Hebrew era". 9603 * Hebrew era dates are any date after Tishri 1, 1, which is the same as 9604 * September 7, 3760 BC in the Gregorian calendar. 9605 * 9606 * @return {number} 1 if this date is in the Hebrew era, -1 if it is before the 9607 * Hebrew era 9608 */ 9609 ilib.Date.HebrewDate.prototype.getEra = function() { 9610 return (this.year < 1) ? -1 : 1; 9611 }; 9612 9613 /** 9614 * Return the name of the calendar that governs this date. 9615 * 9616 * @return {string} a string giving the name of the calendar 9617 */ 9618 ilib.Date.HebrewDate.prototype.getCalendar = function() { 9619 return "hebrew"; 9620 }; 9621 9622 // register with the factory method 9623 ilib.Date._constructors["hebrew"] = ilib.Date.HebrewDate; 9624 /* 9625 * islamic.js - Represent a Islamic calendar object. 9626 * 9627 * Copyright © 2012-2014, JEDLSoft 9628 * 9629 * Licensed under the Apache License, Version 2.0 (the "License"); 9630 * you may not use this file except in compliance with the License. 9631 * You may obtain a copy of the License at 9632 * 9633 * http://www.apache.org/licenses/LICENSE-2.0 9634 * 9635 * Unless required by applicable law or agreed to in writing, software 9636 * distributed under the License is distributed on an "AS IS" BASIS, 9637 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9638 * 9639 * See the License for the specific language governing permissions and 9640 * limitations under the License. 9641 */ 9642 9643 9644 /* !depends calendar.js locale.js date.js julianday.js util/utils.js util/math.js */ 9645 9646 /** 9647 * @class 9648 * Construct a new Islamic calendar object. This class encodes information about 9649 * the civil Islamic calendar. The civil Islamic calendar is a tabular islamic 9650 * calendar where the dates are calculated by arithmetic rules. This differs from 9651 * the religious Islamic calendar which is used to mark the beginning of particular 9652 * holidays. The religious calendar depends on the first sighting of the new 9653 * crescent moon to determine the first day of the new month. Because humans and 9654 * weather are both involved, the actual time of sighting varies, so it is not 9655 * really possible to precalculate the religious calendar. Certain groups, such 9656 * as the Islamic Society of North America, decreed in in 2007 that they will use 9657 * a calendar based on calculations rather than observations to determine the 9658 * beginning of lunar months, and therefore the dates of holidays.<p> 9659 * 9660 * Depends directive: !depends islamic.js 9661 * 9662 * @constructor 9663 * @implements ilib.Cal 9664 */ 9665 ilib.Cal.Islamic = function() { 9666 this.type = "islamic"; 9667 }; 9668 9669 /** 9670 * the lengths of each month 9671 * @private 9672 * @const 9673 * @type Array.<number> 9674 */ 9675 ilib.Cal.Islamic.monthLengths = [ 9676 30, /* Muharram */ 9677 29, /* Saffar */ 9678 30, /* Rabi'I */ 9679 29, /* Rabi'II */ 9680 30, /* Jumada I */ 9681 29, /* Jumada II */ 9682 30, /* Rajab */ 9683 29, /* Sha'ban */ 9684 30, /* Ramadan */ 9685 29, /* Shawwal */ 9686 30, /* Dhu al-Qa'da */ 9687 29 /* Dhu al-Hijja */ 9688 ]; 9689 9690 9691 /** 9692 * Return the number of months in the given year. The number of months in a year varies 9693 * for luni-solar calendars because in some years, an extra month is needed to extend the 9694 * days in a year to an entire solar year. The month is represented as a 1-based number 9695 * where 1=first month, 2=second month, etc. 9696 * 9697 * @param {number} year a year for which the number of months is sought 9698 */ 9699 ilib.Cal.Islamic.prototype.getNumMonths = function(year) { 9700 return 12; 9701 }; 9702 9703 /** 9704 * Return the number of days in a particular month in a particular year. This function 9705 * can return a different number for a month depending on the year because of things 9706 * like leap years. 9707 * 9708 * @param {number} month the month for which the length is sought 9709 * @param {number} year the year within which that month can be found 9710 * @return {number} the number of days within the given month in the given year 9711 */ 9712 ilib.Cal.Islamic.prototype.getMonLength = function(month, year) { 9713 if (month !== 12) { 9714 return ilib.Cal.Islamic.monthLengths[month-1]; 9715 } else { 9716 return this.isLeapYear(year) ? 30 : 29; 9717 } 9718 }; 9719 9720 /** 9721 * Return true if the given year is a leap year in the Islamic calendar. 9722 * The year parameter may be given as a number, or as a IslamicDate object. 9723 * @param {number} year the year for which the leap year information is being sought 9724 * @return {boolean} true if the given year is a leap year 9725 */ 9726 ilib.Cal.Islamic.prototype.isLeapYear = function(year) { 9727 return (ilib.mod((14 + 11 * year), 30) < 11); 9728 }; 9729 9730 /** 9731 * Return the type of this calendar. 9732 * 9733 * @return {string} the name of the type of this calendar 9734 */ 9735 ilib.Cal.Islamic.prototype.getType = function() { 9736 return this.type; 9737 }; 9738 9739 /** 9740 * Return a date instance for this calendar type using the given 9741 * options. 9742 * @param {Object} options options controlling the construction of 9743 * the date instance 9744 * @return {ilib.Date} a date appropriate for this calendar type 9745 */ 9746 ilib.Cal.Islamic.prototype.newDateInstance = function (options) { 9747 return new ilib.Date.IslamicDate(options); 9748 }; 9749 9750 /*register this calendar for the factory method */ 9751 ilib.Cal._constructors["islamic"] = ilib.Cal.Islamic; 9752 9753 /* 9754 * util/search.js - Misc search utility routines 9755 * 9756 * Copyright © 2013, JEDLSoft 9757 * 9758 * Licensed under the Apache License, Version 2.0 (the "License"); 9759 * you may not use this file except in compliance with the License. 9760 * You may obtain a copy of the License at 9761 * 9762 * http://www.apache.org/licenses/LICENSE-2.0 9763 * 9764 * Unless required by applicable law or agreed to in writing, software 9765 * distributed under the License is distributed on an "AS IS" BASIS, 9766 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9767 * 9768 * See the License for the specific language governing permissions and 9769 * limitations under the License. 9770 */ 9771 9772 // !depends ilibglobal.js 9773 9774 /** 9775 * Binary search a sorted array for a particular target value. 9776 * If the exact value is not found, it returns the index of the smallest 9777 * entry that is greater than the given target value.<p> 9778 * 9779 * The comparator 9780 * parameter is a function that knows how to compare elements of the 9781 * array and the target. The function should return a value greater than 0 9782 * if the array element is greater than the target, a value less than 0 if 9783 * the array element is less than the target, and 0 if the array element 9784 * and the target are equivalent.<p> 9785 * 9786 * If the comparator function is not specified, this function assumes 9787 * the array and the target are numeric values and should be compared 9788 * as such.<p> 9789 * 9790 * Depends directive: !depends utils.js 9791 * 9792 * @static 9793 * @param {*} target element being sought 9794 * @param {Array} arr the array being searched 9795 * @param {?function(*,*)=} comparator a comparator that is appropriate for comparing two entries 9796 * in the array 9797 * @return the index of the array into which the value would fit if 9798 * inserted, or -1 if given array is not an array or the target is not 9799 * a number 9800 */ 9801 ilib.bsearch = function(target, arr, comparator) { 9802 if (typeof(arr) === 'undefined' || !arr || typeof(target) === 'undefined') { 9803 return -1; 9804 } 9805 9806 var high = arr.length - 1, 9807 low = 0, 9808 mid = 0, 9809 value, 9810 cmp = comparator || ilib.bsearch.numbers; 9811 9812 while (low <= high) { 9813 mid = Math.floor((high+low)/2); 9814 value = cmp(arr[mid], target); 9815 if (value > 0) { 9816 high = mid - 1; 9817 } else if (value < 0) { 9818 low = mid + 1; 9819 } else { 9820 return mid; 9821 } 9822 } 9823 9824 return low; 9825 }; 9826 9827 /** 9828 * Returns whether or not the given element is greater than, less than, 9829 * or equal to the given target.<p> 9830 * 9831 * @private 9832 * @static 9833 * @param {number} element the element being tested 9834 * @param {number} target the target being sought 9835 */ 9836 ilib.bsearch.numbers = function(element, target) { 9837 return element - target; 9838 }; 9839 9840 /** 9841 * Do a bisection search of a function for a particular target value.<p> 9842 * 9843 * The function to search is a function that takes a numeric parameter, 9844 * does calculations, and returns gives a numeric result. The 9845 * function should should be smooth and not have any discontinuities 9846 * between the low and high values of the parameter. 9847 * 9848 * Depends directive: !depends utils.js 9849 * 9850 * @static 9851 * @param {number} target value being sought 9852 * @param {number} low the lower bounds to start searching 9853 * @param {number} high the upper bounds to start searching 9854 * @param {number} precision minimum precision to support. Use 0 if you want to use the default. 9855 * @param {?function(number)=} func function to search 9856 * @return an approximation of the input value to the function that gives the desired 9857 * target output value, correct to within the error range of Javascript floating point 9858 * arithmetic, or NaN if there was some error 9859 */ 9860 ilib.bisectionSearch = function(target, low, high, precision, func) { 9861 if (typeof(target) !== 'number' || 9862 typeof(low) !== 'number' || 9863 typeof(high) !== 'number' || 9864 typeof(func) !== 'function') { 9865 return NaN; 9866 } 9867 9868 var mid = 0, 9869 value, 9870 pre = precision > 0 ? precision : 1e-13; 9871 9872 function compareSignificantDigits(a, b) { 9873 var leftParts = a.toExponential().split('e'); 9874 var rightParts = b.toExponential().split('e'); 9875 var left = new Number(leftParts[0]); 9876 var right = new Number(rightParts[0]); 9877 9878 return leftParts[1] === rightParts[1] && Math.abs(left - right) < pre; 9879 } 9880 9881 do { 9882 mid = (high+low)/2; 9883 value = func(mid); 9884 if (value > target) { 9885 high = mid; 9886 } else if (value < target) { 9887 low = mid; 9888 } 9889 } while (high - low > pre); 9890 9891 return mid; 9892 }; 9893 9894 9895 /* 9896 * islamicdate.js - Represent a date in the Islamic calendar 9897 * 9898 * Copyright © 2012-2014, JEDLSoft 9899 * 9900 * Licensed under the Apache License, Version 2.0 (the "License"); 9901 * you may not use this file except in compliance with the License. 9902 * You may obtain a copy of the License at 9903 * 9904 * http://www.apache.org/licenses/LICENSE-2.0 9905 * 9906 * Unless required by applicable law or agreed to in writing, software 9907 * distributed under the License is distributed on an "AS IS" BASIS, 9908 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9909 * 9910 * See the License for the specific language governing permissions and 9911 * limitations under the License. 9912 */ 9913 9914 /* !depends 9915 date.js 9916 calendar/islamic.js 9917 util/utils.js 9918 util/search.js 9919 util/math.js 9920 localeinfo.js 9921 julianday.js 9922 */ 9923 9924 /** 9925 * @class 9926 * Construct a new Islamic RD date number object. The constructor parameters can 9927 * contain any of the following properties: 9928 * 9929 * <ul> 9930 * <li><i>unixtime<i> - sets the time of this instance according to the given 9931 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970. 9932 * 9933 * <li><i>julianday</i> - sets the time of this instance according to the given 9934 * Julian Day instance or the Julian Day given as a float 9935 * 9936 * <li><i>year</i> - any integer, including 0 9937 * 9938 * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc. 9939 * 9940 * <li><i>day</i> - 1 to 31 9941 * 9942 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 9943 * is always done with an unambiguous 24 hour representation 9944 * 9945 * <li><i>minute</i> - 0 to 59 9946 * 9947 * <li><i>second</i> - 0 to 59 9948 * 9949 * <li><i>millisecond</i> - 0 to 999 9950 * 9951 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 9952 * </ul> 9953 * 9954 * If the constructor is called with another Islamic date instance instead of 9955 * a parameter block, the other instance acts as a parameter block and its 9956 * settings are copied into the current instance.<p> 9957 * 9958 * If the constructor is called with no arguments at all or if none of the 9959 * properties listed above are present, then the RD is calculate based on 9960 * the current date at the time of instantiation. <p> 9961 * 9962 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 9963 * specified in the params, it is assumed that they have the smallest possible 9964 * value in the range for the property (zero or one).<p> 9965 * 9966 * Depends directive: !depends islamicdate.js 9967 * 9968 * @private 9969 * @constructor 9970 * @extends ilib.Date.RataDie 9971 * @param {Object=} params parameters that govern the settings and behaviour of this Islamic RD date 9972 */ 9973 ilib.Date.IslamicRataDie = function(params) { 9974 this.cal = params && params.cal || new ilib.Cal.Islamic(); 9975 this.rd = undefined; 9976 ilib.Date.RataDie.call(this, params); 9977 }; 9978 9979 ilib.Date.IslamicRataDie.prototype = new ilib.Date.RataDie(); 9980 ilib.Date.IslamicRataDie.prototype.parent = ilib.Date.RataDie; 9981 ilib.Date.IslamicRataDie.prototype.constructor = ilib.Date.IslamicRataDie; 9982 9983 /** 9984 * The difference between a zero Julian day and the first Islamic date 9985 * of Friday, July 16, 622 CE Julian. 9986 * @private 9987 * @const 9988 * @type number 9989 */ 9990 ilib.Date.IslamicRataDie.prototype.epoch = 1948439.5; 9991 9992 /** 9993 * Calculate the Rata Die (fixed day) number of the given date from the 9994 * date components. 9995 * 9996 * @protected 9997 * @param {Object} date the date components to calculate the RD from 9998 */ 9999 ilib.Date.IslamicRataDie.prototype._setDateComponents = function(date) { 10000 var days = (date.year - 1) * 354 + 10001 Math.ceil(29.5 * (date.month - 1)) + 10002 date.day + 10003 Math.floor((3 + 11 * date.year) / 30) - 1; 10004 var time = (date.hour * 3600000 + 10005 date.minute * 60000 + 10006 date.second * 1000 + 10007 date.millisecond) / 10008 86400000; 10009 10010 //console.log("getRataDie: converting " + JSON.stringify(date)); 10011 //console.log("getRataDie: days is " + days); 10012 //console.log("getRataDie: time is " + time); 10013 //console.log("getRataDie: rd is " + (days + time)); 10014 10015 this.rd = days + time; 10016 }; 10017 10018 /** 10019 * @class 10020 * Construct a new civil Islamic date object. The constructor can be called 10021 * with a params object that can contain the following properties:<p> 10022 * 10023 * <ul> 10024 * <li><i>julianday</i> - the Julian Day to set into this date 10025 * <li><i>year</i> - any integer except 0. Years go from -1 (BCE) to 1 (CE), skipping the zero year 10026 * <li><i>month</i> - 1 to 12, where 1 means Muharram, 2 means Saffar, etc. 10027 * <li><i>day</i> - 1 to 30 10028 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 10029 * is always done with an unambiguous 24 hour representation 10030 * <li><i>minute</i> - 0 to 59 10031 * <li><i>second</i> - 0 to 59 10032 * <li><i>millisecond</i> - 0 to 999 10033 * <li><i>locale</i> - the ilib.TimeZone instance or time zone name as a string 10034 * of this julian date. The date/time is kept in the local time. The time zone 10035 * is used later if this date is formatted according to a different time zone and 10036 * the difference has to be calculated, or when the date format has a time zone 10037 * component in it. 10038 * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 10039 * given, it can be inferred from this locale. For locales that span multiple 10040 * time zones, the one with the largest population is chosen as the one that 10041 * represents the locale. 10042 * 10043 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 10044 * </ul> 10045 * 10046 * If called with another Islamic date argument, the date components of the given 10047 * date are copied into the current one.<p> 10048 * 10049 * If the constructor is called with no arguments at all or if none of the 10050 * properties listed above 10051 * from <i>julianday</i> through <i>millisecond</i> are present, then the date 10052 * components are 10053 * filled in with the current date at the time of instantiation. Note that if 10054 * you do not give the time zone when defaulting to the current time and the 10055 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 10056 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 10057 * Mean Time").<p> 10058 * 10059 * Depends directive: !depends islamicdate.js 10060 * 10061 * @constructor 10062 * @extends ilib.Date 10063 * @param {Object=} params parameters that govern the settings and behaviour of this Islamic date 10064 */ 10065 ilib.Date.IslamicDate = function(params) { 10066 this.cal = new ilib.Cal.Islamic(); 10067 10068 if (params) { 10069 if (params.locale) { 10070 this.locale = (typeof(params.locale) === 'string') ? new ilib.Locale(params.locale) : params.locale; 10071 var li = new ilib.LocaleInfo(this.locale); 10072 this.timezone = li.getTimeZone(); 10073 } 10074 if (params.timezone) { 10075 this.timezone = params.timezone; 10076 } 10077 10078 if (params.year || params.month || params.day || params.hour || 10079 params.minute || params.second || params.millisecond ) { 10080 /** 10081 * Year in the Islamic calendar. 10082 * @type number 10083 */ 10084 this.year = parseInt(params.year, 10) || 0; 10085 10086 /** 10087 * The month number, ranging from 1 to 12 (December). 10088 * @type number 10089 */ 10090 this.month = parseInt(params.month, 10) || 1; 10091 10092 /** 10093 * The day of the month. This ranges from 1 to 30. 10094 * @type number 10095 */ 10096 this.day = parseInt(params.day, 10) || 1; 10097 10098 /** 10099 * The hour of the day. This can be a number from 0 to 23, as times are 10100 * stored unambiguously in the 24-hour clock. 10101 * @type number 10102 */ 10103 this.hour = parseInt(params.hour, 10) || 0; 10104 10105 /** 10106 * The minute of the hours. Ranges from 0 to 59. 10107 * @type number 10108 */ 10109 this.minute = parseInt(params.minute, 10) || 0; 10110 10111 /** 10112 * The second of the minute. Ranges from 0 to 59. 10113 * @type number 10114 */ 10115 this.second = parseInt(params.second, 10) || 0; 10116 10117 /** 10118 * The millisecond of the second. Ranges from 0 to 999. 10119 * @type number 10120 */ 10121 this.millisecond = parseInt(params.millisecond, 10) || 0; 10122 10123 /** 10124 * The day of the year. Ranges from 1 to 355. 10125 * @type number 10126 */ 10127 this.dayOfYear = parseInt(params.dayOfYear, 10); 10128 10129 if (typeof(params.dst) === 'boolean') { 10130 this.dst = params.dst; 10131 } 10132 10133 this.rd = this.newRd(this); 10134 10135 // add the time zone offset to the rd to convert to UTC 10136 if (!this.tz) { 10137 this.tz = new ilib.TimeZone({id: this.timezone}); 10138 } 10139 // getOffsetMillis requires that this.year, this.rd, and this.dst 10140 // are set in order to figure out which time zone rules apply and 10141 // what the offset is at that point in the year 10142 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000; 10143 if (this.offset !== 0) { 10144 this.rd = this.newRd({ 10145 rd: this.rd.getRataDie() - this.offset 10146 }); 10147 } 10148 } 10149 } 10150 10151 if (!this.rd) { 10152 this.rd = this.newRd(params); 10153 this._calcDateComponents(); 10154 } 10155 }; 10156 10157 ilib.Date.IslamicDate.prototype = new ilib.Date({noinstance: true}); 10158 ilib.Date.IslamicDate.prototype.parent = ilib.Date; 10159 ilib.Date.IslamicDate.prototype.constructor = ilib.Date.IslamicDate; 10160 10161 /** 10162 * the cumulative lengths of each month, for a non-leap year 10163 * @private 10164 * @const 10165 * @type Array.<number> 10166 */ 10167 ilib.Date.IslamicDate.cumMonthLengths = [ 10168 0, /* Muharram */ 10169 30, /* Saffar */ 10170 59, /* Rabi'I */ 10171 89, /* Rabi'II */ 10172 118, /* Jumada I */ 10173 148, /* Jumada II */ 10174 177, /* Rajab */ 10175 207, /* Sha'ban */ 10176 236, /* Ramadan */ 10177 266, /* Shawwal */ 10178 295, /* Dhu al-Qa'da */ 10179 325, /* Dhu al-Hijja */ 10180 354 10181 ]; 10182 10183 /** 10184 * Number of days difference between RD 0 of the Gregorian calendar and 10185 * RD 0 of the Islamic calendar. 10186 * @private 10187 * @const 10188 * @type number 10189 */ 10190 ilib.Date.IslamicDate.GregorianDiff = 227015; 10191 10192 /** 10193 * Return a new RD for this date type using the given params. 10194 * @protected 10195 * @param {Object=} params the parameters used to create this rata die instance 10196 * @returns {ilib.Date.RataDie} the new RD instance for the given params 10197 */ 10198 ilib.Date.IslamicDate.prototype.newRd = function (params) { 10199 return new ilib.Date.IslamicRataDie(params); 10200 }; 10201 10202 /** 10203 * Return the year for the given RD 10204 * @protected 10205 * @param {number} rd RD to calculate from 10206 * @returns {number} the year for the RD 10207 */ 10208 ilib.Date.IslamicDate.prototype._calcYear = function(rd) { 10209 return Math.floor((30 * rd + 10646) / 10631); 10210 }; 10211 10212 /** 10213 * Calculate date components for the given RD date. 10214 * @protected 10215 */ 10216 ilib.Date.IslamicDate.prototype._calcDateComponents = function () { 10217 var remainder, 10218 rd = this.rd.getRataDie(); 10219 10220 this.year = this._calcYear(rd); 10221 10222 if (typeof(this.offset) === "undefined") { 10223 this.year = this._calcYear(rd); 10224 10225 // now offset the RD by the time zone, then recalculate in case we were 10226 // near the year boundary 10227 if (!this.tz) { 10228 this.tz = new ilib.TimeZone({id: this.timezone}); 10229 } 10230 this.offset = this.tz.getOffsetMillis(this) / 86400000; 10231 } 10232 10233 if (this.offset !== 0) { 10234 rd += this.offset; 10235 this.year = this._calcYear(rd); 10236 } 10237 10238 //console.log("IslamicDate.calcComponent: calculating for rd " + rd); 10239 //console.log("IslamicDate.calcComponent: year is " + ret.year); 10240 var yearStart = this.newRd({ 10241 year: this.year, 10242 month: 1, 10243 day: 1, 10244 hour: 0, 10245 minute: 0, 10246 second: 0, 10247 millisecond: 0 10248 }); 10249 remainder = rd - yearStart.getRataDie() + 1; 10250 10251 this.dayOfYear = remainder; 10252 10253 //console.log("IslamicDate.calcComponent: remainder is " + remainder); 10254 10255 this.month = ilib.bsearch(remainder, ilib.Date.IslamicDate.cumMonthLengths); 10256 remainder -= ilib.Date.IslamicDate.cumMonthLengths[this.month-1]; 10257 10258 //console.log("IslamicDate.calcComponent: month is " + this.month + " and remainder is " + remainder); 10259 10260 this.day = Math.floor(remainder); 10261 remainder -= this.day; 10262 10263 //console.log("IslamicDate.calcComponent: day is " + this.day + " and remainder is " + remainder); 10264 10265 // now convert to milliseconds for the rest of the calculation 10266 remainder = Math.round(remainder * 86400000); 10267 10268 this.hour = Math.floor(remainder/3600000); 10269 remainder -= this.hour * 3600000; 10270 10271 this.minute = Math.floor(remainder/60000); 10272 remainder -= this.minute * 60000; 10273 10274 this.second = Math.floor(remainder/1000); 10275 remainder -= this.second * 1000; 10276 10277 this.millisecond = remainder; 10278 }; 10279 10280 /** 10281 * Return the day of the week of this date. The day of the week is encoded 10282 * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday. 10283 * 10284 * @return {number} the day of the week 10285 */ 10286 ilib.Date.IslamicDate.prototype.getDayOfWeek = function() { 10287 var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0)); 10288 return ilib.mod(rd-2, 7); 10289 }; 10290 10291 /** 10292 * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 10293 * 354 or 355, regardless of months or weeks, etc. That is, Muharran 1st is day 1, and 10294 * Dhu al-Hijja 29 is 354. 10295 * @return {number} the ordinal day of the year 10296 */ 10297 ilib.Date.IslamicDate.prototype.getDayOfYear = function() { 10298 return ilib.Date.IslamicDate.cumMonthLengths[this.month-1] + this.day; 10299 }; 10300 10301 /** 10302 * Return the era for this date as a number. The value for the era for Islamic 10303 * calendars is -1 for "before the Islamic era" and 1 for "the Islamic era". 10304 * Islamic era dates are any date after Muharran 1, 1, which is the same as 10305 * July 16, 622 CE in the Gregorian calendar. 10306 * 10307 * @return {number} 1 if this date is in the common era, -1 if it is before the 10308 * common era 10309 */ 10310 ilib.Date.IslamicDate.prototype.getEra = function() { 10311 return (this.year < 1) ? -1 : 1; 10312 }; 10313 10314 /** 10315 * Return the name of the calendar that governs this date. 10316 * 10317 * @return {string} a string giving the name of the calendar 10318 */ 10319 ilib.Date.IslamicDate.prototype.getCalendar = function() { 10320 return "islamic"; 10321 }; 10322 10323 //register with the factory method 10324 ilib.Date._constructors["islamic"] = ilib.Date.IslamicDate; 10325 /* 10326 * julian.js - Represent a Julian calendar object. 10327 * 10328 * Copyright © 2012-2014, JEDLSoft 10329 * 10330 * Licensed under the Apache License, Version 2.0 (the "License"); 10331 * you may not use this file except in compliance with the License. 10332 * You may obtain a copy of the License at 10333 * 10334 * http://www.apache.org/licenses/LICENSE-2.0 10335 * 10336 * Unless required by applicable law or agreed to in writing, software 10337 * distributed under the License is distributed on an "AS IS" BASIS, 10338 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10339 * 10340 * See the License for the specific language governing permissions and 10341 * limitations under the License. 10342 */ 10343 10344 10345 /* !depends calendar.js locale.js date.js julianday.js util/utils.js util/math.js */ 10346 10347 /** 10348 * @class 10349 * Construct a new Julian calendar object. This class encodes information about 10350 * a Julian calendar.<p> 10351 * 10352 * Depends directive: !depends julian.js 10353 * 10354 * @constructor 10355 * @implements ilib.Cal 10356 */ 10357 ilib.Cal.Julian = function() { 10358 this.type = "julian"; 10359 }; 10360 10361 /* the lengths of each month */ 10362 ilib.Cal.Julian.monthLengths = [ 10363 31, /* Jan */ 10364 28, /* Feb */ 10365 31, /* Mar */ 10366 30, /* Apr */ 10367 31, /* May */ 10368 30, /* Jun */ 10369 31, /* Jul */ 10370 31, /* Aug */ 10371 30, /* Sep */ 10372 31, /* Oct */ 10373 30, /* Nov */ 10374 31 /* Dec */ 10375 ]; 10376 10377 /** 10378 * Return the number of months in the given year. The number of months in a year varies 10379 * for lunar calendars because in some years, an extra month is needed to extend the 10380 * days in a year to an entire solar year. The month is represented as a 1-based number 10381 * where 1=Jaunary, 2=February, etc. until 12=December. 10382 * 10383 * @param {number} year a year for which the number of months is sought 10384 */ 10385 ilib.Cal.Julian.prototype.getNumMonths = function(year) { 10386 return 12; 10387 }; 10388 10389 /** 10390 * Return the number of days in a particular month in a particular year. This function 10391 * can return a different number for a month depending on the year because of things 10392 * like leap years. 10393 * 10394 * @param {number} month the month for which the length is sought 10395 * @param {number} year the year within which that month can be found 10396 * @return {number} the number of days within the given month in the given year 10397 */ 10398 ilib.Cal.Julian.prototype.getMonLength = function(month, year) { 10399 if (month !== 2 || !this.isLeapYear(year)) { 10400 return ilib.Cal.Julian.monthLengths[month-1]; 10401 } else { 10402 return 29; 10403 } 10404 }; 10405 10406 /** 10407 * Return true if the given year is a leap year in the Julian calendar. 10408 * The year parameter may be given as a number, or as a JulDate object. 10409 * @param {number|ilib.Date.JulDate} year the year for which the leap year information is being sought 10410 * @return {boolean} true if the given year is a leap year 10411 */ 10412 ilib.Cal.Julian.prototype.isLeapYear = function(year) { 10413 var y = (typeof(year) === 'number' ? year : year.year); 10414 return ilib.mod(y, 4) === ((year > 0) ? 0 : 3); 10415 }; 10416 10417 /** 10418 * Return the type of this calendar. 10419 * 10420 * @return {string} the name of the type of this calendar 10421 */ 10422 ilib.Cal.Julian.prototype.getType = function() { 10423 return this.type; 10424 }; 10425 10426 /** 10427 * Return a date instance for this calendar type using the given 10428 * options. 10429 * @param {Object} options options controlling the construction of 10430 * the date instance 10431 * @return {ilib.Date} a date appropriate for this calendar type 10432 */ 10433 ilib.Cal.Julian.prototype.newDateInstance = function (options) { 10434 return new ilib.Date.JulDate(options); 10435 }; 10436 10437 /* register this calendar for the factory method */ 10438 ilib.Cal._constructors["julian"] = ilib.Cal.Julian; 10439 /* 10440 * juliandate.js - Represent a date in the Julian calendar 10441 * 10442 * Copyright © 2012-2014, JEDLSoft 10443 * 10444 * Licensed under the Apache License, Version 2.0 (the "License"); 10445 * you may not use this file except in compliance with the License. 10446 * You may obtain a copy of the License at 10447 * 10448 * http://www.apache.org/licenses/LICENSE-2.0 10449 * 10450 * Unless required by applicable law or agreed to in writing, software 10451 * distributed under the License is distributed on an "AS IS" BASIS, 10452 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10453 * 10454 * See the License for the specific language governing permissions and 10455 * limitations under the License. 10456 */ 10457 10458 /* !depends 10459 date.js 10460 calendar/julian.js 10461 util/utils.js 10462 util/search.js 10463 util/math.js 10464 localeinfo.js 10465 julianday.js 10466 */ 10467 10468 /** 10469 * @class 10470 * Construct a new Julian RD date number object. The constructor parameters can 10471 * contain any of the following properties: 10472 * 10473 * <ul> 10474 * <li><i>unixtime<i> - sets the time of this instance according to the given 10475 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970. 10476 * 10477 * <li><i>julianday</i> - sets the time of this instance according to the given 10478 * Julian Day instance or the Julian Day given as a float 10479 * 10480 * <li><i>year</i> - any integer, including 0 10481 * 10482 * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc. 10483 * 10484 * <li><i>day</i> - 1 to 31 10485 * 10486 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 10487 * is always done with an unambiguous 24 hour representation 10488 * 10489 * <li><i>minute</i> - 0 to 59 10490 * 10491 * <li><i>second</i> - 0 to 59 10492 * 10493 * <li><i>millisecond</i> - 0 to 999 10494 * 10495 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 10496 * </ul> 10497 * 10498 * If the constructor is called with another Julian date instance instead of 10499 * a parameter block, the other instance acts as a parameter block and its 10500 * settings are copied into the current instance.<p> 10501 * 10502 * If the constructor is called with no arguments at all or if none of the 10503 * properties listed above are present, then the RD is calculate based on 10504 * the current date at the time of instantiation. <p> 10505 * 10506 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 10507 * specified in the params, it is assumed that they have the smallest possible 10508 * value in the range for the property (zero or one).<p> 10509 * 10510 * Depends directive: !depends juliandate.js 10511 * 10512 * @private 10513 * @constructor 10514 * @extends ilib.Date.RataDie 10515 * @param {Object=} params parameters that govern the settings and behaviour of this Julian RD date 10516 */ 10517 ilib.Date.JulianRataDie = function(params) { 10518 this.cal = params && params.cal || new ilib.Cal.Julian(); 10519 this.rd = undefined; 10520 ilib.Date.RataDie.call(this, params); 10521 }; 10522 10523 ilib.Date.JulianRataDie.prototype = new ilib.Date.RataDie(); 10524 ilib.Date.JulianRataDie.prototype.parent = ilib.Date.RataDie; 10525 ilib.Date.JulianRataDie.prototype.constructor = ilib.Date.JulianRataDie; 10526 10527 /** 10528 * The difference between a zero Julian day and the first Julian date 10529 * of Friday, July 16, 622 CE Julian. 10530 * @private 10531 * @const 10532 * @type number 10533 */ 10534 ilib.Date.JulianRataDie.prototype.epoch = 1721422.5; 10535 10536 /** 10537 * Calculate the Rata Die (fixed day) number of the given date from the 10538 * date components. 10539 * 10540 * @protected 10541 * @param {Object} date the date components to calculate the RD from 10542 */ 10543 ilib.Date.JulianRataDie.prototype._setDateComponents = function(date) { 10544 var year = date.year + ((date.year < 0) ? 1 : 0); 10545 var years = 365 * (year - 1) + Math.floor((year-1)/4); 10546 var dayInYear = (date.month > 1 ? ilib.Date.JulDate.cumMonthLengths[date.month-1] : 0) + 10547 date.day + 10548 (this.cal.isLeapYear(date.year) && date.month > 2 ? 1 : 0); 10549 var rdtime = (date.hour * 3600000 + 10550 date.minute * 60000 + 10551 date.second * 1000 + 10552 date.millisecond) / 10553 86400000; 10554 10555 /* 10556 console.log("calcRataDie: converting " + JSON.stringify(parts)); 10557 console.log("getRataDie: year is " + years); 10558 console.log("getRataDie: day in year is " + dayInYear); 10559 console.log("getRataDie: rdtime is " + rdtime); 10560 console.log("getRataDie: rd is " + (years + dayInYear + rdtime)); 10561 */ 10562 10563 this.rd = years + dayInYear + rdtime; 10564 }; 10565 10566 /** 10567 * @class 10568 * Construct a new date object for the Julian Calendar. The constructor can be called 10569 * with a parameter object that contains any of the following properties: 10570 * 10571 * <ul> 10572 * <li><i>unixtime<i> - sets the time of this instance according to the given 10573 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970 (Gregorian). 10574 * <li><i>julianday</i> - the Julian Day to set into this date 10575 * <li><i>year</i> - any integer except 0. Years go from -1 (BCE) to 1 (CE), skipping the zero 10576 * year which doesn't exist in the Julian calendar 10577 * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc. 10578 * <li><i>day</i> - 1 to 31 10579 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 10580 * is always done with an unambiguous 24 hour representation 10581 * <li><i>minute</i> - 0 to 59 10582 * <li><i>second</i> - 0 to 59 10583 * <li><i>millisecond<i> - 0 to 999 10584 * <li><i>locale</i> - the ilib.TimeZone instance or time zone name as a string 10585 * of this julian date. The date/time is kept in the local time. The time zone 10586 * is used later if this date is formatted according to a different time zone and 10587 * the difference has to be calculated, or when the date format has a time zone 10588 * component in it. 10589 * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 10590 * given, it can be inferred from this locale. For locales that span multiple 10591 * time zones, the one with the largest population is chosen as the one that 10592 * represents the locale. 10593 * 10594 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 10595 * </ul> 10596 * 10597 * NB. The <a href="http://en.wikipedia.org/wiki/Julian_date">Julian Day</a> 10598 * (ilib.JulianDay) object is a <i>different</i> object than a 10599 * <a href="http://en.wikipedia.org/wiki/Julian_calendar">date in 10600 * the Julian calendar</a> and the two are not to be confused. The Julian Day 10601 * object represents time as a number of whole and fractional days since the 10602 * beginning of the epoch, whereas a date in the Julian 10603 * calendar is a regular date that signifies year, month, day, etc. using the rules 10604 * of the Julian calendar. The naming of Julian Days and the Julian calendar are 10605 * unfortunately close, and come from history.<p> 10606 * 10607 * If called with another Julian date argument, the date components of the given 10608 * date are copied into the current one.<p> 10609 * 10610 * If the constructor is called with no arguments at all or if none of the 10611 * properties listed above 10612 * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 10613 * components are 10614 * filled in with the current date at the time of instantiation. Note that if 10615 * you do not give the time zone when defaulting to the current time and the 10616 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 10617 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 10618 * Mean Time").<p> 10619 * 10620 * Depends directive: !depends juliandate.js 10621 * 10622 * @constructor 10623 * @extends ilib.Date 10624 * @param {Object=} params parameters that govern the settings and behaviour of this Julian date 10625 */ 10626 ilib.Date.JulDate = function(params) { 10627 this.cal = new ilib.Cal.Julian(); 10628 10629 if (params) { 10630 if (params.locale) { 10631 this.locale = (typeof(params.locale) === 'string') ? new ilib.Locale(params.locale) : params.locale; 10632 var li = new ilib.LocaleInfo(this.locale); 10633 this.timezone = li.getTimeZone(); 10634 } 10635 if (params.timezone) { 10636 this.timezone = params.timezone; 10637 } 10638 10639 if (params.year || params.month || params.day || params.hour || 10640 params.minute || params.second || params.millisecond ) { 10641 /** 10642 * Year in the Julian calendar. 10643 * @type number 10644 */ 10645 this.year = parseInt(params.year, 10) || 0; 10646 /** 10647 * The month number, ranging from 1 (January) to 12 (December). 10648 * @type number 10649 */ 10650 this.month = parseInt(params.month, 10) || 1; 10651 /** 10652 * The day of the month. This ranges from 1 to 31. 10653 * @type number 10654 */ 10655 this.day = parseInt(params.day, 10) || 1; 10656 /** 10657 * The hour of the day. This can be a number from 0 to 23, as times are 10658 * stored unambiguously in the 24-hour clock. 10659 * @type number 10660 */ 10661 this.hour = parseInt(params.hour, 10) || 0; 10662 /** 10663 * The minute of the hours. Ranges from 0 to 59. 10664 * @type number 10665 */ 10666 this.minute = parseInt(params.minute, 10) || 0; 10667 /** 10668 * The second of the minute. Ranges from 0 to 59. 10669 * @type number 10670 */ 10671 this.second = parseInt(params.second, 10) || 0; 10672 /** 10673 * The millisecond of the second. Ranges from 0 to 999. 10674 * @type number 10675 */ 10676 this.millisecond = parseInt(params.millisecond, 10) || 0; 10677 10678 /** 10679 * The day of the year. Ranges from 1 to 383. 10680 * @type number 10681 */ 10682 this.dayOfYear = parseInt(params.dayOfYear, 10); 10683 10684 if (typeof(params.dst) === 'boolean') { 10685 this.dst = params.dst; 10686 } 10687 10688 this.rd = this.newRd(this); 10689 10690 // add the time zone offset to the rd to convert to UTC 10691 if (!this.tz) { 10692 this.tz = new ilib.TimeZone({id: this.timezone}); 10693 } 10694 // getOffsetMillis requires that this.year, this.rd, and this.dst 10695 // are set in order to figure out which time zone rules apply and 10696 // what the offset is at that point in the year 10697 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000; 10698 if (this.offset !== 0) { 10699 this.rd = this.newRd({ 10700 rd: this.rd.getRataDie() - this.offset 10701 }); 10702 } 10703 } 10704 } 10705 10706 if (!this.rd) { 10707 this.rd = this.newRd(params); 10708 this._calcDateComponents(); 10709 } 10710 }; 10711 10712 ilib.Date.JulDate.prototype = new ilib.Date({noinstance: true}); 10713 ilib.Date.JulDate.prototype.parent = ilib.Date; 10714 ilib.Date.JulDate.prototype.constructor = ilib.Date.JulDate; 10715 10716 /** 10717 * the cumulative lengths of each month, for a non-leap year 10718 * @private 10719 * @const 10720 * @type Array.<number> 10721 */ 10722 ilib.Date.JulDate.cumMonthLengths = [ 10723 0, /* Jan */ 10724 31, /* Feb */ 10725 59, /* Mar */ 10726 90, /* Apr */ 10727 120, /* May */ 10728 151, /* Jun */ 10729 181, /* Jul */ 10730 212, /* Aug */ 10731 243, /* Sep */ 10732 273, /* Oct */ 10733 304, /* Nov */ 10734 334, /* Dec */ 10735 365 10736 ]; 10737 10738 /** 10739 * the cumulative lengths of each month, for a leap year 10740 * @private 10741 * @const 10742 * @type Array.<number> 10743 */ 10744 ilib.Date.JulDate.cumMonthLengthsLeap = [ 10745 0, /* Jan */ 10746 31, /* Feb */ 10747 60, /* Mar */ 10748 91, /* Apr */ 10749 121, /* May */ 10750 152, /* Jun */ 10751 182, /* Jul */ 10752 213, /* Aug */ 10753 244, /* Sep */ 10754 274, /* Oct */ 10755 305, /* Nov */ 10756 335, /* Dec */ 10757 366 10758 ]; 10759 10760 /** 10761 * Return a new RD for this date type using the given params. 10762 * @protected 10763 * @param {Object=} params the parameters used to create this rata die instance 10764 * @returns {ilib.Date.RataDie} the new RD instance for the given params 10765 */ 10766 ilib.Date.JulDate.prototype.newRd = function (params) { 10767 return new ilib.Date.JulianRataDie(params); 10768 }; 10769 10770 /** 10771 * Return the year for the given RD 10772 * @protected 10773 * @param {number} rd RD to calculate from 10774 * @returns {number} the year for the RD 10775 */ 10776 ilib.Date.JulDate.prototype._calcYear = function(rd) { 10777 var year = Math.floor((4*(Math.floor(rd)-1) + 1464)/1461); 10778 10779 return (year <= 0) ? year - 1 : year; 10780 }; 10781 10782 /** 10783 * Calculate date components for the given RD date. 10784 * @protected 10785 */ 10786 ilib.Date.JulDate.prototype._calcDateComponents = function () { 10787 var remainder, 10788 cumulative, 10789 rd = this.rd.getRataDie(); 10790 10791 this.year = this._calcYear(rd); 10792 10793 if (typeof(this.offset) === "undefined") { 10794 this.year = this._calcYear(rd); 10795 10796 // now offset the RD by the time zone, then recalculate in case we were 10797 // near the year boundary 10798 if (!this.tz) { 10799 this.tz = new ilib.TimeZone({id: this.timezone}); 10800 } 10801 this.offset = this.tz.getOffsetMillis(this) / 86400000; 10802 } 10803 10804 if (this.offset !== 0) { 10805 rd += this.offset; 10806 this.year = this._calcYear(rd); 10807 } 10808 10809 var jan1 = this.newRd({ 10810 year: this.year, 10811 month: 1, 10812 day: 1, 10813 hour: 0, 10814 minute: 0, 10815 second: 0, 10816 millisecond: 0 10817 }); 10818 remainder = rd + 1 - jan1.getRataDie(); 10819 10820 cumulative = this.cal.isLeapYear(this.year) ? 10821 ilib.Date.JulDate.cumMonthLengthsLeap : 10822 ilib.Date.JulDate.cumMonthLengths; 10823 10824 this.month = ilib.bsearch(Math.floor(remainder), cumulative); 10825 remainder = remainder - cumulative[this.month-1]; 10826 10827 this.day = Math.floor(remainder); 10828 remainder -= this.day; 10829 // now convert to milliseconds for the rest of the calculation 10830 remainder = Math.round(remainder * 86400000); 10831 10832 this.hour = Math.floor(remainder/3600000); 10833 remainder -= this.hour * 3600000; 10834 10835 this.minute = Math.floor(remainder/60000); 10836 remainder -= this.minute * 60000; 10837 10838 this.second = Math.floor(remainder/1000); 10839 remainder -= this.second * 1000; 10840 10841 this.millisecond = remainder; 10842 }; 10843 10844 /** 10845 * Return the day of the week of this date. The day of the week is encoded 10846 * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday. 10847 * 10848 * @return {number} the day of the week 10849 */ 10850 ilib.Date.JulDate.prototype.getDayOfWeek = function() { 10851 var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0)); 10852 return ilib.mod(rd-2, 7); 10853 }; 10854 10855 /** 10856 * Return the name of the calendar that governs this date. 10857 * 10858 * @return {string} a string giving the name of the calendar 10859 */ 10860 ilib.Date.JulDate.prototype.getCalendar = function() { 10861 return "julian"; 10862 }; 10863 10864 //register with the factory method 10865 ilib.Date._constructors["julian"] = ilib.Date.JulDate; 10866 /* 10867 * gregoriandate.js - Represent a date in the Gregorian calendar 10868 * 10869 * Copyright © 2012-2014, JEDLSoft 10870 * 10871 * Licensed under the Apache License, Version 2.0 (the "License"); 10872 * you may not use this file except in compliance with the License. 10873 * You may obtain a copy of the License at 10874 * 10875 * http://www.apache.org/licenses/LICENSE-2.0 10876 * 10877 * Unless required by applicable law or agreed to in writing, software 10878 * distributed under the License is distributed on an "AS IS" BASIS, 10879 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10880 * 10881 * See the License for the specific language governing permissions and 10882 * limitations under the License. 10883 */ 10884 10885 /* !depends 10886 date.js 10887 calendar/gregorian.js 10888 util/utils.js 10889 util/search.js 10890 util/math.js 10891 localeinfo.js 10892 julianday.js 10893 calendar/gregratadie.js 10894 timezone.js 10895 */ 10896 10897 /** 10898 * @class 10899 * Construct a new Gregorian date object. The constructor parameters can 10900 * contain any of the following properties: 10901 * 10902 * <ul> 10903 * <li><i>unixtime<i> - sets the time of this instance according to the given 10904 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970. 10905 * 10906 * <li><i>julianday</i> - sets the time of this instance according to the given 10907 * Julian Day instance or the Julian Day given as a float 10908 * 10909 * <li><i>year</i> - any integer, including 0 10910 * 10911 * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc. 10912 * 10913 * <li><i>day</i> - 1 to 31 10914 * 10915 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 10916 * is always done with an unambiguous 24 hour representation 10917 * 10918 * <li><i>minute</i> - 0 to 59 10919 * 10920 * <li><i>second</i> - 0 to 59 10921 * 10922 * <li><i>millisecond</i> - 0 to 999 10923 * 10924 * <li><i>dst</i> - boolean used to specify whether the given time components are 10925 * intended to be in daylight time or not. This is only used in the overlap 10926 * time when transitioning from DST to standard time, and the time components are 10927 * ambiguous. Otherwise at all other times of the year, this flag is ignored. 10928 * If you specify the date using unix time (UTC) or a julian day, then the time is 10929 * already unambiguous and this flag does not need to be specified. 10930 * <p> 10931 * For example, in the US, the transition out of daylight savings time 10932 * in 2014 happens at Nov 2, 2014 2:00am Daylight Time, when the time falls 10933 * back to Nov 2, 2014 1:00am Standard Time. If you give a date/time components as 10934 * "Nov 2, 2014 1:30am", then there are two 1:30am times in that day, and you would 10935 * have to give the standard flag to indicate which of those two you mean. 10936 * (dst=true means daylight time, dst=false means standard time). 10937 * 10938 * <li><i>timezone</i> - the ilib.TimeZone instance or time zone name as a string 10939 * of this gregorian date. The date/time is kept in the local time. The time zone 10940 * is used later if this date is formatted according to a different time zone and 10941 * the difference has to be calculated, or when the date format has a time zone 10942 * component in it. 10943 * 10944 * <li><i>locale</i> - locale for this gregorian date. If the time zone is not 10945 * given, it can be inferred from this locale. For locales that span multiple 10946 * time zones, the one with the largest population is chosen as the one that 10947 * represents the locale. 10948 * 10949 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 10950 * </ul> 10951 * 10952 * If the constructor is called with another Gregorian date instance instead of 10953 * a parameter block, the other instance acts as a parameter block and its 10954 * settings are copied into the current instance.<p> 10955 * 10956 * If the constructor is called with no arguments at all or if none of the 10957 * properties listed above 10958 * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 10959 * components are 10960 * filled in with the current date at the time of instantiation. Note that if 10961 * you do not give the time zone when defaulting to the current time and the 10962 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 10963 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 10964 * Mean Time").<p> 10965 * 10966 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 10967 * specified in the params, it is assumed that they have the smallest possible 10968 * value in the range for the property (zero or one).<p> 10969 * 10970 * Depends directive: !depends gregoriandate.js 10971 * 10972 * @constructor 10973 * @extends ilib.Date 10974 * @param {Object=} params parameters that govern the settings and behaviour of this Gregorian date 10975 */ 10976 ilib.Date.GregDate = function(params) { 10977 this.cal = new ilib.Cal.Gregorian(); 10978 this.timezone = "local"; 10979 10980 if (params) { 10981 if (typeof(params.noinstance) === 'boolean' && params.noinstance) { 10982 // for doing inheritance, so don't need to fill in the data. The inheriting class only wants the methods. 10983 return; 10984 } 10985 if (params.locale) { 10986 this.locale = (typeof(params.locale) === 'string') ? new ilib.Locale(params.locale) : params.locale; 10987 var li = new ilib.LocaleInfo(this.locale); 10988 this.timezone = li.getTimeZone(); 10989 } 10990 if (params.timezone) { 10991 this.timezone = params.timezone.toString(); 10992 } 10993 10994 if (params.year || params.month || params.day || params.hour || 10995 params.minute || params.second || params.millisecond ) { 10996 this.year = parseInt(params.year, 10) || 0; 10997 this.month = parseInt(params.month, 10) || 1; 10998 this.day = parseInt(params.day, 10) || 1; 10999 this.hour = parseInt(params.hour, 10) || 0; 11000 this.minute = parseInt(params.minute, 10) || 0; 11001 this.second = parseInt(params.second, 10) || 0; 11002 this.millisecond = parseInt(params.millisecond, 10) || 0; 11003 if (typeof(params.dst) === 'boolean') { 11004 this.dst = params.dst; 11005 } 11006 this.rd = this.newRd(params); 11007 11008 // add the time zone offset to the rd to convert to UTC 11009 this.offset = 0; 11010 if (this.timezone === "local" && typeof(params.dst) === 'undefined') { 11011 // if dst is defined, the intrinsic Date object has no way of specifying which version of a time you mean 11012 // in the overlap time at the end of DST. Do you mean the daylight 1:30am or the standard 1:30am? In this 11013 // case, use the ilib calculations below, which can distinguish between the two properly 11014 var d = new Date(this.year, this.month-1, this.day, this.hour, this.minute, this.second, this.millisecond); 11015 this.offset = -d.getTimezoneOffset() / 1440; 11016 } else { 11017 if (!this.tz) { 11018 this.tz = new ilib.TimeZone({id: this.timezone}); 11019 } 11020 // getOffsetMillis requires that this.year, this.rd, and this.dst 11021 // are set in order to figure out which time zone rules apply and 11022 // what the offset is at that point in the year 11023 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000; 11024 } 11025 if (this.offset !== 0) { 11026 this.rd = this.newRd({ 11027 rd: this.rd.getRataDie() - this.offset 11028 }); 11029 } 11030 } 11031 } 11032 11033 if (!this.rd) { 11034 this.rd = this.newRd(params); 11035 this._calcDateComponents(); 11036 } 11037 }; 11038 11039 ilib.Date.GregDate.prototype = new ilib.Date({noinstance: true}); 11040 ilib.Date.GregDate.prototype.parent = ilib.Date; 11041 ilib.Date.GregDate.prototype.constructor = ilib.Date.GregDate; 11042 11043 /** 11044 * Return a new RD for this date type using the given params. 11045 * @private 11046 * @param {Object=} params the parameters used to create this rata die instance 11047 * @returns {ilib.Date.RataDie} the new RD instance for the given params 11048 */ 11049 ilib.Date.GregDate.prototype.newRd = function (params) { 11050 return new ilib.Date.GregRataDie(params); 11051 }; 11052 11053 /** 11054 * Calculates the Gregorian year for a given rd number. 11055 * @private 11056 * @static 11057 */ 11058 ilib.Date.GregDate._calcYear = function(rd) { 11059 var days400, 11060 days100, 11061 days4, 11062 years400, 11063 years100, 11064 years4, 11065 years1, 11066 year; 11067 11068 years400 = Math.floor((rd - 1) / 146097); 11069 days400 = ilib.mod((rd - 1), 146097); 11070 years100 = Math.floor(days400 / 36524); 11071 days100 = ilib.mod(days400, 36524); 11072 years4 = Math.floor(days100 / 1461); 11073 days4 = ilib.mod(days100, 1461); 11074 years1 = Math.floor(days4 / 365); 11075 11076 year = 400 * years400 + 100 * years100 + 4 * years4 + years1; 11077 if (years100 !== 4 && years1 !== 4) { 11078 year++; 11079 } 11080 return year; 11081 }; 11082 11083 /** 11084 * @private 11085 */ 11086 ilib.Date.GregDate.prototype._calcYear = function(rd) { 11087 return ilib.Date.GregDate._calcYear(rd); 11088 }; 11089 11090 /** 11091 * Calculate the date components for the current time zone 11092 * @private 11093 */ 11094 ilib.Date.GregDate.prototype._calcDateComponents = function () { 11095 if (this.timezone === "local" && this.rd.getRataDie() >= -99280837 && this.rd.getRataDie() <= 100719163) { 11096 // console.log("using js Date to calculate offset"); 11097 // use the intrinsic JS Date object to do the tz conversion for us, which 11098 // guarantees that it follows the system tz database settings 11099 var d = new Date(this.rd.getTimeExtended()); 11100 11101 /** 11102 * Year in the Gregorian calendar. 11103 * @type number 11104 */ 11105 this.year = d.getFullYear(); 11106 11107 /** 11108 * The month number, ranging from 1 (January) to 12 (December). 11109 * @type number 11110 */ 11111 this.month = d.getMonth()+1; 11112 11113 /** 11114 * The day of the month. This ranges from 1 to 31. 11115 * @type number 11116 */ 11117 this.day = d.getDate(); 11118 11119 /** 11120 * The hour of the day. This can be a number from 0 to 23, as times are 11121 * stored unambiguously in the 24-hour clock. 11122 * @type number 11123 */ 11124 this.hour = d.getHours(); 11125 11126 /** 11127 * The minute of the hours. Ranges from 0 to 59. 11128 * @type number 11129 */ 11130 this.minute = d.getMinutes(); 11131 11132 /** 11133 * The second of the minute. Ranges from 0 to 59. 11134 * @type number 11135 */ 11136 this.second = d.getSeconds(); 11137 11138 /** 11139 * The millisecond of the second. Ranges from 0 to 999. 11140 * @type number 11141 */ 11142 this.millisecond = d.getMilliseconds(); 11143 11144 this.offset = -d.getTimezoneOffset() / 1440; 11145 } else { 11146 // console.log("using ilib to calculate offset. tz is " + this.timezone); 11147 // console.log("GregDate._calcDateComponents: date is " + JSON.stringify(this) + " parent is " + JSON.stringify(this.parent) + " and parent.parent is " + JSON.stringify(this.parent.parent)); 11148 if (typeof(this.offset) === "undefined") { 11149 // console.log("calculating offset"); 11150 this.year = this._calcYear(this.rd.getRataDie()); 11151 11152 // now offset the RD by the time zone, then recalculate in case we were 11153 // near the year boundary 11154 if (!this.tz) { 11155 this.tz = new ilib.TimeZone({id: this.timezone}); 11156 } 11157 this.offset = this.tz.getOffsetMillis(this) / 86400000; 11158 // } else { 11159 // console.log("offset is already defined somehow. type is " + typeof(this.offset)); 11160 // console.trace("Stack is this one"); 11161 } 11162 // console.log("offset is " + this.offset); 11163 var rd = this.rd.getRataDie(); 11164 if (this.offset !== 0) { 11165 rd += this.offset; 11166 } 11167 this.year = this._calcYear(rd); 11168 11169 var yearStartRd = this.newRd({ 11170 year: this.year, 11171 month: 1, 11172 day: 1, 11173 cal: this.cal 11174 }); 11175 11176 // remainder is days into the year 11177 var remainder = rd - yearStartRd.getRataDie() + 1; 11178 11179 var cumulative = ilib.Cal.Gregorian.prototype.isLeapYear.call(this.cal, this.year) ? 11180 ilib.Date.GregRataDie.cumMonthLengthsLeap : 11181 ilib.Date.GregRataDie.cumMonthLengths; 11182 11183 this.month = ilib.bsearch(Math.floor(remainder), cumulative); 11184 remainder = remainder - cumulative[this.month-1]; 11185 11186 this.day = Math.floor(remainder); 11187 remainder -= this.day; 11188 // now convert to milliseconds for the rest of the calculation 11189 remainder = Math.round(remainder * 86400000); 11190 11191 this.hour = Math.floor(remainder/3600000); 11192 remainder -= this.hour * 3600000; 11193 11194 this.minute = Math.floor(remainder/60000); 11195 remainder -= this.minute * 60000; 11196 11197 this.second = Math.floor(remainder/1000); 11198 remainder -= this.second * 1000; 11199 11200 this.millisecond = Math.floor(remainder); 11201 } 11202 }; 11203 11204 /** 11205 * Return the day of the week of this date. The day of the week is encoded 11206 * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday. 11207 * 11208 * @return {number} the day of the week 11209 */ 11210 ilib.Date.GregDate.prototype.getDayOfWeek = function() { 11211 var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0)); 11212 return ilib.mod(rd, 7); 11213 }; 11214 11215 /** 11216 * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 11217 * 365, regardless of months or weeks, etc. That is, January 1st is day 1, and 11218 * December 31st is 365 in regular years, or 366 in leap years. 11219 * @return {number} the ordinal day of the year 11220 */ 11221 ilib.Date.GregDate.prototype.getDayOfYear = function() { 11222 var cumulativeMap = this.cal.isLeapYear(this.year) ? 11223 ilib.Date.GregRataDie.cumMonthLengthsLeap : 11224 ilib.Date.GregRataDie.cumMonthLengths; 11225 11226 return cumulativeMap[this.month-1] + this.day; 11227 }; 11228 11229 /** 11230 * Return the era for this date as a number. The value for the era for Gregorian 11231 * calendars is -1 for "before the common era" (BCE) and 1 for "the common era" (CE). 11232 * BCE dates are any date before Jan 1, 1 CE. In the proleptic Gregorian calendar, 11233 * there is a year 0, so any years that are negative or zero are BCE. In the Julian 11234 * calendar, there is no year 0. Instead, the calendar goes straight from year -1 to 11235 * 1. 11236 * @return {number} 1 if this date is in the common era, -1 if it is before the 11237 * common era 11238 */ 11239 ilib.Date.GregDate.prototype.getEra = function() { 11240 return (this.year < 1) ? -1 : 1; 11241 }; 11242 11243 /** 11244 * Return the name of the calendar that governs this date. 11245 * 11246 * @return {string} a string giving the name of the calendar 11247 */ 11248 ilib.Date.GregDate.prototype.getCalendar = function() { 11249 return "gregorian"; 11250 }; 11251 11252 // register with the factory method 11253 ilib.Date._constructors["gregorian"] = ilib.Date.GregDate; 11254 /* 11255 * thaisolar.js - Represent a Thai solar calendar object. 11256 * 11257 * Copyright © 2013-2014, JEDLSoft 11258 * 11259 * Licensed under the Apache License, Version 2.0 (the "License"); 11260 * you may not use this file except in compliance with the License. 11261 * You may obtain a copy of the License at 11262 * 11263 * http://www.apache.org/licenses/LICENSE-2.0 11264 * 11265 * Unless required by applicable law or agreed to in writing, software 11266 * distributed under the License is distributed on an "AS IS" BASIS, 11267 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11268 * 11269 * See the License for the specific language governing permissions and 11270 * limitations under the License. 11271 */ 11272 11273 11274 /* !depends calendar.js locale.js date.js julianday.js calendar/gregorian.js util/utils.js util/math.js */ 11275 11276 /** 11277 * @class 11278 * Construct a new Thai solar calendar object. This class encodes information about 11279 * a Thai solar calendar.<p> 11280 * 11281 * Depends directive: !depends thaisolar.js 11282 * 11283 * @constructor 11284 * @implements ilib.Cal 11285 */ 11286 ilib.Cal.ThaiSolar = function() { 11287 this.type = "thaisolar"; 11288 }; 11289 11290 ilib.Cal.ThaiSolar.prototype = new ilib.Cal.Gregorian(); 11291 ilib.Cal.ThaiSolar.prototype.parent = ilib.Cal.Gregorian; 11292 ilib.Cal.ThaiSolar.prototype.constructor = ilib.Cal.ThaiSolar; 11293 11294 /** 11295 * Return true if the given year is a leap year in the Thai solar calendar. 11296 * The year parameter may be given as a number, or as a ThaiSolarDate object. 11297 * @param {number|ilib.Date.ThaiSolarDate} year the year for which the leap year information is being sought 11298 * @return {boolean} true if the given year is a leap year 11299 */ 11300 ilib.Cal.ThaiSolar.prototype.isLeapYear = function(year) { 11301 var y = (typeof(year) === 'number' ? year : year.getYears()); 11302 y -= 543; 11303 var centuries = ilib.mod(y, 400); 11304 return (ilib.mod(y, 4) === 0 && centuries !== 100 && centuries !== 200 && centuries !== 300); 11305 }; 11306 11307 /** 11308 * Return a date instance for this calendar type using the given 11309 * options. 11310 * @param {Object} options options controlling the construction of 11311 * the date instance 11312 * @return {ilib.Date} a date appropriate for this calendar type 11313 */ 11314 ilib.Cal.ThaiSolar.prototype.newDateInstance = function (options) { 11315 return new ilib.Date.ThaiSolarDate(options); 11316 }; 11317 11318 /* register this calendar for the factory method */ 11319 ilib.Cal._constructors["thaisolar"] = ilib.Cal.ThaiSolar; 11320 /* 11321 * thaisolardate.js - Represent a date in the ThaiSolar calendar 11322 * 11323 * Copyright © 2013-2014, JEDLSoft 11324 * 11325 * Licensed under the Apache License, Version 2.0 (the "License"); 11326 * you may not use this file except in compliance with the License. 11327 * You may obtain a copy of the License at 11328 * 11329 * http://www.apache.org/licenses/LICENSE-2.0 11330 * 11331 * Unless required by applicable law or agreed to in writing, software 11332 * distributed under the License is distributed on an "AS IS" BASIS, 11333 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11334 * 11335 * See the License for the specific language governing permissions and 11336 * limitations under the License. 11337 */ 11338 11339 /* !depends 11340 date.js 11341 calendar/gregorian.js 11342 util/jsutils.js 11343 */ 11344 11345 /** 11346 * @class 11347 * Construct a new Thai solar date object. The constructor parameters can 11348 * contain any of the following properties: 11349 * 11350 * <ul> 11351 * <li><i>unixtime<i> - sets the time of this instance according to the given 11352 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970. 11353 * 11354 * <li><i>julianday</i> - sets the time of this instance according to the given 11355 * Julian Day instance or the Julian Day given as a float 11356 * 11357 * <li><i>year</i> - any integer, including 0 11358 * 11359 * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc. 11360 * 11361 * <li><i>day</i> - 1 to 31 11362 * 11363 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 11364 * is always done with an unambiguous 24 hour representation 11365 * 11366 * <li><i>minute</i> - 0 to 59 11367 * 11368 * <li><i>second</i> - 0 to 59 11369 * 11370 * <li><i>millisecond</i> - 0 to 999 11371 * 11372 * <li><i>timezone</i> - the ilib.TimeZone instance or time zone name as a string 11373 * of this Thai solar date. The date/time is kept in the local time. The time zone 11374 * is used later if this date is formatted according to a different time zone and 11375 * the difference has to be calculated, or when the date format has a time zone 11376 * component in it. 11377 * 11378 * <li><i>locale</i> - locale for this Thai solar date. If the time zone is not 11379 * given, it can be inferred from this locale. For locales that span multiple 11380 * time zones, the one with the largest population is chosen as the one that 11381 * represents the locale. 11382 * </ul> 11383 * 11384 * If the constructor is called with another Thai solar date instance instead of 11385 * a parameter block, the other instance acts as a parameter block and its 11386 * settings are copied into the current instance.<p> 11387 * 11388 * If the constructor is called with no arguments at all or if none of the 11389 * properties listed above 11390 * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 11391 * components are 11392 * filled in with the current date at the time of instantiation. Note that if 11393 * you do not give the time zone when defaulting to the current time and the 11394 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 11395 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 11396 * Mean Time").<p> 11397 * 11398 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 11399 * specified in the params, it is assumed that they have the smallest possible 11400 * value in the range for the property (zero or one).<p> 11401 * 11402 * Depends directive: !depends thaisolardate.js 11403 * 11404 * @constructor 11405 * @extends ilib.Date.GregDate 11406 * @param {Object=} params parameters that govern the settings and behaviour of this Thai solar date 11407 */ 11408 ilib.Date.ThaiSolarDate = function(params) { 11409 var p = params; 11410 if (params) { 11411 // there is 198327 days difference between the Thai solar and 11412 // Gregorian epochs which is equivalent to 543 years 11413 p = {}; 11414 ilib.shallowCopy(params, p); 11415 if (typeof(p.year) !== 'undefined') { 11416 p.year -= 543; 11417 } 11418 if (typeof(p.rd) !== 'undefined') { 11419 p.rd -= 198327; 11420 } 11421 } 11422 this.rd = undefined; // clear these out so that the GregDate constructor can set it 11423 this.offset = undefined; 11424 //console.log("ThaiSolarDate.constructor: date is " + JSON.stringify(this) + " parent is " + JSON.stringify(this.parent) + " and parent.parent is " + JSON.stringify(this.parent.parent)); 11425 ilib.Date.GregDate.call(this, p); 11426 this.cal = new ilib.Cal.ThaiSolar(); 11427 // make sure the year is set correctly 11428 if (params && typeof(params.year) !== 'undefined') { 11429 this.year = parseInt(params.year, 10); 11430 } 11431 }; 11432 11433 ilib.Date.ThaiSolarDate.prototype = new ilib.Date.GregDate({noinstance: true}); 11434 ilib.Date.ThaiSolarDate.prototype.parent = ilib.Date.GregDate.prototype; 11435 ilib.Date.ThaiSolarDate.prototype.constructor = ilib.Date.ThaiSolarDate; 11436 11437 /** 11438 * the difference between a zero Julian day and the zero Thai Solar date. 11439 * This is some 543 years before the start of the Gregorian epoch. 11440 * @private 11441 * @const 11442 * @type number 11443 */ 11444 ilib.Date.ThaiSolarDate.epoch = 1523097.5; 11445 11446 /** 11447 * Calculate the date components for the current time zone 11448 * @protected 11449 */ 11450 ilib.Date.ThaiSolarDate.prototype._calcDateComponents = function () { 11451 // there is 198327 days difference between the Thai solar and 11452 // Gregorian epochs which is equivalent to 543 years 11453 // console.log("ThaiSolarDate._calcDateComponents: date is " + JSON.stringify(this) + " parent is " + JSON.stringify(this.parent) + " and parent.parent is " + JSON.stringify(this.parent.parent)); 11454 this.parent._calcDateComponents.call(this); 11455 this.year += 543; 11456 }; 11457 11458 /** 11459 * Return the Rata Die (fixed day) number of this date. 11460 * 11461 * @protected 11462 * @return {number} the rd date as a number 11463 */ 11464 ilib.Date.ThaiSolarDate.prototype.getRataDie = function() { 11465 // there is 198327 days difference between the Thai solar and 11466 // Gregorian epochs which is equivalent to 543 years 11467 return this.rd.getRataDie() + 198327; 11468 }; 11469 11470 /** 11471 * Return a new Gregorian date instance that represents the first instance of the 11472 * given day of the week before the current date. The day of the week is encoded 11473 * as a number where 0 = Sunday, 1 = Monday, etc. 11474 * 11475 * @param {number} dow the day of the week before the current date that is being sought 11476 * @return {ilib.Date} the date being sought 11477 */ 11478 ilib.Date.ThaiSolarDate.prototype.before = function (dow) { 11479 return this.cal.newDateInstance({ 11480 rd: this.rd.before(dow, this.offset) + 198327, 11481 timezone: this.timezone 11482 }); 11483 }; 11484 11485 /** 11486 * Return a new Gregorian date instance that represents the first instance of the 11487 * given day of the week after the current date. The day of the week is encoded 11488 * as a number where 0 = Sunday, 1 = Monday, etc. 11489 * 11490 * @param {number} dow the day of the week after the current date that is being sought 11491 * @return {ilib.Date} the date being sought 11492 */ 11493 ilib.Date.ThaiSolarDate.prototype.after = function (dow) { 11494 return this.cal.newDateInstance({ 11495 rd: this.rd.after(dow, this.offset) + 198327, 11496 timezone: this.timezone 11497 }); 11498 }; 11499 11500 /** 11501 * Return a new Gregorian date instance that represents the first instance of the 11502 * given day of the week on or before the current date. The day of the week is encoded 11503 * as a number where 0 = Sunday, 1 = Monday, etc. 11504 * 11505 * @param {number} dow the day of the week on or before the current date that is being sought 11506 * @return {ilib.Date} the date being sought 11507 */ 11508 ilib.Date.ThaiSolarDate.prototype.onOrBefore = function (dow) { 11509 return this.cal.newDateInstance({ 11510 rd: this.rd.onOrBefore(dow, this.offset) + 198327, 11511 timezone: this.timezone 11512 }); 11513 }; 11514 11515 /** 11516 * Return a new Gregorian date instance that represents the first instance of the 11517 * given day of the week on or after the current date. The day of the week is encoded 11518 * as a number where 0 = Sunday, 1 = Monday, etc. 11519 * 11520 * @param {number} dow the day of the week on or after the current date that is being sought 11521 * @return {ilib.Date} the date being sought 11522 */ 11523 ilib.Date.ThaiSolarDate.prototype.onOrAfter = function (dow) { 11524 return this.cal.newDateInstance({ 11525 rd: this.rd.onOrAfter(dow, this.offset) + 198327, 11526 timezone: this.timezone 11527 }); 11528 }; 11529 11530 /** 11531 * Return the name of the calendar that governs this date. 11532 * 11533 * @return {string} a string giving the name of the calendar 11534 */ 11535 ilib.Date.ThaiSolarDate.prototype.getCalendar = function() { 11536 return "thaisolar"; 11537 }; 11538 11539 //register with the factory method 11540 ilib.Date._constructors["thaisolar"] = ilib.Date.ThaiSolarDate; 11541 11542 11543 /* 11544 * persian.js - Represent a Persian algorithmic calendar object. 11545 * 11546 * Copyright © 2014, JEDLSoft 11547 * 11548 * Licensed under the Apache License, Version 2.0 (the "License"); 11549 * you may not use this file except in compliance with the License. 11550 * You may obtain a copy of the License at 11551 * 11552 * http://www.apache.org/licenses/LICENSE-2.0 11553 * 11554 * Unless required by applicable law or agreed to in writing, software 11555 * distributed under the License is distributed on an "AS IS" BASIS, 11556 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11557 * 11558 * See the License for the specific language governing permissions and 11559 * limitations under the License. 11560 */ 11561 11562 11563 /* !depends calendar.js locale.js date.js julianday.js util/utils.js util/math.js */ 11564 11565 /** 11566 * @class 11567 * Construct a new Persian algorithmic calendar object. This class encodes information about 11568 * a Persian algorithmic calendar.<p> 11569 * 11570 * Depends directive: !depends persian.js 11571 * 11572 * @constructor 11573 * @implements ilib.Cal 11574 */ 11575 ilib.Cal.PersianAlgo = function() { 11576 this.type = "persian-algo"; 11577 }; 11578 11579 /** 11580 * @private 11581 * @const 11582 * @type Array.<number> 11583 * the lengths of each month 11584 */ 11585 ilib.Cal.PersianAlgo.monthLengths = [ 11586 31, // Farvardin 11587 31, // Ordibehesht 11588 31, // Khordad 11589 31, // Tir 11590 31, // Mordad 11591 31, // Shahrivar 11592 30, // Mehr 11593 30, // Aban 11594 30, // Azar 11595 30, // Dey 11596 30, // Bahman 11597 29 // Esfand 11598 ]; 11599 11600 /** 11601 * Return the number of months in the given year. The number of months in a year varies 11602 * for some luni-solar calendars because in some years, an extra month is needed to extend the 11603 * days in a year to an entire solar year. The month is represented as a 1-based number 11604 * where 1=first month, 2=second month, etc. 11605 * 11606 * @param {number} year a year for which the number of months is sought 11607 * @return {number} The number of months in the given year 11608 */ 11609 ilib.Cal.PersianAlgo.prototype.getNumMonths = function(year) { 11610 return 12; 11611 }; 11612 11613 /** 11614 * Return the number of days in a particular month in a particular year. This function 11615 * can return a different number for a month depending on the year because of things 11616 * like leap years. 11617 * 11618 * @param {number} month the month for which the length is sought 11619 * @param {number} year the year within which that month can be found 11620 * @return {number} the number of days within the given month in the given year 11621 */ 11622 ilib.Cal.PersianAlgo.prototype.getMonLength = function(month, year) { 11623 if (month !== 12 || !this.isLeapYear(year)) { 11624 return ilib.Cal.PersianAlgo.monthLengths[month-1]; 11625 } else { 11626 // Month 12, Esfand, has 30 days instead of 29 in leap years 11627 return 30; 11628 } 11629 }; 11630 11631 /** 11632 * Return the equivalent year in the 2820 year cycle that begins on 11633 * Far 1, 474. This particular cycle obeys the cycle-of-years formula 11634 * whereas the others do not specifically. This cycle can be used as 11635 * a proxy for other years outside of the cycle by shifting them into 11636 * the cycle. 11637 * @param {number} year year to find the equivalent cycle year for 11638 * @returns {number} the equivalent cycle year 11639 */ 11640 ilib.Cal.PersianAlgo.prototype.equivalentCycleYear = function(year) { 11641 var y = year - (year >= 0 ? 474 : 473); 11642 return ilib.mod(y, 2820) + 474; 11643 }; 11644 11645 /** 11646 * Return true if the given year is a leap year in the Persian calendar. 11647 * The year parameter may be given as a number, or as a PersAlgoDate object. 11648 * @param {number} year the year for which the leap year information is being sought 11649 * @return {boolean} true if the given year is a leap year 11650 */ 11651 ilib.Cal.PersianAlgo.prototype.isLeapYear = function(year) { 11652 return (ilib.mod((this.equivalentCycleYear(year) + 38) * 682, 2816) < 682); 11653 }; 11654 11655 /** 11656 * Return the type of this calendar. 11657 * 11658 * @return {string} the name of the type of this calendar 11659 */ 11660 ilib.Cal.PersianAlgo.prototype.getType = function() { 11661 return this.type; 11662 }; 11663 11664 /** 11665 * Return a date instance for this calendar type using the given 11666 * options. 11667 * @param {Object} options options controlling the construction of 11668 * the date instance 11669 * @return {ilib.Date} a date appropriate for this calendar type 11670 */ 11671 ilib.Cal.PersianAlgo.prototype.newDateInstance = function (options) { 11672 return new ilib.Date.PersAlgoDate(options); 11673 }; 11674 11675 /* register this calendar for the factory method */ 11676 ilib.Cal._constructors["persian-algo"] = ilib.Cal.PersianAlgo; 11677 11678 /* 11679 * persiandate.js - Represent a date in the Persian algorithmic calendar 11680 * 11681 * Copyright © 2014, JEDLSoft 11682 * 11683 * Licensed under the Apache License, Version 2.0 (the "License"); 11684 * you may not use this file except in compliance with the License. 11685 * You may obtain a copy of the License at 11686 * 11687 * http://www.apache.org/licenses/LICENSE-2.0 11688 * 11689 * Unless required by applicable law or agreed to in writing, software 11690 * distributed under the License is distributed on an "AS IS" BASIS, 11691 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11692 * 11693 * See the License for the specific language governing permissions and 11694 * limitations under the License. 11695 */ 11696 11697 /* !depends 11698 date.js 11699 calendar/persian.js 11700 util/utils.js 11701 util/search.js 11702 util/math.js 11703 localeinfo.js 11704 julianday.js 11705 */ 11706 11707 /** 11708 * Construct a new Persian RD date number object. The constructor parameters can 11709 * contain any of the following properties: 11710 * 11711 * <ul> 11712 * <li><i>unixtime<i> - sets the time of this instance according to the given 11713 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian 11714 * 11715 * <li><i>julianday</i> - sets the time of this instance according to the given 11716 * Julian Day instance or the Julian Day given as a float 11717 * 11718 * <li><i>year</i> - any integer, including 0 11719 * 11720 * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc. 11721 * 11722 * <li><i>day</i> - 1 to 31 11723 * 11724 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 11725 * is always done with an unambiguous 24 hour representation 11726 * 11727 * <li><i>minute</i> - 0 to 59 11728 * 11729 * <li><i>second</i> - 0 to 59 11730 * 11731 * <li><i>millisecond</i> - 0 to 999 11732 * 11733 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 11734 * </ul> 11735 * 11736 * If the constructor is called with another Persian date instance instead of 11737 * a parameter block, the other instance acts as a parameter block and its 11738 * settings are copied into the current instance.<p> 11739 * 11740 * If the constructor is called with no arguments at all or if none of the 11741 * properties listed above are present, then the RD is calculate based on 11742 * the current date at the time of instantiation. <p> 11743 * 11744 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 11745 * specified in the params, it is assumed that they have the smallest possible 11746 * value in the range for the property (zero or one).<p> 11747 * 11748 * Depends directive: !depends persiandate.js 11749 * 11750 * @private 11751 * @class 11752 * @constructor 11753 * @extends ilib.Date.RataDie 11754 * @param {Object=} params parameters that govern the settings and behaviour of this Persian RD date 11755 */ 11756 ilib.Date.PersAlgoRataDie = function(params) { 11757 this.cal = params && params.cal || new ilib.Cal.PersianAlgo(); 11758 this.rd = undefined; 11759 ilib.Date.RataDie.call(this, params); 11760 }; 11761 11762 ilib.Date.PersAlgoRataDie.prototype = new ilib.Date.RataDie(); 11763 ilib.Date.PersAlgoRataDie.prototype.parent = ilib.Date.RataDie; 11764 ilib.Date.PersAlgoRataDie.prototype.constructor = ilib.Date.PersAlgoRataDie; 11765 11766 /** 11767 * The difference between a zero Julian day and the first Persian date 11768 * @private 11769 * @const 11770 * @type number 11771 */ 11772 ilib.Date.PersAlgoRataDie.prototype.epoch = 1948319.5; 11773 11774 /** 11775 * Calculate the Rata Die (fixed day) number of the given date from the 11776 * date components. 11777 * 11778 * @protected 11779 * @param {Object} date the date components to calculate the RD from 11780 */ 11781 ilib.Date.PersAlgoRataDie.prototype._setDateComponents = function(date) { 11782 var year = this.cal.equivalentCycleYear(date.year); 11783 var y = date.year - (date.year >= 0 ? 474 : 473); 11784 var rdOfYears = 1029983 * Math.floor(y/2820) + 365 * (year - 1) + Math.floor((682 * year - 110) / 2816); 11785 var dayInYear = (date.month > 1 ? ilib.Date.PersAlgoDate.cumMonthLengths[date.month-1] : 0) + date.day; 11786 var rdtime = (date.hour * 3600000 + 11787 date.minute * 60000 + 11788 date.second * 1000 + 11789 date.millisecond) / 11790 86400000; 11791 11792 /* 11793 // console.log("getRataDie: converting " + JSON.stringify(this)); 11794 console.log("getRataDie: year is " + year); 11795 console.log("getRataDie: rd of years is " + rdOfYears); 11796 console.log("getRataDie: day in year is " + dayInYear); 11797 console.log("getRataDie: rdtime is " + rdtime); 11798 console.log("getRataDie: rd is " + (rdOfYears + dayInYear + rdtime)); 11799 */ 11800 11801 this.rd = rdOfYears + dayInYear + rdtime; 11802 }; 11803 11804 /** 11805 * Return the rd number of the particular day of the week on or before the 11806 * given rd. eg. The Sunday on or before the given rd. 11807 * @private 11808 * @param {number} rd the rata die date of the reference date 11809 * @param {number} dayOfWeek the day of the week that is being sought relative 11810 * to the current date 11811 * @return {number} the rd of the day of the week 11812 */ 11813 ilib.Date.PersAlgoRataDie.prototype._onOrBefore = function(rd, dayOfWeek) { 11814 return rd - ilib.mod(Math.floor(rd) - dayOfWeek - 3, 7); 11815 }; 11816 11817 11818 /** 11819 * @class 11820 * 11821 * Construct a new Persian date object. The constructor parameters can 11822 * contain any of the following properties: 11823 * 11824 * <ul> 11825 * <li><i>unixtime<i> - sets the time of this instance according to the given 11826 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian 11827 * 11828 * <li><i>julianday</i> - sets the time of this instance according to the given 11829 * Julian Day instance or the Julian Day given as a float 11830 * 11831 * <li><i>year</i> - any integer, including 0 11832 * 11833 * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc. 11834 * 11835 * <li><i>day</i> - 1 to 31 11836 * 11837 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 11838 * is always done with an unambiguous 24 hour representation 11839 * 11840 * <li><i>minute</i> - 0 to 59 11841 * 11842 * <li><i>second</i> - 0 to 59 11843 * 11844 * <li><i>millisecond</i> - 0 to 999 11845 * 11846 * <li><i>timezone</i> - the ilib.TimeZone instance or time zone name as a string 11847 * of this persian date. The date/time is kept in the local time. The time zone 11848 * is used later if this date is formatted according to a different time zone and 11849 * the difference has to be calculated, or when the date format has a time zone 11850 * component in it. 11851 * 11852 * <li><i>locale</i> - locale for this persian date. If the time zone is not 11853 * given, it can be inferred from this locale. For locales that span multiple 11854 * time zones, the one with the largest population is chosen as the one that 11855 * represents the locale. 11856 * 11857 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 11858 * </ul> 11859 * 11860 * If the constructor is called with another Persian date instance instead of 11861 * a parameter block, the other instance acts as a parameter block and its 11862 * settings are copied into the current instance.<p> 11863 * 11864 * If the constructor is called with no arguments at all or if none of the 11865 * properties listed above 11866 * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 11867 * components are 11868 * filled in with the current date at the time of instantiation. Note that if 11869 * you do not give the time zone when defaulting to the current time and the 11870 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 11871 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 11872 * Mean Time").<p> 11873 * 11874 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 11875 * specified in the params, it is assumed that they have the smallest possible 11876 * value in the range for the property (zero or one).<p> 11877 * 11878 * Depends directive: !depends persiandate.js 11879 * 11880 * @constructor 11881 * @extends ilib.Date 11882 * @param {Object=} params parameters that govern the settings and behaviour of this Persian date 11883 */ 11884 ilib.Date.PersAlgoDate = function(params) { 11885 this.cal = new ilib.Cal.PersianAlgo(); 11886 this.timezone = "local"; 11887 11888 if (params) { 11889 if (params.locale) { 11890 this.locale = (typeof(params.locale) === 'string') ? new ilib.Locale(params.locale) : params.locale; 11891 var li = new ilib.LocaleInfo(this.locale); 11892 this.timezone = li.getTimeZone(); 11893 } 11894 if (params.timezone) { 11895 this.timezone = params.timezone; 11896 } 11897 11898 if (params.year || params.month || params.day || params.hour || 11899 params.minute || params.second || params.millisecond ) { 11900 /** 11901 * Year in the Persian calendar. 11902 * @type number 11903 */ 11904 this.year = parseInt(params.year, 10) || 0; 11905 11906 /** 11907 * The month number, ranging from 1 to 12 11908 * @type number 11909 */ 11910 this.month = parseInt(params.month, 10) || 1; 11911 11912 /** 11913 * The day of the month. This ranges from 1 to 31. 11914 * @type number 11915 */ 11916 this.day = parseInt(params.day, 10) || 1; 11917 11918 /** 11919 * The hour of the day. This can be a number from 0 to 23, as times are 11920 * stored unambiguously in the 24-hour clock. 11921 * @type number 11922 */ 11923 this.hour = parseInt(params.hour, 10) || 0; 11924 11925 /** 11926 * The minute of the hours. Ranges from 0 to 59. 11927 * @type number 11928 */ 11929 this.minute = parseInt(params.minute, 10) || 0; 11930 11931 /** 11932 * The second of the minute. Ranges from 0 to 59. 11933 * @type number 11934 */ 11935 this.second = parseInt(params.second, 10) || 0; 11936 11937 /** 11938 * The millisecond of the second. Ranges from 0 to 999. 11939 * @type number 11940 */ 11941 this.millisecond = parseInt(params.millisecond, 10) || 0; 11942 11943 /** 11944 * The day of the year. Ranges from 1 to 366. 11945 * @type number 11946 */ 11947 this.dayOfYear = parseInt(params.dayOfYear, 10); 11948 11949 if (typeof(params.dst) === 'boolean') { 11950 this.dst = params.dst; 11951 } 11952 11953 this.rd = this.newRd(this); 11954 11955 // add the time zone offset to the rd to convert to UTC 11956 if (!this.tz) { 11957 this.tz = new ilib.TimeZone({id: this.timezone}); 11958 } 11959 // getOffsetMillis requires that this.year, this.rd, and this.dst 11960 // are set in order to figure out which time zone rules apply and 11961 // what the offset is at that point in the year 11962 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000; 11963 if (this.offset !== 0) { 11964 this.rd = this.newRd({ 11965 rd: this.rd.getRataDie() - this.offset 11966 }); 11967 } 11968 } 11969 } 11970 11971 if (!this.rd) { 11972 this.rd = this.newRd(params); 11973 this._calcDateComponents(); 11974 } 11975 }; 11976 11977 ilib.Date.PersAlgoDate.prototype = new ilib.Date({noinstance: true}); 11978 ilib.Date.PersAlgoDate.prototype.parent = ilib.Date; 11979 ilib.Date.PersAlgoDate.prototype.constructor = ilib.Date.PersAlgoDate; 11980 11981 /** 11982 * @private 11983 * @const 11984 * @type Array.<number> 11985 * the cumulative lengths of each month, for a non-leap year 11986 */ 11987 ilib.Date.PersAlgoDate.cumMonthLengths = [ 11988 0, // Farvardin 11989 31, // Ordibehesht 11990 62, // Khordad 11991 93, // Tir 11992 124, // Mordad 11993 155, // Shahrivar 11994 186, // Mehr 11995 216, // Aban 11996 246, // Azar 11997 276, // Dey 11998 306, // Bahman 11999 336, // Esfand 12000 365 12001 ]; 12002 12003 /** 12004 * Return a new RD for this date type using the given params. 12005 * @protected 12006 * @param {Object=} params the parameters used to create this rata die instance 12007 * @returns {ilib.Date.RataDie} the new RD instance for the given params 12008 */ 12009 ilib.Date.PersAlgoDate.prototype.newRd = function (params) { 12010 return new ilib.Date.PersAlgoRataDie(params); 12011 }; 12012 12013 /** 12014 * Return the year for the given RD 12015 * @protected 12016 * @param {number} rd RD to calculate from 12017 * @returns {number} the year for the RD 12018 */ 12019 ilib.Date.PersAlgoDate.prototype._calcYear = function(rd) { 12020 var shiftedRd = rd - 173126; 12021 var numberOfCycles = Math.floor(shiftedRd / 1029983); 12022 var shiftedDayInCycle = ilib.mod(shiftedRd, 1029983); 12023 var yearInCycle = (shiftedDayInCycle === 1029982) ? 2820 : Math.floor((2816 * shiftedDayInCycle + 1031337) / 1028522); 12024 var year = 474 + 2820 * numberOfCycles + yearInCycle; 12025 return (year > 0) ? year : year - 1; 12026 }; 12027 12028 /** 12029 * @private 12030 * Calculate date components for the given RD date. 12031 */ 12032 ilib.Date.PersAlgoDate.prototype._calcDateComponents = function () { 12033 var remainder, 12034 rd = this.rd.getRataDie(); 12035 12036 this.year = this._calcYear(rd); 12037 12038 if (typeof(this.offset) === "undefined") { 12039 // now offset the RD by the time zone, then recalculate in case we were 12040 // near the year boundary 12041 if (!this.tz) { 12042 this.tz = new ilib.TimeZone({id: this.timezone}); 12043 } 12044 this.offset = this.tz.getOffsetMillis(this) / 86400000; 12045 } 12046 12047 if (this.offset !== 0) { 12048 rd += this.offset; 12049 this.year = this._calcYear(rd); 12050 } 12051 12052 //console.log("PersAlgoDate.calcComponent: calculating for rd " + rd); 12053 //console.log("PersAlgoDate.calcComponent: year is " + ret.year); 12054 var yearStart = this.newRd({ 12055 year: this.year, 12056 month: 1, 12057 day: 1, 12058 hour: 0, 12059 minute: 0, 12060 second: 0, 12061 millisecond: 0 12062 }); 12063 remainder = rd - yearStart.getRataDie() + 1; 12064 12065 this.dayOfYear = remainder; 12066 12067 //console.log("PersAlgoDate.calcComponent: remainder is " + remainder); 12068 12069 this.month = ilib.bsearch(remainder, ilib.Date.PersAlgoDate.cumMonthLengths); 12070 remainder -= ilib.Date.PersAlgoDate.cumMonthLengths[this.month-1]; 12071 12072 //console.log("PersAlgoDate.calcComponent: month is " + this.month + " and remainder is " + remainder); 12073 12074 this.day = Math.floor(remainder); 12075 remainder -= this.day; 12076 12077 //console.log("PersAlgoDate.calcComponent: day is " + this.day + " and remainder is " + remainder); 12078 12079 // now convert to milliseconds for the rest of the calculation 12080 remainder = Math.round(remainder * 86400000); 12081 12082 this.hour = Math.floor(remainder/3600000); 12083 remainder -= this.hour * 3600000; 12084 12085 this.minute = Math.floor(remainder/60000); 12086 remainder -= this.minute * 60000; 12087 12088 this.second = Math.floor(remainder/1000); 12089 remainder -= this.second * 1000; 12090 12091 this.millisecond = remainder; 12092 }; 12093 12094 /** 12095 * Return the day of the week of this date. The day of the week is encoded 12096 * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday. 12097 * 12098 * @return {number} the day of the week 12099 */ 12100 ilib.Date.PersAlgoDate.prototype.getDayOfWeek = function() { 12101 var rd = Math.floor(this.getRataDie()); 12102 return ilib.mod(rd-3, 7); 12103 }; 12104 12105 /** 12106 * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 12107 * 365, regardless of months or weeks, etc. That is, Farvardin 1st is day 1, and 12108 * December 31st is 365 in regular years, or 366 in leap years. 12109 * @return {number} the ordinal day of the year 12110 */ 12111 ilib.Date.PersAlgoDate.prototype.getDayOfYear = function() { 12112 return ilib.Date.PersAlgoDate.cumMonthLengths[this.month-1] + this.day; 12113 }; 12114 12115 /** 12116 * Return the era for this date as a number. The value for the era for Persian 12117 * calendars is -1 for "before the persian era" (BP) and 1 for "the persian era" (anno 12118 * persico or AP). 12119 * BP dates are any date before Farvardin 1, 1 AP. In the proleptic Persian calendar, 12120 * there is a year 0, so any years that are negative or zero are BP. 12121 * @return {number} 1 if this date is in the common era, -1 if it is before the 12122 * common era 12123 */ 12124 ilib.Date.PersAlgoDate.prototype.getEra = function() { 12125 return (this.year < 1) ? -1 : 1; 12126 }; 12127 12128 /** 12129 * Return the name of the calendar that governs this date. 12130 * 12131 * @return {string} a string giving the name of the calendar 12132 */ 12133 ilib.Date.PersAlgoDate.prototype.getCalendar = function() { 12134 return "persian-algo"; 12135 }; 12136 12137 // register with the factory method 12138 ilib.Date._constructors["persian-algo"] = ilib.Date.PersAlgoDate; 12139 /* 12140 * astro.js - Static functions to support astronomical calculations 12141 * 12142 * Copyright © 2014, JEDLSoft 12143 * 12144 * Licensed under the Apache License, Version 2.0 (the "License"); 12145 * you may not use this file except in compliance with the License. 12146 * You may obtain a copy of the License at 12147 * 12148 * http://www.apache.org/licenses/LICENSE-2.0 12149 * 12150 * Unless required by applicable law or agreed to in writing, software 12151 * distributed under the License is distributed on an "AS IS" BASIS, 12152 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12153 * 12154 * See the License for the specific language governing permissions and 12155 * limitations under the License. 12156 */ 12157 12158 /* !depends 12159 ilibglobal.js 12160 date.js 12161 calendar/gregoriandate.js 12162 calendar/gregratadie.js 12163 */ 12164 12165 // !data astro 12166 12167 /* 12168 * These routines were derived from a public domain set of JavaScript 12169 * functions for positional astronomy by John Walker of Fourmilab, 12170 * September 1999. 12171 */ 12172 12173 /** 12174 * Load in all the data needed for astrological calculations. 12175 * 12176 * @param {boolean} sync 12177 * @param {*} loadParams 12178 * @param {function(*)|undefined} callback 12179 */ 12180 ilib.Date.initAstro = function(sync, loadParams, callback) { 12181 if (!ilib.data.astro) { 12182 ilib.loadData({ 12183 name: "astro.json", // countries in their own language 12184 locale: "-", // only need to load the root file 12185 nonLocale: true, 12186 sync: sync, 12187 loadParams: loadParams, 12188 callback: ilib.bind(this, /** @type function() */ function(astroData) { 12189 /** 12190 * @type {{ 12191 * _EquinoxpTerms:Array.<number>, 12192 * _JDE0tab1000:Array.<number>, 12193 * _JDE0tab2000:Array.<number>, 12194 * _deltaTtab:Array.<number>, 12195 * _oterms:Array.<number>, 12196 * _nutArgMult:Array.<number>, 12197 * _nutArgCoeff:Array.<number>, 12198 * _nutCoeffA:Array.<number>, 12199 * _nutCoeffB:Array.<number>, 12200 * _coeff19th:Array.<number>, 12201 * _coeff18th:Array.<number>, 12202 * _solarLongCoeff:Array.<number>, 12203 * _solarLongMultipliers:Array.<number>, 12204 * _solarLongAddends:Array.<number>, 12205 * _meanMoonCoeff:Array.<number>, 12206 * _elongationCoeff:Array.<number>, 12207 * _solarAnomalyCoeff:Array.<number>, 12208 * _lunarAnomalyCoeff:Array.<number>, 12209 * _moonFromNodeCoeff:Array.<number>, 12210 * _eCoeff:Array.<number>, 12211 * _lunarElongationLongCoeff:Array.<number>, 12212 * _solarAnomalyLongCoeff:Array.<number>, 12213 * _lunarAnomalyLongCoeff:Array.<number>, 12214 * _moonFromNodeLongCoeff:Array.<number>, 12215 * _sineCoeff:Array.<number>, 12216 * _nmApproxCoeff:Array.<number>, 12217 * _nmCapECoeff:Array.<number>, 12218 * _nmSolarAnomalyCoeff:Array.<number>, 12219 * _nmLunarAnomalyCoeff:Array.<number>, 12220 * _nmMoonArgumentCoeff:Array.<number>, 12221 * _nmCapOmegaCoeff:Array.<number>, 12222 * _nmEFactor:Array.<number>, 12223 * _nmSolarCoeff:Array.<number>, 12224 * _nmLunarCoeff:Array.<number>, 12225 * _nmMoonCoeff:Array.<number>, 12226 * _nmSineCoeff:Array.<number>, 12227 * _nmAddConst:Array.<number>, 12228 * _nmAddCoeff:Array.<number>, 12229 * _nmAddFactor:Array.<number>, 12230 * _nmExtra:Array.<number> 12231 * }} 12232 */ 12233 ilib.data.astro = astroData; 12234 if (callback && typeof(callback) === 'function') { 12235 callback(astroData); 12236 } 12237 }) 12238 }); 12239 } else { 12240 if (callback && typeof(callback) === 'function') { 12241 callback(ilib.data.astro); 12242 } 12243 } 12244 }; 12245 12246 /** 12247 * Convert degrees to radians. 12248 * 12249 * @static 12250 * @param {number} d angle in degrees 12251 * @return {number} angle in radians 12252 */ 12253 ilib.Date._dtr = function(d) { 12254 return (d * Math.PI) / 180.0; 12255 }; 12256 12257 /** 12258 * Convert radians to degrees. 12259 * 12260 * @static 12261 * @param {number} r angle in radians 12262 * @return {number} angle in degrees 12263 */ 12264 ilib.Date._rtd = function(r) { 12265 return (r * 180.0) / Math.PI; 12266 }; 12267 12268 /** 12269 * Return the cosine of an angle given in degrees. 12270 * @static 12271 * @param {number} d angle in degrees 12272 * @return {number} cosine of the angle. 12273 */ 12274 ilib.Date._dcos = function(d) { 12275 return Math.cos(ilib.Date._dtr(d)); 12276 }; 12277 12278 /** 12279 * Return the sine of an angle given in degrees. 12280 * @static 12281 * @param {number} d angle in degrees 12282 * @return {number} sine of the angle. 12283 */ 12284 ilib.Date._dsin = function(d) { 12285 return Math.sin(ilib.Date._dtr(d)); 12286 }; 12287 12288 /** 12289 * Return the tan of an angle given in degrees. 12290 * @static 12291 * @param {number} d angle in degrees 12292 * @return {number} tan of the angle. 12293 */ 12294 ilib.Date._dtan = function(d) { 12295 return Math.tan(ilib.Date._dtr(d)); 12296 }; 12297 12298 /** 12299 * Range reduce angle in degrees. 12300 * 12301 * @static 12302 * @param {number} a angle to reduce 12303 * @return {number} the reduced angle 12304 */ 12305 ilib.Date._fixangle = function(a) { 12306 return a - 360.0 * (Math.floor(a / 360.0)); 12307 }; 12308 12309 /** 12310 * Range reduce angle in radians. 12311 * 12312 * @static 12313 * @param {number} a angle to reduce 12314 * @return {number} the reduced angle 12315 */ 12316 ilib.Date._fixangr = function(a) { 12317 return a - (2 * Math.PI) * (Math.floor(a / (2 * Math.PI))); 12318 }; 12319 12320 /** 12321 * Determine the Julian Ephemeris Day of an equinox or solstice. The "which" 12322 * argument selects the item to be computed: 12323 * 12324 * <ul> 12325 * <li>0 March equinox 12326 * <li>1 June solstice 12327 * <li>2 September equinox 12328 * <li>3 December solstice 12329 * </ul> 12330 * 12331 * @static 12332 * @param {number} year Gregorian year to calculate for 12333 * @param {number} which Which equinox or solstice to calculate 12334 */ 12335 ilib.Date._equinox = function(year, which) { 12336 var deltaL, i, j, JDE0, JDE, JDE0tab, S, T, W, Y; 12337 12338 /* Initialize terms for mean equinox and solstices. We 12339 have two sets: one for years prior to 1000 and a second 12340 for subsequent years. */ 12341 12342 if (year < 1000) { 12343 JDE0tab = ilib.data.astro._JDE0tab1000; 12344 Y = year / 1000; 12345 } else { 12346 JDE0tab = ilib.data.astro._JDE0tab2000; 12347 Y = (year - 2000) / 1000; 12348 } 12349 12350 JDE0 = JDE0tab[which][0] + (JDE0tab[which][1] * Y) 12351 + (JDE0tab[which][2] * Y * Y) + (JDE0tab[which][3] * Y * Y * Y) 12352 + (JDE0tab[which][4] * Y * Y * Y * Y); 12353 12354 //document.debug.log.value += "JDE0 = " + JDE0 + "\n"; 12355 12356 T = (JDE0 - 2451545.0) / 36525; 12357 //document.debug.log.value += "T = " + T + "\n"; 12358 W = (35999.373 * T) - 2.47; 12359 //document.debug.log.value += "W = " + W + "\n"; 12360 deltaL = 1 + (0.0334 * ilib.Date._dcos(W)) + (0.0007 * ilib.Date._dcos(2 * W)); 12361 //document.debug.log.value += "deltaL = " + deltaL + "\n"; 12362 12363 // Sum the periodic terms for time T 12364 12365 S = 0; 12366 j = 0; 12367 for (i = 0; i < 24; i++) { 12368 S += ilib.data.astro._EquinoxpTerms[j] 12369 * ilib.Date._dcos(ilib.data.astro._EquinoxpTerms[j + 1] + (ilib.data.astro._EquinoxpTerms[j + 2] * T)); 12370 j += 3; 12371 } 12372 12373 //document.debug.log.value += "S = " + S + "\n"; 12374 //document.debug.log.value += "Corr = " + ((S * 0.00001) / deltaL) + "\n"; 12375 12376 JDE = JDE0 + ((S * 0.00001) / deltaL); 12377 12378 return JDE; 12379 }; 12380 12381 /* 12382 * The table of observed Delta T values at the beginning of 12383 * years from 1620 through 2014 as found in astro.json is taken from 12384 * http://www.staff.science.uu.nl/~gent0113/deltat/deltat.htm 12385 * and 12386 * ftp://maia.usno.navy.mil/ser7/deltat.data 12387 */ 12388 12389 /** 12390 * Determine the difference, in seconds, between dynamical time and universal time. 12391 * 12392 * @static 12393 * @param {number} year to calculate the difference for 12394 * @return {number} difference in seconds between dynamical time and universal time 12395 */ 12396 ilib.Date._deltat = function (year) { 12397 var dt, f, i, t; 12398 12399 if ((year >= 1620) && (year <= 2014)) { 12400 i = Math.floor(year - 1620); 12401 f = (year - 1620) - i; /* Fractional part of year */ 12402 dt = ilib.data.astro._deltaTtab[i] + ((ilib.data.astro._deltaTtab[i + 1] - ilib.data.astro._deltaTtab[i]) * f); 12403 } else { 12404 t = (year - 2000) / 100; 12405 if (year < 948) { 12406 dt = 2177 + (497 * t) + (44.1 * t * t); 12407 } else { 12408 dt = 102 + (102 * t) + (25.3 * t * t); 12409 if ((year > 2000) && (year < 2100)) { 12410 dt += 0.37 * (year - 2100); 12411 } 12412 } 12413 } 12414 return dt; 12415 }; 12416 12417 /** 12418 * Calculate the obliquity of the ecliptic for a given 12419 * Julian date. This uses Laskar's tenth-degree 12420 * polynomial fit (J. Laskar, Astronomy and 12421 * Astrophysics, Vol. 157, page 68 [1986]) which is 12422 * accurate to within 0.01 arc second between AD 1000 12423 * and AD 3000, and within a few seconds of arc for 12424 * +/-10000 years around AD 2000. If we're outside the 12425 * range in which this fit is valid (deep time) we 12426 * simply return the J2000 value of the obliquity, which 12427 * happens to be almost precisely the mean. 12428 * 12429 * @static 12430 * @param {number} jd Julian Day to calculate the obliquity for 12431 * @return {number} the obliquity 12432 */ 12433 ilib.Date._obliqeq = function (jd) { 12434 var eps, u, v, i; 12435 12436 v = u = (jd - 2451545.0) / 3652500.0; 12437 12438 eps = 23 + (26 / 60.0) + (21.448 / 3600.0); 12439 12440 if (Math.abs(u) < 1.0) { 12441 for (i = 0; i < 10; i++) { 12442 eps += (ilib.data.astro._oterms[i] / 3600.0) * v; 12443 v *= u; 12444 } 12445 } 12446 return eps; 12447 }; 12448 12449 /** 12450 * Return the position of the sun. We return 12451 * intermediate values because they are useful in a 12452 * variety of other contexts. 12453 * @static 12454 * @param {number} jd find the position of sun on this Julian Day 12455 * @return {Object} the position of the sun and many intermediate 12456 * values 12457 */ 12458 ilib.Date._sunpos = function(jd) { 12459 var ret = {}, 12460 T, T2, T3, Omega, epsilon, epsilon0; 12461 12462 T = (jd - 2451545.0) / 36525.0; 12463 //document.debug.log.value += "Sunpos. T = " + T + "\n"; 12464 T2 = T * T; 12465 T3 = T * T2; 12466 ret.meanLongitude = ilib.Date._fixangle(280.46646 + 36000.76983 * T + 0.0003032 * T2); 12467 //document.debug.log.value += "ret.meanLongitude = " + ret.meanLongitude + "\n"; 12468 ret.meanAnomaly = ilib.Date._fixangle(357.52911 + (35999.05029 * T) - 0.0001537 * T2 - 0.00000048 * T3); 12469 //document.debug.log.value += "ret.meanAnomaly = " + ret.meanAnomaly + "\n"; 12470 ret.eccentricity = 0.016708634 - 0.000042037 * T - 0.0000001267 * T2; 12471 //document.debug.log.value += "e = " + e + "\n"; 12472 ret.equationOfCenter = ((1.914602 - 0.004817 * T - 0.000014 * T2) * ilib.Date._dsin(ret.meanAnomaly)) 12473 + ((0.019993 - 0.000101 * T) * ilib.Date._dsin(2 * ret.meanAnomaly)) 12474 + (0.000289 * ilib.Date._dsin(3 * ret.meanAnomaly)); 12475 //document.debug.log.value += "ret.equationOfCenter = " + ret.equationOfCenter + "\n"; 12476 ret.sunLongitude = ret.meanLongitude + ret.equationOfCenter; 12477 //document.debug.log.value += "ret.sunLongitude = " + ret.sunLongitude + "\n"; 12478 //ret.sunAnomaly = ret.meanAnomaly + ret.equationOfCenter; 12479 //document.debug.log.value += "ret.sunAnomaly = " + ret.sunAnomaly + "\n"; 12480 // ret.sunRadius = (1.000001018 * (1 - (ret.eccentricity * ret.eccentricity))) / (1 + (ret.eccentricity * ilib.Date._dcos(ret.sunAnomaly))); 12481 //document.debug.log.value += "ret.sunRadius = " + ret.sunRadius + "\n"; 12482 Omega = 125.04 - (1934.136 * T); 12483 //document.debug.log.value += "Omega = " + Omega + "\n"; 12484 ret.apparentLong = ret.sunLongitude + (-0.00569) + (-0.00478 * ilib.Date._dsin(Omega)); 12485 //document.debug.log.value += "ret.apparentLong = " + ret.apparentLong + "\n"; 12486 epsilon0 = ilib.Date._obliqeq(jd); 12487 //document.debug.log.value += "epsilon0 = " + epsilon0 + "\n"; 12488 epsilon = epsilon0 + (0.00256 * ilib.Date._dcos(Omega)); 12489 //document.debug.log.value += "epsilon = " + epsilon + "\n"; 12490 //ret.rightAscension = ilib.Date._fixangle(ilib.Date._rtd(Math.atan2(ilib.Date._dcos(epsilon0) * ilib.Date._dsin(ret.sunLongitude), ilib.Date._dcos(ret.sunLongitude)))); 12491 //document.debug.log.value += "ret.rightAscension = " + ret.rightAscension + "\n"; 12492 // ret.declination = ilib.Date._rtd(Math.asin(ilib.Date._dsin(epsilon0) * ilib.Date._dsin(ret.sunLongitude))); 12493 ////document.debug.log.value += "ret.declination = " + ret.declination + "\n"; 12494 ret.inclination = ilib.Date._fixangle(23.4392911 - 0.013004167 * T - 0.00000016389 * T2 + 0.0000005036 * T3); 12495 ret.apparentRightAscension = ilib.Date._fixangle(ilib.Date._rtd(Math.atan2(ilib.Date._dcos(epsilon) * ilib.Date._dsin(ret.apparentLong), ilib.Date._dcos(ret.apparentLong)))); 12496 //document.debug.log.value += "ret.apparentRightAscension = " + ret.apparentRightAscension + "\n"; 12497 //ret.apparentDeclination = ilib.Date._rtd(Math.asin(ilib.Date._dsin(epsilon) * ilib.Date._dsin(ret.apparentLong))); 12498 //document.debug.log.value += "ret.apparentDecliation = " + ret.apparentDecliation + "\n"; 12499 12500 // Angular quantities are expressed in decimal degrees 12501 return ret; 12502 }; 12503 12504 /** 12505 * Calculate the nutation in longitude, deltaPsi, and obliquity, 12506 * deltaEpsilon for a given Julian date jd. Results are returned as an object 12507 * giving deltaPsi and deltaEpsilon in degrees. 12508 * 12509 * @static 12510 * @param {number} jd calculate the nutation of this Julian Day 12511 * @return {Object} the deltaPsi and deltaEpsilon of the nutation 12512 */ 12513 ilib.Date._nutation = function(jd) { 12514 var i, j, 12515 t = (jd - 2451545.0) / 36525.0, 12516 t2, t3, to10, 12517 ta = [], 12518 dp = 0, 12519 de = 0, 12520 ang, 12521 ret = {}; 12522 12523 t3 = t * (t2 = t * t); 12524 12525 /* 12526 * Calculate angles. The correspondence between the elements of our array 12527 * and the terms cited in Meeus are: 12528 * 12529 * ta[0] = D ta[0] = M ta[2] = M' ta[3] = F ta[4] = \Omega 12530 * 12531 */ 12532 12533 ta[0] = ilib.Date._dtr(297.850363 + 445267.11148 * t - 0.0019142 * t2 + t3 / 189474.0); 12534 ta[1] = ilib.Date._dtr(357.52772 + 35999.05034 * t - 0.0001603 * t2 - t3 / 300000.0); 12535 ta[2] = ilib.Date._dtr(134.96298 + 477198.867398 * t + 0.0086972 * t2 + t3 / 56250.0); 12536 ta[3] = ilib.Date._dtr(93.27191 + 483202.017538 * t - 0.0036825 * t2 + t3 / 327270); 12537 ta[4] = ilib.Date._dtr(125.04452 - 1934.136261 * t + 0.0020708 * t2 + t3 / 450000.0); 12538 12539 /* 12540 * Range reduce the angles in case the sine and cosine functions don't do it 12541 * as accurately or quickly. 12542 */ 12543 12544 for (i = 0; i < 5; i++) { 12545 ta[i] = ilib.Date._fixangr(ta[i]); 12546 } 12547 12548 to10 = t / 10.0; 12549 for (i = 0; i < 63; i++) { 12550 ang = 0; 12551 for (j = 0; j < 5; j++) { 12552 if (ilib.data.astro._nutArgMult[(i * 5) + j] != 0) { 12553 ang += ilib.data.astro._nutArgMult[(i * 5) + j] * ta[j]; 12554 } 12555 } 12556 dp += (ilib.data.astro._nutArgCoeff[(i * 4) + 0] + ilib.data.astro._nutArgCoeff[(i * 4) + 1] * to10) * Math.sin(ang); 12557 de += (ilib.data.astro._nutArgCoeff[(i * 4) + 2] + ilib.data.astro._nutArgCoeff[(i * 4) + 3] * to10) * Math.cos(ang); 12558 } 12559 12560 /* 12561 * Return the result, converting from ten thousandths of arc seconds to 12562 * radians in the process. 12563 */ 12564 12565 ret.deltaPsi = dp / (3600.0 * 10000.0); 12566 ret.deltaEpsilon = de / (3600.0 * 10000.0); 12567 12568 return ret; 12569 }; 12570 12571 /** 12572 * Returns the equation of time as a fraction of a day. 12573 * 12574 * @static 12575 * @param {number} jd the Julian Day of the day to calculate for 12576 * @return {number} the equation of time for the given day 12577 */ 12578 ilib.Date._equationOfTime = function(jd) { 12579 var alpha, deltaPsi, E, epsilon, L0, tau, pos; 12580 12581 // 2451545.0 is the Julian day of J2000 epoch 12582 // 365250.0 is the number of days in a Julian millenium 12583 tau = (jd - 2451545.0) / 365250.0; 12584 //console.log("equationOfTime. tau = " + tau); 12585 L0 = 280.4664567 + (360007.6982779 * tau) + (0.03032028 * tau * tau) 12586 + ((tau * tau * tau) / 49931) 12587 + (-((tau * tau * tau * tau) / 15300)) 12588 + (-((tau * tau * tau * tau * tau) / 2000000)); 12589 //console.log("L0 = " + L0); 12590 L0 = ilib.Date._fixangle(L0); 12591 //console.log("L0 = " + L0); 12592 pos = ilib.Date._sunpos(jd); 12593 alpha = pos.apparentRightAscension; 12594 //console.log("alpha = " + alpha); 12595 var nut = ilib.Date._nutation(jd); 12596 deltaPsi = nut.deltaPsi; 12597 //console.log("deltaPsi = " + deltaPsi); 12598 epsilon = ilib.Date._obliqeq(jd) + nut.deltaEpsilon; 12599 //console.log("epsilon = " + epsilon); 12600 //console.log("L0 - 0.0057183 = " + (L0 - 0.0057183)); 12601 //console.log("L0 - 0.0057183 - alpha = " + (L0 - 0.0057183 - alpha)); 12602 //console.log("deltaPsi * cos(epsilon) = " + deltaPsi * ilib.Date._dcos(epsilon)); 12603 12604 E = L0 - 0.0057183 - alpha + deltaPsi * ilib.Date._dcos(epsilon); 12605 // if alpha and L0 are in different quadrants, then renormalize 12606 // so that the difference between them is in the right range 12607 if (E > 180) { 12608 E -= 360; 12609 } 12610 //console.log("E = " + E); 12611 // E = E - 20.0 * (Math.floor(E / 20.0)); 12612 E = E * 4; 12613 //console.log("Efixed = " + E); 12614 E = E / (24 * 60); 12615 //console.log("Eday = " + E); 12616 12617 return E; 12618 }; 12619 12620 /** 12621 * @private 12622 * @static 12623 */ 12624 ilib.Date._poly = function(x, coefficients) { 12625 var result = coefficients[0]; 12626 var xpow = x; 12627 for (var i = 1; i < coefficients.length; i++) { 12628 result += coefficients[i] * xpow; 12629 xpow *= x; 12630 } 12631 return result; 12632 }; 12633 12634 /** 12635 * Calculate the UTC RD from the local RD given "zone" number of minutes 12636 * worth of offset. 12637 * 12638 * @static 12639 * @param {number} local RD of the locale time, given in any calendar 12640 * @param {number} zone number of minutes of offset from UTC for the time zone 12641 * @return {number} the UTC equivalent of the local RD 12642 */ 12643 ilib.Date._universalFromLocal = function(local, zone) { 12644 return local - zone / 1440; 12645 }; 12646 12647 /** 12648 * Calculate the local RD from the UTC RD given "zone" number of minutes 12649 * worth of offset. 12650 * 12651 * @static 12652 * @param {number} local RD of the locale time, given in any calendar 12653 * @param {number} zone number of minutes of offset from UTC for the time zone 12654 * @return {number} the UTC equivalent of the local RD 12655 */ 12656 ilib.Date._localFromUniversal = function(local, zone) { 12657 return local + zone / 1440; 12658 }; 12659 12660 /** 12661 * @private 12662 * @static 12663 * @param {number} c julian centuries of the date to calculate 12664 * @return {number} the aberration 12665 */ 12666 ilib.Date._aberration = function(c) { 12667 return 9.74e-05 * ilib.Date._dcos(177.63 + 35999.01847999999 * c) - 0.005575; 12668 }; 12669 12670 /** 12671 * @private 12672 * 12673 ilib.data.astro._nutCoeffA = [124.90, -1934.134, 0.002063]; 12674 ilib.data.astro._nutCoeffB = [201.11, 72001.5377, 0.00057]; 12675 */ 12676 12677 /** 12678 * @private 12679 * @static 12680 * @param {number} c julian centuries of the date to calculate 12681 * @return {number} the nutation for the given julian century in radians 12682 */ 12683 ilib.Date._nutation2 = function(c) { 12684 var a = ilib.Date._poly(c, ilib.data.astro._nutCoeffA); 12685 var b = ilib.Date._poly(c, ilib.data.astro._nutCoeffB); 12686 // return -0.0000834 * ilib.Date._dsin(a) - 0.0000064 * ilib.Date._dsin(b); 12687 return -0.004778 * ilib.Date._dsin(a) - 0.0003667 * ilib.Date._dsin(b); 12688 }; 12689 12690 12691 /** 12692 * @static 12693 * @private 12694 */ 12695 ilib.Date._ephemerisCorrection = function(jd) { 12696 var year = ilib.Date.GregDate._calcYear(jd - 1721424.5); 12697 12698 if (1988 <= year && year <= 2019) { 12699 return (year - 1933) / 86400; 12700 } 12701 12702 if (1800 <= year && year <= 1987) { 12703 var jul1 = new ilib.Date.GregRataDie({ 12704 year: year, 12705 month: 7, 12706 day: 1, 12707 hour: 0, 12708 minute: 0, 12709 second: 0 12710 }); 12711 // 693596 is the rd of Jan 1, 1900 12712 var theta = (jul1.getRataDie() - 693596) / 36525; 12713 return ilib.Date._poly(theta, (1900 <= year) ? ilib.data.astro._coeff19th : ilib.data.astro._coeff18th); 12714 } 12715 12716 if (1620 <= year && year <= 1799) { 12717 year -= 1600; 12718 return (196.58333 - 4.0675 * year + 0.0219167 * year * year) / 86400; 12719 } 12720 12721 // 660724 is the rd of Jan 1, 1810 12722 var jan1 = new ilib.Date.GregRataDie({ 12723 year: year, 12724 month: 1, 12725 day: 1, 12726 hour: 0, 12727 minute: 0, 12728 second: 0 12729 }); 12730 // var x = 0.5 + (jan1.getRataDie() - 660724); 12731 var x = 0.5 + (jan1.getRataDie() - 660724); 12732 12733 return ((x * x / 41048480) - 15) / 86400; 12734 }; 12735 12736 /** 12737 * @static 12738 * @private 12739 */ 12740 ilib.Date._ephemerisFromUniversal = function(jd) { 12741 return jd + ilib.Date._ephemerisCorrection(jd); 12742 }; 12743 12744 /** 12745 * @static 12746 * @private 12747 */ 12748 ilib.Date._universalFromEphemeris = function(jd) { 12749 return jd - ilib.Date._ephemerisCorrection(jd); 12750 }; 12751 12752 /** 12753 * @static 12754 * @private 12755 */ 12756 ilib.Date._julianCenturies = function(jd) { 12757 // 2451545.0 is the Julian day of J2000 epoch 12758 // 730119.5 is the Gregorian RD of J2000 epoch 12759 // 36525.0 is the number of days in a Julian century 12760 return (ilib.Date._ephemerisFromUniversal(jd) - 2451545.0) / 36525.0; 12761 }; 12762 12763 /** 12764 * Calculate the solar longitude 12765 * 12766 * @static 12767 * @param {number} jd julian day of the date to calculate the longitude for 12768 * @return {number} the solar longitude in degrees 12769 */ 12770 ilib.Date._solarLongitude = function(jd) { 12771 var c = ilib.Date._julianCenturies(jd), 12772 longitude = 0, 12773 len = ilib.data.astro._solarLongCoeff.length, 12774 row; 12775 12776 for (var i = 0; i < len; i++) { 12777 longitude += ilib.data.astro._solarLongCoeff[i] * 12778 ilib.Date._dsin(ilib.data.astro._solarLongAddends[i] + ilib.data.astro._solarLongMultipliers[i] * c); 12779 } 12780 longitude *= 5.729577951308232e-06; 12781 longitude += 282.77718340000001 + 36000.769537439999 * c; 12782 longitude += ilib.Date._aberration(c) + ilib.Date._nutation2(c); 12783 return ilib.Date._fixangle(longitude); 12784 }; 12785 12786 /** 12787 * @static 12788 * @protected 12789 * @param {number} jd 12790 * @return {number} 12791 */ 12792 ilib.Date._lunarLongitude = function (jd) { 12793 var c = ilib.Date._julianCenturies(jd), 12794 meanMoon = ilib.Date._fixangle(ilib.Date._poly(c, ilib.data.astro._meanMoonCoeff)), 12795 elongation = ilib.Date._fixangle(ilib.Date._poly(c, ilib.data.astro._elongationCoeff)), 12796 solarAnomaly = ilib.Date._fixangle(ilib.Date._poly(c, ilib.data.astro._solarAnomalyCoeff)), 12797 lunarAnomaly = ilib.Date._fixangle(ilib.Date._poly(c, ilib.data.astro._lunarAnomalyCoeff)), 12798 moonNode = ilib.Date._fixangle(ilib.Date._poly(c, ilib.data.astro._moonFromNodeCoeff)), 12799 e = ilib.Date._poly(c, ilib.data.astro._eCoeff); 12800 12801 var sum = 0; 12802 for (var i = 0; i < ilib.data.astro._lunarElongationLongCoeff.length; i++) { 12803 var x = ilib.data.astro._solarAnomalyLongCoeff[i]; 12804 12805 sum += ilib.data.astro._sineCoeff[i] * Math.pow(e, Math.abs(x)) * 12806 ilib.Date._dsin(ilib.data.astro._lunarElongationLongCoeff[i] * elongation + x * solarAnomaly + 12807 ilib.data.astro._lunarAnomalyLongCoeff[i] * lunarAnomaly + 12808 ilib.data.astro._moonFromNodeLongCoeff[i] * moonNode); 12809 } 12810 var longitude = sum / 1000000; 12811 var venus = 3958.0 / 1000000 * ilib.Date._dsin(119.75 + c * 131.84899999999999); 12812 var jupiter = 318.0 / 1000000 * ilib.Date._dsin(53.090000000000003 + c * 479264.28999999998); 12813 var flatEarth = 1962.0 / 1000000 * ilib.Date._dsin(meanMoon - moonNode); 12814 12815 return ilib.Date._fixangle(meanMoon + longitude + venus + jupiter + flatEarth + ilib.Date._nutation2(c)); 12816 }; 12817 12818 /** 12819 * @static 12820 * @param {number} n 12821 * @return {number} julian day of the n'th new moon 12822 */ 12823 ilib.Date._newMoonTime = function(n) { 12824 var k = n - 24724; 12825 var c = k / 1236.8499999999999; 12826 var approx = ilib.Date._poly(c, ilib.data.astro._nmApproxCoeff); 12827 var capE = ilib.Date._poly(c, ilib.data.astro._nmCapECoeff); 12828 var solarAnomaly = ilib.Date._poly(c, ilib.data.astro._nmSolarAnomalyCoeff); 12829 var lunarAnomaly = ilib.Date._poly(c, ilib.data.astro._nmLunarAnomalyCoeff); 12830 var moonArgument = ilib.Date._poly(c, ilib.data.astro._nmMoonArgumentCoeff); 12831 var capOmega = ilib.Date._poly(c, ilib.data.astro._nmCapOmegaCoeff); 12832 var correction = -0.00017 * ilib.Date._dsin(capOmega); 12833 for (var i = 0; i < ilib.data.astro._nmSineCoeff.length; i++) { 12834 correction = correction + ilib.data.astro._nmSineCoeff[i] * Math.pow(capE, ilib.data.astro._nmEFactor[i]) * 12835 ilib.Date._dsin(ilib.data.astro._nmSolarCoeff[i] * solarAnomaly + 12836 ilib.data.astro._nmLunarCoeff[i] * lunarAnomaly + 12837 ilib.data.astro._nmMoonCoeff[i] * moonArgument); 12838 } 12839 var additional = 0; 12840 for (var i = 0; i < ilib.data.astro._nmAddConst.length; i++) { 12841 additional = additional + ilib.data.astro._nmAddFactor[i] * 12842 ilib.Date._dsin(ilib.data.astro._nmAddConst[i] + ilib.data.astro._nmAddCoeff[i] * k); 12843 } 12844 var extra = 0.000325 * ilib.Date._dsin(ilib.Date._poly(c, ilib.data.astro._nmExtra)); 12845 return ilib.Date._universalFromEphemeris(approx + correction + extra + additional + ilib.Date.RataDie.gregorianEpoch); 12846 }; 12847 12848 /** 12849 * @static 12850 * @param {number} jd 12851 * @return {number} 12852 */ 12853 ilib.Date._lunarSolarAngle = function(jd) { 12854 var lunar = ilib.Date._lunarLongitude(jd); 12855 var solar = ilib.Date._solarLongitude(jd) 12856 return ilib.Date._fixangle(lunar - solar); 12857 }; 12858 12859 /** 12860 * @static 12861 * @param {number} jd 12862 * @return {number} 12863 */ 12864 ilib.Date._newMoonBefore = function (jd) { 12865 var phase = ilib.Date._lunarSolarAngle(jd); 12866 // 11.450086114414322 is the julian day of the 0th full moon 12867 // 29.530588853000001 is the average length of a month 12868 var guess = Math.round((jd - 11.450086114414322 - ilib.Date.RataDie.gregorianEpoch) / 29.530588853000001 - phase / 360) - 1; 12869 var current, last; 12870 current = last = ilib.Date._newMoonTime(guess); 12871 while (current < jd) { 12872 guess++; 12873 last = current; 12874 current = ilib.Date._newMoonTime(guess); 12875 } 12876 return last; 12877 }; 12878 12879 /** 12880 * @static 12881 * @param {number} jd 12882 * @return {number} 12883 */ 12884 ilib.Date._newMoonAtOrAfter = function (jd) { 12885 var phase = ilib.Date._lunarSolarAngle(jd); 12886 // 11.450086114414322 is the julian day of the 0th full moon 12887 // 29.530588853000001 is the average length of a month 12888 var guess = Math.round((jd - 11.450086114414322 - ilib.Date.RataDie.gregorianEpoch) / 29.530588853000001 - phase / 360); 12889 var current; 12890 while ((current = ilib.Date._newMoonTime(guess)) < jd) { 12891 guess++; 12892 } 12893 return current; 12894 }; 12895 12896 /** 12897 * @static 12898 * @param {number} jd JD to calculate from 12899 * @param {number} longitude longitude to seek 12900 * @returns {number} the JD of the next time that the solar longitude 12901 * is a multiple of the given longitude 12902 */ 12903 ilib.Date._nextSolarLongitude = function(jd, longitude) { 12904 var rate = 365.242189 / 360.0; 12905 var tau = jd + rate * ilib.Date._fixangle(longitude - ilib.Date._solarLongitude(jd)); 12906 var start = Math.max(jd, tau - 5.0); 12907 var end = tau + 5.0; 12908 12909 return ilib.bisectionSearch(0, start, end, 1e-6, function (l) { 12910 return 180 - ilib.Date._fixangle(ilib.Date._solarLongitude(l) - longitude); 12911 }); 12912 }; 12913 12914 /** 12915 * Floor the julian day to midnight of the current julian day. 12916 * 12917 * @static 12918 * @protected 12919 * @param {number} jd the julian to round 12920 * @return {number} the jd floored to the midnight of the julian day 12921 */ 12922 ilib.Date._floorToJD = function(jd) { 12923 return Math.floor(jd - 0.5) + 0.5; 12924 }; 12925 12926 /** 12927 * Floor the julian day to midnight of the current julian day. 12928 * 12929 * @static 12930 * @protected 12931 * @param {number} jd the julian to round 12932 * @return {number} the jd floored to the midnight of the julian day 12933 */ 12934 ilib.Date._ceilToJD = function(jd) { 12935 return Math.ceil(jd + 0.5) - 0.5; 12936 }; 12937 12938 /* 12939 * persratadie.js - Represent a rata die date in the Persian calendar 12940 * 12941 * Copyright © 2014, JEDLSoft 12942 * 12943 * Licensed under the Apache License, Version 2.0 (the "License"); 12944 * you may not use this file except in compliance with the License. 12945 * You may obtain a copy of the License at 12946 * 12947 * http://www.apache.org/licenses/LICENSE-2.0 12948 * 12949 * Unless required by applicable law or agreed to in writing, software 12950 * distributed under the License is distributed on an "AS IS" BASIS, 12951 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12952 * 12953 * See the License for the specific language governing permissions and 12954 * limitations under the License. 12955 */ 12956 12957 /* !depends 12958 date.js 12959 util/utils.js 12960 util/math.js 12961 calendar/ratadie.js 12962 calendar/astro.js 12963 calendar/gregoriandate.js 12964 */ 12965 12966 /** 12967 * Construct a new Persian RD date number object. The constructor parameters can 12968 * contain any of the following properties: 12969 * 12970 * <ul> 12971 * <li><i>unixtime<i> - sets the time of this instance according to the given 12972 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian 12973 * 12974 * <li><i>julianday</i> - sets the time of this instance according to the given 12975 * Julian Day instance or the Julian Day given as a float 12976 * 12977 * <li><i>year</i> - any integer, including 0 12978 * 12979 * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc. 12980 * 12981 * <li><i>day</i> - 1 to 31 12982 * 12983 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 12984 * is always done with an unambiguous 24 hour representation 12985 * 12986 * <li><i>minute</i> - 0 to 59 12987 * 12988 * <li><i>second</i> - 0 to 59 12989 * 12990 * <li><i>millisecond</i> - 0 to 999 12991 * 12992 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 12993 * </ul> 12994 * 12995 * If the constructor is called with another Persian date instance instead of 12996 * a parameter block, the other instance acts as a parameter block and its 12997 * settings are copied into the current instance.<p> 12998 * 12999 * If the constructor is called with no arguments at all or if none of the 13000 * properties listed above are present, then the RD is calculate based on 13001 * the current date at the time of instantiation. <p> 13002 * 13003 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 13004 * specified in the params, it is assumed that they have the smallest possible 13005 * value in the range for the property (zero or one).<p> 13006 * 13007 * Depends directive: !depends persiandate.js 13008 * 13009 * @private 13010 * @class 13011 * @constructor 13012 * @extends ilib.Date.RataDie 13013 * @param {Object=} params parameters that govern the settings and behaviour of this Persian RD date 13014 */ 13015 ilib.Date.PersAstroRataDie = function(params) { 13016 this.rd = undefined; 13017 ilib.Date.initAstro( 13018 params && typeof(params.sync) === 'boolean' ? params.sync : true, 13019 params && params.loadParams, 13020 ilib.bind(this, function (x) { 13021 ilib.Date.RataDie.call(this, params); 13022 if (params && typeof(params.callback) === 'function') { 13023 params.callback(this); 13024 } 13025 }) 13026 ); 13027 }; 13028 13029 ilib.Date.PersAstroRataDie.prototype = new ilib.Date.RataDie(); 13030 ilib.Date.PersAstroRataDie.prototype.parent = ilib.Date.RataDie; 13031 ilib.Date.PersAstroRataDie.prototype.constructor = ilib.Date.PersAstroRataDie; 13032 13033 /** 13034 * The difference between a zero Julian day and the first Persian date 13035 * @private 13036 * @const 13037 * @type number 13038 */ 13039 ilib.Date.PersAstroRataDie.prototype.epoch = 1948319.5; 13040 13041 /** 13042 * @protected 13043 */ 13044 ilib.Date.PersAstroRataDie.prototype._tehranEquinox = function(year) { 13045 var equJED, equJD, equAPP, equTehran, dtTehran, eot; 13046 13047 // March equinox in dynamical time 13048 equJED = ilib.Date._equinox(year, 0); 13049 13050 // Correct for delta T to obtain Universal time 13051 equJD = equJED - (ilib.Date._deltat(year) / (24 * 60 * 60)); 13052 13053 // Apply the equation of time to yield the apparent time at Greenwich 13054 eot = ilib.Date._equationOfTime(equJED) * 360; 13055 eot = (eot - 20 * Math.floor(eot/20)) / 360; 13056 equAPP = equJD + eot; 13057 13058 /* 13059 * Finally, we must correct for the constant difference between 13060 * the Greenwich meridian and the time zone standard for Iran 13061 * Standard time, 52 degrees 30 minutes to the East. 13062 */ 13063 13064 dtTehran = 52.5 / 360; 13065 equTehran = equAPP + dtTehran; 13066 13067 return equTehran; 13068 }; 13069 13070 /** 13071 * Calculate the year based on the given Julian day. 13072 * @protected 13073 * @param {number} jd the Julian day to get the year for 13074 * @return {{year:number,equinox:number}} the year and the last equinox 13075 */ 13076 ilib.Date.PersAstroRataDie.prototype._getYear = function(jd) { 13077 var gd = new ilib.Date.GregDate({julianday: jd}); 13078 var guess = gd.getYears() - 2, 13079 nexteq, 13080 ret = {}; 13081 13082 //ret.equinox = Math.floor(this._tehranEquinox(guess)); 13083 ret.equinox = this._tehranEquinox(guess); 13084 while (ret.equinox > jd) { 13085 guess--; 13086 // ret.equinox = Math.floor(this._tehranEquinox(guess)); 13087 ret.equinox = this._tehranEquinox(guess); 13088 } 13089 nexteq = ret.equinox - 1; 13090 // if the equinox falls after noon, then the day after that is the start of the 13091 // next year, so truncate the JD to get the noon of the day before the day with 13092 //the equinox on it, then add 0.5 to get the midnight of that day 13093 while (!(Math.floor(ret.equinox) + 0.5 <= jd && jd < Math.floor(nexteq) + 0.5)) { 13094 ret.equinox = nexteq; 13095 guess++; 13096 // nexteq = Math.floor(this._tehranEquinox(guess)); 13097 nexteq = this._tehranEquinox(guess); 13098 } 13099 13100 // Mean solar tropical year is 365.24219878 days 13101 ret.year = Math.round((ret.equinox - this.epoch - 1) / 365.24219878) + 1; 13102 13103 return ret; 13104 }; 13105 13106 /** 13107 * Calculate the Rata Die (fixed day) number of the given date from the 13108 * date components. 13109 * 13110 * @protected 13111 * @param {Object} date the date components to calculate the RD from 13112 */ 13113 ilib.Date.PersAstroRataDie.prototype._setDateComponents = function(date) { 13114 var adr, guess, jd; 13115 13116 // Mean solar tropical year is 365.24219878 days 13117 guess = this.epoch + 1 + 365.24219878 * (date.year - 2); 13118 adr = {year: date.year - 1, equinox: 0}; 13119 13120 while (adr.year < date.year) { 13121 adr = this._getYear(guess); 13122 guess = adr.equinox + (365.24219878 + 2); 13123 } 13124 13125 jd = Math.floor(adr.equinox) + 13126 ((date.month <= 7) ? 13127 ((date.month - 1) * 31) : 13128 (((date.month - 1) * 30) + 6) 13129 ) + 13130 (date.day - 1 + 0.5); // add 0.5 so that we convert JDs, which start at noon to RDs which start at midnight 13131 13132 jd += (date.hour * 3600000 + 13133 date.minute * 60000 + 13134 date.second * 1000 + 13135 date.millisecond) / 13136 86400000; 13137 13138 this.rd = jd - this.epoch; 13139 }; 13140 13141 /** 13142 * Return the rd number of the particular day of the week on or before the 13143 * given rd. eg. The Sunday on or before the given rd. 13144 * @private 13145 * @param {number} rd the rata die date of the reference date 13146 * @param {number} dayOfWeek the day of the week that is being sought relative 13147 * to the current date 13148 * @return {number} the rd of the day of the week 13149 */ 13150 ilib.Date.PersAstroRataDie.prototype._onOrBefore = function(rd, dayOfWeek) { 13151 return rd - ilib.mod(Math.floor(rd) - dayOfWeek - 3, 7); 13152 }; 13153 13154 /* 13155 * persianastro.js - Represent a Persian astronomical (Hijjri) calendar object. 13156 * 13157 * Copyright © 2014, JEDLSoft 13158 * 13159 * Licensed under the Apache License, Version 2.0 (the "License"); 13160 * you may not use this file except in compliance with the License. 13161 * You may obtain a copy of the License at 13162 * 13163 * http://www.apache.org/licenses/LICENSE-2.0 13164 * 13165 * Unless required by applicable law or agreed to in writing, software 13166 * distributed under the License is distributed on an "AS IS" BASIS, 13167 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13168 * 13169 * See the License for the specific language governing permissions and 13170 * limitations under the License. 13171 */ 13172 13173 13174 /* !depends 13175 calendar/astro.js 13176 calendar.js 13177 locale.js 13178 date.js 13179 julianday.js 13180 util/utils.js 13181 calendar/persratadie.js 13182 */ 13183 13184 /** 13185 * @class 13186 * Construct a new Persian astronomical (Hijjri) calendar object. This class encodes 13187 * information about a Persian calendar. This class differs from the 13188 * Persian calendar in that the leap years are calculated based on the 13189 * astronomical observations of the sun in Teheran, instead of calculating 13190 * the leap years based on a regular cyclical rhythm algorithm.<p> 13191 * 13192 * Depends directive: !depends persianastro.js 13193 * 13194 * @constructor 13195 * @implements ilib.Cal 13196 */ 13197 ilib.Cal.Persian = function() { 13198 this.type = "persian"; 13199 }; 13200 13201 /** 13202 * @private 13203 * @const 13204 * @type Array.<number> 13205 * the lengths of each month 13206 */ 13207 ilib.Cal.Persian.monthLengths = [ 13208 31, // Farvardin 13209 31, // Ordibehesht 13210 31, // Khordad 13211 31, // Tir 13212 31, // Mordad 13213 31, // Shahrivar 13214 30, // Mehr 13215 30, // Aban 13216 30, // Azar 13217 30, // Dey 13218 30, // Bahman 13219 29 // Esfand 13220 ]; 13221 13222 /** 13223 * Return the number of months in the given year. The number of months in a year varies 13224 * for some luni-solar calendars because in some years, an extra month is needed to extend the 13225 * days in a year to an entire solar year. The month is represented as a 1-based number 13226 * where 1=first month, 2=second month, etc. 13227 * 13228 * @param {number} year a year for which the number of months is sought 13229 * @return {number} The number of months in the given year 13230 */ 13231 ilib.Cal.Persian.prototype.getNumMonths = function(year) { 13232 return 12; 13233 }; 13234 13235 /** 13236 * Return the number of days in a particular month in a particular year. This function 13237 * can return a different number for a month depending on the year because of things 13238 * like leap years. 13239 * 13240 * @param {number} month the month for which the length is sought 13241 * @param {number} year the year within which that month can be found 13242 * @return {number} the number of days within the given month in the given year 13243 */ 13244 ilib.Cal.Persian.prototype.getMonLength = function(month, year) { 13245 if (month !== 12 || !this.isLeapYear(year)) { 13246 return ilib.Cal.Persian.monthLengths[month-1]; 13247 } else { 13248 // Month 12, Esfand, has 30 days instead of 29 in leap years 13249 return 30; 13250 } 13251 }; 13252 13253 /** 13254 * Return true if the given year is a leap year in the Persian astronomical calendar. 13255 * @param {number} year the year for which the leap year information is being sought 13256 * @return {boolean} true if the given year is a leap year 13257 */ 13258 ilib.Cal.Persian.prototype.isLeapYear = function(year) { 13259 var rdNextYear = new ilib.Date.PersAstroRataDie({ 13260 cal: this, 13261 year: year + 1, 13262 month: 1, 13263 day: 1, 13264 hour: 0, 13265 minute: 0, 13266 second: 0, 13267 millisecond: 0 13268 }); 13269 var rdThisYear = new ilib.Date.PersAstroRataDie({ 13270 cal: this, 13271 year: year, 13272 month: 1, 13273 day: 1, 13274 hour: 0, 13275 minute: 0, 13276 second: 0, 13277 millisecond: 0 13278 }); 13279 return (rdNextYear.getRataDie() - rdThisYear.getRataDie()) > 365; 13280 }; 13281 13282 /** 13283 * Return the type of this calendar. 13284 * 13285 * @return {string} the name of the type of this calendar 13286 */ 13287 ilib.Cal.Persian.prototype.getType = function() { 13288 return this.type; 13289 }; 13290 13291 /** 13292 * Return a date instance for this calendar type using the given 13293 * options. 13294 * @param {Object} options options controlling the construction of 13295 * the date instance 13296 * @return {ilib.Date} a date appropriate for this calendar type 13297 */ 13298 ilib.Cal.Persian.prototype.newDateInstance = function (options) { 13299 return new ilib.Date.PersDate(options); 13300 }; 13301 13302 /* register this calendar for the factory method */ 13303 ilib.Cal._constructors["persian"] = ilib.Cal.Persian; 13304 13305 /* 13306 * persianastrodate.js - Represent a date in the Persian astronomical (Hijjri) calendar 13307 * 13308 * Copyright © 2014, JEDLSoft 13309 * 13310 * Licensed under the Apache License, Version 2.0 (the "License"); 13311 * you may not use this file except in compliance with the License. 13312 * You may obtain a copy of the License at 13313 * 13314 * http://www.apache.org/licenses/LICENSE-2.0 13315 * 13316 * Unless required by applicable law or agreed to in writing, software 13317 * distributed under the License is distributed on an "AS IS" BASIS, 13318 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13319 * 13320 * See the License for the specific language governing permissions and 13321 * limitations under the License. 13322 */ 13323 13324 /* !depends 13325 date.js 13326 calendar/persratadie.js 13327 calendar/persianastro.js 13328 util/utils.js 13329 util/search.js 13330 util/math.js 13331 localeinfo.js 13332 julianday.js 13333 */ 13334 13335 // !data astro 13336 13337 /** 13338 * @class 13339 * 13340 * Construct a new Persian astronomical date object. The constructor parameters can 13341 * contain any of the following properties: 13342 * 13343 * <ul> 13344 * <li><i>unixtime<i> - sets the time of this instance according to the given 13345 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian 13346 * 13347 * <li><i>julianday</i> - sets the time of this instance according to the given 13348 * Julian Day instance or the Julian Day given as a float 13349 * 13350 * <li><i>year</i> - any integer, including 0 13351 * 13352 * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc. 13353 * 13354 * <li><i>day</i> - 1 to 31 13355 * 13356 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 13357 * is always done with an unambiguous 24 hour representation 13358 * 13359 * <li><i>minute</i> - 0 to 59 13360 * 13361 * <li><i>second</i> - 0 to 59 13362 * 13363 * <li><i>millisecond</i> - 0 to 999 13364 * 13365 * <li><i>timezone</i> - the ilib.TimeZone instance or time zone name as a string 13366 * of this persian date. The date/time is kept in the local time. The time zone 13367 * is used later if this date is formatted according to a different time zone and 13368 * the difference has to be calculated, or when the date format has a time zone 13369 * component in it. 13370 * 13371 * <li><i>locale</i> - locale for this persian date. If the time zone is not 13372 * given, it can be inferred from this locale. For locales that span multiple 13373 * time zones, the one with the largest population is chosen as the one that 13374 * represents the locale. 13375 * 13376 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 13377 * </ul> 13378 * 13379 * If the constructor is called with another Persian date instance instead of 13380 * a parameter block, the other instance acts as a parameter block and its 13381 * settings are copied into the current instance.<p> 13382 * 13383 * If the constructor is called with no arguments at all or if none of the 13384 * properties listed above 13385 * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 13386 * components are 13387 * filled in with the current date at the time of instantiation. Note that if 13388 * you do not give the time zone when defaulting to the current time and the 13389 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 13390 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 13391 * Mean Time").<p> 13392 * 13393 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 13394 * specified in the params, it is assumed that they have the smallest possible 13395 * value in the range for the property (zero or one).<p> 13396 * 13397 * Depends directive: !depends persiandate.js 13398 * 13399 * @constructor 13400 * @extends ilib.Date 13401 * @param {Object=} params parameters that govern the settings and behaviour of this Persian date 13402 */ 13403 ilib.Date.PersDate = function(params) { 13404 this.cal = new ilib.Cal.Persian(); 13405 this.timezone = "local"; 13406 13407 if (params) { 13408 if (params.locale) { 13409 this.locale = (typeof(params.locale) === 'string') ? new ilib.Locale(params.locale) : params.locale; 13410 var li = new ilib.LocaleInfo(this.locale); 13411 this.timezone = li.getTimeZone(); 13412 } 13413 if (params.timezone) { 13414 this.timezone = params.timezone; 13415 } 13416 } 13417 13418 ilib.Date.initAstro( 13419 params && typeof(params.sync) === 'boolean' ? params.sync : true, 13420 params && params.loadParams, 13421 ilib.bind(this, function (x) { 13422 if (params && (params.year || params.month || params.day || params.hour || 13423 params.minute || params.second || params.millisecond)) { 13424 /** 13425 * Year in the Persian calendar. 13426 * @type number 13427 */ 13428 this.year = parseInt(params.year, 10) || 0; 13429 13430 /** 13431 * The month number, ranging from 1 to 12 13432 * @type number 13433 */ 13434 this.month = parseInt(params.month, 10) || 1; 13435 13436 /** 13437 * The day of the month. This ranges from 1 to 31. 13438 * @type number 13439 */ 13440 this.day = parseInt(params.day, 10) || 1; 13441 13442 /** 13443 * The hour of the day. This can be a number from 0 to 23, as times are 13444 * stored unambiguously in the 24-hour clock. 13445 * @type number 13446 */ 13447 this.hour = parseInt(params.hour, 10) || 0; 13448 13449 /** 13450 * The minute of the hours. Ranges from 0 to 59. 13451 * @type number 13452 */ 13453 this.minute = parseInt(params.minute, 10) || 0; 13454 13455 /** 13456 * The second of the minute. Ranges from 0 to 59. 13457 * @type number 13458 */ 13459 this.second = parseInt(params.second, 10) || 0; 13460 13461 /** 13462 * The millisecond of the second. Ranges from 0 to 999. 13463 * @type number 13464 */ 13465 this.millisecond = parseInt(params.millisecond, 10) || 0; 13466 13467 /** 13468 * The day of the year. Ranges from 1 to 366. 13469 * @type number 13470 */ 13471 this.dayOfYear = parseInt(params.dayOfYear, 10); 13472 13473 if (typeof(params.dst) === 'boolean') { 13474 this.dst = params.dst; 13475 } 13476 13477 this.rd = this.newRd(this); 13478 13479 // add the time zone offset to the rd to convert to UTC 13480 if (!this.tz) { 13481 this.tz = new ilib.TimeZone({id: this.timezone}); 13482 } 13483 // getOffsetMillis requires that this.year, this.rd, and this.dst 13484 // are set in order to figure out which time zone rules apply and 13485 // what the offset is at that point in the year 13486 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000; 13487 if (this.offset !== 0) { 13488 this.rd = this.newRd({ 13489 rd: this.rd.getRataDie() - this.offset 13490 }); 13491 } 13492 } 13493 13494 if (!this.rd) { 13495 this.rd = this.newRd(params); 13496 this._calcDateComponents(); 13497 } 13498 13499 if (params && typeof(params.onLoad) === 'function') { 13500 params.onLoad(this); 13501 } 13502 }) 13503 ); 13504 }; 13505 13506 ilib.Date.PersDate.prototype = new ilib.Date({noinstance: true}); 13507 ilib.Date.PersDate.prototype.parent = ilib.Date; 13508 ilib.Date.PersDate.prototype.constructor = ilib.Date.PersDate; 13509 13510 /** 13511 * @private 13512 * @const 13513 * @type Array.<number> 13514 * the cumulative lengths of each month, for a non-leap year 13515 */ 13516 ilib.Date.PersDate.cumMonthLengths = [ 13517 0, // Farvardin 13518 31, // Ordibehesht 13519 62, // Khordad 13520 93, // Tir 13521 124, // Mordad 13522 155, // Shahrivar 13523 186, // Mehr 13524 216, // Aban 13525 246, // Azar 13526 276, // Dey 13527 306, // Bahman 13528 336, // Esfand 13529 366 13530 ]; 13531 13532 /** 13533 * Return a new RD for this date type using the given params. 13534 * @protected 13535 * @param {Object=} params the parameters used to create this rata die instance 13536 * @returns {ilib.Date.RataDie} the new RD instance for the given params 13537 */ 13538 ilib.Date.PersDate.prototype.newRd = function (params) { 13539 return new ilib.Date.PersAstroRataDie(params); 13540 }; 13541 13542 /** 13543 * Return the year for the given RD 13544 * @protected 13545 * @param {number} rd RD to calculate from 13546 * @returns {number} the year for the RD 13547 */ 13548 ilib.Date.PersDate.prototype._calcYear = function(rd) { 13549 var julianday = rd + this.rd.epoch; 13550 return this.rd._getYear(julianday).year; 13551 }; 13552 13553 /** 13554 * @private 13555 * Calculate date components for the given RD date. 13556 */ 13557 ilib.Date.PersDate.prototype._calcDateComponents = function () { 13558 var remainder, 13559 rd = this.rd.getRataDie(); 13560 13561 this.year = this._calcYear(rd); 13562 13563 if (typeof(this.offset) === "undefined") { 13564 // now offset the RD by the time zone, then recalculate in case we were 13565 // near the year boundary 13566 if (!this.tz) { 13567 this.tz = new ilib.TimeZone({id: this.timezone}); 13568 } 13569 this.offset = this.tz.getOffsetMillis(this) / 86400000; 13570 } 13571 13572 if (this.offset !== 0) { 13573 rd += this.offset; 13574 this.year = this._calcYear(rd); 13575 } 13576 13577 //console.log("PersDate.calcComponent: calculating for rd " + rd); 13578 //console.log("PersDate.calcComponent: year is " + ret.year); 13579 var yearStart = this.newRd({ 13580 year: this.year, 13581 month: 1, 13582 day: 1, 13583 hour: 0, 13584 minute: 0, 13585 second: 0, 13586 millisecond: 0 13587 }); 13588 remainder = rd - yearStart.getRataDie() + 1; 13589 13590 this.dayOfYear = remainder; 13591 13592 //console.log("PersDate.calcComponent: remainder is " + remainder); 13593 13594 this.month = ilib.bsearch(Math.floor(remainder), ilib.Date.PersDate.cumMonthLengths); 13595 remainder -= ilib.Date.PersDate.cumMonthLengths[this.month-1]; 13596 13597 //console.log("PersDate.calcComponent: month is " + this.month + " and remainder is " + remainder); 13598 13599 this.day = Math.floor(remainder); 13600 remainder -= this.day; 13601 13602 //console.log("PersDate.calcComponent: day is " + this.day + " and remainder is " + remainder); 13603 13604 // now convert to milliseconds for the rest of the calculation 13605 remainder = Math.round(remainder * 86400000); 13606 13607 this.hour = Math.floor(remainder/3600000); 13608 remainder -= this.hour * 3600000; 13609 13610 this.minute = Math.floor(remainder/60000); 13611 remainder -= this.minute * 60000; 13612 13613 this.second = Math.floor(remainder/1000); 13614 remainder -= this.second * 1000; 13615 13616 this.millisecond = remainder; 13617 }; 13618 13619 /** 13620 * Return the day of the week of this date. The day of the week is encoded 13621 * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday. 13622 * 13623 * @return {number} the day of the week 13624 */ 13625 ilib.Date.PersDate.prototype.getDayOfWeek = function() { 13626 var rd = Math.floor(this.getRataDie()); 13627 return ilib.mod(rd-3, 7); 13628 }; 13629 13630 /** 13631 * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 13632 * 365, regardless of months or weeks, etc. That is, Farvardin 1st is day 1, and 13633 * December 31st is 365 in regular years, or 366 in leap years. 13634 * @return {number} the ordinal day of the year 13635 */ 13636 ilib.Date.PersDate.prototype.getDayOfYear = function() { 13637 return ilib.Date.PersDate.cumMonthLengths[this.month-1] + this.day; 13638 }; 13639 13640 /** 13641 * Return the era for this date as a number. The value for the era for Persian 13642 * calendars is -1 for "before the persian era" (BP) and 1 for "the persian era" (anno 13643 * persico or AP). 13644 * BP dates are any date before Farvardin 1, 1 AP. In the proleptic Persian calendar, 13645 * there is a year 0, so any years that are negative or zero are BP. 13646 * @return {number} 1 if this date is in the common era, -1 if it is before the 13647 * common era 13648 */ 13649 ilib.Date.PersDate.prototype.getEra = function() { 13650 return (this.year < 1) ? -1 : 1; 13651 }; 13652 13653 /** 13654 * Return the name of the calendar that governs this date. 13655 * 13656 * @return {string} a string giving the name of the calendar 13657 */ 13658 ilib.Date.PersDate.prototype.getCalendar = function() { 13659 return "persian"; 13660 }; 13661 13662 // register with the factory method 13663 ilib.Date._constructors["persian"] = ilib.Date.PersDate; 13664 /* 13665 * han.js - Represent a Han Chinese Lunar calendar object. 13666 * 13667 * Copyright © 2014, JEDLSoft 13668 * 13669 * Licensed under the Apache License, Version 2.0 (the "License"); 13670 * you may not use this file except in compliance with the License. 13671 * You may obtain a copy of the License at 13672 * 13673 * http://www.apache.org/licenses/LICENSE-2.0 13674 * 13675 * Unless required by applicable law or agreed to in writing, software 13676 * distributed under the License is distributed on an "AS IS" BASIS, 13677 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13678 * 13679 * See the License for the specific language governing permissions and 13680 * limitations under the License. 13681 */ 13682 13683 /* !depends calendar.js locale.js date.js julianday.js util/utils.js util/math.js calendar/astro.js */ 13684 13685 /** 13686 * @class 13687 * Construct a new Han algorithmic calendar object. This class encodes information about 13688 * a Han algorithmic calendar.<p> 13689 * 13690 * Depends directive: !depends han.js 13691 * 13692 * @constructor 13693 * @param {Object=} params optional parameters to load the calendrical data 13694 * @implements ilib.Cal 13695 */ 13696 ilib.Cal.Han = function(params) { 13697 this.type = "han"; 13698 var sync = params && typeof(params.sync) === 'boolean' ? params.sync : true; 13699 13700 ilib.Date.initAstro(sync, params && params.loadParams, /** @type {function ((Object|null)=): ?} */ ilib.bind(this, function (x) { 13701 if (params && typeof(params.callback) === 'function') { 13702 params.callback(this); 13703 } 13704 })); 13705 }; 13706 13707 /** 13708 * @protected 13709 * @static 13710 * @param {number} year 13711 * @param {number=} cycle 13712 * @return {number} 13713 */ 13714 ilib.Cal.Han._getElapsedYear = function(year, cycle) { 13715 var elapsedYear = year || 0; 13716 if (typeof(year) !== 'undefined' && year < 61 && typeof(cycle) !== 'undefined') { 13717 elapsedYear = 60 * cycle + year; 13718 } 13719 return elapsedYear; 13720 }; 13721 13722 /** 13723 * @protected 13724 * @static 13725 * @param {number} jd julian day to calculate from 13726 * @param {number} longitude longitude to seek 13727 * @returns {number} the julian day of the next time that the solar longitude 13728 * is a multiple of the given longitude 13729 */ 13730 ilib.Cal.Han._hanNextSolarLongitude = function(jd, longitude) { 13731 var tz = ilib.Cal.Han._chineseTZ(jd); 13732 var uni = ilib.Date._universalFromLocal(jd, tz); 13733 var sol = ilib.Date._nextSolarLongitude(uni, longitude); 13734 return ilib.Date._localFromUniversal(sol, tz); 13735 }; 13736 13737 /** 13738 * @protected 13739 * @static 13740 * @param {number} jd julian day to calculate from 13741 * @returns {number} the major solar term for the julian day 13742 */ 13743 ilib.Cal.Han._majorSTOnOrAfter = function(jd) { 13744 var tz = ilib.Cal.Han._chineseTZ(jd); 13745 var uni = ilib.Date._universalFromLocal(jd, tz); 13746 var next = ilib.Date._fixangle(30 * Math.ceil(ilib.Date._solarLongitude(uni)/30)); 13747 return ilib.Cal.Han._hanNextSolarLongitude(jd, next); 13748 }; 13749 13750 /** 13751 * @protected 13752 * @static 13753 * @param {number} year the year for which the leap year information is being sought 13754 * @param {number=} cycle if the given year < 60, this can specify the cycle. If the 13755 * cycle is not given, then the year should be given as elapsed years since the beginning 13756 * of the epoch 13757 */ 13758 ilib.Cal.Han._solsticeBefore = function (year, cycle) { 13759 var elapsedYear = ilib.Cal.Han._getElapsedYear(year, cycle); 13760 var gregyear = elapsedYear - 2697; 13761 var rd = new ilib.Date.GregRataDie({ 13762 year: gregyear-1, 13763 month: 12, 13764 day: 15, 13765 hour: 0, 13766 minute: 0, 13767 second: 0, 13768 millisecond: 0 13769 }); 13770 return ilib.Cal.Han._majorSTOnOrAfter(rd.getRataDie() + ilib.Date.RataDie.gregorianEpoch); 13771 }; 13772 13773 /** 13774 * @protected 13775 * @static 13776 * @param {number} jd julian day to calculate from 13777 * @returns {number} the current major solar term 13778 */ 13779 ilib.Cal.Han._chineseTZ = function(jd) { 13780 var year = ilib.Date.GregDate._calcYear(jd - ilib.Date.RataDie.gregorianEpoch); 13781 return year < 1929 ? 465.6666666666666666 : 480; 13782 }; 13783 13784 /** 13785 * @protected 13786 * @static 13787 * @param {number} jd julian day to calculate from 13788 * @returns {number} the julian day of next new moon on or after the given julian day date 13789 */ 13790 ilib.Cal.Han._newMoonOnOrAfter = function(jd) { 13791 var tz = ilib.Cal.Han._chineseTZ(jd); 13792 var uni = ilib.Date._universalFromLocal(jd, tz); 13793 var moon = ilib.Date._newMoonAtOrAfter(uni); 13794 // floor to the start of the julian day 13795 return ilib.Date._floorToJD(ilib.Date._localFromUniversal(moon, tz)); 13796 }; 13797 13798 /** 13799 * @protected 13800 * @static 13801 * @param {number} jd julian day to calculate from 13802 * @returns {number} the julian day of previous new moon before the given julian day date 13803 */ 13804 ilib.Cal.Han._newMoonBefore = function(jd) { 13805 var tz = ilib.Cal.Han._chineseTZ(jd); 13806 var uni = ilib.Date._universalFromLocal(jd, tz); 13807 var moon = ilib.Date._newMoonBefore(uni); 13808 // floor to the start of the julian day 13809 return ilib.Date._floorToJD(ilib.Date._localFromUniversal(moon, tz)); 13810 }; 13811 13812 /** 13813 * @static 13814 * @protected 13815 * @param {number} year the year for which the leap year information is being sought 13816 * @param {number=} cycle if the given year < 60, this can specify the cycle. If the 13817 * cycle is not given, then the year should be given as elapsed years since the beginning 13818 * of the epoch 13819 */ 13820 ilib.Cal.Han._leapYearCalc = function(year, cycle) { 13821 var ret = { 13822 elapsedYear: ilib.Cal.Han._getElapsedYear(year, cycle) 13823 }; 13824 ret.solstice1 = ilib.Cal.Han._solsticeBefore(ret.elapsedYear); 13825 ret.solstice2 = ilib.Cal.Han._solsticeBefore(ret.elapsedYear+1); 13826 // ceil to the end of the julian day 13827 ret.m1 = ilib.Cal.Han._newMoonOnOrAfter(ilib.Date._ceilToJD(ret.solstice1)); 13828 ret.m2 = ilib.Cal.Han._newMoonBefore(ilib.Date._ceilToJD(ret.solstice2)); 13829 13830 return ret; 13831 }; 13832 13833 /** 13834 * @protected 13835 * @static 13836 * @param {number} jd julian day to calculate from 13837 * @returns {number} the current major solar term 13838 */ 13839 ilib.Cal.Han._currentMajorST = function(jd) { 13840 var s = ilib.Date._solarLongitude(ilib.Date._universalFromLocal(jd, ilib.Cal.Han._chineseTZ(jd))); 13841 return ilib.amod(2 + Math.floor(s/30), 12); 13842 }; 13843 13844 /** 13845 * @protected 13846 * @static 13847 * @param {number} jd julian day to calculate from 13848 * @returns {boolean} true if there is no major solar term in the same year 13849 */ 13850 ilib.Cal.Han._noMajorST = function(jd) { 13851 return ilib.Cal.Han._currentMajorST(jd) === ilib.Cal.Han._currentMajorST(ilib.Cal.Han._newMoonOnOrAfter(jd+1)); 13852 }; 13853 13854 /** 13855 * Return the number of months in the given year. The number of months in a year varies 13856 * for some luni-solar calendars because in some years, an extra month is needed to extend the 13857 * days in a year to an entire solar year. The month is represented as a 1-based number 13858 * where 1=first month, 2=second month, etc. 13859 * 13860 * @param {number} year a year for which the number of months is sought 13861 * @param {number=} cycle if the given year < 60, this can specify the cycle. If the 13862 * cycle is not given, then the year should be given as elapsed years since the beginning 13863 * of the epoch 13864 * @return {number} The number of months in the given year 13865 */ 13866 ilib.Cal.Han.prototype.getNumMonths = function(year, cycle) { 13867 return this.isLeapYear(year, cycle) ? 13 : 12; 13868 }; 13869 13870 /** 13871 * Return the number of days in a particular month in a particular year. This function 13872 * can return a different number for a month depending on the year because of things 13873 * like leap years. 13874 * 13875 * @param {number} month the elapsed month for which the length is sought 13876 * @param {number} year the elapsed year within which that month can be found 13877 * @return {number} the number of days within the given month in the given year 13878 */ 13879 ilib.Cal.Han.prototype.getMonLength = function(month, year) { 13880 // distance between two new moons in Nanjing China 13881 var calc = ilib.Cal.Han._leapYearCalc(year); 13882 var priorNewMoon = ilib.Cal.Han._newMoonOnOrAfter(calc.m1 + month * 29); 13883 var postNewMoon = ilib.Cal.Han._newMoonOnOrAfter(priorNewMoon + 1); 13884 return postNewMoon - priorNewMoon; 13885 }; 13886 13887 /** 13888 * Return the equivalent year in the 2820 year cycle that begins on 13889 * Far 1, 474. This particular cycle obeys the cycle-of-years formula 13890 * whereas the others do not specifically. This cycle can be used as 13891 * a proxy for other years outside of the cycle by shifting them into 13892 * the cycle. 13893 * @param {number} year year to find the equivalent cycle year for 13894 * @returns {number} the equivalent cycle year 13895 */ 13896 ilib.Cal.Han.prototype.equivalentCycleYear = function(year) { 13897 var y = year - (year >= 0 ? 474 : 473); 13898 return ilib.mod(y, 2820) + 474; 13899 }; 13900 13901 /** 13902 * Return true if the given year is a leap year in the Han calendar. 13903 * If the year is given as a year/cycle combination, then the year should be in the 13904 * range [1,60] and the given cycle is the cycle in which the year is located. If 13905 * the year is greater than 60, then 13906 * it represents the total number of years elapsed in the proleptic calendar since 13907 * the beginning of the Chinese epoch in on 15 Feb, -2636 (Gregorian). In this 13908 * case, the cycle parameter is ignored. 13909 * 13910 * @param {number} year the year for which the leap year information is being sought 13911 * @param {number=} cycle if the given year < 60, this can specify the cycle. If the 13912 * cycle is not given, then the year should be given as elapsed years since the beginning 13913 * of the epoch 13914 * @return {boolean} true if the given year is a leap year 13915 */ 13916 ilib.Cal.Han.prototype.isLeapYear = function(year, cycle) { 13917 var calc = ilib.Cal.Han._leapYearCalc(year, cycle); 13918 return Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12; 13919 }; 13920 13921 /** 13922 * Return the month of the year that is the leap month. If the given year is 13923 * not a leap year, then this method will return -1. 13924 * 13925 * @param {number} year the year for which the leap year information is being sought 13926 * @param {number=} cycle if the given year < 60, this can specify the cycle. If the 13927 * cycle is not given, then the year should be given as elapsed years since the beginning 13928 * of the epoch 13929 * @return {number} the number of the month that is doubled in this leap year, or -1 13930 * if this is not a leap year 13931 */ 13932 ilib.Cal.Han.prototype.getLeapMonth = function(year, cycle) { 13933 var calc = ilib.Cal.Han._leapYearCalc(year, cycle); 13934 13935 if (Math.round((calc.m2 - calc.m1) / 29.530588853000001) != 12) { 13936 return -1; // no leap month 13937 } 13938 13939 // search between rd1 and rd2 for the first month with no major solar term. That is our leap month. 13940 var month = 0; 13941 var m = ilib.Cal.Han._newMoonOnOrAfter(calc.m1+1); 13942 while (!ilib.Cal.Han._noMajorST(m)) { 13943 month++; 13944 m = ilib.Cal.Han._newMoonOnOrAfter(m+1); 13945 } 13946 13947 // return the number of the month that is doubled 13948 return month; 13949 }; 13950 13951 /** 13952 * Return the date of Chinese New Years in the given calendar year. 13953 * 13954 * @param {number} year the Chinese year for which the new year information is being sought 13955 * @param {number=} cycle if the given year < 60, this can specify the cycle. If the 13956 * cycle is not given, then the year should be given as elapsed years since the beginning 13957 * of the epoch 13958 * @return {number} the julian day of the beginning of the given year 13959 */ 13960 ilib.Cal.Han.prototype.newYears = function(year, cycle) { 13961 var calc = ilib.Cal.Han._leapYearCalc(year, cycle); 13962 var m2 = ilib.Cal.Han._newMoonOnOrAfter(calc.m1+1); 13963 if (Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12 && 13964 (ilib.Cal.Han._noMajorST(calc.m1) || ilib.Cal.Han._noMajorST(m2)) ) { 13965 return ilib.Cal.Han._newMoonOnOrAfter(m2+1); 13966 } 13967 return m2; 13968 }; 13969 13970 /** 13971 * Return the type of this calendar. 13972 * 13973 * @return {string} the name of the type of this calendar 13974 */ 13975 ilib.Cal.Han.prototype.getType = function() { 13976 return this.type; 13977 }; 13978 13979 /** 13980 * Return a date instance for this calendar type using the given 13981 * options. 13982 * @param {Object} options options controlling the construction of 13983 * the date instance 13984 * @return {ilib.Date} a date appropriate for this calendar type 13985 */ 13986 ilib.Cal.Han.prototype.newDateInstance = function (options) { 13987 return new ilib.Date.HanDate(options); 13988 }; 13989 13990 /* register this calendar for the factory method */ 13991 ilib.Cal._constructors["han"] = ilib.Cal.Han; 13992 13993 /* 13994 * handate.js - Represent a date in the Han algorithmic calendar 13995 * 13996 * Copyright © 2014, JEDLSoft 13997 * 13998 * Licensed under the Apache License, Version 2.0 (the "License"); 13999 * you may not use this file except in compliance with the License. 14000 * You may obtain a copy of the License at 14001 * 14002 * http://www.apache.org/licenses/LICENSE-2.0 14003 * 14004 * Unless required by applicable law or agreed to in writing, software 14005 * distributed under the License is distributed on an "AS IS" BASIS, 14006 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14007 * 14008 * See the License for the specific language governing permissions and 14009 * limitations under the License. 14010 */ 14011 14012 /* !depends 14013 date.js 14014 calendar/gregratadie.js 14015 calendar/gregoriandate.js 14016 calendar/han.js 14017 calendar/astro.js 14018 util/utils.js 14019 util/search.js 14020 util/math.js 14021 localeinfo.js 14022 julianday.js 14023 */ 14024 14025 /** 14026 * Construct a new Han RD date number object. The constructor parameters can 14027 * contain any of the following properties: 14028 * 14029 * <ul> 14030 * <li><i>unixtime<i> - sets the time of this instance according to the given 14031 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian 14032 * 14033 * <li><i>julianday</i> - sets the time of this instance according to the given 14034 * Julian Day instance or the Julian Day given as a float 14035 * 14036 * <li><i>cycle</i> - any integer giving the number of 60-year cycle in which the date is located. 14037 * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious 14038 * linear count of years since the beginning of the epoch, much like other calendars. This linear 14039 * count is never used. If both the cycle and year are given, the year is wrapped to the range 0 14040 * to 60 and treated as if it were a year in the regular 60-year cycle. 14041 * 14042 * <li><i>year</i> - any integer, including 0 14043 * 14044 * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc. 14045 * 14046 * <li><i>day</i> - 1 to 31 14047 * 14048 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 14049 * is always done with an unambiguous 24 hour representation 14050 * 14051 * <li><i>minute</i> - 0 to 59 14052 * 14053 * <li><i>second</i> - 0 to 59 14054 * 14055 * <li><i>millisecond</i> - 0 to 999 14056 * 14057 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 14058 * </ul> 14059 * 14060 * If the constructor is called with another Han date instance instead of 14061 * a parameter block, the other instance acts as a parameter block and its 14062 * settings are copied into the current instance.<p> 14063 * 14064 * If the constructor is called with no arguments at all or if none of the 14065 * properties listed above are present, then the RD is calculate based on 14066 * the current date at the time of instantiation. <p> 14067 * 14068 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 14069 * specified in the params, it is assumed that they have the smallest possible 14070 * value in the range for the property (zero or one).<p> 14071 * 14072 * Depends directive: !depends handate.js 14073 * 14074 * @private 14075 * @class 14076 * @constructor 14077 * @extends ilib.Date.RataDie 14078 * @param {Object=} params parameters that govern the settings and behaviour of this Han RD date 14079 */ 14080 ilib.Date.HanRataDie = function(params) { 14081 this.rd = undefined; 14082 if (params && params.cal) { 14083 this.cal = params.cal; 14084 ilib.Date.RataDie.call(this, params); 14085 if (params && typeof(params.callback) === 'function') { 14086 params.callback(this); 14087 } 14088 } else { 14089 new ilib.Cal.Han({ 14090 sync: params && params.sync, 14091 loadParams: params && params.loadParams, 14092 callback: ilib.bind(this, function(c) { 14093 this.cal = c; 14094 ilib.Date.RataDie.call(this, params); 14095 if (params && typeof(params.callback) === 'function') { 14096 params.callback(this); 14097 } 14098 }) 14099 }); 14100 } 14101 }; 14102 14103 ilib.Date.HanRataDie.prototype = new ilib.Date.RataDie(); 14104 ilib.Date.HanRataDie.prototype.parent = ilib.Date.RataDie; 14105 ilib.Date.HanRataDie.prototype.constructor = ilib.Date.HanRataDie; 14106 14107 /** 14108 * The difference between a zero Julian day and the first Han date 14109 * which is February 15, -2636 (Gregorian). 14110 * @private 14111 * @const 14112 * @type number 14113 */ 14114 ilib.Date.HanRataDie.epoch = 758325.5; 14115 14116 /** 14117 * Calculate the Rata Die (fixed day) number of the given date from the 14118 * date components. 14119 * 14120 * @protected 14121 * @param {Object} date the date components to calculate the RD from 14122 */ 14123 ilib.Date.HanRataDie.prototype._setDateComponents = function(date) { 14124 var calc = ilib.Cal.Han._leapYearCalc(date.year, date.cycle); 14125 var m2 = ilib.Cal.Han._newMoonOnOrAfter(calc.m1+1); 14126 var newYears; 14127 this.leapYear = (Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12); 14128 if (this.leapYear && (ilib.Cal.Han._noMajorST(calc.m1) || ilib.Cal.Han._noMajorST(m2)) ) { 14129 newYears = ilib.Cal.Han._newMoonOnOrAfter(m2+1); 14130 } else { 14131 newYears = m2; 14132 } 14133 14134 var priorNewMoon = ilib.Cal.Han._newMoonOnOrAfter(calc.m1 + date.month * 29); // this is a julian day 14135 this.priorLeapMonth = ilib.Date.HanDate._priorLeapMonth(newYears, ilib.Cal.Han._newMoonBefore(priorNewMoon)); 14136 this.leapMonth = (this.leapYear && ilib.Cal.Han._noMajorST(priorNewMoon) && !this.priorLeapMonth); 14137 14138 var rdtime = (date.hour * 3600000 + 14139 date.minute * 60000 + 14140 date.second * 1000 + 14141 date.millisecond) / 14142 86400000; 14143 14144 /* 14145 console.log("getRataDie: converting " + JSON.stringify(date) + " to an RD"); 14146 console.log("getRataDie: year is " + date.year + " plus cycle " + date.cycle); 14147 console.log("getRataDie: isLeapYear is " + this.leapYear); 14148 console.log("getRataDie: priorNewMoon is " + priorNewMoon); 14149 console.log("getRataDie: day in month is " + date.day); 14150 console.log("getRataDie: rdtime is " + rdtime); 14151 console.log("getRataDie: rd is " + (priorNewMoon + date.day - 1 + rdtime)); 14152 */ 14153 14154 this.rd = priorNewMoon + date.day - 1 + rdtime - ilib.Date.RataDie.gregorianEpoch; 14155 }; 14156 14157 /** 14158 * Return the rd number of the particular day of the week on or before the 14159 * given rd. eg. The Sunday on or before the given rd. 14160 * @private 14161 * @param {number} rd the rata die date of the reference date 14162 * @param {number} dayOfWeek the day of the week that is being sought relative 14163 * to the current date 14164 * @return {number} the rd of the day of the week 14165 */ 14166 ilib.Date.HanRataDie.prototype._onOrBefore = function(rd, dayOfWeek) { 14167 return rd - ilib.mod(Math.floor(rd) - dayOfWeek, 7); 14168 }; 14169 14170 /** 14171 * @class 14172 * 14173 * Construct a new Han date object. The constructor parameters can 14174 * contain any of the following properties: 14175 * 14176 * <ul> 14177 * <li><i>unixtime<i> - sets the time of this instance according to the given 14178 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian 14179 * 14180 * <li><i>julianday</i> - sets the time of this instance according to the given 14181 * Julian Day instance or the Julian Day given as a float 14182 * 14183 * <li><i>cycle</i> - any integer giving the number of 60-year cycle in which the date is located. 14184 * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious 14185 * linear count of years since the beginning of the epoch, much like other calendars. This linear 14186 * count is never used. If both the cycle and year are given, the year is wrapped to the range 0 14187 * to 60 and treated as if it were a year in the regular 60-year cycle. 14188 * 14189 * <li><i>year</i> - any integer, including 0 14190 * 14191 * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc. 14192 * 14193 * <li><i>day</i> - 1 to 31 14194 * 14195 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 14196 * is always done with an unambiguous 24 hour representation 14197 * 14198 * <li><i>minute</i> - 0 to 59 14199 * 14200 * <li><i>second</i> - 0 to 59 14201 * 14202 * <li><i>millisecond</i> - 0 to 999 14203 * 14204 * <li><i>timezone</i> - the ilib.TimeZone instance or time zone name as a string 14205 * of this han date. The date/time is kept in the local time. The time zone 14206 * is used later if this date is formatted according to a different time zone and 14207 * the difference has to be calculated, or when the date format has a time zone 14208 * component in it. 14209 * 14210 * <li><i>locale</i> - locale for this han date. If the time zone is not 14211 * given, it can be inferred from this locale. For locales that span multiple 14212 * time zones, the one with the largest population is chosen as the one that 14213 * represents the locale. 14214 * 14215 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 14216 * </ul> 14217 * 14218 * If the constructor is called with another Han date instance instead of 14219 * a parameter block, the other instance acts as a parameter block and its 14220 * settings are copied into the current instance.<p> 14221 * 14222 * If the constructor is called with no arguments at all or if none of the 14223 * properties listed above 14224 * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 14225 * components are 14226 * filled in with the current date at the time of instantiation. Note that if 14227 * you do not give the time zone when defaulting to the current time and the 14228 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 14229 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 14230 * Mean Time").<p> 14231 * 14232 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 14233 * specified in the params, it is assumed that they have the smallest possible 14234 * value in the range for the property (zero or one).<p> 14235 * 14236 * Depends directive: !depends handate.js 14237 * 14238 * @constructor 14239 * @extends ilib.Date 14240 * @param {Object=} params parameters that govern the settings and behaviour of this Han date 14241 */ 14242 ilib.Date.HanDate = function(params) { 14243 this.timezone = "local"; 14244 if (params) { 14245 if (params.locale) { 14246 this.locale = (typeof(params.locale) === 'string') ? new ilib.Locale(params.locale) : params.locale; 14247 var li = new ilib.LocaleInfo(this.locale); 14248 this.timezone = li.getTimeZone(); 14249 } 14250 if (params.timezone) { 14251 this.timezone = params.timezone; 14252 } 14253 } 14254 14255 new ilib.Cal.Han({ 14256 sync: params && typeof(params) === 'boolean' ? params.sync : true, 14257 loadParams: params && params.loadParams, 14258 callback: ilib.bind(this, function (cal) { 14259 this.cal = cal; 14260 14261 if (params && (params.year || params.month || params.day || params.hour || 14262 params.minute || params.second || params.millisecond || params.cycle || params.cycleYear)) { 14263 if (typeof(params.cycle) !== 'undefined') { 14264 /** 14265 * Cycle number in the Han calendar. 14266 * @type number 14267 */ 14268 this.cycle = parseInt(params.cycle, 10) || 0; 14269 14270 var year = (typeof(params.year) !== 'undefined' ? parseInt(params.year, 10) : parseInt(params.cycleYear, 10)) || 0; 14271 14272 /** 14273 * Year in the Han calendar. 14274 * @type number 14275 */ 14276 this.year = ilib.Cal.Han._getElapsedYear(year, this.cycle); 14277 } else { 14278 if (typeof(params.year) !== 'undefined') { 14279 this.year = parseInt(params.year, 10) || 0; 14280 this.cycle = Math.floor((this.year - 1) / 60); 14281 } else { 14282 this.year = this.cycle = 0; 14283 } 14284 } 14285 14286 /** 14287 * The month number, ranging from 1 to 13 14288 * @type number 14289 */ 14290 this.month = parseInt(params.month, 10) || 1; 14291 14292 /** 14293 * The day of the month. This ranges from 1 to 30. 14294 * @type number 14295 */ 14296 this.day = parseInt(params.day, 10) || 1; 14297 14298 /** 14299 * The hour of the day. This can be a number from 0 to 23, as times are 14300 * stored unambiguously in the 24-hour clock. 14301 * @type number 14302 */ 14303 this.hour = parseInt(params.hour, 10) || 0; 14304 14305 /** 14306 * The minute of the hours. Ranges from 0 to 59. 14307 * @type number 14308 */ 14309 this.minute = parseInt(params.minute, 10) || 0; 14310 14311 /** 14312 * The second of the minute. Ranges from 0 to 59. 14313 * @type number 14314 */ 14315 this.second = parseInt(params.second, 10) || 0; 14316 14317 /** 14318 * The millisecond of the second. Ranges from 0 to 999. 14319 * @type number 14320 */ 14321 this.millisecond = parseInt(params.millisecond, 10) || 0; 14322 14323 // derived properties 14324 14325 /** 14326 * Year in the cycle of the Han calendar 14327 * @type number 14328 */ 14329 this.cycleYear = ilib.amod(this.year, 60); 14330 14331 /** 14332 * The day of the year. Ranges from 1 to 384. 14333 * @type number 14334 */ 14335 this.dayOfYear = parseInt(params.dayOfYear, 10); 14336 14337 if (typeof(params.dst) === 'boolean') { 14338 this.dst = params.dst; 14339 } 14340 14341 this.newRd({ 14342 cal: this.cal, 14343 cycle: this.cycle, 14344 year: this.year, 14345 month: this.month, 14346 day: this.day, 14347 hour: this.hour, 14348 minute: this.minute, 14349 second: this.second, 14350 millisecond: this.millisecond, 14351 sync: params && typeof(params.sync) === 'boolean' ? params.sync : true, 14352 loadParams: params && params.loadParams, 14353 callback: ilib.bind(this, function (rd) { 14354 if (rd) { 14355 this.rd = rd; 14356 14357 // add the time zone offset to the rd to convert to UTC 14358 if (!this.tz) { 14359 this.tz = new ilib.TimeZone({id: this.timezone}); 14360 } 14361 // getOffsetMillis requires that this.year, this.rd, and this.dst 14362 // are set in order to figure out which time zone rules apply and 14363 // what the offset is at that point in the year 14364 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000; 14365 if (this.offset !== 0) { 14366 this.rd = this.newRd({ 14367 cal: this.cal, 14368 rd: this.rd.getRataDie() - this.offset 14369 }); 14370 this._calcLeap(); 14371 } else { 14372 // re-use the derived properties from the RD calculations 14373 this.leapMonth = this.rd.leapMonth; 14374 this.priorLeapMonth = this.rd.priorLeapMonth; 14375 this.leapYear = this.rd.leapYear; 14376 } 14377 } 14378 14379 if (!this.rd) { 14380 this.rd = this.newRd(ilib.merge(params || {}, { 14381 cal: this.cal 14382 })); 14383 this._calcDateComponents(); 14384 } 14385 14386 if (params && typeof(params.onLoad) === 'function') { 14387 params.onLoad(this); 14388 } 14389 }) 14390 }); 14391 } else { 14392 if (!this.rd) { 14393 this.rd = this.newRd(ilib.merge(params || {}, { 14394 cal: this.cal 14395 })); 14396 this._calcDateComponents(); 14397 } 14398 14399 if (params && typeof(params.onLoad) === 'function') { 14400 params.onLoad(this); 14401 } 14402 } 14403 }) 14404 }); 14405 14406 }; 14407 14408 ilib.Date.HanDate.prototype = new ilib.Date({noinstance: true}); 14409 ilib.Date.HanDate.prototype.parent = ilib.Date; 14410 ilib.Date.HanDate.prototype.constructor = ilib.Date.HanDate; 14411 14412 /** 14413 * Return a new RD for this date type using the given params. 14414 * @protected 14415 * @param {Object=} params the parameters used to create this rata die instance 14416 * @returns {ilib.Date.RataDie} the new RD instance for the given params 14417 */ 14418 ilib.Date.HanDate.prototype.newRd = function (params) { 14419 return new ilib.Date.HanRataDie(params); 14420 }; 14421 14422 /** 14423 * @protected 14424 * @static 14425 * @param {number} jd1 first julian day 14426 * @param {number} jd2 second julian day 14427 * @returns {boolean} true if there is a leap month earlier in the same year 14428 * as the given months 14429 */ 14430 ilib.Date.HanDate._priorLeapMonth = function(jd1, jd2) { 14431 return jd2 >= jd1 && 14432 (ilib.Date.HanDate._priorLeapMonth(jd1, ilib.Cal.Han._newMoonBefore(jd2)) || 14433 ilib.Cal.Han._noMajorST(jd2)); 14434 }; 14435 14436 /** 14437 * Return the year for the given RD 14438 * @protected 14439 * @param {number} rd RD to calculate from 14440 * @returns {number} the year for the RD 14441 */ 14442 ilib.Date.HanDate.prototype._calcYear = function(rd) { 14443 var gregdate = new ilib.Date.GregDate({ 14444 rd: rd, 14445 timezone: this.timezone 14446 }); 14447 var hanyear = gregdate.year + 2697; 14448 var newYears = this.cal.newYears(hanyear); 14449 return hanyear - ((rd + ilib.Date.RataDie.gregorianEpoch < newYears) ? 1 : 0); 14450 }; 14451 14452 /** 14453 * @private 14454 * Calculate the leap year and months from the RD. 14455 */ 14456 ilib.Date.HanDate.prototype._calcLeap = function() { 14457 var jd = this.rd.getRataDie() + ilib.Date.RataDie.gregorianEpoch; 14458 14459 var calc = ilib.Cal.Han._leapYearCalc(this.year); 14460 var m2 = ilib.Cal.Han._newMoonOnOrAfter(calc.m1+1); 14461 this.leapYear = Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12; 14462 14463 var newYears = (this.leapYear && 14464 (ilib.Cal.Han._noMajorST(calc.m1) || ilib.Cal.Han._noMajorST(m2))) ? 14465 ilib.Cal.Han._newMoonOnOrAfter(m2+1) : m2; 14466 14467 var m = ilib.Cal.Han._newMoonBefore(jd + 1); 14468 this.priorLeapMonth = ilib.Date.HanDate._priorLeapMonth(newYears, ilib.Cal.Han._newMoonBefore(m)); 14469 this.leapMonth = (this.leapYear && ilib.Cal.Han._noMajorST(m) && !this.priorLeapMonth); 14470 }; 14471 14472 /** 14473 * @private 14474 * Calculate date components for the given RD date. 14475 */ 14476 ilib.Date.HanDate.prototype._calcDateComponents = function () { 14477 var remainder, 14478 jd = this.rd.getRataDie() + ilib.Date.RataDie.gregorianEpoch; 14479 14480 // console.log("HanDate._calcDateComponents: calculating for jd " + jd); 14481 14482 if (typeof(this.offset) === "undefined") { 14483 // now offset the jd by the time zone, then recalculate in case we were 14484 // near the year boundary 14485 if (!this.tz) { 14486 this.tz = new ilib.TimeZone({id: this.timezone}); 14487 } 14488 this.offset = this.tz.getOffsetMillis(this) / 86400000; 14489 } 14490 14491 if (this.offset !== 0) { 14492 jd += this.offset; 14493 } 14494 14495 // use the Gregorian calendar objects as a convenient way to short-cut some 14496 // of the date calculations 14497 14498 var gregyear = ilib.Date.GregDate._calcYear(this.rd.getRataDie()); 14499 this.year = gregyear + 2697; 14500 var calc = ilib.Cal.Han._leapYearCalc(this.year); 14501 var m2 = ilib.Cal.Han._newMoonOnOrAfter(calc.m1+1); 14502 this.leapYear = Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12; 14503 var newYears = (this.leapYear && 14504 (ilib.Cal.Han._noMajorST(calc.m1) || ilib.Cal.Han._noMajorST(m2))) ? 14505 ilib.Cal.Han._newMoonOnOrAfter(m2+1) : m2; 14506 14507 // See if it's between Jan 1 and the Chinese new years of that Gregorian year. If 14508 // so, then the Han year is actually the previous one 14509 if (jd < newYears) { 14510 this.year--; 14511 calc = ilib.Cal.Han._leapYearCalc(this.year); 14512 m2 = ilib.Cal.Han._newMoonOnOrAfter(calc.m1+1); 14513 this.leapYear = Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12; 14514 newYears = (this.leapYear && 14515 (ilib.Cal.Han._noMajorST(calc.m1) || ilib.Cal.Han._noMajorST(m2))) ? 14516 ilib.Cal.Han._newMoonOnOrAfter(m2+1) : m2; 14517 } 14518 // month is elapsed month, not the month number + leap month boolean 14519 var m = ilib.Cal.Han._newMoonBefore(jd + 1); 14520 this.month = Math.round((m - calc.m1) / 29.530588853000001); 14521 14522 this.priorLeapMonth = ilib.Date.HanDate._priorLeapMonth(newYears, ilib.Cal.Han._newMoonBefore(m)); 14523 this.leapMonth = (this.leapYear && ilib.Cal.Han._noMajorST(m) && !this.priorLeapMonth); 14524 14525 this.cycle = Math.floor((this.year - 1) / 60); 14526 this.cycleYear = ilib.amod(this.year, 60); 14527 this.day = ilib.Date._floorToJD(jd) - m + 1; 14528 14529 /* 14530 console.log("HanDate._calcDateComponents: year is " + this.year); 14531 console.log("HanDate._calcDateComponents: isLeapYear is " + this.leapYear); 14532 console.log("HanDate._calcDateComponents: cycle is " + this.cycle); 14533 console.log("HanDate._calcDateComponents: cycleYear is " + this.cycleYear); 14534 console.log("HanDate._calcDateComponents: month is " + this.month); 14535 console.log("HanDate._calcDateComponents: isLeapMonth is " + this.leapMonth); 14536 console.log("HanDate._calcDateComponents: day is " + this.day); 14537 */ 14538 14539 // floor to the start of the julian day 14540 remainder = jd - ilib.Date._floorToJD(jd); 14541 14542 // console.log("HanDate._calcDateComponents: time remainder is " + remainder); 14543 14544 // now convert to milliseconds for the rest of the calculation 14545 remainder = Math.round(remainder * 86400000); 14546 14547 this.hour = Math.floor(remainder/3600000); 14548 remainder -= this.hour * 3600000; 14549 14550 this.minute = Math.floor(remainder/60000); 14551 remainder -= this.minute * 60000; 14552 14553 this.second = Math.floor(remainder/1000); 14554 remainder -= this.second * 1000; 14555 14556 this.millisecond = remainder; 14557 }; 14558 14559 /** 14560 * Return the year within the Chinese cycle of this date. Cycles are 60 14561 * years long, and the value returned from this method is the number of the year 14562 * within this cycle. The year returned from getYear() is the total elapsed 14563 * years since the beginning of the Chinese epoch and does not include 14564 * the cycles. 14565 * 14566 * @return {number} the year within the current Chinese cycle 14567 */ 14568 ilib.Date.HanDate.prototype.getCycleYears = function() { 14569 return this.cycleYear; 14570 }; 14571 14572 /** 14573 * Return the Chinese cycle number of this date. Cycles are 60 years long, 14574 * and the value returned from getCycleYear() is the number of the year 14575 * within this cycle. The year returned from getYear() is the total elapsed 14576 * years since the beginning of the Chinese epoch and does not include 14577 * the cycles. 14578 * 14579 * @return {number} the current Chinese cycle 14580 */ 14581 ilib.Date.HanDate.prototype.getCycles = function() { 14582 return this.cycle; 14583 }; 14584 14585 /** 14586 * Return whether the year of this date is a leap year in the Chinese Han 14587 * calendar. 14588 * 14589 * @return {boolean} true if the year of this date is a leap year in the 14590 * Chinese Han calendar. 14591 */ 14592 ilib.Date.HanDate.prototype.isLeapYear = function() { 14593 return this.leapYear; 14594 }; 14595 14596 /** 14597 * Return whether the month of this date is a leap month in the Chinese Han 14598 * calendar. 14599 * 14600 * @return {boolean} true if the month of this date is a leap month in the 14601 * Chinese Han calendar. 14602 */ 14603 ilib.Date.HanDate.prototype.isLeapMonth = function() { 14604 return this.leapMonth; 14605 }; 14606 14607 /** 14608 * Return the day of the week of this date. The day of the week is encoded 14609 * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday. 14610 * 14611 * @return {number} the day of the week 14612 */ 14613 ilib.Date.HanDate.prototype.getDayOfWeek = function() { 14614 var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0)); 14615 return ilib.mod(rd, 7); 14616 }; 14617 14618 /** 14619 * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 14620 * 365, regardless of months or weeks, etc. That is, Farvardin 1st is day 1, and 14621 * December 31st is 365 in regular years, or 366 in leap years. 14622 * @return {number} the ordinal day of the year 14623 */ 14624 ilib.Date.HanDate.prototype.getDayOfYear = function() { 14625 var newYears = this.cal.newYears(this.year); 14626 var priorNewMoon = ilib.Cal.Han._newMoonOnOrAfter(newYears + (this.month -1) * 29); 14627 return priorNewMoon - newYears + this.day; 14628 }; 14629 14630 /** 14631 * Return the era for this date as a number. The value for the era for Han 14632 * calendars is -1 for "before the han era" (BP) and 1 for "the han era" (anno 14633 * persico or AP). 14634 * BP dates are any date before Farvardin 1, 1 AP. In the proleptic Han calendar, 14635 * there is a year 0, so any years that are negative or zero are BP. 14636 * @return {number} 1 if this date is in the common era, -1 if it is before the 14637 * common era 14638 */ 14639 ilib.Date.HanDate.prototype.getEra = function() { 14640 return (this.year < 1) ? -1 : 1; 14641 }; 14642 14643 /** 14644 * Return the name of the calendar that governs this date. 14645 * 14646 * @return {string} a string giving the name of the calendar 14647 */ 14648 ilib.Date.HanDate.prototype.getCalendar = function() { 14649 return "han"; 14650 }; 14651 14652 // register with the factory method 14653 ilib.Date._constructors["han"] = ilib.Date.HanDate; 14654 /* 14655 * ethiopic.js - Represent a Ethiopic calendar object. 14656 * 14657 * Copyright © 2015, JEDLSoft 14658 * 14659 * Licensed under the Apache License, Version 2.0 (the "License"); 14660 * you may not use this file except in compliance with the License. 14661 * You may obtain a copy of the License at 14662 * 14663 * http://www.apache.org/licenses/LICENSE-2.0 14664 * 14665 * Unless required by applicable law or agreed to in writing, software 14666 * distributed under the License is distributed on an "AS IS" BASIS, 14667 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14668 * 14669 * See the License for the specific language governing permissions and 14670 * limitations under the License. 14671 */ 14672 14673 14674 /* !depends calendar.js locale.js date.js julianday.js util/utils.js util/math.js */ 14675 14676 /** 14677 * @class 14678 * Construct a new Ethiopic calendar object. This class encodes information about 14679 * a Ethiopic calendar.<p> 14680 * 14681 * Depends directive: !depends ethiopic.js 14682 * 14683 * @constructor 14684 * @implements ilib.Cal 14685 */ 14686 ilib.Cal.Ethiopic = function() { 14687 this.type = "ethiopic"; 14688 }; 14689 14690 /** 14691 * Return the number of months in the given year. The number of months in a year varies 14692 * for lunar calendars because in some years, an extra month is needed to extend the 14693 * days in a year to an entire solar year. The month is represented as a 1-based number 14694 * where 1=Maskaram, 2=Teqemt, etc. until 13=Paguemen. 14695 * 14696 * @param {number} year a year for which the number of months is sought 14697 */ 14698 ilib.Cal.Ethiopic.prototype.getNumMonths = function(year) { 14699 return 13; 14700 }; 14701 14702 /** 14703 * Return the number of days in a particular month in a particular year. This function 14704 * can return a different number for a month depending on the year because of things 14705 * like leap years. 14706 * 14707 * @param {number|string} month the month for which the length is sought 14708 * @param {number} year the year within which that month can be found 14709 * @return {number} the number of days within the given month in the given year 14710 */ 14711 ilib.Cal.Ethiopic.prototype.getMonLength = function(month, year) { 14712 var m = month; 14713 switch (typeof(m)) { 14714 case "string": 14715 m = parseInt(m, 10); 14716 break; 14717 case "function": 14718 case "object": 14719 case "undefined": 14720 return 30; 14721 break; 14722 } 14723 if (m < 13) { 14724 return 30; 14725 } else { 14726 return this.isLeapYear(year) ? 6 : 5; 14727 } 14728 }; 14729 14730 /** 14731 * Return true if the given year is a leap year in the Ethiopic calendar. 14732 * The year parameter may be given as a number, or as a JulDate object. 14733 * @param {number|ilib.Date.JulDate|string} year the year for which the leap year information is being sought 14734 * @return {boolean} true if the given year is a leap year 14735 */ 14736 ilib.Cal.Ethiopic.prototype.isLeapYear = function(year) { 14737 var y = year; 14738 switch (typeof(y)) { 14739 case "string": 14740 y = parseInt(y, 10); 14741 break; 14742 case "object": 14743 if (typeof(y.year) !== "number") { // in case it is an ilib.Date object 14744 return false; 14745 } 14746 y = y.year; 14747 break; 14748 case "function": 14749 case "undefined": 14750 return false; 14751 break; 14752 } 14753 return ilib.mod(y, 4) === 3; 14754 }; 14755 14756 /** 14757 * Return the type of this calendar. 14758 * 14759 * @return {string} the name of the type of this calendar 14760 */ 14761 ilib.Cal.Ethiopic.prototype.getType = function() { 14762 return this.type; 14763 }; 14764 14765 /** 14766 * Return a date instance for this calendar type using the given 14767 * options. 14768 * @param {Object} options options controlling the construction of 14769 * the date instance 14770 * @return {ilib.Date} a date appropriate for this calendar type 14771 */ 14772 ilib.Cal.Ethiopic.prototype.newDateInstance = function (options) { 14773 return new ilib.Date.EthiopicDate(options); 14774 }; 14775 14776 /* register this calendar for the factory method */ 14777 ilib.Cal._constructors["ethiopic"] = ilib.Cal.Ethiopic; 14778 /* 14779 * ethiopicdate.js - Represent a date in the Ethiopic calendar 14780 * 14781 * Copyright © 2015, JEDLSoft 14782 * 14783 * Licensed under the Apache License, Version 2.0 (the "License"); 14784 * you may not use this file except in compliance with the License. 14785 * You may obtain a copy of the License at 14786 * 14787 * http://www.apache.org/licenses/LICENSE-2.0 14788 * 14789 * Unless required by applicable law or agreed to in writing, software 14790 * distributed under the License is distributed on an "AS IS" BASIS, 14791 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14792 * 14793 * See the License for the specific language governing permissions and 14794 * limitations under the License. 14795 */ 14796 14797 /* !depends 14798 date.js 14799 calendar/ethiopic.js 14800 util/utils.js 14801 util/search.js 14802 util/math.js 14803 localeinfo.js 14804 julianday.js 14805 */ 14806 14807 /** 14808 * @class 14809 * Construct a new Ethiopic RD date number object. The constructor parameters can 14810 * contain any of the following properties: 14811 * 14812 * <ul> 14813 * <li><i>unixtime<i> - sets the time of this instance according to the given 14814 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970. 14815 * 14816 * <li><i>julianday</i> - sets the time of this instance according to the given 14817 * Julian Day instance or the Julian Day given as a float 14818 * 14819 * <li><i>year</i> - any integer, including 0 14820 * 14821 * <li><i>month</i> - 1 to 12, where 1 means Maskaram, 2 means Teqemt, etc., and 13 means Paguemen 14822 * 14823 * <li><i>day</i> - 1 to 30 14824 * 14825 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 14826 * is always done with an unambiguous 24 hour representation 14827 * 14828 * <li><i>minute</i> - 0 to 59 14829 * 14830 * <li><i>second</i> - 0 to 59 14831 * 14832 * <li><i>millisecond</i> - 0 to 999 14833 * 14834 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 14835 * </ul> 14836 * 14837 * If the constructor is called with another Ethiopic date instance instead of 14838 * a parameter block, the other instance acts as a parameter block and its 14839 * settings are copied into the current instance.<p> 14840 * 14841 * If the constructor is called with no arguments at all or if none of the 14842 * properties listed above are present, then the RD is calculate based on 14843 * the current date at the time of instantiation. <p> 14844 * 14845 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 14846 * specified in the params, it is assumed that they have the smallest possible 14847 * value in the range for the property (zero or one).<p> 14848 * 14849 * Depends directive: !depends ethiopicdate.js 14850 * 14851 * @private 14852 * @constructor 14853 * @extends ilib.Date.RataDie 14854 * @param {Object=} params parameters that govern the settings and behaviour of this Ethiopic RD date 14855 */ 14856 ilib.Date.EthiopicRataDie = function(params) { 14857 this.cal = params && params.cal || new ilib.Cal.Ethiopic(); 14858 this.rd = undefined; 14859 ilib.Date.RataDie.call(this, params); 14860 }; 14861 14862 ilib.Date.EthiopicRataDie.prototype = new ilib.Date.RataDie(); 14863 ilib.Date.EthiopicRataDie.prototype.parent = ilib.Date.RataDie; 14864 ilib.Date.EthiopicRataDie.prototype.constructor = ilib.Date.EthiopicRataDie; 14865 14866 /** 14867 * The difference between the zero Julian day and the first Ethiopic date 14868 * of Friday, August 29, 8 CE Julian at 6:00am UTC.<p> 14869 * 14870 * See <a href="http://us.wow.com/wiki/Time_in_Ethiopia?s_chn=90&s_pt=aolsem&v_t=aolsem" 14871 * Time in Ethiopia</a> for information about how time is handled in Ethiopia. 14872 * 14873 * @protected 14874 * @type number 14875 */ 14876 ilib.Date.EthiopicRataDie.prototype.epoch = 1724219.75; 14877 14878 /** 14879 * Calculate the Rata Die (fixed day) number of the given date from the 14880 * date components. 14881 * 14882 * @protected 14883 * @param {Object} date the date components to calculate the RD from 14884 */ 14885 ilib.Date.EthiopicRataDie.prototype._setDateComponents = function(date) { 14886 var year = date.year; 14887 var years = 365 * (year - 1) + Math.floor(year/4); 14888 var dayInYear = (date.month-1) * 30 + date.day; 14889 var rdtime = (date.hour * 3600000 + 14890 date.minute * 60000 + 14891 date.second * 1000 + 14892 date.millisecond) / 14893 86400000; 14894 14895 /* 14896 console.log("calcRataDie: converting " + JSON.stringify(parts)); 14897 console.log("getRataDie: year is " + years); 14898 console.log("getRataDie: day in year is " + dayInYear); 14899 console.log("getRataDie: rdtime is " + rdtime); 14900 console.log("getRataDie: rd is " + (years + dayInYear + rdtime)); 14901 */ 14902 14903 this.rd = years + dayInYear + rdtime; 14904 }; 14905 14906 /** 14907 * @class 14908 * Construct a new date object for the Ethiopic Calendar. The constructor can be called 14909 * with a parameter object that contains any of the following properties: 14910 * 14911 * <ul> 14912 * <li><i>unixtime<i> - sets the time of this instance according to the given 14913 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970 (Gregorian). 14914 * <li><i>julianday</i> - the Julian Day to set into this date 14915 * <li><i>year</i> - any integer 14916 * <li><i>month</i> - 1 to 13, where 1 means Maskaram, 2 means Teqemt, etc., and 13 means Paguemen 14917 * <li><i>day</i> - 1 to 30 14918 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 14919 * is always done with an unambiguous 24 hour representation 14920 * <li><i>minute</i> - 0 to 59 14921 * <li><i>second</i> - 0 to 59 14922 * <li><i>millisecond<i> - 0 to 999 14923 * <li><i>locale</i> - the ilib.TimeZone instance or time zone name as a string 14924 * of this ethiopic date. The date/time is kept in the local time. The time zone 14925 * is used later if this date is formatted according to a different time zone and 14926 * the difference has to be calculated, or when the date format has a time zone 14927 * component in it. 14928 * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 14929 * given, it can be inferred from this locale. For locales that span multiple 14930 * time zones, the one with the largest population is chosen as the one that 14931 * represents the locale. 14932 * 14933 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 14934 * </ul> 14935 * 14936 * If called with another Ethiopic date argument, the date components of the given 14937 * date are copied into the current one.<p> 14938 * 14939 * If the constructor is called with no arguments at all or if none of the 14940 * properties listed above 14941 * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 14942 * components are 14943 * filled in with the current date at the time of instantiation. Note that if 14944 * you do not give the time zone when defaulting to the current time and the 14945 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 14946 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 14947 * Mean Time").<p> 14948 * 14949 * Depends directive: !depends ethiopicdate.js 14950 * 14951 * @constructor 14952 * @extends ilib.Date 14953 * @param {Object=} params parameters that govern the settings and behaviour of this Ethiopic date 14954 */ 14955 ilib.Date.EthiopicDate = function(params) { 14956 this.cal = new ilib.Cal.Ethiopic(); 14957 14958 if (params) { 14959 if (typeof(params.noinstance) === 'boolean' && params.noinstance) { 14960 // for doing inheritance, so don't need to fill in the data. The inheriting class only wants the methods. 14961 return; 14962 } 14963 if (params.locale) { 14964 this.locale = (typeof(params.locale) === 'string') ? new ilib.Locale(params.locale) : params.locale; 14965 var li = new ilib.LocaleInfo(this.locale); 14966 this.timezone = li.getTimeZone(); 14967 } 14968 if (params.timezone) { 14969 this.timezone = params.timezone; 14970 } 14971 14972 if (params.year || params.month || params.day || params.hour || 14973 params.minute || params.second || params.millisecond ) { 14974 /** 14975 * Year in the Ethiopic calendar. 14976 * @type number 14977 */ 14978 this.year = parseInt(params.year, 10) || 0; 14979 /** 14980 * The month number, ranging from 1 (Maskaram) to 13 (Paguemen). 14981 * @type number 14982 */ 14983 this.month = parseInt(params.month, 10) || 1; 14984 /** 14985 * The day of the month. This ranges from 1 to 30. 14986 * @type number 14987 */ 14988 this.day = parseInt(params.day, 10) || 1; 14989 /** 14990 * The hour of the day. This can be a number from 0 to 23, as times are 14991 * stored unambiguously in the 24-hour clock. 14992 * @type number 14993 */ 14994 this.hour = parseInt(params.hour, 10) || 0; 14995 /** 14996 * The minute of the hours. Ranges from 0 to 59. 14997 * @type number 14998 */ 14999 this.minute = parseInt(params.minute, 10) || 0; 15000 /** 15001 * The second of the minute. Ranges from 0 to 59. 15002 * @type number 15003 */ 15004 this.second = parseInt(params.second, 10) || 0; 15005 /** 15006 * The millisecond of the second. Ranges from 0 to 999. 15007 * @type number 15008 */ 15009 this.millisecond = parseInt(params.millisecond, 10) || 0; 15010 15011 /** 15012 * The day of the year. Ranges from 1 to 366. 15013 * @type number 15014 */ 15015 this.dayOfYear = parseInt(params.dayOfYear, 10); 15016 15017 if (typeof(params.dst) === 'boolean') { 15018 this.dst = params.dst; 15019 } 15020 15021 this.rd = this.newRd(this); 15022 15023 // add the time zone offset to the rd to convert to UTC 15024 if (!this.tz) { 15025 this.tz = new ilib.TimeZone({id: this.timezone}); 15026 } 15027 // getOffsetMillis requires that this.year, this.rd, and this.dst 15028 // are set in order to figure out which time zone rules apply and 15029 // what the offset is at that point in the year 15030 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000; 15031 if (this.offset !== 0) { 15032 this.rd = this.newRd({ 15033 rd: this.rd.getRataDie() - this.offset 15034 }); 15035 } 15036 } 15037 } 15038 15039 if (!this.rd) { 15040 this.rd = this.newRd(params); 15041 this._calcDateComponents(); 15042 } 15043 }; 15044 15045 ilib.Date.EthiopicDate.prototype = new ilib.Date({ noinstance: true }); 15046 ilib.Date.EthiopicDate.prototype.parent = ilib.Date; 15047 ilib.Date.EthiopicDate.prototype.constructor = ilib.Date.EthiopicDate; 15048 15049 /** 15050 * Return a new RD for this date type using the given params. 15051 * @protected 15052 * @param {Object=} params the parameters used to create this rata die instance 15053 * @returns {ilib.Date.RataDie} the new RD instance for the given params 15054 */ 15055 ilib.Date.EthiopicDate.prototype.newRd = function (params) { 15056 return new ilib.Date.EthiopicRataDie(params); 15057 }; 15058 15059 /** 15060 * Return the year for the given RD 15061 * @protected 15062 * @param {number} rd RD to calculate from 15063 * @returns {number} the year for the RD 15064 */ 15065 ilib.Date.EthiopicDate.prototype._calcYear = function(rd) { 15066 var year = Math.floor((4*(Math.floor(rd)-1) + 1463)/1461); 15067 15068 return year; 15069 }; 15070 15071 /** 15072 * Calculate date components for the given RD date. 15073 * @protected 15074 */ 15075 ilib.Date.EthiopicDate.prototype._calcDateComponents = function () { 15076 var remainder, 15077 cumulative, 15078 rd = this.rd.getRataDie(); 15079 15080 this.year = this._calcYear(rd); 15081 15082 if (typeof(this.offset) === "undefined") { 15083 this.year = this._calcYear(rd); 15084 15085 // now offset the RD by the time zone, then recalculate in case we were 15086 // near the year boundary 15087 if (!this.tz) { 15088 this.tz = new ilib.TimeZone({id: this.timezone}); 15089 } 15090 this.offset = this.tz.getOffsetMillis(this) / 86400000; 15091 } 15092 15093 if (this.offset !== 0) { 15094 rd += this.offset; 15095 this.year = this._calcYear(rd); 15096 } 15097 15098 var jan1 = this.newRd({ 15099 year: this.year, 15100 month: 1, 15101 day: 1, 15102 hour: 0, 15103 minute: 0, 15104 second: 0, 15105 millisecond: 0 15106 }); 15107 remainder = rd + 1 - jan1.getRataDie(); 15108 15109 this.month = Math.floor((remainder-1)/30) + 1; 15110 remainder = remainder - (this.month-1) * 30; 15111 15112 this.day = Math.floor(remainder); 15113 remainder -= this.day; 15114 // now convert to milliseconds for the rest of the calculation 15115 remainder = Math.round(remainder * 86400000); 15116 15117 this.hour = Math.floor(remainder/3600000); 15118 remainder -= this.hour * 3600000; 15119 15120 this.minute = Math.floor(remainder/60000); 15121 remainder -= this.minute * 60000; 15122 15123 this.second = Math.floor(remainder/1000); 15124 remainder -= this.second * 1000; 15125 15126 this.millisecond = remainder; 15127 }; 15128 15129 /** 15130 * Return the day of the week of this date. The day of the week is encoded 15131 * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday. 15132 * 15133 * @return {number} the day of the week 15134 */ 15135 ilib.Date.EthiopicDate.prototype.getDayOfWeek = function() { 15136 var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0)); 15137 return ilib.mod(rd-4, 7); 15138 }; 15139 15140 /** 15141 * Return the name of the calendar that governs this date. 15142 * 15143 * @return {string} a string giving the name of the calendar 15144 */ 15145 ilib.Date.EthiopicDate.prototype.getCalendar = function() { 15146 return "ethiopic"; 15147 }; 15148 15149 //register with the factory method 15150 ilib.Date._constructors["ethiopic"] = ilib.Date.EthiopicDate; 15151 /* 15152 * coptic.js - Represent a Coptic calendar object. 15153 * 15154 * Copyright © 2015, JEDLSoft 15155 * 15156 * Licensed under the Apache License, Version 2.0 (the "License"); 15157 * you may not use this file except in compliance with the License. 15158 * You may obtain a copy of the License at 15159 * 15160 * http://www.apache.org/licenses/LICENSE-2.0 15161 * 15162 * Unless required by applicable law or agreed to in writing, software 15163 * distributed under the License is distributed on an "AS IS" BASIS, 15164 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15165 * 15166 * See the License for the specific language governing permissions and 15167 * limitations under the License. 15168 */ 15169 15170 15171 /* !depends calendar.js locale.js date.js julianday.js util/utils.js util/math.js calendar/ethiopic.js */ 15172 15173 /** 15174 * @class 15175 * Construct a new Coptic calendar object. This class encodes information about 15176 * a Coptic calendar.<p> 15177 * 15178 * Depends directive: !depends coptic.js 15179 * 15180 * @constructor 15181 * @implements ilib.Cal 15182 */ 15183 ilib.Cal.Coptic = function() { 15184 this.type = "coptic"; 15185 }; 15186 15187 ilib.Cal.Coptic.prototype = new ilib.Cal.Ethiopic(); 15188 ilib.Cal.Coptic.prototype.parent = ilib.Cal.Coptic.prototype; 15189 ilib.Cal.Coptic.prototype.constructor = ilib.Cal.Coptic; 15190 15191 /** 15192 * Return a date instance for this calendar type using the given 15193 * options. 15194 * @param {Object} options options controlling the construction of 15195 * the date instance 15196 * @return {ilib.Date} a date appropriate for this calendar type 15197 */ 15198 ilib.Cal.Coptic.prototype.newDateInstance = function (options) { 15199 return new ilib.Date.CopticDate(options); 15200 }; 15201 15202 /* register this calendar for the factory method */ 15203 ilib.Cal._constructors["coptic"] = ilib.Cal.Coptic; 15204 /* 15205 * copticdate.js - Represent a date in the Coptic calendar 15206 * 15207 * Copyright © 2015, JEDLSoft 15208 * 15209 * Licensed under the Apache License, Version 2.0 (the "License"); 15210 * you may not use this file except in compliance with the License. 15211 * You may obtain a copy of the License at 15212 * 15213 * http://www.apache.org/licenses/LICENSE-2.0 15214 * 15215 * Unless required by applicable law or agreed to in writing, software 15216 * distributed under the License is distributed on an "AS IS" BASIS, 15217 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15218 * 15219 * See the License for the specific language governing permissions and 15220 * limitations under the License. 15221 */ 15222 15223 /* !depends 15224 date.js 15225 calendar/coptic.js 15226 util/utils.js 15227 util/search.js 15228 util/math.js 15229 localeinfo.js 15230 julianday.js 15231 calendar/ethiopicdate.js 15232 */ 15233 15234 /** 15235 * @class 15236 * Construct a new Coptic RD date number object. The constructor parameters can 15237 * contain any of the following properties: 15238 * 15239 * <ul> 15240 * <li><i>unixtime<i> - sets the time of this instance according to the given 15241 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970. 15242 * 15243 * <li><i>julianday</i> - sets the time of this instance according to the given 15244 * Julian Day instance or the Julian Day given as a float 15245 * 15246 * <li><i>year</i> - any integer, including 0 15247 * 15248 * <li><i>month</i> - 1 to 13, where 1 means Thoout, 2 means Paope, etc., and 13 means Epagomene 15249 * 15250 * <li><i>day</i> - 1 to 30 15251 * 15252 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 15253 * is always done with an unambiguous 24 hour representation 15254 * 15255 * <li><i>minute</i> - 0 to 59 15256 * 15257 * <li><i>second</i> - 0 to 59 15258 * 15259 * <li><i>millisecond</i> - 0 to 999 15260 * 15261 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 15262 * </ul> 15263 * 15264 * If the constructor is called with another Coptic date instance instead of 15265 * a parameter block, the other instance acts as a parameter block and its 15266 * settings are copied into the current instance.<p> 15267 * 15268 * If the constructor is called with no arguments at all or if none of the 15269 * properties listed above are present, then the RD is calculate based on 15270 * the current date at the time of instantiation. <p> 15271 * 15272 * If any of the properties from <i>year</i> through <i>millisecond</i> are not 15273 * specified in the params, it is assumed that they have the smallest possible 15274 * value in the range for the property (zero or one).<p> 15275 * 15276 * Depends directive: !depends copticdate.js 15277 * 15278 * @private 15279 * @constructor 15280 * @extends ilib.Date.EthiopicRataDie 15281 * @param {Object=} params parameters that govern the settings and behaviour of this Coptic RD date 15282 */ 15283 ilib.Date.CopticRataDie = function(params) { 15284 this.cal = params && params.cal || new ilib.Cal.Coptic(); 15285 this.rd = undefined; 15286 /** 15287 * The difference between the zero Julian day and the first Coptic date 15288 * of Friday, August 29, 284 CE Julian at 7:00am UTC. 15289 * @private 15290 * @const 15291 * @type number 15292 */ 15293 this.epoch = 1825028.5; 15294 15295 var tmp = {}; 15296 if (params) { 15297 ilib.shallowCopy(params, tmp); 15298 } 15299 tmp.cal = this.cal; // override the cal parameter that may be passed in 15300 ilib.Date.EthiopicRataDie.call(this, tmp); 15301 }; 15302 15303 ilib.Date.CopticRataDie.prototype = new ilib.Date.EthiopicRataDie(); 15304 ilib.Date.CopticRataDie.prototype.parent = ilib.Date.EthiopicRataDie; 15305 ilib.Date.CopticRataDie.prototype.constructor = ilib.Date.CopticRataDie; 15306 15307 /** 15308 * @class 15309 * Construct a new date object for the Coptic Calendar. The constructor can be called 15310 * with a parameter object that contains any of the following properties: 15311 * 15312 * <ul> 15313 * <li><i>unixtime<i> - sets the time of this instance according to the given 15314 * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970 (Gregorian). 15315 * <li><i>julianday</i> - the Julian Day to set into this date 15316 * <li><i>year</i> - any integer 15317 * <li><i>month</i> - 1 to 13, where 1 means Thoout, 2 means Paope, etc., and 13 means Epagomene 15318 * <li><i>day</i> - 1 to 30 15319 * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 15320 * is always done with an unambiguous 24 hour representation 15321 * <li><i>minute</i> - 0 to 59 15322 * <li><i>second</i> - 0 to 59 15323 * <li><i>millisecond<i> - 0 to 999 15324 * <li><i>locale</i> - the ilib.TimeZone instance or time zone name as a string 15325 * of this coptic date. The date/time is kept in the local time. The time zone 15326 * is used later if this date is formatted according to a different time zone and 15327 * the difference has to be calculated, or when the date format has a time zone 15328 * component in it. 15329 * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 15330 * given, it can be inferred from this locale. For locales that span multiple 15331 * time zones, the one with the largest population is chosen as the one that 15332 * represents the locale. 15333 * 15334 * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one. 15335 * </ul> 15336 * 15337 * If called with another Coptic date argument, the date components of the given 15338 * date are copied into the current one.<p> 15339 * 15340 * If the constructor is called with no arguments at all or if none of the 15341 * properties listed above 15342 * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 15343 * components are 15344 * filled in with the current date at the time of instantiation. Note that if 15345 * you do not give the time zone when defaulting to the current time and the 15346 * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the 15347 * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 15348 * Mean Time").<p> 15349 * 15350 * Depends directive: !depends copticdate.js 15351 * 15352 * @constructor 15353 * @extends ilib.Date.EthiopicDate 15354 * @param {Object=} params parameters that govern the settings and behaviour of this Coptic date 15355 */ 15356 ilib.Date.CopticDate = function(params) { 15357 this.rd = undefined; // clear these out so that the EthiopicDate constructor can set it 15358 ilib.Date.EthiopicDate.call(this, params); 15359 this.cal = new ilib.Cal.Coptic(); 15360 }; 15361 15362 ilib.Date.CopticDate.prototype = new ilib.Date.EthiopicDate({noinstance: true}); 15363 ilib.Date.CopticDate.prototype.parent = ilib.Date.EthiopicDate.prototype; 15364 ilib.Date.CopticDate.prototype.constructor = ilib.Date.CopticDate; 15365 15366 /** 15367 * Return a new RD for this date type using the given params. 15368 * @protected 15369 * @param {Object=} params the parameters used to create this rata die instance 15370 * @returns {ilib.Date.RataDie} the new RD instance for the given params 15371 */ 15372 ilib.Date.CopticDate.prototype.newRd = function (params) { 15373 return new ilib.Date.CopticRataDie(params); 15374 }; 15375 15376 /** 15377 * Return the day of the week of this date. The day of the week is encoded 15378 * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday. 15379 * 15380 * @return {number} the day of the week 15381 */ 15382 ilib.Date.CopticDate.prototype.getDayOfWeek = function() { 15383 var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0)); 15384 return ilib.mod(rd-3, 7); 15385 }; 15386 15387 /** 15388 * Return the name of the calendar that governs this date. 15389 * 15390 * @return {string} a string giving the name of the calendar 15391 */ 15392 ilib.Date.CopticDate.prototype.getCalendar = function() { 15393 return "coptic"; 15394 }; 15395 15396 //register with the factory method 15397 ilib.Date._constructors["coptic"] = ilib.Date.CopticDate; 15398 /* 15399 * ctype.js - Character type definitions 15400 * 15401 * Copyright © 2012-2014, JEDLSoft 15402 * 15403 * Licensed under the Apache License, Version 2.0 (the "License"); 15404 * you may not use this file except in compliance with the License. 15405 * You may obtain a copy of the License at 15406 * 15407 * http://www.apache.org/licenses/LICENSE-2.0 15408 * 15409 * Unless required by applicable law or agreed to in writing, software 15410 * distributed under the License is distributed on an "AS IS" BASIS, 15411 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15412 * 15413 * See the License for the specific language governing permissions and 15414 * limitations under the License. 15415 */ 15416 15417 // !depends ilibglobal.js locale.js util/search.js 15418 15419 // !data ctype 15420 15421 /** 15422 * Provides a set of static routines that return information about characters. 15423 * These routines emulate the C-library ctype functions. The characters must be 15424 * encoded in utf-16, as no other charsets are currently supported. Only the first 15425 * character of the given string is tested. 15426 * @namespace 15427 */ 15428 ilib.CType = { 15429 /** 15430 * Actual implementation for withinRange. Searches the given object for ranges. 15431 * The range names are taken from the Unicode range names in 15432 * http://www.unicode.org/Public/UNIDATA/extracted/DerivedGeneralCategory.txt 15433 * 15434 * <ul> 15435 * <li>Cn - Unassigned 15436 * <li>Lu - Uppercase_Letter 15437 * <li>Ll - Lowercase_Letter 15438 * <li>Lt - Titlecase_Letter 15439 * <li>Lm - Modifier_Letter 15440 * <li>Lo - Other_Letter 15441 * <li>Mn - Nonspacing_Mark 15442 * <li>Me - Enclosing_Mark 15443 * <li>Mc - Spacing_Mark 15444 * <li>Nd - Decimal_Number 15445 * <li>Nl - Letter_Number 15446 * <li>No - Other_Number 15447 * <li>Zs - Space_Separator 15448 * <li>Zl - Line_Separator 15449 * <li>Zp - Paragraph_Separator 15450 * <li>Cc - Control 15451 * <li>Cf - Format 15452 * <li>Co - Private_Use 15453 * <li>Cs - Surrogate 15454 * <li>Pd - Dash_Punctuation 15455 * <li>Ps - Open_Punctuation 15456 * <li>Pe - Close_Punctuation 15457 * <li>Pc - Connector_Punctuation 15458 * <li>Po - Other_Punctuation 15459 * <li>Sm - Math_Symbol 15460 * <li>Sc - Currency_Symbol 15461 * <li>Sk - Modifier_Symbol 15462 * <li>So - Other_Symbol 15463 * <li>Pi - Initial_Punctuation 15464 * <li>Pf - Final_Punctuation 15465 * </ul> 15466 * 15467 * @protected 15468 * @param {number} num code point of the character to examine 15469 * @param {string} rangeName the name of the range to check 15470 * @param {Object} obj object containing the character range data 15471 * @return {boolean} true if the first character is within the named 15472 * range 15473 */ 15474 _inRange: function(num, rangeName, obj) { 15475 var range, i; 15476 if (num < 0 || !rangeName || !obj) { 15477 return false; 15478 } 15479 15480 range = obj[rangeName]; 15481 if (!range) { 15482 return false; 15483 } 15484 15485 var compare = function(singlerange, target) { 15486 if (singlerange.length === 1) { 15487 return singlerange[0] - target; 15488 } else { 15489 return target < singlerange[0] ? singlerange[0] - target : 15490 (target > singlerange[1] ? singlerange[1] - target : 0); 15491 } 15492 }; 15493 var result = ilib.bsearch(num, range, compare); 15494 return result < range.length && compare(range[result], num) === 0; 15495 }, 15496 15497 /** 15498 * Return whether or not the first character is within the named range 15499 * of Unicode characters. The valid list of range names are taken from 15500 * the Unicode 6.0 spec. Characters in all ranges of Unicode are supported, 15501 * including those supported in Javascript via UTF-16. Currently, this method 15502 * supports the following range names: 15503 * 15504 * <ul> 15505 * <li><i>ascii</i> - basic ASCII 15506 * <li><i>latin</i> - Latin, Latin Extended Additional, Latin Extended-C, Latin Extended-D 15507 * <li><i>armenian</i> 15508 * <li><i>greek</i> - Greek, Greek Extended 15509 * <li><i>cyrillic</i> - Cyrillic, Cyrillic Extended-A, Cyrillic Extended-B 15510 * <li><i>georgian</i> - Georgian, Georgian Supplement 15511 * <li><i>glagolitic</i> 15512 * <li><i>gothic</i> 15513 * <li><i>ogham</i> 15514 * <li><i>oldpersian</i> 15515 * <li><i>runic</i> 15516 * <li><i>ipa</i> - IPA, Phonetic Extensions, Phonetic Extensions Supplement 15517 * <li><i>phonetic</i> 15518 * <li><i>modifiertone</i> - Modifier Tone Letters 15519 * <li><i>spacing</i> 15520 * <li><i>diacritics</i> 15521 * <li><i>halfmarks</i> - Combining Half Marks 15522 * <li><i>small</i> - Small Form Variants 15523 * <li><i>bamum</i> - Bamum, Bamum Supplement 15524 * <li><i>ethiopic</i> - Ethiopic, Ethiopic Extended, Ethiopic Extended-A 15525 * <li><i>nko</i> 15526 * <li><i>osmanya</i> 15527 * <li><i>tifinagh</i> 15528 * <li><i>val</i> 15529 * <li><i>arabic</i> - Arabic, Arabic Supplement, Arabic Presentation Forms-A, 15530 * Arabic Presentation Forms-B 15531 * <li><i>carlan</i> 15532 * <li><i>hebrew</i> 15533 * <li><i>mandaic</i> 15534 * <li><i>samaritan</i> 15535 * <li><i>syriac</i> 15536 * <li><i>mongolian</i> 15537 * <li><i>phagspa</i> 15538 * <li><i>tibetan</i> 15539 * <li><i>bengali</i> 15540 * <li><i>devanagari</i> - Devanagari, Devanagari Extended 15541 * <li><i>gujarati</i> 15542 * <li><i>gurmukhi</i> 15543 * <li><i>kannada</i> 15544 * <li><i>lepcha</i> 15545 * <li><i>limbu</i> 15546 * <li><i>malayalam</i> 15547 * <li><i>meetaimayek</i> 15548 * <li><i>olchiki</i> 15549 * <li><i>oriya</i> 15550 * <li><i>saurashtra</i> 15551 * <li><i>sinhala</i> 15552 * <li><i>sylotinagri</i> - Syloti Nagri 15553 * <li><i>tamil</i> 15554 * <li><i>telugu</i> 15555 * <li><i>thaana</i> 15556 * <li><i>vedic</i> 15557 * <li><i>batak</i> 15558 * <li><i>balinese</i> 15559 * <li><i>buginese</i> 15560 * <li><i>cham</i> 15561 * <li><i>javanese</i> 15562 * <li><i>kayahli</i> 15563 * <li><i>khmer</i> 15564 * <li><i>lao</i> 15565 * <li><i>myanmar</i> - Myanmar, Myanmar Extended-A 15566 * <li><i>newtailue</i> 15567 * <li><i>rejang</i> 15568 * <li><i>sundanese</i> 15569 * <li><i>taile</i> 15570 * <li><i>taitham</i> 15571 * <li><i>taiviet</i> 15572 * <li><i>thai</i> 15573 * <li><i>buhld</i> 15574 * <li><i>hanunoo</i> 15575 * <li><i>tagalog</i> 15576 * <li><i>tagbanwa</i> 15577 * <li><i>bopomofo</i> - Bopomofo, Bopomofo Extended 15578 * <li><i>cjk</i> - the CJK unified ideographs (Han), CJK Unified Ideographs 15579 * Extension A, CJK Unified Ideographs Extension B, CJK Unified Ideographs 15580 * Extension C, CJK Unified Ideographs Extension D, Ideographic Description 15581 * Characters (=isIdeo()) 15582 * <li><i>cjkcompatibility</i> - CJK Compatibility, CJK Compatibility 15583 * Ideographs, CJK Compatibility Forms, CJK Compatibility Ideographs Supplement 15584 * <li><i>cjkradicals</i> - the CJK radicals, KangXi radicals 15585 * <li><i>hangul</i> - Hangul Jamo, Hangul Syllables, Hangul Jamo Extended-A, 15586 * Hangul Jamo Extended-B, Hangul Compatibility Jamo 15587 * <li><i>cjkpunct</i> - CJK symbols and punctuation 15588 * <li><i>cjkstrokes</i> - CJK strokes 15589 * <li><i>hiragana</i> 15590 * <li><i>katakana</i> - Katakana, Katakana Phonetic Extensions, Kana Supplement 15591 * <li><i>kanbun</i> 15592 * <li><i>lisu</i> 15593 * <li><i>yi</i> - Yi Syllables, Yi Radicals 15594 * <li><i>cherokee</i> 15595 * <li><i>canadian</i> - Unified Canadian Aboriginal Syllabics, Unified Canadian 15596 * Aboriginal Syllabics Extended 15597 * <li><i>presentation</i> - Alphabetic presentation forms 15598 * <li><i>vertical</i> - Vertical Forms 15599 * <li><i>width</i> - Halfwidth and Fullwidth Forms 15600 * <li><i>punctuation</i> - General punctuation, Supplemental Punctuation 15601 * <li><i>box</i> - Box Drawing 15602 * <li><i>block</i> - Block Elements 15603 * <li><i>letterlike</i> - Letterlike symbols 15604 * <li><i>mathematical</i> - Mathematical alphanumeric symbols, Miscellaneous 15605 * Mathematical Symbols-A, Miscellaneous Mathematical Symbols-B 15606 * <li><i>enclosedalpha</i> - Enclosed alphanumerics, Enclosed Alphanumeric Supplement 15607 * <li><i>enclosedcjk</i> - Enclosed CJK letters and months, Enclosed Ideographic Supplement 15608 * <li><i>cjkcompatibility</i> - CJK compatibility 15609 * <li><i>apl</i> - APL symbols 15610 * <li><i>controlpictures</i> - Control pictures 15611 * <li><i>misc</i> - Miscellaneous technical 15612 * <li><i>ocr</i> - Optical character recognition (OCR) 15613 * <li><i>combining</i> - Combining Diacritical Marks, Combining Diacritical Marks 15614 * for Symbols, Combining Diacritical Marks Supplement 15615 * <li><i>digits</i> - ASCII digits (=isDigit()) 15616 * <li><i>indicnumber</i> - Common Indic Number Forms 15617 * <li><i>numbers</i> - Number dorms 15618 * <li><i>supersub</i> - Super- and subscripts 15619 * <li><i>arrows</i> - Arrows, Miscellaneous Symbols and Arrows, Supplemental Arrows-A, 15620 * Supplemental Arrows-B 15621 * <li><i>operators</i> - Mathematical operators, supplemental 15622 * mathematical operators 15623 * <li><i>geometric</i> - Geometric shapes 15624 * <li><i>ancient</i> - Ancient symbols 15625 * <li><i>braille</i> - Braille patterns 15626 * <li><i>currency</i> - Currency symbols 15627 * <li><i>dingbats</i> 15628 * <li><i>gamesymbols</i> 15629 * <li><i>yijing</i> - Yijing Hexagram Symbols 15630 * <li><i>specials</i> 15631 * <li><i>variations</i> - Variation Selectors, Variation Selectors Supplement 15632 * <li><i>privateuse</i> - Private Use Area, Supplementary Private Use Area-A, 15633 * Supplementary Private Use Area-B 15634 * <li><i>supplementarya</i> - Supplementary private use area-A 15635 * <li><i>supplementaryb</i> - Supplementary private use area-B 15636 * <li><i>highsurrogates</i> - High Surrogates, High Private Use Surrogates 15637 * <li><i>lowsurrogates</i> 15638 * <li><i>reserved</i> 15639 * <li><i>noncharacters</i> 15640 * </ul><p> 15641 * 15642 * Depends directive: !depends ctype.js 15643 * 15644 * @param {string|ilib.String|number} ch character or code point to examine 15645 * @param {string} rangeName the name of the range to check 15646 * @return {boolean} true if the first character is within the named 15647 * range 15648 */ 15649 withinRange: function(ch, rangeName) { 15650 if (!rangeName) { 15651 return false; 15652 } 15653 var num; 15654 switch (typeof(ch)) { 15655 case 'number': 15656 num = ch; 15657 break; 15658 case 'string': 15659 num = ilib.String.toCodePoint(ch, 0); 15660 break; 15661 case 'undefined': 15662 return false; 15663 default: 15664 num = ch._toCodePoint(0); 15665 break; 15666 } 15667 15668 return ilib.CType._inRange(num, rangeName.toLowerCase(), ilib.data.ctype); 15669 }, 15670 15671 /** 15672 * @protected 15673 * @param {boolean} sync 15674 * @param {Object} loadParams 15675 * @param {function(*)|undefined} onLoad 15676 */ 15677 _init: function(sync, loadParams, onLoad) { 15678 ilib.CType._load("ctype", sync, loadParams, onLoad); 15679 }, 15680 15681 /** 15682 * @protected 15683 * @param {string} name 15684 * @param {boolean} sync 15685 * @param {Object} loadParams 15686 * @param {function(*)|undefined} onLoad 15687 */ 15688 _load: function (name, sync, loadParams, onLoad) { 15689 if (!ilib.data[name]) { 15690 var loadName = name ? name + ".json" : "ctype.json"; 15691 ilib.loadData({ 15692 name: loadName, 15693 locale: "-", 15694 nonlocale: true, 15695 sync: sync, 15696 loadParams: loadParams, 15697 callback: /** @type function(Object=):undefined */ ilib.bind(this, /** @type function() */ function(ct) { 15698 ilib.data[name] = ct; 15699 if (onLoad && typeof(onLoad) === 'function') { 15700 onLoad(ilib.data[name]); 15701 } 15702 }) 15703 }); 15704 } else { 15705 if (onLoad && typeof(onLoad) === 'function') { 15706 onLoad(ilib.data[name]); 15707 } 15708 } 15709 } 15710 }; 15711 15712 /* 15713 * ctype.isdigit.js - Character type is digit 15714 * 15715 * Copyright © 2012-2013, JEDLSoft 15716 * 15717 * Licensed under the Apache License, Version 2.0 (the "License"); 15718 * you may not use this file except in compliance with the License. 15719 * You may obtain a copy of the License at 15720 * 15721 * http://www.apache.org/licenses/LICENSE-2.0 15722 * 15723 * Unless required by applicable law or agreed to in writing, software 15724 * distributed under the License is distributed on an "AS IS" BASIS, 15725 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15726 * 15727 * See the License for the specific language governing permissions and 15728 * limitations under the License. 15729 */ 15730 15731 // !depends ctype.js 15732 15733 // !data ctype 15734 15735 /** 15736 * Return whether or not the first character is a digit character in the 15737 * Latin script.<p> 15738 * 15739 * Depends directive: !depends ctype.isdigit.js 15740 * 15741 * @param {string|ilib.String|number} ch character or code point to examine 15742 * @return {boolean} true if the first character is a digit character in the 15743 * Latin script. 15744 */ 15745 ilib.CType.isDigit = function (ch) { 15746 var num; 15747 switch (typeof(ch)) { 15748 case 'number': 15749 num = ch; 15750 break; 15751 case 'string': 15752 num = ilib.String.toCodePoint(ch, 0); 15753 break; 15754 case 'undefined': 15755 return false; 15756 default: 15757 num = ch._toCodePoint(0); 15758 break; 15759 } 15760 return ilib.CType._inRange(num, 'digit', ilib.data.ctype); 15761 }; 15762 15763 /** 15764 * @protected 15765 * @param {boolean} sync 15766 * @param {Object} loadParams 15767 * @param {function(*)|undefined} onLoad 15768 */ 15769 ilib.CType.isDigit._init = function (sync, loadParams, onLoad) { 15770 ilib.CType._init(sync, loadParams, onLoad); 15771 }; 15772 15773 /* 15774 * ctype.isspace.js - Character type is space char 15775 * 15776 * Copyright © 2012-2013, JEDLSoft 15777 * 15778 * Licensed under the Apache License, Version 2.0 (the "License"); 15779 * you may not use this file except in compliance with the License. 15780 * You may obtain a copy of the License at 15781 * 15782 * http://www.apache.org/licenses/LICENSE-2.0 15783 * 15784 * Unless required by applicable law or agreed to in writing, software 15785 * distributed under the License is distributed on an "AS IS" BASIS, 15786 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15787 * 15788 * See the License for the specific language governing permissions and 15789 * limitations under the License. 15790 */ 15791 15792 // !depends ctype.js 15793 15794 // !data ctype ctype_z 15795 15796 /** 15797 * Return whether or not the first character is a whitespace character.<p> 15798 * 15799 * Depends directive: !depends ctype.isspace.js 15800 * 15801 * @param {string|ilib.String|number} ch character or code point to examine 15802 * @return {boolean} true if the first character is a whitespace character. 15803 */ 15804 ilib.CType.isSpace = function (ch) { 15805 var num; 15806 switch (typeof(ch)) { 15807 case 'number': 15808 num = ch; 15809 break; 15810 case 'string': 15811 num = ilib.String.toCodePoint(ch, 0); 15812 break; 15813 case 'undefined': 15814 return false; 15815 default: 15816 num = ch._toCodePoint(0); 15817 break; 15818 } 15819 15820 return ilib.CType._inRange(num, 'space', ilib.data.ctype) || 15821 ilib.CType._inRange(num, 'Zs', ilib.data.ctype_z) || 15822 ilib.CType._inRange(num, 'Zl', ilib.data.ctype_z) || 15823 ilib.CType._inRange(num, 'Zp', ilib.data.ctype_z); 15824 }; 15825 15826 /** 15827 * @protected 15828 * @param {boolean} sync 15829 * @param {Object} loadParams 15830 * @param {function(*)|undefined} onLoad 15831 */ 15832 ilib.CType.isSpace._init = function (sync, loadParams, onLoad) { 15833 ilib.CType._load("ctype_z", sync, loadParams, function () { 15834 ilib.CType._init(sync, loadParams, onLoad); 15835 }); 15836 }; 15837 15838 /* 15839 * numprs.js - Parse a number in any locale 15840 * 15841 * Copyright © 2012-2014, JEDLSoft 15842 * 15843 * Licensed under the Apache License, Version 2.0 (the "License"); 15844 * you may not use this file except in compliance with the License. 15845 * You may obtain a copy of the License at 15846 * 15847 * http://www.apache.org/licenses/LICENSE-2.0 15848 * 15849 * Unless required by applicable law or agreed to in writing, software 15850 * distributed under the License is distributed on an "AS IS" BASIS, 15851 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15852 * 15853 * See the License for the specific language governing permissions and 15854 * limitations under the License. 15855 */ 15856 15857 /* 15858 !depends 15859 ilibglobal.js 15860 locale.js 15861 strings.js 15862 ctype.isdigit.js 15863 ctype.isspace.js 15864 */ 15865 15866 /** 15867 * @class 15868 * Parse a string as a number, ignoring all locale-specific formatting.<p> 15869 * 15870 * This class is different from the standard Javascript parseInt() and parseFloat() 15871 * functions in that the number to be parsed can have formatting characters in it 15872 * that are not supported by those two 15873 * functions, and it handles numbers written in other locales properly. For example, 15874 * if you pass the string "203,231.23" to the parseFloat() function in Javascript, it 15875 * will return you the number 203. The ilib.Number class will parse it correctly and 15876 * the value() function will return the number 203231.23. If you pass parseFloat() the 15877 * string "203.231,23" with the locale set to de-DE, it will return you 203 again. This 15878 * class will return the correct number 203231.23 again.<p> 15879 * 15880 * The options object may contain any of the following properties: 15881 * 15882 * <ul> 15883 * <li><i>locale</i> - specify the locale of the string to parse. This is used to 15884 * figure out what the decimal point character is. If not specified, the default locale 15885 * for the app or browser is used. 15886 * <li><i>type</i> - specify whether this string should be interpretted as a number, 15887 * currency, or percentage amount. When the number is interpretted as a currency 15888 * amount, the getCurrency() method will return something useful, otherwise it will 15889 * return undefined. If 15890 * the number is to be interpretted as percentage amount and there is a percentage sign 15891 * in the string, then the number will be returned 15892 * as a fraction from the valueOf() method. If there is no percentage sign, then the 15893 * number will be returned as a regular number. That is "58.3%" will be returned as the 15894 * number 0.583 but "58.3" will be returned as 58.3. Valid values for this property 15895 * are "number", "currency", and "percentage". Default if this is not specified is 15896 * "number". 15897 * <li><i>onLoad</i> - a callback function to call when the locale data is fully 15898 * loaded. When the onLoad option is given, this class will attempt to 15899 * load any missing locale data using the ilib loader callback. 15900 * When the constructor is done (even if the data is already preassembled), the 15901 * onLoad function is called with the current instance as a parameter, so this 15902 * callback can be used with preassembled or dynamic loading or a mix of the two. 15903 * 15904 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 15905 * asynchronously. If this option is given as "false", then the "onLoad" 15906 * callback must be given, as the instance returned from this constructor will 15907 * not be usable for a while. 15908 * 15909 * <li><i>loadParams</i> - an object containing parameters to pass to the 15910 * loader callback function when locale data is missing. The parameters are not 15911 * interpretted or modified in any way. They are simply passed along. The object 15912 * may contain any property/value pairs as long as the calling code is in 15913 * agreement with the loader callback function as to what those parameters mean. 15914 * </ul> 15915 * <p> 15916 * 15917 * Depends directive: !depends numprs.js 15918 * 15919 * @constructor 15920 * @param {string|number|Number|ilib.Number|undefined} str a string to parse as a number, or a number value 15921 * @param {Object=} options Options controlling how the instance should be created 15922 */ 15923 ilib.Number = function (str, options) { 15924 var i, stripped = "", 15925 sync = true, 15926 loadParams, 15927 onLoad; 15928 15929 this.locale = new ilib.Locale(); 15930 this.type = "number"; 15931 15932 if (options) { 15933 if (options.locale) { 15934 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 15935 } 15936 if (options.type) { 15937 switch (options.type) { 15938 case "number": 15939 case "currency": 15940 case "percentage": 15941 this.type = options.type; 15942 break; 15943 default: 15944 break; 15945 } 15946 } 15947 if (typeof(options.sync) !== 'undefined') { 15948 sync = (options.sync == true); 15949 } 15950 loadParams = options.loadParams; 15951 onLoad = options.onLoad; 15952 } 15953 15954 ilib.CType.isDigit._init(sync, loadParams, /** @type {function()|undefined} */ ilib.bind(this, function() { 15955 ilib.CType.isSpace._init(sync, loadParams, /** @type {function()|undefined} */ ilib.bind(this, function() { 15956 new ilib.LocaleInfo(this.locale, { 15957 sync: sync, 15958 onLoad: ilib.bind(this, function (li) { 15959 this.decimal = li.getDecimalSeparator(); 15960 15961 switch (typeof(str)) { 15962 case 'string': 15963 // stripping should work for all locales, because you just ignore all the 15964 // formatting except the decimal char 15965 var unary = true; // looking for the unary minus still? 15966 this.str = str || "0"; 15967 i = 0; 15968 for (i = 0; i < this.str.length; i++) { 15969 if (unary && this.str.charAt(i) === '-') { 15970 unary = false; 15971 stripped += this.str.charAt(i); 15972 } else if (ilib.CType.isDigit(this.str.charAt(i))) { 15973 stripped += this.str.charAt(i); 15974 unary = false; 15975 } else if (this.str.charAt(i) === this.decimal) { 15976 stripped += "."; // always convert to period 15977 unary = false; 15978 } // else ignore 15979 } 15980 this.value = parseFloat(stripped); 15981 break; 15982 case 'number': 15983 this.str = "" + str; 15984 this.value = str; 15985 break; 15986 15987 case 'object': 15988 this.value = /** @type {number} */ str.valueOf(); 15989 this.str = "" + this.value; 15990 break; 15991 15992 case 'undefined': 15993 this.value = 0; 15994 this.str = "0"; 15995 break; 15996 } 15997 15998 switch (this.type) { 15999 default: 16000 // don't need to do anything special for other types 16001 break; 16002 case "percentage": 16003 if (this.str.indexOf(li.getPercentageSymbol()) !== -1) { 16004 this.value /= 100; 16005 } 16006 break; 16007 case "currency": 16008 stripped = ""; 16009 i = 0; 16010 while (i < this.str.length && 16011 !ilib.CType.isDigit(this.str.charAt(i)) && 16012 !ilib.CType.isSpace(this.str.charAt(i))) { 16013 stripped += this.str.charAt(i++); 16014 } 16015 if (stripped.length === 0) { 16016 while (i < this.str.length && 16017 ilib.CType.isDigit(this.str.charAt(i)) || 16018 ilib.CType.isSpace(this.str.charAt(i)) || 16019 this.str.charAt(i) === '.' || 16020 this.str.charAt(i) === ',' ) { 16021 i++; 16022 } 16023 while (i < this.str.length && 16024 !ilib.CType.isDigit(this.str.charAt(i)) && 16025 !ilib.CType.isSpace(this.str.charAt(i))) { 16026 stripped += this.str.charAt(i++); 16027 } 16028 } 16029 new ilib.Currency({ 16030 locale: this.locale, 16031 sign: stripped, 16032 sync: sync, 16033 onLoad: ilib.bind(this, function (cur) { 16034 this.currency = cur; 16035 if (options && typeof(options.onLoad) === 'function') { 16036 options.onLoad(this); 16037 } 16038 }) 16039 }); 16040 return; 16041 } 16042 16043 if (options && typeof(options.onLoad) === 'function') { 16044 options.onLoad(this); 16045 } 16046 }) 16047 }); 16048 })); 16049 })); 16050 }; 16051 16052 ilib.Number.prototype = { 16053 /** 16054 * Return the locale for this formatter instance. 16055 * @return {ilib.Locale} the locale instance for this formatter 16056 */ 16057 getLocale: function () { 16058 return this.locale; 16059 }, 16060 16061 /** 16062 * Return the original string that this number instance was created with. 16063 * @return {string} the original string 16064 */ 16065 toString: function () { 16066 return this.str; 16067 }, 16068 16069 /** 16070 * If the type of this Number instance is "currency", then the parser will attempt 16071 * to figure out which currency this amount represents. The amount can be written 16072 * with any of the currency signs or ISO 4217 codes that are currently 16073 * recognized by ilib, and the currency signs may occur before or after the 16074 * numeric portion of the string. If no currency can be recognized, then the 16075 * default currency for the locale is returned. If multiple currencies can be 16076 * recognized (for example if the currency sign is "$"), then this method 16077 * will prefer the one for the current locale. If multiple currencies can be 16078 * recognized, but none are used in the current locale, then the first currency 16079 * encountered will be used. This may produce random results, though the larger 16080 * currencies occur earlier in the list. For example, if the sign found in the 16081 * string is "$" and that is not the sign of the currency of the current locale 16082 * then the US dollar will be recognized, as it is the largest currency that uses 16083 * the "$" as its sign. 16084 * 16085 * @return {ilib.Currency|undefined} the currency instance for this amount, or 16086 * undefined if this Number object is not of type currency 16087 */ 16088 getCurrency: function () { 16089 return this.currency; 16090 }, 16091 16092 /** 16093 * Return the value of this number object as a primitive number instance. 16094 * @return {number} the value of this number instance 16095 */ 16096 valueOf: function () { 16097 return this.value; 16098 } 16099 }; 16100 16101 /* 16102 * currency.js - Currency definition 16103 * 16104 * Copyright © 2012-2014, JEDLSoft 16105 * 16106 * Licensed under the Apache License, Version 2.0 (the "License"); 16107 * you may not use this file except in compliance with the License. 16108 * You may obtain a copy of the License at 16109 * 16110 * http://www.apache.org/licenses/LICENSE-2.0 16111 * 16112 * Unless required by applicable law or agreed to in writing, software 16113 * distributed under the License is distributed on an "AS IS" BASIS, 16114 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16115 * 16116 * See the License for the specific language governing permissions and 16117 * limitations under the License. 16118 */ 16119 16120 // !depends ilibglobal.js locale.js 16121 16122 // !data currency 16123 16124 /** 16125 * @class 16126 * Create a new currency information instance. Instances of this class encode 16127 * information about a particular currency.<p> 16128 * 16129 * Note: that if you are looking to format currency for display, please see 16130 * the number formatting class {ilib.NumFmt}. This class only gives information 16131 * about currencies.<p> 16132 * 16133 * The options can contain any of the following properties: 16134 * 16135 * <ul> 16136 * <li><i>locale</i> - specify the locale for this instance 16137 * <li><i>code</i> - find info on a specific currency with the given ISO 4217 code 16138 * <li><i>sign</i> - search for a currency that uses this sign 16139 * <li><i>onLoad</i> - a callback function to call when the currency data is fully 16140 * loaded. When the onLoad option is given, this class will attempt to 16141 * load any missing locale data using the ilib loader callback. 16142 * When the constructor is done (even if the data is already preassembled), the 16143 * onLoad function is called with the current instance as a parameter, so this 16144 * callback can be used with preassembled or dynamic loading or a mix of the two. 16145 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 16146 * asynchronously. If this option is given as "false", then the "onLoad" 16147 * callback must be given, as the instance returned from this constructor will 16148 * not be usable for a while. 16149 * <li><i>loadParams</i> - an object containing parameters to pass to the 16150 * loader callback function when locale data is missing. The parameters are not 16151 * interpretted or modified in any way. They are simply passed along. The object 16152 * may contain any property/value pairs as long as the calling code is in 16153 * agreement with the loader callback function as to what those parameters mean. 16154 * </ul> 16155 * 16156 * When searching for a currency by its sign, this class cannot guarantee 16157 * that it will return info about a specific currency. The reason is that currency 16158 * signs are sometimes shared between different currencies and the sign is 16159 * therefore ambiguous. If you need a 16160 * guarantee, find the currency using the code instead.<p> 16161 * 16162 * The way this class finds a currency by sign is the following. If the sign is 16163 * unambiguous, then 16164 * the currency is returned. If there are multiple currencies that use the same 16165 * sign, and the current locale uses that sign, then the default currency for 16166 * the current locale is returned. If there are multiple, but the current locale 16167 * does not use that sign, then the currency with the largest circulation is 16168 * returned. For example, if you are in the en-GB locale, and the sign is "$", 16169 * then this class will notice that there are multiple currencies with that 16170 * sign (USD, CAD, AUD, HKD, MXP, etc.) Since "$" is not used in en-GB, it will 16171 * pick the one with the largest circulation, which in this case is the US Dollar 16172 * (USD).<p> 16173 * 16174 * If neither the code or sign property is set, the currency that is most common 16175 * for the locale 16176 * will be used instead. If the locale is not set, the default locale will be used. 16177 * If the code is given, but it is not found in the list of known currencies, this 16178 * constructor will throw an exception. If the sign is given, but it is not found, 16179 * this constructor will default to the currency for the current locale. If both 16180 * the code and sign properties are given, then the sign property will be ignored 16181 * and only the code property used. If the locale is given, but it is not a known 16182 * locale, this class will default to the default locale instead.<p> 16183 * 16184 * Depends directive: !depends currency.js 16185 * 16186 * @constructor 16187 * @param options {Object} a set of properties to govern how this instance is constructed. 16188 * @throws "currency xxx is unknown" when the given currency code is not in the list of 16189 * known currencies. xxx is replaced with the requested code. 16190 */ 16191 ilib.Currency = function (options) { 16192 this.sync = true; 16193 16194 if (options) { 16195 if (options.code) { 16196 this.code = options.code; 16197 } 16198 if (options.locale) { 16199 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 16200 } 16201 if (options.sign) { 16202 this.sign = options.sign; 16203 } 16204 if (typeof(options.sync) !== 'undefined') { 16205 this.sync = options.sync; 16206 } 16207 if (options.loadParams) { 16208 this.loadParams = options.loadParams; 16209 } 16210 } 16211 16212 this.locale = this.locale || new ilib.Locale(); 16213 if (typeof(ilib.data.currency) === 'undefined') { 16214 ilib.loadData({ 16215 name: "currency.json", 16216 object: ilib.Currency, 16217 locale: "-", 16218 sync: this.sync, 16219 loadParams: this.loadParams, 16220 callback: /** @type function(Object=):undefined */ ilib.bind(this, /** @type function() */ function(currency) { 16221 ilib.data.currency = currency; 16222 this._loadLocinfo(options && options.onLoad); 16223 }) 16224 }); 16225 } else { 16226 this._loadLocinfo(options && options.onLoad); 16227 } 16228 }; 16229 16230 /** 16231 * Return an array of the ids for all ISO 4217 currencies that 16232 * this copy of ilib knows about. 16233 * 16234 * @static 16235 * @return {Array.<string>} an array of currency ids that this copy of ilib knows about. 16236 */ 16237 ilib.Currency.getAvailableCurrencies = function() { 16238 var ret = [], 16239 cur, 16240 currencies = new ilib.ResBundle({ 16241 name: "currency" 16242 }).getResObj(); 16243 16244 for (cur in currencies) { 16245 if (cur && currencies[cur]) { 16246 ret.push(cur); 16247 } 16248 } 16249 16250 return ret; 16251 }; 16252 16253 ilib.Currency.prototype = { 16254 /** 16255 * @private 16256 */ 16257 _loadLocinfo: function(onLoad) { 16258 new ilib.LocaleInfo(this.locale, { 16259 onLoad: ilib.bind(this, function (li) { 16260 var currInfo; 16261 16262 this.locinfo = li; 16263 if (this.code) { 16264 currInfo = ilib.data.currency[this.code]; 16265 if (!currInfo) { 16266 throw "currency " + this.code + " is unknown"; 16267 } 16268 } else if (this.sign) { 16269 currInfo = ilib.data.currency[this.sign]; // maybe it is really a code... 16270 if (typeof(currInfo) !== 'undefined') { 16271 this.code = this.sign; 16272 } else { 16273 this.code = this.locinfo.getCurrency(); 16274 currInfo = ilib.data.currency[this.code]; 16275 if (currInfo.sign !== this.sign) { 16276 // current locale does not use the sign, so search for it 16277 for (var cur in ilib.data.currency) { 16278 if (cur && ilib.data.currency[cur]) { 16279 currInfo = ilib.data.currency[cur]; 16280 if (currInfo.sign === this.sign) { 16281 // currency data is already ordered so that the currency with the 16282 // largest circulation is at the beginning, so all we have to do 16283 // is take the first one in the list that matches 16284 this.code = cur; 16285 break; 16286 } 16287 } 16288 } 16289 } 16290 } 16291 } 16292 16293 if (!currInfo || !this.code) { 16294 this.code = this.locinfo.getCurrency(); 16295 currInfo = ilib.data.currency[this.code]; 16296 } 16297 16298 this.name = currInfo.name; 16299 this.fractionDigits = currInfo.decimals; 16300 this.sign = currInfo.sign; 16301 16302 if (typeof(onLoad) === 'function') { 16303 onLoad(this); 16304 } 16305 }) 16306 }); 16307 }, 16308 16309 /** 16310 * Return the ISO 4217 currency code for this instance. 16311 * @return {string} the ISO 4217 currency code for this instance 16312 */ 16313 getCode: function () { 16314 return this.code; 16315 }, 16316 16317 /** 16318 * Return the default number of fraction digits that is typically used 16319 * with this type of currency. 16320 * @return {number} the number of fraction digits for this currency 16321 */ 16322 getFractionDigits: function () { 16323 return this.fractionDigits; 16324 }, 16325 16326 /** 16327 * Return the sign commonly used to represent this currency. 16328 * @return {string} the sign commonly used to represent this currency 16329 */ 16330 getSign: function () { 16331 return this.sign; 16332 }, 16333 16334 /** 16335 * Return the name of the currency in English. 16336 * @return {string} the name of the currency in English 16337 */ 16338 getName: function () { 16339 return this.name; 16340 }, 16341 16342 /** 16343 * Return the locale for this currency. If the options to the constructor 16344 * included a locale property in order to find the currency that is appropriate 16345 * for that locale, then the locale is returned here. If the options did not 16346 * include a locale, then this method returns undefined. 16347 * @return {ilib.Locale} the locale used in the constructor of this instance, 16348 * or undefined if no locale was given in the constructor 16349 */ 16350 getLocale: function () { 16351 return this.locale; 16352 } 16353 }; 16354 16355 /* 16356 * numfmt.js - Number formatter definition 16357 * 16358 * Copyright © 2012-2014, JEDLSoft 16359 * 16360 * Licensed under the Apache License, Version 2.0 (the "License"); 16361 * you may not use this file except in compliance with the License. 16362 * You may obtain a copy of the License at 16363 * 16364 * http://www.apache.org/licenses/LICENSE-2.0 16365 * 16366 * Unless required by applicable law or agreed to in writing, software 16367 * distributed under the License is distributed on an "AS IS" BASIS, 16368 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16369 * 16370 * See the License for the specific language governing permissions and 16371 * limitations under the License. 16372 */ 16373 16374 // !depends ilibglobal.js locale.js strings.js currency.js 16375 /* 16376 !depends 16377 ilibglobal.js 16378 locale.js 16379 localeinfo.js 16380 util/utils.js 16381 util/math.js 16382 currency.js 16383 strings.js 16384 util/jsutils.js 16385 */ 16386 16387 // !data localeinfo currency 16388 16389 /** 16390 * @class 16391 * Create a new number formatter instance. Locales differ in the way that digits 16392 * in a formatted number are grouped, in the way the decimal character is represented, 16393 * etc. Use this formatter to get it right for any locale.<p> 16394 * 16395 * This formatter can format plain numbers, currency amounts, and percentage amounts.<p> 16396 * 16397 * As with all formatters, the recommended 16398 * practice is to create one formatter and use it multiple times to format various 16399 * numbers.<p> 16400 * 16401 * The options can contain any of the following properties: 16402 * 16403 * <ul> 16404 * <li><i>locale</i> - use the conventions of the specified locale when figuring out how to 16405 * format a number. 16406 * <li><i>type</i> - the type of this formatter. Valid values are "number", "currency", or 16407 * "percentage". If this property is not specified, the default is "number". 16408 * <li><i>currency</i> - the ISO 4217 3-letter currency code to use when the formatter type 16409 * is "currency". This property is required for currency formatting. If the type property 16410 * is "currency" and the currency property is not specified, the constructor will throw a 16411 * an exception. 16412 * <li><i>maxFractionDigits</i> - the maximum number of digits that should appear in the 16413 * formatted output after the decimal. A value of -1 means unlimited, and 0 means only print 16414 * the integral part of the number. 16415 * <li><i>minFractionDigits</i> - the minimum number of fractional digits that should 16416 * appear in the formatted output. If the number does not have enough fractional digits 16417 * to reach this minimum, the number will be zero-padded at the end to get to the limit. 16418 * If the type of the formatter is "currency" and this 16419 * property is not specified, then the minimum fraction digits is set to the normal number 16420 * of digits used with that currency, which is almost always 0, 2, or 3 digits. 16421 * <li><i>useNative</i> - the flag used to determaine whether to use the native script settings 16422 * for formatting the numbers . 16423 * <li><i>roundingMode</i> - When the maxFractionDigits or maxIntegerDigits is specified, 16424 * this property governs how the least significant digits are rounded to conform to that 16425 * maximum. The value of this property is a string with one of the following values: 16426 * <ul> 16427 * <li><i>up</i> - round away from zero 16428 * <li><i>down</i> - round towards zero. This has the effect of truncating the number 16429 * <li><i>ceiling</i> - round towards positive infinity 16430 * <li><i>floor</i> - round towards negative infinity 16431 * <li><i>halfup</i> - round towards nearest neighbour. If equidistant, round up. 16432 * <li><i>halfdown</i> - round towards nearest neighbour. If equidistant, round down. 16433 * <li><i>halfeven</i> - round towards nearest neighbour. If equidistant, round towards the even neighbour 16434 * <li><i>halfodd</i> - round towards nearest neighbour. If equidistant, round towards the odd neighbour 16435 * </ul> 16436 * When the type of the formatter is "currency" and the <i>roundingMode</i> property is not 16437 * set, then the standard legal rounding rules for the locale are followed. If the type 16438 * is "number" or "percentage" and the <i>roundingMode</i> property is not set, then the 16439 * default mode is "halfdown".</i>. 16440 * 16441 * <li><i>style</i> - When the type of this formatter is "currency", the currency amount 16442 * can be formatted in the following styles: "common" and "iso". The common style is the 16443 * one commonly used in every day writing where the currency unit is represented using a 16444 * symbol. eg. "$57.35" for fifty-seven dollars and thirty five cents. The iso style is 16445 * the international style where the currency unit is represented using the ISO 4217 code. 16446 * eg. "USD 57.35" for the same amount. The default is "common" style if the style is 16447 * not specified.<p> 16448 * 16449 * When the type of this formatter is "number", the style can be one of the following: 16450 * <ul> 16451 * <li><i>standard - format a fully specified floating point number properly for the locale 16452 * <li><i>scientific</i> - use scientific notation for all numbers. That is, 1 integral 16453 * digit, followed by a number of fractional digits, followed by an "e" which denotes 16454 * exponentiation, followed digits which give the power of 10 in the exponent. 16455 * <li><i>native</i> - format a floating point number using the native digits and 16456 * formatting symbols for the script of the locale. 16457 * <li><i>nogrouping</i> - format a floating point number without grouping digits for 16458 * the integral portion of the number 16459 * </ul> 16460 * Note that if you specify a maximum number 16461 * of integral digits, the formatter with a standard style will give you standard 16462 * formatting for smaller numbers and scientific notation for larger numbers. The default 16463 * is standard style if this is not specified. 16464 * 16465 * <li><i>onLoad</i> - a callback function to call when the format data is fully 16466 * loaded. When the onLoad option is given, this class will attempt to 16467 * load any missing locale data using the ilib loader callback. 16468 * When the constructor is done (even if the data is already preassembled), the 16469 * onLoad function is called with the current instance as a parameter, so this 16470 * callback can be used with preassembled or dynamic loading or a mix of the two. 16471 * 16472 * <li>sync - tell whether to load any missing locale data synchronously or 16473 * asynchronously. If this option is given as "false", then the "onLoad" 16474 * callback must be given, as the instance returned from this constructor will 16475 * not be usable for a while. 16476 * 16477 * <li><i>loadParams</i> - an object containing parameters to pass to the 16478 * loader callback function when locale data is missing. The parameters are not 16479 * interpretted or modified in any way. They are simply passed along. The object 16480 * may contain any property/value pairs as long as the calling code is in 16481 * agreement with the loader callback function as to what those parameters mean. 16482 * </ul> 16483 * <p> 16484 * 16485 * Depends directive: !depends numfmt.js 16486 * 16487 * @constructor 16488 * @param {Object.<string,*>} options A set of options that govern how the formatter will behave 16489 */ 16490 ilib.NumFmt = function (options) { 16491 var sync = true; 16492 this.locale = new ilib.Locale(); 16493 /** 16494 * @private 16495 * @type {string} 16496 */ 16497 this.type = "number"; 16498 var loadParams = undefined; 16499 16500 if (options) { 16501 if (options.locale) { 16502 this.locale = (typeof (options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 16503 } 16504 16505 if (options.type) { 16506 if (options.type === 'number' || 16507 options.type === 'currency' || 16508 options.type === 'percentage') { 16509 this.type = options.type; 16510 } 16511 } 16512 16513 if (options.currency) { 16514 /** 16515 * @private 16516 * @type {string} 16517 */ 16518 this.currency = options.currency; 16519 } 16520 16521 if (typeof (options.maxFractionDigits) === 'number') { 16522 /** 16523 * @private 16524 * @type {number|undefined} 16525 */ 16526 this.maxFractionDigits = this._toPrimitive(options.maxFractionDigits); 16527 } 16528 if (typeof (options.minFractionDigits) === 'number') { 16529 /** 16530 * @private 16531 * @type {number|undefined} 16532 */ 16533 this.minFractionDigits = this._toPrimitive(options.minFractionDigits); 16534 // enforce the limits to avoid JS exceptions 16535 if (this.minFractionDigits < 0) { 16536 this.minFractionDigits = 0; 16537 } 16538 if (this.minFractionDigits > 20) { 16539 this.minFractionDigits = 20; 16540 } 16541 } 16542 if (options.style) { 16543 /** 16544 * @private 16545 * @type {string} 16546 */ 16547 this.style = options.style; 16548 } 16549 if (typeof(options.useNative) === 'boolean') { 16550 /** 16551 * @private 16552 * @type {boolean} 16553 * */ 16554 this.useNative = options.useNative; 16555 } 16556 /** 16557 * @private 16558 * @type {string} 16559 */ 16560 this.roundingMode = options.roundingMode; 16561 16562 if (typeof (options.sync) !== 'undefined') { 16563 /** @type {boolean} */ 16564 sync = (options.sync == true); 16565 } 16566 16567 loadParams = options.loadParams; 16568 } 16569 16570 /** 16571 * @private 16572 * @type {ilib.LocaleInfo|undefined} 16573 */ 16574 this.localeInfo = undefined; 16575 16576 new ilib.LocaleInfo(this.locale, { 16577 sync: sync, 16578 loadParams: loadParams, 16579 onLoad: ilib.bind(this, function (li) { 16580 /** 16581 * @private 16582 * @type {ilib.LocaleInfo|undefined} 16583 */ 16584 this.localeInfo = li; 16585 16586 if (this.type === "number") { 16587 this.templateNegative = new ilib.String(this.localeInfo.getNegativeNumberFormat() || "-{n}"); 16588 } else if (this.type === "currency") { 16589 var templates; 16590 16591 if (!this.currency || typeof (this.currency) != 'string') { 16592 throw "A currency property is required in the options to the number formatter constructor when the type property is set to currency."; 16593 } 16594 16595 new ilib.Currency({ 16596 locale: this.locale, 16597 code: this.currency, 16598 sync: sync, 16599 loadParams: loadParams, 16600 onLoad: ilib.bind(this, function (cur) { 16601 this.currencyInfo = cur; 16602 if (this.style !== "common" && this.style !== "iso") { 16603 this.style = "common"; 16604 } 16605 16606 if (typeof(this.maxFractionDigits) !== 'number' && typeof(this.minFractionDigits) !== 'number') { 16607 this.minFractionDigits = this.maxFractionDigits = this.currencyInfo.getFractionDigits(); 16608 } 16609 16610 templates = this.localeInfo.getCurrencyFormats(); 16611 this.template = new ilib.String(templates[this.style] || templates.common); 16612 this.templateNegative = new ilib.String(templates[this.style + "Negative"] || templates["commonNegative"]); 16613 this.sign = (this.style === "iso") ? this.currencyInfo.getCode() : this.currencyInfo.getSign(); 16614 16615 if (!this.roundingMode) { 16616 this.roundingMode = this.currencyInfo && this.currencyInfo.roundingMode; 16617 } 16618 16619 this._init(); 16620 16621 if (options && typeof (options.onLoad) === 'function') { 16622 options.onLoad(this); 16623 } 16624 }) 16625 }); 16626 return; 16627 } else if (this.type === "percentage") { 16628 this.template = new ilib.String(this.localeInfo.getPercentageFormat() || "{n}%"); 16629 this.templateNegative = new ilib.String(this.localeInfo.getNegativePercentageFormat() || this.localeInfo.getNegativeNumberFormat() + "%"); 16630 } 16631 16632 this._init(); 16633 16634 if (options && typeof (options.onLoad) === 'function') { 16635 options.onLoad(this); 16636 } 16637 }) 16638 }); 16639 }; 16640 16641 /** 16642 * Return an array of available locales that this formatter can format 16643 * @static 16644 * @return {Array.<ilib.Locale>|undefined} an array of available locales 16645 */ 16646 ilib.NumFmt.getAvailableLocales = function () { 16647 return undefined; 16648 }; 16649 16650 /** 16651 * @private 16652 * @const 16653 * @type string 16654 */ 16655 ilib.NumFmt.zeros = "0000000000000000000000000000000000000000000000000000000000000000000000"; 16656 16657 ilib.NumFmt.prototype = { 16658 /** 16659 * Return true if this formatter uses native digits to format the number. If the useNative 16660 * option is given to the constructor, then this flag will be honoured. If the useNative 16661 * option is not given to the constructor, this this formatter will use native digits if 16662 * the locale typically uses native digits. 16663 * 16664 * @return {boolean} true if this formatter will format with native digits, false otherwise 16665 */ 16666 getUseNative: function() { 16667 if (typeof(this.useNative) === "boolean") { 16668 return this.useNative; 16669 } 16670 return (this.localeInfo.getDigitsStyle() === "native"); 16671 }, 16672 16673 /** 16674 * @private 16675 */ 16676 _init: function () { 16677 if (this.maxFractionDigits < this.minFractionDigits) { 16678 this.minFractionDigits = this.maxFractionDigits; 16679 } 16680 16681 if (!this.roundingMode) { 16682 this.roundingMode = this.localeInfo.getRoundingMode(); 16683 } 16684 16685 if (!this.roundingMode) { 16686 this.roundingMode = "halfdown"; 16687 } 16688 16689 // set up the function, so we only have to figure it out once 16690 // and not every time we do format() 16691 this.round = ilib._roundFnc[this.roundingMode]; 16692 if (!this.round) { 16693 this.roundingMode = "halfdown"; 16694 this.round = ilib._roundFnc[this.roundingMode]; 16695 } 16696 16697 if (this.style === "nogrouping") { 16698 this.prigroupSize = this.secgroupSize = 0; 16699 } else { 16700 this.prigroupSize = this.localeInfo.getPrimaryGroupingDigits(); 16701 this.secgroupSize = this.localeInfo.getSecondaryGroupingDigits(); 16702 this.groupingSeparator = this.getUseNative() ? this.localeInfo.getNativeGroupingSeparator() : this.localeInfo.getGroupingSeparator(); 16703 } 16704 this.decimalSeparator = this.getUseNative() ? this.localeInfo.getNativeDecimalSeparator() : this.localeInfo.getDecimalSeparator(); 16705 16706 if (this.getUseNative()) { 16707 var nd = this.localeInfo.getNativeDigits() || this.localeInfo.getDigits(); 16708 if (nd) { 16709 this.digits = nd.split(""); 16710 } 16711 } 16712 16713 this.exponentSymbol = this.localeInfo.getExponential() || "e"; 16714 }, 16715 16716 /* 16717 * @private 16718 */ 16719 _pad: function (str, length, left) { 16720 return (str.length >= length) ? 16721 str : 16722 (left ? 16723 ilib.NumFmt.zeros.substring(0, length - str.length) + str : 16724 str + ilib.NumFmt.zeros.substring(0, length - str.length)); 16725 }, 16726 16727 /** 16728 * @private 16729 * @param {Number|ilib.Number|string|number} num object, string, or number to convert to a primitive number 16730 * @return {number} the primitive number equivalent of the argument 16731 */ 16732 _toPrimitive: function (num) { 16733 var n = 0; 16734 16735 switch (typeof (num)) { 16736 case 'number': 16737 n = num; 16738 break; 16739 case 'string': 16740 n = parseFloat(num); 16741 break; 16742 case 'object': 16743 // Number.valueOf() is incorrectly documented as being of type "string" rather than "number", so coerse 16744 // the type here to shut the type checker up 16745 n = /** @type {number} */ num.valueOf(); 16746 break; 16747 } 16748 16749 return n; 16750 }, 16751 16752 /** 16753 * Format the number using scientific notation as a positive number. Negative 16754 * formatting to be applied later. 16755 * @private 16756 * @param {number} num the number to format 16757 * @return {string} the formatted number 16758 */ 16759 _formatScientific: function (num) { 16760 var n = new Number(num); 16761 var formatted; 16762 16763 var factor, 16764 str = n.toExponential(), 16765 parts = str.split("e"), 16766 significant = parts[0], 16767 exponent = parts[1], 16768 numparts, 16769 integral, 16770 fraction; 16771 16772 if (this.maxFractionDigits > 0) { 16773 // if there is a max fraction digits setting, round the fraction to 16774 // the right length first by dividing or multiplying by powers of 10. 16775 // manipulate the fraction digits so as to 16776 // avoid the rounding errors of floating point numbers 16777 factor = Math.pow(10, this.maxFractionDigits); 16778 significant = this.round(significant * factor) / factor; 16779 } 16780 numparts = ("" + significant).split("."); 16781 integral = numparts[0]; 16782 fraction = numparts[1]; 16783 16784 if (typeof(this.maxFractionDigits) !== 'undefined') { 16785 fraction = fraction.substring(0, this.maxFractionDigits); 16786 } 16787 if (typeof(this.minFractionDigits) !== 'undefined') { 16788 fraction = this._pad(fraction || "", this.minFractionDigits, false); 16789 } 16790 formatted = integral; 16791 if (fraction.length) { 16792 formatted += this.decimalSeparator + fraction; 16793 } 16794 formatted += this.exponentSymbol + exponent; 16795 return formatted; 16796 }, 16797 16798 /** 16799 * Formats the number as a positive number. Negative formatting to be applied later. 16800 * @private 16801 * @param {number} num the number to format 16802 * @return {string} the formatted number 16803 */ 16804 _formatStandard: function (num) { 16805 var i; 16806 var k; 16807 16808 if (typeof(this.maxFractionDigits) !== 'undefined' && this.maxFractionDigits > -1) { 16809 var factor = Math.pow(10, this.maxFractionDigits); 16810 num = this.round(num * factor) / factor; 16811 } 16812 16813 num = Math.abs(num); 16814 16815 var parts = ("" + num).split("."), 16816 integral = parts[0], 16817 fraction = parts[1], 16818 cycle, 16819 formatted; 16820 16821 integral = integral.toString(); 16822 16823 if (this.minFractionDigits > 0) { 16824 fraction = this._pad(fraction || "", this.minFractionDigits, false); 16825 } 16826 16827 if (this.secgroupSize > 0) { 16828 if (integral.length > this.prigroupSize) { 16829 var size1 = this.prigroupSize; 16830 var size2 = integral.length; 16831 var size3 = size2 - size1; 16832 integral = integral.slice(0, size3) + this.groupingSeparator + integral.slice(size3); 16833 var num_sec = integral.substring(0, integral.indexOf(this.groupingSeparator)); 16834 k = num_sec.length; 16835 while (k > this.secgroupSize) { 16836 var secsize1 = this.secgroupSize; 16837 var secsize2 = num_sec.length; 16838 var secsize3 = secsize2 - secsize1; 16839 integral = integral.slice(0, secsize3) + this.groupingSeparator + integral.slice(secsize3); 16840 num_sec = integral.substring(0, integral.indexOf(this.groupingSeparator)); 16841 k = num_sec.length; 16842 } 16843 } 16844 16845 formatted = integral; 16846 } else if (this.prigroupSize !== 0) { 16847 cycle = ilib.mod(integral.length - 1, this.prigroupSize); 16848 16849 formatted = ""; 16850 16851 for (i = 0; i < integral.length - 1; i++) { 16852 formatted += integral.charAt(i); 16853 if (cycle === 0) { 16854 formatted += this.groupingSeparator; 16855 } 16856 cycle = ilib.mod(cycle - 1, this.prigroupSize); 16857 } 16858 formatted += integral.charAt(integral.length - 1); 16859 } else { 16860 formatted = integral; 16861 } 16862 16863 if (fraction && (typeof(this.maxFractionDigits) === 'undefined' || this.maxFractionDigits > 0)) { 16864 formatted += this.decimalSeparator; 16865 formatted += fraction; 16866 } 16867 16868 if (this.digits) { 16869 formatted = ilib.mapString(formatted, this.digits); 16870 } 16871 16872 return formatted; 16873 }, 16874 16875 /** 16876 * Format a number according to the settings of this number formatter instance. 16877 * @param num {number|string|Number|ilib.Number} a floating point number to format 16878 * @return {string} a string containing the formatted number 16879 */ 16880 format: function (num) { 16881 var formatted, n; 16882 16883 if (typeof (num) === 'undefined') { 16884 return ""; 16885 } 16886 16887 // convert to a real primitive number type 16888 n = this._toPrimitive(num); 16889 16890 if (this.type === "number") { 16891 formatted = (this.style === "scientific") ? 16892 this._formatScientific(n) : 16893 this._formatStandard(n); 16894 16895 if (num < 0) { 16896 formatted = this.templateNegative.format({n: formatted}); 16897 } 16898 } else { 16899 formatted = this._formatStandard(n); 16900 var template = (n < 0) ? this.templateNegative : this.template; 16901 formatted = template.format({ 16902 n: formatted, 16903 s: this.sign 16904 }); 16905 } 16906 16907 return formatted; 16908 }, 16909 16910 /** 16911 * Return the type of formatter. Valid values are "number", "currency", and 16912 * "percentage". 16913 * 16914 * @return {string} the type of formatter 16915 */ 16916 getType: function () { 16917 return this.type; 16918 }, 16919 16920 /** 16921 * Return the locale for this formatter instance. 16922 * @return {ilib.Locale} the locale instance for this formatter 16923 */ 16924 getLocale: function () { 16925 return this.locale; 16926 }, 16927 16928 /** 16929 * Returns true if this formatter groups together digits in the integral 16930 * portion of a number, based on the options set up in the constructor. In 16931 * most western European cultures, this means separating every 3 digits 16932 * of the integral portion of a number with a particular character. 16933 * 16934 * @return {boolean} true if this formatter groups digits in the integral 16935 * portion of the number 16936 */ 16937 isGroupingUsed: function () { 16938 return (this.groupingSeparator !== 'undefined' && this.groupingSeparator.length > 0); 16939 }, 16940 16941 /** 16942 * Returns the maximum fraction digits set up in the constructor. 16943 * 16944 * @return {number} the maximum number of fractional digits this 16945 * formatter will format, or -1 for no maximum 16946 */ 16947 getMaxFractionDigits: function () { 16948 return typeof (this.maxFractionDigits) !== 'undefined' ? this.maxFractionDigits : -1; 16949 }, 16950 16951 /** 16952 * Returns the minimum fraction digits set up in the constructor. If 16953 * the formatter has the type "currency", then the minimum fraction 16954 * digits is the amount of digits that is standard for the currency 16955 * in question unless overridden in the options to the constructor. 16956 * 16957 * @return {number} the minimum number of fractional digits this 16958 * formatter will format, or -1 for no minimum 16959 */ 16960 getMinFractionDigits: function () { 16961 return typeof (this.minFractionDigits) !== 'undefined' ? this.minFractionDigits : -1; 16962 }, 16963 16964 /** 16965 * Returns the ISO 4217 code for the currency that this formatter formats. 16966 * IF the typeof this formatter is not "currency", then this method will 16967 * return undefined. 16968 * 16969 * @return {string} the ISO 4217 code for the currency that this formatter 16970 * formats, or undefined if this not a currency formatter 16971 */ 16972 getCurrency: function () { 16973 return this.currencyInfo && this.currencyInfo.getCode(); 16974 }, 16975 16976 /** 16977 * Returns the rounding mode set up in the constructor. The rounding mode 16978 * controls how numbers are rounded when the integral or fraction digits 16979 * of a number are limited. 16980 * 16981 * @return {string} the name of the rounding mode used in this formatter 16982 */ 16983 getRoundingMode: function () { 16984 return this.roundingMode; 16985 }, 16986 16987 /** 16988 * If this formatter is a currency formatter, then the style determines how the 16989 * currency is denoted in the formatted output. This method returns the style 16990 * that this formatter will produce. (See the constructor comment for more about 16991 * the styles.) 16992 * @return {string} the name of the style this formatter will use to format 16993 * currency amounts, or "undefined" if this formatter is not a currency formatter 16994 */ 16995 getStyle: function () { 16996 return this.style; 16997 } 16998 }; 16999 17000 /* 17001 * durfmt.js - Date formatter definition 17002 * 17003 * Copyright © 2012-2014, JEDLSoft 17004 * 17005 * Licensed under the Apache License, Version 2.0 (the "License"); 17006 * you may not use this file except in compliance with the License. 17007 * You may obtain a copy of the License at 17008 * 17009 * http://www.apache.org/licenses/LICENSE-2.0 17010 * 17011 * Unless required by applicable law or agreed to in writing, software 17012 * distributed under the License is distributed on an "AS IS" BASIS, 17013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17014 * 17015 * See the License for the specific language governing permissions and 17016 * limitations under the License. 17017 */ 17018 17019 /* 17020 !depends 17021 ilibglobal.js 17022 locale.js 17023 date.js 17024 strings.js 17025 resources.js 17026 localeinfo.js 17027 util/jsutils.js 17028 */ 17029 17030 // !data dateformats sysres 17031 // !resbundle sysres 17032 17033 /** 17034 * @class 17035 * Create a new duration formatter instance. The duration formatter is immutable once 17036 * it is created, but can format as many different durations as needed with the same 17037 * options. Create different duration formatter instances for different purposes 17038 * and then keep them cached for use later if you have more than one duration to 17039 * format.<p> 17040 * 17041 * Duration formatters format lengths of time. The duration formatter is meant to format 17042 * durations of such things as the length of a song or a movie or a meeting, or the 17043 * current position in that song or movie while playing it. If you wish to format a 17044 * period of time that has a specific start and end date/time, then use a 17045 * [ilib.DateRngFmt] instance instead and call its format method.<p> 17046 * 17047 * The options may contain any of the following properties: 17048 * 17049 * <ul> 17050 * <li><i>locale</i> - locale to use when formatting the duration. If the locale is 17051 * not specified, then the default locale of the app or web page will be used. 17052 * 17053 * <li><i>length</i> - Specify the length of the format to use. The length is the approximate size of the 17054 * formatted string. 17055 * 17056 * <ul> 17057 * <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 17058 * <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 17059 * <li><i>long</i> - use a long representation of the duration. This is a fully specified format, but some of the textual 17060 * parts may still be abbreviated. eg. 1 yr 1 mo 1 wk 1 day 1 hr 1 min 1 sec 17061 * <li><i>full</i> - use a full representation of the duration. This is a fully specified format where all the textual 17062 * parts are spelled out completely. eg. 1 year, 1 month, 1 week, 1 day, 1 hour, 1 minute and 1 second 17063 * </ul> 17064 * 17065 * <li><i>style<i> - whether hours, minutes, and seconds should be formatted as a text string 17066 * or as a regular time as on a clock. eg. text is "1 hour, 15 minutes", whereas clock is "1:15:00". Valid 17067 * values for this property are "text" or "clock". Default if this property is not specified 17068 * is "text". 17069 * 17070 *<li><i>useNative</i> - the flag used to determaine whether to use the native script settings 17071 * for formatting the numbers . 17072 * 17073 * <li><i>onLoad</i> - a callback function to call when the format data is fully 17074 * loaded. When the onLoad option is given, this class will attempt to 17075 * load any missing locale data using the ilib loader callback. 17076 * When the constructor is done (even if the data is already preassembled), the 17077 * onLoad function is called with the current instance as a parameter, so this 17078 * callback can be used with preassembled or dynamic loading or a mix of the two. 17079 * 17080 * <li>sync - tell whether to load any missing locale data synchronously or 17081 * asynchronously. If this option is given as "false", then the "onLoad" 17082 * callback must be given, as the instance returned from this constructor will 17083 * not be usable for a while. 17084 * 17085 * <li><i>loadParams</i> - an object containing parameters to pass to the 17086 * loader callback function when locale data is missing. The parameters are not 17087 * interpretted or modified in any way. They are simply passed along. The object 17088 * may contain any property/value pairs as long as the calling code is in 17089 * agreement with the loader callback function as to what those parameters mean. 17090 * </ul> 17091 * <p> 17092 * 17093 * Depends directive: !depends durfmt.js 17094 * 17095 * @constructor 17096 * @param {?Object} options options governing the way this date formatter instance works 17097 */ 17098 ilib.DurFmt = function(options) { 17099 var sync = true; 17100 var loadParams = undefined; 17101 17102 this.locale = new ilib.Locale(); 17103 this.length = "short"; 17104 this.style = "text"; 17105 17106 if (options) { 17107 if (options.locale) { 17108 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 17109 } 17110 17111 if (options.length) { 17112 if (options.length === 'short' || 17113 options.length === 'medium' || 17114 options.length === 'long' || 17115 options.length === 'full') { 17116 this.length = options.length; 17117 } 17118 } 17119 17120 if (options.style) { 17121 if (options.style === 'text' || options.style === 'clock') { 17122 this.style = options.style; 17123 } 17124 } 17125 17126 if (typeof(options.sync) !== 'undefined') { 17127 sync = (options.sync == true); 17128 } 17129 17130 if (typeof(options.useNative) === 'boolean') { 17131 this.useNative = options.useNative; 17132 } 17133 17134 loadParams = options.loadParams; 17135 } 17136 17137 new ilib.ResBundle({ 17138 locale: this.locale, 17139 name: "sysres", 17140 sync: sync, 17141 loadParams: loadParams, 17142 onLoad: ilib.bind(this, function (sysres) { 17143 switch (this.length) { 17144 case 'short': 17145 this.components = { 17146 year: sysres.getString("#{num}y"), 17147 month: sysres.getString("#{num}m", "durationShortMonths"), 17148 week: sysres.getString("#{num}w"), 17149 day: sysres.getString("#{num}d"), 17150 hour: sysres.getString("#{num}h"), 17151 minute: sysres.getString("#{num}m", "durationShortMinutes"), 17152 second: sysres.getString("#{num}s"), 17153 millisecond: sysres.getString("#{num}m", "durationShortMillis"), 17154 separator: sysres.getString(" ", "separatorShort"), 17155 finalSeparator: "" // not used at this length 17156 }; 17157 break; 17158 17159 case 'medium': 17160 this.components = { 17161 year: sysres.getString("1#1 yr|#{num} yrs", "durationMediumYears"), 17162 month: sysres.getString("1#1 mo|#{num} mos"), 17163 week: sysres.getString("1#1 wk|#{num} wks", "durationMediumWeeks"), 17164 day: sysres.getString("1#1 dy|#{num} dys"), 17165 hour: sysres.getString("1#1 hr|#{num} hrs", "durationMediumHours"), 17166 minute: sysres.getString("1#1 mi|#{num} min"), 17167 second: sysres.getString("1#1 se|#{num} sec"), 17168 millisecond: sysres.getString("#{num} ms"), 17169 separator: sysres.getString(" ", "separatorMedium"), 17170 finalSeparator: "" // not used at this length 17171 }; 17172 break; 17173 17174 case 'long': 17175 this.components = { 17176 year: sysres.getString("1#1 yr|#{num} yrs"), 17177 month: sysres.getString("1#1 mon|#{num} mons"), 17178 week: sysres.getString("1#1 wk|#{num} wks"), 17179 day: sysres.getString("1#1 day|#{num} days", "durationLongDays"), 17180 hour: sysres.getString("1#1 hr|#{num} hrs"), 17181 minute: sysres.getString("1#1 min|#{num} min"), 17182 second: sysres.getString("1#1 sec|#{num} sec"), 17183 millisecond: sysres.getString("#{num} ms"), 17184 separator: sysres.getString(", ", "separatorLong"), 17185 finalSeparator: "" // not used at this length 17186 }; 17187 break; 17188 17189 case 'full': 17190 this.components = { 17191 year: sysres.getString("1#1 year|#{num} years"), 17192 month: sysres.getString("1#1 month|#{num} months"), 17193 week: sysres.getString("1#1 week|#{num} weeks"), 17194 day: sysres.getString("1#1 day|#{num} days"), 17195 hour: sysres.getString("1#1 hour|#{num} hours"), 17196 minute: sysres.getString("1#1 minute|#{num} minutes"), 17197 second: sysres.getString("1#1 second|#{num} seconds"), 17198 millisecond: sysres.getString("1#1 millisecond|#{num} milliseconds"), 17199 separator: sysres.getString(", ", "separatorFull"), 17200 finalSeparator: sysres.getString(" and ", "finalSeparatorFull") 17201 }; 17202 break; 17203 } 17204 17205 if (this.style === 'clock') { 17206 new ilib.DateFmt({ 17207 locale: this.locale, 17208 calendar: "gregorian", 17209 type: "time", 17210 time: "ms", 17211 sync: sync, 17212 loadParams: loadParams, 17213 useNative: this.useNative, 17214 onLoad: ilib.bind(this, function (fmtMS) { 17215 this.timeFmtMS = fmtMS; 17216 new ilib.DateFmt({ 17217 locale: this.locale, 17218 calendar: "gregorian", 17219 type: "time", 17220 time: "hm", 17221 sync: sync, 17222 loadParams: loadParams, 17223 useNative: this.useNative, 17224 onLoad: ilib.bind(this, function (fmtHM) { 17225 this.timeFmtHM = fmtHM; 17226 new ilib.DateFmt({ 17227 locale: this.locale, 17228 calendar: "gregorian", 17229 type: "time", 17230 time: "hms", 17231 sync: sync, 17232 loadParams: loadParams, 17233 useNative: this.useNative, 17234 onLoad: ilib.bind(this, function (fmtHMS) { 17235 this.timeFmtHMS = fmtHMS; 17236 17237 // munge with the template to make sure that the hours are not formatted mod 12 17238 this.timeFmtHM.template = this.timeFmtHM.template.replace(/hh?/, 'H'); 17239 this.timeFmtHM.templateArr = this.timeFmtHM._tokenize(this.timeFmtHM.template); 17240 this.timeFmtHMS.template = this.timeFmtHMS.template.replace(/hh?/, 'H'); 17241 this.timeFmtHMS.templateArr = this.timeFmtHMS._tokenize(this.timeFmtHMS.template); 17242 17243 this._init(this.timeFmtHM.locinfo, options && options.onLoad); 17244 }) 17245 }); 17246 }) 17247 }); 17248 }) 17249 }); 17250 return; 17251 } 17252 17253 new ilib.LocaleInfo(this.locale, { 17254 sync: sync, 17255 loadParams: loadParams, 17256 onLoad: ilib.bind(this, function (li) { 17257 this._init(li, options && options.onLoad); 17258 }) 17259 }); 17260 }) 17261 }); 17262 }; 17263 17264 /** 17265 * @private 17266 * @static 17267 */ 17268 ilib.DurFmt.complist = { 17269 "text": ["year", "month", "week", "day", "hour", "minute", "second", "millisecond"], 17270 "clock": ["year", "month", "week", "day"] 17271 }; 17272 17273 /** 17274 * @private 17275 */ 17276 ilib.DurFmt.prototype._mapDigits = function(str) { 17277 if (this.useNative && this.digits) { 17278 return ilib.mapString(str.toString(), this.digits); 17279 } 17280 return str; 17281 }; 17282 17283 /** 17284 * @private 17285 * @param {ilib.LocaleInfo} locinfo 17286 * @param {function(ilib.DurFmt)|undefined} onLoad 17287 */ 17288 ilib.DurFmt.prototype._init = function(locinfo, onLoad) { 17289 var digits; 17290 if (typeof(this.useNative) === 'boolean') { 17291 // if the caller explicitly said to use native or not, honour that despite what the locale data says... 17292 if (this.useNative) { 17293 digits = locinfo.getNativeDigits(); 17294 if (digits) { 17295 this.digits = digits; 17296 } 17297 } 17298 } else if (locinfo.getDigitsStyle() === "native") { 17299 // else if the locale usually uses native digits, then use them 17300 digits = locinfo.getNativeDigits(); 17301 if (digits) { 17302 this.useNative = true; 17303 this.digits = digits; 17304 } 17305 } // else use western digits always 17306 17307 if (typeof(onLoad) === 'function') { 17308 onLoad(this); 17309 } 17310 }; 17311 17312 /** 17313 * Format a duration according to the format template of this formatter instance.<p> 17314 * 17315 * The components parameter should be an object that contains any or all of these 17316 * numeric properties: 17317 * 17318 * <ul> 17319 * <li>year 17320 * <li>month 17321 * <li>week 17322 * <li>day 17323 * <li>hour 17324 * <li>minute 17325 * <li>second 17326 * </ul> 17327 * <p> 17328 * 17329 * When a property is left out of the components parameter or has a value of 0, it will not 17330 * be formatted into the output string, except for times that include 0 minutes and 0 seconds. 17331 * 17332 * This formatter will not ensure that numbers for each component property is within the 17333 * valid range for that component. This allows you to format durations that are longer 17334 * than normal range. For example, you could format a duration has being "33 hours" rather 17335 * than "1 day, 9 hours". 17336 * 17337 * @param {Object} components date/time components to be formatted into a duration string 17338 * @return {ilib.String} a string with the duration formatted according to the style and 17339 * locale set up for this formatter instance. If the components parameter is empty or 17340 * undefined, an empty string is returned. 17341 */ 17342 ilib.DurFmt.prototype.format = function (components) { 17343 var i, list, temp, fmt, secondlast = true, str = ""; 17344 17345 list = ilib.DurFmt.complist[this.style]; 17346 //for (i = 0; i < list.length; i++) { 17347 for (i = list.length-1; i >= 0; i--) { 17348 //console.log("Now dealing with " + list[i]); 17349 if (typeof(components[list[i]]) !== 'undefined' && components[list[i]] != 0) { 17350 if (str.length > 0) { 17351 str = ((this.length === 'full' && secondlast) ? this.components.finalSeparator : this.components.separator) + str; 17352 secondlast = false; 17353 } 17354 str = this.components[list[i]].formatChoice(components[list[i]], {num: this._mapDigits(components[list[i]])}) + str; 17355 } 17356 } 17357 17358 if (this.style === 'clock') { 17359 if (typeof(components.hour) !== 'undefined') { 17360 fmt = (typeof(components.second) !== 'undefined') ? this.timeFmtHMS : this.timeFmtHM; 17361 } else { 17362 fmt = this.timeFmtMS; 17363 } 17364 17365 if (str.length > 0) { 17366 str += this.components.separator; 17367 } 17368 str += fmt._formatTemplate(components, fmt.templateArr); 17369 } 17370 17371 return new ilib.String(str); 17372 }; 17373 17374 /** 17375 * Return the locale that was used to construct this duration formatter object. If the 17376 * locale was not given as parameter to the constructor, this method returns the default 17377 * locale of the system. 17378 * 17379 * @return {ilib.Locale} locale that this duration formatter was constructed with 17380 */ 17381 ilib.DurFmt.prototype.getLocale = function () { 17382 return this.locale; 17383 }; 17384 17385 /** 17386 * Return the length that was used to construct this duration formatter object. If the 17387 * length was not given as parameter to the constructor, this method returns the default 17388 * length. Valid values are "short", "medium", "long", and "full". 17389 * 17390 * @return {string} length that this duration formatter was constructed with 17391 */ 17392 ilib.DurFmt.prototype.getLength = function () { 17393 return this.length; 17394 }; 17395 17396 /** 17397 * Return the style that was used to construct this duration formatter object. Returns 17398 * one of "text" or "clock". 17399 * 17400 * @return {string} style that this duration formatter was constructed with 17401 */ 17402 ilib.DurFmt.prototype.getStyle = function () { 17403 return this.style; 17404 }; 17405 17406 /* 17407 * ctype.islpha.js - Character type is alphabetic 17408 * 17409 * Copyright © 2012-2013, JEDLSoft 17410 * 17411 * Licensed under the Apache License, Version 2.0 (the "License"); 17412 * you may not use this file except in compliance with the License. 17413 * You may obtain a copy of the License at 17414 * 17415 * http://www.apache.org/licenses/LICENSE-2.0 17416 * 17417 * Unless required by applicable law or agreed to in writing, software 17418 * distributed under the License is distributed on an "AS IS" BASIS, 17419 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17420 * 17421 * See the License for the specific language governing permissions and 17422 * limitations under the License. 17423 */ 17424 17425 // !depends ctype.js 17426 17427 // !data ctype_l 17428 17429 /** 17430 * Return whether or not the first character is alphabetic.<p> 17431 * 17432 * Depends directive: !depends ctype.isalnum.js 17433 * 17434 * @param {string|ilib.String|number} ch character or code point to examine 17435 * @return {boolean} true if the first character is alphabetic. 17436 */ 17437 ilib.CType.isAlpha = function (ch) { 17438 var num; 17439 switch (typeof(ch)) { 17440 case 'number': 17441 num = ch; 17442 break; 17443 case 'string': 17444 num = ilib.String.toCodePoint(ch, 0); 17445 break; 17446 case 'undefined': 17447 return false; 17448 default: 17449 num = ch._toCodePoint(0); 17450 break; 17451 } 17452 return ilib.CType._inRange(num, 'Lu', ilib.data.ctype_l) || 17453 ilib.CType._inRange(num, 'Ll', ilib.data.ctype_l) || 17454 ilib.CType._inRange(num, 'Lt', ilib.data.ctype_l) || 17455 ilib.CType._inRange(num, 'Lm', ilib.data.ctype_l) || 17456 ilib.CType._inRange(num, 'Lo', ilib.data.ctype_l); 17457 }; 17458 17459 /** 17460 * @protected 17461 * @param {boolean} sync 17462 * @param {Object} loadParams 17463 * @param {function(*)|undefined} onLoad 17464 */ 17465 ilib.CType.isAlpha._init = function (sync, loadParams, onLoad) { 17466 ilib.CType._load("ctype_l", sync, loadParams, onLoad); 17467 }; 17468 17469 17470 17471 /* 17472 * ctype.isalnum.js - Character type alphanumeric 17473 * 17474 * Copyright © 2012-2013, JEDLSoft 17475 * 17476 * Licensed under the Apache License, Version 2.0 (the "License"); 17477 * you may not use this file except in compliance with the License. 17478 * You may obtain a copy of the License at 17479 * 17480 * http://www.apache.org/licenses/LICENSE-2.0 17481 * 17482 * Unless required by applicable law or agreed to in writing, software 17483 * distributed under the License is distributed on an "AS IS" BASIS, 17484 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17485 * 17486 * See the License for the specific language governing permissions and 17487 * limitations under the License. 17488 */ 17489 17490 // !depends ctype.js ctype.isalpha.js ctype.isdigit.js 17491 17492 /** 17493 * Return whether or not the first character is alphabetic or numeric.<p> 17494 * 17495 * Depends directive: !depends ctype.isalnum.js 17496 * 17497 * @param {string|ilib.String|number} ch character or code point to examine 17498 * @return {boolean} true if the first character is alphabetic or numeric 17499 */ 17500 ilib.CType.isAlnum = function isAlnum(ch) { 17501 var num; 17502 switch (typeof(ch)) { 17503 case 'number': 17504 num = ch; 17505 break; 17506 case 'string': 17507 num = ilib.String.toCodePoint(ch, 0); 17508 break; 17509 case 'undefined': 17510 return false; 17511 default: 17512 num = ch._toCodePoint(0); 17513 break; 17514 } 17515 return ilib.CType.isAlpha(num) || ilib.CType.isDigit(num); 17516 }; 17517 17518 /** 17519 * @protected 17520 * @param {boolean} sync 17521 * @param {Object} loadParams 17522 * @param {function(*)|undefined} onLoad 17523 */ 17524 ilib.CType.isAlnum._init = function (sync, loadParams, onLoad) { 17525 ilib.CType.isAlpha._init(sync, loadParams, function () { 17526 ilib.CType.isDigit._init(sync, loadParams, onLoad); 17527 }); 17528 }; 17529 17530 /* 17531 * ctype.isascii.js - Character type is ASCII 17532 * 17533 * Copyright © 2012-2013, JEDLSoft 17534 * 17535 * Licensed under the Apache License, Version 2.0 (the "License"); 17536 * you may not use this file except in compliance with the License. 17537 * You may obtain a copy of the License at 17538 * 17539 * http://www.apache.org/licenses/LICENSE-2.0 17540 * 17541 * Unless required by applicable law or agreed to in writing, software 17542 * distributed under the License is distributed on an "AS IS" BASIS, 17543 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17544 * 17545 * See the License for the specific language governing permissions and 17546 * limitations under the License. 17547 */ 17548 17549 // !depends ctype.js 17550 17551 // !data ctype 17552 17553 /** 17554 * Return whether or not the first character is in the ASCII range.<p> 17555 * 17556 * Depends directive: !depends ctype.isascii.js 17557 * 17558 * @param {string|ilib.String|number} ch character or code point to examine 17559 * @return {boolean} true if the first character is in the ASCII range. 17560 */ 17561 ilib.CType.isAscii = function (ch) { 17562 var num; 17563 switch (typeof(ch)) { 17564 case 'number': 17565 num = ch; 17566 break; 17567 case 'string': 17568 num = ilib.String.toCodePoint(ch, 0); 17569 break; 17570 case 'undefined': 17571 return false; 17572 default: 17573 num = ch._toCodePoint(0); 17574 break; 17575 } 17576 return ilib.CType._inRange(num, 'ascii', ilib.data.ctype); 17577 }; 17578 17579 /** 17580 * @protected 17581 * @param {boolean} sync 17582 * @param {Object} loadParams 17583 * @param {function(*)|undefined} onLoad 17584 */ 17585 ilib.CType.isAscii._init = function (sync, loadParams, onLoad) { 17586 ilib.CType._init(sync, loadParams, onLoad); 17587 }; 17588 17589 /* 17590 * ctype.isblank.js - Character type is blank 17591 * 17592 * Copyright © 2012-2013, JEDLSoft 17593 * 17594 * Licensed under the Apache License, Version 2.0 (the "License"); 17595 * you may not use this file except in compliance with the License. 17596 * You may obtain a copy of the License at 17597 * 17598 * http://www.apache.org/licenses/LICENSE-2.0 17599 * 17600 * Unless required by applicable law or agreed to in writing, software 17601 * distributed under the License is distributed on an "AS IS" BASIS, 17602 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17603 * 17604 * See the License for the specific language governing permissions and 17605 * limitations under the License. 17606 */ 17607 17608 // !depends ctype.js 17609 17610 // !data ctype 17611 17612 /** 17613 * Return whether or not the first character is a blank character.<p> 17614 * 17615 * Depends directive: !depends ctype.isblank.js 17616 * 17617 * ie. a space or a tab. 17618 * @param {string|ilib.String|number} ch character or code point to examine 17619 * @return {boolean} true if the first character is a blank character. 17620 */ 17621 ilib.CType.isBlank = function (ch) { 17622 var num; 17623 switch (typeof(ch)) { 17624 case 'number': 17625 num = ch; 17626 break; 17627 case 'string': 17628 num = ilib.String.toCodePoint(ch, 0); 17629 break; 17630 case 'undefined': 17631 return false; 17632 default: 17633 num = ch._toCodePoint(0); 17634 break; 17635 } 17636 return ilib.CType._inRange(num, 'blank', ilib.data.ctype); 17637 }; 17638 17639 /** 17640 * @protected 17641 * @param {boolean} sync 17642 * @param {Object} loadParams 17643 * @param {function(*)|undefined} onLoad 17644 */ 17645 ilib.CType.isBlank._init = function (sync, loadParams, onLoad) { 17646 ilib.CType._init(sync, loadParams, onLoad); 17647 }; 17648 17649 /* 17650 * ctype.iscntrl.js - Character type is control character 17651 * 17652 * Copyright © 2012-2013, JEDLSoft 17653 * 17654 * Licensed under the Apache License, Version 2.0 (the "License"); 17655 * you may not use this file except in compliance with the License. 17656 * You may obtain a copy of the License at 17657 * 17658 * http://www.apache.org/licenses/LICENSE-2.0 17659 * 17660 * Unless required by applicable law or agreed to in writing, software 17661 * distributed under the License is distributed on an "AS IS" BASIS, 17662 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17663 * 17664 * See the License for the specific language governing permissions and 17665 * limitations under the License. 17666 */ 17667 17668 // !depends ctype.js 17669 17670 // !data ctype_c 17671 17672 /** 17673 * Return whether or not the first character is a control character.<p> 17674 * 17675 * Depends directive: !depends ctype.iscntrl.js 17676 * 17677 * @param {string|ilib.String|number} ch character or code point to examine 17678 * @return {boolean} true if the first character is a control character. 17679 */ 17680 ilib.CType.isCntrl = function (ch) { 17681 var num; 17682 switch (typeof(ch)) { 17683 case 'number': 17684 num = ch; 17685 break; 17686 case 'string': 17687 num = ilib.String.toCodePoint(ch, 0); 17688 break; 17689 case 'undefined': 17690 return false; 17691 default: 17692 num = ch._toCodePoint(0); 17693 break; 17694 } 17695 return ilib.CType._inRange(num, 'Cc', ilib.data.ctype_c); 17696 }; 17697 17698 /** 17699 * @protected 17700 * @param {boolean} sync 17701 * @param {Object} loadParams 17702 * @param {function(*)|undefined} onLoad 17703 */ 17704 ilib.CType.isCntrl._init = function (sync, loadParams, onLoad) { 17705 ilib.CType._load("ctype_c", sync, loadParams, onLoad); 17706 }; 17707 17708 /* 17709 * ctype.isgraph.js - Character type is graph char 17710 * 17711 * Copyright © 2012-2013, JEDLSoft 17712 * 17713 * Licensed under the Apache License, Version 2.0 (the "License"); 17714 * you may not use this file except in compliance with the License. 17715 * You may obtain a copy of the License at 17716 * 17717 * http://www.apache.org/licenses/LICENSE-2.0 17718 * 17719 * Unless required by applicable law or agreed to in writing, software 17720 * distributed under the License is distributed on an "AS IS" BASIS, 17721 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17722 * 17723 * See the License for the specific language governing permissions and 17724 * limitations under the License. 17725 */ 17726 17727 // !depends ctype.js ctype.isspace.js ctype.iscntrl.js 17728 17729 /** 17730 * Return whether or not the first character is any printable character 17731 * other than space.<p> 17732 * 17733 * Depends directive: !depends ctype.isgraph.js 17734 * 17735 * @param {string|ilib.String|number} ch character or code point to examine 17736 * @return {boolean} true if the first character is any printable character 17737 * other than space. 17738 */ 17739 ilib.CType.isGraph = function (ch) { 17740 var num; 17741 switch (typeof(ch)) { 17742 case 'number': 17743 num = ch; 17744 break; 17745 case 'string': 17746 num = ilib.String.toCodePoint(ch, 0); 17747 break; 17748 case 'undefined': 17749 return false; 17750 default: 17751 num = ch._toCodePoint(0); 17752 break; 17753 } 17754 return typeof(ch) !== 'undefined' && ch.length > 0 && !ilib.CType.isSpace(num) && !ilib.CType.isCntrl(num); 17755 }; 17756 17757 /** 17758 * @protected 17759 * @param {boolean} sync 17760 * @param {Object} loadParams 17761 * @param {function(*)|undefined} onLoad 17762 */ 17763 ilib.CType.isGraph._init = function (sync, loadParams, onLoad) { 17764 ilib.CType.isSpace._init(sync, loadParams, function () { 17765 ilib.CType.isCntrl._init(sync, loadParams, onLoad); 17766 }); 17767 }; 17768 17769 /* 17770 * ctype.js - Character type definitions 17771 * 17772 * Copyright © 2012-2013, JEDLSoft 17773 * 17774 * Licensed under the Apache License, Version 2.0 (the "License"); 17775 * you may not use this file except in compliance with the License. 17776 * You may obtain a copy of the License at 17777 * 17778 * http://www.apache.org/licenses/LICENSE-2.0 17779 * 17780 * Unless required by applicable law or agreed to in writing, software 17781 * distributed under the License is distributed on an "AS IS" BASIS, 17782 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17783 * 17784 * See the License for the specific language governing permissions and 17785 * limitations under the License. 17786 */ 17787 17788 // !depends ctype.js 17789 17790 // !data ctype 17791 17792 /** 17793 * Return whether or not the first character is an ideographic character.<p> 17794 * 17795 * Depends directive: !depends ctype.isideo.js 17796 * 17797 * @param {string|ilib.String|number} ch character or code point to examine 17798 * @return {boolean} true if the first character is an ideographic character. 17799 */ 17800 ilib.CType.isIdeo = function (ch) { 17801 var num; 17802 switch (typeof(ch)) { 17803 case 'number': 17804 num = ch; 17805 break; 17806 case 'string': 17807 num = ilib.String.toCodePoint(ch, 0); 17808 break; 17809 case 'undefined': 17810 return false; 17811 default: 17812 num = ch._toCodePoint(0); 17813 break; 17814 } 17815 17816 return ilib.CType._inRange(num, 'cjk', ilib.data.ctype) || 17817 ilib.CType._inRange(num, 'cjkradicals', ilib.data.ctype) || 17818 ilib.CType._inRange(num, 'enclosedcjk', ilib.data.ctype) || 17819 ilib.CType._inRange(num, 'cjkpunct', ilib.data.ctype) || 17820 ilib.CType._inRange(num, 'cjkcompatibility', ilib.data.ctype); 17821 }; 17822 17823 /** 17824 * @protected 17825 * @param {boolean} sync 17826 * @param {Object} loadParams 17827 * @param {function(*)|undefined} onLoad 17828 */ 17829 ilib.CType.isIdeo._init = function (sync, loadParams, onLoad) { 17830 ilib.CType._init(sync, loadParams, onLoad); 17831 }; 17832 17833 /* 17834 * ctype.islower.js - Character type is lower case letter 17835 * 17836 * Copyright © 2012-2013, JEDLSoft 17837 * 17838 * Licensed under the Apache License, Version 2.0 (the "License"); 17839 * you may not use this file except in compliance with the License. 17840 * You may obtain a copy of the License at 17841 * 17842 * http://www.apache.org/licenses/LICENSE-2.0 17843 * 17844 * Unless required by applicable law or agreed to in writing, software 17845 * distributed under the License is distributed on an "AS IS" BASIS, 17846 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17847 * 17848 * See the License for the specific language governing permissions and 17849 * limitations under the License. 17850 */ 17851 17852 // !depends ctype.js 17853 17854 // !data ctype_l 17855 17856 /** 17857 * Return whether or not the first character is lower-case. For alphabetic 17858 * characters in scripts that do not make a distinction between upper- and 17859 * lower-case, this function always returns true.<p> 17860 * 17861 * Depends directive: !depends ctype.islower.js 17862 * 17863 * @param {string|ilib.String|number} ch character or code point to examine 17864 * @return {boolean} true if the first character is lower-case. 17865 */ 17866 ilib.CType.isLower = function (ch) { 17867 var num; 17868 switch (typeof(ch)) { 17869 case 'number': 17870 num = ch; 17871 break; 17872 case 'string': 17873 num = ilib.String.toCodePoint(ch, 0); 17874 break; 17875 case 'undefined': 17876 return false; 17877 default: 17878 num = ch._toCodePoint(0); 17879 break; 17880 } 17881 17882 return ilib.CType._inRange(num, 'Ll', ilib.data.ctype_l); 17883 }; 17884 17885 /** 17886 * @protected 17887 * @param {boolean} sync 17888 * @param {Object} loadParams 17889 * @param {function(*)|undefined} onLoad 17890 */ 17891 ilib.CType.isLower._init = function (sync, loadParams, onLoad) { 17892 ilib.CType._load("ctype_l", sync, loadParams, onLoad); 17893 }; 17894 17895 /* 17896 * ctype.isprint.js - Character type is printable char 17897 * 17898 * Copyright © 2012-2013, JEDLSoft 17899 * 17900 * Licensed under the Apache License, Version 2.0 (the "License"); 17901 * you may not use this file except in compliance with the License. 17902 * You may obtain a copy of the License at 17903 * 17904 * http://www.apache.org/licenses/LICENSE-2.0 17905 * 17906 * Unless required by applicable law or agreed to in writing, software 17907 * distributed under the License is distributed on an "AS IS" BASIS, 17908 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17909 * 17910 * See the License for the specific language governing permissions and 17911 * limitations under the License. 17912 */ 17913 17914 // !depends ctype.js ctype.iscntrl.js 17915 17916 /** 17917 * Return whether or not the first character is any printable character, 17918 * including space.<p> 17919 * 17920 * Depends directive: !depends ctype.isprint.js 17921 * 17922 * @param {string|ilib.String|number} ch character or code point to examine 17923 * @return {boolean} true if the first character is printable. 17924 */ 17925 ilib.CType.isPrint = function (ch) { 17926 return typeof(ch) !== 'undefined' && ch.length > 0 && !ilib.CType.isCntrl(ch); 17927 }; 17928 17929 /** 17930 * @protected 17931 * @param {boolean} sync 17932 * @param {Object} loadParams 17933 * @param {function(*)|undefined} onLoad 17934 */ 17935 ilib.CType.isPrint._init = function (sync, loadParams, onLoad) { 17936 ilib.CType.isCntrl._init(sync, loadParams, onLoad); 17937 }; 17938 17939 /* 17940 * ctype.ispunct.js - Character type is punctuation 17941 * 17942 * Copyright © 2012-2013, JEDLSoft 17943 * 17944 * Licensed under the Apache License, Version 2.0 (the "License"); 17945 * you may not use this file except in compliance with the License. 17946 * You may obtain a copy of the License at 17947 * 17948 * http://www.apache.org/licenses/LICENSE-2.0 17949 * 17950 * Unless required by applicable law or agreed to in writing, software 17951 * distributed under the License is distributed on an "AS IS" BASIS, 17952 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17953 * 17954 * See the License for the specific language governing permissions and 17955 * limitations under the License. 17956 */ 17957 17958 // !depends ctype.js 17959 17960 // !data ctype_p 17961 17962 /** 17963 * Return whether or not the first character is punctuation.<p> 17964 * 17965 * Depends directive: !depends ctype.isprint.js 17966 * 17967 * @param {string|ilib.String|number} ch character or code point to examine 17968 * @return {boolean} true if the first character is punctuation. 17969 */ 17970 ilib.CType.isPunct = function (ch) { 17971 var num; 17972 switch (typeof(ch)) { 17973 case 'number': 17974 num = ch; 17975 break; 17976 case 'string': 17977 num = ilib.String.toCodePoint(ch, 0); 17978 break; 17979 case 'undefined': 17980 return false; 17981 default: 17982 num = ch._toCodePoint(0); 17983 break; 17984 } 17985 17986 return ilib.CType._inRange(num, 'Pd', ilib.data.ctype_p) || 17987 ilib.CType._inRange(num, 'Ps', ilib.data.ctype_p) || 17988 ilib.CType._inRange(num, 'Pe', ilib.data.ctype_p) || 17989 ilib.CType._inRange(num, 'Pc', ilib.data.ctype_p) || 17990 ilib.CType._inRange(num, 'Po', ilib.data.ctype_p) || 17991 ilib.CType._inRange(num, 'Pi', ilib.data.ctype_p) || 17992 ilib.CType._inRange(num, 'Pf', ilib.data.ctype_p); 17993 }; 17994 17995 /** 17996 * @protected 17997 * @param {boolean} sync 17998 * @param {Object} loadParams 17999 * @param {function(*)|undefined} onLoad 18000 */ 18001 ilib.CType.isPunct._init = function (sync, loadParams, onLoad) { 18002 ilib.CType._load("ctype_p", sync, loadParams, onLoad); 18003 }; 18004 18005 /* 18006 * ctype.isupper.js - Character type is upper-case letter 18007 * 18008 * Copyright © 2012-2013, JEDLSoft 18009 * 18010 * Licensed under the Apache License, Version 2.0 (the "License"); 18011 * you may not use this file except in compliance with the License. 18012 * You may obtain a copy of the License at 18013 * 18014 * http://www.apache.org/licenses/LICENSE-2.0 18015 * 18016 * Unless required by applicable law or agreed to in writing, software 18017 * distributed under the License is distributed on an "AS IS" BASIS, 18018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18019 * 18020 * See the License for the specific language governing permissions and 18021 * limitations under the License. 18022 */ 18023 18024 // !depends ctype.js 18025 18026 // !data ctype_l 18027 18028 /** 18029 * Return whether or not the first character is upper-case. For alphabetic 18030 * characters in scripts that do not make a distinction between upper- and 18031 * lower-case, this function always returns true.<p> 18032 * 18033 * Depends directive: !depends ctype.isupper.js 18034 * 18035 * @param {string|ilib.String|number} ch character or code point to examine 18036 * @return {boolean} true if the first character is upper-case. 18037 */ 18038 ilib.CType.isUpper = function (ch) { 18039 var num; 18040 switch (typeof(ch)) { 18041 case 'number': 18042 num = ch; 18043 break; 18044 case 'string': 18045 num = ilib.String.toCodePoint(ch, 0); 18046 break; 18047 case 'undefined': 18048 return false; 18049 default: 18050 num = ch._toCodePoint(0); 18051 break; 18052 } 18053 18054 return ilib.CType._inRange(num, 'Lu', ilib.data.ctype_l); 18055 }; 18056 18057 /** 18058 * @protected 18059 * @param {boolean} sync 18060 * @param {Object} loadParams 18061 * @param {function(*)|undefined} onLoad 18062 */ 18063 ilib.CType.isUpper._init = function (sync, loadParams, onLoad) { 18064 ilib.CType._load("ctype_l", sync, loadParams, onLoad); 18065 }; 18066 18067 /* 18068 * ctype.isdigit.js - Character type is digit 18069 * 18070 * Copyright © 2012-2013, JEDLSoft 18071 * 18072 * Licensed under the Apache License, Version 2.0 (the "License"); 18073 * you may not use this file except in compliance with the License. 18074 * You may obtain a copy of the License at 18075 * 18076 * http://www.apache.org/licenses/LICENSE-2.0 18077 * 18078 * Unless required by applicable law or agreed to in writing, software 18079 * distributed under the License is distributed on an "AS IS" BASIS, 18080 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18081 * 18082 * See the License for the specific language governing permissions and 18083 * limitations under the License. 18084 */ 18085 18086 // !depends ctype.js 18087 18088 // !data ctype 18089 18090 /** 18091 * Return whether or not the first character is a hexadecimal digit written 18092 * in the Latin script. (0-9 or A-F)<p> 18093 * 18094 * Depends directive: !depends ctype.isxdigit.js 18095 * 18096 * @param {string|ilib.String|number} ch character or code point to examine 18097 * @return {boolean} true if the first character is a hexadecimal digit written 18098 * in the Latin script. 18099 */ 18100 ilib.CType.isXdigit = function (ch) { 18101 var num; 18102 switch (typeof(ch)) { 18103 case 'number': 18104 num = ch; 18105 break; 18106 case 'string': 18107 num = ilib.String.toCodePoint(ch, 0); 18108 break; 18109 case 'undefined': 18110 return false; 18111 default: 18112 num = ch._toCodePoint(0); 18113 break; 18114 } 18115 18116 return ilib.CType._inRange(num, 'xdigit', ilib.data.ctype); 18117 }; 18118 18119 /** 18120 * @protected 18121 * @param {boolean} sync 18122 * @param {Object} loadParams 18123 * @param {function(*)|undefined} onLoad 18124 */ 18125 ilib.CType.isXdigit._init = function (sync, loadParams, onLoad) { 18126 ilib.CType._init(sync, loadParams, onLoad); 18127 }; 18128 18129 /* 18130 * ctype.isscript.js - Character type is script 18131 * 18132 * Copyright © 2012-2013, JEDLSoft 18133 * 18134 * Licensed under the Apache License, Version 2.0 (the "License"); 18135 * you may not use this file except in compliance with the License. 18136 * You may obtain a copy of the License at 18137 * 18138 * http://www.apache.org/licenses/LICENSE-2.0 18139 * 18140 * Unless required by applicable law or agreed to in writing, software 18141 * distributed under the License is distributed on an "AS IS" BASIS, 18142 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18143 * 18144 * See the License for the specific language governing permissions and 18145 * limitations under the License. 18146 */ 18147 18148 // !depends ctype.js 18149 18150 // !data scriptToRange 18151 18152 /** 18153 * Return whether or not the first character in the given string is 18154 * in the given script. The script is given as the 4-letter ISO 18155 * 15924 script code.<p> 18156 * 18157 * Depends directive: !depends ctype.isscript.js 18158 * 18159 * @param {string|ilib.String|number} ch character or code point to examine 18160 * @param {string} script the 4-letter ISO 15924 to query against 18161 * @return {boolean} true if the first character is in the given script, and 18162 * false otherwise 18163 */ 18164 ilib.CType.isScript = function (ch, script) { 18165 var num; 18166 switch (typeof(ch)) { 18167 case 'number': 18168 num = ch; 18169 break; 18170 case 'string': 18171 num = ilib.String.toCodePoint(ch, 0); 18172 break; 18173 case 'undefined': 18174 return false; 18175 default: 18176 num = ch._toCodePoint(0); 18177 break; 18178 } 18179 18180 return ilib.CType._inRange(num, script, ilib.data.scriptToRange); 18181 }; 18182 18183 /** 18184 * @protected 18185 * @param {boolean} sync 18186 * @param {Object} loadParams 18187 * @param {function(*)|undefined} onLoad 18188 */ 18189 ilib.CType.isScript._init = function (sync, loadParams, onLoad) { 18190 ilib.CType._load("scriptToRange", sync, loadParams, onLoad); 18191 }; 18192 18193 18194 /* 18195 * scriptinfo.js - information about scripts 18196 * 18197 * Copyright © 2012-2014, JEDLSoft 18198 * 18199 * Licensed under the Apache License, Version 2.0 (the "License"); 18200 * you may not use this file except in compliance with the License. 18201 * You may obtain a copy of the License at 18202 * 18203 * http://www.apache.org/licenses/LICENSE-2.0 18204 * 18205 * Unless required by applicable law or agreed to in writing, software 18206 * distributed under the License is distributed on an "AS IS" BASIS, 18207 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18208 * 18209 * See the License for the specific language governing permissions and 18210 * limitations under the License. 18211 */ 18212 18213 // !depends ilibglobal.js 18214 18215 // !data scripts 18216 18217 /** 18218 * @class 18219 * Create a new script info instance. This class encodes information about 18220 * scripts, which are sets of characters used in a writing system.<p> 18221 * 18222 * The options object may contain any of the following properties: 18223 * 18224 * <ul> 18225 * <li><i>onLoad</i> - a callback function to call when the script info object is fully 18226 * loaded. When the onLoad option is given, the script info object will attempt to 18227 * load any missing locale data using the ilib loader callback. 18228 * When the constructor is done (even if the data is already preassembled), the 18229 * onLoad function is called with the current instance as a parameter, so this 18230 * callback can be used with preassembled or dynamic loading or a mix of the two. 18231 * 18232 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 18233 * asynchronously. If this option is given as "false", then the "onLoad" 18234 * callback must be given, as the instance returned from this constructor will 18235 * not be usable for a while. 18236 * 18237 * <li><i>loadParams</i> - an object containing parameters to pass to the 18238 * loader callback function when locale data is missing. The parameters are not 18239 * interpretted or modified in any way. They are simply passed along. The object 18240 * may contain any property/value pairs as long as the calling code is in 18241 * agreement with the loader callback function as to what those parameters mean. 18242 * </ul> 18243 * 18244 * Depends directive: !depends scriptinfo.js 18245 * 18246 * @constructor 18247 * @param {string} script The ISO 15924 4-letter identifier for the script 18248 * @param {Object} options parameters to initialize this matcher 18249 */ 18250 ilib.ScriptInfo = function(script, options) { 18251 var sync = true, 18252 loadParams = undefined; 18253 18254 this.script = script; 18255 18256 if (options) { 18257 if (typeof(options.sync) !== 'undefined') { 18258 sync = (options.sync == true); 18259 } 18260 18261 if (typeof(options.loadParams) !== 'undefined') { 18262 loadParams = options.loadParams; 18263 } 18264 } 18265 18266 if (!ilib.ScriptInfo.cache) { 18267 ilib.ScriptInfo.cache = {}; 18268 } 18269 18270 if (!ilib.data.scripts) { 18271 ilib.loadData({ 18272 object: ilib.ScriptInfo, 18273 locale: "-", 18274 name: "scripts.json", 18275 sync: sync, 18276 loadParams: loadParams, 18277 callback: ilib.bind(this, function (info) { 18278 if (!info) { 18279 info = {"Latn":{"nb":215,"nm":"Latin","lid":"Latin","rtl":false,"ime":false,"casing":true}}; 18280 var spec = this.locale.getSpec().replace(/-/g, "_"); 18281 ilib.ScriptInfo.cache[spec] = info; 18282 } 18283 ilib.data.scripts = info; 18284 this.info = script && ilib.data.scripts[script]; 18285 if (options && typeof(options.onLoad) === 'function') { 18286 options.onLoad(this); 18287 } 18288 }) 18289 }); 18290 } else { 18291 this.info = ilib.data.scripts[script]; 18292 } 18293 18294 }; 18295 18296 /** 18297 * Return an array of all ISO 15924 4-letter identifier script identifiers that 18298 * this copy of ilib knows about. 18299 * @static 18300 * @return {Array.<string>} an array of all script identifiers that this copy of 18301 * ilib knows about 18302 */ 18303 ilib.ScriptInfo.getAllScripts = function() { 18304 var ret = [], 18305 script = undefined, 18306 scripts = ilib.data.scripts; 18307 18308 for (script in scripts) { 18309 if (script && scripts[script]) { 18310 ret.push(script); 18311 } 18312 } 18313 18314 return ret; 18315 }; 18316 18317 ilib.ScriptInfo.prototype = { 18318 /** 18319 * Return the 4-letter ISO 15924 identifier associated 18320 * with this script. 18321 * @return {string} the 4-letter ISO code for this script 18322 */ 18323 getCode: function () { 18324 return this.info && this.script; 18325 }, 18326 18327 /** 18328 * Get the ISO 15924 code number associated with this 18329 * script. 18330 * 18331 * @return {number} the ISO 15924 code number 18332 */ 18333 getCodeNumber: function () { 18334 return this.info && this.info.nb || 0; 18335 }, 18336 18337 /** 18338 * Get the name of this script in English. 18339 * 18340 * @return {string} the name of this script in English 18341 */ 18342 getName: function () { 18343 return this.info && this.info.nm; 18344 }, 18345 18346 /** 18347 * Get the long identifier assciated with this script. 18348 * 18349 * @return {string} the long identifier of this script 18350 */ 18351 getLongCode: function () { 18352 return this.info && this.info.lid; 18353 }, 18354 18355 /** 18356 * Return the usual direction that text in this script is written 18357 * in. Possible return values are "rtl" for right-to-left, 18358 * "ltr" for left-to-right, and "ttb" for top-to-bottom. 18359 * 18360 * @return {string} the usual direction that text in this script is 18361 * written in 18362 */ 18363 getScriptDirection: function() { 18364 return (this.info && typeof(this.info.rtl) !== 'undefined' && this.info.rtl) ? "rtl" : "ltr"; 18365 }, 18366 18367 /** 18368 * Return true if this script typically requires an input method engine 18369 * to enter its characters. 18370 * 18371 * @return {boolean} true if this script typically requires an IME 18372 */ 18373 getNeedsIME: function () { 18374 return this.info && this.info.ime ? true : false; // converts undefined to false 18375 }, 18376 18377 /** 18378 * Return true if this script uses lower- and upper-case characters. 18379 * 18380 * @return {boolean} true if this script uses letter case 18381 */ 18382 getCasing: function () { 18383 return this.info && this.info.casing ? true : false; // converts undefined to false 18384 } 18385 }; 18386 /* 18387 * nameprs.js - Person name parser 18388 * 18389 * Copyright © 2013-2014, JEDLSoft 18390 * 18391 * Licensed under the Apache License, Version 2.0 (the "License"); 18392 * you may not use this file except in compliance with the License. 18393 * You may obtain a copy of the License at 18394 * 18395 * http://www.apache.org/licenses/LICENSE-2.0 18396 * 18397 * Unless required by applicable law or agreed to in writing, software 18398 * distributed under the License is distributed on an "AS IS" BASIS, 18399 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18400 * 18401 * See the License for the specific language governing permissions and 18402 * limitations under the License. 18403 */ 18404 18405 /* !depends 18406 ilibglobal.js 18407 locale.js 18408 util/utils.js 18409 ctype.isalpha.js 18410 ctype.isideo.js 18411 ctype.ispunct.js 18412 ctype.isspace.js 18413 util/jsutils.js 18414 */ 18415 18416 // !data name 18417 18418 // notes: 18419 // icelandic given names: http://en.wiktionary.org/wiki/Appendix:Icelandic_given_names 18420 // danish approved given names: http://www.familiestyrelsen.dk/samliv/navne/ 18421 // http://www.mentalfloss.com/blogs/archives/59277 18422 // other countries with first name restrictions: Norway, China, New Zealand, Japan, Sweden, Germany, Hungary 18423 18424 /** 18425 * @class 18426 * A class to parse names of people. Different locales have different conventions when it 18427 * comes to naming people.<p> 18428 * 18429 * The options can contain any of the following properties: 18430 * 18431 * <ul> 18432 * <li><i>locale</i> - use the rules and conventions of the given locale in order to parse 18433 * the name 18434 * <li><i>style</i> - explicitly use the named style to parse the name. Valid values so 18435 * far are "western" and "asian". If this property is not specified, then the style will 18436 * be gleaned from the name itself. This class will count the total number of Latin or Asian 18437 * characters. If the majority of the characters are in one style, that style will be 18438 * used to parse the whole name. 18439 * <li><i>order</i> - explicitly use the given order for names. In some locales, such 18440 * as Russian, names may be written equally validly as "givenName familyName" or "familyName 18441 * givenName". This option tells the parser which order to prefer, and overrides the 18442 * default order for the locale. Valid values are "gf" (given-family) or "fg" (family-given). 18443 * <li><i>useSpaces</i> - explicitly specifies whether to use spaces or not between the given name , middle name 18444 * and family name. 18445 * <li>onLoad - a callback function to call when the name info is fully 18446 * loaded and the name has been parsed. When the onLoad option is given, the name object 18447 * will attempt to load any missing locale data using the ilib loader callback. 18448 * When the constructor is done (even if the data is already preassembled), the 18449 * onLoad function is called with the current instance as a parameter, so this 18450 * callback can be used with preassembled or dynamic loading or a mix of the two. 18451 * 18452 * <li>sync - tell whether to load any missing locale data synchronously or 18453 * asynchronously. If this option is given as "false", then the "onLoad" 18454 * callback must be given, as the instance returned from this constructor will 18455 * not be usable for a while. 18456 * 18457 * <li><i>loadParams</i> - an object containing parameters to pass to the 18458 * loader callback function when locale data is missing. The parameters are not 18459 * interpretted or modified in any way. They are simply passed along. The object 18460 * may contain any property/value pairs as long as the calling code is in 18461 * agreement with the loader callback function as to what those parameters mean. 18462 * </ul> 18463 * 18464 * When the parser has completed its parsing, it fills in the fields listed below.<p> 18465 * 18466 * For names that include auxilliary words, such as the family name "van der Heijden", all 18467 * of the auxilliary words ("van der") will be included in the field.<p> 18468 * 18469 * For names in Spanish locales, it is assumed that the family name is doubled. That is, 18470 * a person may have a paternal family name followed by a maternal family name. All 18471 * family names will be listed in the familyName field as normal, separated by spaces. 18472 * When formatting the short version of such names, only the paternal family name will 18473 * be used. 18474 * 18475 * Depends directive: !depends nameprs.js 18476 * 18477 * @constructor 18478 * @param {string|ilib.Name=} name the name to parse 18479 * @param {Object=} options Options governing the construction of this name instance 18480 */ 18481 ilib.Name = function (name, options) { 18482 var sync = true; 18483 18484 if (!name || name.length === 0) { 18485 return; 18486 } 18487 18488 this.loadParams = {}; 18489 18490 if (options) { 18491 if (options.locale) { 18492 this.locale = (typeof (options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 18493 } 18494 18495 if (options.style && (options.style === "asian" || options.style === "western")) { 18496 this.style = options.style; 18497 } 18498 18499 if (options.order && (options.order === "gmf" || options.order === "fmg" || options.order === "fgm")) { 18500 this.order = options.order; 18501 } 18502 18503 if (typeof (options.sync) !== 'undefined') { 18504 sync = (options.sync == true); 18505 } 18506 18507 if (typeof (options.loadParams) !== 'undefined') { 18508 this.loadParams = options.loadParams; 18509 } 18510 } 18511 18512 if (!ilib.Name.cache) { 18513 ilib.Name.cache = {}; 18514 } 18515 18516 this.locale = this.locale || new ilib.Locale(); 18517 18518 ilib.CType.isAlpha._init(sync, this.loadParams, /** @type {function()|undefined} */ ilib.bind(this, function() { 18519 ilib.CType.isIdeo._init(sync, this.loadParams, /** @type {function()|undefined} */ ilib.bind(this, function() { 18520 ilib.CType.isPunct._init(sync, this.loadParams, /** @type {function()|undefined} */ ilib.bind(this, function() { 18521 ilib.CType.isSpace._init(sync, this.loadParams, /** @type {function()|undefined} */ ilib.bind(this, function() { 18522 ilib.loadData({ 18523 object: ilib.Name, 18524 locale: this.locale, 18525 name: "name.json", 18526 sync: sync, 18527 loadParams: this.loadParams, 18528 callback: ilib.bind(this, function (info) { 18529 if (!info) { 18530 info = ilib.Name.defaultInfo; 18531 var spec = this.locale.getSpec().replace(/-/g, "_"); 18532 ilib.Name.cache[spec] = info; 18533 } 18534 if (typeof (name) === 'object') { 18535 // copy constructor 18536 /** 18537 * The prefixes for this name 18538 * @type {string|Array.<string>} 18539 */ 18540 this.prefix = name.prefix; 18541 /** 18542 * The given (personal) name in this name. 18543 * @type {string|Array.<string>} 18544 */ 18545 this.givenName = name.givenName; 18546 /** 18547 * The middle names used in this name. If there are multiple middle names, they all 18548 * appear in this field separated by spaces. 18549 * @type {string|Array.<string>} 18550 */ 18551 this.middleName = name.middleName; 18552 /** 18553 * The family names in this name. If there are multiple family names, they all 18554 * appear in this field separated by spaces. 18555 * @type {string|Array.<string>} 18556 */ 18557 this.familyName = name.familyName; 18558 /** 18559 * The suffixes for this name. If there are multiple suffixes, they all 18560 * appear in this field separated by spaces. 18561 * @type {string|Array.<string>} 18562 */ 18563 this.suffix = name.suffix; 18564 18565 // private properties 18566 this.locale = name.locale; 18567 this.style = name.style; 18568 this.order = name.order; 18569 this.useSpaces = name.useSpaces; 18570 this.isAsianName = name.isAsianName; 18571 return; 18572 } 18573 /** 18574 * @type {{ 18575 * nameStyle:string, 18576 * order:string, 18577 * prefixes:Array.<string>, 18578 * suffixes:Array.<string>, 18579 * auxillaries:Array.<string>, 18580 * honorifics:Array.<string>, 18581 * knownFamilyNames:Array.<string>, 18582 * noCompoundFamilyNames:boolean, 18583 * sortByHeadWord:boolean 18584 * }} */ 18585 this.info = info; 18586 this._init(name); 18587 if (options && typeof(options.onLoad) === 'function') { 18588 options.onLoad(this); 18589 } 18590 }) 18591 }); 18592 })); 18593 })); 18594 })); 18595 })); 18596 }; 18597 18598 ilib.Name.defaultInfo = ilib.data.name || { 18599 "components": { 18600 "short": { 18601 "g": 1, 18602 "f": 1 18603 }, 18604 "medium": { 18605 "g": 1, 18606 "m": 1, 18607 "f": 1 18608 }, 18609 "long": { 18610 "p": 1, 18611 "g": 1, 18612 "m": 1, 18613 "f": 1 18614 }, 18615 "full": { 18616 "p": 1, 18617 "g": 1, 18618 "m": 1, 18619 "f": 1, 18620 "s": 1 18621 } 18622 }, 18623 "format": "{prefix} {givenName} {middleName} {familyName}{suffix}", 18624 "sortByHeadWord": false, 18625 "nameStyle": "western", 18626 "conjunctions": { 18627 "and1": "and", 18628 "and2": "and", 18629 "or1": "or", 18630 "or2": "or" 18631 }, 18632 "auxillaries": { 18633 "von": 1, 18634 "von der": 1, 18635 "von den": 1, 18636 "van": 1, 18637 "van der": 1, 18638 "van de": 1, 18639 "van den": 1, 18640 "de": 1, 18641 "di": 1, 18642 "de": 1, 18643 "la": 1, 18644 "lo": 1, 18645 "des": 1, 18646 "le": 1, 18647 "les": 1, 18648 "du": 1, 18649 "de la": 1, 18650 "del": 1, 18651 "de los": 1, 18652 "de las": 1 18653 }, 18654 "prefixes": [ 18655 "doctor", 18656 "dr", 18657 "mr", 18658 "mrs", 18659 "ms", 18660 "mister", 18661 "madame", 18662 "madamoiselle", 18663 "miss", 18664 "monsieur", 18665 "señor", 18666 "señora", 18667 "señorita" 18668 ], 18669 "suffixes": [ 18670 ",", 18671 "junior", 18672 "jr", 18673 "senior", 18674 "sr", 18675 "i", 18676 "ii", 18677 "iii", 18678 "esq", 18679 "phd", 18680 "md" 18681 ], 18682 "patronymicName":[ ], 18683 "familyNames":[ ] 18684 }; 18685 18686 /** 18687 * Return true if the given character is in the range of the Han, Hangul, or kana 18688 * scripts. 18689 * @static 18690 * @protected 18691 */ 18692 ilib.Name._isAsianChar = function(c) { 18693 return ilib.CType.isIdeo(c) || 18694 ilib.CType.withinRange(c, "hangul") || 18695 ilib.CType.withinRange(c, "hiragana") || 18696 ilib.CType.withinRange(c, "katakana"); 18697 }; 18698 18699 18700 /** 18701 * @static 18702 * @protected 18703 */ 18704 ilib.Name._isAsianName = function (name, language) { 18705 // the idea is to count the number of asian chars and the number 18706 // of latin chars. If one is greater than the other, choose 18707 // that style. 18708 var asian = 0, 18709 latin = 0, 18710 i; 18711 18712 if (name && name.length > 0) { 18713 for (i = 0; i < name.length; i++) { 18714 var c = name.charAt(i); 18715 18716 if (ilib.Name._isAsianChar(c)) { 18717 if (language =="ko" || language =="ja" || language =="zh") { 18718 return true; 18719 } 18720 asian++; 18721 } else if (ilib.CType.isAlpha(c)) { 18722 if (!language =="ko" || !language =="ja" || !language =="zh") { 18723 return false; 18724 } 18725 latin++; 18726 } 18727 } 18728 18729 return latin < asian; 18730 } 18731 18732 return false; 18733 }; 18734 18735 /** 18736 * Return true if any Latin letters are found in the string. Return 18737 * false if all the characters are non-Latin. 18738 * @static 18739 * @protected 18740 */ 18741 ilib.Name._isEuroName = function (name, language) { 18742 var c, 18743 n = new ilib.String(name), 18744 it = n.charIterator(); 18745 18746 while (it.hasNext()) { 18747 c = it.next(); 18748 18749 if (!ilib.Name._isAsianChar(c) && !ilib.CType.isPunct(c) && !ilib.CType.isSpace(c)) { 18750 return true; 18751 } else if (ilib.Name._isAsianChar(c) && (language =="ko" || language =="ja" || language =="zh")) { 18752 return false; 18753 } 18754 } 18755 return false; 18756 }; 18757 18758 ilib.Name.prototype = { 18759 /** 18760 * @protected 18761 */ 18762 _init: function (name) { 18763 var parts, prefixArray, prefix, prefixLower, 18764 suffixArray, suffix, suffixLower, 18765 i, info, hpSuffix; 18766 var currentLanguage = this.locale.getLanguage(); 18767 18768 if (name) { 18769 // for DFISH-12905, pick off the part that the LDAP server automatically adds to our names in HP emails 18770 i = name.search(/\s*[,\/\(\[\{<]/); 18771 if (i !== -1) { 18772 hpSuffix = name.substring(i); 18773 hpSuffix = hpSuffix.replace(/\s+/g, ' '); // compress multiple whitespaces 18774 suffixArray = hpSuffix.split(" "); 18775 var conjunctionIndex = this._findLastConjunction(suffixArray); 18776 if (conjunctionIndex > -1) { 18777 // it's got conjunctions in it, so this is not really a suffix 18778 hpSuffix = undefined; 18779 } else { 18780 name = name.substring(0, i); 18781 } 18782 } 18783 18784 this.isAsianName = ilib.Name._isAsianName(name, currentLanguage); 18785 if (this.info.nameStyle === "asian" || this.info.order === "fmg" || this.info.order === "fgm") { 18786 info = this.isAsianName ? this.info : ilib.data.name; 18787 } else { 18788 info = this.isAsianName ? ilib.data.name : this.info; 18789 } 18790 18791 if (this.isAsianName) { 18792 // all-asian names 18793 if (this.useSpaces == false) { 18794 name = name.replace(/\s+/g, ''); // eliminate all whitespaces 18795 } 18796 parts = name.trim().split(''); 18797 } 18798 //} 18799 else { 18800 name = name.replace(/, /g, ' , '); 18801 name = name.replace(/\s+/g, ' '); // compress multiple whitespaces 18802 parts = name.trim().split(' '); 18803 } 18804 18805 // check for prefixes 18806 if (parts.length > 1) { 18807 for (i = parts.length; i > 0; i--) { 18808 prefixArray = parts.slice(0, i); 18809 prefix = prefixArray.join(this.isAsianName ? '' : ' '); 18810 prefixLower = prefix.toLowerCase(); 18811 prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods 18812 if (this.info.prefixes && 18813 (this.info.prefixes.indexOf(prefixLower) > -1 || this._isConjunction(prefixLower))) { 18814 if (this.prefix) { 18815 if (!this.isAsianName) { 18816 this.prefix += ' '; 18817 } 18818 this.prefix += prefix; 18819 } else { 18820 this.prefix = prefix; 18821 } 18822 parts = parts.slice(i); 18823 i = parts.length; 18824 } 18825 } 18826 } 18827 // check for suffixes 18828 if (parts.length > 1) { 18829 for (i = parts.length; i > 0; i--) { 18830 suffixArray = parts.slice(-i); 18831 suffix = suffixArray.join(this.isAsianName ? '' : ' '); 18832 suffixLower = suffix.toLowerCase(); 18833 suffixLower = suffixLower.replace(/[\.]/g, ''); // ignore periods 18834 if (this.info.suffixes && this.info.suffixes.indexOf(suffixLower) > -1) { 18835 if (this.suffix) { 18836 if (!this.isAsianName && !ilib.CType.isPunct(this.suffix.charAt(0))) { 18837 this.suffix = ' ' + this.suffix; 18838 } 18839 this.suffix = suffix + this.suffix; 18840 } else { 18841 this.suffix = suffix; 18842 } 18843 parts = parts.slice(0, parts.length - i); 18844 i = parts.length; 18845 } 18846 } 18847 } 18848 18849 if (hpSuffix) { 18850 this.suffix = (this.suffix && this.suffix + hpSuffix) || hpSuffix; 18851 } 18852 18853 // adjoin auxillary words to their headwords 18854 if (parts.length > 1 && !this.isAsianName) { 18855 parts = this._joinAuxillaries(parts, this.isAsianName); 18856 } 18857 18858 if (this.isAsianName) { 18859 this._parseAsianName(parts, currentLanguage); 18860 } else { 18861 this._parseWesternName(parts); 18862 } 18863 18864 this._joinNameArrays(); 18865 } 18866 }, 18867 18868 /** 18869 * @return {number} 18870 * 18871 _findSequence: function(parts, hash, isAsian) { 18872 var sequence, sequenceLower, sequenceArray, aux = [], i, ret = {}; 18873 18874 if (parts.length > 0 && hash) { 18875 //console.info("_findSequence: finding sequences"); 18876 for (var start = 0; start < parts.length-1; start++) { 18877 for ( i = parts.length; i > start; i-- ) { 18878 sequenceArray = parts.slice(start, i); 18879 sequence = sequenceArray.join(isAsian ? '' : ' '); 18880 sequenceLower = sequence.toLowerCase(); 18881 sequenceLower = sequenceLower.replace(/[,\.]/g, ''); // ignore commas and periods 18882 18883 //console.info("_findSequence: checking sequence: '" + sequenceLower + "'"); 18884 18885 if ( sequenceLower in hash ) { 18886 ret.match = sequenceArray; 18887 ret.start = start; 18888 ret.end = i; 18889 return ret; 18890 //console.info("_findSequence: Found sequence '" + sequence + "' New parts list is " + JSON.stringify(parts)); 18891 } 18892 } 18893 } 18894 } 18895 18896 return undefined; 18897 }, 18898 */ 18899 18900 /** 18901 * @protected 18902 * @param {Array} parts 18903 * @param {Array} names 18904 * @param {boolean} isAsian 18905 * @param {boolean=} noCompoundPrefix 18906 */ 18907 _findPrefix: function (parts, names, isAsian, noCompoundPrefix) { 18908 var i, prefix, prefixLower, prefixArray, aux = []; 18909 18910 if (parts.length > 0 && names) { 18911 for (i = parts.length; i > 0; i--) { 18912 prefixArray = parts.slice(0, i); 18913 prefix = prefixArray.join(isAsian ? '' : ' '); 18914 prefixLower = prefix.toLowerCase(); 18915 prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods 18916 18917 if (prefixLower in names) { 18918 aux = aux.concat(isAsian ? prefix : prefixArray); 18919 if (noCompoundPrefix) { 18920 // don't need to parse further. Just return it as is. 18921 return aux; 18922 } 18923 parts = parts.slice(i); 18924 i = parts.length + 1; 18925 } 18926 } 18927 } 18928 18929 return aux; 18930 }, 18931 18932 /** 18933 * @protected 18934 */ 18935 _findSuffix: function (parts, names, isAsian) { 18936 var i, j, seq = ""; 18937 18938 for (i = 0; i < names.length; i++) { 18939 if (parts.length >= names[i].length) { 18940 j = 0; 18941 while (j < names[i].length && parts[parts.length - j] === names[i][names[i].length - j]) { 18942 j++; 18943 } 18944 if (j >= names[i].length) { 18945 seq = parts.slice(parts.length - j).join(isAsian ? "" : " ") + (isAsian ? "" : " ") + seq; 18946 parts = parts.slice(0, parts.length - j); 18947 i = -1; // restart the search 18948 } 18949 } 18950 } 18951 18952 this.suffix = seq; 18953 return parts; 18954 }, 18955 18956 /** 18957 * @protected 18958 * Tell whether or not the given word is a conjunction in this language. 18959 * @param {string} word the word to test 18960 * @return {boolean} true if the word is a conjunction 18961 */ 18962 _isConjunction: function _isConjunction(word) { 18963 return (this.info.conjunctions.and1 === word || 18964 this.info.conjunctions.and2 === word || 18965 this.info.conjunctions.or1 === word || 18966 this.info.conjunctions.or2 === word || 18967 ("&" === word) || 18968 ("+" === word)); 18969 }, 18970 18971 /** 18972 * Find the last instance of 'and' in the name 18973 * @protected 18974 * @param {Array.<string>} parts 18975 * @return {number} 18976 */ 18977 _findLastConjunction: function _findLastConjunction(parts) { 18978 var conjunctionIndex = -1, 18979 index, part; 18980 18981 for (index = 0; index < parts.length; index++) { 18982 part = parts[index]; 18983 if (typeof (part) === 'string') { 18984 part = part.toLowerCase(); 18985 // also recognize English 18986 if ("and" === part || "or" === part || "&" === part || "+" === part) { 18987 conjunctionIndex = index; 18988 } 18989 if (this._isConjunction(part)) { 18990 conjunctionIndex = index; 18991 } 18992 } 18993 } 18994 return conjunctionIndex; 18995 }, 18996 18997 /** 18998 * @protected 18999 * @param {Array.<string>} parts the current array of name parts 19000 * @param {boolean} isAsian true if the name is being parsed as an Asian name 19001 * @return {Array.<string>} the remaining parts after the prefixes have been removed 19002 */ 19003 _extractPrefixes: function (parts, isAsian) { 19004 var i = this._findPrefix(parts, this.info.prefixes, isAsian); 19005 if (i > 0) { 19006 this.prefix = parts.slice(0, i).join(isAsian ? "" : " "); 19007 return parts.slice(i); 19008 } 19009 // prefixes not found, so just return the array unmodified 19010 return parts; 19011 }, 19012 19013 /** 19014 * @protected 19015 * @param {Array.<string>} parts the current array of name parts 19016 * @param {boolean} isAsian true if the name is being parsed as an Asian name 19017 * @return {Array.<string>} the remaining parts after the suffices have been removed 19018 */ 19019 _extractSuffixes: function (parts, isAsian) { 19020 var i = this._findSuffix(parts, this.info.suffixes, isAsian); 19021 if (i > 0) { 19022 this.suffix = parts.slice(i).join(isAsian ? "" : " "); 19023 return parts.slice(0, i); 19024 } 19025 // suffices not found, so just return the array unmodified 19026 return parts; 19027 }, 19028 19029 /** 19030 * Adjoin auxillary words to their head words. 19031 * @protected 19032 * @param {Array.<string>} parts the current array of name parts 19033 * @param {boolean} isAsian true if the name is being parsed as an Asian name 19034 * @return {Array.<string>} the parts after the auxillary words have been plucked onto their head word 19035 */ 19036 _joinAuxillaries: function (parts, isAsian) { 19037 var start, i, prefixArray, prefix, prefixLower; 19038 19039 if (this.info.auxillaries && (parts.length > 2 || this.prefix)) { 19040 for (start = 0; start < parts.length - 1; start++) { 19041 for (i = parts.length; i > start; i--) { 19042 prefixArray = parts.slice(start, i); 19043 prefix = prefixArray.join(' '); 19044 prefixLower = prefix.toLowerCase(); 19045 prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods 19046 19047 if (prefixLower in this.info.auxillaries) { 19048 parts.splice(start, i + 1 - start, prefixArray.concat(parts[i])); 19049 i = start; 19050 } 19051 } 19052 } 19053 } 19054 19055 return parts; 19056 }, 19057 19058 /** 19059 * Recursively join an array or string into a long string. 19060 * @protected 19061 */ 19062 _joinArrayOrString: function _joinArrayOrString(part) { 19063 var i; 19064 if (typeof (part) === 'object') { 19065 for (i = 0; i < part.length; i++) { 19066 part[i] = this._joinArrayOrString(part[i]); 19067 } 19068 var ret = ""; 19069 part.forEach(function (segment) { 19070 if (ret.length > 0 && !ilib.CType.isPunct(segment.charAt(0))) { 19071 ret += ' '; 19072 } 19073 ret += segment; 19074 }); 19075 19076 return ret; 19077 } 19078 19079 return part; 19080 }, 19081 19082 /** 19083 * @protected 19084 */ 19085 _joinNameArrays: function _joinNameArrays() { 19086 var prop; 19087 for (prop in this) { 19088 19089 if (this[prop] !== undefined && typeof (this[prop]) === 'object' && this[prop] instanceof Array) { 19090 19091 this[prop] = this._joinArrayOrString(this[prop]); 19092 } 19093 } 19094 }, 19095 19096 /** 19097 * @protected 19098 */ 19099 _parseAsianName: function (parts, language) { 19100 var familyNameArray = this._findPrefix(parts, this.info.knownFamilyNames, true, this.info.noCompoundFamilyNames); 19101 var tempFullName = parts.join(''); 19102 19103 if (familyNameArray && familyNameArray.length > 0) { 19104 this.familyName = familyNameArray.join(''); 19105 this.givenName = parts.slice(this.familyName.length).join(''); 19106 19107 //Overide parsing rules if spaces are found in korean 19108 if (language === "ko" && tempFullName.search(/\s*[/\s]/) > -1 && !this.suffix) { 19109 this._parseKoreanName(tempFullName); 19110 } 19111 } else if (this.locale.getLanguage() === "ja") { 19112 this._parseJapaneseName(parts); 19113 } else if (this.suffix || this.prefix) { 19114 this.familyName = parts.join(''); 19115 } else { 19116 this.givenName = parts.join(''); 19117 } 19118 }, 19119 19120 /** 19121 * @protected 19122 */ 19123 _parseKoreanName: function (name) { 19124 var tempName = name; 19125 19126 var spaceSplit = tempName.split(" "); 19127 var spceCount = spaceSplit.length; 19128 var fistSpaceIndex = tempName.indexOf(" "); 19129 var lastSpaceIndex = tempName.lastIndexOf(" "); 19130 19131 if (spceCount === 2) { 19132 this.familyName = spaceSplit[0]; 19133 this.givenName = tempName.slice(fistSpaceIndex, tempName.length); 19134 } else { 19135 this.familyName = spaceSplit[0]; 19136 this.middleName = tempName.slice(fistSpaceIndex, lastSpaceIndex); 19137 this.givenName = tempName.slice(lastSpaceIndex, tempName.length); 19138 } 19139 19140 }, 19141 19142 /** 19143 * @protected 19144 */ 19145 _parseJapaneseName: function (parts) { 19146 if (this.suffix && this.suffix.length > 1 && this.info.honorifics.indexOf(this.suffix)>-1) { 19147 if (parts.length === 1) { 19148 if (ilib.CType.withinRange(parts[0], "cjk")) { 19149 this.familyName = parts[0]; 19150 } else { 19151 this.givenName = parts[0]; 19152 } 19153 return; 19154 } else if (parts.length === 2) { 19155 this.familyName = parts.slice(0,parts.length).join("") 19156 return; 19157 } 19158 } 19159 if (parts.length > 1) { 19160 var fn = ""; 19161 for (var i = 0; i < parts.length; i++) { 19162 if (ilib.CType.withinRange(parts[i], "cjk")) { 19163 fn += parts[i]; 19164 } else if (fn.length > 1 && ilib.CType.withinRange(parts[i], "hiragana")) { 19165 this.familyName = fn; 19166 this.givenName = parts.slice(i,parts.length).join(""); 19167 return; 19168 } else { 19169 break; 19170 } 19171 } 19172 } 19173 if (parts.length === 1) { 19174 this.familyName = parts[0]; 19175 } else if (parts.length === 2) { 19176 this.familyName = parts[0]; 19177 this.givenName = parts[1]; 19178 } else if (parts.length === 3) { 19179 this.familyName = parts[0]; 19180 this.givenName = parts.slice(1,parts.length).join(""); 19181 } else if (parts.length > 3) { 19182 this.familyName = parts.slice(0,2).join("") 19183 this.givenName = parts.slice(2,parts.length).join(""); 19184 } 19185 }, 19186 19187 /** 19188 * @protected 19189 */ 19190 _parseSpanishName: function (parts) { 19191 var conjunctionIndex; 19192 19193 if (parts.length === 1) { 19194 if (this.prefix || typeof (parts[0]) === 'object') { 19195 this.familyName = parts[0]; 19196 } else { 19197 this.givenName = parts[0]; 19198 } 19199 } else if (parts.length === 2) { 19200 // we do G F 19201 this.givenName = parts[0]; 19202 this.familyName = parts[1]; 19203 } else if (parts.length === 3) { 19204 conjunctionIndex = this._findLastConjunction(parts); 19205 // if there's an 'and' in the middle spot, put everything in the first name 19206 if (conjunctionIndex === 1) { 19207 this.givenName = parts; 19208 } else { 19209 // else, do G F F 19210 this.givenName = parts[0]; 19211 this.familyName = parts.slice(1); 19212 } 19213 } else if (parts.length > 3) { 19214 //there are at least 4 parts to this name 19215 19216 conjunctionIndex = this._findLastConjunction(parts); 19217 ////console.log("@@@@@@@@@@@@@@@@"+conjunctionIndex) 19218 if (conjunctionIndex > 0) { 19219 // if there's a conjunction that's not the first token, put everything up to and 19220 // including the token after it into the first name, the last 2 tokens into 19221 // the family name (if they exist) and everything else in to the middle name 19222 // 0 1 2 3 4 5 19223 // G A G 19224 // G A G F 19225 // G G A G 19226 // G A G F F 19227 // G G A G F 19228 // G G G A G 19229 // G A G M F F 19230 // G G A G F F 19231 // G G G A G F 19232 // G G G G A G 19233 this.givenName = parts.splice(0, conjunctionIndex + 2); 19234 if (parts.length > 1) { 19235 this.familyName = parts.splice(parts.length - 2, 2); 19236 if (parts.length > 0) { 19237 this.middleName = parts; 19238 } 19239 } else if (parts.length === 1) { 19240 this.familyName = parts[0]; 19241 } 19242 } else { 19243 this.givenName = parts.splice(0, 1); 19244 this.familyName = parts.splice(parts.length - 2, 2); 19245 this.middleName = parts; 19246 } 19247 } 19248 }, 19249 19250 /** 19251 * @protected 19252 */ 19253 _parseIndonesianName: function (parts) { 19254 var conjunctionIndex; 19255 19256 if (parts.length === 1) { 19257 //if (this.prefix || typeof(parts[0]) === 'object') { 19258 //this.familyName = parts[0]; 19259 //} else { 19260 this.givenName = parts[0]; 19261 //} 19262 //} else if (parts.length === 2) { 19263 // we do G F 19264 //this.givenName = parts[0]; 19265 //this.familyName = parts[1]; 19266 } else if (parts.length >= 2) { 19267 //there are at least 3 parts to this name 19268 19269 conjunctionIndex = this._findLastConjunction(parts); 19270 if (conjunctionIndex > 0) { 19271 // if there's a conjunction that's not the first token, put everything up to and 19272 // including the token after it into the first name, the last 2 tokens into 19273 // the family name (if they exist) and everything else in to the middle name 19274 // 0 1 2 3 4 5 19275 // G A G 19276 // G A G F 19277 // G G A G 19278 // G A G F F 19279 // G G A G F 19280 // G G G A G 19281 // G A G M F F 19282 // G G A G F F 19283 // G G G A G F 19284 // G G G G A G 19285 this.givenName = parts.splice(0, conjunctionIndex + 2); 19286 if (parts.length > 1) { 19287 //this.familyName = parts.splice(parts.length-2, 2); 19288 //if ( parts.length > 0 ) { 19289 this.middleName = parts; 19290 } 19291 //} else if (parts.length === 1) { 19292 // this.familyName = parts[0]; 19293 //} 19294 } else { 19295 this.givenName = parts.splice(0, 1); 19296 //this.familyName = parts.splice(parts.length-2, 2); 19297 this.middleName = parts; 19298 } 19299 } 19300 }, 19301 19302 /** 19303 * @protected 19304 */ 19305 _parseGenericWesternName: function (parts) { 19306 /* Western names are parsed as follows, and rules are applied in this 19307 * order: 19308 * 19309 * G 19310 * G F 19311 * G M F 19312 * G M M F 19313 * P F 19314 * P G F 19315 */ 19316 var conjunctionIndex; 19317 19318 if (parts.length === 1) { 19319 if (this.prefix || typeof (parts[0]) === 'object') { 19320 // already has a prefix, so assume it goes with the family name like "Dr. Roberts" or 19321 // it is a name with auxillaries, which is almost always a family name 19322 this.familyName = parts[0]; 19323 } else { 19324 this.givenName = parts[0]; 19325 } 19326 } else if (parts.length === 2) { 19327 // we do G F 19328 if (this.info.order == 'fgm') { 19329 this.givenName = parts[1]; 19330 this.familyName = parts[0]; 19331 } else if (this.info.order == "gmf" || typeof (this.info.order) == 'undefined') { 19332 this.givenName = parts[0]; 19333 this.familyName = parts[1]; 19334 } 19335 } else if (parts.length >= 3) { 19336 //find the first instance of 'and' in the name 19337 conjunctionIndex = this._findLastConjunction(parts); 19338 19339 if (conjunctionIndex > 0) { 19340 // if there's a conjunction that's not the first token, put everything up to and 19341 // including the token after it into the first name, the last token into 19342 // the family name (if it exists) and everything else in to the middle name 19343 // 0 1 2 3 4 5 19344 // G A G M M F 19345 // G G A G M F 19346 // G G G A G F 19347 // G G G G A G 19348 //if(this.order == "gmf") { 19349 this.givenName = parts.slice(0, conjunctionIndex + 2); 19350 19351 if (conjunctionIndex + 1 < parts.length - 1) { 19352 this.familyName = parts.splice(parts.length - 1, 1); 19353 ////console.log(this.familyName); 19354 if (conjunctionIndex + 2 < parts.length - 1) { 19355 this.middleName = parts.slice(conjunctionIndex + 2, parts.length - conjunctionIndex - 3); 19356 } 19357 } else if (this.order == "fgm") { 19358 this.familyName = parts.slice(0, conjunctionIndex + 2); 19359 if (conjunctionIndex + 1 < parts.length - 1) { 19360 this.middleName = parts.splice(parts.length - 1, 1); 19361 if (conjunctionIndex + 2 < parts.length - 1) { 19362 this.givenName = parts.slice(conjunctionIndex + 2, parts.length - conjunctionIndex - 3); 19363 } 19364 } 19365 } 19366 } else { 19367 this.givenName = parts[0]; 19368 19369 this.middleName = parts.slice(1, parts.length - 1); 19370 19371 this.familyName = parts[parts.length - 1]; 19372 } 19373 } 19374 }, 19375 19376 /** 19377 * parse patrinomic name from the russian names 19378 * @protected 19379 * @param {Array.<string>} parts the current array of name parts 19380 * @return number index of the part which contains patronymic name 19381 */ 19382 _findPatronymicName: function(parts) { 19383 var index, part; 19384 for (index = 0; index < parts.length; index++) { 19385 part = parts[index]; 19386 if (typeof (part) === 'string') { 19387 part = part.toLowerCase(); 19388 19389 var subLength = this.info.patronymicName.length; 19390 while(subLength--) { 19391 if(part.indexOf(this.info.patronymicName[subLength])!== -1 ) 19392 return index; 19393 } 19394 } 19395 } 19396 return -1; 19397 }, 19398 19399 /** 19400 * find if the given part is patronymic name 19401 * 19402 * @protected 19403 * @param {string} part string from name parts @ 19404 * @return number index of the part which contains familyName 19405 */ 19406 _isPatronymicName: function(part) { 19407 var pName; 19408 if ( typeof (part) === 'string') { 19409 pName = part.toLowerCase(); 19410 19411 var subLength = this.info.patronymicName.length; 19412 while (subLength--) { 19413 if (pName.indexOf(this.info.patronymicName[subLength]) !== -1) 19414 return true; 19415 } 19416 } 19417 return false; 19418 }, 19419 19420 /** 19421 * find family name from the russian name 19422 * 19423 * @protected 19424 * @param {Array.<string>} parts the current array of name parts 19425 * @return boolean true if patronymic, false otherwise 19426 */ 19427 _findFamilyName: function(parts) { 19428 var index, part, substring; 19429 for (index = 0; index < parts.length; index++) { 19430 part = parts[index]; 19431 19432 if ( typeof (part) === 'string') { 19433 part = part.toLowerCase(); 19434 var length = part.length - 1; 19435 19436 if (this.info.familyName.indexOf(part) !== -1) { 19437 return index; 19438 } else if (part[length] === 'в' || part[length] === 'н' || 19439 part[length] === 'й') { 19440 substring = part.slice(0, -1); 19441 if (this.info.familyName.indexOf(substring) !== -1) { 19442 return index; 19443 } 19444 } else if ((part[length - 1] === 'в' && part[length] === 'а') || 19445 (part[length - 1] === 'н' && part[length] === 'а') || 19446 (part[length - 1] === 'а' && part[length] === 'я')) { 19447 substring = part.slice(0, -2); 19448 if (this.info.familyName.indexOf(substring) !== -1) { 19449 return index; 19450 } 19451 } 19452 } 19453 } 19454 return -1; 19455 }, 19456 19457 /** 19458 * parse russian name 19459 * 19460 * @protected 19461 * @param {Array.<string>} parts the current array of name parts 19462 * @return 19463 */ 19464 _parseRussianName: function(parts) { 19465 var conjunctionIndex, familyIndex = -1; 19466 19467 if (parts.length === 1) { 19468 if (this.prefix || typeof (parts[0]) === 'object') { 19469 // already has a prefix, so assume it goes with the family name 19470 // like "Dr. Roberts" or 19471 // it is a name with auxillaries, which is almost always a 19472 // family name 19473 this.familyName = parts[0]; 19474 } else { 19475 this.givenName = parts[0]; 19476 } 19477 } else if (parts.length === 2) { 19478 // we do G F 19479 if (this.info.order === 'fgm') { 19480 this.givenName = parts[1]; 19481 this.familyName = parts[0]; 19482 } else if (this.info.order === "gmf") { 19483 this.givenName = parts[0]; 19484 this.familyName = parts[1]; 19485 } else if ( typeof (this.info.order) === 'undefined') { 19486 if (this._isPatronymicName(parts[1]) === true) { 19487 this.middleName = parts[1]; 19488 this.givenName = parts[0]; 19489 } else if ((familyIndex = this._findFamilyName(parts)) !== -1) { 19490 if (familyIndex === 1) { 19491 this.givenName = parts[0]; 19492 this.familyName = parts[1]; 19493 } else { 19494 this.familyName = parts[0]; 19495 this.givenName = parts[1]; 19496 } 19497 19498 } else { 19499 this.givenName = parts[0]; 19500 this.familyName = parts[1]; 19501 } 19502 19503 } 19504 } else if (parts.length >= 3) { 19505 // find the first instance of 'and' in the name 19506 conjunctionIndex = this._findLastConjunction(parts); 19507 var patronymicNameIndex = this._findPatronymicName(parts); 19508 if (conjunctionIndex > 0) { 19509 // if there's a conjunction that's not the first token, put 19510 // everything up to and 19511 // including the token after it into the first name, the last 19512 // token into 19513 // the family name (if it exists) and everything else in to the 19514 // middle name 19515 // 0 1 2 3 4 5 19516 // G A G M M F 19517 // G G A G M F 19518 // G G G A G F 19519 // G G G G A G 19520 // if(this.order == "gmf") { 19521 this.givenName = parts.slice(0, conjunctionIndex + 2); 19522 19523 if (conjunctionIndex + 1 < parts.length - 1) { 19524 this.familyName = parts.splice(parts.length - 1, 1); 19525 // //console.log(this.familyName); 19526 if (conjunctionIndex + 2 < parts.length - 1) { 19527 this.middleName = parts.slice(conjunctionIndex + 2, 19528 parts.length - conjunctionIndex - 3); 19529 } 19530 } else if (this.order == "fgm") { 19531 this.familyName = parts.slice(0, conjunctionIndex + 2); 19532 if (conjunctionIndex + 1 < parts.length - 1) { 19533 this.middleName = parts.splice(parts.length - 1, 1); 19534 if (conjunctionIndex + 2 < parts.length - 1) { 19535 this.givenName = parts.slice(conjunctionIndex + 2, 19536 parts.length - conjunctionIndex - 3); 19537 } 19538 } 19539 } 19540 } else if (patronymicNameIndex !== -1) { 19541 this.middleName = parts[patronymicNameIndex]; 19542 19543 if (patronymicNameIndex === (parts.length - 1)) { 19544 this.familyName = parts[0]; 19545 this.givenName = parts.slice(1, patronymicNameIndex); 19546 } else { 19547 this.givenName = parts.slice(0, patronymicNameIndex); 19548 19549 this.familyName = parts[parts.length - 1]; 19550 } 19551 } else { 19552 this.givenName = parts[0]; 19553 19554 this.middleName = parts.slice(1, parts.length - 1); 19555 19556 this.familyName = parts[parts.length - 1]; 19557 } 19558 } 19559 }, 19560 19561 19562 /** 19563 * @protected 19564 */ 19565 _parseWesternName: function (parts) { 19566 19567 if (this.locale.getLanguage() === "es" || this.locale.getLanguage() === "pt") { 19568 // in spain and mexico and portugal, we parse names differently than in the rest of the world 19569 // because of the double family names 19570 this._parseSpanishName(parts); 19571 } else if (this.locale.getLanguage() === "ru") { 19572 /* 19573 * In Russian, names can be given equally validly as given-family 19574 * or family-given. Use the value of the "order" property of the 19575 * constructor options to give the default when the order is ambiguous. 19576 */ 19577 this._parseRussianName(parts); 19578 } else if (this.locale.getLanguage() === "id") { 19579 // in indonesia, we parse names differently than in the rest of the world 19580 // because names don't have family names usually. 19581 this._parseIndonesianName(parts); 19582 } else { 19583 this._parseGenericWesternName(parts); 19584 } 19585 }, 19586 19587 /** 19588 * When sorting names with auxiliary words (like "van der" or "de los"), determine 19589 * which is the "head word" and return a string that can be easily sorted by head 19590 * word. In English, names are always sorted by initial characters. In places like 19591 * the Netherlands or Germany, family names are sorted by the head word of a list 19592 * of names rather than the first element of that name. 19593 * @return {string|undefined} a string containing the family name[s] to be used for sorting 19594 * in the current locale, or undefined if there is no family name in this object 19595 */ 19596 getSortFamilyName: function () { 19597 var name, 19598 auxillaries, 19599 auxString, 19600 parts, 19601 i; 19602 19603 // no name to sort by 19604 if (!this.familyName) { 19605 return undefined; 19606 } 19607 19608 // first break the name into parts 19609 if (this.info) { 19610 if (this.info.sortByHeadWord) { 19611 if (typeof (this.familyName) === 'string') { 19612 name = this.familyName.replace(/\s+/g, ' '); // compress multiple whitespaces 19613 parts = name.trim().split(' '); 19614 } else { 19615 // already split 19616 parts = /** @type Array */ this.familyName; 19617 } 19618 19619 auxillaries = this._findPrefix(parts, this.info.auxillaries, false); 19620 if (auxillaries && auxillaries.length > 0) { 19621 if (typeof (this.familyName) === 'string') { 19622 auxString = auxillaries.join(' '); 19623 name = this.familyName.substring(auxString.length + 1) + ', ' + auxString; 19624 } else { 19625 name = parts.slice(auxillaries.length).join(' ') + 19626 ', ' + 19627 parts.slice(0, auxillaries.length).join(' '); 19628 } 19629 } 19630 } else if (this.info.knownFamilyNames && this.familyName) { 19631 parts = this.familyName.split(''); 19632 var familyNameArray = this._findPrefix(parts, this.info.knownFamilyNames, true, this.info.noCompoundFamilyNames); 19633 name = ""; 19634 for (i = 0; i < familyNameArray.length; i++) { 19635 name += (this.info.knownFamilyNames[familyNameArray[i]] || ""); 19636 } 19637 } 19638 } 19639 19640 return name || this.familyName; 19641 }, 19642 19643 getHeadFamilyName: function () {}, 19644 19645 /** 19646 * @protected 19647 * Return a shallow copy of the current instance. 19648 */ 19649 clone: function () { 19650 return new ilib.Name(this); 19651 } 19652 }; 19653 19654 /* 19655 * namefmt.js - Format person names for display 19656 * 19657 * Copyright © 2013-2014, JEDLSoft 19658 * 19659 * Licensed under the Apache License, Version 2.0 (the "License"); 19660 * you may not use this file except in compliance with the License. 19661 * You may obtain a copy of the License at 19662 * 19663 * http://www.apache.org/licenses/LICENSE-2.0 19664 * 19665 * Unless required by applicable law or agreed to in writing, software 19666 * distributed under the License is distributed on an "AS IS" BASIS, 19667 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19668 * 19669 * See the License for the specific language governing permissions and 19670 * limitations under the License. 19671 */ 19672 19673 /* !depends 19674 ilibglobal.js 19675 locale.js 19676 strings.js 19677 nameprs.js 19678 ctype.ispunct.js 19679 */ 19680 19681 // !data name 19682 19683 /** 19684 * @class 19685 * Creates a formatter that can format person name instances (ilib.Name) for display to 19686 * a user. The options may contain the following properties: 19687 * 19688 * <ul> 19689 * <li><i>locale</i> - Use the conventions of the given locale to construct the name format. 19690 * <li><i>style</i> - Format the name with the given style. The value of this property 19691 * should be one of the following strings: 19692 * <ul> 19693 * <li><i>short</i> - Format a short name with just the given and family names. 19694 * <li><i>medium</i> - Format a medium-length name with the given, middle, and family names. 19695 * <li><i>long</i> - Format a long name with all names available in the given name object, including 19696 * prefixes. 19697 * <li><i>full</i> - Format a long name with all names available in the given name object, including 19698 * prefixes and suffixes. 19699 * </ul> 19700 * <li><i>components</i> - Format the name with the given components in the correct 19701 * order for those components. Components are encoded as a string of letters representing 19702 * the desired components: 19703 * <ul> 19704 * <li><i>p</i> - prefixes 19705 * <li><i>g</i> - given name 19706 * <li><i>m</i> - middle names 19707 * <li><i>f</i> - family name 19708 * <li><i>s</i> - suffixes 19709 * </ul> 19710 * <p> 19711 * 19712 * For example, the string "pf" would mean to only format any prefixes and family names 19713 * together and leave out all the other parts of the name.<p> 19714 * 19715 * The components can be listed in any order in the string. The <i>components</i> option 19716 * overrides the <i>style</i> option if both are specified. 19717 * 19718 * <li>onLoad - a callback function to call when the locale info object is fully 19719 * loaded. When the onLoad option is given, the localeinfo object will attempt to 19720 * load any missing locale data using the ilib loader callback. 19721 * When the constructor is done (even if the data is already preassembled), the 19722 * onLoad function is called with the current instance as a parameter, so this 19723 * callback can be used with preassembled or dynamic loading or a mix of the two. 19724 * 19725 * <li>sync - tell whether to load any missing locale data synchronously or 19726 * asynchronously. If this option is given as "false", then the "onLoad" 19727 * callback must be given, as the instance returned from this constructor will 19728 * not be usable for a while. 19729 * 19730 * <li><i>loadParams</i> - an object containing parameters to pass to the 19731 * loader callback function when locale data is missing. The parameters are not 19732 * interpretted or modified in any way. They are simply passed along. The object 19733 * may contain any property/value pairs as long as the calling code is in 19734 * agreement with the loader callback function as to what those parameters mean. 19735 * </ul> 19736 * 19737 * Formatting names is a locale-dependent function, as the order of the components 19738 * depends on the locale. The following explains some of the details:<p> 19739 * 19740 * <ul> 19741 * <li>In Western countries, the given name comes first, followed by a space, followed 19742 * by the family name. In Asian countries, the family name comes first, followed immediately 19743 * by the given name with no space. But, that format is only used with Asian names written 19744 * in ideographic characters. In Asian countries, especially ones where both an Asian and 19745 * a Western language are used (Hong Kong, Singapore, etc.), the convention is often to 19746 * follow the language of the name. That is, Asian names are written in Asian style, and 19747 * Western names are written in Western style. This class follows that convention as 19748 * well. 19749 * <li>In other Asian countries, Asian names 19750 * written in Latin script are written with Asian ordering. eg. "Xu Ping-an" instead 19751 * of the more Western order "Ping-an Xu", as the order is thought to go with the style 19752 * that is appropriate for the name rather than the style for the language being written. 19753 * <li>In some Spanish speaking countries, people often take both their maternal and 19754 * paternal last names as their own family name. When formatting a short or medium style 19755 * of that family name, only the paternal name is used. In the long style, all the names 19756 * are used. eg. "Juan Julio Raul Lopez Ortiz" took the name "Lopez" from his father and 19757 * the name "Ortiz" from his mother. His family name would be "Lopez Ortiz". The formatted 19758 * short style of his name would be simply "Juan Lopez" which only uses his paternal 19759 * family name of "Lopez". 19760 * <li>In many Western languages, it is common to use auxillary words in family names. For 19761 * example, the family name of "Ludwig von Beethoven" in German is "von Beethoven", not 19762 * "Beethoven". This class ensures that the family name is formatted correctly with 19763 * all auxillary words. 19764 * </ul> 19765 * 19766 * Depends directive: !depends namefmt.js 19767 * 19768 * @constructor 19769 * @param {Object} options A set of options that govern how the formatter will behave 19770 */ 19771 ilib.NameFmt = function(options) { 19772 var sync = true; 19773 19774 this.style = "short"; 19775 this.loadParams = {}; 19776 19777 if (options) { 19778 if (options.locale) { 19779 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 19780 } 19781 19782 if (options.style) { 19783 this.style = options.style; 19784 } 19785 19786 if (options.components) { 19787 this.components = options.components; 19788 } 19789 19790 if (typeof(options.sync) !== 'undefined') { 19791 sync = (options.sync == true); 19792 } 19793 19794 if (typeof(options.loadParams) !== 'undefined') { 19795 this.loadParams = options.loadParams; 19796 } 19797 } 19798 19799 // set up defaults in case we need them 19800 this.defaultEuroTemplate = new ilib.String("{prefix} {givenName} {middleName} {familyName}{suffix}"); 19801 this.defaultAsianTemplate = new ilib.String("{prefix}{familyName}{givenName}{middleName}{suffix}"); 19802 this.useFirstFamilyName = false; 19803 19804 switch (this.style) { 19805 default: 19806 case "s": 19807 case "short": 19808 this.style = "short"; 19809 break; 19810 case "m": 19811 case "medium": 19812 this.style = "medium"; 19813 break; 19814 case "l": 19815 case "long": 19816 this.style = "long"; 19817 break; 19818 case "f": 19819 case "full": 19820 this.style = "full"; 19821 break; 19822 } 19823 19824 if (!ilib.Name.cache) { 19825 ilib.Name.cache = {}; 19826 } 19827 19828 this.locale = this.locale || new ilib.Locale(); 19829 19830 ilib.CType.isPunct._init(sync, this.loadParams, /** @type {function()|undefined} */ ilib.bind(this, function() { 19831 ilib.loadData({ 19832 object: ilib.Name, 19833 locale: this.locale, 19834 name: "name.json", 19835 sync: sync, 19836 loadParams: this.loadParams, 19837 callback: ilib.bind(this, function (info) { 19838 if (!info) { 19839 info = ilib.Name.defaultInfo; 19840 var spec = this.locale.getSpec().replace(/-/g, "_"); 19841 ilib.Name.cache[spec] = info; 19842 } 19843 this.info = info; 19844 this._init(); 19845 if (options && typeof(options.onLoad) === 'function') { 19846 options.onLoad(this); 19847 } 19848 }) 19849 }); 19850 })); 19851 }; 19852 19853 ilib.NameFmt.prototype = { 19854 /** 19855 * @protected 19856 */ 19857 _init: function() { 19858 if (this.components) { 19859 var valids = {"p":1,"g":1,"m":1,"f":1,"s":1}, 19860 arr = this.components.split(""); 19861 this.comps = {}; 19862 for (var i = 0; i < arr.length; i++) { 19863 if (valids[arr[i].toLowerCase()]) { 19864 this.comps[arr[i].toLowerCase()] = true; 19865 } 19866 } 19867 } else { 19868 this.comps = this.info.components[this.style]; 19869 } 19870 19871 this.template = new ilib.String(this.info.format); 19872 19873 if (this.locale.language === "es" && (this.style !== "long" && this.style !== "full")) { 19874 this.useFirstFamilyName = true; // in spanish, they have 2 family names, the maternal and paternal 19875 } 19876 19877 this.isAsianLocale = (this.info.nameStyle === "asian"); 19878 }, 19879 19880 /** 19881 * adjoin auxillary words to their head words 19882 * @protected 19883 */ 19884 _adjoinAuxillaries: function (parts, namePrefix) { 19885 var start, i, prefixArray, prefix, prefixLower; 19886 19887 //console.info("_adjoinAuxillaries: finding and adjoining aux words in " + parts.join(' ')); 19888 19889 if ( this.info.auxillaries && (parts.length > 2 || namePrefix) ) { 19890 for ( start = 0; start < parts.length-1; start++ ) { 19891 for ( i = parts.length; i > start; i-- ) { 19892 prefixArray = parts.slice(start, i); 19893 prefix = prefixArray.join(' '); 19894 prefixLower = prefix.toLowerCase(); 19895 prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods 19896 19897 //console.info("_adjoinAuxillaries: checking aux prefix: '" + prefixLower + "' which is " + start + " to " + i); 19898 19899 if ( prefixLower in this.info.auxillaries ) { 19900 //console.info("Found! Old parts list is " + JSON.stringify(parts)); 19901 parts.splice(start, i+1-start, prefixArray.concat(parts[i])); 19902 //console.info("_adjoinAuxillaries: Found! New parts list is " + JSON.stringify(parts)); 19903 i = start; 19904 } 19905 } 19906 } 19907 } 19908 19909 //console.info("_adjoinAuxillaries: done. Result is " + JSON.stringify(parts)); 19910 19911 return parts; 19912 }, 19913 19914 /** 19915 * Return the locale for this formatter instance. 19916 * @return {ilib.Locale} the locale instance for this formatter 19917 */ 19918 getLocale: function () { 19919 return this.locale; 19920 }, 19921 19922 /** 19923 * Return the style of names returned by this formatter 19924 * @return {string} the style of names returned by this formatter 19925 */ 19926 getStyle: function () { 19927 return this.style; 19928 }, 19929 19930 /** 19931 * Return the list of components used to format names in this formatter 19932 * @return {string} the list of components 19933 */ 19934 getComponents: function () { 19935 return this.components; 19936 }, 19937 19938 /** 19939 * Format the name for display in the current locale with the options set up 19940 * in the constructor of this formatter instance.<p> 19941 * 19942 * If the name does not contain all the parts required for the style, those parts 19943 * will be left blank.<p> 19944 * 19945 * There are two basic styles of formatting: European, and Asian. If this formatter object 19946 * is set for European style, but an Asian name is passed to the format method, then this 19947 * method will format the Asian name with a generic Asian template. Similarly, if the 19948 * formatter is set for an Asian style, and a European name is passed to the format method, 19949 * the formatter will use a generic European template.<p> 19950 * 19951 * This means it is always safe to format any name with a formatter for any locale. You should 19952 * always get something at least reasonable as output.<p> 19953 * 19954 * @param {ilib.Name} name the name to format 19955 * @return {string|undefined} the name formatted according to the style of this formatter instance 19956 */ 19957 format: function(name) { 19958 var formatted, temp, modified, isAsianName; 19959 var currentLanguage = this.locale.getLanguage(); 19960 19961 if (!name || typeof(name) !== 'object') { 19962 return undefined; 19963 } 19964 19965 if ((typeof(name.isAsianName) === 'boolean' && !name.isAsianName) || 19966 ilib.Name._isEuroName([name.givenName, name.middleName, name.familyName].join(""), currentLanguage)) { 19967 isAsianName = false; // this is a euro name, even if the locale is asian 19968 modified = name.clone(); 19969 19970 // handle the case where there is no space if there is punctuation in the suffix like ", Phd". 19971 // Otherwise, put a space in to transform "PhD" to " PhD" 19972 /* 19973 console.log("suffix is " + modified.suffix); 19974 if ( modified.suffix ) { 19975 console.log("first char is " + modified.suffix.charAt(0)); 19976 console.log("isPunct(modified.suffix.charAt(0)) is " + ilib.CType.isPunct(modified.suffix.charAt(0))); 19977 } 19978 */ 19979 if (modified.suffix && ilib.CType.isPunct(modified.suffix.charAt(0)) === false) { 19980 modified.suffix = ' ' + modified.suffix; 19981 } 19982 19983 if (this.useFirstFamilyName && name.familyName) { 19984 var familyNameParts = modified.familyName.trim().split(' '); 19985 if (familyNameParts.length > 1) { 19986 familyNameParts = this._adjoinAuxillaries(familyNameParts, name.prefix); 19987 } //in spain and mexico, we parse names differently than in the rest of the world 19988 19989 modified.familyName = familyNameParts[0]; 19990 } 19991 19992 modified._joinNameArrays(); 19993 } else { 19994 isAsianName = true; 19995 modified = name; 19996 if (modified.suffix && currentLanguage === "ko" && this.info.honorifics.indexOf(name.suffix) == -1) { 19997 modified.suffix = ' ' + modified.suffix; 19998 } 19999 } 20000 20001 if (!this.template || isAsianName !== this.isAsianLocale) { 20002 temp = isAsianName ? this.defaultAsianTemplate : this.defaultEuroTemplate; 20003 } else { 20004 temp = this.template; 20005 } 20006 20007 var parts = { 20008 prefix: this.comps["p"] && modified.prefix || "", 20009 givenName: this.comps["g"] && modified.givenName || "", 20010 middleName: this.comps["m"] && modified.middleName || "", 20011 familyName: this.comps["f"] && modified.familyName || "", 20012 suffix: this.comps["s"] && modified.suffix || "" 20013 }; 20014 20015 formatted = temp.format(parts); 20016 return formatted.replace(/\s+/g, ' ').trim(); 20017 } 20018 }; 20019 20020 /** 20021 * addressprs.js - Represent a mailing address 20022 * 20023 * Copyright © 2013-2014, JEDLSoft 20024 * 20025 * Licensed under the Apache License, Version 2.0 (the "License"); 20026 * you may not use this file except in compliance with the License. 20027 * You may obtain a copy of the License at 20028 * 20029 * http://www.apache.org/licenses/LICENSE-2.0 20030 * 20031 * Unless required by applicable law or agreed to in writing, software 20032 * distributed under the License is distributed on an "AS IS" BASIS, 20033 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20034 * 20035 * See the License for the specific language governing permissions and 20036 * limitations under the License. 20037 */ 20038 20039 /*globals console RegExp */ 20040 20041 /* !depends 20042 ilibglobal.js 20043 locale.js 20044 ctype.isideo.js 20045 ctype.isascii.js 20046 ctype.isdigit.js 20047 */ 20048 20049 // !data address countries nativecountries ctrynames 20050 20051 /** 20052 * @class 20053 * Create a new Address instance and parse a physical address.<p> 20054 * 20055 * This function parses a physical address written in a free-form string. 20056 * It returns an object with a number of properties from the list below 20057 * that it may have extracted from that address.<p> 20058 * 20059 * The following is a list of properties that the algorithm will return:<p> 20060 * 20061 * <ul> 20062 * <li><i>streetAddress</i>: The street address, including house numbers and all. 20063 * <li><i>locality</i>: The locality of this address (usually a city or town). 20064 * <li><i>region</i>: The region where the locality is located. In the US, this 20065 * corresponds to states. In other countries, this may be provinces, 20066 * cantons, prefectures, etc. In some smaller countries, there are no 20067 * such divisions. 20068 * <li><i>postalCode</i>: Country-specific code for expediting mail. In the US, 20069 * this is the zip code. 20070 * <li><i>country</i>: The country of the address. 20071 * <li><i>countryCode</i>: The ISO 3166 2-letter region code for the destination 20072 * country in this address. 20073 * </ul> 20074 * 20075 * The above properties will not necessarily appear in the instance. For 20076 * any individual property, if the free-form address does not contain 20077 * that property or it cannot be parsed out, the it is left out.<p> 20078 * 20079 * The options parameter may contain any of the following properties: 20080 * 20081 * <ul> 20082 * <li><i>locale</i> - locale or localeSpec to use to parse the address. If not 20083 * specified, this function will use the current ilib locale 20084 * 20085 * <li><i>onLoad</i> - a callback function to call when the address info for the 20086 * locale is fully loaded and the address has been parsed. When the onLoad 20087 * option is given, the address object 20088 * will attempt to load any missing locale data using the ilib loader callback. 20089 * When the constructor is done (even if the data is already preassembled), the 20090 * onLoad function is called with the current instance as a parameter, so this 20091 * callback can be used with preassembled or dynamic loading or a mix of the two. 20092 * 20093 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 20094 * asynchronously. If this option is given as "false", then the "onLoad" 20095 * callback must be given, as the instance returned from this constructor will 20096 * not be usable for a while. 20097 * 20098 * <li><i>loadParams</i> - an object containing parameters to pass to the 20099 * loader callback function when locale data is missing. The parameters are not 20100 * interpretted or modified in any way. They are simply passed along. The object 20101 * may contain any property/value pairs as long as the calling code is in 20102 * agreement with the loader callback function as to what those parameters mean. 20103 * </ul> 20104 * 20105 * When an address cannot be parsed properly, the entire address will be placed 20106 * into the streetAddress property.<p> 20107 * 20108 * When the freeformAddress is another ilib.Address, this will act like a copy 20109 * constructor.<p> 20110 * 20111 * Depends directive: !depends addressprs.js 20112 * 20113 * @constructor 20114 * @param {string|ilib.Address} freeformAddress free-form address to parse, or a 20115 * javascript object containing the fields 20116 * @param {Object} options options to the parser 20117 */ 20118 ilib.Address = function (freeformAddress, options) { 20119 var address; 20120 20121 if (!freeformAddress) { 20122 return undefined; 20123 } 20124 20125 this.sync = true; 20126 this.loadParams = {}; 20127 20128 if (options) { 20129 if (options.locale) { 20130 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 20131 } 20132 20133 if (typeof(options.sync) !== 'undefined') { 20134 this.sync = (options.sync == true); 20135 } 20136 20137 if (options.loadParams) { 20138 this.loadParams = options.loadParams; 20139 } 20140 } 20141 20142 this.locale = this.locale || new ilib.Locale(); 20143 // initialize from an already parsed object 20144 if (typeof(freeformAddress) === 'object') { 20145 /** 20146 * The street address, including house numbers and all. 20147 * @expose 20148 * @type {string|undefined} 20149 */ 20150 this.streetAddress = freeformAddress.streetAddress; 20151 /** 20152 * The locality of this address (usually a city or town). 20153 * @expose 20154 * @type {string|undefined} 20155 */ 20156 this.locality = freeformAddress.locality; 20157 /** 20158 * The region (province, canton, prefecture, state, etc.) where the address is located. 20159 * @expose 20160 * @type {string|undefined} 20161 */ 20162 this.region = freeformAddress.region; 20163 /** 20164 * Country-specific code for expediting mail. In the US, this is the zip code. 20165 * @expose 20166 * @type {string|undefined} 20167 */ 20168 this.postalCode = freeformAddress.postalCode; 20169 /** 20170 * Optional city-specific code for a particular post office, used to expidite 20171 * delivery. 20172 * @expose 20173 * @type {string|undefined} 20174 */ 20175 this.postOffice = freeformAddress.postOffice; 20176 /** 20177 * The country of the address. 20178 * @expose 20179 * @type {string|undefined} 20180 */ 20181 this.country = freeformAddress.country; 20182 if (freeformAddress.countryCode) { 20183 /** 20184 * The 2 or 3 letter ISO 3166 region code for the destination country in this address. 20185 * @expose 20186 * @type {string} 20187 * 20188 */ 20189 this.countryCode = freeformAddress.countryCode; 20190 } 20191 if (freeformAddress.format) { 20192 /** 20193 * private 20194 * @type {string} 20195 */ 20196 this.format = freeformAddress.format; 20197 } 20198 return this; 20199 } 20200 20201 address = freeformAddress.replace(/[ \t\r]+/g, " ").trim(); 20202 address = address.replace(/[\s\n]+$/, ""); 20203 address = address.replace(/^[\s\n]+/, ""); 20204 //console.log("\n\n-------------\nAddress is '" + address + "'"); 20205 20206 this.lines = address.split(/[,,\n]/g); 20207 this.removeEmptyLines(this.lines); 20208 20209 ilib.Address.shared = ilib.Address.shared || {}; 20210 if (typeof(ilib.Address.ctry) === 'undefined') { 20211 ilib.Address.ctry = {}; // make sure not to conflict with the address info 20212 } 20213 ilib.CType.isAscii._init(this.sync, this.loadParams, /** @type {function(*)|undefined} */ ilib.bind(this, function() { 20214 ilib.CType.isIdeo._init(this.sync, this.loadParams, /** @type {function(*)|undefined} */ ilib.bind(this, function() { 20215 ilib.CType.isDigit._init(this.sync, this.loadParams, /** @type {function(*)|undefined} */ ilib.bind(this, function() { 20216 if (typeof(ilib.data.nativecountries) === 'undefined') { 20217 ilib.loadData({ 20218 name: "nativecountries.json", // countries in their own language 20219 locale: "-", // only need to load the root file 20220 sync: this.sync, 20221 loadParams: this.loadParams, 20222 callback: /** @type function(Object=):undefined */ ilib.bind(this, /** @type function() */ function(nativecountries) { 20223 ilib.data.nativecountries = nativecountries; 20224 this._loadCountries(options && options.onLoad); 20225 }) 20226 }); 20227 } else { 20228 this._loadCountries(options && options.onLoad); 20229 } 20230 })); 20231 })); 20232 })); 20233 }; 20234 20235 /** @protected */ 20236 ilib.Address.prototype = { 20237 /** 20238 * @private 20239 */ 20240 _loadCountries: function(onLoad) { 20241 if (typeof(ilib.data.countries) === 'undefined') { 20242 ilib.loadData({ 20243 name: "countries.json", // countries in English 20244 locale: "-", // only need to load the root file 20245 sync: this.sync, 20246 loadParams: this.loadParams, 20247 callback: /** @type function(Object=):undefined */ ilib.bind(this, /** @type function() */ function(countries) { 20248 ilib.data.countries = countries; 20249 this._loadCtrynames(onLoad); 20250 }) 20251 }); 20252 } else { 20253 this._loadCtrynames(onLoad); 20254 } 20255 }, 20256 20257 /** 20258 * @private 20259 */ 20260 _loadCtrynames: function(onLoad) { 20261 ilib.loadData({ 20262 name: "ctrynames.json", 20263 object: ilib.Address.ctry, 20264 locale: this.locale, 20265 sync: this.sync, 20266 loadParams: this.loadParams, 20267 callback: /** @type function(Object=):undefined */ ilib.bind(this, /** @type function() */ function(ctrynames) { 20268 this._determineDest(ctrynames, onLoad); 20269 }) 20270 }); 20271 }, 20272 20273 /** 20274 * @private 20275 * @param {Object?} ctrynames 20276 */ 20277 _findDest: function (ctrynames) { 20278 var match; 20279 20280 for (var countryName in ctrynames) { 20281 if (countryName && countryName !== "generated") { 20282 // find the longest match in the current table 20283 // ctrynames contains the country names mapped to region code 20284 // for efficiency, only test for things longer than the current match 20285 if (!match || match.text.length < countryName.length) { 20286 var temp = this._findCountry(countryName); 20287 if (temp) { 20288 match = temp; 20289 this.country = match.text; 20290 this.countryCode = ctrynames[countryName]; 20291 } 20292 } 20293 } 20294 } 20295 return match; 20296 }, 20297 20298 /** 20299 * @private 20300 * @param {Object?} localizedCountries 20301 * @param {function(ilib.Address):undefined} callback 20302 */ 20303 _determineDest: function (localizedCountries, callback) { 20304 var match; 20305 20306 /* 20307 * First, find the name of the destination country, as that determines how to parse 20308 * the rest of the address. For any address, there are three possible ways 20309 * that the name of the country could be written: 20310 * 1. In the current language 20311 * 2. In its own native language 20312 * 3. In English 20313 * We'll try all three. 20314 */ 20315 var tables = []; 20316 if (localizedCountries) { 20317 tables.push(localizedCountries); 20318 } 20319 tables.push(ilib.data.nativecountries); 20320 tables.push(ilib.data.countries); 20321 20322 for (var i = 0; i < tables.length; i++) { 20323 match = this._findDest(tables[i]); 20324 20325 if (match) { 20326 this.lines[match.line] = this.lines[match.line].substring(0, match.start) + this.lines[match.line].substring(match.start + match.text.length); 20327 20328 this._init(callback); 20329 return; 20330 } 20331 } 20332 20333 // no country, so try parsing it as if we were in the same country 20334 this.country = undefined; 20335 this.countryCode = this.locale.getRegion(); 20336 this._init(callback); 20337 }, 20338 20339 /** 20340 * @private 20341 * @param {function(ilib.Address):undefined} callback 20342 */ 20343 _init: function(callback) { 20344 ilib.loadData({ 20345 object: ilib.Address, 20346 locale: new ilib.Locale(this.countryCode), 20347 name: "address.json", 20348 sync: this.sync, 20349 loadParams: this.loadParams, 20350 callback: /** @type function(Object=):undefined */ ilib.bind(this, function(info) { 20351 if (!info || ilib.isEmpty(info)) { 20352 // load the "unknown" locale instead 20353 ilib.loadData({ 20354 object: ilib.Address, 20355 locale: new ilib.Locale("XX"), 20356 name: "address.json", 20357 sync: this.sync, 20358 loadParams: this.loadParams, 20359 callback: /** @type function(Object=):undefined */ ilib.bind(this, function(info) { 20360 this.info = info; 20361 this._parseAddress(); 20362 if (typeof(callback) === 'function') { 20363 callback(this); 20364 } 20365 }) 20366 }); 20367 } else { 20368 this.info = info; 20369 this._parseAddress(); 20370 if (typeof(callback) === 'function') { 20371 callback(this); 20372 } 20373 } 20374 }) 20375 }); 20376 }, 20377 20378 /** 20379 * @private 20380 */ 20381 _parseAddress: function() { 20382 // clean it up first 20383 var i, 20384 asianChars = 0, 20385 latinChars = 0, 20386 startAt, 20387 infoFields, 20388 field, 20389 pattern, 20390 matchFunction, 20391 match, 20392 fieldNumber; 20393 20394 // for locales that support both latin and asian character addresses, 20395 // decide if we are parsing an asian or latin script address 20396 if (this.info && this.info.multiformat) { 20397 for (var j = 0; j < this.lines.length; j++) { 20398 var line = new ilib.String(this.lines[j]); 20399 var it = line.charIterator(); 20400 while (it.hasNext()) { 20401 var c = it.next(); 20402 if (ilib.CType.isIdeo(c) || ilib.CType.withinRange(c, "Hangul")) { 20403 asianChars++; 20404 } else if (ilib.CType.isAscii(c) && !ilib.CType.isDigit(c)) { 20405 latinChars++; 20406 } 20407 } 20408 } 20409 20410 this.format = (asianChars >= latinChars) ? "asian" : "latin"; 20411 startAt = this.info.startAt[this.format]; 20412 infoFields = this.info.fields[this.format]; 20413 // //console.log("multiformat locale: format is now " + this.format); 20414 } else { 20415 startAt = (this.info && this.info.startAt) || "end"; 20416 infoFields = this.info.fields; 20417 } 20418 this.compare = (startAt === "end") ? this.endsWith : this.startsWith; 20419 20420 //console.log("this.lines is: " + JSON.stringify(this.lines)); 20421 20422 for (i = 0; i < infoFields.length && this.lines.length > 0; i++) { 20423 /** @type {{name:string, line:string, pattern:(string|Array.<string>), matchGroup:number}} */ 20424 field = infoFields[i]; 20425 this.removeEmptyLines(this.lines); 20426 //console.log("Searching for field " + field.name); 20427 if (field.pattern) { 20428 if (typeof(field.pattern) === 'string') { 20429 pattern = new RegExp(field.pattern, "img"); 20430 matchFunction = this.matchRegExp; 20431 } else { 20432 pattern = field.pattern; 20433 matchFunction = this.matchPattern; 20434 } 20435 20436 switch (field.line) { 20437 case 'startAtFirst': 20438 for (fieldNumber = 0; fieldNumber < this.lines.length; fieldNumber++) { 20439 match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt); 20440 if (match) { 20441 break; 20442 } 20443 } 20444 break; 20445 case 'startAtLast': 20446 for (fieldNumber = this.lines.length-1; fieldNumber >= 0; fieldNumber--) { 20447 match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt); 20448 if (match) { 20449 break; 20450 } 20451 } 20452 break; 20453 case 'first': 20454 fieldNumber = 0; 20455 match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt); 20456 break; 20457 case 'last': 20458 default: 20459 fieldNumber = this.lines.length - 1; 20460 match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt); 20461 break; 20462 } 20463 if (match) { 20464 // //console.log("found match for " + field.name + ": " + JSON.stringify(match)); 20465 // //console.log("remaining line is " + match.line); 20466 this.lines[fieldNumber] = match.line; 20467 this[field.name] = match.match; 20468 } 20469 } else { 20470 // if nothing is given, default to taking the whole field 20471 this[field.name] = this.lines.splice(fieldNumber,1)[0].trim(); 20472 //console.log("typeof(this[field.name]) is " + typeof(this[field.name]) + " and value is " + JSON.stringify(this[field.name])); 20473 } 20474 } 20475 20476 // all the left overs go in the street address field 20477 this.removeEmptyLines(this.lines); 20478 if (this.lines.length > 0) { 20479 //console.log("this.lines is " + JSON.stringify(this.lines) + " and splicing to get streetAddress"); 20480 // Korea uses spaces between words, despite being an "asian" locale 20481 var joinString = (this.info.joinString && this.info.joinString[this.format]) || ((this.format && this.format === "asian") ? "" : ", "); 20482 this.streetAddress = this.lines.join(joinString).trim(); 20483 } 20484 20485 this.lines = undefined; 20486 //console.log("final result is " + JSON.stringify(this)); 20487 }, 20488 20489 /** 20490 * @protected 20491 * Find the named country either at the end or the beginning of the address. 20492 */ 20493 _findCountry: function(name) { 20494 var start = -1, match, line = 0; 20495 20496 if (this.lines.length > 0) { 20497 start = this.startsWith(this.lines[line], name); 20498 if (start === -1) { 20499 line = this.lines.length-1; 20500 start = this.endsWith(this.lines[line], name); 20501 } 20502 if (start !== -1) { 20503 match = { 20504 text: this.lines[line].substring(start, start + name.length), 20505 line: line, 20506 start: start 20507 }; 20508 } 20509 } 20510 20511 return match; 20512 }, 20513 20514 endsWith: function (subject, query) { 20515 var start = subject.length-query.length, 20516 i, 20517 pat; 20518 //console.log("endsWith: checking " + query + " against " + subject); 20519 for (i = 0; i < query.length; i++) { 20520 // TODO: use case mapper instead of toLowerCase() 20521 if (subject.charAt(start+i).toLowerCase() !== query.charAt(i).toLowerCase()) { 20522 return -1; 20523 } 20524 } 20525 if (start > 0) { 20526 pat = /\s/; 20527 if (!pat.test(subject.charAt(start-1))) { 20528 // make sure if we are not at the beginning of the string, that the match is 20529 // not the end of some other word 20530 return -1; 20531 } 20532 } 20533 return start; 20534 }, 20535 20536 startsWith: function (subject, query) { 20537 var i; 20538 // //console.log("startsWith: checking " + query + " against " + subject); 20539 for (i = 0; i < query.length; i++) { 20540 // TODO: use case mapper instead of toLowerCase() 20541 if (subject.charAt(i).toLowerCase() !== query.charAt(i).toLowerCase()) { 20542 return -1; 20543 } 20544 } 20545 return 0; 20546 }, 20547 20548 removeEmptyLines: function (arr) { 20549 var i = 0; 20550 20551 while (i < arr.length) { 20552 if (arr[i]) { 20553 arr[i] = arr[i].trim(); 20554 if (arr[i].length === 0) { 20555 arr.splice(i,1); 20556 } else { 20557 i++; 20558 } 20559 } else { 20560 arr.splice(i,1); 20561 } 20562 } 20563 }, 20564 20565 matchRegExp: function(address, line, expression, matchGroup, startAt) { 20566 var lastMatch, 20567 match, 20568 ret = {}, 20569 last; 20570 20571 //console.log("searching for regexp " + expression.source + " in line " + line); 20572 20573 match = expression.exec(line); 20574 if (startAt === 'end') { 20575 while (match !== null && match.length > 0) { 20576 //console.log("found matches " + JSON.stringify(match)); 20577 lastMatch = match; 20578 match = expression.exec(line); 20579 } 20580 match = lastMatch; 20581 } 20582 20583 if (match && match !== null) { 20584 //console.log("found matches " + JSON.stringify(match)); 20585 matchGroup = matchGroup || 0; 20586 if (match[matchGroup] !== undefined) { 20587 ret.match = match[matchGroup].trim(); 20588 ret.match = ret.match.replace(/^\-|\-+$/, ''); 20589 ret.match = ret.match.replace(/\s+$/, ''); 20590 last = (startAt === 'end') ? line.lastIndexOf(match[matchGroup]) : line.indexOf(match[matchGroup]); 20591 //console.log("last is " + last); 20592 ret.line = line.slice(0,last); 20593 if (address.format !== "asian") { 20594 ret.line += " "; 20595 } 20596 ret.line += line.slice(last+match[matchGroup].length); 20597 ret.line = ret.line.trim(); 20598 //console.log("found match " + ret.match + " from matchgroup " + matchGroup + " and rest of line is " + ret.line); 20599 return ret; 20600 } 20601 //} else { 20602 //console.log("no match"); 20603 } 20604 20605 return undefined; 20606 }, 20607 20608 matchPattern: function(address, line, pattern, matchGroup) { 20609 var start, 20610 j, 20611 ret = {}; 20612 20613 //console.log("searching in line " + line); 20614 20615 // search an array of possible fixed strings 20616 //console.log("Using fixed set of strings."); 20617 for (j = 0; j < pattern.length; j++) { 20618 start = address.compare(line, pattern[j]); 20619 if (start !== -1) { 20620 ret.match = line.substring(start, start+pattern[j].length); 20621 if (start !== 0) { 20622 ret.line = line.substring(0,start).trim(); 20623 } else { 20624 ret.line = line.substring(pattern[j].length).trim(); 20625 } 20626 //console.log("found match " + ret.match + " and rest of line is " + ret.line); 20627 return ret; 20628 } 20629 } 20630 20631 return undefined; 20632 } 20633 }; 20634 20635 /* 20636 * addressfmt.js - Format an address 20637 * 20638 * Copyright © 2013-2014, JEDLSoft 20639 * 20640 * Licensed under the Apache License, Version 2.0 (the "License"); 20641 * you may not use this file except in compliance with the License. 20642 * You may obtain a copy of the License at 20643 * 20644 * http://www.apache.org/licenses/LICENSE-2.0 20645 * 20646 * Unless required by applicable law or agreed to in writing, software 20647 * distributed under the License is distributed on an "AS IS" BASIS, 20648 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20649 * 20650 * See the License for the specific language governing permissions and 20651 * limitations under the License. 20652 */ 20653 20654 /* !depends 20655 ilibglobal.js 20656 locale.js 20657 addressprs.js 20658 */ 20659 20660 // !data address 20661 20662 /** 20663 * @class 20664 * Create a new formatter object to format physical addresses in a particular way. 20665 * 20666 * The options object may contain the following properties, both of which are optional: 20667 * 20668 * <ul> 20669 * <li><i>locale</i> - the locale to use to format this address. If not specified, it uses the default locale 20670 * 20671 * <li><i>style</i> - the style of this address. The default style for each country usually includes all valid 20672 * fields for that country. 20673 * 20674 * <li><i>onLoad</i> - a callback function to call when the address info for the 20675 * locale is fully loaded and the address has been parsed. When the onLoad 20676 * option is given, the address formatter object 20677 * will attempt to load any missing locale data using the ilib loader callback. 20678 * When the constructor is done (even if the data is already preassembled), the 20679 * onLoad function is called with the current instance as a parameter, so this 20680 * callback can be used with preassembled or dynamic loading or a mix of the two. 20681 * 20682 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 20683 * asynchronously. If this option is given as "false", then the "onLoad" 20684 * callback must be given, as the instance returned from this constructor will 20685 * not be usable for a while. 20686 * 20687 * <li><i>loadParams</i> - an object containing parameters to pass to the 20688 * loader callback function when locale data is missing. The parameters are not 20689 * interpretted or modified in any way. They are simply passed along. The object 20690 * may contain any property/value pairs as long as the calling code is in 20691 * agreement with the loader callback function as to what those parameters mean. 20692 * </ul> 20693 * 20694 * Depends directive: !depends addressfmt.js 20695 * 20696 * @constructor 20697 * @param {Object} options options that configure how this formatter should work 20698 * Returns a formatter instance that can format multiple addresses. 20699 */ 20700 ilib.AddressFmt = function(options) { 20701 this.sync = true; 20702 this.styleName = 'default'; 20703 this.loadParams = {}; 20704 this.locale = new ilib.Locale(); 20705 20706 if (options) { 20707 if (options.locale) { 20708 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 20709 } 20710 20711 if (typeof(options.sync) !== 'undefined') { 20712 this.sync = (options.sync == true); 20713 } 20714 20715 if (options.style) { 20716 this.styleName = options.style; 20717 } 20718 20719 if (options.loadParams) { 20720 this.loadParams = options.loadParams; 20721 } 20722 } 20723 20724 // console.log("Creating formatter for region: " + this.locale.region); 20725 ilib.loadData({ 20726 name: "address.json", 20727 object: ilib.Address, 20728 locale: this.locale, 20729 sync: this.sync, 20730 loadParams: this.loadParams, 20731 callback: /** @type function(Object?):undefined */ ilib.bind(this, function(info) { 20732 if (!info || ilib.isEmpty(info)) { 20733 // load the "unknown" locale instead 20734 ilib.loadData({ 20735 name: "address.json", 20736 object: ilib.Address, 20737 locale: new ilib.Locale("XX"), 20738 sync: this.sync, 20739 loadParams: this.loadParams, 20740 callback: /** @type function(Object?):undefined */ ilib.bind(this, function(info) { 20741 this.info = info; 20742 this._init(); 20743 if (options && typeof(options.onLoad) === 'function') { 20744 options.onLoad(this); 20745 } 20746 }) 20747 }); 20748 } else { 20749 this.info = info; 20750 this._init(); 20751 if (options && typeof(options.onLoad) === 'function') { 20752 options.onLoad(this); 20753 } 20754 } 20755 }) 20756 }); 20757 }; 20758 20759 /** 20760 * @private 20761 */ 20762 ilib.AddressFmt.prototype._init = function () { 20763 this.style = this.info && this.info.formats && this.info.formats[this.styleName]; 20764 20765 // use generic default -- should not happen, but just in case... 20766 this.style = this.style || (this.info && this.info.formats["default"]) || "{streetAddress}\n{locality} {region} {postalCode}\n{country}"; 20767 }; 20768 20769 /** 20770 * This function formats a physical address (ilib.Address instance) for display. 20771 * Whitespace is trimmed from the beginning and end of final resulting string, and 20772 * multiple consecutive whitespace characters in the middle of the string are 20773 * compressed down to 1 space character. 20774 * 20775 * If the Address instance is for a locale that is different than the locale for this 20776 * formatter, then a hybrid address is produced. The country name is located in the 20777 * correct spot for the current formatter's locale, but the rest of the fields are 20778 * formatted according to the default style of the locale of the actual address. 20779 * 20780 * Example: a mailing address in China, but formatted for the US might produce the words 20781 * "People's Republic of China" in English at the last line of the address, and the 20782 * Chinese-style address will appear in the first line of the address. In the US, the 20783 * country is on the last line, but in China the country is usually on the first line. 20784 * 20785 * @param {ilib.Address} address Address to format 20786 * @eturns {string} Returns a string containing the formatted address 20787 */ 20788 ilib.AddressFmt.prototype.format = function (address) { 20789 var ret, template, other, format; 20790 20791 if (!address) { 20792 return ""; 20793 } 20794 // console.log("formatting address: " + JSON.stringify(address)); 20795 if (address.countryCode && 20796 address.countryCode !== this.locale.region && 20797 ilib.Locale._isRegionCode(this.locale.region) && 20798 this.locale.region !== "XX") { 20799 // we are formatting an address that is sent from this country to another country, 20800 // so only the country should be in this locale, and the rest should be in the other 20801 // locale 20802 // console.log("formatting for another locale. Loading in its settings: " + address.countryCode); 20803 other = new ilib.AddressFmt({ 20804 locale: new ilib.Locale(address.countryCode), 20805 style: this.styleName 20806 }); 20807 return other.format(address); 20808 } 20809 20810 if (typeof(this.style) === 'object') { 20811 format = this.style[address.format || "latin"]; 20812 } else { 20813 format = this.style; 20814 } 20815 20816 // console.log("Using format: " + format); 20817 // make sure we have a blank string for any missing parts so that 20818 // those template parts get blanked out 20819 var params = { 20820 country: address.country || "", 20821 region: address.region || "", 20822 locality: address.locality || "", 20823 streetAddress: address.streetAddress || "", 20824 postalCode: address.postalCode || "", 20825 postOffice: address.postOffice || "" 20826 }; 20827 template = new ilib.String(format); 20828 ret = template.format(params); 20829 ret = ret.replace(/[ \t]+/g, ' '); 20830 ret = ret.replace("\n ", "\n"); 20831 ret = ret.replace(" \n", "\n"); 20832 return ret.replace(/\n+/g, '\n').trim(); 20833 }; 20834 20835 /* 20836 * glyphstring.js - ilib string subclass that allows you to access 20837 * whole glyphs at a time 20838 * 20839 * Copyright © 2014, JEDLSoft 20840 * 20841 * Licensed under the Apache License, Version 2.0 (the "License"); 20842 * you may not use this file except in compliance with the License. 20843 * You may obtain a copy of the License at 20844 * 20845 * http://www.apache.org/licenses/LICENSE-2.0 20846 * 20847 * Unless required by applicable law or agreed to in writing, software 20848 * distributed under the License is distributed on an "AS IS" BASIS, 20849 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20850 * 20851 * See the License for the specific language governing permissions and 20852 * limitations under the License. 20853 */ 20854 20855 // !depends strings.js ctype.js 20856 // !data norm ctype_m 20857 20858 /** 20859 * @class 20860 * Create a new glyph string instance. This string inherits from 20861 * the ilib.String class, and adds methods that allow you to access 20862 * whole glyphs at a time. <p> 20863 * 20864 * In Unicode, various accented characters can be created by using 20865 * a base character and one or more combining characters following 20866 * it. These appear on the screen to the user as a single glyph. 20867 * For example, the Latin character "a" (U+0061) followed by the 20868 * combining diaresis character "¨" (U+0308) combine together to 20869 * form the "a with diaresis" glyph "ä", which looks like a single 20870 * character on the screen.<p> 20871 * 20872 * The big problem with combining characters for web developers is 20873 * that many CSS engines do not ellipsize text between glyphs. They 20874 * only deal with single Unicode characters. So if a particular space 20875 * only allows for 4 characters, the CSS engine will truncate a 20876 * string at 4 Unicode characters and then add the ellipsis (...) 20877 * character. What if the fourth Unicode character is the "a" and 20878 * the fifth one is the diaresis? Then a string like "xxxäxxx" that 20879 * is ellipsized at 4 characters will appear as "xxxa..." on the 20880 * screen instead of "xxxä...".<p> 20881 * 20882 * In the Latin script as it is commonly used, it is not so common 20883 * to form accented characters using combining accents, so the above 20884 * example is mostly for illustrative purposes. It is not unheard of 20885 * however. The situation is much, much worse in scripts such as Thai and 20886 * Devanagari that normally make very heavy use of combining characters. 20887 * These scripts do so because Unicode does not include pre-composed 20888 * versions of the accented characters like it does for Latin, so 20889 * combining accents are the only way to create these accented and 20890 * combined versions of the characters.<p> 20891 * 20892 * The solution to thise problem is not to use the the CSS property 20893 * "text-overflow: ellipsis" in your web site, ever. Instead, use 20894 * a glyph string to truncate text between glyphs instead of between 20895 * characters.<p> 20896 * 20897 * Glyph strings are also useful for truncation, hyphenation, and 20898 * line wrapping, as all of these should be done between glyphs instead 20899 * of between characters.<p> 20900 * 20901 * The options parameter is optional, and may contain any combination 20902 * of the following properties:<p> 20903 * 20904 * <ul> 20905 * <li><i>onLoad</i> - a callback function to call when the locale data are 20906 * fully loaded. When the onLoad option is given, this object will attempt to 20907 * load any missing locale data using the ilib loader callback. 20908 * When the constructor is done (even if the data is already preassembled), the 20909 * onLoad function is called with the current instance as a parameter, so this 20910 * callback can be used with preassembled or dynamic loading or a mix of the two. 20911 * 20912 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 20913 * asynchronously. If this option is given as "false", then the "onLoad" 20914 * callback must be given, as the instance returned from this constructor will 20915 * not be usable for a while. 20916 * 20917 * <li><i>loadParams</i> - an object containing parameters to pass to the 20918 * loader callback function when locale data is missing. The parameters are not 20919 * interpretted or modified in any way. They are simply passed along. The object 20920 * may contain any property/value pairs as long as the calling code is in 20921 * agreement with the loader callback function as to what those parameters mean. 20922 * </ul> 20923 * 20924 * 20925 * Depends directive: !depends glyphstring.js 20926 * 20927 * @constructor 20928 * @param {string|ilib.String=} str initialize this instance with this string 20929 * @param {Object=} options options governing the way this instance works 20930 */ 20931 ilib.GlyphString = function (str, options) { 20932 ilib.String.call(this, str); 20933 20934 var sync = true; 20935 var loadParams = {}; 20936 if (options) { 20937 if (typeof(options.sync) === 'boolean') { 20938 sync = options.sync; 20939 } 20940 if (options.loadParams) { 20941 loadParams = options.loadParams; 20942 } 20943 } 20944 20945 ilib.CType._load("ctype_m", sync, loadParams, function() { 20946 if (typeof(ilib.data.norm) === 'undefined' || typeof(ilib.data.norm.ccc) === 'undefined') { 20947 ilib.loadData({ 20948 object: ilib.GlyphString, 20949 locale: "-", 20950 name: "norm.json", 20951 nonlocale: true, 20952 sync: sync, 20953 loadParams: loadParams, 20954 callback: ilib.bind(this, function (norm) { 20955 ilib.data.norm = norm; 20956 if (options && typeof(options.onLoad) === 'function') { 20957 options.onLoad(this); 20958 } 20959 }) 20960 }); 20961 } else { 20962 if (options && typeof(options.onLoad) === 'function') { 20963 options.onLoad(this); 20964 } 20965 } 20966 }); 20967 }; 20968 20969 ilib.GlyphString.prototype = new ilib.String(); 20970 ilib.GlyphString.parent = ilib.String; 20971 ilib.GlyphString.prototype.constructor = ilib.GlyphString; 20972 20973 //ilib.GlyphString.prototype.iterator = function () { 20974 20975 //}; 20976 20977 /** 20978 * Return true if the given character is a leading Jamo (Choseong) character. 20979 * 20980 * @private 20981 * @static 20982 * @param {number} n code point to check 20983 * @return {boolean} true if the character is a leading Jamo character, 20984 * false otherwise 20985 */ 20986 ilib.GlyphString._isJamoL = function (n) { 20987 return (n >= 0x1100 && n <= 0x1112); 20988 }; 20989 20990 /** 20991 * Return true if the given character is a vowel Jamo (Jungseong) character. 20992 * 20993 * @private 20994 * @static 20995 * @param {number} n code point to check 20996 * @return {boolean} true if the character is a vowel Jamo character, 20997 * false otherwise 20998 */ 20999 ilib.GlyphString._isJamoV = function (n) { 21000 return (n >= 0x1161 && n <= 0x1175); 21001 }; 21002 21003 /** 21004 * Return true if the given character is a trailing Jamo (Jongseong) character. 21005 * 21006 * @private 21007 * @static 21008 * @param {number} n code point to check 21009 * @return {boolean} true if the character is a trailing Jamo character, 21010 * false otherwise 21011 */ 21012 ilib.GlyphString._isJamoT = function (n) { 21013 return (n >= 0x11A8 && n <= 0x11C2); 21014 }; 21015 21016 /** 21017 * Return true if the given character is a precomposed Hangul character. 21018 * 21019 * @private 21020 * @static 21021 * @param {number} n code point to check 21022 * @return {boolean} true if the character is a precomposed Hangul character, 21023 * false otherwise 21024 */ 21025 ilib.GlyphString._isHangul = function (n) { 21026 return (n >= 0xAC00 && n <= 0xD7A3); 21027 }; 21028 21029 /** 21030 * Algorithmically compose an L and a V combining Jamo characters into 21031 * a precomposed Korean syllabic Hangul character. Both should already 21032 * be in the proper ranges for L and V characters. 21033 * 21034 * @private 21035 * @static 21036 * @param {number} lead the code point of the lead Jamo character to compose 21037 * @param {number} trail the code point of the trailing Jamo character to compose 21038 * @return {string} the composed Hangul character 21039 */ 21040 ilib.GlyphString._composeJamoLV = function (lead, trail) { 21041 var lindex = lead - 0x1100; 21042 var vindex = trail - 0x1161; 21043 return ilib.String.fromCodePoint(0xAC00 + (lindex * 21 + vindex) * 28); 21044 }; 21045 21046 /** 21047 * Algorithmically compose a Hangul LV and a combining Jamo T character 21048 * into a precomposed Korean syllabic Hangul character. 21049 * 21050 * @private 21051 * @static 21052 * @param {number} lead the code point of the lead Hangul character to compose 21053 * @param {number} trail the code point of the trailing Jamo T character to compose 21054 * @return {string} the composed Hangul character 21055 */ 21056 ilib.GlyphString._composeJamoLVT = function (lead, trail) { 21057 return ilib.String.fromCodePoint(lead + (trail - 0x11A7)); 21058 }; 21059 21060 /** 21061 * Compose one character out of a leading character and a 21062 * trailing character. If the characters are Korean Jamo, they 21063 * will be composed algorithmically. If they are any other 21064 * characters, they will be looked up in the nfc tables. 21065 * 21066 * @private 21067 * @static 21068 * @param {string} lead leading character to compose 21069 * @param {string} trail the trailing character to compose 21070 * @return {string} the fully composed character, or undefined if 21071 * there is no composition for those two characters 21072 */ 21073 ilib.GlyphString._compose = function (lead, trail) { 21074 var first = lead.charCodeAt(0); 21075 var last = trail.charCodeAt(0); 21076 if (ilib.GlyphString._isHangul(first) && ilib.GlyphString._isJamoT(last)) { 21077 return ilib.GlyphString._composeJamoLVT(first, last); 21078 } else if (ilib.GlyphString._isJamoL(first) && ilib.GlyphString._isJamoV(last)) { 21079 return ilib.GlyphString._composeJamoLV(first, last); 21080 } 21081 21082 var c = lead + trail; 21083 return (ilib.data.norm.nfc && ilib.data.norm.nfc[c]); 21084 }; 21085 21086 /** 21087 * Return an iterator that will step through all of the characters 21088 * in the string one at a time, taking care to step through decomposed 21089 * characters and through surrogate pairs in the UTF-16 encoding 21090 * as single characters. <p> 21091 * 21092 * The GlyphString class will return decomposed Unicode characters 21093 * as a single unit that a user might see on the screen as a single 21094 * glyph. If the 21095 * next character in the iteration is a base character and it is 21096 * followed by combining characters, the base and all its following 21097 * combining characters are returned as a single unit.<p> 21098 * 21099 * The standard Javascript String's charAt() method only 21100 * returns information about a particular 16-bit character in the 21101 * UTF-16 encoding scheme. 21102 * If the index is pointing to a low- or high-surrogate character, 21103 * it will return that surrogate character rather 21104 * than the surrogate pair which represents a character 21105 * in the supplementary planes.<p> 21106 * 21107 * The iterator instance returned has two methods, hasNext() which 21108 * returns true if the iterator has more characters to iterate through, 21109 * and next() which returns the next character.<p> 21110 * 21111 * @override 21112 * @return {Object} an iterator 21113 * that iterates through all the characters in the string 21114 */ 21115 ilib.GlyphString.prototype.charIterator = function() { 21116 var it = ilib.String.prototype.charIterator.call(this); 21117 21118 /** 21119 * @constructor 21120 */ 21121 function _chiterator (istring) { 21122 this.index = 0; 21123 this.spacingCombining = false; 21124 this.hasNext = function () { 21125 return !!this.nextChar || it.hasNext(); 21126 }; 21127 this.next = function () { 21128 var ch = this.nextChar || it.next(), 21129 prevCcc = ilib.data.norm.ccc[ch], 21130 nextCcc, 21131 composed = ch; 21132 21133 this.nextChar = undefined; 21134 this.spacingCombining = false; 21135 21136 if (ilib.data.norm.ccc && 21137 (typeof(ilib.data.norm.ccc[ch]) === 'undefined' || ilib.data.norm.ccc[ch] === 0)) { 21138 // found a starter... find all the non-starters until the next starter. Must include 21139 // the next starter because under some odd circumstances, two starters sometimes recompose 21140 // together to form another character 21141 var notdone = true; 21142 while (it.hasNext() && notdone) { 21143 this.nextChar = it.next(); 21144 nextCcc = ilib.data.norm.ccc[this.nextChar]; 21145 var codePoint = ilib.String.toCodePoint(this.nextChar, 0); 21146 // Mn characters are Marks that are non-spacing. These do not take more room than an accent, so they should be 21147 // considered part of the on-screen glyph, even if they are non-combining. Mc are marks that are spacing 21148 // and combining, which means they are part of the glyph, but they cause the glyph to use up more space than 21149 // just the base character alone. 21150 var isMn = ilib.CType._inRange(codePoint, "Mn", ilib.data.ctype_m); 21151 var isMc = ilib.CType._inRange(codePoint, "Mc", ilib.data.ctype_m); 21152 if (isMn || isMc || (typeof(nextCcc) !== 'undefined' && nextCcc !== 0)) { 21153 if (isMc) { 21154 this.spacingCombining = true; 21155 } 21156 ch += this.nextChar; 21157 this.nextChar = undefined; 21158 } else { 21159 // found the next starter. See if this can be composed with the previous starter 21160 var testChar = ilib.GlyphString._compose(composed, this.nextChar); 21161 if (prevCcc === 0 && typeof(testChar) !== 'undefined') { 21162 // not blocked and there is a mapping 21163 composed = testChar; 21164 ch += this.nextChar; 21165 this.nextChar = undefined; 21166 } else { 21167 // finished iterating, leave this.nextChar for the next next() call 21168 notdone = false; 21169 } 21170 } 21171 prevCcc = nextCcc; 21172 } 21173 } 21174 return ch; 21175 }; 21176 // Returns true if the last character returned by the "next" method included 21177 // spacing combining characters. If it does, then the character was wider than 21178 // just the base character alone, and the truncation code will not add it. 21179 this.wasSpacingCombining = function() { 21180 return this.spacingCombining; 21181 }; 21182 }; 21183 return new _chiterator(this); 21184 }; 21185 21186 /** 21187 * Truncate the current string at the given number of whole glyphs and return 21188 * the resulting string. 21189 * 21190 * @param {number} length the number of whole glyphs to keep in the string 21191 * @return {string} a string truncated to the requested number of glyphs 21192 */ 21193 ilib.GlyphString.prototype.truncate = function(length) { 21194 var it = this.charIterator(); 21195 var tr = ""; 21196 for (var i = 0; i < length-1 && it.hasNext(); i++) { 21197 tr += it.next(); 21198 } 21199 21200 /* 21201 * handle the last character separately. If it contains spacing combining 21202 * accents, then we must assume that it uses up more horizontal space on 21203 * the screen than just the base character by itself, and therefore this 21204 * method will not truncate enough characters to fit in the given length. 21205 * In this case, we have to chop off not only the combining characters, 21206 * but also the base character as well because the base without the 21207 * combining accents is considered a different character. 21208 */ 21209 if (i < length && it.hasNext()) { 21210 var c = it.next(); 21211 if (!it.wasSpacingCombining()) { 21212 tr += c; 21213 } 21214 } 21215 return tr; 21216 }; 21217 21218 /** 21219 * Truncate the current string at the given number of glyphs and add an ellipsis 21220 * to indicate that is more to the string. The ellipsis forms the last character 21221 * in the string, so the string is actually truncated at length-1 glyphs. 21222 * 21223 * @param {number} length the number of whole glyphs to keep in the string 21224 * including the ellipsis 21225 * @return {string} a string truncated to the requested number of glyphs 21226 * with an ellipsis 21227 */ 21228 ilib.GlyphString.prototype.ellipsize = function(length) { 21229 return this.truncate(length > 0 ? length-1 : 0) + "…"; 21230 }; 21231 21232 21233 /* 21234 * normstring.js - ilib normalized string subclass definition 21235 * 21236 * Copyright © 2013-2014, JEDLSoft 21237 * 21238 * Licensed under the Apache License, Version 2.0 (the "License"); 21239 * you may not use this file except in compliance with the License. 21240 * You may obtain a copy of the License at 21241 * 21242 * http://www.apache.org/licenses/LICENSE-2.0 21243 * 21244 * Unless required by applicable law or agreed to in writing, software 21245 * distributed under the License is distributed on an "AS IS" BASIS, 21246 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21247 * 21248 * See the License for the specific language governing permissions and 21249 * limitations under the License. 21250 */ 21251 21252 // !depends strings.js glyphstring.js 21253 21254 /** 21255 * @class 21256 * Create a new normalized string instance. This string inherits from 21257 * the ilib.GlyphString class, and adds the normalize method. It can be 21258 * used anywhere that a normal Javascript string is used. <p> 21259 * 21260 * Depends directive: !depends normstring.js 21261 * 21262 * @constructor 21263 * @param {string|ilib.String=} str initialize this instance with this string 21264 */ 21265 ilib.NormString = function (str) { 21266 ilib.GlyphString.call(this, str); 21267 }; 21268 21269 ilib.NormString.prototype = new ilib.GlyphString(); 21270 ilib.NormString.parent = ilib.GlyphString; 21271 ilib.NormString.prototype.constructor = ilib.NormString; 21272 21273 /** 21274 * Initialize the normalized string routines statically. This 21275 * is intended to be called in a dynamic-load version of ilib 21276 * to load the data need to normalize strings before any instances 21277 * of ilib.NormString are created.<p> 21278 * 21279 * The options parameter may contain any of the following properties: 21280 * 21281 * <ul> 21282 * <li><i>form</i> - {string} the normalization form to load 21283 * <li><i>script</i> - {string} load the normalization for this script. If the 21284 * script is given as "all" then the normalization data for all scripts 21285 * is loaded at the same time 21286 * <li><i>sync</i> - {boolean} whether to load the files synchronously or not 21287 * <li><i>loadParams</i> - {Object} parameters to the loader function 21288 * <li><i>onLoad</i> - {function()} a function to call when the 21289 * files are done being loaded 21290 * </ul> 21291 * 21292 * @param {Object} options an object containing properties that govern 21293 * how to initialize the data 21294 */ 21295 ilib.NormString.init = function(options) { 21296 if (!ilib._load || (typeof(ilib._load) !== 'function' && !(ilib._load instanceof ilib.Loader))) { 21297 // can't do anything 21298 return; 21299 } 21300 var form = "nfkc"; 21301 var script = "all"; 21302 var sync = true; 21303 var onLoad = undefined; 21304 var loadParams = undefined; 21305 if (options) { 21306 form = options.form || "nfkc"; 21307 script = options.script || "all"; 21308 sync = typeof(options.sync) !== 'undefined' ? options.sync : true; 21309 onLoad = typeof(options.onLoad) === 'function' ? options.onLoad : undefined; 21310 if (options.loadParams) { 21311 loadParams = options.loadParams; 21312 } 21313 } 21314 var formDependencies = { 21315 "nfd": ["nfd"], 21316 "nfc": ["nfd"], 21317 "nfkd": ["nfkd", "nfd"], 21318 "nfkc": ["nfkd", "nfd"] 21319 }; 21320 var files = ["norm.json"]; 21321 var forms = formDependencies[form]; 21322 for (var f in forms) { 21323 files.push(forms[f] + "/" + script + ".json"); 21324 } 21325 21326 ilib._callLoadData(files, sync, loadParams, function(arr) { 21327 ilib.data.norm = arr[0]; 21328 for (var i = 1; i < arr.length; i++) { 21329 if (typeof(arr[i]) !== 'undefined') { 21330 ilib.data.norm[forms[i-1]] = arr[i]; 21331 } 21332 } 21333 21334 if (onLoad) { 21335 onLoad(arr); 21336 } 21337 }); 21338 }; 21339 21340 /** 21341 * Algorithmically decompose a precomposed Korean syllabic Hangul 21342 * character into its individual combining Jamo characters. The given 21343 * character must be in the range of Hangul characters U+AC00 to U+D7A3. 21344 * 21345 * @private 21346 * @static 21347 * @param {number} cp code point of a Korean Hangul character to decompose 21348 * @return {string} the decomposed string of Jamo characters 21349 */ 21350 ilib.NormString._decomposeHangul = function (cp) { 21351 var sindex = cp - 0xAC00; 21352 var result = String.fromCharCode(0x1100 + sindex / 588) + 21353 String.fromCharCode(0x1161 + (sindex % 588) / 28); 21354 var t = sindex % 28; 21355 if (t !== 0) { 21356 result += String.fromCharCode(0x11A7 + t); 21357 } 21358 return result; 21359 }; 21360 21361 /** 21362 * Expand one character according to the given canonical and 21363 * compatibility mappings. 21364 * 21365 * @private 21366 * @static 21367 * @param {string} ch character to map 21368 * @param {Object} canon the canonical mappings to apply 21369 * @param {Object=} compat the compatibility mappings to apply, or undefined 21370 * if only the canonical mappings are needed 21371 * @return {string} the mapped character 21372 */ 21373 ilib.NormString._expand = function (ch, canon, compat) { 21374 var i, 21375 expansion = "", 21376 n = ch.charCodeAt(0); 21377 if (ilib.GlyphString._isHangul(n)) { 21378 expansion = ilib.NormString._decomposeHangul(n); 21379 } else { 21380 var result = canon[ch]; 21381 if (!result && compat) { 21382 result = compat[ch]; 21383 } 21384 if (result && result !== ch) { 21385 for (i = 0; i < result.length; i++) { 21386 expansion += ilib.NormString._expand(result[i], canon, compat); 21387 } 21388 } else { 21389 expansion = ch; 21390 } 21391 } 21392 return expansion; 21393 }; 21394 21395 /** 21396 * Perform the Unicode Normalization Algorithm upon the string and return 21397 * the resulting new string. The current string is not modified. 21398 * 21399 * <h2>Forms</h2> 21400 * 21401 * The forms of possible normalizations are defined by the <a 21402 * href="http://www.unicode.org/reports/tr15/">Unicode Standard 21403 * Annex (UAX) 15</a>. The form parameter is a string that may have one 21404 * of the following values: 21405 * 21406 * <ul> 21407 * <li>nfd - Canonical decomposition. This decomposes characters into 21408 * their exactly equivalent forms. For example, "ü" would decompose 21409 * into a "u" followed by the combining diaeresis character. 21410 * <li>nfc - Canonical decomposition followed by canonical composition. 21411 * This decomposes and then recomposes character into their shortest 21412 * exactly equivalent forms by recomposing as many combining characters 21413 * as possible. For example, "ü" followed by a combining 21414 * macron character would decompose into a "u" followed by the combining 21415 * macron characters the combining diaeresis character, and then be recomposed into 21416 * the u with macron and diaeresis "ṻ" character. The reason that 21417 * the "nfc" form decomposes and then recomposes is that combining characters 21418 * have a specific order under the Unicode Normalization Algorithm, and 21419 * partly composed characters such as the "ü" followed by combining 21420 * marks may change the order of the combining marks when decomposed and 21421 * recomposed. 21422 * <li>nfkd - Compatibility decomposition. This decomposes characters 21423 * into compatible forms that may not be exactly equivalent semantically, 21424 * as well as performing canonical decomposition as well. 21425 * For example, the "œ" ligature character decomposes to the two 21426 * characters "oe" because they are compatible even though they are not 21427 * exactly the same semantically. 21428 * <li>nfkc - Compatibility decomposition followed by canonical composition. 21429 * This decomposes characters into compatible forms, then recomposes 21430 * characters using the canonical composition. That is, it breaks down 21431 * characters into the compatible forms, and then recombines all combining 21432 * marks it can with their base characters. For example, the character 21433 * "ǽ" would be normalized to "aé" by first decomposing 21434 * the character into "a" followed by "e" followed by the combining acute accent 21435 * combining mark, and then recomposed to an "a" followed by the "e" 21436 * with acute accent. 21437 * </ul> 21438 * 21439 * <h2>Operation</h2> 21440 * 21441 * Two strings a and b can be said to be canonically equivalent if 21442 * normalize(a) = normalize(b) 21443 * under the nfc normalization form. Two strings can be said to be compatible if 21444 * normalize(a) = normalize(b) under the nfkc normalization form.<p> 21445 * 21446 * The canonical normalization is often used to see if strings are 21447 * equivalent to each other, and thus is useful when implementing parsing 21448 * algorithms or exact matching algorithms. It can also be used to ensure 21449 * that any string output produces a predictable sequence of characters.<p> 21450 * 21451 * Compatibility normalization 21452 * does not always preserve the semantic meaning of all the characters, 21453 * although this is sometimes the behaviour that you are after. It is useful, 21454 * for example, when doing searches of user-input against text in documents 21455 * where the matches are supposed to "fuzzy". In this case, both the query 21456 * string and the document string would be mapped to their compatibility 21457 * normalized forms, and then compared.<p> 21458 * 21459 * Compatibility normalization also does not guarantee round-trip conversion 21460 * to and from legacy character sets as the normalization is "lossy". It is 21461 * akin to doing a lower- or upper-case conversion on text -- after casing, 21462 * you cannot tell what case each character is in the original string. It is 21463 * good for matching and searching, but it rarely good for output because some 21464 * distinctions or meanings in the original text have been lost.<p> 21465 * 21466 * Note that W3C normalization for HTML also escapes and unescapes 21467 * HTML character entities such as "ü" for u with diaeresis. This 21468 * method does not do such escaping or unescaping. If normalization is required 21469 * for HTML strings with entities, unescaping should be performed on the string 21470 * prior to calling this method.<p> 21471 * 21472 * <h2>Data</h2> 21473 * 21474 * Normalization requires a fair amount of mapping data, much of which you may 21475 * not need for the characters expected in your texts. It is possible to assemble 21476 * a copy of ilib that saves space by only including normalization data for 21477 * those scripts that you expect to encounter in your data.<p> 21478 * 21479 * The normalization data is organized by normalization form and within there 21480 * by script. To include the normalization data for a particular script with 21481 * a particular normalization form, use the directive: 21482 * 21483 * <pre><code> 21484 * !depends <form>/<script>.js 21485 * </code></pre> 21486 * 21487 * Where <form> is the normalization form ("nfd", "nfc", "nfkd", or "nfkc"), and 21488 * <script> is the ISO 15924 code for the script you would like to 21489 * support. Example: to load in the NFC data for Cyrillic, you would use: 21490 * 21491 * <pre><code> 21492 * !depends nfc/Cyrl.js 21493 * </code></pre> 21494 * 21495 * Note that because certain normalization forms include others in their algorithm, 21496 * their data also depends on the data for the other forms. For example, if you 21497 * include the "nfc" data for a script, you will automatically get the "nfd" data 21498 * for that same script as well because the NFC algorithm does NFD normalization 21499 * first. Here are the dependencies:<p> 21500 * 21501 * <ul> 21502 * <li>NFD -> no dependencies 21503 * <li>NFC -> NFD 21504 * <li>NFKD -> NFD 21505 * <li>NFKC -> NFKD, NFD, NFC 21506 * </ul> 21507 * 21508 * A special value for the script dependency is "all" which will cause the data for 21509 * all scripts 21510 * to be loaded for that normalization form. This would be useful if you know that 21511 * you are going to normalize a lot of multilingual text or cannot predict which scripts 21512 * will appear in the input. Because the NFKC form depends on all others, you can 21513 * get all of the data for all forms automatically by depending on "nfkc/all.js". 21514 * Note that the normalization data for practically all script automatically depend 21515 * on data for the Common script (code "Zyyy") which contains all of the characters 21516 * that are commonly used in many different scripts. Examples of characters in the 21517 * Common script are the ASCII punctuation characters, or the ASCII Arabic 21518 * numerals "0" through "9".<p> 21519 * 21520 * By default, none of the data for normalization is automatically 21521 * included in the preassembled iliball.js file. 21522 * If you would like to normalize strings, you must assemble 21523 * your own copy of ilib and explicitly include the normalization data 21524 * for those scripts as per the instructions above. This normalization method will 21525 * produce output, even without the normalization data. However, the output will be 21526 * simply the same thing as its input for all scripts 21527 * except Korean Hangul and Jamo, which are decomposed and recomposed 21528 * algorithmically and therefore do not rely on data.<p> 21529 * 21530 * If characters are encountered for which there are no normalization data, they 21531 * will be passed through to the output string unmodified. 21532 * 21533 * @param {string} form The normalization form requested 21534 * @return {ilib.String} a new instance of an ilib.String that has been normalized 21535 * according to the requested form. The current instance is not modified. 21536 */ 21537 ilib.NormString.prototype.normalize = function (form) { 21538 var i; 21539 21540 if (typeof(form) !== 'string' || this.str.length === 0) { 21541 return new ilib.String(this.str); 21542 } 21543 21544 var nfc = false, 21545 nfkd = false; 21546 21547 switch (form) { 21548 default: 21549 break; 21550 21551 case "nfc": 21552 nfc = true; 21553 break; 21554 21555 case "nfkd": 21556 nfkd = true; 21557 break; 21558 21559 case "nfkc": 21560 nfkd = true; 21561 nfc = true; 21562 break; 21563 } 21564 21565 // decompose 21566 var decomp = ""; 21567 21568 if (nfkd) { 21569 var ch, it = ilib.String.prototype.charIterator.call(this); 21570 while (it.hasNext()) { 21571 ch = it.next(); 21572 decomp += ilib.NormString._expand(ch, ilib.data.norm.nfd, ilib.data.norm.nfkd); 21573 } 21574 } else { 21575 var ch, it = ilib.String.prototype.charIterator.call(this); 21576 while (it.hasNext()) { 21577 ch = it.next(); 21578 decomp += ilib.NormString._expand(ch, ilib.data.norm.nfd); 21579 } 21580 } 21581 21582 // now put the combining marks in a fixed order by 21583 // sorting on the combining class 21584 function compareByCCC(left, right) { 21585 return ilib.data.norm.ccc[left] - ilib.data.norm.ccc[right]; 21586 } 21587 21588 function ccc(c) { 21589 return ilib.data.norm.ccc[c] || 0; 21590 } 21591 21592 var dstr = new ilib.String(decomp); 21593 var it = dstr.charIterator(); 21594 var cpArray = []; 21595 21596 // easier to deal with as an array of chars 21597 while (it.hasNext()) { 21598 cpArray.push(it.next()); 21599 } 21600 21601 i = 0; 21602 while (i < cpArray.length) { 21603 if (typeof(ilib.data.norm.ccc[cpArray[i]]) !== 'undefined' && ccc(cpArray[i]) !== 0) { 21604 // found a non-starter... rearrange all the non-starters until the next starter 21605 var end = i+1; 21606 while (end < cpArray.length && 21607 typeof(ilib.data.norm.ccc[cpArray[end]]) !== 'undefined' && 21608 ccc(cpArray[end]) !== 0) { 21609 end++; 21610 } 21611 21612 // simple sort of the non-starter chars 21613 if (end - i > 1) { 21614 cpArray = cpArray.slice(0,i).concat(cpArray.slice(i, end).sort(compareByCCC), cpArray.slice(end)); 21615 } 21616 } 21617 i++; 21618 } 21619 21620 if (nfc) { 21621 i = 0; 21622 while (i < cpArray.length) { 21623 if (typeof(ilib.data.norm.ccc[cpArray[i]]) === 'undefined' || ilib.data.norm.ccc[cpArray[i]] === 0) { 21624 // found a starter... find all the non-starters until the next starter. Must include 21625 // the next starter because under some odd circumstances, two starters sometimes recompose 21626 // together to form another character 21627 var end = i+1; 21628 var notdone = true; 21629 while (end < cpArray.length && notdone) { 21630 if (typeof(ilib.data.norm.ccc[cpArray[end]]) !== 'undefined' && 21631 ilib.data.norm.ccc[cpArray[end]] !== 0) { 21632 if (ccc(cpArray[end-1]) < ccc(cpArray[end])) { 21633 // not blocked 21634 var testChar = ilib.GlyphString._compose(cpArray[i], cpArray[end]); 21635 if (typeof(testChar) !== 'undefined') { 21636 cpArray[i] = testChar; 21637 21638 // delete the combining char 21639 cpArray.splice(end,1); 21640 21641 // restart the iteration, just in case there is more to recompose with the new char 21642 end = i; 21643 } 21644 } 21645 end++; 21646 } else { 21647 // found the next starter. See if this can be composed with the previous starter 21648 var testChar = ilib.GlyphString._compose(cpArray[i], cpArray[end]); 21649 if (ccc(cpArray[end-1]) === 0 && typeof(testChar) !== 'undefined') { 21650 // not blocked and there is a mapping 21651 cpArray[i] = testChar; 21652 21653 // delete the combining char 21654 cpArray.splice(end,1); 21655 21656 // restart the iteration, just in case there is more to recompose with the new char 21657 end = i+1; 21658 } else { 21659 // finished iterating 21660 notdone = false; 21661 } 21662 } 21663 } 21664 } 21665 i++; 21666 } 21667 } 21668 21669 return new ilib.String(cpArray.length > 0 ? cpArray.join("") : ""); 21670 }; 21671 21672 /** 21673 * @override 21674 * Return an iterator that will step through all of the characters 21675 * in the string one at a time, taking care to step through decomposed 21676 * characters and through surrogate pairs in UTF-16 encoding 21677 * properly. <p> 21678 * 21679 * The NormString class will return decomposed Unicode characters 21680 * as a single unit that a user might see on the screen. If the 21681 * next character in the iteration is a base character and it is 21682 * followed by combining characters, the base and all its following 21683 * combining characters are returned as a single unit.<p> 21684 * 21685 * The standard Javascript String's charAt() method only 21686 * returns information about a particular 16-bit character in the 21687 * UTF-16 encoding scheme. 21688 * If the index is pointing to a low- or high-surrogate character, 21689 * it will return that surrogate character rather 21690 * than the surrogate pair which represents a character 21691 * in the supplementary planes.<p> 21692 * 21693 * The iterator instance returned has two methods, hasNext() which 21694 * returns true if the iterator has more characters to iterate through, 21695 * and next() which returns the next character.<p> 21696 * 21697 * @return {Object} an iterator 21698 * that iterates through all the characters in the string 21699 */ 21700 ilib.NormString.prototype.charIterator = function() { 21701 var it = ilib.String.prototype.charIterator.call(this); 21702 21703 /** 21704 * @constructor 21705 */ 21706 function _chiterator (istring) { 21707 /** 21708 * @private 21709 */ 21710 var ccc = function(c) { 21711 return ilib.data.norm.ccc[c] || 0; 21712 }; 21713 21714 this.index = 0; 21715 this.hasNext = function () { 21716 return !!this.nextChar || it.hasNext(); 21717 }; 21718 this.next = function () { 21719 var ch = this.nextChar || it.next(), 21720 prevCcc = ccc(ch), 21721 nextCcc, 21722 composed = ch; 21723 21724 this.nextChar = undefined; 21725 21726 if (ilib.data.norm.ccc && 21727 (typeof(ilib.data.norm.ccc[ch]) === 'undefined' || ccc(ch) === 0)) { 21728 // found a starter... find all the non-starters until the next starter. Must include 21729 // the next starter because under some odd circumstances, two starters sometimes recompose 21730 // together to form another character 21731 var notdone = true; 21732 while (it.hasNext() && notdone) { 21733 this.nextChar = it.next(); 21734 nextCcc = ccc(this.nextChar); 21735 if (typeof(ilib.data.norm.ccc[this.nextChar]) !== 'undefined' && nextCcc !== 0) { 21736 ch += this.nextChar; 21737 this.nextChar = undefined; 21738 } else { 21739 // found the next starter. See if this can be composed with the previous starter 21740 var testChar = ilib.GlyphString._compose(composed, this.nextChar); 21741 if (prevCcc === 0 && typeof(testChar) !== 'undefined') { 21742 // not blocked and there is a mapping 21743 composed = testChar; 21744 ch += this.nextChar; 21745 this.nextChar = undefined; 21746 } else { 21747 // finished iterating, leave this.nextChar for the next next() call 21748 notdone = false; 21749 } 21750 } 21751 prevCcc = nextCcc; 21752 } 21753 } 21754 return ch; 21755 }; 21756 }; 21757 return new _chiterator(this); 21758 }; 21759 21760 21761 /* 21762 * collate.js - Collation routines 21763 * 21764 * Copyright © 2013-2014, JEDLSoft 21765 * 21766 * Licensed under the Apache License, Version 2.0 (the "License"); 21767 * you may not use this file except in compliance with the License. 21768 * You may obtain a copy of the License at 21769 * 21770 * http://www.apache.org/licenses/LICENSE-2.0 21771 * 21772 * Unless required by applicable law or agreed to in writing, software 21773 * distributed under the License is distributed on an "AS IS" BASIS, 21774 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21775 * 21776 * See the License for the specific language governing permissions and 21777 * limitations under the License. 21778 */ 21779 21780 // !depends locale.js ilibglobal.js numprs.js ctype.ispunct.js normstring.js util/math.js 21781 21782 // !data collation 21783 21784 /** 21785 * @class 21786 * Represents a buffered source of code points. The input string is first 21787 * normalized so that combining characters come out in a standardized order. 21788 * If the "ignorePunctuation" flag is turned on, then punctuation 21789 * characters are skipped. 21790 * 21791 * @constructor 21792 * @private 21793 * @param {ilib.NormString|string} str a string to get code points from 21794 * @param {boolean} ignorePunctuation whether or not to ignore punctuation 21795 * characters 21796 */ 21797 ilib.CodePointSource = function(str, ignorePunctuation) { 21798 this.chars = []; 21799 // first convert the string to a normalized sequence of characters 21800 var s = (typeof(str) === "string") ? new ilib.NormString(str) : str; 21801 this.it = s.charIterator(); 21802 this.ignorePunctuation = typeof(ignorePunctuation) === "boolean" && ignorePunctuation; 21803 }; 21804 21805 /** 21806 * Return the first num code points in the source without advancing the 21807 * source pointer. If there are not enough code points left in the 21808 * string to satisfy the request, this method will return undefined. 21809 * 21810 * @param {number} num the number of characters to peek ahead 21811 * @return {string|undefined} a string formed out of up to num code points from 21812 * the start of the string, or undefined if there are not enough character left 21813 * in the source to complete the request 21814 */ 21815 ilib.CodePointSource.prototype.peek = function(num) { 21816 if (num < 1) { 21817 return undefined; 21818 } 21819 if (this.chars.length < num && this.it.hasNext()) { 21820 for (var i = 0; this.chars.length < 4 && this.it.hasNext(); i++) { 21821 var c = this.it.next(); 21822 if (c && !this.ignorePunctuation || !ilib.CType.isPunct(c)) { 21823 this.chars.push(c); 21824 } 21825 } 21826 } 21827 if (this.chars.length < num) { 21828 return undefined; 21829 } 21830 return this.chars.slice(0, num).join(""); 21831 }; 21832 /** 21833 * Advance the source pointer by the given number of code points. 21834 * @param {number} num number of code points to advance 21835 */ 21836 ilib.CodePointSource.prototype.consume = function(num) { 21837 if (num > 0) { 21838 this.peek(num); // for the iterator to go forward if needed 21839 if (num < this.chars.length) { 21840 this.chars = this.chars.slice(num); 21841 } else { 21842 this.chars = []; 21843 } 21844 } 21845 }; 21846 21847 21848 /** 21849 * @class 21850 * An iterator through a sequence of collation elements. This 21851 * iterator takes a source of code points, converts them into 21852 * collation elements, and allows the caller to get single 21853 * elements at a time. 21854 * 21855 * @constructor 21856 * @private 21857 * @param {ilib.CodePointSource} source source of code points to 21858 * convert to collation elements 21859 * @param {Object} map mapping from sequences of code points to 21860 * collation elements 21861 * @param {number} keysize size in bits of the collation elements 21862 */ 21863 ilib.ElementIterator = function (source, map, keysize) { 21864 this.elements = []; 21865 this.source = source; 21866 this.map = map; 21867 this.keysize = keysize; 21868 }; 21869 21870 /** 21871 * @private 21872 */ 21873 ilib.ElementIterator.prototype._fillBuffer = function () { 21874 var str = undefined; 21875 21876 // peek ahead by up to 4 characters, which may combine 21877 // into 1 or more collation elements 21878 for (var i = 4; i > 0; i--) { 21879 str = this.source.peek(i); 21880 if (str && this.map[str]) { 21881 this.elements = this.elements.concat(this.map[str]); 21882 this.source.consume(i); 21883 return; 21884 } 21885 } 21886 21887 if (str) { 21888 // no mappings for the first code point, so just use its 21889 // Unicode code point as a proxy for its sort order. Shift 21890 // it by the key size so that everything unknown sorts 21891 // after things that have mappings 21892 this.elements.push(str.charCodeAt(0) << this.keysize); 21893 this.source.consume(1); 21894 } else { 21895 // end of the string 21896 return undefined; 21897 } 21898 }; 21899 21900 /** 21901 * Return true if there are more collation elements left to 21902 * iterate through. 21903 * @returns {boolean} true if there are more elements left to 21904 * iterate through, and false otherwise 21905 */ 21906 ilib.ElementIterator.prototype.hasNext = function () { 21907 if (this.elements.length < 1) { 21908 this._fillBuffer(); 21909 } 21910 return !!this.elements.length; 21911 }; 21912 21913 /** 21914 * Return the next collation element. If more than one collation 21915 * element is generated from a sequence of code points 21916 * (ie. an "expansion"), then this class will buffer the 21917 * other elements and return them on subsequent calls to 21918 * this method. 21919 * 21920 * @returns {number|undefined} the next collation element or 21921 * undefined for no more collation elements 21922 */ 21923 ilib.ElementIterator.prototype.next = function () { 21924 if (this.elements.length < 1) { 21925 this._fillBuffer(); 21926 } 21927 var ret = this.elements[0]; 21928 this.elements = this.elements.slice(1); 21929 return ret; 21930 }; 21931 21932 21933 /** 21934 * @class 21935 * A class that implements a locale-sensitive comparator function 21936 * for use with sorting function. The comparator function 21937 * assumes that the strings it is comparing contain Unicode characters 21938 * encoded in UTF-16.<p> 21939 * 21940 * Collations usually depend only on the language, because most collation orders 21941 * are shared between locales that speak the same language. There are, however, a 21942 * number of instances where a locale collates differently than other locales 21943 * that share the same language. There are also a number of instances where a 21944 * locale collates differently based on the script used. This object can handle 21945 * these cases automatically if a full locale is specified in the options rather 21946 * than just a language code.<p> 21947 * 21948 * <h2>Options</h2> 21949 * 21950 * The options parameter can contain any of the following properties: 21951 * 21952 * <ul> 21953 * <li><i>locale</i> - String|Locale. The locale which the comparator function 21954 * will collate with. Default: the current iLib locale. 21955 * 21956 * <li><i>sensitivity</i> - String. Sensitivity or strength of collator. This is one of 21957 * "primary", "base", "secondary", "accent", "tertiary", "case", "quaternary", or 21958 * "variant". Default: "primary" 21959 * <ol> 21960 * <li>base or primary - Only the primary distinctions between characters are significant. 21961 * Another way of saying that is that the collator will be case-, accent-, and 21962 * variation-insensitive, and only distinguish between the base characters 21963 * <li>case or secondary - Both the primary and secondary distinctions between characters 21964 * are significant. That is, the collator will be accent- and variation-insensitive 21965 * and will distinguish between base characters and character case. 21966 * <li>accent or tertiary - The primary, secondary, and tertiary distinctions between 21967 * characters are all significant. That is, the collator will be 21968 * variation-insensitive, but accent-, case-, and base-character-sensitive. 21969 * <li>variant or quaternary - All distinctions between characters are significant. That is, 21970 * the algorithm is base character-, case-, accent-, and variation-sensitive. 21971 * </ol> 21972 * 21973 * <li><i>upperFirst</i> - boolean. When collating case-sensitively in a script that 21974 * has the concept of case, put upper-case 21975 * characters first, otherwise lower-case will come first. Warning: some browsers do 21976 * not implement this feature or at least do not implement it properly, so if you are 21977 * using the native collator with this option, you may get different results in different 21978 * browsers. To guarantee the same results, set useNative to false to use the ilib 21979 * collator implementation. This of course will be somewhat slower, but more 21980 * predictable. Default: true 21981 * 21982 * <li><i>reverse</i> - boolean. Return the list sorted in reverse order. When the 21983 * upperFirst option is also set to true, upper-case characters would then come at 21984 * the end of the list. Default: false. 21985 * 21986 * <li><i>scriptOrder</i> - string. When collating strings in multiple scripts, 21987 * this property specifies what order those scripts should be sorted. The default 21988 * Unicode Collation Algorithm (UCA) already has a default order for scripts, but 21989 * this can be tailored via this property. The value of this option is a 21990 * space-separated list of ISO 15924 scripts codes. If a code is specified in this 21991 * property, its default data must be included using the JS assembly tool. If the 21992 * data is not included, the ordering for the script will be ignored. Default: 21993 * the default order defined by the UCA. 21994 * 21995 * <li><i>style</i> - The value of the style parameter is dependent on the locale. 21996 * For some locales, there are different styles of collating strings depending 21997 * on what kind of strings are being collated or what the preference of the user 21998 * is. For example, in German, there is a phonebook order and a dictionary ordering 21999 * that sort the same array of strings slightly differently. 22000 * The static method {@link ilib.Collator#getAvailableStyles} will return a list of styles that ilib 22001 * currently knows about for any given locale. If the value of the style option is 22002 * not recognized for a locale, it will be ignored. Default style is "standard".<p> 22003 * 22004 * <li><i>usage</i> - Whether this collator will be used for searching or sorting. 22005 * Valid values are simply the strings "sort" or "search". When used for sorting, 22006 * it is good idea if a collator produces a stable sort. That is, the order of the 22007 * sorted array of strings should not depend on the order of the strings in the 22008 * input array. As such, when a collator is supposed to act case insensitively, 22009 * it nonetheless still distinguishes between case after all other criteria 22010 * are satisfied so that strings that are distinguished only by case do not sort 22011 * randomly. For searching, we would like to match two strings that different only 22012 * by case, so the collator must return equals in that situation instead of 22013 * further distinguishing by case. Default is "sort". 22014 * 22015 * <li><i>numeric</i> - Treat the left and right strings as if they started with 22016 * numbers and sort them numerically rather than lexically. 22017 * 22018 * <li><i>ignorePunctuation</i> - Skip punctuation characters when comparing the 22019 * strings. 22020 * 22021 * <li>onLoad - a callback function to call when the collator object is fully 22022 * loaded. When the onLoad option is given, the collator object will attempt to 22023 * load any missing locale data using the ilib loader callback. 22024 * When the constructor is done (even if the data is already preassembled), the 22025 * onLoad function is called with the current instance as a parameter, so this 22026 * callback can be used with preassembled or dynamic loading or a mix of the two. 22027 * 22028 * <li>sync - tell whether to load any missing locale data synchronously or 22029 * asynchronously. If this option is given as "false", then the "onLoad" 22030 * callback must be given, as the instance returned from this constructor will 22031 * not be usable for a while. 22032 * 22033 * <li><i>loadParams</i> - an object containing parameters to pass to the 22034 * loader callback function when locale data is missing. The parameters are not 22035 * interpretted or modified in any way. They are simply passed along. The object 22036 * may contain any property/value pairs as long as the calling code is in 22037 * agreement with the loader callback function as to what those parameters mean. 22038 * 22039 * <li><i>useNative</i> - when this option is true, use the native Intl object 22040 * provided by the Javascript engine, if it exists, to implement this class. If 22041 * it doesn't exist, or if this parameter is false, then this class uses a pure 22042 * Javascript implementation, which is slower and uses a lot more memory, but 22043 * works everywhere that ilib works. Default is "true". 22044 * </ul> 22045 * 22046 * <h2>Operation</h2> 22047 * 22048 * The Collator constructor returns a collator object tailored with the above 22049 * options. The object contains an internal compare() method which compares two 22050 * strings according to those options. This can be used directly to compare 22051 * two strings, but is not useful for passing to the javascript sort function 22052 * because then it will not have its collation data available. Instead, use the 22053 * getComparator() method to retrieve a function that is bound to the collator 22054 * object. (You could also bind it yourself using ilib.bind()). The bound function 22055 * can be used with the standard Javascript array sorting algorithm, or as a 22056 * comparator with your own sorting algorithm.<p> 22057 * 22058 * Example using the standard Javascript array sorting call with the bound 22059 * function:<p> 22060 * 22061 * <code> 22062 * <pre> 22063 * var arr = ["ö", "oe", "ü", "o", "a", "ae", "u", "ß", "ä"]; 22064 * var collator = ilib.Collator({locale: 'de-DE', style: "dictionary"}); 22065 * arr.sort(collator.getComparator()); 22066 * console.log(JSON.stringify(arr)); 22067 * </pre> 22068 * </code> 22069 * <p> 22070 * 22071 * Would give the output:<p> 22072 * 22073 * <code> 22074 * <pre> 22075 * ["a", "ae", "ä", "o", "oe", "ö", "ß", "u", "ü"] 22076 * </pre> 22077 * </code> 22078 * 22079 * When sorting an array of Javascript objects according to one of the 22080 * string properties of the objects, wrap the collator's compare function 22081 * in your own comparator function that knows the structure of the objects 22082 * being sorted:<p> 22083 * 22084 * <code> 22085 * <pre> 22086 * var collator = ilib.Collator({locale: 'de-DE'}); 22087 * var myComparator = function (collator) { 22088 * var comparator = collator.getComparator(); 22089 * // left and right are your own objects 22090 * return function (left, right) { 22091 * return comparator(left.x.y.textProperty, right.x.y.textProperty); 22092 * }; 22093 * }; 22094 * arr.sort(myComparator(collator)); 22095 * </pre> 22096 * </code> 22097 * <p> 22098 * 22099 * <h2>Sort Keys</h2> 22100 * 22101 * The collator class also has a method to retrieve the sort key for a 22102 * string. The sort key is an array of values that represent how each 22103 * character in the string should be collated according to the characteristics 22104 * of the collation algorithm and the given options. Thus, sort keys can be 22105 * compared directly value-for-value with other sort keys that were generated 22106 * by the same collator, and the resulting ordering is guaranteed to be the 22107 * same as if the original strings were compared by the collator. 22108 * Sort keys generated by different collators are not guaranteed to give 22109 * any reasonable results when compared together unless the two collators 22110 * were constructed with 22111 * exactly the same options and therefore end up representing the exact same 22112 * collation sequence.<p> 22113 * 22114 * A good rule of thumb is that you would use a sort key if you had 10 or more 22115 * items to sort or if your array might be resorted arbitrarily. For example, if your 22116 * user interface was displaying a table with 100 rows in it, and each row had 22117 * 4 sortable text columns which could be sorted in acending or descending order, 22118 * the recommended practice would be to generate a sort key for each of the 4 22119 * sortable fields in each row and store that in the Javascript representation of the 22120 * table data. Then, when the user clicks on a column header to resort the 22121 * table according to that column, the resorting would be relatively quick 22122 * because it would only be comparing arrays of values, and not recalculating 22123 * the collation values for each character in each string for every comparison.<p> 22124 * 22125 * For tables that are large, it is usually a better idea to do the sorting 22126 * on the server side, especially if the table is the result of a database 22127 * query. In this case, the table is usually a view of the cursor of a large 22128 * results set, and only a few entries are sent to the front end at a time. 22129 * In order to sort the set efficiently, it should be done on the database 22130 * level instead. 22131 * 22132 * <h2>Data</h2> 22133 * 22134 * Doing correct collation entails a huge amount of mapping data, much of which is 22135 * not necessary when collating in one language with one script, which is the most 22136 * common case. Thus, ilib implements a number of ways to include the data you 22137 * need or leave out the data you don't need using the JS assembly tool: 22138 * 22139 * <ol> 22140 * <li>Full multilingual data - if you are sorting multilingual data and need to collate 22141 * text written in multiple scripts, you can use the directive "!data collation/ducet" to 22142 * load in the full collation data. This allows the collator to perform the entire 22143 * Unicode Collation Algorithm (UCA) based on the Default Unicode Collation Element 22144 * Table (DUCET). The data is very large, on the order of multiple megabytes, but 22145 * sometimes it is necessary. 22146 * <li>A few scripts - if you are sorting text written in only a few scripts, you may 22147 * want to include only the data for those scripts. Each ISO 15924 script code has its 22148 * own data available in a separate file, so you can use the data directive to include 22149 * only the data for the scripts you need. For example, use 22150 * "!data collation/Latn" to retrieve the collation information for the Latin script. 22151 * Because the "ducet" table mentioned in the previous point is a superset of the 22152 * tables for all other scripts, you do not need to include explicitly the data for 22153 * any particular script when using "ducet". That is, you either include "ducet" or 22154 * you include a specific list of scripts. 22155 * <li>Only one script - if you are sorting text written only in one script, you can 22156 * either include the data directly as in the previous point, or you can rely on the 22157 * locale to include the correct data for you. In this case, you can use the directive 22158 * "!data collate" to load in the locale's collation data for its most common script. 22159 * </ol> 22160 * 22161 * With any of the above ways of including the data, the collator will only perform the 22162 * correct language-sensitive sorting for the given locale. All other scripts will be 22163 * sorted in the default manner according to the UCA. For example, if you include the 22164 * "ducet" data and pass in "de-DE" (German for Germany) as the locale spec, then 22165 * only the Latin script (the default script for German) will be sorted according to 22166 * German rules. All other scripts in the DUCET, such as Japanese or Arabic, will use 22167 * the default UCA collation rules.<p> 22168 * 22169 * If this collator encounters a character for which it has no collation data, it will 22170 * sort those characters by pure Unicode value after all characters for which it does have 22171 * collation data. For example, if you only loaded in the German collation data (ie. the 22172 * data for the Latin script tailored to German) to sort a list of person names, but that 22173 * list happens to include the names of a few Japanese people written in Japanese 22174 * characters, the Japanese names will sort at the end of the list after all German names, 22175 * and will sort according to the Unicode values of the characters. 22176 * 22177 * @constructor 22178 * @param {Object} options options governing how the resulting comparator 22179 * function will operate 22180 */ 22181 ilib.Collator = function(options) { 22182 var sync = true, 22183 loadParams = undefined, 22184 useNative = true; 22185 22186 // defaults 22187 /** 22188 * @private 22189 * @type {ilib.Locale} 22190 */ 22191 this.locale = new ilib.Locale(ilib.getLocale()); 22192 22193 /** @private */ 22194 this.caseFirst = "upper"; 22195 /** @private */ 22196 this.sensitivity = "variant"; 22197 /** @private */ 22198 this.level = 4; 22199 /** @private */ 22200 this.usage = "sort"; 22201 /** @private */ 22202 this.reverse = false; 22203 /** @private */ 22204 this.numeric = false; 22205 /** @private */ 22206 this.style = "standard"; 22207 /** @private */ 22208 this.ignorePunctuation = false; 22209 22210 if (options) { 22211 if (options.locale) { 22212 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 22213 } 22214 if (options.sensitivity) { 22215 switch (options.sensitivity) { 22216 case 'primary': 22217 case 'base': 22218 this.sensitivity = "base"; 22219 this.level = 1; 22220 break; 22221 case 'secondary': 22222 case 'case': 22223 this.sensitivity = "case"; 22224 this.level = 2; 22225 break; 22226 case 'tertiary': 22227 case 'accent': 22228 this.sensitivity = "accent"; 22229 this.level = 3; 22230 break; 22231 case 'quaternary': 22232 case 'variant': 22233 this.sensitivity = "variant"; 22234 this.level = 4; 22235 break; 22236 } 22237 } 22238 if (typeof(options.upperFirst) !== 'undefined') { 22239 this.caseFirst = options.upperFirst ? "upper" : "lower"; 22240 } 22241 22242 if (typeof(options.ignorePunctuation) !== 'undefined') { 22243 this.ignorePunctuation = options.ignorePunctuation; 22244 } 22245 if (typeof(options.sync) !== 'undefined') { 22246 sync = (options.sync == true); 22247 } 22248 22249 loadParams = options.loadParams; 22250 if (typeof(options.useNative) !== 'undefined') { 22251 useNative = options.useNative; 22252 } 22253 22254 if (options.usage === "sort" || options.usage === "search") { 22255 this.usage = options.usage; 22256 } 22257 22258 if (typeof(options.reverse) === 'boolean') { 22259 this.reverse = options.reverse; 22260 } 22261 22262 if (typeof(options.numeric) === 'boolean') { 22263 this.numeric = options.numeric; 22264 } 22265 22266 if (typeof(options.style) === 'string') { 22267 this.style = options.style; 22268 } 22269 } 22270 22271 if (this.usage === "sort") { 22272 // produces a stable sort 22273 this.level = 4; 22274 } 22275 22276 if (useNative && typeof(Intl) !== 'undefined' && Intl) { 22277 // this engine is modern and supports the new Intl object! 22278 //console.log("implemented natively"); 22279 /** 22280 * @private 22281 * @type {{compare:function(string,string)}} 22282 */ 22283 this.collator = new Intl.Collator(this.locale.getSpec(), this); 22284 22285 if (options && typeof(options.onLoad) === 'function') { 22286 options.onLoad(this); 22287 } 22288 } else { 22289 //console.log("implemented in pure JS"); 22290 if (!ilib.Collator.cache) { 22291 ilib.Collator.cache = {}; 22292 } 22293 22294 // else implement in pure Javascript 22295 ilib.loadData({ 22296 object: ilib.Collator, 22297 locale: this.locale, 22298 name: "collation.json", 22299 replace: true, 22300 sync: sync, 22301 loadParams: loadParams, 22302 callback: ilib.bind(this, function (collation) { 22303 if (!collation) { 22304 collation = ilib.data.collation; 22305 var spec = this.locale.getSpec().replace(/-/g, '_'); 22306 ilib.Collator.cache[spec] = collation; 22307 } 22308 this._init(collation); 22309 new ilib.LocaleInfo(this.locale, { 22310 sync: sync, 22311 loadParams: loadParams, 22312 onLoad: ilib.bind(this, function(li) { 22313 this.li = li; 22314 if (this.ignorePunctuation) { 22315 ilib.CType.isPunct._init(sync, loadParams, ilib.bind(this, function() { 22316 if (options && typeof(options.onLoad) === 'function') { 22317 options.onLoad(this); 22318 } 22319 })); 22320 } else { 22321 if (options && typeof(options.onLoad) === 'function') { 22322 options.onLoad(this); 22323 } 22324 } 22325 }) 22326 }); 22327 }) 22328 }); 22329 } 22330 }; 22331 22332 ilib.Collator.prototype = { 22333 /** 22334 * @private 22335 * Bit pack an array of values into a single number 22336 * @param {number|null|Array.<number>} arr array of values to bit pack 22337 */ 22338 _pack: function (arr) { 22339 var value = 0; 22340 if (arr) { 22341 if (typeof(arr) === 'number') { 22342 arr = [ arr ]; 22343 } 22344 for (var i = 0; i < this.level; i++) { 22345 if (i > 0) { 22346 value <<= this.collation.bits[i]; 22347 } 22348 if (i === 2 && this.caseFirst === "lower") { 22349 // sort the lower case first instead of upper 22350 value = value | (1 - (typeof(arr[i]) !== "undefined" ? arr[i] : 0)); 22351 } else { 22352 value = value | arr[i]; 22353 } 22354 } 22355 } 22356 return value; 22357 }, 22358 22359 /** 22360 * @private 22361 * Return the rule packed into an array of collation elements. 22362 * @param {Array.<number|null|Array.<number>>} rule 22363 * @return {Array.<number>} a bit-packed array of numbers 22364 */ 22365 _packRule: function(rule) { 22366 if (rule[0] instanceof Array) { 22367 var ret = []; 22368 for (var i = 0; i < rule.length; i++) { 22369 ret.push(this._pack(rule[i])); 22370 } 22371 return ret; 22372 } else { 22373 return [ this._pack(rule) ]; 22374 } 22375 }, 22376 22377 /** 22378 * @private 22379 */ 22380 _init: function(rules) { 22381 /** 22382 * @private 22383 * @type {{scripts:Array.<string>,bits:Array.<number>,maxes:Array.<number>,bases:Array.<number>,map:Object.<string,Array.<number|null|Array.<number>>>}} 22384 */ 22385 this.collation = rules[this.style]; 22386 this.map = {}; 22387 this.keysize = 0; 22388 for (var i = 0; i < this.level; i++) { 22389 this.keysize += this.collation.bits[i]; 22390 } 22391 var remainder = ilib.mod(this.keysize, 4); 22392 this.keysize += (remainder > 0) ? (4 - remainder) : 0; // round to the nearest 4 to find how many bits to use in hex 22393 22394 for (var r in this.collation.map) { 22395 if (r) { 22396 this.map[r] = this._packRule(this.collation.map[r]); 22397 } 22398 } 22399 }, 22400 22401 /** 22402 * @private 22403 */ 22404 _basicCompare: function(left, right) { 22405 if (this.numeric) { 22406 var lvalue = new ilib.Number(left, {locale: this.locale}); 22407 var rvalue = new ilib.Number(right, {locale: this.locale}); 22408 if (isNaN(lvalue.valueOf())) { 22409 if (isNaN(rvalue.valueOf())) { 22410 return 0; 22411 } 22412 return 1; 22413 } else if (isNaN(rvalue.valueOf())) { 22414 return -1; 22415 } 22416 return lvalue.valueOf() - rvalue.valueOf(); 22417 } else { 22418 var l = (left instanceof ilib.NormString) ? left : new ilib.NormString(left), 22419 r = (right instanceof ilib.NormString) ? right : new ilib.NormString(right), 22420 lelements, 22421 relements; 22422 22423 // if the reverse sort is on, switch the char sources so that the result comes out swapped 22424 lelements = new ilib.ElementIterator(new ilib.CodePointSource(l, this.ignorePunctuation), this.map, this.keysize); 22425 relements = new ilib.ElementIterator(new ilib.CodePointSource(r, this.ignorePunctuation), this.map, this.keysize); 22426 22427 while (lelements.hasNext() && relements.hasNext()) { 22428 var diff = lelements.next() - relements.next(); 22429 if (diff) { 22430 return diff; 22431 } 22432 } 22433 if (!lelements.hasNext() && !relements.hasNext()) { 22434 return 0; 22435 } else if (lelements.hasNext()) { 22436 return 1; 22437 } else { 22438 return -1; 22439 } 22440 } 22441 }, 22442 22443 /** 22444 * Compare two strings together according to the rules of this 22445 * collator instance. Do not use this function directly with 22446 * Array.sort, as it will not have its collation data available 22447 * and therefore will not function properly. Use the function 22448 * returned by getComparator() instead. 22449 * 22450 * @param {string} left the left string to compare 22451 * @param {string} right the right string to compare 22452 * @return {number} a negative number if left comes before right, a 22453 * positive number if right comes before left, and zero if left and 22454 * right are equivalent according to this collator 22455 */ 22456 compare: function (left, right) { 22457 // last resort: use the "C" locale 22458 if (this.collator) { 22459 // implemented by the core engine 22460 return this.collator.compare(left, right); 22461 } 22462 22463 var ret = this._basicCompare(left, right); 22464 return this.reverse ? -ret : ret; 22465 }, 22466 22467 /** 22468 * Return a comparator function that can compare two strings together 22469 * according to the rules of this collator instance. The function 22470 * returns a negative number if the left 22471 * string comes before right, a positive number if the right string comes 22472 * before the left, and zero if left and right are equivalent. If the 22473 * reverse property was given as true to the collator constructor, this 22474 * function will 22475 * switch the sign of those values to cause sorting to happen in the 22476 * reverse order. 22477 * 22478 * @return {function(...)|undefined} a comparator function that 22479 * can compare two strings together according to the rules of this 22480 * collator instance 22481 */ 22482 getComparator: function() { 22483 // bind the function to this instance so that we have the collation 22484 // rules available to do the work 22485 if (this.collator) { 22486 // implemented by the core engine 22487 return this.collator.compare; 22488 } 22489 22490 return /** @type function(string,string):number */ ilib.bind(this, this.compare); 22491 }, 22492 22493 /** 22494 * Return a sort key string for the given string. The sort key 22495 * string is a list of values that represent each character 22496 * in the original string. The sort key 22497 * values for any particular character consists of 3 numbers that 22498 * encode the primary, secondary, and tertiary characteristics 22499 * of that character. The values of each characteristic are 22500 * modified according to the strength of this collator instance 22501 * to give the correct collation order. The idea is that this 22502 * sort key string is directly comparable byte-for-byte to 22503 * other sort key strings generated by this collator without 22504 * any further knowledge of the collation rules for the locale. 22505 * More formally, if a < b according to the rules of this collation, 22506 * then it is guaranteed that sortkey(a) < sortkey(b) when compared 22507 * byte-for-byte. The sort key string can therefore be used 22508 * without the collator to sort an array of strings efficiently 22509 * because the work of determining the applicability of various 22510 * collation rules is done once up-front when generating 22511 * the sort key.<p> 22512 * 22513 * The sort key string can be treated as a regular, albeit somewhat 22514 * odd-looking, string. That is, it can be pass to regular 22515 * Javascript functions without problems. 22516 * 22517 * @param {string} str the original string to generate the sort key for 22518 * @return {string} a sort key string for the given string 22519 */ 22520 sortKey: function (str) { 22521 if (!str) { 22522 return ""; 22523 } 22524 22525 if (this.collator) { 22526 // native, no sort keys available 22527 return str; 22528 } 22529 22530 function pad(str, limit) { 22531 return "0000000000000000".substring(0, limit - str.length) + str; 22532 } 22533 22534 if (this.numeric) { 22535 var v = new ilib.Number(str, {locale: this.locale}); 22536 var s = isNaN(v.valueOf()) ? "" : v.valueOf().toString(16); 22537 return pad(s, 16); 22538 } else { 22539 var n = (typeof(str) === "string") ? new ilib.NormString(str) : str, 22540 ret = "", 22541 lelements = new ilib.ElementIterator(new ilib.CodePointSource(n, this.ignorePunctuation), this.map, this.keysize), 22542 element; 22543 22544 while (lelements.hasNext()) { 22545 element = lelements.next(); 22546 if (this.reverse) { 22547 element = (1 << this.keysize) - element; 22548 } 22549 ret += pad(element.toString(16), this.keysize/4); 22550 } 22551 } 22552 return ret; 22553 } 22554 }; 22555 22556 /** 22557 * Retrieve the list of collation style names that are available for the 22558 * given locale. This list varies depending on the locale, and depending 22559 * on whether or not the data for that locale was assembled into this copy 22560 * of ilib. 22561 * 22562 * @param {ilib.Locale|string=} locale The locale for which the available 22563 * styles are being sought 22564 * @return Array.<string> an array of style names that are available for 22565 * the given locale 22566 */ 22567 ilib.Collator.getAvailableStyles = function (locale) { 22568 return [ "standard" ]; 22569 }; 22570 22571 /** 22572 * Retrieve the list of ISO 15924 script codes that are available in this 22573 * copy of ilib. This list varies depending on whether or not the data for 22574 * various scripts was assembled into this copy of ilib. If the "ducet" 22575 * data is assembled into this copy of ilib, this method will report the 22576 * entire list of scripts as being available. If a collator instance is 22577 * instantiated with a script code that is not on the list returned by this 22578 * function, it will be ignored and text in that script will be sorted by 22579 * numeric Unicode values of the characters. 22580 * 22581 * @return Array.<string> an array of ISO 15924 script codes that are 22582 * available 22583 */ 22584 ilib.Collator.getAvailableScripts = function () { 22585 return [ "Latn" ]; 22586 }; 22587 22588 /* 22589 * all.js - include file for normalization data for a particular script 22590 * 22591 * Copyright © 2012, JEDLSoft 22592 * 22593 * Licensed under the Apache License, Version 2.0 (the "License"); 22594 * you may not use this file except in compliance with the License. 22595 * You may obtain a copy of the License at 22596 * 22597 * http://www.apache.org/licenses/LICENSE-2.0 22598 * 22599 * Unless required by applicable law or agreed to in writing, software 22600 * distributed under the License is distributed on an "AS IS" BASIS, 22601 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22602 * 22603 * See the License for the specific language governing permissions and 22604 * limitations under the License. 22605 */ 22606 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */ 22607 // !depends util/utils.js 22608 // !data norm nfd/all 22609 ilib.data.norm.nfd = ilib.merge(ilib.data.norm.nfd || {}, ilib.data.nfd_all); 22610 ilib.data.nfd_all = undefined; 22611 /* 22612 * all.js - include file for normalization data for a particular script 22613 * 22614 * Copyright © 2012, JEDLSoft 22615 * 22616 * Licensed under the Apache License, Version 2.0 (the "License"); 22617 * you may not use this file except in compliance with the License. 22618 * You may obtain a copy of the License at 22619 * 22620 * http://www.apache.org/licenses/LICENSE-2.0 22621 * 22622 * Unless required by applicable law or agreed to in writing, software 22623 * distributed under the License is distributed on an "AS IS" BASIS, 22624 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22625 * 22626 * See the License for the specific language governing permissions and 22627 * limitations under the License. 22628 */ 22629 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */ 22630 // !depends util/utils.js 22631 // !depends nfd/all.js 22632 // !data norm 22633 22634 /* 22635 * all.js - include file for normalization data for a particular script 22636 * 22637 * Copyright © 2012, JEDLSoft 22638 * 22639 * Licensed under the Apache License, Version 2.0 (the "License"); 22640 * you may not use this file except in compliance with the License. 22641 * You may obtain a copy of the License at 22642 * 22643 * http://www.apache.org/licenses/LICENSE-2.0 22644 * 22645 * Unless required by applicable law or agreed to in writing, software 22646 * distributed under the License is distributed on an "AS IS" BASIS, 22647 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22648 * 22649 * See the License for the specific language governing permissions and 22650 * limitations under the License. 22651 */ 22652 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */ 22653 // !depends util/utils.js 22654 // !depends nfd/all.js 22655 // !data norm nfkd/all 22656 ilib.data.norm.nfkd = ilib.merge(ilib.data.norm.nfkd || {}, ilib.data.nfkd_all); 22657 ilib.data.nfkd_all = undefined; 22658 /* 22659 * all.js - include file for normalization data for a particular script 22660 * 22661 * Copyright © 2012, JEDLSoft 22662 * 22663 * Licensed under the Apache License, Version 2.0 (the "License"); 22664 * you may not use this file except in compliance with the License. 22665 * You may obtain a copy of the License at 22666 * 22667 * http://www.apache.org/licenses/LICENSE-2.0 22668 * 22669 * Unless required by applicable law or agreed to in writing, software 22670 * distributed under the License is distributed on an "AS IS" BASIS, 22671 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22672 * 22673 * See the License for the specific language governing permissions and 22674 * limitations under the License. 22675 */ 22676 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */ 22677 // !depends util/utils.js 22678 // !depends nfd/all.js nfc/all.js nfkd/all.js 22679 // !data norm 22680 22681 /* 22682 * localematch.js - Locale matcher definition 22683 * 22684 * Copyright © 2013-2014, JEDLSoft 22685 * 22686 * Licensed under the Apache License, Version 2.0 (the "License"); 22687 * you may not use this file except in compliance with the License. 22688 * You may obtain a copy of the License at 22689 * 22690 * http://www.apache.org/licenses/LICENSE-2.0 22691 * 22692 * Unless required by applicable law or agreed to in writing, software 22693 * distributed under the License is distributed on an "AS IS" BASIS, 22694 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22695 * 22696 * See the License for the specific language governing permissions and 22697 * limitations under the License. 22698 */ 22699 22700 // !depends ilibglobal.js locale.js 22701 // !data likelylocales 22702 22703 /** 22704 * @class 22705 * Create a new locale matcher instance. This is used 22706 * to see which locales can be matched with each other in 22707 * various ways.<p> 22708 * 22709 * The options object may contain any of the following properties: 22710 * 22711 * <ul> 22712 * <li><i>locale</i> - the locale to match 22713 * 22714 * <li><i>onLoad</i> - a callback function to call when the locale matcher object is fully 22715 * loaded. When the onLoad option is given, the locale matcher object will attempt to 22716 * load any missing locale data using the ilib loader callback. 22717 * When the constructor is done (even if the data is already preassembled), the 22718 * onLoad function is called with the current instance as a parameter, so this 22719 * callback can be used with preassembled or dynamic loading or a mix of the two. 22720 * 22721 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 22722 * asynchronously. If this option is given as "false", then the "onLoad" 22723 * callback must be given, as the instance returned from this constructor will 22724 * not be usable for a while. 22725 * 22726 * <li><i>loadParams</i> - an object containing parameters to pass to the 22727 * loader callback function when locale data is missing. The parameters are not 22728 * interpretted or modified in any way. They are simply passed along. The object 22729 * may contain any property/value pairs as long as the calling code is in 22730 * agreement with the loader callback function as to what those parameters mean. 22731 * </ul> 22732 * 22733 * Depends directive: !depends localematch.js 22734 * 22735 * @constructor 22736 * @param {Object} options parameters to initialize this matcher 22737 */ 22738 ilib.LocaleMatcher = function(options) { 22739 var sync = true, 22740 loadParams = undefined; 22741 22742 this.locale = new ilib.Locale(); 22743 22744 if (options) { 22745 if (typeof(options.locale) !== 'undefined') { 22746 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 22747 } 22748 22749 if (typeof(options.sync) !== 'undefined') { 22750 sync = (options.sync == true); 22751 } 22752 22753 if (typeof(options.loadParams) !== 'undefined') { 22754 loadParams = options.loadParams; 22755 } 22756 } 22757 22758 if (!ilib.LocaleMatcher.cache) { 22759 ilib.LocaleMatcher.cache = {}; 22760 } 22761 22762 if (typeof(ilib.data.likelylocales) === 'undefined') { 22763 ilib.loadData({ 22764 object: ilib.LocaleMatcher, 22765 locale: "-", 22766 name: "likelylocales.json", 22767 sync: sync, 22768 loadParams: loadParams, 22769 callback: ilib.bind(this, function (info) { 22770 if (!info) { 22771 info = {}; 22772 var spec = this.locale.getSpec().replace(/-/g, "_"); 22773 ilib.LocaleMatcher.cache[spec] = info; 22774 } 22775 /** @type {Object.<string,string>} */ 22776 this.info = info; 22777 if (options && typeof(options.onLoad) === 'function') { 22778 options.onLoad(this); 22779 } 22780 }) 22781 }); 22782 } else { 22783 this.info = /** @type {Object.<string,string>} */ ilib.data.likelylocales; 22784 } 22785 }; 22786 22787 22788 ilib.LocaleMatcher.prototype = { 22789 /** 22790 * Return the locale used to construct this instance. 22791 * @return {ilib.Locale|undefined} the locale for this matcher 22792 */ 22793 getLocale: function() { 22794 return this.locale; 22795 }, 22796 22797 /** 22798 * Return an ilib.Locale instance that is fully specified based on partial information 22799 * given to the constructor of this locale matcher instance. For example, if the locale 22800 * spec given to this locale matcher instance is simply "ru" (for the Russian language), 22801 * then it will fill in the missing region and script tags and return a locale with 22802 * the specifier "ru-Cyrl-RU". (ie. Russian language, Cyrillic, Russian Federation). 22803 * Any one or two of the language, script, or region parts may be left unspecified, 22804 * and the other one or two parts will be filled in automatically. If this 22805 * class has no information about the given locale, then the locale of this 22806 * locale matcher instance is returned unchanged. 22807 * 22808 * @returns {ilib.Locale} the most likely completion of the partial locale given 22809 * to the constructor of this locale matcher instance 22810 */ 22811 getLikelyLocale: function () { 22812 if (typeof(this.info[this.locale.getSpec()]) === 'undefined') { 22813 return this.locale; 22814 } 22815 22816 return new ilib.Locale(this.info[this.locale.getSpec()]); 22817 } 22818 }; 22819 22820 22821 /* 22822 * casemapper.js - define upper- and lower-case mapper 22823 * 22824 * Copyright © 2014-2015, JEDLSoft 22825 * 22826 * Licensed under the Apache License, Version 2.0 (the "License"); 22827 * you may not use this file except in compliance with the License. 22828 * You may obtain a copy of the License at 22829 * 22830 * http://www.apache.org/licenses/LICENSE-2.0 22831 * 22832 * Unless required by applicable law or agreed to in writing, software 22833 * distributed under the License is distributed on an "AS IS" BASIS, 22834 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22835 * 22836 * See the License for the specific language governing permissions and 22837 * limitations under the License. 22838 */ 22839 22840 // !depends locale.js util/utils.js 22841 22842 /** 22843 * @class 22844 * Create a new string mapper instance that maps strings to upper or 22845 * lower case. This mapping will work for any string as characters 22846 * that have no case will be returned unchanged.<p> 22847 * 22848 * The options may contain any of the following properties: 22849 * 22850 * <ul> 22851 * <li><i>locale</i> - locale to use when loading the mapper. Some maps are 22852 * locale-dependent, and this locale selects the right one. Default if this is 22853 * not specified is the current locale. 22854 * 22855 * <li><i>direction</i> - "toupper" for upper-casing, or "tolower" for lower-casing. 22856 * Default if not specified is "toupper". 22857 * </ul> 22858 * 22859 * Depends directive: !depends casemapper.js 22860 * 22861 * @constructor 22862 * @param {Object=} options options to initialize this mapper 22863 */ 22864 ilib.CaseMapper = function (options) { 22865 this.up = true; 22866 this.locale = new ilib.Locale(); 22867 22868 if (options) { 22869 if (typeof(options.locale) !== 'undefined') { 22870 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 22871 } 22872 22873 this.up = (!options.direction || options.direction === "toupper"); 22874 } 22875 22876 this.mapData = this.up ? { 22877 "ß": "SS", // German 22878 'ΐ': 'Ι', // Greek 22879 'ά': 'Α', 22880 'έ': 'Ε', 22881 'ή': 'Η', 22882 'ί': 'Ι', 22883 'ΰ': 'Υ', 22884 'ϊ': 'Ι', 22885 'ϋ': 'Υ', 22886 'ό': 'Ο', 22887 'ύ': 'Υ', 22888 'ώ': 'Ω', 22889 'Ӏ': 'Ӏ', // Russian and slavic languages 22890 'ӏ': 'Ӏ' 22891 } : { 22892 'Ӏ': 'Ӏ' // Russian and slavic languages 22893 }; 22894 22895 switch (this.locale.getLanguage()) { 22896 case "az": 22897 case "tr": 22898 case "crh": 22899 case "kk": 22900 case "krc": 22901 case "tt": 22902 var lower = "iı"; 22903 var upper = "İI"; 22904 this._setUpMap(lower, upper); 22905 break; 22906 case "fr": 22907 if (this.up && this.locale.getRegion() !== "CA") { 22908 this._setUpMap("àáâãäçèéêëìíîïñòóôöùúûü", "AAAAACEEEEIIIINOOOOUUUU"); 22909 } 22910 break; 22911 } 22912 22913 if (ilib._getBrowser() === "ie") { 22914 // IE is missing these mappings for some reason 22915 if (this.up) { 22916 this.mapData['ς'] = 'Σ'; 22917 } 22918 this._setUpMap("ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⳁⳉⳋ", "ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⳀⳈⳊ"); // Coptic 22919 // Georgian Nuskhuri <-> Asomtavruli 22920 this._setUpMap("ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥ", "ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ"); 22921 } 22922 }; 22923 22924 ilib.CaseMapper.prototype = { 22925 /** 22926 * @private 22927 */ 22928 _charMapper: function(string) { 22929 if (!string) { 22930 return string; 22931 } 22932 var input = (typeof(string) === 'string') ? new ilib.String(string) : string.toString(); 22933 var ret = ""; 22934 var it = input.charIterator(); 22935 var c; 22936 22937 while (it.hasNext()) { 22938 c = it.next(); 22939 if (!this.up && c === 'Σ') { 22940 if (it.hasNext()) { 22941 c = it.next(); 22942 var code = c.charCodeAt(0); 22943 // if the next char is not a greek letter, this is the end of the word so use the 22944 // final form of sigma. Otherwise, use the mid-word form. 22945 ret += ((code < 0x0388 && code !== 0x0386) || code > 0x03CE) ? 'ς' : 'σ'; 22946 ret += c.toLowerCase(); 22947 } else { 22948 // no next char means this is the end of the word, so use the final form of sigma 22949 ret += 'ς'; 22950 } 22951 } else { 22952 if (this.mapData[c]) { 22953 ret += this.mapData[c]; 22954 } else { 22955 ret += this.up ? c.toUpperCase() : c.toLowerCase(); 22956 } 22957 } 22958 } 22959 22960 return ret; 22961 }, 22962 22963 /** @private */ 22964 _setUpMap: function(lower, upper) { 22965 var from, to; 22966 if (this.up) { 22967 from = lower; 22968 to = upper; 22969 } else { 22970 from = upper; 22971 to = lower; 22972 } 22973 for (var i = 0; i < upper.length; i++) { 22974 this.mapData[from[i]] = to[i]; 22975 } 22976 }, 22977 22978 /** 22979 * Return the locale that this mapper was constructed with. 22980 * @returns {ilib.Locale} the locale that this mapper was constructed with 22981 */ 22982 getLocale: function () { 22983 return this.locale; 22984 }, 22985 22986 /** 22987 * Map a string to lower case in a locale-sensitive manner. 22988 * 22989 * @param {string|undefined} string 22990 * @return {string|undefined} 22991 */ 22992 map: function (string) { 22993 return this._charMapper(string); 22994 } 22995 }; 22996 /* 22997 * numplan.js - Represent a phone numbering plan. 22998 * 22999 * Copyright © 2014, JEDLSoft 23000 * 23001 * Licensed under the Apache License, Version 2.0 (the "License"); 23002 * you may not use this file except in compliance with the License. 23003 * You may obtain a copy of the License at 23004 * 23005 * http://www.apache.org/licenses/LICENSE-2.0 23006 * 23007 * Unless required by applicable law or agreed to in writing, software 23008 * distributed under the License is distributed on an "AS IS" BASIS, 23009 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23010 * 23011 * See the License for the specific language governing permissions and 23012 * limitations under the License. 23013 */ 23014 23015 /* 23016 !depends 23017 ilibglobal.js 23018 locale.js 23019 localeinfo.js 23020 */ 23021 23022 // !data numplan 23023 23024 /** 23025 * @class 23026 * Create a numbering plan information instance for a particular country's plan.<p> 23027 * 23028 * The options may contain any of the following properties: 23029 * 23030 * <ul> 23031 * <li><i>locale</i> - locale for which the numbering plan is sought. This locale 23032 * will be mapped to the actual numbering plan, which may be shared amongst a 23033 * number of countries. 23034 * 23035 * <li>onLoad - a callback function to call when the date format object is fully 23036 * loaded. When the onLoad option is given, the DateFmt object will attempt to 23037 * load any missing locale data using the ilib loader callback. 23038 * When the constructor is done (even if the data is already preassembled), the 23039 * onLoad function is called with the current instance as a parameter, so this 23040 * callback can be used with preassembled or dynamic loading or a mix of the two. 23041 * 23042 * <li>sync - tell whether to load any missing locale data synchronously or 23043 * asynchronously. If this option is given as "false", then the "onLoad" 23044 * callback must be given, as the instance returned from this constructor will 23045 * not be usable for a while. 23046 * 23047 * <li><i>loadParams</i> - an object containing parameters to pass to the 23048 * loader callback function when locale data is missing. The parameters are not 23049 * interpretted or modified in any way. They are simply passed along. The object 23050 * may contain any property/value pairs as long as the calling code is in 23051 * agreement with the loader callback function as to what those parameters mean. 23052 * </ul> 23053 * 23054 * Depends directive: !depends phone/numplan.js 23055 * 23056 * @constructor 23057 * @package 23058 * @param {Object} options options governing the way this plan is loaded 23059 */ 23060 ilib.NumPlan = function (options) { 23061 var sync = true, 23062 loadParams = {}; 23063 23064 this.locale = new ilib.Locale(); 23065 23066 if (options) { 23067 if (options.locale) { 23068 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 23069 } 23070 23071 if (typeof(options.sync) !== 'undefined') { 23072 sync = (options.sync == true); 23073 } 23074 23075 if (options.loadParams) { 23076 loadParams = options.loadParams; 23077 } 23078 } 23079 23080 ilib.loadData({ 23081 name: "numplan.json", 23082 object: ilib.NumPlan, 23083 locale: this.locale, 23084 sync: sync, 23085 loadParams: loadParams, 23086 callback: ilib.bind(this, function (npdata) { 23087 if (!npdata) { 23088 npdata = { 23089 "region": "XX", 23090 "skipTrunk": false, 23091 "trunkCode": "0", 23092 "iddCode": "00", 23093 "dialingPlan": "closed", 23094 "commonFormatChars": " ()-./", 23095 "fieldLengths": { 23096 "areaCode": 0, 23097 "cic": 0, 23098 "mobilePrefix": 0, 23099 "serviceCode": 0 23100 } 23101 }; 23102 } 23103 23104 /** 23105 * @type {{ 23106 * region:string, 23107 * skipTrunk:boolean, 23108 * trunkCode:string, 23109 * iddCode:string, 23110 * dialingPlan:string, 23111 * commonFormatChars:string, 23112 * fieldLengths:Object.<string,number>, 23113 * contextFree:boolean, 23114 * findExtensions:boolean, 23115 * trunkRequired:boolean, 23116 * extendedAreaCodes:boolean 23117 * }} 23118 */ 23119 this.npdata = npdata; 23120 if (options && typeof(options.onLoad) === 'function') { 23121 options.onLoad(this); 23122 } 23123 }) 23124 }); 23125 }; 23126 23127 ilib.NumPlan.prototype = { 23128 /** 23129 * Return the name of this plan. This may be different than the 23130 * name of the region because sometimes multiple countries share 23131 * the same plan. 23132 * @return {string} the name of the plan 23133 */ 23134 getName: function() { 23135 return this.npdata.region; 23136 }, 23137 23138 /** 23139 * Return the trunk code of the current plan as a string. 23140 * @return {string|undefined} the trunk code of the plan or 23141 * undefined if there is no trunk code in this plan 23142 */ 23143 getTrunkCode: function() { 23144 return this.npdata.trunkCode; 23145 }, 23146 23147 /** 23148 * Return the international direct dialing code of this plan. 23149 * @return {string} the IDD code of this plan 23150 */ 23151 getIDDCode: function() { 23152 return this.npdata.iddCode; 23153 }, 23154 23155 /** 23156 * Return the plan style for this plan. The plan style may be 23157 * one of: 23158 * 23159 * <ul> 23160 * <li>"open" - area codes may be left off if the caller is 23161 * dialing to another number within the same area code 23162 * <li>"closed" - the area code must always be specified, even 23163 * if calling another number within the same area code 23164 * </ul> 23165 * 23166 * @return {string} the plan style, "open" or "closed" 23167 */ 23168 getPlanStyle: function() { 23169 return this.npdata.dialingPlan; 23170 }, 23171 /** [Need Comment] 23172 * Return a contextFree 23173 * 23174 * @return {boolean} 23175 */ 23176 getContextFree: function() { 23177 return this.npdata.contextFree; 23178 }, 23179 /** [Need Comment] 23180 * Return a findExtensions 23181 * 23182 * @return {boolean} 23183 */ 23184 getFindExtensions: function() { 23185 return this.npdata.findExtensions; 23186 }, 23187 /** [Need Comment] 23188 * Return a skipTrunk 23189 * 23190 * @return {boolean} 23191 */ 23192 getSkipTrunk: function() { 23193 return this.npdata.skipTrunk; 23194 }, 23195 /** [Need Comment] 23196 * Return a skipTrunk 23197 * 23198 * @return {boolean} 23199 */ 23200 getTrunkRequired: function() { 23201 return this.npdata.trunkRequired; 23202 }, 23203 /** 23204 * Return true if this plan uses extended area codes. 23205 * @return {boolean} true if the plan uses extended area codes 23206 */ 23207 getExtendedAreaCode: function() { 23208 return this.npdata.extendedAreaCodes; 23209 }, 23210 /** 23211 * Return a string containing all of the common format characters 23212 * used to format numbers. 23213 * @return {string} the common format characters fused in this locale 23214 */ 23215 getCommonFormatChars: function() { 23216 return this.npdata.commonFormatChars; 23217 }, 23218 23219 /** 23220 * Return the length of the field with the given name. If the length 23221 * is returned as 0, this means it is variable length. 23222 * 23223 * @param {string} field name of the field for which the length is 23224 * being sought 23225 * @return {number} if positive, this gives the length of the given 23226 * field. If zero, the field is variable length. If negative, the 23227 * field is not known. 23228 */ 23229 getFieldLength: function (field) { 23230 var dataField = this.npdata.fieldLengths; 23231 23232 return dataField[field]; 23233 } 23234 }; 23235 23236 /* 23237 * phoneloc.js - Represent a phone locale object. 23238 * 23239 * Copyright © 2014, JEDLSoft 23240 * 23241 * Licensed under the Apache License, Version 2.0 (the "License"); 23242 * you may not use this file except in compliance with the License. 23243 * You may obtain a copy of the License at 23244 * 23245 * http://www.apache.org/licenses/LICENSE-2.0 23246 * 23247 * Unless required by applicable law or agreed to in writing, software 23248 * distributed under the License is distributed on an "AS IS" BASIS, 23249 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23250 * 23251 * See the License for the specific language governing permissions and 23252 * limitations under the License. 23253 */ 23254 23255 /* 23256 !depends 23257 ilibglobal.js 23258 locale.js 23259 localeinfo.js 23260 */ 23261 23262 // !data phoneloc 23263 23264 /** 23265 * @class 23266 * 23267 * @param {Object} options Options that govern how this phone locale works 23268 * @constructor 23269 * @private 23270 * @extends ilib.Locale 23271 */ 23272 ilib.Locale.PhoneLoc = function(options) { 23273 var region, 23274 mcc, 23275 cc, 23276 sync = true, 23277 loadParams = {}, 23278 locale; 23279 23280 locale = (options && options.locale) || ilib.getLocale(); 23281 23282 this.parent.call(this, locale); 23283 23284 region = this.region; 23285 23286 if (options) { 23287 if (typeof(options.mcc) !== 'undefined') { 23288 mcc = options.mcc; 23289 } 23290 23291 if (typeof(options.countryCode) !== 'undefined') { 23292 cc = options.countryCode; 23293 } 23294 23295 if (typeof(options.sync) !== 'undefined') { 23296 sync = (options.sync == true); 23297 } 23298 23299 if (options.loadParams) { 23300 loadParams = options.loadParams; 23301 } 23302 } 23303 23304 ilib.loadData({ 23305 name: "phoneloc.json", 23306 object: ilib.Locale.PhoneLoc, 23307 nonlocale: true, 23308 sync: sync, 23309 loadParams: loadParams, 23310 callback: ilib.bind(this, function (data) { 23311 /** @type {{mcc2reg:Object.<string,string>,cc2reg:Object.<string,string>,reg2cc:Object.<string,string>,area2reg:Object.<string,string>}} */ 23312 this.mappings = data; 23313 23314 if (typeof(mcc) !== 'undefined') { 23315 region = this.mappings.mcc2reg[mcc]; 23316 } 23317 23318 if (typeof(cc) !== 'undefined') { 23319 region = this.mappings.cc2reg[cc]; 23320 } 23321 23322 if (!region) { 23323 region = "XX"; 23324 } 23325 23326 this.region = this._normPhoneReg(region); 23327 this._genSpec(); 23328 23329 if (options && typeof(options.onLoad) === 'function') { 23330 options.onLoad(this); 23331 } 23332 }) 23333 }); 23334 }; 23335 23336 ilib.Locale.PhoneLoc.prototype = new ilib.Locale(); 23337 ilib.Locale.PhoneLoc.prototype.parent = ilib.Locale; 23338 ilib.Locale.PhoneLoc.prototype.constructor = ilib.Locale.PhoneLoc; 23339 23340 /** 23341 * Map a mobile carrier code to a region code. 23342 * 23343 * @static 23344 * @package 23345 * @param {string|undefined} mcc the MCC to map 23346 * @return {string|undefined} the region code 23347 */ 23348 23349 ilib.Locale.PhoneLoc.prototype._mapMCCtoRegion = function(mcc) { 23350 if (!mcc) { 23351 return undefined; 23352 } 23353 return this.mappings.mcc2reg && this.mappings.mcc2reg[mcc] || "XX"; 23354 }; 23355 23356 /** 23357 * Map a country code to a region code. 23358 * 23359 * @static 23360 * @package 23361 * @param {string|undefined} cc the country code to map 23362 * @return {string|undefined} the region code 23363 */ 23364 ilib.Locale.PhoneLoc.prototype._mapCCtoRegion = function(cc) { 23365 if (!cc) { 23366 return undefined; 23367 } 23368 return this.mappings.cc2reg && this.mappings.cc2reg[cc] || "XX"; 23369 }; 23370 23371 /** 23372 * Map a region code to a country code. 23373 * 23374 * @static 23375 * @package 23376 * @param {string|undefined} region the region code to map 23377 * @return {string|undefined} the country code 23378 */ 23379 ilib.Locale.PhoneLoc.prototype._mapRegiontoCC = function(region) { 23380 if (!region) { 23381 return undefined; 23382 } 23383 return this.mappings.reg2cc && this.mappings.reg2cc[region] || "0"; 23384 }; 23385 23386 /** 23387 * Map a country code to a region code. 23388 * 23389 * @static 23390 * @package 23391 * @param {string|undefined} cc the country code to map 23392 * @param {string|undefined} area the area code within the country code's numbering plan 23393 * @return {string|undefined} the region code 23394 */ 23395 ilib.Locale.PhoneLoc.prototype._mapAreatoRegion = function(cc, area) { 23396 if (!cc) { 23397 return undefined; 23398 } 23399 if (cc in this.mappings.area2reg) { 23400 return this.mappings.area2reg[cc][area] || this.mappings.area2reg[cc]["default"]; 23401 } else { 23402 return this.mappings.cc2reg[cc]; 23403 } 23404 }; 23405 23406 /** 23407 * Return the region that controls the dialing plan in the given 23408 * region. (ie. the "normalized phone region".) 23409 * 23410 * @static 23411 * @package 23412 * @param {string} region the region code to normalize 23413 * @return {string} the normalized region code 23414 */ 23415 ilib.Locale.PhoneLoc.prototype._normPhoneReg = function(region) { 23416 var norm; 23417 23418 // Map all NANP regions to the right region, so that they get parsed using the 23419 // correct state table 23420 switch (region) { 23421 case "US": // usa 23422 case "CA": // canada 23423 case "AG": // antigua and barbuda 23424 case "BS": // bahamas 23425 case "BB": // barbados 23426 case "DM": // dominica 23427 case "DO": // dominican republic 23428 case "GD": // grenada 23429 case "JM": // jamaica 23430 case "KN": // st. kitts and nevis 23431 case "LC": // st. lucia 23432 case "VC": // st. vincent and the grenadines 23433 case "TT": // trinidad and tobago 23434 case "AI": // anguilla 23435 case "BM": // bermuda 23436 case "VG": // british virgin islands 23437 case "KY": // cayman islands 23438 case "MS": // montserrat 23439 case "TC": // turks and caicos 23440 case "AS": // American Samoa 23441 case "VI": // Virgin Islands, U.S. 23442 case "PR": // Puerto Rico 23443 case "MP": // Northern Mariana Islands 23444 case "T:": // East Timor 23445 case "GU": // Guam 23446 norm = "US"; 23447 break; 23448 23449 // these all use the Italian dialling plan 23450 case "IT": // italy 23451 case "SM": // san marino 23452 case "VA": // vatican city 23453 norm = "IT"; 23454 break; 23455 23456 // all the French dependencies are on the French dialling plan 23457 case "FR": // france 23458 case "GF": // french guiana 23459 case "MQ": // martinique 23460 case "GP": // guadeloupe, 23461 case "BL": // saint barthélemy 23462 case "MF": // saint martin 23463 case "RE": // réunion, mayotte 23464 norm = "FR"; 23465 break; 23466 default: 23467 norm = region; 23468 break; 23469 } 23470 return norm; 23471 }; 23472 /* 23473 * handler.js - Handle phone number parse states 23474 * 23475 * Copyright © 2014, JEDLSoft 23476 * 23477 * Licensed under the Apache License, Version 2.0 (the "License"); 23478 * you may not use this file except in compliance with the License. 23479 * You may obtain a copy of the License at 23480 * 23481 * http://www.apache.org/licenses/LICENSE-2.0 23482 * 23483 * Unless required by applicable law or agreed to in writing, software 23484 * distributed under the License is distributed on an "AS IS" BASIS, 23485 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23486 * 23487 * See the License for the specific language governing permissions and 23488 * limitations under the License. 23489 */ 23490 23491 /* 23492 !depends 23493 ilibglobal.js 23494 phone/phoneloc.js 23495 */ 23496 23497 /** 23498 * @class 23499 * [Need Comments] globals console ilib PhoneLoc 23500 * 23501 * @private 23502 * @constructor 23503 */ 23504 ilib.StateHandler = function _StateHandler () { 23505 return this; 23506 }; 23507 23508 ilib.StateHandler.prototype = { 23509 /** 23510 * @private 23511 * @param {string} number phone number 23512 * @param {Object} fields the fields that have been extracted so far 23513 * @param {Object} regionSettings settings used to parse the rest of the number 23514 */ 23515 processSubscriberNumber: function(number, fields, regionSettings) { 23516 var last; 23517 23518 last = number.search(/[xwtp,;]/i); // last digit of the local number 23519 23520 if (last > -1) { 23521 if (last > 0) { 23522 fields.subscriberNumber = number.substring(0, last); 23523 } 23524 // strip x's which are there to indicate a break between the local subscriber number and the extension, but 23525 // are not themselves a dialable character 23526 fields.extension = number.substring(last).replace('x', ''); 23527 } else { 23528 if (number.length) { 23529 fields.subscriberNumber = number; 23530 } 23531 } 23532 23533 if (regionSettings.plan.getFieldLength('maxLocalLength') && 23534 fields.subscriberNumber && 23535 fields.subscriberNumber.length > regionSettings.plan.getFieldLength('maxLocalLength')) { 23536 fields.invalid = true; 23537 } 23538 }, 23539 /** 23540 * @private 23541 * @param {string} fieldName 23542 * @param {number} length length of phone number 23543 * @param {string} number phone number 23544 * @param {number} currentChar currentChar to be parsed 23545 * @param {Object} fields the fields that have been extracted so far 23546 * @param {Object} regionSettings settings used to parse the rest of the number 23547 * @param {boolean} noExtractTrunk 23548 */ 23549 processFieldWithSubscriberNumber: function(fieldName, length, number, currentChar, fields, regionSettings, noExtractTrunk) { 23550 var ret, end; 23551 23552 if (length !== undefined && length > 0) { 23553 // fixed length 23554 end = length; 23555 if (regionSettings.plan.getTrunkCode() === "0" && number.charAt(0) === "0") { 23556 end += regionSettings.plan.getTrunkCode().length; // also extract the trunk access code 23557 } 23558 } else { 23559 // variable length 23560 // the setting is the negative of the length to add, so subtract to make it positive 23561 end = currentChar + 1 - length; 23562 } 23563 23564 if (fields[fieldName] !== undefined) { 23565 // we have a spurious recognition, because this number already contains that field! So, just put 23566 // everything into the subscriberNumber as the default 23567 this.processSubscriberNumber(number, fields, regionSettings); 23568 } else { 23569 fields[fieldName] = number.substring(0, end); 23570 if (number.length > end) { 23571 this.processSubscriberNumber(number.substring(end), fields, regionSettings); 23572 } 23573 } 23574 23575 ret = { 23576 number: "" 23577 }; 23578 23579 return ret; 23580 }, 23581 /** 23582 * @private 23583 * @param {string} fieldName 23584 * @param {number} length length of phone number 23585 * @param {string} number phone number 23586 * @param {number} currentChar currentChar to be parsed 23587 * @param {Object} fields the fields that have been extracted so far 23588 * @param {Object} regionSettings settings used to parse the rest of the number 23589 */ 23590 processField: function(fieldName, length, number, currentChar, fields, regionSettings) { 23591 var ret = {}, end; 23592 23593 if (length !== undefined && length > 0) { 23594 // fixed length 23595 end = length; 23596 if (regionSettings.plan.getTrunkCode() === "0" && number.charAt(0) === "0") { 23597 end += regionSettings.plan.getTrunkCode().length; // also extract the trunk access code 23598 } 23599 } else { 23600 // variable length 23601 // the setting is the negative of the length to add, so subtract to make it positive 23602 end = currentChar + 1 - length; 23603 } 23604 23605 if (fields[fieldName] !== undefined) { 23606 // we have a spurious recognition, because this number already contains that field! So, just put 23607 // everything into the subscriberNumber as the default 23608 this.processSubscriberNumber(number, fields, regionSettings); 23609 ret.number = ""; 23610 } else { 23611 fields[fieldName] = number.substring(0, end); 23612 ret.number = (number.length > end) ? number.substring(end) : ""; 23613 } 23614 23615 return ret; 23616 }, 23617 /** 23618 * @private 23619 * @param {string} number phone number 23620 * @param {number} currentChar currentChar to be parsed 23621 * @param {Object} fields the fields that have been extracted so far 23622 * @param {Object} regionSettings settings used to parse the rest of the number 23623 */ 23624 trunk: function(number, currentChar, fields, regionSettings) { 23625 var ret, trunkLength; 23626 23627 if (fields.trunkAccess !== undefined) { 23628 // What? We already have one? Okay, put the rest of this in the subscriber number as the default behaviour then. 23629 this.processSubscriberNumber(number, fields, regionSettings); 23630 number = ""; 23631 } else { 23632 trunkLength = regionSettings.plan.getTrunkCode().length; 23633 fields.trunkAccess = number.substring(0, trunkLength); 23634 number = (number.length > trunkLength) ? number.substring(trunkLength) : ""; 23635 } 23636 23637 ret = { 23638 number: number 23639 }; 23640 23641 return ret; 23642 }, 23643 /** 23644 * @private 23645 * @param {string} number phone number 23646 * @param {number} currentChar currentChar to be parsed 23647 * @param {Object} fields the fields that have been extracted so far 23648 * @param {Object} regionSettings settings used to parse the rest of the number 23649 */ 23650 plus: function(number, currentChar, fields, regionSettings) { 23651 var ret = {}; 23652 23653 if (fields.iddPrefix !== undefined) { 23654 // What? We already have one? Okay, put the rest of this in the subscriber number as the default behaviour then. 23655 this.processSubscriberNumber(number, fields, regionSettings); 23656 ret.number = ""; 23657 } else { 23658 // found the idd prefix, so save it and cause the function to parse the next part 23659 // of the number with the idd table 23660 fields.iddPrefix = number.substring(0, 1); 23661 23662 ret = { 23663 number: number.substring(1), 23664 table: 'idd' // shared subtable that parses the country code 23665 }; 23666 } 23667 return ret; 23668 }, 23669 /** 23670 * @private 23671 * @param {string} number phone number 23672 * @param {number} currentChar currentChar to be parsed 23673 * @param {Object} fields the fields that have been extracted so far 23674 * @param {Object} regionSettings settings used to parse the rest of the number 23675 */ 23676 idd: function(number, currentChar, fields, regionSettings) { 23677 var ret = {}; 23678 23679 if (fields.iddPrefix !== undefined) { 23680 // What? We already have one? Okay, put the rest of this in the subscriber number as the default behaviour then. 23681 this.processSubscriberNumber(number, fields, regionSettings); 23682 ret.number = ""; 23683 } else { 23684 // found the idd prefix, so save it and cause the function to parse the next part 23685 // of the number with the idd table 23686 fields.iddPrefix = number.substring(0, currentChar+1); 23687 23688 ret = { 23689 number: number.substring(currentChar+1), 23690 table: 'idd' // shared subtable that parses the country code 23691 }; 23692 } 23693 23694 return ret; 23695 }, 23696 /** 23697 * @private 23698 * @param {string} number phone number 23699 * @param {number} currentChar currentChar to be parsed 23700 * @param {Object} fields the fields that have been extracted so far 23701 * @param {Object} regionSettings settings used to parse the rest of the number 23702 */ 23703 country: function(number, currentChar, fields, regionSettings) { 23704 var ret, cc; 23705 23706 // found the country code of an IDD number, so save it and cause the function to 23707 // parse the rest of the number with the regular table for this locale 23708 fields.countryCode = number.substring(0, currentChar+1); 23709 cc = fields.countryCode.replace(/[wWpPtT\+#\*]/g, ''); // fix for NOV-108200 23710 // console.log("Found country code " + fields.countryCode + ". Switching to country " + locale.region + " to parse the rest of the number"); 23711 23712 ret = { 23713 number: number.substring(currentChar+1), 23714 countryCode: cc 23715 }; 23716 23717 return ret; 23718 }, 23719 /** 23720 * @private 23721 * @param {string} number phone number 23722 * @param {number} currentChar currentChar to be parsed 23723 * @param {Object} fields the fields that have been extracted so far 23724 * @param {Object} regionSettings settings used to parse the rest of the number 23725 */ 23726 cic: function(number, currentChar, fields, regionSettings) { 23727 return this.processField('cic', regionSettings.plan.getFieldLength('cic'), number, currentChar, fields, regionSettings); 23728 }, 23729 /** 23730 * @private 23731 * @param {string} number phone number 23732 * @param {number} currentChar currentChar to be parsed 23733 * @param {Object} fields the fields that have been extracted so far 23734 * @param {Object} regionSettings settings used to parse the rest of the number 23735 */ 23736 service: function(number, currentChar, fields, regionSettings) { 23737 return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('serviceCode'), number, currentChar, fields, regionSettings, false); 23738 }, 23739 /** 23740 * @private 23741 * @param {string} number phone number 23742 * @param {number} currentChar currentChar to be parsed 23743 * @param {Object} fields the fields that have been extracted so far 23744 * @param {Object} regionSettings settings used to parse the rest of the number 23745 */ 23746 area: function(number, currentChar, fields, regionSettings) { 23747 var ret, last, end, localLength; 23748 23749 last = number.search(/[xwtp]/i); // last digit of the local number 23750 localLength = (last > -1) ? last : number.length; 23751 23752 if (regionSettings.plan.getFieldLength('areaCode') > 0) { 23753 // fixed length 23754 end = regionSettings.plan.getFieldLength('areaCode'); 23755 if (regionSettings.plan.getTrunkCode() === number.charAt(0)) { 23756 end += regionSettings.plan.getTrunkCode().length; // also extract the trunk access code 23757 localLength -= regionSettings.plan.getTrunkCode().length; 23758 } 23759 } else { 23760 // variable length 23761 // the setting is the negative of the length to add, so subtract to make it positive 23762 end = currentChar + 1 - regionSettings.plan.getFieldLength('areaCode'); 23763 } 23764 23765 // substring() extracts the part of the string up to but not including the end character, 23766 // so add one to compensate 23767 if (regionSettings.plan.getFieldLength('maxLocalLength') !== undefined) { 23768 if (fields.trunkAccess !== undefined || fields.mobilePrefix !== undefined || 23769 fields.countryCode !== undefined || 23770 localLength > regionSettings.plan.getFieldLength('maxLocalLength')) { 23771 // too long for a local number by itself, or a different final state already parsed out the trunk 23772 // or mobile prefix, then consider the rest of this number to be an area code + part of the subscriber number 23773 fields.areaCode = number.substring(0, end); 23774 if (number.length > end) { 23775 this.processSubscriberNumber(number.substring(end), fields, regionSettings); 23776 } 23777 } else { 23778 // shorter than the length needed for a local number, so just consider it a local number 23779 this.processSubscriberNumber(number, fields, regionSettings); 23780 } 23781 } else { 23782 fields.areaCode = number.substring(0, end); 23783 if (number.length > end) { 23784 this.processSubscriberNumber(number.substring(end), fields, regionSettings); 23785 } 23786 } 23787 23788 // extensions are separated from the number by a dash in Germany 23789 if (regionSettings.plan.getFindExtensions() !== undefined && fields.subscriberNumber !== undefined) { 23790 var dash = fields.subscriberNumber.indexOf("-"); 23791 if (dash > -1) { 23792 fields.subscriberNumber = fields.subscriberNumber.substring(0, dash); 23793 fields.extension = fields.subscriberNumber.substring(dash+1); 23794 } 23795 } 23796 23797 ret = { 23798 number: "" 23799 }; 23800 23801 return ret; 23802 }, 23803 /** 23804 * @private 23805 * @param {string} number phone number 23806 * @param {number} currentChar currentChar to be parsed 23807 * @param {Object} fields the fields that have been extracted so far 23808 * @param {Object} regionSettings settings used to parse the rest of the number 23809 */ 23810 none: function(number, currentChar, fields, regionSettings) { 23811 var ret; 23812 23813 // this is a last resort function that is called when nothing is recognized. 23814 // When this happens, just put the whole stripped number into the subscriber number 23815 23816 if (number.length > 0) { 23817 this.processSubscriberNumber(number, fields, regionSettings); 23818 if (currentChar > 0 && currentChar < number.length) { 23819 // if we were part-way through parsing, and we hit an invalid digit, 23820 // indicate that the number could not be parsed properly 23821 fields.invalid = true; 23822 } 23823 } 23824 23825 ret = { 23826 number:"" 23827 }; 23828 23829 return ret; 23830 }, 23831 /** 23832 * @private 23833 * @param {string} number phone number 23834 * @param {number} currentChar currentChar to be parsed 23835 * @param {Object} fields the fields that have been extracted so far 23836 * @param {Object} regionSettings settings used to parse the rest of the number 23837 */ 23838 vsc: function(number, currentChar, fields, regionSettings) { 23839 var ret, length, end; 23840 23841 if (fields.vsc === undefined) { 23842 length = regionSettings.plan.getFieldLength('vsc') || 0; 23843 if (length !== undefined && length > 0) { 23844 // fixed length 23845 end = length; 23846 } else { 23847 // variable length 23848 // the setting is the negative of the length to add, so subtract to make it positive 23849 end = currentChar + 1 - length; 23850 } 23851 23852 // found a VSC code (ie. a "star code"), so save it and cause the function to 23853 // parse the rest of the number with the same table for this locale 23854 fields.vsc = number.substring(0, end); 23855 number = (number.length > end) ? "^" + number.substring(end) : ""; 23856 } else { 23857 // got it twice??? Okay, this is a bogus number then. Just put everything else into the subscriber number as the default 23858 this.processSubscriberNumber(number, fields, regionSettings); 23859 number = ""; 23860 } 23861 23862 // treat the rest of the number as if it were a completely new number 23863 ret = { 23864 number: number 23865 }; 23866 23867 return ret; 23868 }, 23869 /** 23870 * @private 23871 * @param {string} number phone number 23872 * @param {number} currentChar currentChar to be parsed 23873 * @param {Object} fields the fields that have been extracted so far 23874 * @param {Object} regionSettings settings used to parse the rest of the number 23875 */ 23876 cell: function(number, currentChar, fields, regionSettings) { 23877 return this.processFieldWithSubscriberNumber('mobilePrefix', regionSettings.plan.getFieldLength('mobilePrefix'), number, currentChar, fields, regionSettings, false); 23878 }, 23879 /** 23880 * @private 23881 * @param {string} number phone number 23882 * @param {number} currentChar currentChar to be parsed 23883 * @param {Object} fields the fields that have been extracted so far 23884 * @param {Object} regionSettings settings used to parse the rest of the number 23885 */ 23886 personal: function(number, currentChar, fields, regionSettings) { 23887 return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('personal'), number, currentChar, fields, regionSettings, false); 23888 }, 23889 /** 23890 * @private 23891 * @param {string} number phone number 23892 * @param {number} currentChar currentChar to be parsed 23893 * @param {Object} fields the fields that have been extracted so far 23894 * @param {Object} regionSettings settings used to parse the rest of the number 23895 */ 23896 emergency: function(number, currentChar, fields, regionSettings) { 23897 return this.processFieldWithSubscriberNumber('emergency', regionSettings.plan.getFieldLength('emergency'), number, currentChar, fields, regionSettings, true); 23898 }, 23899 /** 23900 * @private 23901 * @param {string} number phone number 23902 * @param {number} currentChar currentChar to be parsed 23903 * @param {Object} fields the fields that have been extracted so far 23904 * @param {Object} regionSettings settings used to parse the rest of the number 23905 */ 23906 premium: function(number, currentChar, fields, regionSettings) { 23907 return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('premium'), number, currentChar, fields, regionSettings, false); 23908 }, 23909 /** 23910 * @private 23911 * @param {string} number phone number 23912 * @param {number} currentChar currentChar to be parsed 23913 * @param {Object} fields the fields that have been extracted so far 23914 * @param {Object} regionSettings settings used to parse the rest of the number 23915 */ 23916 special: function(number, currentChar, fields, regionSettings) { 23917 return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('special'), number, currentChar, fields, regionSettings, false); 23918 }, 23919 /** 23920 * @private 23921 * @param {string} number phone number 23922 * @param {number} currentChar currentChar to be parsed 23923 * @param {Object} fields the fields that have been extracted so far 23924 * @param {Object} regionSettings settings used to parse the rest of the number 23925 */ 23926 service2: function(number, currentChar, fields, regionSettings) { 23927 return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('service2'), number, currentChar, fields, regionSettings, false); 23928 }, 23929 /** 23930 * @private 23931 * @param {string} number phone number 23932 * @param {number} currentChar currentChar to be parsed 23933 * @param {Object} fields the fields that have been extracted so far 23934 * @param {Object} regionSettings settings used to parse the rest of the number 23935 */ 23936 service3: function(number, currentChar, fields, regionSettings) { 23937 return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('service3'), number, currentChar, fields, regionSettings, false); 23938 }, 23939 /** 23940 * @private 23941 * @param {string} number phone number 23942 * @param {number} currentChar currentChar to be parsed 23943 * @param {Object} fields the fields that have been extracted so far 23944 * @param {Object} regionSettings settings used to parse the rest of the number 23945 */ 23946 service4: function(number, currentChar, fields, regionSettings) { 23947 return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('service4'), number, currentChar, fields, regionSettings, false); 23948 }, 23949 /** 23950 * @private 23951 * @param {string} number phone number 23952 * @param {number} currentChar currentChar to be parsed 23953 * @param {Object} fields the fields that have been extracted so far 23954 * @param {Object} regionSettings settings used to parse the rest of the number 23955 */ 23956 cic2: function(number, currentChar, fields, regionSettings) { 23957 return this.processField('cic', regionSettings.plan.getFieldLength('cic2'), number, currentChar, fields, regionSettings); 23958 }, 23959 /** 23960 * @private 23961 * @param {string} number phone number 23962 * @param {number} currentChar currentChar to be parsed 23963 * @param {Object} fields the fields that have been extracted so far 23964 * @param {Object} regionSettings settings used to parse the rest of the number 23965 */ 23966 cic3: function(number, currentChar, fields, regionSettings) { 23967 return this.processField('cic', regionSettings.plan.getFieldLength('cic3'), number, currentChar, fields, regionSettings); 23968 }, 23969 /** 23970 * @private 23971 * @param {string} number phone number 23972 * @param {number} currentChar currentChar to be parsed 23973 * @param {Object} fields the fields that have been extracted so far 23974 * @param {Object} regionSettings settings used to parse the rest of the number 23975 */ 23976 start: function(number, currentChar, fields, regionSettings) { 23977 // don't do anything except transition to the next state 23978 return { 23979 number: number 23980 }; 23981 }, 23982 /** 23983 * @private 23984 * @param {string} number phone number 23985 * @param {number} currentChar currentChar to be parsed 23986 * @param {Object} fields the fields that have been extracted so far 23987 * @param {Object} regionSettings settings used to parse the rest of the number 23988 */ 23989 local: function(number, currentChar, fields, regionSettings) { 23990 // in open dialling plans, we can tell that this number is a local subscriber number because it 23991 // starts with a digit that indicates as such 23992 this.processSubscriberNumber(number, fields, regionSettings); 23993 return { 23994 number: "" 23995 }; 23996 } 23997 }; 23998 23999 // context-sensitive handler 24000 /** 24001 * @class 24002 * @private 24003 * @constructor 24004 */ 24005 ilib.CSStateHandler = function () { 24006 return this; 24007 }; 24008 24009 ilib.CSStateHandler.prototype = new ilib.StateHandler(); 24010 ilib.CSStateHandler.prototype.special = function (number, currentChar, fields, regionSettings) { 24011 var ret; 24012 24013 // found a special area code that is both a node and a leaf. In 24014 // this state, we have found the leaf, so chop off the end 24015 // character to make it a leaf. 24016 if (number.charAt(0) === "0") { 24017 fields.trunkAccess = number.charAt(0); 24018 fields.areaCode = number.substring(1, currentChar); 24019 } else { 24020 fields.areaCode = number.substring(0, currentChar); 24021 } 24022 this.processSubscriberNumber(number.substring(currentChar), fields, regionSettings); 24023 24024 ret = { 24025 number: "" 24026 }; 24027 24028 return ret; 24029 }; 24030 24031 /** 24032 * @class 24033 * @private 24034 * @constructor 24035 */ 24036 ilib.USStateHandler = function () { 24037 return this; 24038 }; 24039 24040 ilib.USStateHandler.prototype = new ilib.StateHandler(); 24041 ilib.USStateHandler.prototype.vsc = function (number, currentChar, fields, regionSettings) { 24042 var ret; 24043 24044 // found a VSC code (ie. a "star code") 24045 fields.vsc = number; 24046 24047 // treat the rest of the number as if it were a completely new number 24048 ret = { 24049 number: "" 24050 }; 24051 24052 return ret; 24053 }; 24054 24055 /** 24056 * @protected 24057 * @static 24058 */ 24059 ilib._handlerFactory = function (locale, plan) { 24060 if (plan.getContextFree() !== undefined && typeof(plan.getContextFree()) === 'boolean' && plan.getContextFree() === false) { 24061 return new ilib.CSStateHandler(); 24062 } 24063 var region = (locale && locale.getRegion()) || "ZZ"; 24064 switch (region) { 24065 case 'US': 24066 return new ilib.USStateHandler(); 24067 break; 24068 default: 24069 return new ilib.StateHandler(); 24070 } 24071 }; 24072 /* 24073 * phonenum.js - Represent a phone number. 24074 * 24075 * Copyright © 2014, JEDLSoft 24076 * 24077 * Licensed under the Apache License, Version 2.0 (the "License"); 24078 * you may not use this file except in compliance with the License. 24079 * You may obtain a copy of the License at 24080 * 24081 * http://www.apache.org/licenses/LICENSE-2.0 24082 * 24083 * Unless required by applicable law or agreed to in writing, software 24084 * distributed under the License is distributed on an "AS IS" BASIS, 24085 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24086 * 24087 * See the License for the specific language governing permissions and 24088 * limitations under the License. 24089 */ 24090 24091 /* 24092 !depends 24093 ilibglobal.js 24094 locale.js 24095 localeinfo.js 24096 phone/numplan.js 24097 phone/phoneloc.js 24098 phone/handler.js 24099 */ 24100 24101 // !data states idd mnc 24102 24103 /** 24104 * @class 24105 * Create a new phone number instance that parses the phone number parameter for its 24106 * constituent parts, and store them as separate fields in the returned object. 24107 * 24108 * The options object may include any of these properties: 24109 * 24110 * <ul> 24111 * <li><i>locale</i> The locale with which to parse the number. This gives a clue as to which 24112 * numbering plan to use. 24113 * <li><i>mcc</i> The mobile carrier code (MCC) associated with the carrier that the phone is 24114 * currently connected to, if known. This also can give a clue as to which numbering plan to 24115 * use 24116 * <li>onLoad - a callback function to call when this instance is fully 24117 * loaded. When the onLoad option is given, this class will attempt to 24118 * load any missing locale data using the ilib loader callback. 24119 * When the constructor is done (even if the data is already preassembled), the 24120 * onLoad function is called with the current instance as a parameter, so this 24121 * callback can be used with preassembled or dynamic loading or a mix of the two. 24122 * <li>sync - tell whether to load any missing locale data synchronously or 24123 * asynchronously. If this option is given as "false", then the "onLoad" 24124 * callback must be given, as the instance returned from this constructor will 24125 * not be usable for a while. 24126 * <li><i>loadParams</i> - an object containing parameters to pass to the 24127 * loader callback function when locale data is missing. The parameters are not 24128 * interpretted or modified in any way. They are simply passed along. The object 24129 * may contain any property/value pairs as long as the calling code is in 24130 * agreement with the loader callback function as to what those parameters mean. 24131 * </ul> 24132 * 24133 * This function is locale-sensitive, and will assume any number passed to it is 24134 * appropriate for the given locale. If the MCC is given, this method will assume 24135 * that numbers without an explicit country code have been dialled within the country 24136 * given by the MCC. This affects how things like area codes are parsed. If the MCC 24137 * is not given, this method will use the given locale to determine the country 24138 * code. If the locale is not explicitly given either, then this function uses the 24139 * region of current locale as the default.<p> 24140 * 24141 * The input number may contain any formatting characters for the given locale. Each 24142 * field that is returned in the json object is a simple string of digits with 24143 * all formatting and whitespace characters removed.<p> 24144 * 24145 * The number is decomposed into its parts, regardless if the number 24146 * contains formatting characters. If a particular part cannot be extracted from given 24147 * number, the field will not be returned as a field in the object. If no fields can be 24148 * extracted from the number at all, then all digits found in the string will be 24149 * returned in the subscriberNumber field. If the number parameter contains no 24150 * digits, an empty object is returned.<p> 24151 * 24152 * This instance can contain any of the following fields after parsing is done: 24153 * 24154 * <ul> 24155 * <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 24156 * <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 24157 * <li>countryCode - if this number is an international direct dial number, this is the country code 24158 * <li>cic - for "dial-around" services (access to other carriers), this is the prefix used as the carrier identification code 24159 * <li>emergency - an emergency services number 24160 * <li>mobilePrefix - prefix that introduces a mobile phone number 24161 * <li>trunkAccess - trunk access code (long-distance access) 24162 * <li>serviceCode - like a geographic area code, but it is a required prefix for various services 24163 * <li>areaCode - geographic area codes 24164 * <li>subscriberNumber - the unique number of the person or company that pays for this phone line 24165 * <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. 24166 * <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 24167 * </ul> 24168 * 24169 * The following rules determine how the number is parsed: 24170 * 24171 * <ol> 24172 * <li>If the number starts with a character that is alphabetic instead of numeric, do 24173 * not parse the number at all. There is a good chance that it is not really a phone number. 24174 * In this case, an empty instance will be returned. 24175 * <li>If the phone number uses the plus notation or explicitly uses the international direct 24176 * dialing prefix for the given locale, then the country code is identified in 24177 * the number. The rules of given locale are used to parse the IDD prefix, and then the rules 24178 * of the country in the prefix are used to parse the rest of the number. 24179 * <li>If a country code is provided as an argument to the function call, use that country's 24180 * parsing rules for the number. This is intended for programs like a Contacts application that 24181 * know what the country is of the person that owns the phone number and can pass that on as 24182 * a hint. 24183 * <li>If the appropriate locale cannot be easily determined, default to using the rules 24184 * for the current user's region. 24185 * </ol> 24186 * 24187 * Example: parsing the number "+49 02101345345-78" will give the following properties in the 24188 * resulting phone number instance: 24189 * 24190 * <pre> 24191 * { 24192 * iddPrefix: "+", 24193 * countryCode: "49", 24194 * areaCode: "02101", 24195 * subscriberNumber: "345345", 24196 * extension: "78" 24197 * } 24198 * </pre> 24199 * 24200 * Note that in this example, because international direct dialing is explicitly used 24201 * in the number, the part of this number after the IDD prefix and country code will be 24202 * parsed exactly the same way in all locales with German rules (country code 49). 24203 * 24204 * Regions currently supported are: 24205 * 24206 * <ul> 24207 * <li>NANP (North American Numbering Plan) countries - USA, Canada, Bermuda, various Caribbean nations 24208 * <li>UK 24209 * <li>Republic of Ireland 24210 * <li>Germany 24211 * <li>France 24212 * <li>Spain 24213 * <li>Italy 24214 * <li>Mexico 24215 * <li>India 24216 * <li>People's Republic of China 24217 * <li>Netherlands 24218 * <li>Belgium 24219 * <li>Luxembourg 24220 * <li>Australia 24221 * <li>New Zealand 24222 * <li>Singapore 24223 * <li>Korea 24224 * <li>Japan 24225 * <li>Russia 24226 * <li>Brazil 24227 * </ul> 24228 * 24229 * @constructor 24230 * @param {!string|ilib.PhoneNumber} number A free-form phone number to be parsed, or another phone 24231 * number instance to copy 24232 * @param {Object=} options options that guide the parser in parsing the number 24233 */ 24234 ilib.PhoneNumber = function(number, options) { 24235 var stateData, 24236 regionSettings; 24237 24238 this.sync = true; 24239 this.loadParams = {}; 24240 24241 if (!number || (typeof number === "string" && number.length === 0)) { 24242 return this; 24243 } 24244 24245 if (options) { 24246 if (typeof(options.sync) === 'boolean') { 24247 this.sync = options.sync; 24248 } 24249 24250 if (options.loadParams) { 24251 this.loadParams = options.loadParams; 24252 } 24253 24254 if (typeof(options.onLoad) === 'function') { 24255 /** 24256 * @private 24257 * @type {function(ilib.PhoneNumber)} 24258 */ 24259 this.onLoad = options.onLoad; 24260 } 24261 } 24262 24263 if (typeof number === "object") { 24264 /** 24265 * The vertical service code. These are codes that typically 24266 * start with a star or hash, like "*69" for "dial back the 24267 * last number that called me". 24268 * @type {string|undefined} 24269 */ 24270 this.vsc = number.vsc; 24271 24272 /** 24273 * The international direct dialing prefix. This is always 24274 * followed by the country code. 24275 * @type {string} 24276 */ 24277 this.iddPrefix = number.iddPrefix; 24278 24279 /** 24280 * The unique IDD country code for the country where the 24281 * phone number is serviced. 24282 * @type {string|undefined} 24283 */ 24284 this.countryCode = number.countryCode; 24285 24286 /** 24287 * The digits required to access the trunk. 24288 * @type {string|undefined} 24289 */ 24290 this.trunkAccess = number.trunkAccess; 24291 24292 /** 24293 * The carrier identification code used to identify 24294 * alternate long distance or international carriers. 24295 * @type {string|undefined} 24296 */ 24297 this.cic = number.cic; 24298 24299 /** 24300 * Identifies an emergency number that is typically 24301 * short, such as "911" in North America or "112" in 24302 * many other places in the world. 24303 * @type {string|undefined} 24304 */ 24305 this.emergency = number.emergency; 24306 24307 /** 24308 * The prefix of the subscriber number that indicates 24309 * that this is the number of a mobile phone. 24310 * @type {string|undefined} 24311 */ 24312 this.mobilePrefix = number.mobilePrefix; 24313 24314 /** 24315 * The prefix that identifies this number as commercial 24316 * service number. 24317 * @type {string|undefined} 24318 */ 24319 this.serviceCode = number.serviceCode; 24320 24321 /** 24322 * The area code prefix of a land line number. 24323 * @type {string|undefined} 24324 */ 24325 this.areaCode = number.areaCode; 24326 24327 /** 24328 * The unique number associated with the subscriber 24329 * of this phone. 24330 * @type {string|undefined} 24331 */ 24332 this.subscriberNumber = number.subscriberNumber; 24333 24334 /** 24335 * The direct dial extension number. 24336 * @type {string|undefined} 24337 */ 24338 this.extension = number.extension; 24339 24340 /** 24341 * @private 24342 * @type {boolean} 24343 */ 24344 this.invalid = number.invalid; 24345 24346 if (number.plan && number.locale) { 24347 /** 24348 * @private 24349 * @type {ilib.NumPlan} 24350 */ 24351 this.plan = number.plan; 24352 24353 /** 24354 * @private 24355 * @type {ilib.Locale.PhoneLoc} 24356 */ 24357 this.locale = number.locale; 24358 24359 /** 24360 * @private 24361 * @type {ilib.NumPlan} 24362 */ 24363 this.destinationPlan = number.destinationPlan; 24364 24365 /** 24366 * @private 24367 * @type {ilib.Locale.PhoneLoc} 24368 */ 24369 this.destinationLocale = number.destinationLocale; 24370 24371 if (options && typeof(options.onLoad) === 'function') { 24372 options.onLoad(this); 24373 } 24374 return; 24375 } 24376 } 24377 24378 new ilib.Locale.PhoneLoc({ 24379 locale: options && options.locale, 24380 mcc: options && options.mcc, 24381 sync: this.sync, 24382 loadParams: this.loadParams, 24383 onLoad: ilib.bind(this, function(loc) { 24384 this.locale = this.destinationLocale = loc; 24385 new ilib.NumPlan({ 24386 locale: this.locale, 24387 sync: this.sync, 24388 loadParms: this.loadParams, 24389 onLoad: ilib.bind(this, function (plan) { 24390 this.plan = this.destinationPlan = plan; 24391 24392 if (typeof number === "object") { 24393 // the copy constructor code above did not find the locale 24394 // or plan before, but now they are loaded, so we can return 24395 // already without going further 24396 return; 24397 } 24398 ilib.loadData({ 24399 name: "states.json", 24400 object: ilib.PhoneNumber, 24401 locale: this.locale, 24402 sync: this.sync, 24403 loadParams: ilib.merge(this.loadParams, { 24404 returnOne: true 24405 }), 24406 callback: ilib.bind(this, function (stdata) { 24407 if (!stdata) { 24408 stdata = ilib.PhoneNumber._defaultStates; 24409 } 24410 24411 stateData = stdata; 24412 24413 regionSettings = { 24414 stateData: stateData, 24415 plan: plan, 24416 handler: ilib._handlerFactory(this.locale, plan) 24417 }; 24418 24419 // use ^ to indicate the beginning of the number, because certain things only match at the beginning 24420 number = "^" + number.replace(/\^/g, ''); 24421 number = ilib.PhoneNumber._stripFormatting(number); 24422 24423 this._parseNumber(number, regionSettings, options); 24424 }) 24425 }); 24426 }) 24427 }); 24428 }) 24429 }); 24430 }; 24431 24432 /** 24433 * Parse an International Mobile Subscriber Identity (IMSI) number into its 3 constituent parts: 24434 * 24435 * <ol> 24436 * <li>mcc - Mobile Country Code, which identifies the country where the phone is currently receiving 24437 * service. 24438 * <li>mnc - Mobile Network Code, which identifies the carrier which is currently providing service to the phone 24439 * <li>msin - Mobile Subscription Identifier Number. This is a unique number identifying the mobile phone on 24440 * the network, which usually maps to an account/subscriber in the carrier's database. 24441 * </ol> 24442 * 24443 * Because this function may need to load data to identify the above parts, you can pass an options 24444 * object that controls how the data is loaded. The options may contain any of the following properties: 24445 * 24446 * <ul> 24447 * <li>onLoad - a callback function to call when the parsing is done. When the onLoad option is given, 24448 * this method will attempt to load the locale data using the ilib loader callback. When it is done 24449 * (even if the data is already preassembled), the onLoad function is called with the parsing results 24450 * as a parameter, so this callback can be used with preassembled or dynamic, synchronous or 24451 * asynchronous loading or a mix of the above. 24452 * <li>sync - tell whether to load any missing locale data synchronously or asynchronously. If this 24453 * option is given as "false", then the "onLoad" callback must be given, as the results returned from 24454 * this constructor will not be usable for a while. 24455 * <li><i>loadParams</i> - an object containing parameters to pass to the loader callback function 24456 * when locale data is missing. The parameters are not interpretted or modified in any way. They are 24457 * simply passed along. The object may contain any property/value pairs as long as the calling code is in 24458 * agreement with the loader callback function as to what those parameters mean. 24459 * </ul> 24460 * 24461 * @static 24462 * @param {string} imsi IMSI number to parse 24463 * @param {Object} options options controlling the loading of the locale data 24464 * @return {{mcc:string,mnc:string,msin:string}|undefined} components of the IMSI number, when the locale data 24465 * is loaded synchronously, or undefined if asynchronous 24466 */ 24467 ilib.PhoneNumber.parseImsi = function(imsi, options) { 24468 var sync = true, 24469 loadParams = {}, 24470 fields = {}; 24471 24472 if (!imsi) { 24473 return undefined; 24474 } 24475 24476 if (options) { 24477 if (typeof(options.sync) !== 'undefined') { 24478 sync = (options.sync == true); 24479 } 24480 24481 if (options.loadParams) { 24482 loadParams = options.loadParams; 24483 } 24484 } 24485 24486 if (ilib.data.mnc) { 24487 fields = ilib.PhoneNumber._parseImsi(ilib.data.mnc, imsi); 24488 24489 if (options && typeof(options.onLoad) === 'function') { 24490 options.onLoad(fields); 24491 } 24492 } else { 24493 ilib.loadData({ 24494 name: "mnc.json", 24495 object: ilib.PhoneNumber, 24496 nonlocale: true, 24497 sync: sync, 24498 loadParams: loadParams, 24499 callback: ilib.bind(this, function(data) { 24500 ilib.data.mnc = data; 24501 fields = ilib.PhoneNumber._parseImsi(data, imsi); 24502 24503 if (options && typeof(options.onLoad) === 'function') { 24504 options.onLoad(fields); 24505 } 24506 }) 24507 }); 24508 } 24509 return fields; 24510 }; 24511 24512 24513 /** 24514 * @static 24515 * @protected 24516 */ 24517 ilib.PhoneNumber._parseImsi = function(data, imsi) { 24518 var ch, 24519 i, 24520 currentState, 24521 end, 24522 handlerMethod, 24523 state = 0, 24524 newState, 24525 fields = {}, 24526 lastLeaf, 24527 consumed = 0; 24528 24529 currentState = data; 24530 if (!currentState) { 24531 // can't parse anything 24532 return undefined; 24533 } 24534 24535 i = 0; 24536 while (i < imsi.length) { 24537 ch = ilib.PhoneNumber._getCharacterCode(imsi.charAt(i)); 24538 // console.info("parsing char " + imsi.charAt(i) + " code: " + ch); 24539 if (ch >= 0) { 24540 newState = currentState.s && currentState.s[ch]; 24541 24542 if (typeof(newState) === 'object') { 24543 if (typeof(newState.l) !== 'undefined') { 24544 // save for latter if needed 24545 lastLeaf = newState; 24546 consumed = i; 24547 } 24548 // console.info("recognized digit " + ch + " continuing..."); 24549 // recognized digit, so continue parsing 24550 currentState = newState; 24551 i++; 24552 } else { 24553 if ((typeof(newState) === 'undefined' || newState === 0 || 24554 (typeof(newState) === 'object' && typeof(newState.l) === 'undefined')) && 24555 lastLeaf) { 24556 // this is possibly a look-ahead and it didn't work... 24557 // so fall back to the last leaf and use that as the 24558 // final state 24559 newState = lastLeaf; 24560 i = consumed; 24561 } 24562 24563 if ((typeof(newState) === 'number' && newState) || 24564 (typeof(newState) === 'object' && typeof(newState.l) !== 'undefined')) { 24565 // final state 24566 var stateNumber = typeof(newState) === 'number' ? newState : newState.l; 24567 handlerMethod = ilib.PhoneNumber._states[stateNumber]; 24568 24569 // console.info("reached final state " + newState + " handler method is " + handlerMethod + " and i is " + i); 24570 24571 // deal with syntactic ambiguity by using the "special" end state instead of "area" 24572 if ( handlerMethod === "area" ) { 24573 end = i+1; 24574 } else { 24575 // unrecognized imsi, so just assume the mnc is 3 digits 24576 end = 6; 24577 } 24578 24579 fields.mcc = imsi.substring(0,3); 24580 fields.mnc = imsi.substring(3,end); 24581 fields.msin = imsi.substring(end); 24582 24583 return fields; 24584 } else { 24585 // parse error 24586 if (imsi.length >= 6) { 24587 fields.mcc = imsi.substring(0,3); 24588 fields.mnc = imsi.substring(3,6); 24589 fields.msin = imsi.substring(6); 24590 } 24591 return fields; 24592 } 24593 } 24594 } else if (ch === -1) { 24595 // non-transition character, continue parsing in the same state 24596 i++; 24597 } else { 24598 // should not happen 24599 // console.info("skipping character " + ch); 24600 // not a digit, plus, pound, or star, so this is probably a formatting char. Skip it. 24601 i++; 24602 } 24603 } 24604 24605 if (i >= imsi.length && imsi.length >= 6) { 24606 // we reached the end of the imsi, but did not finish recognizing anything. 24607 // Default to last resort and assume 3 digit mnc 24608 fields.mcc = imsi.substring(0,3); 24609 fields.mnc = imsi.substring(3,6); 24610 fields.msin = imsi.substring(6); 24611 } else { 24612 // unknown or not enough characters for a real imsi 24613 fields = undefined; 24614 } 24615 24616 // console.info("Globalization.Phone.parseImsi: final result is: " + JSON.stringify(fields)); 24617 return fields; 24618 }; 24619 24620 /** 24621 * @static 24622 * @private 24623 */ 24624 ilib.PhoneNumber._stripFormatting = function(str) { 24625 var ret = ""; 24626 var i; 24627 24628 for (i = 0; i < str.length; i++) { 24629 if (ilib.PhoneNumber._getCharacterCode(str.charAt(i)) >= -1) { 24630 ret += str.charAt(i); 24631 } 24632 } 24633 return ret; 24634 }; 24635 24636 /** 24637 * @static 24638 * @protected 24639 */ 24640 ilib.PhoneNumber._getCharacterCode = function(ch) { 24641 if (ch >= '0' && ch <= '9') { 24642 return ch - '0'; 24643 } 24644 switch (ch) { 24645 case '+': 24646 return 10; 24647 case '*': 24648 return 11; 24649 case '#': 24650 return 12; 24651 case '^': 24652 return 13; 24653 case 'p': // pause chars 24654 case 'P': 24655 case 't': 24656 case 'T': 24657 case 'w': 24658 case 'W': 24659 return -1; 24660 case 'x': 24661 case 'X': 24662 case ',': 24663 case ';': // extension char 24664 return -1; 24665 } 24666 return -2; 24667 }; 24668 24669 /** 24670 * @private 24671 */ 24672 ilib.PhoneNumber._states = [ 24673 "none", 24674 "unknown", 24675 "plus", 24676 "idd", 24677 "cic", 24678 "service", 24679 "cell", 24680 "area", 24681 "vsc", 24682 "country", 24683 "personal", 24684 "special", 24685 "trunk", 24686 "premium", 24687 "emergency", 24688 "service2", 24689 "service3", 24690 "service4", 24691 "cic2", 24692 "cic3", 24693 "start", 24694 "local" 24695 ]; 24696 24697 /** 24698 * @private 24699 */ 24700 ilib.PhoneNumber._fieldOrder = [ 24701 "vsc", 24702 "iddPrefix", 24703 "countryCode", 24704 "trunkAccess", 24705 "cic", 24706 "emergency", 24707 "mobilePrefix", 24708 "serviceCode", 24709 "areaCode", 24710 "subscriberNumber", 24711 "extension" 24712 ]; 24713 24714 ilib.PhoneNumber._defaultStates = { 24715 "s": [ 24716 0, 24717 21, // 1 -> local 24718 21, // 2 -> local 24719 21, // 3 -> local 24720 21, // 4 -> local 24721 21, // 5 -> local 24722 21, // 6 -> local 24723 21, // 7 -> local 24724 21, // 8 -> local 24725 21, // 9 -> local 24726 0,0,0, 24727 { // ^ 24728 "s": [ 24729 { // ^0 24730 "s": [3], // ^00 -> idd 24731 "l": 12 // ^0 -> trunk 24732 }, 24733 21, // ^1 -> local 24734 21, // ^2 -> local 24735 21, // ^3 -> local 24736 21, // ^4 -> local 24737 21, // ^5 -> local 24738 21, // ^6 -> local 24739 21, // ^7 -> local 24740 21, // ^8 -> local 24741 21, // ^9 -> local 24742 2 // ^+ -> plus 24743 ] 24744 } 24745 ] 24746 }; 24747 24748 ilib.PhoneNumber.prototype = { 24749 /** 24750 * @protected 24751 * @param {string} number 24752 * @param {Object} regionData 24753 * @param {Object} options 24754 * @param {string} countryCode 24755 */ 24756 _parseOtherCountry: function(number, regionData, options, countryCode) { 24757 new ilib.Locale.PhoneLoc({ 24758 locale: this.locale, 24759 countryCode: countryCode, 24760 sync: this.sync, 24761 loadParms: this.loadParams, 24762 onLoad: ilib.bind(this, function (loc) { 24763 /* 24764 * this.locale is the locale where this number is being parsed, 24765 * and is used to parse the IDD prefix, if any, and this.destinationLocale is 24766 * the locale of the rest of this number after the IDD prefix. 24767 */ 24768 /** @type {ilib.Locale.PhoneLoc} */ 24769 this.destinationLocale = loc; 24770 24771 ilib.loadData({ 24772 name: "states.json", 24773 object: ilib.PhoneNumber, 24774 locale: this.destinationLocale, 24775 sync: this.sync, 24776 loadParams: ilib.merge(this.loadParams, { 24777 returnOne: true 24778 }), 24779 callback: ilib.bind(this, function (stateData) { 24780 if (!stateData) { 24781 stateData = ilib.PhoneNumber._defaultStates; 24782 } 24783 24784 new ilib.NumPlan({ 24785 locale: this.destinationLocale, 24786 sync: this.sync, 24787 loadParms: this.loadParams, 24788 onLoad: ilib.bind(this, function (plan) { 24789 /* 24790 * this.plan is the plan where this number is being parsed, 24791 * and is used to parse the IDD prefix, if any, and this.destinationPlan is 24792 * the plan of the rest of this number after the IDD prefix in the 24793 * destination locale. 24794 */ 24795 /** @type {ilib.NumPlan} */ 24796 this.destinationPlan = plan; 24797 24798 var regionSettings = { 24799 stateData: stateData, 24800 plan: plan, 24801 handler: ilib._handlerFactory(this.destinationLocale, plan) 24802 }; 24803 24804 // for plans that do not skip the trunk code when dialing from 24805 // abroad, we need to treat the number from here on in as if it 24806 // were parsing a local number from scratch. That way, the parser 24807 // does not get confused between parts of the number at the 24808 // beginning of the number, and parts in the middle. 24809 if (!plan.getSkipTrunk()) { 24810 number = '^' + number; 24811 } 24812 24813 // recursively call the parser with the new states data 24814 // to finish the parsing 24815 this._parseNumber(number, regionSettings, options); 24816 }) 24817 }); 24818 }) 24819 }); 24820 }) 24821 }); 24822 }, 24823 24824 /** 24825 * @protected 24826 * @param {string} number 24827 * @param {Object} regionData 24828 * @param {Object} options 24829 */ 24830 _parseNumber: function(number, regionData, options) { 24831 var i, ch, 24832 regionSettings, 24833 newState, 24834 dot, 24835 handlerMethod, 24836 result, 24837 currentState = regionData.stateData, 24838 lastLeaf = undefined, 24839 consumed = 0; 24840 24841 regionSettings = regionData; 24842 dot = 14; // special transition which matches all characters. See AreaCodeTableMaker.java 24843 24844 i = 0; 24845 while (i < number.length) { 24846 ch = ilib.PhoneNumber._getCharacterCode(number.charAt(i)); 24847 if (ch >= 0) { 24848 // newState = stateData.states[state][ch]; 24849 newState = currentState.s && currentState.s[ch]; 24850 24851 if (!newState && currentState.s && currentState.s[dot]) { 24852 newState = currentState.s[dot]; 24853 } 24854 24855 if (typeof(newState) === 'object' && i+1 < number.length) { 24856 if (typeof(newState.l) !== 'undefined') { 24857 // this is a leaf node, so save that for later if needed 24858 lastLeaf = newState; 24859 consumed = i; 24860 } 24861 // console.info("recognized digit " + ch + " continuing..."); 24862 // recognized digit, so continue parsing 24863 currentState = newState; 24864 i++; 24865 } else { 24866 if ((typeof(newState) === 'undefined' || newState === 0 || 24867 (typeof(newState) === 'object' && typeof(newState.l) === 'undefined')) && 24868 lastLeaf) { 24869 // this is possibly a look-ahead and it didn't work... 24870 // so fall back to the last leaf and use that as the 24871 // final state 24872 newState = lastLeaf; 24873 i = consumed; 24874 consumed = 0; 24875 lastLeaf = undefined; 24876 } 24877 24878 if ((typeof(newState) === 'number' && newState) || 24879 (typeof(newState) === 'object' && typeof(newState.l) !== 'undefined')) { 24880 // final state 24881 var stateNumber = typeof(newState) === 'number' ? newState : newState.l; 24882 handlerMethod = ilib.PhoneNumber._states[stateNumber]; 24883 24884 if (number.charAt(0) === '^') { 24885 result = regionSettings.handler[handlerMethod](number.slice(1), i-1, this, regionSettings); 24886 } else { 24887 result = regionSettings.handler[handlerMethod](number, i, this, regionSettings); 24888 } 24889 24890 // reparse whatever is left 24891 number = result.number; 24892 i = consumed = 0; 24893 lastLeaf = undefined; 24894 24895 //console.log("reparsing with new number: " + number); 24896 currentState = regionSettings.stateData; 24897 // if the handler requested a special sub-table, use it for this round of parsing, 24898 // otherwise, set it back to the regular table to continue parsing 24899 24900 if (result.countryCode !== undefined) { 24901 this._parseOtherCountry(number, regionData, options, result.countryCode); 24902 // don't process any further -- let the work be done in the onLoad callbacks 24903 return; 24904 } else if (result.table !== undefined) { 24905 ilib.loadData({ 24906 name: result.table + ".json", 24907 object: ilib.PhoneNumber, 24908 nonlocale: true, 24909 sync: this.sync, 24910 loadParams: this.loadParams, 24911 callback: ilib.bind(this, function (data) { 24912 if (!data) { 24913 data = ilib.PhoneNumber._defaultStates; 24914 } 24915 24916 regionSettings = { 24917 stateData: data, 24918 plan: regionSettings.plan, 24919 handler: regionSettings.handler 24920 }; 24921 24922 // recursively call the parser with the new states data 24923 // to finish the parsing 24924 this._parseNumber(number, regionSettings, options); 24925 }) 24926 }); 24927 // don't process any further -- let the work be done in the onLoad callbacks 24928 return; 24929 } else if (result.skipTrunk !== undefined) { 24930 ch = ilib.PhoneNumber._getCharacterCode(regionSettings.plan.getTrunkCode()); 24931 currentState = currentState.s && currentState.s[ch]; 24932 } 24933 } else { 24934 handlerMethod = (typeof(newState) === 'number') ? "none" : "local"; 24935 // failed parse. Either no last leaf to fall back to, or there was an explicit 24936 // zero in the table. Put everything else in the subscriberNumber field as the 24937 // default place 24938 if (number.charAt(0) === '^') { 24939 result = regionSettings.handler[handlerMethod](number.slice(1), i-1, this, regionSettings); 24940 } else { 24941 result = regionSettings.handler[handlerMethod](number, i, this, regionSettings); 24942 } 24943 break; 24944 } 24945 } 24946 } else if (ch === -1) { 24947 // non-transition character, continue parsing in the same state 24948 i++; 24949 } else { 24950 // should not happen 24951 // console.info("skipping character " + ch); 24952 // not a digit, plus, pound, or star, so this is probably a formatting char. Skip it. 24953 i++; 24954 } 24955 } 24956 if (i >= number.length && currentState !== regionData.stateData) { 24957 handlerMethod = (typeof(currentState.l) === 'undefined' || currentState === 0) ? "none" : "local"; 24958 // we reached the end of the phone number, but did not finish recognizing anything. 24959 // Default to last resort and put everything that is left into the subscriber number 24960 //console.log("Reached end of number before parsing was complete. Using handler for method none.") 24961 if (number.charAt(0) === '^') { 24962 result = regionSettings.handler[handlerMethod](number.slice(1), i-1, this, regionSettings); 24963 } else { 24964 result = regionSettings.handler[handlerMethod](number, i, this, regionSettings); 24965 } 24966 } 24967 24968 // let the caller know we are done parsing 24969 if (this.onLoad) { 24970 this.onLoad(this); 24971 } 24972 }, 24973 /** 24974 * @protected 24975 */ 24976 _getPrefix: function() { 24977 return this.areaCode || this.serviceCode || this.mobilePrefix || ""; 24978 }, 24979 24980 /** 24981 * @protected 24982 */ 24983 _hasPrefix: function() { 24984 return (this._getPrefix() !== ""); 24985 }, 24986 24987 /** 24988 * Exclusive or -- return true, if one is defined and the other isn't 24989 * @protected 24990 */ 24991 _xor : function(left, right) { 24992 if ((left === undefined && right === undefined ) || (left !== undefined && right !== undefined)) { 24993 return false; 24994 } else { 24995 return true; 24996 } 24997 }, 24998 24999 /** 25000 * return a version of the phone number that contains only the dialable digits in the correct order 25001 * @protected 25002 */ 25003 _join: function () { 25004 var fieldName, formatted = ""; 25005 25006 try { 25007 for (var field in ilib.PhoneNumber._fieldOrder) { 25008 if (typeof field === 'string' && typeof ilib.PhoneNumber._fieldOrder[field] === 'string') { 25009 fieldName = ilib.PhoneNumber._fieldOrder[field]; 25010 // console.info("normalize: formatting field " + fieldName); 25011 if (this[fieldName] !== undefined) { 25012 formatted += this[fieldName]; 25013 } 25014 } 25015 } 25016 } catch ( e ) { 25017 //console.warn("caught exception in _join: " + e); 25018 throw e; 25019 } 25020 return formatted; 25021 }, 25022 25023 /** 25024 * This routine will compare the two phone numbers in an locale-sensitive 25025 * manner to see if they possibly reference the same phone number.<p> 25026 * 25027 * In many places, 25028 * there are multiple ways to reach the same phone number. In North America for 25029 * example, you might have a number with the trunk access code of "1" and another 25030 * without, and they reference the exact same phone number. This is considered a 25031 * strong match. For a different pair of numbers, one may be a local number and 25032 * the other a full phone number with area code, which may reference the same 25033 * phone number if the local number happens to be located in that area code. 25034 * However, you cannot say for sure if it is in that area code, so it will 25035 * be considered a somewhat weaker match.<p> 25036 * 25037 * Similarly, in other countries, there are sometimes different ways of 25038 * reaching the same destination, and the way that numbers 25039 * match depends on the locale.<p> 25040 * 25041 * The various phone number fields are handled differently for matches. There 25042 * are various fields that do not need to match at all. For example, you may 25043 * type equally enter "00" or "+" into your phone to start international direct 25044 * dialling, so the iddPrefix field does not need to match at all.<p> 25045 * 25046 * Typically, fields that require matches need to match exactly if both sides have a value 25047 * for that field. If both sides specify a value and those values differ, that is 25048 * a strong non-match. If one side does not have a value and the other does, that 25049 * causes a partial match, because the number with the missing field may possibly 25050 * have an implied value that matches the other number. For example, the numbers 25051 * "650-555-1234" and "555-1234" have a partial match as the local number "555-1234" 25052 * might possibly have the same 650 area code as the first number, and might possibly 25053 * not. If both side do not specify a value for a particular field, that field is 25054 * considered matching.<p> 25055 * 25056 * The values of following fields are ignored when performing matches: 25057 * 25058 * <ul> 25059 * <li>vsc 25060 * <li>iddPrefix 25061 * <li>cic 25062 * <li>trunkAccess 25063 * </ul> 25064 * 25065 * The values of the following fields matter if they do not match: 25066 * 25067 * <ul> 25068 * <li>countryCode - A difference causes a moderately strong problem except for 25069 * certain countries where there is a way to access the same subscriber via IDD 25070 * and via intranetwork dialling 25071 * <li>mobilePrefix - A difference causes a possible non-match 25072 * <li>serviceCode - A difference causes a possible non-match 25073 * <li>areaCode - A difference causes a possible non-match 25074 * <li>subscriberNumber - A difference causes a very strong non-match 25075 * <li>extension - A difference causes a minor non-match 25076 * </ul> 25077 * 25078 * @param {string|ilib.PhoneNumber} other other phone number to compare this one to 25079 * @return {number} non-negative integer describing the percentage quality of the 25080 * match. 100 means a very strong match (100%), and lower numbers are less and 25081 * less strong, down to 0 meaning not at all a match. 25082 */ 25083 compare: function (other) { 25084 var match = 100, 25085 FRdepartments = {"590":1, "594":1, "596":1, "262":1}, 25086 ITcountries = {"378":1, "379":1}, 25087 thisPrefix, 25088 otherPrefix, 25089 currentCountryCode = 0; 25090 25091 if (typeof this.locale.region === "string") { 25092 currentCountryCode = this.locale._mapRegiontoCC(this.locale.region); 25093 } 25094 25095 // subscriber number must be present and must match 25096 if (!this.subscriberNumber || !other.subscriberNumber || this.subscriberNumber !== other.subscriberNumber) { 25097 return 0; 25098 } 25099 25100 // extension must match if it is present 25101 if (this._xor(this.extension, other.extension) || this.extension !== other.extension) { 25102 return 0; 25103 } 25104 25105 if (this._xor(this.countryCode, other.countryCode)) { 25106 // if one doesn't have a country code, give it some demerit points, but if the 25107 // one that has the country code has something other than the current country 25108 // add even more. Ignore the special cases where you can dial the same number internationally or via 25109 // the local numbering system 25110 switch (this.locale.getRegion()) { 25111 case 'FR': 25112 if (this.countryCode in FRdepartments || other.countryCode in FRdepartments) { 25113 if (this.areaCode !== other.areaCode || this.mobilePrefix !== other.mobilePrefix) { 25114 match -= 100; 25115 } 25116 } else { 25117 match -= 16; 25118 } 25119 break; 25120 case 'IT': 25121 if (this.countryCode in ITcountries || other.countryCode in ITcountries) { 25122 if (this.areaCode !== other.areaCode) { 25123 match -= 100; 25124 } 25125 } else { 25126 match -= 16; 25127 } 25128 break; 25129 default: 25130 match -= 16; 25131 if ((this.countryCode !== undefined && this.countryCode !== currentCountryCode) || 25132 (other.countryCode !== undefined && other.countryCode !== currentCountryCode)) { 25133 match -= 16; 25134 } 25135 } 25136 } else if (this.countryCode !== other.countryCode) { 25137 // ignore the special cases where you can dial the same number internationally or via 25138 // the local numbering system 25139 if (other.countryCode === '33' || this.countryCode === '33') { 25140 // france 25141 if (this.countryCode in FRdepartments || other.countryCode in FRdepartments) { 25142 if (this.areaCode !== other.areaCode || this.mobilePrefix !== other.mobilePrefix) { 25143 match -= 100; 25144 } 25145 } else { 25146 match -= 100; 25147 } 25148 } else if (this.countryCode === '39' || other.countryCode === '39') { 25149 // italy 25150 if (this.countryCode in ITcountries || other.countryCode in ITcountries) { 25151 if (this.areaCode !== other.areaCode) { 25152 match -= 100; 25153 } 25154 } else { 25155 match -= 100; 25156 } 25157 } else { 25158 match -= 100; 25159 } 25160 } 25161 25162 if (this._xor(this.serviceCode, other.serviceCode)) { 25163 match -= 20; 25164 } else if (this.serviceCode !== other.serviceCode) { 25165 match -= 100; 25166 } 25167 25168 if (this._xor(this.mobilePrefix, other.mobilePrefix)) { 25169 match -= 20; 25170 } else if (this.mobilePrefix !== other.mobilePrefix) { 25171 match -= 100; 25172 } 25173 25174 if (this._xor(this.areaCode, other.areaCode)) { 25175 // one has an area code, the other doesn't, so dock some points. It could be a match if the local 25176 // number in the one number has the same implied area code as the explicit area code in the other number. 25177 match -= 12; 25178 } else if (this.areaCode !== other.areaCode) { 25179 match -= 100; 25180 } 25181 25182 thisPrefix = this._getPrefix(); 25183 otherPrefix = other._getPrefix(); 25184 25185 if (thisPrefix && otherPrefix && thisPrefix !== otherPrefix) { 25186 match -= 100; 25187 } 25188 25189 // make sure we are between 0 and 100 25190 if (match < 0) { 25191 match = 0; 25192 } else if (match > 100) { 25193 match = 100; 25194 } 25195 25196 return match; 25197 }, 25198 25199 /** 25200 * Determine whether or not the other phone number is exactly equal to the current one.<p> 25201 * 25202 * The difference between the compare method and the equals method is that the compare 25203 * method compares normalized numbers with each other and returns the degree of match, 25204 * whereas the equals operator returns true iff the two numbers contain the same fields 25205 * and the fields are exactly the same. Functions and other non-phone number properties 25206 * are not compared. 25207 * @param {string|ilib.PhoneNumber} other another phone number to compare to this one 25208 * @return {boolean} true if the numbers are the same, false otherwise 25209 */ 25210 equals: function equals(other) { 25211 if (other.locale && this.locale && !this.locale.equals(other.locale) && (!this.countryCode || !other.countryCode)) { 25212 return false; 25213 } 25214 25215 for (var p in other) { 25216 if (p !== undefined && this[p] !== undefined && typeof(this[p]) !== 'object') { 25217 if (other[p] === undefined) { 25218 /*console.error("PhoneNumber.equals: other is missing property " + p + " which has the value " + this[p] + " in this"); 25219 console.error("this is : " + JSON.stringify(this)); 25220 console.error("other is: " + JSON.stringify(other));*/ 25221 return false; 25222 } 25223 if (this[p] !== other[p]) { 25224 /*console.error("PhoneNumber.equals: difference in property " + p); 25225 console.error("this is : " + JSON.stringify(this)); 25226 console.error("other is: " + JSON.stringify(other));*/ 25227 return false; 25228 } 25229 } 25230 } 25231 for (var p in other) { 25232 if (p !== undefined && other[p] !== undefined && typeof(other[p]) !== 'object') { 25233 if (this[p] === undefined) { 25234 /*console.error("PhoneNumber.equals: this is missing property " + p + " which has the value " + other[p] + " in the other"); 25235 console.error("this is : " + JSON.stringify(this)); 25236 console.error("other is: " + JSON.stringify(other));*/ 25237 return false; 25238 } 25239 if (this[p] !== other[p]) { 25240 /*console.error("PhoneNumber.equals: difference in property " + p); 25241 console.error("this is : " + JSON.stringify(this)); 25242 console.error("other is: " + JSON.stringify(other));*/ 25243 return false; 25244 } 25245 } 25246 } 25247 return true; 25248 }, 25249 25250 25251 /** 25252 * @private 25253 * @param {{ 25254 * mcc:string, 25255 * defaultAreaCode:string, 25256 * country:string, 25257 * networkType:string, 25258 * assistedDialing:boolean, 25259 * sms:boolean, 25260 * manualDialing:boolean 25261 * }} options an object containing options to help in normalizing. 25262 * @param {ilib.PhoneNumber} norm 25263 * @param {ilib.Locale.PhoneLoc} homeLocale 25264 * @param {ilib.Locale.PhoneLoc} currentLocale 25265 * @param {ilib.NumPlan} currentPlan 25266 * @param {ilib.Locale.PhoneLoc} destinationLocale 25267 * @param {ilib.NumPlan} destinationPlan 25268 * @param {boolean} sync 25269 * @param {Object|undefined} loadParams 25270 */ 25271 _doNormalize: function(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams) { 25272 var formatted = ""; 25273 25274 if (!norm.invalid && options && options.assistedDialing) { 25275 // don't normalize things that don't have subscriber numbers. Also, don't normalize 25276 // manually dialed local numbers. Do normalize local numbers in contact entries. 25277 if (norm.subscriberNumber && 25278 (!options.manualDialing || 25279 norm.iddPrefix || 25280 norm.countryCode || 25281 norm.trunkAccess)) { 25282 // console.log("normalize: assisted dialling normalization of " + JSON.stringify(norm)); 25283 if (currentLocale.getRegion() !== destinationLocale.getRegion()) { 25284 // we are currently calling internationally 25285 if (!norm._hasPrefix() && 25286 options.defaultAreaCode && 25287 destinationLocale.getRegion() === homeLocale.getRegion() && 25288 (!destinationPlan.getFieldLength("minLocalLength") || 25289 norm.subscriberNumber.length >= destinationPlan.getFieldLength("minLocalLength"))) { 25290 // area code is required when dialling from international, but only add it if we are dialing 25291 // to our home area. Otherwise, the default area code is not valid! 25292 norm.areaCode = options.defaultAreaCode; 25293 if (!destinationPlan.getSkipTrunk() && destinationPlan.getTrunkCode()) { 25294 // some phone systems require the trunk access code, even when dialling from international 25295 norm.trunkAccess = destinationPlan.getTrunkCode(); 25296 } 25297 } 25298 25299 if (norm.trunkAccess && destinationPlan.getSkipTrunk()) { 25300 // on some phone systems, the trunk access code is dropped when dialling from international 25301 delete norm.trunkAccess; 25302 } 25303 25304 // make sure to get the country code for the destination region, not the current region! 25305 if (options.sms) { 25306 if (homeLocale.getRegion() === "US" && currentLocale.getRegion() !== "US") { 25307 if (destinationLocale.getRegion() !== "US") { 25308 norm.iddPrefix = "011"; // non-standard code to make it go through the US first 25309 norm.countryCode = norm.countryCode || homeLocale._mapRegiontoCC(destinationLocale.getRegion()); 25310 } else if (options.networkType === "cdma") { 25311 delete norm.iddPrefix; 25312 delete norm.countryCode; 25313 if (norm.areaCode) { 25314 norm.trunkAccess = "1"; 25315 } 25316 } else if (norm.areaCode) { 25317 norm.iddPrefix = "+"; 25318 norm.countryCode = "1"; 25319 delete norm.trunkAccess; 25320 } 25321 } else { 25322 norm.iddPrefix = (options.networkType === "cdma") ? currentPlan.getIDDCode() : "+"; 25323 norm.countryCode = norm.countryCode || homeLocale._mapRegiontoCC(destinationLocale.region); 25324 } 25325 } else if (norm._hasPrefix() && !norm.countryCode) { 25326 norm.countryCode = homeLocale._mapRegiontoCC(destinationLocale.region); 25327 } 25328 25329 if (norm.countryCode && !options.sms) { 25330 // for CDMA, make sure to get the international dialling access code for the current region, not the destination region 25331 // all umts carriers support plus dialing 25332 norm.iddPrefix = (options.networkType === "cdma") ? currentPlan.getIDDCode() : "+"; 25333 } 25334 } else { 25335 // console.log("normalize: dialing within the country"); 25336 if (options.defaultAreaCode) { 25337 if (destinationPlan.getPlanStyle() === "open") { 25338 if (!norm.trunkAccess && norm._hasPrefix() && destinationPlan.getTrunkCode()) { 25339 // call is not local to this area code, so you have to dial the trunk code and the area code 25340 norm.trunkAccess = destinationPlan.getTrunkCode(); 25341 } 25342 } else { 25343 // In closed plans, you always have to dial the area code, even if the call is local. 25344 if (!norm._hasPrefix()) { 25345 if (destinationLocale.getRegion() === homeLocale.getRegion()) { 25346 norm.areaCode = options.defaultAreaCode; 25347 if (destinationPlan.getTrunkRequired() && destinationPlan.getTrunkCode()) { 25348 norm.trunkAccess = norm.trunkAccess || destinationPlan.getTrunkCode(); 25349 } 25350 } 25351 } else { 25352 if (destinationPlan.getTrunkRequired() && destinationPlan.getTrunkCode()) { 25353 norm.trunkAccess = norm.trunkAccess || destinationPlan.getTrunkCode(); 25354 } 25355 } 25356 } 25357 } 25358 25359 if (options.sms && 25360 homeLocale.getRegion() === "US" && 25361 currentLocale.getRegion() !== "US") { 25362 norm.iddPrefix = "011"; // make it go through the US first 25363 if (destinationPlan.getSkipTrunk() && norm.trunkAccess) { 25364 delete norm.trunkAccess; 25365 } 25366 } else if (norm.iddPrefix || norm.countryCode) { 25367 // we are in our destination country, so strip the international dialling prefixes 25368 delete norm.iddPrefix; 25369 delete norm.countryCode; 25370 25371 if ((destinationPlan.getPlanStyle() === "open" || destinationPlan.getTrunkRequired()) && destinationPlan.getTrunkCode()) { 25372 norm.trunkAccess = destinationPlan.getTrunkCode(); 25373 } 25374 } 25375 } 25376 } 25377 } else if (!norm.invalid) { 25378 // console.log("normalize: non-assisted normalization"); 25379 if (!norm._hasPrefix() && options && options.defaultAreaCode && destinationLocale.getRegion() === homeLocale.region) { 25380 norm.areaCode = options.defaultAreaCode; 25381 } 25382 25383 if (!norm.countryCode && norm._hasPrefix()) { 25384 norm.countryCode = homeLocale._mapRegiontoCC(destinationLocale.getRegion()); 25385 } 25386 25387 if (norm.countryCode) { 25388 if (options && options.networkType && options.networkType === "cdma") { 25389 norm.iddPrefix = currentPlan.getIDDCode(); 25390 } else { 25391 // all umts carriers support plus dialing 25392 norm.iddPrefix = "+"; 25393 } 25394 25395 if (destinationPlan.getSkipTrunk() && norm.trunkAccess) { 25396 delete norm.trunkAccess; 25397 } else if (!destinationPlan.getSkipTrunk() && !norm.trunkAccess && destinationPlan.getTrunkCode()) { 25398 norm.trunkAccess = destinationPlan.getTrunkCode(); 25399 } 25400 } 25401 } 25402 25403 // console.info("normalize: after normalization, the normalized phone number is: " + JSON.stringify(norm)); 25404 formatted = norm._join(); 25405 25406 return formatted; 25407 }, 25408 25409 /** 25410 * @private 25411 * @param {{ 25412 * mcc:string, 25413 * defaultAreaCode:string, 25414 * country:string, 25415 * networkType:string, 25416 * assistedDialing:boolean, 25417 * sms:boolean, 25418 * manualDialing:boolean 25419 * }} options an object containing options to help in normalizing. 25420 * @param {ilib.PhoneNumber} norm 25421 * @param {ilib.Locale.PhoneLoc} homeLocale 25422 * @param {ilib.Locale.PhoneLoc} currentLocale 25423 * @param {ilib.NumPlan} currentPlan 25424 * @param {ilib.Locale.PhoneLoc} destinationLocale 25425 * @param {ilib.NumPlan} destinationPlan 25426 * @param {boolean} sync 25427 * @param {Object|undefined} loadParams 25428 * @param {function(string)} callback 25429 */ 25430 _doReparse: function(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams, callback) { 25431 var formatted, 25432 tempRegion; 25433 25434 if (options && 25435 options.assistedDialing && 25436 !norm.trunkAccess && 25437 !norm.iddPrefix && 25438 norm.subscriberNumber && 25439 norm.subscriberNumber.length > destinationPlan.getFieldLength("maxLocalLength")) { 25440 25441 // numbers that are too long are sometimes international direct dialed numbers that 25442 // are missing the IDD prefix. So, try reparsing it using a plus in front to see if that works. 25443 new ilib.PhoneNumber("+" + this._join(), { 25444 locale: this.locale, 25445 sync: sync, 25446 loadParms: loadParams, 25447 onLoad: ilib.bind(this, function (data) { 25448 tempRegion = (data.countryCode && data.locale._mapCCtoRegion(data.countryCode)); 25449 25450 if (tempRegion && tempRegion !== "unknown" && tempRegion !== "SG") { 25451 // only use it if it is a recognized country code. Singapore (SG) is a special case. 25452 norm = data; 25453 destinationLocale = data.destinationLocale; 25454 destinationPlan = data.destinationPlan; 25455 } 25456 25457 formatted = this._doNormalize(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams); 25458 if (typeof(callback) === 'function') { 25459 callback(formatted); 25460 } 25461 }) 25462 }); 25463 } else if (options && options.assistedDialing && norm.invalid && currentLocale.region !== norm.locale.region) { 25464 // if this number is not valid for the locale it was parsed with, try again with the current locale 25465 // console.log("norm is invalid. Attempting to reparse with the current locale"); 25466 25467 new ilib.PhoneNumber(this._join(), { 25468 locale: currentLocale, 25469 sync: sync, 25470 loadParms: loadParams, 25471 onLoad: ilib.bind(this, function (data) { 25472 if (data && !data.invalid) { 25473 norm = data; 25474 } 25475 25476 formatted = this._doNormalize(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams); 25477 if (typeof(callback) === 'function') { 25478 callback(formatted); 25479 } 25480 }) 25481 }); 25482 } else { 25483 formatted = this._doNormalize(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams); 25484 if (typeof(callback) === 'function') { 25485 callback(formatted); 25486 } 25487 } 25488 }, 25489 25490 /** 25491 * This function normalizes the current phone number to a canonical format and returns a 25492 * string with that phone number. If parts are missing, this function attempts to fill in 25493 * those parts.<p> 25494 * 25495 * The options object contains a set of properties that can possibly help normalize 25496 * this number by providing "extra" information to the algorithm. The options 25497 * parameter may be null or an empty object if no hints can be determined before 25498 * this call is made. If any particular hint is not 25499 * available, it does not need to be present in the options object.<p> 25500 * 25501 * The following is a list of hints that the algorithm will look for in the options 25502 * object: 25503 * 25504 * <ul> 25505 * <li><i>mcc</i> the mobile carrier code of the current network upon which this 25506 * phone is operating. This is translated into an IDD country code. This is 25507 * useful if the number being normalized comes from CNAP (callerid) and the 25508 * MCC is known. 25509 * <li><i>defaultAreaCode</i> the area code of the phone number of the current 25510 * device, if available. Local numbers in a person's contact list are most 25511 * probably in this same area code. 25512 * <li><i>country</i> the 2 letter ISO 3166 code of the country if it is 25513 * known from some other means such as parsing the physical address of the 25514 * person associated with the phone number, or the from the domain name 25515 * of the person's email address 25516 * <li><i>networkType</i> specifies whether the phone is currently connected to a 25517 * CDMA network or a UMTS network. Valid values are the strings "cdma" and "umts". 25518 * If one of those two strings are not specified, or if this property is left off 25519 * completely, this method will assume UMTS. 25520 * </ul> 25521 * 25522 * The following are a list of options that control the behaviour of the normalization: 25523 * 25524 * <ul> 25525 * <li><i>assistedDialing</i> if this is set to true, the number will be normalized 25526 * so that it can dialled directly on the type of network this phone is 25527 * currently connected to. This allows customers to dial numbers or use numbers 25528 * in their contact list that are specific to their "home" region when they are 25529 * roaming and those numbers would not otherwise work with the current roaming 25530 * carrier as they are. The home region is 25531 * specified as the phoneRegion system preference that is settable in the 25532 * regional settings app. With assisted dialling, this method will add or 25533 * remove international direct dialling prefixes and country codes, as well as 25534 * national trunk access codes, as required by the current roaming carrier and the 25535 * home region in order to dial the number properly. If it is not possible to 25536 * construct a full international dialling sequence from the options and hints given, 25537 * this function will not modify the phone number, and will return "undefined". 25538 * If assisted dialling is false or not specified, then this method will attempt 25539 * to add all the information it can to the number so that it is as fully 25540 * specified as possible. This allows two numbers to be compared more easily when 25541 * those two numbers were otherwise only partially specified. 25542 * <li><i>sms</i> set this option to true for the following conditions: 25543 * <ul> 25544 * <li>assisted dialing is turned on 25545 * <li>the phone number represents the destination of an SMS message 25546 * <li>the phone is UMTS 25547 * <li>the phone is SIM-locked to its carrier 25548 * </ul> 25549 * This enables special international direct dialling codes to route the SMS message to 25550 * the correct carrier. If assisted dialling is not turned on, this option has no 25551 * affect. 25552 * <li><i>manualDialing</i> set this option to true if the user is entering this number on 25553 * the keypad directly, and false when the number comes from a stored location like a 25554 * contact entry or a call log entry. When true, this option causes the normalizer to 25555 * not perform any normalization on numbers that look like local numbers in the home 25556 * country. If false, all numbers go through normalization. This option only has an effect 25557 * when the assistedDialing option is true as well, otherwise it is ignored. 25558 * </ul> 25559 * 25560 * If both a set of options and a locale are given, and they offer conflicting 25561 * information, the options will take precedence. The idea is that the locale 25562 * tells you the region setting that the user has chosen (probably in 25563 * firstuse), whereas the the hints are more current information such as 25564 * where the phone is currently operating (the MCC).<p> 25565 * 25566 * This function performs the following types of normalizations with assisted 25567 * dialling turned on: 25568 * 25569 * <ol> 25570 * <li>If the current location of the phone matches the home country, this is a 25571 * domestic call. 25572 * <ul> 25573 * <li>Remove any iddPrefix and countryCode fields, as they are not needed 25574 * <li>Add in a trunkAccess field that may be necessary to call a domestic numbers 25575 * in the home country 25576 * </ul> 25577 * <li> If the current location of the phone does not match the home country, 25578 * attempt to form a whole international number. 25579 * <ul> 25580 * <li>Add in the area code if it is missing from the phone number and the area code 25581 * of the current phone is available in the hints 25582 * <li>Add the country dialling code for the home country if it is missing from the 25583 * phone number 25584 * <li>Add or replace the iddPrefix with the correct one for the current country. The 25585 * phone number will have been parsed with the settings for the home country, so 25586 * the iddPrefix may be incorrect for the 25587 * current country. The iddPrefix for the current country can be "+" if the phone 25588 * is connected to a UMTS network, and either a "+" or a country-dependent 25589 * sequences of digits for CDMA networks. 25590 * </ul> 25591 * </ol> 25592 * 25593 * This function performs the following types of normalization with assisted 25594 * dialling turned off: 25595 * 25596 * <ul> 25597 * <li>Normalize the international direct dialing prefix to be a plus or the 25598 * international direct dialling access code for the current country, depending 25599 * on the network type. 25600 * <li>If a number is a local number (ie. it is missing its area code), 25601 * use a default area code from the hints if available. CDMA phones always know their area 25602 * code, and GSM/UMTS phones know their area code in many instances, but not always 25603 * (ie. not on Vodaphone or Telcel phones). If the default area code is not available, 25604 * do not add it. 25605 * <li>In assisted dialling mode, if a number is missing its country code, 25606 * use the current MCC number if 25607 * it is available to figure out the current country code, and prepend that 25608 * to the number. If it is not available, leave it off. Also, use that 25609 * country's settings to parse the number instead of the current format 25610 * locale. 25611 * <li>For North American numbers with an area code but no trunk access 25612 * code, add in the trunk access code. 25613 * <li>For other countries, if the country code is added in step 3, remove the 25614 * trunk access code when required by that country's conventions for 25615 * international calls. If the country requires a trunk access code for 25616 * international calls and it doesn't exist, add one. 25617 * </ul> 25618 * 25619 * This method modifies the current object, and also returns a string 25620 * containing the normalized phone number that can be compared directly against 25621 * other normalized numbers. The canonical format for phone numbers that is 25622 * returned from thhomeLocaleis method is simply an uninterrupted and unformatted string 25623 * of dialable digits. 25624 * 25625 * @param {{ 25626 * mcc:string, 25627 * defaultAreaCode:string, 25628 * country:string, 25629 * networkType:string, 25630 * assistedDialing:boolean, 25631 * sms:boolean, 25632 * manualDialing:boolean 25633 * }} options an object containing options to help in normalizing. 25634 * @return {string|undefined} the normalized string, or undefined if the number 25635 * could not be normalized 25636 */ 25637 normalize: function(options) { 25638 var norm, 25639 sync = true, 25640 loadParams = {}; 25641 25642 25643 if (options) { 25644 if (typeof(options.sync) !== 'undefined') { 25645 sync = (options.sync == true); 25646 } 25647 25648 if (options.loadParams) { 25649 loadParams = options.loadParams; 25650 } 25651 } 25652 25653 // Clone this number, so we don't mess with the original. 25654 // No need to do this asynchronously because it's a copy constructor which doesn't 25655 // load any extra files. 25656 norm = new ilib.PhoneNumber(this); 25657 25658 var normalized; 25659 25660 if (options && (typeof(options.mcc) !== 'undefined' || typeof(options.country) !== 'undefined')) { 25661 new ilib.Locale.PhoneLoc({ 25662 mcc: options.mcc, 25663 countryCode: options.countryCode, 25664 locale: this.locale, 25665 sync: sync, 25666 loadParams: loadParams, 25667 onLoad: ilib.bind(this, function(loc) { 25668 new ilib.NumPlan({ 25669 locale: loc, 25670 sync: sync, 25671 loadParms: loadParams, 25672 onLoad: ilib.bind(this, function (plan) { 25673 this._doReparse(options, norm, this.locale, loc, plan, this.destinationLocale, this.destinationPlan, sync, loadParams, function (fmt) { 25674 normalized = fmt; 25675 25676 if (options && typeof(options.onLoad) === 'function') { 25677 options.onLoad(fmt); 25678 } 25679 }); 25680 }) 25681 }); 25682 }) 25683 }); 25684 } else { 25685 this._doReparse(options, norm, this.locale, this.locale, this.plan, this.destinationLocale, this.destinationPlan, sync, loadParams, function (fmt) { 25686 normalized = fmt; 25687 25688 if (options && typeof(options.onLoad) === 'function') { 25689 options.onLoad(fmt); 25690 } 25691 }); 25692 } 25693 25694 // return the value for the synchronous case 25695 return normalized; 25696 } 25697 }; 25698 /* 25699 * phonefmt.js - Represent a phone number formatter. 25700 * 25701 * Copyright © 2014, JEDLSoft 25702 * 25703 * Licensed under the Apache License, Version 2.0 (the "License"); 25704 * you may not use this file except in compliance with the License. 25705 * You may obtain a copy of the License at 25706 * 25707 * http://www.apache.org/licenses/LICENSE-2.0 25708 * 25709 * Unless required by applicable law or agreed to in writing, software 25710 * distributed under the License is distributed on an "AS IS" BASIS, 25711 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 25712 * 25713 * See the License for the specific language governing permissions and 25714 * limitations under the License. 25715 */ 25716 25717 /* 25718 !depends 25719 ilibglobal.js 25720 locale.js 25721 localeinfo.js 25722 phone/numplan.js 25723 phone/phonenum.js 25724 */ 25725 25726 // !data phonefmt 25727 25728 /** 25729 * @class 25730 * Create a new phone number formatter object that formats numbers according to the parameters.<p> 25731 * 25732 * The options object can contain zero or more of the following parameters: 25733 * 25734 * <ul> 25735 * <li><i>locale</i> locale to use to format this number, or undefined to use the default locale 25736 * <li><i>style</i> the name of style to use to format numbers, or undefined to use the default style 25737 * <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 25738 * 25739 * <li><i>onLoad</i> - a callback function to call when the locale data is fully loaded and the address has been 25740 * parsed. When the onLoad option is given, the address formatter object 25741 * will attempt to load any missing locale data using the ilib loader callback. 25742 * When the constructor is done (even if the data is already preassembled), the 25743 * onLoad function is called with the current instance as a parameter, so this 25744 * callback can be used with preassembled or dynamic loading or a mix of the two. 25745 * 25746 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 25747 * asynchronously. If this option is given as "false", then the "onLoad" 25748 * callback must be given, as the instance returned from this constructor will 25749 * not be usable for a while. 25750 * 25751 * <li><i>loadParams</i> - an object containing parameters to pass to the 25752 * loader callback function when locale data is missing. The parameters are not 25753 * interpretted or modified in any way. They are simply passed along. The object 25754 * may contain any property/value pairs as long as the calling code is in 25755 * agreement with the loader callback function as to what those parameters mean. 25756 * </ul> 25757 * 25758 * Some regions have more than one style of formatting, and the style parameter 25759 * selects which style the user prefers. An array of style names that this locale 25760 * supports can be found by calling {@link ilib.PhoneFmt.getAvailableStyles}. 25761 * Example phone numbers can be retrieved for each style by calling 25762 * {@link ilib.PhoneFmt.getStyleExample}. 25763 * <p> 25764 * 25765 * If the MCC is given, numbers will be formatted in the manner of the country 25766 * specified by the MCC. If it is not given, but the locale is, the manner of 25767 * the country in the locale will be used. If neither the locale or MCC are not given, 25768 * then the country of the current ilib locale is used. 25769 * 25770 * @constructor 25771 * @param {Object} options properties that control how this formatter behaves 25772 */ 25773 ilib.PhoneFmt = function(options) { 25774 this.sync = true; 25775 this.styleName = 'default', 25776 this.loadParams = {}; 25777 25778 var locale = new ilib.Locale(); 25779 25780 if (options) { 25781 if (options.locale) { 25782 locale = options.locale; 25783 } 25784 25785 if (typeof(options.sync) !== 'undefined') { 25786 this.sync = (options.sync == true); 25787 } 25788 25789 if (options.loadParams) { 25790 this.loadParams = options.loadParams; 25791 } 25792 25793 if (options.style) { 25794 this.style = options.style; 25795 } 25796 } 25797 25798 new ilib.Locale.PhoneLoc({ 25799 locale: locale, 25800 mcc: options && options.mcc, 25801 countryCode: options && options.countryCode, 25802 onLoad: ilib.bind(this, function (data) { 25803 /** @type {ilib.Locale.PhoneLoc} */ 25804 this.locale = data; 25805 25806 new ilib.NumPlan({ 25807 locale: this.locale, 25808 sync: this.sync, 25809 loadParms: this.loadParams, 25810 onLoad: ilib.bind(this, function (plan) { 25811 /** @type {ilib.NumPlan} */ 25812 this.plan = plan; 25813 25814 ilib.loadData({ 25815 name: "phonefmt.json", 25816 object: ilib.PhoneFmt, 25817 locale: this.locale, 25818 sync: this.sync, 25819 loadParams: ilib.merge(this.loadParams, { 25820 returnOne: true 25821 }), 25822 callback: ilib.bind(this, function (fmtdata) { 25823 this.fmtdata = fmtdata; 25824 25825 if (options && typeof(options.onLoad) === 'function') { 25826 options.onLoad(this); 25827 } 25828 }) 25829 }); 25830 }) 25831 }); 25832 }) 25833 }); 25834 }; 25835 25836 ilib.PhoneFmt.prototype = { 25837 /** 25838 * 25839 * @protected 25840 * @param {string} part 25841 * @param {Object} formats 25842 * @param {boolean} mustUseAll 25843 */ 25844 _substituteDigits: function(part, formats, mustUseAll) { 25845 var formatString, 25846 formatted = "", 25847 partIndex = 0, 25848 templates, 25849 i; 25850 25851 // console.info("Globalization.Phone._substituteDigits: typeof(formats) is " + typeof(formats)); 25852 if (!part) { 25853 return formatted; 25854 } 25855 25856 if (typeof(formats) === "object") { 25857 templates = (typeof(formats.template) !== 'undefined') ? formats.template : formats; 25858 if (part.length > templates.length) { 25859 // too big, so just use last resort rule. 25860 throw "part " + part + " is too big. We do not have a format template to format it."; 25861 } 25862 // use the format in this array that corresponds to the digit length of this 25863 // part of the phone number 25864 formatString = templates[part.length-1]; 25865 // console.info("Globalization.Phone._substituteDigits: formats is an Array: " + JSON.stringify(formats)); 25866 } else { 25867 formatString = formats; 25868 } 25869 25870 for (i = 0; i < formatString.length; i++) { 25871 if (formatString.charAt(i) === "X") { 25872 formatted += part.charAt(partIndex); 25873 partIndex++; 25874 } else { 25875 formatted += formatString.charAt(i); 25876 } 25877 } 25878 25879 if (mustUseAll && partIndex < part.length-1) { 25880 // didn't use the whole thing in this format? Hmm... go to last resort rule 25881 throw "too many digits in " + part + " for format " + formatString; 25882 } 25883 25884 return formatted; 25885 }, 25886 25887 /** 25888 * Returns the style with the given name, or the default style if there 25889 * is no style with that name. 25890 * @protected 25891 * @return {{example:string,whole:Object.<string,string>,partial:Object.<string,string>}|Object.<string,string>} 25892 */ 25893 _getStyle: function (name, fmtdata) { 25894 return fmtdata[name] || fmtdata["default"]; 25895 }, 25896 25897 /** 25898 * Do the actual work of formatting the phone number starting at the given 25899 * field in the regular field order. 25900 * 25901 * @param {!ilib.PhoneNumber} number 25902 * @param {{ 25903 * partial:boolean, 25904 * style:string, 25905 * mcc:string, 25906 * locale:(string|ilib.Locale), 25907 * sync:boolean, 25908 * loadParams:Object, 25909 * onLoad:function(string) 25910 * }} options Parameters which control how to format the number 25911 * @param {number} startField 25912 */ 25913 _doFormat: function(number, options, startField, locale, fmtdata, callback) { 25914 var sync = true, 25915 loadParams = {}, 25916 temp, 25917 templates, 25918 fieldName, 25919 countryCode, 25920 isWhole, 25921 style, 25922 formatted = "", 25923 styleTemplates, 25924 lastFieldName; 25925 25926 if (options) { 25927 if (typeof(options.sync) !== 'undefined') { 25928 sync = (options.sync == true); 25929 } 25930 25931 if (options.loadParams) { 25932 loadParams = options.loadParams; 25933 } 25934 } 25935 25936 style = this.style; // default style for this formatter 25937 25938 // figure out what style to use for this type of number 25939 if (number.countryCode) { 25940 // dialing from outside the country 25941 // check to see if it to a mobile number because they are often formatted differently 25942 style = (number.mobilePrefix) ? "internationalmobile" : "international"; 25943 } else if (number.mobilePrefix !== undefined) { 25944 style = "mobile"; 25945 } else if (number.serviceCode !== undefined && typeof(fmtdata["service"]) !== 'undefined') { 25946 // if there is a special format for service numbers, then use it 25947 style = "service"; 25948 } 25949 25950 isWhole = (!options || !options.partial); 25951 styleTemplates = this._getStyle(style, fmtdata); 25952 25953 // console.log("Style ends up being " + style + " and using subtype " + (isWhole ? "whole" : "partial")); 25954 styleTemplates = (isWhole ? styleTemplates.whole : styleTemplates.partial) || styleTemplates; 25955 25956 for (var i = startField; i < ilib.PhoneNumber._fieldOrder.length; i++) { 25957 fieldName = ilib.PhoneNumber._fieldOrder[i]; 25958 // console.info("format: formatting field " + fieldName + " value: " + number[fieldName]); 25959 if (number[fieldName] !== undefined) { 25960 if (styleTemplates[fieldName] !== undefined) { 25961 templates = styleTemplates[fieldName]; 25962 if (fieldName === "trunkAccess") { 25963 if (number.areaCode === undefined && number.serviceCode === undefined && number.mobilePrefix === undefined) { 25964 templates = "X"; 25965 } 25966 } 25967 if (lastFieldName && typeof(styleTemplates[lastFieldName].suffix) !== 'undefined') { 25968 if (fieldName !== "extension" && number[fieldName].search(/[xwtp,;]/i) <= -1) { 25969 formatted += styleTemplates[lastFieldName].suffix; 25970 } 25971 } 25972 lastFieldName = fieldName; 25973 25974 // console.info("format: formatting field " + fieldName + " with templates " + JSON.stringify(templates)); 25975 temp = this._substituteDigits(number[fieldName], templates, (fieldName === "subscriberNumber")); 25976 // console.info("format: formatted is: " + temp); 25977 formatted += temp; 25978 25979 if (fieldName === "countryCode") { 25980 // switch to the new country to format the rest of the number 25981 countryCode = number.countryCode.replace(/[wWpPtT\+#\*]/g, ''); // fix for NOV-108200 25982 25983 new ilib.Locale.PhoneLoc({ 25984 locale: this.locale, 25985 sync: sync, 25986 loadParms: loadParams, 25987 countryCode: countryCode, 25988 onLoad: ilib.bind(this, function (/** @type {ilib.Locale.PhoneLoc} */ locale) { 25989 ilib.loadData({ 25990 name: "phonefmt.json", 25991 object: ilib.PhoneFmt, 25992 locale: locale, 25993 sync: sync, 25994 loadParams: ilib.merge(loadParams, { 25995 returnOne: true 25996 }), 25997 callback: ilib.bind(this, function (fmtdata) { 25998 // console.info("format: switching to region " + locale.region + " and style " + style + " to format the rest of the number "); 25999 26000 var subfmt = ""; 26001 26002 this._doFormat(number, options, i+1, locale, fmtdata, function (subformat) { 26003 subfmt = subformat; 26004 if (typeof(callback) === 'function') { 26005 callback(formatted + subformat); 26006 } 26007 }); 26008 26009 formatted += subfmt; 26010 }) 26011 }); 26012 }) 26013 }); 26014 return formatted; 26015 } 26016 } else { 26017 //console.warn("PhoneFmt.format: cannot find format template for field " + fieldName + ", region " + locale.region + ", style " + style); 26018 // use default of "minimal formatting" so we don't miss parts because of bugs in the format templates 26019 formatted += number[fieldName]; 26020 } 26021 } 26022 } 26023 26024 if (typeof(callback) === 'function') { 26025 callback(formatted); 26026 } 26027 26028 return formatted; 26029 }, 26030 26031 /** 26032 * Format the parts of a phone number appropriately according to the settings in 26033 * this formatter instance. 26034 * 26035 * The options can contain zero or more of these properties: 26036 * 26037 * <ul> 26038 * <li><i>partial</i> boolean which tells whether or not this phone number 26039 * represents a partial number or not. The default is false, which means the number 26040 * represents a whole number. 26041 * <li><i>style</i> style to use to format the number, if different from the 26042 * default style or the style specified in the constructor 26043 * <li><i>locale</i> The locale with which to parse the number. This gives a clue as to which 26044 * numbering plan to use. 26045 * <li><i>mcc</i> The mobile carrier code (MCC) associated with the carrier that the phone is 26046 * currently connected to, if known. This also can give a clue as to which numbering plan to 26047 * use 26048 * <li><i>onLoad</i> - a callback function to call when the date format object is fully 26049 * loaded. When the onLoad option is given, the DateFmt object will attempt to 26050 * load any missing locale data using the ilib loader callback. 26051 * When the constructor is done (even if the data is already preassembled), the 26052 * onLoad function is called with the current instance as a parameter, so this 26053 * callback can be used with preassembled or dynamic loading or a mix of the two. 26054 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 26055 * asynchronously. If this option is given as "false", then the "onLoad" 26056 * callback must be given, as the instance returned from this constructor will 26057 * not be usable for a while. 26058 * <li><i>loadParams</i> - an object containing parameters to pass to the 26059 * loader callback function when locale data is missing. The parameters are not 26060 * interpretted or modified in any way. They are simply passed along. The object 26061 * may contain any property/value pairs as long as the calling code is in 26062 * agreement with the loader callback function as to what those parameters mean. 26063 * </ul> 26064 * 26065 * The partial parameter specifies whether or not the phone number contains 26066 * a partial phone number or if it is a whole phone number. A partial 26067 * number is usually a number as the user is entering it with a dial pad. The 26068 * reason is that certain types of phone numbers should be formatted differently 26069 * depending on whether or not it represents a whole number. Specifically, SMS 26070 * short codes are formatted differently.<p> 26071 * 26072 * Example: a subscriber number of "48773" in the US would get formatted as: 26073 * 26074 * <ul> 26075 * <li>partial: 487-73 (perhaps the user is in the process of typing a whole phone 26076 * number such as 487-7379) 26077 * <li>whole: 48773 (this is the entire SMS short code) 26078 * </ul> 26079 * 26080 * Any place in the UI where the user types in phone numbers, such as the keypad in 26081 * the phone app, should pass in partial: true to this formatting routine. All other 26082 * places, such as the call log in the phone app, should pass in partial: false, or 26083 * leave the partial flag out of the parameters entirely. 26084 * 26085 * @param {!ilib.PhoneNumber} number object containing the phone number to format 26086 * @param {{ 26087 * partial:boolean, 26088 * style:string, 26089 * mcc:string, 26090 * locale:(string|ilib.Locale), 26091 * sync:boolean, 26092 * loadParams:Object, 26093 * onLoad:function(string) 26094 * }} options Parameters which control how to format the number 26095 * @return {string} Returns the formatted phone number as a string. 26096 */ 26097 format: function (number, options) { 26098 var formatted = "", 26099 callback; 26100 26101 callback = options && options.onLoad; 26102 26103 try { 26104 this._doFormat(number, options, 0, this.locale, this.fmtdata, function (fmt) { 26105 formatted = fmt; 26106 26107 if (typeof(callback) === 'function') { 26108 callback(fmt); 26109 } 26110 }); 26111 } catch (e) { 26112 if (typeof(e) === 'string') { 26113 // console.warn("caught exception: " + e + ". Using last resort rule."); 26114 // if there was some exception, use this last resort rule 26115 formatted = ""; 26116 for (var field in ilib.PhoneNumber._fieldOrder) { 26117 if (typeof field === 'string' && typeof ilib.PhoneNumber._fieldOrder[field] === 'string' && number[ilib.PhoneNumber._fieldOrder[field]] !== undefined) { 26118 // just concatenate without any formatting 26119 formatted += number[ilib.PhoneNumber._fieldOrder[field]]; 26120 if (ilib.PhoneNumber._fieldOrder[field] === 'countryCode') { 26121 formatted += ' '; // fix for NOV-107894 26122 } 26123 } 26124 } 26125 } else { 26126 throw e; 26127 } 26128 26129 if (typeof(callback) === 'function') { 26130 callback(formatted); 26131 } 26132 } 26133 return formatted; 26134 }, 26135 26136 /** 26137 * Return an array of names of all available styles that can be used with the current 26138 * formatter. 26139 * @return {Array.<string>} an array of names of styles that are supported by this formatter 26140 */ 26141 getAvailableStyles: function () { 26142 var ret = [], 26143 style; 26144 26145 if (this.fmtdata) { 26146 for (style in this.fmtdata) { 26147 if (this.fmtdata[style].example) { 26148 ret.push(style); 26149 } 26150 } 26151 } 26152 return ret; 26153 }, 26154 26155 /** 26156 * Return an example phone number formatted with the given style. 26157 * 26158 * @param {string|undefined} style style to get an example of, or undefined to use 26159 * the current default style for this formatter 26160 * @return {string|undefined} an example phone number formatted according to the 26161 * given style, or undefined if the style is not recognized or does not have an 26162 * example 26163 */ 26164 getStyleExample: function (style) { 26165 return this.fmtdata[style].example || undefined; 26166 } 26167 }; 26168 26169 /* 26170 * phonegeo.js - Represent a phone number geolocator object. 26171 * 26172 * Copyright © 2014, JEDLSoft 26173 * 26174 * Licensed under the Apache License, Version 2.0 (the "License"); 26175 * you may not use this file except in compliance with the License. 26176 * You may obtain a copy of the License at 26177 * 26178 * http://www.apache.org/licenses/LICENSE-2.0 26179 * 26180 * Unless required by applicable law or agreed to in writing, software 26181 * distributed under the License is distributed on an "AS IS" BASIS, 26182 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26183 * 26184 * See the License for the specific language governing permissions and 26185 * limitations under the License. 26186 */ 26187 26188 /* 26189 !depends 26190 ilibglobal.js 26191 locale.js 26192 localeinfo.js 26193 phone/numplan.js 26194 phone/phoneloc.js 26195 phone/phonenum.js 26196 */ 26197 26198 // !data iddarea area extarea extstates phoneres 26199 26200 /** 26201 * @class 26202 * Create an instance that can geographically locate a phone number.<p> 26203 * 26204 * The location of the number is calculated according to the following rules: 26205 * 26206 * <ol> 26207 * <li>If the areaCode property is undefined or empty, or if the number specifies a 26208 * country code for which we do not have information, then the area property may be 26209 * missing from the returned object. In this case, only the country object will be returned. 26210 * 26211 * <li>If there is no area code, but there is a mobile prefix, service code, or emergency 26212 * code, then a fixed string indicating the type of number will be returned. 26213 * 26214 * <li>The country object is filled out according to the countryCode property of the phone 26215 * number. 26216 * 26217 * <li>If the phone number does not have an explicit country code, the MCC will be used if 26218 * it is available. The country code can be gleaned directly from the MCC. If the MCC 26219 * of the carrier to which the phone is currently connected is available, it should be 26220 * passed in so that local phone numbers will look correct. 26221 * 26222 * <li>If the country's dialling plan mandates a fixed length for phone numbers, and a 26223 * particular number exceeds that length, then the area code will not be given on the 26224 * assumption that the number has problems in the first place and we cannot guess 26225 * correctly. 26226 * </ol> 26227 * 26228 * The returned area property varies in specificity according 26229 * to the locale. In North America, the area is no finer than large parts of states 26230 * or provinces. In Germany and the UK, the area can be as fine as small towns.<p> 26231 * 26232 * If the number passed in is invalid, no geolocation will be performed. If the location 26233 * information about the country where the phone number is located is not available, 26234 * then the area information will be missing and only the country will be available.<p> 26235 * 26236 * The options parameter can contain any one of the following properties: 26237 * 26238 * <ul> 26239 * <li><i>locale</i> The locale parameter is used to load translations of the names of regions and 26240 * areas if available. For example, if the locale property is given as "en-US" (English for USA), 26241 * but the phone number being geolocated is in Germany, then this class would return the the names 26242 * of the country (Germany) and region inside of Germany in English instead of German. That is, a 26243 * phone number in Munich and return the country "Germany" and the area code "Munich" 26244 * instead of "Deutschland" and "München". The default display locale is the current ilib locale. 26245 * If translations are not available, the region and area names are given in English, which should 26246 * always be available. 26247 * <li><i>mcc</i> The mcc of the current mobile carrier, if known. 26248 * 26249 * <li><i>onLoad</i> - a callback function to call when the data for the 26250 * locale is fully loaded. When the onLoad option is given, this object 26251 * will attempt to load any missing locale data using the ilib loader callback. 26252 * When the constructor is done (even if the data is already preassembled), the 26253 * onLoad function is called with the current instance as a parameter, so this 26254 * callback can be used with preassembled or dynamic loading or a mix of the two. 26255 * 26256 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 26257 * asynchronously. If this option is given as "false", then the "onLoad" 26258 * callback must be given, as the instance returned from this constructor will 26259 * not be usable for a while. 26260 * 26261 * <li><i>loadParams</i> - an object containing parameters to pass to the 26262 * loader callback function when locale data is missing. The parameters are not 26263 * interpretted or modified in any way. They are simply passed along. The object 26264 * may contain any property/value pairs as long as the calling code is in 26265 * agreement with the loader callback function as to what those parameters mean. 26266 * </ul> 26267 * 26268 * @constructor 26269 * @param {Object} options parameters controlling the geolocation of the phone number. 26270 */ 26271 ilib.GeoLocator = function(options) { 26272 var sync = true, 26273 loadParams = {}, 26274 locale = ilib.getLocale(); 26275 26276 if (options) { 26277 if (options.locale) { 26278 locale = options.locale; 26279 } 26280 26281 if (typeof(options.sync) === 'boolean') { 26282 sync = options.sync; 26283 } 26284 26285 if (options.loadParams) { 26286 loadParams = options.loadParams; 26287 } 26288 } 26289 26290 new ilib.Locale.PhoneLoc({ 26291 locale: locale, 26292 mcc: options && options.mcc, 26293 countryCode: options && options.countryCode, 26294 sync: sync, 26295 loadParams: loadParams, 26296 onLoad: ilib.bind(this, function (loc) { 26297 this.locale = loc; 26298 new ilib.NumPlan({ 26299 locale: this.locale, 26300 sync: sync, 26301 loadParams: loadParams, 26302 onLoad: ilib.bind(this, function (plan) { 26303 this.plan = plan; 26304 26305 new ilib.ResBundle({ 26306 locale: this.locale, 26307 name: "phoneres", 26308 sync: sync, 26309 loadParams: loadParams, 26310 onLoad: ilib.bind(this, function (rb) { 26311 this.rb = rb; 26312 26313 ilib.loadData({ 26314 name: "iddarea.json", 26315 object: ilib.GeoLocator, 26316 nonlocale: true, 26317 sync: sync, 26318 loadParams: loadParams, 26319 callback: ilib.bind(this, function (data) { 26320 this.regiondata = data; 26321 ilib.loadData({ 26322 name: "area.json", 26323 object: ilib.GeoLocator, 26324 locale: this.locale, 26325 sync: sync, 26326 loadParams: ilib.merge(loadParams, { 26327 returnOne: true 26328 }), 26329 callback: ilib.bind(this, function (areadata) { 26330 this.areadata = areadata; 26331 26332 if (options && typeof(options.onLoad) === 'function') { 26333 options.onLoad(this); 26334 } 26335 }) 26336 }); 26337 }) 26338 }); 26339 }) 26340 }); 26341 }) 26342 }); 26343 }) 26344 }); 26345 }; 26346 26347 ilib.GeoLocator.prototype = { 26348 /** 26349 * @private 26350 * 26351 * Used for locales where the area code is very general, and you need to add in 26352 * the initial digits of the subscriber number in order to get the area 26353 * 26354 * @param {string} number 26355 * @param {Object} stateTable 26356 */ 26357 _parseAreaAndSubscriber: function (number, stateTable) { 26358 var ch, 26359 i, 26360 handlerMethod, 26361 newState, 26362 prefix = "", 26363 consumed, 26364 lastLeaf, 26365 currentState, 26366 dot = 14; // special transition which matches all characters. See AreaCodeTableMaker.java 26367 26368 if (!number || !stateTable) { 26369 // can't parse anything 26370 return undefined; 26371 } 26372 26373 //console.log("GeoLocator._parseAreaAndSubscriber: parsing number " + number); 26374 26375 currentState = stateTable; 26376 i = 0; 26377 while (i < number.length) { 26378 ch = ilib.PhoneNumber._getCharacterCode(number.charAt(i)); 26379 if (ch >= 0) { 26380 // newState = stateData.states[state][ch]; 26381 newState = currentState.s && currentState.s[ch]; 26382 26383 if (!newState && currentState.s && currentState.s[dot]) { 26384 newState = currentState.s[dot]; 26385 } 26386 26387 if (typeof(newState) === 'object') { 26388 if (typeof(newState.l) !== 'undefined') { 26389 // save for latter if needed 26390 lastLeaf = newState; 26391 consumed = i; 26392 } 26393 // console.info("recognized digit " + ch + " continuing..."); 26394 // recognized digit, so continue parsing 26395 currentState = newState; 26396 i++; 26397 } else { 26398 if (typeof(newState) === 'undefined' || newState === 0) { 26399 // this is possibly a look-ahead and it didn't work... 26400 // so fall back to the last leaf and use that as the 26401 // final state 26402 newState = lastLeaf; 26403 i = consumed; 26404 } 26405 26406 if ((typeof(newState) === 'number' && newState) || 26407 (typeof(newState) === 'object' && typeof(newState.l) !== 'undefined')) { 26408 // final state 26409 var stateNumber = typeof(newState) === 'number' ? newState : newState.l; 26410 handlerMethod = ilib.PhoneNumber._states[stateNumber]; 26411 26412 //console.info("reached final state " + newState + " handler method is " + handlerMethod + " and i is " + i); 26413 26414 return (handlerMethod === "area") ? number.substring(0, i+1) : undefined; 26415 } else { 26416 // failed parse. Either no last leaf to fall back to, or there was an explicit 26417 // zero in the table 26418 break; 26419 } 26420 } 26421 } else if (ch === -1) { 26422 // non-transition character, continue parsing in the same state 26423 i++; 26424 } else { 26425 // should not happen 26426 // console.info("skipping character " + ch); 26427 // not a digit, plus, pound, or star, so this is probably a formatting char. Skip it. 26428 i++; 26429 } 26430 } 26431 return undefined; 26432 }, 26433 /** 26434 * @private 26435 * @param prefix 26436 * @param table 26437 * @returns 26438 */ 26439 _matchPrefix: function(prefix, table) { 26440 var i, matchedDot, matchesWithDots = []; 26441 26442 if (table[prefix]) { 26443 return table[prefix]; 26444 } 26445 for (var entry in table) { 26446 if (entry && typeof(entry) === 'string') { 26447 i = 0; 26448 matchedDot = false; 26449 while (i < entry.length && (entry.charAt(i) === prefix.charAt(i) || entry.charAt(i) === '.')) { 26450 if (entry.charAt(i) === '.') { 26451 matchedDot = true; 26452 } 26453 i++; 26454 } 26455 if (i >= entry.length) { 26456 if (matchedDot) { 26457 matchesWithDots.push(entry); 26458 } else { 26459 return table[entry]; 26460 } 26461 } 26462 } 26463 } 26464 26465 // match entries with dots last, so sort the matches so that the entry with the 26466 // most dots sorts last. The entry that ends up at the beginning of the list is 26467 // the best match because it has the fewest dots 26468 if (matchesWithDots.length > 0) { 26469 matchesWithDots.sort(function (left, right) { 26470 return (right < left) ? -1 : ((left < right) ? 1 : 0); 26471 }); 26472 return table[matchesWithDots[0]]; 26473 } 26474 26475 return undefined; 26476 }, 26477 /** 26478 * @private 26479 * @param number 26480 * @param data 26481 * @param locale 26482 * @param plan 26483 * @param options 26484 * @returns {Object} 26485 */ 26486 _getAreaInfo: function(number, data, locale, plan, options) { 26487 var sync = true, 26488 ret = {}, 26489 countryCode, 26490 areaInfo, 26491 temp, 26492 areaCode, 26493 geoTable, 26494 tempNumber, 26495 prefix; 26496 26497 if (options && typeof(options.sync) === 'boolean') { 26498 sync = options.sync; 26499 } 26500 26501 prefix = number.areaCode || number.serviceCode; 26502 geoTable = data; 26503 26504 if (prefix !== undefined) { 26505 if (plan.getExtendedAreaCode()) { 26506 // for countries where the area code is very general and large, and you need a few initial 26507 // digits of the subscriber number in order find the actual area 26508 tempNumber = prefix + number.subscriberNumber; 26509 tempNumber = tempNumber.replace(/[wWpPtT\+#\*]/g, ''); // fix for NOV-108200 26510 26511 ilib.loadData({ 26512 name: "extarea.json", 26513 object: ilib.GeoLocator, 26514 locale: locale, 26515 sync: sync, 26516 loadParams: ilib.merge((options && options.loadParams) || {}, {returnOne: true}), 26517 callback: ilib.bind(this, function (data) { 26518 this.extarea = data; 26519 ilib.loadData({ 26520 name: "extstates.json", 26521 object: ilib.GeoLocator, 26522 locale: locale, 26523 sync: sync, 26524 loadParams: ilib.merge((options && options.loadParams) || {}, {returnOne: true}), 26525 callback: ilib.bind(this, function (data) { 26526 this.extstates = data; 26527 geoTable = this.extarea; 26528 if (this.extarea && this.extstates) { 26529 prefix = this._parseAreaAndSubscriber(tempNumber, this.extstates); 26530 } 26531 26532 if (!prefix) { 26533 // not a recognized prefix, so now try the general table 26534 geoTable = this.areadata; 26535 prefix = number.areaCode || number.serviceCode; 26536 } 26537 26538 if ((!plan.fieldLengths || 26539 plan.getFieldLength('maxLocalLength') === undefined || 26540 !number.subscriberNumber || 26541 number.subscriberNumber.length <= plan.fieldLengths('maxLocalLength'))) { 26542 areaInfo = this._matchPrefix(prefix, geoTable); 26543 if (areaInfo && areaInfo.sn && areaInfo.ln) { 26544 //console.log("Found areaInfo " + JSON.stringify(areaInfo)); 26545 ret.area = { 26546 sn: this.rb.getString(areaInfo.sn).toString(), 26547 ln: this.rb.getString(areaInfo.ln).toString() 26548 }; 26549 } 26550 } 26551 }) 26552 }); 26553 }) 26554 }); 26555 26556 } else if (!plan || 26557 plan.getFieldLength('maxLocalLength') === undefined || 26558 !number.subscriberNumber || 26559 number.subscriberNumber.length <= plan.getFieldLength('maxLocalLength')) { 26560 if (geoTable) { 26561 areaCode = prefix.replace(/[wWpPtT\+#\*]/g, ''); 26562 areaInfo = this._matchPrefix(areaCode, geoTable); 26563 26564 if (areaInfo && areaInfo.sn && areaInfo.ln) { 26565 ret.area = { 26566 sn: this.rb.getString(areaInfo.sn).toString(), 26567 ln: this.rb.getString(areaInfo.ln).toString() 26568 }; 26569 } else if (number.serviceCode) { 26570 ret.area = { 26571 sn: this.rb.getString("Service Number").toString(), 26572 ln: this.rb.getString("Service Number").toString() 26573 }; 26574 } 26575 } else { 26576 countryCode = number.locale._mapRegiontoCC(this.locale.getRegion()); 26577 if (countryCode !== "0" && this.regiondata) { 26578 temp = this.regiondata[countryCode]; 26579 if (temp && temp.sn) { 26580 ret.country = { 26581 sn: this.rb.getString(temp.sn).toString(), 26582 ln: this.rb.getString(temp.ln).toString(), 26583 code: this.locale.getRegion() 26584 }; 26585 } 26586 } 26587 } 26588 } else { 26589 countryCode = number.locale._mapRegiontoCC(this.locale.getRegion()); 26590 if (countryCode !== "0" && this.regiondata) { 26591 temp = this.regiondata[countryCode]; 26592 if (temp && temp.sn) { 26593 ret.country = { 26594 sn: this.rb.getString(temp.sn).toString(), 26595 ln: this.rb.getString(temp.ln).toString(), 26596 code: this.locale.getRegion() 26597 }; 26598 } 26599 } 26600 } 26601 26602 } else if (number.mobilePrefix) { 26603 ret.area = { 26604 sn: this.rb.getString("Mobile Number").toString(), 26605 ln: this.rb.getString("Mobile Number").toString() 26606 }; 26607 } else if (number.emergency) { 26608 ret.area = { 26609 sn: this.rb.getString("Emergency Services Number").toString(), 26610 ln: this.rb.getString("Emergency Services Number").toString() 26611 }; 26612 } 26613 26614 return ret; 26615 }, 26616 /** 26617 * Returns a the location of the given phone number, if known. 26618 * The returned object has 2 properties, each of which has an sn (short name) 26619 * and an ln (long name) string. Additionally, the country code, if given, 26620 * includes the 2 letter ISO code for the recognized country. 26621 * { 26622 * "country": { 26623 * "sn": "North America", 26624 * "ln": "North America and the Caribbean Islands", 26625 * "code": "us" 26626 * }, 26627 * "area": { 26628 * "sn": "California", 26629 * "ln": "Central California: San Jose, Los Gatos, Milpitas, Sunnyvale, Cupertino, Gilroy" 26630 * } 26631 * } 26632 * 26633 * The location name is subject to the following rules: 26634 * 26635 * If the areaCode property is undefined or empty, or if the number specifies a 26636 * country code for which we do not have information, then the area property may be 26637 * missing from the returned object. In this case, only the country object will be returned. 26638 * 26639 * If there is no area code, but there is a mobile prefix, service code, or emergency 26640 * code, then a fixed string indicating the type of number will be returned. 26641 * 26642 * The country object is filled out according to the countryCode property of the phone 26643 * number. 26644 * 26645 * If the phone number does not have an explicit country code, the MCC will be used if 26646 * it is available. The country code can be gleaned directly from the MCC. If the MCC 26647 * of the carrier to which the phone is currently connected is available, it should be 26648 * passed in so that local phone numbers will look correct. 26649 * 26650 * If the country's dialling plan mandates a fixed length for phone numbers, and a 26651 * particular number exceeds that length, then the area code will not be given on the 26652 * assumption that the number has problems in the first place and we cannot guess 26653 * correctly. 26654 * 26655 * The returned area property varies in specificity according 26656 * to the locale. In North America, the area is no finer than large parts of states 26657 * or provinces. In Germany and the UK, the area can be as fine as small towns. 26658 * 26659 * The strings returned from this function are already localized to the 26660 * given locale, and thus are ready for display to the user. 26661 * 26662 * If the number passed in is invalid, an empty object is returned. If the location 26663 * information about the country where the phone number is located is not available, 26664 * then the area information will be missing and only the country will be returned. 26665 * 26666 * The options parameter can contain any one of the following properties: 26667 * 26668 * <ul> 26669 * <li><i>locale</i> The locale parameter is used to load translations of the names of regions and 26670 * areas if available. For example, if the locale property is given as "en-US" (English for USA), 26671 * but the phone number being geolocated is in Germany, then this class would return the the names 26672 * of the country (Germany) and region inside of Germany in English instead of German. That is, a 26673 * phone number in Munich and return the country "Germany" and the area code "Munich" 26674 * instead of "Deutschland" and "München". The default display locale is the current ilib locale. 26675 * If translations are not available, the region and area names are given in English, which should 26676 * always be available. 26677 * <li><i>mcc</i> The mcc of the current mobile carrier, if known. 26678 * 26679 * <li><i>onLoad</i> - a callback function to call when the data for the 26680 * locale is fully loaded. When the onLoad option is given, this object 26681 * will attempt to load any missing locale data using the ilib loader callback. 26682 * When the constructor is done (even if the data is already preassembled), the 26683 * onLoad function is called with the current instance as a parameter, so this 26684 * callback can be used with preassembled or dynamic loading or a mix of the two. 26685 * 26686 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 26687 * asynchronously. If this option is given as "false", then the "onLoad" 26688 * callback must be given, as the instance returned from this constructor will 26689 * not be usable for a while. 26690 * 26691 * <li><i>loadParams</i> - an object containing parameters to pass to the 26692 * loader callback function when locale data is missing. The parameters are not 26693 * interpretted or modified in any way. They are simply passed along. The object 26694 * may contain any property/value pairs as long as the calling code is in 26695 * agreement with the loader callback function as to what those parameters mean. 26696 * </ul> 26697 * 26698 * @param {ilib.PhoneNumber} number phone number to locate 26699 * @param {Object} options options governing the way this ares is loaded 26700 * @return {Object} an object 26701 * that describes the country and the area in that country corresponding to this 26702 * phone number. Each of the country and area contain a short name (sn) and long 26703 * name (ln) that describes the location. 26704 */ 26705 locate: function(number, options) { 26706 var loadParams = {}, 26707 ret = {}, 26708 region, 26709 countryCode, 26710 temp, 26711 plan, 26712 areaResult, 26713 phoneLoc = this.locale, 26714 sync = true; 26715 26716 if (number === undefined || typeof(number) !== 'object' || !(number instanceof ilib.PhoneNumber)) { 26717 return ret; 26718 } 26719 26720 if (options) { 26721 if (typeof(options.sync) !== 'undefined') { 26722 sync = (options.sync == true); 26723 } 26724 26725 if (options.loadParams) { 26726 loadParams = options.loadParams; 26727 } 26728 } 26729 26730 // console.log("GeoLocator.locate: looking for geo for number " + JSON.stringify(number)); 26731 region = this.locale.getRegion(); 26732 if (number.countryCode !== undefined && this.regiondata) { 26733 countryCode = number.countryCode.replace(/[wWpPtT\+#\*]/g, ''); 26734 temp = this.regiondata[countryCode]; 26735 phoneLoc = number.destinationLocale; 26736 plan = number.destinationPlan; 26737 ret.country = { 26738 sn: this.rb.getString(temp.sn).toString(), 26739 ln: this.rb.getString(temp.ln).toString(), 26740 code: phoneLoc.getRegion() 26741 }; 26742 } 26743 26744 if (!plan) { 26745 plan = this.plan; 26746 } 26747 26748 ilib.loadData({ 26749 name: "area.json", 26750 object: ilib.GeoLocator, 26751 locale: phoneLoc, 26752 sync: sync, 26753 loadParams: ilib.merge(loadParams, { 26754 returnOne: true 26755 }), 26756 callback: ilib.bind(this, function (areadata) { 26757 if (areadata) { 26758 this.areadata = areadata; 26759 } 26760 areaResult = this._getAreaInfo(number, this.areadata, phoneLoc, plan, options); 26761 ret = ilib.merge(ret, areaResult); 26762 26763 if (ret.country === undefined) { 26764 countryCode = number.locale._mapRegiontoCC(region); 26765 26766 if (countryCode !== "0" && this.regiondata) { 26767 temp = this.regiondata[countryCode]; 26768 if (temp && temp.sn) { 26769 ret.country = { 26770 sn: this.rb.getString(temp.sn).toString(), 26771 ln: this.rb.getString(temp.ln).toString(), 26772 code: this.locale.getRegion() 26773 }; 26774 } 26775 } 26776 } 26777 }) 26778 }); 26779 26780 return ret; 26781 }, 26782 26783 /** 26784 * Returns a string that describes the ISO-3166-2 country code of the given phone 26785 * number.<p> 26786 * 26787 * If the phone number is a local phone number and does not contain 26788 * any country information, this routine will return the region for the current 26789 * formatter instance. 26790 * 26791 * @param {ilib.PhoneNumber} number An ilib.PhoneNumber instance 26792 * @return {string} 26793 */ 26794 country: function(number) { 26795 var countryCode, 26796 region, 26797 phoneLoc; 26798 26799 if (!number || !(number instanceof ilib.PhoneNumber)) { 26800 return ""; 26801 } 26802 26803 phoneLoc = number.locale; 26804 26805 region = (number.countryCode && phoneLoc._mapCCtoRegion(number.countryCode)) || 26806 (number.locale && number.locale.region) || 26807 phoneLoc.locale.getRegion() || 26808 this.locale.getRegion(); 26809 26810 countryCode = number.countryCode || phoneLoc._mapRegiontoCC(region); 26811 26812 if (number.areaCode) { 26813 region = phoneLoc._mapAreatoRegion(countryCode, number.areaCode); 26814 } else if (countryCode === "33" && number.serviceCode) { 26815 // french departments are in the service code, not the area code 26816 region = phoneLoc._mapAreatoRegion(countryCode, number.serviceCode); 26817 } 26818 return region; 26819 } 26820 }; 26821 /* 26822 * unit.js - Unit class 26823 * 26824 * Copyright © 2014, JEDLSoft 26825 * 26826 * Licensed under the Apache License, Version 2.0 (the "License"); 26827 * you may not use this file except in compliance with the License. 26828 * You may obtain a copy of the License at 26829 * 26830 * http://www.apache.org/licenses/LICENSE-2.0 26831 * 26832 * Unless required by applicable law or agreed to in writing, software 26833 * distributed under the License is distributed on an "AS IS" BASIS, 26834 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26835 * 26836 * See the License for the specific language governing permissions and 26837 * limitations under the License. 26838 */ 26839 26840 /* 26841 !depends 26842 ilibglobal.js 26843 locale.js 26844 localeinfo.js 26845 */ 26846 26847 26848 /** 26849 * @class 26850 * Create a measurement instance. The measurement is immutable once 26851 * it is created, but can be converted to other measurements later.<p> 26852 * 26853 * The options may contain any of the following properties: 26854 * 26855 * <ul> 26856 * <li><i>amount</i> - either a numeric amount for this measurement given 26857 * as a number of the specified units, or another ilib.Measurement instance 26858 * to convert to the requested units. If converting to new units, the type 26859 * of measure between the other instance's units and the current units 26860 * must be the same. That is, you can only convert one unit of mass to 26861 * another. You cannot convert a unit of mass into a unit of length. 26862 * 26863 * <li><i>unit</i> - units of this measurement. Use the 26864 * static call {@link ilib.Measurement.getAvailableUnits} 26865 * to find out what units this version of ilib supports. If the given unit 26866 * is not a base unit, the amount will be normalized to the number of base units 26867 * and stored as that number of base units. 26868 * For example, if an instance is constructed with 1 kg, this will be converted 26869 * automatically into 1000 g, as grams are the base unit and kg is merely a 26870 * commonly used scale of grams. 26871 * </ul> 26872 * 26873 * Here are some examples of converting a length into new units. The first method 26874 * is via the constructor by passing the old measurement in as the amount property. 26875 * 26876 * <pre> 26877 * var measurement1 = new ilib.Measurement({ 26878 * amount: 5, 26879 * units: "kilometers" 26880 * }); 26881 * var measurement2 = new ilib.Measurement({ 26882 * amount: measurement1, 26883 * units: "miles" 26884 * }); 26885 * </pre> 26886 * 26887 * The value in measurement2 will end up being about 3.125 miles. 26888 * 26889 * The second method will be using the convert method. 26890 * 26891 * <pre> 26892 * var measurement1 = new ilib.Measurement({ 26893 * amount: 5, 26894 * units: "kilometers" 26895 * }); 26896 * var measurement2 = measurement1.convert("miles"); 26897 * }); 26898 * </pre> 26899 * 26900 * The value in measurement2 will again end up being about 3.125 miles. 26901 * 26902 * @constructor 26903 * @param {Object} options options that control the construction of this instance 26904 */ 26905 ilib.Measurement = function(options) { 26906 if (!options || typeof(options.unit) === 'undefined') { 26907 return undefined; 26908 } 26909 26910 this.amount = options.amount || 0; 26911 var measure = undefined; 26912 26913 for (var c in ilib.Measurement._constructors) { 26914 var measurement = ilib.Measurement._constructors[c]; 26915 if (typeof(measurement.aliases[options.unit]) !== 'undefined') { 26916 measure = c; 26917 break; 26918 } 26919 } 26920 26921 if (!measure || typeof(measure) === 'undefined') { 26922 return new ilib.Measurement.Unknown({ 26923 unit: options.unit, 26924 amount: options.amount 26925 }); 26926 } else { 26927 return new ilib.Measurement._constructors[measure](options); 26928 } 26929 }; 26930 26931 /** 26932 * @private 26933 */ 26934 ilib.Measurement._constructors = {}; 26935 26936 /** 26937 * Return a list of all possible units that this version of ilib supports. 26938 * Typically, the units are given as their full names in English. Unit names 26939 * are case-insensitive. 26940 * 26941 * @static 26942 * @return {Array.<string>} an array of strings containing names of units available 26943 */ 26944 ilib.Measurement.getAvailableUnits = function () { 26945 var units = []; 26946 for (var c in ilib.Measurement._constructors) { 26947 var measure = ilib.Measurement._constructors[c]; 26948 units = units.concat(measure.getMeasures()); 26949 } 26950 return units; 26951 }; 26952 26953 ilib.Measurement.metricScales = { 26954 "femto": {"symbol": "f", "scale": -15}, 26955 "pico": {"symbol": "p", "scale": -12}, 26956 "nano": {"symbol": "n", "scale": -9}, 26957 "micro": {"symbol": "µ", "scale": -6}, 26958 "milli": {"symbol": "m", "scale": -3}, 26959 "centi": {"symbol": "c", "scale": -2}, 26960 "deci": {"symbol": "d", "scale": -1}, 26961 "deca": {"symbol": "da", "scale": 1}, 26962 "hecto": {"symbol": "h", "scale": 2}, 26963 "kilo": {"symbol": "k", "scale": 3}, 26964 "mega": {"symbol": "M", "scale": 6}, 26965 "giga": {"symbol": "G", "scale": 9}, 26966 "peta": {"symbol": "P", "scale": 12}, 26967 "exa": {"symbol": "E", "scale": 18} 26968 }; 26969 26970 ilib.Measurement.prototype = { 26971 /** 26972 * Return the normalized name of the given units. If the units are 26973 * not recognized, this method returns its parameter unmodified.<p> 26974 * 26975 * Examples: 26976 * 26977 * <ui> 26978 * <li>"metres" gets normalized to "meter"<br> 26979 * <li>"ml" gets normalized to "milliliter"<br> 26980 * <li>"foobar" gets normalized to "foobar" (no change because it is not recognized) 26981 * </ul> 26982 * 26983 * @param {string} name name of the units to normalize. 26984 * @returns {string} normalized name of the units 26985 */ 26986 normalizeUnits: function(name) { 26987 return this.aliases[name] || name; 26988 }, 26989 26990 /** 26991 * Return the normalized units used in this measurement. 26992 * @return {string} name of the unit of measurement 26993 */ 26994 getUnit: function() { 26995 return this.unit; 26996 }, 26997 26998 /** 26999 * Return the units originally used to construct this measurement 27000 * before it was normalized. 27001 * @return {string} name of the unit of measurement 27002 */ 27003 getOriginalUnit: function() { 27004 return this.originalUnit; 27005 }, 27006 27007 /** 27008 * Return the numeric amount of this measurement. 27009 * @return {number} the numeric amount of this measurement 27010 */ 27011 getAmount: function() { 27012 return this.amount; 27013 }, 27014 27015 /** 27016 * Return the type of this measurement. Examples are "mass", 27017 * "length", "speed", etc. Measurements can only be converted 27018 * to measurements of the same type.<p> 27019 * 27020 * The type of the units is determined automatically from the 27021 * units. For example, the unit "grams" is type "mass". Use the 27022 * static call {@link ilib.Measurement.getAvailableUnits} 27023 * to find out what units this version of ilib supports. 27024 * 27025 * @abstract 27026 * @return {string} the name of the type of this measurement 27027 */ 27028 getMeasure: function() {}, 27029 27030 /** 27031 * Return a new measurement instance that is converted to a new 27032 * measurement unit. Measurements can only be converted 27033 * to measurements of the same type.<p> 27034 * 27035 * @abstract 27036 * @param {string} to The name of the units to convert to 27037 * @return {ilib.Measurement|undefined} the converted measurement 27038 * or undefined if the requested units are for a different 27039 * measurement type 27040 */ 27041 convert: function(to) {}, 27042 27043 /** 27044 * Scale the measurement unit to an acceptable level. The scaling 27045 * happens so that the integer part of the amount is as small as 27046 * possible without being below zero. This will result in the 27047 * largest units that can represent this measurement without 27048 * fractions. Measurements can only be scaled to other measurements 27049 * of the same type. 27050 * 27051 * @abstract 27052 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 27053 * or undefined if the system can be inferred from the current measure 27054 * @return {ilib.Measurement} a new instance that is scaled to the 27055 * right level 27056 */ 27057 scale: function(measurementsystem) {}, 27058 27059 /** 27060 * Localize the measurement to the commonly used measurement in that locale, for example 27061 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 27062 * the formatted number should be automatically converted to the most appropriate 27063 * measure in the other system, in this case, mph. The formatted result should 27064 * appear as "37.3 mph". 27065 * 27066 * @abstract 27067 * @param {string} locale current locale string 27068 * @returns {ilib.Measurement} a new instance that is converted to locale 27069 */ 27070 localize: function(locale) {} 27071 }; 27072 27073 /* 27074 * unitfmt.js - Unit formatter class 27075 * 27076 * Copyright © 2014, JEDLSoft 27077 * 27078 * Licensed under the Apache License, Version 2.0 (the "License"); 27079 * you may not use this file except in compliance with the License. 27080 * You may obtain a copy of the License at 27081 * 27082 * http://www.apache.org/licenses/LICENSE-2.0 27083 * 27084 * Unless required by applicable law or agreed to in writing, software 27085 * distributed under the License is distributed on an "AS IS" BASIS, 27086 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 27087 * 27088 * See the License for the specific language governing permissions and 27089 * limitations under the License. 27090 */ 27091 27092 /* 27093 !depends 27094 ilibglobal.js 27095 locale.js 27096 resources.js 27097 localeinfo.js 27098 strings.js 27099 */ 27100 27101 // !data unitfmt 27102 27103 /** 27104 * @class 27105 * Create a new unit formatter instance. The unit formatter is immutable once 27106 * it is created, but can format as many different strings with different values 27107 * as needed with the same options. Create different unit formatter instances 27108 * for different purposes and then keep them cached for use later if you have 27109 * more than one unit string to format.<p> 27110 * 27111 * The options may contain any of the following properties: 27112 * 27113 * <ul> 27114 * <li><i>locale</i> - locale to use when formatting the units. The locale also 27115 * controls the translation of the names of the units. If the locale is 27116 * not specified, then the default locale of the app or web page will be used. 27117 * 27118 * <li><i>autoScale</i> - when true, automatically scale the amount to get the smallest 27119 * number greater than 1, where possible, possibly by converting units within the locale's 27120 * measurement system. For example, if the current locale is "en-US", and we have 27121 * a measurement containing 278 fluid ounces, then the number "278" can be scaled down 27122 * by converting the units to a larger one such as gallons. The scaled size would be 27123 * 2.17188 gallons. Since iLib does not have a US customary measure larger than gallons, 27124 * it cannot scale it down any further. If the amount is less than the smallest measure 27125 * already, it cannot be scaled down any further and no autoscaling will be applied. 27126 * Default for the autoScale property is "true", so it only needs to be specified when 27127 * you want to turn off autoscaling. 27128 * 27129 * <li><i>autoConvert</i> - automatically convert the units to the nearest appropriate 27130 * measure of the same type in the measurement system used by the locale. For example, 27131 * if a measurement of length is given in meters, but the current locale is "en-US" 27132 * which uses the US Customary system, then the nearest appropriate measure would be 27133 * "yards", and the amount would be converted from meters to yards automatically before 27134 * being formatted. Default for the autoConvert property is "true", so it only needs to 27135 * be specified when you want to turn off autoconversion. 27136 * 27137 * <li><i>maxFractionDigits</i> - the maximum number of digits that should appear in the 27138 * formatted output after the decimal. A value of -1 means unlimited, and 0 means only print 27139 * the integral part of the number. 27140 * 27141 * <li><i>minFractionDigits</i> - the minimum number of fractional digits that should 27142 * appear in the formatted output. If the number does not have enough fractional digits 27143 * to reach this minimum, the number will be zero-padded at the end to get to the limit. 27144 * 27145 * <li><i>roundingMode</i> - When the maxFractionDigits or maxIntegerDigits is specified, 27146 * this property governs how the least significant digits are rounded to conform to that 27147 * maximum. The value of this property is a string with one of the following values: 27148 * <ul> 27149 * <li><i>up</i> - round away from zero 27150 * <li><i>down</i> - round towards zero. This has the effect of truncating the number 27151 * <li><i>ceiling</i> - round towards positive infinity 27152 * <li><i>floor</i> - round towards negative infinity 27153 * <li><i>halfup</i> - round towards nearest neighbour. If equidistant, round up. 27154 * <li><i>halfdown</i> - round towards nearest neighbour. If equidistant, round down. 27155 * <li><i>halfeven</i> - round towards nearest neighbour. If equidistant, round towards the even neighbour 27156 * <li><i>halfodd</i> - round towards nearest neighbour. If equidistant, round towards the odd neighbour 27157 * </ul> 27158 * 27159 * <li><i>onLoad</i> - a callback function to call when the date format object is fully 27160 * loaded. When the onLoad option is given, the UnitFmt object will attempt to 27161 * load any missing locale data using the ilib loader callback. 27162 * When the constructor is done (even if the data is already preassembled), the 27163 * onLoad function is called with the current instance as a parameter, so this 27164 * callback can be used with preassembled or dynamic loading or a mix of the two. 27165 * 27166 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 27167 * asynchronously. If this option is given as "false", then the "onLoad" 27168 * callback must be given, as the instance returned from this constructor will 27169 * not be usable for a while. 27170 * 27171 * <li><i>loadParams</i> - an object containing parameters to pass to the 27172 * loader callback function when locale data is missing. The parameters are not 27173 * interpretted or modified in any way. They are simply passed along. The object 27174 * may contain any property/value pairs as long as the calling code is in 27175 * agreement with the loader callback function as to what those parameters mean. 27176 * </ul> 27177 * 27178 * Here is an example of how you might use the unit formatter to format a string with 27179 * the correct units.<p> 27180 * 27181 * Depends directive: !depends unitfmt.js 27182 * 27183 * @constructor 27184 * @param {Object} options options governing the way this date formatter instance works 27185 */ 27186 ilib.UnitFmt = function(options) { 27187 var sync = true, 27188 loadParams = undefined; 27189 27190 this.length = "long"; 27191 this.scale = true; 27192 this.measurementType = 'undefined'; 27193 this.convert = true; 27194 this.locale = new ilib.Locale(); 27195 27196 if (options) { 27197 if (options.locale) { 27198 this.locale = (typeof(options.locale) === 'string') ? new ilib.Locale(options.locale) : options.locale; 27199 } 27200 27201 if (typeof(options.sync) === 'boolean') { 27202 sync = options.sync; 27203 } 27204 27205 if (typeof(options.loadParams) !== 'undefined') { 27206 loadParams = options.loadParams; 27207 } 27208 27209 if (options.length) { 27210 this.length = options.length; 27211 } 27212 27213 if (typeof(options.autoScale) === 'boolean') { 27214 this.scale = options.autoScale; 27215 } 27216 27217 if (typeof(options.autoConvert) === 'boolean') { 27218 this.convert = options.autoConvert; 27219 } 27220 27221 if (typeof(options.useNative) === 'boolean') { 27222 this.useNative = options.useNative; 27223 } 27224 27225 if (options.measurementSystem) { 27226 this.measurementSystem = options.measurementSystem; 27227 } 27228 27229 if (typeof (options.maxFractionDigits) === 'number') { 27230 /** 27231 * @private 27232 * @type {number|undefined} 27233 */ 27234 this.maxFractionDigits = options.maxFractionDigits; 27235 } 27236 if (typeof (options.minFractionDigits) === 'number') { 27237 /** 27238 * @private 27239 * @type {number|undefined} 27240 */ 27241 this.minFractionDigits = options.minFractionDigits; 27242 } 27243 /** 27244 * @private 27245 * @type {string} 27246 */ 27247 this.roundingMode = options.roundingMode; 27248 } 27249 27250 if (!ilib.UnitFmt.cache) { 27251 ilib.UnitFmt.cache = {}; 27252 } 27253 27254 ilib.loadData({ 27255 object: ilib.UnitFmt, 27256 locale: this.locale, 27257 name: "unitfmt.json", 27258 sync: sync, 27259 loadParams: loadParams, 27260 callback: ilib.bind(this, function (format) { 27261 var formatted = format; 27262 this.template = formatted["unitfmt"][this.length]; 27263 if (options && typeof(options.onLoad) === 'function') { 27264 options.onLoad(this); 27265 } 27266 }) 27267 }); 27268 }; 27269 27270 ilib.UnitFmt.prototype = { 27271 27272 /** 27273 * Return the locale used with this formatter instance. 27274 * @return {ilib.Locale} the ilib.Locale instance for this formatter 27275 */ 27276 getLocale: function() { 27277 return this.locale; 27278 }, 27279 27280 /** 27281 * Return the template string that is used to format date/times for this 27282 * formatter instance. This will work, even when the template property is not explicitly 27283 * given in the options to the constructor. Without the template option, the constructor 27284 * will build the appropriate template according to the options and use that template 27285 * in the format method. 27286 * 27287 * @return {string} the format template for this formatter 27288 */ 27289 getTemplate: function() { 27290 return this.template; 27291 }, 27292 27293 /** 27294 * Convert this formatter to a string representation by returning the 27295 * format template. This method delegates to getTemplate. 27296 * 27297 * @return {string} the format template 27298 */ 27299 toString: function() { 27300 return this.getTemplate(); 27301 }, 27302 27303 /** 27304 * Return whether or not this formatter will auto-scale the units while formatting. 27305 * @returns {boolean} true if auto-scaling is turned on 27306 */ 27307 getScale: function() { 27308 return this.scale; 27309 }, 27310 27311 /** 27312 * Return the measurement system that is used for this formatter. 27313 * @returns {string} the measurement system used in this formatter 27314 */ 27315 getMeasurementSystem: function() { 27316 return this.measurementSystem; 27317 }, 27318 27319 /** 27320 * Format a particular unit instance according to the settings of this 27321 * formatter object. 27322 * 27323 * @param {ilib.Measurement} measurement measurement to format 27324 * @return {string} the formatted version of the given date instance 27325 */ 27326 format: function (measurement) { 27327 var u = this.convert ? measurement.localize(this.locale.getSpec()) : measurement; 27328 u = this.scale ? u.scale(this.measurementSystem) : u; 27329 var formatted = new ilib.String(this.template[u.getUnit()]); 27330 // make sure to use the right plural rules 27331 formatted.setLocale(this.locale, true, undefined, undefined); 27332 var numFmt = new ilib.NumFmt({ 27333 locale: this.locale, 27334 useNative: this.useNative, 27335 maxFractionDigits: this.maxFractionDigits, 27336 minFractionDigits: this.minFractionDigits, 27337 roundingMode: this.roundingMode 27338 }); 27339 formatted = formatted.formatChoice(u.amount,{n:numFmt.format(u.amount)}); 27340 return formatted.length > 0 ? formatted : u.amount +" " + u.unit; 27341 } 27342 }; 27343 27344 /* 27345 * Length.js - Unit conversions for Lengths/lengths 27346 * 27347 * Copyright © 2014, JEDLSoft 27348 * 27349 * Licensed under the Apache License, Version 2.0 (the "License"); 27350 * you may not use this file except in compliance with the License. 27351 * You may obtain a copy of the License at 27352 * 27353 * http://www.apache.org/licenses/LICENSE-2.0 27354 * 27355 * Unless required by applicable law or agreed to in writing, software 27356 * distributed under the License is distributed on an "AS IS" BASIS, 27357 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 27358 * 27359 * See the License for the specific language governing permissions and 27360 * limitations under the License. 27361 */ 27362 27363 /* 27364 !depends 27365 ilibglobal.js 27366 */ 27367 27368 /** 27369 * @class 27370 * Create a new length measurement instance. 27371 * 27372 * @constructor 27373 * @extends ilib.Measurement 27374 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 27375 * the construction of this instance 27376 */ 27377 ilib.Measurement.Length = function (options) { 27378 this.unit = "meter"; 27379 this.amount = 0; 27380 this.aliases = ilib.Measurement.Length.aliases; // share this table in all instances 27381 27382 if (options) { 27383 if (typeof(options.unit) !== 'undefined') { 27384 this.originalUnit = options.unit; 27385 this.unit = this.aliases[options.unit] || options.unit; 27386 } 27387 27388 if (typeof(options.amount) === 'object') { 27389 if (options.amount.getMeasure() === "length") { 27390 this.amount = ilib.Measurement.Length.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 27391 } else { 27392 throw "Cannot convert unit " + options.amount.unit + " to a length"; 27393 } 27394 } else if (typeof(options.amount) !== 'undefined') { 27395 this.amount = parseFloat(options.amount); 27396 } 27397 } 27398 27399 if (typeof(ilib.Measurement.Length.ratios[this.unit]) === 'undefined') { 27400 throw "Unknown unit: " + options.unit; 27401 } 27402 }; 27403 27404 ilib.Measurement.Length.ratios = { 27405 /* index, µm mm cm inch dm foot yard m dam hm km mile nm Mm Gm */ 27406 "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 ], 27407 "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 ], 27408 "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 ], 27409 "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 ], 27410 "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 ], 27411 "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 ], 27412 "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 ], 27413 "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 ], 27414 "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 ], 27415 "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 ], 27416 "kilometer": [ 11, 1e9, 1e6, 1e5, 39370.1, 1e4, 3280.84, 1093.61, 1000, 100, 10, 1, 0.621373, 0.539957, 0.001, 1e-4 ], 27417 "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 ], 27418 "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 ], 27419 "megameter": [ 14, 1e12, 1e9, 1e6, 3.93701e7, 1e5, 3.28084e6, 1.09361e6, 1e4, 1000, 100, 10, 621.373, 539.957, 1, 0.001 ], 27420 "gigameter": [ 15, 1e15, 1e12, 1e9, 3.93701e10, 1e8, 3.28084e9, 1.09361e9, 1e7, 1e6, 1e5, 1e4, 621373.0, 539957.0, 1000, 1 ] 27421 }; 27422 27423 ilib.Measurement.Length.metricSystem = { 27424 "micrometer": 1, 27425 "millimeter": 2, 27426 "centimeter": 3, 27427 "decimeter": 5, 27428 "meter": 8, 27429 "decameter": 9, 27430 "hectometer": 10, 27431 "kilometer": 11, 27432 "megameter": 14, 27433 "gigameter": 15 27434 }; 27435 ilib.Measurement.Length.imperialSystem = { 27436 "inch": 4, 27437 "foot": 6, 27438 "yard": 7, 27439 "mile": 12, 27440 "nauticalmile": 13 27441 }; 27442 ilib.Measurement.Length.uscustomarySystem = { 27443 "inch": 4, 27444 "foot": 6, 27445 "yard": 7, 27446 "mile": 12, 27447 "nauticalmile": 13 27448 }; 27449 27450 ilib.Measurement.Length.metricToUScustomary = { 27451 "micrometer": "inch", 27452 "millimeter": "inch", 27453 "centimeter": "inch", 27454 "decimeter": "inch", 27455 "meter": "yard", 27456 "decameter": "yard", 27457 "hectometer": "mile", 27458 "kilometer": "mile", 27459 "megameter": "nauticalmile", 27460 "gigameter": "nauticalmile" 27461 }; 27462 ilib.Measurement.Length.usCustomaryToMetric = { 27463 "inch": "centimeter", 27464 "foot": "centimeter", 27465 "yard": "meter", 27466 "mile": "kilometer", 27467 "nauticalmile": "kilometer" 27468 }; 27469 27470 ilib.Measurement.Length.prototype = new ilib.Measurement({}); 27471 ilib.Measurement.Length.prototype.parent = ilib.Measurement; 27472 ilib.Measurement.Length.prototype.constructor = ilib.Measurement.Length; 27473 27474 /** 27475 * Return the type of this measurement. Examples are "mass", 27476 * "length", "speed", etc. Measurements can only be converted 27477 * to measurements of the same type.<p> 27478 * 27479 * The type of the units is determined automatically from the 27480 * units. For example, the unit "grams" is type "mass". Use the 27481 * static call {@link ilib.Measurement.getAvailableUnits} 27482 * to find out what units this version of ilib supports. 27483 * 27484 * @return {string} the name of the type of this measurement 27485 */ 27486 ilib.Measurement.Length.prototype.getMeasure = function() { 27487 return "length"; 27488 }; 27489 27490 /** 27491 * Localize the measurement to the commonly used measurement in that locale. For example 27492 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 27493 * the formatted number should be automatically converted to the most appropriate 27494 * measure in the other system, in this case, mph. The formatted result should 27495 * appear as "37.3 mph". 27496 * 27497 * @abstract 27498 * @param {string} locale current locale string 27499 * @returns {ilib.Measurement} a new instance that is converted to locale 27500 */ 27501 ilib.Measurement.Length.prototype.localize = function(locale) { 27502 var to; 27503 if (locale === "en-US" || locale === "en-GB") { 27504 to = ilib.Measurement.Length.metricToUScustomary[this.unit] || this.unit; 27505 } else { 27506 to = ilib.Measurement.Length.usCustomaryToMetric[this.unit] || this.unit; 27507 } 27508 return new ilib.Measurement.Length({ 27509 unit: to, 27510 amount: this 27511 }); 27512 }; 27513 27514 /** 27515 * Return a new measurement instance that is converted to a new 27516 * measurement unit. Measurements can only be converted 27517 * to measurements of the same type.<p> 27518 * 27519 * @param {string} to The name of the units to convert to 27520 * @return {ilib.Measurement|undefined} the converted measurement 27521 * or undefined if the requested units are for a different 27522 * measurement type 27523 */ 27524 ilib.Measurement.Length.prototype.convert = function(to) { 27525 if (!to || typeof(ilib.Measurement.Length.ratios[this.normalizeUnits(to)]) === 'undefined') { 27526 return undefined; 27527 } 27528 return new ilib.Measurement({ 27529 unit: to, 27530 amount: this 27531 }); 27532 }; 27533 27534 /** 27535 * Scale the measurement unit to an acceptable level. The scaling 27536 * happens so that the integer part of the amount is as small as 27537 * possible without being below zero. This will result in the 27538 * largest units that can represent this measurement without 27539 * fractions. Measurements can only be scaled to other measurements 27540 * of the same type. 27541 * 27542 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 27543 * or undefined if the system can be inferred from the current measure 27544 * @return {ilib.Measurement} a new instance that is scaled to the 27545 * right level 27546 */ 27547 ilib.Measurement.Length.prototype.scale = function(measurementsystem) { 27548 var mSystem; 27549 if (measurementsystem === "metric" || (typeof(measurementsystem) === 'undefined' 27550 && typeof(ilib.Measurement.Length.metricSystem[this.unit]) !== 'undefined')) { 27551 mSystem = ilib.Measurement.Length.metricSystem; 27552 } else if (measurementsystem === "imperial" || (typeof(measurementsystem) === 'undefined' 27553 && typeof(ilib.Measurement.Length.imperialSystem[this.unit]) !== 'undefined')) { 27554 mSystem = ilib.Measurement.Length.imperialSystem; 27555 } else if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined' 27556 && typeof(ilib.Measurement.Length.uscustomarySystem[this.unit]) !== 'undefined')) { 27557 mSystem = ilib.Measurement.Length.uscustomarySystem; 27558 } else { 27559 return new ilib.Measurement.Length({ 27560 unit: this.unit, 27561 amount: this.amount 27562 }); 27563 } 27564 27565 var length = this.amount; 27566 var munit = this.unit; 27567 var fromRow = ilib.Measurement.Length.ratios[this.unit]; 27568 27569 for (var m in mSystem) { 27570 var tmp = this.amount * fromRow[mSystem[m]]; 27571 if (tmp < 1) break; 27572 length = tmp; 27573 munit = m; 27574 } 27575 27576 return new ilib.Measurement.Length({ 27577 unit: munit, 27578 amount: length 27579 }); 27580 }; 27581 27582 ilib.Measurement.Length.aliases = { 27583 "miles": "mile", 27584 "mile":"mile", 27585 "nauticalmiles": "nauticalmile", 27586 "nautical mile": "nauticalmile", 27587 "nautical miles": "nauticalmile", 27588 "nauticalmile":"nauticalmile", 27589 "yards": "yard", 27590 "yard": "yard", 27591 "feet": "foot", 27592 "foot": "foot", 27593 "inches": "inch", 27594 "inch": "inch", 27595 "meters": "meter", 27596 "metre": "meter", 27597 "metres": "meter", 27598 "m": "meter", 27599 "meter": "meter", 27600 "micrometers": "micrometer", 27601 "micrometres": "micrometer", 27602 "micrometre": "micrometer", 27603 "µm": "micrometer", 27604 "micrometer": "micrometer", 27605 "millimeters": "millimeter", 27606 "millimetres": "millimeter", 27607 "millimetre": "millimeter", 27608 "mm": "millimeter", 27609 "millimeter": "millimeter", 27610 "centimeters": "centimeter", 27611 "centimetres": "centimeter", 27612 "centimetre": "centimeter", 27613 "cm": "centimeter", 27614 "centimeter": "centimeter", 27615 "decimeters": "decimeter", 27616 "decimetres": "decimeter", 27617 "decimetre": "decimeter", 27618 "dm": "decimeter", 27619 "decimeter": "decimeter", 27620 "decameters": "decameter", 27621 "decametres": "decameter", 27622 "decametre": "decameter", 27623 "dam": "decameter", 27624 "decameter": "decameter", 27625 "hectometers": "hectometer", 27626 "hectometres": "hectometer", 27627 "hectometre": "hectometer", 27628 "hm": "hectometer", 27629 "hectometer": "hectometer", 27630 "kilometers": "kilometer", 27631 "kilometres": "kilometer", 27632 "kilometre": "kilometer", 27633 "km": "kilometer", 27634 "kilometer": "kilometer", 27635 "megameters": "megameter", 27636 "megametres": "megameter", 27637 "megametre": "megameter", 27638 "Mm": "megameter", 27639 "megameter": "megameter", 27640 "gigameters": "gigameter", 27641 "gigametres": "gigameter", 27642 "gigametre": "gigameter", 27643 "Gm": "gigameter", 27644 "gigameter": "gigameter" 27645 }; 27646 27647 /** 27648 * Convert a length to another measure. 27649 * @static 27650 * @param to {string} unit to convert to 27651 * @param from {string} unit to convert from 27652 * @param length {number} amount to be convert 27653 * @returns {number|undefined} the converted amount 27654 */ 27655 ilib.Measurement.Length.convert = function(to, from, length) { 27656 from = ilib.Measurement.Length.aliases[from] || from; 27657 to = ilib.Measurement.Length.aliases[to] || to; 27658 var fromRow = ilib.Measurement.Length.ratios[from]; 27659 var toRow = ilib.Measurement.Length.ratios[to]; 27660 if (typeof(from) === 'undefined' || typeof(to) === 'undefined') { 27661 return undefined; 27662 } 27663 return length * fromRow[toRow[0]]; 27664 }; 27665 27666 /** 27667 * @private 27668 * @static 27669 */ 27670 ilib.Measurement.Length.getMeasures = function () { 27671 var ret = []; 27672 for (var m in ilib.Measurement.Length.ratios) { 27673 ret.push(m); 27674 } 27675 return ret; 27676 }; 27677 27678 //register with the factory method 27679 ilib.Measurement._constructors["length"] = ilib.Measurement.Length; 27680 27681 /* 27682 * Speed.js - Unit conversions for Speeds/speeds 27683 * 27684 * Copyright © 2014, JEDLSoft 27685 * 27686 * Licensed under the Apache License, Version 2.0 (the "License"); 27687 * you may not use this file except in compliance with the License. 27688 * You may obtain a copy of the License at 27689 * 27690 * http://www.apache.org/licenses/LICENSE-2.0 27691 * 27692 * Unless required by applicable law or agreed to in writing, software 27693 * distributed under the License is distributed on an "AS IS" BASIS, 27694 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 27695 * 27696 * See the License for the specific language governing permissions and 27697 * limitations under the License. 27698 */ 27699 27700 /* 27701 !depends 27702 ilibglobal.js 27703 unit.js 27704 */ 27705 27706 /** 27707 * @class 27708 * Create a new speed measurement instance. 27709 * 27710 * @constructor 27711 * @extends ilib.Measurement 27712 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 27713 * the construction of this instance 27714 */ 27715 ilib.Measurement.Speed = function (options) { 27716 this.unit = "meters/second"; 27717 this.amount = 0; 27718 this.aliases = ilib.Measurement.Speed.aliases; // share this table in all instances 27719 27720 if (options) { 27721 if (typeof(options.unit) !== 'undefined') { 27722 this.originalUnit = options.unit; 27723 this.unit = this.aliases[options.unit] || options.unit; 27724 } 27725 27726 if (typeof(options.amount) === 'object') { 27727 if (options.amount.getMeasure() === "speed") { 27728 this.amount = ilib.Measurement.Speed.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 27729 } else { 27730 throw "Cannot convert units " + options.amount.unit + " to a speed"; 27731 } 27732 } else if (typeof(options.amount) !== 'undefined') { 27733 this.amount = parseFloat(options.amount); 27734 } 27735 } 27736 27737 if (typeof(ilib.Measurement.Speed.ratios[this.unit]) === 'undefined') { 27738 throw "Unknown unit: " + options.unit; 27739 } 27740 }; 27741 27742 ilib.Measurement.Speed.ratios = { 27743 /* index, k/h f/s miles/h knot m/s km/s miles/s */ 27744 "kilometer/hour": [ 1, 1, 0.911344, 0.621371, 0.539957, 0.277778, 2.77778e-4, 1.72603109e-4 ], 27745 "feet/second": [ 2, 1.09728, 1, 0.681818, 0.592484, 0.3048, 3.048e-4, 1.89393939e-4 ], 27746 "miles/hour": [ 3, 1.60934, 1.46667, 1, 0.868976, 0.44704, 4.4704e-4, 2.77777778e-4 ], 27747 "knot": [ 4, 1.852, 1.68781, 1.15078, 1, 0.514444, 5.14444e-4, 3.19660958e-4 ], 27748 "meters/second": [ 5, 3.6, 3.28084, 2.236936, 1.94384, 1, 0.001, 6.21371192e-4 ], 27749 "kilometer/second": [ 6, 3600, 3280.8399, 2236.93629, 1943.84449, 1000, 1, 0.621371192 ], 27750 "miles/second": [ 7, 5793.6384, 5280, 3600, 3128.31447, 1609.344, 1.609344, 1 ] 27751 }; 27752 27753 ilib.Measurement.Speed.metricSystem = { 27754 "kilometer/hour": 1, 27755 "meters/second": 5, 27756 "kilometer/second": 6 27757 }; 27758 ilib.Measurement.Speed.imperialSystem = { 27759 "feet/second": 2, 27760 "miles/hour": 3, 27761 "knot": 4, 27762 "miles/second": 7 27763 }; 27764 ilib.Measurement.Speed.uscustomarySystem = { 27765 "feet/second": 2, 27766 "miles/hour": 3, 27767 "knot": 4, 27768 "miles/second": 7 27769 }; 27770 27771 ilib.Measurement.Speed.metricToUScustomary = { 27772 "kilometer/hour": "miles/hour", 27773 "meters/second": "feet/second", 27774 "kilometer/second": "miles/second" 27775 }; 27776 ilib.Measurement.Speed.UScustomaryTometric = { 27777 "miles/hour": "kilometer/hour", 27778 "feet/second": "meters/second", 27779 "miles/second": "kilometer/second", 27780 "knot": "kilometer/hour" 27781 }; 27782 27783 ilib.Measurement.Speed.prototype = new ilib.Measurement({}); 27784 ilib.Measurement.Speed.prototype.parent = ilib.Measurement; 27785 ilib.Measurement.Speed.prototype.constructor = ilib.Measurement.Speed; 27786 27787 /** 27788 * Return the type of this measurement. Examples are "mass", 27789 * "length", "speed", etc. Measurements can only be converted 27790 * to measurements of the same type.<p> 27791 * 27792 * The type of the units is determined automatically from the 27793 * units. For example, the unit "grams" is type "mass". Use the 27794 * static call {@link ilib.Measurement.getAvailableUnits} 27795 * to find out what units this version of ilib supports. 27796 * 27797 * @return {string} the name of the type of this measurement 27798 */ 27799 ilib.Measurement.Speed.prototype.getMeasure = function() { 27800 return "speed"; 27801 }; 27802 27803 /** 27804 * Return a new measurement instance that is converted to a new 27805 * measurement unit. Measurements can only be converted 27806 * to measurements of the same type.<p> 27807 * 27808 * @param {string} to The name of the units to convert to 27809 * @return {ilib.Measurement|undefined} the converted measurement 27810 * or undefined if the requested units are for a different 27811 * measurement type 27812 */ 27813 ilib.Measurement.Speed.prototype.convert = function(to) { 27814 if (!to || typeof(ilib.Measurement.Speed.ratios[this.normalizeUnits(to)]) === 'undefined') { 27815 return undefined; 27816 } 27817 return new ilib.Measurement({ 27818 unit: to, 27819 amount: this 27820 }); 27821 }; 27822 27823 /** 27824 * Scale the measurement unit to an acceptable level. The scaling 27825 * happens so that the integer part of the amount is as small as 27826 * possible without being below zero. This will result in the 27827 * largest units that can represent this measurement without 27828 * fractions. Measurements can only be scaled to other measurements 27829 * of the same type. 27830 * 27831 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 27832 * or undefined if the system can be inferred from the current measure 27833 * @return {ilib.Measurement} a new instance that is scaled to the 27834 * right level 27835 */ 27836 ilib.Measurement.Speed.prototype.scale = function(measurementsystem) { 27837 var mSystem; 27838 if (measurementsystem === "metric" || 27839 (typeof (measurementsystem) === 'undefined' && typeof (ilib.Measurement.Speed.metricSystem[this.unit]) !== 'undefined')) { 27840 mSystem = ilib.Measurement.Speed.metricSystem; 27841 } else if (measurementsystem === "imperial" || 27842 (typeof (measurementsystem) === 'undefined' && typeof (ilib.Measurement.Speed.imperialSystem[this.unit]) !== 'undefined')) { 27843 mSystem = ilib.Measurement.Speed.imperialSystem; 27844 } else if (measurementsystem === "uscustomary" || 27845 (typeof (measurementsystem) === 'undefined' && typeof (ilib.Measurement.Speed.uscustomarySystem[this.unit]) !== 'undefined')) { 27846 mSystem = ilib.Measurement.Speed.uscustomarySystem; 27847 } else { 27848 return new ilib.Measurement.Speed({ 27849 unit: this.unit, 27850 amount: this.amount 27851 }); 27852 } 27853 27854 var speed = this.amount; 27855 var munit = this.unit; 27856 var fromRow = ilib.Measurement.Speed.ratios[this.unit]; 27857 27858 for ( var m in mSystem) { 27859 var tmp = this.amount * fromRow[mSystem[m]]; 27860 if (tmp < 1) 27861 break; 27862 speed = tmp; 27863 munit = m; 27864 } 27865 27866 return new ilib.Measurement.Speed({ 27867 unit: munit, 27868 amount: speed 27869 }); 27870 }; 27871 27872 /** 27873 * Localize the measurement to the commonly used measurement in that locale. For example 27874 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 27875 * the formatted number should be automatically converted to the most appropriate 27876 * measure in the other system, in this case, mph. The formatted result should 27877 * appear as "37.3 mph". 27878 * 27879 * @abstract 27880 * @param {string} locale current locale string 27881 * @returns {ilib.Measurement} a new instance that is converted to locale 27882 */ 27883 ilib.Measurement.Speed.prototype.localize = function(locale) { 27884 var to; 27885 if (locale === "en-US" || locale === "en-GB") { 27886 to = ilib.Measurement.Speed.metricToUScustomary[this.unit] || this.unit; 27887 } else { 27888 to = ilib.Measurement.Speed.UScustomaryTometric[this.unit] || this.unit; 27889 } 27890 return new ilib.Measurement.Speed({ 27891 unit: to, 27892 amount: this 27893 }); 27894 }; 27895 27896 ilib.Measurement.Speed.aliases = { 27897 "foot/sec": "feet/second", 27898 "foot/s": "feet/second", 27899 "feet/s": "feet/second", 27900 "f/s": "feet/second", 27901 "feet/second": "feet/second", 27902 "feet/sec": "feet/second", 27903 "meter/sec": "meters/second", 27904 "meter/s": "meters/second", 27905 "meters/s": "meters/second", 27906 "metre/sec": "meters/second", 27907 "metre/s": "meters/second", 27908 "metres/s": "meters/second", 27909 "mt/sec": "meters/second", 27910 "m/sec": "meters/second", 27911 "mt/s": "meters/second", 27912 "m/s": "meters/second", 27913 "mps": "meters/second", 27914 "meters/second": "meters/second", 27915 "meters/sec": "meters/second", 27916 "kilometer/hour": "kilometer/hour", 27917 "km/hour": "kilometer/hour", 27918 "kilometers/hour": "kilometer/hour", 27919 "kmh": "kilometer/hour", 27920 "km/h": "kilometer/hour", 27921 "kilometer/h": "kilometer/hour", 27922 "kilometers/h": "kilometer/hour", 27923 "km/hr": "kilometer/hour", 27924 "kilometer/hr": "kilometer/hour", 27925 "kilometers/hr": "kilometer/hour", 27926 "kilometre/hour": "kilometer/hour", 27927 "mph": "miles/hour", 27928 "mile/hour": "miles/hour", 27929 "mile/hr": "miles/hour", 27930 "mile/h": "miles/hour", 27931 "miles/h": "miles/hour", 27932 "miles/hr": "miles/hour", 27933 "miles/hour": "miles/hour", 27934 "kn": "knot", 27935 "kt": "knot", 27936 "kts": "knot", 27937 "knots": "knot", 27938 "nm/h": "knot", 27939 "nm/hr": "knot", 27940 "nauticalmile/h": "knot", 27941 "nauticalmile/hr": "knot", 27942 "nauticalmile/hour": "knot", 27943 "nauticalmiles/hr": "knot", 27944 "nauticalmiles/hour": "knot", 27945 "knot": "knot", 27946 "kilometer/second": "kilometer/second", 27947 "kilometer/sec": "kilometer/second", 27948 "kilometre/sec": "kilometer/second", 27949 "Kilometre/sec": "kilometer/second", 27950 "kilometers/second": "kilometer/second", 27951 "kilometers/sec": "kilometer/second", 27952 "kilometres/sec": "kilometer/second", 27953 "Kilometres/sec": "kilometer/second", 27954 "km/sec": "kilometer/second", 27955 "Km/s": "kilometer/second", 27956 "km/s": "kilometer/second", 27957 "miles/second": "miles/second", 27958 "miles/sec": "miles/second", 27959 "miles/s": "miles/second", 27960 "mile/s": "miles/second", 27961 "mile/sec": "miles/second", 27962 "Mile/s": "miles/second" 27963 }; 27964 27965 /** 27966 * Convert a speed to another measure. 27967 * @static 27968 * @param to {string} unit to convert to 27969 * @param from {string} unit to convert from 27970 * @param speed {number} amount to be convert 27971 * @returns {number|undefined} the converted amount 27972 */ 27973 ilib.Measurement.Speed.convert = function(to, from, speed) { 27974 from = ilib.Measurement.Speed.aliases[from] || from; 27975 to = ilib.Measurement.Speed.aliases[to] || to; 27976 var fromRow = ilib.Measurement.Speed.ratios[from]; 27977 var toRow = ilib.Measurement.Speed.ratios[to]; 27978 if (typeof(from) === 'undefined' || typeof(to) === 'undefined') { 27979 return undefined; 27980 } 27981 var result = speed * fromRow[toRow[0]]; 27982 return result; 27983 }; 27984 27985 /** 27986 * @private 27987 * @static 27988 */ 27989 ilib.Measurement.Speed.getMeasures = function () { 27990 var ret = []; 27991 for (var m in ilib.Measurement.Speed.ratios) { 27992 ret.push(m); 27993 } 27994 return ret; 27995 }; 27996 27997 //register with the factory method 27998 ilib.Measurement._constructors["speed"] = ilib.Measurement.Speed; 27999 28000 /* 28001 * digitalStorage.js - Unit conversions for Digital Storage 28002 * 28003 * Copyright © 2014, JEDLSoft 28004 * 28005 * Licensed under the Apache License, Version 2.0 (the "License"); 28006 * you may not use this file except in compliance with the License. 28007 * You may obtain a copy of the License at 28008 * 28009 * http://www.apache.org/licenses/LICENSE-2.0 28010 * 28011 * Unless required by applicable law or agreed to in writing, software 28012 * distributed under the License is distributed on an "AS IS" BASIS, 28013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28014 * 28015 * See the License for the specific language governing permissions and 28016 * limitations under the License. 28017 */ 28018 28019 /* 28020 !depends 28021 ilibglobal.js 28022 unit.js 28023 */ 28024 28025 /** 28026 * @class 28027 * Create a new DigitalStorage measurement instance. 28028 * 28029 * @constructor 28030 * @extends ilib.Measurement 28031 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 28032 * the construction of this instance 28033 */ 28034 ilib.Measurement.DigitalStorage = function (options) { 28035 this.unit = "bit"; 28036 this.amount = 0; 28037 this.aliases = ilib.Measurement.DigitalStorage.aliases; // share this table in all instances 28038 28039 if (options) { 28040 if (typeof(options.unit) !== 'undefined') { 28041 this.originalUnit = options.unit; 28042 this.unit = this.aliases[options.unit] || options.unit; 28043 } 28044 28045 if (typeof(options.amount) === 'object') { 28046 if (options.amount.getMeasure() === "digitalStorage") { 28047 this.amount = ilib.Measurement.DigitalStorage.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 28048 } else { 28049 throw "Cannot convert unit " + options.amount.unit + " to a digitalStorage"; 28050 } 28051 } else if (typeof(options.amount) !== 'undefined') { 28052 this.amount = parseFloat(options.amount); 28053 } 28054 } 28055 28056 if (typeof(ilib.Measurement.DigitalStorage.ratios[this.unit]) === 'undefined') { 28057 throw "Unknown unit: " + options.unit; 28058 } 28059 }; 28060 28061 ilib.Measurement.DigitalStorage.ratios = { 28062 /* bit byte kb kB mb mB gb gB tb tB pb pB */ 28063 "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 ], 28064 "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 ], 28065 "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 ], 28066 "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 ], 28067 "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 ], 28068 "megabyte": [ 6, 8388608, 1048576, 8192, 1024, 8, 1, 0.0078125, 0.0009765625, 7.629394531e-6, 9.536743164e-7, 7.450580597e-9, 9.313225746e-10 ], 28069 "gigabit": [ 7, 1073741824, 134217728, 1048576, 131072, 1024, 128, 1, 0.125, 0.0009765625, 1.220703125e-4, 9.536743164e-7, 1.192092896e-7 ], 28070 "gigabyte": [ 8, 8589934592, 1073741824, 8388608, 1048576, 8192, 1024, 8, 1, 0.0078125, 0.0009765625, 7.629394531e-6, 9.536743164e-7 ], 28071 "terabit": [ 9, 1.099511628e12, 137438953472, 1073741824, 134217728, 1048576, 131072, 1024, 128, 1, 0.125, 0.0009765625, 1.220703125e-4 ], 28072 "terabyte": [ 10, 8.796093022e12, 1.099511628e12, 8589934592, 1073741824, 8388608, 1048576, 8192, 1024, 8, 1, 0.0078125, 0.0009765625 ], 28073 "petabit": [ 11, 1.125899907e15, 1.407374884e14, 1.099511628e12, 137438953472, 1073741824, 134217728, 1048576, 131072, 1024, 128, 1, 0.125 ], 28074 "petabyte": [ 12, 9.007199255e15, 1.125899907e15, 8.796093022e12, 1.099511628e12, 8589934592, 1073741824, 8388608, 1048576, 8192, 1024, 8, 1 ] 28075 }; 28076 ilib.Measurement.DigitalStorage.prototype = new ilib.Measurement({}); 28077 ilib.Measurement.DigitalStorage.prototype.parent = ilib.Measurement; 28078 ilib.Measurement.DigitalStorage.prototype.constructor = ilib.Measurement.DigitalStorage; 28079 28080 /** 28081 * Return the type of this measurement. Examples are "mass", 28082 * "length", "speed", etc. Measurements can only be converted 28083 * to measurements of the same type.<p> 28084 * 28085 * The type of the units is determined automatically from the 28086 * units. For example, the unit "grams" is type "mass". Use the 28087 * static call {@link ilib.Measurement.getAvailableUnits} 28088 * to find out what units this version of ilib supports. 28089 * 28090 * @return {string} the name of the type of this measurement 28091 */ 28092 ilib.Measurement.DigitalStorage.prototype.getMeasure = function() { 28093 return "digitalStorage"; 28094 }; 28095 28096 /** 28097 * Return a new measurement instance that is converted to a new 28098 * measurement unit. Measurements can only be converted 28099 * to measurements of the same type.<p> 28100 * 28101 * @param {string} to The name of the units to convert to 28102 * @return {ilib.Measurement|undefined} the converted measurement 28103 * or undefined if the requested units are for a different 28104 * measurement type 28105 * 28106 */ 28107 ilib.Measurement.DigitalStorage.prototype.convert = function(to) { 28108 if (!to || typeof(ilib.Measurement.DigitalStorage.ratios[this.normalizeUnits(to)]) === 'undefined') { 28109 return undefined; 28110 } 28111 return new ilib.Measurement({ 28112 unit: to, 28113 amount: this 28114 }); 28115 }; 28116 28117 /** 28118 * Localize the measurement to the commonly used measurement in that locale. For example 28119 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 28120 * the formatted number should be automatically converted to the most appropriate 28121 * measure in the other system, in this case, mph. The formatted result should 28122 * appear as "37.3 mph". 28123 * 28124 * @abstract 28125 * @param {string} locale current locale string 28126 * @returns {ilib.Measurement} a new instance that is converted to locale 28127 */ 28128 ilib.Measurement.DigitalStorage.prototype.localize = function(locale) { 28129 return new ilib.Measurement.DigitalStorage({ 28130 unit: this.unit, 28131 amount: this.amount 28132 }); 28133 }; 28134 28135 /** 28136 * Scale the measurement unit to an acceptable level. The scaling 28137 * happens so that the integer part of the amount is as small as 28138 * possible without being below zero. This will result in the 28139 * largest units that can represent this measurement without 28140 * fractions. Measurements can only be scaled to other measurements 28141 * of the same type. 28142 * 28143 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 28144 * or undefined if the system can be inferred from the current measure 28145 * @return {ilib.Measurement} a new instance that is scaled to the 28146 * right level 28147 */ 28148 ilib.Measurement.DigitalStorage.prototype.scale = function(measurementsystem) { 28149 28150 var fromRow = ilib.Measurement.DigitalStorage.ratios[this.unit]; 28151 var dStorage = this.amount; 28152 var munit = this.unit; 28153 var i=1; 28154 28155 for (var m in ilib.Measurement.DigitalStorage.ratios) { 28156 var tmp = this.amount * fromRow[i]; 28157 if (tmp < 1) break; 28158 dStorage = tmp; 28159 munit = m; 28160 ++i 28161 } 28162 28163 return new ilib.Measurement.DigitalStorage({ 28164 unit: munit, 28165 amount: dStorage 28166 }); 28167 }; 28168 28169 ilib.Measurement.DigitalStorage.aliases = { 28170 "bits": "bit", 28171 "bit": "bit", 28172 "Bits": "bit", 28173 "Bit": "bit", 28174 "byte": "byte", 28175 "bytes": "byte", 28176 "Byte": "byte", 28177 "Bytes": "byte", 28178 "kilobits": "kilobit", 28179 "Kilobits": "kilobit", 28180 "KiloBits": "kilobit", 28181 "kiloBits": "kilobit", 28182 "kilobit": "kilobit", 28183 "Kilobit": "kilobit", 28184 "kiloBit": "kilobit", 28185 "KiloBit": "kilobit", 28186 "kb": "kilobit", 28187 "Kb": "kilobit", 28188 "kilobyte": "kilobyte", 28189 "Kilobyte": "kilobyte", 28190 "kiloByte": "kilobyte", 28191 "KiloByte": "kilobyte", 28192 "kilobytes": "kilobyte", 28193 "Kilobytes": "kilobyte", 28194 "kiloBytes": "kilobyte", 28195 "KiloBytes": "kilobyte", 28196 "kB": "kilobyte", 28197 "KB": "kilobyte", 28198 "megabit": "megabit", 28199 "Megabit": "megabit", 28200 "megaBit": "megabit", 28201 "MegaBit": "megabit", 28202 "megabits": "megabit", 28203 "Megabits": "megabit", 28204 "megaBits": "megabit", 28205 "MegaBits": "megabit", 28206 "Mb": "megabit", 28207 "mb": "megabit", 28208 "megabyte": "megabyte", 28209 "Megabyte": "megabyte", 28210 "megaByte": "megabyte", 28211 "MegaByte": "megabyte", 28212 "megabytes": "megabyte", 28213 "Megabytes": "megabyte", 28214 "megaBytes": "megabyte", 28215 "MegaBytes": "megabyte", 28216 "MB": "megabyte", 28217 "mB": "megabyte", 28218 "gigabit": "gigabit", 28219 "Gigabit": "gigabit", 28220 "gigaBit": "gigabit", 28221 "GigaBit": "gigabit", 28222 "gigabits": "gigabit", 28223 "Gigabits": "gigabit", 28224 "gigaBits": "gigabyte", 28225 "GigaBits": "gigabit", 28226 "Gb": "gigabit", 28227 "gb": "gigabit", 28228 "gigabyte": "gigabyte", 28229 "Gigabyte": "gigabyte", 28230 "gigaByte": "gigabyte", 28231 "GigaByte": "gigabyte", 28232 "gigabytes": "gigabyte", 28233 "Gigabytes": "gigabyte", 28234 "gigaBytes": "gigabyte", 28235 "GigaBytes": "gigabyte", 28236 "GB": "gigabyte", 28237 "gB": "gigabyte", 28238 "terabit": "terabit", 28239 "Terabit": "terabit", 28240 "teraBit": "terabit", 28241 "TeraBit": "terabit", 28242 "terabits": "terabit", 28243 "Terabits": "terabit", 28244 "teraBits": "terabit", 28245 "TeraBits": "terabit", 28246 "tb": "terabit", 28247 "Tb": "terabit", 28248 "terabyte": "terabyte", 28249 "Terabyte": "terabyte", 28250 "teraByte": "terabyte", 28251 "TeraByte": "terabyte", 28252 "terabytes": "terabyte", 28253 "Terabytes": "terabyte", 28254 "teraBytes": "terabyte", 28255 "TeraBytes": "terabyte", 28256 "TB": "terabyte", 28257 "tB": "terabyte", 28258 "petabit": "petabit", 28259 "Petabit": "petabit", 28260 "petaBit": "petabit", 28261 "PetaBit": "petabit", 28262 "petabits": "petabit", 28263 "Petabits": "petabit", 28264 "petaBits": "petabit", 28265 "PetaBits": "petabit", 28266 "pb": "petabit", 28267 "Pb": "petabit", 28268 "petabyte": "petabyte", 28269 "Petabyte": "petabyte", 28270 "petaByte": "petabyte", 28271 "PetaByte": "petabyte", 28272 "petabytes": "petabyte", 28273 "Petabytes": "petabyte", 28274 "petaBytes": "petabyte", 28275 "PetaBytes": "petabyte", 28276 "PB": "petabyte", 28277 "pB": "petabyte" 28278 }; 28279 28280 /** 28281 * Convert a digitalStorage to another measure. 28282 * @static 28283 * @param to {string} unit to convert to 28284 * @param from {string} unit to convert from 28285 * @param digitalStorage {number} amount to be convert 28286 * @returns {number|undefined} the converted amount 28287 */ 28288 ilib.Measurement.DigitalStorage.convert = function(to, from, digitalStorage) { 28289 from = ilib.Measurement.DigitalStorage.aliases[from] || from; 28290 to = ilib.Measurement.DigitalStorage.aliases[to] || to; 28291 var fromRow = ilib.Measurement.DigitalStorage.ratios[from]; 28292 var toRow = ilib.Measurement.DigitalStorage.ratios[to]; 28293 if (typeof(from) === 'undefined' || typeof(to) === 'undefined') { 28294 return undefined; 28295 } 28296 var result = digitalStorage * fromRow[toRow[0]]; 28297 return result; 28298 }; 28299 28300 /** 28301 * @private 28302 * @static 28303 */ 28304 ilib.Measurement.DigitalStorage.getMeasures = function () { 28305 var ret = []; 28306 for (var m in ilib.Measurement.DigitalStorage.ratios) { 28307 ret.push(m); 28308 } 28309 return ret; 28310 }; 28311 28312 //register with the factory method 28313 ilib.Measurement._constructors["digitalStorage"] = ilib.Measurement.DigitalStorage; 28314 28315 /* 28316 * temperature.js - Unit conversions for Temperature/temperature 28317 * 28318 * Copyright © 2014, JEDLSoft 28319 * 28320 * Licensed under the Apache License, Version 2.0 (the "License"); 28321 * you may not use this file except in compliance with the License. 28322 * You may obtain a copy of the License at 28323 * 28324 * http://www.apache.org/licenses/LICENSE-2.0 28325 * 28326 * Unless required by applicable law or agreed to in writing, software 28327 * distributed under the License is distributed on an "AS IS" BASIS, 28328 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28329 * 28330 * See the License for the specific language governing permissions and 28331 * limitations under the License. 28332 */ 28333 28334 /* 28335 !depends 28336 ilibglobal.js 28337 unit.js 28338 */ 28339 28340 /** 28341 * @class 28342 * Create a new Temperature measurement instance. 28343 * 28344 * @constructor 28345 * @extends ilib.Measurement 28346 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 28347 * the construction of this instance 28348 */ 28349 ilib.Measurement.Temperature = function (options) { 28350 this.unit = "celsius"; 28351 this.amount = 0; 28352 this.aliases = ilib.Measurement.Temperature.aliases; // share this table in all instances 28353 28354 if (options) { 28355 if (typeof(options.unit) !== 'undefined') { 28356 this.originalUnit = options.unit; 28357 this.unit = this.aliases[options.unit] || options.unit; 28358 } 28359 28360 if (typeof(options.amount) === 'object') { 28361 if (options.amount.getMeasure() === "temperature") { 28362 this.amount = ilib.Measurement.Temperature.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 28363 } else { 28364 throw "Cannot convert unit " + options.amount.unit + " to a temperature"; 28365 } 28366 } else if (typeof(options.amount) !== 'undefined') { 28367 this.amount = parseFloat(options.amount); 28368 } 28369 } 28370 }; 28371 28372 ilib.Measurement.Temperature.prototype = new ilib.Measurement({}); 28373 ilib.Measurement.Temperature.prototype.parent = ilib.Measurement; 28374 ilib.Measurement.Temperature.prototype.constructor = ilib.Measurement.Temperature; 28375 28376 /** 28377 * Return the type of this measurement. Examples are "mass", 28378 * "length", "speed", etc. Measurements can only be converted 28379 * to measurements of the same type.<p> 28380 * 28381 * The type of the units is determined automatically from the 28382 * units. For example, the unit "grams" is type "mass". Use the 28383 * static call {@link ilib.Measurement.getAvailableUnits} 28384 * to find out what units this version of ilib supports. 28385 * 28386 * @return {string} the name of the type of this measurement 28387 */ 28388 ilib.Measurement.Temperature.prototype.getMeasure = function() { 28389 return "temperature"; 28390 }; 28391 28392 ilib.Measurement.Temperature.aliases = { 28393 "Celsius": "celsius", 28394 "celsius": "celsius", 28395 "C": "celsius", 28396 "centegrade": "celsius", 28397 "Centegrade": "celsius", 28398 "centigrade": "celsius", 28399 "Centigrade": "celsius", 28400 "fahrenheit": "fahrenheit", 28401 "Fahrenheit": "fahrenheit", 28402 "F": "fahrenheit", 28403 "kelvin": "kelvin", 28404 "K": "kelvin", 28405 "Kelvin": "kelvin", 28406 "°F": "fahrenheit", 28407 "℉": "fahrenheit", 28408 "℃": "celsius", 28409 "°C": "celsius" 28410 }; 28411 28412 /** 28413 * Return a new measurement instance that is converted to a new 28414 * measurement unit. Measurements can only be converted 28415 * to measurements of the same type.<p> 28416 * 28417 * @param {string} to The name of the units to convert to 28418 * @return {ilib.Measurement|undefined} the converted measurement 28419 * or undefined if the requested units are for a different 28420 * measurement type 28421 */ 28422 ilib.Measurement.Temperature.prototype.convert = function(to) { 28423 if (!to || typeof(ilib.Measurement.Temperature.ratios[this.normalizeUnits(to)]) === 'undefined') { 28424 return undefined; 28425 } 28426 return new ilib.Measurement({ 28427 unit: to, 28428 amount: this 28429 }); 28430 }; 28431 28432 /** 28433 * Convert a temperature to another measure. 28434 * @static 28435 * @param to {string} unit to convert to 28436 * @param from {string} unit to convert from 28437 * @param temperature {number} amount to be convert 28438 * @returns {number|undefined} the converted amount 28439 */ 28440 ilib.Measurement.Temperature.convert = function(to, from, temperature) { 28441 var result = 0; 28442 from = ilib.Measurement.Temperature.aliases[from] || from; 28443 to = ilib.Measurement.Temperature.aliases[to] || to; 28444 if (from === to) 28445 return temperature; 28446 28447 else if (from === "celsius") { 28448 if (to === "fahrenheit") { 28449 result = ((temperature * 9 / 5) + 32); 28450 } else if (to === "kelvin") { 28451 result = (temperature + 273.15); 28452 } 28453 28454 } else if (from === "fahrenheit") { 28455 if (to === "celsius") { 28456 result = ((5 / 9 * (temperature - 32))); 28457 } else if (to === "kelvin") { 28458 result = ((temperature + 459.67) * 5 / 9); 28459 } 28460 } else if (from === "kelvin") { 28461 if (to === "celsius") { 28462 result = (temperature - 273.15); 28463 } else if (to === "fahrenheit") { 28464 result = ((temperature * 9 / 5) - 459.67); 28465 } 28466 } 28467 28468 return result; 28469 }; 28470 28471 /** 28472 * Scale the measurement unit to an acceptable level. The scaling 28473 * happens so that the integer part of the amount is as small as 28474 * possible without being below zero. This will result in the 28475 * largest units that can represent this measurement without 28476 * fractions. Measurements can only be scaled to other measurements 28477 * of the same type. 28478 * 28479 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 28480 * or undefined if the system can be inferred from the current measure 28481 * @return {ilib.Measurement} a new instance that is scaled to the 28482 * right level 28483 */ 28484 ilib.Measurement.Temperature.prototype.scale = function(measurementsystem) { 28485 return new ilib.Measurement.Temperature({ 28486 unit: this.unit, 28487 amount: this.amount 28488 }); 28489 }; 28490 28491 /** 28492 * @private 28493 * @static 28494 */ 28495 ilib.Measurement.Temperature.getMeasures = function () { 28496 return ["celsius", "kelvin", "fahrenheit"]; 28497 }; 28498 ilib.Measurement.Temperature.metricToUScustomary = { 28499 "celsius": "fahrenheit" 28500 }; 28501 ilib.Measurement.Temperature.usCustomaryToMetric = { 28502 "fahrenheit": "celsius" 28503 }; 28504 28505 /** 28506 * Localize the measurement to the commonly used measurement in that locale. For example 28507 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 28508 * the formatted number should be automatically converted to the most appropriate 28509 * measure in the other system, in this case, mph. The formatted result should 28510 * appear as "37.3 mph". 28511 * 28512 * @abstract 28513 * @param {string} locale current locale string 28514 * @returns {ilib.Measurement} a new instance that is converted to locale 28515 */ 28516 ilib.Measurement.Temperature.prototype.localize = function(locale) { 28517 var to; 28518 if (locale === "en-US" ) { 28519 to = ilib.Measurement.Temperature.metricToUScustomary[this.unit] || this.unit; 28520 } else { 28521 to = ilib.Measurement.Temperature.usCustomaryToMetric[this.unit] || this.unit; 28522 } 28523 return new ilib.Measurement.Temperature({ 28524 unit: to, 28525 amount: this 28526 }); 28527 }; 28528 //register with the factory method 28529 ilib.Measurement._constructors["temperature"] = ilib.Measurement.Temperature; 28530 28531 /* 28532 * Unknown.js - Dummy unit conversions for unknown types 28533 * 28534 * Copyright © 2014, JEDLSoft 28535 * 28536 * Licensed under the Apache License, Version 2.0 (the "License"); 28537 * you may not use this file except in compliance with the License. 28538 * You may obtain a copy of the License at 28539 * 28540 * http://www.apache.org/licenses/LICENSE-2.0 28541 * 28542 * Unless required by applicable law or agreed to in writing, software 28543 * distributed under the License is distributed on an "AS IS" BASIS, 28544 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28545 * 28546 * See the License for the specific language governing permissions and 28547 * limitations under the License. 28548 */ 28549 28550 /* 28551 !depends 28552 ilibglobal.js 28553 unit.js 28554 */ 28555 28556 /** 28557 * @class 28558 * Create a new unknown measurement instance. 28559 * 28560 * @constructor 28561 * @extends ilib.Measurement 28562 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 28563 * the construction of this instance 28564 */ 28565 ilib.Measurement.Unknown = function (options) { 28566 if (options) { 28567 this.unit = options.unit; 28568 this.amount = options.amount; 28569 } 28570 }; 28571 28572 ilib.Measurement.Unknown.prototype = new ilib.Measurement({}); 28573 ilib.Measurement.Unknown.prototype.parent = ilib.Measurement; 28574 ilib.Measurement.Unknown.prototype.constructor = ilib.Measurement.Unknown; 28575 28576 ilib.Measurement.Unknown.aliases = { 28577 "unknown":"unknown" 28578 }; 28579 28580 28581 /** 28582 * Return the type of this measurement. Examples are "mass", 28583 * "length", "speed", etc. Measurements can only be converted 28584 * to measurements of the same type.<p> 28585 * 28586 * The type of the units is determined automatically from the 28587 * units. For example, the unit "grams" is type "mass". Use the 28588 * static call {@link ilib.Measurement.getAvailableUnits} 28589 * to find out what units this version of ilib supports. 28590 * 28591 * @return {string} the name of the type of this measurement 28592 */ 28593 ilib.Measurement.Unknown.prototype.getMeasure = function() { 28594 return "unknown"; 28595 }; 28596 28597 /** 28598 * Return a new measurement instance that is converted to a new 28599 * measurement unit. Measurements can only be converted 28600 * to measurements of the same type.<p> 28601 * 28602 * @param {string} to The name of the units to convert to 28603 * @return {ilib.Measurement|undefined} the converted measurement 28604 * or undefined if the requested units are for a different 28605 * measurement type 28606 */ 28607 ilib.Measurement.Unknown.prototype.convert = function(to) { 28608 return undefined; 28609 }; 28610 28611 /** 28612 * Convert a unknown to another measure. 28613 * @static 28614 * @param {string} to unit to convert to 28615 * @param {string} from unit to convert from 28616 * @param {number} unknown amount to be convert 28617 * @returns {number|undefined} the converted amount 28618 */ 28619 ilib.Measurement.Unknown.convert = function(to, from, unknown) { 28620 return undefined; 28621 }; 28622 28623 /** 28624 * Localize the measurement to the commonly used measurement in that locale. For example 28625 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 28626 * the formatted number should be automatically converted to the most appropriate 28627 * measure in the other system, in this case, mph. The formatted result should 28628 * appear as "37.3 mph". 28629 * 28630 * @abstract 28631 * @param {string} locale current locale string 28632 * @returns {ilib.Measurement} a new instance that is converted to locale 28633 */ 28634 ilib.Measurement.Unknown.prototype.localize = function(locale) { 28635 return new ilib.Measurement.Unknown({ 28636 unit: this.unit, 28637 amount: this.amount 28638 }); 28639 }; 28640 28641 /** 28642 * Scale the measurement unit to an acceptable level. The scaling 28643 * happens so that the integer part of the amount is as small as 28644 * possible without being below zero. This will result in the 28645 * largest units that can represent this measurement without 28646 * fractions. Measurements can only be scaled to other measurements 28647 * of the same type. 28648 * 28649 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 28650 * or undefined if the system can be inferred from the current measure 28651 * @return {ilib.Measurement} a new instance that is scaled to the 28652 * right level 28653 */ 28654 ilib.Measurement.Unknown.prototype.scale = function(measurementsystem) { 28655 return new ilib.Measurement.Unknown({ 28656 unit: this.unit, 28657 amount: this.amount 28658 }); 28659 }; 28660 28661 /** 28662 * @private 28663 * @static 28664 */ 28665 ilib.Measurement.Unknown.getMeasures = function () { 28666 return []; 28667 }; 28668 28669 //register with the factory method 28670 ilib.Measurement._constructors["unknown"] = ilib.Measurement.Unknown; 28671 28672 28673 /* 28674 * Time.js - Unit conversions for Times/times 28675 * 28676 * Copyright © 2014, JEDLSoft 28677 * 28678 * Licensed under the Apache License, Version 2.0 (the "License"); 28679 * you may not use this file except in compliance with the License. 28680 * You may obtain a copy of the License at 28681 * 28682 * http://www.apache.org/licenses/LICENSE-2.0 28683 * 28684 * Unless required by applicable law or agreed to in writing, software 28685 * distributed under the License is distributed on an "AS IS" BASIS, 28686 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28687 * 28688 * See the License for the specific language governing permissions and 28689 * limitations under the License. 28690 */ 28691 28692 /* 28693 !depends 28694 ilibglobal.js 28695 unit.js 28696 */ 28697 28698 /** 28699 * @class 28700 * Create a new time measurement instance. 28701 * 28702 * @constructor 28703 * @extends ilib.Measurement 28704 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 28705 * the construction of this instance 28706 */ 28707 ilib.Measurement.Time = function (options) { 28708 this.unit = "ns"; 28709 this.amount = 0; 28710 this.aliases = ilib.Measurement.Time.aliases; // share this table in all instances 28711 28712 if (options) { 28713 if (typeof(options.unit) !== 'undefined') { 28714 this.originalUnit = options.unit; 28715 this.unit = this.aliases[options.unit] || options.unit; 28716 } 28717 28718 if (typeof(options.amount) === 'object') { 28719 if (options.amount.getMeasure() === "time") { 28720 this.amount = ilib.Measurement.Time.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 28721 } else { 28722 throw "Cannot convert units " + options.amount.unit + " to a time"; 28723 } 28724 } else if (typeof(options.amount) !== 'undefined') { 28725 this.amount = parseFloat(options.amount); 28726 } 28727 } 28728 28729 if (typeof(ilib.Measurement.Time.ratios[this.unit]) === 'undefined') { 28730 throw "Unknown unit: " + options.unit; 28731 } 28732 }; 28733 28734 ilib.Measurement.Time.ratios = { 28735 /* index nsec msec mlsec sec min hour day week month year decade century */ 28736 "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 ], 28737 "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 ], 28738 "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 ], 28739 "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 ], 28740 "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 ], 28741 "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 ], 28742 "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 ], 28743 "week": [ 8, 6.048e+14, 6.048e+11, 6.048e+8, 604800, 10080, 168, 7, 1, 0.229984, 0.0191654, 0.00191654, 0.000191654 ], 28744 "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 ], 28745 "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 ], 28746 "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 ], 28747 "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 ] 28748 }; 28749 28750 ilib.Measurement.Time.prototype = new ilib.Measurement({}); 28751 ilib.Measurement.Time.prototype.parent = ilib.Measurement; 28752 ilib.Measurement.Time.prototype.constructor = ilib.Measurement.Time; 28753 28754 /** 28755 * Return the type of this measurement. Examples are "mass", 28756 * "length", "speed", etc. Measurements can only be converted 28757 * to measurements of the same type.<p> 28758 * 28759 * The type of the units is determined automatically from the 28760 * units. For example, the unit "grams" is type "mass". Use the 28761 * static call {@link ilib.Measurement.getAvailableUnits} 28762 * to find out what units this version of ilib supports. 28763 * 28764 * @return {string} the name of the type of this measurement 28765 */ 28766 ilib.Measurement.Time.prototype.getMeasure = function() { 28767 return "time"; 28768 }; 28769 28770 /** 28771 * Return a new measurement instance that is converted to a new 28772 * measurement unit. Measurements can only be converted 28773 * to measurements of the same type.<p> 28774 * 28775 * @param {string} to The name of the units to convert to 28776 * @return {ilib.Measurement|undefined} the converted measurement 28777 * or undefined if the requested units are for a different 28778 * measurement type 28779 */ 28780 ilib.Measurement.Time.prototype.convert = function(to) { 28781 if (!to || typeof(ilib.Measurement.Time.ratios[this.normalizeUnits(to)]) === 'undefined') { 28782 return undefined; 28783 } 28784 return new ilib.Measurement({ 28785 unit: to, 28786 amount: this 28787 }); 28788 }; 28789 28790 ilib.Measurement.Time.aliases = { 28791 "ns": "nanosecond", 28792 "NS": "nanosecond", 28793 "nS": "nanosecond", 28794 "Ns": "nanosecond", 28795 "Nanosecond": "nanosecond", 28796 "Nanoseconds": "nanosecond", 28797 "nanosecond": "nanosecond", 28798 "nanoseconds": "nanosecond", 28799 "NanoSecond": "nanosecond", 28800 "NanoSeconds": "nanosecond", 28801 "μs": "microsecond", 28802 "μS": "microsecond", 28803 "microsecond": "microsecond", 28804 "microseconds": "microsecond", 28805 "Microsecond": "microsecond", 28806 "Microseconds": "microsecond", 28807 "MicroSecond": "microsecond", 28808 "MicroSeconds": "microsecond", 28809 "ms": "millisecond", 28810 "MS": "millisecond", 28811 "mS": "millisecond", 28812 "Ms": "millisecond", 28813 "millisecond": "millisecond", 28814 "milliseconds": "millisecond", 28815 "Millisecond": "millisecond", 28816 "Milliseconds": "millisecond", 28817 "MilliSecond": "millisecond", 28818 "MilliSeconds": "millisecond", 28819 "s": "second", 28820 "S": "second", 28821 "sec": "second", 28822 "second": "second", 28823 "seconds": "second", 28824 "Second": "second", 28825 "Seconds": "second", 28826 "min": "minute", 28827 "Min": "minute", 28828 "minute": "minute", 28829 "minutes": "minute", 28830 "Minute": "minute", 28831 "Minutes": "minute", 28832 "h": "hour", 28833 "H": "hour", 28834 "hr": "hour", 28835 "Hr": "hour", 28836 "hR": "hour", 28837 "HR": "hour", 28838 "hour": "hour", 28839 "hours": "hour", 28840 "Hour": "hour", 28841 "Hours": "hour", 28842 "Hrs": "hour", 28843 "hrs": "hour", 28844 "day": "day", 28845 "days": "day", 28846 "Day": "day", 28847 "Days": "day", 28848 "week": "week", 28849 "weeks": "week", 28850 "Week": "week", 28851 "Weeks": "week", 28852 "month": "month", 28853 "Month": "month", 28854 "months": "month", 28855 "Months": "month", 28856 "year": "year", 28857 "years": "year", 28858 "Year": "year", 28859 "Years": "year", 28860 "yr": "year", 28861 "Yr": "year", 28862 "yrs": "year", 28863 "Yrs": "year", 28864 "decade": "decade", 28865 "decades": "decade", 28866 "Decade": "decade", 28867 "Decades": "decade", 28868 "century": "century", 28869 "centuries": "century", 28870 "Century": "century", 28871 "Centuries": "century" 28872 }; 28873 28874 /** 28875 * Convert a time to another measure. 28876 * @static 28877 * @param to {string} unit to convert to 28878 * @param from {string} unit to convert from 28879 * @param time {number} amount to be convert 28880 * @returns {number|undefined} the converted amount 28881 */ 28882 ilib.Measurement.Time.convert = function(to, from, time) { 28883 from = ilib.Measurement.Time.aliases[from] || from; 28884 to = ilib.Measurement.Time.aliases[to] || to; 28885 var fromRow = ilib.Measurement.Time.ratios[from]; 28886 var toRow = ilib.Measurement.Time.ratios[to]; 28887 if (typeof(from) === 'undefined' || typeof(to) === 'undefined') { 28888 return undefined; 28889 } 28890 return time * fromRow[toRow[0]]; 28891 }; 28892 28893 /** 28894 * Localize the measurement to the commonly used measurement in that locale. For example 28895 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 28896 * the formatted number should be automatically converted to the most appropriate 28897 * measure in the other system, in this case, mph. The formatted result should 28898 * appear as "37.3 mph". 28899 * 28900 * @abstract 28901 * @param {string} locale current locale string 28902 * @returns {ilib.Measurement} a new instance that is converted to locale 28903 */ 28904 ilib.Measurement.Time.prototype.localize = function(locale) { 28905 return new ilib.Measurement.Time({ 28906 unit: this.unit, 28907 amount: this.amount 28908 }); 28909 }; 28910 28911 /** 28912 * Scale the measurement unit to an acceptable level. The scaling 28913 * happens so that the integer part of the amount is as small as 28914 * possible without being below zero. This will result in the 28915 * largest units that can represent this measurement without 28916 * fractions. Measurements can only be scaled to other measurements 28917 * of the same type. 28918 * 28919 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 28920 * or undefined if the system can be inferred from the current measure 28921 * @return {ilib.Measurement} a new instance that is scaled to the 28922 * right level 28923 */ 28924 ilib.Measurement.Time.prototype.scale = function(measurementsystem) { 28925 28926 var fromRow = ilib.Measurement.Time.ratios[this.unit]; 28927 var time = this.amount; 28928 var munit = this.unit; 28929 var i=1; 28930 28931 for (var m in ilib.Measurement.Time.ratios) { 28932 var tmp = this.amount * fromRow[i]; 28933 if (tmp < 1) break; 28934 time = tmp; 28935 munit = m; 28936 ++i 28937 } 28938 28939 return new ilib.Measurement.Time({ 28940 unit: munit, 28941 amount: time 28942 }); 28943 }; 28944 /** 28945 * @private 28946 * @static 28947 */ 28948 ilib.Measurement.Time.getMeasures = function () { 28949 var ret = []; 28950 for (var m in ilib.Measurement.Time.ratios) { 28951 ret.push(m); 28952 } 28953 return ret; 28954 }; 28955 28956 //register with the factory method 28957 ilib.Measurement._constructors["time"] = ilib.Measurement.Time; 28958 28959 /* 28960 * Mass.js - Unit conversions for Mass/mass 28961 * 28962 * Copyright © 2014, JEDLSoft 28963 * 28964 * Licensed under the Apache License, Version 2.0 (the "License"); 28965 * you may not use this file except in compliance with the License. 28966 * You may obtain a copy of the License at 28967 * 28968 * http://www.apache.org/licenses/LICENSE-2.0 28969 * 28970 * Unless required by applicable law or agreed to in writing, software 28971 * distributed under the License is distributed on an "AS IS" BASIS, 28972 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28973 * 28974 * See the License for the specific language governing permissions and 28975 * limitations under the License. 28976 */ 28977 28978 /* 28979 !depends 28980 ilibglobal.js 28981 */ 28982 28983 /** 28984 * @class 28985 * Create a new mass measurement instance. 28986 * 28987 * @constructor 28988 * @extends ilib.Measurement 28989 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 28990 * the construction of this instance 28991 */ 28992 ilib.Measurement.Mass = function (options) { 28993 this.unit = "ns"; 28994 this.amount = 0; 28995 this.aliases = ilib.Measurement.Mass.aliases; // share this table in all instances 28996 28997 if (options) { 28998 if (typeof(options.unit) !== 'undefined') { 28999 this.originalUnit = options.unit; 29000 this.unit = this.aliases[options.unit] || options.unit; 29001 } 29002 29003 if (typeof(options.amount) === 'object') { 29004 if (options.amount.getMeasure() === "mass") { 29005 this.amount = ilib.Measurement.Mass.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 29006 } else { 29007 throw "Cannot convert units " + options.amount.unit + " to a mass"; 29008 } 29009 } else if (typeof(options.amount) !== 'undefined') { 29010 this.amount = parseFloat(options.amount); 29011 } 29012 } 29013 29014 if (typeof(ilib.Measurement.Mass.ratios[this.unit]) === 'undefined') { 29015 throw "Unknown unit: " + options.unit; 29016 } 29017 }; 29018 29019 ilib.Measurement.Mass.ratios = { 29020 /* index µg mg g oz lp kg st sh ton mt ton ln ton */ 29021 "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 ], 29022 "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 ], 29023 "gram": [ 3, 1e+6, 1000, 1, 0.035274, 0.00220462, 0.001, 0.000157473, 1.1023e-6, 1e-6, 9.8421e-7 ], 29024 "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 ], 29025 "pound": [ 5, 4.536e+8, 453592, 453.592, 16, 1, 0.453592, 0.0714286, 0.0005, 0.000453592, 0.000446429 ], 29026 "kilogram": [ 6, 1e+9, 1e+6, 1000, 35.274, 2.20462, 1, 0.157473, 0.00110231, 0.001, 0.000984207 ], 29027 "stone": [ 7, 6.35e+9, 6.35e+6, 6350.29, 224, 14, 6.35029, 1, 0.007, 0.00635029, 0.00625 ], 29028 "short ton": [ 8, 9.072e+11, 9.072e+8, 907185, 32000, 2000, 907.185, 142.857, 1, 0.907185, 0.892857 ], 29029 "metric ton": [ 9, 1e+12, 1e+9, 1e+6, 35274, 2204.62, 1000, 157.473, 1.10231, 1, 0.984207 ], 29030 "long ton": [ 10, 1.016e+12, 1.016e+9, 1.016e+6, 35840, 2240, 1016.05, 160, 1.12, 1.01605, 1 ] 29031 }; 29032 29033 ilib.Measurement.Mass.metricSystem = { 29034 "microgram": 1, 29035 "milligram": 2, 29036 "gram": 3, 29037 "kilogram": 6, 29038 "metric ton": 9 29039 }; 29040 ilib.Measurement.Mass.imperialSystem = { 29041 "ounce": 4, 29042 "pound": 5, 29043 "stone": 7, 29044 "long ton": 10 29045 }; 29046 ilib.Measurement.Mass.uscustomarySystem = { 29047 "ounce": 4, 29048 "pound": 5, 29049 "short ton": 8 29050 }; 29051 29052 ilib.Measurement.Mass.metricToUScustomary = { 29053 "microgram": "ounce", 29054 "milligram": "ounce", 29055 "gram": "ounce", 29056 "kilogram": "pound", 29057 "metric ton": "long ton" 29058 }; 29059 ilib.Measurement.Mass.metricToImperial = { 29060 "microgram": "ounce", 29061 "milligram": "ounce", 29062 "gram": "ounce", 29063 "kilogram": "pound", 29064 "metric ton": "short ton" 29065 }; 29066 29067 ilib.Measurement.Mass.imperialToMetric = { 29068 "ounce": "gram", 29069 "pound": "kilogram", 29070 "stone": "kilogram", 29071 "short ton": "metric ton" 29072 }; 29073 ilib.Measurement.Mass.imperialToUScustomary = { 29074 "ounce": "ounce", 29075 "pound": "pound", 29076 "stone": "stone", 29077 "short ton": "long ton" 29078 }; 29079 29080 ilib.Measurement.Mass.uScustomaryToImperial = { 29081 "ounce": "ounce", 29082 "pound": "pound", 29083 "stone": "stone", 29084 "long ton": "short ton" 29085 }; 29086 ilib.Measurement.Mass.uScustomarylToMetric = { 29087 "ounce": "gram", 29088 "pound": "kilogram", 29089 "stone": "kilogram", 29090 "long ton": "metric ton" 29091 }; 29092 29093 29094 ilib.Measurement.Mass.prototype = new ilib.Measurement({}); 29095 ilib.Measurement.Mass.prototype.parent = ilib.Measurement; 29096 ilib.Measurement.Mass.prototype.constructor = ilib.Measurement.Mass; 29097 29098 /** 29099 * Localize the measurement to the commonly used measurement in that locale. For example 29100 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 29101 * the formatted number should be automatically converted to the most appropriate 29102 * measure in the other system, in this case, mph. The formatted result should 29103 * appear as "37.3 mph". 29104 * 29105 * @abstract 29106 * @param {string} locale current locale string 29107 * @returns {ilib.Measurement} a new instance that is converted to locale 29108 */ 29109 ilib.Measurement.Mass.prototype.localize = function(locale) { 29110 var to; 29111 if (locale === "en-US") { 29112 to = ilib.Measurement.Mass.metricToUScustomary[this.unit] || 29113 ilib.Measurement.Mass.imperialToUScustomary[this.unit] || this.unit; 29114 } else if (locale === "en-GB") { 29115 to = ilib.Measurement.Mass.metricToImperial[this.unit] || 29116 ilib.Measurement.Mass.uScustomaryToImperial[this.unit] || this.unit; 29117 } else { 29118 to = ilib.Measurement.Mass.uScustomarylToMetric[this.unit] || 29119 ilib.Measurement.Mass.imperialToUScustomary[this.unit] || this.unit; 29120 } 29121 return new ilib.Measurement.Mass({ 29122 unit: to, 29123 amount: this 29124 }); 29125 }; 29126 29127 /** 29128 * Return the type of this measurement. Examples are "mass", 29129 * "length", "speed", etc. Measurements can only be converted 29130 * to measurements of the same type.<p> 29131 * 29132 * The type of the units is determined automatically from the 29133 * units. For example, the unit "grams" is type "mass". Use the 29134 * static call {@link ilib.Measurement.getAvailableUnits} 29135 * to find out what units this version of ilib supports. 29136 * 29137 * @return {string} the name of the type of this measurement 29138 */ 29139 ilib.Measurement.Mass.prototype.getMeasure = function() { 29140 return "mass"; 29141 }; 29142 29143 /** 29144 * Return a new measurement instance that is converted to a new 29145 * measurement unit. Measurements can only be converted 29146 * to measurements of the same type.<p> 29147 * 29148 * @param {string} to The name of the units to convert to 29149 * @return {ilib.Measurement|undefined} the converted measurement 29150 * or undefined if the requested units are for a different 29151 * measurement type 29152 */ 29153 ilib.Measurement.Mass.prototype.convert = function(to) { 29154 if (!to || typeof(ilib.Measurement.Mass.ratios[this.normalizeUnits(to)]) === 'undefined') { 29155 return undefined; 29156 } 29157 return new ilib.Measurement({ 29158 unit: to, 29159 amount: this 29160 }); 29161 }; 29162 29163 ilib.Measurement.Mass.aliases = { 29164 "µg":"microgram", 29165 "microgram":"microgram", 29166 "mcg":"microgram", 29167 "milligram":"milligram", 29168 "mg":"milligram", 29169 "milligrams":"milligram", 29170 "Milligram":"milligram", 29171 "Milligrams":"milligram", 29172 "MilliGram":"milligram", 29173 "MilliGrams":"milligram", 29174 "g":"gram", 29175 "gram":"gram", 29176 "grams":"gram", 29177 "Gram":"gram", 29178 "Grams":"gram", 29179 "ounce":"ounce", 29180 "oz":"ounce", 29181 "Ounce":"ounce", 29182 "℥":"ounce", 29183 "pound":"pound", 29184 "poundm":"pound", 29185 "℔":"pound", 29186 "lb":"pound", 29187 "pounds":"pound", 29188 "Pound":"pound", 29189 "Pounds":"pound", 29190 "kilogram":"kilogram", 29191 "kg":"kilogram", 29192 "kilograms":"kilogram", 29193 "kilo grams":"kilogram", 29194 "kilo gram":"kilogram", 29195 "Kilogram":"kilogram", 29196 "Kilograms":"kilogram", 29197 "KiloGram":"kilogram", 29198 "KiloGrams":"kilogram", 29199 "Kilo gram":"kilogram", 29200 "Kilo grams":"kilogram", 29201 "Kilo Gram":"kilogram", 29202 "Kilo Grams":"kilogram", 29203 "stone":"stone", 29204 "st":"stone", 29205 "stones":"stone", 29206 "Stone":"stone", 29207 "short ton":"short ton", 29208 "Short ton":"short ton", 29209 "Short Ton":"short ton", 29210 "metric ton":"metric ton", 29211 "metricton":"metric ton", 29212 "t":"metric ton", 29213 "tonne":"metric ton", 29214 "Tonne":"metric ton", 29215 "Metric Ton":"metric ton", 29216 "MetricTon":"metric ton", 29217 "long ton":"long ton", 29218 "longton":"long ton", 29219 "Longton":"long ton", 29220 "Long ton":"long ton", 29221 "Long Ton":"long ton", 29222 "ton":"long ton", 29223 "Ton":"long ton" 29224 }; 29225 29226 /** 29227 * Convert a mass to another measure. 29228 * @static 29229 * @param to {string} unit to convert to 29230 * @param from {string} unit to convert from 29231 * @param mass {number} amount to be convert 29232 * @returns {number|undefined} the converted amount 29233 */ 29234 ilib.Measurement.Mass.convert = function(to, from, mass) { 29235 from = ilib.Measurement.Mass.aliases[from] || from; 29236 to = ilib.Measurement.Mass.aliases[to] || to; 29237 var fromRow = ilib.Measurement.Mass.ratios[from]; 29238 var toRow = ilib.Measurement.Mass.ratios[to]; 29239 if (typeof(from) === 'undefined' || typeof(to) === 'undefined') { 29240 return undefined; 29241 } 29242 return mass * fromRow[toRow[0]]; 29243 }; 29244 29245 /** 29246 * Scale the measurement unit to an acceptable level. The scaling 29247 * happens so that the integer part of the amount is as small as 29248 * possible without being below zero. This will result in the 29249 * largest units that can represent this measurement without 29250 * fractions. Measurements can only be scaled to other measurements 29251 * of the same type. 29252 * 29253 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 29254 * or undefined if the system can be inferred from the current measure 29255 * @return {ilib.Measurement} a new instance that is scaled to the 29256 * right level 29257 */ 29258 ilib.Measurement.Mass.prototype.scale = function(measurementsystem) { 29259 var mSystem; 29260 if (measurementsystem === "metric" || (typeof(measurementsystem) === 'undefined' 29261 && typeof(ilib.Measurement.Mass.metricSystem[this.unit]) !== 'undefined')) { 29262 mSystem = ilib.Measurement.Mass.metricSystem; 29263 } else if (measurementsystem === "imperial" || (typeof(measurementsystem) === 'undefined' 29264 && typeof(ilib.Measurement.Mass.imperialSystem[this.unit]) !== 'undefined')) { 29265 mSystem = ilib.Measurement.Mass.imperialSystem; 29266 } else if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined' 29267 && typeof(ilib.Measurement.Mass.uscustomarySystem[this.unit]) !== 'undefined')) { 29268 mSystem = ilib.Measurement.Mass.uscustomarySystem; 29269 } else { 29270 return new ilib.Measurement.Mass({ 29271 unit: this.unit, 29272 amount: this.amount 29273 }); 29274 } 29275 29276 var mass = this.amount; 29277 var munit = this.amount; 29278 var fromRow = ilib.Measurement.Mass.ratios[this.unit]; 29279 29280 for (var m in mSystem) { 29281 var tmp = this.amount * fromRow[mSystem[m]]; 29282 if (tmp < 1) break; 29283 mass = tmp; 29284 munit = m; 29285 } 29286 29287 return new ilib.Measurement.Mass({ 29288 unit: munit, 29289 amount: mass 29290 }); 29291 }; 29292 29293 /** 29294 * @private 29295 * @static 29296 */ 29297 ilib.Measurement.Mass.getMeasures = function () { 29298 var ret = []; 29299 for (var m in ilib.Measurement.Mass.ratios) { 29300 ret.push(m); 29301 } 29302 return ret; 29303 }; 29304 29305 //register with the factory method 29306 ilib.Measurement._constructors["mass"] = ilib.Measurement.Mass; 29307 29308 /* 29309 * area.js - Unit conversions for Area 29310 * 29311 * Copyright © 2014, JEDLSoft 29312 * 29313 * Licensed under the Apache License, Version 2.0 (the "License"); 29314 * you may not use this file except in compliance with the License. 29315 * You may obtain a copy of the License at 29316 * 29317 * http://www.apache.org/licenses/LICENSE-2.0 29318 * 29319 * Unless required by applicable law or agreed to in writing, software 29320 * distributed under the License is distributed on an "AS IS" BASIS, 29321 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29322 * 29323 * See the License for the specific language governing permissions and 29324 * limitations under the License. 29325 */ 29326 29327 /* 29328 !depends 29329 ilibglobal.js 29330 unit.js 29331 */ 29332 29333 /** 29334 * @class 29335 * Create a new area measurement instance. 29336 * @constructor 29337 * @extends ilib.Measurement 29338 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 29339 * the construction of this instance 29340 */ 29341 ilib.Measurement.Area = function (options) { 29342 this.unit = "square km"; 29343 this.amount = 0; 29344 this.aliases = ilib.Measurement.Area.aliases; // share this table in all instances 29345 29346 if (options) { 29347 if (typeof(options.unit) !== 'undefined') { 29348 this.originalUnit = options.unit; 29349 this.unit = this.aliases[options.unit] || options.unit; 29350 } 29351 29352 if (typeof(options.amount) === 'object') { 29353 if (options.amount.getMeasure() === "area") { 29354 this.amount = ilib.Measurement.Area.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 29355 } else { 29356 throw "Cannot convert unit " + options.amount.unit + " to area"; 29357 } 29358 } else if (typeof(options.amount) !== 'undefined') { 29359 this.amount = parseFloat(options.amount); 29360 } 29361 } 29362 29363 if (typeof(ilib.Measurement.Area.ratios[this.unit]) === 'undefined') { 29364 throw "Unknown unit: " + options.unit; 29365 } 29366 }; 29367 29368 ilib.Measurement.Area.ratios = { 29369 /* index square cm, square meter, hectare, square km, , square inch square foot, square yard, acre, square mile */ 29370 "square centimeter":[1, 1, 0.0001, 1e-8, 1e-10, 0.15500031, 0.00107639104, 0.000119599005, 2.47105381e-8, 3.86102159e-11 ], 29371 "square meter": [2, 10000, 1, 1e-4, 1e-6, 1550, 10.7639, 1.19599, 0.000247105, 3.861e-7 ], 29372 "hectare": [3, 100000000, 10000, 1, 0.01, 1.55e+7, 107639, 11959.9, 2.47105 , 0.00386102 ], 29373 "square km": [4, 10000000000, 1e+6, 100, 1, 1.55e+9, 1.076e+7, 1.196e+6, 247.105 , 0.386102 ], 29374 "square inch": [5, 6.4516, 0.00064516, 6.4516e-8, 6.4516e-10, 1, 0.000771605, 0.0007716051, 1.5942e-7, 2.491e-10 ], 29375 "square foot": [6, 929.0304, 0.092903, 9.2903e-6, 9.2903e-8, 144, 1, 0.111111, 2.2957e-5, 3.587e-8 ], 29376 "square yard": [7, 8361.2736, 0.836127, 8.3613e-5, 8.3613e-7, 1296, 9, 1, 0.000206612, 3.2283e-7 ], 29377 "acre": [8, 40468564.2, 4046.86, 0.404686, 0.00404686, 6.273e+6, 43560, 4840, 1, 0.0015625 ], 29378 "square mile": [9, 2.58998811e+10, 2.59e+6, 258.999, 2.58999, 4.014e+9, 2.788e+7, 3.098e+6, 640, 1 ] 29379 } 29380 29381 ilib.Measurement.Area.prototype = new ilib.Measurement({}); 29382 ilib.Measurement.Area.prototype.parent = ilib.Measurement; 29383 ilib.Measurement.Area.prototype.constructor = ilib.Measurement.Area; 29384 29385 /** 29386 * Return the type of this measurement. Examples are "mass", 29387 * "length", "speed", etc. Measurements can only be converted 29388 * to measurements of the same type.<p> 29389 * 29390 * The type of the units is determined automatically from the 29391 * units. For example, the unit "grams" is type "mass". Use the 29392 * static call {@link ilib.Measurement.getAvailableUnits} 29393 * to find out what units this version of ilib supports. 29394 * 29395 * @return {string} the name of the type of this measurement 29396 */ 29397 ilib.Measurement.Area.prototype.getMeasure = function() { 29398 return "area"; 29399 }; 29400 29401 /** 29402 * Return a new measurement instance that is converted to a new 29403 * measurement unit. Measurements can only be converted 29404 * to measurements of the same type.<p> 29405 * 29406 * @param {string} to The name of the units to convert to 29407 * @return {ilib.Measurement|undefined} the converted measurement 29408 * or undefined if the requested units are for a different 29409 * measurement type 29410 * 29411 */ 29412 ilib.Measurement.Area.prototype.convert = function(to) { 29413 if (!to || typeof(ilib.Measurement.Area.ratios[this.normalizeUnits(to)]) === 'undefined') { 29414 return undefined; 29415 } 29416 return new ilib.Measurement({ 29417 unit: to, 29418 amount: this 29419 }); 29420 }; 29421 29422 ilib.Measurement.Area.aliases = { 29423 "square centimeter":"square centimeter", 29424 "square cm":"square centimeter", 29425 "sq cm":"square centimeter", 29426 "Square Cm":"square centimeter", 29427 "square Centimeters":"square centimeter", 29428 "square Centimeter":"square centimeter", 29429 "square Centimetre":"square centimeter", 29430 "square Centimetres":"square centimeter", 29431 "square centimeters":"square centimeter", 29432 "Square km": "square km", 29433 "Square kilometre":"square km", 29434 "square kilometer":"square km", 29435 "square kilometre":"square km", 29436 "square kilometers":"square km", 29437 "square kilometres":"square km", 29438 "square km":"square km", 29439 "sq km":"square km", 29440 "km2":"square km", 29441 "Hectare":"hectare", 29442 "hectare":"hectare", 29443 "ha":"hectare", 29444 "Square meter": "square meter", 29445 "Square meters":"square meter", 29446 "square meter": "square meter", 29447 "square meters":"square meter", 29448 "Square metre": "square meter", 29449 "Square metres":"square meter", 29450 "square metres": "square meter", 29451 "Square Metres":"square meter", 29452 "sqm":"square meter", 29453 "m2": "square meter", 29454 "Square mile":"square mile", 29455 "Square miles":"square mile", 29456 "square mile":"square mile", 29457 "square miles":"square mile", 29458 "square mi":"square mile", 29459 "Square mi":"square mile", 29460 "sq mi":"square mile", 29461 "mi2":"square mile", 29462 "Acre": "acre", 29463 "acre": "acre", 29464 "Acres":"acre", 29465 "acres":"acre", 29466 "Square yard": "square yard", 29467 "Square yards":"square yard", 29468 "square yard": "square yard", 29469 "square yards":"square yard", 29470 "yd2":"square yard", 29471 "Square foot": "square foot", 29472 "square foot": "square foot", 29473 "Square feet": "square foot", 29474 "Square Feet": "square foot", 29475 "sq ft":"square foot", 29476 "ft2":"square foot", 29477 "Square inch":"square inch", 29478 "square inch":"square inch", 29479 "Square inches":"square inch", 29480 "square inches":"square inch", 29481 "in2":"square inch" 29482 }; 29483 29484 /** 29485 * Convert a Area to another measure. 29486 * @static 29487 * @param to {string} unit to convert to 29488 * @param from {string} unit to convert from 29489 * @param area {number} amount to be convert 29490 * @returns {number|undefined} the converted amount 29491 */ 29492 ilib.Measurement.Area.convert = function(to, from, area) { 29493 from = ilib.Measurement.Area.aliases[from] || from; 29494 to = ilib.Measurement.Area.aliases[to] || to; 29495 var fromRow = ilib.Measurement.Area.ratios[from]; 29496 var toRow = ilib.Measurement.Area.ratios[to]; 29497 if (typeof(from) === 'undefined' || typeof(to) === 'undefined') { 29498 return undefined; 29499 } 29500 return area* fromRow[toRow[0]]; 29501 }; 29502 29503 /** 29504 * @private 29505 * @static 29506 */ 29507 ilib.Measurement.Area.getMeasures = function () { 29508 var ret = []; 29509 for (var m in ilib.Measurement.Area.ratios) { 29510 ret.push(m); 29511 } 29512 return ret; 29513 }; 29514 29515 ilib.Measurement.Area.metricSystem = { 29516 "square centimeter" : 1, 29517 "square meter" : 2, 29518 "hectare" : 3, 29519 "square km" : 4 29520 }; 29521 ilib.Measurement.Area.imperialSystem = { 29522 "square inch" : 5, 29523 "square foot" : 6, 29524 "square yard" : 7, 29525 "acre" : 8, 29526 "square mile" : 9 29527 }; 29528 ilib.Measurement.Area.uscustomarySystem = { 29529 "square inch" : 5, 29530 "square foot" : 6, 29531 "square yard" : 7, 29532 "acre" : 8, 29533 "square mile" : 9 29534 }; 29535 29536 ilib.Measurement.Area.metricToUScustomary = { 29537 "square centimeter" : "square inch", 29538 "square meter" : "square yard", 29539 "hectare" : "acre", 29540 "square km" : "square mile" 29541 }; 29542 ilib.Measurement.Area.usCustomaryToMetric = { 29543 "square inch" : "square centimeter", 29544 "square foot" : "square meter", 29545 "square yard" : "square meter", 29546 "acre" : "hectare", 29547 "square mile" : "square km" 29548 }; 29549 29550 29551 /** 29552 * Scale the measurement unit to an acceptable level. The scaling 29553 * happens so that the integer part of the amount is as small as 29554 * possible without being below zero. This will result in the 29555 * largest units that can represent this measurement without 29556 * fractions. Measurements can only be scaled to other measurements 29557 * of the same type. 29558 * 29559 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 29560 * or undefined if the system can be inferred from the current measure 29561 * @return {ilib.Measurement} a new instance that is scaled to the 29562 * right level 29563 */ 29564 ilib.Measurement.Area.prototype.scale = function(measurementsystem) { 29565 var fromRow = ilib.Measurement.Area.ratios[this.unit]; 29566 var mSystem; 29567 29568 if (measurementsystem === "metric" || (typeof(measurementsystem) === 'undefined' 29569 && typeof(ilib.Measurement.Area.metricSystem[this.unit]) !== 'undefined')) { 29570 mSystem = ilib.Measurement.Area.metricSystem; 29571 } 29572 29573 else if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined' 29574 && typeof(ilib.Measurement.Area.metricSystem[this.unit]) !== 'undefined')) { 29575 mSystem = ilib.Measurement.Area.uscustomarySystem; 29576 } 29577 29578 else if (measurementsystem === "imperial" || (typeof(measurementsystem) === 'undefined' 29579 && typeof(ilib.Measurement.Area.metricSystem[this.unit]) !== 'undefined')) { 29580 mSystem = ilib.Measurement.Area.imperialSystem; 29581 } 29582 29583 var area = this.amount; 29584 var munit = this.unit; 29585 29586 for (var m in mSystem) { 29587 var tmp = this.amount * fromRow[mSystem[m]]; 29588 if (tmp < 1) break; 29589 area = tmp; 29590 munit = m; 29591 } 29592 29593 return new ilib.Measurement.Area({ 29594 unit: munit, 29595 amount: area 29596 }); 29597 }; 29598 29599 /** 29600 * Localize the measurement to the commonly used measurement in that locale. For example 29601 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 29602 * the formatted number should be automatically converted to the most appropriate 29603 * measure in the other system, in this case, mph. The formatted result should 29604 * appear as "37.3 mph". 29605 * 29606 * @abstract 29607 * @param {string} locale current locale string 29608 * @returns {ilib.Measurement} a new instance that is converted to locale 29609 */ 29610 ilib.Measurement.Area.prototype.localize = function(locale) { 29611 var to; 29612 if (locale === "en-US" || locale === "en-GB") { 29613 to = ilib.Measurement.Area.metricToUScustomary[this.unit] || this.unit; 29614 } else { 29615 to = ilib.Measurement.Area.usCustomaryToMetric[this.unit] || this.unit; 29616 } 29617 return new ilib.Measurement.Area({ 29618 unit: to, 29619 amount: this 29620 }); 29621 }; 29622 29623 29624 //register with the factory method 29625 ilib.Measurement._constructors["area"] = ilib.Measurement.Area; 29626 29627 /* 29628 * fuelconsumption.js - Unit conversions for FuelConsumption 29629 * 29630 * Copyright © 2014, JEDLSoft 29631 * 29632 * Licensed under the Apache License, Version 2.0 (the "License"); 29633 * you may not use this file except in compliance with the License. 29634 * You may obtain a copy of the License at 29635 * 29636 * http://www.apache.org/licenses/LICENSE-2.0 29637 * 29638 * Unless required by applicable law or agreed to in writing, software 29639 * distributed under the License is distributed on an "AS IS" BASIS, 29640 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29641 * 29642 * See the License for the specific language governing permissions and 29643 * limitations under the License. 29644 */ 29645 /* 29646 !depends 29647 ilibglobal.js 29648 */ 29649 /** 29650 * @class 29651 * Create a new fuelconsumption measurement instance. 29652 * 29653 * @constructor 29654 * @extends ilib.Measurement 29655 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 29656 * the construction of this instance 29657 */ 29658 ilib.Measurement.FuelConsumption = function(options) { 29659 this.unit = "km/liter"; 29660 this.amount = 0; 29661 this.aliases = ilib.Measurement.FuelConsumption.aliases; // share this table in all instances 29662 29663 if (options) { 29664 if (typeof(options.unit) !== 'undefined') { 29665 this.originalUnit = options.unit; 29666 this.unit = this.aliases[options.unit] || options.unit; 29667 } 29668 29669 if (typeof(options.amount) === 'object') { 29670 if (options.amount.getMeasure() === "fuelconsumption") { 29671 this.amount = ilib.Measurement.FuelConsumption.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 29672 } else { 29673 throw "Cannot convert unit " + options.amount.unit + " to fuelconsumption"; 29674 } 29675 } else if (typeof(options.amount) !== 'undefined') { 29676 this.amount = parseFloat(options.amount); 29677 } 29678 } 29679 }; 29680 29681 29682 ilib.Measurement.FuelConsumption.ratios = [ 29683 "km/liter", 29684 "liter/100km", 29685 "mpg", 29686 "mpg(imp)" 29687 ]; 29688 29689 ilib.Measurement.FuelConsumption.prototype = new ilib.Measurement({}); 29690 ilib.Measurement.FuelConsumption.prototype.parent = ilib.Measurement; 29691 ilib.Measurement.FuelConsumption.prototype.constructor = ilib.Measurement.FuelConsumption; 29692 29693 /** 29694 * Return the type of this measurement. Examples are "mass", 29695 * "length", "speed", etc. Measurements can only be converted 29696 * to measurements of the same type.<p> 29697 * 29698 * The type of the units is determined automatically from the 29699 * units. For example, the unit "grams" is type "mass". Use the 29700 * static call {@link ilib.Measurement.getAvailableUnits} 29701 * to find out what units this version of ilib supports. 29702 * 29703 * @return {string} the name of the type of this measurement 29704 */ 29705 ilib.Measurement.FuelConsumption.prototype.getMeasure = function() { 29706 return "fuelconsumption"; 29707 }; 29708 29709 /** 29710 * Return a new measurement instance that is converted to a new 29711 * measurement unit. Measurements can only be converted 29712 * to measurements of the same type.<p> 29713 * 29714 * @param {string} to The name of the units to convert to 29715 * @return {ilib.Measurement|undefined} the converted measurement 29716 * or undefined if the requested units are for a different 29717 * measurement type 29718 */ 29719 ilib.Measurement.FuelConsumption.prototype.convert = function(to) { 29720 if (!to || typeof(ilib.Measurement.FuelConsumption.ratios[this.normalizeUnits(to)]) === 'undefined') { 29721 return undefined; 29722 } 29723 return new ilib.Measurement({ 29724 unit: to, 29725 amount: this 29726 }); 29727 }; 29728 /*["km/liter", "liter/100km", "mpg", "mpg(imp)"*/ 29729 ilib.Measurement.FuelConsumption.aliases = { 29730 "Km/liter": "km/liter", 29731 "KM/Liter": "km/liter", 29732 "KM/L": "km/liter", 29733 "Kilometers Per Liter": "km/liter", 29734 "kilometers per liter": "km/liter", 29735 "km/l": "km/liter", 29736 "Kilometers/Liter": "km/liter", 29737 "Kilometer/Liter": "km/liter", 29738 "kilometers/liter": "km/liter", 29739 "kilometer/liter": "km/liter", 29740 "km/liter": "km/liter", 29741 "Liter/100km": "liter/100km", 29742 "Liters/100km": "liter/100km", 29743 "Liter/100kms": "liter/100km", 29744 "Liters/100kms": "liter/100km", 29745 "liter/100km": "liter/100km", 29746 "liters/100kms": "liter/100km", 29747 "liters/100km": "liter/100km", 29748 "liter/100kms": "liter/100km", 29749 "Liter/100KM": "liter/100km", 29750 "Liters/100KM": "liter/100km", 29751 "L/100km": "liter/100km", 29752 "L/100KM": "liter/100km", 29753 "l/100KM": "liter/100km", 29754 "l/100km": "liter/100km", 29755 "l/100kms": "liter/100km", 29756 "MPG(US)": "mpg", 29757 "USMPG ": "mpg", 29758 "mpg": "mpg", 29759 "mpgUS": "mpg", 29760 "mpg(US)": "mpg", 29761 "mpg(us)": "mpg", 29762 "mpg-us": "mpg", 29763 "mpg Imp": "mpg(imp)", 29764 "MPG(imp)": "mpg(imp)", 29765 "mpg(imp)": "mpg(imp)", 29766 "mpg-imp": "mpg(imp)" 29767 }; 29768 29769 ilib.Measurement.FuelConsumption.metricToUScustomary = { 29770 "km/liter": "mpg", 29771 "liter/100km": "mpg" 29772 }; 29773 ilib.Measurement.FuelConsumption.metricToImperial = { 29774 "km/liter": "mpg(imp)", 29775 "liter/100km": "mpg(imp)" 29776 }; 29777 29778 ilib.Measurement.FuelConsumption.imperialToMetric = { 29779 "mpg(imp)": "km/liter" 29780 }; 29781 ilib.Measurement.FuelConsumption.imperialToUScustomary = { 29782 "mpg(imp)": "mpg" 29783 }; 29784 29785 ilib.Measurement.FuelConsumption.uScustomaryToImperial = { 29786 "mpg": "mpg(imp)" 29787 }; 29788 ilib.Measurement.FuelConsumption.uScustomarylToMetric = { 29789 "mpg": "km/liter" 29790 }; 29791 29792 /** 29793 * Localize the measurement to the commonly used measurement in that locale. For example 29794 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 29795 * the formatted number should be automatically converted to the most appropriate 29796 * measure in the other system, in this case, mph. The formatted result should 29797 * appear as "37.3 mph". 29798 * 29799 * @abstract 29800 * @param {string} locale current locale string 29801 * @returns {ilib.Measurement} a new instance that is converted to locale 29802 */ 29803 ilib.Measurement.FuelConsumption.prototype.localize = function(locale) { 29804 var to; 29805 if (locale === "en-US") { 29806 to = ilib.Measurement.FuelConsumption.metricToUScustomary[this.unit] || 29807 ilib.Measurement.FuelConsumption.imperialToUScustomary[this.unit] || 29808 this.unit; 29809 } else if (locale === "en-GB") { 29810 to = ilib.Measurement.FuelConsumption.metricToImperial[this.unit] || 29811 ilib.Measurement.FuelConsumption.uScustomaryToImperial[this.unit] || 29812 this.unit; 29813 } else { 29814 to = ilib.Measurement.FuelConsumption.uScustomarylToMetric[this.unit] || 29815 ilib.Measurement.FuelConsumption.imperialToUScustomary[this.unit] || 29816 this.unit; 29817 } 29818 return new ilib.Measurement.FuelConsumption({ 29819 unit: to, 29820 amount: this 29821 }); 29822 }; 29823 29824 /** 29825 * Convert a FuelConsumption to another measure. 29826 * 29827 * @static 29828 * @param to {string} unit to convert to 29829 * @param from {string} unit to convert from 29830 * @param fuelConsumption {number} amount to be convert 29831 * @returns {number|undefined} the converted amount 29832 */ 29833 ilib.Measurement.FuelConsumption.convert = function(to, from, fuelConsumption) { 29834 from = ilib.Measurement.FuelConsumption.aliases[from] || from; 29835 to = ilib.Measurement.FuelConsumption.aliases[to] || to; 29836 var returnValue = 0; 29837 29838 switch (from) { 29839 case "km/liter": 29840 switch (to) { 29841 case "km/liter": 29842 returnValue = fuelConsumption * 1; 29843 break; 29844 case "liter/100km": 29845 returnValue = 100 / fuelConsumption; 29846 break; 29847 case "mpg": 29848 returnValue = fuelConsumption * 2.35215; 29849 break; 29850 case "mpg(imp)": 29851 returnValue = fuelConsumption * 2.82481; 29852 break; 29853 } 29854 break; 29855 case "liter/100km": 29856 switch (to) { 29857 case "km/liter": 29858 returnValue = 100 / fuelConsumption; 29859 break; 29860 case "liter/100km": 29861 returnValue = fuelConsumption * 1; 29862 break; 29863 case "mpg": 29864 returnValue = 235.215 / fuelConsumption; 29865 break; 29866 case "mpg(imp)": 29867 returnValue = 282.481 / fuelConsumption; 29868 break; 29869 } 29870 break; 29871 case "mpg": 29872 switch (to) { 29873 case "km/liter": 29874 returnValue = fuelConsumption * 0.425144; 29875 break; 29876 case "liter/100km": 29877 returnValue = 235.215 / fuelConsumption; 29878 break; 29879 case "mpg": 29880 returnValue = 1 * fuelConsumption; 29881 break; 29882 case "mpg(imp)": 29883 returnValue = 1.20095 * fuelConsumption; 29884 break; 29885 } 29886 break; 29887 case "mpg(imp)": 29888 switch (to) { 29889 case "km/liter": 29890 returnValue = fuelConsumption * 0.354006; 29891 break; 29892 case "liter/100km": 29893 returnValue = 282.481 / fuelConsumption; 29894 break; 29895 case "mpg": 29896 returnValue = 0.832674 * fuelConsumption; 29897 break; 29898 case "mpg(imp)": 29899 returnValue = 1 * fuelConsumption; 29900 break; 29901 } 29902 break; 29903 } 29904 return returnValue; 29905 }; 29906 29907 /** 29908 * Scale the measurement unit to an acceptable level. The scaling 29909 * happens so that the integer part of the amount is as small as 29910 * possible without being below zero. This will result in the 29911 * largest units that can represent this measurement without 29912 * fractions. Measurements can only be scaled to other measurements 29913 * of the same type. 29914 * 29915 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 29916 * or undefined if the system can be inferred from the current measure 29917 * @return {ilib.Measurement} a new instance that is scaled to the 29918 * right level 29919 */ 29920 ilib.Measurement.FuelConsumption.prototype.scale = function(measurementsystem) { 29921 return new ilib.Measurement.FuelConsumption({ 29922 unit: this.unit, 29923 amount: this.amount 29924 }); 29925 }; 29926 29927 /** 29928 * @private 29929 * @static 29930 */ 29931 ilib.Measurement.FuelConsumption.getMeasures = function() { 29932 var ret = []; 29933 ret.push("km/liter"); 29934 ret.push("liter/100km"); 29935 ret.push("mpg"); 29936 ret.push("mpg(imp)"); 29937 29938 return ret; 29939 }; 29940 29941 //register with the factory method 29942 ilib.Measurement._constructors["fuelconsumption"] = ilib.Measurement.FuelConsumption; 29943 29944 /* 29945 * volume.js - Unit conversions for volume 29946 * 29947 * Copyright © 2014, JEDLSoft 29948 * 29949 * Licensed under the Apache License, Version 2.0 (the "License"); 29950 * you may not use this file except in compliance with the License. 29951 * You may obtain a copy of the License at 29952 * 29953 * http://www.apache.org/licenses/LICENSE-2.0 29954 * 29955 * 29956 * Unless required by applicable law or agreed to in writing, software 29957 * distributed under the License is distributed on an "AS IS" BASIS, 29958 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29959 * 29960 * See the License for the specific language governing permissions and 29961 * limitations under the License. 29962 */ 29963 29964 /* 29965 !depends 29966 ilibglobal.js 29967 unit.js 29968 */ 29969 29970 /** 29971 * @class 29972 * Create a new Volume measurement instance. 29973 * 29974 * @constructor 29975 * @extends ilib.Measurement 29976 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 29977 * the construction of this instance 29978 */ 29979 ilib.Measurement.Volume = function (options) { 29980 this.unit = "cubic meter"; 29981 this.amount = 0; 29982 this.aliases = ilib.Measurement.Volume.aliases; // share this table in all instances 29983 29984 if (options) { 29985 if (typeof(options.unit) !== 'undefined') { 29986 this.originalUnit = options.unit; 29987 this.unit = this.aliases[options.unit] || options.unit; 29988 } 29989 29990 if (typeof(options.amount) === 'object') { 29991 if (options.amount.getMeasure() === "volume") { 29992 this.amount = ilib.Measurement.Volume.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 29993 } else { 29994 throw "Cannot convert unit " + options.amount.unit + " to a volume"; 29995 } 29996 } else if (typeof(options.amount) !== 'undefined') { 29997 this.amount = parseFloat(options.amount); 29998 } 29999 } 30000 30001 if (typeof(ilib.Measurement.Volume.ratios[this.unit]) === 'undefined') { 30002 throw "Unknown unit: " + options.unit; 30003 } 30004 }; 30005 30006 ilib.Measurement.Volume.ratios = { 30007 /* 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, */ 30008 "tsp" : [1, 1, 0.333333, 0.300781, 0.166667, 0.0208333, 0.0104167, 0.00130208, 0.00130208, 0.000174063, 4.92892, 0.00492892, 4.9289e-6, 0.832674, 0.277558, 0.173474, 0.00867369, 0.00433684, 0.00108421 ], 30009 "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 ], 30010 "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 ], 30011 "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 ], 30012 "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 ], 30013 "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 ], 30014 "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 ], 30015 "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 ], 30016 "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 ], 30017 "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 ], 30018 "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 ], 30019 "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 ], 30020 "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 ], 30021 "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 ], 30022 "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 ], 30023 "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 ], 30024 "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 ], 30025 "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 ] 30026 }; 30027 30028 ilib.Measurement.Volume.prototype = new ilib.Measurement({}); 30029 ilib.Measurement.Volume.prototype.parent = ilib.Measurement; 30030 ilib.Measurement.Volume.prototype.constructor = ilib.Measurement.Volume; 30031 30032 /** 30033 * Return the type of this measurement. Examples are "mass", 30034 * "length", "speed", etc. Measurements can only be converted 30035 * to measurements of the same type.<p> 30036 * 30037 * The type of the units is determined automatically from the 30038 * units. For example, the unit "grams" is type "mass". Use the 30039 * static call {@link ilib.Measurement.getAvailableUnits} 30040 * to find out what units this version of ilib supports. 30041 * 30042 * @return {string} the name of the type of this measurement 30043 */ 30044 ilib.Measurement.Volume.prototype.getMeasure = function() { 30045 return "volume"; 30046 }; 30047 30048 /** 30049 * Return a new measurement instance that is converted to a new 30050 * measurement unit. Measurements can only be converted 30051 * to measurements of the same type.<p> 30052 * 30053 * @param {string} to The name of the units to convert to 30054 * @return {ilib.Measurement|undefined} the converted measurement 30055 * or undefined if the requested units are for a different 30056 * measurement type 30057 */ 30058 ilib.Measurement.Volume.prototype.convert = function(to) { 30059 if (!to || typeof(ilib.Measurement.Volume.ratios[this.normalizeUnits(to)]) === 'undefined') { 30060 return undefined; 30061 } 30062 return new ilib.Measurement({ 30063 unit: to, 30064 amount: this 30065 }); 30066 }; 30067 30068 ilib.Measurement.Volume.aliases = { 30069 "US gal": "gallon", 30070 "US gallon": "gallon", 30071 "US Gal": "gallon", 30072 "US Gallons": "gallon", 30073 "Gal(US)": "gallon", 30074 "gal(US)": "gallon", 30075 "gallon": "gallon", 30076 "quart": "quart", 30077 "US quart": "quart", 30078 "US quarts": "quart", 30079 "US Quart": "quart", 30080 "US Quarts": "quart", 30081 "US qt": "quart", 30082 "Qt(US)": "quart", 30083 "qt(US)": "quart", 30084 "US pint": "pint", 30085 "US Pint": "pint", 30086 "pint": "pint", 30087 "pint(US)": "pint", 30088 "Pint(US)": "pint", 30089 "US cup": "cup", 30090 "US Cup": "cup", 30091 "cup(US)": "cup", 30092 "Cup(US)": "cup", 30093 "cup": "cup", 30094 "us ounce": "us ounce", 30095 "US ounce": "us ounce", 30096 "℥": "us ounce", 30097 "US Oz": "us ounce", 30098 "oz(US)": "us ounce", 30099 "Oz(US)": "us ounce", 30100 "US tbsp": "tbsp", 30101 "tbsp": "tbsp", 30102 "tbsp(US)": "tbsp", 30103 "US tablespoon": "tbsp", 30104 "US tsp": "tsp", 30105 "tsp(US)": "tsp", 30106 "tsp": "tsp", 30107 "Cubic meter": "cubic meter", 30108 "cubic meter": "cubic meter", 30109 "Cubic metre": "cubic meter", 30110 "cubic metre": "cubic meter", 30111 "m3": "cubic meter", 30112 "Liter": "liter", 30113 "Liters": "liter", 30114 "liter": "liter", 30115 "L": "liter", 30116 "l": "liter", 30117 "Milliliter": "milliliter", 30118 "ML": "milliliter", 30119 "ml": "milliliter", 30120 "milliliter": "milliliter", 30121 "mL": "milliliter", 30122 "Imperial gal": "imperial gallon", 30123 "imperial gallon": "imperial gallon", 30124 "Imperial gallon": "imperial gallon", 30125 "gallon(imperial)": "imperial gallon", 30126 "gal(imperial)": "imperial gallon", 30127 "Imperial quart": "imperial quart", 30128 "imperial quart": "imperial quart", 30129 "Imperial Quart": "imperial quart", 30130 "IMperial qt": "imperial quart", 30131 "qt(Imperial)": "imperial quart", 30132 "quart(imperial)": "imperial quart", 30133 "Imperial pint": "imperial pint", 30134 "imperial pint": "imperial pint", 30135 "pint(Imperial)": "imperial pint", 30136 "imperial oz": "imperial ounce", 30137 "imperial ounce": "imperial ounce", 30138 "Imperial Ounce": "imperial ounce", 30139 "Imperial tbsp": "imperial tbsp", 30140 "imperial tbsp": "imperial tbsp", 30141 "tbsp(Imperial)": "imperial tbsp", 30142 "Imperial tsp": "imperial tsp", 30143 "imperial tsp": "imperial tsp", 30144 "tsp(Imperial)": "imperial tsp", 30145 "Cubic foot": "cubic foot", 30146 "cubic foot": "cubic foot", 30147 "Cubic Foot": "cubic foot", 30148 "Cubic feet": "cubic foot", 30149 "cubic Feet": "cubic foot", 30150 "cubic ft": "cubic foot", 30151 "ft3": "cubic foot", 30152 "Cubic inch": "cubic inch", 30153 "Cubic inches": "cubic inch", 30154 "cubic inches": "cubic inch", 30155 "cubic inch": "cubic inch", 30156 "cubic in": "cubic inch", 30157 "cu in": "cubic inch", 30158 "cu inch": "cubic inch", 30159 "inch³": "cubic inch", 30160 "in³": "cubic inch", 30161 "inch^3": "cubic inch", 30162 "in^3": "cubic inch", 30163 "c.i": "cubic inch", 30164 "CI": "cubic inch", 30165 "cui": "cubic inch" 30166 }; 30167 30168 /** 30169 * Convert a volume to another measure. 30170 * @static 30171 * @param to {string} unit to convert to 30172 * @param from {string} unit to convert from 30173 * @param volume {number} amount to be convert 30174 * @returns {number|undefined} the converted amount 30175 */ 30176 ilib.Measurement.Volume.convert = function(to, from, volume) { 30177 from = ilib.Measurement.Volume.aliases[from] || from; 30178 to = ilib.Measurement.Volume.aliases[to] || to; 30179 var fromRow = ilib.Measurement.Volume.ratios[from]; 30180 var toRow = ilib.Measurement.Volume.ratios[to]; 30181 if (typeof(from) === 'undefined' || typeof(to) === 'undefined') { 30182 return undefined; 30183 } 30184 var result = volume * fromRow[toRow[0]]; 30185 return result; 30186 }; 30187 30188 /** 30189 * @private 30190 * @static 30191 */ 30192 ilib.Measurement.Volume.getMeasures = function () { 30193 var ret = []; 30194 for (var m in ilib.Measurement.Volume.ratios) { 30195 ret.push(m); 30196 } 30197 return ret; 30198 }; 30199 ilib.Measurement.Volume.metricSystem = { 30200 "milliliter": 10, 30201 "liter": 11, 30202 "cubic meter": 12 30203 }; 30204 ilib.Measurement.Volume.imperialSystem = { 30205 "imperial tsp": 13, 30206 "imperial tbsp": 14, 30207 "imperial ounce": 15, 30208 "imperial pint": 16, 30209 "imperial quart": 17, 30210 "imperial gallon": 18 30211 }; 30212 ilib.Measurement.Volume.uscustomarySystem = { 30213 "tsp": 1, 30214 "tbsp": 2, 30215 "cubic inch": 3, 30216 "us ounce": 4, 30217 "cup": 5, 30218 "pint": 6, 30219 "quart": 7, 30220 "gallon": 8, 30221 "cubic foot": 9 30222 }; 30223 30224 ilib.Measurement.Volume.metricToUScustomary = { 30225 "milliliter": "tsp", 30226 "liter": "quart", 30227 "cubic meter": "cubic foot" 30228 }; 30229 ilib.Measurement.Volume.metricToImperial = { 30230 "milliliter": "imperial tsp", 30231 "liter": "imperial quart", 30232 "cubic meter": "imperial gallon" 30233 }; 30234 30235 ilib.Measurement.Volume.imperialToMetric = { 30236 "imperial tsp": "milliliter", 30237 "imperial tbsp": "milliliter", 30238 "imperial ounce": "milliliter", 30239 "imperial pint": "liter", 30240 "imperial quart": "liter", 30241 "imperial gallon": "cubic meter" 30242 }; 30243 ilib.Measurement.Volume.imperialToUScustomary = { 30244 "imperial tsp": "tsp", 30245 "imperial tbsp": "tbsp", 30246 "imperial ounce": "us ounce", 30247 "imperial pint": "pint", 30248 "imperial quart": "quart", 30249 "imperial gallon": "gallon" 30250 }; 30251 30252 ilib.Measurement.Volume.uScustomaryToImperial = { 30253 "tsp": "imperial tsp", 30254 "tbsp": "imperial tbsp", 30255 "cubic inch": "imperial tbsp", 30256 "us ounce": "imperial ounce", 30257 "cup": "imperial ounce", 30258 "pint": "imperial pint", 30259 "quart": "imperial quart", 30260 "gallon": "imperial gallon", 30261 "cubic foot": "imperial gallon" 30262 }; 30263 ilib.Measurement.Volume.uScustomarylToMetric = { 30264 "tsp": "milliliter", 30265 "tbsp": "milliliter", 30266 "cubic inch": "milliliter", 30267 "us ounce": "milliliter", 30268 "cup": "milliliter", 30269 "pint": "liter", 30270 "quart": "liter", 30271 "gallon": "cubic meter", 30272 "cubic foot": "cubic meter" 30273 }; 30274 30275 /** 30276 * Localize the measurement to the commonly used measurement in that locale. For example 30277 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 30278 * the formatted number should be automatically converted to the most appropriate 30279 * measure in the other system, in this case, mph. The formatted result should 30280 * appear as "37.3 mph". 30281 * 30282 * @abstract 30283 * @param {string} locale current locale string 30284 * @returns {ilib.Measurement} a new instance that is converted to locale 30285 */ 30286 ilib.Measurement.Volume.prototype.localize = function(locale) { 30287 var to; 30288 if (locale === "en-US") { 30289 to = ilib.Measurement.Volume.metricToUScustomary[this.unit] || 30290 ilib.Measurement.Volume.imperialToUScustomary[this.unit] || 30291 this.unit; 30292 } else if (locale === "en-GB") { 30293 to = ilib.Measurement.Volume.metricToImperial[this.unit] || 30294 ilib.Measurement.Volume.uScustomaryToImperial[this.unit] || 30295 this.unit; 30296 } else { 30297 to = ilib.Measurement.Volume.uScustomarylToMetric[this.unit] || 30298 ilib.Measurement.Volume.imperialToUScustomary[this.unit] || 30299 this.unit; 30300 } 30301 return new ilib.Measurement.Volume({ 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 {ilib.Measurement} a new instance that is scaled to the 30318 * right level 30319 */ 30320 ilib.Measurement.Volume.prototype.scale = function(measurementsystem) { 30321 var fromRow = ilib.Measurement.Volume.ratios[this.unit]; 30322 var mSystem; 30323 30324 if (measurementsystem === "metric"|| (typeof(measurementsystem) === 'undefined' 30325 && typeof(ilib.Measurement.Volume.metricSystem[this.unit]) !== 'undefined')) { 30326 mSystem = ilib.Measurement.Volume.metricSystem; 30327 } else if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined' 30328 && typeof(ilib.Measurement.Volume.uscustomarySystem[this.unit]) !== 'undefined')) { 30329 mSystem = ilib.Measurement.Volume.uscustomarySystem; 30330 } else if (measurementsystem === "imperial"|| (typeof(measurementsystem) === 'undefined' 30331 && typeof(ilib.Measurement.Volume.imperialSystem[this.unit]) !== 'undefined')) { 30332 mSystem = ilib.Measurement.Volume.imperialSystem; 30333 } 30334 30335 var volume = this.amount; 30336 var munit = this.unit; 30337 30338 for (var m in mSystem) { 30339 var tmp = this.amount * fromRow[mSystem[m]]; 30340 if (tmp < 1) break; 30341 volume = tmp; 30342 munit = m; 30343 } 30344 30345 return new ilib.Measurement.Volume({ 30346 unit: munit, 30347 amount: volume 30348 }); 30349 }; 30350 30351 30352 30353 //register with the factory method 30354 ilib.Measurement._constructors["volume"] = ilib.Measurement.Volume; 30355 30356 30357 /* 30358 * Energy.js - Unit conversions for Energys/energys 30359 * 30360 * Copyright © 2014, JEDLSoft 30361 * 30362 * Licensed under the Apache License, Version 2.0 (the "License"); 30363 * you may not use this file except in compliance with the License. 30364 * You may obtain a copy of the License at 30365 * 30366 * http://www.apache.org/licenses/LICENSE-2.0 30367 * 30368 * Unless required by applicable law or agreed to in writing, software 30369 * distributed under the License is distributed on an "AS IS" BASIS, 30370 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30371 * 30372 * See the License for the specific language governing permissions and 30373 * limitations under the License. 30374 */ 30375 30376 /* 30377 !depends 30378 ilibglobal.js 30379 */ 30380 30381 /** 30382 * @class 30383 * Create a new energy measurement instance. 30384 * 30385 * @constructor 30386 * @extends ilib.Measurement 30387 * @param options {{unit:string,amount:number|string|undefined}} Options controlling 30388 * the construction of this instance 30389 */ 30390 ilib.Measurement.Energy = function (options) { 30391 this.unit = "ns"; 30392 this.amount = 0; 30393 this.aliases = ilib.Measurement.Energy.aliases; // share this table in all instances 30394 30395 if (options) { 30396 if (typeof(options.unit) !== 'undefined') { 30397 this.originalUnit = options.unit; 30398 this.unit = this.aliases[options.unit] || options.unit; 30399 } 30400 30401 if (typeof(options.amount) === 'object') { 30402 if (options.amount.getMeasure() === "energy") { 30403 this.amount = ilib.Measurement.Energy.convert(this.unit, options.amount.getUnit(), options.amount.getAmount()); 30404 } else { 30405 throw "Cannot convert units " + options.amount.unit + " to a energy"; 30406 } 30407 } else if (typeof(options.amount) !== 'undefined') { 30408 this.amount = parseFloat(options.amount); 30409 } 30410 } 30411 30412 if (typeof(ilib.Measurement.Energy.ratios[this.unit]) === 'undefined') { 30413 throw "Unknown unit: " + options.unit; 30414 } 30415 }; 30416 30417 ilib.Measurement.Energy.ratios = { 30418 /* index mJ J BTU kJ Wh Cal MJ kWh gJ MWh GWh */ 30419 "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 ], 30420 "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 ], 30421 "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 ], 30422 "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 ], 30423 "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 ], 30424 "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 ], 30425 "megajoule": [ 7, 1e+9, 1e+6, 947.81707775, 1000, 277.77777778, 238.84589663, 1, 0.27777777778, 0.001, 2.7777777778e-4, 2.7777777778e-7 ], 30426 "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 ], 30427 "gigajoule": [ 9, 1e+12, 1e+9, 947817.07775, 1e+6, 277777.77778, 238845.89663, 1000, 277.77777778, 1, 0.27777777778, 2.7777777778e-4 ], 30428 "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 ], 30429 "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 ] 30430 }; 30431 30432 ilib.Measurement.Energy.prototype = new ilib.Measurement({}); 30433 ilib.Measurement.Energy.prototype.parent = ilib.Measurement; 30434 ilib.Measurement.Energy.prototype.constructor = ilib.Measurement.Energy; 30435 30436 /** 30437 * Return the type of this measurement. Examples are "mass", 30438 * "length", "speed", etc. Measurements can only be converted 30439 * to measurements of the same type.<p> 30440 * 30441 * The type of the units is determined automatically from the 30442 * units. For example, the unit "grams" is type "mass". Use the 30443 * static call {@link ilib.Measurement.getAvailableUnits} 30444 * to find out what units this version of ilib supports. 30445 * 30446 * @return {string} the name of the type of this measurement 30447 */ 30448 ilib.Measurement.Energy.prototype.getMeasure = function() { 30449 return "energy"; 30450 }; 30451 30452 /** 30453 * Return a new measurement instance that is converted to a new 30454 * measurement unit. Measurements can only be converted 30455 * to measurements of the same type.<p> 30456 * 30457 * @param {string} to The name of the units to convert to 30458 * @return {ilib.Measurement|undefined} the converted measurement 30459 * or undefined if the requested units are for a different 30460 * measurement type 30461 */ 30462 ilib.Measurement.Energy.prototype.convert = function(to) { 30463 if (!to || typeof(ilib.Measurement.Energy.ratios[this.normalizeUnits(to)]) === 'undefined') { 30464 return undefined; 30465 } 30466 return new ilib.Measurement({ 30467 unit: to, 30468 amount: this 30469 }); 30470 }; 30471 30472 ilib.Measurement.Energy.aliases = { 30473 "milli joule": "millijoule", 30474 "millijoule": "millijoule", 30475 "MilliJoule": "millijoule", 30476 "milliJ": "millijoule", 30477 "joule": "joule", 30478 "J": "joule", 30479 "j": "joule", 30480 "Joule": "joule", 30481 "Joules": "joule", 30482 "joules": "joule", 30483 "BTU": "BTU", 30484 "btu": "BTU", 30485 "British thermal unit": "BTU", 30486 "british thermal unit": "BTU", 30487 "kilo joule": "kilojoule", 30488 "kJ": "kilojoule", 30489 "kj": "kilojoule", 30490 "Kj": "kilojoule", 30491 "kiloJoule": "kilojoule", 30492 "kilojoule": "kilojoule", 30493 "kjoule": "kilojoule", 30494 "watt hour": "watt hour", 30495 "Wh": "watt hour", 30496 "wh": "watt hour", 30497 "watt-hour": "watt hour", 30498 "calorie": "calorie", 30499 "Cal": "calorie", 30500 "cal": "calorie", 30501 "Calorie": "calorie", 30502 "calories": "calorie", 30503 "mega joule": "megajoule", 30504 "MJ": "megajoule", 30505 "megajoule": "megajoule", 30506 "megajoules": "megajoule", 30507 "Megajoules": "megajoule", 30508 "megaJoules": "megajoule", 30509 "MegaJoules": "megajoule", 30510 "megaJoule": "megajoule", 30511 "MegaJoule": "megajoule", 30512 "kilo Watt hour": "kilowatt hour", 30513 "kWh": "kilowatt hour", 30514 "kiloWh": "kilowatt hour", 30515 "KiloWh": "kilowatt hour", 30516 "KiloWatt-hour": "kilowatt hour", 30517 "kilowatt hour": "kilowatt hour", 30518 "kilowatt-hour": "kilowatt hour", 30519 "KiloWatt-hours": "kilowatt hour", 30520 "kilowatt-hours": "kilowatt hour", 30521 "Kilo Watt-hour": "kilowatt hour", 30522 "Kilo Watt-hours": "kilowatt hour", 30523 "giga joule": "gigajoule", 30524 "gJ": "gigajoule", 30525 "GJ": "gigajoule", 30526 "GigaJoule": "gigajoule", 30527 "gigaJoule": "gigajoule", 30528 "gigajoule": "gigajoule", 30529 "GigaJoules": "gigajoule", 30530 "gigaJoules": "gigajoule", 30531 "Gigajoules": "gigajoule", 30532 "gigajoules": "gigajoule", 30533 "mega watt hour": "megawatt hour", 30534 "MWh": "megawatt hour", 30535 "MegaWh": "megawatt hour", 30536 "megaWh": "megawatt hour", 30537 "megaWatthour": "megawatt hour", 30538 "megaWatt-hour": "megawatt hour", 30539 "mega Watt-hour": "megawatt hour", 30540 "megaWatt hour": "megawatt hour", 30541 "megawatt hour": "megawatt hour", 30542 "mega Watt hour": "megawatt hour", 30543 "giga watt hour": "gigawatt hour", 30544 "gWh": "gigawatt hour", 30545 "GWh": "gigawatt hour", 30546 "gigaWh": "gigawatt hour", 30547 "gigaWatt-hour": "gigawatt hour", 30548 "gigawatt-hour": "gigawatt hour", 30549 "gigaWatt hour": "gigawatt hour", 30550 "gigawatt hour": "gigawatt hour", 30551 "gigawatthour": "gigawatt hour" 30552 }; 30553 30554 /** 30555 * Convert a energy to another measure. 30556 * @static 30557 * @param to {string} unit to convert to 30558 * @param from {string} unit to convert from 30559 * @param energy {number} amount to be convert 30560 * @returns {number|undefined} the converted amount 30561 */ 30562 ilib.Measurement.Energy.convert = function(to, from, energy) { 30563 from = ilib.Measurement.Energy.aliases[from] || from; 30564 to = ilib.Measurement.Energy.aliases[to] || to; 30565 var fromRow = ilib.Measurement.Energy.ratios[from]; 30566 var toRow = ilib.Measurement.Energy.ratios[to]; 30567 if (typeof(from) === 'undefined' || typeof(to) === 'undefined') { 30568 return undefined; 30569 } 30570 return energy * fromRow[toRow[0]]; 30571 }; 30572 30573 /** 30574 * @private 30575 * @static 30576 */ 30577 ilib.Measurement.Energy.getMeasures = function () { 30578 var ret = []; 30579 for (var m in ilib.Measurement.Energy.ratios) { 30580 ret.push(m); 30581 } 30582 return ret; 30583 }; 30584 30585 ilib.Measurement.Energy.metricJouleSystem = { 30586 "millijoule": 1, 30587 "joule": 2, 30588 "kilojoule": 4, 30589 "megajoule": 7, 30590 "gigajoule": 9 30591 }; 30592 ilib.Measurement.Energy.metricWattHourSystem = { 30593 "watt hour": 5, 30594 "kilowatt hour": 8, 30595 "megawatt hour": 10, 30596 "gigawatt hour": 11 30597 }; 30598 30599 ilib.Measurement.Energy.imperialSystem = { 30600 "BTU": 3 30601 }; 30602 ilib.Measurement.Energy.uscustomarySystem = { 30603 "calorie": 6 30604 }; 30605 30606 ilib.Measurement.Energy.metricToImperial = { 30607 "millijoule": "BTU", 30608 "joule": "BTU", 30609 "kilojoule": "BTU", 30610 "megajoule": "BTU", 30611 "gigajoule": "BTU" 30612 }; 30613 ilib.Measurement.Energy.imperialToMetric = { 30614 "BTU": "joule" 30615 }; 30616 30617 /** 30618 * Localize the measurement to the commonly used measurement in that locale. For example 30619 * If a user's locale is "en-US" and the measurement is given as "60 kmh", 30620 * the formatted number should be automatically converted to the most appropriate 30621 * measure in the other system, in this case, mph. The formatted result should 30622 * appear as "37.3 mph". 30623 * 30624 * @abstract 30625 * @param {string} locale current locale string 30626 * @returns {ilib.Measurement} a new instance that is converted to locale 30627 */ 30628 ilib.Measurement.Energy.prototype.localize = function(locale) { 30629 var to; 30630 if (locale === "en-GB") { 30631 to = ilib.Measurement.Energy.metricToImperial[this.unit] || this.unit; 30632 } else { 30633 to = ilib.Measurement.Energy.imperialToMetric[this.unit] || this.unit; 30634 } 30635 30636 return new ilib.Measurement.Energy({ 30637 unit: to, 30638 amount: this 30639 }); 30640 }; 30641 30642 /** 30643 * Scale the measurement unit to an acceptable level. The scaling 30644 * happens so that the integer part of the amount is as small as 30645 * possible without being below zero. This will result in the 30646 * largest units that can represent this measurement without 30647 * fractions. Measurements can only be scaled to other measurements 30648 * of the same type. 30649 * 30650 * @param {string=} measurementsystem system to use (uscustomary|imperial|metric), 30651 * or undefined if the system can be inferred from the current measure 30652 * @return {ilib.Measurement} a new instance that is scaled to the 30653 * right level 30654 */ 30655 ilib.Measurement.Energy.prototype.scale = function(measurementsystem) { 30656 var fromRow = ilib.Measurement.Energy.ratios[this.unit]; 30657 var mSystem; 30658 30659 if ((measurementsystem === "metric" && typeof(ilib.Measurement.Energy.metricJouleSystem[this.unit]) !== 'undefined')|| (typeof(measurementsystem) === 'undefined' 30660 && typeof(ilib.Measurement.Energy.metricJouleSystem[this.unit]) !== 'undefined')) { 30661 mSystem = ilib.Measurement.Energy.metricJouleSystem; 30662 } 30663 else if ((measurementsystem === "metric" && typeof(ilib.Measurement.Energy.metricWattHourSystem[this.unit]) !== 'undefined')|| (typeof(measurementsystem) === 'undefined' 30664 && typeof(ilib.Measurement.Energy.metricWattHourSystem[this.unit]) !== 'undefined')) { 30665 mSystem = ilib.Measurement.Energy.metricWattHourSystem; 30666 } 30667 30668 else if (measurementsystem === "uscustomary" || (typeof(measurementsystem) === 'undefined' 30669 && typeof(ilib.Measurement.Energy.uscustomarySystem[this.unit]) !== 'undefined')) { 30670 mSystem = ilib.Measurement.Energy.uscustomarySystem; 30671 } 30672 else if (measurementsystem === "imperial"|| (typeof(measurementsystem) === 'undefined' 30673 && typeof(ilib.Measurement.Energy.imperialSystem[this.unit]) !== 'undefined')) { 30674 mSystem = ilib.Measurement.Energy.imperialSystem; 30675 } 30676 30677 var energy = this.amount; 30678 var munit = this.unit; 30679 30680 for (var m in mSystem) { 30681 var tmp = this.amount * fromRow[mSystem[m]]; 30682 if (tmp < 1) break; 30683 energy = tmp; 30684 munit = m; 30685 } 30686 30687 return new ilib.Measurement.Energy({ 30688 unit: munit, 30689 amount: energy 30690 }); 30691 }; 30692 //register with the factory method 30693 ilib.Measurement._constructors["energy"] = ilib.Measurement.Energy; 30694 30695 /** 30696 * @license 30697 * Copyright © 2012-2013, JEDLSoft 30698 * 30699 * Licensed under the Apache License, Version 2.0 (the "License"); 30700 * you may not use this file except in compliance with the License. 30701 * You may obtain a copy of the License at 30702 * 30703 * http://www.apache.org/licenses/LICENSE-2.0 30704 * 30705 * Unless required by applicable law or agreed to in writing, software 30706 * distributed under the License is distributed on an "AS IS" BASIS, 30707 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30708 * 30709 * See the License for the specific language governing permissions and 30710 * limitations under the License. 30711 */ 30712 30713 /* 30714 * ilib-full-inc.js - metafile that includes all other js files 30715 */ 30716 30717 /* !depends 30718 ilibglobal.js 30719 daterangefmt.js 30720 date.js 30721 calendar/hebrewdate.js 30722 calendar/hebrew.js 30723 calendar/islamic.js 30724 calendar/islamicdate.js 30725 calendar/julian.js 30726 calendar/juliandate.js 30727 calendar/gregorian.js 30728 calendar/gregoriandate.js 30729 calendar/thaisolar.js 30730 calendar/thaisolardate.js 30731 calendar/persian.js 30732 calendar/persiandate.js 30733 calendar/persianastro.js 30734 calendar/persianastrodate.js 30735 calendar/han.js 30736 calendar/handate.js 30737 calendar/ethiopic.js 30738 calendar/ethiopicdate.js 30739 calendar/coptic.js 30740 calendar/copticdate.js 30741 numprs.js 30742 numfmt.js 30743 julianday.js 30744 datefmt.js 30745 calendar.js 30746 util/utils.js 30747 locale.js 30748 strings.js 30749 durfmt.js 30750 resources.js 30751 ctype.js 30752 localeinfo.js 30753 daterangefmt.js 30754 ctype.isalnum.js 30755 ctype.isalpha.js 30756 ctype.isascii.js 30757 ctype.isblank.js 30758 ctype.iscntrl.js 30759 ctype.isdigit.js 30760 ctype.isgraph.js 30761 ctype.isideo.js 30762 ctype.islower.js 30763 ctype.isprint.js 30764 ctype.ispunct.js 30765 ctype.isspace.js 30766 ctype.isupper.js 30767 ctype.isxdigit.js 30768 ctype.isscript.js 30769 scriptinfo.js 30770 nameprs.js 30771 namefmt.js 30772 addressprs.js 30773 addressfmt.js 30774 collate.js 30775 nfkc/all.js 30776 localematch.js 30777 normstring.js 30778 maps/casemapper.js 30779 glyphstring.js 30780 phone/phonefmt.js 30781 phone/phonegeo.js 30782 phone/phonenum.js 30783 unit.js 30784 unitfmt.js 30785 units/length.js 30786 units/speed.js 30787 units/digitalStorage.js 30788 units/temperature.js 30789 units/unknown.js 30790 units/time.js 30791 units/mass.js 30792 units/area.js 30793 units/fuelConsumption.js 30794 units/volume.js 30795 units/energy.js 30796 */ 30797 30798