home *** CD-ROM | disk | FTP | other *** search
/ Freelog 117 / FreelogNo117-OctobreNovembre2013.iso / Theme / 8GadgetPack / 8GadgetPackSetup.msi / Gadgets.7z / Gadgets / tweetz.gadget / jquery.glob.js < prev    next >
Text File  |  2013-04-09  |  50KB  |  1,333 lines

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