summaryrefslogtreecommitdiff
path: root/www/wiki/resources/src/mediawiki.language/mediawiki.language.numbers.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/resources/src/mediawiki.language/mediawiki.language.numbers.js')
-rw-r--r--www/wiki/resources/src/mediawiki.language/mediawiki.language.numbers.js312
1 files changed, 312 insertions, 0 deletions
diff --git a/www/wiki/resources/src/mediawiki.language/mediawiki.language.numbers.js b/www/wiki/resources/src/mediawiki.language/mediawiki.language.numbers.js
new file mode 100644
index 00000000..2392089c
--- /dev/null
+++ b/www/wiki/resources/src/mediawiki.language/mediawiki.language.numbers.js
@@ -0,0 +1,312 @@
+/*
+ * Number-related utilities for mediawiki.language.
+ */
+( function ( mw, $ ) {
+ /**
+ * @class mw.language
+ */
+
+ /**
+ * Replicate a string 'n' times.
+ *
+ * @private
+ * @param {string} str The string to replicate
+ * @param {number} num Number of times to replicate the string
+ * @return {string}
+ */
+ function replicate( str, num ) {
+ var buf = [];
+
+ if ( num <= 0 || !str ) {
+ return '';
+ }
+
+ while ( num-- ) {
+ buf.push( str );
+ }
+ return buf.join( '' );
+ }
+
+ /**
+ * Pad a string to guarantee that it is at least `size` length by
+ * filling with the character `ch` at either the start or end of the
+ * string. Pads at the start, by default.
+ *
+ * Example: Fill the string to length 10 with '+' characters on the right.
+ *
+ * pad( 'blah', 10, '+', true ); // => 'blah++++++'
+ *
+ * @private
+ * @param {string} text The string to pad
+ * @param {number} size The length to pad to
+ * @param {string} [ch='0'] Character to pad with
+ * @param {boolean} [end=false] Adds padding at the end if true, otherwise pads at start
+ * @return {string}
+ */
+ function pad( text, size, ch, end ) {
+ var out, padStr;
+
+ if ( !ch ) {
+ ch = '0';
+ }
+
+ out = String( text );
+ padStr = replicate( ch, Math.ceil( ( size - out.length ) / ch.length ) );
+
+ return end ? out + padStr : padStr + out;
+ }
+
+ /**
+ * Apply numeric pattern to absolute value using options. Gives no
+ * consideration to local customs.
+ *
+ * Adapted from dojo/number library with thanks
+ * <http://dojotoolkit.org/reference-guide/1.8/dojo/number.html>
+ *
+ * @private
+ * @param {number} value the number to be formatted, ignores sign
+ * @param {string} pattern the number portion of a pattern (e.g. `#,##0.00`)
+ * @param {Object} [options] If provided, all option keys must be present:
+ * @param {string} options.decimal The decimal separator. Defaults to: `'.'`.
+ * @param {string} options.group The group separator. Defaults to: `','`.
+ * @param {number|null} options.minimumGroupingDigits
+ * @return {string}
+ */
+ function commafyNumber( value, pattern, options ) {
+ var padLength,
+ patternDigits,
+ index,
+ whole,
+ off,
+ remainder,
+ patternParts = pattern.split( '.' ),
+ maxPlaces = ( patternParts[ 1 ] || [] ).length,
+ valueParts = String( Math.abs( value ) ).split( '.' ),
+ fractional = valueParts[ 1 ] || '',
+ groupSize = 0,
+ groupSize2 = 0,
+ pieces = [];
+
+ options = options || {
+ group: ',',
+ decimal: '.'
+ };
+
+ if ( isNaN( value ) ) {
+ return value;
+ }
+
+ if ( patternParts[ 1 ] ) {
+ // Pad fractional with trailing zeros
+ padLength = ( patternParts[ 1 ] && patternParts[ 1 ].lastIndexOf( '0' ) + 1 );
+
+ if ( padLength > fractional.length ) {
+ valueParts[ 1 ] = pad( fractional, padLength, '0', true );
+ }
+
+ // Truncate fractional
+ if ( maxPlaces < fractional.length ) {
+ valueParts[ 1 ] = fractional.slice( 0, maxPlaces );
+ }
+ } else {
+ if ( valueParts[ 1 ] ) {
+ valueParts.pop();
+ }
+ }
+
+ // Pad whole with leading zeros
+ patternDigits = patternParts[ 0 ].replace( ',', '' );
+
+ padLength = patternDigits.indexOf( '0' );
+
+ if ( padLength !== -1 ) {
+ padLength = patternDigits.length - padLength;
+
+ if ( padLength > valueParts[ 0 ].length ) {
+ valueParts[ 0 ] = pad( valueParts[ 0 ], padLength );
+ }
+
+ // Truncate whole
+ if ( patternDigits.indexOf( '#' ) === -1 ) {
+ valueParts[ 0 ] = valueParts[ 0 ].slice( valueParts[ 0 ].length - padLength );
+ }
+ }
+
+ // Add group separators
+ index = patternParts[ 0 ].lastIndexOf( ',' );
+
+ if ( index !== -1 ) {
+ groupSize = patternParts[ 0 ].length - index - 1;
+ remainder = patternParts[ 0 ].slice( 0, index );
+ index = remainder.lastIndexOf( ',' );
+ if ( index !== -1 ) {
+ groupSize2 = remainder.length - index - 1;
+ }
+ }
+
+ if (
+ options.minimumGroupingDigits === null ||
+ valueParts[ 0 ].length >= groupSize + options.minimumGroupingDigits
+ ) {
+ for ( whole = valueParts[ 0 ]; whole; ) {
+ off = groupSize ? whole.length - groupSize : 0;
+ pieces.push( ( off > 0 ) ? whole.slice( off ) : whole );
+ whole = ( off > 0 ) ? whole.slice( 0, off ) : '';
+
+ if ( groupSize2 ) {
+ groupSize = groupSize2;
+ groupSize2 = null;
+ }
+ }
+ valueParts[ 0 ] = pieces.reverse().join( options.group );
+ }
+
+ return valueParts.join( options.decimal );
+ }
+
+ /**
+ * Helper function to flip transformation tables.
+ *
+ * @param {...Object} Transformation tables
+ * @return {Object}
+ */
+ function flipTransform() {
+ var i, key, table, flipped = {};
+
+ // Ensure we strip thousand separators. This might be overwritten.
+ flipped[ ',' ] = '';
+
+ for ( i = 0; i < arguments.length; i++ ) {
+ table = arguments[ i ];
+ for ( key in table ) {
+ if ( table.hasOwnProperty( key ) ) {
+ // The thousand separator should be deleted
+ flipped[ table[ key ] ] = key === ',' ? '' : key;
+ }
+ }
+ }
+
+ return flipped;
+ }
+
+ $.extend( mw.language, {
+
+ /**
+ * Converts a number using #getDigitTransformTable.
+ *
+ * @param {number} num Value to be converted
+ * @param {boolean} [integer=false] Whether to convert the return value to an integer
+ * @return {number|string} Formatted number
+ */
+ convertNumber: function ( num, integer ) {
+ var transformTable, digitTransformTable, separatorTransformTable,
+ i, numberString, convertedNumber, pattern, minimumGroupingDigits;
+
+ // Quick shortcut for plain numbers
+ if ( integer && parseInt( num, 10 ) === num ) {
+ return num;
+ }
+
+ // Load the transformation tables (can be empty)
+ digitTransformTable = mw.language.getDigitTransformTable();
+ separatorTransformTable = mw.language.getSeparatorTransformTable();
+
+ if ( integer ) {
+ // Reverse the digit transformation tables if we are doing unformatting
+ transformTable = flipTransform( separatorTransformTable, digitTransformTable );
+ numberString = String( num );
+ } else {
+ // This check being here means that digits can still be unformatted
+ // even if we do not produce them. This seems sane behavior.
+ if ( mw.config.get( 'wgTranslateNumerals' ) ) {
+ transformTable = digitTransformTable;
+ }
+
+ // Commaying is more complex, so we handle it here separately.
+ // When unformatting, we just use separatorTransformTable.
+ pattern = mw.language.getData( mw.config.get( 'wgUserLanguage' ),
+ 'digitGroupingPattern' ) || '#,##0.###';
+ minimumGroupingDigits = mw.language.getData( mw.config.get( 'wgUserLanguage' ),
+ 'minimumGroupingDigits' ) || null;
+ numberString = mw.language.commafy( num, pattern, minimumGroupingDigits );
+ }
+
+ if ( transformTable ) {
+ convertedNumber = '';
+ for ( i = 0; i < numberString.length; i++ ) {
+ if ( transformTable.hasOwnProperty( numberString[ i ] ) ) {
+ convertedNumber += transformTable[ numberString[ i ] ];
+ } else {
+ convertedNumber += numberString[ i ];
+ }
+ }
+ } else {
+ convertedNumber = numberString;
+ }
+
+ if ( integer ) {
+ // Parse string to integer. This loses decimals!
+ convertedNumber = parseInt( convertedNumber, 10 );
+ }
+
+ return convertedNumber;
+ },
+
+ /**
+ * Get the digit transform table for current UI language.
+ *
+ * @return {Object|Array}
+ */
+ getDigitTransformTable: function () {
+ return mw.language.getData( mw.config.get( 'wgUserLanguage' ),
+ 'digitTransformTable' ) || [];
+ },
+
+ /**
+ * Get the separator transform table for current UI language.
+ *
+ * @return {Object|Array}
+ */
+ getSeparatorTransformTable: function () {
+ return mw.language.getData( mw.config.get( 'wgUserLanguage' ),
+ 'separatorTransformTable' ) || [];
+ },
+
+ /**
+ * Apply pattern to format value as a string.
+ *
+ * Using patterns from [Unicode TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns).
+ *
+ * @param {number} value
+ * @param {string} pattern Pattern string as described by Unicode TR35
+ * @param {number|null} [minimumGroupingDigits=null]
+ * @throws {Error} If unable to find a number expression in `pattern`.
+ * @return {string}
+ */
+ commafy: function ( value, pattern, minimumGroupingDigits ) {
+ var numberPattern,
+ transformTable = mw.language.getSeparatorTransformTable(),
+ group = transformTable[ ',' ] || ',',
+ numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/, // not precise, but good enough
+ decimal = transformTable[ '.' ] || '.',
+ patternList = pattern.split( ';' ),
+ positivePattern = patternList[ 0 ];
+
+ pattern = patternList[ ( value < 0 ) ? 1 : 0 ] || ( '-' + positivePattern );
+ numberPattern = positivePattern.match( numberPatternRE );
+ minimumGroupingDigits = minimumGroupingDigits !== undefined ? minimumGroupingDigits : null;
+
+ if ( !numberPattern ) {
+ throw new Error( 'unable to find a number expression in pattern: ' + pattern );
+ }
+
+ return pattern.replace( numberPatternRE, commafyNumber( value, numberPattern[ 0 ], {
+ minimumGroupingDigits: minimumGroupingDigits,
+ decimal: decimal,
+ group: group
+ } ) );
+ }
+
+ } );
+
+}( mediaWiki, jQuery ) );