1index.html//JS Globalize @0-2CE52E34 2/*! 3 * Globalize 4 * 5 * http://github.com/jquery/globalize 6 * 7 * Copyright Software Freedom Conservancy, Inc. 8 * Dual licensed under the MIT or GPL Version 2 licenses. 9 * http://jquery.org/license 10 */ 11 12(function (window, undefined) { 13 14var arrayLocaleInfo = "en|en|US|Yes;No;|2|.|,|January;February;March;April;May;June;July;August;September;October;November;December|Jan;Feb;Mar;Apr;May;Jun;Jul;Aug;Sep;Oct;Nov;Dec|Sunday;Monday;Tuesday;Wednesday;Thursday;Friday;Saturday|Sun;Mon;Tue;Wed;Thu;Fri;Sat|m/d/yyyy|dddd, mmmm dd, yyyy|h:nn tt|h:nn:ss tt|0|AM|PM".split("|"); 15 function convertTojQueryDateFormat(srcFormat, expand) { 16 expand = !expand ? false : true; 17 switch (srcFormat) { 18 case "": 19 case "ShortDate": 20 return expand ? Globalize.culture().calendars.standard.patterns[srcFormat] : srcFormat; 21 // return "d"; // "M/d/yyyy" 22 case "GeneralDate": 23 return expand ? Globalize.culture().calendars.standard.patterns[srcFormat] : srcFormat; 24 // return "f"; // "dddd, MMMM dd, yyyy h:mm tt" 25 case "LongDate": 26 return expand ? Globalize.culture().calendars.standard.patterns[srcFormat] : srcFormat; 27 // return "D"; // "dddd, MMMM dd, yyyy" 28 case "ShortTime": 29 return expand ? Globalize.culture().calendars.standard.patterns[srcFormat] : srcFormat; 30 // return "t"; // "h:mm tt" 31 case "LongTime": 32 return expand ? Globalize.culture().calendars.standard.patterns[srcFormat] : srcFormat; 33 // return "T"; // "h:mm:ss tt"; 34 } 35 srcFormat = srcFormat.replace("AM/PM", "tt"); 36 srcFormat = srcFormat.replace("am/pm", "tt"); 37 srcFormat = srcFormat.replace("M/P", "t"); 38 srcFormat = srcFormat.replace("a/p", "t"); 39 40 srcFormat = srcFormat.replace(/w/g, ""); 41 srcFormat = srcFormat.replace(/m/g, "M"); 42 srcFormat = srcFormat.replace(/n/g, "m"); 43 return srcFormat; 44 } 45 46 function getLocaleInfo(id) 47 { 48 switch (id) 49 { 50 case "LanguageAndCountry": return arrayLocaleInfo[0]; 51 case "language": return arrayLocaleInfo[1]; 52 case "country": return arrayLocaleInfo[2]; 53 case "booleanFormat": return arrayLocaleInfo[3]; 54 case "decimalDigits": return parseInt(arrayLocaleInfo[4]); 55 case "decimalSeparator": return arrayLocaleInfo[5]; 56 case "groupSeparator": return arrayLocaleInfo[6]; 57 case "monthNames": return arrayLocaleInfo[7]; 58 case "monthShortNames": return arrayLocaleInfo[8]; 59 case "weekdayNames": return arrayLocaleInfo[9]; 60 case "weekdayShortNames": return arrayLocaleInfo[10]; 61 case "shortDate": return convertTojQueryDateFormat(arrayLocaleInfo[11], true); 62 case "longDate": return convertTojQueryDateFormat(arrayLocaleInfo[12], true); 63 case "shortTime": return convertTojQueryDateFormat(arrayLocaleInfo[13], true); 64 case "longTime": return convertTojQueryDateFormat(arrayLocaleInfo[14], true); 65 case "firstWeekDay": return parseInt(arrayLocaleInfo[15]); 66 case "AMDesignator": return arrayLocaleInfo[16]; 67 case "PMDesignator": return arrayLocaleInfo[17]; 68 } 69 return ""; 70 } 71 72 var listMonths = getLocaleInfo("monthNames").split(";"); 73 listMonths.push(""); 74 var listShortMonths = getLocaleInfo("monthShortNames").split(";"); 75 listShortMonths.push(""); 76 var firstWeekDay = getLocaleInfo("firstWeekDay"); 77 var listWeekdays = getLocaleInfo("weekdayNames").split(";"); 78 var listShortWeekdays = getLocaleInfo("weekdayShortNames").split(";"); 79 var amDesignator = getLocaleInfo("AMDesignator"); 80 var pmDesignator = getLocaleInfo("PMDesignator"); 81 82 83 84 85 var Globalize, 86 // private variables 87 regexHex, 88 regexInfinity, 89 regexParseFloat, 90 regexTrim, 91 // private JavaScript utility functions 92 arrayIndexOf, 93 endsWith, 94 extend, 95 isArray, 96 isFunction, 97 isObject, 98 startsWith, 99 trim, 100 truncate, 101 zeroPad, 102 // private Globalization utility functions 103 appendPreOrPostMatch, 104 expandFormat, 105 formatDate, 106 formatNumber, 107 getTokenRegExp, 108 getEra, 109 getEraYear, 110 parseExact, 111 parseNegativePattern; 112 113 // Global variable (Globalize) or CommonJS module (globalize) 114 Globalize = function (cultureSelector) { 115 return new Globalize.prototype.init(cultureSelector); 116 }; 117 118 if (typeof require !== "undefined" 119 && typeof exports !== "undefined" 120 && typeof module !== "undefined") { 121 // Assume CommonJS 122 module.exports = Globalize; 123 } else { 124 // Export as global variable 125 window.Globalize = Globalize; 126 } 127 128 Globalize.cultures = {}; 129 130 Globalize.prototype = { 131 constructor: Globalize, 132 init: function (cultureSelector) { 133 this.cultures = Globalize.cultures; 134 this.cultureSelector = cultureSelector; 135 136 return this; 137 } 138 }; 139 Globalize.prototype.init.prototype = Globalize.prototype; 140 141 // 1. When defining a culture, all fields are required except the ones stated as optional. 142 // 2. Each culture should have a ".calendars" object with at least one calendar named "standard" 143 // which serves as the default calendar in use by that culture. 144 // 3. Each culture should have a ".calendar" object which is the current calendar being used, 145 // it may be dynamically changed at any time to one of the calendars in ".calendars". 146 Globalize.cultures["default"] = { 147 // A unique name for the culture in the form <language code>-<country/region code> 148 name: getLocaleInfo("LanguageAndCountry"), 149 // the name of the culture in the english language 150 englishName: getLocaleInfo("language"), 151 // the name of the culture in its own language 152 nativeName: getLocaleInfo("language"), 153 // whether the culture uses right-to-left text 154 isRTL: false, 155 // "language" is used for so-called "specific" cultures. 156 // For example, the culture "es-CL" means "Spanish, in Chili". 157 // It represents the Spanish-speaking culture as it is in Chili, 158 // which might have different formatting rules or even translations 159 // than Spanish in Spain. A "neutral" culture is one that is not 160 // specific to a region. For example, the culture "es" is the generic 161 // Spanish culture, which may be a more generalized version of the language 162 // that may or may not be what a specific culture expects. 163 // For a specific culture like "es-CL", the "language" field refers to the 164 // neutral, generic culture information for the language it is using. 165 // This is not always a simple matter of the string before the dash. 166 // For example, the "zh-Hans" culture is netural (Simplified Chinese). 167 // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage 168 // field is "zh-CHS", not "zh". 169 // This field should be used to navigate from a specific culture to it's 170 // more general, neutral culture. If a culture is already as general as it 171 // can get, the language may refer to itself. 172 language: getLocaleInfo("language"), 173 // numberFormat defines general number formatting rules, like the digits in 174 // each grouping, the group separator, and how negative numbers are displayed. 175 numberFormat: { 176 // [negativePattern] 177 // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency,
178 // but is still defined as an array for consistency with them. 179 // negativePattern: one of "(n)|-n|- n|n-|n -" 180 pattern: ["-n"], 181 // number of decimal places normally shown 182 decimals: getLocaleInfo("decimalDigits"), 183 // string that separates number groups, as in 1,000,000 184 ",": getLocaleInfo("groupSeparator"), 185 // string that separates a number from the fractional portion, as in 1.99 186 ".": getLocaleInfo("decimalSeparator"), 187 // array of numbers indicating the size of each number group. 188 // TODO: more detailed description and example 189 groupSizes: [3], 190 // symbol used for positive numbers 191 "+": "+", 192 // symbol used for negative numbers 193 "-": "-", 194 // symbol used for NaN (Not-A-Number) 195 NaN: "NaN", 196 // symbol used for Negative Infinity 197 negativeInfinity: "-Infinity", 198 // symbol used for Positive Infinity 199 positiveInfinity: "Infinity", 200 percent: { 201 // [negativePattern, positivePattern] 202 // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %" 203 // positivePattern: one of "n %|n%|%n|% n" 204 pattern: ["-n %", "n %"], 205 // number of decimal places normally shown 206 decimals: getLocaleInfo("decimalDigits"), 207 // array of numbers indicating the size of each number group. 208 // TODO: more detailed description and example 209 groupSizes: [3], 210 // string that separates number groups, as in 1,000,000 211 ",": getLocaleInfo("groupSeparator"), 212 // string that separates a number from the fractional portion, as in 1.99 213 ".": getLocaleInfo("decimalSeparator"), 214 // symbol used to represent a percentage 215 symbol: "%" 216 }, 217 currency: { 218 // [negativePattern, positivePattern] 219 // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)" 220 // positivePattern: one of "$n|n$|$ n|n $" 221 pattern: ["($n)", "$n"], 222 // number of decimal places normally shown 223 decimals: getLocaleInfo("decimalDigits"), 224 // array of numbers indicating the size of each number group. 225 // TODO: more detailed description and example 226 groupSizes: [3], 227 // string that separates number groups, as in 1,000,000 228 ",": getLocaleInfo("groupSeparator"), 229 // string that separates a number from the fractional portion, as in 1.99 230 ".": getLocaleInfo("decimalSeparator"), 231 // symbol used to represent currency 232 symbol: "$" 233 } 234 }, 235 // calendars defines all the possible calendars used by this culture. 236 // There should be at least one defined with name "standard", and is the default 237 // calendar used by the culture. 238 // A calendar contains information about how dates are formatted, information about 239 // the calendar's eras, a standard set of the date formats, 240 // translations for day and month names, and if the calendar is not based on the Gregorian 241 // calendar, conversion functions to and from the Gregorian calendar. 242 calendars: { 243 standard: { 244 // name that identifies the type of calendar this is 245 name: "Gregorian_CCSStandart", 246 // separator of parts of a date (e.g. "/" in 11/05/1955) 247 "/": "/", 248 // separator of parts of a time (e.g. ":" in 05:44 PM) 249 ":": ":", 250 // the first day of the week (0 = Sunday, 1 = Monday, etc) 251 firstDay: firstWeekDay, 252 days: { 253 // full day names 254 names: listWeekdays, 255 // abbreviated day names 256 namesAbbr: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], 257 // shortest day names 258 namesShort: listShortWeekdays 259 }, 260 months: { 261 // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar) 262 names: listMonths, 263 // abbreviated month names 264 namesAbbr: listShortMonths 265 }, 266 // AM and PM designators in one of these forms: 267 // The usual view, and the upper and lower case versions 268 // [ standard, lowercase, uppercase ] 269 // The culture does not use AM or PM (likely all standard date formats use 24 hour time) 270 // null 271 AM: amDesignator == "" ? null : [amDesignator, amDesignator.toLowerCase(), amDesignator.toUpperCase()], 272 PM: pmDesignator == "" ? null : [pmDesignator, pmDesignator.toLowerCase(), pmDesignator.toUpperCase()], 273 eras: [ 274 // eras in reverse chronological order. 275 // name: the name of the era in this culture (e.g. A.D., C.E.)
276 // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era. 277 // offset: offset in years from gregorian calendar 278 { 279 "name": "A.D.", 280 "start": null, 281 "offset": 0 282} 283 ], 284 // when a two digit year is given, it will never be parsed as a four digit 285 // year greater than this year (in the appropriate era for the culture) 286 // Set it as a full year (e.g. 2029) or use an offset format starting from 287 // the current year: "+19" would correspond to 2029 if the current year 2010. 288 twoDigitYearMax: 2029, 289 // set of predefined date and time patterns used by the culture 290 // these represent the format someone in this culture would expect 291 // to see given the portions of the date that are shown. 292 patterns: { 293 // short date pattern 294 d: getLocaleInfo("shortDate"), 295 "ShortDate": getLocaleInfo("shortDate"), 296 // long date pattern 297 D: getLocaleInfo("longDate"), 298 "LongDate": getLocaleInfo("longDate"), 299 // short time pattern 300 t: getLocaleInfo("shortTime"), 301 "ShortTime": getLocaleInfo("shortTime"), 302 // long time pattern 303 T: getLocaleInfo("longTime"), 304 "LongTime": getLocaleInfo("longTime"), 305 // long date, short time pattern 306 f: "dddd, MMMM dd, yyyy h:mm tt", 307 // long date, long time pattern 308 F: "dddd, MMMM dd, yyyy h:mm:ss tt", 309 "GeneralDate": getLocaleInfo("shortDate") + ", " + getLocaleInfo("longTime"), 310 // month/day pattern 311 M: "MMMM dd", 312 // month/year pattern 313 Y: "yyyy MMMM", 314 // S is a sortable format that does not vary by culture 315 S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss" 316 } 317 // optional fields for each calendar: 318 /* 319 monthsGenitive: 320 Same as months but used when the day preceeds the month. 321 Omit if the culture has no genitive distinction in month names. 322 For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx 323 convert: 324 Allows for the support of non-gregorian based calendars. This convert object is used to 325 to convert a date to and from a gregorian calendar date to handle parsing and formatting. 326 The two functions: 327 fromGregorian( date ) 328 Given the date as a parameter, return an array with parts [ year, month, day ] 329 corresponding to the non-gregorian based year, month, and day for the calendar. 330 toGregorian( year, month, day ) 331 Given the non-gregorian year, month, and day, return a new Date() object 332 set to the corresponding date in the gregorian calendar. 333 */ 334 } 335 }, 336 // For localized strings 337 messages: { 338 CCS_Now: "Now", 339 CCS_RequiredField: "The value in field {0} is required.", 340 CCS_MinimumLength: "The number of symbols in field {0} can't be less than {1}.", 341 CCS_MaximumLength: "The number of symbols in field {0} can't be greater than {1}.", 342 CCS_IncorrectFormat: "The value in field {0} is not valid. Use the following format: {1}.", 343 CCS_IncorrectValue: "The value in field {0} is not valid." 344 } 345 }; 346 347 Globalize.cultures["default"].calendar = Globalize.cultures["default"].calendars.standard; 348 349 Globalize.cultures[getLocaleInfo("LanguageAndCountry")] = Globalize.cultures["default"]; 350 351 Globalize.cultureSelector = getLocaleInfo("LanguageAndCountry"); 352 353 // 354 // private variables 355 // 356 357 regexHex = /^0x[a-f0-9]+$/i; 358 regexInfinity = /^[+-]?infinity$/i; 359 regexParseFloat = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/; 360 regexTrim = /^\s+|\s+$/g; 361 362 // 363 // private JavaScript utility functions 364 // 365 366 arrayIndexOf = function (array, item) { 367 if (array.indexOf) { 368 return array.indexOf(item); 369 } 370 for (var i = 0, length = array.length; i < length; i++) { 371 if (array[i] === item) { 372 return i; 373 } 374 } 375 return -1; 376 }; 377 378 endsWith = function (value, pattern) { 379 return value.substr(value.length - pattern.length) === pattern; 380 }; 381 382 extend = function (deep) { 383 var options, name, src, copy, copyIsArray, clone, 384 target = arguments[0] || {}, 385 i = 1, 386 length = arguments.length, 387 deep = false; 388 389 // Handle a deep copy situation 390 if (typeof target === "boolean") { 391 deep = target; 392 target = arguments[1] || {}; 393 // skip the boolean and the target 394 i = 2; 395 } 396 397 // Handle case when target is a string or something (possible in deep copy) 398 if (typeof target !== "object" && !isFunction(target)) { 399 target = {}; 400 } 401 402 for (; i < length; i++) { 403 // Only deal with non-null/undefined values 404 if ((options = arguments[i]) != null) { 405 // Extend the base object 406 for (name in options) { 407 src = target[name]; 408 copy = options[name]; 409 410 // Prevent never-ending loop 411 if (target === copy) { 412 continue; 413 } 414 415 // Recurse if we're merging plain objects or arrays 416 if (deep && copy && (isObject(copy) || (copyIsArray = isArray(copy)))) { 417 if (copyIsArray) { 418 copyIsArray = false; 419 clone = src && isArray(src) ? src : []; 420 421 } else { 422 clone = src && isObject(src) ? src : {}; 423 } 424 425 // Never move original objects, clone them 426 target[name] = extend(deep, clone, copy); 427 428 // Don't bring in undefined values 429 } else if (copy !== undefined) { 430 target[name] = copy; 431 } 432 } 433 } 434 } 435 436 // Return the modified object 437 return target; 438 }; 439 440 isArray = Array.isArray || function (obj) { 441 return Object.prototype.toString.call(obj) === "[object Array]"; 442 }; 443 444 isFunction = function (obj) { 445 return Object.prototype.toString.call(obj) === "[object Function]" 446 } 447 448 isObject = function (obj) { 449 return Object.prototype.toString.call(obj) === "[object Object]"; 450 }; 451 452 startsWith = function (value, pattern) { 453 return value.indexOf(pattern) === 0; 454 }; 455 456 trim = function (value) { 457 return (value + "").replace(regexTrim, ""); 458 }; 459 460 truncate = function (value) { 461 return value | 0; 462 }; 463 464 zeroPad = function (str, count, left) { 465 var l; 466 for (l = str.length; l < count; l += 1) { 467 str = (left ? ("0" + str) : (str + "0")); 468 } 469 return str; 470 }; 471 472 // 473 // private Globalization utility functions 474 // 475
476 appendPreOrPostMatch = function (preMatch, strings) { 477 // appends pre- and post- token match strings while removing escaped characters. 478 // Returns a single quote count which is used to determine if the token occurs 479 // in a string literal. 480 var quoteCount = 0, 481 escaped = false; 482 for (var i = 0, il = preMatch.length; i < il; i++) { 483 var c = preMatch.charAt(i); 484 switch (c) { 485 case "\'": 486 if (escaped) { 487 strings.push("\'"); 488 } 489 else { 490 quoteCount++; 491 } 492 escaped = false; 493 break; 494 case "\\": 495 if (escaped) { 496 strings.push("\\"); 497 } 498 escaped = !escaped; 499 break; 500 default: 501 strings.push(c); 502 escaped = false; 503 break; 504 } 505 } 506 return quoteCount; 507 }; 508 509 expandFormat = function (cal, format) { 510 // expands unspecified or single character date formats into the full pattern. 511 format = format || "F"; 512 var pattern, 513 patterns = cal.patterns, 514 len = format.length; 515 if (len === 1) { 516 pattern = patterns[format]; 517 if (!pattern) { 518 throw "Invalid date format string \'" + format + "\'."; 519 } 520 format = pattern; 521 } 522 else if (len === 2 && format.charAt(0) === "%") { 523 // %X escape format -- intended as a custom format string that is only one character, not a built-in format. 524 format = format.charAt(1); 525 } 526 return format; 527 }; 528 529 formatDate = function (value, format, culture) { 530 var cal = culture.calendar, 531 convert = cal.convert; 532 533 if (!format || !format.length || format === "i") { 534 var ret; 535 if (culture && culture.name.length) { 536 if (convert) { 537 // non-gregorian calendar, so we cannot use built-in toLocaleString() 538 ret = formatDate(value, cal.patterns.F, culture); 539 } 540 else { 541 var eraDate = new Date(value.getTime()), 542 era = getEra(value, cal.eras); 543 eraDate.setFullYear(getEraYear(value, cal, era)); 544 ret = eraDate.toLocaleString(); 545 } 546 } 547 else { 548 ret = value.toString(); 549 } 550 return ret; 551 } 552 553 var eras = cal.eras, 554 sortable = format === "s"; 555 format = expandFormat(cal, format); 556 557 // Start with an empty string 558 ret = []; 559 var hour, 560 zeros = ["0", "00", "000"], 561 foundDay, 562 checkedDay, 563 dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g, 564 quoteCount = 0, 565 tokenRegExp = getTokenRegExp(), 566 converted; 567 568 function padZeros(num, c) { 569 var r, s = num + ""; 570 if (c > 1 && s.length < c) { 571 r = (zeros[c - 2] + s); 572 return r.substr(r.length - c, c); 573 } 574 else { 575 r = s; 576 } 577 return r; 578 } 579 580 function hasDay() { 581 if (foundDay || checkedDay) { 582 return foundDay; 583 } 584 foundDay = dayPartRegExp.test(format); 585 checkedDay = true; 586 return foundDay; 587 } 588 589 function getPart(date, part) { 590 if (converted) { 591 return converted[part]; 592 } 593 switch (part) { 594 case 0: return date.getFullYear(); 595 case 1: return date.getMonth(); 596 case 2: return date.getDate(); 597 } 598 } 599 600 if (!sortable && convert) { 601 converted = convert.fromGregorian(value); 602 } 603 604 for (; ; ) { 605 // Save the current index 606 var index = tokenRegExp.lastIndex, 607 // Look for the next pattern 608 ar = tokenRegExp.exec(format); 609 610 // Append the text before the pattern (or the end of the string if not found) 611 var preMatch = format.slice(index, ar ? ar.index : format.length); 612 quoteCount += appendPreOrPostMatch(preMatch, ret); 613 614 if (!ar) { 615 break; 616 } 617 618 // do not replace any matches that occur inside a string literal. 619 if (quoteCount % 2) {
620 ret.push(ar[0]); 621 continue; 622 } 623 624 var current = ar[0], 625 clength = current.length; 626 627 switch (current) { 628 case "ddd": 629 //Day of the week, as a three-letter abbreviation 630 case "dddd": 631 // Day of the week, using the full name 632 var names = (clength === 3) ? cal.days.namesAbbr : cal.days.names; 633 ret.push(names[value.getDay()]); 634 break; 635 case "d": 636 // Day of month, without leading zero for single-digit days 637 case "dd": 638 // Day of month, with leading zero for single-digit days 639 foundDay = true; 640 ret.push( 641 padZeros(getPart(value, 2), clength) 642 ); 643 break; 644 case "MMM": 645 // Month, as a three-letter abbreviation 646 case "MMMM": 647 // Month, using the full name 648 var part = getPart(value, 1); 649 ret.push( 650 (cal.monthsGenitive && hasDay()) 651 ? 652 cal.monthsGenitive[clength === 3 ? "namesAbbr" : "names"][part] 653 : 654 cal.months[clength === 3 ? "namesAbbr" : "names"][part] 655 ); 656 break; 657 case "M": 658 // Month, as digits, with no leading zero for single-digit months 659 case "MM": 660 // Month, as digits, with leading zero for single-digit months 661 ret.push( 662 padZeros(getPart(value, 1) + 1, clength) 663 ); 664 break; 665 case "y": 666 // Year, as two digits, but with no leading zero for years less than 10 667 case "yy": 668 // Year, as two digits, with leading zero for years less than 10 669 case "yyyy": 670 // Year represented by four full digits 671 part = converted ? converted[0] : getEraYear(value, cal, getEra(value, eras), sortable); 672 if (clength < 4) { 673 part = part % 100; 674 } 675 ret.push( 676 padZeros(part, clength) 677 ); 678 break; 679 case "h": 680 // Hours with no leading zero for single-digit hours, using 12-hour clock 681 case "hh": 682 // Hours with leading zero for single-digit hours, using 12-hour clock 683 hour = value.getHours() % 12; 684 if (hour === 0) hour = 12; 685 ret.push( 686 padZeros(hour, clength) 687 ); 688 break; 689 case "H": 690 // Hours with no leading zero for single-digit hours, using 24-hour clock 691 case "HH": 692 // Hours with leading zero for single-digit hours, using 24-hour clock 693 ret.push( 694 padZeros(value.getHours(), clength) 695 ); 696 break; 697 case "m": 698 // Minutes with no leading zero for single-digit minutes 699 case "mm": 700 // Minutes with leading zero for single-digit minutes 701 ret.push( 702 padZeros(value.getMinutes(), clength) 703 ); 704 break; 705 case "s": 706 // Seconds with no leading zero for single-digit seconds 707 case "ss": 708 // Seconds with leading zero for single-digit seconds 709 ret.push( 710 padZeros(value.getSeconds(), clength) 711 ); 712 break; 713 case "t": 714 // One character am/pm indicator ("a" or "p") 715 case "tt": 716 // Multicharacter am/pm indicator 717 part = value.getHours() < 12 ? (cal.AM ? cal.AM[0] : " ") : (cal.PM ? cal.PM[0] : " "); 718 ret.push(clength === 1 ? part.charAt(0) : part); 719 break; 720 case "f": 721 // Deciseconds 722 case "ff": 723 // Centiseconds 724 case "fff": 725 // Milliseconds
726 ret.push( 727 padZeros(value.getMilliseconds(), 3).substr(0, clength) 728 ); 729 break; 730 case "z": 731 // Time zone offset, no leading zero 732 case "zz": 733 // Time zone offset with leading zero 734 hour = value.getTimezoneOffset() / 60; 735 ret.push( 736 (hour <= 0 ? "+" : "-") + padZeros(Math.floor(Math.abs(hour)), clength) 737 ); 738 break; 739 case "zzz": 740 // Time zone offset with leading zero 741 hour = value.getTimezoneOffset() / 60; 742 ret.push( 743 (hour <= 0 ? "+" : "-") + padZeros(Math.floor(Math.abs(hour)), 2) 744 // Hard coded ":" separator, rather than using cal.TimeSeparator 745 // Repeated here for consistency, plus ":" was already assumed in date parsing. 746 + ":" + padZeros(Math.abs(value.getTimezoneOffset() % 60), 2) 747 ); 748 break; 749 case "g": 750 case "gg": 751 if (cal.eras) { 752 ret.push( 753 cal.eras[getEra(value, eras)].name 754 ); 755 } 756 break; 757 case "/": 758 ret.push(cal["/"]); 759 break; 760 default: 761 throw "Invalid date format pattern \'" + current + "\'."; 762 break; 763 } 764 } 765 return ret.join(""); 766 }; 767 768 // formatNumber 769 (function () { 770 var expandNumber; 771 772 expandNumber = function (number, precision, formatInfo) { 773 var groupSizes = formatInfo.groupSizes, 774 curSize = groupSizes[0], 775 curGroupIndex = 1, 776 factor = Math.pow(10, precision), 777 rounded = Math.round(number * factor) / factor; 778 779 if (!isFinite(rounded)) { 780 rounded = number; 781 } 782 number = rounded; 783 784 var numberString = number + "", 785 right = "", 786 split = numberString.split(/e/i), 787 exponent = split.length > 1 ? parseInt(split[1], 10) : 0; 788 numberString = split[0]; 789 split = numberString.split("."); 790 numberString = split[0]; 791 right = split.length > 1 ? split[1] : ""; 792 793 var l; 794 if (exponent > 0) { 795 right = zeroPad(right, exponent, false); 796 numberString += right.slice(0, exponent); 797 right = right.substr(exponent); 798 } 799 else if (exponent < 0) { 800 exponent = -exponent; 801 numberString = zeroPad(numberString, exponent + 1); 802 right = numberString.slice(-exponent, numberString.length) + right; 803 numberString = numberString.slice(0, -exponent); 804 } 805 806 if (precision > 0) { 807 right = formatInfo["."] + 808 ((right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision)); 809 } 810 else { 811 right = ""; 812 } 813 814 var stringIndex = numberString.length - 1, 815 sep = formatInfo[","], 816 ret = ""; 817 818 while (stringIndex >= 0) { 819 if (curSize === 0 || curSize > stringIndex) { 820 return numberString.slice(0, stringIndex + 1) + (ret.length ? (sep + ret + right) : right); 821 } 822 ret = numberString.slice(stringIndex - curSize + 1, stringIndex + 1) + (ret.length ? (sep + ret) : ""); 823 824 stringIndex -= curSize; 825 826 if (curGroupIndex < groupSizes.length) { 827 curSize = groupSizes[curGroupIndex]; 828 curGroupIndex++; 829 } 830 } 831 832 return numberString.slice(0, stringIndex + 1) + sep + ret + right; 833 }; 834 835 formatNumber = function (value, format, culture) { 836 if (!isFinite(value)) { 837 if (value === Infinity) { 838 return culture.numberFormat.positiveInfinity; 839 } 840 if (value === -Infinity) { 841 return culture.numberFormat.negativeInfinity; 842 } 843 return culture.numberFormat.NaN; 844 } 845 if (!format || format === "i") {
846 return culture.name.length ? value.toLocaleString() : value.toString(); 847 } 848 format = format || "D"; 849 850 var nf = culture.numberFormat, 851 number = Math.abs(value), 852 precision = -1, 853 pattern; 854 if (format.length > 1) precision = parseInt(format.slice(1), 10); 855 856 var current = format.charAt(0).toUpperCase(), 857 formatInfo; 858 859 switch (current) { 860 case "D": 861 pattern = "n"; 862 number = truncate(number); 863 if (precision !== -1) { 864 number = zeroPad("" + number, precision, true); 865 } 866 if (value < 0) number = "-" + number; 867 break; 868 case "N": 869 formatInfo = nf; 870 // fall through 871 case "C": 872 formatInfo = formatInfo || nf.currency; 873 // fall through 874 case "P": 875 formatInfo = formatInfo || nf.percent; 876 pattern = value < 0 ? formatInfo.pattern[0] : (formatInfo.pattern[1] || "n"); 877 if (precision === -1) precision = formatInfo.decimals; 878 number = expandNumber(number * (current === "P" ? 100 : 1), precision, formatInfo); 879 break; 880 default: 881 throw "Bad number format specifier: " + current; 882 } 883 884 var patternParts = /n|\$|-|%/g, 885 ret = ""; 886 for (; ; ) { 887 var index = patternParts.lastIndex, 888 ar = patternParts.exec(pattern); 889 890 ret += pattern.slice(index, ar ? ar.index : pattern.length); 891 892 if (!ar) { 893 break; 894 } 895 896 switch (ar[0]) { 897 case "n": 898 ret += number; 899 break; 900 case "$": 901 ret += nf.currency.symbol; 902 break; 903 case "-": 904 // don't make 0 negative 905 if (/[1-9]/.test(number)) { 906 ret += nf["-"]; 907 } 908 break; 909 case "%": 910 ret += nf.percent.symbol; 911 break; 912 } 913 } 914 915 return ret; 916 }; 917 918 } ()); 919 920 getTokenRegExp = function () { 921 // regular expression for matching date and time tokens in format strings. 922 return /\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g; 923 }; 924 925 getEra = function (date, eras) { 926 if (!eras) return 0; 927 var start, ticks = date.getTime(); 928 for (var i = 0, l = eras.length; i < l; i++) { 929 start = eras[i].start; 930 if (start === null || ticks >= start) { 931 return i; 932 } 933 } 934 return 0; 935 }; 936 937 getEraYear = function (date, cal, era, sortable) { 938 var year = date.getFullYear(); 939 if (!sortable && cal.eras) { 940 // convert normal gregorian year to era-shifted gregorian 941 // year by subtracting the era offset 942 year -= cal.eras[era].offset; 943 } 944 return year; 945 }; 946 947 // parseExact 948 (function () { 949 var expandYear, 950 getDayIndex, 951 getMonthIndex, 952 getParseRegExp, 953 outOfRange, 954 toUpper, 955 toUpperArray; 956 957 expandYear = function (cal, year) { 958 // expands 2-digit year into 4 digits. 959 var now = new Date(), 960 era = getEra(now); 961 if (year < 100) { 962 var twoDigitYearMax = cal.twoDigitYearMax; 963 twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt(twoDigitYearMax, 10) : twoDigitYearMax; 964 var curr = getEraYear(now, cal, era); 965 year += curr - (curr % 100); 966 if (year > twoDigitYearMax) { 967 year -= 100; 968 } 969 } 970 return year; 971 }; 972 973 getDayIndex = function (cal, value, abbr) { 974 var ret, 975 days = cal.days, 976 upperDays = cal._upperDays; 977 if (!upperDays) { 978 cal._upperDays = upperDays = [ 979 toUpperArray(days.names), 980 toUpperArray(days.namesAbbr), 981 toUpperArray(days.namesShort) 982 ]; 983 } 984 value = toUpper(value); 985 if (abbr) { 986 ret = arrayIndexOf(upperDays[1], value); 987 if (ret === -1) { 988 ret = arrayIndexOf(upperDays[2], value); 989 } 990 } 991 else { 992 ret = arrayIndexOf(upperDays[0], value); 993 } 994 return ret; 995 }; 996 997 getMonthIndex = function (cal, value, abbr) { 998 var months = cal.months, 999 monthsGen = cal.monthsGenitive || cal.months, 1000 upperMonths = cal._upperMonths, 1001 upperMonthsGen = cal._upperMonthsGen;
1002 if (!upperMonths) { 1003 cal._upperMonths = upperMonths = [ 1004 toUpperArray(months.names), 1005 toUpperArray(months.namesAbbr) 1006 ]; 1007 cal._upperMonthsGen = upperMonthsGen = [ 1008 toUpperArray(monthsGen.names), 1009 toUpperArray(monthsGen.namesAbbr) 1010 ]; 1011 } 1012 value = toUpper(value); 1013 var i = arrayIndexOf(abbr ? upperMonths[1] : upperMonths[0], value); 1014 if (i < 0) { 1015 i = arrayIndexOf(abbr ? upperMonthsGen[1] : upperMonthsGen[0], value); 1016 } 1017 return i; 1018 }; 1019 1020 getParseRegExp = function (cal, format) { 1021 // converts a format string into a regular expression with groups that 1022 // can be used to extract date fields from a date string. 1023 // check for a cached parse regex. 1024 var re = cal._parseRegExp; 1025 if (!re) { 1026 cal._parseRegExp = re = {}; 1027 } 1028 else { 1029 var reFormat = re[format]; 1030 if (reFormat) { 1031 return reFormat; 1032 } 1033 } 1034 1035 // expand single digit formats, then escape regular expression characters. 1036 var expFormat = expandFormat(cal, format).replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1"), 1037 regexp = ["^"], 1038 groups = [], 1039 index = 0, 1040 quoteCount = 0, 1041 tokenRegExp = getTokenRegExp(), 1042 match; 1043 1044 // iterate through each date token found. 1045 while ((match = tokenRegExp.exec(expFormat)) !== null) { 1046 var preMatch = expFormat.slice(index, match.index); 1047 index = tokenRegExp.lastIndex; 1048 1049 // don't replace any matches that occur inside a string literal. 1050 quoteCount += appendPreOrPostMatch(preMatch, regexp); 1051 if (quoteCount % 2) { 1052 regexp.push(match[0]); 1053 continue; 1054 } 1055 1056 // add a regex group for the token. 1057 var m = match[0], 1058 len = m.length, 1059 add; 1060 switch (m) { 1061 case "dddd": case "ddd": 1062 case "MMMM": case "MMM": 1063 case "gg": case "g": 1064 add = "(\\D+)"; 1065 break; 1066 case "tt": case "t": 1067 add = "(\\D*)"; 1068 break; 1069 case "yyyy": 1070 case "fff": 1071 case "ff": 1072 case "f": 1073 add = "(\\d{" + len + "})"; 1074 break; 1075 case "dd": case "d": 1076 case "MM": case "M": 1077 case "yy": case "y": 1078 case "HH": case "H": 1079 case "hh": case "h": 1080 case "mm": case "m": 1081 case "ss": case "s": 1082 add = "(\\d\\d?)"; 1083 break; 1084 case "zzz": 1085 add = "([+-]?\\d\\d?:\\d{2})"; 1086 break; 1087 case "zz": case "z": 1088 add = "([+-]?\\d\\d?)"; 1089 break; 1090 case "/": 1091 add = "(\\" + cal["/"] + ")"; 1092 break; 1093 default: 1094 throw "Invalid date format pattern \'" + m + "\'."; 1095 break; 1096 } 1097 if (add) { 1098 regexp.push(add); 1099 } 1100 groups.push(match[0]); 1101 } 1102 appendPreOrPostMatch(expFormat.slice(index), regexp); 1103 regexp.push("$"); 1104 1105 // allow whitespace to differ when matching formats. 1106 var regexpStr = regexp.join("").replace(/\s+/g, "\\s+"), 1107 parseRegExp = { "regExp": regexpStr, "groups": groups }; 1108 1109 // cache the regex for this format. 1110 return re[format] = parseRegExp; 1111 }; 1112 1113 outOfRange = function (value, low, high) { 1114 return value < low || value > high; 1115 }; 1116 1117 toUpper = function (value) { 1118 // "he-IL" has non-breaking space in weekday names. 1119 return value.split("\u00A0").join(" ").toUpperCase(); 1120 }; 1121 1122 toUpperArray = function (arr) { 1123 var results = []; 1124 for (var i = 0, l = arr.length; i < l; i++) { 1125 results[i] = toUpper(arr[i]); 1126 } 1127 return results; 1128 }; 1129 1130 parseExact = function (value, format, culture) { 1131 // try to parse the date string by matching against the format str
1131ing 1132 // while using the specified culture for date field names. 1133 value = trim(value); 1134 var cal = culture.calendar, 1135 // convert date formats into regular expressions with groupings. 1136 // use the regexp to determine the input format and extract the date fields. 1137 parseInfo = getParseRegExp(cal, format), 1138 match = new RegExp(parseInfo.regExp).exec(value); 1139 if (match === null) { 1140 return null; 1141 } 1142 // found a date format that matches the input. 1143 var groups = parseInfo.groups, 1144 era = null, year = null, month = null, date = null, weekDay = null, 1145 hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null, 1146 pmHour = false; 1147 // iterate the format groups to extract and set the date fields. 1148 for (var j = 0, jl = groups.length; j < jl; j++) { 1149 var matchGroup = match[j + 1]; 1150 if (matchGroup) { 1151 var current = groups[j], 1152 clength = current.length, 1153 matchInt = parseInt(matchGroup, 10); 1154 switch (current) { 1155 case "dd": case "d": 1156 // Day of month. 1157 date = matchInt; 1158 // check that date is generally in valid range, also checking overflow below. 1159 if (outOfRange(date, 1, 31)) return null; 1160 break; 1161 case "MMM": case "MMMM": 1162 month = getMonthIndex(cal, matchGroup, clength === 3); 1163 if (outOfRange(month, 0, 11)) return null; 1164 break; 1165 case "M": case "MM": 1166 // Month. 1167 month = matchInt - 1; 1168 if (outOfRange(month, 0, 11)) return null; 1169 break; 1170 case "y": case "yy": 1171 case "yyyy": 1172 year = clength < 4 ? expandYear(cal, matchInt) : matchInt; 1173 if (outOfRange(year, 0, 9999)) return null; 1174 break; 1175 case "h": case "hh": 1176 // Hours (12-hour clock). 1177 hour = matchInt; 1178 if (hour === 12) hour = 0; 1179 if (outOfRange(hour, 0, 11)) return null; 1180 break; 1181 case "H": case "HH": 1182 // Hours (24-hour clock). 1183 hour = matchInt; 1184 if (outOfRange(hour, 0, 23)) return null; 1185 break; 1186 case "m": case "mm": 1187 // Minutes. 1188 min = matchInt; 1189 if (outOfRange(min, 0, 59)) return null; 1190 break; 1191 case "s": case "ss": 1192 // Seconds. 1193 sec = matchInt; 1194 if (outOfRange(sec, 0, 59)) return null; 1195 break; 1196 case "tt": case "t": 1197 // AM/PM designator. 1198 // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of 1199 // the AM tokens. If not, fail the parse for this format. 1200 pmHour = cal.PM && (matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2]); 1201 if ( 1202 !pmHour && ( 1203 !cal.AM || (matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2]) 1204 ) 1205 ) return null; 1206 break; 1207 case "f": 1208 // Deciseconds. 1209 case "ff": 1210 // Centiseconds. 1211 case "fff": 1212 // Milliseconds. 1213 msec = matchInt * Math.pow(10, 3 - clength); 1214 if (outOfRange(msec, 0, 999)) return null; 1215 break; 1216 case "ddd": 1217 // Day of week. 1218 case "dddd": 1219 // Day of week. 1220 weekDay = getDayIndex(cal, matchGroup, clength === 3); 1221 if (outOfRange(weekDay, 0, 6)) return null; 1222 break; 1223 case "zzz": 1224 // Time zone offset in +/- hours:min. 1225 var offsets = matchGroup.split(/:/); 1226 if (offsets.length !== 2) return null; 1227 hourOffset = parseInt(offsets[0], 10); 1228 if (outOfRange(hourOffset, -12, 13)) return null; 1229 var minOffset = parseInt(offsets[1], 10); 1230 if (outOfRange(minOffset, 0, 59)) return null; 1231 tzMinOffset = (hourOffset * 60) + (startsWith(matchGroup, "-") ? -minOffset : minOffset); 1232 break; 1233 case "z": case "zz": 1234 // Time zone offset in +/- hours. 1235 hourOffset = matchInt; 1236 if (outOfRange(hourOffset, -12, 13)) return null; 1237 tzMinOffset = hourOffset * 60; 1238 break; 1239 case "g": case "gg": 1240 var eraName = matchGroup; 1241 if (!eraName || !cal.eras) return null; 1242 eraName = trim(eraName.toLowerCase()); 1243 for (var i = 0, l = cal.eras.length; i < l; i++) { 1244 if (eraName === cal.eras[i].name.toLowerCase()) { 1245 era = i; 1246 break; 1247 } 1248 } 1249 // could not find an era with that name 1250 if (era === null) return null; 1251 break; 1252 } 1253 } 1254 } 1255 var result = new Date(), defaultYear, convert = cal.convert; 1256 defaultYear = convert ? convert.fromGregorian(result)[0] : result.getFullYear(); 1257 if (year === null) { 1258 year = defaultYear; 1259 } 1260 else if (cal.eras) { 1261 // year must be shifted to normal gregorian year 1262 // but not if year was not specified, its already normal gregorian 1263 // per the main if clause above. 1264 year += cal.eras[(era || 0)].offset; 1265 } 1266 // set default day and month to 1 and January, so if unspecified, these are the defaults 1267 // instead of the current day/month. 1268 if (month === null) {
1269 month = 0; 1270 } 1271 if (date === null) { 1272 date = 1; 1273 } 1274 // now have year, month, and date, but in the culture's calendar. 1275 // convert to gregorian if necessary 1276 if (convert) { 1277 result = convert.toGregorian(year, month, date); 1278 // conversion failed, must be an invalid match 1279 if (result === null) return null; 1280 } 1281 else { 1282 // have to set year, month and date together to avoid overflow based on current date. 1283 result.setFullYear(year, month, date); 1284 // check to see if date overflowed for specified month (only checked 1-31 above). 1285 if (result.getDate() !== date) return null; 1286 // invalid day of week. 1287 if (weekDay !== null && result.getDay() !== weekDay) { 1288 return null; 1289 } 1290 } 1291 // if pm designator token was found make sure the hours fit the 24-hour clock. 1292 if (pmHour && hour < 12) { 1293 hour += 12; 1294 } 1295 result.setHours(hour, min, sec, msec); 1296 if (tzMinOffset !== null) { 1297 // adjust timezone to utc before applying local offset. 1298 var adjustedMin = result.getMinutes() - (tzMinOffset + result.getTimezoneOffset()); 1299 // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours 1300 // to ensure both these fields will not exceed this range. adjustedMin will range 1301 // somewhere between -1440 and 1500, so we only need to split this into hours. 1302 result.setHours(result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60); 1303 } 1304 return result; 1305 }; 1306 } ()); 1307 1308 parseNegativePattern = function (value, nf, negativePattern) { 1309 var neg = nf["-"], 1310 pos = nf["+"], 1311 ret; 1312 switch (negativePattern) { 1313 case "n -": 1314 neg = " " + neg; 1315 pos = " " + pos; 1316 // fall through 1317 case "n-": 1318 if (endsWith(value, neg)) { 1319 ret = ["-", value.substr(0, value.length - neg.length)]; 1320 } 1321 else if (endsWith(value, pos)) { 1322 ret = ["+", value.substr(0, value.length - pos.length)]; 1323 } 1324 break; 1325 case "- n": 1326 neg += " "; 1327 pos += " "; 1328 // fall through 1329 case "-n": 1330 if (startsWith(value, neg)) { 1331 ret = ["-", value.substr(neg.length)]; 1332 } 1333 else if (startsWith(value, pos)) { 1334 ret = ["+", value.substr(pos.length)]; 1335 } 1336 break; 1337 case "(n)": 1338 if (startsWith(value, "(") && endsWith(value, ")")) { 1339 ret = ["-", value.substr(1, value.length - 2)]; 1340 } 1341 break; 1342 } 1343 return ret || ["", value]; 1344 }; 1345 1346 // 1347 // public instance functions 1348 // 1349 1350 Globalize.prototype.findClosestCulture = function (cultureSelector) { 1351 return Globalize.findClosestCulture.call(this, cultureSelector); 1352 }; 1353 1354 Globalize.prototype.format = function (value, format, cultureSelector) { 1355 return Globalize.format.call(this, value, format, cultureSelector); 1356 }; 1357 1358 Globalize.prototype.localize = function (key, cultureSelector) { 1359 return Globalize.localize.call(this, key, cultureSelector); 1360 }; 1361 1362 Globalize.prototype.parseInt = function (value, radix, cultureSelector) { 1363 return Globalize.parseInt.call(this, value, radix, cultureSelector); 1364 }; 1365 1366 Globalize.prototype.parseFloat = function (value, radix, cultureSelector) { 1367 return Globalize.parseFloat.call(this, value, radix, cultureSelector); 1368 }; 1369 1370 Globalize.prototype.culture = function (cultureSelector) { 1371 return Globalize.culture.call(this, cultureSelector); 1372 }; 1373 1374 // 1375 // public singleton functions 1376 // 1377 1378 Globalize.addCultureInfo = function (cultureName, baseCultureName, info) { 1379 1380 var base = {}, 1381 isNew = false; 1382 1383 if (typeof cultureName !== "string") { 1384 // cultureName argument is optional string. If not specified, assume info is first 1385 // and only argument. Specified info deep-extends current culture. 1386 info = cultureName; 1387 cultureName = this.culture().name; 1388 base = this.cultures[cultureName]; 1389 } else if (typeof baseCultureName !== "string") { 1390 // baseCultureName argument is optional string. If not specified, assume info is second 1391 // argument. Specified info deep-extends specified culture. 1392 // If specified culture does not exist, create by deep-extending default 1393 info = baseCultureName; 1394 isNew = (this.cultures[cultureName] == null); 1395 base = this.cultures[cultureName] || this.cultures["default"]; 1396 } else { 1397 // cultureName and baseCultureName specified. Assume a new culture is being created 1398 // by deep-extending an specified base culture 1399 isNew = true; 1400 base = this.cultures[baseCultureName]; 1401 } 1402 1403 this.cultures[cultureName] = extend(true, {}, 1404 base, 1405 info 1406 ); 1407 // Make the standard calendar the current culture if it's a new culture 1408 if (isNew) { 1409 this.cultures[cultureName].calendar = this.cultures[cultureName].calendars.standard; 1410 } 1411 }; 1412 1413 Globalize.findClosestCulture = function (name) { 1414 var match; 1415 if (!name) { 1416 return this.cultures[this.cultureSelector] || this.cultures["default"]; 1417 } 1418 if (typeof name === "string") { 1419 name = name.split(","); 1420 } 1421 if (isArray(name)) { 1422 var lang, 1423 cultures = this.cultures, 1424 list = name, 1425 i, l = list.length, 1426 prioritized = []; 1427 for (i = 0; i < l; i++) { 1428 name = trim(list[i]); 1429 var pri, parts = name.split(";"); 1430 lang = trim(parts[0]); 1431 if (parts.length === 1) { 1432 pri = 1; 1433 } 1434 else { 1435 name = trim(parts[1]); 1436 if (name.indexOf("q=") === 0) { 1437 name = name.substr(2); 1438 pri = parseFloat(name); 1439 pri = isNaN(pri) ? 0 : pri; 1440 } 1441 else { 1442 pri = 1; 1443 } 1444 } 1445 prioritized.push({ lang: lang, pri: pri }); 1446 } 1447 prioritized.sort(function (a, b) { 1448 return a.pri < b.pri ? 1 : -1; 1449 }); 1450 1451 // exact match 1452 for (i = 0; i < l; i++) { 1453 lang = prioritized[i].lang; 1454 match = cultures[lang]; 1455 if (match) { 1456 return match; 1457 } 1458 } 1459 1460 // neutral language match 1461 for (i = 0; i < l; i++) { 1462 lang = prioritized[i].lang; 1463 do { 1464 var index = lang.lastIndexOf("-"); 1465 if (index === -1) { 1466 break; 1467 } 1468 // strip off the last part. e.g. en-US => en 1469 lang = lang.substr(0, index); 1470 match = cultures[lang]; 1471 if (match) { 1472 return match; 1473 } 1474 } 1475 while (1); 1476 } 1477 1478 // last resort: match first culture using that language 1479 for (i = 0; i < l; i++) { 1480 lang = prioritized[i].lang; 1481 for (var cultureKey in cultures) { 1482 var culture = cultures[cultureKey]; 1483 if (culture.language == lang) { 1484 return culture; 1485 } 1486 } 1487 } 1488 } 1489 else if (typeof name === "object") { 1490 return name; 1491 } 1492 return match || null; 1493 }; 1494 1495 Globalize.format = function (value, format, cultureSelector) { 1496 culture = this.findClosestCulture(cultureSelector); 1497 if (value instanceof Date) { 1498 value = formatDate(value, format, culture); 1499 } 1500 else if (typeof value === "number") { 1501 value = formatNumber(value, format, culture); 1502 } 1503 return value; 1504 }; 1505 1506 Globalize.localize = function (key, cultureSelector) { 1507 return this.findClosestCulture(cultureSelector).messages[key] || 1508 this.cultures["default"].messages[key]; 1509 }; 1510 1511 Globalize.parseDate = function (value, formats, culture) { 1512 culture = this.findClosestCulture(culture); 1513 1514 var date, prop, patterns; 1515 if (formats) { 1516 if (typeof formats === "string") { 1517 formats = [formats]; 1518 } 1519 if (formats.length) { 1520 for (var i = 0, l = formats.length; i < l; i++) { 1521 var format = formats[i]; 1522 if (format) { 1523 date = parseExact(value, format, culture); 1524 if (date) { 1525 break; 1526 } 1527 } 1528 } 1529 } 1530 } else { 1531 patterns = culture.calendar.patterns; 1532 for (prop in patterns) { 1533 date = parseExact(value, patterns[prop], culture); 1534 if (date) { 1535 break; 1536 } 1537 } 1538 } 1539 1540 return date || null; 1541 }; 1542 1543 Globalize.parseInt = function (value, radix, cultureSelector) { 1544 return truncate(Globalize.parseFloat(value, radix, cultureSelector)); 1545 }; 1546 1547 Globalize.parseFloat = function (value, radix, cultureSelector) { 1548 // radix argument is optional 1549 if (typeof radix !== "number") { 1550 cultureSelector = radix;
1551 radix = 10; 1552 } 1553 1554 var culture = this.findClosestCulture(cultureSelector); 1555 var ret = NaN, 1556 nf = culture.numberFormat; 1557 1558 if (value.indexOf(culture.numberFormat.currency.symbol) > -1) { 1559 // remove currency symbol 1560 value = value.replace(culture.numberFormat.currency.symbol, ""); 1561 // replace decimal seperator 1562 value = value.replace(culture.numberFormat.currency["."], culture.numberFormat["."]); 1563 } 1564 1565 // trim leading and trailing whitespace 1566 value = trim(value); 1567 1568 // allow infinity or hexidecimal 1569 if (regexInfinity.test(value)) { 1570 ret = parseFloat(value); 1571 } 1572 else if (!radix && regexHex.test(value)) { 1573 ret = parseInt(value, 16); 1574 } 1575 else { 1576 1577 // determine sign and number 1578 var signInfo = parseNegativePattern(value, nf, nf.pattern[0]), 1579 sign = signInfo[0], 1580 num = signInfo[1]; 1581 1582 // #44 - try parsing as "(n)" 1583 if (sign === "" && nf.pattern[0] !== "(n)") { 1584 signInfo = parseNegativePattern(value, nf, "(n)"); 1585 sign = signInfo[0]; 1586 num = signInfo[1]; 1587 } 1588 1589 // try parsing as "-n" 1590 if (sign === "" && nf.pattern[0] !== "-n") { 1591 signInfo = parseNegativePattern(value, nf, "-n"); 1592 sign = signInfo[0]; 1593 num = signInfo[1]; 1594 } 1595 1596 sign = sign || "+"; 1597 1598 // determine exponent and number 1599 var exponent, 1600 intAndFraction, 1601 exponentPos = num.indexOf("e"); 1602 if (exponentPos < 0) exponentPos = num.indexOf("E"); 1603 if (exponentPos < 0) { 1604 intAndFraction = num; 1605 exponent = null; 1606 } 1607 else { 1608 intAndFraction = num.substr(0, exponentPos); 1609 exponent = num.substr(exponentPos + 1); 1610 } 1611 // determine decimal position 1612 var integer, 1613 fraction, 1614 decSep = nf["."], 1615 decimalPos = intAndFraction.indexOf(decSep); 1616 if (decimalPos < 0) { 1617 integer = intAndFraction; 1618 fraction = null; 1619 } 1620 else { 1621 integer = intAndFraction.substr(0, decimalPos); 1622 fraction = intAndFraction.substr(decimalPos + decSep.length); 1623 } 1624 // handle groups (e.g. 1,000,000) 1625 var groupSep = nf[","]; 1626 integer = integer.split(groupSep).join(""); 1627 var altGroupSep = groupSep.replace(/\u00A0/g, " "); 1628 if (groupSep !== altGroupSep) { 1629 integer = integer.split(altGroupSep).join(""); 1630 } 1631 // build a natively parsable number string 1632 var p = sign + integer; 1633 if (fraction !== null) { 1634 p += "." + fraction; 1635 } 1636 if (exponent !== null) { 1637 // exponent itself may have a number patternd 1638 var expSignInfo = parseNegativePattern(exponent, nf, "-n"); 1639 p += "e" + (expSignInfo[0] || "+") + expSignInfo[1]; 1640 } 1641 if (regexParseFloat.test(p)) { 1642 ret = parseFloat(p); 1643 } 1644 } 1645 return ret; 1646 }; 1647 1648 Globalize.culture = function (cultureSelector) { 1649 // setter 1650 if (typeof cultureSelector !== "undefined") { 1651 this.cultureSelector = cultureSelector; 1652 } 1653 // getter 1654 return this.findClosestCulture(cultureSelector) || this.culture["default"]; 1655 }; 1656 1657} (this)); 1658 1659//End JS Globalize 1660
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.