Cees Hek - Number.Format-0.03

Documentation | Source

NAME

Number.Format - JavaScript library for formatting numbers

SYNOPSIS

  JSAN.use('Number.Format');
  nf = new Number.Format();
  formatted = nf.round(number, precision);
  formatted = nf.format_number(number, precision, trailing_zeroes);
  formatted = nf.format_negative(number, picture);
  formatted = nf.format_picture(number, picture);
  formatted = nf.format_price(number, precision);
  formatted = nf.format_bytes(number, precision);
  number    = nf.unformat_number(formatted);

DESCRIPTION

These functions provide an easy means of formatting numbers in a manner suitable for displaying to the user. This library is a direct port of the Number::Format perl module written by William R. Ward.

To use this library you need to create a Number.Format object, which you can think of as a formatting engine. Then call one of the object methods that are defined below. The constructor new() can be used to set the parameters of the formatting engine. Valid parameters are:

  thousands_sep     - character inserted between groups of 3 digits
  decimal_point     - character separating integer and fractional parts
  mon_thousands_sep - like 'thousands_sep', but used for format_price
  mon_decimal_point - like 'decimal_point', but used for format_price
  int_curr_symbol   - character(s) denoting currency (see format_price())
  decimal_digits    - number of digits to the right of dec point (def 2)
  decimal_fill      - boolean; whether to add zeroes to fill out decimal
  neg_format        - format to display negative numbers (def ``-x'')
  kilo_suffix       - suffix to add when format_bytes formats kilobytes
  mega_suffix       -    "    "  "    "        "         "    megabytes
  giga_suffix       -    "    "  "    "        "         "    gigabytes

The default values for all the parameters are:

  thousands_sep     = ','
  decimal_point     = '.'
  mon_thousands_sep = ','
  mon_decimal_point = '.'
  int_curr_symbol   = 'USD'
  decimal_digits    = 2
  decimal_fill      = 0
  neg_format        = '-x'
  kilo_suffix       = 'K'
  mega_suffix       = 'M'
  giga_suffix       = 'G'

Once the Number.Format object has been created, you can alter any of the parameters directly, by changing their value in the object.

  nf = new Number.Format( { kilo_suffix : 'kb' } );
  nf.kilo_suffix = 'Kb';

The decimal_fill and decimal_digits values affect the output of format_number(). Setting decimal_digits is like giving that value as the $precision argument to that function. Setting decimal_fill to a true value causes format_number() to append zeroes to the right of the decimal digits until the length is the specified number of digits.

neg_format is only used by format_negative() and is a string containing the letter 'x', where that letter will be replaced by a positive representation of the number being passed to that function. format_number() and format_price() utilize this feature by calling format_negative() if the number was less than 0.

kilo_suffix, mega_suffix, and giga_suffix are used by format_bytes() when the value is over 1024, 1024*1024, or 1024*1024*1024, respectively. The default values are "K", "M", and "G". Note: we can't do TERA because of integer overflows on 32-bit systems.

The only restrictions on decimal_point and thousands_sep are that they must not be digits, must not be identical, and must each be one character. There are no restrictions on int_curr_symbol.

For example, a German user might include this in their code:

  de = new Number.Format({thousands_sep   : '.',
                          decimal_point   : ',',
                          int_curr_symbol : 'DEM'});
  formatted = de.format_number(number);

METHODS

new({ param1 : value1, ... })

Creates a new Number.Format object. Valid keys for the args are any of the parameters described above. Keys must be in all lowercase. Example:

  de = new Number.Format({thousands_sep   : '.',
                          decimal_point   : ',',
                          int_curr_symbol : 'DEM'});
round(number, precision)

Rounds the number to the specified precision. If precision is omitted, the value of the decimal_digits parameter is used (default value 2). Both input and output are numeric (the function uses math operators rather than string manipulation to do its job), The value of precision may be any integer, positive or negative. Examples:

  round(3.14159)       yields    3.14
  round(3.14159, 4)    yields    3.1416
  round(42.00, 4)      yields    42
  round(1234, -2)      yields    1200
Since this is a mathematical rather than string oriented function, there will be no trailing zeroes to the right of the decimal point, and the decimal_point and thousands_sep variables are ignored. To format your number using the decimal_point and thousands_sep variables, use format_number() instead.

