[ADD] handling of binary fields to the listview
[odoo/odoo.git] / addons / web / static / src / js / formats.js
1
2 openerp.web.formats = function(openerp) {
3 var _t = openerp.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 openerp.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 openerp.web.insert_thousand_seps = function (num) {
59     var negative = num[0] === '-';
60     num = (negative ? num.slice(1) : num);
61     return (negative ? '-' : '') + openerp.web.intersperse(
62         num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep);
63 };
64 /**
65  * Formats a single atomic value based on a field descriptor
66  *
67  * @param {Object} value read from OpenERP
68  * @param {Object} descriptor union of orm field and view field
69  * @param {Object} [descriptor.widget] widget to use to display the value
70  * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
71  * @param {Object} [descriptor.digits] used for the formatting of floats
72  * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
73  */
74 openerp.web.format_value = function (value, descriptor, value_if_empty) {
75     // If NaN value, display as with a `false` (empty cell)
76     if (typeof value === 'number' && isNaN(value)) {
77         value = false;
78     }
79     //noinspection FallthroughInSwitchStatementJS
80     switch (value) {
81         case '':
82             if (descriptor.type === 'char') {
83                 return '';
84             }
85             console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
86         case false:
87         case Infinity:
88         case -Infinity:
89             return value_if_empty === undefined ?  '' : value_if_empty;
90     }
91     var l10n = _t.database.parameters;
92     switch (descriptor.widget || descriptor.type) {
93         case 'integer':
94             return openerp.web.insert_thousand_seps(
95                 _.str.sprintf('%d', value));
96         case 'float':
97             var precision = descriptor.digits ? descriptor.digits[1] : 2;
98             var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
99             formatted[0] = openerp.web.insert_thousand_seps(formatted[0]);
100             return formatted.join(l10n.decimal_point);
101         case 'float_time':
102             return _.str.sprintf("%02d:%02d",
103                     Math.floor(value),
104                     Math.round((value % 1) * 60));
105         case 'progressbar':
106             return _.str.sprintf(
107                 '<progress value="%.2f" max="100.0">%.2f%%</progress>',
108                     value, value);
109         case 'many2one':
110             // name_get value format
111             return value[1];
112         case 'datetime':
113             if (typeof(value) == "string")
114                 value = openerp.web.auto_str_to_date(value);
115
116             return value.format(l10n.date_format
117                         + ' ' + l10n.time_format);
118         case 'date':
119             if (typeof(value) == "string")
120                 value = openerp.web.auto_str_to_date(value);
121             return value.format(l10n.date_format);
122         case 'time':
123             if (typeof(value) == "string")
124                 value = openerp.web.auto_str_to_date(value);
125             return value.format(l10n.time_format);
126         case 'selection':
127             // Each choice is [value, label]
128             if(_.isArray(value)) {
129                  value = value[0]
130             }
131             var result = _(descriptor.selection).detect(function (choice) {
132                 return choice[0] === value;
133             });
134             if (result) { return result[1]; }
135             return;
136         default:
137             return value;
138     }
139 };
140
141 openerp.web.parse_value = function (value, descriptor, value_if_empty) {
142     var date_pattern = Date.normalizeFormat(_t.database.parameters.date_format),
143         time_pattern = Date.normalizeFormat(_t.database.parameters.time_format);
144     switch (value) {
145         case false:
146         case "":
147             return value_if_empty === undefined ?  false : value_if_empty;
148     }
149     switch (descriptor.widget || descriptor.type) {
150         case 'integer':
151             var tmp;
152             do {
153                 tmp = value;
154                 value = value.replace(openerp.web._t.database.parameters.thousands_sep, "");
155             } while(tmp !== value);
156             tmp = Number(value);
157             if (isNaN(tmp))
158                 throw new Error(value + " is not a correct integer");
159             return tmp;
160         case 'float':
161             var tmp = Number(value);
162             if (!isNaN(tmp))
163                 return tmp;
164
165             var tmp2 = value;
166             do {
167                 tmp = tmp2;
168                 tmp2 = tmp.replace(openerp.web._t.database.parameters.thousands_sep, "");
169             } while(tmp !== tmp2);
170             var reformatted_value = tmp.replace(openerp.web._t.database.parameters.decimal_point, ".");
171             var parsed = Number(reformatted_value);
172             if (isNaN(parsed))
173                 throw new Error(value + " is not a correct float");
174             return parsed;
175         case 'float_time':
176             var float_time_pair = value.split(":");
177             if (float_time_pair.length != 2)
178                 return openerp.web.parse_value(value, {type: "float"});
179             var hours = openerp.web.parse_value(float_time_pair[0], {type: "integer"});
180             var minutes = openerp.web.parse_value(float_time_pair[1], {type: "integer"});
181             return hours + (minutes / 60);
182         case 'progressbar':
183             return openerp.web.parse_value(value, {type: "float"});
184         case 'datetime':
185             var datetime = Date.parseExact(
186                     value, (date_pattern + ' ' + time_pattern));
187             if (datetime !== null)
188                 return openerp.web.datetime_to_str(datetime);
189             datetime = Date.parse(value);
190             if (datetime !== null)
191                 return openerp.web.datetime_to_str(datetime);
192             throw new Error(value + " is not a valid datetime");
193         case 'date':
194             var date = Date.parseExact(value, date_pattern);
195             if (date !== null)
196                 return openerp.web.date_to_str(date);
197             date = Date.parse(value);
198             if (date !== null)
199                 return openerp.web.date_to_str(date);
200             throw new Error(value + " is not a valid date");
201         case 'time':
202             var time = Date.parseExact(value, time_pattern);
203             if (time !== null)
204                 return openerp.web.time_to_str(time);
205             time = Date.parse(value);
206             if (time !== null)
207                 return openerp.web.time_to_str(time);
208             throw new Error(value + " is not a valid time");
209     }
210     return value;
211 };
212
213 openerp.web.auto_str_to_date = function(value, type) {
214     try {
215         return openerp.web.str_to_datetime(value);
216     } catch(e) {}
217     try {
218         return openerp.web.str_to_date(value);
219     } catch(e) {}
220     try {
221         return openerp.web.str_to_time(value);
222     } catch(e) {}
223     throw new Error("'" + value + "' is not a valid date, datetime nor time");
224 };
225
226 openerp.web.auto_date_to_str = function(value, type) {
227     switch(type) {
228         case 'datetime':
229             return openerp.web.datetime_to_str(value);
230         case 'date':
231             return openerp.web.date_to_str(value);
232         case 'time':
233             return openerp.web.time_to_str(value);
234         default:
235             throw new Error(type + " is not convertible to date, datetime nor time");
236     }
237 };
238
239 /**
240  * Formats a provided cell based on its field type. Most of the field types
241  * return a correctly formatted value, but some tags and fields are
242  * special-cased in their handling:
243  *
244  * * buttons will return an actual ``<button>`` tag with a bunch of error handling
245  *
246  * * boolean fields will return a checkbox input, potentially disabled
247  *
248  * * binary fields will return a link to download the binary data as a file
249  *
250  * @param {Object} row_data record whose values should be displayed in the cell
251  * @param {Object} column column descriptor
252  * @param {"button"|"field"} column.tag base control type
253  * @param {String} column.type widget type for a field control
254  * @param {String} [column.string] button label
255  * @param {String} [column.icon] button icon
256  * @param {Object} [options]
257  * @param {String} [options.value_if_empty=''] what to display if the field's value is ``false``
258  * @param {Boolean} [options.process_modifiers=true] should the modifiers be computed ?
259  * @param {String} [options.model] current record's model
260  * @param {Number} [options.id] current record's id
261  *
262  */
263 openerp.web.format_cell = function (row_data, column, options) {
264     options = options || {};
265     var attrs = {};
266     if (options.process_modifiers !== false) {
267         attrs = column.modifiers_for(row_data);
268     }
269     if (attrs.invisible) { return ''; }
270
271     if (column.tag === 'button') {
272         return _.template('<button type="button" title="<%-title%>" <%=additional_attributes%> >' +
273             '<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' +
274             '</button>', {
275                 title: column.string || '',
276                 additional_attributes: isNaN(row_data["id"].value) && openerp.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ?
277                     'disabled="disabled" class="oe-listview-button-disabled"' : '',
278                 prefix: openerp.connection.prefix,
279                 icon: column.icon,
280                 alt: column.string || ''
281             });
282     }
283     if (!row_data[column.id]) {
284         return options.value_if_empty === undefined ? '' : options.value_if_empty;
285     }
286
287     switch (column.type) {
288     case "boolean":
289         return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
290                  row_data[column.id].value ? 'checked="checked"' : '');
291     case "binary":
292         return _.str.sprintf('<a href="%(href)s">%(text)s</a> (%(size)s)', {
293             text: _t("Download"),
294             href: _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d',
295                     openerp.connection.session_id, options.model,
296                     column.id, options.id),
297             size: row_data[column.id].value
298         });
299     }
300
301     return openerp.web.format_value(
302             row_data[column.id].value, column, options.value_if_empty);
303 }
304
305 };