2011-08-10 12:55:16 +00:00
/ * !
2013-11-30 12:22:17 +00:00
* Globalize v1 . 0.0 pre
2011-08-10 12:55:16 +00:00
*
* http : //github.com/jquery/globalize
*
2013-11-30 12:22:17 +00:00
* Copyright 2005 , 2013 jQuery Foundation , Inc . and other contributors
* Released under the MIT license
2011-08-10 12:55:16 +00:00
* http : //jquery.org/license
2013-11-30 12:22:17 +00:00
*
* Date : 2013 - 12 - 01 T12 : 08 Z
2011-08-10 12:55:16 +00:00
* /
2013-11-30 12:22:17 +00:00
( function ( root , factory ) {
if ( typeof define === "function" && define . amd ) {
// AMD.
define ( factory ) ;
} else if ( typeof module === "object" && typeof module . exports === "object" ) {
// Node. CommonJS.
module . exports = factory ( ) ;
} else {
// Global
root . Globalize = factory ( ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
} ( this , function ( ) {
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
/ * *
* CLDR JavaScript Library v0 . 2.4 - pre
* http : //jquery.com/
*
* Copyright 2013 Rafael Xavier de Souza
* Released under the MIT license
* http : //jquery.org/license
*
* Date : 2013 - 11 - 30 T11 : 30 Z
* /
/ * !
* CLDR JavaScript Library v0 . 2.4 - pre 2013 - 11 - 30 T11 : 30 Z MIT license © Rafael Xavier
* http : //git.io/h4lmVg
* /
var Cldr = ( function ( ) {
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
var alwaysArray = function ( stringOrArray ) {
return typeof stringOrArray === "string" ? [ stringOrArray ] : stringOrArray ;
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
var common = function ( Cldr ) {
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
Cldr . prototype . main = function ( path ) {
path = alwaysArray ( path ) ;
return this . get ( [ "main/{languageId}" ] . concat ( path ) ) ;
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
var arrayIsArray = Array . isArray || function ( obj ) {
return Object . prototype . toString . call ( obj ) === "[object Array]" ;
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
var pathNormalize = function ( path , attributes ) {
if ( arrayIsArray ( path ) ) {
path = path . join ( "/" ) ;
}
if ( typeof path !== "string" ) {
throw new Error ( "invalid path \"" + path + "\"" ) ;
}
// 1: Ignore leading slash `/`
// 2: Ignore leading `cldr/`
path = path
. replace ( /^\// , "" ) /* 1 */
. replace ( /^cldr\// , "" ) ; /* 2 */
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
// Replace {attribute}'s
path = path . replace ( /{[a-zA-Z]+}/g , function ( name ) {
name = name . replace ( /^{([^}]*)}$/ , "$1" ) ;
return attributes [ name ] ;
} ) ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
return path . split ( "/" ) ;
} ;
var arraySome = function ( array , callback ) {
var i , length ;
if ( array . some ) {
return array . some ( callback ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
for ( i = 0 , length = array . length ; i < length ; i ++ ) {
if ( callback ( array [ i ] , i , array ) ) {
return true ;
2011-08-10 12:55:16 +00:00
}
}
2013-11-30 12:22:17 +00:00
return false ;
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
// Return the maximized language id as defined in
// http://www.unicode.org/reports/tr35/#Likely_Subtags
// 1. Canonicalize.
// 1.1 Make sure the input locale is in canonical form: uses the right separator, and has the right casing.
// TODO Right casing? What df? It seems languages are lowercase, scripts are Capitalized, territory is uppercase. I am leaving this as an exercise to the user.
// 1.2 Replace any deprecated subtags with their canonical values using the <alias> data in supplemental metadata. Use the first value in the replacement list, if it exists. Language tag replacements may have multiple parts, such as "sh" ➞ "sr_Latn" or mo" ➞ "ro_MD". In such a case, the original script and/or region are retained if there is one. Thus "sh_Arab_AQ" ➞ "sr_Arab_AQ", not "sr_Latn_AQ".
// TODO What <alias> data?
// 1.3 If the tag is grandfathered (see <variable id="$grandfathered" type="choice"> in the supplemental data), then return it.
// TODO grandfathered?
// 1.4 Remove the script code 'Zzzz' and the region code 'ZZ' if they occur.
// 1.5 Get the components of the cleaned-up source tag (languages, scripts, and regions), plus any variants and extensions.
// 2. Lookup. Lookup each of the following in order, and stop on the first match:
// 2.1 languages_scripts_regions
// 2.2 languages_regions
// 2.3 languages_scripts
// 2.4 languages
// 2.5 und_scripts
// 3. Return
// 3.1 If there is no match, either return an error value, or the match for "und" (in APIs where a valid language tag is required).
// 3.2 Otherwise there is a match = languagem_scriptm_regionm
// 3.3 Let xr = xs if xs is not empty, and xm otherwise.
// 3.4 Return the language tag composed of languager _ scriptr _ regionr + variants + extensions .
//
// @subtags [Array] normalized language id subtags tuple (see init.js).
var likelySubtags = function ( cldr , subtags , options ) {
var match , matchFound ,
language = subtags [ 0 ] ,
script = subtags [ 1 ] ,
territory = subtags [ 2 ] ;
options = options || { } ;
// Skip if (language, script, territory) is not empty [3.3]
if ( language !== "und" && script !== "Zzzz" && territory !== "ZZ" ) {
return [ language , script , territory ] ;
}
// Skip if no supplemental likelySubtags data is present
if ( typeof cldr . get ( "supplemental/likelySubtags" ) === "undefined" ) {
return ;
}
// [2]
matchFound = arraySome ( [
[ language , script , territory ] ,
[ language , territory ] ,
[ language , script ] ,
[ language ] ,
[ "und" , script ]
] , function ( test ) {
return match = ! ( /\b(Zzzz|ZZ)\b/ ) . test ( test . join ( "_" ) ) /* [1.4] */ && cldr . get ( [ "supplemental/likelySubtags" , test . join ( "_" ) ] ) ;
} ) ;
// [3]
if ( matchFound ) {
// [3.2 .. 3.4]
match = match . split ( "_" ) ;
return [
language !== "und" ? language : match [ 0 ] ,
script !== "Zzzz" ? script : match [ 1 ] ,
territory !== "ZZ" ? territory : match [ 2 ]
] ;
} else if ( options . force ) {
// [3.1.2]
return cldr . get ( "supplemental/likelySubtags/und" ) . split ( "_" ) ;
} else {
// [3.1.1]
return ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
// Given a locale, remove any fields that Add Likely Subtags would add.
// http://www.unicode.org/reports/tr35/#Likely_Subtags
// 1. First get max = AddLikelySubtags(inputLocale). If an error is signaled, return it.
// 2. Remove the variants from max.
// 3. Then for trial in {language, language _ region, language _ script}. If AddLikelySubtags(trial) = max, then return trial + variants.
// 4. If you do not get a match, return max + variants.
//
// @maxLanguageId [Array] maxLanguageId tuple (see init.js).
var removeLikelySubtags = function ( cldr , maxLanguageId ) {
var match , matchFound ,
language = maxLanguageId [ 0 ] ,
script = maxLanguageId [ 1 ] ,
territory = maxLanguageId [ 2 ] ;
// [3]
matchFound = arraySome ( [
[ [ language , "Zzzz" , "ZZ" ] , [ language ] ] ,
[ [ language , "Zzzz" , territory ] , [ language , territory ] ] ,
[ [ language , script , "ZZ" ] , [ language , script ] ]
] , function ( test ) {
var result = likelySubtags ( cldr , test [ 0 ] ) ;
match = test [ 1 ] ;
return result && result [ 0 ] === maxLanguageId [ 0 ] &&
result [ 1 ] === maxLanguageId [ 1 ] &&
result [ 2 ] === maxLanguageId [ 2 ] ;
} ) ;
// [4]
return matchFound ? match : maxLanguageId ;
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
var supplemental = function ( cldr ) {
var prepend , supplemental ;
prepend = function ( prepend ) {
return function ( path ) {
path = alwaysArray ( path ) ;
return cldr . get ( [ prepend ] . concat ( path ) ) ;
} ;
} ;
supplemental = prepend ( "supplemental" ) ;
// Week Data
// http://www.unicode.org/reports/tr35/tr35-dates.html#Week_Data
supplemental . weekData = prepend ( "supplemental/weekData" ) ;
supplemental . weekData . firstDay = function ( ) {
return cldr . get ( "supplemental/weekData/firstDay/{territory}" ) ||
cldr . get ( "supplemental/weekData/firstDay/001" ) ;
} ;
supplemental . weekData . minDays = function ( ) {
var minDays = cldr . get ( "supplemental/weekData/minDays/{territory}" ) ||
cldr . get ( "supplemental/weekData/minDays/001" ) ;
return parseInt ( minDays , 10 ) ;
} ;
// Time Data
// http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data
supplemental . timeData = prepend ( "supplemental/timeData" ) ;
supplemental . timeData . allowed = function ( ) {
return cldr . get ( "supplemental/timeData/{territory}/_allowed" ) ||
cldr . get ( "supplemental/timeData/001/_allowed" ) ;
} ;
supplemental . timeData . preferred = function ( ) {
return cldr . get ( "supplemental/timeData/{territory}/_preferred" ) ||
cldr . get ( "supplemental/timeData/001/_preferred" ) ;
} ;
return supplemental ;
} ;
var init = function ( locale ) {
var language , languageId , maxLanguageId , script , territory , unicodeLanguageId , variant ;
if ( typeof locale !== "string" ) {
throw new Error ( "invalid locale type: \"" + JSON . stringify ( locale ) + "\"" ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
// Normalize locale code.
// Get (or deduce) the "triple subtags": language, territory (also aliased as region), and script subtags.
// Get the variant subtags (calendar, collation, currency, etc).
// refs:
// - http://www.unicode.org/reports/tr35/#Field_Definitions
// - http://www.unicode.org/reports/tr35/#Language_and_Locale_IDs
// - http://www.unicode.org/reports/tr35/#Unicode_locale_identifier
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
locale = locale . replace ( /-/ , "_" ) ;
// TODO normalize unicode locale extensions. Currently, skipped.
// unicodeLocaleExtensions = locale.split( "_u_" )[ 1 ];
locale = locale . split ( "_u_" ) [ 0 ] ;
// TODO normalize transformed extensions. Currently, skipped.
// transformedExtensions = locale.split( "_t_" )[ 1 ];
locale = locale . split ( "_t_" ) [ 0 ] ;
unicodeLanguageId = locale ;
// unicodeLanguageId = ...
switch ( true ) {
// language_script_territory..
case /^[a-z]{2}_[A-Z][a-z]{3}_[A-Z0-9]{2}(\b|_)/ . test ( unicodeLanguageId ) :
language = unicodeLanguageId . split ( "_" ) [ 0 ] ;
script = unicodeLanguageId . split ( "_" ) [ 1 ] ;
territory = unicodeLanguageId . split ( "_" ) [ 2 ] ;
variant = unicodeLanguageId . split ( "_" ) [ 3 ] ;
2011-08-10 12:55:16 +00:00
break ;
2013-11-30 12:22:17 +00:00
// language_script..
case /^[a-z]{2}_[A-Z][a-z]{3}(\b|_)/ . test ( unicodeLanguageId ) :
language = unicodeLanguageId . split ( "_" ) [ 0 ] ;
script = unicodeLanguageId . split ( "_" ) [ 1 ] ;
territory = "ZZ" ;
variant = unicodeLanguageId . split ( "_" ) [ 2 ] ;
2011-08-10 12:55:16 +00:00
break ;
2013-11-30 12:22:17 +00:00
// language_territory..
case /^[a-z]{2}_[A-Z0-9]{2}(\b|_)/ . test ( unicodeLanguageId ) :
language = unicodeLanguageId . split ( "_" ) [ 0 ] ;
script = "Zzzz" ;
territory = unicodeLanguageId . split ( "_" ) [ 1 ] ;
variant = unicodeLanguageId . split ( "_" ) [ 2 ] ;
2011-08-10 12:55:16 +00:00
break ;
2013-11-30 12:22:17 +00:00
// language.., or root
case /^([a-z]{2}|root)(\b|_)/ . test ( unicodeLanguageId ) :
language = unicodeLanguageId . split ( "_" ) [ 0 ] ;
script = "Zzzz" ;
territory = "ZZ" ;
variant = unicodeLanguageId . split ( "_" ) [ 1 ] ;
2011-08-10 12:55:16 +00:00
break ;
2013-11-30 12:22:17 +00:00
default :
language = "und" ;
2011-08-10 12:55:16 +00:00
break ;
}
2013-11-30 12:22:17 +00:00
// When a locale id does not specify a language, or territory (region), or script, they are obtained by Likely Subtags.
maxLanguageId = likelySubtags ( this , [ language , script , territory ] , { force : true } ) || unicodeLanguageId . split ( "_" ) ;
language = maxLanguageId [ 0 ] ;
script = maxLanguageId [ 1 ] ;
territory = maxLanguageId [ 2 ] ;
// TODO json content distributed on zip file use languageId with `-` on main.<lang>. Why `-` vs. `_` ?
languageId = removeLikelySubtags ( this , maxLanguageId ) . join ( "_" ) ;
// Set attributes
this . attributes = {
// Unicode Language Id
languageId : languageId ,
maxLanguageId : maxLanguageId . join ( "_" ) ,
// Unicode Language Id Subtabs
language : language ,
script : script ,
territory : territory ,
region : territory , /* alias */
variant : variant
} ;
this . locale = variant ? [ languageId , variant ] . join ( "_" ) : languageId ;
// Inlcude supplemental helper
this . supplemental = supplemental ( this ) ;
} ;
// @path: normalized path
var resourceGet = function ( data , path ) {
var i ,
node = data ,
length = path . length ;
for ( i = 0 ; i < length - 1 ; i ++ ) {
node = node [ path [ i ] ] ;
if ( ! node ) {
return undefined ;
}
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
return node [ path [ i ] ] ;
} ;
var bundleParentLookup = function ( Cldr , locale ) {
var parent ;
if ( locale === "root" ) {
return ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
// First, try to find parent on supplemental data.
parent = resourceGet ( Cldr . _resolved , pathNormalize ( [ "supplemental/parentLocales/parentLocale" , locale ] ) ) ;
if ( parent ) {
return parent ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
// Or truncate locale.
parent = locale . substr ( 0 , locale . lastIndexOf ( "_" ) ) ;
if ( ! parent ) {
return "root" ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
return parent ;
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
// @path: normalized path
var resourceSet = function ( data , path , value ) {
var i ,
node = data ,
length = path . length ;
for ( i = 0 ; i < length - 1 ; i ++ ) {
if ( ! node [ path [ i ] ] ) {
node [ path [ i ] ] = { } ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
node = node [ path [ i ] ] ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
node [ path [ i ] ] = value ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
var arrayForEach = function ( array , callback ) {
var i , length ;
if ( array . forEach ) {
return array . forEach ( callback ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
for ( i = 0 , length = array . length ; i < length ; i ++ ) {
callback ( array [ i ] , i , array ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
} ;
var jsonMerge = ( function ( ) {
// Returns new deeply merged JSON.
//
// Eg.
// merge( { a: { b: 1, c: 2 } }, { a: { b: 3, d: 4 } } )
// -> { a: { b: 3, c: 2, d: 4 } }
//
// @arguments JSON's
//
var merge = function ( ) {
var destination = { } ,
sources = [ ] . slice . call ( arguments , 0 ) ;
arrayForEach ( sources , function ( source ) {
var prop ;
for ( prop in source ) {
if ( prop in destination && arrayIsArray ( destination [ prop ] ) ) {
// Concat Arrays
destination [ prop ] = destination [ prop ] . concat ( source [ prop ] ) ;
} else if ( prop in destination && typeof destination [ prop ] === "object" ) {
// Merge Objects
destination [ prop ] = merge ( destination [ prop ] , source [ prop ] ) ;
} else {
// Set new values
destination [ prop ] = source [ prop ] ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
}
} ) ;
return destination ;
} ;
return merge ;
} ( ) ) ;
var itemLookup = ( function ( ) {
var lookup ;
lookup = function ( Cldr , locale , path , attributes , childLocale ) {
var normalizedPath , parent , value ;
// 1: Finish recursion
// 2: Avoid infinite loop
if ( typeof locale === "undefined" /* 1 */ || locale === childLocale /* 2 */ ) {
return ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
// Resolve path
normalizedPath = pathNormalize ( path , attributes ) ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
// Check resolved (cached) data first
value = resourceGet ( Cldr . _resolved , normalizedPath ) ;
if ( value ) {
return value ;
}
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
// Check raw data
value = resourceGet ( Cldr . _raw , normalizedPath ) ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
if ( ! value ) {
// Or, lookup at parent locale
parent = bundleParentLookup ( Cldr , locale ) ;
value = lookup ( Cldr , parent , path , jsonMerge ( attributes , { languageId : parent } ) , locale ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
// Set resolved (cached)
resourceSet ( Cldr . _resolved , normalizedPath , value ) ;
return value ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
return lookup ;
2011-08-10 12:55:16 +00:00
} ( ) ) ;
2013-11-30 12:22:17 +00:00
var itemGetResolved = function ( Cldr , path , attributes ) {
// Resolve path
var normalizedPath = pathNormalize ( path , attributes ) ;
return resourceGet ( Cldr . _resolved , normalizedPath ) ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
var Cldr = function ( ) {
init . apply ( this , arguments ) ;
} ;
Cldr . _resolved = { } ;
Cldr . _raw = { } ;
// Load resolved or unresolved cldr data
// @json [JSON]
Cldr . load = function ( json ) {
if ( typeof json !== "object" ) {
throw new Error ( "invalid json" ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
Cldr . _raw = jsonMerge ( Cldr . _raw , json ) ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
Cldr . prototype . get = function ( path ) {
// Simplify locale using languageId (there are no other resource bundles)
// 1: during init(), get is called, but languageId is not defined. Use "" as a workaround in this very specific scenario.
var locale = this . attributes && this . attributes . languageId || "" /* 1 */ ;
return itemGetResolved ( Cldr , path , this . attributes ) ||
itemLookup ( Cldr , locale , path , this . attributes ) ;
} ;
common ( Cldr ) ;
return Cldr ;
} ( ) ) ;
var arrayMap = function ( array , callback ) {
var clone , i , length ;
if ( array . map ) {
return array . map ( callback ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
for ( clone = [ ] , i = 0 , length = array . length ; i < length ; i ++ ) {
clone [ i ] = callback ( array [ i ] , i , array ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
return clone ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
var objectValues = function ( object ) {
var i ,
result = [ ] ;
for ( i in object ) {
result . push ( object [ i ] ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
return result ;
} ;
/ * *
* allPreset ( )
*
* @ cldr [ Cldr instance ] .
*
* Return an Array with all ( skeleton , date , time , datetime ) presets .
* /
var datetimeAllPresets = function ( cldr ) {
var result = [ ] ;
// Skeleton
result = objectValues ( cldr . main ( "dates/calendars/gregorian/dateTimeFormats/availableFormats" ) ) ;
// Time
result = result . concat ( objectValues ( cldr . main ( "dates/calendars/gregorian/timeFormats" ) ) ) ;
// Date
result = result . concat ( objectValues ( cldr . main ( "dates/calendars/gregorian/dateFormats" ) ) ) ;
// Datetime
result = result . concat ( arrayMap ( objectValues ( cldr . main ( "dates/calendars/gregorian/dateTimeFormats" ) ) , function ( datetimeFormat , key ) {
if ( typeof datetimeFormat !== "string" ) {
return datetimeFormat ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
return datetimeFormat
. replace ( /\{0\}/ , cldr . main ( [
"dates/calendars/gregorian/timeFormats" ,
key
] ) )
. replace ( /\{1\}/ , cldr . main ( [
"dates/calendars/gregorian/dateFormats" ,
key
] ) ) ;
} ) ) ;
return arrayMap ( result , function ( pattern ) {
return { pattern : pattern } ;
} ) ;
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
/ * *
* expandPattern ( pattern , cldr )
*
* @ pattern [ String or Object ] if String , it ' s considered a skeleton . Object accepts :
* - skeleton : [ String ] lookup availableFormat ;
* - date : [ String ] ( "full" | "long" | "medium" | "short" ) ;
* - time : [ String ] ( "full" | "long" | "medium" | "short" ) ;
* - datetime : [ String ] ( "full" | "long" | "medium" | "short" ) ;
* - pattern : [ String ] For more info see datetime / format . js .
*
* @ cldr [ Cldr instance ] .
*
* Return the corresponding pattern .
* Eg for "en" :
* - "GyMMMd" returns "MMM d, y G" ;
* - { skeleton : "GyMMMd" } returns "MMM d, y G" ;
* - { date : "full" } returns "EEEE, MMMM d, y" ;
* - { time : "full" } returns "h:mm:ss a zzzz" ;
* - { datetime : "full" } returns "EEEE, MMMM d, y 'at' h:mm:ss a zzzz" ;
* - { pattern : "dd/mm" } returns "dd/mm" ;
* /
var datetimeExpandPattern = function ( pattern , cldr ) {
var result ;
if ( typeof pattern === "string" ) {
pattern = { skeleton : pattern } ;
}
if ( typeof pattern === "object" ) {
switch ( true ) {
case "skeleton" in pattern :
result = cldr . main ( [
"dates/calendars/gregorian/dateTimeFormats/availableFormats" ,
pattern . skeleton
] ) ;
2011-08-10 12:55:16 +00:00
break ;
2013-11-30 12:22:17 +00:00
case "date" in pattern :
case "time" in pattern :
result = cldr . main ( [
"dates/calendars/gregorian" ,
"date" in pattern ? "dateFormats" : "timeFormats" ,
( pattern . date || pattern . time )
] ) ;
2011-08-10 12:55:16 +00:00
break ;
2013-11-30 12:22:17 +00:00
case "datetime" in pattern :
result = cldr . main ( [
"dates/calendars/gregorian/dateTimeFormats" ,
pattern . datetime
] ) ;
if ( result ) {
result = result
. replace ( /\{0\}/ , cldr . main ( [
"dates/calendars/gregorian/timeFormats" ,
pattern . datetime
] ) )
. replace ( /\{1\}/ , cldr . main ( [
"dates/calendars/gregorian/dateFormats" ,
pattern . datetime
] ) ) ;
}
2011-08-10 12:55:16 +00:00
break ;
2013-11-30 12:22:17 +00:00
case "pattern" in pattern :
result = pattern . pattern ;
2011-08-10 12:55:16 +00:00
break ;
2013-11-30 12:22:17 +00:00
2011-08-10 12:55:16 +00:00
default :
2013-11-30 12:22:17 +00:00
throw new Error ( "Invalid pattern" ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
} else {
throw new Error ( "Invalid pattern" ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
if ( ! result ) {
throw new Error ( "Pattern not found" ) ;
}
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
return result ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
var datetimeWeekDays = [ "sun" , "mon" , "tue" , "wed" , "thu" , "fri" , "sat" ] ;
var arrayIndexOf = function ( array , item ) {
if ( array . indexOf ) {
return array . indexOf ( item ) ;
}
for ( var i = 0 , length = array . length ; i < length ; i ++ ) {
if ( array [ i ] === item ) {
return i ;
}
}
return - 1 ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
/ * *
* firstDayOfWeek
* /
var datetimeFirstDayOfWeek = function ( cldr ) {
return arrayIndexOf ( datetimeWeekDays , cldr . supplemental . weekData . firstDay ( ) ) ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
/ * *
* dayOfWeek
*
* Return the day of the week normalized by the territory ' s firstDay [ 0 - 6 ] .
* Eg for "mon" :
* - return 0 if territory is GB , or BR , or DE , or FR ( week starts on "mon" ) ;
* - return 1 if territory is US ( week starts on "sun" ) ;
* - return 2 if territory is EG ( week starts on "sat" ) ;
* /
var datetimeDayOfWeek = function ( date , cldr ) {
return ( date . getDay ( ) - datetimeFirstDayOfWeek ( cldr ) + 7 ) % 7 ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
/ * *
* distanceInDays ( from , to )
*
* Return the distance in days between from and to Dates .
* /
var datetimeDistanceInDays = function ( from , to ) {
var inDays = 864e5 ;
return ( to . getTime ( ) - from . getTime ( ) ) / inDays ;
} ;
/ * *
* startOf
*
* Return the
* /
var datetimeStartOf = function ( date , unit ) {
date = new Date ( date . getTime ( ) ) ;
switch ( unit ) {
case "year" :
date . setMonth ( 0 ) ;
/* falls through */
case "month" :
date . setDate ( 1 ) ;
/* falls through */
case "day" :
date . setHours ( 0 ) ;
/* falls through */
case "hour" :
date . setMinutes ( 0 ) ;
/* falls through */
case "minute" :
date . setSeconds ( 0 ) ;
/* falls through */
case "second" :
date . setMilliseconds ( 0 ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
return date ;
} ;
/ * *
* dayOfYear
*
* Return the distance in days of the date to the begin of the year [ 0 - d ] .
* /
var datetimeDayOfYear = function ( date ) {
return Math . floor ( datetimeDistanceInDays ( datetimeStartOf ( date , "year" ) , date ) ) ;
} ;
/ * *
* millisecondsInDay
* /
var datetimeMillisecondsInDay = function ( date ) {
// TODO Handle daylight savings discontinuities
return date - datetimeStartOf ( date , "day" ) ;
} ;
var datetimePatternRe = ( /([a-z])\1*|'[^']+'|''|./ig ) ;
var stringPad = function ( str , count , right ) {
var length ;
if ( typeof str !== "string" ) {
str = String ( str ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
for ( length = str . length ; length < count ; length += 1 ) {
str = ( right ? ( str + "0" ) : ( "0" + str ) ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
return str ;
2011-08-10 12:55:16 +00:00
} ;
2013-11-30 12:22:17 +00:00
/ * *
* format ( date , pattern , cldr )
*
* @ date [ Date instance ] .
*
* @ pattern [ String ] raw pattern .
* ref : http : //www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
*
* @ cldr [ Cldr instance ] .
*
* TODO Support other calendar types .
*
* Disclosure : this function borrows excerpts of dojo / date / locale .
* /
var datetimeFormat = function ( date , pattern , cldr ) {
var widths = [ "abbreviated" , "wide" , "narrow" ] ;
return pattern . replace ( datetimePatternRe , function ( current ) {
var pad , ret ,
chr = current . charAt ( 0 ) ,
length = current . length ;
if ( chr === "j" ) {
// Locale preferred hHKk.
// http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data
chr = cldr . supplemental . timeData . preferred ( ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
switch ( chr ) {
// Era
case "G" :
ret = cldr . main ( [
"dates/calendars/gregorian/eras" ,
length <= 3 ? "eraAbbr" : ( length === 4 ? "eraNames" : "eraNarrow" ) ,
date . getFullYear ( ) < 0 ? 0 : 1
] ) ;
break ;
// Year
case "y" :
// Plain year.
// The length specifies the padding, but for two letters it also specifies the maximum length.
ret = String ( date . getFullYear ( ) ) ;
pad = true ;
if ( length === 2 ) {
ret = ret . substr ( ret . length - 2 ) ;
}
break ;
case "Y" :
// Year in "Week of Year"
// The length specifies the padding, but for two letters it also specifies the maximum length.
// yearInWeekofYear = date + DaysInAWeek - (dayOfWeek - firstDay) - minDays
ret = new Date ( date . getTime ( ) ) ;
ret . setDate ( ret . getDate ( ) + 7 - ( datetimeDayOfWeek ( date , cldr ) - datetimeFirstDayOfWeek ( cldr ) ) - cldr . supplemental . weekData . minDays ( ) ) ;
ret = String ( ret . getFullYear ( ) ) ;
pad = true ;
if ( length === 2 ) {
ret = ret . substr ( ret . length - 2 ) ;
}
break ;
case "u" : // Extended year. Need to be implemented.
case "U" : // Cyclic year name. Need to be implemented.
throw new Error ( "Not implemented" ) ;
// Quarter
case "Q" :
case "q" :
ret = Math . ceil ( ( date . getMonth ( ) + 1 ) / 3 ) ;
if ( length <= 2 ) {
pad = true ;
} else {
// http://unicode.org/cldr/trac/ticket/6788
ret = cldr . main ( [
"dates/calendars/gregorian/quarters" ,
chr === "Q" ? "format" : "stand-alone" ,
widths [ length - 3 ] ,
ret
] ) ;
}
break ;
// Month
case "M" :
case "L" :
ret = date . getMonth ( ) + 1 ;
if ( length <= 2 ) {
pad = true ;
} else {
ret = cldr . main ( [
"dates/calendars/gregorian/months" ,
chr === "M" ? "format" : "stand-alone" ,
widths [ length - 3 ] ,
ret
] ) ;
}
break ;
// Week
case "w" :
// Week of Year.
// woy = ceil( ( doy + dow of 1/1 ) / 7 ) - minDaysStuff ? 1 : 0.
// TODO should pad on ww? Not documented, but I guess so.
ret = datetimeDayOfWeek ( datetimeStartOf ( date , "year" ) , cldr ) ;
ret = Math . ceil ( ( datetimeDayOfYear ( date ) + ret ) / 7 ) - ( 7 - ret >= cldr . supplemental . weekData . minDays ( ) ? 0 : 1 ) ;
pad = true ;
break ;
case "W" :
// Week of Month.
// wom = ceil( ( dom + dow of `1/month` ) / 7 ) - minDaysStuff ? 1 : 0.
ret = datetimeDayOfWeek ( datetimeStartOf ( date , "month" ) , cldr ) ;
ret = Math . ceil ( ( date . getDate ( ) + ret ) / 7 ) - ( 7 - ret >= cldr . supplemental . weekData . minDays ( ) ? 0 : 1 ) ;
break ;
// Day
case "d" :
ret = date . getDate ( ) ;
pad = true ;
break ;
case "D" :
ret = datetimeDayOfYear ( date ) + 1 ;
pad = true ;
break ;
case "F" :
// Day of Week in month. eg. 2nd Wed in July.
ret = Math . floor ( date . getDate ( ) / 7 ) + 1 ;
break ;
case "g+" :
// Modified Julian day. Need to be implemented.
throw new Error ( "Not implemented" ) ;
// Week day
case "e" :
case "c" :
if ( length <= 2 ) {
// Range is [1-7] (deduced by example provided on documentation)
// TODO Should pad with zeros (not specified in the docs)?
ret = datetimeDayOfWeek ( date , cldr ) + 1 ;
pad = true ;
break ;
}
/* falls through */
case "E" :
ret = datetimeWeekDays [ date . getDay ( ) ] ;
if ( length === 6 ) {
// If short day names are not explicitly specified, abbreviated day names are used instead.
// http://www.unicode.org/reports/tr35/tr35-dates.html#months_days_quarters_eras
// http://unicode.org/cldr/trac/ticket/6790
ret = cldr . main ( [
"dates/calendars/gregorian/days" ,
[ chr === "c" ? "stand-alone" : "format" ] ,
"short" ,
ret
] ) || cldr . main ( [
"dates/calendars/gregorian/days" ,
[ chr === "c" ? "stand-alone" : "format" ] ,
"abbreviated" ,
ret
] ) ;
} else {
ret = cldr . main ( [
"dates/calendars/gregorian/days" ,
[ chr === "c" ? "stand-alone" : "format" ] ,
widths [ length < 3 ? 0 : length - 3 ] ,
ret
] ) ;
}
break ;
// Period (AM or PM)
case "a" :
ret = cldr . main ( [
"dates/calendars/gregorian/dayPeriods/format/wide" ,
date . getHours ( ) < 12 ? "am" : "pm"
] ) ;
break ;
// Hour
case "h" : // 1-12
ret = ( date . getHours ( ) % 12 ) || 12 ;
pad = true ;
break ;
case "H" : // 0-23
ret = date . getHours ( ) ;
pad = true ;
break ;
case "K" : // 0-11
ret = date . getHours ( ) % 12 ;
pad = true ;
break ;
case "k" : // 1-24
ret = date . getHours ( ) || 24 ;
pad = true ;
break ;
// Minute
case "m" :
ret = date . getMinutes ( ) ;
pad = true ;
break ;
// Second
case "s" :
ret = date . getSeconds ( ) ;
pad = true ;
break ;
case "S" :
ret = Math . round ( date . getMilliseconds ( ) * Math . pow ( 10 , length - 3 ) ) ;
pad = true ;
break ;
case "A" :
ret = Math . round ( datetimeMillisecondsInDay ( date ) * Math . pow ( 10 , length - 3 ) ) ;
pad = true ;
break ;
// Zone
// see http://www.unicode.org/reports/tr35/tr35-dates.html#Using_Time_Zone_Names ?
// Need to be implemented.
case "z" :
case "Z" :
case "O" :
case "v" :
case "V" :
case "X" :
case "x" :
throw new Error ( "Not implemented" ) ;
// Anything else is considered a literal, including [ ,:/.'@#], chinese, japonese, and arabic characters.
default :
return current ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
if ( pad ) {
ret = stringPad ( ret , length ) ;
2013-07-11 13:58:35 +00:00
}
2013-11-30 12:22:17 +00:00
return ret ;
2011-08-10 12:55:16 +00:00
} ) ;
2013-11-30 12:22:17 +00:00
} ;
var arrayEvery = function ( array , callback ) {
var i , length ;
if ( array . every ) {
return array . every ( callback ) ;
}
for ( i = 0 , length = array . length ; i < length ; i ++ ) {
if ( ! callback ( array [ i ] , i , array ) ) {
return false ;
2011-08-10 12:55:16 +00:00
}
}
2013-11-30 12:22:17 +00:00
return true ;
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
/ * *
* tokenizer ( value , pattern )
*
* Returns an Array of tokens , eg . value "5 o'clock PM" , pattern "h 'o''clock' a" :
* [ {
* type : "h" ,
* lexeme : "5"
* } , {
* type : "literal" ,
* lexeme : " "
* } , {
* type : "literal" ,
* lexeme : "o'clock"
* } , {
* type : "literal" ,
* lexeme : " "
* } , {
* type : "a" ,
* lexeme : "PM" ,
* value : "pm"
* } ]
*
* OBS : lexeme ' s are always String and may return invalid ranges depending of the token type . Eg . "99" for month number .
*
* Return an empty Array when not successfully parsed .
* /
var datetimeTokenizer = function ( value , pattern , cldr ) {
var valid ,
tokens = [ ] ,
widths = [ "abbreviated" , "wide" , "narrow" ] ;
valid = arrayEvery ( pattern . match ( datetimePatternRe ) , function ( current ) {
var chr , length , tokenRe ,
token = { } ;
function oneDigitIfLengthOne ( ) {
if ( length === 1 ) {
return tokenRe = /\d/ ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
}
function oneOrTwoDigitsIfLengthOne ( ) {
if ( length === 1 ) {
return tokenRe = /\d\d?/ ;
2011-08-10 12:55:16 +00:00
}
}
2013-11-30 12:22:17 +00:00
function twoDigitsIfLengthTwo ( ) {
if ( length === 2 ) {
return tokenRe = /\d\d/ ;
2011-08-10 12:55:16 +00:00
}
}
2013-11-30 12:22:17 +00:00
// Brute-force test every locale entry in an attempt to match the given value.
// Return the first found one (and set token accordingly), or null.
function lookup ( path ) {
var i , re ,
data = cldr . main ( path ) ;
for ( i in data ) {
re = new RegExp ( "^" + data [ i ] ) ;
if ( re . test ( value ) ) {
token . value = i ;
return tokenRe = new RegExp ( data [ i ] ) ;
}
}
return null ;
}
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
token . type = current ;
chr = current . charAt ( 0 ) ,
length = current . length ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
switch ( chr ) {
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
// Era
case "G" :
lookup ( [
"dates/calendars/gregorian/eras" ,
length <= 3 ? "eraAbbr" : ( length === 4 ? "eraNames" : "eraNarrow" )
] ) ;
break ;
// Year
case "y" :
case "Y" :
// number l=1:+, l=2:{2}, l=3:{3,}, l=4:{4,}, ...
if ( length === 1 ) {
tokenRe = /\d+/ ;
} else if ( length === 2 ) {
tokenRe = /\d\d/ ;
} else {
tokenRe = new RegExp ( "\\d{" + length + ",}" ) ;
}
break ;
case "u" : // Extended year. Need to be implemented.
case "U" : // Cyclic year name. Need to be implemented.
throw new Error ( "Not implemented" ) ;
// Quarter
case "Q" :
case "q" :
// number l=1:{1}, l=2:{2}.
// lookup l=3...
oneDigitIfLengthOne ( ) || twoDigitsIfLengthTwo ( ) || lookup ( [
"dates/calendars/gregorian/quarters" ,
chr === "Q" ? "format" : "stand-alone" ,
widths [ length - 3 ]
] ) ;
break ;
// Month
case "M" :
case "L" :
// number l=1:{1,2}, l=2:{2}.
// lookup l=3...
oneOrTwoDigitsIfLengthOne ( ) || twoDigitsIfLengthTwo ( ) || lookup ( [
"dates/calendars/gregorian/months" ,
chr === "M" ? "format" : "stand-alone" ,
widths [ length - 3 ]
] ) ;
break ;
// Day (see d below)
case "D" :
// number {l,3}.
if ( length <= 3 ) {
tokenRe = new RegExp ( "\\d{" + length + ",3}" ) ;
}
break ;
case "W" :
case "F" :
// number l=1:{1}.
oneDigitIfLengthOne ( ) ;
break ;
case "g+" :
// Modified Julian day. Need to be implemented.
throw new Error ( "Not implemented" ) ;
// Week day
case "e" :
case "c" :
// number l=1:{1}, l=2:{2}.
// lookup for length >=3.
if ( length <= 2 ) {
oneDigitIfLengthOne ( ) || twoDigitsIfLengthTwo ( ) ;
2011-08-10 12:55:16 +00:00
break ;
}
2013-11-30 12:22:17 +00:00
/* falls through */
case "E" :
if ( length === 6 ) {
// Note: if short day names are not explicitly specified, abbreviated day names are used instead http://www.unicode.org/reports/tr35/tr35-dates.html#months_days_quarters_eras
lookup ( [
"dates/calendars/gregorian/days" ,
[ chr === "c" ? "stand-alone" : "format" ] ,
"short"
] ) || lookup ( [
"dates/calendars/gregorian/days" ,
[ chr === "c" ? "stand-alone" : "format" ] ,
"abbreviated"
] ) ;
} else {
lookup ( [
"dates/calendars/gregorian/days" ,
[ chr === "c" ? "stand-alone" : "format" ] ,
widths [ length < 3 ? 0 : length - 3 ]
] ) ;
}
break ;
// Period (AM or PM)
case "a" :
lookup ( [
"dates/calendars/gregorian/dayPeriods/format/wide"
] ) ;
break ;
// Week, Day, Hour, Minute, or Second
case "w" :
case "d" :
case "h" :
case "H" :
case "K" :
case "k" :
case "j" :
case "m" :
case "s" :
// number l1:{1,2}, l2:{2}.
oneOrTwoDigitsIfLengthOne ( ) || twoDigitsIfLengthTwo ( ) ;
break ;
case "S" :
// number {l}.
tokenRe = new RegExp ( "\\d{" + length + "}" ) ;
break ;
case "A" :
// number {l+5}.
tokenRe = new RegExp ( "\\d{" + ( length + 5 ) + "}" ) ;
break ;
// Zone
// see http://www.unicode.org/reports/tr35/tr35-dates.html#Using_Time_Zone_Names ?
// Need to be implemented.
case "z" :
case "Z" :
case "O" :
case "v" :
case "V" :
case "X" :
case "x" :
throw new Error ( "Not implemented" ) ;
case "'" :
token . type = "literal" ;
if ( current . charAt ( 1 ) === "'" ) {
tokenRe = /'/ ;
} else {
tokenRe = /'[^']+'/ ;
}
break ;
default :
token . type = "literal" ;
tokenRe = /./ ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
if ( ! tokenRe ) {
return false ;
}
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
// Get lexeme and consume it.
value = value . replace ( new RegExp ( "^" + tokenRe . source ) , function ( lexeme ) {
token . lexeme = lexeme ;
return "" ;
} ) ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
if ( ! token . lexeme ) {
return false ;
}
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
tokens . push ( token ) ;
return true ;
} ) ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
return valid ? tokens : [ ] ;
} ;
2011-08-10 12:55:16 +00:00
2013-07-11 13:58:35 +00:00
2013-11-30 12:22:17 +00:00
var datetimeParse = ( function ( ) {
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
function outOfRange ( value , low , high ) {
return value < low || value > high ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
/ * *
* parse
*
* ref : http : //www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
* /
return function ( value , pattern , cldr ) {
var amPm , era , hour24 , valid ,
YEAR = 0 ,
MONTH = 1 ,
DAY = 2 ,
HOUR = 3 ,
MINUTE = 4 ,
SECOND = 5 ,
MILLISECONDS = 6 ,
date = new Date ( ) ,
tokens = datetimeTokenizer ( value , pattern , cldr ) ,
truncateAt = [ ] ,
units = [ "year" , "month" , "day" , "hour" , "minute" , "second" , "milliseconds" ] ;
if ( ! tokens . length ) {
return null ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
valid = arrayEvery ( tokens , function ( token ) {
var century , chr , value , length ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
if ( token . type === "literal" ) {
// continue
return true ;
}
chr = token . type . charAt ( 0 ) ;
length = token . type . length ;
if ( chr === "j" ) {
// Locale preferred hHKk.
// http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data
chr = cldr . supplemental . timeData . preferred ( ) ;
}
switch ( chr ) {
// Era
case "G" :
truncateAt . push ( YEAR ) ;
era = + token . value ;
break ;
// Year
case "y" :
value = + token . lexeme ;
if ( length === 2 ) {
if ( outOfRange ( value , 0 , 99 ) ) {
return false ;
}
// mimic dojo/date/locale: choose century to apply, according to a sliding window of 80 years before and 20 years after present year.
century = Math . floor ( date . getFullYear ( ) / 100 ) * 100 ;
value += century ;
if ( value > date . getFullYear ( ) + 20 ) {
value -= 100 ;
}
}
date . setFullYear ( value ) ;
truncateAt . push ( YEAR ) ;
break ;
case "Y" : // Year in "Week of Year"
case "u" : // Extended year. Need to be implemented.
case "U" : // Cyclic year name. Need to be implemented.
throw new Error ( "Not implemented" ) ;
// Quarter (skip)
case "Q" :
case "q" :
break ;
// Month
case "M" :
case "L" :
if ( length <= 2 ) {
value = + token . lexeme ;
} else {
value = + token . value ;
}
if ( outOfRange ( value , 1 , 12 ) ) {
return false ;
}
date . setMonth ( value - 1 ) ;
truncateAt . push ( MONTH ) ;
break ;
// Week (skip)
case "w" : // Week of Year.
case "W" : // Week of Month.
break ;
// Day
case "d" :
value = + token . lexeme ;
if ( outOfRange ( value , 1 , 31 ) ) {
return false ;
}
date . setDate ( value ) ;
truncateAt . push ( DAY ) ;
break ;
case "D" :
value = + token . lexeme ;
if ( outOfRange ( value , 1 , 366 ) ) {
return false ;
}
date . setMonth ( 0 ) ;
date . setDate ( value ) ;
truncateAt . push ( DAY ) ;
break ;
case "F" :
// Day of Week in month. eg. 2nd Wed in July.
// Skip
break ;
case "g+" :
// Modified Julian day. Need to be implemented.
throw new Error ( "Not implemented" ) ;
// Week day
case "e" :
case "c" :
case "E" :
// Skip.
// value = arrayIndexOf( datetimeWeekDays, token.value );
break ;
// Period (AM or PM)
case "a" :
amPm = token . value ;
break ;
// Hour
case "K" : // 0-11
value = + token . lexeme + 1 ;
/* falls through */
case "h" : // 1-12
value = value || + token . lexeme ;
if ( outOfRange ( value , 1 , 12 ) ) {
return false ;
}
date . setHours ( value ) ;
truncateAt . push ( HOUR ) ;
break ;
case "H" : // 0-23
value = + token . lexeme + 1 ;
/* falls through */
case "k" : // 1-24
hour24 = true ;
value = value || + token . lexeme ;
if ( outOfRange ( value , 1 , 24 ) ) {
return false ;
}
date . setHours ( value ) ;
truncateAt . push ( HOUR ) ;
break ;
// Minute
case "m" :
value = + token . lexeme ;
if ( outOfRange ( value , 0 , 59 ) ) {
return false ;
}
date . setMinutes ( value ) ;
truncateAt . push ( MINUTE ) ;
break ;
// Second
case "s" :
value = + token . lexeme ;
if ( outOfRange ( value , 0 , 59 ) ) {
return false ;
}
date . setSeconds ( value ) ;
truncateAt . push ( SECOND ) ;
break ;
case "A" :
date . setHours ( 0 ) ;
date . setMinutes ( 0 ) ;
date . setSeconds ( 0 ) ;
/* falls through */
case "S" :
value = Math . round ( + token . lexeme * Math . pow ( 10 , 3 - length ) ) ;
date . setMilliseconds ( value ) ;
truncateAt . push ( MILLISECONDS ) ;
break ;
// Zone
// see http://www.unicode.org/reports/tr35/tr35-dates.html#Using_Time_Zone_Names ?
// Need to be implemented.
case "z" :
case "Z" :
case "O" :
case "v" :
case "V" :
case "X" :
case "x" :
throw new Error ( "Not implemented" ) ;
}
return true ;
} ) ;
if ( ! valid || amPm && hour24 ) {
return null ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
if ( era === 0 ) {
// 1 BC = year 0
date . setFullYear ( date . getFullYear ( ) * - 1 + 1 ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
if ( amPm === "pm" && date . getHours ( ) !== 12 ) {
date . setHours ( date . getHours ( ) + 12 ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
// Truncate date at the most precise unit defined. Eg.
// If value is "12/31", and pattern is "MM/dd":
// => new Date( <current Year>, 12, 31, 0, 0, 0, 0 );
truncateAt = Math . max . apply ( null , truncateAt ) ;
date = datetimeStartOf ( date , units [ truncateAt ] ) ;
return date ;
} ;
} ( ) ) ;
var arrayIsArray = Array . isArray || function ( obj ) {
return Object . prototype . toString . call ( obj ) === "[object Array]" ;
} ;
var alwaysArray = function ( stringOrArray ) {
return arrayIsArray ( stringOrArray ) ? stringOrArray : [ stringOrArray ] ;
} ;
var arraySome = function ( array , callback ) {
var i , length ;
if ( array . some ) {
return array . some ( callback ) ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
for ( i = 0 , length = array . length ; i < length ; i ++ ) {
if ( callback ( array [ i ] , i , array ) ) {
return true ;
}
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
return false ;
} ;
2011-08-10 12:55:16 +00:00
2013-11-30 12:22:17 +00:00
var defaultLocale ;
function getLocale ( locale ) {
return locale ? new Cldr ( locale ) : defaultLocale ;
2011-08-10 12:55:16 +00:00
}
2013-11-30 12:22:17 +00:00
var Globalize = { } ;
/ * *
* Globalize . load ( json )
*
* @ json [ JSON ]
*
* Load resolved or unresolved cldr data .
* Somewhat equivalent to previous Globalize . addCultureInfo ( ... ) .
* /
Globalize . load = function ( json ) {
Cldr . load ( json ) ;
} ;
/ * *
* Globalize . loadTranslations ( locale , json )
*
* @ locale [ String ]
*
* @ json [ JSON ]
*
* Load translation data per locale .
* /
Globalize . loadTranslations = function ( locale , json ) {
var customData = {
"globalize-translation" : { }
} ;
locale = new Cldr ( locale ) ;
customData [ "globalize-translation" ] [ locale . attributes . languageId ] = json ;
Cldr . load ( customData ) ;
} ;
/ * *
* Globalize . locale ( locale )
*
* @ locale [ String ]
*
* Set default locale .
* Somewhat equivalent to previous culture ( selector ) .
* /
Globalize . locale = function ( locale ) {
if ( arguments . length ) {
defaultLocale = new Cldr ( locale ) ;
}
return defaultLocale ;
} ;
/ * *
* Globalize . format ( value , pattern , locale )
*
* @ value [ Date or Number ]
*
* @ pattern [ String or Object ] see datetime / expand _pattern for more info .
*
* @ locale [ String ]
*
* Formats a date or number according to the given pattern string and the given locale ( or the default locale if not specified ) .
* /
Globalize . format = function ( value , pattern , locale ) {
locale = getLocale ( locale ) ;
if ( value instanceof Date ) {
if ( ! pattern ) {
throw new Error ( "Missing pattern" ) ;
}
pattern = datetimeExpandPattern ( pattern , locale ) ;
value = datetimeFormat ( value , pattern , locale ) ;
} else if ( typeof value === "number" ) {
// TODO value = numberFormat( value, pattern, locale );
throw new Error ( "Number Format not implemented yet" ) ;
}
return value ;
} ;
/ * *
* Globalize . parseDate ( value , patterns , locale )
*
* @ value [ Date ]
*
* @ patterns [ Array ] Optional . See datetime / expand _pattern for more info about each pattern . Defaults to the list of all presets defined in the locale ( see datetime / all _presets for more info ) .
*
* @ locale [ String ]
*
* Return a Date instance or null .
* /
Globalize . parseDate = function ( value , patterns , locale ) {
var date ;
locale = getLocale ( locale ) ;
if ( typeof value !== "string" ) {
throw new Error ( "invalid value (" + value + "), string expected" ) ;
}
if ( ! patterns ) {
patterns = datetimeAllPresets ( locale ) ;
} else {
patterns = alwaysArray ( patterns ) ;
}
arraySome ( patterns , function ( pattern ) {
pattern = datetimeExpandPattern ( pattern , locale ) ;
date = datetimeParse ( value , pattern , locale ) ;
return ! ! date ;
} ) ;
return date || null ;
} ;
/ * *
* Globalize . translate ( path , locale )
*
* @ path [ String or Array ]
*
* @ locale [ String ]
*
* Translate item given its path .
* /
Globalize . translate = function ( path , locale ) {
locale = getLocale ( locale ) ;
path = alwaysArray ( path ) ;
return locale . get ( [ "globalize-translation/{languageId}" ] . concat ( path ) ) ;
} ;
return Globalize ;
} ) ) ;