You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

376 lines
14 KiB

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