| /*! |
| * Globalize |
| * |
| * http://github.com/jquery/globalize |
| * |
| * Copyright Software Freedom Conservancy, Inc. |
| * Dual licensed under the MIT or GPL Version 2 licenses. |
| * http://jquery.org/license |
| */ |
| |
| (function( window, undefined ) { |
| |
| var Globalize, |
| // private variables |
| regexHex, |
| regexInfinity, |
| regexParseFloat, |
| regexTrim, |
| // private JavaScript utility functions |
| arrayIndexOf, |
| endsWith, |
| extend, |
| isArray, |
| isFunction, |
| isObject, |
| startsWith, |
| trim, |
| truncate, |
| zeroPad, |
| // private Globalization utility functions |
| appendPreOrPostMatch, |
| expandFormat, |
| formatDate, |
| formatNumber, |
| getTokenRegExp, |
| getEra, |
| getEraYear, |
| parseExact, |
| parseNegativePattern; |
| |
| // Global variable (Globalize) or CommonJS module (globalize) |
| Globalize = function( cultureSelector ) { |
| return new Globalize.prototype.init( cultureSelector ); |
| }; |
| |
| if ( typeof require !== "undefined" |
| && typeof exports !== "undefined" |
| && typeof module !== "undefined" ) { |
| // Assume CommonJS |
| module.exports = Globalize; |
| } else { |
| // Export as global variable |
| window.Globalize = Globalize; |
| } |
| |
| Globalize.cultures = {}; |
| |
| Globalize.prototype = { |
| constructor: Globalize, |
| init: function( cultureSelector ) { |
| this.cultures = Globalize.cultures; |
| this.cultureSelector = cultureSelector; |
| |
| return this; |
| } |
| }; |
| Globalize.prototype.init.prototype = Globalize.prototype; |
| |
| // 1. When defining a culture, all fields are required except the ones stated as optional. |
| // 2. Each culture should have a ".calendars" object with at least one calendar named "standard" |
| // which serves as the default calendar in use by that culture. |
| // 3. Each culture should have a ".calendar" object which is the current calendar being used, |
| // it may be dynamically changed at any time to one of the calendars in ".calendars". |
| Globalize.cultures[ "default" ] = { |
| // A unique name for the culture in the form <language code>-<country/region code> |
| name: "en", |
| // the name of the culture in the english language |
| englishName: "English", |
| // the name of the culture in its own language |
| nativeName: "English", |
| // whether the culture uses right-to-left text |
| isRTL: false, |
| // "language" is used for so-called "specific" cultures. |
| // For example, the culture "es-CL" means "Spanish, in Chili". |
| // It represents the Spanish-speaking culture as it is in Chili, |
| // which might have different formatting rules or even translations |
| // than Spanish in Spain. A "neutral" culture is one that is not |
| // specific to a region. For example, the culture "es" is the generic |
| // Spanish culture, which may be a more generalized version of the language |
| // that may or may not be what a specific culture expects. |
| // For a specific culture like "es-CL", the "language" field refers to the |
| // neutral, generic culture information for the language it is using. |
| // This is not always a simple matter of the string before the dash. |
| // For example, the "zh-Hans" culture is netural (Simplified Chinese). |
| // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage |
| // field is "zh-CHS", not "zh". |
| // This field should be used to navigate from a specific culture to it's |
| // more general, neutral culture. If a culture is already as general as it |
| // can get, the language may refer to itself. |
| language: "en", |
| // numberFormat defines general number formatting rules, like the digits in |
| // each grouping, the group separator, and how negative numbers are displayed. |
| numberFormat: { |
| // [negativePattern] |
| // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency, |
| // but is still defined as an array for consistency with them. |
| // negativePattern: one of "(n)|-n|- n|n-|n -" |
| pattern: [ "-n" ], |
| // number of decimal places normally shown |
| decimals: 2, |
| // string that separates number groups, as in 1,000,000 |
| ",": ",", |
| // string that separates a number from the fractional portion, as in 1.99 |
| ".": ".", |
| // array of numbers indicating the size of each number group. |
| // TODO: more detailed description and example |
| groupSizes: [ 3 ], |
| // symbol used for positive numbers |
| "+": "+", |
| // symbol used for negative numbers |
| "-": "-", |
| // symbol used for NaN (Not-A-Number) |
| NaN: "NaN", |
| // symbol used for Negative Infinity |
| negativeInfinity: "-Infinity", |
| // symbol used for Positive Infinity |
| positiveInfinity: "Infinity", |
| percent: { |
| // [negativePattern, positivePattern] |
| // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %" |
| // positivePattern: one of "n %|n%|%n|% n" |
| pattern: [ "-n %", "n %" ], |
| // number of decimal places normally shown |
| decimals: 2, |
| // array of numbers indicating the size of each number group. |
| // TODO: more detailed description and example |
| groupSizes: [ 3 ], |
| // string that separates number groups, as in 1,000,000 |
| ",": ",", |
| // string that separates a number from the fractional portion, as in 1.99 |
| ".": ".", |
| // symbol used to represent a percentage |
| symbol: "%" |
| }, |
| currency: { |
| // [negativePattern, positivePattern] |
| // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)" |
| // positivePattern: one of "$n|n$|$ n|n $" |
| pattern: [ "($n)", "$n" ], |
| // number of decimal places normally shown |
| decimals: 2, |
| // array of numbers indicating the size of each number group. |
| // TODO: more detailed description and example |
| groupSizes: [ 3 ], |
| // string that separates number groups, as in 1,000,000 |
| ",": ",", |
| // string that separates a number from the fractional portion, as in 1.99 |
| ".": ".", |
| // symbol used to represent currency |
| symbol: "$" |
| } |
| }, |
| // calendars defines all the possible calendars used by this culture. |
| // There should be at least one defined with name "standard", and is the default |
| // calendar used by the culture. |
| // A calendar contains information about how dates are formatted, information about |
| // the calendar's eras, a standard set of the date formats, |
| // translations for day and month names, and if the calendar is not based on the Gregorian |
| // calendar, conversion functions to and from the Gregorian calendar. |
| calendars: { |
| standard: { |
| // name that identifies the type of calendar this is |
| name: "Gregorian_USEnglish", |
| // separator of parts of a date (e.g. "/" in 11/05/1955) |
| "/": "/", |
| // separator of parts of a time (e.g. ":" in 05:44 PM) |
| ":": ":", |
| // the first day of the week (0 = Sunday, 1 = Monday, etc) |
| firstDay: 0, |
| days: { |
| // full day names |
| names: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], |
| // abbreviated day names |
| namesAbbr: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], |
| // shortest day names |
| namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ] |
| }, |
| months: { |
| // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar) |
| names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ], |
| // abbreviated month names |
| namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ] |
| }, |
| // AM and PM designators in one of these forms: |
| // The usual view, and the upper and lower case versions |
| // [ standard, lowercase, uppercase ] |
| // The culture does not use AM or PM (likely all standard date formats use 24 hour time) |
| // null |
| AM: [ "AM", "am", "AM" ], |
| PM: [ "PM", "pm", "PM" ], |
| eras: [ |
| // eras in reverse chronological order. |
| // name: the name of the era in this culture (e.g. A.D., C.E.) |
| // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era. |
| // offset: offset in years from gregorian calendar |
| { |
| "name": "A.D.", |
| "start": null, |
| "offset": 0 |
| } |
| ], |
| // when a two digit year is given, it will never be parsed as a four digit |
| // year greater than this year (in the appropriate era for the culture) |
| // Set it as a full year (e.g. 2029) or use an offset format starting from |
| // the current year: "+19" would correspond to 2029 if the current year 2010. |
| twoDigitYearMax: 2029, |
| // set of predefined date and time patterns used by the culture |
| // these represent the format someone in this culture would expect |
| // to see given the portions of the date that are shown. |
| patterns: { |
| // short date pattern |
| d: "M/d/yyyy", |
| // long date pattern |
| D: "dddd, MMMM dd, yyyy", |
| // short time pattern |
| t: "h:mm tt", |
| // long time pattern |
| T: "h:mm:ss tt", |
| // long date, short time pattern |
| f: "dddd, MMMM dd, yyyy h:mm tt", |
| // long date, long time pattern |
| F: "dddd, MMMM dd, yyyy h:mm:ss tt", |
| // month/day pattern |
| M: "MMMM dd", |
| // month/year pattern |
| Y: "yyyy MMMM", |
| // S is a sortable format that does not vary by culture |
| S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss" |
| } |
| // optional fields for each calendar: |
| /* |
| monthsGenitive: |
| Same as months but used when the day preceeds the month. |
| Omit if the culture has no genitive distinction in month names. |
| For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx |
| convert: |
| Allows for the support of non-gregorian based calendars. This convert object is used to |
| to convert a date to and from a gregorian calendar date to handle parsing and formatting. |
| The two functions: |
| fromGregorian( date ) |
| Given the date as a parameter, return an array with parts [ year, month, day ] |
| corresponding to the non-gregorian based year, month, and day for the calendar. |
| toGregorian( year, month, day ) |
| Given the non-gregorian year, month, and day, return a new Date() object |
| set to the corresponding date in the gregorian calendar. |
| */ |
| } |
| }, |
| // For localized strings |
| messages: {} |
| }; |
| |
| Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard; |
| |
| Globalize.cultures[ "en" ] = Globalize.cultures[ "default" ]; |
| |
| Globalize.cultureSelector = "en"; |
| |
| // |
| // private variables |
| // |
| |
| regexHex = /^0x[a-f0-9]+$/i; |
| regexInfinity = /^[+-]?infinity$/i; |
| regexParseFloat = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/; |
| regexTrim = /^\s+|\s+$/g; |
| |
| // |
| // private JavaScript utility functions |
| // |
| |
| arrayIndexOf = function( array, item ) { |
| if ( array.indexOf ) { |
| return array.indexOf( item ); |
| } |
| for ( var i = 0, length = array.length; i < length; i++ ) { |
| if ( array[i] === item ) { |
| return i; |
| } |
| } |
| return -1; |
| }; |
| |
| endsWith = function( value, pattern ) { |
| return value.substr( value.length - pattern.length ) === pattern; |
| }; |
| |
| extend = function( deep ) { |
| var options, name, src, copy, copyIsArray, clone, |
| target = arguments[0] || {}, |
| i = 1, |
| length = arguments.length, |
| deep = false; |
| |
| // Handle a deep copy situation |
| if ( typeof target === "boolean" ) { |
| deep = target; |
| target = arguments[1] || {}; |
| // skip the boolean and the target |
| i = 2; |
| } |
| |
| // Handle case when target is a string or something (possible in deep copy) |
| if ( typeof target !== "object" && !isFunction(target) ) { |
| target = {}; |
| } |
| |
| for ( ; i < length; i++ ) { |
| // Only deal with non-null/undefined values |
| if ( (options = arguments[ i ]) != null ) { |
| // Extend the base object |
| for ( name in options ) { |
| src = target[ name ]; |
| copy = options[ name ]; |
| |
| // Prevent never-ending loop |
| if ( target === copy ) { |
| continue; |
| } |
| |
| // Recurse if we're merging plain objects or arrays |
| if ( deep && copy && ( isObject(copy) || (copyIsArray = isArray(copy)) ) ) { |
| if ( copyIsArray ) { |
| copyIsArray = false; |
| clone = src && isArray(src) ? src : []; |
| |
| } else { |
| clone = src && isObject(src) ? src : {}; |
| } |
| |
| // Never move original objects, clone them |
| target[ name ] = extend( deep, clone, copy ); |
| |
| // Don't bring in undefined values |
| } else if ( copy !== undefined ) { |
| target[ name ] = copy; |
| } |
| } |
| } |
| } |
| |
| // Return the modified object |
| return target; |
| }; |
| |
| isArray = Array.isArray || function( obj ) { |
| return Object.prototype.toString.call( obj ) === "[object Array]"; |
| }; |
| |
| isFunction = function( obj ) { |
| return Object.prototype.toString.call( obj ) === "[object Function]"; |
| }; |
| |
| isObject = function( obj ) { |
| return Object.prototype.toString.call( obj ) === "[object Object]"; |
| }; |
| |
| startsWith = function( value, pattern ) { |
| return value.indexOf( pattern ) === 0; |
| }; |
| |
| trim = function( value ) { |
| return ( value + "" ).replace( regexTrim, "" ); |
| }; |
| |
| truncate = function( value ) { |
| if ( isNaN( value ) ) { |
| return NaN; |
| } |
| return Math[ value < 0 ? "ceil" : "floor" ]( value ); |
| }; |
| |
| zeroPad = function( str, count, left ) { |
| var l; |
| for ( l = str.length; l < count; l += 1 ) { |
| str = ( left ? ("0" + str) : (str + "0") ); |
| } |
| return str; |
| }; |
| |
| // |
| // private Globalization utility functions |
| // |
| |
| appendPreOrPostMatch = function( preMatch, strings ) { |
| // appends pre- and post- token match strings while removing escaped characters. |
| // Returns a single quote count which is used to determine if the token occurs |
| // in a string literal. |
| var quoteCount = 0, |
| escaped = false; |
| for ( var i = 0, il = preMatch.length; i < il; i++ ) { |
| var c = preMatch.charAt( i ); |
| switch ( c ) { |
| case "\'": |
| if ( escaped ) { |
| strings.push( "\'" ); |
| } |
| else { |
| quoteCount++; |
| } |
| escaped = false; |
| break; |
| case "\\": |
| if ( escaped ) { |
| strings.push( "\\" ); |
| } |
| escaped = !escaped; |
| break; |
| default: |
| strings.push( c ); |
| escaped = false; |
| break; |
| } |
| } |
| return quoteCount; |
| }; |
| |
| expandFormat = function( cal, format ) { |
| // expands unspecified or single character date formats into the full pattern. |
| format = format || "F"; |
| var pattern, |
| patterns = cal.patterns, |
| len = format.length; |
| if ( len === 1 ) { |
| pattern = patterns[ format ]; |
| if ( !pattern ) { |
| throw "Invalid date format string \'" + format + "\'."; |
| } |
| format = pattern; |
| } |
| else if ( len === 2 && format.charAt(0) === "%" ) { |
| // %X escape format -- intended as a custom format string that is only one character, not a built-in format. |
| format = format.charAt( 1 ); |
| } |
| return format; |
| }; |
| |
| formatDate = function( value, format, culture ) { |
| var cal = culture.calendar, |
| convert = cal.convert; |
| |
| if ( !format || !format.length || format === "i" ) { |
| var ret; |
| if ( culture && culture.name.length ) { |
| if ( convert ) { |
| // non-gregorian calendar, so we cannot use built-in toLocaleString() |
| ret = formatDate( value, cal.patterns.F, culture ); |
| } |
| else { |
| var eraDate = new Date( value.getTime() ), |
| era = getEra( value, cal.eras ); |
| eraDate.setFullYear( getEraYear(value, cal, era) ); |
| ret = eraDate.toLocaleString(); |
| } |
| } |
| else { |
| ret = value.toString(); |
| } |
| return ret; |
| } |
| |
| var eras = cal.eras, |
| sortable = format === "s"; |
| format = expandFormat( cal, format ); |
| |
| // Start with an empty string |
| ret = []; |
| var hour, |
| zeros = [ "0", "00", "000" ], |
| foundDay, |
| checkedDay, |
| dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g, |
| quoteCount = 0, |
| tokenRegExp = getTokenRegExp(), |
| converted; |
| |
| function padZeros( num, c ) { |
| var r, s = num + ""; |
| if ( c > 1 && s.length < c ) { |
| r = ( zeros[c - 2] + s); |
| return r.substr( r.length - c, c ); |
| } |
| else { |
| r = s; |
| } |
| return r; |
| } |
| |
| function hasDay() { |
| if ( foundDay || checkedDay ) { |
| return foundDay; |
| } |
| foundDay = dayPartRegExp.test( format ); |
| checkedDay = true; |
| return foundDay; |
| } |
| |
| function getPart( date, part ) { |
| if ( converted ) { |
| return converted[ part ]; |
| } |
| switch ( part ) { |
| case 0: return date.getFullYear(); |
| case 1: return date.getMonth(); |
| case 2: return date.getDate(); |
| } |
| } |
| |
| if ( !sortable && convert ) { |
| converted = convert.fromGregorian( value ); |
| } |
| |
| for ( ; ; ) { |
| // Save the current index |
| var index = tokenRegExp.lastIndex, |
| // Look for the next pattern |
| ar = tokenRegExp.exec( format ); |
| |
| // Append the text before the pattern (or the end of the string if not found) |
| var preMatch = format.slice( index, ar ? ar.index : format.length ); |
| quoteCount += appendPreOrPostMatch( preMatch, ret ); |
| |
| if ( !ar ) { |
| break; |
| } |
| |
| // do not replace any matches that occur inside a string literal. |
| if ( quoteCount % 2 ) { |
| ret.push( ar[0] ); |
| continue; |
| } |
| |
| var current = ar[ 0 ], |
| clength = current.length; |
| |
| switch ( current ) { |
| case "ddd": |
| //Day of the week, as a three-letter abbreviation |
| case "dddd": |
| // Day of the week, using the full name |
| var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names; |
| ret.push( names[value.getDay()] ); |
| break; |
| case "d": |
| // Day of month, without leading zero for single-digit days |
| case "dd": |
| // Day of month, with leading zero for single-digit days |
| foundDay = true; |
| ret.push( |
| padZeros( getPart(value, 2), clength ) |
| ); |
| break; |
| case "MMM": |
| // Month, as a three-letter abbreviation |
| case "MMMM": |
| // Month, using the full name |
| var part = getPart( value, 1 ); |
| ret.push( |
| ( cal.monthsGenitive && hasDay() ) |
| ? |
| cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] |
| : |
| cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] |
| ); |
| break; |
| case "M": |
| // Month, as digits, with no leading zero for single-digit months |
| case "MM": |
| // Month, as digits, with leading zero for single-digit months |
| ret.push( |
| padZeros( getPart(value, 1) + 1, clength ) |
| ); |
| break; |
| case "y": |
| // Year, as two digits, but with no leading zero for years less than 10 |
| case "yy": |
| // Year, as two digits, with leading zero for years less than 10 |
| case "yyyy": |
| // Year represented by four full digits |
| part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra(value, eras), sortable ); |
| if ( clength < 4 ) { |
| part = part % 100; |
| } |
| ret.push( |
| padZeros( part, clength ) |
| ); |
| break; |
| case "h": |
| // Hours with no leading zero for single-digit hours, using 12-hour clock |
| case "hh": |
| // Hours with leading zero for single-digit hours, using 12-hour clock |
| hour = value.getHours() % 12; |
| if ( hour === 0 ) hour = 12; |
| ret.push( |
| padZeros( hour, clength ) |
| ); |
| break; |
| case "H": |
| // Hours with no leading zero for single-digit hours, using 24-hour clock |
| case "HH": |
| // Hours with leading zero for single-digit hours, using 24-hour clock |
| ret.push( |
| padZeros( value.getHours(), clength ) |
| ); |
| break; |
| case "m": |
| // Minutes with no leading zero for single-digit minutes |
| case "mm": |
| // Minutes with leading zero for single-digit minutes |
| ret.push( |
| padZeros( value.getMinutes(), clength ) |
| ); |
| break; |
| case "s": |
| // Seconds with no leading zero for single-digit seconds |
| case "ss": |
| // Seconds with leading zero for single-digit seconds |
| ret.push( |
| padZeros( value.getSeconds(), clength ) |
| ); |
| break; |
| case "t": |
| // One character am/pm indicator ("a" or "p") |
| case "tt": |
| // Multicharacter am/pm indicator |
| part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " ); |
| ret.push( clength === 1 ? part.charAt(0) : part ); |
| break; |
| case "f": |
| // Deciseconds |
| case "ff": |
| // Centiseconds |
| case "fff": |
| // Milliseconds |
| ret.push( |
| padZeros( value.getMilliseconds(), 3 ).substr( 0, clength ) |
| ); |
| break; |
| case "z": |
| // Time zone offset, no leading zero |
| case "zz": |
| // Time zone offset with leading zero |
| hour = value.getTimezoneOffset() / 60; |
| ret.push( |
| ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), clength ) |
| ); |
| break; |
| case "zzz": |
| // Time zone offset with leading zero |
| hour = value.getTimezoneOffset() / 60; |
| ret.push( |
| ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), 2 ) |
| // Hard coded ":" separator, rather than using cal.TimeSeparator |
| // Repeated here for consistency, plus ":" was already assumed in date parsing. |
| + ":" + padZeros( Math.abs(value.getTimezoneOffset() % 60), 2 ) |
| ); |
| break; |
| case "g": |
| case "gg": |
| if ( cal.eras ) { |
| ret.push( |
| cal.eras[ getEra(value, eras) ].name |
| ); |
| } |
| break; |
| case "/": |
| ret.push( cal["/"] ); |
| break; |
| default: |
| throw "Invalid date format pattern \'" + current + "\'."; |
| break; |
| } |
| } |
| return ret.join( "" ); |
| }; |
| |
| // formatNumber |
| (function() { |
| var expandNumber; |
| |
| expandNumber = function( number, precision, formatInfo ) { |
| var groupSizes = formatInfo.groupSizes, |
| curSize = groupSizes[ 0 ], |
| curGroupIndex = 1, |
| factor = Math.pow( 10, precision ), |
| rounded = Math.round( number * factor ) / factor; |
| |
| if ( !isFinite(rounded) ) { |
| rounded = number; |
| } |
| number = rounded; |
| |
| var numberString = number+"", |
| right = "", |
| split = numberString.split( /e/i ), |
| exponent = split.length > 1 ? parseInt( split[1], 10 ) : 0; |
| numberString = split[ 0 ]; |
| split = numberString.split( "." ); |
| numberString = split[ 0 ]; |
| right = split.length > 1 ? split[ 1 ] : ""; |
| |
| var l; |
| if ( exponent > 0 ) { |
| right = zeroPad( right, exponent, false ); |
| numberString += right.slice( 0, exponent ); |
| right = right.substr( exponent ); |
| } |
| else if ( exponent < 0 ) { |
| exponent = -exponent; |
| numberString = zeroPad( numberString, exponent + 1 ); |
| right = numberString.slice( -exponent, numberString.length ) + right; |
| numberString = numberString.slice( 0, -exponent ); |
| } |
| |
| if ( precision > 0 ) { |
| right = formatInfo[ "." ] + |
| ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) ); |
| } |
| else { |
| right = ""; |
| } |
| |
| var stringIndex = numberString.length - 1, |
| sep = formatInfo[ "," ], |
| ret = ""; |
| |
| while ( stringIndex >= 0 ) { |
| if ( curSize === 0 || curSize > stringIndex ) { |
| return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? (sep + ret + right) : right ); |
| } |
| ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? (sep + ret) : "" ); |
| |
| stringIndex -= curSize; |
| |
| if ( curGroupIndex < groupSizes.length ) { |
| curSize = groupSizes[ curGroupIndex ]; |
| curGroupIndex++; |
| } |
| } |
| |
| return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right; |
| }; |
| |
| formatNumber = function( value, format, culture ) { |
| if ( !isFinite(value) ) { |
| if ( value === Infinity ) { |
| return culture.numberFormat.positiveInfinity; |
| } |
| if ( value === -Infinity ) { |
| return culture.numberFormat.negativeInfinity; |
| } |
| return culture.numberFormat.NaN; |
| } |
| if ( !format || format === "i" ) { |
| return culture.name.length ? value.toLocaleString() : value.toString(); |
| } |
| format = format || "D"; |
| |
| var nf = culture.numberFormat, |
| number = Math.abs( value ), |
| precision = -1, |
| pattern; |
| if ( format.length > 1 ) precision = parseInt( format.slice(1), 10 ); |
| |
| var current = format.charAt( 0 ).toUpperCase(), |
| formatInfo; |
| |
| switch ( current ) { |
| case "D": |
| pattern = "n"; |
| number = truncate( number ); |
| if ( precision !== -1 ) { |
| number = zeroPad( "" + number, precision, true ); |
| } |
| if ( value < 0 ) number = "-" + number; |
| break; |
| case "N": |
| formatInfo = nf; |
| // fall through |
| case "C": |
| formatInfo = formatInfo || nf.currency; |
| // fall through |
| case "P": |
| formatInfo = formatInfo || nf.percent; |
| pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" ); |
| if ( precision === -1 ) precision = formatInfo.decimals; |
| number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo ); |
| break; |
| default: |
| throw "Bad number format specifier: " + current; |
| } |
| |
| var patternParts = /n|\$|-|%/g, |
| ret = ""; |
| for ( ; ; ) { |
| var index = patternParts.lastIndex, |
| ar = patternParts.exec( pattern ); |
| |
| ret += pattern.slice( index, ar ? ar.index : pattern.length ); |
| |
| if ( !ar ) { |
| break; |
| } |
| |
| switch ( ar[0] ) { |
| case "n": |
| ret += number; |
| break; |
| case "$": |
| ret += nf.currency.symbol; |
| break; |
| case "-": |
| // don't make 0 negative |
| if ( /[1-9]/.test(number) ) { |
| ret += nf[ "-" ]; |
| } |
| break; |
| case "%": |
| ret += nf.percent.symbol; |
| break; |
| } |
| } |
| |
| return ret; |
| }; |
| |
| }()); |
| |
| getTokenRegExp = function() { |
| // regular expression for matching date and time tokens in format strings. |
| 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; |
| }; |
| |
| getEra = function( date, eras ) { |
| if ( !eras ) return 0; |
| var start, ticks = date.getTime(); |
| for ( var i = 0, l = eras.length; i < l; i++ ) { |
| start = eras[ i ].start; |
| if ( start === null || ticks >= start ) { |
| return i; |
| } |
| } |
| return 0; |
| }; |
| |
| getEraYear = function( date, cal, era, sortable ) { |
| var year = date.getFullYear(); |
| if ( !sortable && cal.eras ) { |
| // convert normal gregorian year to era-shifted gregorian |
| // year by subtracting the era offset |
| year -= cal.eras[ era ].offset; |
| } |
| return year; |
| }; |
| |
| // parseExact |
| (function() { |
| var expandYear, |
| getDayIndex, |
| getMonthIndex, |
| getParseRegExp, |
| outOfRange, |
| toUpper, |
| toUpperArray; |
| |
| expandYear = function( cal, year ) { |
| // expands 2-digit year into 4 digits. |
| if ( year < 100 ) { |
| var now = new Date(), |
| era = getEra( now ), |
| curr = getEraYear( now, cal, era ), |
| twoDigitYearMax = cal.twoDigitYearMax; |
| twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax; |
| year += curr - ( curr % 100 ); |
| if ( year > twoDigitYearMax ) { |
| year -= 100; |
| } |
| } |
| return year; |
| }; |
| |
| getDayIndex = function ( cal, value, abbr ) { |
| var ret, |
| days = cal.days, |
| upperDays = cal._upperDays; |
| if ( !upperDays ) { |
| cal._upperDays = upperDays = [ |
| toUpperArray( days.names ), |
| toUpperArray( days.namesAbbr ), |
| toUpperArray( days.namesShort ) |
| ]; |
| } |
| value = toUpper( value ); |
| if ( abbr ) { |
| ret = arrayIndexOf( upperDays[1], value ); |
| if ( ret === -1 ) { |
| ret = arrayIndexOf( upperDays[2], value ); |
| } |
| } |
| else { |
| ret = arrayIndexOf( upperDays[0], value ); |
| } |
| return ret; |
| }; |
| |
| getMonthIndex = function( cal, value, abbr ) { |
| var months = cal.months, |
| monthsGen = cal.monthsGenitive || cal.months, |
| upperMonths = cal._upperMonths, |
| upperMonthsGen = cal._upperMonthsGen; |
| if ( !upperMonths ) { |
| cal._upperMonths = upperMonths = [ |
| toUpperArray( months.names ), |
| toUpperArray( months.namesAbbr ) |
| ]; |
| cal._upperMonthsGen = upperMonthsGen = [ |
| toUpperArray( monthsGen.names ), |
| toUpperArray( monthsGen.namesAbbr ) |
| ]; |
| } |
| value = toUpper( value ); |
| var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value ); |
| if ( i < 0 ) { |
| i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value ); |
| } |
| return i; |
| }; |
| |
| getParseRegExp = function( cal, format ) { |
| // converts a format string into a regular expression with groups that |
| // can be used to extract date fields from a date string. |
| // check for a cached parse regex. |
| var re = cal._parseRegExp; |
| if ( !re ) { |
| cal._parseRegExp = re = {}; |
| } |
| else { |
| var reFormat = re[ format ]; |
| if ( reFormat ) { |
| return reFormat; |
| } |
| } |
| |
| // expand single digit formats, then escape regular expression characters. |
| var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ), |
| regexp = [ "^" ], |
| groups = [], |
| index = 0, |
| quoteCount = 0, |
| tokenRegExp = getTokenRegExp(), |
| match; |
| |
| // iterate through each date token found. |
| while ( (match = tokenRegExp.exec(expFormat)) !== null ) { |
| var preMatch = expFormat.slice( index, match.index ); |
| index = tokenRegExp.lastIndex; |
| |
| // don't replace any matches that occur inside a string literal. |
| quoteCount += appendPreOrPostMatch( preMatch, regexp ); |
| if ( quoteCount % 2 ) { |
| regexp.push( match[0] ); |
| continue; |
| } |
| |
| // add a regex group for the token. |
| var m = match[ 0 ], |
| len = m.length, |
| add; |
| switch ( m ) { |
| case "dddd": case "ddd": |
| case "MMMM": case "MMM": |
| case "gg": case "g": |
| add = "(\\D+)"; |
| break; |
| case "tt": case "t": |
| add = "(\\D*)"; |
| break; |
| case "yyyy": |
| case "fff": |
| case "ff": |
| case "f": |
| add = "(\\d{" + len + "})"; |
| break; |
| case "dd": case "d": |
| case "MM": case "M": |
| case "yy": case "y": |
| case "HH": case "H": |
| case "hh": case "h": |
| case "mm": case "m": |
| case "ss": case "s": |
| add = "(\\d\\d?)"; |
| break; |
| case "zzz": |
| add = "([+-]?\\d\\d?:\\d{2})"; |
| break; |
| case "zz": case "z": |
| add = "([+-]?\\d\\d?)"; |
| break; |
| case "/": |
| add = "(\\" + cal[ "/" ] + ")"; |
| break; |
| default: |
| throw "Invalid date format pattern \'" + m + "\'."; |
| break; |
| } |
| if ( add ) { |
| regexp.push( add ); |
| } |
| groups.push( match[0] ); |
| } |
| appendPreOrPostMatch( expFormat.slice(index), regexp ); |
| regexp.push( "$" ); |
| |
| // allow whitespace to differ when matching formats. |
| var regexpStr = regexp.join( "" ).replace( /\s+/g, "\\s+" ), |
| parseRegExp = { "regExp": regexpStr, "groups": groups }; |
| |
| // cache the regex for this format. |
| return re[ format ] = parseRegExp; |
| }; |
| |
| outOfRange = function( value, low, high ) { |
| return value < low || value > high; |
| }; |
| |
| toUpper = function( value ) { |
| // "he-IL" has non-breaking space in weekday names. |
| return value.split( "\u00A0" ).join( " " ).toUpperCase(); |
| }; |
| |
| toUpperArray = function( arr ) { |
| var results = []; |
| for ( var i = 0, l = arr.length; i < l; i++ ) { |
| results[ i ] = toUpper( arr[i] ); |
| } |
| return results; |
| }; |
| |
| parseExact = function( value, format, culture ) { |
| // try to parse the date string by matching against the format string |
| // while using the specified culture for date field names. |
| value = trim( value ); |
| var cal = culture.calendar, |
| // convert date formats into regular expressions with groupings. |
| // use the regexp to determine the input format and extract the date fields. |
| parseInfo = getParseRegExp( cal, format ), |
| match = new RegExp( parseInfo.regExp ).exec( value ); |
| if ( match === null ) { |
| return null; |
| } |
| // found a date format that matches the input. |
| var groups = parseInfo.groups, |
| era = null, year = null, month = null, date = null, weekDay = null, |
| hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null, |
| pmHour = false; |
| // iterate the format groups to extract and set the date fields. |
| for ( var j = 0, jl = groups.length; j < jl; j++ ) { |
| var matchGroup = match[ j + 1 ]; |
| if ( matchGroup ) { |
| var current = groups[ j ], |
| clength = current.length, |
| matchInt = parseInt( matchGroup, 10 ); |
| switch ( current ) { |
| case "dd": case "d": |
| // Day of month. |
| date = matchInt; |
| // check that date is generally in valid range, also checking overflow below. |
| if ( outOfRange(date, 1, 31) ) return null; |
| break; |
| case "MMM": case "MMMM": |
| month = getMonthIndex( cal, matchGroup, clength === 3 ); |
| if ( outOfRange(month, 0, 11) ) return null; |
| break; |
| case "M": case "MM": |
| // Month. |
| month = matchInt - 1; |
| if ( outOfRange(month, 0, 11) ) return null; |
| break; |
| case "y": case "yy": |
| case "yyyy": |
| year = clength < 4 ? expandYear( cal, matchInt ) : matchInt; |
| if ( outOfRange(year, 0, 9999) ) return null; |
| break; |
| case "h": case "hh": |
| // Hours (12-hour clock). |
| hour = matchInt; |
| if ( hour === 12 ) hour = 0; |
| if ( outOfRange(hour, 0, 11) ) return null; |
| break; |
| case "H": case "HH": |
| // Hours (24-hour clock). |
| hour = matchInt; |
| if ( outOfRange(hour, 0, 23) ) return null; |
| break; |
| case "m": case "mm": |
| // Minutes. |
| min = matchInt; |
| if ( outOfRange(min, 0, 59) ) return null; |
| break; |
| case "s": case "ss": |
| // Seconds. |
| sec = matchInt; |
| if ( outOfRange(sec, 0, 59) ) return null; |
| break; |
| case "tt": case "t": |
| // AM/PM designator. |
| // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of |
| // the AM tokens. If not, fail the parse for this format. |
| pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] ); |
| if ( |
| !pmHour && ( |
| !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] ) |
| ) |
| ) return null; |
| break; |
| case "f": |
| // Deciseconds. |
| case "ff": |
| // Centiseconds. |
| case "fff": |
| // Milliseconds. |
| msec = matchInt * Math.pow( 10, 3 - clength ); |
| if ( outOfRange(msec, 0, 999) ) return null; |
| break; |
| case "ddd": |
| // Day of week. |
| case "dddd": |
| // Day of week. |
| weekDay = getDayIndex( cal, matchGroup, clength === 3 ); |
| if ( outOfRange(weekDay, 0, 6) ) return null; |
| break; |
| case "zzz": |
| // Time zone offset in +/- hours:min. |
| var offsets = matchGroup.split( /:/ ); |
| if ( offsets.length !== 2 ) return null; |
| hourOffset = parseInt( offsets[0], 10 ); |
| if ( outOfRange(hourOffset, -12, 13) ) return null; |
| var minOffset = parseInt( offsets[1], 10 ); |
| if ( outOfRange(minOffset, 0, 59) ) return null; |
| tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset ); |
| break; |
| case "z": case "zz": |
| // Time zone offset in +/- hours. |
| hourOffset = matchInt; |
| if ( outOfRange(hourOffset, -12, 13) ) return null; |
| tzMinOffset = hourOffset * 60; |
| break; |
| case "g": case "gg": |
| var eraName = matchGroup; |
| if ( !eraName || !cal.eras ) return null; |
| eraName = trim( eraName.toLowerCase() ); |
| for ( var i = 0, l = cal.eras.length; i < l; i++ ) { |
| if ( eraName === cal.eras[i].name.toLowerCase() ) { |
| era = i; |
| break; |
| } |
| } |
| // could not find an era with that name |
| if ( era === null ) return null; |
| break; |
| } |
| } |
| } |
| var result = new Date(), defaultYear, convert = cal.convert; |
| defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear(); |
| if ( year === null ) { |
| year = defaultYear; |
| } |
| else if ( cal.eras ) { |
| // year must be shifted to normal gregorian year |
| // but not if year was not specified, its already normal gregorian |
| // per the main if clause above. |
| year += cal.eras[( era || 0 )].offset; |
| } |
| // set default day and month to 1 and January, so if unspecified, these are the defaults |
| // instead of the current day/month. |
| if ( month === null ) { |
| month = 0; |
| } |
| if ( date === null ) { |
| date = 1; |
| } |
| // now have year, month, and date, but in the culture's calendar. |
| // convert to gregorian if necessary |
| if ( convert ) { |
| result = convert.toGregorian( year, month, date ); |
| // conversion failed, must be an invalid match |
| if ( result === null ) return null; |
| } |
| else { |
| // have to set year, month and date together to avoid overflow based on current date. |
| result.setFullYear( year, month, date ); |
| // check to see if date overflowed for specified month (only checked 1-31 above). |
| if ( result.getDate() !== date ) return null; |
| // invalid day of week. |
| if ( weekDay !== null && result.getDay() !== weekDay ) { |
| return null; |
| } |
| } |
| // if pm designator token was found make sure the hours fit the 24-hour clock. |
| if ( pmHour && hour < 12 ) { |
| hour += 12; |
| } |
| result.setHours( hour, min, sec, msec ); |
| if ( tzMinOffset !== null ) { |
| // adjust timezone to utc before applying local offset. |
| var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() ); |
| // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours |
| // to ensure both these fields will not exceed this range. adjustedMin will range |
| // somewhere between -1440 and 1500, so we only need to split this into hours. |
| result.setHours( result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60 ); |
| } |
| return result; |
| }; |
| }()); |
| |
| parseNegativePattern = function( value, nf, negativePattern ) { |
| var neg = nf[ "-" ], |
| pos = nf[ "+" ], |
| ret; |
| switch ( negativePattern ) { |
| case "n -": |
| neg = " " + neg; |
| pos = " " + pos; |
| // fall through |
| case "n-": |
| if ( endsWith(value, neg) ) { |
| ret = [ "-", value.substr(0, value.length - neg.length) ]; |
| } |
| else if ( endsWith(value, pos) ) { |
| ret = [ "+", value.substr(0, value.length - pos.length) ]; |
| } |
| break; |
| case "- n": |
| neg += " "; |
| pos += " "; |
| // fall through |
| case "-n": |
| if ( startsWith(value, neg) ) { |
| ret = [ "-", value.substr(neg.length) ]; |
| } |
| else if ( startsWith(value, pos) ) { |
| ret = [ "+", value.substr(pos.length) ]; |
| } |
| break; |
| case "(n)": |
| if ( startsWith(value, "(") && endsWith(value, ")") ) { |
| ret = [ "-", value.substr(1, value.length - 2) ]; |
| } |
| break; |
| } |
| return ret || [ "", value ]; |
| }; |
| |
| // |
| // public instance functions |
| // |
| |
| Globalize.prototype.findClosestCulture = function( cultureSelector ) { |
| return Globalize.findClosestCulture.call( this, cultureSelector ); |
| }; |
| |
| Globalize.prototype.format = function( value, format, cultureSelector ) { |
| return Globalize.format.call( this, value, format, cultureSelector ); |
| }; |
| |
| Globalize.prototype.localize = function( key, cultureSelector ) { |
| return Globalize.localize.call( this, key, cultureSelector ); |
| }; |
| |
| Globalize.prototype.parseInt = function( value, radix, cultureSelector ) { |
| return Globalize.parseInt.call( this, value, radix, cultureSelector ); |
| }; |
| |
| Globalize.prototype.parseFloat = function( value, radix, cultureSelector ) { |
| return Globalize.parseFloat.call( this, value, radix, cultureSelector ); |
| }; |
| |
| Globalize.prototype.culture = function( cultureSelector ) { |
| return Globalize.culture.call( this, cultureSelector ); |
| }; |
| |
| // |
| // public singleton functions |
| // |
| |
| Globalize.addCultureInfo = function( cultureName, baseCultureName, info ) { |
| |
| var base = {}, |
| isNew = false; |
| |
| if ( typeof cultureName !== "string" ) { |
| // cultureName argument is optional string. If not specified, assume info is first |
| // and only argument. Specified info deep-extends current culture. |
| info = cultureName; |
| cultureName = this.culture().name; |
| base = this.cultures[ cultureName ]; |
| } else if ( typeof baseCultureName !== "string" ) { |
| // baseCultureName argument is optional string. If not specified, assume info is second |
| // argument. Specified info deep-extends specified culture. |
| // If specified culture does not exist, create by deep-extending default |
| info = baseCultureName; |
| isNew = ( this.cultures[ cultureName ] == null ); |
| base = this.cultures[ cultureName ] || this.cultures[ "default" ]; |
| } else { |
| // cultureName and baseCultureName specified. Assume a new culture is being created |
| // by deep-extending an specified base culture |
| isNew = true; |
| base = this.cultures[ baseCultureName ]; |
| } |
| |
| this.cultures[ cultureName ] = extend(true, {}, |
| base, |
| info |
| ); |
| // Make the standard calendar the current culture if it's a new culture |
| if ( isNew ) { |
| this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard; |
| } |
| }; |
| |
| Globalize.findClosestCulture = function( name ) { |
| var match; |
| if ( !name ) { |
| return this.findClosestCulture( this.cultureSelector ) || this.cultures[ "default" ]; |
| } |
| if ( typeof name === "string" ) { |
| name = name.split( "," ); |
| } |
| if ( isArray(name) ) { |
| var lang, |
| cultures = this.cultures, |
| list = name, |
| i, l = list.length, |
| prioritized = []; |
| for ( i = 0; i < l; i++ ) { |
| name = trim( list[i] ); |
| var pri, parts = name.split( ";" ); |
| lang = trim( parts[0] ); |
| if ( parts.length === 1 ) { |
| pri = 1; |
| } |
| else { |
| name = trim( parts[1] ); |
| if ( name.indexOf("q=") === 0 ) { |
| name = name.substr( 2 ); |
| pri = parseFloat( name ); |
| pri = isNaN( pri ) ? 0 : pri; |
| } |
| else { |
| pri = 1; |
| } |
| } |
| prioritized.push({ lang: lang, pri: pri }); |
| } |
| prioritized.sort(function( a, b ) { |
| return a.pri < b.pri ? 1 : -1; |
| }); |
| |
| // exact match |
| for ( i = 0; i < l; i++ ) { |
| lang = prioritized[ i ].lang; |
| match = cultures[ lang ]; |
| if ( match ) { |
| return match; |
| } |
| } |
| |
| // neutral language match |
| for ( i = 0; i < l; i++ ) { |
| lang = prioritized[ i ].lang; |
| do { |
| var index = lang.lastIndexOf( "-" ); |
| if ( index === -1 ) { |
| break; |
| } |
| // strip off the last part. e.g. en-US => en |
| lang = lang.substr( 0, index ); |
| match = cultures[ lang ]; |
| if ( match ) { |
| return match; |
| } |
| } |
| while ( 1 ); |
| } |
| |
| // last resort: match first culture using that language |
| for ( i = 0; i < l; i++ ) { |
| lang = prioritized[ i ].lang; |
| for ( var cultureKey in cultures ) { |
| var culture = cultures[ cultureKey ]; |
| if ( culture.language == lang ) { |
| return culture; |
| } |
| } |
| } |
| } |
| else if ( typeof name === "object" ) { |
| return name; |
| } |
| return match || null; |
| }; |
| |
| Globalize.format = function( value, format, cultureSelector ) { |
| culture = this.findClosestCulture( cultureSelector ); |
| if ( value instanceof Date ) { |
| value = formatDate( value, format, culture ); |
| } |
| else if ( typeof value === "number" ) { |
| value = formatNumber( value, format, culture ); |
| } |
| return value; |
| }; |
| |
| Globalize.localize = function( key, cultureSelector ) { |
| return this.findClosestCulture( cultureSelector ).messages[ key ] || |
| this.cultures[ "default" ].messages[ key ]; |
| }; |
| |
| Globalize.parseDate = function( value, formats, culture ) { |
| culture = this.findClosestCulture( culture ); |
| |
| var date, prop, patterns; |
| if ( formats ) { |
| if ( typeof formats === "string" ) { |
| formats = [ formats ]; |
| } |
| if ( formats.length ) { |
| for ( var i = 0, l = formats.length; i < l; i++ ) { |
| var format = formats[ i ]; |
| if ( format ) { |
| date = parseExact( value, format, culture ); |
| if ( date ) { |
| break; |
| } |
| } |
| } |
| } |
| } else { |
| patterns = culture.calendar.patterns; |
| for ( prop in patterns ) { |
| date = parseExact( value, patterns[prop], culture ); |
| if ( date ) { |
| break; |
| } |
| } |
| } |
| |
| return date || null; |
| }; |
| |
| Globalize.parseInt = function( value, radix, cultureSelector ) { |
| return truncate( Globalize.parseFloat(value, radix, cultureSelector) ); |
| }; |
| |
| Globalize.parseFloat = function( value, radix, cultureSelector ) { |
| // radix argument is optional |
| if ( typeof radix !== "number" ) { |
| cultureSelector = radix; |
| radix = 10; |
| } |
| |
| var culture = this.findClosestCulture( cultureSelector ); |
| var ret = NaN, |
| nf = culture.numberFormat; |
| |
| if ( value.indexOf(culture.numberFormat.currency.symbol) > -1 ) { |
| // remove currency symbol |
| value = value.replace( culture.numberFormat.currency.symbol, "" ); |
| // replace decimal seperator |
| value = value.replace( culture.numberFormat.currency["."], culture.numberFormat["."] ); |
| } |
| |
| // trim leading and trailing whitespace |
| value = trim( value ); |
| |
| // allow infinity or hexidecimal |
| if ( regexInfinity.test(value) ) { |
| ret = parseFloat( value ); |
| } |
| else if ( !radix && regexHex.test(value) ) { |
| ret = parseInt( value, 16 ); |
| } |
| else { |
| |
| // determine sign and number |
| var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ), |
| sign = signInfo[ 0 ], |
| num = signInfo[ 1 ]; |
| |
| // #44 - try parsing as "(n)" |
| if ( sign === "" && nf.pattern[0] !== "(n)" ) { |
| signInfo = parseNegativePattern( value, nf, "(n)" ); |
| sign = signInfo[ 0 ]; |
| num = signInfo[ 1 ]; |
| } |
| |
| // try parsing as "-n" |
| if ( sign === "" && nf.pattern[0] !== "-n" ) { |
| signInfo = parseNegativePattern( value, nf, "-n" ); |
| sign = signInfo[ 0 ]; |
| num = signInfo[ 1 ]; |
| } |
| |
| sign = sign || "+"; |
| |
| // determine exponent and number |
| var exponent, |
| intAndFraction, |
| exponentPos = num.indexOf( "e" ); |
| if ( exponentPos < 0 ) exponentPos = num.indexOf( "E" ); |
| if ( exponentPos < 0 ) { |
| intAndFraction = num; |
| exponent = null; |
| } |
| else { |
| intAndFraction = num.substr( 0, exponentPos ); |
| exponent = num.substr( exponentPos + 1 ); |
| } |
| // determine decimal position |
| var integer, |
| fraction, |
| decSep = nf[ "." ], |
| decimalPos = intAndFraction.indexOf( decSep ); |
| if ( decimalPos < 0 ) { |
| integer = intAndFraction; |
| fraction = null; |
| } |
| else { |
| integer = intAndFraction.substr( 0, decimalPos ); |
| fraction = intAndFraction.substr( decimalPos + decSep.length ); |
| } |
| // handle groups (e.g. 1,000,000) |
| var groupSep = nf[ "," ]; |
| integer = integer.split( groupSep ).join( "" ); |
| var altGroupSep = groupSep.replace( /\u00A0/g, " " ); |
| if ( groupSep !== altGroupSep ) { |
| integer = integer.split( altGroupSep ).join( "" ); |
| } |
| // build a natively parsable number string |
| var p = sign + integer; |
| if ( fraction !== null ) { |
| p += "." + fraction; |
| } |
| if ( exponent !== null ) { |
| // exponent itself may have a number patternd |
| var expSignInfo = parseNegativePattern( exponent, nf, "-n" ); |
| p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ]; |
| } |
| if ( regexParseFloat.test(p) ) { |
| ret = parseFloat( p ); |
| } |
| } |
| return ret; |
| }; |
| |
| Globalize.culture = function( cultureSelector ) { |
| // setter |
| if ( typeof cultureSelector !== "undefined" ) { |
| this.cultureSelector = cultureSelector; |
| } |
| // getter |
| return this.findClosestCulture( cultureSelector ) || this.culture[ "default" ]; |
| }; |
| |
| window.Globalize = Globalize; |
| |
| if ( typeof define === "function" ) { |
| define( "globalize/globalize", [], function () { return Globalize; } ); |
| } |
| |
| }( window )); |