diff options
Diffstat (limited to 'addons/web_editor/static/src')
| -rw-r--r-- | addons/web_editor/static/src/js/backend.js | 22 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/base.js | 10 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/editor.js | 13 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/inline.js | 6 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/rte.js | 6 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/rte.summernote.js | 114 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/snippets.animation.js | 4 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/snippets.editor.js | 195 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/snippets.options.js | 1387 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/summernote.js | 6 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/translator.js | 4 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/widgets.js | 38 | ||||
| -rw-r--r-- | addons/web_editor/static/src/xml/editor.xml | 508 | ||||
| -rw-r--r-- | addons/web_editor/static/src/xml/snippets.xml | 47 | ||||
| -rw-r--r-- | addons/web_editor/static/src/xml/translator.xml | 13 |
15 files changed, 1066 insertions, 1307 deletions
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') || "<p><br/></p>"); 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') === "<p><br/></p>" || !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 = $('<div />').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 = $('<li/>') + .append($('<a/>', {href: '#' + id}) + .append($('<i/>', {'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 = $('<li/>') + .append($('<a/>', {href: '#' + id, 'aria-controls': id}) + .append($('<i/>', {'class': 'fa fa-magic'}))); + $toggles.prepend($li); + + $tabs.prepend($('<div/>', {'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 = $('<div class="btn-group"/>'); $alt.prependTo($imagePopover.find('.popover-content')); - $alt.append('<button class="btn btn-default btn-sm btn-small" data-event="alt"><strong>Alt: </strong><span class="o_image_alt"></span></button>'); + $alt.append('<button class="btn btn-default btn-sm btn-small" data-event="alt"><strong>' + _t('Description') + ': </strong><span class="o_image_alt"/></button>'); // padding button var $padding = $('<div class="btn-group"/>'); @@ -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){ - $("<div id='oe_manipulators' class='o_css_editor'></div>").appendTo('body'); + if(!$('#oe_manipulators').length) { + $("<div/>", {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 = $("<div class='oe_drop_zone oe_insert'></div>"); + var zone_template = $("<div class='oe_drop_zone oe_insert'/>"); 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'; - -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 qweb = core.qweb; - -/* ----- 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') || $('<div>'); - 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); - }, - - _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.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.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(); - - // triggers preview or apply methods if a menu item has been clicked - this.select(event.type === "click" ? "click" : "over", $next); - if (event.type === 'click') { + 'use strict'; + + 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 _t = core._t; + + /* ----- Editor option (object link the the xml with data-js) ---- */ + + 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') || $('<div>'); + 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); + }, + + _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.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.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(); + + // 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"); - }, - - 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_blur : function () { - }, - - on_clone: function ($clone) { - }, - - on_move: function () { - }, - - on_remove: 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); + this.$target.on('snippet-option-reset', _.bind(this.set_active, this)); + this._bind_li_menu(); + }, + + on_focus : function () { + this._bind_li_menu(); + }, + + on_blur : function () { + }, + + on_clone: function ($clone) { + }, + + on_move: function () { + }, + + on_remove: 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); + }); + 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); - }); - - for (var k in self.reset_methods) { - var method = self.reset_methods[k]; - if (method) { - self[method]("reset", null); + 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; } - } - 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); - } + 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; + 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; + } } + return false; } - return false; - } - function method(el) { - var data = {}; - for (var k in el.dataset) { - if (filter (k)) { - data[k] = el.dataset[k]; + function method(el) { + var data = {}; + for (var k in el.dataset) { + if (filter (k)) { + data[k] = el.dataset[k]; + } } + return data; } - return data; - } - while (el && this.$el.is(el) || _.some(this.$el.map(function () {return $.contains(this, el);}).get()) ) { - if (hasData(el)) { - $methods.push(el); + 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; } - el = el.parentNode; - } - $methods.reverse(); + $methods.reverse(); - _.each($methods, function (el) { - var $el = $(el); - var methods = method(el); + _.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); + 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+"'"); } - 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]'); + // 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)')); + 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); + this.$target.removeClass(classes); + this.$target.addClass(active_classes); - if (type !== 'reset') { - this.$target.toggleClass(value); + 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); + } } - }, - select_class: function (type, value, $li) { - var $lis = this.$el.find('[data-select_class]').add(this.$el).filter('[data-select_class]'); + }); + + registry.colorpicker = SnippetOption.extend({ + start: function () { + var self = this; + var res = this._super(); + + if (!this.$el.find('.colorpicker').length) { + var $pt = $(qweb.render('web_editor.snippet.option.colorpicker')); + var $clpicker = $(qweb.render('web_editor.colorpicker')); + + var $toggles = $pt.find('.o_colorpicker_section_menu'); + var $tabs = $pt.find('.o_colorpicker_section_tabs'); + + var $sections = $clpicker.find('.o_colorpicker_section'); + if ($sections.length) { + $sections.each(function () { + var $section = $(this); + var id = 'o_palette_' + $section.data('name') + _.uniqueId(); + + var $li = $('<li/>') + .append($('<a/>', {href: '#' + id}) + .append($('<i/>', {'class': $section.data('iconClass') || '', html: $section.data('iconContent') || ''}))); + $toggles.append($li); + + $tabs.append($section.addClass('tab-pane').attr('id', id)); + }); + $toggles.find('li').first().addClass('active'); + $tabs.find('div').first().addClass('active'); + + $toggles.on('click mouseover', '> li > a', function (e) { + e.preventDefault(); + e.stopPropagation(); + $(this).tab('show'); + }); + } else { + $toggles.parent().empty().append($clpicker); + } - var classes = $lis.map(function () {return $(this).data('select_class');}).get(); + 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'); + } - 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); - }, + var classes = []; + this.$el.find(".colorpicker button").map(function () { + var $color = $(this); + var className = 'bg-' + $color.data('color'); - clean_for_save: editor.dummy -}); + $color.addClass(className); -/* ----- 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); - } - } -}); + if (self.$target.hasClass(className)) { + self.color = className; + $color.addClass("selected"); + } + classes.push(className); + }); + this.classes = classes.join(" "); + + 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"); + }); -var colorpicker = Option.extend({ - start: function () { - var self = this; - var res = this._super(); - - var paletteTemplate = '<div class="colorpicker">'+ - '<div class="note-palette-title">Background Color</div>' + - '<div class="btn-group palette-reset">' + - '<div class="note-color-reset" data-event="foreColor"' + - ' data-value="inherit" title="None"><i class="material-icons"></i> '+ - '</div>' + - '</div>' + - - '<div class="tabs_container">' + - '<ul class="tabs_toggles">'+ - '<li class="on"><a href="#" data-tab-target=".palette_theme"><i class="material-icons"></i></a></li>' + - '<li><a href="#" data-tab-target=".palette_grayscale"><i class="material-icons"></i></a></li>' + - '<li><a href="#" data-tab-target=".palette_ui"><i class="material-icons"></i></a></li>' + - '<li><a href="#" data-tab-target=".palette_spectrum"><i class="material-icons"></i></a></li>' + - '</ul>' + - '<div class="tabs">' + - '<div class="tab palette_theme on"/>'+ - '<div class="tab palette_grayscale"/>'+ - '<div class="tab palette_ui"/>'+ - '<div class="tab palette_spectrum">'+ - '<div class="note-color-palette"></div>' + - '</div>' + - '</div>' + - '</div>' + - - '</div></div>'; - 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'); + this.$el.find('.note-color-reset').on('click', function () { + self.$target.removeClass(self.classes); + }); } - - 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"); + }); + + 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); } - classes.push(color); - }); - this.classes = classes.join(" "); - - 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"); + }, + 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 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; + + var self = this; + var $image = $('<img class="hidden"/>'); + $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); + } -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') + 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 (!this.$el.find('li[data-background].active').size()) { + this.$el.find('li[data-background=""]:not([data-choose_image])').addClass("active"); } else { - $btn.find('.background_position_li').removeClass('hidden') + this.$el.find('li[data-background=""]:not([data-choose_image])').removeClass("active"); } - }) - }, - 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') - }) - - // 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'); - - } 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') } + }); + + 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'); + } - var bg_pos = self.$target.css('background-position').split(' '), - bg_siz = self.$target.css('background-size').split(' '); + 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('%', '')); + } - 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('%','')) - } + // Focus Point + self.$focus = self.modal.$(".o_focus_point"); + self.update_pos_information(); - // Collapse events - $('#collapse_cover').on('show.bs.collapse', function(){ - $('#radio1').prop('checked', true); - $modal.find("#bg_size").val('o_bg_img_cover'); - }); - - $('#collapse_contain').on('show.bs.collapse', function(){ - $('#radio2').prop('checked', true); - $modal.find("#bg_size").val('o_bg_img_contain'); - }); - - $('#collapse_custom').on('show.bs.collapse', function(){ - $('#radio3').prop('checked', true); - $modal.find("#bg_size").val('o_bg_img_custom'); - }); - - // Focus Point - var img_url = self.$target.css('background-image').replace(/url\(['"]*|['"]*\)|^none$/g, ""); - - 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 class="img-responsive" src="'+ img_url +'"/>') - $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') - } else { - self.$target.addClass('o_bg_img_no_repeat') - } - 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 img_url = /\(['"]?([^'"]+)['"]?\)/g.exec(self.$target.css('background-image')); + img_url = (img_url && img_url[1]) || ''; + var $img = $('<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")); -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; - - var self = this; - var $image = $('<img class="hidden"/>'); - $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); - } + // 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; - 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"); + 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; + }); - 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"); - } - } -}); + 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 */ + /* t-field options */ -var many2one = Option.extend({ - start: function () { - var self = this; + 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'); + 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')); + // 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(); + }); - 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); - // 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' + // 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 { - self.$btn.find('a').on('click', function (e) { - self.clear(); - }); + domain.push(['id', '=', name]); } - },0); - // bind search input - this.$search.find('input') - .focus() - .on('keyup', function(e) { - self.find_existing($(this).val()); + 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(), + } }); + }, - // 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]); + 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 { - domain.push('|', ['name', 'ilike', name], ['email', 'ilike', name]); + self.$target.html($(li).data("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); } + }); - 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 -}; - + 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<this.icons.length; k++) { if (this.icons.indexOf(cls) !== -1) { this.$('#fa-icon').val(cls); @@ -852,7 +852,7 @@ var VideoDialog = Widget.extend({ 'keyup input#urlvideo': 'change_input', 'change input#embedvideo': 'change_input', 'keyup input#embedvideo': 'change_input', - 'keydown.dismiss.bs.modal': function(){}, + 'keydown.dismiss.bs.modal': function () {}, }), init: function (parent, media) { this._super(); diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index 3891e7caf89..765d3d15354 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -1,23 +1,19 @@ <templates id="template" xml:space="preserve"> <t t-name="web_editor.editorbar"> - <div class="o_css_editor navbar navbar-fixed-top hidden-xs" id="web_editor-top-edit"> - <div id="web_editor-toolbars" /> + <div id="web_editor-top-edit" class="o_css_editor"> + <div id="web_editor-toolbars"/> <form class="navbar-form text-muted"> - <div id="web_editor-top-actions"> - <a class="btn btn-link btn-xs" data-action="cancel" href="#"> - <i class="material-icons">clear</i>DISCARD</a> - <button class="btn full compl btn-xs" data-action="save" type="button"> - <i class="fa fa-floppy-o"></i> SAVE</button> - </div> + <button type="button" class="btn btn-sm btn-default" data-action="cancel"><i class="fa fa-times"/> Discard</button> + <button type="button" class="btn btn-sm btn-primary" data-action="save"><i class="fa fa-floppy-o"/> Save</button> </form> </div> </t> <t t-name="web_editor.dialog"> - <div aria-hidden="true" class="modal web_editor-dialog" role="dialog" tabindex="-1"> + <div class="modal o_website_modal web_editor-dialog" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> - <button aria-hidden="true" class="close" data-dismiss="modal" type="button">×</button> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 class="modal-title"> <t t-esc="title"/> </h3> @@ -26,350 +22,129 @@ <t t-raw="0"/> </div> <div class="modal-footer"> - <button class="btn btn-primary save" t-if="!button" type="button">Save</button> - <button class="btn btn-primary save" t-if="button" type="button"> - <t t-raw="button"/> - </button> + <button class="btn btn-sm btn-primary save" t-if="!button" type="button">Save</button> + <button class="btn btn-sm btn-primary save" t-if="button" type="button"><t t-raw="button"/></button> <button class="btn hidden wait" disabled="disabled" type="button"/> - <span>or </span> - <a aria-hidden="true" data-dismiss="modal" href="#">Discard</a> + <a class="btn btn-sm btn-default" data-dismiss="modal" href="#" aria-hidden="true">Discard</a> </div> </div> </div> </div> </t> - <t t-name="web_editor.dialog.background_position"> - <div aria-hidden="true" class="modal website_modal web_editor-background_position" role="dialog" tabindex="-1"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button aria-hidden="true" class="close" data-dismiss="modal" type="button">×</button> - <h3 class="modal-title">Background Image Options</h3> - </div> - <div class="modal-body"> - <div class="panel-group" id="bg_image_opt_accordition" role="tablist" aria-multiselectable="true"> - <div class="panel panel-default"> - <div class="panel-heading" role="tab" id="heading_cover"> - <label role="button" class="radio inline" data-toggle="collapse" data-parent="#bg_image_opt_accordition" href="#collapse_cover"> - <input id="radio1" class="opt_select" name="rdo" type="radio"/>COVER</label> - </div> - <div class="panel-collapse collapse" role="tabpanel" id="collapse_cover"> - <div class="panel-body"> - - <a class="help-controll collapsed" role="button" data-toggle="collapse" href="#cover_help" aria-expanded="false" aria-controls="cover_help"><i class="material-icons">help</i></a> - - <div class="help collapse" id="cover_help"> - - <div class="simulator cover_bg"> - <div class="bg"></div> - <div class="el"></div> - </div> - - <div class="help-text"> - <p>The 'cover' method scale the background image to be as large as possible so that the background area is completely covered by the background image. Some parts of the background image may not be in view</p> - </div> - </div> - <p><strong>Assigning a focal point to ensure that the targeted area will be always visible</strong></p> - <div class="cover_editing_container"> - <h6>Click on the picture</h6> - <div class="object"> - <span class="grid grid-1"/> - <span class="grid grid-2"/> - <span class="grid grid-3"/> - <span class="grid grid-4"/> - <span class="focus_point"/> - <div class="ui_info">X: <span class="x"/> Y: <span class="y"/></div> - </div> - </div> - </div> - </div> - </div> - <div class="panel panel-default"> - <div class="panel-heading" role="tab" id="heading_contain"> - <label role="button" class="radio inline" data-toggle="collapse" data-parent="#bg_image_opt_accordition" href="#collapse_contain"> - <input id="radio2" class="opt_select" name="rdo" type="radio"/>CONTAIN</label> - </div> - <div class="panel-collapse collapse" role="tabpanel" id="collapse_contain"> - <div class="panel-body"> - <a class="help-controll collapsed" role="button" data-toggle="collapse" href="#contain_help" aria-expanded="false" aria-controls="contain_help"><i class="material-icons">help</i></a> - - <div class="help collapse" id="contain_help"> - - <div class="simulator contain_bg"> - <div class="bg"></div> - <div class="el"></div> - </div> - - <div class="help-text"> - <p>Scale the image to the largest size such that both its width and its height can fit inside the content area</p> - </div> - </div> - - <form class="form-horizontal"> - <div class="form-group"> - <label class="col-sm-2 control-label" for="repeatInput">Repeat</label> - <div class="col-sm-5"> - <div class="checkbox"> - <label> - <input id="contain_repeat" type="checkbox"/> - </label> - </div> - </div> - </div> - </form> - </div> - </div> - </div> - <div class="panel panel-default"> - <div class="panel-heading" role="tab" id="heading_custom"> - <label role="button" class="radio inline" data-toggle="collapse" data-parent="#bg_image_opt_accordition" href="#collapse_custom"> - <input id="radio3" class="opt_select" name="rdo" type="radio"/>CUSTOM</label> - </div> - <div class="panel-collapse collapse" role="tabpanel" id="collapse_custom"> - <div class="panel-body"> - <form class="form-horizontal"> - <fieldset> - <legend>Background size</legend> - <p>Sets the width and height of the background image in percent of the parent element.</p> - <div class="form-group"> - <label class="col-sm-3 control-label" for="custom_size_x">Background width</label> - <div class="col-sm-5"> - <div class="input-group"> - <input type="number" min="0" max="100" class="form-control input-sm" id="custom_size_x" placeholder="auto"/> - <div class="input-group-addon">%</div> - </div> - </div> - </div> - <div class="form-group"> - <label class="col-sm-3 control-label" for="custom_size_y">Background height</label> - <div class="col-sm-5"> - <div class="input-group"> - <input type="number" min="0" max="100" class="form-control input-sm" id="custom_size_y" placeholder="auto"/> - <div class="input-group-addon">%</div> - </div> - </div> - </div> - </fieldset> - <fieldset> - <legend>Background position</legend> - <p>Set the starting position of the background image.</p> - <div class="form-group"> - <label class="col-sm-3 control-label" for="custom_pos_x">Horizontal</label> - <div class="col-sm-5"> - <div class="input-group"> - <input type="number" min="0" max="100" class="form-control input-sm" id="custom_pos_x" placeholder="auto"/> - <div class="input-group-addon">%</div> - </div> - </div> - </div> - <div class="form-group"> - <label class="col-sm-3 control-label" for="custom_pos_x">Vertical</label> - <div class="col-sm-5"> - <div class="input-group"> - <input type="number" min="0" max="100" class="form-control input-sm" id="custom_pos_y" placeholder="auto"/> - <div class="input-group-addon">%</div> - </div> - </div> - </div> - </fieldset> - <fieldset> - <legend>Background repeat</legend> - <p>Define if/how the background image will be repeated</p> - <select class="form-control" id="custom_repeat"> - <option value="o_bg_img_no_repeat">No repeat</option> - <option value="o_bg_img_repeat">Repeat both</option> - <option value="o_bg_img_repeat_x">Repeat x</option> - <option value="o_bg_img_repeat_y">Repeat y</option> - </select> - </fieldset> - </form> - - </div> - </div> - </div> <!-- / .panel --> - </div> <!-- / .panel-group --> - <input id="pos_x" type="hidden"/> - <input id="pos_y" type="hidden"/> - <input id="bg_size" type="hidden"/> - </div> <!-- /.modal-body --> + <div t-name="web_editor.dialog.background_position" class="o_bg_img_opt_modal"> + <label class="radio-inline"><input type="radio" name="o_bg_img_opt" value="cover"/> Cover</label> + <label class="radio-inline"><input type="radio" name="o_bg_img_opt" value="contain"/> Contain</label> + <label class="radio-inline"><input type="radio" name="o_bg_img_opt" value="custom"/> Custom</label> - <div class="modal-footer"> - <button class="btn btn-primary save" data-dismiss="modal" type="button">Save</button> or <a aria-hidden="true" data-dismiss="modal" href="#">Discard</a> - </div> + <div class="o_bg_img_opt" data-value="cover"> + <a class="help-control collapsed" role="button" data-toggle="collapse" href="#cover_help" aria-expanded="false" aria-controls="cover_help"><span class="fa fa-question-circle"/></a> + <div class="help collapse" id="cover_help"> + <div class="simulator cover_bg"> + <div class="bg"/> + <div class="el"/> + </div> + <p class="help-text">Scale the image to be as large as it needs to fill the whole content area. Part of the image may not be visible.</p> + </div> + <p><strong>Assigning a focal point to ensure that the targeted area will be always visible</strong></p> + <div class="o_bg_img_opt_cover_edition"> + <h6>Click on the picture</h6> + <div class="o_bg_img_opt_object"> + <span class="grid grid-1"/> + <span class="grid grid-2"/> + <span class="grid grid-3"/> + <span class="grid grid-4"/> + <span class="o_focus_point"/> + <div class="o_bg_img_opt_ui_info">X: <span class="o_x"/> Y: <span class="o_y"/></div> </div> </div> </div> - </t> - <t t-name="web_editor.dialog.background_position"> - <div aria-hidden="true" class="modal website_modal web_editor-background_position" role="dialog" tabindex="-1"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button aria-hidden="true" class="close" data-dismiss="modal" type="button">×</button> - <h3 class="modal-title">Background Image Options</h3> - </div> - <div class="modal-body"> - <div class="panel-group" id="bg_image_opt_accordition" role="tablist" aria-multiselectable="true"> - <div class="panel panel-default"> - <div class="panel-heading" role="tab" id="heading_cover"> - <label role="button" class="radio inline" data-toggle="collapse" data-parent="#bg_image_opt_accordition" href="#collapse_cover"> - <input id="radio1" class="opt_select" name="rdo" type="radio"/>COVER</label> - </div> - <div class="panel-collapse collapse" role="tabpanel" id="collapse_cover"> - <div class="panel-body"> - - <a class="help-controll collapsed" role="button" data-toggle="collapse" href="#cover_help" aria-expanded="false" aria-controls="cover_help"><i class="material-icons">help</i></a> - - <div class="help collapse" id="cover_help"> - - <div class="simulator cover_bg"> - <div class="bg"></div> - <div class="el"></div> - </div> - - <div class="help-text"> - <p>The 'cover' method scale the background image to be as large as possible so that the background area is completely covered by the background image. Some parts of the background image may not be in view</p> - </div> - </div> - <p><strong>Assigning a focal point to ensure that the targeted area will be always visible</strong></p> - <div class="cover_editing_container"> - <h6>Click on the picture</h6> - <div class="object"> - <span class="grid grid-1"/> - <span class="grid grid-2"/> - <span class="grid grid-3"/> - <span class="grid grid-4"/> - <span class="focus_point"/> - <div class="ui_info">X: <span class="x"/> Y: <span class="y"/></div> - </div> - </div> - </div> - </div> - </div> - <div class="panel panel-default"> - <div class="panel-heading" role="tab" id="heading_contain"> - <label role="button" class="radio inline" data-toggle="collapse" data-parent="#bg_image_opt_accordition" href="#collapse_contain"> - <input id="radio2" class="opt_select" name="rdo" type="radio"/>CONTAIN</label> - </div> - <div class="panel-collapse collapse" role="tabpanel" id="collapse_contain"> - <div class="panel-body"> - <a class="help-controll collapsed" role="button" data-toggle="collapse" href="#contain_help" aria-expanded="false" aria-controls="contain_help"><i class="material-icons">help</i></a> - - <div class="help collapse" id="contain_help"> - - <div class="simulator contain_bg"> - <div class="bg"></div> - <div class="el"></div> - </div> - - <div class="help-text"> - <p>Scale the image to the largest size such that both its width and its height can fit inside the content area</p> - </div> - </div> - - <form class="form-horizontal"> - <div class="form-group"> - <label class="col-sm-2 control-label" for="repeatInput">Repeat</label> - <div class="col-sm-5"> - <div class="checkbox"> - <label> - <input id="contain_repeat" type="checkbox"/> - </label> - </div> - </div> - </div> - </form> - </div> - </div> - </div> - <div class="panel panel-default"> - <div class="panel-heading" role="tab" id="heading_custom"> - <label role="button" class="radio inline" data-toggle="collapse" data-parent="#bg_image_opt_accordition" href="#collapse_custom"> - <input id="radio3" class="opt_select" name="rdo" type="radio"/>CUSTOM</label> - </div> - <div class="panel-collapse collapse" role="tabpanel" id="collapse_custom"> - <div class="panel-body"> - <form class="form-horizontal"> - <fieldset> - <legend>Background size</legend> - <p>Sets the width and height of the background image in percent of the parent element.</p> - <div class="form-group"> - <label class="col-sm-3 control-label" for="custom_size_x">Background width</label> - <div class="col-sm-5"> - <div class="input-group"> - <input type="number" min="0" max="100" class="form-control input-sm" id="custom_size_x" placeholder="auto"/> - <div class="input-group-addon">%</div> - </div> - </div> - </div> - <div class="form-group"> - <label class="col-sm-3 control-label" for="custom_size_y">Background height</label> - <div class="col-sm-5"> - <div class="input-group"> - <input type="number" min="0" max="100" class="form-control input-sm" id="custom_size_y" placeholder="auto"/> - <div class="input-group-addon">%</div> - </div> - </div> - </div> - </fieldset> - <fieldset> - <legend>Background position</legend> - <p>Set the starting position of the background image.</p> - <div class="form-group"> - <label class="col-sm-3 control-label" for="custom_pos_x">Horizontal</label> - <div class="col-sm-5"> - <div class="input-group"> - <input type="number" min="0" max="100" class="form-control input-sm" id="custom_pos_x" placeholder="auto"/> - <div class="input-group-addon">%</div> - </div> - </div> - </div> - <div class="form-group"> - <label class="col-sm-3 control-label" for="custom_pos_x">Vertical</label> - <div class="col-sm-5"> - <div class="input-group"> - <input type="number" min="0" max="100" class="form-control input-sm" id="custom_pos_y" placeholder="auto"/> - <div class="input-group-addon">%</div> - </div> - </div> - </div> - </fieldset> - <fieldset> - <legend>Background repeat</legend> - <p>Define if/how the background image will be repeated</p> - <select class="form-control" id="custom_repeat"> - <option value="o_bg_img_no_repeat">No repeat</option> - <option value="o_bg_img_repeat">Repeat both</option> - <option value="o_bg_img_repeat_x">Repeat x</option> - <option value="o_bg_img_repeat_y">Repeat y</option> - </select> - </fieldset> - </form> - - </div> - </div> - </div> <!-- / .panel --> - </div> <!-- / .panel-group --> - <input id="pos_x" type="hidden"/> - <input id="pos_y" type="hidden"/> - <input id="bg_size" type="hidden"/> - </div> <!-- /.modal-body --> + <div class="o_bg_img_opt" data-value="contain"> + <a class="help-control collapsed" role="button" data-toggle="collapse" href="#contain_help" aria-expanded="false" aria-controls="contain_help"><span class="fa fa-question-circle"/></a> + <div class="help collapse" id="contain_help"> + <div class="simulator contain_bg"> + <div class="bg"/> + <div class="el"/> + </div> + <p class="help-text">Scale the image to the largest size such that both its width and height can fit inside the content area</p> + </div> - <div class="modal-footer"> - <button class="btn btn-primary save" data-dismiss="modal" type="button">Save</button> or <a aria-hidden="true" data-dismiss="modal" href="#">Discard</a> + <form class="form-horizontal"> + <div class="form-group"> + <label class="col-sm-2 control-label" for="repeatInput">Repeat</label> + <div class="col-sm-5"> + <div class="checkbox"> + <label> + <input type="checkbox" id="o_bg_img_opt_contain_repeat"/> + </label> + </div> </div> - </div> - </div> + </form> </div> - </t> + <form class="o_bg_img_opt form-horizontal" data-value="custom"> + <fieldset> + <legend>Background size</legend> + <p>Sets the width and height of the background image in percent of the parent element.</p> + <div class="form-group"> + <label class="col-sm-3 control-label" for="o_bg_img_opt_custom_size_x">Background width</label> + <div class="col-sm-5"> + <div class="input-group"> + <input type="number" min="0" max="100" class="form-control input-sm" id="o_bg_img_opt_custom_size_x" placeholder="auto"/> + <div class="input-group-addon">%</div> + </div> + </div> + </div> + <div class="form-group"> + <label class="col-sm-3 control-label" for="o_bg_img_opt_custom_size_y">Background height</label> + <div class="col-sm-5"> + <div class="input-group"> + <input type="number" min="0" max="100" class="form-control input-sm" id="o_bg_img_opt_custom_size_y" placeholder="auto"/> + <div class="input-group-addon">%</div> + </div> + </div> + </div> + </fieldset> + <fieldset> + <legend>Background position</legend> + <p>Set the starting position of the background image.</p> + <div class="form-group"> + <label class="col-sm-3 control-label" for="o_bg_img_opt_custom_pos_x">Horizontal</label> + <div class="col-sm-5"> + <div class="input-group"> + <input type="number" min="0" max="100" class="form-control input-sm" id="o_bg_img_opt_custom_pos_x" placeholder="auto"/> + <div class="input-group-addon">%</div> + </div> + </div> + </div> + <div class="form-group"> + <label class="col-sm-3 control-label" for="o_bg_img_opt_custom_pos_y">Vertical</label> + <div class="col-sm-5"> + <div class="input-group"> + <input type="number" min="0" max="100" class="form-control input-sm" id="o_bg_img_opt_custom_pos_y" placeholder="auto"/> + <div class="input-group-addon">%</div> + </div> + </div> + </div> + </fieldset> + <fieldset> + <legend>Background repeat</legend> + <p>Define if/how the background image will be repeated</p> + <select class="form-control" id="o_bg_img_opt_custom_repeat"> + <option value="">No repeat</option> + <option value="o_bg_img_opt_repeat">Repeat both</option> + <option value="o_bg_img_opt_repeat_x">Repeat x</option> + <option value="o_bg_img_opt_repeat_y">Repeat y</option> + </select> + </fieldset> + </form> + </div> <t t-name="web_editor.dialog.media"> - <div aria-hidden="true" class="modal note-image-dialog" role="dialog" tabindex="-1"> - <div class="modal-dialog select-media"> + <div class="modal note-image-dialog o_website_modal" tabindex="-1" role="dialog" aria-hidden="true"> + <div class="modal-dialog modal-lg o_select_media_dialog"> <div class="modal-content"> <div class="modal-header"> - <button aria-hidden="true" class="close" data-dismiss="modal" type="button">×</button> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 class="modal-title">Select a Media</h3> </div> <div class="modal-body"> @@ -378,7 +153,7 @@ <li><a href="#editor-media-document" data-toggle="tab">Document</a></li> <li><a href="#editor-media-icon" data-toggle="tab">Pictogram</a></li> <li><a href="#editor-media-video" data-toggle="tab">Video</a></li> - <li class="search" style="float: right;"> + <li class="search"> <ul class="pager mb0 mt0"> <li class="previous disabled"> <a href="#">← Previous</a> @@ -388,10 +163,11 @@ </li> </ul> </li> - <li class="search" style="float: right;"> + <li class="search"> <form action="#"> - <div class="form-group font-icons fa fa-search mb0"> + <div class="form-group"> <input class="form-control" id="icon-search" type="search"/> + <span class="fa fa-search"/> </div> </form> </li> @@ -399,15 +175,16 @@ <!-- Tab panes --> <div class="tab-content"> <div class="tab-pane fade in active" id="editor-media-image"/> + <div class="tab-pane fade" id="editor-media-document"/> <div class="tab-pane fade" id="editor-media-icon"/> <div class="tab-pane fade" id="editor-media-video"/> </div> </div> <div class="modal-footer"> - <button class="btn btn-primary save" type="button">Save</button> - <button class="btn hidden wait" disabled="disabled" type="button"/>or - - <a aria-hidden="true" data-dismiss="modal" href="#">Discard</a></div> + <button class="btn btn-sm btn-primary save" type="button">Save</button> + <button class="btn btn-sm hidden wait" disabled="disabled" type="button"/> + <a class="btn btn-sm btn-default" data-dismiss="modal" href="#" aria-hidden="true">Discard</a> + </div> </div> </div> </div> @@ -425,7 +202,7 @@ <input type="file" name="upload" t-att-accept="widget.accept" multiple="multiple" style="position: absolute; opacity: 0; width: 1px; height: 1px;"/> <input type="hidden" name="disable_optimization" value=""/> <div class="btn-group"> - <button class="btn btn-primary filepicker" type="button">Upload an image from your computer</button> + <button class="btn btn-primary filepicker" type="button">Upload an image</button> <button class="btn btn-primary dropdown-toggle" data-toggle="dropdown" type="button"> <span class="caret"/> <span class="sr-only">Alternate Upload</span> @@ -447,7 +224,7 @@ </div> </div> </div> - <input name="func" type="hidden"/> + <input type="hidden" name="func"/> <div class="help-block"/> <div class="existing-attachments"> <t t-call="web_editor.dialog.image.existing"/> @@ -457,11 +234,11 @@ <iframe class="hidden" name="fileframe" src="about:blank"/> </t> <t t-name="web_editor.dialog.image.existing"> - <div aria-hidden="true" class="modal" role="dialog" tabindex="-1"> + <div class="modal" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal-dialog select-image"> <div class="modal-content"> <div class="modal-header"> - <button aria-hidden="true" class="close" data-dismiss="modal" type="button">×</button> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 class="modal-title">Select a Picture</h3> </div> <div class="modal-body has-error"> @@ -469,7 +246,7 @@ <div class="help-block"/> </div> <div class="modal-footer"> - <a aria-hidden="true" data-dismiss="modal" href="#">Discard</a> + <a data-dismiss="modal" href="#" aria-hidden="true">Discard</a> </div> </div> </div> @@ -478,8 +255,8 @@ <t t-name="web_editor.dialog.image.existing.content"> <div class="existing-attachments"> <div class="row mt16" t-as="row" t-foreach="rows"> - <div class="col-sm-2 existing-attachment-cell" t-as="attachment" t-foreach="row"> - <i class="fa fa-times existing-attachment-remove" t-att-data-id="attachment.id"/> + <div class="col-sm-2 o_existing_attachment_cell" t-as="attachment" t-foreach="row"> + <i class="fa fa-times o_existing_attachment_remove" t-att-data-id="attachment.id"/> <div class="o_attachment_border"><div t-att-data-src="attachment.src" t-att-data-url="attachment.url" t-att-alt="attachment.name" t-att-title="attachment.name" t-att-data-id="attachment.id" t-att-data-mimetype="attachment.mimetype" class="o_image"/></div> </div> </div> @@ -499,39 +276,37 @@ </div> </t> <t t-name="web_editor.discard"> - <div aria-hidden="true" class="modal" role="dialog" tabindex="-1"> + <div class="modal o_website_modal" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> - <button aria-hidden="true" class="close" data-dismiss="modal" type="button">×</button> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 class="modal-title">Discard edition</h3> </div> <div class="modal-body"> - <p>If you discard the current edition, - <em>all</em> - unsaved changes will be lost.</p> + <p>If you discard the current edition, <em>all</em> unsaved changes will be lost.</p> <p>You can cancel to return to the edition mode.</p> </div> <div class="modal-footer"> - <button class="btn btn-danger" type="button">Discard</button>or - - <a aria-hidden="true" data-dismiss="modal" href="#">Cancel</a></div> + <button class="btn btn-sm btn-danger" type="button">Discard</button> + <a class="btn btn-sm btn-default" data-dismiss="modal" href="#" aria-hidden="true">Cancel</a> + </div> </div> </div> </div> </t> <t t-name="web_editor.dialog.alt"> <t t-call="web_editor.dialog"> - <t t-set="title">Change media Alt and Title</t> + <t t-set="title">Change media description and tooltip</t> <form action="#" class="form-horizontal"> <div class="form-group"> - <label class="col-sm-2 control-label" for="alt">Alt:</label> + <label class="col-sm-2 control-label" for="alt">Description</label> <div class="col-sm-9"> <input class="form-control" id="alt" required="required" t-att-value="widget.alt" type="text"/> </div> </div> <div class="form-group"> - <label class="col-sm-2 control-label" for="title">Title:</label> + <label class="col-sm-2 control-label" for="title">Tooltip</label> <div class="col-sm-9"> <input class="form-control" id="title" required="required" t-att-value="widget.title" type="text"/> </div> @@ -541,8 +316,8 @@ </t> <t t-name="web_editor.dialog.font-icons"> <form action="#"> - <input id="fa-icon" type="hidden"/> - <input id="fa-size" type="hidden" value="fa-1x"/> + <input type="hidden" id="fa-icon"/> + <input type="hidden" id="fa-size" value="fa-1x"/> <div class="font-icons-icons"> <t t-call="web_editor.dialog.font-icons.icons"> <t t-set="iconsParser" t-value="widget.iconsParser"/> @@ -691,4 +466,11 @@ </form> </t> </t> + <t t-name="web_editor.components.switch"> + <label class="o_switch" t-att-for="id"> + <input type="checkbox" t-att-id="id"/> + <span/> + <t t-if="label"><t t-esc="label"/></t> + </label> + </t> </templates> diff --git a/addons/web_editor/static/src/xml/snippets.xml b/addons/web_editor/static/src/xml/snippets.xml index 3ec1bf42308..cbf1e6bef70 100644 --- a/addons/web_editor/static/src/xml/snippets.xml +++ b/addons/web_editor/static/src/xml/snippets.xml @@ -13,36 +13,30 @@ </li> </t> - <t t-name="web_editor.snippets"> - <div id='web_editor_snippets' class="hidden-xs o_open"> - <div id="o_left_bar"></div> - </div> - </t> - <t t-name="web_editor.snippet_overlay"> <div class="oe_overlay"> + <div class="oe_handles"> + <div class="oe_handle n readonly"/> + <div class="oe_handle e readonly"/> + <div class="oe_handle w readonly"/> + <div class="oe_handle s readonly"/> + <div class="oe_handle size readonly"> + <div class="oe_handle_button auto_size">Auto Resize</div> + <div class="oe_handle_button size">Resize</div> + </div> + </div> <div class="oe_overlay_options" contentEditable="false"> <div class="btn-group"> - <a href="#" class="btn btn-default btn-sm oe_snippet_parent" title="Select Container Block"><i class="fa fa-upload"></i></a> + <a href="#" class="btn btn-default btn-sm oe_snippet_parent" title="Select Container Block"><i class="fa fa-upload"/></a> <div class="dropdown oe_options hidden btn-group"> - <a href="#" data-toggle="dropdown" class="btn btn-primary btn-sm" title="Customize">Customize </a> - <ul class="dropdown-menu" role="menu"></ul> + <a href="#" data-toggle="dropdown" class="btn btn-primary btn-sm" title="Customize">Customize</a> + <ul class="dropdown-menu" role="menu"/> </div> <a href="#" class="btn btn-default btn-sm oe_snippet_move" title="Drag to Move"><i class="fa fa-arrows ml4 mr4"></i></a> <a href="#" class="btn btn-default btn-sm oe_snippet_clone" title="Duplicate Container"><i class="fa fa-files-o ml4 mr4"></i></a> <a href="#" class="btn btn-default btn-sm oe_snippet_remove" title="Remove Block"><i class="fa fa-trash-o ml4 mr4"></i></a> </div> </div> - <div class='oe_handles'> - <div class='oe_handle n readonly'><div></div></div> - <div class='oe_handle e readonly'><div></div></div> - <div class='oe_handle w readonly'><div></div></div> - <div class='oe_handle size readonly'> - <div class="oe_handle_button auto_size">Auto Resize</div> - <div class="oe_handle_button size">Resize</div> - </div> - <div class='oe_handle s readonly'><div></div></div> - </div> </div> </t> @@ -50,8 +44,21 @@ <div class='oe_snippet_manipulator'></div> </t> - <!-- t-field options --> + <!-- options --> + <div t-name="web_editor.snippet.option.colorpicker" class="colorpicker"> + <div class="note-palette-title">Background Color</div> + <div class="btn-group palette-reset"> + <div class="note-color-reset" data-event="foreColor" data-value="inherit" title="None"> + <i class="fa fa-ban"/> + </div> + </div> + <div class="o_colorpicker_sections"> + <ul class="nav nav-pills o_colorpicker_section_menu"/> + <div class="tab-content o_colorpicker_section_tabs"/> + </div> + </div> + <!-- t-field options --> <t t-name="web_editor.many2one.button"> <div class="dropdown"> <a href="#" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" title="Search Contact"><i class="fa fa-search"></i></a> diff --git a/addons/web_editor/static/src/xml/translator.xml b/addons/web_editor/static/src/xml/translator.xml index 244fc67ec4a..c3aad20471c 100644 --- a/addons/web_editor/static/src/xml/translator.xml +++ b/addons/web_editor/static/src/xml/translator.xml @@ -1,14 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <templates id="template" xml:space="preserve"> -<t t-name="web_editor.translator"> - <div id="web_editor-top-edit" class="o_css_editor o_translate navbar navbar-inverse navbar-fixed-top hidden-xs"> - <form class="navbar-form text-muted"> - <button type="button" data-action="save" class="btn btn-primary btn-xs">Save</button> - <span>or</span> - <a href="#" data-action="cancel" class="btn btn-link btn-xs">Discard</a> - </form> - </div> +<t t-name="web_editor.translator" t-extend="web_editor.editorbar"> + <t t-jquery="#web_editor-top-edit"> + $(this).addClass('o_translate'); + </t> + <t t-jquery="#web_editor-toolbars" t-operation="replace"/> </t> <t t-name="web_editor.translator.attributes"> |
