PageSourceSearch

https://erus.ru/Content/js/lib/plugins-jquery/globalize/globalize.js

js erus.ru collected 2026-10-07 19:33:39 UTC 62,753 bytes, 1,586 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.