/* This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. */
/* eslint-disable no-redeclare */
var Sao = {
__version__: '7.2.7',
};
/* eslint-enable no-redeclare */
(function() {
'use strict';
// Browser compatibility: polyfill
if (!('contains' in String.prototype)) {
String.prototype.contains = function(str, startIndex) {
return -1 !== String.prototype.indexOf.call(this, str, startIndex);
};
}
if (!String.prototype.startsWith) {
Object.defineProperty(String.prototype, 'startsWith', {
enumerable: false,
configurable: false,
writable: false,
value: function(searchString, position) {
position = position || 0;
return this.indexOf(searchString, position) === position;
}
});
}
if (!String.prototype.endsWith) {
Object.defineProperty(String.prototype, 'endsWith', {
enumerable: false,
configurable: false,
writable: false,
value: function(searchString, position) {
position = position || this.length;
position = position - searchString.length;
var lastIndex = this.lastIndexOf(searchString);
return lastIndex !== -1 && lastIndex === position;
}
});
}
if (!String.prototype.padEnd) {
String.prototype.padEnd = function padEnd(targetLength, padString) {
targetLength = targetLength >> 0;
padString = String(
typeof padString !== 'undefined' ? padString : ' ');
if (this.length > targetLength) {
return String(this);
} else {
targetLength = targetLength - this.length;
if (targetLength > padString.length) {
padString += padString.repeat(
targetLength / padString.length);
}
return String(this) + padString.slice(0, targetLength);
}
};
}
if (!String.prototype.padStart) {
String.prototype.padStart = function padStart(targetLength, padString) {
targetLength = targetLength >> 0;
padString = String(
typeof padString !== 'undefined' ? padString : ' ');
if (this.length > targetLength) {
return String(this);
} else {
targetLength = targetLength - this.length;
if (targetLength > padString.length) {
padString += padString.repeat(
targetLength / padString.length);
}
return padString.slice(0, targetLength) + String(this);
}
};
}
if (!Array.prototype.some) {
Array.prototype.some = function(fun /*, thisp */) {
if (this === null) {
throw new TypeError();
}
var thisp, i,
t = Object(this),
len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError();
}
thisp = arguments[1];
for (i = 0; i < len; i++) {
if (i in t && fun.call(thisp, t[i], i, t)) {
return true;
}
}
return false;
};
}
if (!Array.from) {
Array.from = function (value) {
// Implementation is not strictly equivalent but works for most
// cases
var result = [];
for (const e of value) {
result.push(e);
}
return result;
};
}
if (!Set.prototype.intersection) {
Set.prototype.intersection = function(other) {
if (this === null) {
throw new TypeError();
}
const result = new Set();
for (const key of other.keys()) {
if (this.has(key)) {
result.add(key)
}
}
return result;
}
}
if (!Set.prototype.isSubsetOf) {
Set.prototype.isSubsetOf = function(other) {
if (this === null) {
throw new TypeError();
}
for (const key of this.keys()) {
if (!other.has(key)) {
return false;
}
}
return true;
}
}
Sao.setdefault = function(object, key, value) {
if (!Object.prototype.hasOwnProperty.call(object, key)) {
object[key] = value;
}
return object[key];
};
// Ensure RichText doesn't use style with css
try {
document.execCommand('styleWithCSS', false, false);
} catch (e) {
// continue
}
try {
document.execCommand('useCSS', false, true);
} catch (e) {
// continue
}
// Add .uniqueId to jQuery
jQuery.fn.extend({
uniqueId: (function() {
var uuid = 0;
return function() {
return this.each(function() {
if (!this.id) {
this.id = "ui-id-" + (++uuid);
}
});
};
})()
});
window.onbeforeunload = function(e) {
if (Sao.main_menu_screen) {
Sao.main_menu_screen.save_tree_state(true);
}
if (Sao.Tab.tabs.length) {
var dialog = Sao.i18n.gettext("Are your sure to leave?");
e.returnValue = dialog;
return dialog;
}
};
Sao.class_ = function(Parent, props) {
var ClassConstructor = function() {
if (!(this instanceof ClassConstructor))
throw new Error('Constructor function requires new operator');
this.Class = ClassConstructor;
if (this.init) {
this.init.apply(this, arguments);
}
};
// Plug prototype chain
ClassConstructor.prototype = Object.create(Parent.prototype);
ClassConstructor._super = Parent.prototype;
if (props) {
for (var name in props) {
Object.defineProperty(ClassConstructor.prototype, name,
Object.getOwnPropertyDescriptor(props, name));
}
}
// Method to create new instance with a list of arguments
function F(args) {
return ClassConstructor.apply(this, args);
}
F.prototype = ClassConstructor.prototype;
ClassConstructor.new_ = function(args) {
return new F(args);
};
return ClassConstructor;
};
Sao.Logging = Sao.class_(Object, {
init: function() {
this.level = Sao.Logging.ERROR;
},
set_level: function(level) {
this.level = level;
},
_log: function(level, logger, args) {
if (this.level <= level) {
logger.apply(console, args);
}
},
debug: function() {
this._log(Sao.Logging.DEBUG, console.log, arguments);
},
info: function() {
this._log(Sao.Logging.INFO, console.info, arguments);
},
warn: function() {
this._log(Sao.Logging.WARNING, console.warn, arguments);
},
error: function() {
this._log(Sao.Logging.ERROR, console.error, arguments);
},
critical: function() {
this._log(Sao.Logging.CRITICAL, console.error, arguments);
},
assert: function() {
if (this.level <= Sao.Logging.DEBUG) {
console.assert.apply(console, arguments);
}
},
});
Sao.Logging.CRITICAL = 50;
Sao.Logging.ERROR = 40;
Sao.Logging.WARNING = 30;
Sao.Logging.INFO = 20;
Sao.Logging.DEBUG = 10;
Sao.Logging.NOTSET = 0;
Sao.Logger = new Sao.Logging();
Sao.Decimal = Number;
var _moment_to_string = moment.prototype.toString;
moment.prototype.toString = function() {
if (this.isDate) {
return this.format('YYYY-MM-DD');
} else if (this.isDateTime) {
if (this.milliseconds()) {
return this.format('YYYY-MM-DD HH:mm:ss.SSSSSS');
} else {
return this.format('YYYY-MM-DD HH:mm:ss');
}
} else if (this.isTime) {
if (this.milliseconds()) {
return this.format('HH:mm:ss.SSSSSS');
} else {
return this.format('HH:mm:ss');
}
} else {
return _moment_to_string.call(this);
}
};
Sao.Date = function(year, month, day) {
var date;
if (month === undefined) {
date = moment(year);
year = undefined;
}
else {
date = moment();
}
date.year(year);
date.month(month);
date.date(day);
date.set({hour: 0, minute: 0, second: 0, millisecond: 0});
date.isDate = true;
return date;
};
// Add 1 day to the limit because setting time make it out of the range
Sao.Date.min = moment(new Date((-100000000 + 1) * 86400000));
Sao.Date.min.set({hour: 0, minute: 0, second: 0, millisecond: 0});
Sao.Date.min.isDate = true;
Sao.Date.max = moment(new Date(100000000 * 86400000));
Sao.Date.max.set({hour: 0, minute: 0, second: 0, millisecond: 0});
Sao.Date.max.isDate = true;
Sao.DateTime = function(
year, month, day,
hour=0, minute=0, second=0, millisecond=0, utc=false) {
var datetime;
if (month === undefined) {
datetime = moment(year);
year = undefined;
}
else {
datetime = moment();
}
if (utc) {
datetime.utc();
}
datetime.year(year);
datetime.month(month);
datetime.date(day);
if (month !== undefined) {
datetime.hour(hour);
datetime.minute(minute);
datetime.second(second);
datetime.milliseconds(millisecond);
}
datetime.isDateTime = true;
datetime.local();
datetime.todate = function() {
return Sao.Date(this.year(), this.month(), this.date());
};
datetime.totime = function() {
return Sao.Time(
this.hour(), this.minute(), this.second(), this.millisecond());
};
return datetime;
};
Sao.DateTime.combine = function(date, time) {
return Sao.DateTime(
date.year(), date.month(), date.date(),
time.hour(), time.minute(), time.second(), time.millisecond());
};
Sao.DateTime.min = moment(new Date(-100000000 * 86400000)).local();
Sao.DateTime.min.isDateTime = true;
Sao.DateTime.max = moment(new Date(100000000 * 86400000)).local();
Sao.DateTime.max.isDateTime = true;
Sao.Time = function(hour, minute, second, millisecond) {
var time = moment({hour: hour, minute: minute, second: second,
millisecond: millisecond || 0});
time.isTime = true;
return time;
};
Sao.TimeDelta = function(days, seconds,
milliseconds, minutes, hours, weeks) {
var timedelta = moment.duration({
days: days,
seconds: seconds,
milliseconds: milliseconds,
minutes: minutes,
hours: hours,
weeks: weeks
});
timedelta.isTimeDelta = true;
return timedelta;
};
Sao.config = {};
Sao.config.limit = 1000;
Sao.config.display_size = 20;
Sao.config.use_ag_grid = true;
var doc_version = Sao.__version__.split('.').slice(0, 2);
if (parseInt(doc_version[1], 10) % 2) {
doc_version = 'latest';
} else {
doc_version = doc_version.join('.');
}
Sao.config.doc_url = `https://docs.tryton.org/${doc_version}`;
Sao.config.bug_url = 'https://bugs.tryton.org/';
Sao.config.title = "Trad'on";
Sao.config.icon_colors = '#267f82,#3e4950,#e78e42'.split(',');
Sao.config.calendar_colors = '#fff,#267f82'.split(',');
Sao.config.graph_color = '#267f82';
Sao.config.bus_timeout = 10 * 60 * 1000;
Sao.config.image_max_size = Math.pow(10, 6);
Sao.i18n = i18n();
Sao.i18n.setlang = function(lang) {
if (!lang) {
lang = (navigator.language ||
navigator.browserLanguage ||
navigator.userLanguage ||
'en').replace('-', '_');
}
jQuery('html').attr('lang', lang);
Sao.i18n.setLocale(lang);
moment.locale(lang.slice(0, 2));
return jQuery.getJSON('locale/' + lang + '.json').then(function(data) {
if (!data[''].language) {
data[''].language = lang;
}
if (!data['']['plural-forms']) {
data['']['plural-forms'] = 'nplurals=2; plural=(n!=1);';
}
// gettext.js requires to dump untranslated keys
for (var key in data) {
if ('' === key) {
continue;
}
data[key] = 2 == data[key].length ? data[key][1] : data[key].slice(1);
}
Sao.i18n.loadJSON(data);
}, function() {
if (~lang.indexOf('_')) {
return Sao.i18n.setlang(lang.split('_').slice(0, -1).join('_'));
}
});
};
Sao.i18n.getlang = function() {
return Sao.i18n.getLocale();
};
Sao.i18n.BC47 = function(lang) {
return lang.replace('_', '-');
};
Sao.i18n.set_direction = function(direction) {
Sao.i18n.rtl = (direction === 'rtl');
jQuery('html').attr('dir', direction);
jQuery('.row-offcanvas')
.removeClass('row-offcanvas-left row-offcanvas-right')
.addClass(Sao.i18n.rtl ? 'row-offcanvas-right' : 'row-offcanvas-left');
};
Sao.i18n.locale = {};
Sao.BOM_UTF8 = '\uFEFF';
Sao.get_preferences = function() {
var session = Sao.Session.current_session;
return session.reload_context().then(function() {
return Sao.rpc({
'method': 'model.res.user.get_preferences',
'params': [false, {}]
}, session).then(function(preferences) {
var deferreds = [];
deferreds.push(Sao.common.MODELACCESS.load_models());
deferreds.push(Sao.common.MODELHISTORY.load_history());
deferreds.push(Sao.common.MODELNOTIFICATION.load_names());
deferreds.push(Sao.common.VIEW_SEARCH.load_searches());
return jQuery.when.apply(jQuery, deferreds).then(function() {
for (const action_id of (preferences.actions || [])) {
Sao.Action.execute(action_id, {}, null, {});
}
Sao.set_title();
var new_lang = preferences.language != Sao.i18n.getLocale();
var prm = jQuery.Deferred();
Sao.i18n.setlang(preferences.language).always(function() {
if (new_lang) {
Sao.user_menu(preferences);
}
prm.resolve(preferences);
});
Sao.i18n.set_direction(preferences.language_direction);
Sao.i18n.locale = preferences.locale;
Sao.common.MODELNAME.clear();
return prm;
});
});
});
};
Sao.set_title = function(name) {
var title = [name, Sao.config.title];
document.title = title.filter(function(e) {return e;}).join(' - ');
// Remplacer le texte par une image avec le bon style
var theme_mode = document.documentElement.getAttribute(
'data-theme-mode') || 'light';
var logo_src = theme_mode === 'dark' ?
'dist/tradon-dark.png' : 'dist/tradon.png';
if (jQuery('#title').length) {
jQuery('#title').replaceWith(
''
);
} else if (jQuery('#title-logo').length) {
jQuery('#title-logo').attr('src', logo_src);
}
// Ajouter un style au conteneur parent si nécessaire
jQuery('.navbar-brand').css({
'display': 'flex',
'align-items': 'center'
});
};
Sao.set_url = function(path, name) {
var session = Sao.Session.current_session;
if (session) {
var url = '#' + session.database;
if (path) {
url += '/' + path;
}
window.location = url;
}
Sao.set_title(name);
};
window.onhashchange = function() {
var session = Sao.Session.current_session;
if (!session) {
return;
}
var url,
database = '#' + session.database;
if (window.location.hash == database) {
url = '';
} else if (window.location.hash.startsWith(database + '/')) {
url = window.location.hash.substr(database.length + 1);
} else {
return;
}
var tab;
if (!url) {
tab = Sao.Tab.tabs.get_current();
if (tab) {
Sao.set_url(tab.get_url(), tab.name);
}
} else {
url = decodeURIComponent(url);
for (const tab of Sao.Tab.tabs) {
if (decodeURIComponent(tab.get_url()) == url) {
tab.show();
return;
}
}
Sao.open_url();
}
};
Sao.open_url = function(url) {
function loads(value) {
return Sao.rpc.convertJSONObject(jQuery.parseJSON(value));
}
if (url === undefined) {
url = window.location.hash.substr(1);
}
var i = url.indexOf(';');
var path, params = {};
if (i >= 0) {
path = url.substring(0, i);
for (const part of url.substring(i + 1).split('&')) {
if (part) {
var item = part.split('=').map(decodeURIComponent);
params[item[0]] = item[1];
}
}
} else {
path = url;
}
path = path.split('/').slice(1);
var type = path.shift();
function open_model(path) {
var attributes = {};
attributes.model = path.shift();
if (!attributes.model) {
return;
}
try {
attributes.view_ids = loads(params.views || '[]');
if (params.limit !== undefined) {
attributes.limit = loads(params.limit || 'null');
}
attributes.name = loads(params.name || '""');
attributes.search_value = loads(params.search_value || '[]');
attributes.domain = loads(params.domain || '[]');
attributes.context = loads(params.context || '{}');
attributes.context_model = params.context_model;
attributes.tab_domain = loads(params.tab_domain || '[]');
} catch (e) {
return;
}
var res_id = path.shift();
if (res_id) {
res_id = Number(res_id);
if (isNaN(res_id)) {
return;
}
attributes.res_id = res_id;
attributes.mode = ['form', 'tree'];
}
try {
Sao.Tab.create(attributes);
} catch (e) {
// Prevent crashing the client
return;
}
}
function open_wizard(path) {
var attributes = {};
attributes.action = path[0];
if (!attributes.action) {
return;
}
try {
attributes.data = loads(params.data || '{}');
attributes.direct_print = loads(params.direct_print || 'false');
attributes.name = loads(params.name || '""');
attributes.window = loads(params.window || 'false');
attributes.context = loads(params.context || '{}');
} catch (e) {
return;
}
try {
Sao.Wizard.create(attributes);
} catch (e) {
// Prevent crashing the client
return;
}
}
function open_report(path) {
var attributes = {};
attributes.name = path[0];
if (!attributes.name) {
return;
}
try {
attributes.data = loads(params.data || '{}');
attributes.direct_print = loads(params.direct_print || 'false');
attributes.context = loads(params.context || '{}');
} catch (e) {
return;
}
try {
Sao.Action.exec_report(attributes);
} catch (e) {
// Prevent crashing the client
return;
}
}
function open_url() {
var url;
try {
url = loads(params.url || 'false');
} catch (e) {
return;
}
if (url) {
window.open(url, '_blank', 'noreferrer,noopener');
}
}
switch (type) {
case 'model':
open_model(path);
break;
case 'wizard':
open_wizard(path);
break;
case 'report':
open_report(path);
break;
case 'url':
open_url();
break;
}
};
Sao.login = function() {
Sao.set_title();
(function loadInterFont() {
const link = document.createElement('link');
link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap';
link.rel = 'stylesheet';
document.head.appendChild(link);
})();
Sao.i18n.setlang().always(function() {
Sao.Session.server_version()
.then(function(version) {
if (JSON.stringify(version.split('.').slice(0, 2)) !==
JSON.stringify(Sao.__version__.split('.').slice(0, 2)))
{
Sao.common.warning.run(
Sao.i18n.gettext(
"Incompatible version of the server."),
Sao.i18n.gettext("Version mismatch"));
} else {
Sao.Session.get_credentials()
.then(function(session) {
Sao.Session.current_session = session;
return session.reload_context();
})
.then(Sao.get_preferences)
.then(function(preferences) {
Sao.menu(preferences);
Sao.user_menu(preferences);
Sao.open_url();
Sao.Bus.listen();
});
}
}, function() {
Sao.common.warning.run(
Sao.i18n.gettext(
"Could not connect to the server."),
Sao.i18n.gettext("Connection error"));
});
});
};
Sao.logout = function() {
var session = Sao.Session.current_session;
if (!session || !session.session) {
// Do not save states if there is no session
Sao.main_menu_screen = null;
}
Sao.Tab.tabs.close(true).done(function() {
jQuery('#user-preferences').empty();
jQuery('#global-search').empty();
jQuery('#menu').empty();
session.do_logout().always(Sao.login);
Sao.set_title();
});
};
Sao.preferences = function() {
return Sao.Tab.tabs.close(true).then(function() {
jQuery('#user-preferences').empty();
jQuery('#global-search').empty();
jQuery('#menu').empty();
new Sao.Window.Preferences(function() {
Sao.Session.current_session.reset_context();
Sao.get_preferences().then(function(preferences) {
Sao.menu(preferences);
Sao.user_menu(preferences);
});
});
});
};
Sao.favorites_menu = function() {
jQuery(window).click(function() {
Sao.favorites_menu_clear();
});
if (!jQuery('#user-favorites').children('.dropdown-menu').length) {
var name = Sao.main_menu_screen.model_name + '.favorite';
var session = Sao.Session.current_session;
var args = {
'method': 'model.' + name + '.get',
};
var menu = jQuery('