/* This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. */ (function() { 'use strict'; Sao.common = {}; Sao.common.BACKSPACE_KEYCODE = 8; Sao.common.TAB_KEYCODE = 9; Sao.common.RETURN_KEYCODE = 13; Sao.common.UP_KEYCODE = 38; Sao.common.DOWN_KEYCODE = 40; Sao.common.DELETE_KEYCODE = 46; Sao.common.F2_KEYCODE = 113; Sao.common.F3_KEYCODE = 114; Sao.common.SELECTION_NONE = 1; Sao.common.SELECTION_SINGLE = 2; // Not implemented yet Sao.common.SELECTION_MULTIPLE = 3; Sao.common.compare = function(arr1, arr2) { if (arr1.length != arr2.length) { return false; } for (var i = 0; i < arr1.length; i++) { if (arr1[i] instanceof Array && arr2[i] instanceof Array) { if (!Sao.common.compare(arr1[i], arr2[i])) { return false; } } else if (arr1[i] != arr2[i]) { return false; } } return true; }; Sao.common.contains = function(array1, array2) { for (var i = 0; i < array1.length; i++) { if (Sao.common.compare(array1[i], array2)) { return true; } } return false; }; // Find the intersection of two arrays. // The arrays must be sorted. Sao.common.intersect = function(a, b) { var ai = 0, bi = 0; var result = []; while (ai < a.length && bi < b.length) { if (a[ai] < b[bi]) { ai++; } else if (a[ai] > b[bi]) { bi++; } else { result.push(a[ai]); ai++; bi++; } } return result; }; Sao.common.selection = function(title, values, alwaysask) { if (alwaysask === undefined) { alwaysask = false; } var prm = jQuery.Deferred(); if ((Object.keys(values).length == 1) && (!alwaysask)) { var key = Object.keys(values)[0]; prm.resolve(values[key]); return prm; } // TODO return prm.fail(); }; Sao.common.date_format = function() { if (Sao.Session.current_session) { var context = Sao.Session.current_session.context; if (context.locale && context.locale.date) { return context.locale.date .replace('%d', 'dd') .replace('%j', 'oo') .replace('%a', 'D') .replace('%A', 'DD') .replace('%m', 'mm') .replace('%b', 'M') .replace('%B', 'MM') .replace('%y', 'y') .replace('%Y', 'yy'); } } return jQuery.datepicker.W3C; }; Sao.common.format_time = function(format, date) { var pad = Sao.common.pad; return format.replace('%H', pad(date.getHours(), 2)) .replace('%M', pad(date.getMinutes(), 2)) .replace('%S', pad(date.getSeconds(), 2)) .replace('%f', pad(date.getMilliseconds(), 3)); }; Sao.common.parse_time = function(format, value) { if (jQuery.isEmptyObject(value)) { return null; } var getNumber = function(pattern) { var i = format.indexOf(pattern); if (~i) { var number = parseInt(value.slice(i, i + pattern.length), 10); if (!isNaN(number)) { return number; } } return 0; }; return new Sao.Time(getNumber('%H'), getNumber('%M'), getNumber('%S'), getNumber('%f')); }; Sao.common.format_datetime = function(date_format, time_format, date) { return (jQuery.datepicker.formatDate(date_format, date) + ' ' + Sao.common.format_time(time_format, date)); }; Sao.common.parse_datetime = function(date_format, time_format, value) { value = jQuery.datepicker.parseDate(date_format, value); if (!value) { return null; } var date = Sao.DateTime(value); var time_value = value.replace(jQuery.datepicker.formatDate( date_format, date), '').trim(); var time = Sao.common.parse_time(time_format, time_value); date.setHours(time.getHours()); date.setMinutes(time.getMinutes()); date.setSeconds(time.getSeconds()); return date; }; Sao.common.pad = function(number, length) { var str = '' + number; while (str.length < length) { str = '0' + str; } return str; }; Sao.common.text_to_float_time = function(text, conversion, digit) { // TODO return text; }; Sao.common.ModelAccess = Sao.class_(Object, { init: function() { this.batchnum = 100; this._access = {}; }, load_models: function(refresh) { var prm = jQuery.Deferred(); if (!refresh) { this._access = {}; } Sao.rpc({ 'method': 'model.ir.model.list_models', 'params': [{}] }, Sao.Session.current_session).then(function(models) { var deferreds = []; var update_access = function(access) { this._access = jQuery.extend(this._access, access); }; for (var i = 0; i < models.length; i += this.batchnum) { var to_load = models.slice(i, i + this.batchnum); deferreds.push(Sao.rpc({ 'method': 'model.ir.model.access.get_access', 'params': [to_load, {}] }, Sao.Session.current_session) .then(update_access.bind(this))); } jQuery.when.apply(jQuery, deferreds).then( prm.resolve, prm.reject); }.bind(this)); return prm; }, get: function(model) { return this._access[model]; } }); Sao.common.MODELACCESS = new Sao.common.ModelAccess(); Sao.common.ModelHistory = Sao.class_(Object, { init: function() { this._models = []; }, load_history: function() { this._models = []; return Sao.rpc({ 'method': 'model.ir.model.list_history', 'params': [{}] }, Sao.Session.current_session).then(function(models) { this._models = models; }.bind(this)); }, contains: function(model) { return ~this._models.indexOf(model); } }); Sao.common.MODELHISTORY = new Sao.common.ModelHistory(); Sao.common.humanize = function(size) { var sizes = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; for (var i =0, len = sizes.length; i < len; i++) { if (size < 1000) { return size.toPrecision(4) + ' ' + sizes[i]; } size /= 1000; } }; Sao.common.EvalEnvironment = function(parent_, eval_type) { if (eval_type === undefined) eval_type = 'eval'; var environment; if (eval_type == 'eval') { environment = parent_.get_eval(); } else { environment = {}; for (var key in parent_.model.fields) { var field = parent_.model.fields[key]; environment[key] = field.get_on_change_value(parent_); } } environment.id = parent_.id; if (parent_.group.parent) Object.defineProperty(environment, '_parent_' + parent_.group.parent_name, { 'enumerable': true, 'get': function() { return Sao.common.EvalEnvironment(parent_.group.parent, eval_type); } }); environment.get = function(item, default_) { if (this.hasOwnProperty(item)) return this[item]; return default_; }; return environment; }; Sao.common.selection_mixin = {}; Sao.common.selection_mixin.init = function() { this.selection = null; this.inactive_selection = []; this._last_domain = null; this._values2selection = {}; this._domain_cache = {}; if (this.nullable_widget === undefined) { this.nullable_widget = true; } }; Sao.common.selection_mixin.init_selection = function(value, callback) { if (!value) { value = {}; (this.attributes.selection_change_with || []).forEach(function(e) { value[e] = null; }); } var key = JSON.stringify(value); var selection = this.attributes.selection || []; var prepare_selection = function(selection) { selection = jQuery.extend([], selection); if (this.attributes.sort === undefined || this.attributes.sort) { selection.sort(function(a, b) { return a[1].localeCompare(b[1]); }); } this.selection = jQuery.extend([], selection); if (callback) callback(this.selection); }; if (!(selection instanceof Array) && !(key in this._values2selection)) { var prm; if (this.attributes.selection_change_with) { prm = this.model.execute(selection, [value]); } else { prm = this.model.execute(selection, []); } prm.pipe(prepare_selection.bind(this)); } else { if (key in this._values2selection) { selection = this._values2selection.selection; } prepare_selection.call(this, selection); } this.inactive_selection = []; }; Sao.common.selection_mixin.update_selection = function(record, field, callback) { if (!field) { if (callback) { callback(this.selection); } return; } var domain = field.get_domain(record); if (field.description.type == 'reference') { // The domain on reference field is not only based on the selection // so the selection can not be filtered. domain = []; } if (!('relation' in this.attributes)) { var change_with = this.attributes.selection_change_with || []; var value = record._get_on_change_args(change_with); delete value.id; Sao.common.selection_mixin.init_selection.call(this, value, function() { Sao.common.selection_mixin.filter_selection.call(this, domain, record, field); if (callback) { callback(this.selection); } }.bind(this)); } else { var context = field.get_context(record); var jdomain = JSON.stringify([domain, context]); if (jdomain in this._domain_cache) { this.selection = this._domain_cache[jdomain]; this._last_domain = [domain, context]; } if ((this._last_domain !== null) && Sao.common.compare(domain, this._last_domain[0]) && (JSON.stringify(context) == JSON.stringify(this._last_domain[1]))) { if (callback) { callback(this.selection); } return; } var prm = Sao.rpc({ 'method': 'model.' + this.attributes.relation + '.search_read', 'params': [domain, 0, null, null, ['rec_name'], context] }, record.model.session); prm.done(function(result) { var selection = []; result.forEach(function(x) { selection.push([x.id, x.rec_name]); }); if (this.nullable_widget) { selection.push([null, '']); } this._last_domain = domain; this._domain_cache[jdomain] = selection; this.selection = jQuery.extend([], selection); if (callback) { callback(this.selection); } }.bind(this)); prm.fail(function() { this._last_domain = null; this.selection = []; if (callback) { callback(this.selection); } }.bind(this)); } }; Sao.common.selection_mixin.filter_selection = function( domain, record, field) { if (jQuery.isEmptyObject(domain)) { return; } var inversion = new Sao.common.DomainInversion(); this.selection = this.selection.filter(function(value) { var context = {}; context[this.field_name] = value[0]; return inversion.eval_domain(domain, context); }.bind(this)); }; Sao.common.selection_mixin.get_inactive_selection = function(value) { if (!this.attributes.relation) { return jQuery.when([]); } for (var i = 0, len = this.inactive_selection.length; i < len; i++) { if (value == this.inactive_selection[i][0]) { return jQuery.when(this.inactive_selection[i]); } } var prm = Sao.rpc({ 'method': 'model.' + this.attributes.relation + '.read', 'params': [[value], ['rec_name'], {}] }, Sao.Session.current_session); return prm.then(function(result) { this.inactive_selection.push([result[0].id, result[0].rec_name]); return [result[0].id, result[0].rec_name]; }.bind(this)); }; Sao.common.Button = Sao.class_(Object, { init: function(attributes) { this.attributes = attributes; this.el = jQuery('