odoo.define('web_editor.rte.summernote', function (require) { 'use strict'; var ajax = require('web.ajax'); var Class = require('web.Class'); var core = require('web.core'); var ColorpickerDialog = require('web.colorpicker'); var mixins = require('web.mixins'); var base = require('web_editor.base'); var weContext = require('web_editor.context'); var rte = require('web_editor.rte'); var weWidgets = require('web_editor.widget'); var QWeb = core.qweb; var _t = core._t; ajax.jsonRpc('/web/dataset/call', 'call', { 'model': 'ir.ui.view', 'method': 'read_template', 'args': ['web_editor.colorpicker', weContext.get()] }).done(function (data) { QWeb.add_template(data); }); // Summernote Lib (neek change to make accessible: method and object) var dom = $.summernote.core.dom; var range = $.summernote.core.range; var eventHandler = $.summernote.eventHandler; var renderer = $.summernote.renderer; var tplButton = renderer.getTemplate().button; var tplIconButton = renderer.getTemplate().iconButton; var tplDropdown = renderer.getTemplate().dropdown; function _rgbToHex(cssColor) { var rgba = cssColor.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/); if (!rgba) { return cssColor; } if (rgba[4]) { return cssColor; } var hex = ColorpickerDialog.prototype.convertRgbToHex( parseInt(rgba[1]), parseInt(rgba[2]), parseInt(rgba[3]) ); if (!hex) { return cssColor; // TODO handle error } return hex.hex.toUpperCase(); } // Update and change the popovers content, and add history button var fn_createPalette = renderer.createPalette; renderer.createPalette = function ($container, options) { fn_createPalette.call(this, $container, options); if (!QWeb.has_template('web_editor.colorpicker')) { return; } var $clpicker = $(QWeb.render('web_editor.colorpicker')); var groups; if ($clpicker.is("colorpicker")) { groups = _.map($clpicker.find('[data-name="theme"]'), function (el) { return $(el).find("button").empty(); }); } else { groups = [$clpicker.find("button").empty()]; } var html = "
" + _t("Theme colors") + "
" + _.map(groups, function ($group) { var $row = $("
", {"class": "note-color-row mb8"}).append($group); var $after_breaks = $row.find(".o_small + :not(.o_small)"); if ($after_breaks.length === 0) { $after_breaks = $row.find(":nth-child(8n+9)"); } $after_breaks.addClass("o_clear"); return $row[0].outerHTML; }).join("") + "
" + _t("Common colors") + "
"; var $palettes = $container.find(".note-color .note-color-palette"); $palettes.prepend(html); // Find the custom colors which are used in the page and add them to the color palette var colors = []; var $editable = window.__EditorMenuBar_$editable || $(); _.each($editable.find('[style*="color"]'), function (element) { if (element.style.color) { colors.push(element.style.color); } if (element.style.backgroundColor) { colors.push(element.style.backgroundColor); } }); var $customColorPalettes = $container.find('.note-color .note-custom-color-palette').append($('
', {class: "note-color-row"})); var $customColorRows = $customColorPalettes.find('.note-color-row'); _.each(_.uniq(colors), function (color) { var hexColor = _rgbToHex(color); if (_.indexOf(_.flatten(options.colors), hexColor) < 0) { // Create button for used custom color for backColor and foreColor both and add them into palette $customColorRows.append(''); //////////////// link popover $linkPopover.find('.popover-body').append($airPopover.find(".note-history").clone()); $linkPopover.find('button[data-event="showLinkDialog"] i').attr("class", "fa fa-link"); $linkPopover.find('button[data-event="unlink"]').before($airPopover.find('button[data-event="showImageDialog"]').clone()); //////////////// text/air popover //// highlight the text format $airPopover.find('.note-style .dropdown-toggle').on('mousedown', function () { var $format = $airPopover.find('[data-event="formatBlock"]'); var node = range.create().sc; var formats = $format.map(function () { return $(this).data("value"); }).get(); while (node && (!node.tagName || (!node.tagName || formats.indexOf(node.tagName.toLowerCase()) === -1))) { node = node.parentNode; } $format.removeClass('active'); $format.filter('[data-value="'+(node ? node.tagName.toLowerCase() : "p")+'"]') .addClass("active"); }); //////////////// tooltip setTimeout(function () { $airPopover.add($linkPopover).add($imagePopover).find("button") .tooltip('dispose') .tooltip({ container: 'body', trigger: 'hover', placement: 'bottom' }).on('click', function () {$(this).tooltip('hide');}); }); return $popover; }; var fn_boutton_update = eventHandler.modules.popover.button.update; eventHandler.modules.popover.button.update = function ($container, oStyle) { // stop animation when edit content var previous = $(".note-control-selection").data('target'); if (previous) { var $previous = $(previous); $previous.css({"-webkit-animation-play-state": "", "animation-play-state": "", "-webkit-transition": "", "transition": "", "-webkit-animation": "", "animation": ""}); $previous.find('.o_we_selected_image').addBack('.o_we_selected_image').removeClass('o_we_selected_image'); } // end fn_boutton_update.call(this, $container, oStyle); $container.find('.note-color').removeClass('d-none'); if (oStyle.image) { $container.find('[data-event]').removeClass("active"); $container.find('a[data-event="padding"][data-value="small"]').toggleClass("active", $(oStyle.image).hasClass("padding-small")); $container.find('a[data-event="padding"][data-value="medium"]').toggleClass("active", $(oStyle.image).hasClass("padding-medium")); $container.find('a[data-event="padding"][data-value="large"]').toggleClass("active", $(oStyle.image).hasClass("padding-large")); $container.find('a[data-event="padding"][data-value="xl"]').toggleClass("active", $(oStyle.image).hasClass("padding-xl")); $container.find('a[data-event="padding"][data-value=""]').toggleClass("active", !$container.find('li a.active[data-event="padding"]').length); $(oStyle.image).addClass('o_we_selected_image'); if (dom.isImgFont(oStyle.image)) { $container.find('[data-event="customColor"][data-value="foreColor"]').attr('data-color', $(oStyle.image).css('color')); $container.find('[data-event="customColor"][data-value="backColor"]').attr('data-color', $(oStyle.image).css('background-color')); $container.find('.note-fore-color-preview > button').css('border-bottom-color', $(oStyle.image).css('color')); $container.find('.note-back-color-preview > button').css('border-bottom-color', $(oStyle.image).css('background-color')); $container.find('.btn-group:not(.only_fa):has(button[data-event="resize"],button[data-value="img-thumbnail"])').addClass('d-none'); $container.find('.only_fa').removeClass('d-none'); $container.find('button[data-event="resizefa"][data-value="2"]').toggleClass("active", $(oStyle.image).hasClass("fa-2x")); $container.find('button[data-event="resizefa"][data-value="3"]').toggleClass("active", $(oStyle.image).hasClass("fa-3x")); $container.find('button[data-event="resizefa"][data-value="4"]').toggleClass("active", $(oStyle.image).hasClass("fa-4x")); $container.find('button[data-event="resizefa"][data-value="5"]').toggleClass("active", $(oStyle.image).hasClass("fa-5x")); $container.find('button[data-event="resizefa"][data-value="1"]').toggleClass("active", !$container.find('.active[data-event="resizefa"]').length); $container.find('button[data-event="imageShape"][data-value="fa-spin"]').toggleClass("active", $(oStyle.image).hasClass("fa-spin")); $container.find('button[data-event="imageShape"][data-value="shadow"]').toggleClass("active", $(oStyle.image).hasClass("shadow")); $container.find('.btn-group:has(button[data-event="imageShape"])').removeClass("d-none"); } else { $container.find('.d-none:not(.only_fa, .note-recent-color)').removeClass('d-none'); $container.find('.only_fa').addClass('d-none'); var width = ($(oStyle.image).attr('style') || '').match(/(^|;|\s)width:\s*([0-9]+%)/); if (width) { width = width[2]; } $container.find('button[data-event="resize"][data-value="auto"]').toggleClass("active", width !== "100%" && width !== "50%" && width !== "25%"); $container.find('button[data-event="resize"][data-value="1"]').toggleClass("active", width === "100%"); $container.find('button[data-event="resize"][data-value="0.5"]').toggleClass("active", width === "50%"); $container.find('button[data-event="resize"][data-value="0.25"]').toggleClass("active", width === "25%"); $container.find('button[data-event="imageShape"][data-value="shadow"]').toggleClass("active", $(oStyle.image).hasClass("shadow")); if (!$(oStyle.image).is("img")) { $container.find('.btn-group:has(button[data-event="imageShape"])').addClass('d-none'); } $container.find('.note-color').addClass('d-none'); } $container.find('button[data-event="floatMe"][data-value="left"]').toggleClass("active", $(oStyle.image).hasClass("float-left")); $container.find('button[data-event="floatMe"][data-value="center"]').toggleClass("active", $(oStyle.image).hasClass("d-block mx-auto")); $container.find('button[data-event="floatMe"][data-value="right"]').toggleClass("active", $(oStyle.image).hasClass("float-right")); $(oStyle.image).trigger('attributes_change'); } else { $container.find('[data-event="customColor"][data-value="foreColor"]').attr('data-color', oStyle.color); $container.find('[data-event="customColor"][data-value="backColor"]').attr('data-color', oStyle['background-color']); $container.find('.note-fore-color-preview > button').css('border-bottom-color', oStyle.color); $container.find('.note-back-color-preview > button').css('border-bottom-color', oStyle['background-color']); } }; var fn_popover_update = eventHandler.modules.popover.update; eventHandler.modules.popover.update = function ($popover, oStyle, isAirMode) { var $imagePopover = $popover.find('.note-image-popover'); var $linkPopover = $popover.find('.note-link-popover'); var $airPopover = $popover.find('.note-air-popover'); fn_popover_update.call(this, $popover, oStyle, isAirMode); if (oStyle.image) { if (oStyle.image.parentNode.className.match(/(^|\s)media_iframe_video(\s|$)/i)) { oStyle.image = oStyle.image.parentNode; } var alt = $(oStyle.image).attr("alt"); $imagePopover.find('.o_image_alt').text( (alt || "").replace(/"/g, '"') ).parent().toggle(oStyle.image.tagName === "IMG"); $imagePopover.show(); // for video tag (non-void) we select the range over the tag, // for other media types we get the first descendant leaf element var target_node = oStyle.image; if (!oStyle.image.className.match(/(^|\s)media_iframe_video(\s|$)/i)) { target_node = dom.firstChild(target_node); } range.createFromNode(target_node).select(); // save range on the editor so it is not lost if restored eventHandler.modules.editor.saveRange(dom.makeLayoutInfo(target_node).editable()); } else { $(".note-control-selection").hide(); } if (oStyle.image || (oStyle.range && (!oStyle.range.isCollapsed() || (oStyle.range.sc.tagName && !dom.isAnchor(oStyle.range.sc)))) || (oStyle.image && !$(oStyle.image).closest('a').length)) { $linkPopover.hide(); oStyle.anchor = false; } if (oStyle.image || oStyle.anchor || (oStyle.range && !$(oStyle.range.sc).closest('.note-editable').length)) { $airPopover.hide(); } else { $airPopover.show(); } }; var fn_handle_update = eventHandler.modules.handle.update; eventHandler.modules.handle.update = function ($handle, oStyle, isAirMode) { fn_handle_update.call(this, $handle, oStyle, isAirMode); if (oStyle.image) { $handle.find('.note-control-selection').hide(); } }; // Hack for image and link editor function getImgTarget($editable) { var $handle = $editable ? dom.makeLayoutInfo($editable).handle() : undefined; return $(".note-control-selection", $handle).data('target'); } eventHandler.modules.editor.padding = function ($editable, sValue) { var $target = $(getImgTarget($editable)); var paddings = "small medium large xl".split(/\s+/); $editable.data('NoteHistory').recordUndo(); if (sValue.length) { paddings.splice(paddings.indexOf(sValue),1); $target.toggleClass('padding-'+sValue); } $target.removeClass("padding-" + paddings.join(" padding-")); }; eventHandler.modules.editor.resize = function ($editable, sValue) { var $target = $(getImgTarget($editable)); $editable.data('NoteHistory').recordUndo(); var width = ($target.attr('style') || '').match(/(^|;|\s)width:\s*([0-9]+)%/); if (width) { width = width[2]/100; } $target.css('width', (width !== sValue && sValue !== "auto") ? (sValue * 100) + '%' : ''); }; eventHandler.modules.editor.resizefa = function ($editable, sValue) { var $target = $(getImgTarget($editable)); $editable.data('NoteHistory').recordUndo(); $target.attr('class', $target.attr('class').replace(/\s*fa-[0-9]+x/g, '')); if (+sValue > 1) { $target.addClass('fa-'+sValue+'x'); } }; eventHandler.modules.editor.floatMe = function ($editable, sValue) { var $target = $(getImgTarget($editable)); $editable.data('NoteHistory').recordUndo(); switch (sValue) { case 'center': $target.toggleClass('d-block mx-auto').removeClass('float-right float-left'); break; case 'left': $target.toggleClass('float-left').removeClass('float-right d-block mx-auto'); break; case 'right': $target.toggleClass('float-right').removeClass('float-left d-block mx-auto'); break; } }; eventHandler.modules.editor.imageShape = function ($editable, sValue) { var $target = $(getImgTarget($editable)); $editable.data('NoteHistory').recordUndo(); $target.toggleClass(sValue); }; eventHandler.modules.linkDialog.showLinkDialog = function ($editable, $dialog, linkInfo) { $editable.data('range').select(); $editable.data('NoteHistory').recordUndo(); var def = new $.Deferred(); core.bus.trigger('link_dialog_demand', { $editable: $editable, linkInfo: linkInfo, onSave: function (linkInfo) { linkInfo.range.select(); $editable.data('range', linkInfo.range); def.resolve(linkInfo); $editable.trigger('keyup'); $('.note-popover .note-link-popover').show(); }, onCancel: def.reject.bind(def), }); return def; }; eventHandler.modules.imageDialog.showImageDialog = function ($editable) { var r = $editable.data('range'); if (r.sc.tagName && r.sc.childNodes.length) { r.sc = r.sc.childNodes[r.so]; } var media = $(r.sc).parents().addBack().filter(function (i, el) { return dom.isImg(el); })[0]; core.bus.trigger('media_dialog_demand', { $editable: $editable, media: media, options: { lastFilters: ['background'], onUpload: $editable.data('callbacks').onUpload, noVideos: $editable.data('oe-model') === "mail.compose.message", }, }); return new $.Deferred().reject(); }; $.summernote.pluginEvents.alt = function (event, editor, layoutInfo, sorted) { var $editable = layoutInfo.editable(); var $selection = layoutInfo.handle().find('.note-control-selection'); core.bus.trigger('alt_dialog_demand', { $editable: $editable, media: $selection.data('target'), }); }; $.summernote.pluginEvents.customColor = function (event, editor, layoutInfo, customColor) { var defaultColor = event.target.dataset.color; core.bus.trigger('color_picker_dialog_demand', { color: defaultColor === 'rgba(0, 0, 0, 0)' ? 'rgb(255, 0, 0)' : defaultColor, onSave: function (color) { var $palettes = $(event.currentTarget).find('.note-custom-color-palette > .note-color-row') .append(('