diff --git a/addons/mass_mailing/static/src/js/mass_mailing_editor.js b/addons/mass_mailing/static/src/js/mass_mailing_editor.js index 7b312373e8d..7e84ff62594 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing_editor.js +++ b/addons/mass_mailing/static/src/js/mass_mailing_editor.js @@ -165,7 +165,7 @@ web_editor.Class.include({ } var $editable = $("#editable_area"); $(".o_table_handler").remove(); - $editable.parent().add("#oe_snippets, #templates, .note-popover").toggleClass("hidden"); + $editable.add("#oe_snippets, #templates, .note-popover").toggleClass("hidden"); $("#choose_template").children().toggleClass("hidden"); $("body").trigger("resize"); $(window.top).trigger('resize'); @@ -177,7 +177,7 @@ web_editor.Class.include({ var $editable = $("#editable_area"); this.rte.historyRecordUndo($editable); $editable.html( $(event.target).closest(".theme_thumbnail").find(".js_content").html() ); - $editable.parent().add("#oe_snippets, #templates, .note-popover").toggleClass("hidden"); + $editable.add("#oe_snippets, #templates, .note-popover").toggleClass("hidden"); $("#choose_template").children().toggleClass("hidden"); setTimeout(function () { $("body").trigger("resize"); @@ -191,8 +191,8 @@ web_editor.Class.include({ event.preventDefault(); }, set_snippet_theme: function (theme) { - $("#o_left_bar .o_panel_body > div").addClass("hidden"); - $("#o_left_bar .o_panel_body > div."+theme).removeClass("hidden"); + $("#oe_snippets .o_panel_body > div").addClass("hidden"); + $("#oe_snippets .o_panel_body > div."+theme).removeClass("hidden"); $("#editable_area").trigger("content_changed"); }, get_snippet_template: function (mailing_model) { @@ -225,7 +225,7 @@ snippets_editor.Class.include({ clean_for_save: function () { this._super(); var $editable = $("#editable_area"); - var theme = ($("#o_left_bar .o_panel_body > div:not(.hidden)").attr("class") || "").replace(/^\s*|\s*o_mail_block[^\s]+\s*|\s*oe_snippet\s*|\s*ui-draggable\s*|\s*$/g, ''); + var theme = ($("#oe_snippets .o_panel_body > div:not(.hidden)").attr("class") || "").replace(/^\s*|\s*o_mail_block[^\s]+\s*|\s*oe_snippet\s*|\s*ui-draggable\s*|\s*$/g, ''); var $theme = $("#editable_area [data-snippet-theme]").removeAttr("data-snippet-theme").removeData("snippet-theme"); $editable.children().first().attr("data-snippet-theme", theme); $editable.find(":not(br):hidden").remove(); diff --git a/addons/web_editor/static/lib/summernote/src/js/Renderer.js b/addons/web_editor/static/lib/summernote/src/js/Renderer.js index 189dc1707aa..52d0975ee47 100644 --- a/addons/web_editor/static/lib/summernote/src/js/Renderer.js +++ b/addons/web_editor/static/lib/summernote/src/js/Renderer.js @@ -31,11 +31,10 @@ define([ var title = options.title; var className = options.className; var dropdown = options.dropdown; - var dropdown2 = options.dropdown2; - var wrapper = options.wrapper; + var no_caret = options.no_caret; var hide = options.hide; - var btn = (dropdown ? '
' : '') + '' + (dropdown || '') + (dropdown ? '
' : ''); - - if (wrapper) btn = $(wrapper).html(btn).prop('outerHTML'); - - return btn; }; /** @@ -230,77 +225,52 @@ define([ value: '{"backColor":"#B35E9B"}' }); - var foreground = ''; - - var background = ''; + var foreground = formatStr(colorpicker_template, [lang.color.foreground, lang.color.reset, 'foreColor']); + var background = formatStr(colorpicker_template, [lang.color.background, lang.color.transparent, 'backColor']); var foregroundButton = tplButton('', { title: lang.color.foreground, - wrapper: '', - dropdown: foreground + className: 'o_foreground_toggle', + dropdown: foreground, + no_caret: true, }); var backgroundButton = tplButton('', { title: lang.color.background, - wrapper: '', - dropdown: background + className: 'o_background_toggle', + dropdown: background, + no_caret: true, }); return colorButton + foregroundButton + backgroundButton; + + function formatStr(str, values) { + return str.replace(/{(\d+)}/g, function (match, number) { + return (values[number] !== undefined)? values[number] : match; + }); + }; }, bold: function (lang, options) { return tplIconButton(options.iconPrefix + options.icons.font.bold, { @@ -848,13 +818,13 @@ define([ }); var body = document.body; - var container = $('#web_editor-toolbars') + var $container = $('#web_editor-toolbars') // create Popover var $popover = $(this.tplPopovers(langInfo, options)); // ODOO: user (maybe) overrided method $popover.addClass('note-air-layout'); $popover.attr('id', 'note-popover-' + id); - $popover.appendTo(container); + $popover.appendTo($container); createTooltip($popover, keyMap); this.createPalette($popover, options); // ODOO: use (maybe) overrided method @@ -862,7 +832,7 @@ define([ var $handle = $(tplHandles(options)); $handle.addClass('note-air-layout'); $handle.attr('id', 'note-handle-' + id); - $handle.appendTo(container); + $handle.appendTo($container); // create Dialog var $dialog = $(tplDialogs(langInfo, options)); @@ -871,7 +841,7 @@ define([ $dialog.find('button.close, a.modal-close').click(function () { $(this).closest('.modal').modal('hide'); }); - $dialog.appendTo(container); + $dialog.appendTo($container); }; /** diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js index 72bf61cbb7f..08712a05d0e 100644 --- a/addons/web_editor/static/src/js/backend.js +++ b/addons/web_editor/static/src/js/backend.js @@ -51,7 +51,7 @@ var FieldTextHtmlSimple = widget.extend({ } return config; }, - start: function() { + start: function () { var def = this._super.apply(this, arguments); this.$translate.remove(); this.$translate = $(); @@ -59,7 +59,7 @@ var FieldTextHtmlSimple = widget.extend({ this.$content.trigger('mouseup'); return def; }, - initialize_content: function() { + initialize_content: function () { var self = this; this.$textarea = this.$("textarea").val(this.get('value') || "


"); this.$content = $(); @@ -118,20 +118,20 @@ var FieldTextHtmlSimple = widget.extend({ return value; }, focus: function() { - if (this.get("effective_readonly")) { + if (!(!this.get("effective_readonly") && this.$textarea)) { return false; } // on IE an error may occur when creating range on not displayed element try { - return this.$content.focusInEnd(); + return this.$textarea.focusInEnd(); } catch (e) { - return this.$content.focus(); + return this.$textarea.focus(); } }, resize: function() { this.$('iframe').css('height', '0px').css('height', Math.max(30, Math.min(this.$content[0] ? this.$content[0].scrollHeight : 0, 500)) + 'px'); }, - render_value: function() { + render_value: function () { var value = this.get('value'); this.$textarea.val(value || ''); this.$content.html(this.text_to_html(value)); @@ -148,10 +148,10 @@ var FieldTextHtmlSimple = widget.extend({ this.$('.note-toolbar').find('button[data-event="undo"]').attr('disabled', false); } }, - is_false: function() { + is_false: function () { return !this.get('value') || this.get('value') === "


" || !this.get('value').match(/\S/); }, - before_save: function() { + before_save: function () { if (this.options['style-inline']) { transcoder.class_to_style(this.$content); transcoder.font_to_img(this.$content); @@ -268,7 +268,7 @@ var FieldTextHtml = widget.extend({ return src; }, - initialize_content: function() { + initialize_content: function () { this.$el.closest('.modal-body').css('max-height', 'none'); this.$iframe = this.$el.find('iframe'); this.document = null; @@ -334,7 +334,7 @@ var FieldTextHtml = widget.extend({ self.initialize_content(); }); }, - render_value: function() { + render_value: function () { if (this.lang !== this.view.dataset.context.lang || this.$iframe.attr('src').match(/[?&]edit_translations=1/)) { return; } @@ -354,7 +354,7 @@ var FieldTextHtml = widget.extend({ } } }, - is_false: function() { + is_false: function () { return this.get('value') === false || !this.$content.html() || !this.$content.html().match(/\S/); }, before_save: function () { diff --git a/addons/web_editor/static/src/js/base.js b/addons/web_editor/static/src/js/base.js index 4d8d128f60c..f65ecaa7c02 100644 --- a/addons/web_editor/static/src/js/base.js +++ b/addons/web_editor/static/src/js/base.js @@ -30,7 +30,7 @@ data.dom_ready = dom_ready; // todo: remove and load a bundle of translated templates var all_ready; -data.ready = function() { +data.ready = function () { if (!all_ready) { all_ready = $.when(dom_ready, ajax.loadXML()).then(translations); } else if(all_ready.state() === "resolved") { // can add async template @@ -40,9 +40,9 @@ data.ready = function() { }; function translations() { - function translate_node(node){ + function translate_node(node) { if(node.nodeType === 3) { // TEXT_NODE - if(node.nodeValue.match(/\S/)){ + if(node.nodeValue.match(/\S/)) { var space = node.nodeValue.match(/^([\s]*)([\s\S]*?)([\s]*)$/); node.nodeValue = space[1] + $.trim(_t(space[2])) + space[3]; } @@ -55,11 +55,11 @@ function translations() { 'mods': ['web_editor'], 'lang': data.get_context().lang }) - .then(function(trans) { + .then(function (trans) { _t.database.set_bundle(trans); }).then(function () { var keys = _.keys(qweb.templates); - for (var i = 0; i < keys.length; i++){ + for (var i = 0; i < keys.length; i++) { translate_node(qweb.templates[keys[i]]); } }).promise(); diff --git a/addons/web_editor/static/src/js/editor.js b/addons/web_editor/static/src/js/editor.js index 88d86bc825b..9e842bed4bc 100644 --- a/addons/web_editor/static/src/js/editor.js +++ b/addons/web_editor/static/src/js/editor.js @@ -63,9 +63,9 @@ editor.Class = Widget.extend({ template: 'web_editor.editorbar', events: { 'click button[data-action=save]': 'save', - 'click a[data-action=cancel]': 'cancel', + 'click button[data-action=cancel]': 'cancel', }, - init: function(parent) { + init: function (parent) { var self = this; var res = this._super.apply(this, arguments); this.parent = parent; @@ -75,7 +75,7 @@ editor.Class = Widget.extend({ }); return res; }, - start: function() { + start: function () { var self = this; $('.dropdown-toggle').dropdown(); @@ -86,14 +86,13 @@ editor.Class = Widget.extend({ this.rte.start(); var flag = false; - window.onbeforeunload = function(event) { + window.onbeforeunload = function (event) { if (rte.history.getEditableHasUndo().length && !flag) { flag = true; setTimeout(function () {flag=false;},0); return _t('This document is not saved!'); } }; - this.$('button[data-action="save"]').prop('disabled', true); return this._super(); }, display_placeholder: function () { @@ -119,9 +118,7 @@ editor.Class = Widget.extend({ } }); }, - rte_changed: function () { - this.$('button[data-action=save]').prop('disabled', !rte.history.getEditableHasUndo().length); - }, + rte_changed: function () {}, save: function () { return this.rte.save().then(function () { editor.reload(); diff --git a/addons/web_editor/static/src/js/inline.js b/addons/web_editor/static/src/js/inline.js index 2b6ee1c6595..8f63d603b20 100644 --- a/addons/web_editor/static/src/js/inline.js +++ b/addons/web_editor/static/src/js/inline.js @@ -57,10 +57,10 @@ snippet_editor.Class.include({ transcoder.class_to_style($editable); // fix outlook image rendering bug - _.each(['width', 'height'], function(attribute) { - $editable.find('img[style*="width"], img[style*="height"]').attr(attribute, function(){ + _.each(['width', 'height'], function (attribute) { + $editable.find('img[style*="width"], img[style*="height"]').attr(attribute, function () { return $(this)[attribute](); - }).css(attribute, function(){ + }).css(attribute, function () { return $(this).get(0).style[attribute] || 'auto'; }); }); diff --git a/addons/web_editor/static/src/js/rte.js b/addons/web_editor/static/src/js/rte.js index becaf978d17..bbd5e6616aa 100644 --- a/addons/web_editor/static/src/js/rte.js +++ b/addons/web_editor/static/src/js/rte.js @@ -203,7 +203,7 @@ var history = new History(); // add focusIn to jQuery to allow to move caret into a div of a contentEditable area $.extend($.expr[':'],{ - o_editable: function(node,i,m){ + o_editable: function (node,i,m) { while (node) { if (node.className) { if (node.className.indexOf('o_not_editable')!==-1 ) { @@ -423,11 +423,11 @@ var RTE = Widget.extend({ /** * Get HTML cloned element with text nodes escaped for XML storage */ - getEscapedElement: function($el) { + getEscapedElement: function ($el) { var escaped_el = $el.clone(); var to_escape = escaped_el.find('*').addBack(); to_escape = to_escape.not(to_escape.filter('object,iframe,script,style,[data-oe-model][data-oe-model!="ir.ui.view"]').find('*').addBack()); - to_escape.contents().each(function(){ + to_escape.contents().each(function () { if(this.nodeType == 3) { this.nodeValue = $('
').text(this.nodeValue).html(); } diff --git a/addons/web_editor/static/src/js/rte.summernote.js b/addons/web_editor/static/src/js/rte.summernote.js index 28e1a32a55e..31ee5db72fe 100644 --- a/addons/web_editor/static/src/js/rte.summernote.js +++ b/addons/web_editor/static/src/js/rte.summernote.js @@ -44,27 +44,68 @@ renderer.createPalette = function ($container, options) { return; } - var $color = $container.find('.note-color'); - var html = $(QWeb.render('web_editor.colorpicker')); - var theme = html.find('.theme') + var $clpicker = $(QWeb.render('web_editor.colorpicker')); - $color.find('.palette_theme').prepend(html.find('.theme')); - $color.find('.palette_ui').prepend(html.find('.ui')); - $color.find('.palette_grayscale').prepend(html.find('.grayscale')); + $container.find('.colorpicker').each(function (i, picker) { + var $picker = $(picker); - var $bg = $color.find('.background_toggle .dropdown-menu button'); - var $fore = $color.find('.foreground_toggle .dropdown-menu button'); + var $sections = $clpicker.find('.o_colorpicker_section').clone(true); + + var $toggles = $picker.find('.o_colorpicker_section_menu'); + var $tabs = $picker.find('.o_colorpicker_section_tabs'); + var $defaultToggle = $toggles.find('li'); + var $defaultTab = $tabs.find('.tab-pane'); + + var newDefaultID = _.uniqueId(); + $defaultToggle.children('a').attr('href', $defaultToggle.children('a').attr('href') + newDefaultID); + $defaultTab.attr('id', $defaultTab.attr('id') + newDefaultID); + + if ($sections.length) { + $sections.each(function () { + var $section = $(this); + var id = 'o_palette_' + $section.data('name') + _.uniqueId(); + + var $li = $('
  • ') + .append($('', {href: '#' + id}) + .append($('', {'class': $section.data('iconClass') || '', html: $section.data('iconContent') || ''}))); + $defaultToggle.before($li); + + $defaultTab.before($section.addClass('tab-pane').attr('id', id)); + }); + } else { + var id = 'o_palette_other' + _.uniqueId(); + + var $li = $('
  • ') + .append($('', {href: '#' + id, 'aria-controls': id}) + .append($('', {'class': 'fa fa-magic'}))); + $toggles.prepend($li); + + $tabs.prepend($('
    ', {'class': 'tab-pane', id: id}).append($clpicker.clone(true))); + } + + $toggles.find('li:first-child').addClass('active'); + $tabs.find('div:first-child').addClass('active'); + + $toggles.on('click mouseover', '> li > a', function (e) { + e.preventDefault(); + e.stopPropagation(); + $(this).tab('show'); + }); + }); + + var $bg = $container.find('.o_background_toggle .dropdown-menu button'); + var $fore = $container.find('.o_foreground_toggle .dropdown-menu button'); $bg.each(function () { - $(this).attr('data-event', 'backColor').attr('data-value', - $(this).attr($(this).attr('class') == 'note-color-btn' ?'title':'class') - ); + var $el = $(this); + var className = $el.hasClass('note-color-btn')? $el.attr('title') : ('bg-' + $el.data('color')); + $el.attr('data-event', 'backColor').attr('data-value', className).addClass(className); }); $fore.each(function () { - $(this).attr('data-event', 'foreColor').attr('data-value', - $(this).attr($(this).attr('class') == 'note-color-btn' ?'title':'class').replace(/bg-/, 'text-') - ); - }); + var $el = $(this); + var className = $el.hasClass('note-color-btn')? $el.attr('title') : ('text-' + $el.data('color')); + $el.attr('data-event', 'foreColor').attr('data-value', className).addClass(className); + }); }; var fn_tplPopovers = renderer.tplPopovers; @@ -92,7 +133,7 @@ renderer.tplPopovers = function (lang, options) { var $alt = $('
    '); $alt.prependTo($imagePopover.find('.popover-content')); - $alt.append(''); + $alt.append(''); // padding button var $padding = $('
    '); @@ -209,24 +250,7 @@ eventHandler.modules.popover.button.update = function ($container, oStyle) { fn_boutton_update.call(this, $container, oStyle); - $container.find('.note-color').removeClass("hidden") - - // ACTIVATE TABS - $container.find('[data-tab-target]').each(function(){ - var $el = $(this); - var target = $el.data('tab-target'); - var $parent = $el.parent().closest('.tabs_container'); - var $parentLi = $el.parent().closest('li'); - - $parentLi.on('click', function(){return false}); - - $el.on('click', function(){ - $parent.find('.tab, .tabs_toggles li').removeClass('on'); - $parent.find(target).add($parentLi).addClass('on') - return false; - }) - }) - + $container.find('.note-color').removeClass("hidden"); if (oStyle.image) { $container.find('[data-event]').parent().removeClass("active"); @@ -728,17 +752,33 @@ eventHandler.attach = function (oLayoutInfo, options) { $(document).on('mousedown', summernote_mousedown); $(document).on('mouseup', summernote_mouseup); oLayoutInfo.editor().off('click').on('click', function (e) {e.preventDefault();}); // if the content editable is a link - oLayoutInfo.editor().on('dblclick', 'img, .media_iframe_video, span.fa, i.fa, span.fa, a.o_image', function (event) { + var imageSelector = 'img, .media_iframe_video, span.fa, i.fa, span.fa, a.o_image'; + var show_tooltip = true; + oLayoutInfo.editor().on('dblclick', imageSelector, function (event) { + show_tooltip = false; if (!$(event.target).closest(".note-toolbar").length) { // prevent icon edition of top bar for default summernote new widgets.MediaDialog(oLayoutInfo.editor(), event.target).appendTo(document.body); } }); + oLayoutInfo.editor().on('click', imageSelector, function (event) { + if (!$(event.target).closest(".note-toolbar").length) { // prevent icon edition of top bar for default summernote + setTimeout(function () { + if (show_tooltip) { + $(event.target).tooltip({title: _t('Double-click to edit'), trigger: 'manuel', container: 'body'}).tooltip('show'); + setTimeout(function () { + $(event.target).tooltip('destroy'); + }, 800); + } + }, 400); + show_tooltip = true; + } + }); if(oLayoutInfo.editor().is('[data-oe-model][data-oe-type="image"]')) { oLayoutInfo.editor().on('click', 'img', function (event) { $(event.target).trigger("dblclick"); }); } - oLayoutInfo.editable().on('mousedown', function(e) { + oLayoutInfo.editable().on('mousedown', function (e) { if(dom.isImg(e.target)) { range.createFromNode(e.target).select(); } @@ -898,7 +938,7 @@ $.summernote.lang.odoo = { recent: _t('Recent Color'), more: _t('More Color'), background: _t('Background Color'), - foreground: _t('Foreground Color'), + foreground: _t('Font Color'), transparent: _t('Transparent'), setTransparent: _t('Set transparent'), reset: _t('Reset'), diff --git a/addons/web_editor/static/src/js/snippets.animation.js b/addons/web_editor/static/src/js/snippets.animation.js index bb67d026e3c..f396bafcc30 100644 --- a/addons/web_editor/static/src/js/snippets.animation.js +++ b/addons/web_editor/static/src/js/snippets.animation.js @@ -43,7 +43,7 @@ var start = function (editable_mode, $target) { else continue; } - $(selector).each(function() { + $(selector).each(function () { var $snipped_id = $(this); if ( !$snipped_id.parents("#oe_snippets").length && !$snipped_id.parent("body").length && @@ -58,7 +58,7 @@ var start = function (editable_mode, $target) { }; var stop = function () { - $(ready).each(function() { + $(ready).each(function () { var $snipped_id = $(this); if ($snipped_id.data("snippet-view")) { $snipped_id.data("snippet-view").stop(); diff --git a/addons/web_editor/static/src/js/snippets.editor.js b/addons/web_editor/static/src/js/snippets.editor.js index 93e09c4e7f0..ef033dc07f3 100644 --- a/addons/web_editor/static/src/js/snippets.editor.js +++ b/addons/web_editor/static/src/js/snippets.editor.js @@ -18,7 +18,7 @@ var dummy = editor.dummy; ajax.loadXML('/web_editor/static/src/xml/snippets.xml', qweb); editor.Class.include({ - init: function() { + init: function () { var self = this; var res = this._super.apply(this, arguments); var $editable = this.rte.editable(); @@ -46,10 +46,10 @@ editor.Class.include({ /* ----- SNIPPET SELECTOR ---- */ $.extend($.expr[':'],{ - hasData: function(node,i,m){ + hasData: function (node,i,m) { return !!_.toArray(node.dataset).length; }, - data: function(node,i,m){ + data: function (node,i,m) { return $(node).data(m[3]); } }); @@ -75,28 +75,23 @@ $.fn.extend({ /* ----- BuildingBlock (managment of drag&drop and focus/blur snippets) ---- */ var BuildingBlock = Widget.extend({ - template: 'web_editor.snippets', + id: "oe_snippets", activeSnippets: [], init: function (parent, $editable) { this.parent = parent; this.$editable = $editable; this._super.apply(this, arguments); - if(!$('#oe_manipulators').length){ - $("
    ").appendTo('body'); + if(!$('#oe_manipulators').length) { + $("
    ", {id: "oe_manipulators", 'class': "o_css_editor"}).appendTo('body'); } this.$active_snipped_id = false; this.snippets = []; data.instance = this; }, - start: function() { + start: function () { var self = this; - this.$snippet = $("#oe_snippets"); - - this.$el - .on("mouseenter", function () { self.show(); }) - .on("mouseleave", function (event) { if (event.clientX>0 && event.clientY>0) self.hide(); }); $(window).resize(function () { setTimeout('$(document).click()',0); @@ -130,8 +125,7 @@ var BuildingBlock = Widget.extend({ self.$active_snipped_id.data("snippet-editor").$overlay.addClass('o_keypress'); } if ((event.metaKey || (event.ctrlKey && !event.altKey)) && event.shiftKey && event.keyCode >= 48 && event.keyCode <= 57) { - self.$snippet.find('.scroll:first > ul li:eq('+(event.keyCode-49)+') a').trigger("click"); - self.show(); + self.$('.scroll:first > ul li:eq('+(event.keyCode-49)+') a').trigger("click"); event.preventDefault(); } }); @@ -188,22 +182,19 @@ var BuildingBlock = Widget.extend({ var self = this; var url = this._get_snippet_url(); if (!url || !url.length) { - this.$snippet.detach(); + this.$el.detach(); return; } - this.$el.find("#o_left_bar").addClass("hidden"); return ajax.jsonRpc(url, 'call', {}).then(function (html) { self.compute_snippet_templates(html); self.trigger("snippets:ready"); }, function () { - self.$snippet.hide(); console.warn('Snippets template not found:', url); }); }, compute_snippet_templates: function (html) { var self = this; var $html = $(html); - var $left_bar = this.$el.find("#o_left_bar"); var $ul = $html.siblings("ul"); var $scroll = $html.siblings("#o_scroll"); @@ -309,16 +300,14 @@ var BuildingBlock = Widget.extend({ // hide scroll if no snippets defined if (!number) { - this.$snippet.detach(); - } else { - this.$el.find("#o_left_bar").removeClass("hidden"); + this.$el.detach(); } $("body").toggleClass("editor_has_snippets", !!number); // select all default text to edit (if snippet default text) self.$snippets.find('.oe_snippet_body, .oe_snippet_body *') .contents() - .filter(function() { + .filter(function () { return this.nodeType === 3 && this.textContent.match(/\S/); }).parent().addClass("o_default_snippet_text"); $(document).on("mouseup", ".o_default_snippet_text", function (event) { @@ -328,7 +317,6 @@ var BuildingBlock = Widget.extend({ var r = $.summernote.core.range.create(); $(r && r.sc).closest(".o_default_snippet_text").removeClass("o_default_snippet_text"); }); - // end // clean t-oe $html.find('[data-oe-model], [data-oe-type]').each(function () { @@ -339,14 +327,13 @@ var BuildingBlock = Widget.extend({ } } }); - // end $html.find('.o_not_editable').attr("contentEditable", false); - $left_bar.html($html); + this.$el.html($html); // animate for list of snippet blocks - $left_bar.on('click', '.scroll-link', function (event) { + this.$el.on('click', '.scroll-link', function (event) { event.preventDefault(); var targetOffset = $($(this).attr("href")).position().top - $ul.outerHeight() + $scroll[0].scrollTop; $scroll.animate({'scrollTop': targetOffset}, 750); @@ -367,55 +354,33 @@ var BuildingBlock = Widget.extend({ } $(last).addClass("active"); }); - // end - - // display scrollbar - $(window).on('resize', function () { - $scroll.css("overflow", ""); - var height = $left_bar.height() - $ul.outerHeight(); - $scroll.css("height", height); - var $last = $scroll.children(":visible").last().children(".o_panel_body"); - $last.css({'min-height': (height-$last.prev().outerHeight())+'px'}); - if ($scroll[0].scrollHeight + $ul[0].scrollHeight > document.body.clientHeight) { - $scroll.css("overflow", "auto").css("width", "100%"); - } else { - $scroll.css("width", ""); - } - }).trigger('resize'); - // end self.make_snippet_draggable(self.$snippets); }, - cover_target: function ($el, $target){ - if($el.data('not-cover_target')) return; + cover_target: function ($el, $target) { + if ($el.data('not-cover_target')) { + return; + } var pos = $target.offset(); var mt = parseInt($target.css("margin-top") || 0); var mb = parseInt($target.css("margin-bottom") || 0); - var width = $target.outerWidth(); - var bigger = pos.left+width > $("body").outerWidth()-8; $el.css({ - 'width': width, - 'top': pos.top - mt - 5, - 'left': pos.left-1 + width: $target.outerWidth(), + left: pos.left, + top: pos.top - mt, }); - $el.find(".oe_handle.e,.oe_handle.w").css({'height': $target.outerHeight() + mt + mb+1}); - if (bigger) { - $el.find(".oe_handle.e").css({right: 0, margin: 0}) - .find("div").css({right: 0, left: 'auto'}); - } else { - $el.find(".oe_handle.e").css({right: "", margin: ""}) - .find("div").css({right: "", left: ""}); - } - $el.find(".oe_handle.s").css({'top': $target.outerHeight() + mt + mb}); - $el.find(".oe_handle.size").css({'top': $target.outerHeight() + mt}); - $el.find(".oe_handle.s,.oe_handle.n").css({'width': width-2}); + $el.find('.oe_handles').css({ + height: $target.outerHeight(true), + }); + + $el.toggleClass('o_top_cover', pos.top <= (this.$('#o_scroll').position().top + 15)); }, show_blocks: function () { var self = this; var cache = {}; - this.$snippet.find(".o_panel").each(function () { + this.$(".o_panel").each(function () { var catcheck = false; var $category = $(this); $category.find(".oe_snippet_body").each(function () { @@ -450,15 +415,6 @@ var BuildingBlock = Widget.extend({ $('#oe_snippets .scroll a[data-toggle="tab"][href="#' + $category.attr("id") + '"]').toggle(catcheck); }); }, - show: function () { - var self = this; - this.make_active(false); - this.$el.addClass("o_open"); - this.show_blocks(); - }, - hide: function () { - this.$el.removeClass("o_open"); - }, bind_snippet_click_editor: function () { var self = this; var snipped_event_flag; @@ -541,7 +497,7 @@ var BuildingBlock = Widget.extend({ this.cover_target($snippet.data('overlay'), $snippet); this.snippet_focus($snippet); } - this.$snippet.trigger('snippet-activated', $snippet); + this.$el.trigger('snippet-activated', $snippet); if ($snippet) { $snippet.trigger('snippet-activated', $snippet); } @@ -554,7 +510,7 @@ var BuildingBlock = Widget.extend({ }, // activate drag and drop for the snippets in the snippet toolbar - make_snippet_draggable: function($snippets){ + make_snippet_draggable: function ($snippets) { var self = this; var $tumb = $snippets.find(".oe_snippet_thumbnail_img:first"); var left = $tumb.outerWidth()/2; @@ -572,8 +528,7 @@ var BuildingBlock = Widget.extend({ 'left': left, 'top': top }, - start: function(){ - self.hide(); + start: function () { dropped = false; // snippet_selectors => to get drop-near, drop-in $snippet = $(this); @@ -602,23 +557,25 @@ var BuildingBlock = Widget.extend({ return; } + self.make_active(false); self.activate_insertion_zones($selector_siblings, $selector_children); $('.oe_drop_zone').droppable({ - over: function(){ + over: function () { dropped = true; - $(this).first().after($toInsert); + $(this).first().after($toInsert).addClass('hidden'); }, - out: function(){ + out: function () { var prev = $toInsert.prev(); - if(this === prev[0]){ + if(this === prev[0]) { dropped = false; $toInsert.detach(); + $(this).removeClass('hidden'); } - } + }, }); }, - stop: function(ev, ui){ + stop: function (ev, ui) { $toInsert.removeClass('oe_snippet_body'); if (! dropped && self.$editable.find('.oe_drop_zone') && ui.position.top > 3) { @@ -658,7 +615,7 @@ var BuildingBlock = Widget.extend({ $target = $toInsert; setTimeout(function () { - self.$snippet.trigger('snippet-dropped', $target); + self.$el.trigger('snippet-dropped', $target); animation.start(true, $target); @@ -693,8 +650,8 @@ var BuildingBlock = Widget.extend({ }, // return the original snippet in the editor bar from a snippet id (string) - get_snippet_from_id: function(id){ - return $('.oe_snippet').filter(function(){ + get_snippet_from_id: function (id) { + return $('.oe_snippet').filter(function () { return $(this).data('option') === id; }).first(); }, @@ -704,12 +661,12 @@ var BuildingBlock = Widget.extend({ // in case the selected elements have children themselves, dropzones will be interleaved // with them. // selector.siblings -> will insert drop zones after and before selected elements - activate_insertion_zones: function($selector_siblings, $selector_children){ + activate_insertion_zones: function ($selector_siblings, $selector_children) { var self = this; - var zone_template = $("
    "); + var zone_template = $("
    "); if ($selector_children) { - $selector_children.each(function (){ + $selector_children.each(function () { var $zone = $(this); var css = window.getComputedStyle(this); var float = css.float || css.cssFloat; @@ -719,7 +676,7 @@ var BuildingBlock = Widget.extend({ var node = $drop[0].previousSibling; var test = !!(node && ((!node.tagName && node.textContent.match(/\S/)) || node.tagName === "BR")); if (test) { - $drop.addClass("oe_vertical oe_vertical_text").css({ + $drop.addClass("oe_vertical").css({ 'height': parseInt(window.getComputedStyle($zone[0]).lineHeight), 'float': 'none', 'display': 'inline-block' @@ -734,7 +691,7 @@ var BuildingBlock = Widget.extend({ var node = $drop[0].nextSibling; var test = !!(node && ((!node.tagName && node.textContent.match(/\S/)) || node.tagName === "BR")); if (test) { - $drop.addClass("oe_vertical oe_vertical_text").css({ + $drop.addClass("oe_vertical").css({ 'height': parseInt(window.getComputedStyle($zone[0]).lineHeight), 'float': 'none', 'display': 'inline-block' @@ -752,20 +709,20 @@ var BuildingBlock = Widget.extend({ } if ($selector_siblings) { - $selector_siblings.filter(':not(.oe_drop_zone):not(.oe_drop_clone)').each(function (){ + $selector_siblings.filter(':not(.oe_drop_zone):not(.oe_drop_clone)').each(function () { var $zone = $(this); var $drop; var css = window.getComputedStyle(this); var float = css.float || css.cssFloat; - if($zone.prev('.oe_drop_zone:visible').length === 0){ + if($zone.prev('.oe_drop_zone:visible').length === 0) { $drop = zone_template.clone(); if (float === "left" || float === "right") { $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.prev().outerHeight() || Infinity), 30)); } $zone.before($drop); } - if($zone.next('.oe_drop_zone:visible').length === 0){ + if($zone.next('.oe_drop_zone:visible').length === 0) { $drop = zone_template.clone(); if (float === "left" || float === "right") { $drop.addClass("oe_vertical").css('height', Math.max(Math.min($zone.outerHeight(), $zone.next().outerHeight() || Infinity), 30)); @@ -785,7 +742,7 @@ var BuildingBlock = Widget.extend({ // Cleaning consecutive zone and up zones placed between floating or inline elements. We do not like these kind of zones. var $zones = self.$editable.find('.oe_drop_zone:not(.oe_vertical)'); - $zones.each(function (){ + $zones.each(function () { var zone = $(this); var prev = zone.prev(); var next = zone.next(); @@ -799,12 +756,12 @@ var BuildingBlock = Widget.extend({ var disp_prev = prev.css('display') || null; var disp_next = next.css('display') || null; if( (float_prev === 'left' || float_prev === 'right') - && (float_next === 'left' || float_next === 'right') ){ + && (float_next === 'left' || float_next === 'right') ) { zone.remove(); }else if( !( disp_prev === null || disp_next === null || disp_prev === 'block' - || disp_next === 'block' )){ + || disp_next === 'block' )) { zone.remove(); } }); @@ -812,21 +769,21 @@ var BuildingBlock = Widget.extend({ // generate drop zones covering the elements selected by the selector // we generate overlay drop zones only to get an idea of where the snippet are, the drop - activate_overlay_zones: function($targets){ + activate_overlay_zones: function ($targets) { var self = this; - function is_visible($el){ + function is_visible($el) { return $el.css('display') != 'none' && $el.css('opacity') != '0' && $el.css('visibility') != 'hidden'; } // filter out invisible elements - $targets = $targets.filter(function(){ return is_visible($(this)); }); + $targets = $targets.filter(function () { return is_visible($(this)); }); // filter out elements with invisible parents - $targets = $targets.filter(function(){ - var parents = $(this).parents().filter(function(){ return !is_visible($(this)); }); + $targets = $targets.filter(function () { + var parents = $(this).parents().filter(function () { return !is_visible($(this)); }); return parents.length === 0; }); @@ -839,7 +796,7 @@ var BuildingBlock = Widget.extend({ if (document.body && document.body.addEventListener) { $zone.on("click mousedown mousedown", function passThrough(event) { event.preventDefault(); - $target.each(function() { + $target.each(function () { // check if clicked point (taken from event) is inside element event.srcElement = this; $(this).trigger(event.type); @@ -849,8 +806,8 @@ var BuildingBlock = Widget.extend({ } $zone.appendTo('#oe_manipulators'); - $zone.data('target',$target); - $target.data('overlay',$zone); + $zone.data('target', $target); + $target.data('overlay', $zone); var timer; $target.closest('.o_editable').on("content_changed", function (event) { @@ -890,7 +847,7 @@ var Editor = Class.extend({ }, // activate drag and drop for the snippets in the snippet toolbar - _drag_and_drop: function(){ + _drag_and_drop: function () { var self = this; this.dropped = false; this.$overlay.draggable({ @@ -902,7 +859,7 @@ var Editor = Class.extend({ left: 18, top: 14 }, - helper: function() { + helper: function () { var $clone = $(this).clone().css({width: "24px", height: "24px", border: 0}); $clone.find(".oe_overlay_options >:not(:contains(.oe_snippet_move)), .oe_handle").remove(); $clone.find(":not(.glyphicon)").css({position: 'absolute', top: 0, left: 0}); @@ -913,25 +870,24 @@ var Editor = Class.extend({ stop: _.bind(self._drag_and_drop_stop, self) }); }, - _drag_and_drop_after_insert_dropzone: function (){}, - _drag_and_drop_active_drop_zone: function ($zones){ + _drag_and_drop_after_insert_dropzone: function () {}, + _drag_and_drop_active_drop_zone: function ($zones) { var self = this; $zones.droppable({ - over: function(){ + over: function () { $(".oe_drop_zone.hide").removeClass("hide"); $(this).addClass("hide").first().after(self.$target); self.dropped = true; }, - out: function(){ + out: function () { $(this).removeClass("hide"); self.$target.detach(); self.dropped = false; }, }); }, - _drag_and_drop_start: function (){ + _drag_and_drop_start: function () { var self = this; - self.buildingBlock.hide(); self.buildingBlock.editor_busy = true; self.size = { width: self.$target.width(), @@ -952,6 +908,7 @@ var Editor = Class.extend({ else $selector_children = $selector_children.add(self.selector_children[i].all()); } + self.buildingBlock.make_active(false); self.buildingBlock.activate_insertion_zones($selector_siblings, $selector_children); $("body").addClass('move-important'); @@ -959,7 +916,7 @@ var Editor = Class.extend({ self._drag_and_drop_after_insert_dropzone(); self._drag_and_drop_active_drop_zone($('.oe_drop_zone')); }, - _drag_and_drop_stop: function (){ + _drag_and_drop_stop: function () { var self = this; var $dropzone = this.$target.prev(); var prev = $dropzone.length && $dropzone[0].previousSibling; @@ -984,7 +941,7 @@ var Editor = Class.extend({ $parent.prepend(this.$target); } - for (var i in this.styles){ + for (var i in this.styles) { this.styles[i].on_move(); } } @@ -992,9 +949,9 @@ var Editor = Class.extend({ self.buildingBlock.editor_busy = false; self.get_parent_block(); - setTimeout(function () { + _.defer(function () { self.buildingBlock.cover_target(self.$target.data('overlay'), self.$target); - },0); + }); }, load_style_options: function () { @@ -1071,7 +1028,7 @@ var Editor = Class.extend({ this.$target.after($clone); this.buildingBlock.call_for_all_snippets($clone, function (editor, $snippet) { - for (var i in editor.styles){ + for (var i in editor.styles) { editor.styles[i].on_clone($snippet); } }); @@ -1086,7 +1043,7 @@ var Editor = Class.extend({ var index = _.indexOf(this.buildingBlock.snippets, this.$target.get(0)); this.buildingBlock.call_for_all_snippets(this.$target, function (editor, $snippet) { - for (var i in editor.styles){ + for (var i in editor.styles) { editor.styles[i].on_remove(); } }); @@ -1120,7 +1077,7 @@ var Editor = Class.extend({ * (after the insertion of this.$body, if this.$body exists) */ drop_and_build_snippet: function () { - for (var i in this.styles){ + for (var i in this.styles) { this.styles[i].drop_and_build_snippet(); } }, @@ -1130,7 +1087,7 @@ var Editor = Class.extend({ */ on_focus : function () { this.$overlay.addClass('oe_active'); - for (var i in this.styles){ + for (var i in this.styles) { this.styles[i].on_focus(); } }, @@ -1139,7 +1096,7 @@ var Editor = Class.extend({ * This method is called when the user click outside the snippet in the dom, after a focus */ on_blur : function () { - for (var i in this.styles){ + for (var i in this.styles) { this.styles[i].on_blur(); } this.$overlay.removeClass('oe_active'); diff --git a/addons/web_editor/static/src/js/snippets.options.js b/addons/web_editor/static/src/js/snippets.options.js index cbf8fea5c4e..811d0e9c175 100644 --- a/addons/web_editor/static/src/js/snippets.options.js +++ b/addons/web_editor/static/src/js/snippets.options.js @@ -1,766 +1,747 @@ odoo.define('web_editor.snippets.options', function (require) { -'use strict'; + 'use strict'; -var Class = require('web.Class'); -var ajax = require('web.ajax'); -var core = require('web.core'); -var base = require('web_editor.base'); -var editor = require('web_editor.editor'); -var widget = require('web_editor.widget'); -var animation = require('web_editor.snippets.animation'); + var Class = require('web.Class'); + var ajax = require('web.ajax'); + var core = require('web.core'); + var Dialog = require('web.Dialog'); + var base = require('web_editor.base'); + var editor = require('web_editor.editor'); + var widget = require('web_editor.widget'); + var animation = require('web_editor.snippets.animation'); -var qweb = core.qweb; + var qweb = core.qweb; + var _t = core._t; -/* ----- Editor option (object link the the xml with data-js) ---- */ + /* ----- Editor option (object link the the xml with data-js) ---- */ -var Option = Class.extend({ - // initialisation (don't overwrite) - init: function (BuildingBlock, editor, $target, option_id) { - this.buildingBlock = BuildingBlock; - this.editor = editor; - this.$target = $target; - var option = this.buildingBlock.templateOptions[option_id]; - var styles = this.$target.data("snippet-option-ids") || {}; - styles[option_id] = this; - this.$target.data("snippet-option-ids", styles); - this.$overlay = this.$target.data('overlay') || $('
    '); - this.option= option_id; - this.$el = option.$el.find(">li").clone(); - this.data = option.$el.data(); - }, + var SnippetOption = Class.extend({ + // initialisation (don't overwrite) + init: function (BuildingBlock, editor, $target, option_id) { + this.buildingBlock = BuildingBlock; + this.editor = editor; + this.$target = $target; + var option = this.buildingBlock.templateOptions[option_id]; + var styles = this.$target.data("snippet-option-ids") || {}; + styles[option_id] = this; + this.$target.data("snippet-option-ids", styles); + this.$overlay = this.$target.data('overlay') || $('
    '); + this.option= option_id; + this.$el = option.$el.find(">li").clone(); + this.data = option.$el.data(); + }, - // helper for this.$target.find - $: function (selector) { - return this.$target(selector); - }, + // helper for this.$target.find + $: function (selector) { + return this.$target(selector); + }, - _bind_li_menu: function () { - this.$el.filter("li:hasData").find('a:first') - .off('mouseenter click') - .on('mouseenter click', _.bind(this._mouse, this)); + _bind_li_menu: function () { + this.$el.filter("li:hasData").find('a:first') + .off('mouseenter click') + .on('mouseenter click', _.bind(this._mouse, this)); - this.$el - .off('mouseenter click', "li:hasData a") - .on('mouseenter click', "li:hasData a", _.bind(this._mouse, this)); + this.$el + .off('mouseenter click', "li:hasData a") + .on('mouseenter click', "li:hasData a", _.bind(this._mouse, this)); - this.$el.closest("ul").add(this.$el) - .off('mouseleave') - .on('mouseleave', _.bind(this.reset, this)); + this.$el.closest("ul").add(this.$el) + .off('mouseleave') + .on('mouseleave', _.bind(this.reset, this)); - this.$el - .off('mouseleave', "ul") - .on('mouseleave', "ul", _.bind(this.reset, this)); + this.$el + .off('mouseleave', "ul") + .on('mouseleave', "ul", _.bind(this.reset, this)); - this.reset_methods = []; - }, + this.reset_methods = []; + }, - /** - * this method handles mouse:over and mouse:leave on the snippet editor menu - */ - _time_mouseleave: null, - _mouse: function (event) { - var $next = $(event.currentTarget).parent(); + /** + * this method handles mouse:over and mouse:leave on the snippet editor menu + */ + _time_mouseleave: null, + _mouse: function (event) { + var $next = $(event.currentTarget).parent(); - // triggers preview or apply methods if a menu item has been clicked - this.select(event.type === "click" ? "click" : "over", $next); - if (event.type === 'click') { + // triggers preview or apply methods if a menu item has been clicked + this.select(event.type === "click" ? "click" : "over", $next); + if (event.type === 'click') { + this.set_active(); + this.$target.trigger("snippet-option-change", [this]); + } else { + this.$target.trigger("snippet-option-preview", [this]); + } + }, + /* + * select and set item active or not (add highlight item and his parents) + * called before start + */ + set_active: function () { + var classes = _.uniq((this.$target.attr("class") || '').split(/\s+/)); + this.$el.find('[data-toggle_class], [data-select_class]') + .add(this.$el) + .filter('[data-toggle_class], [data-select_class]') + .removeClass("active") + .filter('[data-toggle_class="' + classes.join('"], [data-toggle_class="') + '"] ,'+ + '[data-select_class="' + classes.join('"], [data-select_class="') + '"]') + .addClass("active"); + }, + + start: function () { this.set_active(); - this.$target.trigger("snippet-option-change", [this]); - } else { - this.$target.trigger("snippet-option-preview", [this]); - } - }, - /* - * select and set item active or not (add highlight item and his parents) - * called before start - */ - set_active: function () { - var classes = _.uniq((this.$target.attr("class") || '').split(/\s+/)); - this.$el.find('[data-toggle_class], [data-select_class]') - .add(this.$el) - .filter('[data-toggle_class], [data-select_class]') - .removeClass("active") - .filter('[data-toggle_class="' + classes.join('"], [data-toggle_class="') + '"] ,'+ - '[data-select_class="' + classes.join('"], [data-select_class="') + '"]') - .addClass("active"); - }, + this.$target.on('snippet-option-reset', _.bind(this.set_active, this)); + this._bind_li_menu(); + }, - start: function () { - this.set_active(); - this.$target.on('snippet-option-reset', _.bind(this.set_active, this)); - this._bind_li_menu(); - }, + on_focus : function () { + this._bind_li_menu(); + }, - on_focus : function () { - this._bind_li_menu(); - }, + on_blur : function () { + }, - on_blur : function () { - }, + on_clone: function ($clone) { + }, - on_clone: function ($clone) { - }, + on_move: function () { + }, - on_move: function () { - }, + on_remove: function () { + }, - on_remove: function () { - }, + drop_and_build_snippet: function () { + }, - drop_and_build_snippet: function () { - }, + reset: function () { + var self = this; + var lis = self.$el.add(self.$el.find('li')).filter('.active').get(); + lis.reverse(); + _.each(lis, function (li) { + var $li = $(li); + for (var k in self.reset_methods) { + var method = self.reset_methods[k]; + if ($li.is('[data-'+method+']') || $li.closest('[data-'+method+']').size()) { + delete self.reset_methods[k]; + } + } + self.select("reset", $li); + }); - reset: function () { - var self = this; - var lis = self.$el.add(self.$el.find('li')).filter('.active').get(); - lis.reverse(); - _.each(lis, function (li) { - var $li = $(li); for (var k in self.reset_methods) { var method = self.reset_methods[k]; - if ($li.is('[data-'+method+']') || $li.closest('[data-'+method+']').size()) { - delete self.reset_methods[k]; + if (method) { + self[method]("reset", null); } } - self.select("reset", $li); - }); + self.reset_methods = []; + self.$target.trigger("snippet-option-reset", [this]); + }, - for (var k in self.reset_methods) { - var method = self.reset_methods[k]; - if (method) { - self[method]("reset", null); + // call data-method args as method (data-only can be used) + select: function (type, $li) { + var self = this, + $methods = [], + el = $li[0]; + + if ($li.data('only') && type !== $li.data('only')) { + return; } - } - self.reset_methods = []; - self.$target.trigger("snippet-option-reset", [this]); - }, - // call data-method args as method (data-only can be used) - select: function (type, $li) { - var self = this, - $methods = [], - el = $li[0]; - - if ($li.data('only') && type !== $li.data('only')) { - return; - } - - if (type==="click") { - this.reset(); - this.buildingBlock.parent.rte.historyRecordUndo(this.$target); - } - - function filter (k) { return k !== 'oeId' && k !== 'oeModel' && k !== 'oeField' && k !== 'oeXpath' && k !== 'oeSourceId' && k !== 'only';} - function hasData(el) { - for (var k in el.dataset) { - if (filter (k)) { - return true; - } + if (type==="click") { + this.reset(); + this.buildingBlock.parent.rte.historyRecordUndo(this.$target); } - return false; - } - function method(el) { - var data = {}; - for (var k in el.dataset) { - if (filter (k)) { - data[k] = el.dataset[k]; - } - } - return data; - } - while (el && this.$el.is(el) || _.some(this.$el.map(function () {return $.contains(this, el);}).get()) ) { - if (hasData(el)) { - $methods.push(el); - } - el = el.parentNode; - } - - $methods.reverse(); - - _.each($methods, function (el) { - var $el = $(el); - var methods = method(el); - - for (var k in methods) { - if (self[k]) { - if (type !== "reset" && self.reset_methods.indexOf(k) === -1) { - self.reset_methods.push(k); + function filter (k) { return k !== 'oeId' && k !== 'oeModel' && k !== 'oeField' && k !== 'oeXpath' && k !== 'oeSourceId' && k !== 'only';} + function hasData(el) { + for (var k in el.dataset) { + if (filter (k)) { + return true; } - self[k](type, methods[k], $el); - } else { - console.error("'"+self.option+"' snippet have not method '"+k+"'"); } - } - }); - }, - - // default method for snippet - toggle_class: function (type, value, $li) { - var $lis = this.$el.find('[data-toggle_class]').add(this.$el).filter('[data-toggle_class]'); - - function map ($lis) { - return $lis.map(function () {return $(this).data("toggle_class");}).get().join(" "); - } - var classes = map($lis); - var active_classes = map($lis.filter('.active, :has(.active)')); - - this.$target.removeClass(classes); - this.$target.addClass(active_classes); - - if (type !== 'reset') { - this.$target.toggleClass(value); - } - }, - select_class: function (type, value, $li) { - var $lis = this.$el.find('[data-select_class]').add(this.$el).filter('[data-select_class]'); - - var classes = $lis.map(function () {return $(this).data('select_class');}).get(); - - this.$target.removeClass(classes.join(" ")); - if(value) this.$target.addClass(value); - }, - eval: function (type, value, $li) { - var fn = new Function("node", "type", "value", "$li", value); - fn.call(this, this, type, value, $li); - }, - - clean_for_save: editor.dummy -}); - -/* ----- default options ---- */ - -// to remove after 9.0 (keep for compatibility without update with -u) -var media = Option.extend({ - start: function () { - var self =this; - this._super(); - animation.start(true, this.$target); - this.$target.closest(".o_editable").on("content_changed", function () { - if (!self.$target.parent().length) { - self.$target.remove(); - self.$overlay.remove(); - } - }); - }, - edition: function (type, value) { - if(type !== "click") return; - var self = this; - var _editor = new widget.MediaDialog(this.$target.closest('.o_editable'), this.$target[0]); - _editor.appendTo(document.body); - _editor.on('saved', this, function (item, old) { - self.editor.on_blur(); - self.buildingBlock.make_active(false); - if (self.$target.parent().data("oe-field") !== "image") { - setTimeout(function () { - self.buildingBlock.make_active($(item)); - },0); - } - $(item).trigger("content_changed"); - }); - }, - on_focus : function () { - var self = this; - var $parent = this.$target.parent(); - - if ($parent.data("oe-field") === "image" && $parent.hasClass('o_editable')) { - this.$overlay.addClass("hidden"); - self.edition('click', null); - self.buildingBlock.make_active(false); - } - } -}); - -var colorpicker = Option.extend({ - start: function () { - var self = this; - var res = this._super(); - - var paletteTemplate = '
    '+ - '
    Background Color
    ' + - '
    ' + - '
    '+ - '
    ' + - '
    ' + - - '
    ' + - '' + - '
    ' + - '
    '+ - '
    '+ - '
    '+ - '
    '+ - '
    ' + - '
    ' + - '
    ' + - '
    ' + - - '
    '; - var $pt = $(paletteTemplate); - - if (!this.$el.find('.colorpicker').length) { - var $clpicker = $(qweb.render('web_editor.colorpicker')) - var pTheme = $clpicker.find('.theme') - var pGray = $clpicker.find('.grayscale') - var pUi = $clpicker.find('.ui') - $pt.find('.palette_theme').append(pTheme).end() - .find('.palette_grayscale').append(pGray).end() - .find('.palette_ui').append(pUi) - this.$el.find('li').append($pt); - - // ACTIVATE TABS - this.$el.find('[data-tab-target]').each(function(){ - var $el = $(this); - var target = $el.data('tab-target'); - var $parent = $el.parent().closest('.tabs_container'); - var $parentLi = $el.parent().closest('li'); - - $parentLi.on('click', function(){return false}); - - $el.on('click', function(){ - $parent.find('.tab, .tabs_toggles li').removeClass('on'); - $parent.find(target).add($parentLi).addClass('on') return false; - }) - }) - } - if (this.$el.data('area')) { - this.$target = this.$target.find(this.$el.data('area')); - this.$el.removeData('area').removeAttr('area'); - } - - var classes = []; - this.$el.find(".colorpicker button").map(function () { - var $color = $(this); - var color = $color.attr("class"); - if (self.$target.hasClass(color)) { - self.color = color; - $color.parent().addClass("selected"); } - classes.push(color); - }); - this.classes = classes.join(" "); + function method(el) { + var data = {}; + for (var k in el.dataset) { + if (filter (k)) { + data[k] = el.dataset[k]; + } + } + return data; + } - this.bind_events(); - return res; - }, - bind_events: function () { - var self = this; - var $td = this.$el.find(".tab"); - var $colors = this.$el.find(".colorpicker button"); - $colors - .mouseenter(function () { - self.$target.removeClass(self.classes).addClass($(this).attr("class")); - }) - .mouseleave(function () { - self.$target.removeClass(self.classes) - .addClass($colors.filter(".selected").attr("class")); - }) - .click(function () { - $colors.removeClass("selected"); - $(this).addClass("selected"); - self.$target.closest(".o_editable").trigger("content_changed"); + while (el && this.$el.is(el) || _.some(this.$el.map(function () {return $.contains(this, el);}).get()) ) { + if (hasData(el)) { + $methods.push(el); + } + el = el.parentNode; + } + + $methods.reverse(); + + _.each($methods, function (el) { + var $el = $(el); + var methods = method(el); + + for (var k in methods) { + if (self[k]) { + if (type !== "reset" && self.reset_methods.indexOf(k) === -1) { + self.reset_methods.push(k); + } + self[k](type, methods[k], $el); + } else { + console.error("'"+self.option+"' snippet have not method '"+k+"'"); + } + } }); - } -}); + }, -var background_position = Option.extend({ - start: function(){ - var self = this; - var $btn = self.$overlay.find('.oe_options'); - $btn.on('show.bs.dropdown', function(){ - if (self.$target.css('background-image') == 'none'){ - $btn.find('.background_position_li').addClass('hidden') - } else { - $btn.find('.background_position_li').removeClass('hidden') + // default method for snippet + toggle_class: function (type, value, $li) { + var $lis = this.$el.find('[data-toggle_class]').add(this.$el).filter('[data-toggle_class]'); + + function map ($lis) { + return $lis.map(function () {return $(this).data("toggle_class");}).get().join(" "); } - }) - }, - background_position: function(type, value, $li){ - if (type != 'click') return; - var self = this; - var $modal = $(qweb.render('web_editor.dialog.background_position')); - $modal.modal() - $modal.on('hidden.bs.modal', function(){ - setTimeout(function(){ - $modal.remove() - },2000) - }) - self.editing(self, $modal) - }, - editing: function(self, $modal){ - // Init main collapse - $modal.find(".panel-collapse.collapse").collapse({ - toggle: false, - parent: $('#bg_image_opt_accordition') - }) + var classes = map($lis); + var active_classes = map($lis.filter('.active, :has(.active)')); - // Fetch data form $target - if(self.$target.hasClass('o_bg_img_contain')){ - $modal.find("#bg_size").val('o_bg_img_contain').end() - .find("#radio2").prop('checked', true).end() - .find("#collapse_contain").collapse('show'); + this.$target.removeClass(classes); + this.$target.addClass(active_classes); - } else if (self.$target.hasClass('o_bg_img_cover')){ - $modal.find("#bg_size").val('o_bg_img_cover').end() - .find("#radio1").prop('checked', true).end() - .find("#collapse_cover").collapse('show'); - } else if (self.$target.hasClass('o_bg_img_custom')){ - $modal.find("#bg_size").val('o_bg_img_custom').end() - .find("#radio3").prop('checked', true).end() - .find("#collapse_custom").collapse('show'); - } - if(self.$target.hasClass("o_bg_img_repeat")){ - $modal.find("#contain_repeat").prop('checked', true); - $modal.find("#custom_repeat").val('o_bg_img_repeat') - } else if (self.$target.hasClass("o_bg_img_no_repeat")){ - $modal.find("#custom_repeat").val('o_bg_img_no_repeat') - } else if (self.$target.hasClass("o_bg_img_repeat_x")){ - $modal.find("#custom_repeat").val('o_bg_img_repeat_x') - } else if (self.$target.hasClass("o_bg_img_repeat_y")){ - $modal.find("#custom_repeat").val('o_bg_img_repeat_y') + if (type !== 'reset') { + this.$target.toggleClass(value); + } + }, + select_class: function (type, value, $li) { + var $lis = this.$el.find('[data-select_class]').add(this.$el).filter('[data-select_class]'); + + var classes = $lis.map(function () {return $(this).data('select_class');}).get(); + + this.$target.removeClass(classes.join(" ")); + if(value) this.$target.addClass(value); + }, + eval: function (type, value, $li) { + var fn = new Function("node", "type", "value", "$li", value); + fn.call(this, this, type, value, $li); + }, + + clean_for_save: editor.dummy + }); + var registry = {}; + + /* ----- default options ---- */ + + // to remove after 9.0 (keep for compatibility without update with -u) + registry.media = SnippetOption.extend({ + start: function () { + var self =this; + this._super(); + animation.start(true, this.$target); + this.$target.closest(".o_editable").on("content_changed", function () { + if (!self.$target.parent().length) { + self.$target.remove(); + self.$overlay.remove(); + } + }); + }, + edition: function (type, value) { + if(type !== "click") return; + var self = this; + var _editor = new widget.MediaDialog(this.$target.closest('.o_editable'), this.$target[0]); + _editor.appendTo(document.body); + _editor.on('saved', this, function (item, old) { + self.editor.on_blur(); + self.buildingBlock.make_active(false); + if (self.$target.parent().data("oe-field") !== "image") { + setTimeout(function () { + self.buildingBlock.make_active($(item)); + },0); + } + $(item).trigger("content_changed"); + }); + }, + on_focus : function () { + var self = this; + var $parent = this.$target.parent(); + + if ($parent.data("oe-field") === "image" && $parent.hasClass('o_editable')) { + this.$overlay.addClass("hidden"); + self.edition('click', null); + self.buildingBlock.make_active(false); + } } + }); - var bg_pos = self.$target.css('background-position').split(' '), - bg_siz = self.$target.css('background-size').split(' '); + registry.colorpicker = SnippetOption.extend({ + start: function () { + var self = this; + var res = this._super(); - if( bg_pos.length > 1 ) { - $modal.find("#custom_pos_x").val(bg_pos[0].replace('%','')) - $modal.find("#custom_pos_y").val(bg_pos[1].replace('%','')) - } - if( bg_siz.length > 1 ) { - $modal.find("#custom_size_x").val(bg_siz[0].replace('%','')) - $modal.find("#custom_size_y").val(bg_siz[1].replace('%','')) - } + if (!this.$el.find('.colorpicker').length) { + var $pt = $(qweb.render('web_editor.snippet.option.colorpicker')); + var $clpicker = $(qweb.render('web_editor.colorpicker')); - // Collapse events - $('#collapse_cover').on('show.bs.collapse', function(){ - $('#radio1').prop('checked', true); - $modal.find("#bg_size").val('o_bg_img_cover'); - }); + var $toggles = $pt.find('.o_colorpicker_section_menu'); + var $tabs = $pt.find('.o_colorpicker_section_tabs'); - $('#collapse_contain').on('show.bs.collapse', function(){ - $('#radio2').prop('checked', true); - $modal.find("#bg_size").val('o_bg_img_contain'); - }); + var $sections = $clpicker.find('.o_colorpicker_section'); + if ($sections.length) { + $sections.each(function () { + var $section = $(this); + var id = 'o_palette_' + $section.data('name') + _.uniqueId(); - $('#collapse_custom').on('show.bs.collapse', function(){ - $('#radio3').prop('checked', true); - $modal.find("#bg_size").val('o_bg_img_custom'); - }); + var $li = $('
  • ') + .append($('', {href: '#' + id}) + .append($('', {'class': $section.data('iconClass') || '', html: $section.data('iconContent') || ''}))); + $toggles.append($li); - // Focus Point - var img_url = self.$target.css('background-image').replace(/url\(['"]*|['"]*\)|^none$/g, ""); + $tabs.append($section.addClass('tab-pane').attr('id', id)); + }); + $toggles.find('li').first().addClass('active'); + $tabs.find('div').first().addClass('active'); - var $object = $modal.find(".object"), - $focus = $modal.find(".focus_point"); - - $modal.find("#pos_x").val(bg_pos[0]).end() - .find("#pos_y").val(bg_pos[1]).end() - .find(".ui_info .x").text(bg_pos[0]).end() - .find(".ui_info .y").text(bg_pos[1]) - $focus.css({ - left: bg_pos[0], - top: bg_pos[1] - }) - setTimeout(function(){$focus.addClass("ready") },50) - - var $img = $('') - $img.prependTo($object) - $img.on('load',function(){ - self.retrive_click($img, $focus, $modal, self); - }) - - // Save - $modal.find('.save').on('click',function(){self.save($modal, self) }) - }, - retrive_click: function($img, $focus, $modal, self){ - // Prevent clicks on ui items - $focus.on('click', function(){$img.mousedown(); }) - $modal.find('.ui_info').on('click', function(){$img.mousedown(); }) - $modal.find('.grid').on('click', function(){$img.mousedown(); }) - - // Retrive click position - $img.click(function (e) { - var posX = e.pageX - $(this).offset().left, - posY = e.pageY - $(this).offset().top; - var res = { - x: (self.range_value(posX/$img.width()*100).toFixed(2)) + '%', - y: (self.range_value(posY/$img.height()*100).toFixed(2)) + '%' - }; - $focus.css({top: res.y, left: res.x }) - - $modal.find("#pos_x").val(res.x).end() - .find("#pos_y").val(res.y).end() - .find(".ui_info .x").text(res.x).end() - .find(".ui_info .y").text(res.y).end() - .find(".ui_info").hide() - - setTimeout(function(){$modal.find(".ui_info").show() },50) - }); - }, - range_value: function(value){ - if (value < 0) value = 0; - if (value > 100) value = 100; - return (value) - }, - save: function($modal, self){ - self.clean(self.$target); - self.$target.addClass($modal.find("#bg_size").val()); - switch ($modal.find("#bg_size").val()) { - case "o_bg_img_cover": - self.$target.css('background-position', $modal.find("#pos_x").val() + ' ' +$modal.find("#pos_y").val()) - break; - case "o_bg_img_contain": - if ($modal.find("#contain_repeat").is(":checked")){ - self.$target.addClass('o_bg_img_repeat') + $toggles.on('click mouseover', '> li > a', function (e) { + e.preventDefault(); + e.stopPropagation(); + $(this).tab('show'); + }); } else { - self.$target.addClass('o_bg_img_no_repeat') + $toggles.parent().empty().append($clpicker); } - break; - case "o_bg_img_custom": - self.$target.addClass($modal.find("#custom_repeat").val()); - self.$target.css('background-size', $modal.find("#custom_size_x").val() + '% ' + $modal.find("#custom_size_y").val() + '%'); - self.$target.css('background-position', $modal.find("#custom_pos_x").val() + '% ' + $modal.find("#custom_pos_y").val() + '%'); - break; - } - }, - clean: function($target){ - $target.removeClass('o_bg_img_cover o_bg_img_contain o_bg_img_custom o_bg_img_repeat o_bg_img_repeat_x o_bg_img_repeat_y o_bg_img_no_repeat') - .css('background-size', '') - .css('background-position', ''); - }, -}); -var background = Option.extend({ - start: function ($change_target) { - this.$target = $change_target || this.$target; - this._super(); - var src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, ""); - if (this.$target.hasClass('oe_custom_bg')) { - this.$el.find('li[data-choose_image]').data("background", src).attr("data-background", src); - } - }, - background: function(type, value, $li) { - if (value && value.length) { - this.$target.attr("style", 'background-image: url(' + value + '); ' + (this.$target.attr("style") || '').replace(/background-image:[^;]+/, '') ); - this.$target.addClass("oe_img_bg"); - } else { - this.$target.css("background-image", ""); - this.$target.removeClass("oe_img_bg").removeClass("oe_custom_bg"); - } - }, - select_class : function(type, value, $li) { - this.background(type, '', $li); - this._super(type, value, $li); - }, - choose_image: function(type, value, $li) { - if(type !== "click") return; + this.$el.find('li').append($pt); + } + if (this.$el.data('area')) { + this.$target = this.$target.find(this.$el.data('area')); + this.$el.removeData('area').removeAttr('area'); + } - var self = this; - var $image = $(''); - $image.attr("src", value); - $image.appendTo(self.$target); + var classes = []; + this.$el.find(".colorpicker button").map(function () { + var $color = $(this); + var className = 'bg-' + $color.data('color'); - var _editor = new widget.MediaDialog(null, $image[0]); - _editor.appendTo(document.body); - _editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden'); + $color.addClass(className); - _editor.on('saved', self, function () { - var value = $image.attr("src"); - $image.remove(); - self.$el.find('li[data-choose_image]').data("background", value).attr("data-background", value); - self.background(type, value,$li); - self.$target.addClass('oe_custom_bg'); - self.$target.trigger("snippet-option-change", [self]); - self.set_active(); - }); - _editor.on('cancel', self, function () { - $image.remove(); - }); - }, - set_active: function () { - var self = this; - var src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, ""); - this._super(); - - if (this.$target.hasClass('oe_custom_bg')) { - this.$el.find('li[data-choose_image]').data("background", src).attr("data-background", src); - } - - this.$el.find('li[data-background]:not([data-background=""])') - .removeClass("active") - .each(function () { - var background = $(this).data("background") || $(this).attr("data-background"); - if ((src.length && background.length && src.indexOf(background) !== -1) || (!src.length && !background.length)) { - $(this).addClass("active"); + if (self.$target.hasClass(className)) { + self.color = className; + $color.addClass("selected"); } + classes.push(className); }); + this.classes = classes.join(" "); - if (!this.$el.find('li[data-background].active').size()) { - this.$el.find('li[data-background=""]:not([data-choose_image])').addClass("active"); - } else { - this.$el.find('li[data-background=""]:not([data-choose_image])').removeClass("active"); - } - } -}); - -/* t-field options */ - -var many2one = Option.extend({ - start: function () { - var self = this; - - this.Model = this.$target.data('oe-many2one-model'); - this.ID = +this.$target.data('oe-many2one-id'); - - // create search button and bind search bar - this.$btn = $(qweb.render("web_editor.many2one.button")) - .insertAfter(this.$overlay.find('.oe_options')); - - this.$ul = this.$btn.find("ul"); - this.$search = this.$ul.find('li:first'); - this.$search.find('input').on('mousedown click mouseup keyup keydown', function (e) { - e.stopPropagation(); - }); - - // move menu item - setTimeout(function () { - if (self.$overlay.find('.oe_options').hasClass('hidden')) { - self.$btn.css('height', '0').find('> a').addClass('hidden'); - self.$ul.show().css({ - 'top': '-24px', 'margin': '0', 'padding': '2px 0', 'position': 'relative' + this.bind_events(); + return res; + }, + bind_events: function () { + var self = this; + var $colors = this.$el.find(".colorpicker button"); + $colors + .mouseenter(function () { + self.$target.removeClass(self.classes).addClass('bg-' + $(this).data("color")); + }) + .mouseleave(function () { + self.$target.removeClass(self.classes) + .addClass('bg-' + $colors.filter(".selected").data("color")); + }) + .click(function () { + $colors.removeClass("selected"); + $(this).addClass("selected"); + self.$target.closest(".o_editable").trigger("content_changed"); }); - } else { - self.$btn.find('a').on('click', function (e) { - self.clear(); - }); - } - },0); - // bind search input - this.$search.find('input') - .focus() - .on('keyup', function(e) { - self.find_existing($(this).val()); + this.$el.find('.note-color-reset').on('click', function () { + self.$target.removeClass(self.classes); }); - - // bind result - this.$ul.on('click', "li:not(:first) a", function (e) { - self.select_record(this); - }); - }, - - on_focus: function () { - this.$target.attr('contentEditable', 'false'); - this.clear(); - this._super(); - }, - - clear: function () { - var self = this; - this.$search.siblings().remove(); - self.$search.find('input').val(""); - setTimeout(function () { - self.$search.find('input').focus(); - },0); - }, - - find_existing: function (name) { - var self = this; - var domain = []; - if (!name || !name.length) { - self.$search.siblings().remove(); - return; } - if (isNaN(+name)) { - if (this.Model !== "res.partner") { - domain.push(['name', 'ilike', name]); + }); + + registry.background = SnippetOption.extend({ + start: function ($change_target) { + this.$target = $change_target || this.$target; + this._super(); + var src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, ""); + if (this.$target.hasClass('oe_custom_bg')) { + this.$el.find('li[data-choose_image]').data("background", src).attr("data-background", src); + } + }, + background: function (type, value, $li) { + if (value && value.length) { + this.$target.attr("style", 'background-image: url(' + value + '); ' + (this.$target.attr("style") || '').replace(/background-image:[^;]+/, '') ); + this.$target.addClass("oe_img_bg"); } else { - domain.push('|', ['name', 'ilike', name], ['email', 'ilike', name]); + this.$target.css("background-image", ""); + this.$target.removeClass("oe_img_bg oe_custom_bg"); } - } else { - domain.push(['id', '=', name]); - } + }, + select_class : function (type, value, $li) { + this.background(type, '', $li); + this._super(type, value, $li); + }, + choose_image: function (type, value, $li) { + if(type !== "click") return; - ajax.jsonRpc('/web/dataset/call_kw', 'call', { - model: this.Model, - method: 'search_read', - args: [domain, this.Model === "res.partner" ? ['name', 'display_name', 'city', 'country_id'] : ['name', 'display_name']], - kwargs: { - order: 'name DESC', - limit: 5, - context: base.get_context(), + var self = this; + var $image = $(''); + $image.attr("src", value); + $image.appendTo(self.$target); + + var _editor = new widget.MediaDialog(null, $image[0]); + _editor.appendTo(document.body); + _editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden'); + + _editor.on('saved', self, function () { + var value = $image.attr("src"); + $image.remove(); + self.$el.find('li[data-choose_image]').data("background", value).attr("data-background", value); + self.background(type, value,$li); + self.$target.addClass('oe_custom_bg'); + self.$target.trigger("snippet-option-change", [self]); + self.set_active(); + }); + _editor.on('cancel', self, function () { + $image.remove(); + }); + }, + set_active: function () { + var self = this; + var src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, ""); + this._super(); + + if (this.$target.hasClass('oe_custom_bg')) { + this.$el.find('li[data-choose_image]').data("background", src).attr("data-background", src); } - }).then(function (result){ - self.$search.siblings().remove(); - self.$search.after(qweb.render("web_editor.many2one.search",{contacts:result})); - }); - }, - get_contact_rendering: function (options) { - return ajax.jsonRpc('/web/dataset/call_kw', 'call', { - model: 'ir.qweb.field.contact', - method: 'get_record_to_html', - args: [[this.ID]], - kwargs: { - options: options, - context: base.get_context(), - } - }); - }, - - select_record: function (li) { - var self = this; - - this.ID = +$(li).data("id"); - this.$target.attr('data-oe-many2one-id', this.ID).data('oe-many2one-id', this.ID); - - this.buildingBlock.parent.rte.historyRecordUndo(this.$target); - this.$target.trigger('content_changed'); - - if (self.$target.data('oe-type') === "contact") { - $('[data-oe-contact-options]') - .filter('[data-oe-model="'+self.$target.data('oe-model')+'"]') - .filter('[data-oe-id="'+self.$target.data('oe-id')+'"]') - .filter('[data-oe-field="'+self.$target.data('oe-field')+'"]') - .filter('[data-oe-contact-options!="'+self.$target.data('oe-contact-options')+'"]') - .add(self.$target) - .attr('data-oe-many2one-id', self.ID).data('oe-many2one-id', self.ID) + this.$el.find('li[data-background]:not([data-background=""])') + .removeClass("active") .each(function () { - var $node = $(this); - self.get_contact_rendering($node.data('oe-contact-options')) - .then(function (html){ - $node.html(html); - }); + var background = $(this).data("background") || $(this).attr("data-background"); + if ((src.length && background.length && src.indexOf(background) !== -1) || (!src.length && !background.length)) { + $(this).addClass("active"); + } }); - } else { - self.$target.html($(li).data("name")); + + if (!this.$el.find('li[data-background].active').size()) { + this.$el.find('li[data-background=""]:not([data-choose_image])').addClass("active"); + } else { + this.$el.find('li[data-background=""]:not([data-choose_image])').removeClass("active"); + } } + }); - setTimeout(function () { - self.buildingBlock.make_active(false); - },0); - } -}); - -/* end*/ - -return { - 'registry': { - 'media': media, - 'colorpicker': colorpicker, - 'background_position': background_position, - 'background': background, - 'many2one': many2one, - }, - 'Class': Option -}; - + registry.background_position = SnippetOption.extend({ + start: function () { + var self = this; + var $btn = this.$overlay.find('.oe_options'); + $btn.on('show.bs.dropdown', function () { + if (self.$target.css('background-image') === 'none') { + $btn.find('.background_position_li').addClass('hidden'); + } else { + $btn.find('.background_position_li').removeClass('hidden'); + } + }); + }, + background_position: function (type, value, $li) { + if (type != 'click') { return; } + var self = this; + + this.previous_state = [this.$target.attr('class'), this.$target.css('background-size'), this.$target.css('background-position')]; + + this.bg_pos = self.$target.css('background-position').split(' '); + this.bg_siz = self.$target.css('background-size').split(' '); + + this.modal = new Dialog(null, { + title: _t("Background Image Options"), + $content: $(qweb.render('web_editor.dialog.background_position')), + buttons: [ + {text: _t("Ok"), classes: "btn-primary", close: true, click: _.bind(this.save, this)}, + {text: _t("Discard"), close: true, click: _.bind(this.discard, this)}, + ], + }).open(); + + this.modal.opened().then(function () { + // Fetch data form $target + var value = ((self.$target.hasClass('o_bg_img_opt_contain'))? 'contain' : ((self.$target.hasClass('o_bg_img_opt_custom'))? 'custom' : 'cover')); + self.modal.$("> label > input[value=" + value + "]").prop('checked', true); + + if(self.$target.hasClass("o_bg_img_opt_repeat")) { + self.modal.$("#o_bg_img_opt_contain_repeat").prop('checked', true); + self.modal.$("#o_bg_img_opt_custom_repeat").val('o_bg_img_opt_repeat'); + } else if (self.$target.hasClass("o_bg_img_opt_repeat_x")) { + self.modal.$("#o_bg_img_opt_custom_repeat").val('o_bg_img_opt_repeat_x'); + } else if (self.$target.hasClass("o_bg_img_opt_repeat_y")) { + self.modal.$("#o_bg_img_opt_custom_repeat").val('o_bg_img_opt_repeat_y'); + } + + if(self.bg_pos.length > 1) { + self.bg_pos = { + x: self.bg_pos[0], + y: self.bg_pos[1], + }; + self.modal.$("#o_bg_img_opt_custom_pos_x").val(self.bg_pos.x.replace('%', '')); + self.modal.$("#o_bg_img_opt_custom_pos_y").val(self.bg_pos.y.replace('%', '')); + } + if(self.bg_siz.length > 1) { + self.modal.$("#o_bg_img_opt_custom_size_x").val(self.bg_siz[0].replace('%', '')); + self.modal.$("#o_bg_img_opt_custom_size_y").val(self.bg_siz[1].replace('%', '')); + } + + // Focus Point + self.$focus = self.modal.$(".o_focus_point"); + self.update_pos_information(); + + var img_url = /\(['"]?([^'"]+)['"]?\)/g.exec(self.$target.css('background-image')); + img_url = (img_url && img_url[1]) || ''; + var $img = $('', {'class': 'img img-responsive', src: img_url}); + $img.on('load', function () { + self.bind_img_events($img); + }); + $img.prependTo(self.modal.$(".o_bg_img_opt_object")); + + // Bind events + self.modal.$el.on('change', '> label > input', function (e) { + self.modal.$('> .o_bg_img_opt').addClass('o_hidden') + .filter("[data-value=" + e.target.value + "]") + .removeClass('o_hidden'); + }); + self.modal.$el.on('change', 'input, select', function (e) { + self.save(); + }); + self.modal.$("> label > input:checked").trigger('change'); + }); + }, + bind_img_events: function ($img) { + var self = this; + + var mousedown = false; + $img.on('mousedown', function (e) { + mousedown = true; + }); + $img.on('mousemove', function (e) { + if (mousedown) { + _update(e); + } + }); + $img.on('mouseup', function (e) { + self.$focus.addClass('o_with_transition'); + _update(e); + setTimeout(function () { + self.$focus.removeClass('o_with_transition'); + }, 200); + mousedown = false; + }); + + function _update(e) { + var posX = e.pageX - $(e.target).offset().left; + var posY = e.pageY - $(e.target).offset().top; + self.bg_pos = { + x: clip_value(posX/$img.width()*100).toFixed(2) + '%', + y: clip_value(posY/$img.height()*100).toFixed(2) + '%', + }; + self.update_pos_information(); + self.save(); + } + + function clip_value(value) { + return Math.max(0, Math.min(value, 100)); + } + }, + update_pos_information: function () { + this.modal.$(".o_bg_img_opt_ui_info .o_x").text(this.bg_pos.x); + this.modal.$(".o_bg_img_opt_ui_info .o_y").text(this.bg_pos.y); + this.$focus.css({ + left: this.bg_pos.x, + top: this.bg_pos.y, + }); + }, + save: function () { + this.clean(); + + var bg_img_size = this.modal.$('> :not(label):not(.o_hidden)').data('value') || 'cover'; + switch (bg_img_size) { + case "cover": + this.$target.css('background-position', this.bg_pos.x + ' ' + this.bg_pos.y); + break; + case "contain": + this.$target.addClass('o_bg_img_opt_contain'); + this.$target.toggleClass('o_bg_img_opt_repeat', this.modal.$("#o_bg_img_opt_contain_repeat").prop("checked")); + break; + case "custom": + this.$target.addClass('o_bg_img_opt_custom'); + var sizeX = this.modal.$("#o_bg_img_opt_custom_size_x").val(); + var sizeY = this.modal.$("#o_bg_img_opt_custom_size_y").val(); + var posX = this.modal.$("#o_bg_img_opt_custom_pos_x").val(); + var posY = this.modal.$("#o_bg_img_opt_custom_pos_y").val(); + this.$target.addClass(this.modal.$("#o_bg_img_opt_custom_repeat").val()) + .css({ + 'background-size': ((sizeX)? sizeX + '%' : 'auto') + " " + ((sizeY)? sizeY + '%' : 'auto'), + 'background-position': ((posX)? posX + '%' : 'auto') + " " + ((posY)? posY + '%' : 'auto'), + }); + break; + } + }, + discard: function () { + this.clean(); + if (this.previous_state) { + this.$target.addClass(this.previous_state[0]).css({ + 'background-size': this.previous_state[1], + 'background-position': this.previous_state[2], + }); + } + }, + clean: function () { + this.$target.removeClass('o_bg_img_opt_contain o_bg_img_opt_custom o_bg_img_opt_repeat o_bg_img_opt_repeat_x o_bg_img_opt_repeat_y') + .css({ + 'background-size': '', + 'background-position': '', + }); + }, + }); + + /* t-field options */ + + registry.many2one = SnippetOption.extend({ + start: function () { + var self = this; + + this.Model = this.$target.data('oe-many2one-model'); + this.ID = +this.$target.data('oe-many2one-id'); + + // create search button and bind search bar + this.$btn = $(qweb.render("web_editor.many2one.button")) + .insertAfter(this.$overlay.find('.oe_options')); + + this.$ul = this.$btn.find("ul"); + this.$search = this.$ul.find('li:first'); + this.$search.find('input').on('mousedown click mouseup keyup keydown', function (e) { + e.stopPropagation(); + }); + + // move menu item + setTimeout(function () { + if (self.$overlay.find('.oe_options').hasClass('hidden')) { + self.$btn.css('height', '0').find('> a').addClass('hidden'); + self.$ul.show().css({ + 'top': '-24px', 'margin': '0', 'padding': '2px 0', 'position': 'relative' + }); + } else { + self.$btn.find('a').on('click', function (e) { + self.clear(); + }); + } + },0); + + // bind search input + this.$search.find('input') + .focus() + .on('keyup', function (e) { + self.find_existing($(this).val()); + }); + + // bind result + this.$ul.on('click', "li:not(:first) a", function (e) { + self.select_record(this); + }); + }, + + on_focus: function () { + this.$target.attr('contentEditable', 'false'); + this.clear(); + this._super(); + }, + + clear: function () { + var self = this; + this.$search.siblings().remove(); + self.$search.find('input').val(""); + setTimeout(function () { + self.$search.find('input').focus(); + },0); + }, + + find_existing: function (name) { + var self = this; + var domain = []; + if (!name || !name.length) { + self.$search.siblings().remove(); + return; + } + if (isNaN(+name)) { + if (this.Model !== "res.partner") { + domain.push(['name', 'ilike', name]); + } else { + domain.push('|', ['name', 'ilike', name], ['email', 'ilike', name]); + } + } else { + domain.push(['id', '=', name]); + } + + ajax.jsonRpc('/web/dataset/call_kw', 'call', { + model: this.Model, + method: 'search_read', + args: [domain, this.Model === "res.partner" ? ['name', 'display_name', 'city', 'country_id'] : ['name', 'display_name']], + kwargs: { + order: 'name DESC', + limit: 5, + context: base.get_context(), + } + }).then(function (result) { + self.$search.siblings().remove(); + self.$search.after(qweb.render("web_editor.many2one.search",{contacts:result})); + }); + }, + + get_contact_rendering: function (options) { + return ajax.jsonRpc('/web/dataset/call_kw', 'call', { + model: 'ir.qweb.field.contact', + method: 'get_record_to_html', + args: [[this.ID]], + kwargs: { + options: options, + context: base.get_context(), + } + }); + }, + + select_record: function (li) { + var self = this; + + this.ID = +$(li).data("id"); + this.$target.attr('data-oe-many2one-id', this.ID).data('oe-many2one-id', this.ID); + + this.buildingBlock.parent.rte.historyRecordUndo(this.$target); + this.$target.trigger('content_changed'); + + if (self.$target.data('oe-type') === "contact") { + $('[data-oe-contact-options]') + .filter('[data-oe-model="'+self.$target.data('oe-model')+'"]') + .filter('[data-oe-id="'+self.$target.data('oe-id')+'"]') + .filter('[data-oe-field="'+self.$target.data('oe-field')+'"]') + .filter('[data-oe-contact-options!="'+self.$target.data('oe-contact-options')+'"]') + .add(self.$target) + .attr('data-oe-many2one-id', self.ID).data('oe-many2one-id', self.ID) + .each(function () { + var $node = $(this); + self.get_contact_rendering($node.data('oe-contact-options')) + .then(function (html) { + $node.html(html); + }); + }); + } else { + self.$target.html($(li).data("name")); + } + + setTimeout(function () { + self.buildingBlock.make_active(false); + },0); + } + }); + + return { + registry: registry, + Class: SnippetOption, + }; }); diff --git a/addons/web_editor/static/src/js/summernote.js b/addons/web_editor/static/src/js/summernote.js index 775c4398268..5c9e86dc8df 100644 --- a/addons/web_editor/static/src/js/summernote.js +++ b/addons/web_editor/static/src/js/summernote.js @@ -977,7 +977,7 @@ $.summernote.pluginEvents.tab = function (event, editor, layoutInfo, outdent) { $.summernote.pluginEvents.indent(event, editor, layoutInfo); } } else { - if (!outdent){ + if (!outdent) { if(dom.isText(r.sc)) { var next = r.sc.splitText(r.so); } else { @@ -1876,11 +1876,11 @@ eventHandler.modules.toolbar.button.updateRecentColor = function (elBtn, sEvent, font.style.color = sValue !== 'inherit' ? sValue : ""; } } else { - font.className = font.className.replace(/(^|\s+)bg-\S+/, ''); if (sValue.indexOf('bg-') !== -1) { font.className += ' ' + sValue; font.style.backgroundColor = ""; } else { + font.className = font.className.replace(/(^|\s+)bg-\S+/, ''); font.style.backgroundColor = sValue !== 'inherit' ? sValue : ""; } } @@ -1905,7 +1905,7 @@ eventHandler.modules.editor.redo = function ($popover) { // use image toolbar if current range is on image var fn_editor_currentstyle = eventHandler.modules.editor.currentStyle; -eventHandler.modules.editor.currentStyle = function(target) { +eventHandler.modules.editor.currentStyle = function (target) { var styleInfo = fn_editor_currentstyle.apply(this, arguments); // with our changes for inline editor, the targeted element could be a button of the editor if(!styleInfo.image || !dom.isEditable(styleInfo.image)) { diff --git a/addons/web_editor/static/src/js/translator.js b/addons/web_editor/static/src/js/translator.js index 39efbe1db2f..6932d5f9ed2 100644 --- a/addons/web_editor/static/src/js/translator.js +++ b/addons/web_editor/static/src/js/translator.js @@ -116,7 +116,6 @@ var Translate = Widget.extend({ }, start: function () { this._super(); - this.$('button[data-action=save]').prop('disabled', true); return this.edit(); }, setTarget: function ($target) { @@ -158,7 +157,7 @@ var Translate = Widget.extend({ }, edit: function () { var flag = false; - window.onbeforeunload = function(event) { + window.onbeforeunload = function (event) { if ($('.o_editable.o_dirty').length && !flag) { flag = true; setTimeout(function () {flag=false;},0); @@ -179,7 +178,6 @@ var Translate = Widget.extend({ var $node = $(node); var trans = this.getTranlationObject($node[0]); $node.toggleClass('o_dirty', trans.value !== $node.html().replace(/[ \t\n\r]+/, ' ')); - this.$('button[data-action=save]').prop('disabled', !$('.o_editable.o_dirty').length); }, getTranlationObject: function (node) { var $node = $(node); diff --git a/addons/web_editor/static/src/js/widgets.js b/addons/web_editor/static/src/js/widgets.js index 165f0b0e914..8db4c66180c 100644 --- a/addons/web_editor/static/src/js/widgets.js +++ b/addons/web_editor/static/src/js/widgets.js @@ -39,8 +39,8 @@ var Dialog = Widget.extend({ close: function () { this.$el.modal('hide'); }, - stop_escape: function(event) { - if($(".modal.in").length>0 && event.which == 27){ + stop_escape: function (event) { + if($(".modal.in").length>0 && event.which == 27) { event.stopPropagation(); } } @@ -72,7 +72,7 @@ var alt = Dialog.extend({ }, }); -var click_event = function(el, type) { +var click_event = function (el, type) { var evt = document.createEvent("MouseEvents"); evt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, el); el.dispatchEvent(evt); @@ -239,14 +239,14 @@ var ImageDialog = Widget.extend({ }, 'click button.filepicker': function () { var filepicker = this.$('input[type=file]'); - if (!_.isEmpty(filepicker)){ + if (!_.isEmpty(filepicker)) { filepicker[0].click(); } }, 'click .js_disable_optimization': function () { this.$('input[name="disable_optimization"]').val('1'); var filepicker = this.$('button.filepicker'); - if (!_.isEmpty(filepicker)){ + if (!_.isEmpty(filepicker)) { filepicker[0].click(); } }, @@ -256,8 +256,8 @@ var ImageDialog = Widget.extend({ 'keyup input.url': "change_input", //'change select.image-style': 'preview_image', 'click .existing-attachments [data-src]': 'select_existing', - 'click .existing-attachment-remove': 'try_remove', - 'keydown.dismiss.bs.modal': function(){}, + 'click .o_existing_attachment_remove': 'try_remove', + 'keydown.dismiss.bs.modal': function () {}, }), init: function (parent, media, options) { this._super(); @@ -417,7 +417,7 @@ var ImageDialog = Widget.extend({ this.$('button.filepicker').removeClass('btn-danger btn-success'); this.$('form').submit(); }, - file_selected: function(attachment, error) { + file_selected: function (attachment, error) { var $button = this.$('button.filepicker'); if (!error) { $button.addClass('btn-success'); @@ -497,14 +497,14 @@ var ImageDialog = Widget.extend({ }, selected_existing: function () { var self = this; - this.$('.existing-attachment-cell.media_selected').removeClass("media_selected"); - var $select = this.$('.existing-attachment-cell [data-src]').filter(function () { + this.$('.o_existing_attachment_cell.o_selected').removeClass("o_selected"); + var $select = this.$('.o_existing_attachment_cell [data-src]').filter(function () { var $img = $(this); return !!_.find(self.images, function (v) { return (v.url === $img.data("src") || ($img.data("url") && v.url === $img.data("url")) || v.id === $img.data("id")); }); }); - $select.closest('.existing-attachment-cell').addClass("media_selected"); + $select.closest('.o_existing_attachment_cell').addClass("o_selected"); return $select; }, try_remove: function (e) { @@ -535,7 +535,7 @@ var ImageDialog = Widget.extend({ var cacheCssSelectors = {}; -var getCssSelectors = function(filter) { +var getCssSelectors = function (filter) { var css = []; if (cacheCssSelectors[filter]) { return cacheCssSelectors[filter]; @@ -580,7 +580,7 @@ var getCssSelectors = function(filter) { } return cacheCssSelectors[filter] = css; }; -var computeFonts = _.once(function() { +var computeFonts = _.once(function () { _.each(fontIcons, function (data) { data.cssData = getCssSelectors(data.parser); data.alias = []; @@ -628,14 +628,14 @@ var fontIconsDialog = Widget.extend({ e.stopPropagation(); this.$('#fa-icon').val(e.target.getAttribute('data-id')); - $(".font-icons-icon").removeClass("font-icons-selected"); - $(e.target).addClass("font-icons-selected"); + $(".font-icons-icon").removeClass("o_selected"); + $(e.target).addClass("o_selected"); }, - 'keydown.dismiss.bs.modal': function(){}, + 'keydown.dismiss.bs.modal': function () {}, }), // extract list of font (like awsome) from the cheatsheet. - renderElement: function() { + renderElement: function () { this.iconsParser = fontIcons; this.icons = _.flatten(_.map(fontIcons, function (data) { return data.icons; @@ -750,7 +750,7 @@ var fontIconsDialog = Widget.extend({ continue; case '': continue; default: - $(".font-icons-icon").removeClass("font-icons-selected").filter("[data-alias*=',"+cls+",']").addClass("font-icons-selected"); + $(".font-icons-icon").removeClass("o_selected").filter("[data-alias*=',"+cls+",']").addClass("o_selected"); for (var k=0; k -
  • Document
  • Pictogram
  • Video
  • - - @@ -399,15 +175,16 @@
    +
    + +
    @@ -425,7 +202,7 @@
    - +
    - +
    @@ -457,11 +234,11 @@ -
    -
    - - + + + $(this).parent().addClass('o_website_modal'); + - - - +
    +