format_number(number, precision, trailing_zeroes)

Formats a number by adding thousands_sep between each set of 3 digits to the left of the decimal point, substituting decimal_point for the decimal point, and rounding to the specified precision using round(). Note that precision is a maximum precision specifier; trailing zeroes will only appear in the output if trailing_zeroes is provided, or the parameter decimal_fill is set, with a value that is true. If precision is omitted, the value of the decimal_digits parameter (default value of 2) is used. Examples:

  format_number(12345.6789)      yields   '12,345.68'
  format_number(123456.789, 2)   yields   '123,456.79'
  format_number(1234567.89, 2)   yields   '1,234,567.89'
  format_number(1234567.8, 2)    yields   '1,234,567.8'
  format_number(1234567.8, 2, 1) yields   '1,234,567.80'
  format_number(1.23456789, 6)   yields   '1.234568'
Of course the output would have your values of thousands_sep and decimal_point instead of ',' and '.' respectively.

format_negative(number, picture)

Formats a negative number. Picture should be a string that contains the letter x where the number should be inserted. For example, for standard negative numbers you might use ``-x'', while for accounting purposes you might use ``(x)''. If the specified number begins with a ``-'' character, that will be removed before formatting, but formatting will occur whether or not the number is negative.

format_picture(number, picture)

Returns a string based on picture with the # characters replaced by digits from number. If the length of the integer part of number is too large to fit, the # characters are replaced with asterisks (*) instead. Examples:

  format_picture(100.023, 'USD ##,###.##')   yields   'USD    100.02'
  format_picture(1000.23, 'USD ##,###.##')   yields   'USD  1,000.23'
  format_picture(10002.3, 'USD ##,###.##')   yields   'USD 10,002.30'
  format_picture(100023,  'USD ##,###.##')   yields   'USD **,***.**'
  format_picture(1.00023, 'USD #.###,###')   yields   'USD 1.002,300'
The comma (,) and period (.) you see in the picture examples should match the values of thousands_sep and decimal_point, respectively, for proper operation. However, the thousands_sep characters in picture need not occur every three digits; the only use of that variable by this function is to remove leading commas (see the first example above). There may not be more than one instance of decimal_point in picture.

The value of neg_format is used to determine how negative numbers are displayed. The result of this is that the output of this function my have unexpected spaces before and/or after the number. This is necessary so that positive and negative numbers are formatted into a space the same size. If you are only using positive numbers and want to avoid this problem, set neg_format to "x".

format_price(number, precision)

Returns a string containing number formatted similarly to format_number(), except that the decimal portion may have trailing zeroes added to make it be exactly precision characters long, and the currency string will be prefixed.

If the int_curr_symbol attribute of the object is the empty string, no currency will be added.

If precision is not provided, the default of 2 will be used. Examples:

  format_price(12.95)   yields   'USD 12.95'
  format_price(12)      yields   'USD 12.00'
  format_price(12, 3)   yields   '12.000'
The third example assumes that int_curr_symbol is the empty string.

format_bytes(number, precision)

Returns a string containing number formatted similarly to format_number(), except that if the number is over 1024, it will be divided by 1024 and kilo_suffix appended to the end; or if it is over 1048576 (1024*1024), it will be divided by 1048576 and mega_suffix appended to the end; or if it is over 1073741824 (1024*1024*1024), it will be divided by 1073741824 and giga_suffix appended to the end. Negative values will result in an error.

If precision is not provided, the default of 2 will be used. Examples:

  format_bytes(12.95)   yields   '12.95'
  format_bytes(2048)    yields   '2K'
  format_bytes(1048576) yields   '1M'
unformat_number(formatted)

Converts a string as returned by format_number(), format_price(), or format_picture(), and returns the corresponding value as a numeric scalar. Returns undef if the number does not contain any digits. Examples:

  unformat_number('USD 12.95')   yields   12.95
  unformat_number('USD 12.00')   yields   12
  unformat_number('foobar')      yields   undef
  unformat_number('1234-567@.8') yields   1234567.8
The value of decimal_point is used to determine where to separate the integer and decimal portions of the input. All other non-digit characters, including but not limited to int_curr_symbol and thousands_sep, are removed.

If the number matches the pattern of neg_format or there is a ``-'' character before any of the digits, then a negative number is returned.

