2 openerp.web.formats = function(instance) {
3 var _t = instance.web._t;
6 * Intersperses ``separator`` in ``str`` at the positions indicated by
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
13 * There are two special values:
16 * indicates the insertion should end now
18 * indicates that the previous section pattern should be repeated (until all
19 * of ``str`` is consumed)
22 * @param {Array<Number>} indices
23 * @param {String} separator
26 instance.web.intersperse = function (str, indices, separator) {
27 separator = separator || '';
28 var result = [], last = str.length;
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)
35 } else if(section === 0 && i === 0) {
36 // repeats previous section, which there is none => stop
38 } else if (section === 0) {
39 // repeat previous section forever
40 //noinspection AssignmentToForLoopParameterJS
41 section = indices[--i];
43 result.push(str.substring(last-section, last));
47 var s = str.substring(0, last);
48 if (s) { result.push(s); }
49 return result.reverse().join(separator);
52 * Insert "thousands" separators in the provided number (which is actually
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);
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
70 * @param {String} value original format
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] !== '%')) {
79 output.push(character);
81 return output.join('');
83 var normalize_format = function (format) {
84 return Date.normalizeFormat(instance.web.strip_raw_chars(format));
87 * Formats a single atomic value based on a field descriptor
89 * @param {Object} value read from OpenERP
90 * @param {Object} descriptor union of orm field and view field
91 * @param {Object} [descriptor.widget] widget to use to display the value
92 * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
93 * @param {Object} [descriptor.digits] used for the formatting of floats
94 * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
96 instance.web.format_value = function (value, descriptor, value_if_empty) {
97 // If NaN value, display as with a `false` (empty cell)
98 if (typeof value === 'number' && isNaN(value)) {
101 //noinspection FallthroughInSwitchStatementJS
104 if (descriptor.type === 'char') {
107 console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
111 return value_if_empty === undefined ? '' : value_if_empty;
113 var l10n = _t.database.parameters;
114 switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
116 return value.toString();
118 return instance.web.insert_thousand_seps(
119 _.str.sprintf('%d', value));
121 var digits = descriptor.digits ? descriptor.digits : [69,2];
122 digits = typeof digits === "string" ? py.eval(digits) : digits;
123 var precision = digits[1];
124 var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
125 formatted[0] = instance.web.insert_thousand_seps(formatted[0]);
126 return formatted.join(l10n.decimal_point);
128 var pattern = '%02d:%02d';
130 value = Math.abs(value);
131 pattern = '-' + pattern;
133 return _.str.sprintf(pattern,
135 Math.round((value % 1) * 60));
137 // name_get value format
141 return _.str.sprintf(_t("(%d records)"), value.length);
143 if (typeof(value) == "string")
144 value = instance.web.auto_str_to_date(value);
146 return value.toString(normalize_format(l10n.date_format)
147 + ' ' + normalize_format(l10n.time_format));
149 if (typeof(value) == "string")
150 value = instance.web.auto_str_to_date(value);
151 return value.toString(normalize_format(l10n.date_format));
153 if (typeof(value) == "string")
154 value = instance.web.auto_str_to_date(value);
155 return value.toString(normalize_format(l10n.time_format));
156 case 'selection': case 'statusbar':
157 // Each choice is [value, label]
158 if(_.isArray(value)) {
161 var result = _(descriptor.selection).detect(function (choice) {
162 return choice[0] === value;
164 if (result) { return result[1]; }
171 instance.web.parse_value = function (value, descriptor, value_if_empty) {
172 var date_pattern = normalize_format(_t.database.parameters.date_format),
173 time_pattern = normalize_format(_t.database.parameters.time_format);
177 return value_if_empty === undefined ? false : value_if_empty;
179 switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
184 value = value.replace(instance.web._t.database.parameters.thousands_sep, "");
185 } while(tmp !== value);
188 throw new Error(value + " is not a correct integer");
191 var tmp = Number(value);
198 tmp2 = tmp.replace(instance.web._t.database.parameters.thousands_sep, "");
199 } while(tmp !== tmp2);
200 var reformatted_value = tmp.replace(instance.web._t.database.parameters.decimal_point, ".");
201 var parsed = Number(reformatted_value);
203 throw new Error(value + " is not a correct float");
207 if (value[0] === '-') {
208 value = value.slice(1);
211 var float_time_pair = value.split(":");
212 if (float_time_pair.length != 2)
213 return factor * instance.web.parse_value(value, {type: "float"});
214 var hours = instance.web.parse_value(float_time_pair[0], {type: "integer"});
215 var minutes = instance.web.parse_value(float_time_pair[1], {type: "integer"});
216 return factor * (hours + (minutes / 60));
218 return instance.web.parse_value(value, {type: "float"});
220 var datetime = Date.parseExact(
221 value, (date_pattern + ' ' + time_pattern));
222 if (datetime !== null)
223 return instance.web.datetime_to_str(datetime);
224 datetime = Date.parse(value);
225 if (datetime !== null)
226 return instance.web.datetime_to_str(datetime);
227 throw new Error(value + " is not a valid datetime");
229 var date = Date.parseExact(value, date_pattern);
231 return instance.web.date_to_str(date);
232 date = Date.parse(value);
234 return instance.web.date_to_str(date);
235 throw new Error(value + " is not a valid date");
237 var time = Date.parseExact(value, time_pattern);
239 return instance.web.time_to_str(time);
240 time = Date.parse(value);
242 return instance.web.time_to_str(time);
243 throw new Error(value + " is not a valid time");
248 instance.web.auto_str_to_date = function(value, type) {
250 return instance.web.str_to_datetime(value);
253 return instance.web.str_to_date(value);
256 return instance.web.str_to_time(value);
258 throw new Error("'" + value + "' is not a valid date, datetime nor time");
261 instance.web.auto_date_to_str = function(value, type) {
264 return instance.web.datetime_to_str(value);
266 return instance.web.date_to_str(value);
268 return instance.web.time_to_str(value);
270 throw new Error(type + " is not convertible to date, datetime nor time");
275 * Formats a provided cell based on its field type. Most of the field types
276 * return a correctly formatted value, but some tags and fields are
277 * special-cased in their handling:
279 * * buttons will return an actual ``<button>`` tag with a bunch of error handling
281 * * boolean fields will return a checkbox input, potentially disabled
283 * * binary fields will return a link to download the binary data as a file
285 * @param {Object} row_data record whose values should be displayed in the cell
286 * @param {Object} column column descriptor
287 * @param {"button"|"field"} column.tag base control type
288 * @param {String} column.type widget type for a field control
289 * @param {String} [column.string] button label
290 * @param {String} [column.icon] button icon
291 * @param {Object} [options]
292 * @param {String} [options.value_if_empty=''] what to display if the field's value is ``false``
293 * @param {Boolean} [options.process_modifiers=true] should the modifiers be computed ?
294 * @param {String} [options.model] current record's model
295 * @param {Number} [options.id] current record's id
298 instance.web.format_cell = function (row_data, column, options) {
299 options = options || {};
301 if (options.process_modifiers !== false) {
302 attrs = column.modifiers_for(row_data);
304 if (attrs.invisible) { return ''; }
306 if (column.tag === 'button') {
307 return _.template('<button type="button" title="<%-title%>" <%=additional_attributes%> >' +
308 '<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' +
310 title: column.string || '',
311 additional_attributes: isNaN(row_data["id"].value) && instance.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ?
312 'disabled="disabled" class="oe-listview-button-disabled"' : '',
313 prefix: instance.connection.prefix,
315 alt: column.string || ''
318 if (!row_data[column.id]) {
319 return options.value_if_empty === undefined ? '' : options.value_if_empty;
322 switch (column.widget || column.type) {
324 return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
325 row_data[column.id].value ? 'checked="checked"' : '');
327 var text = _t("Download"),
328 download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', instance.connection.session_id, options.model, column.id, options.id);
329 if (column.filename) {
330 download_url += '&filename_field=' + column.filename;
331 if (row_data[column.filename]) {
332 text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
333 row_data[column.filename].value, {type: 'char'}));
336 return _.template('<a href="<%-href%>"><%-text%></a> (%<-size%>)', {
339 size: row_data[column.id].value
343 '<progress value="<%-value%>" max="100"><%-value%>%</progress>', {
344 value: _.str.sprintf("%.0f", row_data[column.id].value || 0)
348 return _.escape(instance.web.format_value(
349 row_data[column.id].value, column, options.value_if_empty));