1 /* ************************************************************************
3 qooxdoo - the new era of web development
8 2006 STZ-IDA, Germany, http://www.stz-ida.de
11 LGPL: http://www.gnu.org/licenses/lgpl.html
12 EPL: http://www.eclipse.org/org/documents/epl-v10.php
13 See the LICENSE file in the project's top-level directory for details.
16 * Til Schneider (til132)
18 ************************************************************************ */
20 /* ************************************************************************
22 * #require(qx.locale.Number)
24 ************************************************************************ */
27 * A formatter and parser for numbers.
29 * @param locale {String} optional locale to be used
31 qx.OO.defineClass("qx.util.format.NumberFormat", qx.util.format.Format,
33 qx.util.format.Format.call(this);
34 this._locale = locale;
39 * The minimum number of integer digits (digits before the decimal separator).
40 * Missing digits will be filled up with 0 ("19" -> "0019").
42 qx.OO.addProperty({ name:"minimumIntegerDigits", type:"number", defaultValue:0, allowNull:false });
45 * The maximum number of integer digits (superfluos digits will be cut off
48 qx.OO.addProperty({ name:"maximumIntegerDigits", type:"number", defaultValue:null });
51 * The minimum number of fraction digits (digits after the decimal separator).
52 * Missing digits will be filled up with 0 ("1.5" -> "1.500")
54 qx.OO.addProperty({ name:"minimumFractionDigits", type:"number", defaultValue:0, allowNull:false });
57 * The maximum number of fraction digits (digits after the decimal separator).
58 * Superflous digits will cause rounding ("1.8277" -> "1.83")
60 qx.OO.addProperty({ name:"maximumFractionDigits", type:"number", defaultValue:null });
62 /** Whether thousand groupings should be used {e.g. "1,432,234.65"}. */
63 qx.OO.addProperty({ name:"groupingUsed", type:"boolean", defaultValue:true, allowNull:false });
65 /** The prefix to put before the number {"EUR " -> "EUR 12.31"}. */
66 qx.OO.addProperty({ name:"prefix", type:"string", defaultValue:"", allowNull:false });
68 /** Sets the postfix to put after the number {" %" -> "56.13 %"}. */
69 qx.OO.addProperty({ name:"postfix", type:"string", defaultValue:"", allowNull:false });
75 * @param num {number} the number to format.
76 * @return {String} the formatted number as a string.
78 qx.Proto.format = function(num) {
79 var NumberFormat = qx.util.format.NumberFormat;
81 var negative = (num < 0);
85 if (this.getMaximumFractionDigits() != null) {
87 var mover = Math.pow(10, this.getMaximumFractionDigits());
88 num = Math.round(num * mover) / mover;
91 if (num != 0) { // Math.log(0) = -Infinity
92 var integerDigits = Math.max(parseInt(Math.log(num) / Math.LN10) + 1, 1);
97 var numStr = "" + num;
99 // Prepare the integer part
100 var integerStr = numStr.substring(0, integerDigits);
101 while (integerStr.length < this.getMinimumIntegerDigits()) {
102 integerStr = "0" + integerStr;
104 if (this.getMaximumIntegerDigits() != null && integerStr.length > this.getMaximumIntegerDigits()) {
105 // NOTE: We cut off even though we did rounding before, because there
106 // may be rounding errors ("12.24000000000001" -> "12.24")
107 integerStr = integerStr.substring(integerStr.length - this.getMaximumIntegerDigits());
110 // Prepare the fraction part
111 var fractionStr = numStr.substring(integerDigits + 1);
112 while (fractionStr.length < this.getMinimumFractionDigits()) {
115 if (this.getMaximumFractionDigits() != null && fractionStr.length > this.getMaximumFractionDigits()) {
116 // We have already rounded -> Just cut off the rest
117 fractionStr = fractionStr.substring(0, this.getMaximumFractionDigits());
120 // Add the thousand groupings
121 if (this.getGroupingUsed()) {
122 var origIntegerStr = integerStr;
125 for (groupPos = origIntegerStr.length; groupPos > 3; groupPos -= 3) {
126 integerStr = "" + qx.locale.Number.getGroupSeparator(this._locale)
127 + origIntegerStr.substring(groupPos - 3, groupPos) + integerStr;
129 integerStr = origIntegerStr.substring(0, groupPos) + integerStr;
132 // Workaround: prefix and postfix are null even their defaultValue is "" and
133 // allowNull is set to false?!?
134 var prefix = this.getPrefix() ? this.getPrefix() : "";
135 var postfix = this.getPostfix() ? this.getPostfix() : "";
137 // Assemble the number
138 var str = prefix + (negative ? "-" : "") + integerStr;
139 if (fractionStr.length > 0) {
140 str += "" + qx.locale.Number.getDecimalSeparator(this._locale) + fractionStr;
151 * @param str {String} the string to parse.
153 * @return {Double} the number.
155 qx.Proto.parse = function(str) {
156 var NumberFormat = qx.util.format.NumberFormat;
158 // use the escaped separators for regexp
159 var groupSepEsc = qx.lang.String.escapeRegexpChars(qx.locale.Number.getGroupSeparator(this._locale)+"");
160 var decimalSepEsc = qx.lang.String.escapeRegexpChars(qx.locale.Number.getDecimalSeparator(this._locale)+"");
162 var regex = new RegExp(qx.lang.String.escapeRegexpChars(this.getPrefix())
163 + '(-)?([0-9' + groupSepEsc + ']+)'
164 + '(' + decimalSepEsc + '\\d+)?'
165 + qx.lang.String.escapeRegexpChars(this.getPostfix()));
167 var hit = regex.exec(str);
169 throw new Error("Number string '" + str + "' does not match the number format");
172 var negative = (hit[1] == "-");
173 var integerStr = hit[2];
174 var fractionStr = hit[3];
176 // Remove the thousand groupings
177 integerStr = integerStr.replace(new RegExp(groupSepEsc), "");
179 var asStr = (negative ? "-" : "") + integerStr;
180 if (fractionStr != null && fractionStr.length != 0) {
181 // Remove the leading decimal separator from the fractions string
182 fractionStr = fractionStr.replace(new RegExp(decimalSepEsc),"");
183 asStr += "." + fractionStr;
185 return parseFloat(asStr);
190 * Returns the default number format.
192 * @return {NumberFormat} the default number format.
194 qx.Class.getInstance = function() {
195 var NumberFormat = qx.util.format.NumberFormat;
196 if (NumberFormat._instance == null) {
197 NumberFormat._instance = new NumberFormat();
199 return NumberFormat._instance;
204 * Returns an integer number format.
206 * @return {NumberFormat} an integer number format.
208 qx.Class.getIntegerInstance = function() {
209 var NumberFormat = qx.util.format.NumberFormat;
210 if (NumberFormat._integerInstance == null) {
211 NumberFormat._integerInstance = new NumberFormat();
212 NumberFormat._integerInstance.setMaximumFractionDigits(0);
214 return NumberFormat._integerInstance;