From 3751379f1e9a4c215fb6eb898b4ccc67659b9ace Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Tue, 10 May 2022 21:51:50 +0700 Subject: initial commit 2 --- .../static/src/js/wysiwyg/widgets/alt_dialog.js | 62 + .../static/src/js/wysiwyg/widgets/color_palette.js | 410 ++++++ .../static/src/js/wysiwyg/widgets/dialog.js | 81 ++ .../src/js/wysiwyg/widgets/image_crop_widget.js | 213 +++ .../static/src/js/wysiwyg/widgets/link_dialog.js | 339 +++++ .../static/src/js/wysiwyg/widgets/media.js | 1463 ++++++++++++++++++++ .../static/src/js/wysiwyg/widgets/media_dialog.js | 279 ++++ .../static/src/js/wysiwyg/widgets/widgets.js | 29 + 8 files changed, 2876 insertions(+) create mode 100644 addons/web_editor/static/src/js/wysiwyg/widgets/alt_dialog.js create mode 100644 addons/web_editor/static/src/js/wysiwyg/widgets/color_palette.js create mode 100644 addons/web_editor/static/src/js/wysiwyg/widgets/dialog.js create mode 100644 addons/web_editor/static/src/js/wysiwyg/widgets/image_crop_widget.js create mode 100644 addons/web_editor/static/src/js/wysiwyg/widgets/link_dialog.js create mode 100644 addons/web_editor/static/src/js/wysiwyg/widgets/media.js create mode 100644 addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js create mode 100644 addons/web_editor/static/src/js/wysiwyg/widgets/widgets.js (limited to 'addons/web_editor/static/src/js/wysiwyg/widgets') diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/alt_dialog.js b/addons/web_editor/static/src/js/wysiwyg/widgets/alt_dialog.js new file mode 100644 index 00000000..80f143b6 --- /dev/null +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/alt_dialog.js @@ -0,0 +1,62 @@ +odoo.define('wysiwyg.widgets.AltDialog', function (require) { +'use strict'; + +var core = require('web.core'); +var Dialog = require('wysiwyg.widgets.Dialog'); + +var _t = core._t; + +/** + * Let users change the alt & title of a media. + */ +var AltDialog = Dialog.extend({ + template: 'wysiwyg.widgets.alt', + xmlDependencies: Dialog.prototype.xmlDependencies.concat( + ['/web_editor/static/src/xml/wysiwyg.xml'] + ), + + /** + * @constructor + */ + init: function (parent, options, media) { + options = options || {}; + this._super(parent, _.extend({}, { + title: _t("Change media description and tooltip") + }, options)); + + this.trigger_up('getRecordInfo', { + recordInfo: options, + callback: function (recordInfo) { + _.defaults(options, recordInfo); + }, + }); + + this.media = media; + var allEscQuots = /"/g; + this.alt = ($(this.media).attr('alt') || "").replace(allEscQuots, '"'); + var title = $(this.media).attr('title') || $(this.media).data('original-title') || ""; + this.tag_title = (title).replace(allEscQuots, '"'); + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @override + */ + save: function () { + var alt = this.$('#alt').val(); + var title = this.$('#title').val(); + var allNonEscQuots = /"/g; + $(this.media).attr('alt', alt ? alt.replace(allNonEscQuots, """) : null) + .attr('title', title ? title.replace(allNonEscQuots, """) : null); + $(this.media).trigger('content_changed'); + this.final_data = this.media; + return this._super.apply(this, arguments); + }, +}); + + +return AltDialog; +}); diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/color_palette.js b/addons/web_editor/static/src/js/wysiwyg/widgets/color_palette.js new file mode 100644 index 00000000..d00abd1a --- /dev/null +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/color_palette.js @@ -0,0 +1,410 @@ +odoo.define('web_editor.ColorPalette', function (require) { +'use strict'; + +const ajax = require('web.ajax'); +const core = require('web.core'); +const session = require('web.session'); +const {ColorpickerWidget} = require('web.Colorpicker'); +const Widget = require('web.Widget'); +const summernoteCustomColors = require('web_editor.rte.summernote_custom_colors'); +const weUtils = require('web_editor.utils'); + +const qweb = core.qweb; + +const ColorPaletteWidget = Widget.extend({ + // ! for xmlDependencies, see loadDependencies function + template: 'web_editor.snippet.option.colorpicker', + events: { + 'click .o_we_color_btn': '_onColorButtonClick', + 'mouseenter .o_we_color_btn': '_onColorButtonEnter', + 'mouseleave .o_we_color_btn': '_onColorButtonLeave', + 'click .o_we_colorpicker_switch_pane_btn': '_onSwitchPaneButtonClick', + }, + custom_events: { + 'colorpicker_select': '_onColorPickerSelect', + 'colorpicker_preview': '_onColorPickerPreview', + }, + /** + * @override + * + * @param {Object} [options] + * @param {string} [options.selectedColor] The class or css attribute color selected by default. + * @param {boolean} [options.resetButton=true] Whether to display or not the reset button. + * @param {string[]} [options.excluded=[]] Sections not to display. + * @param {string[]} [options.excludeSectionOf] Extra section to exclude: the one containing the named color. + * @param {JQuery} [options.$editable=$()] Editable content from which the custom colors are retrieved. + */ + init: function (parent, options) { + this._super.apply(this, arguments); + this.summernoteCustomColorsArray = [].concat(...summernoteCustomColors); + this.style = window.getComputedStyle(document.documentElement); + this.options = _.extend({ + selectedColor: false, + resetButton: true, + excluded: [], + excludeSectionOf: null, + $editable: $(), + withCombinations: false, + }, options || {}); + + this.selectedColor = ''; + this.resetButton = this.options.resetButton; + this.withCombinations = this.options.withCombinations; + + this.trigger_up('request_editable', {callback: val => this.options.$editable = val}); + }, + /** + * @override + */ + willStart: async function () { + await this._super(...arguments); + await ColorPaletteWidget.loadDependencies(this); + }, + /** + * @override + */ + start: async function () { + const res = this._super.apply(this, arguments); + + const $colorSection = this.$('.o_colorpicker_sections[data-color-tab="theme-colors"]'); + const $clpicker = qweb.has_template('web_editor.colorpicker') + ? $(qweb.render('web_editor.colorpicker')) + : $(`
`); + $clpicker.find('button').addClass('o_we_color_btn'); + $clpicker.appendTo($colorSection); + + // Remove excluded palettes (note: only hide them to still be able + // to remove their related colors on the DOM target) + _.each(this.options.excluded, exc => { + this.$('[data-name="' + exc + '"]').addClass('d-none'); + }); + if (this.options.excludeSectionOf) { + this.$('[data-name]:has([data-color="' + this.options.excludeSectionOf + '"])').addClass('d-none'); + } + + this.el.querySelectorAll('.o_colorpicker_section').forEach(elem => { + $(elem).prepend('
' + (elem.dataset.display || '') + '
'); + }); + + // Render common colors + if (!this.options.excluded.includes('common')) { + const $commonColorSection = this.$('[data-name="common"]'); + summernoteCustomColors.forEach((colorRow, i) => { + if (i === 0) { + return; // Ignore the summernote gray palette and use ours + } + const $div = $('
', {class: 'clearfix'}).appendTo($commonColorSection); + colorRow.forEach(color => { + $div.append(this._createColorButton(color, ['o_common_color'])); + }); + }); + } + + // Compute class colors + const compatibilityColorNames = ['primary', 'secondary', 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'success', 'info', 'warning', 'danger']; + this.colorNames = [...compatibilityColorNames]; + this.colorToColorNames = {}; + this.el.querySelectorAll('button[data-color]').forEach(elem => { + const colorName = elem.dataset.color; + const $color = $(elem); + const isCCName = weUtils.isColorCombinationName(colorName); + if (isCCName) { + $color.find('.o_we_cc_preview_wrapper').addClass(`o_cc o_cc${colorName}`); + } else { + $color.addClass(`bg-${colorName}`); + } + this.colorNames.push(colorName); + if (!isCCName && !elem.classList.contains('d-none')) { + const color = weUtils.getCSSVariableValue(colorName, this.style); + this.colorToColorNames[color] = colorName; + } + }); + + // Select selected Color and build customColors. + // If no color is selected selectedColor is an empty string (transparent is interpreted as no color) + if (this.options.selectedColor) { + let selectedColor = this.options.selectedColor; + if (compatibilityColorNames.includes(selectedColor)) { + selectedColor = weUtils.getCSSVariableValue(selectedColor, this.style) || selectedColor; + } + selectedColor = ColorpickerWidget.normalizeCSSColor(selectedColor); + if (selectedColor !== 'rgba(0, 0, 0, 0)') { + this.selectedColor = this.colorToColorNames[selectedColor] || selectedColor; + } + } + this._buildCustomColors(); + this._markSelectedColor(); + + // Colorpicker + let defaultColor = this.selectedColor; + if (defaultColor && !ColorpickerWidget.isCSSColor(defaultColor)) { + defaultColor = weUtils.getCSSVariableValue(defaultColor, this.style); + } + this.colorPicker = new ColorpickerWidget(this, { + defaultColor: defaultColor, + }); + await this.colorPicker.prependTo($colorSection); + + // TODO Added as a fix. In master, the widget should probably not be + // instantiated at all. + if (this.options.excluded.includes('custom')) { + this.colorPicker.$el.addClass('d-none'); + } + + return res; + }, + /** + * Return a list of the color names used in the color palette + */ + getColorNames: function () { + return this.colorNames; + }, + /** + * Sets the currently selected color + * + * @param {string} color rgb[a] + */ + setSelectedColor: function (color) { + this._selectColor({color: color}); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _buildCustomColors: function () { + if (this.options.excluded.includes('custom')) { + return; + } + this.el.querySelectorAll('.o_custom_color').forEach(el => el.remove()); + const existingColors = new Set(this.summernoteCustomColorsArray.concat( + Object.keys(this.colorToColorNames) + )); + this.trigger_up('get_custom_colors', { + onSuccess: (colors) => { + colors.forEach(color => { + this._addCustomColor(existingColors, color); + }); + }, + }); + weUtils.getCSSVariableValue('custom-colors', this.style).split(' ').forEach(v => { + const color = weUtils.getCSSVariableValue(v.substring(1, v.length - 1), this.style); + if (ColorpickerWidget.isCSSColor(color)) { + this._addCustomColor(existingColors, color); + } + }); + _.each(this.options.$editable.find('[style*="color"]'), el => { + for (const colorProp of ['color', 'backgroundColor']) { + this._addCustomColor(existingColors, el.style[colorProp]); + } + }); + if (this.selectedColor) { + this._addCustomColor(existingColors, this.selectedColor); + } + }, + /** + * Add the color to the custom color section if it is not in the existingColors. + * + * @param {string[]} existingColors Colors currently in the colorpicker + * @param {string} color Color to add to the cuustom colors + */ + _addCustomColor: function (existingColors, color) { + if (!color) { + return; + } + if (!ColorpickerWidget.isCSSColor(color)) { + color = weUtils.getCSSVariableValue(color, this.style); + } + const normColor = ColorpickerWidget.normalizeCSSColor(color); + if (!existingColors.has(normColor)) { + this._addCustomColorButton(normColor); + existingColors.add(normColor); + } + }, + /** + * Add a custom button in the coresponding section. + * + * @private + * @param {string} color + * @param {string[]} classes - classes added to the button + * @returns {jQuery} + */ + _addCustomColorButton: function (color, classes = []) { + classes.push('o_custom_color'); + const $themeSection = this.$('.o_colorpicker_section[data-name="theme"]'); + const $button = this._createColorButton(color, classes); + return $button.appendTo($themeSection); + }, + /** + * Return a color button. + * + * @param {string} color + * @param {string[]} classes - classes added to the button + * @returns {jQuery} + */ + _createColorButton: function (color, classes) { + return $('