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