2 * Number related utilities for mediawiki.language
7 * Pad a string to guarantee that it is at least `size` length by
8 * filling with the character `ch` at either the start or end of the
9 * string. Pads at the start, by default.
11 * Fill the string to length 10 with '+' characters on the right. Yields 'blah++++++'.
12 * pad('blah', 10, '+', true);
14 * @param {string} text The string to pad
15 * @param {Number} size To provide padding
16 * @param {string} ch Character to pad, defaults to '0'
17 * @param {Boolean} end Adds padding at the end if true, otherwise pads at start
20 function pad ( text
, size
, ch
, end
) {
25 var out
= String( text
),
26 padStr
= replicate( ch
, Math
.ceil( ( size
- out
.length
) / ch
.length
) );
28 return end
? out
+ padStr
: padStr
+ out
;
32 * Efficiently replicate a string n times.
34 * @param {string} str The string to replicate
35 * @param {Number} num Number of times to replicate the string
38 function replicate ( str
, num
) {
39 if ( num
<= 0 || !str
) {
48 return buf
.join( '' );
52 * Apply numeric pattern to absolute value using options. Gives no
53 * consideration to local customs.
55 * Adapted from dojo/number library with thanks
56 * http://dojotoolkit.org/reference-guide/1.8/dojo/number.html
58 * @param {Number} value the number to be formatted, ignores sign
59 * @param {string} pattern the number portion of a pattern (e.g. `#,##0.00`)
60 * @param {string} options.decimalThe decimal separator
61 * @param {string} options.group The group separator
65 function commafyNumber( value
, pattern
, options
) {
66 options
= options
|| {
71 if ( isNaN( value
) ) {
81 patternParts
= pattern
.split( '.' ),
82 maxPlaces
= ( patternParts
[1] || [] ).length
,
83 valueParts
= String( Math
.abs( value
) ).split( '.' ),
84 fractional
= valueParts
[1] || '',
89 if ( patternParts
[1] ) {
90 // Pad fractional with trailing zeros
91 padLength
= ( patternParts
[1] && patternParts
[1].lastIndexOf( '0' ) + 1 );
93 if ( padLength
> fractional
.length
) {
94 valueParts
[1] = pad( fractional
, padLength
, '0', true );
97 // Truncate fractional
98 if ( maxPlaces
< fractional
.length
) {
99 valueParts
[1] = fractional
.substr( 0, maxPlaces
);
102 if ( valueParts
[1] ) {
107 // Pad whole with leading zeros
108 patternDigits
= patternParts
[0].replace( ',', '' );
110 padLength
= patternDigits
.indexOf( '0' );
112 if ( padLength
!== -1 ) {
113 padLength
= patternDigits
.length
- padLength
;
115 if ( padLength
> valueParts
[0].length
) {
116 valueParts
[0] = pad( valueParts
[0], padLength
);
120 if ( patternDigits
.indexOf( '#' ) === -1 ) {
121 valueParts
[0] = valueParts
[0].substr( valueParts
[0].length
- padLength
);
125 // Add group separators
126 index
= patternParts
[0].lastIndexOf( ',' );
128 if ( index
!== -1 ) {
129 groupSize
= patternParts
[0].length
- index
- 1;
130 remainder
= patternParts
[0].substr( 0, index
);
131 index
= remainder
.lastIndexOf( ',' );
132 if ( index
!== -1 ) {
133 groupSize2
= remainder
.length
- index
- 1;
137 for ( whole
= valueParts
[0]; whole
; ) {
138 off
= whole
.length
- groupSize
;
140 pieces
.push( ( off
> 0 ) ? whole
.substr( off
) : whole
);
141 whole
= ( off
> 0 ) ? whole
.slice( 0, off
) : '';
144 groupSize
= groupSize2
;
147 valueParts
[0] = pieces
.reverse().join( options
.group
);
149 return valueParts
.join( options
.decimal );
152 $.extend( mw
.language
, {
155 * Converts a number using digitTransformTable.
157 * @param {Number} num Value to be converted
158 * @param {boolean} integer Convert the return value to an integer
159 * @return {Number|string} Formatted number
161 convertNumber: function ( num
, integer
) {
162 var i
, tmp
, transformTable
, numberString
, convertedNumber
, pattern
;
164 pattern
= mw
.language
.getData( mw
.config
.get( 'wgUserLanguage' ),
165 'digitGroupingPattern' ) || '#,##0.###';
167 // Set the target transform table:
168 transformTable
= mw
.language
.getDigitTransformTable();
170 if ( !transformTable
) {
174 // Check if the 'restore' to Latin number flag is set:
176 if ( parseInt( num
, 10 ) === num
) {
180 for ( i
in transformTable
) {
181 tmp
[ transformTable
[ i
] ] = i
;
183 transformTable
= tmp
;
184 numberString
= num
+ '';
186 numberString
= mw
.language
.commafy( num
, pattern
);
189 convertedNumber
= '';
190 for ( i
= 0; i
< numberString
.length
; i
++ ) {
191 if ( transformTable
[ numberString
[i
] ] ) {
192 convertedNumber
+= transformTable
[numberString
[i
]];
194 convertedNumber
+= numberString
[i
];
197 return integer
? parseInt( convertedNumber
, 10 ) : convertedNumber
;
200 getDigitTransformTable: function () {
201 return mw
.language
.getData( mw
.config
.get( 'wgUserLanguage' ),
202 'digitTransformTable' ) || [];
205 getSeparatorTransformTable: function () {
206 return mw
.language
.getData( mw
.config
.get( 'wgUserLanguage' ),
207 'separatorTransformTable' ) || [];
211 * Apply pattern to format value as a string using as per
212 * unicode.org TR35 - http://www.unicode.org/reports/tr35/#Number_Format_Patterns.
214 * @param {Number} value
215 * @param {string} pattern Pattern string as described by Unicode TR35
219 commafy: function ( value
, pattern
) {
221 transformTable
= mw
.language
.getSeparatorTransformTable(),
222 group
= transformTable
[','] || ',',
223 numberPatternRE
= /[#0,]*[#0](?:\.0*#*)?/, // not precise, but good enough
224 decimal = transformTable
['.'] || '.',
225 patternList
= pattern
.split( ';' ),
226 positivePattern
= patternList
[0];
228 pattern
= patternList
[ ( value
< 0 ) ? 1 : 0] || ( '-' + positivePattern
);
229 numberPattern
= positivePattern
.match( numberPatternRE
);
231 if ( !numberPattern
) {
232 throw new Error( 'unable to find a number expression in pattern: ' + pattern
);
235 return pattern
.replace( numberPatternRE
, commafyNumber( value
, numberPattern
[0], {
243 }( mediaWiki
, jQuery
) );