Merge "Removing badge drop shadow per Vibha"
[lhc/web/wiklou.git] / resources / mediawiki.language / mediawiki.language.numbers.js
1 /*
2 * Number related utilities for mediawiki.language
3 */
4 ( function ( mw, $ ) {
5
6 /**
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.
10 * example:
11 * Fill the string to length 10 with '+' characters on the right. Yields 'blah++++++'.
12 * pad('blah', 10, '+', true);
13 *
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
18 * @return {string}
19 */
20 function pad ( text, size, ch, end ) {
21 if ( !ch ) {
22 ch = '0';
23 }
24
25 var out = String( text ),
26 padStr = replicate( ch, Math.ceil( ( size - out.length ) / ch.length ) );
27
28 return end ? out + padStr : padStr + out;
29 }
30
31 /**
32 * Efficiently replicate a string n times.
33 *
34 * @param {string} str The string to replicate
35 * @param {Number} num Number of times to replicate the string
36 * @return {string}
37 */
38 function replicate ( str, num ) {
39 if ( num <= 0 || !str ) {
40 return '';
41 }
42
43 var buf = [];
44 while (num) {
45 buf.push( str );
46 str += str;
47 }
48 return buf.join( '' );
49 }
50
51 /**
52 * Apply numeric pattern to absolute value using options. Gives no
53 * consideration to local customs.
54 *
55 * Adapted from dojo/number library with thanks
56 * http://dojotoolkit.org/reference-guide/1.8/dojo/number.html
57 *
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
62 *
63 * @return {string}
64 */
65 function commafyNumber( value, pattern, options ) {
66 options = options || {
67 group: ',',
68 decimal: '.'
69 };
70
71 if ( isNaN( value) ) {
72 return value;
73 }
74
75 var padLength,
76 patternDigits,
77 index,
78 whole,
79 off,
80 remainder,
81 patternParts = pattern.split( '.' ),
82 maxPlaces = ( patternParts[1] || [] ).length,
83 valueParts = String( Math.abs( value ) ).split( '.' ),
84 fractional = valueParts[1] || '',
85 groupSize = 0,
86 groupSize2 = 0,
87 pieces = [];
88
89 if ( patternParts[1] ) {
90 // Pad fractional with trailing zeros
91 padLength = ( patternParts[1] && patternParts[1].lastIndexOf( '0' ) + 1 );
92
93 if ( padLength > fractional.length ) {
94 valueParts[1] = pad( fractional, padLength, '0', true );
95 }
96
97 // Truncate fractional
98 if ( maxPlaces < fractional.length ) {
99 valueParts[1] = fractional.substr( 0, maxPlaces );
100 }
101 } else {
102 if ( valueParts[1] ) {
103 valueParts.pop();
104 }
105 }
106
107 // Pad whole with leading zeros
108 patternDigits = patternParts[0].replace( ',', '' );
109
110 padLength = patternDigits.indexOf( '0' );
111
112 if ( padLength !== -1 ) {
113 padLength = patternDigits.length - padLength;
114
115 if ( padLength > valueParts[0].length ) {
116 valueParts[0] = pad( valueParts[0], padLength );
117 }
118
119 // Truncate whole
120 if ( patternDigits.indexOf( '#' ) === -1 ) {
121 valueParts[0] = valueParts[0].substr( valueParts[0].length - padLength );
122 }
123 }
124
125 // Add group separators
126 index = patternParts[0].lastIndexOf( ',' );
127
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;
134 }
135 }
136
137 for ( whole = valueParts[0]; whole; ) {
138 off = whole.length - groupSize;
139
140 pieces.push( ( off > 0 ) ? whole.substr( off ) : whole );
141 whole = ( off > 0 ) ? whole.slice( 0, off ) : '';
142
143 if ( groupSize2 ) {
144 groupSize = groupSize2;
145 }
146 }
147 valueParts[0] = pieces.reverse().join( options.group );
148
149 return valueParts.join( options.decimal );
150 }
151
152 $.extend( mw.language, {
153
154 /**
155 * Converts a number using digitTransformTable.
156 *
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
160 */
161 convertNumber: function ( num, integer ) {
162 var i, tmp, transformTable, numberString, convertedNumber, pattern;
163
164 pattern = mw.language.getData( mw.config.get( 'wgUserLanguage' ),
165 'digitGroupingPattern' ) || '#,##0.###';
166
167 // Set the target transform table:
168 transformTable = mw.language.getDigitTransformTable();
169
170 if ( !transformTable ) {
171 return num;
172 }
173
174 // Check if the 'restore' to Latin number flag is set:
175 if ( integer ) {
176 if ( parseInt( num, 10 ) === num ) {
177 return num;
178 }
179 tmp = [];
180 for ( i in transformTable ) {
181 tmp[ transformTable[ i ] ] = i;
182 }
183 transformTable = tmp;
184 numberString = num + '';
185 } else {
186 numberString = mw.language.commafy( num, pattern );
187 }
188
189 convertedNumber = '';
190 for ( i = 0; i < numberString.length; i++ ) {
191 if ( transformTable[ numberString[i] ] ) {
192 convertedNumber += transformTable[numberString[i]];
193 } else {
194 convertedNumber += numberString[i];
195 }
196 }
197 return integer ? parseInt( convertedNumber, 10 ) : convertedNumber;
198 },
199
200 getDigitTransformTable: function () {
201 return mw.language.getData( mw.config.get( 'wgUserLanguage' ),
202 'digitTransformTable' ) || [];
203 },
204
205 getSeparatorTransformTable: function () {
206 return mw.language.getData( mw.config.get( 'wgUserLanguage' ),
207 'separatorTransformTable' ) || [];
208 },
209
210 /**
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.
213 *
214 * @param {Number} value
215 * @param {string} pattern Pattern string as described by Unicode TR35
216 * @throws Error
217 * @returns {String}
218 */
219 commafy: function ( value, pattern ) {
220 var numberPattern,
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];
227
228 pattern = patternList[ ( value < 0 ) ? 1 : 0] || ( '-' + positivePattern );
229 numberPattern = positivePattern.match( numberPatternRE );
230
231 if ( !numberPattern ) {
232 throw new Error( 'unable to find a number expression in pattern: ' + pattern );
233 }
234
235 return pattern.replace( numberPatternRE, commafyNumber( value, numberPattern[0], {
236 decimal: decimal,
237 group: group
238 } ) );
239 }
240
241 } );
242
243 }( mediaWiki, jQuery ) );