flectra.define('web.AbstractWebClient', function (require) { "use strict"; /** * AbstractWebClient * * This class defines a simple, basic web client. It is mostly functional. * The WebClient is in some way the most important class for the web framework: * - this is the class that instantiate everything else, * - it is the top of the component tree, * - it coordinates many events bubbling up */ var ActionManager = require('web.ActionManager'); var concurrency = require('web.concurrency'); var core = require('web.core'); var config = require('web.config'); var crash_manager = require('web.crash_manager'); var data_manager = require('web.data_manager'); var Dialog = require('web.Dialog'); var Loading = require('web.Loading'); var mixins = require('web.mixins'); var NotificationManager = require('web.notification').NotificationManager; var RainbowMan = require('web.rainbow_man'); var session = require('web.session'); var Widget = require('web.Widget'); var _t = core._t; var qweb = core.qweb; var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { custom_events: { clear_uncommitted_changes: function (e) { this.clear_uncommitted_changes().then(e.data.callback); }, toggle_fullscreen: function (event) { this.toggle_fullscreen(event.data.fullscreen); }, current_action_updated: function (e) { this.current_action_updated(e.data.action); }, // GENERIC SERVICES // the next events are dedicated to generic services required by // downstream widgets. Mainly side effects, such as rpcs, notifications // or cache. // notifications, warnings and effects notification: function (e) { if(this.notification_manager) { this.notification_manager.notify(e.data.title, e.data.message, e.data.sticky); } }, warning: '_onDisplayWarning', load_views: function (event) { var params = { model: event.data.modelName, context: event.data.context, views_descr: event.data.views, }; return data_manager .load_views(params, event.data.options || {}) .then(event.data.on_success); }, load_filters: function (event) { return data_manager .load_filters(event.data.dataset, event.data.action_id) .then(event.data.on_success); }, show_effect: '_onShowEffect', // session get_session: function (event) { if (event.data.callback) { event.data.callback(session); } }, do_action: function (event) { this.do_action(event.data.action, event.data.options || {}).then(function (result) { if (event.data.on_success) { event.data.on_success(result); } }).fail(function (result) { if (event.data.on_fail) { event.data.on_fail(result); } }); }, }, init: function (parent) { this.client_options = {}; mixins.ServiceProvider.init.call(this); this._super(parent); this.origin = undefined; this._current_state = null; this.menu_dm = new concurrency.DropMisordered(); this.action_mutex = new concurrency.Mutex(); this.set('title_part', {"zopenerp": "Flectra"}); }, start: function () { var self = this; // we add the o_touch_device css class to allow CSS to target touch // devices. This is only for styling purpose, if you need javascript // specific behaviour for touch device, just use the config object // exported by web.config this.$el.toggleClass('o_touch_device', config.device.touch); this.on("change:title_part", this, this._title_changed); this._title_changed(); return session.is_bound .then(function () { self.bind_events(); return $.when( self.set_action_manager(), self.set_notification_manager(), self.set_loading() ); }).then(function () { if (session.session_is_valid()) { return self.show_application(); } else { // database manager needs the webclient to keep going even // though it has no valid session return $.when(); } }).then(function () { // Listen to 'scroll' event and propagate it on main bus self.action_manager.$el.on('scroll', core.bus.trigger.bind(core.bus, 'scroll')); core.bus.trigger('web_client_ready'); }); }, bind_events: function () { var self = this; $('.oe_systray').show(); this.$el.on('mouseenter', '.oe_systray > div:not([data-toggle=tooltip])', function () { $(this).attr('data-toggle', 'tooltip').tooltip().trigger('mouseenter'); }); this.$el.on('click', '.oe_dropdown_toggle', function (ev) { ev.preventDefault(); var $toggle = $(this); var doc_width = $(document).width(); var $menu = $toggle.siblings('.oe_dropdown_menu'); $menu = $menu.size() >= 1 ? $menu : $toggle.find('.oe_dropdown_menu'); var state = $menu.is('.oe_opened'); setTimeout(function () { // Do not alter propagation $toggle.add($menu).toggleClass('oe_opened', !state); if (!state) { // Move $menu if outside window's edge var offset = $menu.offset(); var menu_width = $menu.width(); var x = doc_width - offset.left - menu_width - 2; if (x < 0) { $menu.offset({ left: offset.left + x }).width(menu_width); } } }, 0); }); core.bus.on('click', this, function (ev) { $('.tooltip').remove(); if (!$(ev.target).is('input[type=file]')) { self.$('.oe_dropdown_menu.oe_opened, .oe_dropdown_toggle.oe_opened').removeClass('oe_opened'); } }); core.bus.on('connection_lost', this, this.on_connection_lost); core.bus.on('connection_restored', this, this.on_connection_restored); // crash manager integration core.bus.on('rpc_error', crash_manager, crash_manager.rpc_error); window.onerror = function (message, file, line, col, error) { // Scripts injected in DOM (eg: google API's js files) won't return a clean error on window.onerror. // The browser will just give you a 'Script error.' as message and nothing else for security issue. // To enable onerror to work properly with CORS file, you should: // 1. add crossorigin="anonymous" to your