~aw/smart-dev/trunk

« back to all changes in this revision

Viewing changes to web_smartux/static/src/js/formats.js

  • Committer: Anders Wallenquist
  • Date: 2014-09-09 11:56:02 UTC
  • Revision ID: anders.wallenquist@vertel.se-20140909115602-a3j4fuaj7jjo3t68
Initial commit

Show diffs side-by-side

added added

removed removed

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