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