diff --git a/addons/barcodes/static/src/js/barcode_form_view.js b/addons/barcodes/static/src/js/barcode_form_view.js index 69a59fdaafa..73df8a3c03f 100644 --- a/addons/barcodes/static/src/js/barcode_form_view.js +++ b/addons/barcodes/static/src/js/barcode_form_view.js @@ -285,18 +285,20 @@ FormController.include({ }); }}, {text: _t('Discard'), close: true}], $content: $content, - }).open(); - // This line set the value of the key which triggered the _set_quantity in the input - var $input = this.dialog.$content.find('.o_set_qty_input').focus().val(character); - - var $selectBtn = this.dialog.$footer.find('.btn-primary'); - $input.on('keypress', function (event){ - if (event.which === 13) { - event.preventDefault(); - $input.off(); - $selectBtn.click(); - } }); + this.dialog.opened().then(function () { + // This line set the value of the key which triggered the _set_quantity in the input + var $input = self.dialog.$('.o_set_qty_input').focus().val(character); + var $selectBtn = self.dialog.$footer.find('.btn-primary'); + $input.on('keypress', function (event){ + if (event.which === 13) { + event.preventDefault(); + $input.off(); + $selectBtn.click(); + } + }); + }); + this.dialog.open(); }, }); diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index 98d169c11e2..844f529d998 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -85,13 +85,16 @@ FormController.include({ var dialog = new Dialog(this, { title: _t("Edit Layout"), $content: QWeb.render('DashBoard.layouts', _.clone(event.data)) - }).open(); - dialog.$el.find('li').click(function () { - var layout = $(this).attr('data-layout'); - self.renderer.changeLayout(layout); - self._saveDashboard(); - dialog.close(); }); + dialog.opened().then(function () { + dialog.$('li').click(function () { + var layout = $(this).attr('data-layout'); + self.renderer.changeLayout(layout); + self._saveDashboard(); + dialog.close(); + }); + }); + dialog.open(); }, /** diff --git a/addons/mail/static/src/js/followers.js b/addons/mail/static/src/js/followers.js index d60e3ede753..651165dde30 100644 --- a/addons/mail/static/src/js/followers.js +++ b/addons/mail/static/src/js/followers.js @@ -311,11 +311,14 @@ var Followers = AbstractField.extend({ event.preventDefault(); this._inviteFollower(true); }, + /** + * @param {Event} event + */ _onEditSubtype: function (event) { var self = this; var $currentTarget = $(event.currentTarget); var follower_id = $currentTarget.data('follower-id'); // id of model mail_follower - return this._rpc({ + this._rpc({ route: '/mail/read_subscription_data', params: {res_model: this.model, follower_id: follower_id}, }) @@ -339,8 +342,11 @@ var Followers = AbstractField.extend({ close: true, }, ], - }).open(); - self._displaySubtypes(data, true, is_channel); + }); + self.dialog.opened().then(function () { + self._displaySubtypes(data, true, is_channel); + }); + self.dialog.open(); }); }, _onFollowButtonClicked: function () { diff --git a/addons/project/static/src/js/project.js b/addons/project/static/src/js/project.js index a1ca1a60302..95e1cc953a1 100644 --- a/addons/project/static/src/js/project.js +++ b/addons/project/static/src/js/project.js @@ -70,23 +70,25 @@ KanbanRecord.include({ self._updateRecord({displayed_image_id: false}); }}, {text: _t("Discard"), close: true}], $content: $content, - }).open(); - - var $selectBtn = dialog.$footer.find('.btn-primary'); - $content.on('click', 'img', function (ev) { - $imgs.not(ev.currentTarget).removeClass('o_selected'); - $selectBtn.prop('disabled', !$(ev.currentTarget).toggleClass('o_selected').hasClass('o_selected')); }); + dialog.opened().then(function () { + var $selectBtn = dialog.$footer.find('.btn-primary'); + $content.on('click', 'img', function (ev) { + $imgs.not(ev.currentTarget).removeClass('o_selected'); + $selectBtn.prop('disabled', !$(ev.currentTarget).toggleClass('o_selected').hasClass('o_selected')); + }); - $content.on('dblclick', 'img', function (ev) { - var $img = $(ev.currentTarget); - var data = { - id: $img.data('id'), - display_name: $img.data('name') - }; - self._updateRecord({displayed_image_id: data}); - dialog.close(); + $content.on('dblclick', 'img', function (ev) { + var $img = $(ev.currentTarget); + var data = { + id: $img.data('id'), + display_name: $img.data('name') + }; + self._updateRecord({displayed_image_id: data}); + dialog.close(); + }); }); + dialog.open(); } }, }); diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py index 9fc3baa41ce..ab31c49733b 100644 --- a/addons/web/__manifest__.py +++ b/addons/web/__manifest__.py @@ -19,7 +19,11 @@ This module provides the core of the Odoo Web Client. 'views/report_templates.xml', ], 'qweb': [ - "static/src/xml/*.xml", + "static/src/xml/base.xml", + "static/src/xml/kanban.xml", + "static/src/xml/rainbow_man.xml", + "static/src/xml/report.xml", + "static/src/xml/web_calendar.xml", ], 'bootstrap': True, # load translations for login screen } diff --git a/addons/web/static/src/js/chrome/action_manager.js b/addons/web/static/src/js/chrome/action_manager.js index 5d4a44ba420..131a31ad776 100644 --- a/addons/web/static/src/js/chrome/action_manager.js +++ b/addons/web/static/src/js/chrome/action_manager.js @@ -731,7 +731,7 @@ var ActionManager = Widget.extend({ * @param {String} executor.klass CSS class to add on the dialog root, if action.target=new * @param {Function} executor.post_process cleanup called after a widget has been added as inner_widget * @param {Object} options - * @return {*} + * @return {Deferred<*>} */ ir_actions_common: function(executor, options) { var self = this; @@ -772,10 +772,13 @@ var ActionManager = Widget.extend({ }; this.dialog.on("closed", null, this.dialog.on_close); this.dialog_widget = executor.widget(); + var $dialogFooter; if (this.dialog_widget instanceof ViewManager) { executor.action.viewManager = this.dialog_widget; + $dialogFooter = $('
'); // fake dialog footer in which view + // manager buttons will be put _.defaults(this.dialog_widget.flags, { - $buttons: this.dialog.$footer, + $buttons: $dialogFooter, footer_to_buttons: true, }); if (this.dialog_widget.action.view_mode === 'form') { @@ -790,15 +793,24 @@ var ActionManager = Widget.extend({ this.dialog_widget.setParent(this.dialog); var fragment = document.createDocumentFragment(); - return this.dialog_widget.appendTo(fragment).then(function() { + return this.dialog_widget.appendTo(fragment).then(function () { + var def = $.Deferred(); + self.dialog.opened().then(function () { + dom.append(self.dialog.$el, fragment, { + in_DOM: true, + callbacks: [{widget: self.dialog_widget}], + }); + if ($dialogFooter) { + self.dialog.$footer.empty().append($dialogFooter.contents()); + } + if (options.state && self.dialog_widget.do_load_state) { + return self.dialog_widget.do_load_state(options.state); + } + }) + .done(def.resolve.bind(def)) + .fail(def.reject.bind(def)); self.dialog.open(); - dom.append(self.dialog.$el, fragment, { - in_DOM: true, - callbacks: [{widget: self.dialog_widget}], - }); - if(options.state && self.dialog_widget.do_load_state) { - return self.dialog_widget.do_load_state(options.state); - } + return def; }).then(function () { return executor.action; }); diff --git a/addons/web/static/src/js/core/dialog.js b/addons/web/static/src/js/core/dialog.js index ec3a3109053..23eac8ac5f5 100644 --- a/addons/web/static/src/js/core/dialog.js +++ b/addons/web/static/src/js/core/dialog.js @@ -16,20 +16,30 @@ var _t = core._t; during the lifecycle of the dialog. */ var Dialog = Widget.extend({ - /** - Constructor. + xmlDependencies: ['/web/static/src/xml/dialog.xml'], - @param {Widget} parent - @param {dictionary} options - - title - - subtitle - - size: one of the following: 'large', 'medium', 'small' - - dialogClass: class to add to the modal-body - - buttons: It must be a list of dictionaries -> text, classes, close, click, disabled - -> If no buttons, a "ok" primary button is added with close = true - -> By default: close = false and classes = 'btn-primary' if only one button and 'btn-default' if many buttons - - $content: Some content to replace this.$el . - */ + /** + * @constructor + * @param {Widget} parent + * @param {Object} [options] + * @param {string} [options.title=Odoo] + * @param {string} [options.subtitle] + * @param {string} [options.size=large] - 'large', 'medium' or 'small' + * @param {string} [options.dialogClass] - class to add to the modal-body + * @param {jQuery} [options.$content] + * Element which will be the $el, replace the .modal-body and get the + * modal-body class + * @param {Object[]} [options.buttons] + * List of button descriptions. Note: if no buttons, a "ok" primary + * button is added to allow closing the dialog + * @param {string} [options.buttons[].text] + * @param {string} [options.buttons[].classes] + * Default to 'btn-primary' if only one button, 'btn-default' + * otherwise + * @param {boolean} [options.buttons[].close=false] + * @param {function} [options.buttons[].click] + * @param {boolean} [options.buttons[].disabled] + */ init: function (parent, options) { this._super(parent); this._opened = $.Deferred(); @@ -43,31 +53,42 @@ var Dialog = Widget.extend({ }); this.$content = options.$content; - this.title = options.title; this.subtitle = options.subtitle; - this.$modal = $(QWeb.render('Dialog', {title: this.title, subtitle: this.subtitle})); - - switch(options.size) { - case 'large': - this.$modal.find('.modal-dialog').addClass('modal-lg'); - break; - case 'small': - this.$modal.find('.modal-dialog').addClass('modal-sm'); - break; - } - this.dialogClass = options.dialogClass; - this.$footer = this.$modal.find(".modal-footer"); - - this.set_buttons(options.buttons); - - this.$modal.on('hidden.bs.modal', _.bind(this.destroy, this)); + this.size = options.size; + this.buttons = options.buttons; }, - - renderElement: function() { + /** + * Wait for XML dependencies and instantiate the modal structure (except + * modal-body). + * + * @override + */ + willStart: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + // Render modal once xml dependencies are loaded + self.$modal = $(QWeb.render('Dialog', {title: self.title, subtitle: self.subtitle})); + switch (self.size) { + case 'large': + self.$modal.find('.modal-dialog').addClass('modal-lg'); + break; + case 'small': + self.$modal.find('.modal-dialog').addClass('modal-sm'); + break; + } + self.$footer = self.$modal.find(".modal-footer"); + self.set_buttons(self.buttons); + self.$modal.on('hidden.bs.modal', _.bind(self.destroy, self)); + }); + }, + /** + * @override + */ + renderElement: function () { this._super(); - if(this.$content) { + if (this.$content) { this.setElement(this.$content); } this.$el.addClass('modal-body ' + this.dialogClass); @@ -100,7 +121,7 @@ var Dialog = Widget.extend({ }); }, - set_title: function(title, subtitle) { + set_title: function (title, subtitle) { this.title = title || ""; if (subtitle !== undefined) { this.subtitle = subtitle || ""; @@ -114,15 +135,16 @@ var Dialog = Widget.extend({ return this; }, - opened: function(handler) { + opened: function (handler) { return (handler)? this._opened.then(handler) : this._opened; }, - open: function() { + open: function () { $('.tooltip').remove(); // remove open tooltip if any to prevent them staying when modal is opened var self = this; - this.replace(this.$modal.find(".modal-body")).then(function() { + this.appendTo($('
')).then(function () { + self.$modal.find(".modal-body").replaceWith(self.$el); self.$modal.modal('show'); self._opened.resolve(); }); @@ -130,26 +152,32 @@ var Dialog = Widget.extend({ return self; }, - close: function() { - this.$modal.modal('hide'); + close: function () { + this.destroy(); }, - destroy: function(reason) { + destroy: function (reason) { + // Need to trigger before real destroy but if 'closed' handler destroys + // the widget again, we want to avoid infinite recursion + if (!this.__closed) { + this.__closed = true; + this.trigger("closed", reason); + } + if (this.isDestroyed()) { return; } - - this.trigger("closed", reason); - this._super(); $('.tooltip').remove(); //remove open tooltip if any to prevent them staying when modal has disappeared - this.$modal.modal('hide'); - this.$modal.remove(); + if (this.$modal) { + this.$modal.modal('hide'); + this.$modal.remove(); + } setTimeout(function () { // Keep class modal-open (deleted by bootstrap hide fnct) on body to allow scrolling inside the modal var modals = $('body > .modal').filter(':visible'); - if(modals.length) { + if (modals.length) { modals.last().focus(); $('body').addClass('modal-open'); } diff --git a/addons/web/static/src/js/report/report.editor.js b/addons/web/static/src/js/report/report.editor.js index 57942f9b79c..a36b1596ca2 100644 --- a/addons/web/static/src/js/report/report.editor.js +++ b/addons/web/static/src/js/report/report.editor.js @@ -12,8 +12,6 @@ var trusted_host = utils.get_host_from_url(web_base_url); var trusted_protocol = utils.get_protocol_from_url(web_base_url); var trusted_origin = utils.build_origin(trusted_protocol, trusted_host); -ajax.loadXML('/web/static/src/xml/base_common.xml', core.qweb); - // Patch the editor's behavior when it is launched inside an iframe. if (window.self !== window.top) { $(document.body).addClass('o_in_iframe'); // in order to apply css rules diff --git a/addons/web/static/src/js/services/crash_manager.js b/addons/web/static/src/js/services/crash_manager.js index f09eec23daf..9d3b9fa63b8 100644 --- a/addons/web/static/src/js/services/crash_manager.js +++ b/addons/web/static/src/js/services/crash_manager.js @@ -117,12 +117,17 @@ var CrashManager = core.Class.extend({ var dialog = new Dialog(this, { title: _.str.capitalize(error.type || error.message) || _t("Odoo Error"), $content: $(QWeb.render('CrashManager.error', {error: error})) - }).open(); + }); // When the dialog opens, initialize the copy feature and destroy it when the dialog is closed var $clipboardBtn; var clipboard; dialog.opened(function () { + // When the full traceback is shown, scroll it to the end (useful for better python error reporting) + dialog.$(".o_error_detail").on("shown.bs.collapse", function (e) { + e.target.scrollTop = e.target.scrollHeight; + }); + $clipboardBtn = dialog.$(".o_clipboard_button"); $clipboardBtn.tooltip({title: _t("Copied !"), trigger: "manual", placement: "left"}); clipboard = new window.Clipboard($clipboardBtn[0], { @@ -144,10 +149,7 @@ var CrashManager = core.Class.extend({ clipboard.destroy(); }); - // When the full traceback is shown, scroll it to the end (useful for better python error reporting) - dialog.$(".o_error_detail").on("shown.bs.collapse", function (e) { - e.target.scrollTop = e.target.scrollHeight; - }); + dialog.open(); }, show_message: function(exception) { this.show_error({ diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index d38400c2e84..97b17f00413 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -192,13 +192,16 @@ var FormViewDialog = ViewDialog.extend({ .then(function () { var $buttons = $('
'); self.form_view.renderButtons($buttons); - if ($buttons.children().length) { - self.$footer.empty().append($buttons.contents()); - } - dom.append(_super().$el, fragment, { - callbacks: [{widget: self.form_view}], - in_DOM: true, + self.opened().always(function () { + if ($buttons.children().length) { + self.$footer.empty().append($buttons.contents()); + } + dom.append(self.$el, fragment, { + callbacks: [{widget: self.form_view}], + in_DOM: true, + }); }); + _super(); }); }); @@ -282,7 +285,7 @@ var SelectCreateDialog = ViewDialog.extend({ }, open: function () { - if(this.options.initial_view !== "search") { + if (this.options.initial_view !== "search") { return this.create_edit_record(); } var self = this; @@ -303,10 +306,14 @@ var SelectCreateDialog = ViewDialog.extend({ this.loadViews(this.dataset.model, this.dataset.get_context(), [[false, 'list'], [false, 'search']], {}) .then(this.setup.bind(this, search_defaults)) .then(function (fragment) { - dom.append(_super().$el, fragment, { - callbacks: [{widget: self.list_controller}], - in_DOM: true, + self.opened().then(function () { + dom.append(self.$el, fragment, { + callbacks: [{widget: self.list_controller}], + in_DOM: true, + }); + self.set_buttons(self.__buttons); }); + _super(); }); return this; }, @@ -349,20 +356,20 @@ var SelectCreateDialog = ViewDialog.extend({ }).then(function (controller) { self.list_controller = controller; // Set the dialog's buttons - var buttons = [{ + self.__buttons = [{ text: _t("Cancel"), classes: "btn-default o_form_button_cancel", close: true, }]; if (!self.options.no_create) { - buttons.unshift({ + self.__buttons.unshift({ text: _t("Create"), classes: "btn-primary", click: self.create_edit_record.bind(self) }); } if (!self.options.disable_multiple_selection) { - buttons.unshift({ + self.__buttons.unshift({ text: _t("Select"), classes: "btn-primary o_select_button", disabled: true, @@ -372,7 +379,6 @@ var SelectCreateDialog = ViewDialog.extend({ }, }); } - self.set_buttons(buttons); return self.list_controller.appendTo(fragment); }).then(function () { searchview.toggle_visibility(true); @@ -401,7 +407,7 @@ var SelectCreateDialog = ViewDialog.extend({ self.on_selected([record.res_id]); }, })).open(); - dialog.on('closed', this, this.close.bind(this)); + dialog.on('closed', this, this.close); }, }); diff --git a/addons/web/static/src/js/widgets/data_export.js b/addons/web/static/src/js/widgets/data_export.js index c11551ca2cc..fde9001f860 100644 --- a/addons/web/static/src/js/widgets/data_export.js +++ b/addons/web/static/src/js/widgets/data_export.js @@ -210,16 +210,16 @@ var DataExport = Dialog.extend({ this.row_index = 0; this.row_index_level = 0; + }, + start: function() { + var self = this; + var waitFor = [this._super.apply(this, arguments)]; // The default for the ".modal_content" element is "max-height: 100%;" // but we want it to always expand to "height: 100%;" for this modal. // This can be achieved thanks to LESS modification without touching // the ".modal-content" rules... but not with Internet explorer (11). this.$modal.find(".modal-content").css("height", "100%"); - }, - start: function() { - var self = this; - var waitFor = [this._super.apply(this, arguments)]; this.$fields_list = this.$('.o_fields_list'); this.$import_compat_radios = this.$('.o_import_compat input'); diff --git a/addons/web/static/src/js/widgets/debug_manager.js b/addons/web/static/src/js/widgets/debug_manager.js index 912a30edb2c..baa147d9a1f 100644 --- a/addons/web/static/src/js/widgets/debug_manager.js +++ b/addons/web/static/src/js/widgets/debug_manager.js @@ -351,10 +351,14 @@ DebugManager.include({ this._active_view.controller.open_defaults_dialog(); }, fvg: function() { - var dialog = new Dialog(this, { title: _t("Fields View Get") }).open(); - $('
').text(utils.json_node_to_xml(
-            this._active_view.controller.renderer.arch, true)
-        ).appendTo(dialog.$el);
+        var self = this;
+        var dialog = new Dialog(this, { title: _t("Fields View Get") });
+        dialog.opened().then(function () {
+            $('
').text(utils.json_node_to_xml(
+                self._active_view.controller.renderer.arch, true)
+            ).appendTo(dialog.$el);
+        });
+        dialog.open();
     },
 });
 function make_context(width, height, fn) {
diff --git a/addons/web/static/src/xml/base_common.xml b/addons/web/static/src/xml/dialog.xml
similarity index 100%
rename from addons/web/static/src/xml/base_common.xml
rename to addons/web/static/src/xml/dialog.xml
diff --git a/addons/web/static/tests/helpers/test_utils.js b/addons/web/static/tests/helpers/test_utils.js
index 213d87ef8b0..fbfdb315fa4 100644
--- a/addons/web/static/tests/helpers/test_utils.js
+++ b/addons/web/static/tests/helpers/test_utils.js
@@ -10,6 +10,7 @@ odoo.define('web.test_utils', function (require) {
  * instance of a view, appended in the dom, ready to be tested.
  */
 
+var ajax = require('web.ajax');
 var basic_fields = require('web.basic_fields');
 var config = require('web.config');
 var core = require('web.core');
@@ -490,7 +491,15 @@ function removeSrcAttribute($el, widget) {
     });
 }
 
-return session.is_bound.then(function () {
+// Loading static files cannot be properly simulated when their real content is
+// really needed. This is the case for static XML files so we load them here,
+// before starting the qunit test suite.
+// (session.js is in charge of loading the static xml bundle and we also have
+// to load xml files that are normally lazy loaded by specific widgets).
+return $.when(
+    session.is_bound,
+    ajax.loadXML('/web/static/src/xml/dialog.xml', core.qweb)
+).then(function () {
     setTimeout(function () {
         // this is done with the hope that tests are
         // only started all together...
diff --git a/addons/web_editor/static/src/js/iframe.js b/addons/web_editor/static/src/js/iframe.js
index e2c3b5ef8a4..8b4fd385e81 100644
--- a/addons/web_editor/static/src/js/iframe.js
+++ b/addons/web_editor/static/src/js/iframe.js
@@ -8,8 +8,6 @@ var translator = require('web_editor.translate');
 var rte = require('web_editor.rte');
 var snippet_editor = require('web_editor.snippet.editor');
 
-ajax.loadXML('/web/static/src/xml/base_common.xml', core.qweb);
-
 var callback = window ? window["callback"] : undefined;
 window.top.odoo[callback+"_updown"] = function (value, fields_values, field_name) {
     var $editable = $("#editable_area");
diff --git a/addons/web_editor/static/src/js/widgets.js b/addons/web_editor/static/src/js/widgets.js
index 240a61b0458..3bfae6ce240 100644
--- a/addons/web_editor/static/src/js/widgets.js
+++ b/addons/web_editor/static/src/js/widgets.js
@@ -101,12 +101,13 @@ var MediaDialog = Dialog.extend({
         this.media = media;
         this.isNewMedia = !media;
         this.range = range.create();
-
-        this.$modal.addClass('note-image-dialog');
-        this.$modal.find('.modal-dialog').addClass('o_select_media_dialog');
     },
     start: function () {
         var self = this;
+
+        this.$modal.addClass('note-image-dialog');
+        this.$modal.find('.modal-dialog').addClass('o_select_media_dialog');
+
         this.only_images = this.options.only_images || this.options.select_images || (this.media && ($(this.media).parent().data("oe-field") === "image" || $(this.media).parent().data("oe-type") === "image"));
         if (this.only_images) {
             this.$('[href="#editor-media-document"], [href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden');
diff --git a/addons/web_planner/static/src/js/web_planner_common.js b/addons/web_planner/static/src/js/web_planner_common.js
index 0e611c89655..11ffa3466db 100644
--- a/addons/web_planner/static/src/js/web_planner_common.js
+++ b/addons/web_planner/static/src/js/web_planner_common.js
@@ -50,7 +50,6 @@ var PlannerDialog = Dialog.extend({
     init: function (parent, options, planner) {
         this._super.apply(this, arguments);
 
-        this.$modal.addClass("o_planner_dialog");
         this.planner = planner;
         this.cookie_name = this.planner.planner_application + '_last_page';
         this.pages = [];
@@ -80,6 +79,7 @@ var PlannerDialog = Dialog.extend({
         return $.when(this._super.apply(this, arguments), def);
     },
     start: function() {
+        this.$modal.addClass("o_planner_dialog");
         this.$template.find(".o_planner_page").addBack(".o_planner_page").each((function (index, dom_page) {
             this.pages.push(new Page(dom_page, index));
         }).bind(this));
diff --git a/addons/web_tour/static/src/js/debug_manager.js b/addons/web_tour/static/src/js/debug_manager.js
index 68aab63946b..9ec0058e4a6 100644
--- a/addons/web_tour/static/src/js/debug_manager.js
+++ b/addons/web_tour/static/src/js/debug_manager.js
@@ -29,18 +29,20 @@ DebugManager.include({
                 });
         }
     },
-    start_tour: function() {
+    start_tour: function () {
         var dialog = new Dialog(this, {
             title: 'Tours',
             $content: core.qweb.render('WebClient.DebugManager.ToursDialog', {
                 tours: tour.tours
             }),
-        }).open();
-
-        dialog.$('.o_start_tour').on('click', function(e) {
-            e.preventDefault();
-            tour.run($(e.target).data('name'));
         });
+        dialog.opened().then(function () {
+            dialog.$('.o_start_tour').on('click', function (e) {
+                e.preventDefault();
+                tour.run($(e.target).data('name'));
+            });
+        });
+        dialog.open();
     },
 });
 
diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js
index 75350dce58a..8324e7457ac 100644
--- a/addons/website/static/src/js/website.js
+++ b/addons/website/static/src/js/website.js
@@ -172,9 +172,7 @@ odoo.define('website.website', function (require) {
         form.submit();
     };
 
-    ajax.loadXML('/web/static/src/xml/base_common.xml', qweb).then(function () {
-        ajax.loadXML('/website/static/src/xml/website.xml', qweb);
-    });
+    ajax.loadXML('/website/static/src/xml/website.xml', qweb);
 
     base.ready().then(function () {
         data.topBar = new TopBar();
@@ -282,9 +280,9 @@ odoo.define('website.website', function (require) {
     $('.zoomable img[data-zoom]').zoomOdoo();
 
     Dialog.include({
-        init: function () {
-            this._super.apply(this, arguments);
+        start: function () {
             this.$modal.addClass("o_website_modal");
+            return this._super.apply(this, arguments);
         },
     });