1 /** 2 * Returns a default number format. 3 * 4 * @class Represents a number format, converting between a <tt>number</tt> and a 5 * <tt>string</tt>. This class allows numbers to be formatted with variable 6 * precision (both for the integral and fractional part of the number), optional 7 * thousands grouping, and optional padding. The thousands (",") and decimal 8 * (".") separator can be customized. 9 * 10 * @returns {pv.Format.number} a number format. 11 */ 12 pv.Format.number = function() { 13 var mini = 0, // default minimum integer digits 14 maxi = Infinity, // default maximum integer digits 15 mins = 0, // mini, including group separators 16 minf = 0, // default minimum fraction digits 17 maxf = 0, // default maximum fraction digits 18 maxk = 1, // 10^maxf 19 padi = "0", // default integer pad 20 padf = "0", // default fraction pad 21 padg = true, // whether group separator affects integer padding 22 decimal = ".", // default decimal separator 23 group = ","; // default group separator 24 25 /** @private */ 26 function format(x) { 27 /* Round the fractional part, and split on decimal separator. */ 28 if (Infinity > maxf) x = Math.round(x * maxk) / maxk; 29 var s = String(Math.abs(x)).split("."); 30 31 /* Pad, truncate and group the integral part. */ 32 var i = s[0], n = (x < 0) ? "-" : ""; 33 if (i.length > maxi) i = i.substring(i.length - maxi); 34 if (padg && (i.length < mini)) i = n + new Array(mini - i.length + 1).join(padi) + i; 35 if (i.length > 3) i = i.replace(/\B(?=(?:\d{3})+(?!\d))/g, group); 36 if (!padg && (i.length < mins)) i = new Array(mins - i.length + 1).join(padi) + n + i; 37 s[0] = i; 38 39 /* Pad the fractional part. */ 40 var f = s[1] || ""; 41 if (f.length < minf) s[1] = f + new Array(minf - f.length + 1).join(padf); 42 43 return s.join(decimal); 44 } 45 46 /** 47 * @function 48 * @name pv.Format.number.prototype.format 49 * @param {number} x 50 * @returns {string} 51 */ 52 format.format = format; 53 54 /** 55 * Parses the specified string as a number. Before parsing, leading and 56 * trailing padding is removed. Group separators are also removed, and the 57 * decimal separator is replaced with the standard point ("."). The integer 58 * part is truncated per the maximum integer digits, and the fraction part is 59 * rounded per the maximum fraction digits. 60 * 61 * @function 62 * @name pv.Format.number.prototype.parse 63 * @param {string} x the string to parse. 64 * @returns {number} the parsed number. 65 */ 66 format.parse = function(x) { 67 var re = pv.Format.re; 68 69 /* Remove leading and trailing padding. Split on the decimal separator. */ 70 var s = String(x) 71 .replace(new RegExp("^(" + re(padi) + ")*"), "") 72 .replace(new RegExp("(" + re(padf) + ")*$"), "") 73 .split(decimal); 74 75 /* Remove grouping and truncate the integral part. */ 76 var i = s[0].replace(new RegExp(re(group), "g"), ""); 77 if (i.length > maxi) i = i.substring(i.length - maxi); 78 79 /* Round the fractional part. */ 80 var f = s[1] ? Number("0." + s[1]) : 0; 81 if (Infinity > maxf) f = Math.round(f * maxk) / maxk; 82 83 return Math.round(i) + f; 84 }; 85 86 /** 87 * Sets or gets the minimum and maximum number of integer digits. This 88 * controls the number of decimal digits to display before the decimal 89 * separator for the integral part of the number. If the number of digits is 90 * smaller than the minimum, the digits are padded; if the number of digits is 91 * larger, the digits are truncated, showing only the lower-order digits. The 92 * default range is [0, Infinity]. 93 * 94 * <p>If only one argument is specified to this method, this value is used as 95 * both the minimum and maximum number. If no arguments are specified, a 96 * two-element array is returned containing the minimum and the maximum. 97 * 98 * @function 99 * @name pv.Format.number.prototype.integerDigits 100 * @param {number} [min] the minimum integer digits. 101 * @param {number} [max] the maximum integer digits. 102 * @returns {pv.Format.number} <tt>this</tt>, or the current integer digits. 103 */ 104 format.integerDigits = function(min, max) { 105 if (arguments.length) { 106 mini = Number(min); 107 maxi = (arguments.length > 1) ? Number(max) : mini; 108 mins = mini + Math.floor(mini / 3) * group.length; 109 return this; 110 } 111 return [mini, maxi]; 112 }; 113 114 /** 115 * Sets or gets the minimum and maximum number of fraction digits. The 116 * controls the number of decimal digits to display after the decimal 117 * separator for the fractional part of the number. If the number of digits is 118 * smaller than the minimum, the digits are padded; if the number of digits is 119 * larger, the fractional part is rounded, showing only the higher-order 120 * digits. The default range is [0, 0]. 121 * 122 * <p>If only one argument is specified to this method, this value is used as 123 * both the minimum and maximum number. If no arguments are specified, a 124 * two-element array is returned containing the minimum and the maximum. 125 * 126 * @function 127 * @name pv.Format.number.prototype.fractionDigits 128 * @param {number} [min] the minimum fraction digits. 129 * @param {number} [max] the maximum fraction digits. 130 * @returns {pv.Format.number} <tt>this</tt>, or the current fraction digits. 131 */ 132 format.fractionDigits = function(min, max) { 133 if (arguments.length) { 134 minf = Number(min); 135 maxf = (arguments.length > 1) ? Number(max) : minf; 136 maxk = Math.pow(10, maxf); 137 return this; 138 } 139 return [minf, maxf]; 140 }; 141 142 /** 143 * Sets or gets the character used to pad the integer part. The integer pad is 144 * used when the number of integer digits is smaller than the minimum. The 145 * default pad character is "0" (zero). 146 * 147 * @param {string} [x] the new pad character. 148 * @returns {pv.Format.number} <tt>this</tt> or the current pad character. 149 */ 150 format.integerPad = function(x) { 151 if (arguments.length) { 152 padi = String(x); 153 padg = /\d/.test(padi); 154 return this; 155 } 156 return padi; 157 }; 158 159 /** 160 * Sets or gets the character used to pad the fration part. The fraction pad 161 * is used when the number of fraction digits is smaller than the minimum. The 162 * default pad character is "0" (zero). 163 * 164 * @param {string} [x] the new pad character. 165 * @returns {pv.Format.number} <tt>this</tt> or the current pad character. 166 */ 167 format.fractionPad = function(x) { 168 if (arguments.length) { 169 padf = String(x); 170 return this; 171 } 172 return padf; 173 }; 174 175 /** 176 * Sets or gets the character used as the decimal point, separating the 177 * integer and fraction parts of the number. The default decimal point is ".". 178 * 179 * @param {string} [x] the new decimal separator. 180 * @returns {pv.Format.number} <tt>this</tt> or the current decimal separator. 181 */ 182 format.decimal = function(x) { 183 if (arguments.length) { 184 decimal = String(x); 185 return this; 186 } 187 return decimal; 188 }; 189 190 /** 191 * Sets or gets the character used as the group separator, grouping integer 192 * digits by thousands. The default decimal point is ",". Grouping can be 193 * disabled by using "" for the separator. 194 * 195 * @param {string} [x] the new group separator. 196 * @returns {pv.Format.number} <tt>this</tt> or the current group separator. 197 */ 198 format.group = function(x) { 199 if (arguments.length) { 200 group = x ? String(x) : ""; 201 mins = mini + Math.floor(mini / 3) * group.length; 202 return this; 203 } 204 return group; 205 }; 206 207 return format; 208 }; 209