aboutsummaryrefslogtreecommitdiffstats
path: root/external
diff options
context:
space:
mode:
Diffstat (limited to 'external')
-rw-r--r--external/cldrjs/LICENSE-MIT/LICENSE-MIT22
-rw-r--r--external/cldrjs/cldr.js662
-rw-r--r--external/cldrjs/cldr/event.js585
-rw-r--r--external/cldrjs/cldr/supplemental.js101
-rw-r--r--external/globalize-old/LICENSE (renamed from external/globalize/LICENSE)0
-rw-r--r--external/globalize-old/globalize.culture.de-DE.js (renamed from external/globalize/globalize.culture.de-DE.js)0
-rw-r--r--external/globalize-old/globalize.culture.ja-JP.js (renamed from external/globalize/globalize.culture.ja-JP.js)0
-rw-r--r--external/globalize-old/globalize.js1585
-rw-r--r--external/globalize/LICENSE.txt43
-rw-r--r--external/globalize/globalize-runtime.js236
-rw-r--r--external/globalize/globalize-runtime/date.js1289
-rw-r--r--external/globalize/globalize-runtime/number.js682
-rw-r--r--external/globalize/globalize.js1726
-rw-r--r--external/localization.js115
14 files changed, 5568 insertions, 1478 deletions
diff --git a/external/cldrjs/LICENSE-MIT/LICENSE-MIT b/external/cldrjs/LICENSE-MIT/LICENSE-MIT
new file mode 100644
index 000000000..6123defa8
--- /dev/null
+++ b/external/cldrjs/LICENSE-MIT/LICENSE-MIT
@@ -0,0 +1,22 @@
+Copyright (c) Rafael Xavier de Souza http://rafael.xavier.blog.br
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/external/cldrjs/cldr.js b/external/cldrjs/cldr.js
new file mode 100644
index 000000000..23b6c96fe
--- /dev/null
+++ b/external/cldrjs/cldr.js
@@ -0,0 +1,662 @@
+/**
+ * CLDR JavaScript Library v0.4.3
+ * http://jquery.com/
+ *
+ * Copyright 2013 Rafael Xavier de Souza
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-08-24T01:00Z
+ */
+/*!
+ * CLDR JavaScript Library v0.4.3 2015-08-24T01:00Z MIT license © Rafael Xavier
+ * http://git.io/h4lmVg
+ */
+(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.Cldr = factory();
+ }
+
+}( this, function() {
+
+
+ var arrayIsArray = Array.isArray || function( obj ) {
+ return Object.prototype.toString.call( obj ) === "[object Array]";
+ };
+
+
+
+
+ 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 */
+
+ // Replace {attribute}'s
+ path = path.replace( /{[a-zA-Z]+}/g, function( name ) {
+ name = name.replace( /^{([^}]*)}$/, "$1" );
+ return attributes[ name ];
+ });
+
+ return path.split( "/" );
+ };
+
+
+
+
+ var arraySome = function( array, callback ) {
+ var i, length;
+ if ( array.some ) {
+ return array.some( callback );
+ }
+ for ( i = 0, length = array.length; i < length; i++ ) {
+ if ( callback( array[ i ], i, array ) ) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+
+
+
+ /**
+ * 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 coreLikelySubtags = function( Cldr, cldr, subtags, options ) {
+ var match, matchFound,
+ language = subtags[ 0 ],
+ script = subtags[ 1 ],
+ sep = Cldr.localeSep,
+ 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( sep ) ) /* [1.4] */ && cldr.get( [ "supplemental/likelySubtags", test.join( sep ) ] );
+ });
+
+ // [3]
+ if ( matchFound ) {
+ // [3.2 .. 3.4]
+ match = match.split( sep );
+ 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( sep );
+ } else {
+ // [3.1.1]
+ return;
+ }
+ };
+
+
+
+ /**
+ * 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 coreRemoveLikelySubtags = function( Cldr, 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 = coreLikelySubtags( Cldr, 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;
+ };
+
+
+
+
+ /**
+ * subtags( locale )
+ *
+ * @locale [String]
+ */
+ var coreSubtags = function( locale ) {
+ var aux, unicodeLanguageId,
+ subtags = [];
+
+ locale = locale.replace( /_/, "-" );
+
+ // Unicode locale extensions.
+ aux = locale.split( "-u-" );
+ if ( aux[ 1 ] ) {
+ aux[ 1 ] = aux[ 1 ].split( "-t-" );
+ locale = aux[ 0 ] + ( aux[ 1 ][ 1 ] ? "-t-" + aux[ 1 ][ 1 ] : "");
+ subtags[ 4 /* unicodeLocaleExtensions */ ] = aux[ 1 ][ 0 ];
+ }
+
+ // TODO normalize transformed extensions. Currently, skipped.
+ // subtags[ x ] = locale.split( "-t-" )[ 1 ];
+ unicodeLanguageId = locale.split( "-t-" )[ 0 ];
+
+ // unicode_language_id = "root"
+ // | unicode_language_subtag
+ // (sep unicode_script_subtag)?
+ // (sep unicode_region_subtag)?
+ // (sep unicode_variant_subtag)* ;
+ //
+ // Although unicode_language_subtag = alpha{2,8}, I'm using alpha{2,3}. Because, there's no language on CLDR lengthier than 3.
+ aux = unicodeLanguageId.match( /^(([a-z]{2,3})(-([A-Z][a-z]{3}))?(-([A-Z]{2}|[0-9]{3}))?)(-[a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3})*$|^(root)$/ );
+ if ( aux === null ) {
+ return [ "und", "Zzzz", "ZZ" ];
+ }
+ subtags[ 0 /* language */ ] = aux[ 9 ] /* root */ || aux[ 2 ] || "und";
+ subtags[ 1 /* script */ ] = aux[ 4 ] || "Zzzz";
+ subtags[ 2 /* territory */ ] = aux[ 6 ] || "ZZ";
+ subtags[ 3 /* variant */ ] = aux[ 7 ];
+
+ // 0: language
+ // 1: script
+ // 2: territory (aka region)
+ // 3: variant
+ // 4: unicodeLocaleExtensions
+ return subtags;
+ };
+
+
+
+
+ var arrayForEach = function( array, callback ) {
+ var i, length;
+ if ( array.forEach ) {
+ return array.forEach( callback );
+ }
+ for ( i = 0, length = array.length; i < length; i++ ) {
+ callback( array[ i ], i, array );
+ }
+ };
+
+
+
+
+ /**
+ * bundleLookup( minLanguageId )
+ *
+ * @Cldr [Cldr class]
+ *
+ * @cldr [Cldr instance]
+ *
+ * @minLanguageId [String] requested languageId after applied remove likely subtags.
+ */
+ var bundleLookup = function( Cldr, cldr, minLanguageId ) {
+ var availableBundleMap = Cldr._availableBundleMap,
+ availableBundleMapQueue = Cldr._availableBundleMapQueue;
+
+ if ( availableBundleMapQueue.length ) {
+ arrayForEach( availableBundleMapQueue, function( bundle ) {
+ var existing, maxBundle, minBundle, subtags;
+ subtags = coreSubtags( bundle );
+ maxBundle = coreLikelySubtags( Cldr, cldr, subtags, { force: true } ) || subtags;
+ minBundle = coreRemoveLikelySubtags( Cldr, cldr, maxBundle );
+ minBundle = minBundle.join( Cldr.localeSep );
+ existing = availableBundleMapQueue[ minBundle ];
+ if ( existing && existing.length < bundle.length ) {
+ return;
+ }
+ availableBundleMap[ minBundle ] = bundle;
+ });
+ Cldr._availableBundleMapQueue = [];
+ }
+
+ return availableBundleMap[ minLanguageId ] || null;
+ };
+
+
+
+
+ var objectKeys = function( object ) {
+ var i,
+ result = [];
+
+ if ( Object.keys ) {
+ return Object.keys( object );
+ }
+
+ for ( i in object ) {
+ result.push( i );
+ }
+
+ return result;
+ };
+
+
+
+
+ var createError = function( code, attributes ) {
+ var error, message;
+
+ message = code + ( attributes && JSON ? ": " + JSON.stringify( attributes ) : "" );
+ error = new Error( message );
+ error.code = code;
+
+ // extend( error, attributes );
+ arrayForEach( objectKeys( attributes ), function( attribute ) {
+ error[ attribute ] = attributes[ attribute ];
+ });
+
+ return error;
+ };
+
+
+
+
+ var validate = function( code, check, attributes ) {
+ if ( !check ) {
+ throw createError( code, attributes );
+ }
+ };
+
+
+
+
+ var validatePresence = function( value, name ) {
+ validate( "E_MISSING_PARAMETER", typeof value !== "undefined", {
+ name: name
+ });
+ };
+
+
+
+
+ var validateType = function( value, name, check, expected ) {
+ validate( "E_INVALID_PAR_TYPE", check, {
+ expected: expected,
+ name: name,
+ value: value
+ });
+ };
+
+
+
+
+ var validateTypePath = function( value, name ) {
+ validateType( value, name, typeof value === "string" || arrayIsArray( value ), "String or Array" );
+ };
+
+
+
+
+ /**
+ * Function inspired by jQuery Core, but reduced to our use case.
+ */
+ var isPlainObject = function( obj ) {
+ return obj !== null && "" + obj === "[object Object]";
+ };
+
+
+
+
+ var validateTypePlainObject = function( value, name ) {
+ validateType( value, name, typeof value === "undefined" || isPlainObject( value ), "Plain Object" );
+ };
+
+
+
+
+ var validateTypeString = function( value, name ) {
+ validateType( value, name, typeof value === "string", "a string" );
+ };
+
+
+
+
+ // @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;
+ }
+ }
+ return node[ path[ i ] ];
+ };
+
+
+
+
+ /**
+ * setAvailableBundles( Cldr, json )
+ *
+ * @Cldr [Cldr class]
+ *
+ * @json resolved/unresolved cldr data.
+ *
+ * Set available bundles queue based on passed json CLDR data. Considers a bundle as any String at /main/{bundle}.
+ */
+ var coreSetAvailableBundles = function( Cldr, json ) {
+ var bundle,
+ availableBundleMapQueue = Cldr._availableBundleMapQueue,
+ main = resourceGet( json, [ "main" ] );
+
+ if ( main ) {
+ for ( bundle in main ) {
+ if ( main.hasOwnProperty( bundle ) && bundle !== "root" ) {
+ availableBundleMapQueue.push( bundle );
+ }
+ }
+ }
+ };
+
+
+
+ var alwaysArray = function( somethingOrArray ) {
+ return arrayIsArray( somethingOrArray ) ? somethingOrArray : [ somethingOrArray ];
+ };
+
+
+ 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 && typeof destination[ prop ] === "object" && !arrayIsArray( destination[ prop ] ) ) {
+
+ // Merge Objects
+ destination[ prop ] = merge( destination[ prop ], source[ prop ] );
+
+ } else {
+
+ // Set new values
+ destination[ prop ] = source[ prop ];
+
+ }
+ }
+ });
+ return destination;
+ };
+
+ return merge;
+
+}());
+
+
+ /**
+ * load( Cldr, source, jsons )
+ *
+ * @Cldr [Cldr class]
+ *
+ * @source [Object]
+ *
+ * @jsons [arguments]
+ */
+ var coreLoad = function( Cldr, source, jsons ) {
+ var i, j, json;
+
+ validatePresence( jsons[ 0 ], "json" );
+
+ // Support arbitrary parameters, e.g., `Cldr.load({...}, {...})`.
+ for ( i = 0; i < jsons.length; i++ ) {
+
+ // Support array parameters, e.g., `Cldr.load([{...}, {...}])`.
+ json = alwaysArray( jsons[ i ] );
+
+ for ( j = 0; j < json.length; j++ ) {
+ validateTypePlainObject( json[ j ], "json" );
+ source = jsonMerge( source, json[ j ] );
+ coreSetAvailableBundles( Cldr, json[ j ] );
+ }
+ }
+
+ return source;
+ };
+
+
+
+ var itemGetResolved = function( Cldr, path, attributes ) {
+ // Resolve path
+ var normalizedPath = pathNormalize( path, attributes );
+
+ return resourceGet( Cldr._resolved, normalizedPath );
+ };
+
+
+
+
+ /**
+ * new Cldr()
+ */
+ var Cldr = function( locale ) {
+ this.init( locale );
+ };
+
+ // Build optimization hack to avoid duplicating functions across modules.
+ Cldr._alwaysArray = alwaysArray;
+ Cldr._coreLoad = coreLoad;
+ Cldr._createError = createError;
+ Cldr._itemGetResolved = itemGetResolved;
+ Cldr._jsonMerge = jsonMerge;
+ Cldr._pathNormalize = pathNormalize;
+ Cldr._resourceGet = resourceGet;
+ Cldr._validatePresence = validatePresence;
+ Cldr._validateType = validateType;
+ Cldr._validateTypePath = validateTypePath;
+ Cldr._validateTypePlainObject = validateTypePlainObject;
+
+ Cldr._availableBundleMap = {};
+ Cldr._availableBundleMapQueue = [];
+ Cldr._resolved = {};
+
+ // Allow user to override locale separator "-" (default) | "_". According to http://www.unicode.org/reports/tr35/#Unicode_language_identifier, both "-" and "_" are valid locale separators (eg. "en_GB", "en-GB"). According to http://unicode.org/cldr/trac/ticket/6786 its usage must be consistent throughout the data set.
+ Cldr.localeSep = "-";
+
+ /**
+ * Cldr.load( json [, json, ...] )
+ *
+ * @json [JSON] CLDR data or [Array] Array of @json's.
+ *
+ * Load resolved cldr data.
+ */
+ Cldr.load = function() {
+ Cldr._resolved = coreLoad( Cldr, Cldr._resolved, arguments );
+ };
+
+ /**
+ * .init() automatically run on instantiation/construction.
+ */
+ Cldr.prototype.init = function( locale ) {
+ var attributes, language, maxLanguageId, minLanguageId, script, subtags, territory, unicodeLocaleExtensions, variant,
+ sep = Cldr.localeSep;
+
+ validatePresence( locale, "locale" );
+ validateTypeString( locale, "locale" );
+
+ subtags = coreSubtags( locale );
+
+ unicodeLocaleExtensions = subtags[ 4 ];
+ variant = subtags[ 3 ];
+
+ // 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
+
+ // When a locale id does not specify a language, or territory (region), or script, they are obtained by Likely Subtags.
+ maxLanguageId = coreLikelySubtags( Cldr, this, subtags, { force: true } ) || subtags;
+ language = maxLanguageId[ 0 ];
+ script = maxLanguageId[ 1 ];
+ territory = maxLanguageId[ 2 ];
+
+ minLanguageId = coreRemoveLikelySubtags( Cldr, this, maxLanguageId ).join( sep );
+
+ // Set attributes
+ this.attributes = attributes = {
+ bundle: bundleLookup( Cldr, this, minLanguageId ),
+
+ // Unicode Language Id
+ minlanguageId: minLanguageId,
+ maxLanguageId: maxLanguageId.join( sep ),
+
+ // Unicode Language Id Subtabs
+ language: language,
+ script: script,
+ territory: territory,
+ region: territory, /* alias */
+ variant: variant
+ };
+
+ // Unicode locale extensions.
+ unicodeLocaleExtensions && ( "-" + unicodeLocaleExtensions ).replace( /-[a-z]{3,8}|(-[a-z]{2})-([a-z]{3,8})/g, function( attribute, key, type ) {
+
+ if ( key ) {
+
+ // Extension is in the `keyword` form.
+ attributes[ "u" + key ] = type;
+ } else {
+
+ // Extension is in the `attribute` form.
+ attributes[ "u" + attribute ] = true;
+ }
+ });
+
+ this.locale = locale;
+ };
+
+ /**
+ * .get()
+ */
+ Cldr.prototype.get = function( path ) {
+
+ validatePresence( path, "path" );
+ validateTypePath( path, "path" );
+
+ return itemGetResolved( Cldr, path, this.attributes );
+ };
+
+ /**
+ * .main()
+ */
+ Cldr.prototype.main = function( path ) {
+ validatePresence( path, "path" );
+ validateTypePath( path, "path" );
+
+ validate( "E_MISSING_BUNDLE", this.attributes.bundle !== null, {
+ locale: this.locale
+ });
+
+ path = alwaysArray( path );
+ return this.get( [ "main/{bundle}" ].concat( path ) );
+ };
+
+ return Cldr;
+
+
+
+
+}));
diff --git a/external/cldrjs/cldr/event.js b/external/cldrjs/cldr/event.js
new file mode 100644
index 000000000..ee36d3543
--- /dev/null
+++ b/external/cldrjs/cldr/event.js
@@ -0,0 +1,585 @@
+/**
+ * CLDR JavaScript Library v0.4.3
+ * http://jquery.com/
+ *
+ * Copyright 2013 Rafael Xavier de Souza
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-08-24T01:00Z
+ */
+/*!
+ * CLDR JavaScript Library v0.4.3 2015-08-24T01:00Z MIT license © Rafael Xavier
+ * http://git.io/h4lmVg
+ */
+(function( factory ) {
+
+ if ( typeof define === "function" && define.amd ) {
+ // AMD.
+ define( [ "../cldr" ], factory );
+ } else if ( typeof module === "object" && typeof module.exports === "object" ) {
+ // Node. CommonJS.
+ module.exports = factory( require( "cldrjs" ) );
+ } else {
+ // Global
+ factory( Cldr );
+ }
+
+}(function( Cldr ) {
+
+ // Build optimization hack to avoid duplicating functions across modules.
+ var pathNormalize = Cldr._pathNormalize,
+ validatePresence = Cldr._validatePresence,
+ validateType = Cldr._validateType;
+
+/*!
+ * EventEmitter v4.2.7 - git.io/ee
+ * Oliver Caldwell
+ * MIT license
+ * @preserve
+ */
+
+var EventEmitter;
+/* jshint ignore:start */
+EventEmitter = (function () {
+
+
+ /**
+ * Class for managing events.
+ * Can be extended to provide event functionality in other classes.
+ *
+ * @class EventEmitter Manages event registering and emitting.
+ */
+ function EventEmitter() {}
+
+ // Shortcuts to improve speed and size
+ var proto = EventEmitter.prototype;
+ var exports = this;
+ var originalGlobalValue = exports.EventEmitter;
+
+ /**
+ * Finds the index of the listener for the event in it's storage array.
+ *
+ * @param {Function[]} listeners Array of listeners to search through.
+ * @param {Function} listener Method to look for.
+ * @return {Number} Index of the specified listener, -1 if not found
+ * @api private
+ */
+ function indexOfListener(listeners, listener) {
+ var i = listeners.length;
+ while (i--) {
+ if (listeners[i].listener === listener) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Alias a method while keeping the context correct, to allow for overwriting of target method.
+ *
+ * @param {String} name The name of the target method.
+ * @return {Function} The aliased method
+ * @api private
+ */
+ function alias(name) {
+ return function aliasClosure() {
+ return this[name].apply(this, arguments);
+ };
+ }
+
+ /**
+ * Returns the listener array for the specified event.
+ * Will initialise the event object and listener arrays if required.
+ * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
+ * Each property in the object response is an array of listener functions.
+ *
+ * @param {String|RegExp} evt Name of the event to return the listeners from.
+ * @return {Function[]|Object} All listener functions for the event.
+ */
+ proto.getListeners = function getListeners(evt) {
+ var events = this._getEvents();
+ var response;
+ var key;
+
+ // Return a concatenated array of all matching events if
+ // the selector is a regular expression.
+ if (evt instanceof RegExp) {
+ response = {};
+ for (key in events) {
+ if (events.hasOwnProperty(key) && evt.test(key)) {
+ response[key] = events[key];
+ }
+ }
+ }
+ else {
+ response = events[evt] || (events[evt] = []);
+ }
+
+ return response;
+ };
+
+ /**
+ * Takes a list of listener objects and flattens it into a list of listener functions.
+ *
+ * @param {Object[]} listeners Raw listener objects.
+ * @return {Function[]} Just the listener functions.
+ */
+ proto.flattenListeners = function flattenListeners(listeners) {
+ var flatListeners = [];
+ var i;
+
+ for (i = 0; i < listeners.length; i += 1) {
+ flatListeners.push(listeners[i].listener);
+ }
+
+ return flatListeners;
+ };
+
+ /**
+ * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
+ *
+ * @param {String|RegExp} evt Name of the event to return the listeners from.
+ * @return {Object} All listener functions for an event in an object.
+ */
+ proto.getListenersAsObject = function getListenersAsObject(evt) {
+ var listeners = this.getListeners(evt);
+ var response;
+
+ if (listeners instanceof Array) {
+ response = {};
+ response[evt] = listeners;
+ }
+
+ return response || listeners;
+ };
+
+ /**
+ * Adds a listener function to the specified event.
+ * The listener will not be added if it is a duplicate.
+ * If the listener returns true then it will be removed after it is called.
+ * If you pass a regular expression as the event name then the listener will be added to all events that match it.
+ *
+ * @param {String|RegExp} evt Name of the event to attach the listener to.
+ * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.addListener = function addListener(evt, listener) {
+ var listeners = this.getListenersAsObject(evt);
+ var listenerIsWrapped = typeof listener === 'object';
+ var key;
+
+ for (key in listeners) {
+ if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
+ listeners[key].push(listenerIsWrapped ? listener : {
+ listener: listener,
+ once: false
+ });
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Alias of addListener
+ */
+ proto.on = alias('addListener');
+
+ /**
+ * Semi-alias of addListener. It will add a listener that will be
+ * automatically removed after it's first execution.
+ *
+ * @param {String|RegExp} evt Name of the event to attach the listener to.
+ * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.addOnceListener = function addOnceListener(evt, listener) {
+ return this.addListener(evt, {
+ listener: listener,
+ once: true
+ });
+ };
+
+ /**
+ * Alias of addOnceListener.
+ */
+ proto.once = alias('addOnceListener');
+
+ /**
+ * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
+ * You need to tell it what event names should be matched by a regex.
+ *
+ * @param {String} evt Name of the event to create.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.defineEvent = function defineEvent(evt) {
+ this.getListeners(evt);
+ return this;
+ };
+
+ /**
+ * Uses defineEvent to define multiple events.
+ *
+ * @param {String[]} evts An array of event names to define.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.defineEvents = function defineEvents(evts) {
+ for (var i = 0; i < evts.length; i += 1) {
+ this.defineEvent(evts[i]);
+ }
+ return this;
+ };
+
+ /**
+ * Removes a listener function from the specified event.
+ * When passed a regular expression as the event name, it will remove the listener from all events that match it.
+ *
+ * @param {String|RegExp} evt Name of the event to remove the listener from.
+ * @param {Function} listener Method to remove from the event.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.removeListener = function removeListener(evt, listener) {
+ var listeners = this.getListenersAsObject(evt);
+ var index;
+ var key;
+
+ for (key in listeners) {
+ if (listeners.hasOwnProperty(key)) {
+ index = indexOfListener(listeners[key], listener);
+
+ if (index !== -1) {
+ listeners[key].splice(index, 1);
+ }
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Alias of removeListener
+ */
+ proto.off = alias('removeListener');
+
+ /**
+ * Adds listeners in bulk using the manipulateListeners method.
+ * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
+ * You can also pass it a regular expression to add the array of listeners to all events that match it.
+ * Yeah, this function does quite a bit. That's probably a bad thing.
+ *
+ * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
+ * @param {Function[]} [listeners] An optional array of listener functions to add.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.addListeners = function addListeners(evt, listeners) {
+ // Pass through to manipulateListeners
+ return this.manipulateListeners(false, evt, listeners);
+ };
+
+ /**
+ * Removes listeners in bulk using the manipulateListeners method.
+ * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
+ * You can also pass it an event name and an array of listeners to be removed.
+ * You can also pass it a regular expression to remove the listeners from all events that match it.
+ *
+ * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
+ * @param {Function[]} [listeners] An optional array of listener functions to remove.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.removeListeners = function removeListeners(evt, listeners) {
+ // Pass through to manipulateListeners
+ return this.manipulateListeners(true, evt, listeners);
+ };
+
+ /**
+ * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
+ * The first argument will determine if the listeners are removed (true) or added (false).
+ * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
+ * You can also pass it an event name and an array of listeners to be added/removed.
+ * You can also pass it a regular expression to manipulate the listeners of all events that match it.
+ *
+ * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
+ * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
+ * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
+ var i;
+ var value;
+ var single = remove ? this.removeListener : this.addListener;
+ var multiple = remove ? this.removeListeners : this.addListeners;
+
+ // If evt is an object then pass each of it's properties to this method
+ if (typeof evt === 'object' && !(evt instanceof RegExp)) {
+ for (i in evt) {
+ if (evt.hasOwnProperty(i) && (value = evt[i])) {
+ // Pass the single listener straight through to the singular method
+ if (typeof value === 'function') {
+ single.call(this, i, value);
+ }
+ else {
+ // Otherwise pass back to the multiple function
+ multiple.call(this, i, value);
+ }
+ }
+ }
+ }
+ else {
+ // So evt must be a string
+ // And listeners must be an array of listeners
+ // Loop over it and pass each one to the multiple method
+ i = listeners.length;
+ while (i--) {
+ single.call(this, evt, listeners[i]);
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Removes all listeners from a specified event.
+ * If you do not specify an event then all listeners will be removed.
+ * That means every event will be emptied.
+ * You can also pass a regex to remove all events that match it.
+ *
+ * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.removeEvent = function removeEvent(evt) {
+ var type = typeof evt;
+ var events = this._getEvents();
+ var key;
+
+ // Remove different things depending on the state of evt
+ if (type === 'string') {
+ // Remove all listeners for the specified event
+ delete events[evt];
+ }
+ else if (evt instanceof RegExp) {
+ // Remove all events matching the regex.
+ for (key in events) {
+ if (events.hasOwnProperty(key) && evt.test(key)) {
+ delete events[key];
+ }
+ }
+ }
+ else {
+ // Remove all listeners in all events
+ delete this._events;
+ }
+
+ return this;
+ };
+
+ /**
+ * Alias of removeEvent.
+ *
+ * Added to mirror the node API.
+ */
+ proto.removeAllListeners = alias('removeEvent');
+
+ /**
+ * Emits an event of your choice.
+ * When emitted, every listener attached to that event will be executed.
+ * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
+ * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
+ * So they will not arrive within the array on the other side, they will be separate.
+ * You can also pass a regular expression to emit to all events that match it.
+ *
+ * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
+ * @param {Array} [args] Optional array of arguments to be passed to each listener.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.emitEvent = function emitEvent(evt, args) {
+ var listeners = this.getListenersAsObject(evt);
+ var listener;
+ var i;
+ var key;
+ var response;
+
+ for (key in listeners) {
+ if (listeners.hasOwnProperty(key)) {
+ i = listeners[key].length;
+
+ while (i--) {
+ // If the listener returns true then it shall be removed from the event
+ // The function is executed either with a basic call or an apply if there is an args array
+ listener = listeners[key][i];
+
+ if (listener.once === true) {
+ this.removeListener(evt, listener.listener);
+ }
+
+ response = listener.listener.apply(this, args || []);
+
+ if (response === this._getOnceReturnValue()) {
+ this.removeListener(evt, listener.listener);
+ }
+ }
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Alias of emitEvent
+ */
+ proto.trigger = alias('emitEvent');
+
+ /**
+ * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
+ * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
+ *
+ * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
+ * @param {...*} Optional additional arguments to be passed to each listener.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.emit = function emit(evt) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return this.emitEvent(evt, args);
+ };
+
+ /**
+ * Sets the current value to check against when executing listeners. If a
+ * listeners return value matches the one set here then it will be removed
+ * after execution. This value defaults to true.
+ *
+ * @param {*} value The new value to check for when executing listeners.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.setOnceReturnValue = function setOnceReturnValue(value) {
+ this._onceReturnValue = value;
+ return this;
+ };
+
+ /**
+ * Fetches the current value to check against when executing listeners. If
+ * the listeners return value matches this one then it should be removed
+ * automatically. It will return true by default.
+ *
+ * @return {*|Boolean} The current value to check for or the default, true.
+ * @api private
+ */
+ proto._getOnceReturnValue = function _getOnceReturnValue() {
+ if (this.hasOwnProperty('_onceReturnValue')) {
+ return this._onceReturnValue;
+ }
+ else {
+ return true;
+ }
+ };
+
+ /**
+ * Fetches the events object and creates one if required.
+ *
+ * @return {Object} The events storage object.
+ * @api private
+ */
+ proto._getEvents = function _getEvents() {
+ return this._events || (this._events = {});
+ };
+
+ /**
+ * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
+ *
+ * @return {Function} Non conflicting EventEmitter class.
+ */
+ EventEmitter.noConflict = function noConflict() {
+ exports.EventEmitter = originalGlobalValue;
+ return EventEmitter;
+ };
+
+ return EventEmitter;
+}());
+/* jshint ignore:end */
+
+
+
+ var validateTypeFunction = function( value, name ) {
+ validateType( value, name, typeof value === "undefined" || typeof value === "function", "Function" );
+ };
+
+
+
+
+ var superGet, superInit,
+ globalEe = new EventEmitter();
+
+ function validateTypeEvent( value, name ) {
+ validateType( value, name, typeof value === "string" || value instanceof RegExp, "String or RegExp" );
+ }
+
+ function validateThenCall( method, self ) {
+ return function( event, listener ) {
+ validatePresence( event, "event" );
+ validateTypeEvent( event, "event" );
+
+ validatePresence( listener, "listener" );
+ validateTypeFunction( listener, "listener" );
+
+ return self[ method ].apply( self, arguments );
+ };
+ }
+
+ function off( self ) {
+ return validateThenCall( "off", self );
+ }
+
+ function on( self ) {
+ return validateThenCall( "on", self );
+ }
+
+ function once( self ) {
+ return validateThenCall( "once", self );
+ }
+
+ Cldr.off = off( globalEe );
+ Cldr.on = on( globalEe );
+ Cldr.once = once( globalEe );
+
+ /**
+ * Overload Cldr.prototype.init().
+ */
+ superInit = Cldr.prototype.init;
+ Cldr.prototype.init = function() {
+ var ee;
+ this.ee = ee = new EventEmitter();
+ this.off = off( ee );
+ this.on = on( ee );
+ this.once = once( ee );
+ superInit.apply( this, arguments );
+ };
+
+ /**
+ * getOverload is encapsulated, because of cldr/unresolved. If it's loaded
+ * after cldr/event (and note it overwrites .get), it can trigger this
+ * overload again.
+ */
+ function getOverload() {
+
+ /**
+ * Overload Cldr.prototype.get().
+ */
+ superGet = Cldr.prototype.get;
+ Cldr.prototype.get = function( path ) {
+ var value = superGet.apply( this, arguments );
+ path = pathNormalize( path, this.attributes ).join( "/" );
+ globalEe.trigger( "get", [ path, value ] );
+ this.ee.trigger( "get", [ path, value ] );
+ return value;
+ };
+ }
+
+ Cldr._eventInit = getOverload;
+ getOverload();
+
+ return Cldr;
+
+
+
+
+}));
diff --git a/external/cldrjs/cldr/supplemental.js b/external/cldrjs/cldr/supplemental.js
new file mode 100644
index 000000000..821abc4bf
--- /dev/null
+++ b/external/cldrjs/cldr/supplemental.js
@@ -0,0 +1,101 @@
+/**
+ * CLDR JavaScript Library v0.4.3
+ * http://jquery.com/
+ *
+ * Copyright 2013 Rafael Xavier de Souza
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-08-24T01:00Z
+ */
+/*!
+ * CLDR JavaScript Library v0.4.3 2015-08-24T01:00Z MIT license © Rafael Xavier
+ * http://git.io/h4lmVg
+ */
+(function( factory ) {
+
+ if ( typeof define === "function" && define.amd ) {
+ // AMD.
+ define( [ "../cldr" ], factory );
+ } else if ( typeof module === "object" && typeof module.exports === "object" ) {
+ // Node. CommonJS.
+ module.exports = factory( require( "cldrjs" ) );
+ } else {
+ // Global
+ factory( Cldr );
+ }
+
+}(function( Cldr ) {
+
+ // Build optimization hack to avoid duplicating functions across modules.
+ var alwaysArray = Cldr._alwaysArray;
+
+
+
+ var supplementalMain = 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 initSuper = Cldr.prototype.init;
+
+ /**
+ * .init() automatically ran on construction.
+ *
+ * Overload .init().
+ */
+ Cldr.prototype.init = function() {
+ initSuper.apply( this, arguments );
+ this.supplemental = supplementalMain( this );
+ };
+
+ return Cldr;
+
+
+
+
+}));
diff --git a/external/globalize/LICENSE b/external/globalize-old/LICENSE
index 9c8b02240..9c8b02240 100644
--- a/external/globalize/LICENSE
+++ b/external/globalize-old/LICENSE
diff --git a/external/globalize/globalize.culture.de-DE.js b/external/globalize-old/globalize.culture.de-DE.js
index 5466bd75e..5466bd75e 100644
--- a/external/globalize/globalize.culture.de-DE.js
+++ b/external/globalize-old/globalize.culture.de-DE.js
diff --git a/external/globalize/globalize.culture.ja-JP.js b/external/globalize-old/globalize.culture.ja-JP.js
index a9469d709..a9469d709 100644
--- a/external/globalize/globalize.culture.ja-JP.js
+++ b/external/globalize-old/globalize.culture.ja-JP.js
diff --git a/external/globalize-old/globalize.js b/external/globalize-old/globalize.js
new file mode 100644
index 000000000..a38a32625
--- /dev/null
+++ b/external/globalize-old/globalize.js
@@ -0,0 +1,1585 @@
+/*!
+ * Globalize
+ *
+ * http://github.com/jquery/globalize
+ *
+ * Copyright Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+
+(function( window, undefined ) {
+
+var Globalize,
+ // private variables
+ regexHex,
+ regexInfinity,
+ regexParseFloat,
+ regexTrim,
+ // private JavaScript utility functions
+ arrayIndexOf,
+ endsWith,
+ extend,
+ isArray,
+ isFunction,
+ isObject,
+ startsWith,
+ trim,
+ truncate,
+ zeroPad,
+ // private Globalization utility functions
+ appendPreOrPostMatch,
+ expandFormat,
+ formatDate,
+ formatNumber,
+ getTokenRegExp,
+ getEra,
+ getEraYear,
+ parseExact,
+ parseNegativePattern;
+
+// Global variable (Globalize) or CommonJS module (globalize)
+Globalize = function( cultureSelector ) {
+ return new Globalize.prototype.init( cultureSelector );
+};
+
+if ( typeof require !== "undefined" &&
+ typeof exports !== "undefined" &&
+ typeof module !== "undefined" ) {
+ // Assume CommonJS
+ module.exports = Globalize;
+} else {
+ // Export as global variable
+ window.Globalize = Globalize;
+}
+
+Globalize.cultures = {};
+
+Globalize.prototype = {
+ constructor: Globalize,
+ init: function( cultureSelector ) {
+ this.cultures = Globalize.cultures;
+ this.cultureSelector = cultureSelector;
+
+ return this;
+ }
+};
+Globalize.prototype.init.prototype = Globalize.prototype;
+
+// 1. When defining a culture, all fields are required except the ones stated as optional.
+// 2. Each culture should have a ".calendars" object with at least one calendar named "standard"
+// which serves as the default calendar in use by that culture.
+// 3. Each culture should have a ".calendar" object which is the current calendar being used,
+// it may be dynamically changed at any time to one of the calendars in ".calendars".
+Globalize.cultures[ "default" ] = {
+ // A unique name for the culture in the form <language code>-<country/region code>
+ name: "en",
+ // the name of the culture in the english language
+ englishName: "English",
+ // the name of the culture in its own language
+ nativeName: "English",
+ // whether the culture uses right-to-left text
+ isRTL: false,
+ // "language" is used for so-called "specific" cultures.
+ // For example, the culture "es-CL" means "Spanish, in Chili".
+ // It represents the Spanish-speaking culture as it is in Chili,
+ // which might have different formatting rules or even translations
+ // than Spanish in Spain. A "neutral" culture is one that is not
+ // specific to a region. For example, the culture "es" is the generic
+ // Spanish culture, which may be a more generalized version of the language
+ // that may or may not be what a specific culture expects.
+ // For a specific culture like "es-CL", the "language" field refers to the
+ // neutral, generic culture information for the language it is using.
+ // This is not always a simple matter of the string before the dash.
+ // For example, the "zh-Hans" culture is netural (Simplified Chinese).
+ // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage
+ // field is "zh-CHS", not "zh".
+ // This field should be used to navigate from a specific culture to it's
+ // more general, neutral culture. If a culture is already as general as it
+ // can get, the language may refer to itself.
+ language: "en",
+ // numberFormat defines general number formatting rules, like the digits in
+ // each grouping, the group separator, and how negative numbers are displayed.
+ numberFormat: {
+ // [negativePattern]
+ // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency,
+ // but is still defined as an array for consistency with them.
+ // negativePattern: one of "(n)|-n|- n|n-|n -"
+ pattern: [ "-n" ],
+ // number of decimal places normally shown
+ decimals: 2,
+ // string that separates number groups, as in 1,000,000
+ ",": ",",
+ // string that separates a number from the fractional portion, as in 1.99
+ ".": ".",
+ // array of numbers indicating the size of each number group.
+ // TODO: more detailed description and example
+ groupSizes: [ 3 ],
+ // symbol used for positive numbers
+ "+": "+",
+ // symbol used for negative numbers
+ "-": "-",
+ // symbol used for NaN (Not-A-Number)
+ "NaN": "NaN",
+ // symbol used for Negative Infinity
+ negativeInfinity: "-Infinity",
+ // symbol used for Positive Infinity
+ positiveInfinity: "Infinity",
+ percent: {
+ // [negativePattern, positivePattern]
+ // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
+ // positivePattern: one of "n %|n%|%n|% n"
+ pattern: [ "-n %", "n %" ],
+ // number of decimal places normally shown
+ decimals: 2,
+ // array of numbers indicating the size of each number group.
+ // TODO: more detailed description and example
+ groupSizes: [ 3 ],
+ // string that separates number groups, as in 1,000,000
+ ",": ",",
+ // string that separates a number from the fractional portion, as in 1.99
+ ".": ".",
+ // symbol used to represent a percentage
+ symbol: "%"
+ },
+ currency: {
+ // [negativePattern, positivePattern]
+ // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
+ // positivePattern: one of "$n|n$|$ n|n $"
+ pattern: [ "($n)", "$n" ],
+ // number of decimal places normally shown
+ decimals: 2,
+ // array of numbers indicating the size of each number group.
+ // TODO: more detailed description and example
+ groupSizes: [ 3 ],
+ // string that separates number groups, as in 1,000,000
+ ",": ",",
+ // string that separates a number from the fractional portion, as in 1.99
+ ".": ".",
+ // symbol used to represent currency
+ symbol: "$"
+ }
+ },
+ // calendars defines all the possible calendars used by this culture.
+ // There should be at least one defined with name "standard", and is the default
+ // calendar used by the culture.
+ // A calendar contains information about how dates are formatted, information about
+ // the calendar's eras, a standard set of the date formats,
+ // translations for day and month names, and if the calendar is not based on the Gregorian
+ // calendar, conversion functions to and from the Gregorian calendar.
+ calendars: {
+ standard: {
+ // name that identifies the type of calendar this is
+ name: "Gregorian_USEnglish",
+ // separator of parts of a date (e.g. "/" in 11/05/1955)
+ "/": "/",
+ // separator of parts of a time (e.g. ":" in 05:44 PM)
+ ":": ":",
+ // the first day of the week (0 = Sunday, 1 = Monday, etc)
+ firstDay: 0,
+ days: {
+ // full day names
+ names: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
+ // abbreviated day names
+ namesAbbr: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
+ // shortest day names
+ namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ]
+ },
+ months: {
+ // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar)
+ names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ],
+ // abbreviated month names
+ namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ]
+ },
+ // AM and PM designators in one of these forms:
+ // The usual view, and the upper and lower case versions
+ // [ standard, lowercase, uppercase ]
+ // The culture does not use AM or PM (likely all standard date formats use 24 hour time)
+ // null
+ AM: [ "AM", "am", "AM" ],
+ PM: [ "PM", "pm", "PM" ],
+ eras: [
+ // eras in reverse chronological order.
+ // name: the name of the era in this culture (e.g. A.D., C.E.)
+ // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
+ // offset: offset in years from gregorian calendar
+ {
+ "name": "A.D.",
+ "start": null,
+ "offset": 0
+ }
+ ],
+ // when a two digit year is given, it will never be parsed as a four digit
+ // year greater than this year (in the appropriate era for the culture)
+ // Set it as a full year (e.g. 2029) or use an offset format starting from
+ // the current year: "+19" would correspond to 2029 if the current year 2010.
+ twoDigitYearMax: 2029,
+ // set of predefined date and time patterns used by the culture
+ // these represent the format someone in this culture would expect
+ // to see given the portions of the date that are shown.
+ patterns: {
+ // short date pattern
+ d: "M/d/yyyy",
+ // long date pattern
+ D: "dddd, MMMM dd, yyyy",
+ // short time pattern
+ t: "h:mm tt",
+ // long time pattern
+ T: "h:mm:ss tt",
+ // long date, short time pattern
+ f: "dddd, MMMM dd, yyyy h:mm tt",
+ // long date, long time pattern
+ F: "dddd, MMMM dd, yyyy h:mm:ss tt",
+ // month/day pattern
+ M: "MMMM dd",
+ // month/year pattern
+ Y: "yyyy MMMM",
+ // S is a sortable format that does not vary by culture
+ S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
+ }
+ // optional fields for each calendar:
+ /*
+ monthsGenitive:
+ Same as months but used when the day preceeds the month.
+ Omit if the culture has no genitive distinction in month names.
+ For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx
+ convert:
+ Allows for the support of non-gregorian based calendars. This convert object is used to
+ to convert a date to and from a gregorian calendar date to handle parsing and formatting.
+ The two functions:
+ fromGregorian( date )
+ Given the date as a parameter, return an array with parts [ year, month, day ]
+ corresponding to the non-gregorian based year, month, and day for the calendar.
+ toGregorian( year, month, day )
+ Given the non-gregorian year, month, and day, return a new Date() object
+ set to the corresponding date in the gregorian calendar.
+ */
+ }
+ },
+ // For localized strings
+ messages: {}
+};
+
+Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard;
+
+Globalize.cultures.en = Globalize.cultures[ "default" ];
+
+Globalize.cultureSelector = "en";
+
+//
+// private variables
+//
+
+regexHex = /^0x[a-f0-9]+$/i;
+regexInfinity = /^[+\-]?infinity$/i;
+regexParseFloat = /^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/;
+regexTrim = /^\s+|\s+$/g;
+
+//
+// private JavaScript utility functions
+//
+
+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;
+};
+
+endsWith = function( value, pattern ) {
+ return value.substr( value.length - pattern.length ) === pattern;
+};
+
+extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !isFunction(target) ) {
+ target = {};
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( isObject(copy) || (copyIsArray = isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && isArray(src) ? src : [];
+
+ } else {
+ clone = src && isObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+isArray = Array.isArray || function( obj ) {
+ return Object.prototype.toString.call( obj ) === "[object Array]";
+};
+
+isFunction = function( obj ) {
+ return Object.prototype.toString.call( obj ) === "[object Function]";
+};
+
+isObject = function( obj ) {
+ return Object.prototype.toString.call( obj ) === "[object Object]";
+};
+
+startsWith = function( value, pattern ) {
+ return value.indexOf( pattern ) === 0;
+};
+
+trim = function( value ) {
+ return ( value + "" ).replace( regexTrim, "" );
+};
+
+truncate = function( value ) {
+ if ( isNaN( value ) ) {
+ return NaN;
+ }
+ return Math[ value < 0 ? "ceil" : "floor" ]( value );
+};
+
+zeroPad = function( str, count, left ) {
+ var l;
+ for ( l = str.length; l < count; l += 1 ) {
+ str = ( left ? ("0" + str) : (str + "0") );
+ }
+ return str;
+};
+
+//
+// private Globalization utility functions
+//
+
+appendPreOrPostMatch = function( preMatch, strings ) {
+ // appends pre- and post- token match strings while removing escaped characters.
+ // Returns a single quote count which is used to determine if the token occurs
+ // in a string literal.
+ var quoteCount = 0,
+ escaped = false;
+ for ( var i = 0, il = preMatch.length; i < il; i++ ) {
+ var c = preMatch.charAt( i );
+ switch ( c ) {
+ case "\'":
+ if ( escaped ) {
+ strings.push( "\'" );
+ }
+ else {
+ quoteCount++;
+ }
+ escaped = false;
+ break;
+ case "\\":
+ if ( escaped ) {
+ strings.push( "\\" );
+ }
+ escaped = !escaped;
+ break;
+ default:
+ strings.push( c );
+ escaped = false;
+ break;
+ }
+ }
+ return quoteCount;
+};
+
+expandFormat = function( cal, format ) {
+ // expands unspecified or single character date formats into the full pattern.
+ format = format || "F";
+ var pattern,
+ patterns = cal.patterns,
+ len = format.length;
+ if ( len === 1 ) {
+ pattern = patterns[ format ];
+ if ( !pattern ) {
+ throw "Invalid date format string \'" + format + "\'.";
+ }
+ format = pattern;
+ }
+ else if ( len === 2 && format.charAt(0) === "%" ) {
+ // %X escape format -- intended as a custom format string that is only one character, not a built-in format.
+ format = format.charAt( 1 );
+ }
+ return format;
+};
+
+formatDate = function( value, format, culture ) {
+ var cal = culture.calendar,
+ convert = cal.convert,
+ ret;
+
+ if ( !format || !format.length || format === "i" ) {
+ if ( culture && culture.name.length ) {
+ if ( convert ) {
+ // non-gregorian calendar, so we cannot use built-in toLocaleString()
+ ret = formatDate( value, cal.patterns.F, culture );
+ }
+ else {
+ var eraDate = new Date( value.getTime() ),
+ era = getEra( value, cal.eras );
+ eraDate.setFullYear( getEraYear(value, cal, era) );
+ ret = eraDate.toLocaleString();
+ }
+ }
+ else {
+ ret = value.toString();
+ }
+ return ret;
+ }
+
+ var eras = cal.eras,
+ sortable = format === "s";
+ format = expandFormat( cal, format );
+
+ // Start with an empty string
+ ret = [];
+ var hour,
+ zeros = [ "0", "00", "000" ],
+ foundDay,
+ checkedDay,
+ dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
+ quoteCount = 0,
+ tokenRegExp = getTokenRegExp(),
+ converted;
+
+ function padZeros( num, c ) {
+ var r, s = num + "";
+ if ( c > 1 && s.length < c ) {
+ r = ( zeros[c - 2] + s);
+ return r.substr( r.length - c, c );
+ }
+ else {
+ r = s;
+ }
+ return r;
+ }
+
+ function hasDay() {
+ if ( foundDay || checkedDay ) {
+ return foundDay;
+ }
+ foundDay = dayPartRegExp.test( format );
+ checkedDay = true;
+ return foundDay;
+ }
+
+ function getPart( date, part ) {
+ if ( converted ) {
+ return converted[ part ];
+ }
+ switch ( part ) {
+ case 0:
+ return date.getFullYear();
+ case 1:
+ return date.getMonth();
+ case 2:
+ return date.getDate();
+ default:
+ throw "Invalid part value " + part;
+ }
+ }
+
+ if ( !sortable && convert ) {
+ converted = convert.fromGregorian( value );
+ }
+
+ for ( ; ; ) {
+ // Save the current index
+ var index = tokenRegExp.lastIndex,
+ // Look for the next pattern
+ ar = tokenRegExp.exec( format );
+
+ // Append the text before the pattern (or the end of the string if not found)
+ var preMatch = format.slice( index, ar ? ar.index : format.length );
+ quoteCount += appendPreOrPostMatch( preMatch, ret );
+
+ if ( !ar ) {
+ break;
+ }
+
+ // do not replace any matches that occur inside a string literal.
+ if ( quoteCount % 2 ) {
+ ret.push( ar[0] );
+ continue;
+ }
+
+ var current = ar[ 0 ],
+ clength = current.length;
+
+ switch ( current ) {
+ case "ddd":
+ //Day of the week, as a three-letter abbreviation
+ case "dddd":
+ // Day of the week, using the full name
+ var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names;
+ ret.push( names[value.getDay()] );
+ break;
+ case "d":
+ // Day of month, without leading zero for single-digit days
+ case "dd":
+ // Day of month, with leading zero for single-digit days
+ foundDay = true;
+ ret.push(
+ padZeros( getPart(value, 2), clength )
+ );
+ break;
+ case "MMM":
+ // Month, as a three-letter abbreviation
+ case "MMMM":
+ // Month, using the full name
+ var part = getPart( value, 1 );
+ ret.push(
+ ( cal.monthsGenitive && hasDay() ) ?
+ ( cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] ) :
+ ( cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] )
+ );
+ break;
+ case "M":
+ // Month, as digits, with no leading zero for single-digit months
+ case "MM":
+ // Month, as digits, with leading zero for single-digit months
+ ret.push(
+ padZeros( getPart(value, 1) + 1, clength )
+ );
+ break;
+ case "y":
+ // Year, as two digits, but with no leading zero for years less than 10
+ case "yy":
+ // Year, as two digits, with leading zero for years less than 10
+ case "yyyy":
+ // Year represented by four full digits
+ part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra(value, eras), sortable );
+ if ( clength < 4 ) {
+ part = part % 100;
+ }
+ ret.push(
+ padZeros( part, clength )
+ );
+ break;
+ case "h":
+ // Hours with no leading zero for single-digit hours, using 12-hour clock
+ case "hh":
+ // Hours with leading zero for single-digit hours, using 12-hour clock
+ hour = value.getHours() % 12;
+ if ( hour === 0 ) hour = 12;
+ ret.push(
+ padZeros( hour, clength )
+ );
+ break;
+ case "H":
+ // Hours with no leading zero for single-digit hours, using 24-hour clock
+ case "HH":
+ // Hours with leading zero for single-digit hours, using 24-hour clock
+ ret.push(
+ padZeros( value.getHours(), clength )
+ );
+ break;
+ case "m":
+ // Minutes with no leading zero for single-digit minutes
+ case "mm":
+ // Minutes with leading zero for single-digit minutes
+ ret.push(
+ padZeros( value.getMinutes(), clength )
+ );
+ break;
+ case "s":
+ // Seconds with no leading zero for single-digit seconds
+ case "ss":
+ // Seconds with leading zero for single-digit seconds
+ ret.push(
+ padZeros( value.getSeconds(), clength )
+ );
+ break;
+ case "t":
+ // One character am/pm indicator ("a" or "p")
+ case "tt":
+ // Multicharacter am/pm indicator
+ part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " );
+ ret.push( clength === 1 ? part.charAt(0) : part );
+ break;
+ case "f":
+ // Deciseconds
+ case "ff":
+ // Centiseconds
+ case "fff":
+ // Milliseconds
+ ret.push(
+ padZeros( value.getMilliseconds(), 3 ).substr( 0, clength )
+ );
+ break;
+ case "z":
+ // Time zone offset, no leading zero
+ case "zz":
+ // Time zone offset with leading zero
+ hour = value.getTimezoneOffset() / 60;
+ ret.push(
+ ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), clength )
+ );
+ break;
+ case "zzz":
+ // Time zone offset with leading zero
+ hour = value.getTimezoneOffset() / 60;
+ ret.push(
+ ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), 2 ) +
+ // Hard coded ":" separator, rather than using cal.TimeSeparator
+ // Repeated here for consistency, plus ":" was already assumed in date parsing.
+ ":" + padZeros( Math.abs(value.getTimezoneOffset() % 60), 2 )
+ );
+ break;
+ case "g":
+ case "gg":
+ if ( cal.eras ) {
+ ret.push(
+ cal.eras[ getEra(value, eras) ].name
+ );
+ }
+ break;
+ case "/":
+ ret.push( cal["/"] );
+ break;
+ default:
+ throw "Invalid date format pattern \'" + current + "\'.";
+ }
+ }
+ return ret.join( "" );
+};
+
+// formatNumber
+(function() {
+ var expandNumber;
+
+ expandNumber = function( number, precision, formatInfo ) {
+ var groupSizes = formatInfo.groupSizes,
+ curSize = groupSizes[ 0 ],
+ curGroupIndex = 1,
+ factor = Math.pow( 10, precision ),
+ rounded = Math.round( number * factor ) / factor;
+
+ if ( !isFinite(rounded) ) {
+ rounded = number;
+ }
+ number = rounded;
+
+ var numberString = number+"",
+ right = "",
+ split = numberString.split( /e/i ),
+ exponent = split.length > 1 ? parseInt( split[1], 10 ) : 0;
+ numberString = split[ 0 ];
+ split = numberString.split( "." );
+ numberString = split[ 0 ];
+ right = split.length > 1 ? split[ 1 ] : "";
+
+ if ( exponent > 0 ) {
+ right = zeroPad( right, exponent, false );
+ numberString += right.slice( 0, exponent );
+ right = right.substr( exponent );
+ }
+ else if ( exponent < 0 ) {
+ exponent = -exponent;
+ numberString = zeroPad( numberString, exponent + 1, true );
+ right = numberString.slice( -exponent, numberString.length ) + right;
+ numberString = numberString.slice( 0, -exponent );
+ }
+
+ if ( precision > 0 ) {
+ right = formatInfo[ "." ] +
+ ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) );
+ }
+ else {
+ right = "";
+ }
+
+ var stringIndex = numberString.length - 1,
+ sep = formatInfo[ "," ],
+ ret = "";
+
+ while ( stringIndex >= 0 ) {
+ if ( curSize === 0 || curSize > stringIndex ) {
+ return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? (sep + ret + right) : right );
+ }
+ ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? (sep + ret) : "" );
+
+ stringIndex -= curSize;
+
+ if ( curGroupIndex < groupSizes.length ) {
+ curSize = groupSizes[ curGroupIndex ];
+ curGroupIndex++;
+ }
+ }
+
+ return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right;
+ };
+
+ formatNumber = function( value, format, culture ) {
+ if ( !isFinite(value) ) {
+ if ( value === Infinity ) {
+ return culture.numberFormat.positiveInfinity;
+ }
+ if ( value === -Infinity ) {
+ return culture.numberFormat.negativeInfinity;
+ }
+ return culture.numberFormat.NaN;
+ }
+ if ( !format || format === "i" ) {
+ return culture.name.length ? value.toLocaleString() : value.toString();
+ }
+ format = format || "D";
+
+ var nf = culture.numberFormat,
+ number = Math.abs( value ),
+ precision = -1,
+ pattern;
+ if ( format.length > 1 ) precision = parseInt( format.slice(1), 10 );
+
+ var current = format.charAt( 0 ).toUpperCase(),
+ formatInfo;
+
+ switch ( current ) {
+ case "D":
+ pattern = "n";
+ number = truncate( number );
+ if ( precision !== -1 ) {
+ number = zeroPad( "" + number, precision, true );
+ }
+ if ( value < 0 ) number = "-" + number;
+ break;
+ case "N":
+ formatInfo = nf;
+ /* falls through */
+ case "C":
+ formatInfo = formatInfo || nf.currency;
+ /* falls through */
+ case "P":
+ formatInfo = formatInfo || nf.percent;
+ pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" );
+ if ( precision === -1 ) precision = formatInfo.decimals;
+ number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo );
+ break;
+ default:
+ throw "Bad number format specifier: " + current;
+ }
+
+ var patternParts = /n|\$|-|%/g,
+ ret = "";
+ for ( ; ; ) {
+ var index = patternParts.lastIndex,
+ ar = patternParts.exec( pattern );
+
+ ret += pattern.slice( index, ar ? ar.index : pattern.length );
+
+ if ( !ar ) {
+ break;
+ }
+
+ switch ( ar[0] ) {
+ case "n":
+ ret += number;
+ break;
+ case "$":
+ ret += nf.currency.symbol;
+ break;
+ case "-":
+ // don't make 0 negative
+ if ( /[1-9]/.test(number) ) {
+ ret += nf[ "-" ];
+ }
+ break;
+ case "%":
+ ret += nf.percent.symbol;
+ break;
+ }
+ }
+
+ return ret;
+ };
+
+}());
+
+getTokenRegExp = function() {
+ // regular expression for matching date and time tokens in format strings.
+ 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);
+};
+
+getEra = function( date, eras ) {
+ if ( !eras ) return 0;
+ var start, ticks = date.getTime();
+ for ( var i = 0, l = eras.length; i < l; i++ ) {
+ start = eras[ i ].start;
+ if ( start === null || ticks >= start ) {
+ return i;
+ }
+ }
+ return 0;
+};
+
+getEraYear = function( date, cal, era, sortable ) {
+ var year = date.getFullYear();
+ if ( !sortable && cal.eras ) {
+ // convert normal gregorian year to era-shifted gregorian
+ // year by subtracting the era offset
+ year -= cal.eras[ era ].offset;
+ }
+ return year;
+};
+
+// parseExact
+(function() {
+ var expandYear,
+ getDayIndex,
+ getMonthIndex,
+ getParseRegExp,
+ outOfRange,
+ toUpper,
+ toUpperArray;
+
+ expandYear = function( cal, year ) {
+ // expands 2-digit year into 4 digits.
+ if ( year < 100 ) {
+ var now = new Date(),
+ era = getEra( now ),
+ curr = getEraYear( now, cal, era ),
+ twoDigitYearMax = cal.twoDigitYearMax;
+ twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax;
+ year += curr - ( curr % 100 );
+ if ( year > twoDigitYearMax ) {
+ year -= 100;
+ }
+ }
+ return year;
+ };
+
+ getDayIndex = function ( cal, value, abbr ) {
+ var ret,
+ days = cal.days,
+ upperDays = cal._upperDays;
+ if ( !upperDays ) {
+ cal._upperDays = upperDays = [
+ toUpperArray( days.names ),
+ toUpperArray( days.namesAbbr ),
+ toUpperArray( days.namesShort )
+ ];
+ }
+ value = toUpper( value );
+ if ( abbr ) {
+ ret = arrayIndexOf( upperDays[1], value );
+ if ( ret === -1 ) {
+ ret = arrayIndexOf( upperDays[2], value );
+ }
+ }
+ else {
+ ret = arrayIndexOf( upperDays[0], value );
+ }
+ return ret;
+ };
+
+ getMonthIndex = function( cal, value, abbr ) {
+ var months = cal.months,
+ monthsGen = cal.monthsGenitive || cal.months,
+ upperMonths = cal._upperMonths,
+ upperMonthsGen = cal._upperMonthsGen;
+ if ( !upperMonths ) {
+ cal._upperMonths = upperMonths = [
+ toUpperArray( months.names ),
+ toUpperArray( months.namesAbbr )
+ ];
+ cal._upperMonthsGen = upperMonthsGen = [
+ toUpperArray( monthsGen.names ),
+ toUpperArray( monthsGen.namesAbbr )
+ ];
+ }
+ value = toUpper( value );
+ var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value );
+ if ( i < 0 ) {
+ i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value );
+ }
+ return i;
+ };
+
+ getParseRegExp = function( cal, format ) {
+ // converts a format string into a regular expression with groups that
+ // can be used to extract date fields from a date string.
+ // check for a cached parse regex.
+ var re = cal._parseRegExp;
+ if ( !re ) {
+ cal._parseRegExp = re = {};
+ }
+ else {
+ var reFormat = re[ format ];
+ if ( reFormat ) {
+ return reFormat;
+ }
+ }
+
+ // expand single digit formats, then escape regular expression characters.
+ var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ),
+ regexp = [ "^" ],
+ groups = [],
+ index = 0,
+ quoteCount = 0,
+ tokenRegExp = getTokenRegExp(),
+ match;
+
+ // iterate through each date token found.
+ while ( (match = tokenRegExp.exec(expFormat)) !== null ) {
+ var preMatch = expFormat.slice( index, match.index );
+ index = tokenRegExp.lastIndex;
+
+ // don't replace any matches that occur inside a string literal.
+ quoteCount += appendPreOrPostMatch( preMatch, regexp );
+ if ( quoteCount % 2 ) {
+ regexp.push( match[0] );
+ continue;
+ }
+
+ // add a regex group for the token.
+ var m = match[ 0 ],
+ len = m.length,
+ add;
+ switch ( m ) {
+ case "dddd": case "ddd":
+ case "MMMM": case "MMM":
+ case "gg": case "g":
+ add = "(\\D+)";
+ break;
+ case "tt": case "t":
+ add = "(\\D*)";
+ break;
+ case "yyyy":
+ case "fff":
+ case "ff":
+ case "f":
+ add = "(\\d{" + len + "})";
+ break;
+ case "dd": case "d":
+ case "MM": case "M":
+ case "yy": case "y":
+ case "HH": case "H":
+ case "hh": case "h":
+ case "mm": case "m":
+ case "ss": case "s":
+ add = "(\\d\\d?)";
+ break;
+ case "zzz":
+ add = "([+-]?\\d\\d?:\\d{2})";
+ break;
+ case "zz": case "z":
+ add = "([+-]?\\d\\d?)";
+ break;
+ case "/":
+ add = "(\\/)";
+ break;
+ default:
+ throw "Invalid date format pattern \'" + m + "\'.";
+ }
+ if ( add ) {
+ regexp.push( add );
+ }
+ groups.push( match[0] );
+ }
+ appendPreOrPostMatch( expFormat.slice(index), regexp );
+ regexp.push( "$" );
+
+ // allow whitespace to differ when matching formats.
+ var regexpStr = regexp.join( "" ).replace( /\s+/g, "\\s+" ),
+ parseRegExp = { "regExp": regexpStr, "groups": groups };
+
+ // cache the regex for this format.
+ return re[ format ] = parseRegExp;
+ };
+
+ outOfRange = function( value, low, high ) {
+ return value < low || value > high;
+ };
+
+ toUpper = function( value ) {
+ // "he-IL" has non-breaking space in weekday names.
+ return value.split( "\u00A0" ).join( " " ).toUpperCase();
+ };
+
+ toUpperArray = function( arr ) {
+ var results = [];
+ for ( var i = 0, l = arr.length; i < l; i++ ) {
+ results[ i ] = toUpper( arr[i] );
+ }
+ return results;
+ };
+
+ parseExact = function( value, format, culture ) {
+ // try to parse the date string by matching against the format string
+ // while using the specified culture for date field names.
+ value = trim( value );
+ var cal = culture.calendar,
+ // convert date formats into regular expressions with groupings.
+ // use the regexp to determine the input format and extract the date fields.
+ parseInfo = getParseRegExp( cal, format ),
+ match = new RegExp( parseInfo.regExp ).exec( value );
+ if ( match === null ) {
+ return null;
+ }
+ // found a date format that matches the input.
+ var groups = parseInfo.groups,
+ era = null, year = null, month = null, date = null, weekDay = null,
+ hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
+ pmHour = false;
+ // iterate the format groups to extract and set the date fields.
+ for ( var j = 0, jl = groups.length; j < jl; j++ ) {
+ var matchGroup = match[ j + 1 ];
+ if ( matchGroup ) {
+ var current = groups[ j ],
+ clength = current.length,
+ matchInt = parseInt( matchGroup, 10 );
+ switch ( current ) {
+ case "dd": case "d":
+ // Day of month.
+ date = matchInt;
+ // check that date is generally in valid range, also checking overflow below.
+ if ( outOfRange(date, 1, 31) ) return null;
+ break;
+ case "MMM": case "MMMM":
+ month = getMonthIndex( cal, matchGroup, clength === 3 );
+ if ( outOfRange(month, 0, 11) ) return null;
+ break;
+ case "M": case "MM":
+ // Month.
+ month = matchInt - 1;
+ if ( outOfRange(month, 0, 11) ) return null;
+ break;
+ case "y": case "yy":
+ case "yyyy":
+ year = clength < 4 ? expandYear( cal, matchInt ) : matchInt;
+ if ( outOfRange(year, 0, 9999) ) return null;
+ break;
+ case "h": case "hh":
+ // Hours (12-hour clock).
+ hour = matchInt;
+ if ( hour === 12 ) hour = 0;
+ if ( outOfRange(hour, 0, 11) ) return null;
+ break;
+ case "H": case "HH":
+ // Hours (24-hour clock).
+ hour = matchInt;
+ if ( outOfRange(hour, 0, 23) ) return null;
+ break;
+ case "m": case "mm":
+ // Minutes.
+ min = matchInt;
+ if ( outOfRange(min, 0, 59) ) return null;
+ break;
+ case "s": case "ss":
+ // Seconds.
+ sec = matchInt;
+ if ( outOfRange(sec, 0, 59) ) return null;
+ break;
+ case "tt": case "t":
+ // AM/PM designator.
+ // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of
+ // the AM tokens. If not, fail the parse for this format.
+ pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] );
+ if (
+ !pmHour && (
+ !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] )
+ )
+ ) return null;
+ break;
+ case "f":
+ // Deciseconds.
+ case "ff":
+ // Centiseconds.
+ case "fff":
+ // Milliseconds.
+ msec = matchInt * Math.pow( 10, 3 - clength );
+ if ( outOfRange(msec, 0, 999) ) return null;
+ break;
+ case "ddd":
+ // Day of week.
+ case "dddd":
+ // Day of week.
+ weekDay = getDayIndex( cal, matchGroup, clength === 3 );
+ if ( outOfRange(weekDay, 0, 6) ) return null;
+ break;
+ case "zzz":
+ // Time zone offset in +/- hours:min.
+ var offsets = matchGroup.split( /:/ );
+ if ( offsets.length !== 2 ) return null;
+ hourOffset = parseInt( offsets[0], 10 );
+ if ( outOfRange(hourOffset, -12, 13) ) return null;
+ var minOffset = parseInt( offsets[1], 10 );
+ if ( outOfRange(minOffset, 0, 59) ) return null;
+ tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset );
+ break;
+ case "z": case "zz":
+ // Time zone offset in +/- hours.
+ hourOffset = matchInt;
+ if ( outOfRange(hourOffset, -12, 13) ) return null;
+ tzMinOffset = hourOffset * 60;
+ break;
+ case "g": case "gg":
+ var eraName = matchGroup;
+ if ( !eraName || !cal.eras ) return null;
+ eraName = trim( eraName.toLowerCase() );
+ for ( var i = 0, l = cal.eras.length; i < l; i++ ) {
+ if ( eraName === cal.eras[i].name.toLowerCase() ) {
+ era = i;
+ break;
+ }
+ }
+ // could not find an era with that name
+ if ( era === null ) return null;
+ break;
+ }
+ }
+ }
+ var result = new Date(), defaultYear, convert = cal.convert;
+ defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear();
+ if ( year === null ) {
+ year = defaultYear;
+ }
+ else if ( cal.eras ) {
+ // year must be shifted to normal gregorian year
+ // but not if year was not specified, its already normal gregorian
+ // per the main if clause above.
+ year += cal.eras[( era || 0 )].offset;
+ }
+ // set default day and month to 1 and January, so if unspecified, these are the defaults
+ // instead of the current day/month.
+ if ( month === null ) {
+ month = 0;
+ }
+ if ( date === null ) {
+ date = 1;
+ }
+ // now have year, month, and date, but in the culture's calendar.
+ // convert to gregorian if necessary
+ if ( convert ) {
+ result = convert.toGregorian( year, month, date );
+ // conversion failed, must be an invalid match
+ if ( result === null ) return null;
+ }
+ else {
+ // have to set year, month and date together to avoid overflow based on current date.
+ result.setFullYear( year, month, date );
+ // check to see if date overflowed for specified month (only checked 1-31 above).
+ if ( result.getDate() !== date ) return null;
+ // invalid day of week.
+ if ( weekDay !== null && result.getDay() !== weekDay ) {
+ return null;
+ }
+ }
+ // if pm designator token was found make sure the hours fit the 24-hour clock.
+ if ( pmHour && hour < 12 ) {
+ hour += 12;
+ }
+ result.setHours( hour, min, sec, msec );
+ if ( tzMinOffset !== null ) {
+ // adjust timezone to utc before applying local offset.
+ var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() );
+ // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours
+ // to ensure both these fields will not exceed this range. adjustedMin will range
+ // somewhere between -1440 and 1500, so we only need to split this into hours.
+ result.setHours( result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60 );
+ }
+ return result;
+ };
+}());
+
+parseNegativePattern = function( value, nf, negativePattern ) {
+ var neg = nf[ "-" ],
+ pos = nf[ "+" ],
+ ret;
+ switch ( negativePattern ) {
+ case "n -":
+ neg = " " + neg;
+ pos = " " + pos;
+ /* falls through */
+ case "n-":
+ if ( endsWith(value, neg) ) {
+ ret = [ "-", value.substr(0, value.length - neg.length) ];
+ }
+ else if ( endsWith(value, pos) ) {
+ ret = [ "+", value.substr(0, value.length - pos.length) ];
+ }
+ break;
+ case "- n":
+ neg += " ";
+ pos += " ";
+ /* falls through */
+ case "-n":
+ if ( startsWith(value, neg) ) {
+ ret = [ "-", value.substr(neg.length) ];
+ }
+ else if ( startsWith(value, pos) ) {
+ ret = [ "+", value.substr(pos.length) ];
+ }
+ break;
+ case "(n)":
+ if ( startsWith(value, "(") && endsWith(value, ")") ) {
+ ret = [ "-", value.substr(1, value.length - 2) ];
+ }
+ break;
+ }
+ return ret || [ "", value ];
+};
+
+//
+// public instance functions
+//
+
+Globalize.prototype.findClosestCulture = function( cultureSelector ) {
+ return Globalize.findClosestCulture.call( this, cultureSelector );
+};
+
+Globalize.prototype.format = function( value, format, cultureSelector ) {
+ return Globalize.format.call( this, value, format, cultureSelector );
+};
+
+Globalize.prototype.localize = function( key, cultureSelector ) {
+ return Globalize.localize.call( this, key, cultureSelector );
+};
+
+Globalize.prototype.parseInt = function( value, radix, cultureSelector ) {
+ return Globalize.parseInt.call( this, value, radix, cultureSelector );
+};
+
+Globalize.prototype.parseFloat = function( value, radix, cultureSelector ) {
+ return Globalize.parseFloat.call( this, value, radix, cultureSelector );
+};
+
+Globalize.prototype.culture = function( cultureSelector ) {
+ return Globalize.culture.call( this, cultureSelector );
+};
+
+//
+// public singleton functions
+//
+
+Globalize.addCultureInfo = function( cultureName, baseCultureName, info ) {
+
+ var base = {},
+ isNew = false;
+
+ if ( typeof cultureName !== "string" ) {
+ // cultureName argument is optional string. If not specified, assume info is first
+ // and only argument. Specified info deep-extends current culture.
+ info = cultureName;
+ cultureName = this.culture().name;
+ base = this.cultures[ cultureName ];
+ } else if ( typeof baseCultureName !== "string" ) {
+ // baseCultureName argument is optional string. If not specified, assume info is second
+ // argument. Specified info deep-extends specified culture.
+ // If specified culture does not exist, create by deep-extending default
+ info = baseCultureName;
+ isNew = ( this.cultures[ cultureName ] == null );
+ base = this.cultures[ cultureName ] || this.cultures[ "default" ];
+ } else {
+ // cultureName and baseCultureName specified. Assume a new culture is being created
+ // by deep-extending an specified base culture
+ isNew = true;
+ base = this.cultures[ baseCultureName ];
+ }
+
+ this.cultures[ cultureName ] = extend(true, {},
+ base,
+ info
+ );
+ // Make the standard calendar the current culture if it's a new culture
+ if ( isNew ) {
+ this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard;
+ }
+};
+
+Globalize.findClosestCulture = function( name ) {
+ var match;
+ if ( !name ) {
+ return this.findClosestCulture( this.cultureSelector ) || this.cultures[ "default" ];
+ }
+ if ( typeof name === "string" ) {
+ name = name.split( "," );
+ }
+ if ( isArray(name) ) {
+ var lang,
+ cultures = this.cultures,
+ list = name,
+ i, l = list.length,
+ prioritized = [];
+ for ( i = 0; i < l; i++ ) {
+ name = trim( list[i] );
+ var pri, parts = name.split( ";" );
+ lang = trim( parts[0] );
+ if ( parts.length === 1 ) {
+ pri = 1;
+ }
+ else {
+ name = trim( parts[1] );
+ if ( name.indexOf("q=") === 0 ) {
+ name = name.substr( 2 );
+ pri = parseFloat( name );
+ pri = isNaN( pri ) ? 0 : pri;
+ }
+ else {
+ pri = 1;
+ }
+ }
+ prioritized.push({ lang: lang, pri: pri });
+ }
+ prioritized.sort(function( a, b ) {
+ if ( a.pri < b.pri ) {
+ return 1;
+ } else if ( a.pri > b.pri ) {
+ return -1;
+ }
+ return 0;
+ });
+ // exact match
+ for ( i = 0; i < l; i++ ) {
+ lang = prioritized[ i ].lang;
+ match = cultures[ lang ];
+ if ( match ) {
+ return match;
+ }
+ }
+
+ // neutral language match
+ for ( i = 0; i < l; i++ ) {
+ lang = prioritized[ i ].lang;
+ do {
+ var index = lang.lastIndexOf( "-" );
+ if ( index === -1 ) {
+ break;
+ }
+ // strip off the last part. e.g. en-US => en
+ lang = lang.substr( 0, index );
+ match = cultures[ lang ];
+ if ( match ) {
+ return match;
+ }
+ }
+ while ( 1 );
+ }
+
+ // last resort: match first culture using that language
+ for ( i = 0; i < l; i++ ) {
+ lang = prioritized[ i ].lang;
+ for ( var cultureKey in cultures ) {
+ var culture = cultures[ cultureKey ];
+ if ( culture.language === lang ) {
+ return culture;
+ }
+ }
+ }
+ }
+ else if ( typeof name === "object" ) {
+ return name;
+ }
+ return match || null;
+};
+
+Globalize.format = function( value, format, cultureSelector ) {
+ var culture = this.findClosestCulture( cultureSelector );
+ if ( value instanceof Date ) {
+ value = formatDate( value, format, culture );
+ }
+ else if ( typeof value === "number" ) {
+ value = formatNumber( value, format, culture );
+ }
+ return value;
+};
+
+Globalize.localize = function( key, cultureSelector ) {
+ return this.findClosestCulture( cultureSelector ).messages[ key ] ||
+ this.cultures[ "default" ].messages[ key ];
+};
+
+Globalize.parseDate = function( value, formats, culture ) {
+ culture = this.findClosestCulture( culture );
+
+ var date, prop, patterns;
+ if ( formats ) {
+ if ( typeof formats === "string" ) {
+ formats = [ formats ];
+ }
+ if ( formats.length ) {
+ for ( var i = 0, l = formats.length; i < l; i++ ) {
+ var format = formats[ i ];
+ if ( format ) {
+ date = parseExact( value, format, culture );
+ if ( date ) {
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ patterns = culture.calendar.patterns;
+ for ( prop in patterns ) {
+ date = parseExact( value, patterns[prop], culture );
+ if ( date ) {
+ break;
+ }
+ }
+ }
+
+ return date || null;
+};
+
+Globalize.parseInt = function( value, radix, cultureSelector ) {
+ return truncate( Globalize.parseFloat(value, radix, cultureSelector) );
+};
+
+Globalize.parseFloat = function( value, radix, cultureSelector ) {
+ // radix argument is optional
+ if ( typeof radix !== "number" ) {
+ cultureSelector = radix;
+ radix = 10;
+ }
+
+ var culture = this.findClosestCulture( cultureSelector );
+ var ret = NaN,
+ nf = culture.numberFormat;
+
+ if ( value.indexOf(culture.numberFormat.currency.symbol) > -1 ) {
+ // remove currency symbol
+ value = value.replace( culture.numberFormat.currency.symbol, "" );
+ // replace decimal seperator
+ value = value.replace( culture.numberFormat.currency["."], culture.numberFormat["."] );
+ }
+
+ //Remove percentage character from number string before parsing
+ if ( value.indexOf(culture.numberFormat.percent.symbol) > -1){
+ value = value.replace( culture.numberFormat.percent.symbol, "" );
+ }
+
+ // remove spaces: leading, trailing and between - and number. Used for negative currency pt-BR
+ value = value.replace( / /g, "" );
+
+ // allow infinity or hexidecimal
+ if ( regexInfinity.test(value) ) {
+ ret = parseFloat( value );
+ }
+ else if ( !radix && regexHex.test(value) ) {
+ ret = parseInt( value, 16 );
+ }
+ else {
+
+ // determine sign and number
+ var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ),
+ sign = signInfo[ 0 ],
+ num = signInfo[ 1 ];
+
+ // #44 - try parsing as "(n)"
+ if ( sign === "" && nf.pattern[0] !== "(n)" ) {
+ signInfo = parseNegativePattern( value, nf, "(n)" );
+ sign = signInfo[ 0 ];
+ num = signInfo[ 1 ];
+ }
+
+ // try parsing as "-n"
+ if ( sign === "" && nf.pattern[0] !== "-n" ) {
+ signInfo = parseNegativePattern( value, nf, "-n" );
+ sign = signInfo[ 0 ];
+ num = signInfo[ 1 ];
+ }
+
+ sign = sign || "+";
+
+ // determine exponent and number
+ var exponent,
+ intAndFraction,
+ exponentPos = num.indexOf( "e" );
+ if ( exponentPos < 0 ) exponentPos = num.indexOf( "E" );
+ if ( exponentPos < 0 ) {
+ intAndFraction = num;
+ exponent = null;
+ }
+ else {
+ intAndFraction = num.substr( 0, exponentPos );
+ exponent = num.substr( exponentPos + 1 );
+ }
+ // determine decimal position
+ var integer,
+ fraction,
+ decSep = nf[ "." ],
+ decimalPos = intAndFraction.indexOf( decSep );
+ if ( decimalPos < 0 ) {
+ integer = intAndFraction;
+ fraction = null;
+ }
+ else {
+ integer = intAndFraction.substr( 0, decimalPos );
+ fraction = intAndFraction.substr( decimalPos + decSep.length );
+ }
+ // handle groups (e.g. 1,000,000)
+ var groupSep = nf[ "," ];
+ integer = integer.split( groupSep ).join( "" );
+ var altGroupSep = groupSep.replace( /\u00A0/g, " " );
+ if ( groupSep !== altGroupSep ) {
+ integer = integer.split( altGroupSep ).join( "" );
+ }
+ // build a natively parsable number string
+ var p = sign + integer;
+ if ( fraction !== null ) {
+ p += "." + fraction;
+ }
+ if ( exponent !== null ) {
+ // exponent itself may have a number patternd
+ var expSignInfo = parseNegativePattern( exponent, nf, "-n" );
+ p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ];
+ }
+ if ( regexParseFloat.test(p) ) {
+ ret = parseFloat( p );
+ }
+ }
+ return ret;
+};
+
+Globalize.culture = function( cultureSelector ) {
+ // setter
+ if ( typeof cultureSelector !== "undefined" ) {
+ this.cultureSelector = cultureSelector;
+ }
+ // getter
+ return this.findClosestCulture( cultureSelector ) || this.cultures[ "default" ];
+};
+
+}( this )); \ No newline at end of file
diff --git a/external/globalize/LICENSE.txt b/external/globalize/LICENSE.txt
new file mode 100644
index 000000000..0f9f2e51c
--- /dev/null
+++ b/external/globalize/LICENSE.txt
@@ -0,0 +1,43 @@
+Copyright jQuery Foundation and other contributors, https://jquery.org/
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/jquery/globalize
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====
+
+Copyright and related rights for sample code are waived via CC0. Sample
+code is defined as all source code contained within the doc directory.
+
+CC0: http://creativecommons.org/publicdomain/zero/1.0/
+
+====
+
+All files located in the node_modules and external directories are
+externally maintained libraries used by this software which have their
+own licenses; we recommend you read them, as their terms may differ from
+the terms above.
diff --git a/external/globalize/globalize-runtime.js b/external/globalize/globalize-runtime.js
new file mode 100644
index 000000000..1cee1297e
--- /dev/null
+++ b/external/globalize/globalize-runtime.js
@@ -0,0 +1,236 @@
+/**
+ * Globalize Runtime v1.1.0-rc.6
+ *
+ * http://github.com/jquery/globalize
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-11-18T10:38Z
+ */
+/*!
+ * Globalize Runtime v1.1.0-rc.6 2015-11-18T10:38Z Released under the MIT license
+ * http://git.io/TrdQbw
+ */
+(function( root, factory ) {
+
+ // UMD returnExports
+ if ( typeof define === "function" && define.amd ) {
+
+ // AMD
+ define( factory );
+ } else if ( typeof exports === "object" ) {
+
+ // Node, CommonJS
+ module.exports = factory();
+ } else {
+
+ // Globalize
+ root.Globalize = factory();
+ }
+}( this, function() {
+
+
+/**
+ * A toString method that outputs meaningful values for objects or arrays and
+ * still performs as fast as a plain string in case variable is string, or as
+ * fast as `"" + number` in case variable is a number.
+ * Ref: http://jsperf.com/my-stringify
+ */
+var toString = function( variable ) {
+ return typeof variable === "string" ? variable : ( typeof variable === "number" ? "" +
+ variable : JSON.stringify( variable ) );
+};
+
+
+
+
+/**
+ * formatMessage( message, data )
+ *
+ * @message [String] A message with optional {vars} to be replaced.
+ *
+ * @data [Array or JSON] Object with replacing-variables content.
+ *
+ * Return the formatted message. For example:
+ *
+ * - formatMessage( "{0} second", [ 1 ] ); // 1 second
+ *
+ * - formatMessage( "{0}/{1}", ["m", "s"] ); // m/s
+ *
+ * - formatMessage( "{name} <{email}>", {
+ * name: "Foo",
+ * email: "bar@baz.qux"
+ * }); // Foo <bar@baz.qux>
+ */
+var formatMessage = function( message, data ) {
+
+ // Replace {attribute}'s
+ message = message.replace( /{[0-9a-zA-Z-_. ]+}/g, function( name ) {
+ name = name.replace( /^{([^}]*)}$/, "$1" );
+ return toString( data[ name ] );
+ });
+
+ return message;
+};
+
+
+
+
+var objectExtend = function() {
+ var destination = arguments[ 0 ],
+ sources = [].slice.call( arguments, 1 );
+
+ sources.forEach(function( source ) {
+ var prop;
+ for ( prop in source ) {
+ destination[ prop ] = source[ prop ];
+ }
+ });
+
+ return destination;
+};
+
+
+
+
+var createError = function( code, message, attributes ) {
+ var error;
+
+ message = code + ( message ? ": " + formatMessage( message, attributes ) : "" );
+ error = new Error( message );
+ error.code = code;
+
+ objectExtend( error, attributes );
+
+ return error;
+};
+
+
+
+
+// Based on http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery
+var stringHash = function( str ) {
+ return [].reduce.call( str, function( hash, i ) {
+ var chr = i.charCodeAt( 0 );
+ hash = ( ( hash << 5 ) - hash ) + chr;
+ return hash | 0;
+ }, 0 );
+};
+
+
+
+
+var runtimeKey = function( fnName, locale, args, argsStr ) {
+ var hash;
+ argsStr = argsStr || JSON.stringify( args );
+ hash = stringHash( fnName + locale + argsStr );
+ return hash > 0 ? "a" + hash : "b" + Math.abs( hash );
+};
+
+
+
+
+var validate = function( code, message, check, attributes ) {
+ if ( !check ) {
+ throw createError( code, message, attributes );
+ }
+};
+
+
+
+
+var validateParameterPresence = function( value, name ) {
+ validate( "E_MISSING_PARAMETER", "Missing required parameter `{name}`.",
+ value !== undefined, { name: name });
+};
+
+
+
+
+var validateParameterType = function( value, name, check, expected ) {
+ validate(
+ "E_INVALID_PAR_TYPE",
+ "Invalid `{name}` parameter ({value}). {expected} expected.",
+ check,
+ {
+ expected: expected,
+ name: name,
+ value: value
+ }
+ );
+};
+
+
+
+
+var validateParameterTypeString = function( value, name ) {
+ validateParameterType(
+ value,
+ name,
+ value === undefined || typeof value === "string",
+ "a string"
+ );
+};
+
+
+
+
+// ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions?redirectlocale=en-US&redirectslug=JavaScript%2FGuide%2FRegular_Expressions
+var regexpEscape = function( string ) {
+ return string.replace( /([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1" );
+};
+
+
+
+
+var stringPad = function( str, count, right ) {
+ var length;
+ if ( typeof str !== "string" ) {
+ str = String( str );
+ }
+ for ( length = str.length; length < count; length += 1 ) {
+ str = ( right ? ( str + "0" ) : ( "0" + str ) );
+ }
+ return str;
+};
+
+
+
+
+function Globalize( locale ) {
+ if ( !( this instanceof Globalize ) ) {
+ return new Globalize( locale );
+ }
+
+ validateParameterPresence( locale, "locale" );
+ validateParameterTypeString( locale, "locale" );
+
+ this._locale = locale;
+}
+
+Globalize.locale = function( locale ) {
+ validateParameterTypeString( locale, "locale" );
+
+ if ( arguments.length ) {
+ this._locale = locale;
+ }
+ return this._locale;
+};
+
+Globalize._createError = createError;
+Globalize._formatMessage = formatMessage;
+Globalize._regexpEscape = regexpEscape;
+Globalize._runtimeKey = runtimeKey;
+Globalize._stringPad = stringPad;
+Globalize._validateParameterPresence = validateParameterPresence;
+Globalize._validateParameterTypeString = validateParameterTypeString;
+Globalize._validateParameterType = validateParameterType;
+
+return Globalize;
+
+
+
+
+}));
diff --git a/external/globalize/globalize-runtime/date.js b/external/globalize/globalize-runtime/date.js
new file mode 100644
index 000000000..118a5da05
--- /dev/null
+++ b/external/globalize/globalize-runtime/date.js
@@ -0,0 +1,1289 @@
+/**
+ * Globalize Runtime v1.1.0-rc.6
+ *
+ * http://github.com/jquery/globalize
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-11-18T10:38Z
+ */
+/*!
+ * Globalize Runtime v1.1.0-rc.6 2015-11-18T10:38Z Released under the MIT license
+ * http://git.io/TrdQbw
+ */
+(function( root, factory ) {
+
+ // UMD returnExports
+ if ( typeof define === "function" && define.amd ) {
+
+ // AMD
+ define([
+ "../globalize-runtime",
+ "./number"
+ ], factory );
+ } else if ( typeof exports === "object" ) {
+
+ // Node, CommonJS
+ module.exports = factory(
+ require( "../globalize-runtime" ),
+ require( "./number" )
+ );
+ } else {
+
+ // Extend global
+ factory( root.Globalize );
+ }
+}(this, function( Globalize ) {
+
+var createErrorUnsupportedFeature = Globalize._createErrorUnsupportedFeature,
+ regexpEscape = Globalize._regexpEscape,
+ runtimeKey = Globalize._runtimeKey,
+ stringPad = Globalize._stringPad,
+ validateParameterPresence = Globalize._validateParameterPresence,
+ validateParameterType = Globalize._validateParameterType,
+ validateParameterTypeString = Globalize._validateParameterTypeString;
+
+
+var validateParameterTypeDate = function( value, name ) {
+ validateParameterType( value, name, value === undefined || value instanceof Date, "Date" );
+};
+
+
+
+
+/**
+ * dayOfWeek( date, firstDay )
+ *
+ * @date
+ *
+ * @firstDay the result of `dateFirstDayOfWeek( cldr )`
+ *
+ * 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 dateDayOfWeek = function( date, firstDay ) {
+ return ( date.getDay() - firstDay + 7 ) % 7;
+};
+
+
+
+
+/**
+ * distanceInDays( from, to )
+ *
+ * Return the distance in days between from and to Dates.
+ */
+var dateDistanceInDays = function( from, to ) {
+ var inDays = 864e5;
+ return ( to.getTime() - from.getTime() ) / inDays;
+};
+
+
+
+
+/**
+ * startOf changes the input to the beginning of the given unit.
+ *
+ * For example, starting at the start of a day, resets hours, minutes
+ * seconds and milliseconds to 0. Starting at the month does the same, but
+ * also sets the date to 1.
+ *
+ * Returns the modified date
+ */
+var dateStartOf = 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 );
+ }
+ return date;
+};
+
+
+
+
+/**
+ * dayOfYear
+ *
+ * Return the distance in days of the date to the begin of the year [0-d].
+ */
+var dateDayOfYear = function( date ) {
+ return Math.floor( dateDistanceInDays( dateStartOf( date, "year" ), date ) );
+};
+
+
+
+
+/**
+ * millisecondsInDay
+ */
+var dateMillisecondsInDay = function( date ) {
+
+ // TODO Handle daylight savings discontinuities
+ return date - dateStartOf( date, "day" );
+};
+
+
+
+
+var datePatternRe = ( /([a-z])\1*|'([^']|'')+'|''|./ig );
+
+
+
+
+/**
+ * hourFormat( date, format, timeSeparator, formatNumber )
+ *
+ * Return date's timezone offset according to the format passed.
+ * Eg for format when timezone offset is 180:
+ * - "+H;-H": -3
+ * - "+HHmm;-HHmm": -0300
+ * - "+HH:mm;-HH:mm": -03:00
+ */
+var dateTimezoneHourFormat = function( date, format, timeSeparator, formatNumber ) {
+ var absOffset,
+ offset = date.getTimezoneOffset();
+
+ absOffset = Math.abs( offset );
+ formatNumber = formatNumber || {
+ 1: function( value ) {
+ return stringPad( value, 1 );
+ },
+ 2: function( value ) {
+ return stringPad( value, 2 );
+ }
+ };
+
+ return format
+
+ // Pick the correct sign side (+ or -).
+ .split( ";" )[ offset > 0 ? 1 : 0 ]
+
+ // Localize time separator
+ .replace( ":", timeSeparator )
+
+ // Update hours offset.
+ .replace( /HH?/, function( match ) {
+ return formatNumber[ match.length ]( Math.floor( absOffset / 60 ) );
+ })
+
+ // Update minutes offset and return.
+ .replace( /mm/, function() {
+ return formatNumber[ 2 ]( absOffset % 60 );
+ });
+};
+
+
+
+
+var dateWeekDays = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ];
+
+
+
+
+/**
+ * format( date, properties )
+ *
+ * @date [Date instance].
+ *
+ * @properties
+ *
+ * TODO Support other calendar types.
+ *
+ * Disclosure: this function borrows excerpts of dojo/date/locale.
+ */
+var dateFormat = function( date, numberFormatters, properties ) {
+ var timeSeparator = properties.timeSeparator;
+
+ return properties.pattern.replace( datePatternRe, function( current ) {
+ var 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 = properties.preferredTime;
+ }
+
+ if ( chr === "Z" ) {
+
+ // Z..ZZZ: same as "xxxx".
+ if ( length < 4 ) {
+ chr = "x";
+ length = 4;
+
+ // ZZZZ: same as "OOOO".
+ } else if ( length < 5 ) {
+ chr = "O";
+ length = 4;
+
+ // ZZZZZ: same as "XXXXX"
+ } else {
+ chr = "X";
+ length = 5;
+ }
+ }
+
+ switch ( chr ) {
+
+ // Era
+ case "G":
+ ret = properties.eras[ 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 = date.getFullYear();
+ if ( length === 2 ) {
+ ret = String( ret );
+ 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 -
+ dateDayOfWeek( date, properties.firstDay ) -
+ properties.firstDay -
+ properties.minDays
+ );
+ ret = ret.getFullYear();
+ if ( length === 2 ) {
+ ret = String( ret );
+ ret = +ret.substr( ret.length - 2 );
+ }
+ break;
+
+ // Quarter
+ case "Q":
+ case "q":
+ ret = Math.ceil( ( date.getMonth() + 1 ) / 3 );
+ if ( length > 2 ) {
+ ret = properties.quarters[ chr ][ length ][ ret ];
+ }
+ break;
+
+ // Month
+ case "M":
+ case "L":
+ ret = date.getMonth() + 1;
+ if ( length > 2 ) {
+ ret = properties.months[ chr ][ length ][ 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 = dateDayOfWeek( dateStartOf( date, "year" ), properties.firstDay );
+ ret = Math.ceil( ( dateDayOfYear( date ) + ret ) / 7 ) -
+ ( 7 - ret >= properties.minDays ? 0 : 1 );
+ break;
+
+ case "W":
+
+ // Week of Month.
+ // wom = ceil( ( dom + dow of `1/month` ) / 7 ) - minDaysStuff ? 1 : 0.
+ ret = dateDayOfWeek( dateStartOf( date, "month" ), properties.firstDay );
+ ret = Math.ceil( ( date.getDate() + ret ) / 7 ) -
+ ( 7 - ret >= properties.minDays ? 0 : 1 );
+ break;
+
+ // Day
+ case "d":
+ ret = date.getDate();
+ break;
+
+ case "D":
+ ret = dateDayOfYear( date ) + 1;
+ break;
+
+ case "F":
+
+ // Day of Week in month. eg. 2nd Wed in July.
+ ret = Math.floor( date.getDate() / 7 ) + 1;
+ break;
+
+ // 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 = dateDayOfWeek( date, properties.firstDay ) + 1;
+ break;
+ }
+
+ /* falls through */
+ case "E":
+ ret = dateWeekDays[ date.getDay() ];
+ ret = properties.days[ chr ][ length ][ ret ];
+ break;
+
+ // Period (AM or PM)
+ case "a":
+ ret = properties.dayPeriods[ date.getHours() < 12 ? "am" : "pm" ];
+ break;
+
+ // Hour
+ case "h": // 1-12
+ ret = ( date.getHours() % 12 ) || 12;
+ break;
+
+ case "H": // 0-23
+ ret = date.getHours();
+ break;
+
+ case "K": // 0-11
+ ret = date.getHours() % 12;
+ break;
+
+ case "k": // 1-24
+ ret = date.getHours() || 24;
+ break;
+
+ // Minute
+ case "m":
+ ret = date.getMinutes();
+ break;
+
+ // Second
+ case "s":
+ ret = date.getSeconds();
+ break;
+
+ case "S":
+ ret = Math.round( date.getMilliseconds() * Math.pow( 10, length - 3 ) );
+ break;
+
+ case "A":
+ ret = Math.round( dateMillisecondsInDay( date ) * Math.pow( 10, length - 3 ) );
+ break;
+
+ // Zone
+ case "z":
+ case "O":
+
+ // O: "{gmtFormat}+H;{gmtFormat}-H" or "{gmtZeroFormat}", eg. "GMT-8" or "GMT".
+ // OOOO: "{gmtFormat}{hourFormat}" or "{gmtZeroFormat}", eg. "GMT-08:00" or "GMT".
+ if ( date.getTimezoneOffset() === 0 ) {
+ ret = properties.gmtZeroFormat;
+ } else {
+ ret = dateTimezoneHourFormat(
+ date,
+ length < 4 ? "+H;-H" : properties.tzLongHourFormat,
+ timeSeparator,
+ numberFormatters
+ );
+ ret = properties.gmtFormat.replace( /\{0\}/, ret );
+ }
+ break;
+
+ case "X":
+
+ // Same as x*, except it uses "Z" for zero offset.
+ if ( date.getTimezoneOffset() === 0 ) {
+ ret = "Z";
+ break;
+ }
+
+ /* falls through */
+ case "x":
+
+ // x: hourFormat("+HH;-HH")
+ // xx or xxxx: hourFormat("+HHmm;-HHmm")
+ // xxx or xxxxx: hourFormat("+HH:mm;-HH:mm")
+ ret = length === 1 ? "+HH;-HH" : ( length % 2 ? "+HH:mm;-HH:mm" : "+HHmm;-HHmm" );
+ ret = dateTimezoneHourFormat( date, ret, ":" );
+ break;
+
+ // timeSeparator
+ case ":":
+ ret = timeSeparator;
+ break;
+
+ // ' literals.
+ case "'":
+ current = current.replace( /''/, "'" );
+ if ( length > 2 ) {
+ current = current.slice( 1, -1 );
+ }
+ ret = current;
+ break;
+
+ // Anything else is considered a literal, including [ ,:/.@#], chinese, japonese, and
+ // arabic characters.
+ default:
+ ret = current;
+ }
+ if ( typeof ret === "number" ) {
+ ret = numberFormatters[ length ]( ret );
+ }
+ return ret;
+ });
+};
+
+
+
+
+var dateFormatterFn = function( numberFormatters, properties ) {
+ return function dateFormatter( value ) {
+ validateParameterPresence( value, "value" );
+ validateParameterTypeDate( value, "value" );
+
+ return dateFormat( value, numberFormatters, properties );
+ };
+
+};
+
+
+
+
+/**
+ * isLeapYear( year )
+ *
+ * @year [Number]
+ *
+ * Returns an indication whether the specified year is a leap year.
+ */
+var dateIsLeapYear = function( year ) {
+ return new Date( year, 1, 29 ).getMonth() === 1;
+};
+
+
+
+
+/**
+ * lastDayOfMonth( date )
+ *
+ * @date [Date]
+ *
+ * Return the last day of the given date's month
+ */
+var dateLastDayOfMonth = function( date ) {
+ return new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDate();
+};
+
+
+
+
+/**
+ * Differently from native date.setDate(), this function returns a date whose
+ * day remains inside the month boundaries. For example:
+ *
+ * setDate( FebDate, 31 ): a "Feb 28" date.
+ * setDate( SepDate, 31 ): a "Sep 30" date.
+ */
+var dateSetDate = function( date, day ) {
+ var lastDay = new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDate();
+
+ date.setDate( day < 1 ? 1 : day < lastDay ? day : lastDay );
+};
+
+
+
+
+/**
+ * Differently from native date.setMonth(), this function adjusts date if
+ * needed, so final month is always the one set.
+ *
+ * setMonth( Jan31Date, 1 ): a "Feb 28" date.
+ * setDate( Jan31Date, 8 ): a "Sep 30" date.
+ */
+var dateSetMonth = function( date, month ) {
+ var originalDate = date.getDate();
+
+ date.setDate( 1 );
+ date.setMonth( month );
+ dateSetDate( date, originalDate );
+};
+
+
+
+
+var outOfRange = function( value, low, high ) {
+ return value < low || value > high;
+};
+
+
+
+
+/**
+ * parse( value, tokens, properties )
+ *
+ * @value [String] string date.
+ *
+ * @tokens [Object] tokens returned by date/tokenizer.
+ *
+ * @properties [Object] output returned by date/tokenizer-properties.
+ *
+ * ref: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
+ */
+var dateParse = function( value, tokens, properties ) {
+ var amPm, day, daysOfYear, era, hour, hour12, timezoneOffset, valid,
+ YEAR = 0,
+ MONTH = 1,
+ DAY = 2,
+ HOUR = 3,
+ MINUTE = 4,
+ SECOND = 5,
+ MILLISECONDS = 6,
+ date = new Date(),
+ truncateAt = [],
+ units = [ "year", "month", "day", "hour", "minute", "second", "milliseconds" ];
+
+ if ( !tokens.length ) {
+ return null;
+ }
+
+ valid = tokens.every(function( token ) {
+ var century, chr, value, length;
+
+ 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 = properties.preferredTimeData;
+ }
+
+ switch ( chr ) {
+
+ // Era
+ case "G":
+ truncateAt.push( YEAR );
+ era = +token.value;
+ break;
+
+ // Year
+ case "y":
+ value = token.value;
+ 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"
+ throw createErrorUnsupportedFeature({
+ feature: "year pattern `" + chr + "`"
+ });
+
+ // Quarter (skip)
+ case "Q":
+ case "q":
+ break;
+
+ // Month
+ case "M":
+ case "L":
+ if ( length <= 2 ) {
+ value = token.value;
+ } else {
+ value = +token.value;
+ }
+ if ( outOfRange( value, 1, 12 ) ) {
+ return false;
+ }
+ dateSetMonth( date, value - 1 );
+ truncateAt.push( MONTH );
+ break;
+
+ // Week (skip)
+ case "w": // Week of Year.
+ case "W": // Week of Month.
+ break;
+
+ // Day
+ case "d":
+ day = token.value;
+ truncateAt.push( DAY );
+ break;
+
+ case "D":
+ daysOfYear = token.value;
+ truncateAt.push( DAY );
+ break;
+
+ case "F":
+
+ // Day of Week in month. eg. 2nd Wed in July.
+ // Skip
+ break;
+
+ // Week day
+ case "e":
+ case "c":
+ case "E":
+
+ // Skip.
+ // value = arrayIndexOf( dateWeekDays, token.value );
+ break;
+
+ // Period (AM or PM)
+ case "a":
+ amPm = token.value;
+ break;
+
+ // Hour
+ case "h": // 1-12
+ value = token.value;
+ if ( outOfRange( value, 1, 12 ) ) {
+ return false;
+ }
+ hour = hour12 = true;
+ date.setHours( value === 12 ? 0 : value );
+ truncateAt.push( HOUR );
+ break;
+
+ case "K": // 0-11
+ value = token.value;
+ if ( outOfRange( value, 0, 11 ) ) {
+ return false;
+ }
+ hour = hour12 = true;
+ date.setHours( value );
+ truncateAt.push( HOUR );
+ break;
+
+ case "k": // 1-24
+ value = token.value;
+ if ( outOfRange( value, 1, 24 ) ) {
+ return false;
+ }
+ hour = true;
+ date.setHours( value === 24 ? 0 : value );
+ truncateAt.push( HOUR );
+ break;
+
+ case "H": // 0-23
+ value = token.value;
+ if ( outOfRange( value, 0, 23 ) ) {
+ return false;
+ }
+ hour = true;
+ date.setHours( value );
+ truncateAt.push( HOUR );
+ break;
+
+ // Minute
+ case "m":
+ value = token.value;
+ if ( outOfRange( value, 0, 59 ) ) {
+ return false;
+ }
+ date.setMinutes( value );
+ truncateAt.push( MINUTE );
+ break;
+
+ // Second
+ case "s":
+ value = token.value;
+ 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.value * Math.pow( 10, 3 - length ) );
+ date.setMilliseconds( value );
+ truncateAt.push( MILLISECONDS );
+ break;
+
+ // Zone
+ case "Z":
+ case "z":
+ case "O":
+ case "X":
+ case "x":
+ timezoneOffset = token.value - date.getTimezoneOffset();
+ break;
+ }
+
+ return true;
+ });
+
+ if ( !valid ) {
+ return null;
+ }
+
+ // 12-hour format needs AM or PM, 24-hour format doesn't, ie. return null
+ // if amPm && !hour12 || !amPm && hour12.
+ if ( hour && !( !amPm ^ hour12 ) ) {
+ return null;
+ }
+
+ if ( era === 0 ) {
+
+ // 1 BC = year 0
+ date.setFullYear( date.getFullYear() * -1 + 1 );
+ }
+
+ if ( day !== undefined ) {
+ if ( outOfRange( day, 1, dateLastDayOfMonth( date ) ) ) {
+ return null;
+ }
+ date.setDate( day );
+ } else if ( daysOfYear !== undefined ) {
+ if ( outOfRange( daysOfYear, 1, dateIsLeapYear( date.getFullYear() ) ? 366 : 365 ) ) {
+ return null;
+ }
+ date.setMonth( 0 );
+ date.setDate( daysOfYear );
+ }
+
+ if ( hour12 && amPm === "pm" ) {
+ date.setHours( date.getHours() + 12 );
+ }
+
+ if ( timezoneOffset ) {
+ date.setMinutes( date.getMinutes() + timezoneOffset );
+ }
+
+ // 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 = dateStartOf( date, units[ truncateAt ] );
+
+ return date;
+};
+
+
+
+
+/**
+ * Generated by:
+ *
+ * regenerate().add( require( "unicode-7.0.0/categories/N/symbols" ) ).toString();
+ *
+ * https://github.com/mathiasbynens/regenerate
+ * https://github.com/mathiasbynens/unicode-7.0.0
+ */
+var regexpN = /[0-9\xB2\xB3\xB9\xBC-\xBE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D66-\u0D75\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19]|\uD800[\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDEE1-\uDEFB\uDF20-\uDF23\uDF41\uDF4A\uDFD1-\uDFD5]|\uD801[\uDCA0-\uDCA9]|\uD802[\uDC58-\uDC5F\uDC79-\uDC7F\uDCA7-\uDCAF\uDD16-\uDD1B\uDE40-\uDE47\uDE7D\uDE7E\uDE9D-\uDE9F\uDEEB-\uDEEF\uDF58-\uDF5F\uDF78-\uDF7F\uDFA9-\uDFAF]|\uD803[\uDE60-\uDE7E]|\uD804[\uDC52-\uDC6F\uDCF0-\uDCF9\uDD36-\uDD3F\uDDD0-\uDDD9\uDDE1-\uDDF4\uDEF0-\uDEF9]|\uD805[\uDCD0-\uDCD9\uDE50-\uDE59\uDEC0-\uDEC9]|\uD806[\uDCE0-\uDCF2]|\uD809[\uDC00-\uDC6E]|\uD81A[\uDE60-\uDE69\uDF50-\uDF59\uDF5B-\uDF61]|\uD834[\uDF60-\uDF71]|\uD835[\uDFCE-\uDFFF]|\uD83A[\uDCC7-\uDCCF]|\uD83C[\uDD00-\uDD0C]/;
+
+
+
+
+/**
+ * tokenizer( value, pattern, properties )
+ *
+ * @value [String] string date.
+ *
+ * @properties [Object] output returned by date/tokenizer-properties.
+ *
+ * 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 dateTokenizer = function( value, numberParser, properties ) {
+ var valid,
+ timeSeparator = properties.timeSeparator,
+ tokens = [],
+ widths = [ "abbreviated", "wide", "narrow" ];
+
+ valid = properties.pattern.match( datePatternRe ).every(function( current ) {
+ var chr, length, numeric, tokenRe,
+ token = {};
+
+ function hourFormatParse( tokenRe, numberParser ) {
+ var aux = value.match( tokenRe );
+ numberParser = numberParser || function( value ) {
+ return +value;
+ };
+
+ if ( !aux ) {
+ return false;
+ }
+
+ // hourFormat containing H only, e.g., `+H;-H`
+ if ( aux.length < 8 ) {
+ token.value =
+ ( aux[ 1 ] ? -numberParser( aux[ 1 ] ) : numberParser( aux[ 4 ] ) ) * 60;
+
+ // hourFormat containing H and m, e.g., `+HHmm;-HHmm`
+ } else {
+ token.value =
+ ( aux[ 1 ] ? -numberParser( aux[ 1 ] ) : numberParser( aux[ 7 ] ) ) * 60 +
+ ( aux[ 1 ] ? -numberParser( aux[ 4 ] ) : numberParser( aux[ 10 ] ) );
+ }
+
+ return true;
+ }
+
+ // Transform:
+ // - "+H;-H" -> /\+(\d\d?)|-(\d\d?)/
+ // - "+HH;-HH" -> /\+(\d\d)|-(\d\d)/
+ // - "+HHmm;-HHmm" -> /\+(\d\d)(\d\d)|-(\d\d)(\d\d)/
+ // - "+HH:mm;-HH:mm" -> /\+(\d\d):(\d\d)|-(\d\d):(\d\d)/
+ //
+ // If gmtFormat is GMT{0}, the regexp must fill {0} in each side, e.g.:
+ // - "+H;-H" -> /GMT\+(\d\d?)|GMT-(\d\d?)/
+ function hourFormatRe( hourFormat, gmtFormat, timeSeparator ) {
+ var re;
+
+ if ( !gmtFormat ) {
+ gmtFormat = "{0}";
+ }
+
+ re = hourFormat
+ .replace( "+", "\\+" )
+
+ // Unicode equivalent to (\\d\\d)
+ .replace( /HH|mm/g, "((" + regexpN.source + ")(" + regexpN.source + "))" )
+
+ // Unicode equivalent to (\\d\\d?)
+ .replace( /H|m/g, "((" + regexpN.source + ")(" + regexpN.source + ")?)" );
+
+ if ( timeSeparator ) {
+ re = re.replace( /:/g, timeSeparator );
+ }
+
+ re = re.split( ";" ).map(function( part ) {
+ return gmtFormat.replace( "{0}", part );
+ }).join( "|" );
+
+ return new RegExp( re );
+ }
+
+ function oneDigitIfLengthOne() {
+ if ( length === 1 ) {
+
+ // Unicode equivalent to /\d/
+ numeric = true;
+ return tokenRe = regexpN;
+ }
+ }
+
+ function oneOrTwoDigitsIfLengthOne() {
+ if ( length === 1 ) {
+
+ // Unicode equivalent to /\d\d?/
+ numeric = true;
+ return tokenRe = new RegExp( "(" + regexpN.source + ")(" + regexpN.source + ")?" );
+ }
+ }
+
+ function twoDigitsIfLengthTwo() {
+ if ( length === 2 ) {
+
+ // Unicode equivalent to /\d\d/
+ numeric = true;
+ return tokenRe = new RegExp( "(" + regexpN.source + ")(" + regexpN.source + ")" );
+ }
+ }
+
+ // 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 = properties[ path.join( "/" ) ];
+
+ for ( i in data ) {
+ re = new RegExp( "^" + data[ i ] );
+ if ( re.test( value ) ) {
+ token.value = i;
+ return tokenRe = new RegExp( data[ i ] );
+ }
+ }
+ return null;
+ }
+
+ token.type = current;
+ chr = current.charAt( 0 ),
+ length = current.length;
+
+ if ( chr === "Z" ) {
+
+ // Z..ZZZ: same as "xxxx".
+ if ( length < 4 ) {
+ chr = "x";
+ length = 4;
+
+ // ZZZZ: same as "OOOO".
+ } else if ( length < 5 ) {
+ chr = "O";
+ length = 4;
+
+ // ZZZZZ: same as "XXXXX"
+ } else {
+ chr = "X";
+ length = 5;
+ }
+ }
+
+ switch ( chr ) {
+
+ // Era
+ case "G":
+ lookup([
+ "gregorian/eras",
+ length <= 3 ? "eraAbbr" : ( length === 4 ? "eraNames" : "eraNarrow" )
+ ]);
+ break;
+
+ // Year
+ case "y":
+ case "Y":
+ numeric = true;
+
+ // number l=1:+, l=2:{2}, l=3:{3,}, l=4:{4,}, ...
+ if ( length === 1 ) {
+
+ // Unicode equivalent to /\d+/.
+ tokenRe = new RegExp( "(" + regexpN.source + ")+" );
+ } else if ( length === 2 ) {
+
+ // Unicode equivalent to /\d\d/
+ tokenRe = new RegExp( "(" + regexpN.source + ")(" + regexpN.source + ")" );
+ } else {
+
+ // Unicode equivalent to /\d{length,}/
+ tokenRe = new RegExp( "(" + regexpN.source + "){" + length + ",}" );
+ }
+ break;
+
+ // Quarter
+ case "Q":
+ case "q":
+
+ // number l=1:{1}, l=2:{2}.
+ // lookup l=3...
+ oneDigitIfLengthOne() || twoDigitsIfLengthTwo() || lookup([
+ "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([
+ "gregorian/months",
+ chr === "M" ? "format" : "stand-alone",
+ widths[ length - 3 ]
+ ]);
+ break;
+
+ // Day
+ case "D":
+
+ // number {l,3}.
+ if ( length <= 3 ) {
+
+ // Unicode equivalent to /\d{length,3}/
+ numeric = true;
+ tokenRe = new RegExp( "(" + regexpN.source + "){" + length + ",3}" );
+ }
+ break;
+
+ case "W":
+ case "F":
+
+ // number l=1:{1}.
+ oneDigitIfLengthOne();
+ break;
+
+ // Week day
+ case "e":
+ case "c":
+
+ // number l=1:{1}, l=2:{2}.
+ // lookup for length >=3.
+ if ( length <= 2 ) {
+ oneDigitIfLengthOne() || twoDigitsIfLengthTwo();
+ break;
+ }
+
+ /* 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([
+ "gregorian/days",
+ [ chr === "c" ? "stand-alone" : "format" ],
+ "short"
+ ]) || lookup([
+ "gregorian/days",
+ [ chr === "c" ? "stand-alone" : "format" ],
+ "abbreviated"
+ ]);
+ } else {
+ lookup([
+ "gregorian/days",
+ [ chr === "c" ? "stand-alone" : "format" ],
+ widths[ length < 3 ? 0 : length - 3 ]
+ ]);
+ }
+ break;
+
+ // Period (AM or PM)
+ case "a":
+ lookup([
+ "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}.
+
+ // Unicode equivalent to /\d{length}/
+ numeric = true;
+ tokenRe = new RegExp( "(" + regexpN.source + "){" + length + "}" );
+ break;
+
+ case "A":
+
+ // number {l+5}.
+
+ // Unicode equivalent to /\d{length+5}/
+ numeric = true;
+ tokenRe = new RegExp( "(" + regexpN.source + "){" + ( length + 5 ) + "}" );
+ break;
+
+ // Zone
+ case "z":
+ case "O":
+
+ // O: "{gmtFormat}+H;{gmtFormat}-H" or "{gmtZeroFormat}", eg. "GMT-8" or "GMT".
+ // OOOO: "{gmtFormat}{hourFormat}" or "{gmtZeroFormat}", eg. "GMT-08:00" or "GMT".
+ if ( value === properties[ "timeZoneNames/gmtZeroFormat" ] ) {
+ token.value = 0;
+ tokenRe = new RegExp( properties[ "timeZoneNames/gmtZeroFormat" ] );
+ } else {
+ tokenRe = hourFormatRe(
+ length < 4 ? "+H;-H" : properties[ "timeZoneNames/hourFormat" ],
+ properties[ "timeZoneNames/gmtFormat" ],
+ timeSeparator
+ );
+ if ( !hourFormatParse( tokenRe, numberParser ) ) {
+ return null;
+ }
+ }
+ break;
+
+ case "X":
+
+ // Same as x*, except it uses "Z" for zero offset.
+ if ( value === "Z" ) {
+ token.value = 0;
+ tokenRe = /Z/;
+ break;
+ }
+
+ /* falls through */
+ case "x":
+
+ // x: hourFormat("+HH;-HH")
+ // xx or xxxx: hourFormat("+HHmm;-HHmm")
+ // xxx or xxxxx: hourFormat("+HH:mm;-HH:mm")
+ tokenRe = hourFormatRe(
+ length === 1 ? "+HH;-HH" : ( length % 2 ? "+HH:mm;-HH:mm" : "+HHmm;-HHmm" )
+ );
+ if ( !hourFormatParse( tokenRe ) ) {
+ return null;
+ }
+ break;
+
+ case "'":
+ token.type = "literal";
+ current = current.replace( /''/, "'" );
+ if ( length > 2 ) {
+ current = current.slice( 1, -1 );
+ }
+ tokenRe = new RegExp( regexpEscape( current ) );
+ break;
+
+ default:
+ token.type = "literal";
+ tokenRe = /./;
+ }
+
+ if ( !tokenRe ) {
+ return false;
+ }
+
+ // Get lexeme and consume it.
+ value = value.replace( new RegExp( "^" + tokenRe.source ), function( lexeme ) {
+ token.lexeme = lexeme;
+ if ( numeric ) {
+ token.value = numberParser( lexeme );
+ }
+ return "";
+ });
+
+ if ( !token.lexeme ) {
+ return false;
+ }
+
+ tokens.push( token );
+ return true;
+ });
+
+ return valid ? tokens : [];
+};
+
+
+
+
+var dateParserFn = function( numberParser, parseProperties, tokenizerProperties ) {
+ return function dateParser( value ) {
+ var tokens;
+
+ validateParameterPresence( value, "value" );
+ validateParameterTypeString( value, "value" );
+
+ tokens = dateTokenizer( value, numberParser, tokenizerProperties );
+ return dateParse( value, tokens, parseProperties ) || null;
+ };
+};
+
+
+
+
+Globalize._dateFormatterFn = dateFormatterFn;
+Globalize._dateParserFn = dateParserFn;
+Globalize._dateFormat = dateFormat;
+Globalize._dateParser = dateParse;
+Globalize._dateTokenizer = dateTokenizer;
+Globalize._validateParameterTypeDate = validateParameterTypeDate;
+
+Globalize.dateFormatter =
+Globalize.prototype.dateFormatter = function( options ) {
+ options = options || { skeleton: "yMd" };
+ return Globalize[ runtimeKey( "dateFormatter", this._locale, [ options ] ) ];
+};
+
+Globalize.dateParser =
+Globalize.prototype.dateParser = function( options ) {
+ options = options || { skeleton: "yMd" };
+ return Globalize[ runtimeKey( "dateParser", this._locale, [ options ] ) ];
+};
+
+Globalize.formatDate =
+Globalize.prototype.formatDate = function( value, options ) {
+ validateParameterPresence( value, "value" );
+ validateParameterTypeDate( value, "value" );
+
+ return this.dateFormatter( options )( value );
+};
+
+Globalize.parseDate =
+Globalize.prototype.parseDate = function( value, options ) {
+ validateParameterPresence( value, "value" );
+ validateParameterTypeString( value, "value" );
+
+ return this.dateParser( options )( value );
+};
+
+return Globalize;
+
+
+
+
+}));
diff --git a/external/globalize/globalize-runtime/number.js b/external/globalize/globalize-runtime/number.js
new file mode 100644
index 000000000..b3031471a
--- /dev/null
+++ b/external/globalize/globalize-runtime/number.js
@@ -0,0 +1,682 @@
+/**
+ * Globalize Runtime v1.1.0-rc.6
+ *
+ * http://github.com/jquery/globalize
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-11-18T10:38Z
+ */
+/*!
+ * Globalize Runtime v1.1.0-rc.6 2015-11-18T10:38Z Released under the MIT license
+ * http://git.io/TrdQbw
+ */
+(function( root, factory ) {
+
+ // UMD returnExports
+ if ( typeof define === "function" && define.amd ) {
+
+ // AMD
+ define([
+ "../globalize-runtime"
+ ], factory );
+ } else if ( typeof exports === "object" ) {
+
+ // Node, CommonJS
+ module.exports = factory( require( "../globalize-runtime" ) );
+ } else {
+
+ // Extend global
+ factory( root.Globalize );
+ }
+}(this, function( Globalize ) {
+
+var createError = Globalize._createError,
+ regexpEscape = Globalize._regexpEscape,
+ runtimeKey = Globalize._runtimeKey,
+ stringPad = Globalize._stringPad,
+ validateParameterType = Globalize._validateParameterType,
+ validateParameterPresence = Globalize._validateParameterPresence,
+ validateParameterTypeString = Globalize._validateParameterTypeString;
+
+
+var createErrorUnsupportedFeature = function( feature ) {
+ return createError( "E_UNSUPPORTED", "Unsupported {feature}.", {
+ feature: feature
+ });
+};
+
+
+
+
+var validateParameterTypeNumber = function( value, name ) {
+ validateParameterType(
+ value,
+ name,
+ value === undefined || typeof value === "number",
+ "Number"
+ );
+};
+
+
+
+
+/**
+ * goupingSeparator( number, primaryGroupingSize, secondaryGroupingSize )
+ *
+ * @number [Number].
+ *
+ * @primaryGroupingSize [Number]
+ *
+ * @secondaryGroupingSize [Number]
+ *
+ * Return the formatted number with group separator.
+ */
+var numberFormatGroupingSeparator = function( number, primaryGroupingSize, secondaryGroupingSize ) {
+ var index,
+ currentGroupingSize = primaryGroupingSize,
+ ret = "",
+ sep = ",",
+ switchToSecondary = secondaryGroupingSize ? true : false;
+
+ number = String( number ).split( "." );
+ index = number[ 0 ].length;
+
+ while ( index > currentGroupingSize ) {
+ ret = number[ 0 ].slice( index - currentGroupingSize, index ) +
+ ( ret.length ? sep : "" ) + ret;
+ index -= currentGroupingSize;
+ if ( switchToSecondary ) {
+ currentGroupingSize = secondaryGroupingSize;
+ switchToSecondary = false;
+ }
+ }
+
+ number[ 0 ] = number[ 0 ].slice( 0, index ) + ( ret.length ? sep : "" ) + ret;
+ return number.join( "." );
+};
+
+
+
+
+/**
+ * integerFractionDigits( number, minimumIntegerDigits, minimumFractionDigits,
+ * maximumFractionDigits, round, roundIncrement )
+ *
+ * @number [Number]
+ *
+ * @minimumIntegerDigits [Number]
+ *
+ * @minimumFractionDigits [Number]
+ *
+ * @maximumFractionDigits [Number]
+ *
+ * @round [Function]
+ *
+ * @roundIncrement [Function]
+ *
+ * Return the formatted integer and fraction digits.
+ */
+var numberFormatIntegerFractionDigits = function( number, minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits, round,
+ roundIncrement ) {
+
+ // Fraction
+ if ( maximumFractionDigits ) {
+
+ // Rounding
+ if ( roundIncrement ) {
+ number = round( number, roundIncrement );
+
+ // Maximum fraction digits
+ } else {
+ number = round( number, { exponent: -maximumFractionDigits } );
+ }
+
+ // Minimum fraction digits
+ if ( minimumFractionDigits ) {
+ number = String( number ).split( "." );
+ number[ 1 ] = stringPad( number[ 1 ] || "", minimumFractionDigits, true );
+ number = number.join( "." );
+ }
+ } else {
+ number = round( number );
+ }
+
+ number = String( number );
+
+ // Minimum integer digits
+ if ( minimumIntegerDigits ) {
+ number = number.split( "." );
+ number[ 0 ] = stringPad( number[ 0 ], minimumIntegerDigits );
+ number = number.join( "." );
+ }
+
+ return number;
+};
+
+
+
+
+/**
+ * toPrecision( number, precision, round )
+ *
+ * @number (Number)
+ *
+ * @precision (Number) significant figures precision (not decimal precision).
+ *
+ * @round (Function)
+ *
+ * Return number.toPrecision( precision ) using the given round function.
+ */
+var numberToPrecision = function( number, precision, round ) {
+ var roundOrder;
+
+ // Get number at two extra significant figure precision.
+ number = number.toPrecision( precision + 2 );
+
+ // Then, round it to the required significant figure precision.
+ roundOrder = Math.ceil( Math.log( Math.abs( number ) ) / Math.log( 10 ) );
+ roundOrder -= precision;
+
+ return round( number, { exponent: roundOrder } );
+};
+
+
+
+
+/**
+ * toPrecision( number, minimumSignificantDigits, maximumSignificantDigits, round )
+ *
+ * @number [Number]
+ *
+ * @minimumSignificantDigits [Number]
+ *
+ * @maximumSignificantDigits [Number]
+ *
+ * @round [Function]
+ *
+ * Return the formatted significant digits number.
+ */
+var numberFormatSignificantDigits = function( number, minimumSignificantDigits, maximumSignificantDigits, round ) {
+ var atMinimum, atMaximum;
+
+ // Sanity check.
+ if ( minimumSignificantDigits > maximumSignificantDigits ) {
+ maximumSignificantDigits = minimumSignificantDigits;
+ }
+
+ atMinimum = numberToPrecision( number, minimumSignificantDigits, round );
+ atMaximum = numberToPrecision( number, maximumSignificantDigits, round );
+
+ // Use atMaximum only if it has more significant digits than atMinimum.
+ number = +atMinimum === +atMaximum ? atMinimum : atMaximum;
+
+ // Expand integer numbers, eg. 123e5 to 12300.
+ number = ( +number ).toString( 10 );
+
+ if ( ( /e/ ).test( number ) ) {
+ throw createErrorUnsupportedFeature({
+ feature: "integers out of (1e21, 1e-7)"
+ });
+ }
+
+ // Add trailing zeros if necessary.
+ if ( minimumSignificantDigits - number.replace( /^0+|\./g, "" ).length > 0 ) {
+ number = number.split( "." );
+ number[ 1 ] = stringPad( number[ 1 ] || "", minimumSignificantDigits - number[ 0 ].replace( /^0+/, "" ).length, true );
+ number = number.join( "." );
+ }
+
+ return number;
+};
+
+
+
+
+/**
+ * format( number, properties )
+ *
+ * @number [Number].
+ *
+ * @properties [Object] Output of number/format-properties.
+ *
+ * Return the formatted number.
+ * ref: http://www.unicode.org/reports/tr35/tr35-numbers.html
+ */
+var numberFormat = function( number, properties ) {
+ var infinitySymbol, maximumFractionDigits, maximumSignificantDigits, minimumFractionDigits,
+ minimumIntegerDigits, minimumSignificantDigits, nanSymbol, nuDigitsMap, padding, prefix,
+ primaryGroupingSize, pattern, ret, round, roundIncrement, secondaryGroupingSize, suffix,
+ symbolMap;
+
+ padding = properties[ 1 ];
+ minimumIntegerDigits = properties[ 2 ];
+ minimumFractionDigits = properties[ 3 ];
+ maximumFractionDigits = properties[ 4 ];
+ minimumSignificantDigits = properties[ 5 ];
+ maximumSignificantDigits = properties[ 6 ];
+ roundIncrement = properties[ 7 ];
+ primaryGroupingSize = properties[ 8 ];
+ secondaryGroupingSize = properties[ 9 ];
+ round = properties[ 15 ];
+ infinitySymbol = properties[ 16 ];
+ nanSymbol = properties[ 17 ];
+ symbolMap = properties[ 18 ];
+ nuDigitsMap = properties[ 19 ];
+
+ // NaN
+ if ( isNaN( number ) ) {
+ return nanSymbol;
+ }
+
+ if ( number < 0 ) {
+ pattern = properties[ 12 ];
+ prefix = properties[ 13 ];
+ suffix = properties[ 14 ];
+ } else {
+ pattern = properties[ 11 ];
+ prefix = properties[ 0 ];
+ suffix = properties[ 10 ];
+ }
+
+ // Infinity
+ if ( !isFinite( number ) ) {
+ return prefix + infinitySymbol + suffix;
+ }
+
+ ret = prefix;
+
+ // Percent
+ if ( pattern.indexOf( "%" ) !== -1 ) {
+ number *= 100;
+
+ // Per mille
+ } else if ( pattern.indexOf( "\u2030" ) !== -1 ) {
+ number *= 1000;
+ }
+
+ // Significant digit format
+ if ( !isNaN( minimumSignificantDigits * maximumSignificantDigits ) ) {
+ number = numberFormatSignificantDigits( number, minimumSignificantDigits,
+ maximumSignificantDigits, round );
+
+ // Integer and fractional format
+ } else {
+ number = numberFormatIntegerFractionDigits( number, minimumIntegerDigits,
+ minimumFractionDigits, maximumFractionDigits, round, roundIncrement );
+ }
+
+ // Remove the possible number minus sign
+ number = number.replace( /^-/, "" );
+
+ // Grouping separators
+ if ( primaryGroupingSize ) {
+ number = numberFormatGroupingSeparator( number, primaryGroupingSize,
+ secondaryGroupingSize );
+ }
+
+ ret += number;
+
+ // Scientific notation
+ // TODO implement here
+
+ // Padding/'([^']|'')+'|''|[.,\-+E%\u2030]/g
+ // TODO implement here
+
+ ret += suffix;
+
+ return ret.replace( /('([^']|'')+'|'')|./g, function( character, literal ) {
+
+ // Literals
+ if ( literal ) {
+ literal = literal.replace( /''/, "'" );
+ if ( literal.length > 2 ) {
+ literal = literal.slice( 1, -1 );
+ }
+ return literal;
+ }
+
+ // Symbols
+ character = character.replace( /[.,\-+E%\u2030]/, function( symbol ) {
+ return symbolMap[ symbol ];
+ });
+
+ // Numbering system
+ if ( nuDigitsMap ) {
+ character = character.replace( /[0-9]/, function( digit ) {
+ return nuDigitsMap[ +digit ];
+ });
+ }
+
+ return character;
+ });
+};
+
+
+
+
+var numberFormatterFn = function( properties ) {
+ return function numberFormatter( value ) {
+ validateParameterPresence( value, "value" );
+ validateParameterTypeNumber( value, "value" );
+
+ return numberFormat( value, properties );
+ };
+};
+
+
+
+
+/**
+ * EBNF representation:
+ *
+ * number_pattern_re = prefix_including_padding?
+ * number
+ * scientific_notation?
+ * suffix?
+ *
+ * number = integer_including_group_separator fraction_including_decimal_separator
+ *
+ * integer_including_group_separator =
+ * regexp([0-9,]*[0-9]+)
+ *
+ * fraction_including_decimal_separator =
+ * regexp((\.[0-9]+)?)
+
+ * prefix_including_padding = non_number_stuff
+ *
+ * scientific_notation = regexp(E[+-]?[0-9]+)
+ *
+ * suffix = non_number_stuff
+ *
+ * non_number_stuff = regexp([^0-9]*)
+ *
+ *
+ * Regexp groups:
+ *
+ * 0: number_pattern_re
+ * 1: prefix
+ * 2: integer_including_group_separator fraction_including_decimal_separator
+ * 3: integer_including_group_separator
+ * 4: fraction_including_decimal_separator
+ * 5: scientific_notation
+ * 6: suffix
+ */
+var numberNumberRe = ( /^([^0-9]*)(([0-9,]*[0-9]+)(\.[0-9]+)?)(E[+-]?[0-9]+)?([^0-9]*)$/ );
+
+
+
+
+/**
+ * parse( value, properties )
+ *
+ * @value [String].
+ *
+ * @properties [Object] Parser properties is a reduced pre-processed cldr
+ * data set returned by numberParserProperties().
+ *
+ * Return the parsed Number (including Infinity) or NaN when value is invalid.
+ * ref: http://www.unicode.org/reports/tr35/tr35-numbers.html
+ */
+var numberParse = function( value, properties ) {
+ var aux, infinitySymbol, invertedNuDigitsMap, invertedSymbolMap, localizedDigitRe,
+ localizedSymbolsRe, negativePrefix, negativeSuffix, number, prefix, suffix;
+
+ infinitySymbol = properties[ 0 ];
+ invertedSymbolMap = properties[ 1 ];
+ negativePrefix = properties[ 2 ];
+ negativeSuffix = properties[ 3 ];
+ invertedNuDigitsMap = properties[ 4 ];
+
+ // Infinite number.
+ if ( aux = value.match( infinitySymbol ) ) {
+
+ number = Infinity;
+ prefix = value.slice( 0, aux.length );
+ suffix = value.slice( aux.length + 1 );
+
+ // Finite number.
+ } else {
+
+ // TODO: Create it during setup, i.e., make it a property.
+ localizedSymbolsRe = new RegExp(
+ Object.keys( invertedSymbolMap ).map(function( localizedSymbol ) {
+ return regexpEscape( localizedSymbol );
+ }).join( "|" ),
+ "g"
+ );
+
+ // Reverse localized symbols.
+ value = value.replace( localizedSymbolsRe, function( localizedSymbol ) {
+ return invertedSymbolMap[ localizedSymbol ];
+ });
+
+ // Reverse localized numbering system.
+ if ( invertedNuDigitsMap ) {
+
+ // TODO: Create it during setup, i.e., make it a property.
+ localizedDigitRe = new RegExp(
+ Object.keys( invertedNuDigitsMap ).map(function( localizedDigit ) {
+ return regexpEscape( localizedDigit );
+ }).join( "|" ),
+ "g"
+ );
+ value = value.replace( localizedDigitRe, function( localizedDigit ) {
+ return invertedNuDigitsMap[ localizedDigit ];
+ });
+ }
+
+ // Add padding zero to leading decimal.
+ if ( value.charAt( 0 ) === "." ) {
+ value = "0" + value;
+ }
+
+ // Is it a valid number?
+ value = value.match( numberNumberRe );
+ if ( !value ) {
+
+ // Invalid number.
+ return NaN;
+ }
+
+ prefix = value[ 1 ];
+ suffix = value[ 6 ];
+
+ // Remove grouping separators.
+ number = value[ 2 ].replace( /,/g, "" );
+
+ // Scientific notation
+ if ( value[ 5 ] ) {
+ number += value[ 5 ];
+ }
+
+ number = +number;
+
+ // Is it a valid number?
+ if ( isNaN( number ) ) {
+
+ // Invalid number.
+ return NaN;
+ }
+
+ // Percent
+ if ( value[ 0 ].indexOf( "%" ) !== -1 ) {
+ number /= 100;
+ suffix = suffix.replace( "%", "" );
+
+ // Per mille
+ } else if ( value[ 0 ].indexOf( "\u2030" ) !== -1 ) {
+ number /= 1000;
+ suffix = suffix.replace( "\u2030", "" );
+ }
+ }
+
+ // Negative number
+ // "If there is an explicit negative subpattern, it serves only to specify the negative prefix
+ // and suffix. If there is no explicit negative subpattern, the negative subpattern is the
+ // localized minus sign prefixed to the positive subpattern" UTS#35
+ if ( prefix === negativePrefix && suffix === negativeSuffix ) {
+ number *= -1;
+ }
+
+ return number;
+};
+
+
+
+
+var numberParserFn = function( properties ) {
+ return function numberParser( value ) {
+ validateParameterPresence( value, "value" );
+ validateParameterTypeString( value, "value" );
+
+ return numberParse( value, properties );
+ };
+
+};
+
+
+
+
+var numberTruncate = function( value ) {
+ if ( isNaN( value ) ) {
+ return NaN;
+ }
+ return Math[ value < 0 ? "ceil" : "floor" ]( value );
+};
+
+
+
+
+/**
+ * round( method )
+ *
+ * @method [String] with either "round", "ceil", "floor", or "truncate".
+ *
+ * Return function( value, incrementOrExp ):
+ *
+ * @value [Number] eg. 123.45.
+ *
+ * @incrementOrExp [Number] optional, eg. 0.1; or
+ * [Object] Either { increment: <value> } or { exponent: <value> }
+ *
+ * Return the rounded number, eg:
+ * - round( "round" )( 123.45 ): 123;
+ * - round( "ceil" )( 123.45 ): 124;
+ * - round( "floor" )( 123.45 ): 123;
+ * - round( "truncate" )( 123.45 ): 123;
+ * - round( "round" )( 123.45, 0.1 ): 123.5;
+ * - round( "round" )( 123.45, 10 ): 120;
+ *
+ * Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
+ * Ref: #376
+ */
+var numberRound = function( method ) {
+ method = method || "round";
+ method = method === "truncate" ? numberTruncate : Math[ method ];
+
+ return function( value, incrementOrExp ) {
+ var exp, increment;
+
+ value = +value;
+
+ // If the value is not a number, return NaN.
+ if ( isNaN( value ) ) {
+ return NaN;
+ }
+
+ // Exponent given.
+ if ( typeof incrementOrExp === "object" && incrementOrExp.exponent ) {
+ exp = +incrementOrExp.exponent;
+ increment = 1;
+
+ if ( exp === 0 ) {
+ return method( value );
+ }
+
+ // If the exp is not an integer, return NaN.
+ if ( !( typeof exp === "number" && exp % 1 === 0 ) ) {
+ return NaN;
+ }
+
+ // Increment given.
+ } else {
+ increment = +incrementOrExp || 1;
+
+ if ( increment === 1 ) {
+ return method( value );
+ }
+
+ // If the increment is not a number, return NaN.
+ if ( isNaN( increment ) ) {
+ return NaN;
+ }
+
+ increment = increment.toExponential().split( "e" );
+ exp = +increment[ 1 ];
+ increment = +increment[ 0 ];
+ }
+
+ // Shift & Round
+ value = value.toString().split( "e" );
+ value[ 0 ] = +value[ 0 ] / increment;
+ value[ 1 ] = value[ 1 ] ? ( +value[ 1 ] - exp ) : -exp;
+ value = method( +( value[ 0 ] + "e" + value[ 1 ] ) );
+
+ // Shift back
+ value = value.toString().split( "e" );
+ value[ 0 ] = +value[ 0 ] * increment;
+ value[ 1 ] = value[ 1 ] ? ( +value[ 1 ] + exp ) : exp;
+ return +( value[ 0 ] + "e" + value[ 1 ] );
+ };
+};
+
+
+
+
+Globalize._createErrorUnsupportedFeature = createErrorUnsupportedFeature;
+Globalize._numberFormat = numberFormat;
+Globalize._numberFormatterFn = numberFormatterFn;
+Globalize._numberParse = numberParse;
+Globalize._numberParserFn = numberParserFn;
+Globalize._numberRound = numberRound;
+Globalize._validateParameterPresence = validateParameterPresence;
+Globalize._validateParameterTypeNumber = validateParameterTypeNumber;
+Globalize._validateParameterTypeString = validateParameterTypeString;
+
+Globalize.numberFormatter =
+Globalize.prototype.numberFormatter = function( options ) {
+ options = options || {};
+ return Globalize[ runtimeKey( "numberFormatter", this._locale, [ options ] ) ];
+};
+
+Globalize.numberParser =
+Globalize.prototype.numberParser = function( options ) {
+ options = options || {};
+ return Globalize[ runtimeKey( "numberParser", this._locale, [ options ] ) ];
+};
+
+Globalize.formatNumber =
+Globalize.prototype.formatNumber = function( value, options ) {
+ validateParameterPresence( value, "value" );
+ validateParameterTypeNumber( value, "value" );
+
+ return this.numberFormatter( options )( value );
+};
+
+Globalize.parseNumber =
+Globalize.prototype.parseNumber = function( value, options ) {
+ validateParameterPresence( value, "value" );
+ validateParameterTypeString( value, "value" );
+
+ return this.numberParser( options )( value );
+};
+
+return Globalize;
+
+
+
+
+}));
diff --git a/external/globalize/globalize.js b/external/globalize/globalize.js
index a38a32625..2f744ac64 100644
--- a/external/globalize/globalize.js
+++ b/external/globalize/globalize.js
@@ -1,1585 +1,355 @@
-/*!
- * Globalize
+/**
+ * Globalize v1.0.0
*
* http://github.com/jquery/globalize
*
- * Copyright Software Freedom Conservancy, Inc.
- * Dual licensed under the MIT or GPL Version 2 licenses.
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
* http://jquery.org/license
+ *
+ * Date: 2015-04-23T12:02Z
*/
+/*!
+ * Globalize v1.0.0 2015-04-23T12:02Z Released under the MIT license
+ * http://git.io/TrdQbw
+ */
+(function( root, factory ) {
-(function( window, undefined ) {
-
-var Globalize,
- // private variables
- regexHex,
- regexInfinity,
- regexParseFloat,
- regexTrim,
- // private JavaScript utility functions
- arrayIndexOf,
- endsWith,
- extend,
- isArray,
- isFunction,
- isObject,
- startsWith,
- trim,
- truncate,
- zeroPad,
- // private Globalization utility functions
- appendPreOrPostMatch,
- expandFormat,
- formatDate,
- formatNumber,
- getTokenRegExp,
- getEra,
- getEraYear,
- parseExact,
- parseNegativePattern;
-
-// Global variable (Globalize) or CommonJS module (globalize)
-Globalize = function( cultureSelector ) {
- return new Globalize.prototype.init( cultureSelector );
-};
-
-if ( typeof require !== "undefined" &&
- typeof exports !== "undefined" &&
- typeof module !== "undefined" ) {
- // Assume CommonJS
- module.exports = Globalize;
-} else {
- // Export as global variable
- window.Globalize = Globalize;
-}
+ // UMD returnExports
+ if ( typeof define === "function" && define.amd ) {
-Globalize.cultures = {};
+ // AMD
+ define([
+ "cldr",
+ "cldr/event"
+ ], factory );
+ } else if ( typeof exports === "object" ) {
-Globalize.prototype = {
- constructor: Globalize,
- init: function( cultureSelector ) {
- this.cultures = Globalize.cultures;
- this.cultureSelector = cultureSelector;
+ // Node, CommonJS
+ module.exports = factory( require( "cldrjs" ) );
+ } else {
- return this;
+ // Global
+ root.Globalize = factory( root.Cldr );
}
-};
-Globalize.prototype.init.prototype = Globalize.prototype;
-
-// 1. When defining a culture, all fields are required except the ones stated as optional.
-// 2. Each culture should have a ".calendars" object with at least one calendar named "standard"
-// which serves as the default calendar in use by that culture.
-// 3. Each culture should have a ".calendar" object which is the current calendar being used,
-// it may be dynamically changed at any time to one of the calendars in ".calendars".
-Globalize.cultures[ "default" ] = {
- // A unique name for the culture in the form <language code>-<country/region code>
- name: "en",
- // the name of the culture in the english language
- englishName: "English",
- // the name of the culture in its own language
- nativeName: "English",
- // whether the culture uses right-to-left text
- isRTL: false,
- // "language" is used for so-called "specific" cultures.
- // For example, the culture "es-CL" means "Spanish, in Chili".
- // It represents the Spanish-speaking culture as it is in Chili,
- // which might have different formatting rules or even translations
- // than Spanish in Spain. A "neutral" culture is one that is not
- // specific to a region. For example, the culture "es" is the generic
- // Spanish culture, which may be a more generalized version of the language
- // that may or may not be what a specific culture expects.
- // For a specific culture like "es-CL", the "language" field refers to the
- // neutral, generic culture information for the language it is using.
- // This is not always a simple matter of the string before the dash.
- // For example, the "zh-Hans" culture is netural (Simplified Chinese).
- // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage
- // field is "zh-CHS", not "zh".
- // This field should be used to navigate from a specific culture to it's
- // more general, neutral culture. If a culture is already as general as it
- // can get, the language may refer to itself.
- language: "en",
- // numberFormat defines general number formatting rules, like the digits in
- // each grouping, the group separator, and how negative numbers are displayed.
- numberFormat: {
- // [negativePattern]
- // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency,
- // but is still defined as an array for consistency with them.
- // negativePattern: one of "(n)|-n|- n|n-|n -"
- pattern: [ "-n" ],
- // number of decimal places normally shown
- decimals: 2,
- // string that separates number groups, as in 1,000,000
- ",": ",",
- // string that separates a number from the fractional portion, as in 1.99
- ".": ".",
- // array of numbers indicating the size of each number group.
- // TODO: more detailed description and example
- groupSizes: [ 3 ],
- // symbol used for positive numbers
- "+": "+",
- // symbol used for negative numbers
- "-": "-",
- // symbol used for NaN (Not-A-Number)
- "NaN": "NaN",
- // symbol used for Negative Infinity
- negativeInfinity: "-Infinity",
- // symbol used for Positive Infinity
- positiveInfinity: "Infinity",
- percent: {
- // [negativePattern, positivePattern]
- // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
- // positivePattern: one of "n %|n%|%n|% n"
- pattern: [ "-n %", "n %" ],
- // number of decimal places normally shown
- decimals: 2,
- // array of numbers indicating the size of each number group.
- // TODO: more detailed description and example
- groupSizes: [ 3 ],
- // string that separates number groups, as in 1,000,000
- ",": ",",
- // string that separates a number from the fractional portion, as in 1.99
- ".": ".",
- // symbol used to represent a percentage
- symbol: "%"
- },
- currency: {
- // [negativePattern, positivePattern]
- // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
- // positivePattern: one of "$n|n$|$ n|n $"
- pattern: [ "($n)", "$n" ],
- // number of decimal places normally shown
- decimals: 2,
- // array of numbers indicating the size of each number group.
- // TODO: more detailed description and example
- groupSizes: [ 3 ],
- // string that separates number groups, as in 1,000,000
- ",": ",",
- // string that separates a number from the fractional portion, as in 1.99
- ".": ".",
- // symbol used to represent currency
- symbol: "$"
- }
- },
- // calendars defines all the possible calendars used by this culture.
- // There should be at least one defined with name "standard", and is the default
- // calendar used by the culture.
- // A calendar contains information about how dates are formatted, information about
- // the calendar's eras, a standard set of the date formats,
- // translations for day and month names, and if the calendar is not based on the Gregorian
- // calendar, conversion functions to and from the Gregorian calendar.
- calendars: {
- standard: {
- // name that identifies the type of calendar this is
- name: "Gregorian_USEnglish",
- // separator of parts of a date (e.g. "/" in 11/05/1955)
- "/": "/",
- // separator of parts of a time (e.g. ":" in 05:44 PM)
- ":": ":",
- // the first day of the week (0 = Sunday, 1 = Monday, etc)
- firstDay: 0,
- days: {
- // full day names
- names: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
- // abbreviated day names
- namesAbbr: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
- // shortest day names
- namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ]
- },
- months: {
- // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar)
- names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ],
- // abbreviated month names
- namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ]
- },
- // AM and PM designators in one of these forms:
- // The usual view, and the upper and lower case versions
- // [ standard, lowercase, uppercase ]
- // The culture does not use AM or PM (likely all standard date formats use 24 hour time)
- // null
- AM: [ "AM", "am", "AM" ],
- PM: [ "PM", "pm", "PM" ],
- eras: [
- // eras in reverse chronological order.
- // name: the name of the era in this culture (e.g. A.D., C.E.)
- // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
- // offset: offset in years from gregorian calendar
- {
- "name": "A.D.",
- "start": null,
- "offset": 0
- }
- ],
- // when a two digit year is given, it will never be parsed as a four digit
- // year greater than this year (in the appropriate era for the culture)
- // Set it as a full year (e.g. 2029) or use an offset format starting from
- // the current year: "+19" would correspond to 2029 if the current year 2010.
- twoDigitYearMax: 2029,
- // set of predefined date and time patterns used by the culture
- // these represent the format someone in this culture would expect
- // to see given the portions of the date that are shown.
- patterns: {
- // short date pattern
- d: "M/d/yyyy",
- // long date pattern
- D: "dddd, MMMM dd, yyyy",
- // short time pattern
- t: "h:mm tt",
- // long time pattern
- T: "h:mm:ss tt",
- // long date, short time pattern
- f: "dddd, MMMM dd, yyyy h:mm tt",
- // long date, long time pattern
- F: "dddd, MMMM dd, yyyy h:mm:ss tt",
- // month/day pattern
- M: "MMMM dd",
- // month/year pattern
- Y: "yyyy MMMM",
- // S is a sortable format that does not vary by culture
- S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
- }
- // optional fields for each calendar:
- /*
- monthsGenitive:
- Same as months but used when the day preceeds the month.
- Omit if the culture has no genitive distinction in month names.
- For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx
- convert:
- Allows for the support of non-gregorian based calendars. This convert object is used to
- to convert a date to and from a gregorian calendar date to handle parsing and formatting.
- The two functions:
- fromGregorian( date )
- Given the date as a parameter, return an array with parts [ year, month, day ]
- corresponding to the non-gregorian based year, month, and day for the calendar.
- toGregorian( year, month, day )
- Given the non-gregorian year, month, and day, return a new Date() object
- set to the corresponding date in the gregorian calendar.
- */
- }
- },
- // For localized strings
- messages: {}
-};
+}( this, function( Cldr ) {
-Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard;
-Globalize.cultures.en = Globalize.cultures[ "default" ];
+/**
+ * A toString method that outputs meaningful values for objects or arrays and
+ * still performs as fast as a plain string in case variable is string, or as
+ * fast as `"" + number` in case variable is a number.
+ * Ref: http://jsperf.com/my-stringify
+ */
+var toString = function( variable ) {
+ return typeof variable === "string" ? variable : ( typeof variable === "number" ? "" +
+ variable : JSON.stringify( variable ) );
+};
-Globalize.cultureSelector = "en";
-//
-// private variables
-//
-regexHex = /^0x[a-f0-9]+$/i;
-regexInfinity = /^[+\-]?infinity$/i;
-regexParseFloat = /^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/;
-regexTrim = /^\s+|\s+$/g;
-//
-// private JavaScript utility functions
-//
+/**
+ * formatMessage( message, data )
+ *
+ * @message [String] A message with optional {vars} to be replaced.
+ *
+ * @data [Array or JSON] Object with replacing-variables content.
+ *
+ * Return the formatted message. For example:
+ *
+ * - formatMessage( "{0} second", [ 1 ] ); // 1 second
+ *
+ * - formatMessage( "{0}/{1}", ["m", "s"] ); // m/s
+ *
+ * - formatMessage( "{name} <{email}>", {
+ * name: "Foo",
+ * email: "bar@baz.qux"
+ * }); // Foo <bar@baz.qux>
+ */
+var formatMessage = function( message, data ) {
-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;
-};
+ // Replace {attribute}'s
+ message = message.replace( /{[0-9a-zA-Z-_. ]+}/g, function( name ) {
+ name = name.replace( /^{([^}]*)}$/, "$1" );
+ return toString( data[ name ] );
+ });
-endsWith = function( value, pattern ) {
- return value.substr( value.length - pattern.length ) === pattern;
+ return message;
};
-extend = function() {
- var options, name, src, copy, copyIsArray, clone,
- target = arguments[0] || {},
- i = 1,
- length = arguments.length,
- deep = false;
-
- // Handle a deep copy situation
- if ( typeof target === "boolean" ) {
- deep = target;
- target = arguments[1] || {};
- // skip the boolean and the target
- i = 2;
- }
- // Handle case when target is a string or something (possible in deep copy)
- if ( typeof target !== "object" && !isFunction(target) ) {
- target = {};
- }
- for ( ; i < length; i++ ) {
- // Only deal with non-null/undefined values
- if ( (options = arguments[ i ]) != null ) {
- // Extend the base object
- for ( name in options ) {
- src = target[ name ];
- copy = options[ name ];
-
- // Prevent never-ending loop
- if ( target === copy ) {
- continue;
- }
-
- // Recurse if we're merging plain objects or arrays
- if ( deep && copy && ( isObject(copy) || (copyIsArray = isArray(copy)) ) ) {
- if ( copyIsArray ) {
- copyIsArray = false;
- clone = src && isArray(src) ? src : [];
-
- } else {
- clone = src && isObject(src) ? src : {};
- }
-
- // Never move original objects, clone them
- target[ name ] = extend( deep, clone, copy );
-
- // Don't bring in undefined values
- } else if ( copy !== undefined ) {
- target[ name ] = copy;
- }
- }
+
+var objectExtend = function() {
+ var destination = arguments[ 0 ],
+ sources = [].slice.call( arguments, 1 );
+
+ sources.forEach(function( source ) {
+ var prop;
+ for ( prop in source ) {
+ destination[ prop ] = source[ prop ];
}
- }
+ });
- // Return the modified object
- return target;
+ return destination;
};
-isArray = Array.isArray || function( obj ) {
- return Object.prototype.toString.call( obj ) === "[object Array]";
-};
-isFunction = function( obj ) {
- return Object.prototype.toString.call( obj ) === "[object Function]";
-};
-isObject = function( obj ) {
- return Object.prototype.toString.call( obj ) === "[object Object]";
-};
-startsWith = function( value, pattern ) {
- return value.indexOf( pattern ) === 0;
-};
+var createError = function( code, message, attributes ) {
+ var error;
-trim = function( value ) {
- return ( value + "" ).replace( regexTrim, "" );
-};
+ message = code + ( message ? ": " + formatMessage( message, attributes ) : "" );
+ error = new Error( message );
+ error.code = code;
-truncate = function( value ) {
- if ( isNaN( value ) ) {
- return NaN;
- }
- return Math[ value < 0 ? "ceil" : "floor" ]( value );
-};
+ objectExtend( error, attributes );
-zeroPad = function( str, count, left ) {
- var l;
- for ( l = str.length; l < count; l += 1 ) {
- str = ( left ? ("0" + str) : (str + "0") );
- }
- return str;
+ return error;
};
-//
-// private Globalization utility functions
-//
-
-appendPreOrPostMatch = function( preMatch, strings ) {
- // appends pre- and post- token match strings while removing escaped characters.
- // Returns a single quote count which is used to determine if the token occurs
- // in a string literal.
- var quoteCount = 0,
- escaped = false;
- for ( var i = 0, il = preMatch.length; i < il; i++ ) {
- var c = preMatch.charAt( i );
- switch ( c ) {
- case "\'":
- if ( escaped ) {
- strings.push( "\'" );
- }
- else {
- quoteCount++;
- }
- escaped = false;
- break;
- case "\\":
- if ( escaped ) {
- strings.push( "\\" );
- }
- escaped = !escaped;
- break;
- default:
- strings.push( c );
- escaped = false;
- break;
- }
- }
- return quoteCount;
-};
-expandFormat = function( cal, format ) {
- // expands unspecified or single character date formats into the full pattern.
- format = format || "F";
- var pattern,
- patterns = cal.patterns,
- len = format.length;
- if ( len === 1 ) {
- pattern = patterns[ format ];
- if ( !pattern ) {
- throw "Invalid date format string \'" + format + "\'.";
- }
- format = pattern;
- }
- else if ( len === 2 && format.charAt(0) === "%" ) {
- // %X escape format -- intended as a custom format string that is only one character, not a built-in format.
- format = format.charAt( 1 );
- }
- return format;
-};
-formatDate = function( value, format, culture ) {
- var cal = culture.calendar,
- convert = cal.convert,
- ret;
-
- if ( !format || !format.length || format === "i" ) {
- if ( culture && culture.name.length ) {
- if ( convert ) {
- // non-gregorian calendar, so we cannot use built-in toLocaleString()
- ret = formatDate( value, cal.patterns.F, culture );
- }
- else {
- var eraDate = new Date( value.getTime() ),
- era = getEra( value, cal.eras );
- eraDate.setFullYear( getEraYear(value, cal, era) );
- ret = eraDate.toLocaleString();
- }
- }
- else {
- ret = value.toString();
- }
- return ret;
- }
- var eras = cal.eras,
- sortable = format === "s";
- format = expandFormat( cal, format );
-
- // Start with an empty string
- ret = [];
- var hour,
- zeros = [ "0", "00", "000" ],
- foundDay,
- checkedDay,
- dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
- quoteCount = 0,
- tokenRegExp = getTokenRegExp(),
- converted;
-
- function padZeros( num, c ) {
- var r, s = num + "";
- if ( c > 1 && s.length < c ) {
- r = ( zeros[c - 2] + s);
- return r.substr( r.length - c, c );
- }
- else {
- r = s;
- }
- return r;
+var validate = function( code, message, check, attributes ) {
+ if ( !check ) {
+ throw createError( code, message, attributes );
}
+};
- function hasDay() {
- if ( foundDay || checkedDay ) {
- return foundDay;
- }
- foundDay = dayPartRegExp.test( format );
- checkedDay = true;
- return foundDay;
- }
- function getPart( date, part ) {
- if ( converted ) {
- return converted[ part ];
- }
- switch ( part ) {
- case 0:
- return date.getFullYear();
- case 1:
- return date.getMonth();
- case 2:
- return date.getDate();
- default:
- throw "Invalid part value " + part;
- }
- }
- if ( !sortable && convert ) {
- converted = convert.fromGregorian( value );
- }
- for ( ; ; ) {
- // Save the current index
- var index = tokenRegExp.lastIndex,
- // Look for the next pattern
- ar = tokenRegExp.exec( format );
+var alwaysArray = function( stringOrArray ) {
+ return Array.isArray( stringOrArray ) ? stringOrArray : stringOrArray ? [ stringOrArray ] : [];
+};
- // Append the text before the pattern (or the end of the string if not found)
- var preMatch = format.slice( index, ar ? ar.index : format.length );
- quoteCount += appendPreOrPostMatch( preMatch, ret );
- if ( !ar ) {
- break;
- }
- // do not replace any matches that occur inside a string literal.
- if ( quoteCount % 2 ) {
- ret.push( ar[0] );
- continue;
- }
- var current = ar[ 0 ],
- clength = current.length;
-
- switch ( current ) {
- case "ddd":
- //Day of the week, as a three-letter abbreviation
- case "dddd":
- // Day of the week, using the full name
- var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names;
- ret.push( names[value.getDay()] );
- break;
- case "d":
- // Day of month, without leading zero for single-digit days
- case "dd":
- // Day of month, with leading zero for single-digit days
- foundDay = true;
- ret.push(
- padZeros( getPart(value, 2), clength )
- );
- break;
- case "MMM":
- // Month, as a three-letter abbreviation
- case "MMMM":
- // Month, using the full name
- var part = getPart( value, 1 );
- ret.push(
- ( cal.monthsGenitive && hasDay() ) ?
- ( cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] ) :
- ( cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] )
- );
- break;
- case "M":
- // Month, as digits, with no leading zero for single-digit months
- case "MM":
- // Month, as digits, with leading zero for single-digit months
- ret.push(
- padZeros( getPart(value, 1) + 1, clength )
- );
- break;
- case "y":
- // Year, as two digits, but with no leading zero for years less than 10
- case "yy":
- // Year, as two digits, with leading zero for years less than 10
- case "yyyy":
- // Year represented by four full digits
- part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra(value, eras), sortable );
- if ( clength < 4 ) {
- part = part % 100;
- }
- ret.push(
- padZeros( part, clength )
- );
- break;
- case "h":
- // Hours with no leading zero for single-digit hours, using 12-hour clock
- case "hh":
- // Hours with leading zero for single-digit hours, using 12-hour clock
- hour = value.getHours() % 12;
- if ( hour === 0 ) hour = 12;
- ret.push(
- padZeros( hour, clength )
- );
- break;
- case "H":
- // Hours with no leading zero for single-digit hours, using 24-hour clock
- case "HH":
- // Hours with leading zero for single-digit hours, using 24-hour clock
- ret.push(
- padZeros( value.getHours(), clength )
- );
- break;
- case "m":
- // Minutes with no leading zero for single-digit minutes
- case "mm":
- // Minutes with leading zero for single-digit minutes
- ret.push(
- padZeros( value.getMinutes(), clength )
- );
- break;
- case "s":
- // Seconds with no leading zero for single-digit seconds
- case "ss":
- // Seconds with leading zero for single-digit seconds
- ret.push(
- padZeros( value.getSeconds(), clength )
- );
- break;
- case "t":
- // One character am/pm indicator ("a" or "p")
- case "tt":
- // Multicharacter am/pm indicator
- part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " );
- ret.push( clength === 1 ? part.charAt(0) : part );
- break;
- case "f":
- // Deciseconds
- case "ff":
- // Centiseconds
- case "fff":
- // Milliseconds
- ret.push(
- padZeros( value.getMilliseconds(), 3 ).substr( 0, clength )
- );
- break;
- case "z":
- // Time zone offset, no leading zero
- case "zz":
- // Time zone offset with leading zero
- hour = value.getTimezoneOffset() / 60;
- ret.push(
- ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), clength )
- );
- break;
- case "zzz":
- // Time zone offset with leading zero
- hour = value.getTimezoneOffset() / 60;
- ret.push(
- ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), 2 ) +
- // Hard coded ":" separator, rather than using cal.TimeSeparator
- // Repeated here for consistency, plus ":" was already assumed in date parsing.
- ":" + padZeros( Math.abs(value.getTimezoneOffset() % 60), 2 )
- );
- break;
- case "g":
- case "gg":
- if ( cal.eras ) {
- ret.push(
- cal.eras[ getEra(value, eras) ].name
- );
- }
- break;
- case "/":
- ret.push( cal["/"] );
- break;
- default:
- throw "Invalid date format pattern \'" + current + "\'.";
- }
- }
- return ret.join( "" );
-};
+var validateCldr = function( path, value, options ) {
+ var skipBoolean;
+ options = options || {};
-// formatNumber
-(function() {
- var expandNumber;
+ skipBoolean = alwaysArray( options.skip ).some(function( pathRe ) {
+ return pathRe.test( path );
+ });
- expandNumber = function( number, precision, formatInfo ) {
- var groupSizes = formatInfo.groupSizes,
- curSize = groupSizes[ 0 ],
- curGroupIndex = 1,
- factor = Math.pow( 10, precision ),
- rounded = Math.round( number * factor ) / factor;
+ validate( "E_MISSING_CLDR", "Missing required CLDR content `{path}`.", value || skipBoolean, {
+ path: path
+ });
+};
- if ( !isFinite(rounded) ) {
- rounded = number;
- }
- number = rounded;
-
- var numberString = number+"",
- right = "",
- split = numberString.split( /e/i ),
- exponent = split.length > 1 ? parseInt( split[1], 10 ) : 0;
- numberString = split[ 0 ];
- split = numberString.split( "." );
- numberString = split[ 0 ];
- right = split.length > 1 ? split[ 1 ] : "";
-
- if ( exponent > 0 ) {
- right = zeroPad( right, exponent, false );
- numberString += right.slice( 0, exponent );
- right = right.substr( exponent );
- }
- else if ( exponent < 0 ) {
- exponent = -exponent;
- numberString = zeroPad( numberString, exponent + 1, true );
- right = numberString.slice( -exponent, numberString.length ) + right;
- numberString = numberString.slice( 0, -exponent );
- }
- if ( precision > 0 ) {
- right = formatInfo[ "." ] +
- ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) );
- }
- else {
- right = "";
- }
- var stringIndex = numberString.length - 1,
- sep = formatInfo[ "," ],
- ret = "";
- while ( stringIndex >= 0 ) {
- if ( curSize === 0 || curSize > stringIndex ) {
- return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? (sep + ret + right) : right );
- }
- ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? (sep + ret) : "" );
+var validateDefaultLocale = function( value ) {
+ validate( "E_DEFAULT_LOCALE_NOT_DEFINED", "Default locale has not been defined.",
+ value !== undefined, {} );
+};
- stringIndex -= curSize;
- if ( curGroupIndex < groupSizes.length ) {
- curSize = groupSizes[ curGroupIndex ];
- curGroupIndex++;
- }
- }
- return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right;
- };
-
- formatNumber = function( value, format, culture ) {
- if ( !isFinite(value) ) {
- if ( value === Infinity ) {
- return culture.numberFormat.positiveInfinity;
- }
- if ( value === -Infinity ) {
- return culture.numberFormat.negativeInfinity;
- }
- return culture.numberFormat.NaN;
- }
- if ( !format || format === "i" ) {
- return culture.name.length ? value.toLocaleString() : value.toString();
- }
- format = format || "D";
-
- var nf = culture.numberFormat,
- number = Math.abs( value ),
- precision = -1,
- pattern;
- if ( format.length > 1 ) precision = parseInt( format.slice(1), 10 );
-
- var current = format.charAt( 0 ).toUpperCase(),
- formatInfo;
-
- switch ( current ) {
- case "D":
- pattern = "n";
- number = truncate( number );
- if ( precision !== -1 ) {
- number = zeroPad( "" + number, precision, true );
- }
- if ( value < 0 ) number = "-" + number;
- break;
- case "N":
- formatInfo = nf;
- /* falls through */
- case "C":
- formatInfo = formatInfo || nf.currency;
- /* falls through */
- case "P":
- formatInfo = formatInfo || nf.percent;
- pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" );
- if ( precision === -1 ) precision = formatInfo.decimals;
- number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo );
- break;
- default:
- throw "Bad number format specifier: " + current;
- }
- var patternParts = /n|\$|-|%/g,
- ret = "";
- for ( ; ; ) {
- var index = patternParts.lastIndex,
- ar = patternParts.exec( pattern );
-
- ret += pattern.slice( index, ar ? ar.index : pattern.length );
-
- if ( !ar ) {
- break;
- }
-
- switch ( ar[0] ) {
- case "n":
- ret += number;
- break;
- case "$":
- ret += nf.currency.symbol;
- break;
- case "-":
- // don't make 0 negative
- if ( /[1-9]/.test(number) ) {
- ret += nf[ "-" ];
- }
- break;
- case "%":
- ret += nf.percent.symbol;
- break;
- }
- }
+var validateParameterPresence = function( value, name ) {
+ validate( "E_MISSING_PARAMETER", "Missing required parameter `{name}`.",
+ value !== undefined, { name: name });
+};
- return ret;
- };
-}());
-getTokenRegExp = function() {
- // regular expression for matching date and time tokens in format strings.
- 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);
-};
-getEra = function( date, eras ) {
- if ( !eras ) return 0;
- var start, ticks = date.getTime();
- for ( var i = 0, l = eras.length; i < l; i++ ) {
- start = eras[ i ].start;
- if ( start === null || ticks >= start ) {
- return i;
+/**
+ * range( value, name, minimum, maximum )
+ *
+ * @value [Number].
+ *
+ * @name [String] name of variable.
+ *
+ * @minimum [Number]. The lowest valid value, inclusive.
+ *
+ * @maximum [Number]. The greatest valid value, inclusive.
+ */
+var validateParameterRange = function( value, name, minimum, maximum ) {
+ validate(
+ "E_PAR_OUT_OF_RANGE",
+ "Parameter `{name}` has value `{value}` out of range [{minimum}, {maximum}].",
+ value === undefined || value >= minimum && value <= maximum,
+ {
+ maximum: maximum,
+ minimum: minimum,
+ name: name,
+ value: value
}
- }
- return 0;
+ );
};
-getEraYear = function( date, cal, era, sortable ) {
- var year = date.getFullYear();
- if ( !sortable && cal.eras ) {
- // convert normal gregorian year to era-shifted gregorian
- // year by subtracting the era offset
- year -= cal.eras[ era ].offset;
- }
- return year;
-};
-// parseExact
-(function() {
- var expandYear,
- getDayIndex,
- getMonthIndex,
- getParseRegExp,
- outOfRange,
- toUpper,
- toUpperArray;
-
- expandYear = function( cal, year ) {
- // expands 2-digit year into 4 digits.
- if ( year < 100 ) {
- var now = new Date(),
- era = getEra( now ),
- curr = getEraYear( now, cal, era ),
- twoDigitYearMax = cal.twoDigitYearMax;
- twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax;
- year += curr - ( curr % 100 );
- if ( year > twoDigitYearMax ) {
- year -= 100;
- }
- }
- return year;
- };
-
- getDayIndex = function ( cal, value, abbr ) {
- var ret,
- days = cal.days,
- upperDays = cal._upperDays;
- if ( !upperDays ) {
- cal._upperDays = upperDays = [
- toUpperArray( days.names ),
- toUpperArray( days.namesAbbr ),
- toUpperArray( days.namesShort )
- ];
- }
- value = toUpper( value );
- if ( abbr ) {
- ret = arrayIndexOf( upperDays[1], value );
- if ( ret === -1 ) {
- ret = arrayIndexOf( upperDays[2], value );
- }
- }
- else {
- ret = arrayIndexOf( upperDays[0], value );
- }
- return ret;
- };
-
- getMonthIndex = function( cal, value, abbr ) {
- var months = cal.months,
- monthsGen = cal.monthsGenitive || cal.months,
- upperMonths = cal._upperMonths,
- upperMonthsGen = cal._upperMonthsGen;
- if ( !upperMonths ) {
- cal._upperMonths = upperMonths = [
- toUpperArray( months.names ),
- toUpperArray( months.namesAbbr )
- ];
- cal._upperMonthsGen = upperMonthsGen = [
- toUpperArray( monthsGen.names ),
- toUpperArray( monthsGen.namesAbbr )
- ];
- }
- value = toUpper( value );
- var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value );
- if ( i < 0 ) {
- i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value );
- }
- return i;
- };
-
- getParseRegExp = function( cal, format ) {
- // converts a format string into a regular expression with groups that
- // can be used to extract date fields from a date string.
- // check for a cached parse regex.
- var re = cal._parseRegExp;
- if ( !re ) {
- cal._parseRegExp = re = {};
- }
- else {
- var reFormat = re[ format ];
- if ( reFormat ) {
- return reFormat;
- }
- }
- // expand single digit formats, then escape regular expression characters.
- var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ),
- regexp = [ "^" ],
- groups = [],
- index = 0,
- quoteCount = 0,
- tokenRegExp = getTokenRegExp(),
- match;
-
- // iterate through each date token found.
- while ( (match = tokenRegExp.exec(expFormat)) !== null ) {
- var preMatch = expFormat.slice( index, match.index );
- index = tokenRegExp.lastIndex;
-
- // don't replace any matches that occur inside a string literal.
- quoteCount += appendPreOrPostMatch( preMatch, regexp );
- if ( quoteCount % 2 ) {
- regexp.push( match[0] );
- continue;
- }
-
- // add a regex group for the token.
- var m = match[ 0 ],
- len = m.length,
- add;
- switch ( m ) {
- case "dddd": case "ddd":
- case "MMMM": case "MMM":
- case "gg": case "g":
- add = "(\\D+)";
- break;
- case "tt": case "t":
- add = "(\\D*)";
- break;
- case "yyyy":
- case "fff":
- case "ff":
- case "f":
- add = "(\\d{" + len + "})";
- break;
- case "dd": case "d":
- case "MM": case "M":
- case "yy": case "y":
- case "HH": case "H":
- case "hh": case "h":
- case "mm": case "m":
- case "ss": case "s":
- add = "(\\d\\d?)";
- break;
- case "zzz":
- add = "([+-]?\\d\\d?:\\d{2})";
- break;
- case "zz": case "z":
- add = "([+-]?\\d\\d?)";
- break;
- case "/":
- add = "(\\/)";
- break;
- default:
- throw "Invalid date format pattern \'" + m + "\'.";
- }
- if ( add ) {
- regexp.push( add );
- }
- groups.push( match[0] );
- }
- appendPreOrPostMatch( expFormat.slice(index), regexp );
- regexp.push( "$" );
-
- // allow whitespace to differ when matching formats.
- var regexpStr = regexp.join( "" ).replace( /\s+/g, "\\s+" ),
- parseRegExp = { "regExp": regexpStr, "groups": groups };
-
- // cache the regex for this format.
- return re[ format ] = parseRegExp;
- };
-
- outOfRange = function( value, low, high ) {
- return value < low || value > high;
- };
-
- toUpper = function( value ) {
- // "he-IL" has non-breaking space in weekday names.
- return value.split( "\u00A0" ).join( " " ).toUpperCase();
- };
-
- toUpperArray = function( arr ) {
- var results = [];
- for ( var i = 0, l = arr.length; i < l; i++ ) {
- results[ i ] = toUpper( arr[i] );
- }
- return results;
- };
-
- parseExact = function( value, format, culture ) {
- // try to parse the date string by matching against the format string
- // while using the specified culture for date field names.
- value = trim( value );
- var cal = culture.calendar,
- // convert date formats into regular expressions with groupings.
- // use the regexp to determine the input format and extract the date fields.
- parseInfo = getParseRegExp( cal, format ),
- match = new RegExp( parseInfo.regExp ).exec( value );
- if ( match === null ) {
- return null;
- }
- // found a date format that matches the input.
- var groups = parseInfo.groups,
- era = null, year = null, month = null, date = null, weekDay = null,
- hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
- pmHour = false;
- // iterate the format groups to extract and set the date fields.
- for ( var j = 0, jl = groups.length; j < jl; j++ ) {
- var matchGroup = match[ j + 1 ];
- if ( matchGroup ) {
- var current = groups[ j ],
- clength = current.length,
- matchInt = parseInt( matchGroup, 10 );
- switch ( current ) {
- case "dd": case "d":
- // Day of month.
- date = matchInt;
- // check that date is generally in valid range, also checking overflow below.
- if ( outOfRange(date, 1, 31) ) return null;
- break;
- case "MMM": case "MMMM":
- month = getMonthIndex( cal, matchGroup, clength === 3 );
- if ( outOfRange(month, 0, 11) ) return null;
- break;
- case "M": case "MM":
- // Month.
- month = matchInt - 1;
- if ( outOfRange(month, 0, 11) ) return null;
- break;
- case "y": case "yy":
- case "yyyy":
- year = clength < 4 ? expandYear( cal, matchInt ) : matchInt;
- if ( outOfRange(year, 0, 9999) ) return null;
- break;
- case "h": case "hh":
- // Hours (12-hour clock).
- hour = matchInt;
- if ( hour === 12 ) hour = 0;
- if ( outOfRange(hour, 0, 11) ) return null;
- break;
- case "H": case "HH":
- // Hours (24-hour clock).
- hour = matchInt;
- if ( outOfRange(hour, 0, 23) ) return null;
- break;
- case "m": case "mm":
- // Minutes.
- min = matchInt;
- if ( outOfRange(min, 0, 59) ) return null;
- break;
- case "s": case "ss":
- // Seconds.
- sec = matchInt;
- if ( outOfRange(sec, 0, 59) ) return null;
- break;
- case "tt": case "t":
- // AM/PM designator.
- // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of
- // the AM tokens. If not, fail the parse for this format.
- pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] );
- if (
- !pmHour && (
- !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] )
- )
- ) return null;
- break;
- case "f":
- // Deciseconds.
- case "ff":
- // Centiseconds.
- case "fff":
- // Milliseconds.
- msec = matchInt * Math.pow( 10, 3 - clength );
- if ( outOfRange(msec, 0, 999) ) return null;
- break;
- case "ddd":
- // Day of week.
- case "dddd":
- // Day of week.
- weekDay = getDayIndex( cal, matchGroup, clength === 3 );
- if ( outOfRange(weekDay, 0, 6) ) return null;
- break;
- case "zzz":
- // Time zone offset in +/- hours:min.
- var offsets = matchGroup.split( /:/ );
- if ( offsets.length !== 2 ) return null;
- hourOffset = parseInt( offsets[0], 10 );
- if ( outOfRange(hourOffset, -12, 13) ) return null;
- var minOffset = parseInt( offsets[1], 10 );
- if ( outOfRange(minOffset, 0, 59) ) return null;
- tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset );
- break;
- case "z": case "zz":
- // Time zone offset in +/- hours.
- hourOffset = matchInt;
- if ( outOfRange(hourOffset, -12, 13) ) return null;
- tzMinOffset = hourOffset * 60;
- break;
- case "g": case "gg":
- var eraName = matchGroup;
- if ( !eraName || !cal.eras ) return null;
- eraName = trim( eraName.toLowerCase() );
- for ( var i = 0, l = cal.eras.length; i < l; i++ ) {
- if ( eraName === cal.eras[i].name.toLowerCase() ) {
- era = i;
- break;
- }
- }
- // could not find an era with that name
- if ( era === null ) return null;
- break;
- }
- }
- }
- var result = new Date(), defaultYear, convert = cal.convert;
- defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear();
- if ( year === null ) {
- year = defaultYear;
- }
- else if ( cal.eras ) {
- // year must be shifted to normal gregorian year
- // but not if year was not specified, its already normal gregorian
- // per the main if clause above.
- year += cal.eras[( era || 0 )].offset;
- }
- // set default day and month to 1 and January, so if unspecified, these are the defaults
- // instead of the current day/month.
- if ( month === null ) {
- month = 0;
- }
- if ( date === null ) {
- date = 1;
- }
- // now have year, month, and date, but in the culture's calendar.
- // convert to gregorian if necessary
- if ( convert ) {
- result = convert.toGregorian( year, month, date );
- // conversion failed, must be an invalid match
- if ( result === null ) return null;
- }
- else {
- // have to set year, month and date together to avoid overflow based on current date.
- result.setFullYear( year, month, date );
- // check to see if date overflowed for specified month (only checked 1-31 above).
- if ( result.getDate() !== date ) return null;
- // invalid day of week.
- if ( weekDay !== null && result.getDay() !== weekDay ) {
- return null;
- }
- }
- // if pm designator token was found make sure the hours fit the 24-hour clock.
- if ( pmHour && hour < 12 ) {
- hour += 12;
- }
- result.setHours( hour, min, sec, msec );
- if ( tzMinOffset !== null ) {
- // adjust timezone to utc before applying local offset.
- var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() );
- // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours
- // to ensure both these fields will not exceed this range. adjustedMin will range
- // somewhere between -1440 and 1500, so we only need to split this into hours.
- result.setHours( result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60 );
+
+var validateParameterType = function( value, name, check, expected ) {
+ validate(
+ "E_INVALID_PAR_TYPE",
+ "Invalid `{name}` parameter ({value}). {expected} expected.",
+ check,
+ {
+ expected: expected,
+ name: name,
+ value: value
}
- return result;
- };
-}());
-
-parseNegativePattern = function( value, nf, negativePattern ) {
- var neg = nf[ "-" ],
- pos = nf[ "+" ],
- ret;
- switch ( negativePattern ) {
- case "n -":
- neg = " " + neg;
- pos = " " + pos;
- /* falls through */
- case "n-":
- if ( endsWith(value, neg) ) {
- ret = [ "-", value.substr(0, value.length - neg.length) ];
- }
- else if ( endsWith(value, pos) ) {
- ret = [ "+", value.substr(0, value.length - pos.length) ];
- }
- break;
- case "- n":
- neg += " ";
- pos += " ";
- /* falls through */
- case "-n":
- if ( startsWith(value, neg) ) {
- ret = [ "-", value.substr(neg.length) ];
- }
- else if ( startsWith(value, pos) ) {
- ret = [ "+", value.substr(pos.length) ];
- }
- break;
- case "(n)":
- if ( startsWith(value, "(") && endsWith(value, ")") ) {
- ret = [ "-", value.substr(1, value.length - 2) ];
- }
- break;
- }
- return ret || [ "", value ];
+ );
};
-//
-// public instance functions
-//
-Globalize.prototype.findClosestCulture = function( cultureSelector ) {
- return Globalize.findClosestCulture.call( this, cultureSelector );
-};
-Globalize.prototype.format = function( value, format, cultureSelector ) {
- return Globalize.format.call( this, value, format, cultureSelector );
-};
-Globalize.prototype.localize = function( key, cultureSelector ) {
- return Globalize.localize.call( this, key, cultureSelector );
+var validateParameterTypeLocale = function( value, name ) {
+ validateParameterType(
+ value,
+ name,
+ value === undefined || typeof value === "string" || value instanceof Cldr,
+ "String or Cldr instance"
+ );
};
-Globalize.prototype.parseInt = function( value, radix, cultureSelector ) {
- return Globalize.parseInt.call( this, value, radix, cultureSelector );
-};
-Globalize.prototype.parseFloat = function( value, radix, cultureSelector ) {
- return Globalize.parseFloat.call( this, value, radix, cultureSelector );
-};
-Globalize.prototype.culture = function( cultureSelector ) {
- return Globalize.culture.call( this, cultureSelector );
+
+/**
+ * Function inspired by jQuery Core, but reduced to our use case.
+ */
+var isPlainObject = function( obj ) {
+ return obj !== null && "" + obj === "[object Object]";
};
-//
-// public singleton functions
-//
-
-Globalize.addCultureInfo = function( cultureName, baseCultureName, info ) {
-
- var base = {},
- isNew = false;
-
- if ( typeof cultureName !== "string" ) {
- // cultureName argument is optional string. If not specified, assume info is first
- // and only argument. Specified info deep-extends current culture.
- info = cultureName;
- cultureName = this.culture().name;
- base = this.cultures[ cultureName ];
- } else if ( typeof baseCultureName !== "string" ) {
- // baseCultureName argument is optional string. If not specified, assume info is second
- // argument. Specified info deep-extends specified culture.
- // If specified culture does not exist, create by deep-extending default
- info = baseCultureName;
- isNew = ( this.cultures[ cultureName ] == null );
- base = this.cultures[ cultureName ] || this.cultures[ "default" ];
- } else {
- // cultureName and baseCultureName specified. Assume a new culture is being created
- // by deep-extending an specified base culture
- isNew = true;
- base = this.cultures[ baseCultureName ];
- }
- this.cultures[ cultureName ] = extend(true, {},
- base,
- info
+
+
+var validateParameterTypePlainObject = function( value, name ) {
+ validateParameterType(
+ value,
+ name,
+ value === undefined || isPlainObject( value ),
+ "Plain Object"
);
- // Make the standard calendar the current culture if it's a new culture
- if ( isNew ) {
- this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard;
- }
};
-Globalize.findClosestCulture = function( name ) {
- var match;
- if ( !name ) {
- return this.findClosestCulture( this.cultureSelector ) || this.cultures[ "default" ];
- }
- if ( typeof name === "string" ) {
- name = name.split( "," );
- }
- if ( isArray(name) ) {
- var lang,
- cultures = this.cultures,
- list = name,
- i, l = list.length,
- prioritized = [];
- for ( i = 0; i < l; i++ ) {
- name = trim( list[i] );
- var pri, parts = name.split( ";" );
- lang = trim( parts[0] );
- if ( parts.length === 1 ) {
- pri = 1;
- }
- else {
- name = trim( parts[1] );
- if ( name.indexOf("q=") === 0 ) {
- name = name.substr( 2 );
- pri = parseFloat( name );
- pri = isNaN( pri ) ? 0 : pri;
- }
- else {
- pri = 1;
- }
- }
- prioritized.push({ lang: lang, pri: pri });
- }
- prioritized.sort(function( a, b ) {
- if ( a.pri < b.pri ) {
- return 1;
- } else if ( a.pri > b.pri ) {
- return -1;
- }
- return 0;
- });
- // exact match
- for ( i = 0; i < l; i++ ) {
- lang = prioritized[ i ].lang;
- match = cultures[ lang ];
- if ( match ) {
- return match;
- }
- }
- // neutral language match
- for ( i = 0; i < l; i++ ) {
- lang = prioritized[ i ].lang;
- do {
- var index = lang.lastIndexOf( "-" );
- if ( index === -1 ) {
- break;
- }
- // strip off the last part. e.g. en-US => en
- lang = lang.substr( 0, index );
- match = cultures[ lang ];
- if ( match ) {
- return match;
- }
- }
- while ( 1 );
- }
- // last resort: match first culture using that language
- for ( i = 0; i < l; i++ ) {
- lang = prioritized[ i ].lang;
- for ( var cultureKey in cultures ) {
- var culture = cultures[ cultureKey ];
- if ( culture.language === lang ) {
- return culture;
- }
- }
- }
- }
- else if ( typeof name === "object" ) {
- return name;
- }
- return match || null;
-};
-Globalize.format = function( value, format, cultureSelector ) {
- var culture = this.findClosestCulture( cultureSelector );
- if ( value instanceof Date ) {
- value = formatDate( value, format, culture );
- }
- else if ( typeof value === "number" ) {
- value = formatNumber( value, format, culture );
- }
- return value;
+var alwaysCldr = function( localeOrCldr ) {
+ return localeOrCldr instanceof Cldr ? localeOrCldr : new Cldr( localeOrCldr );
};
-Globalize.localize = function( key, cultureSelector ) {
- return this.findClosestCulture( cultureSelector ).messages[ key ] ||
- this.cultures[ "default" ].messages[ key ];
-};
-Globalize.parseDate = function( value, formats, culture ) {
- culture = this.findClosestCulture( culture );
- var date, prop, patterns;
- if ( formats ) {
- if ( typeof formats === "string" ) {
- formats = [ formats ];
- }
- if ( formats.length ) {
- for ( var i = 0, l = formats.length; i < l; i++ ) {
- var format = formats[ i ];
- if ( format ) {
- date = parseExact( value, format, culture );
- if ( date ) {
- break;
- }
- }
- }
- }
- } else {
- patterns = culture.calendar.patterns;
- for ( prop in patterns ) {
- date = parseExact( value, patterns[prop], culture );
- if ( date ) {
- break;
- }
- }
- }
- return date || null;
+// ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions?redirectlocale=en-US&redirectslug=JavaScript%2FGuide%2FRegular_Expressions
+var regexpEscape = function( string ) {
+ return string.replace( /([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1" );
};
-Globalize.parseInt = function( value, radix, cultureSelector ) {
- return truncate( Globalize.parseFloat(value, radix, cultureSelector) );
-};
-Globalize.parseFloat = function( value, radix, cultureSelector ) {
- // radix argument is optional
- if ( typeof radix !== "number" ) {
- cultureSelector = radix;
- radix = 10;
- }
- var culture = this.findClosestCulture( cultureSelector );
- var ret = NaN,
- nf = culture.numberFormat;
- if ( value.indexOf(culture.numberFormat.currency.symbol) > -1 ) {
- // remove currency symbol
- value = value.replace( culture.numberFormat.currency.symbol, "" );
- // replace decimal seperator
- value = value.replace( culture.numberFormat.currency["."], culture.numberFormat["."] );
+var stringPad = function( str, count, right ) {
+ var length;
+ if ( typeof str !== "string" ) {
+ str = String( str );
}
-
- //Remove percentage character from number string before parsing
- if ( value.indexOf(culture.numberFormat.percent.symbol) > -1){
- value = value.replace( culture.numberFormat.percent.symbol, "" );
+ for ( length = str.length; length < count; length += 1 ) {
+ str = ( right ? ( str + "0" ) : ( "0" + str ) );
}
+ return str;
+};
- // remove spaces: leading, trailing and between - and number. Used for negative currency pt-BR
- value = value.replace( / /g, "" );
- // allow infinity or hexidecimal
- if ( regexInfinity.test(value) ) {
- ret = parseFloat( value );
- }
- else if ( !radix && regexHex.test(value) ) {
- ret = parseInt( value, 16 );
- }
- else {
-
- // determine sign and number
- var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ),
- sign = signInfo[ 0 ],
- num = signInfo[ 1 ];
-
- // #44 - try parsing as "(n)"
- if ( sign === "" && nf.pattern[0] !== "(n)" ) {
- signInfo = parseNegativePattern( value, nf, "(n)" );
- sign = signInfo[ 0 ];
- num = signInfo[ 1 ];
- }
- // try parsing as "-n"
- if ( sign === "" && nf.pattern[0] !== "-n" ) {
- signInfo = parseNegativePattern( value, nf, "-n" );
- sign = signInfo[ 0 ];
- num = signInfo[ 1 ];
- }
- sign = sign || "+";
+function validateLikelySubtags( cldr ) {
+ cldr.once( "get", validateCldr );
+ cldr.get( "supplemental/likelySubtags" );
+}
- // determine exponent and number
- var exponent,
- intAndFraction,
- exponentPos = num.indexOf( "e" );
- if ( exponentPos < 0 ) exponentPos = num.indexOf( "E" );
- if ( exponentPos < 0 ) {
- intAndFraction = num;
- exponent = null;
- }
- else {
- intAndFraction = num.substr( 0, exponentPos );
- exponent = num.substr( exponentPos + 1 );
- }
- // determine decimal position
- var integer,
- fraction,
- decSep = nf[ "." ],
- decimalPos = intAndFraction.indexOf( decSep );
- if ( decimalPos < 0 ) {
- integer = intAndFraction;
- fraction = null;
- }
- else {
- integer = intAndFraction.substr( 0, decimalPos );
- fraction = intAndFraction.substr( decimalPos + decSep.length );
- }
- // handle groups (e.g. 1,000,000)
- var groupSep = nf[ "," ];
- integer = integer.split( groupSep ).join( "" );
- var altGroupSep = groupSep.replace( /\u00A0/g, " " );
- if ( groupSep !== altGroupSep ) {
- integer = integer.split( altGroupSep ).join( "" );
- }
- // build a natively parsable number string
- var p = sign + integer;
- if ( fraction !== null ) {
- p += "." + fraction;
- }
- if ( exponent !== null ) {
- // exponent itself may have a number patternd
- var expSignInfo = parseNegativePattern( exponent, nf, "-n" );
- p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ];
- }
- if ( regexParseFloat.test(p) ) {
- ret = parseFloat( p );
- }
+/**
+ * [new] Globalize( locale|cldr )
+ *
+ * @locale [String]
+ *
+ * @cldr [Cldr instance]
+ *
+ * Create a Globalize instance.
+ */
+function Globalize( locale ) {
+ if ( !( this instanceof Globalize ) ) {
+ return new Globalize( locale );
}
- return ret;
+
+ validateParameterPresence( locale, "locale" );
+ validateParameterTypeLocale( locale, "locale" );
+
+ this.cldr = alwaysCldr( locale );
+
+ validateLikelySubtags( this.cldr );
+}
+
+/**
+ * Globalize.load( json, ... )
+ *
+ * @json [JSON]
+ *
+ * Load resolved or unresolved cldr data.
+ * Somewhat equivalent to previous Globalize.addCultureInfo(...).
+ */
+Globalize.load = function() {
+ // validations are delegated to Cldr.load().
+ Cldr.load.apply( Cldr, arguments );
};
-Globalize.culture = function( cultureSelector ) {
- // setter
- if ( typeof cultureSelector !== "undefined" ) {
- this.cultureSelector = cultureSelector;
+/**
+ * Globalize.locale( [locale|cldr] )
+ *
+ * @locale [String]
+ *
+ * @cldr [Cldr instance]
+ *
+ * Set default Cldr instance if locale or cldr argument is passed.
+ *
+ * Return the default Cldr instance.
+ */
+Globalize.locale = function( locale ) {
+ validateParameterTypeLocale( locale, "locale" );
+
+ if ( arguments.length ) {
+ this.cldr = alwaysCldr( locale );
+ validateLikelySubtags( this.cldr );
}
- // getter
- return this.findClosestCulture( cultureSelector ) || this.cultures[ "default" ];
+ return this.cldr;
};
-}( this )); \ No newline at end of file
+/**
+ * Optimization to avoid duplicating some internal functions across modules.
+ */
+Globalize._alwaysArray = alwaysArray;
+Globalize._createError = createError;
+Globalize._formatMessage = formatMessage;
+Globalize._isPlainObject = isPlainObject;
+Globalize._objectExtend = objectExtend;
+Globalize._regexpEscape = regexpEscape;
+Globalize._stringPad = stringPad;
+Globalize._validate = validate;
+Globalize._validateCldr = validateCldr;
+Globalize._validateDefaultLocale = validateDefaultLocale;
+Globalize._validateParameterPresence = validateParameterPresence;
+Globalize._validateParameterRange = validateParameterRange;
+Globalize._validateParameterTypePlainObject = validateParameterTypePlainObject;
+Globalize._validateParameterType = validateParameterType;
+
+return Globalize;
+
+
+
+
+}));
diff --git a/external/localization.js b/external/localization.js
new file mode 100644
index 000000000..95bb3bafe
--- /dev/null
+++ b/external/localization.js
@@ -0,0 +1,115 @@
+(function( root, factory ) {
+
+ // UMD returnExports
+ if ( typeof define === "function" && define.amd ) {
+
+ // AMD
+ define( ["globalize-runtime/number","globalize-runtime/date"], factory );
+ } else if ( typeof exports === "object" ) {
+
+ // Node, CommonJS
+ module.exports = factory( require("globalize/dist/globalize-runtime/number"), require("globalize/dist/globalize-runtime/date") );
+ } else {
+
+ // Global
+ factory( root.Globalize );
+ }
+}( this, function( Globalize ) {
+
+var validateParameterTypeNumber = Globalize._validateParameterTypeNumber;
+var validateParameterPresence = Globalize._validateParameterPresence;
+var numberRound = Globalize._numberRound;
+var numberFormat = Globalize._numberFormat;
+var numberFormatterFn = Globalize._numberFormatterFn;
+var validateParameterTypeString = Globalize._validateParameterTypeString;
+var numberParse = Globalize._numberParse;
+var numberParserFn = Globalize._numberParserFn;
+var validateParameterTypeDate = Globalize._validateParameterTypeDate;
+var dateFormat = Globalize._dateFormat;
+var dateFormatterFn = Globalize._dateFormatterFn;
+var dateTokenizer = Globalize._dateTokenizer;
+var dateParse = Globalize._dateParse;
+var dateParserFn = Globalize._dateParserFn;
+
+Globalize.b376385760 = numberFormatterFn(["",,2,,,,,,,,"","00","-00","-","",numberRound(),"∞","NaN",{".":",",",":".","%":"%","+":"+","-":"-","E":"E","‰":"‰"},]);
+Globalize.a1711088039 = numberFormatterFn(["",,1,,,,,,,,"","0","-0","-","",numberRound(),"∞","NaN",{".":".",",":",","%":"%","+":"+","-":"-","E":"E","‰":"‰"},]);
+Globalize.a1916379524 = numberFormatterFn(["",,1,,,,,,,,"","0","-0","-","",numberRound(),"∞","ليس رقم",{".":"٫",",":"٬","%":"٪","+":"‏+","-":"‏-","E":"اس","‰":"؉"},"٠١٢٣٤٥٦٧٨٩"]);
+Globalize.b1256031091 = numberFormatterFn(["",,2,,,,,,,,"","00","-00","-","",numberRound(),"∞","NaN",{".":",",",":".","%":"%","+":"+","-":"-","E":"E","‰":"‰"},]);
+Globalize.b1148906457 = numberFormatterFn(["",,1,,,,,,,,"","0","-0","-","",numberRound(),"∞","NaN",{".":",",",":".","%":"%","+":"+","-":"-","E":"E","‰":"‰"},]);
+Globalize.a126395188 = numberFormatterFn(["",,1,,,,,,,,"","0","-0","-","",numberRound(),"∞","NaN",{".":",",",":".","%":"%","+":"+","-":"-","E":"E","‰":"‰"},]);
+Globalize.b203855544 = numberFormatterFn(["",,2,,,,,,,,"","00","-00","-","",numberRound(),"∞","NaN",{".":".",",":",","%":"%","+":"+","-":"-","E":"E","‰":"‰"},]);
+Globalize.a1378886668 = numberFormatterFn(["",,1,,,,,,,,"","0","-0","-","",numberRound(),"∞","NaN",{".":".",",":",","%":"%","+":"+","-":"-","E":"E","‰":"‰"},]);
+Globalize.b1961282698 = numberParserFn(["∞",{".":",",",":".","%":"%","+":"+","-":"-","E":"E","‰":"‰"},"-","",]);
+Globalize.b755631779 = numberParserFn(["∞",{".":"decimal",",":"group","%":"percentSign","+":"plusSign","-":"minusSign","E":"exponential","‰":"perMille","٫":".","٬":",","٪":"%","‏+":"+","‏-":"-","اس":"E","؉":"‰"},"-","",{"0":"invalid","1":"invalid","2":"invalid","3":"invalid","4":"invalid","5":"invalid","6":"invalid","7":"invalid","8":"invalid","9":"invalid","٠":"0","١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9"}]);
+Globalize.b2002841143 = numberParserFn(["∞",{".":",",",":".","%":"%","+":"+","-":"-","E":"E","‰":"‰"},"-","",]);
+Globalize.a1749351181 = numberParserFn(["∞",{".":",",",":".","%":"%","+":"+","-":"-","E":"E","‰":"‰"},"-","",]);
+Globalize.b960923264 = numberParserFn(["∞",{".":".",",":",","%":"%","+":"+","-":"-","E":"E","‰":"‰"},"-","",]);
+Globalize.b1965900303 = numberParserFn(["∞",{".":".",",":",","%":"%","+":"+","-":"-","E":"E","‰":"‰"},"-","",]);
+Globalize.b2076722823 = numberParserFn(["∞",{".":"decimal",",":"group","%":"percentSign","+":"plusSign","-":"minusSign","E":"exponential","‰":"perMille","٫":".","٬":",","٪":"%","‏+":"+","‏-":"-","اس":"E","؉":"‰"},"-","",{"0":"invalid","1":"invalid","2":"invalid","3":"invalid","4":"invalid","5":"invalid","6":"invalid","7":"invalid","8":"invalid","9":"invalid","٠":"0","١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9"}]);
+Globalize.b1293124635 = numberParserFn(["∞",{".":".",",":",","%":"%","+":"+","-":"-","E":"E","‰":"‰"},"-","",]);
+Globalize.a474049536 = numberParserFn(["∞",{".":",",",":".","%":"%","+":"+","-":"-","E":"E","‰":"‰"},"-","",]);
+Globalize.b1370229258 = numberParserFn(["∞",{".":".",",":",","%":"%","+":"+","-":"-","E":"E","‰":"‰"},"-","",]);
+Globalize.a946274711 = dateFormatterFn({}, {"pattern":"EEEEE","timeSeparator":":","days":{"E":{"5":{"sun":"D","mon":"L","tue":"M","wed":"X","thu":"J","fri":"V","sat":"S"}}}});
+Globalize.b1200480509 = dateFormatterFn({}, {"pattern":"MMMM","timeSeparator":":","months":{"M":{"4":{"1":"January","2":"February","3":"March","4":"April","5":"May","6":"June","7":"July","8":"August","9":"September","10":"October","11":"November","12":"December"}}}});
+Globalize.a52764931 = dateFormatterFn({}, {"pattern":"EEEE","timeSeparator":":","days":{"E":{"4":{"sun":"Sunday","mon":"Monday","tue":"Tuesday","wed":"Wednesday","thu":"Thursday","fri":"Friday","sat":"Saturday"}}}});
+Globalize.b617625506 = dateFormatterFn({"1":Globalize("en").numberFormatter({"raw":"0"})}, {"pattern":"c","timeSeparator":":","firstDay":0});
+Globalize.b93641787 = dateFormatterFn({"1":Globalize("en").numberFormatter({"raw":"0"}),"2":Globalize("en").numberFormatter({"raw":"00"})}, {"pattern":"M/d/yy","timeSeparator":":"});
+Globalize.a1636669180 = dateFormatterFn({}, {"pattern":"EEEEE","timeSeparator":":","days":{"E":{"5":{"sun":"S","mon":"M","tue":"T","wed":"W","thu":"T","fri":"F","sat":"S"}}}});
+Globalize.a491609039 = dateFormatterFn({"1":Globalize("zh").numberFormatter({"raw":"0"})}, {"pattern":"y年M月d日EEEE","timeSeparator":":","days":{"E":{"4":{"sun":"星期日","mon":"星期一","tue":"星期二","wed":"星期三","thu":"星期四","fri":"星期五","sat":"星期六"}}}});
+Globalize.b801906653 = dateFormatterFn({}, {"pattern":"EEEEEE","timeSeparator":":","days":{"E":{"6":{"sun":"Su","mon":"Mo","tue":"Tu","wed":"We","thu":"Th","fri":"Fr","sat":"Sa"}}}});
+Globalize.a218160295 = dateFormatterFn({"1":Globalize("en").numberFormatter({"raw":"0"})}, {"pattern":"MMMM d, y","timeSeparator":":","months":{"M":{"4":{"1":"January","2":"February","3":"March","4":"April","5":"May","6":"June","7":"July","8":"August","9":"September","10":"October","11":"November","12":"December"}}}});
+Globalize.a1351587010 = dateFormatterFn({"1":Globalize("zh").numberFormatter({"raw":"0"})}, {"pattern":"y年M月d日","timeSeparator":":"});
+Globalize.b641817676 = dateFormatterFn({"1":Globalize("en").numberFormatter({"raw":"0"})}, {"pattern":"EEEE, MMMM d, y","timeSeparator":":","days":{"E":{"4":{"sun":"Sunday","mon":"Monday","tue":"Tuesday","wed":"Wednesday","thu":"Thursday","fri":"Friday","sat":"Saturday"}}},"months":{"M":{"4":{"1":"January","2":"February","3":"March","4":"April","5":"May","6":"June","7":"July","8":"August","9":"September","10":"October","11":"November","12":"December"}}}});
+Globalize.a864358539 = dateFormatterFn({}, {"pattern":"EEEE","timeSeparator":":","days":{"E":{"4":{"sun":"الأحد","mon":"الاثنين","tue":"الثلاثاء","wed":"الأربعاء","thu":"الخميس","fri":"الجمعة","sat":"السبت"}}}});
+Globalize.a682848010 = dateFormatterFn({"1":Globalize("zh").numberFormatter({"raw":"0"})}, {"pattern":"y/M/d","timeSeparator":":"});
+Globalize.b1382770181 = dateFormatterFn({}, {"pattern":"EEEEEE","timeSeparator":":","days":{"E":{"6":{"sun":"So.","mon":"Mo.","tue":"Di.","wed":"Mi.","thu":"Do.","fri":"Fr.","sat":"Sa."}}}});
+Globalize.b1430109660 = dateFormatterFn({}, {"pattern":"EEEEE","timeSeparator":":","days":{"E":{"5":{"sun":"S","mon":"M","tue":"D","wed":"M","thu":"D","fri":"F","sat":"S"}}}});
+Globalize.a1754951899 = dateFormatterFn({}, {"pattern":"EEEE","timeSeparator":":","days":{"E":{"4":{"sun":"Sonntag","mon":"Montag","tue":"Dienstag","wed":"Mittwoch","thu":"Donnerstag","fri":"Freitag","sat":"Samstag"}}}});
+Globalize.a501706459 = dateFormatterFn({}, {"pattern":"MMMM","timeSeparator":":","months":{"M":{"4":{"1":"Januar","2":"Februar","3":"März","4":"April","5":"Mai","6":"Juni","7":"Juli","8":"August","9":"September","10":"Oktober","11":"November","12":"Dezember"}}}});
+Globalize.b1869521166 = dateFormatterFn({"1":Globalize("de").numberFormatter({"raw":"0"})}, {"pattern":"w","timeSeparator":":","firstDay":1,"minDays":4});
+Globalize.b1252219604 = dateFormatterFn({"1":Globalize("ar").numberFormatter({"raw":"0"})}, {"pattern":"EEEE، d MMMM، y","timeSeparator":":","days":{"E":{"4":{"sun":"الأحد","mon":"الاثنين","tue":"الثلاثاء","wed":"الأربعاء","thu":"الخميس","fri":"الجمعة","sat":"السبت"}}},"months":{"M":{"4":{"1":"يناير","2":"فبراير","3":"مارس","4":"أبريل","5":"مايو","6":"يونيو","7":"يوليو","8":"أغسطس","9":"سبتمبر","10":"أكتوبر","11":"نوفمبر","12":"ديسمبر"}}}});
+Globalize.b1870116986 = dateFormatterFn({"1":Globalize("de").numberFormatter({"raw":"0"})}, {"pattern":"c","timeSeparator":":","firstDay":1});
+Globalize.b674505315 = dateFormatterFn({"2":Globalize("de").numberFormatter({"raw":"00"})}, {"pattern":"dd.MM.yy","timeSeparator":":"});
+Globalize.a1026267252 = dateFormatterFn({}, {"pattern":"EEEEE","timeSeparator":":","days":{"E":{"5":{"sun":"ح","mon":"ن","tue":"ث","wed":"ر","thu":"خ","fri":"ج","sat":"س"}}}});
+Globalize.b285424135 = dateFormatterFn({"1":Globalize("zh").numberFormatter({"raw":"0"})}, {"pattern":"c","timeSeparator":":","firstDay":0});
+Globalize.b388886901 = dateFormatterFn({}, {"pattern":"MMMM","timeSeparator":":","months":{"M":{"4":{"1":"يناير","2":"فبراير","3":"مارس","4":"أبريل","5":"مايو","6":"يونيو","7":"يوليو","8":"أغسطس","9":"سبتمبر","10":"أكتوبر","11":"نوفمبر","12":"ديسمبر"}}}});
+Globalize.a1446348751 = dateFormatterFn({"1":Globalize("de").numberFormatter({"raw":"0"})}, {"pattern":"d. MMMM y","timeSeparator":":","months":{"M":{"4":{"1":"Januar","2":"Februar","3":"März","4":"April","5":"Mai","6":"Juni","7":"Juli","8":"August","9":"September","10":"Oktober","11":"November","12":"Dezember"}}}});
+Globalize.b284828315 = dateFormatterFn({"1":Globalize("zh").numberFormatter({"raw":"0"})}, {"pattern":"w","timeSeparator":":","firstDay":0,"minDays":1});
+Globalize.a586370780 = dateFormatterFn({"1":Globalize("de").numberFormatter({"raw":"0"})}, {"pattern":"EEEE, d. MMMM y","timeSeparator":":","days":{"E":{"4":{"sun":"Sonntag","mon":"Montag","tue":"Dienstag","wed":"Mittwoch","thu":"Donnerstag","fri":"Freitag","sat":"Samstag"}}},"months":{"M":{"4":{"1":"Januar","2":"Februar","3":"März","4":"April","5":"Mai","6":"Juni","7":"Juli","8":"August","9":"September","10":"Oktober","11":"November","12":"Dezember"}}}});
+Globalize.b194087032 = dateFormatterFn({}, {"pattern":"MMMM","timeSeparator":":","months":{"M":{"4":{"1":"一月","2":"二月","3":"三月","4":"四月","5":"五月","6":"六月","7":"七月","8":"八月","9":"九月","10":"十月","11":"十一月","12":"十二月"}}}});
+Globalize.b392241633 = dateFormatterFn({"1":Globalize("ar").numberFormatter({"raw":"0"})}, {"pattern":"d MMMM، y","timeSeparator":":","months":{"M":{"4":{"1":"يناير","2":"فبراير","3":"مارس","4":"أبريل","5":"مايو","6":"يونيو","7":"يوليو","8":"أغسطس","9":"سبتمبر","10":"أكتوبر","11":"نوفمبر","12":"ديسمبر"}}}});
+Globalize.b729298712 = dateFormatterFn({}, {"pattern":"EEEEEE","timeSeparator":":","days":{"E":{"6":{"sun":"DO","mon":"LU","tue":"MA","wed":"MI","thu":"JU","fri":"VI","sat":"SA"}}}});
+Globalize.b617029686 = dateFormatterFn({"1":Globalize("en").numberFormatter({"raw":"0"})}, {"pattern":"w","timeSeparator":":","firstDay":0,"minDays":1});
+Globalize.b1770621176 = dateFormatterFn({}, {"pattern":"EEEE","timeSeparator":":","days":{"E":{"4":{"sun":"domingo","mon":"lunes","tue":"martes","wed":"miércoles","thu":"jueves","fri":"viernes","sat":"sábado"}}}});
+Globalize.a1271100680 = dateFormatterFn({}, {"pattern":"MMMM","timeSeparator":":","months":{"M":{"4":{"1":"enero","2":"febrero","3":"marzo","4":"abril","5":"mayo","6":"junio","7":"julio","8":"agosto","9":"septiembre","10":"octubre","11":"noviembre","12":"diciembre"}}}});
+Globalize.a1150144485 = dateFormatterFn({"1":Globalize("es").numberFormatter({"raw":"0"})}, {"pattern":"w","timeSeparator":":","firstDay":1,"minDays":4});
+Globalize.a1059158408 = dateFormatterFn({}, {"pattern":"EEEE","timeSeparator":":","days":{"E":{"4":{"sun":"星期日","mon":"星期一","tue":"星期二","wed":"星期三","thu":"星期四","fri":"星期五","sat":"星期六"}}}});
+Globalize.a1149548665 = dateFormatterFn({"1":Globalize("es").numberFormatter({"raw":"0"})}, {"pattern":"c","timeSeparator":":","firstDay":1});
+Globalize.b21033846 = dateFormatterFn({"1":Globalize("es").numberFormatter({"raw":"0"}),"2":Globalize("es").numberFormatter({"raw":"00"})}, {"pattern":"d/M/yy","timeSeparator":":"});
+Globalize.b1836232371 = dateFormatterFn({"1":Globalize("ar").numberFormatter({"raw":"0"})}, {"pattern":"d‏/M‏/y","timeSeparator":":"});
+Globalize.b1524871401 = dateFormatterFn({}, {"pattern":"EEEEE","timeSeparator":":","days":{"E":{"5":{"sun":"日","mon":"一","tue":"二","wed":"三","thu":"四","fri":"五","sat":"六"}}}});
+Globalize.b80132650 = dateFormatterFn({"1":Globalize("ar").numberFormatter({"raw":"0"})}, {"pattern":"c","timeSeparator":":","firstDay":6});
+Globalize.b472234174 = dateFormatterFn({"1":Globalize("es").numberFormatter({"raw":"0"})}, {"pattern":"d 'de' MMMM 'de' y","timeSeparator":":","months":{"M":{"4":{"1":"enero","2":"febrero","3":"marzo","4":"abril","5":"mayo","6":"junio","7":"julio","8":"agosto","9":"septiembre","10":"octubre","11":"noviembre","12":"diciembre"}}}});
+Globalize.b25416856 = dateFormatterFn({}, {"pattern":"EEEEEE","timeSeparator":":","days":{"E":{"6":{"sun":"周日","mon":"周一","tue":"周二","wed":"周三","thu":"周四","fri":"周五","sat":"周六"}}}});
+Globalize.b1332212145 = dateFormatterFn({"1":Globalize("es").numberFormatter({"raw":"0"})}, {"pattern":"EEEE, d 'de' MMMM 'de' y","timeSeparator":":","days":{"E":{"4":{"sun":"domingo","mon":"lunes","tue":"martes","wed":"miércoles","thu":"jueves","fri":"viernes","sat":"sábado"}}},"months":{"M":{"4":{"1":"enero","2":"febrero","3":"marzo","4":"abril","5":"mayo","6":"junio","7":"julio","8":"agosto","9":"septiembre","10":"octubre","11":"noviembre","12":"diciembre"}}}});
+Globalize.b79536830 = dateFormatterFn({"1":Globalize("ar").numberFormatter({"raw":"0"})}, {"pattern":"w","timeSeparator":":","firstDay":6,"minDays":1});
+Globalize.a1750470059 = dateFormatterFn({}, {"pattern":"EEEEEE","timeSeparator":":","days":{"E":{"6":{"sun":"الأحد","mon":"الاثنين","tue":"الثلاثاء","wed":"الأربعاء","thu":"الخميس","fri":"الجمعة","sat":"السبت"}}}});
+Globalize.a1915997694 = dateParserFn(Globalize("es").numberParser({"raw":"0"}), {"preferredTimeData":"H"}, {"pattern":"EEEE, d 'de' MMMM 'de' y","timeSeparator":":","gregorian/days/format/wide":{"sun":"domingo","mon":"lunes","tue":"martes","wed":"miércoles","thu":"jueves","fri":"viernes","sat":"sábado"},"gregorian/months/format/wide":{"1":"enero","2":"febrero","3":"marzo","4":"abril","5":"mayo","6":"junio","7":"julio","8":"agosto","9":"septiembre","10":"octubre","11":"noviembre","12":"diciembre"}});
+Globalize.a1889223355 = dateParserFn(Globalize("es").numberParser({"raw":"0"}), {"preferredTimeData":"H"}, {"pattern":"d/M/yy","timeSeparator":":"});
+Globalize.a74024830 = dateParserFn(Globalize("ar").numberParser({"raw":"0"}), {"preferredTimeData":"h"}, {"pattern":"d‏/M‏/y","timeSeparator":":"});
+Globalize.b460386677 = dateParserFn(Globalize("de").numberParser({"raw":"0"}), {"preferredTimeData":"H"}, {"pattern":"EEEE, d. MMMM y","timeSeparator":":","gregorian/days/format/wide":{"sun":"Sonntag","mon":"Montag","tue":"Dienstag","wed":"Mittwoch","thu":"Donnerstag","fri":"Freitag","sat":"Samstag"},"gregorian/months/format/wide":{"1":"Januar","2":"Februar","3":"März","4":"April","5":"Mai","6":"Juni","7":"Juli","8":"August","9":"September","10":"Oktober","11":"November","12":"Dezember"}});
+Globalize.a399591294 = dateParserFn(Globalize("de").numberParser({"raw":"0"}), {"preferredTimeData":"H"}, {"pattern":"d. MMMM y","timeSeparator":":","gregorian/months/format/wide":{"1":"Januar","2":"Februar","3":"März","4":"April","5":"Mai","6":"Juni","7":"Juli","8":"August","9":"September","10":"Oktober","11":"November","12":"Dezember"}});
+Globalize.b1438999090 = dateParserFn(Globalize("ar").numberParser({"raw":"0"}), {"preferredTimeData":"h"}, {"pattern":"d MMMM، y","timeSeparator":":","gregorian/months/format/wide":{"1":"يناير","2":"فبراير","3":"مارس","4":"أبريل","5":"مايو","6":"يونيو","7":"يوليو","8":"أغسطس","9":"سبتمبر","10":"أكتوبر","11":"نوفمبر","12":"ديسمبر"}});
+Globalize.a1235751886 = dateParserFn(Globalize("de").numberParser({"raw":"0"}), {"preferredTimeData":"H"}, {"pattern":"dd.MM.yy","timeSeparator":":"});
+Globalize.b1518991631 = dateParserFn(Globalize("es").numberParser({"raw":"0"}), {"preferredTimeData":"H"}, {"pattern":"d 'de' MMMM 'de' y","timeSeparator":":","gregorian/months/format/wide":{"1":"enero","2":"febrero","3":"marzo","4":"abril","5":"mayo","6":"junio","7":"julio","8":"agosto","9":"septiembre","10":"octubre","11":"noviembre","12":"diciembre"}});
+Globalize.b1701862085 = dateParserFn(Globalize("zh").numberParser({"raw":"0"}), {"preferredTimeData":"h"}, {"pattern":"y/M/d","timeSeparator":":"});
+Globalize.b1688575133 = dateParserFn(Globalize("en").numberParser({"raw":"0"}), {"preferredTimeData":"h"}, {"pattern":"EEEE, MMMM d, y","timeSeparator":":","gregorian/days/format/wide":{"sun":"Sunday","mon":"Monday","tue":"Tuesday","wed":"Wednesday","thu":"Thursday","fri":"Friday","sat":"Saturday"},"gregorian/months/format/wide":{"1":"January","2":"February","3":"March","4":"April","5":"May","6":"June","7":"July","8":"August","9":"September","10":"October","11":"November","12":"December"}});
+Globalize.b828597162 = dateParserFn(Globalize("en").numberParser({"raw":"0"}), {"preferredTimeData":"h"}, {"pattern":"MMMM d, y","timeSeparator":":","gregorian/months/format/wide":{"1":"January","2":"February","3":"March","4":"April","5":"May","6":"June","7":"July","8":"August","9":"September","10":"October","11":"November","12":"December"}});
+Globalize.a304829553 = dateParserFn(Globalize("zh").numberParser({"raw":"0"}), {"preferredTimeData":"h"}, {"pattern":"y年M月d日","timeSeparator":":"});
+Globalize.a1816615414 = dateParserFn(Globalize("en").numberParser({"raw":"0"}), {"preferredTimeData":"h"}, {"pattern":"M/d/yy","timeSeparator":":"});
+Globalize.b555148418 = dateParserFn(Globalize("zh").numberParser({"raw":"0"}), {"preferredTimeData":"h"}, {"pattern":"y年M月d日EEEE","timeSeparator":":","gregorian/days/format/wide":{"sun":"星期日","mon":"星期一","tue":"星期二","wed":"星期三","thu":"星期四","fri":"星期五","sat":"星期六"}});
+Globalize.a1995990235 = dateParserFn(Globalize("ar").numberParser({"raw":"0"}), {"preferredTimeData":"h"}, {"pattern":"EEEE، d MMMM، y","timeSeparator":":","gregorian/days/format/wide":{"sun":"الأحد","mon":"الاثنين","tue":"الثلاثاء","wed":"الأربعاء","thu":"الخميس","fri":"الجمعة","sat":"السبت"},"gregorian/months/format/wide":{"1":"يناير","2":"فبراير","3":"مارس","4":"أبريل","5":"مايو","6":"يونيو","7":"يوليو","8":"أغسطس","9":"سبتمبر","10":"أكتوبر","11":"نوفمبر","12":"ديسمبر"}});
+
+return Globalize;
+
+}));