If the number ends with the kilo_suffix, mega_suffix, or giga_suffix characters, then the number returned will be scaled appropriately.

ACKNOWLEDGEMENTS

This library is a direct port of the Number::Format perl module written by William R. Ward. So the majority of the work was done by William. I merely translated the methods from perl to the equivalent JavaScript code.

BUGS

No known bugs at this time. Please report any problems to the author.

SEE ALSO

JSAN

AUTHOR

Cees Hek <ceeshek@gmail.com>

COPYRIGHT

  Copyright (c) 2006 Cees Hek.  All rights reserved.
  This module is free software; you can redistribute it and/or modify it
  under the terms of the Artistic license.
// Set up namepace
if (!Number) var Number = {};

Number.Format = function (options) {
    // Set all requested options
    for (var key in options)
        this[key] = options[key];

    // Set all default options that were not assigned a value
    if (this.mon_decimal_point == undefined) this.mon_decimal_point = Number.Format.DECIMAL_POINT;
    if (this.mon_thousands_sep == undefined) this.mon_thousands_sep = Number.Format.THOUSANDS_SEP;
    if (this.int_curr_symbol   == undefined) this.int_curr_symbol   = Number.Format.INT_CURR_SYMBOL;
    if (this.decimal_digits    == undefined) this.decimal_digits    = Number.Format.DECIMAL_DIGITS;
    if (this.decimal_fill      == undefined) this.decimal_fill      = Number.Format.DECIMAL_FILL;
    if (this.neg_format        == undefined) this.neg_format        = Number.Format.NEG_FORMAT;
    if (this.kilo_suffix       == undefined) this.kilo_suffix       = Number.Format.KILO_SUFFIX;
    if (this.mega_suffix       == undefined) this.mega_suffix       = Number.Format.MEGA_SUFFIX;
    if (this.giga_suffix       == undefined) this.giga_suffix       = Number.Format.GIGA_SUFFIX;
    if (this.thousands_sep     == undefined) this.thousands_sep     = this.mon_thousands_sep;
    if (this.decimal_point     == undefined) this.decimal_point     = this.mon_decimal_point;
}

Number.Format.VERSION = '0.03';

// Exporter System for JSAN
Number.Format.EXPORT_OK = [
    'round',
    'format_number',
    'format_negative',
    'format_picture',
    'format_price',
    'format_bytes',
    'unformat_number'
];

Number.Format.DECIMAL_POINT   = '.';
Number.Format.THOUSANDS_SEP   = ',';
Number.Format.INT_CURR_SYMBOL = 'USD';
Number.Format.DECIMAL_DIGITS  = 2;
Number.Format.DECIMAL_FILL    = 0;
Number.Format.NEG_FORMAT      = '-x';
Number.Format.KILO_SUFFIX     = 'K';
Number.Format.MEGA_SUFFIX     = 'M';
Number.Format.GIGA_SUFFIX     = 'G';

Number.Format.DEFAULT_LOCALE = {
    mon_thousands_sep : Number.Format.THOUSANDS_SEP,
    mon_decimal_point : Number.Format.DECIMAL_POINT,
    thousands_sep     : Number.Format.THOUSANDS_SEP,
    decimal_point     : Number.Format.DECIMAL_POINT,
    int_curr_symbol   : Number.Format.INT_CURR_SYMBOL,
    neg_format        : Number.Format.NEG_FORMAT,
    kilo_suffix       : Number.Format.KILO_SUFFIX,
    mega_suffix       : Number.Format.MEGA_SUFFIX,
    giga_suffix       : Number.Format.GIGA_SUFFIX,
    decimal_digits    : Number.Format.DECIMAL_DIGITS,
    decimal_fill      : Number.Format.DECIMAL_FILL
};


Number.Format.prototype.round = function (number, precision) {
    if (precision == undefined)
        precision = this.decimal_digits;
    if (precision == undefined)
        precision = 2;
    if (number == undefined)
        number = 0;

    var sign = (number < 0) ? -1 : 1;
    var multiplier = Math.pow(10, precision);
    var result = Math.abs(number);
    result = Math.floor((result * multiplier) + .5000001) / multiplier;
    if (sign < 0)
        result = result * -1;
    return result;
}

