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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2125 - (show 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 /*!
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