@@ -1,7 +1,8 @@
define([
"./number-re",
"../util/regexp/escape"
], function( numberNumberRe, regexpEscape ) {
"../util/regexp/cf-g",
"../util/regexp/dash-g",
"../util/regexp/zs-g"
], function( regexpCfG, regexpDashG, regexpZsG ) {

/**
* parse( value, properties )
@@ -15,103 +16,123 @@ define([
* ref: http://www.unicode.org/reports/tr35/tr35-numbers.html
*/
return 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 ];
});
var grammar, invertedNuDigitsMap, invertedSymbolMap, negative, number, prefix, prefixNSuffix,
suffix, tokenizer, valid;

// Grammar:
// - Value <= NaN | PositiveNumber | NegativeNumber
// - PositiveNumber <= PositivePrefix NumberOrInf PositiveSufix
// - NegativeNumber <= NegativePrefix NumberOrInf
// - NumberOrInf <= Number | Inf
grammar = [
[ "nan" ],
[ "prefix", "infinity", "suffix" ],
[ "prefix", "number", "suffix" ],
[ "negativePrefix", "infinity", "negativeSuffix" ],
[ "negativePrefix", "number", "negativeSuffix" ]
];

invertedSymbolMap = properties[ 0 ];
invertedNuDigitsMap = properties[ 1 ] || {};
tokenizer = properties[ 2 ];

// Loose Matching:
// - Ignore all format characters, which includes RLM, LRM or ALM used to control BIDI
// formatting.
// - Map all characters in [:Zs:] to U+0020 SPACE;
// - Map all characters in [:Dash:] to U+002D HYPHEN-MINUS;
value = value
.replace( regexpCfG, "" )
.replace( regexpDashG, "-" )
.replace( regexpZsG, " " );

function parse( type ) {
return function( lexeme ) {

// Reverse localized symbols and numbering system.
lexeme = lexeme.split( "" ).map(function( character ) {
return invertedSymbolMap[ character ] ||
invertedNuDigitsMap[ character ] ||
character;
}).join( "" );

switch ( type ) {
case "infinity":
number = Infinity;
break;

case "nan":
number = NaN;
break;

case "number":

// Remove grouping separators.
lexeme = lexeme.replace( /,/g, "" );

number = +lexeme;
break;

case "prefix":
case "negativePrefix":
prefix = lexeme;
break;

case "suffix":
suffix = lexeme;
break;

case "negativeSuffix":
suffix = lexeme;
negative = true;
break;

// This should never be reached.
default:
throw new Error( "Internal error" );
}
return "";
};
}

// 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 ];
function tokenizeNParse( _value, grammar ) {
return grammar.some(function( bar ) {
var value = _value;
bar.every(function( type ) {
if ( value.match( tokenizer[ type ] ) === null ) {
return false;
}

// Consume and parse it.
value = value.replace( tokenizer[ type ], parse( type ) );
return true;
});
}

// 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 ];
}
return !value.length;
});
}

number = +number;
valid = tokenizeNParse( value, grammar );

// Is it a valid number?
if ( isNaN( number ) ) {
// NaN
if ( !valid || isNaN( number ) ) {
return NaN;
}

// Invalid number.
return NaN;
}
// TODO make sure prefix && suffix aren't undefined.
prefixNSuffix = "" + prefix + suffix;

// Percent
if ( value[ 0 ].indexOf( "%" ) !== -1 ) {
number /= 100;
suffix = suffix.replace( "%", "" );
// Percent
if ( prefixNSuffix.indexOf( "%" ) !== -1 ) {
number /= 100;

// Per mille
} else if ( value[ 0 ].indexOf( "\u2030" ) !== -1 ) {
number /= 1000;
suffix = suffix.replace( "\u2030", "" );
}
// Per mille
} else if ( prefixNSuffix.indexOf( "\u2030" ) !== -1 ) {
number /= 1000;
}

// 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 ) {
if ( negative ) {
number *= -1;
}

@@ -25,11 +25,11 @@ return function( pattern ) {
}

prefix = pattern[ 1 ];
padding = pattern[ 3 ];
integerFractionOrSignificantPattern = pattern[ 4 ];
significantPattern = pattern[ 8 ];
scientificNotation = pattern[ 9 ];
suffix = pattern[ 10 ];
padding = pattern[ 4 ];
integerFractionOrSignificantPattern = pattern[ 5 ];
significantPattern = pattern[ 9 ];
scientificNotation = pattern[ 10 ];
suffix = pattern[ 11 ];