Number.Format.prototype.format_number = function (number, precision, trailing_zeroes) {
    this._check_seps();
    // Set defaults and standardize number
    if (precision == undefined)
        precision = this.decimal_digits;
    if (trailing_zeroes == undefined)
        trailing_zeroes = this.decimal_fill;

    // Handle negative numbers
    var sign = (number < 0) ? -1 : 1;
    if (sign < 0)
        number = Math.abs(number);
    number = this.round(number, precision);

    // Split integer and decimal parts of the number and add commas
    var integer = Math.floor(number);
    var decimal;

    var str_integer = new String(integer);
    var str_number  = new String(number);
    if (str_integer.length < str_number.length)
        decimal = str_number.substr(str_integer.length + 1)
    if (decimal == undefined)
        decimal = '';

    // Add trailing 0's if $trailing_zeroes is set.
    if (trailing_zeroes && precision > decimal.length)
        for(var i=decimal.length; i < precision; i++)
            decimal += '0'

    // Add leading 0's so length of integer is divisible by 3
    var extra_zeros = 3 - (str_integer.length % 3);
    for(var i=0; i < extra_zeros; i++)
        str_integer = '0' + str_integer;

    // Split integer into groups of 3 characters and insert commas
    str_integer = str_integer.match(/(...)/g).join(this.thousands_sep);

    // Strip off leading zeroes and/or comma
    str_integer = str_integer.replace(/^0+/, '');
    if (str_integer.substr(0,1) == this.thousands_sep)
        str_integer = str_integer.substr(1);
    if (str_integer == '')
        str_integer = '0';

    // Combine integer and decimal parts and return the result.
    var result = (decimal.length > 0) ? str_integer + this.decimal_point + decimal : str_integer;

    return (sign < 0) ? this.format_negative(result) : result;
}

Number.Format.prototype.format_negative = function (number, format) {
    if (format == undefined)
        format = this.neg_format;
    if (format.indexOf('x') < 0)
        throw new Error("Letter x must be present in picture in format_negative");
    number = new String(number);
    number = number.replace(/^-/,'');
    format = format.replace(/x/, number);
    return format;
}

