From bcbfd96740eaeaddc7ef85d298de232bc8687719 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Fri, 3 Aug 2018 15:50:29 +0200 Subject: [PATCH] [REF] website: review customize dialog JS --- addons/website/static/src/js/widgets/theme.js | 397 ++++++++---------- 1 file changed, 181 insertions(+), 216 deletions(-) diff --git a/addons/website/static/src/js/widgets/theme.js b/addons/website/static/src/js/widgets/theme.js index 766e77e4dd4..8e8eb20d030 100644 --- a/addons/website/static/src/js/widgets/theme.js +++ b/addons/website/static/src/js/widgets/theme.js @@ -1,31 +1,33 @@ odoo.define('website.theme', function (require) { 'use strict'; -var ajax = require('web.ajax'); +var config = require('web.config'); var core = require('web.core'); -var session = require('web.session'); -var Widget = require('web.Widget'); +var Dialog = require('web.Dialog'); var weContext = require('web_editor.context'); var websiteNavbarData = require('website.navbar'); -var QWeb = core.qweb; +var _t = core._t; var templateDef = null; -var ThemeCustomizeDialog = Widget.extend({ +var ThemeCustomizeDialog = Dialog.extend({ template: 'website.theme_customize', events: { - 'change input[data-xmlid],input[data-enable],input[data-disable]': 'change_selection', - 'mousedown label:has(input[data-xmlid],input[data-enable],input[data-disable])': function (event) { - var self = this; - this.time_select = _.defer(function () { - var input = $(event.target).find('input').length ? $(event.target).find('input') : $(event.target).parent().find('input'); - self.on_select(input, event); - }); - }, - 'click .close': 'close', - 'click': 'click', + 'change [data-xmlid], [data-enable], [data-disable]': '_onChange', }, + + /** + * @constructor + */ + init: function (parent, options) { + this._super(parent, _.extend({ + title: _t("Customize this theme"), + }, options || {})); + }, + /** + * @override + */ willStart: function () { if (templateDef === null) { templateDef = this._rpc({ @@ -33,246 +35,209 @@ var ThemeCustomizeDialog = Widget.extend({ method: 'read_template', args: ['website.theme_customize', weContext.get()], }).then(function (data) { - return QWeb.add_template(data); + return core.qweb.add_template(data); }); } return $.when(this._super.apply(this, arguments), templateDef); }, + /** + * @override + */ start: function () { var self = this; - this.timer = null; - this.reload = false; - this.flag = false; - this.active_select_tags(); - this.$inputs = this.$('input[data-xmlid],input[data-enable],input[data-disable]'); - setTimeout(function () {self.$el.addClass('in');}, 0); - this.keydown_escape = function (event) { - if (event.keyCode === 27) { - self.close(); - } - }; - $(document).on('keydown', this.keydown_escape); - return this.load_xml_data().then(function () { - self.flag = true; - }); + + this.$modal.addClass('o_theme_customize_modal'); + + this.$inputs = this.$('[data-xmlid], [data-enable], [data-disable]'); + + return $.when( + this._super.apply(this, arguments), + this._loadViews() + ); }, - active_select_tags: function () { - var uniqueID = 0; + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _loadViews: function () { var self = this; - var $selects = this.$('select:has(option[data-xmlid],option[data-enable],option[data-disable])'); - $selects.each(function () { - uniqueID++; - var $select = $(this); - var $options = $select.find('option[data-xmlid], option[data-enable], option[data-disable]'); - $options.each(function () { - var $option = $(this); - var $input = $(''); - $input.attr('id', $option.attr('id')); - $input.attr('data-xmlid', $option.data('xmlid')); - $input.attr('data-enable', $option.data('enable')); - $input.attr('data-disable', $option.data('disable')); - $option.removeAttr('id'); - $option.data('input', $input); - $input.on('update', function () { - $option.attr('selected', $(this).prop('checked')); - }); - self.$el.append($input); - }); - $select.data('value', $options.first()); - $options.first().attr('selected', true); - }); - $selects.change(function () { - var $option = $(this).find('option:selected'); - $(this).data('value').data('input').prop('checked', true).change(); - $(this).data('value', $option); - $option.data('input').change(); - }); - }, - load_xml_data: function () { - var self = this; - $('#theme_error').remove(); return this._rpc({ route: '/website/theme_customize_get', params: { - xml_ids: this.get_xml_ids(this.$inputs), + xml_ids: this._getXMLIDs(this.$inputs), }, - }).done(function (data) { - self.$inputs.filter('[data-xmlid=""]').prop('checked', true).change(); - self.$inputs.filter('[data-xmlid]:not([data-xmlid=""])').each(function () { - if (!_.difference(self.get_xml_ids($(this)), data[1]).length) { - $(this).prop('checked', false).trigger('change', true); - } - if (!_.difference(self.get_xml_ids($(this)), data[0]).length) { - $(this).prop('checked', true).trigger('change', true); + }).done(function (data) { + self.$inputs.prop('checked', false); + _.each(self.$inputs.filter('[data-xmlid]:not([data-xmlid=""])'), function (input) { + var $input = $(input); + if (!_.difference(self._getXMLIDs($input), data[0]).length) { + $input.prop('checked', true); } }); + _.each(self.$inputs.filter('[data-xmlid=""]'), function (input) { + var $input = $(input); + if (!self.$inputs.filter('[name="' + $input.attr('name') + '"]:checked').length) { + $input.prop('checked', true); + } + }); + self._setActive(); }).fail(function (d, error) { - $('body').prepend($('
').text(error.data.message)); + Dialog.alert(this, error.data.message) }); }, - get_inputs: function (string) { - return this.$inputs.filter('#'+string.split(/\s*,\s*/).join(', #')); + /** + * @private + */ + _getInputs: function (string) { + if (!string) { + return $(); + } + return this.$inputs.filter('#' + string.replace(/\s*,\s*/g, ', #')); }, - get_xml_ids: function ($inputs) { - var xml_ids = []; - $inputs.each(function () { - if ($(this).data('xmlid') && $(this).data('xmlid').length) { - xml_ids = xml_ids.concat($(this).data('xmlid').split(/\s*,\s*/)); + /** + * @private + */ + _getXMLIDs: function ($inputs) { + var xmlIDs = []; + _.each($inputs, function (input) { + var $input = $(input); + var xmlID = $input.data('xmlid'); + if (xmlID) { + xmlIDs = xmlIDs.concat(xmlID.split(/\s*,\s*/)); } }); - return xml_ids; + return xmlIDs; }, - update_style: function (enable, disable, reload) { - if (this.$el.hasClass('loading')) { - return; - } - this.$el.addClass('loading'); + /** + * @abstract + * @private + */ + _processChange: function ($inputs, event) {}, + /** + * @private + */ + _setActive: function () { + var self = this; - if (!reload && session.debug !== 'assets') { - var self = this; - return this._rpc({ - route: '/website/theme_customize', - params: { - enable: enable, - disable: disable, - get_bundle: true, - }, - }).then(function (bundleHTML) { - var $links = $('link[href*=".assets_frontend"]'); - var $newLinks = $(bundleHTML).filter('link'); + // Look at all options to see if they are enabled or disabled + var $enable = this.$inputs.filter('[data-xmlid]:checked'); + var $disable = this.$inputs.filter('[data-xmlid]:not(:checked)'); - var linksLoaded = $.Deferred(); - var nbLoaded = 0; - $newLinks.on('load', function (e) { - if (++nbLoaded >= $newLinks.length) { - linksLoaded.resolve(); - } - }); - $newLinks.on('error', function (e) { - linksLoaded.reject(); - window.location.hash = 'theme=true'; - window.location.reload(); - }); + // Mark the labels as checked accordingly + this.$('label').removeClass('checked'); + $enable.closest('label').addClass('checked'); - $links.last().after($newLinks); - return linksLoaded.then(function () { - $links.remove(); - self.$el.removeClass('loading'); - }); + // Mark the option sets as checked if all their option are checked/unchecked + var $sets = this.$inputs.filter('[data-enable], [data-disable]').not('[data-xmlid]'); + _.each($sets, function (set) { + var $set = $(set); + var checked = true; + if (self._getInputs($set.data('enable')).not(':checked').length) { + checked = false; + } + if (self._getInputs($set.data('disable')).filter(':checked').length) { + checked = false; + } + $set.prop('checked', checked).closest('label').toggleClass('checked', checked); + }); + }, + /** + * @private + */ + _updateStyle: function (enable, disable, reload) { + this.$modal.addClass('loading'); + + if (reload || config.debug === 'assets') { + window.location.href = $.param.querystring('/website/theme_customize_reload', { + href: window.location.href, + enable: enable.join(','), + disable: disable.join(','), }); - } else { - var href = '/website/theme_customize_reload'+ - '?href='+encodeURIComponent(window.location.href)+ - '&enable='+encodeURIComponent(enable.join(','))+ - '&disable='+encodeURIComponent(disable.join(',')); - window.location.href = href; return $.Deferred(); } - }, - enable_disable: function ($inputs, enable) { - $inputs.each(function () { - var check = $(this).prop('checked'); - var $label = $(this).closest('label'); - $(this).prop('checked', enable); - if (enable) $label.addClass('checked'); - else $label.removeClass('checked'); - if (check !== enable) { - $(this).change(); - } + + var self = this; + return this._rpc({ + route: '/website/theme_customize', + params: { + enable: enable, + disable: disable, + get_bundle: true, + }, + }).then(function (bundleHTML) { + var frontendLinkSelector = 'link[href*=".assets_frontend"]'; + var $links = $(frontendLinkSelector); + var $newLinks = $(bundleHTML).filter(frontendLinkSelector); + + var linksLoaded = $.Deferred(); + var nbLoaded = 0; + $newLinks.on('load', function (e) { + if (++nbLoaded >= $newLinks.length) { + linksLoaded.resolve(); + } + }); + $newLinks.on('error', function (e) { + linksLoaded.reject(); + window.location.hash = 'theme=true'; + window.location.reload(); + }); + + $links.last().after($newLinks); + return linksLoaded.then(function () { + $links.remove(); + self.$modal.removeClass('loading'); + }); }); }, - change_selection: function (event, init_mode) { + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {Event} ev + */ + _onChange: function (ev) { var self = this; - clearTimeout(this.time_select); - if (this.$el.hasClass('loading')) return; // prevent to change selection when css is loading - - var $option = $(event.target).is('input') ? $(event.target) : $('input', event.target), - $options = $option, - checked = $option.prop('checked'); + // Checkout the option that changed + var $option = $(ev.target).find('input').addBack('input'); + var $options = $option; + var checked = $option.is(':checked'); + // If it was enabled, enable/disable the related input (see data-enable, data-disable) + // and retain the ones that actually changed if (checked) { var $inputs; - if ($option.data('enable')) { - $inputs = this.get_inputs($option.data('enable')); - $options = $options.add($inputs.filter(':not(:checked)')); - this.enable_disable($inputs, true); - } - if ($option.data('disable')) { - $inputs = this.get_inputs($option.data('disable')); - $options = $options.add($inputs.filter(':checked')); - this.enable_disable($inputs, false); - } - $option.closest('label').addClass('checked'); - } else { - $option.closest('label').removeClass('checked'); + // Input to enable + $inputs = this._getInputs($option.data('enable')); + $options = $options.add($inputs.filter(':not(:checked)')); + $inputs.prop('checked', true); + // Input to disable + $inputs = this._getInputs($option.data('disable')); + $options = $options.add($inputs.filter(':checked')); + $inputs.prop('checked', false); } + // Look at all options to see if they are enabled or disabled var $enable = this.$inputs.filter('[data-xmlid]:checked'); - $enable.closest('label').addClass('checked'); var $disable = this.$inputs.filter('[data-xmlid]:not(:checked)'); - $disable.closest('label').removeClass('checked'); - var $sets = this.$inputs.filter('input[data-enable]:not([data-xmlid]), input[data-disable]:not([data-xmlid])'); - $sets.each(function () { - var $set = $(this); - var checked = true; - if ($set.data('enable')) { - self.get_inputs($(this).data('enable')).each(function () { - if (!$(this).prop('checked')) checked = false; - }); - } - if ($set.data('disable')) { - self.get_inputs($(this).data('disable')).each(function () { - if ($(this).prop('checked')) checked = false; - }); - } - if (checked) { - $set.prop('checked', true).closest('label').addClass('checked'); - } else { - $set.prop('checked', false).closest('label').removeClass('checked'); - } - $set.trigger('update'); - }); + this._setActive(); - if (this.flag && $option.data('reload') && document.location.href.match(new RegExp( $option.data('reload') ))) { - this.reload = true; - } - - clearTimeout(this.timer); - if (this.flag) { - this.timer = _.defer(function () { - if (!init_mode) self.on_select($options, event); - self.update_style(self.get_xml_ids($enable), self.get_xml_ids($disable), self.reload); - self.reload = false; - }); - } else { - this.timer = _.defer(function () { - if (!init_mode) self.on_select($options, event); - self.reload = false; - }); - } + // Update the style according to the whole set of options + self._processChange($options, ev); + self._updateStyle( + self._getXMLIDs($enable), + self._getXMLIDs($disable), + $option.data('reload') && window.location.href.match(new RegExp($option.data('reload'))) + ); }, - /* Method call when the user change the selection or click on an input - * @values: all changed inputs - */ - on_select: function ($inputs, event) { - clearTimeout(this.time_select); - }, - click: function (event) { - if (!$(event.target).closest('#theme_customize_modal > *').length) { - this.close(); - } - }, - close: function () { - var self = this; - $(document).off('keydown', this.keydown_escape); - $('#theme_error').remove(); - $('link[href*=".assets_"]').removeAttr('data-loading'); - this.$el.removeClass('in'); - this.$el.addClass('out'); - setTimeout(function () {self.destroy();}, 500); - } }); var ThemeCustomizeMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ @@ -306,11 +271,11 @@ var ThemeCustomizeMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * @returns {Deferred} */ _openThemeCustomizeDialog: function () { - return new ThemeCustomizeDialog(this).appendTo(document.body); + return new ThemeCustomizeDialog(this).open(); }, }); websiteNavbarData.websiteNavbarRegistry.add(ThemeCustomizeMenu, '#theme_customize'); return ThemeCustomizeDialog; -}); +}); \ No newline at end of file