851b60af22920f009b633dbed77a64d4b3b15f83
[odoo/odoo.git] / addons / web / static / src / js / formats.js
1
2 openerp.web.formats = function(instance) {
3 var _t = instance.web._t;
4
5 /**
6  * Intersperses ``separator`` in ``str`` at the positions indicated by
7  * ``indices``.
8  *
9  * ``indices`` is an array of relative offsets (from the previous insertion
10  * position, starting from the end of the string) at which to insert
11  * ``separator``.
12  *
13  * There are two special values:
14  *
15  * ``-1``
16  *   indicates the insertion should end now
17  * ``0``
18  *   indicates that the previous section pattern should be repeated (until all
19  *   of ``str`` is consumed)
20  *
21  * @param {String} str
22  * @param {Array<Number>} indices
23  * @param {String} separator
24  * @returns {String}
25  */
26 instance.web.intersperse = function (str, indices, separator) {
27     separator = separator || '';
28     var result = [], last = str.length;
29
30     for(var i=0; i<indices.length; ++i) {
31         var section = indices[i];
32         if (section === -1 || last <= 0) {
33             // Done with string, or -1 (stops formatting string)
34             break;
35         } else if(section === 0 && i === 0) {
36             // repeats previous section, which there is none => stop
37             break;
38         } else if (section === 0) {
39             // repeat previous section forever
40             //noinspection AssignmentToForLoopParameterJS
41             section = indices[--i];
42         }
43         result.push(str.substring(last-section, last));
44         last -= section;
45     }
46
47     var s = str.substring(0, last);
48     if (s) { result.push(s); }
49     return result.reverse().join(separator);
50 };
51 /**
52  * Insert "thousands" separators in the provided number (which is actually
53  * a string)
54  *
55  * @param {String} num
56  * @returns {String}
57  */
58 instance.web.insert_thousand_seps = function (num) {
59     var negative = num[0] === '-';
60     num = (negative ? num.slice(1) : num);
61     return (negative ? '-' : '') + instance.web.intersperse(
62         num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep);
63 };
64
65 /**
66  * removes literal (non-format) text from a date or time pattern, as datejs can
67  * not deal with literal text in format strings (whatever the format), whereas
68  * strftime allows for literal characters
69  *
70  * @param {String} value original format
71  */
72 instance.web.strip_raw_chars = function (value) {
73     var isletter = /[a-zA-Z]/, output = [];
74     for(var index=0; index < value.length; ++index) {
75         var character = value[index];
76         if(isletter.test(character) && (index === 0 || value[index-1] !== '%')) {
77             continue;
78         }
79         output.push(character);
80     }
81     return output.join('');
82 };
83 var normalize_format = function (format) {
84     return Date.normalizeFormat(instance.web.strip_raw_chars(format));
85 };
86
87 /**
88  * Check with a scary heuristic if the value is a bin_size or not.
89  * If not, compute an approximate size out of the base64 encoded string.
90  *
91  * @param {String} value original format
92  */
93 instance.web.binary_to_binsize = function (value) {
94     if (!value) {
95         return instance.web.human_size(0);
96     }
97     if (value.substr(0, 10).indexOf(' ') == -1) {
98         // Computing approximate size out of base64 encoded string
99         // http://en.wikipedia.org/wiki/Base64#MIME
100         return instance.web.human_size(value.length / 1.37);
101     } else {
102         // already bin_size
103         return value;
104     }
105 };
106
107 /**
108  * Returns a human readable size
109  *
110  * @param {Number} numner of bytes
111  */
112 instance.web.human_size = function(size) {
113     var units = _t("Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb").split(',');
114     var i = 0;
115     while (size >= 1024) {
116         size /= 1024;
117         ++i;
118     }
119     return size.toFixed(2) + ' ' + units[i];
120 };
121
122 /**
123  * Formats a single atomic value based on a field descriptor
124  *
125  * @param {Object} value read from OpenERP
126  * @param {Object} descriptor union of orm field and view field
127  * @param {Object} [descriptor.widget] widget to use to display the value
128  * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
129  * @param {Object} [descriptor.digits] used for the formatting of floats
130  * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
131  */
132 instance.web.format_value = function (value, descriptor, value_if_empty) {
133     // If NaN value, display as with a `false` (empty cell)
134     if (typeof value === 'number' && isNaN(value)) {
135         value = false;
136     }
137     //noinspection FallthroughInSwitchStatementJS
138     switch (value) {
139         case '':
140             if (descriptor.type === 'char') {
141                 return '';
142             }
143             console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
144         case false:
145         case Infinity:
146         case -Infinity:
147             return value_if_empty === undefined ?  '' : value_if_empty;
148     }
149     var l10n = _t.database.parameters;
150     switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
151         case 'id':
152             return value.toString();
153         case 'integer':
154             return instance.web.insert_thousand_seps(
155                 _.str.sprintf('%d', value));
156         case 'float':
157             var digits = descriptor.digits ? descriptor.digits : [69,2];
158             digits = typeof digits === "string" ? py.eval(digits) : digits;
159             var precision = digits[1];
160             var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
161             formatted[0] = instance.web.insert_thousand_seps(formatted[0]);
162             return formatted.join(l10n.decimal_point);
163         case 'float_time':
164             var pattern = '%02d:%02d';
165             if (value < 0) {
166                 value = Math.abs(value);
167                 pattern = '-' + pattern;
168             }
169             var hour = Math.floor(value);
170             var min = Math.round((value % 1) * 60);
171             if (min == 60){
172                 min = 0;
173                 hour = hour + 1;
174             }
175             return _.str.sprintf(pattern, hour, min);
176         case 'many2one':
177             // name_get value format
178             return value[1] ? value[1].split("\n")[0] : value[1];
179         case 'one2many':
180         case 'many2many':
181             if (typeof value === 'string') {
182                 return value;
183             }
184             return _.str.sprintf(_t("(%d records)"), value.length);
185         case 'datetime':
186             if (typeof(value) == "string")
187                 value = instance.web.auto_str_to_date(value);
188
189             return value.toString(normalize_format(l10n.date_format)
190                         + ' ' + normalize_format(l10n.time_format));
191         case 'date':
192             if (typeof(value) == "string")
193                 value = instance.web.auto_str_to_date(value);
194             return value.toString(normalize_format(l10n.date_format));
195         case 'time':
196             if (typeof(value) == "string")
197                 value = instance.web.auto_str_to_date(value);
198             return value.toString(normalize_format(l10n.time_format));
199         case 'selection': case 'statusbar':
200             // Each choice is [value, label]
201             if(_.isArray(value)) {
202                  value = value[0]
203             }
204             var result = _(descriptor.selection).detect(function (choice) {
205                 return choice[0] === value;
206             });
207             if (result) { return result[1]; }
208             return;
209         default:
210             return value;
211     }
212 };
213
214 instance.web.parse_value = function (value, descriptor, value_if_empty) {
215     var date_pattern = normalize_format(_t.database.parameters.date_format),
216         time_pattern = normalize_format(_t.database.parameters.time_format);
217     switch (value) {
218         case false:
219         case "":
220             return value_if_empty === undefined ?  false : value_if_empty;
221     }
222     switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
223         case 'integer':
224             var tmp;
225             do {
226                 tmp = value;
227                 value = value.replace(instance.web._t.database.parameters.thousands_sep, "");
228             } while(tmp !== value);
229             tmp = Number(value);
230             if (isNaN(tmp))
231                 throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value));
232             return tmp;
233         case 'float':
234             var tmp = Number(value);
235             if (!isNaN(tmp))
236                 return tmp;
237
238             var tmp2 = value;
239             do {
240                 tmp = tmp2;
241                 tmp2 = tmp.replace(instance.web._t.database.parameters.thousands_sep, "");
242             } while(tmp !== tmp2);
243             var reformatted_value = tmp.replace(instance.web._t.database.parameters.decimal_point, ".");
244             var parsed = Number(reformatted_value);
245             if (isNaN(parsed))
246                 throw new Error(_.str.sprintf(_t("'%s' is not a correct float"), value));
247             return parsed;
248         case 'float_time':
249             var factor = 1;
250             if (value[0] === '-') {
251                 value = value.slice(1);
252                 factor = -1;
253             }
254             var float_time_pair = value.split(":");
255             if (float_time_pair.length != 2)
256                 return factor * instance.web.parse_value(value, {type: "float"});
257             var hours = instance.web.parse_value(float_time_pair[0], {type: "integer"});
258             var minutes = instance.web.parse_value(float_time_pair[1], {type: "integer"});
259             return factor * (hours + (minutes / 60));
260         case 'progressbar':
261             return instance.web.parse_value(value, {type: "float"});
262         case 'datetime':
263             var datetime = Date.parseExact(
264                     value, (date_pattern + ' ' + time_pattern));
265             if (datetime !== null)
266                 return instance.web.datetime_to_str(datetime);
267             datetime = Date.parse(value);
268             if (datetime !== null)
269                 return instance.web.datetime_to_str(datetime);
270             throw new Error(_.str.sprintf(_t("'%s' is not a correct datetime"), value));
271         case 'date':
272             var date = Date.parseExact(value, date_pattern);
273             if (date !== null)
274                 return instance.web.date_to_str(date);
275             date = Date.parse(value);
276             if (date !== null)
277                 return instance.web.date_to_str(date);
278             throw new Error(_.str.sprintf(_t("'%s' is not a correct date"), value));
279         case 'time':
280             var time = Date.parseExact(value, time_pattern);
281             if (time !== null)
282                 return instance.web.time_to_str(time);
283             time = Date.parse(value);
284             if (time !== null)
285                 return instance.web.time_to_str(time);
286             throw new Error(_.str.sprintf(_t("'%s' is not a correct time"), value));
287     }
288     return value;
289 };
290
291 instance.web.auto_str_to_date = function(value, type) {
292     try {
293         return instance.web.str_to_datetime(value);
294     } catch(e) {}
295     try {
296         return instance.web.str_to_date(value);
297     } catch(e) {}
298     try {
299         return instance.web.str_to_time(value);
300     } catch(e) {}
301     throw new Error(_.str.sprintf(_t("'%s' is not a correct date, datetime nor time"), value));
302 };
303
304 instance.web.auto_date_to_str = function(value, type) {
305     switch(type) {
306         case 'datetime':
307             return instance.web.datetime_to_str(value);
308         case 'date':
309             return instance.web.date_to_str(value);
310         case 'time':
311             return instance.web.time_to_str(value);
312         default:
313             throw new Error(_.str.sprintf(_t("'%s' is not convertible to date, datetime nor time"), type));
314     }
315 };
316
317 };