r20517: re-add cleaned-up webapps
[kai/samba.git] / webapps / qooxdoo-0.6.3-sdk / frontend / framework / source / class / qx / util / format / NumberFormat.js
1 /* ************************************************************************
2
3    qooxdoo - the new era of web development
4
5    http://qooxdoo.org
6
7    Copyright:
8      2006 by STZ-IDA, Germany, http://www.stz-ida.de
9
10    License:
11      LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
12
13    Authors:
14      * Til Schneider (til132)
15
16 ************************************************************************ */
17
18 /* ************************************************************************
19
20
21 ************************************************************************ */
22
23 /**
24  * A formatter and parser for numbers.
25  */
26 qx.OO.defineClass("qx.util.format.NumberFormat", qx.util.format.Format,
27 function() {
28   qx.util.format.Format.call(this);
29 });
30
31
32 /**
33  * The minimum number of integer digits (digits before the decimal separator).
34  * Missing digits will be filled up with 0 ("19" -> "0019").
35  */
36 qx.OO.addProperty({ name:"minimumIntegerDigits", type:"number", defaultValue:0, allowNull:false });
37
38 /**
39  * The maximum number of integer digits (superfluos digits will be cut off
40  * ("1923" -> "23").
41  */
42 qx.OO.addProperty({ name:"maximumIntegerDigits", type:"number", defaultValue:null });
43
44 /**
45  * The minimum number of fraction digits (digits after the decimal separator).
46  * Missing digits will be filled up with 0 ("1.5" -> "1.500")
47  */
48 qx.OO.addProperty({ name:"minimumFractionDigits", type:"number", defaultValue:0, allowNull:false });
49
50 /**
51  * The maximum number of fraction digits (digits after the decimal separator).
52  * Superflous digits will cause rounding ("1.8277" -> "1.83")
53  */
54 qx.OO.addProperty({ name:"maximumFractionDigits", type:"number", defaultValue:null });
55
56 /** Whether thousand groupings should be used {e.g. "1,432,234.65"}. */
57 qx.OO.addProperty({ name:"groupingUsed", type:"boolean", defaultValue:true, allowNull:false });
58
59 /** The prefix to put before the number {"EUR " -> "EUR 12.31"}. */
60 qx.OO.addProperty({ name:"prefix", type:"string", defaultValue:"", allowNull:false });
61
62 /** Sets the postfix to put after the number {" %" -> "56.13 %"}. */
63 qx.OO.addProperty({ name:"postfix", type:"string", defaultValue:"", allowNull:false });
64
65
66 /**
67  * Formats a number.
68  *
69  * @param num {number} the number to format.
70  * @return {string} the formatted number as a string.
71  */
72 qx.Proto.format = function(num) {
73   var NumberFormat = qx.util.format.NumberFormat;
74
75   var negative = (num < 0);
76   if (negative) {
77     num = -num;
78   }
79   if (this.getMaximumFractionDigits() != null) {
80     // Do the rounding
81     var mover = Math.pow(10, this.getMaximumFractionDigits());
82     num = Math.round(num * mover) / mover;
83   }
84
85   if (num != 0) { // Math.log(0) = -Infinity
86     var integerDigits = Math.max(parseInt(Math.log(num) / Math.LN10) + 1, 1);
87   } else {
88     integerDigits = 1;
89   }
90
91   var numStr = "" + num;
92
93   // Prepare the integer part
94   var integerStr = numStr.substring(0, integerDigits);
95   while (integerStr.length < this.getMinimumIntegerDigits()) {
96     integerStr = "0" + integerStr;
97   }
98   if (this.getMaximumIntegerDigits() != null && integerStr.length > this.getMaximumIntegerDigits()) {
99     // NOTE: We cut off even though we did rounding before, because there
100     //     may be rounding errors ("12.24000000000001" -> "12.24")
101     integerStr = integerStr.substring(integerStr.length - this.getMaximumIntegerDigits());
102   }
103
104   // Prepare the fraction part
105   var fractionStr = numStr.substring(integerDigits + 1);
106   while (fractionStr.length < this.getMinimumFractionDigits()) {
107     fractionStr += "0";
108   }
109   if (this.getMaximumFractionDigits() != -1 && fractionStr.length > this.getMaximumFractionDigits()) {
110     // We have already rounded -> Just cut off the rest
111     fractionStr = fractionStr.substring(0, this.getMaximumFractionDigits());
112   }
113
114   // Add the thousand groupings
115   if (this.getGroupingUsed()) {
116     var origIntegerStr = integerStr;
117     integerStr = "";
118     var groupPos;
119     for (groupPos = origIntegerStr.length; groupPos > 3; groupPos -= 3) {
120       integerStr = NumberFormat.GROUPING_SEPARATOR
121         + origIntegerStr.substring(groupPos - 3, groupPos) + integerStr;
122     }
123     integerStr = origIntegerStr.substring(0, groupPos) + integerStr;
124   }
125
126   // Workaround: prefix and postfix are null even their defaultValue is "" and
127   //             allowNull is set to false?!?
128   var prefix  = this.getPrefix()  ? this.getPrefix()  : "";
129   var postfix = this.getPostfix() ? this.getPostfix() : "";
130
131   // Assemble the number
132   var str = prefix + (negative ? "-" : "") + integerStr;
133   if (fractionStr.length > 0) {
134     str += NumberFormat.DECIMAL_SEPARATOR + fractionStr;
135   }
136   str += postfix;
137
138   return str;
139 }
140
141
142 /**
143  * Parses a number.
144  *
145  * @param str {string} the string to parse.
146  *
147  * @return {double} the number.
148  */
149 qx.Proto.parse = function(str) {
150   var NumberFormat = qx.util.format.NumberFormat;
151
152   // use the escaped separators for regexp
153   var groupSepEsc = qx.lang.String.escapeRegexpChars(NumberFormat.GROUPING_SEPARATOR);
154   var decimalSepEsc = qx.lang.String.escapeRegexpChars(NumberFormat.DECIMAL_SEPARATOR);
155
156   var regex = new RegExp(qx.lang.String.escapeRegexpChars(this.getPrefix())
157     + '(-)?([0-9' + groupSepEsc + ']+)'
158     + '(' + decimalSepEsc + '\\d+)?'
159     + qx.lang.String.escapeRegexpChars(this.getPostfix()));
160
161   var hit = regex.exec(str);
162   if (hit == null) {
163     throw new Error("Number string '" + str + "' does not match the number format");
164   }
165
166   var negative = (hit[1] == "-");
167   var integerStr = hit[2];
168   var fractionStr = hit[3];
169
170   // Remove the thousand groupings
171   integerStr = integerStr.replace(new RegExp(groupSepEsc), "");
172
173   var asStr = (negative ? "-" : "") + integerStr;
174   if (fractionStr != null && fractionStr.length != 0) {
175     // Remove the leading decimal separator from the fractions string
176     fractionStr = fractionStr.replace(new RegExp(decimalSepEsc),"");
177     asStr += "." + fractionStr;
178   }
179   return parseFloat(asStr);
180 }
181
182
183 /**
184  * Returns the default number format.
185  *
186  * @return {NumberFormat} the default number format.
187  */
188 qx.Class.getInstance = function() {
189   var NumberFormat = qx.util.format.NumberFormat;
190   if (NumberFormat._instance == null) {
191     NumberFormat._instance = new NumberFormat();
192   }
193   return NumberFormat._instance;
194 }
195
196
197 /**
198  * Returns an integer number format.
199  *
200  * @return {NumberFormat} an integer number format.
201  */
202 qx.Class.getIntegerInstance = function() {
203   var NumberFormat = qx.util.format.NumberFormat;
204   if (NumberFormat._integerInstance == null) {
205     NumberFormat._integerInstance = new NumberFormat();
206     NumberFormat._integerInstance.setMaximumFractionDigits(0);
207   }
208   return NumberFormat._integerInstance;
209 }
210
211
212 /** {string} The decimal separator. */
213 qx.Class.DECIMAL_SEPARATOR = ".";
214
215 /** {string} The thousand grouping separator. */
216 qx.Class.GROUPING_SEPARATOR = ",";