/* 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.8.6', }; /* eslint-enable no-redeclare */ (function() { 'use strict'; if (!('contains' in String.prototype)) { String.prototype.contains = function(str, startIndex) { return -1 !== String.prototype.indexOf.call(this, str, startIndex); }; } // Browser compatibility: polyfill 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; } } if (!Set.prototype.union) { Set.prototype.union = function(other) { if (this === null) { throw new TypeError(); } const result = new Set(this); for (const key of other.keys()) { result.add(key); } return result } } if (!Array.prototype.toReversed) { Object.defineProperty(Array.prototype, 'toReversed', { value: function toReversed() { return this.slice().reverse(); }, writable: true, configurable: 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; 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 = 'Tryton'; 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) { if (!direction) { direction = getComputedStyle(document.documentElement).direction; } Sao.i18n.rtl = (direction === 'rtl'); jQuery('html').attr('dir', direction); }; 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() { var prm = jQuery.when(); for (const action_id of (preferences.actions || [])) { prm = prm.then(() => { return Sao.Action.execute(action_id, {}, null); }); } return prm.then(() => { var prm = jQuery.Deferred(); Sao.set_title(); if (!preferences.language) { session.context.language = Sao.i18n.getLocale(); prm.resolve(preferences); } else { var new_lang = preferences.language != Sao.i18n.getLocale(); 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(' - '); jQuery('#title').text(Sao.config.title); }; 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(); Sao.i18n.setlang().always(function() { Sao.i18n.set_direction(); 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 { let url = window.location.hash.substr(1); 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(url); let user_id = Sao.Session.current_session.user_id; Sao.Bus.register( `notification:${user_id}`, Sao.NotificationMenu.notify); Sao.NotificationMenu.count(); 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(); let user_id = Sao.Session.current_session.user_id; Sao.Bus.unregister( `notification:${user_id}`, Sao.NotificationMenu.notify); 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 (Sao.main_menu_screen && !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('