// Significant digit format
if ( significantPattern ) {
@@ -41,8 +41,8 @@ return function( pattern ) {

// Integer and fractional format
} else {
fractionPattern = pattern[ 7 ];
integerPattern = pattern[ 6 ];
fractionPattern = pattern[ 8 ];
integerPattern = pattern[ 7 ];

if ( fractionPattern ) {

@@ -34,16 +34,17 @@ define(function() {
* 0: number_pattern_re
* 1: prefix
* 2: -
* 3: padding
* 4: (integer_fraction_pattern | significant_pattern)
* 5: integer_fraction_pattern
* 6: integer_pattern
* 7: fraction_pattern
* 8: significant_pattern
* 9: scientific_notation
* 10: suffix
* 11: -
* 3: -
* 4: padding
* 5: (integer_fraction_pattern | significant_pattern)
* 6: integer_fraction_pattern
* 7: integer_pattern
* 8: fraction_pattern
* 9: significant_pattern
* 10: scientific_notation
* 11: suffix
* 12: -
*/
return ( /^(('[^']+'|''|[^*#@0,.E])*)(\*.)?((([#,]*[0,]*0+)(\.0*[0-9]*#*)?)|([#,]*@+#*))(E\+?0+)?(('[^']+'|''|[^*#@0,.E])*)$/ );
return ( /^(('([^']|'')*'|[^*#@0,.E])*)(\*.)?((([#,]*[0,]*0+)(\.0*[0-9]*#*)?)|([#,]*@+#*))(E\+?0+)?(('[^']+'|''|[^*#@0,.E])*)$/ );

});
@@ -0,0 +1,19 @@
define(function() {

/**
* objectMap( object, fn)
*
* - object
*
* - fn( pair ) => pair
*/
return function( object, fn ) {
return Object.keys( object ).map(function( key ) {
return fn([ key, object[ key ] ]);
}).reduce(function( object, pair ) {
object[ pair[ 0 ] ] = pair[ 1 ];
return object;
}, {});
};

});
@@ -0,0 +1,15 @@
define(function() {

/**
* Generated by:
*
* var regenerate = require( "regenerate" );
* var dashSymbols = require( * "unicode-8.0.0/General_Category/Format/symbols" );
* regenerate().add( dashSymbols ).toString();
*
* https://github.com/mathiasbynens/regenerate
* https://github.com/mathiasbynens/unicode-8.0.0
*/
return /[\xAD\u0600-\u0605\u061C\u06DD\u070F\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804\uDCBD|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/g;

});
@@ -0,0 +1,15 @@
define(function() {

/**
* Generated by:
*
* var regenerate = require( "regenerate" );
* var dashSymbols = require( * "unicode-8.0.0/General_Category/Dash_Punctuation/symbols" );
* regenerate().add( dashSymbols ).toString();
*
* https://github.com/mathiasbynens/regenerate
* https://github.com/mathiasbynens/unicode-8.0.0
*/
return /[\-\u058A\u05BE\u1400\u1806\u2010-\u2015\u2E17\u2E1A\u2E3A\u2E3B\u2E40\u301C\u3030\u30A0\uFE31\uFE32\uFE58\uFE63\uFF0D]/g;

});
@@ -0,0 +1,15 @@
define(function() {

/**
* Generated by:
*
* var regenerate = require( "regenerate" );
* var dashSymbols = require( "unicode-8.0.0/General_Category/Space_Separator/symbols" );
* regenerate().add( dashSymbols ).toString();
*
* https://github.com/mathiasbynens/regenerate
* https://github.com/mathiasbynens/unicode-8.0.0
*/
return /[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/g;

});
@@ -0,0 +1,22 @@
define(function() {

/**
* removeLiteralQuotes( string )
*
* Return:
* - `` if input string is `''`.
* - `o'clock` if input string is `'o''clock'`.
* - `foo` if input string is `foo`, i.e., return the same value in case it isn't a single-quoted
* string.
*/
return function( string ) {
if ( string[ 0 ] + string[ string.length - 1 ] !== "''" ) {
return string;
}
if ( string === "''" ) {
return "";
}
return string.replace( /''/g, "'" ).slice( 1, -1 );
};

});
@@ -84,6 +84,10 @@ QUnit.test( "should return a currency formatter", function( assert ) {
assert.equal( de.currencyFormatter( "USD" )( teslaS ), "69.900,00 $" );
assert.equal( zh.currencyFormatter( "USD" )( teslaS ), "US$69,900.00" );

assert.equal( Globalize.currencyFormatter( "USD" )( -teslaS ), "-$69,900.00" );
assert.equal( de.currencyFormatter( "USD" )( -teslaS ), "-69.900,00 $" );
assert.equal( zh.currencyFormatter( "USD" )( -teslaS ), "-US$69,900.00" );

assert.equal( Globalize.currencyFormatter( "USD", code )( teslaS ), "69,900.00 USD" );
assert.equal( de.currencyFormatter( "USD", code )( teslaS ), "69.900,00 USD" );
assert.equal( zh.currencyFormatter( "USD", code )( teslaS ), "69,900.00USD" );
@@ -1,21 +1,27 @@
define([
"globalize",
"json!cldr-data/main/en/numbers.json",
"json!cldr-data/main/en-IN/numbers.json",
"json!cldr-data/main/tr-CY/numbers.json",
"json!cldr-data/supplemental/likelySubtags.json",
"../../util",

"globalize/number"
], function( Globalize, enNumbers, likelySubtags, util ) {
], function( Globalize, enNumbers, enInNumbers, trCyNumbers, likelySubtags, util ) {

function extraSetup() {
Globalize.load( enNumbers );
Globalize.load( enInNumbers );
Globalize.load( trCyNumbers );
}

QUnit.module( ".numberParser( [options] )", {
setup: function() {
Globalize.load( likelySubtags, {
main: {
en: {}
en: {},
"en-IN": {},
"tr-CY": {}
}
});
Globalize.locale( "en" );
@@ -41,9 +47,15 @@ QUnit.test( "should return parser", function( assert ) {
extraSetup();

assert.equal( Globalize.numberParser()( "3" ), 3 );
assert.equal( Globalize( "en-IN" ).numberParser()( "76,54,321" ), 7654321 );

assert.equal( Globalize.numberParser({
style: "percent"
})( "50%" ), 0.5 );

assert.equal( Globalize( "tr-CY" ).numberParser({
style: "percent"
})( "%50" ), 0.5 );
});

QUnit.test( "should allow for runtime compilation", function( assert ) {
@@ -56,7 +68,6 @@ QUnit.test( "should allow for runtime compilation", function( assert ) {
"Globalize(\"en\").numberParser({})",
function( runtimeArgs ) {
assert.deepEqual( runtimeArgs[ 0 ], [
"∞",
{
".": ".",
",": ",",
@@ -66,9 +77,16 @@ QUnit.test( "should allow for runtime compilation", function( assert ) {
"E": "E",
"‰": "‰"
},
"-",
"",
undefined
undefined,
{
infinity: /^/,
nan: /^NaN/,
negativePrefix: /^-/,
negativeSuffix: /^/,
number: /^((\d{1,3}(,\d{3})+|\d+))?(\.\d+)?/,
prefix: /^/,
suffix: /^/
}
]);
}
);
@@ -2,24 +2,28 @@ define([
"globalize",
"json!cldr-data/main/ar/numbers.json",
"json!cldr-data/main/en/numbers.json",
"json!cldr-data/main/en-IN/numbers.json",
"json!cldr-data/main/es/numbers.json",
"json!cldr-data/main/fa/numbers.json",
"json!cldr-data/main/sv/numbers.json",
"json!cldr-data/main/zh/numbers.json",
"json!cldr-data/supplemental/likelySubtags.json",
"json!cldr-data/supplemental/numberingSystems.json",
"../../util",

"globalize/number"
], function( Globalize, arNumbers, enNumbers, esNumbers, svNumbers, zhNumbers, likelySubtags,
numberingSystems, util ) {
], function( Globalize, arNumbers, enNumbers, enInNumbers, esNumbers, faNumbers, svNumbers,
zhNumbers, likelySubtags, numberingSystems, util ) {

var ar, es, sv, zh;
var ar, enIn, es, fa, sv, zh;

function extraSetup() {
Globalize.load(
arNumbers,
enNumbers,
enInNumbers,
esNumbers,
faNumbers,
svNumbers,
zhNumbers,
numberingSystems
@@ -32,13 +36,17 @@ QUnit.module( ".parseNumber( value [, options] )", {
main: {
ar: {},
en: {},
"en-IN": {},
es: {},
fa: {},
sv: {},
zh: {}
}
});
ar = new Globalize( "ar" );
enIn = new Globalize( "en-IN" );
es = new Globalize( "es" );
fa = new Globalize( "fa" );
sv = new Globalize( "sv" );
zh = new Globalize( "zh-u-nu-native" );
Globalize.locale( "en" );
@@ -78,9 +86,21 @@ QUnit.test( "should parse integers", function( assert ) {
extraSetup();

assert.equal( Globalize.parseNumber( "3" ), 3 );
assert.equal( Globalize.parseNumber( "12735" ), 12735 );

// Loose match: ignore control format symbols.
assert.equal( ar.parseNumber( "-٣" ), -3 );

// Grouping separator.
assert.equal( Globalize.parseNumber( "12,735" ), 12735 );
assert.equal( Globalize.parseNumber( "1,2,7,35" ), 12735 );
assert.equal( enIn.parseNumber( "76,54,321" ), 7654321 );
assert.deepEqual( enIn.parseNumber( "654,321" ), NaN );
assert.equal( es.parseNumber( "12.735" ), 12735 );
assert.equal( sv.parseNumber( "12\xA0735" ), 12735 );

// Loose match: map all characters in [:Zs:] to U+0020 SPACE, e.g., accept non-breaking space as
// grouping separator.
assert.equal( sv.parseNumber( "12 735" ), 12735 );
});

QUnit.test( "should parse negative integers", function( assert ) {
@@ -98,7 +118,9 @@ QUnit.test( "should parse decimals", function( assert ) {
extraSetup();

assert.equal( Globalize.parseNumber( "3.14" ), 3.14 );
assert.deepEqual( Globalize.parseNumber( "3,14" ), NaN );
assert.equal( es.parseNumber( "3,14" ), 3.14 );
assert.deepEqual( es.parseNumber( "3.14" ), NaN );
assert.equal( ar.parseNumber( "٣٫١٤" ), 3.14 );
assert.equal( zh.parseNumber( "三.一四" ), 3.14 );
assert.equal( Globalize.parseNumber( "3.00" ), 3 );
@@ -119,25 +141,26 @@ QUnit.test( "should parse negative decimal", function( assert ) {
QUnit.test( "should parse percent", function( assert ) {
extraSetup();

assert.equal( Globalize.parseNumber( "1%" ), 0.01 );
assert.equal( Globalize.parseNumber( "01%" ), 0.01 );
assert.equal( Globalize.parseNumber( "10%" ), 0.1 );
assert.equal( Globalize.parseNumber( "50%" ), 0.5 );
assert.equal( Globalize.parseNumber( "100%" ), 1 );
assert.equal( Globalize.parseNumber( "0.5%" ), 0.005 );
assert.equal( Globalize.parseNumber( "0.5%" ), 0.005 );
assert.equal( ar.parseNumber( "٥٠٪" ), 0.5 );
assert.equal( Globalize.parseNumber( "-10%" ), -0.1 );
});

/**
* Scientific notation
*/
QUnit.test( "should parse scientific notation numbers", function( assert ) {
extraSetup();

assert.equal( Globalize.parseNumber( "3E-3" ), 0.003 );
assert.equal( sv.parseNumber( "3×10^−3" ), 0.003 );
assert.equal( Globalize.parseNumber( "1%", { style: "percent" } ), 0.01 );
assert.equal( Globalize.parseNumber( "01%", { style: "percent" } ), 0.01 );
assert.equal( Globalize.parseNumber( "10%", { style: "percent" } ), 0.1 );
assert.equal( Globalize.parseNumber( "50%", { style: "percent" } ), 0.5 );
assert.equal( Globalize.parseNumber( "100%", { style: "percent" } ), 1 );

assert.equal( Globalize.parseNumber( "0.5%", {
style: "percent",
minimumFractionDigits: 0,
maximumFractionDigits: 1
}), 0.005 );

assert.equal( Globalize.parseNumber( "0.5%", {
style: "percent",
minimumFractionDigits: 0,
maximumFractionDigits: 1
}), 0.005 );

assert.equal( ar.parseNumber( "٥٠٪", { style: "percent" } ), 0.5 );
assert.equal( Globalize.parseNumber( "-10%", { style: "percent" } ), -0.1 );
});

/**
@@ -161,4 +184,43 @@ QUnit.test( "should parse invalid numbers as NaN", function( assert ) {
assert.deepEqual( Globalize.parseNumber( "NaN" ), NaN );
});

/**
* Prefix
*/

QUnit.test( "should parse literals", function( assert ) {
extraSetup();

// TODO: Move this test to parse-currency when implemented.
assert.equal( Globalize.parseNumber( "-$1,214.12", { raw: "'$'#,##0.##" } ), -1214.12 );
});

/**
* Other
*/
QUnit.test( "should parse a formatted number (reverse operation test)", function( assert ) {
extraSetup();
var options;
var number = 12345.67;
assert.equal( Globalize.parseNumber( Globalize.formatNumber( number ) ), number );
assert.equal( ar.parseNumber( ar.formatNumber( number ) ), number );
assert.equal( fa.parseNumber( fa.formatNumber( number ) ), number );

number = -12345.67;
assert.equal( Globalize.parseNumber( Globalize.formatNumber( number ) ), number );
assert.equal( ar.parseNumber( ar.formatNumber( number ) ), number );
assert.equal( fa.parseNumber( fa.formatNumber( number ) ), number );

number = 0.5;
options = { style: "percent" };

assert.equal(
Globalize.parseNumber( Globalize.formatNumber( number, options ), options ),
number
);

assert.equal( ar.parseNumber( ar.formatNumber( number, options ), options ), number );
assert.equal( fa.parseNumber( fa.formatNumber( number, options ), options ), number );
});

});
@@ -37,6 +37,7 @@ QUnit.module( "Currency Symbol Properties" );
QUnit.test( "should return pattern replacing `¤` with the appropriate currency symbol literal", function( assert ) {
assert.deepEqual( symbolProperties( "USD", en, {} ), { pattern: "'$'#,##0.00" } );
assert.deepEqual( symbolProperties( "EUR", en, {} ), { pattern: "'€'#,##0.00" } );
assert.deepEqual( symbolProperties( "CLF", en, {} ), { pattern: "'CLF' #,##0.0000" } );
assert.deepEqual( symbolProperties( "USD", de, {} ), { pattern: "#,##0.00 '$'" } );
assert.deepEqual( symbolProperties( "EUR", de, {} ), { pattern: "#,##0.00 '€'" } );
assert.deepEqual( symbolProperties( "USD", zh, {} ), { pattern: "'US$'#,##0.00" } );
@@ -1,24 +1,33 @@
define([
"cldr",
"src/number/format-properties",
"json!cldr-data/main/ar/numbers.json",
"json!cldr-data/main/en/numbers.json",
"json!cldr-data/main/es/numbers.json",
"json!cldr-data/main/fa/numbers.json",
"json!cldr-data/supplemental/likelySubtags.json",
"json!cldr-data/supplemental/numberingSystems.json",

"cldr/event",
"cldr/supplemental"
], function( Cldr, properties, enNumbers, esNumbers, likelySubtags ) {
], function( Cldr, properties, arNumbers, enNumbers, esNumbers, faNumbers, likelySubtags,
numberingSystems ) {

var en, es;
var ar, en, es, fa;

Cldr.load(
arNumbers,
enNumbers,
esNumbers,
likelySubtags
faNumbers,
likelySubtags,
numberingSystems
);

ar = new Cldr( "ar" );
en = new Cldr( "en" );
es = new Cldr( "es" );
fa = new Cldr( "fa" );

QUnit.module( "Number Format Properties" );

@@ -34,6 +43,7 @@ QUnit.test( "should return negativePattern", function( assert ) {

QUnit.test( "should return negativePrefix", function( assert ) {
assert.equal( properties( "#,##0.0#;(0)", en )[ 13 ], "(" );
assert.equal( properties( "'$'#,##0.0#;-'$'0", en )[ 13 ], "-'$'" );
});

QUnit.test( "should return negativeSuffix", function( assert ) {
@@ -65,6 +75,20 @@ QUnit.test( "should return symbolMap", function( assert ) {
"E": "E",
"‰": "‰"
});

assert.deepEqual( properties( "0", fa )[ 18 ], {
"%": "٪",
"+": "\u200e+\u200e",
",": "٬",
"-": "\u200e−",
".": "٫",
"E": "×۱۰^",
"‰": "؉"
});
});

QUnit.test( "should return nuDigits", function( assert ) {
assert.deepEqual( properties( "0", ar )[ 19 ], "٠١٢٣٤٥٦٧٨٩" );
});

});
@@ -5,18 +5,19 @@ define([
"json!cldr-data/main/ar/numbers.json",
"json!cldr-data/main/en/numbers.json",
"json!cldr-data/main/es/numbers.json",
"json!cldr-data/main/fa/numbers.json",
"json!cldr-data/main/zh/numbers.json",
"json!cldr-data/supplemental/likelySubtags.json",
"json!cldr-data/supplemental/numberingSystems.json",

"cldr/event",
"cldr/supplemental"
], function( Cldr, format, properties, arNumbers, enNumbers, esNumbers, zhNumbers, likelySubtags,
numberingSystems ) {
], function( Cldr, format, properties, arNumbers, enNumbers, esNumbers, faNumbers, zhNumbers,
likelySubtags, numberingSystems ) {

// 1: Earth average diameter according to:
// http://www.wolframalpha.com/input/?i=earth+diameter
var ar, en, es, zh,
var ar, en, es, fa, zh,
deci = 0.1,
earthDiameter = 12735, /* 1 */
pi = 3.14159265359;
@@ -25,6 +26,7 @@ Cldr.load(
arNumbers,
enNumbers,
esNumbers,
faNumbers,
zhNumbers,
likelySubtags,
numberingSystems
@@ -33,6 +35,7 @@ Cldr.load(
ar = new Cldr( "ar" );
en = new Cldr( "en" );
es = new Cldr( "es" );
fa = new Cldr( "fa" );
zh = new Cldr( "zh-u-nu-native" );

QUnit.module( "Number Format" );
@@ -201,6 +204,9 @@ QUnit.test( "should format negative decimal", function( assert ) {
assert.equal( format( -pi, properties( "@@#", en ) ), "-3.14" );
assert.equal( format( -pi, properties( "@@#;(@@#)", en ) ), "(3.14)" );

// The U+002D HYPHEN-MINUS sign shall be localized.
assert.equal( format( -pi, properties( "0.##", fa ) ), "\u200e\u2212۳٫۱۴" );

// The number of digits, minimal digits, and other characteristics shall be ignored in the negative subpattern.
assert.equal( format( -pi, properties( "0.##;(0)", en ) ), "(3.14)" );
assert.equal( format( -pi, properties( "@@#;(0)", en ) ), "(3.14)" );
@@ -292,6 +298,9 @@ QUnit.test( "should format infinite numbers", function( assert ) {

QUnit.test( "should format literal (')", function( assert ) {
assert.equal( format( 69900, properties( "'$'#,##0", en ) ), "$69,900" );

// Make sure quoted characters (in this case, minus sign) aren't localized.
assert.equal( format( -pi, properties( "0.##;'-'0.##", fa ) ), "-۳٫۱۴" );
});

});
@@ -41,4 +41,5 @@ QUnit.test( "should allow different round options", function( assert ) {
QUnit.test( "should handle inaccurate floating point arithmetics", function( assert ) {
assert.equal( formatSignificantDigits( 0.00012345, 1, 3, round ), "0.000123" );
});

});
@@ -1,33 +1,39 @@
define([
"cldr",
"src/number/parse-properties",
"json!cldr-data/main/ar/numbers.json",
"json!cldr-data/main/en/numbers.json",
"json!cldr-data/main/es/numbers.json",
"json!cldr-data/main/fa/numbers.json",
"json!cldr-data/main/sv/numbers.json",
"json!cldr-data/supplemental/likelySubtags.json",

"cldr/event",
"cldr/supplemental"
], function( Cldr, properties, enNumbers, esNumbers, likelySubtags ) {
], function( Cldr, properties, arNumbers, enNumbers, esNumbers, faNumbers, svNumbers,
likelySubtags ) {

var en, es;
var ar, fa, en, es, sv;

Cldr.load(
arNumbers,
enNumbers,
esNumbers,
faNumbers,
svNumbers,
likelySubtags
);

ar = new Cldr( "ar" );
en = new Cldr( "en" );
es = new Cldr( "es" );
fa = new Cldr( "fa" );
sv = new Cldr( "sv" );

QUnit.module( "Number Parse Properties" );

QUnit.test( "should return infinitySymbol", function( assert ) {
assert.equal( properties( "0", en )[ 0 ], "∞" );
});

QUnit.test( "should return invertedSymbolMap", function( assert ) {
assert.deepEqual( properties( "0", es )[ 1 ], {
assert.deepEqual( properties( "0", es )[ 0 ], {
"%": "%",
"+": "+",
",": ".",
@@ -36,16 +42,89 @@ QUnit.test( "should return invertedSymbolMap", function( assert ) {
"E": "E",
"‰": "‰"
});

// Note grouping separator is using regular U+0020 SPACE due to loose matching.
assert.deepEqual( properties( "0", sv )[ 0 ], {
"%": "%",
"+": "+",
",": ".",
" ": ",",
"×10^": "E",
"‰": "‰",
"−": "-"
});
});

QUnit.test( "should return invertedNuDigitsMap", function( assert ) {
assert.deepEqual( properties( "0", ar )[ 1 ], {
"٠": "0",
"١": "1",
"٢": "2",
"٣": "3",
"٤": "4",
"٥": "5",
"٦": "6",
"٧": "7",
"٨": "8",
"٩": "9"
});
});

QUnit.test( "should return infinity tokenizer", function( assert ) {
assert.deepEqual( properties( "0", en )[ 2 ].infinity, /^/ );
});

QUnit.test( "should return NaN tokenizer", function( assert ) {
assert.deepEqual( properties( "0", en )[ 2 ].nan, /^NaN/ );
assert.deepEqual( properties( "0", ar )[ 2 ].nan, /^ليس رقم/ );
});

QUnit.test( "should return negativePrefix tokenizer", function( assert ) {
assert.deepEqual( properties( "0", en )[ 2 ].negativePrefix, /^-/ );
assert.deepEqual( properties( "0;(0)", en )[ 2 ].negativePrefix, /^\(/ );
});

QUnit.test( "should return negativeSuffix tokenizer", function( assert ) {
assert.deepEqual( properties( "0", en )[ 2 ].negativeSuffix, /^/ );
assert.deepEqual( properties( "0;(0)", en )[ 2 ].negativeSuffix, /^\)/ );
});

QUnit.test( "should return number tokenizer", function( assert ) {
assert.deepEqual( properties( "0", en )[ 2 ].number, /^\d+/ );
assert.deepEqual( properties( "0.##", en )[ 2 ].number, /^(\d+)?(\.\d+)?/ );

assert.deepEqual(
properties( "#,##0.##", en )[ 2 ].number,
/^((\d{1,3}(,\d{3})+|\d+))?(\.\d+)?/
);

assert.deepEqual(
properties( "#,##0.##", es )[ 2 ].number,
/^((\d{1,3}(\.\d{3})+|\d+))?(,\d+)?/
);

assert.deepEqual(
properties( "#,##,##0.##", en )[ 2 ].number,
/^((\d{1,2}((,\d{2})*(,\d{3}))?|\d+))?(\.\d+)?/
);

assert.deepEqual(
properties( "#,##0.##", sv )[ 2 ].number,
/^((\d{1,3}( \d{3})+|\d+))?(,\d+)?/
);
});

QUnit.test( "should return negativePrefix", function( assert ) {
assert.equal( properties( "0", en )[ 2 ], "-" );
assert.equal( properties( "0;(0)", en )[ 2 ], "(" );
QUnit.test( "should return prefix tokenizer", function( assert ) {
assert.deepEqual( properties( "%0", en )[ 2 ].prefix, /^%/ );
assert.deepEqual( properties( "'$'0.##", en )[ 2 ].prefix, /^\$/ );
assert.deepEqual( properties( "'$ '0.##", en )[ 2 ].prefix, /^\$ / );
assert.deepEqual( properties( "'foo''bar'0.##", en )[ 2 ].prefix, /^foo'bar/ );
assert.deepEqual( properties( "-'foo''bar'0.##", en )[ 2 ].prefix, /^-foo'bar/ );
});

QUnit.test( "should return negativeSuffix", function( assert ) {
assert.equal( properties( "0", en )[ 3 ], "" );
assert.equal( properties( "0;(0)", en )[ 3 ], ")" );
QUnit.test( "should return suffix tokenizer", function( assert ) {
assert.deepEqual( properties( "#,##0%", en )[ 2 ].suffix, /^%/ );
assert.deepEqual( properties( "#,##0 %", ar )[ 2 ].suffix, /^ ٪/ );
});

});
@@ -35,7 +35,7 @@ ar = new Cldr( "ar" );
en = new Cldr( "en" );
es = new Cldr( "es" );
pt = new Cldr( "pt" );
ru = new Cldr( "sv" );
ru = new Cldr( "ru" );
sv = new Cldr( "sv" );
zh = new Cldr( "zh-u-nu-native" );

@@ -55,15 +55,29 @@ QUnit.test( "should parse zero-padded integers", function( assert ) {

QUnit.test( "should parse grouping separators", function( assert ) {
assert.equal( parse( "12,735", properties( "#,##0.#", en ) ), 12735 );
assert.equal( parse( "12735", properties( "#,##0.#", en ) ), 12735 );
assert.equal( parse( "1,2,7,35", properties( "#,#,#0.#", en ) ), 12735 );
assert.equal( parse( "12.735", properties( "#,##0", es ) ), 12735 );
});

QUnit.test( "should parse invalid grouping separators as NaN", function( assert ) {
assert.deepEqual( parse( "1,2735", properties( "#,##0.#", en ) ), NaN );
assert.deepEqual( parse( "1,2,735", properties( "#,#,#0.#", en ) ), NaN );
assert.deepEqual( parse( "1,27,35", properties( "#,#,#0.#", en ) ), NaN );
assert.deepEqual( parse( "12,7,35", properties( "#,#,#0.#", en ) ), NaN );
assert.deepEqual( parse( "1.2735", properties( "#,##0", es ) ), NaN );
});

QUnit.test( "should parse negative integers", function( assert ) {
assert.equal( parse( "-3", properties( "0", en ) ), -3 );
assert.equal( parse( "(3)", properties( "0;(0)", en ) ), -3 );
});

QUnit.test( "should parse mixed non breaking space and breaking space", function( assert ) {
assert.equal( parse( "12\xA0735", properties( "#,##0", sv ) ), 12735 );
assert.equal( parse( "12 735", properties( "#,##0", sv ) ), 12735 );
});

/**
* Decimals
*/
@@ -76,6 +90,11 @@ QUnit.test( "should parse decimals", function( assert ) {
assert.equal( parse( "3.00", properties( "0.##", en ) ), 3 );
});

QUnit.test( "should parse invalid decimals as NaN", function( assert ) {
assert.deepEqual( parse( "3,14", properties( "0.#", en ) ), NaN );
assert.deepEqual( parse( "3.14", properties( "0.#", es ) ), NaN );
});

QUnit.test( "should parse zero-padded decimals", function( assert ) {
assert.equal( parse( "12735.0", properties( "0.0", en ) ), 12735 );
assert.equal( parse( "0.10", properties( "0.00", en ) ), 0.1 );
@@ -109,6 +128,7 @@ QUnit.test( "should parse percent", function( assert ) {
assert.equal( parse( "100%", properties( "0%", en ) ), 1 );
assert.equal( parse( "0.5%", properties( "##0.#%", en ) ), 0.005 );
assert.equal( parse( "0.5%", properties( "##0.#%", en ) ), 0.005 );
assert.equal( parse( "%100", properties( "%0", en ) ), 1 );
});

QUnit.test( "should localize percent symbol (%)", function( assert ) {
@@ -145,14 +165,6 @@ QUnit.test( "should parse negative mille", function( assert ) {
assert.equal( parse( "(1)\u2030", properties( "0\u2030;(0)\u2030", en ) ), -0.001 );
});

/**
* Scientific notation
*/
QUnit.test( "should parse scientific notation numbers", function( assert ) {
assert.equal( parse( "3E-3", properties( "0", en ) ), 0.003 );
assert.equal( parse( "3×10^−3", properties( "0", sv ) ), 0.003 );
});

/**
* Infinite number
*/
@@ -165,10 +177,23 @@ QUnit.test( "should parse infinite numbers", function( assert ) {
/**
* NaN
*/
QUnit.test( "should parse NaN", function( assert ) {
assert.deepEqual( parse( "NaN", properties( "0", en ) ), NaN );
});

QUnit.test( "should parse invalid numbers as NaN", function( assert ) {
/**
* Prefix
*/
QUnit.test( "should parse invalid prefix as NaN", function( assert ) {
assert.deepEqual( parse( "invalid", properties( "0", en ) ), NaN );
assert.deepEqual( parse( "NaN", properties( "0", en ) ), NaN );
assert.deepEqual( parse( "garbage123", properties( "0", en ) ), NaN );
});

/**
* Suffix
*/
QUnit.test( "should parse invalid suffix as NaN", function( assert ) {
assert.deepEqual( parse( "123garbage", properties( "0", en ) ), NaN );
});

});
@@ -20,6 +20,8 @@ QUnit.test( "should return prefix", function( assert ) {
assert.equal( properties( "0", en )[ 0 ], "" );
assert.equal( properties( "foo 0", en )[ 0 ], "foo " );
assert.equal( properties( "-0", en )[ 0 ], "-" );
assert.equal( properties( "'-'0", en )[ 0 ], "'-'" );
assert.equal( properties( "-'$'0", en )[ 0 ], "-'$'" );
});

QUnit.test( "should return minimumIntegerDigits", function( assert ) {