/[projects]/misc/horsensspejder-web/jquery/jquery-ui-1.10.3/external/globalize.js
ViewVC logotype

Annotation of /misc/horsensspejder-web/jquery/jquery-ui-1.10.3/external/globalize.js

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20