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