Number.Format.prototype.format_picture = function (number, picture) {
    this._check_seps();

    if (picture == undefined)
        picture = '';
    if (number == undefined)
        number = 0;

    // Handle negative numbers
    var neg_prefix_lst = this.neg_format.match(/^([^x]+)/);
    var neg_prefix = (neg_prefix_lst == undefined) ? '' : neg_prefix_lst.shift();
    var pic_prefix_lst = picture.match(/^([^\#]+)/);
    var pic_prefix = (pic_prefix_lst == undefined) ? '' : pic_prefix_lst.shift();
    var neg_pic = this.neg_format;
    var pos_pic = this.neg_format
    pos_pic = pos_pic.replace(/[^x\s]/g, ' ');
    var pos_prefix = neg_prefix;
    pos_prefix = pos_prefix.replace(/[^x\s]/g, ' ');
    neg_pic = neg_pic.replace(/x/, picture);
    pos_pic = pos_pic.replace(/x/, picture);
    var sign = (number < 0) ? -1 : 1;
    if (sign < 0)
        number = Math.abs(number);
    picture = (sign < 0) ? neg_pic : pos_pic;
    var sign_prefix = (sign < 0) ? neg_prefix : pos_prefix;

    // Split up the picture and throw and error if there is more than one $DECIMAL_POINT
    var pic_list = picture.split(this.decimal_point, 3);
    if (pic_list[2] != undefined)
        throw new Error("format_picture(number, picture): Only one decimal separator (decimal_point) permitted in picture.");
    var pic_int = pic_list[0];
    var pic_dec = pic_list[1];
    if (pic_int == undefined)
        pic_int = '';
    if (pic_dec == undefined)
        pic_dec = '';

    // Obtain precision from the length of the decimal part...
    var precision = pic_dec;
    precision = precision.replace(/[^\#]/g, '');
    precision = precision.length;

    // Format the number
    number = this.round(number, precision);

    // Obtain the length of the integer portion just like we did for $precision
    var intsize = pic_int;
    intsize = intsize.replace(/[^\#]/g, '');
    intsize = intsize.length;

    // Split up $number same as we did for $picture earlier
    var number_str = new String(number);
    var num_list = number_str.split('.', 2);
    num_int = num_list[0];
    num_dec = num_list[1];
    if (num_int == undefined)
        num_int = '';
    if (num_dec == undefined)
        num_dec = '';

    // Check if the integer part will fit in the picture
    if (num_int.length > intsize) {
        picture = picture.replace(/\#/g, '*');
        picture = picture.substr(sign_prefix.length + pic_prefix.length); // remove the sign prefix and pic prefix
        picture = picture.replace(/^(\s*)/, "$1" + pic_prefix + sign_prefix);
        return picture;
    }

    // Split each portion of number and picture into arrays of characters
    var num_int_lst = num_int.split('');
    var num_dec_lst = num_dec.split('');
    var pic_int_lst = pic_int.split('');
    var pic_dec_lst = pic_dec.split('');


    // Now we copy those characters into @result.
    var result = new Array();
    if (picture.indexOf(this.decimal_point) >= 0)
        result.push(this.decimal_point);

    // For each characture in the decimal part of the picture, replace '#'
    // signs with digits from the number.
    for (var index in pic_dec_lst) {
        var chr = pic_dec_lst[index];
        if (chr == '#')
            chr = num_dec_lst.shift() || 0;
        result.push(chr);
    }

    // For each character in the integer part of the picture (moving right
    // to left this time), replace '#' signs with digits from the number,
    // or spaces if we've run out of numbers.
    while (pic_int_lst.length > 0) {
        var chr = pic_int_lst.pop();
        if (chr == '#')
            chr = num_int_lst.pop();
        if (chr == undefined || chr == this.thousands_sep && num_int_lst.length == 0)
            chr = ' ';
        result.unshift(chr);
    }

    // Combine @result into a string and return it.
    var result = result.join('');
    result = result.substr(sign_prefix.length + pic_prefix.length); // remove the sign prefix and pic prefix
    for (var index in result)
        if (result.substr(index, 1) != ' ')
            break;
    var spaces = result.substr(0,index);
    var result = result.substr(index);

    result = result.replace(/^(\s*)/, pic_prefix + spaces + sign_prefix);
    return result;
}


Number.Format.prototype.format_price = function (number, precision) {

    if (number == undefined)
        number = 0;
    if (precision == undefined)
        precision = this.decimal_digits;
    if (precision == undefined)
        precision = 2;

    var sign = (number < 0) ? -1 : 1;
    if (sign < 0)
        number = Math.abs(number);

    number = this.format_number(number, precision);
    // Now we make sure the decimal part has enough zeroes
    var number_str = new String(number);
    var parts = number_str.split(this.mon_decimal_point, 2);
    var integer = parts[0];
    var decimal = parts[1];

    if (decimal == undefined) {
        decimal = '';
        for (var index = 0; index < precision; index++)
            decimal += '0';
    }
    var pad_count = precision - decimal.length;
    for (var index = 0; index < pad_count; index++)
        decimal += '0';

    // Combine it all back together and return it.
    var symbol = this.int_curr_symbol.replace(/\s*$/, ' ');
    var result = precision > 0 ? '' + integer + this.mon_decimal_point + decimal : '' + integer;
    result = symbol + result;

    return (sign < 0) ? this.format_negative(result) : result;
}

var giga_limit = 2 << 29;
var mega_limit = 2 << 19;
var kilo_limit = 2 << 9;

Number.Format.prototype.format_bytes = function (number, precision) {

    if (number == undefined)
        number = 0;
    if (precision == undefined)
        precision = this.decimal_digits;
    if (precision == undefined)
        precision = 2;

    if (number < 0)
        throw new Error("format_bytes(number, precision): Negative numbers not allowed in format_bytes.");

    var suffix = "";
    if (number > giga_limit) {
        number /= giga_limit;
        suffix = this.giga_suffix;
    }
    else if (number > mega_limit) {
        number /= mega_limit;
        suffix = this.mega_suffix;
    }
    else if (number > kilo_limit) {
        number /= kilo_limit;
        suffix = this.kilo_suffix;
    }

    number = this.format_number(number, precision);

    return number + suffix;
}

Number.Format.prototype.unformat_number = function (formatted) {
    this._check_seps();

    // Make sure there is at least one digit
    if (formatted.search(/\d/) == -1)
        return;

    // strip any spaces from the end
    formatted = formatted.replace(/\s*$/, '');

    // Detect if it ends with the kilo, mega or giga suffix.
    var multiplier;
    if (formatted.substr(formatted.length - this.kilo_suffix.length) == this.kilo_suffix)
        multiplier = kilo_limit;
    if (formatted.substr(formatted.length - this.mega_suffix.length) == this.mega_suffix)
        multiplier = mega_limit;
    if (formatted.substr(formatted.length - this.giga_suffix.length) == this.giga_suffix)
        multiplier = giga_limit;

    // Split number into integer and decimal parts
    var parts = formatted.split(this.decimal_point, 3);
    if (parts[2] != undefined)
        throw new Error("unformat_number(formatted): Only one decimal separator permitted.");
    var integer = parts[0];
    var decimal = parts[1];

    // It's negative if the first non-digit character is a -
    var sign = formatted.search(/^\D*\-/) == -1 ? 1 : -1;
    // It is also negative if the number is bounded by the neg_format picture
    var neg_parts = this.neg_format.split('x', 2);
    if ( (neg_parts[0] == undefined || formatted.indexOf(neg_parts[0]) > -1) &&
         (neg_parts[1] == undefined || formatted.indexOf(neg_parts[1]) > -1) )
        sign = -1;

    // Strip out all non-digits from integer and decimal parts
    if (integer == undefined)
        integer = '';
    if (decimal == undefined)
        decimal = '';
    integer = integer.replace(/\D/g, '');
    decimal = decimal.replace(/\D/g, '');

    // Join back up, using period, and parse it
    var number = integer + '.' + decimal;
    number = parseFloat(number);
    if (sign < 0)
        number = 0 - number;

    // Scale the number if it ended in a suffix.
    if (multiplier)
        number *= multiplier;

    return number;
}

Number.Format.prototype._check_seps = function () {
    if (this.thousands_sep.length != 1)
        throw new Error("Number.Format: {thousands_sep} must be one character");
    if (this.thousands_sep.search(/\d/) > -1)
        throw new Error("Number.Format: {thousands_sep} may not be numeric");
    if (this.decimal_point.length != 1)
        throw new Error("Number.Format: {decimal_point} must be one character");
    if (this.decimal_point.search(/\d/) > -1)
        throw new Error("Number.Format: {decimal_point} may not be numeric");
    if (this.decimal_point == this.thousands_sep)
        throw new Error("Number.Format: {thousands_sep} and {decimal_point} may not be equal");
}

/*


=head1 NAME

Number.Format - JavaScript library for formatting numbers

=head1 SYNOPSIS

  JSAN.use('Number.Format');
  nf = new Number.Format();
  formatted = nf.round(number, precision);
  formatted = nf.format_number(number, precision, trailing_zeroes);
  formatted = nf.format_negative(number, picture);
  formatted = nf.format_picture(number, picture);
  formatted = nf.format_price(number, precision);
  formatted = nf.format_bytes(number, precision);
  number    = nf.unformat_number(formatted);

=head1 DESCRIPTION

These functions provide an easy means of formatting numbers in a manner
suitable for displaying to the user.  This library is a direct port of the
Number::Format perl module written by William R. Ward.

To use this library you need to create a Number.Format object, which you can
think of as a formatting engine.  Then call one of the object methods that are
defined below.  The constructor C<new()> can be used to set the parameters of
the formatting engine.  Valid parameters are:

  thousands_sep     - character inserted between groups of 3 digits
  decimal_point     - character separating integer and fractional parts
  mon_thousands_sep - like 'thousands_sep', but used for format_price
  mon_decimal_point - like 'decimal_point', but used for format_price
  int_curr_symbol   - character(s) denoting currency (see format_price())
  decimal_digits    - number of digits to the right of dec point (def 2)
  decimal_fill      - boolean; whether to add zeroes to fill out decimal
  neg_format        - format to display negative numbers (def ``-x'')
  kilo_suffix       - suffix to add when format_bytes formats kilobytes
  mega_suffix       -    "    "  "    "        "         "    megabytes
  giga_suffix       -    "    "  "    "        "         "    gigabytes

The default values for all the parameters are:

  thousands_sep     = ','
  decimal_point     = '.'
  mon_thousands_sep = ','
  mon_decimal_point = '.'
  int_curr_symbol   = 'USD'
  decimal_digits    = 2
  decimal_fill      = 0
  neg_format        = '-x'
  kilo_suffix       = 'K'
  mega_suffix       = 'M'
  giga_suffix       = 'G'

Once the Number.Format object has been created, you can alter any of the
parameters directly, by changing their value in the object.

  nf = new Number.Format( { kilo_suffix : 'kb' } );
  nf.kilo_suffix = 'Kb';

The C<decimal_fill> and C<decimal_digits> values affect the output of
C<format_number()>.  Setting C<decimal_digits> is like giving that value as the
C<$precision> argument to that function.  Setting C<decimal_fill> to a true
value causes C<format_number()> to append zeroes to the right of the decimal
digits until the length is the specified number of digits.

C<neg_format> is only used by C<format_negative()> and is a string containing
the letter 'x', where that letter will be replaced by a positive representation
of the number being passed to that function.  C<format_number()> and
C<format_price()> utilize this feature by calling C<format_negative()> if the
number was less than 0.

C<kilo_suffix>, C<mega_suffix>, and C<giga_suffix> are used by
C<format_bytes()> when the value is over 1024, 1024*1024, or 1024*1024*1024,
respectively.  The default values are "K", "M", and "G".  Note: we can't do
TERA because of integer overflows on 32-bit systems.

The only restrictions on C<decimal_point> and C<thousands_sep> are that they
must not be digits, must not be identical, and must each be one character.
There are no restrictions on C<int_curr_symbol>.

For example, a German user might include this in their code:

  de = new Number.Format({thousands_sep   : '.',
                          decimal_point   : ',',
                          int_curr_symbol : 'DEM'});
  formatted = de.format_number(number);


=head1 METHODS

=over 4

=item new({ param1 : value1, ... })

Creates a new Number.Format object.  Valid keys for the args are any of the
parameters described above.  Keys must be in all lowercase.  Example:

  de = new Number.Format({thousands_sep   : '.',
                          decimal_point   : ',',
                          int_curr_symbol : 'DEM'});


=item round(number, precision)

Rounds the number to the specified precision.  If C<precision> is omitted, the
value of the C<decimal_digits> parameter is used (default value 2).  Both input
and output are numeric (the function uses math operators rather than string
manipulation to do its job), The value of C<precision> may be any integer,
positive or negative. Examples:

  round(3.14159)       yields    3.14
  round(3.14159, 4)    yields    3.1416
  round(42.00, 4)      yields    42
  round(1234, -2)      yields    1200

Since this is a mathematical rather than string oriented function, there will
be no trailing zeroes to the right of the decimal point, and the
C<decimal_point> and C<thousands_sep> variables are ignored.  To format your
number using the C<decimal_point> and C<thousands_sep> variables, use
C<format_number()> instead.

=item format_number(number, precision, trailing_zeroes)

Formats a number by adding C<thousands_sep> between each set of 3 digits to the
left of the decimal point, substituting C<decimal_point> for the decimal point,
and rounding to the specified precision using C<round()>.  Note that
C<precision> is a I<maximum> precision specifier; trailing zeroes will only
appear in the output if C<trailing_zeroes> is provided, or the parameter
C<decimal_fill> is set, with a value that is true.  If C<precision> is omitted,
the value of the C<decimal_digits> parameter (default value of 2) is used.
Examples:

  format_number(12345.6789)      yields   '12,345.68'
  format_number(123456.789, 2)   yields   '123,456.79'
  format_number(1234567.89, 2)   yields   '1,234,567.89'
  format_number(1234567.8, 2)    yields   '1,234,567.8'
  format_number(1234567.8, 2, 1) yields   '1,234,567.80'
  format_number(1.23456789, 6)   yields   '1.234568'

Of course the output would have your values of C<thousands_sep> and
C<decimal_point> instead of ',' and '.' respectively.

=item format_negative(number, picture)

Formats a negative number.  Picture should be a string that contains the letter
C<x> where the number should be inserted.  For example, for standard negative
numbers you might use ``C<-x>'', while for accounting purposes you might use
``C<(x)>''.  If the specified number begins with a ``-'' character, that will
be removed before formatting, but formatting will occur whether or not the
number is negative.


=item format_picture(number, picture)

Returns a string based on C<picture> with the C<#> characters
replaced by digits from C<number>.  If the length of the integer part
of number is too large to fit, the C<#> characters are replaced with
asterisks (C<*>) instead.  Examples:

  format_picture(100.023, 'USD ##,###.##')   yields   'USD    100.02'
  format_picture(1000.23, 'USD ##,###.##')   yields   'USD  1,000.23'
  format_picture(10002.3, 'USD ##,###.##')   yields   'USD 10,002.30'
  format_picture(100023,  'USD ##,###.##')   yields   'USD **,***.**'
  format_picture(1.00023, 'USD #.###,###')   yields   'USD 1.002,300'

The comma (,) and period (.) you see in the picture examples should match the
values of C<thousands_sep> and C<decimal_point>, respectively, for proper
operation.  However, the C<thousands_sep> characters in C<picture> need not
occur every three digits; the I<only> use of that variable by this function is
to remove leading commas (see the first example above).  There may not be more
than one instance of C<decimal_point> in C<picture>.

The value of C<neg_format> is used to determine how negative numbers are
displayed.  The result of this is that the output of this function my have
unexpected spaces before and/or after the number.  This is necessary so that
positive and negative numbers are formatted into a space the same size.  If you
are only using positive numbers and want to avoid this problem, set neg_format
to "x".


=item format_price(number, precision)

Returns a string containing C<number> formatted similarly to
C<format_number()>, except that the decimal portion may have trailing zeroes
added to make it be exactly C<precision> characters long, and the currency
string will be prefixed.

If the C<int_curr_symbol> attribute of the object is the empty string, no
currency will be added.

If C<precision> is not provided, the default of 2 will be used.  Examples:

  format_price(12.95)   yields   'USD 12.95'
  format_price(12)      yields   'USD 12.00'
  format_price(12, 3)   yields   '12.000'

The third example assumes that C<int_curr_symbol> is the empty string.


=item format_bytes(number, precision)

Returns a string containing C<number> formatted similarly to
C<format_number()>, except that if the number is over 1024, it will be divided
by 1024 and C<kilo_suffix> appended to the end; or if it is over 1048576
(1024*1024), it will be divided by 1048576 and C<mega_suffix> appended to the
end; or if it is over 1073741824 (1024*1024*1024), it will be divided by
1073741824 and C<giga_suffix> appended to the end.  Negative values will result
in an error.

If C<precision> is not provided, the default of 2 will be used.  Examples:

  format_bytes(12.95)   yields   '12.95'
  format_bytes(2048)    yields   '2K'
  format_bytes(1048576) yields   '1M'


=item unformat_number(formatted)

Converts a string as returned by C<format_number()>, C<format_price()>, or
C<format_picture()>, and returns the corresponding value as a numeric scalar.
Returns C<undef> if the number does not contain any digits.  Examples:

  unformat_number('USD 12.95')   yields   12.95
  unformat_number('USD 12.00')   yields   12
  unformat_number('foobar')      yields   undef
  unformat_number('1234-567@.8') yields   1234567.8

The value of C<decimal_point> is used to determine where to separate the
integer and decimal portions of the input.  All other non-digit characters,
including but not limited to C<int_curr_symbol> and C<thousands_sep>, are
removed.

If the number matches the pattern of C<neg_format> I<or> there is a ``-''
character before any of the digits, then a negative number is returned.

If the number ends with the C<kilo_suffix>, C<mega_suffix>, or C<giga_suffix>
characters, then the number returned will be scaled appropriately.


=back

=head1 ACKNOWLEDGEMENTS

This library is a direct port of the Number::Format perl module written by
William R. Ward.  So the majority of the work was done by William.  I merely
translated the methods from perl to the equivalent JavaScript code.

=head1 BUGS

No known bugs at this time.  Please report any problems to the author.

=head1 SEE ALSO

C<JSAN>

=head1 AUTHOR

Cees Hek <F<ceeshek@gmail.com>>

=head1 COPYRIGHT

  Copyright (c) 2006 Cees Hek.  All rights reserved.
  This module is free software; you can redistribute it and/or modify it
  under the terms of the Artistic license.

=cut

*/