diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index 6e33742803d..00b63e88f63 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -227,7 +227,7 @@ class Image(orm.AbstractModel): url = element.find('img').get('src') url_object = urlparse.urlsplit(url) - query = urlparse.parse_qs(url_object.query) + query = dict(urlparse.parse_qsl(url_object.query)) if url_object.path == '/website/image' and query['model'] == 'ir.attachment': attachment = self.pool['ir.attachment'].browse( cr, uid, int(query['id']), context=context) diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index 68e40e9118b..f10cb6f52e0 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -1,3 +1,4 @@ +@charset "utf-8"; /* THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY * * css for editor buttons, openerp widget included in the website and other @@ -434,7 +435,6 @@ ul.nav-stacked > li > a { .oe_dark { background: #eff8f8; background: rgba(100, 200, 200, 0.14); - background-image: none !important; -webkit-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset; -moz-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset; box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset; @@ -442,20 +442,17 @@ ul.nav-stacked > li > a { .oe_black { background-color: rgba(0, 0, 0, 0.9); - background-image: none !important; } .oe_green { background-color: rgba(0, 128, 0, 0.8); - background-image: none !important; } .oe_red { background-color: rgba(255, 0, 0, 0.8); - background-image: none !important; } -.oe_custom_bg { +.oe_img_bg { height: 100%; background-size: 100%; } diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index ec992256d6e..932f46a1912 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -328,17 +328,13 @@ ul.nav-stacked > li > a .oe_dark background: #eff8f8 background: rgba(100, 200, 200, 0.14) - background-image: none !important +box-shadow(0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset) .oe_black background-color: rgba(0, 0, 0, 0.9) - background-image: none !important .oe_green background-color: rgba(0, 128, 0, 0.8) - background-image: none !important .oe_red background-color: rgba(255, 0, 0, 0.8) - background-image: none !important -.oe_custom_bg +.oe_img_bg height: 100% - background-size: 100% + background-size: 100% \ No newline at end of file diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index feb8e403c54..e8544221b00 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -30,8 +30,8 @@ function link_dialog(editor) { return new website.editor.RTELinkDialog(editor).appendTo(document.body); } - function image_dialog(editor) { - return new website.editor.RTEImageDialog(editor).appendTo(document.body); + function image_dialog(editor, image) { + return new website.editor.RTEImageDialog(editor, image).appendTo(document.body); } // only enable editors manually @@ -53,7 +53,7 @@ && !element.data('cke-realelement') && !element.isReadOnly() && (element.data('oe-model') !== 'ir.ui.view')) { - image_dialog(editor); + image_dialog(editor, element); return; } @@ -69,13 +69,9 @@ }, null, null, 500); var previousSelection; - editor.on('selectionChange', function (evt) { - var selected = evt.data.path.lastElement; - if (previousSelection) { - // cleanup previous selection - $(previousSelection).next().remove(); - previousSelection = null; - } + $(editor.element.$).on('mouseenter', 'img', function () { + if (previousSelection) { return; } + var selected = new CKEDITOR.dom.element(this); if (!selected.is('img') || selected.data('cke-realelement') || selected.isReadOnly() @@ -84,13 +80,13 @@ } // display button - var $el = $(previousSelection = selected.$); + var $el = $((previousSelection = selected).$); var $btn = $('') .insertAfter($el) .click(function (e) { e.preventDefault(); e.stopPropagation(); - image_dialog(editor); + image_dialog(editor, selected); }); var position = $el.position(); @@ -99,6 +95,14 @@ top: $el.height() / 2 + position.top - $btn.outerHeight() / 2, left: $el.width() / 2 + position.left - $btn.outerWidth() / 2, }); + }).on('mouseleave', 'img', function (e) { + var $button = $(previousSelection.$).next('button'); + $button.css('visibility', 'hidden'); + var el = document.elementFromPoint(e.clientX, e.clientY); + $button.css('visibility', ''); + if (el === this) { return; } + $button.remove(); + previousSelection = null; }); editor.on('destroy', function (evt) { if (previousSelection) { @@ -1182,26 +1186,30 @@ }, }); website.editor.RTEImageDialog = website.editor.ImageDialog.extend({ - init: function () { - this._super.apply(this, arguments); + init: function (editor, image) { + this._super(editor); + + this.element = image; this.on('start', this, this.proxy('started')); this.on('save', this, this.proxy('saved')); }, started: function (holder) { - var selection = this.editor.getSelection(); - var el = selection && selection.getSelectedElement(); - this.element = null; - - if (el && el.is('img')) { - this.element = el; - _(this.image_styles).each(function (style) { - if (el.hasClass(style)) { - holder.style = style; - } - }); - holder.url = el.getAttribute('src'); + if (!this.element) { + var selection = this.editor.getSelection(); + this.element = selection && selection.getSelectedElement(); } + + var el = this.element; + if (!el || !el.is('img')) { + return; + } + _(this.image_styles).each(function (style) { + if (el.hasClass(style)) { + holder.style = style; + } + }); + holder.url = el.getAttribute('src'); }, saved: function (data) { var element, editor = this.editor; diff --git a/addons/website/static/src/js/website.snippets.animation.js b/addons/website/static/src/js/website.snippets.animation.js index 7277f80ba95..34edfe9d259 100644 --- a/addons/website/static/src/js/website.snippets.animation.js +++ b/addons/website/static/src/js/website.snippets.animation.js @@ -82,7 +82,7 @@ var self = this; var speed = parseFloat(self.$target.attr("data-scroll-background-ratio") || 0); - if (speed == 1) { + if (speed === 1 || this.$target.css("background-image") === "none") { this.$target.css("background-attachment", "fixed").css("background-position", "0px 0px"); return; } else { @@ -101,7 +101,7 @@ } else { offset = - self.$target.offset().top * speed; } - self.$target.attr("data-scroll-background-offset", offset); + self.$target.attr("data-scroll-background-offset", offset > 0 ? 0 : offset); $(window).scroll(); }; img.src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)/g, ""); diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 7d7aade081f..a4f51c6ab39 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -77,6 +77,7 @@ hack_to_add_snippet_id(); }); + website.snippet.styles = {}; website.snippet.selector = []; website.snippet.BuildingBlock = openerp.Widget.extend({ template: 'website.snippets', @@ -138,22 +139,19 @@ }, fetch_snippet_templates: function () { var self = this; - this.style_templates = {}; openerp.jsonRpc("/website/snippets", 'call', {}) .then(function (html) { var $html = $(html); - var $styles = $html.find("#snippet_styles"); - $styles.find("> [data-snippet-id]").each(function () { + var $styles = $html.find("[data-snippet-style-id]"); + $styles.each(function () { var $style = $(this); - var snipped_id = $style.data('snippet-id'); - self.style_templates[snipped_id] = { - 'snipped-id' : snipped_id, + var style_id = $style.data('snippet-style-id'); + website.snippet.styles[style_id] = { + 'snippet-style-id' : style_id, 'selector': $style.data('selector'), - 'class': $style.data("class"), - 'label': $style.find(".oe_snippet_label").text(), - 'group': $style.find(".oe_snippet_group").text() + '$el': $style, }; }); $styles.addClass("hidden"); @@ -529,13 +527,212 @@ }); + website.snippet.styleRegistry = {}; + website.snippet.StyleEditor = openerp.Class.extend({ + // initialisation (don't overwrite) + init: function (parent, $target, snippet_id) { + this.parent = parent; + this.$target = $target; + var styles = this.$target.data("snippet-style-ids") || {}; + styles[snippet_id] = this; + this.$target.data("snippet-style-ids", styles); + this.$overlay = this.$target.data('overlay'); + this['snippet-style-id'] = snippet_id; + this.$el = website.snippet.styles[snippet_id].$el.find(">li").clone(); + + this.required = this.$el.data("required"); + + this.set_active(); + this.$el.find('li[data-class] a').on('mouseover mouseout click', _.bind(this._mouse, this)); + this.$target.on('snippet-style-reset', _.bind(this.set_active, this)); + + this.start(); + }, + _mouse: function (event) { + var self = this; + + if (event.type === 'mouseout') { + if (!this.over) return; + this.over = false; + } else if (event.type === 'click') { + this.over = false; + }else { + this.over = true; + } + + var $prev, $next; + if (event.type === 'mouseout') { + $prev = $(event.currentTarget).parent(); + $next = this.$el.find("li[data-class].active"); + } else { + $prev = this.$el.find("li[data-class].active"); + $next = $(event.currentTarget).parent(); + } + if (!$prev.length) { + $prev = false; + } + if ($prev && $prev[0] === $next[0]) { + $next = false; + if (this.required) { + return; + } + } + + var np = {'$next': $next, '$prev': $prev}; + + if (event.type === 'click') { + setTimeout(function () { + self.set_active(); + self.$target.trigger("snippet-style-change", [self, np]); + },0); + this.select(event, {'$next': $next, '$prev': $prev}); + } else { + setTimeout(function () { + self.$target.trigger("snippet-style-preview", [self, np]); + },0); + this.preview(event, np); + } + }, + // start is call just after the init + start: function () { + }, + /* select + * called when a user select an item + * variables: np = {$next, $prev} + * $next is false if they are no next item selected + * $prev is false if they are no previous item selected + */ + select: function (event, np) { + var self = this; + // add or remove html class + if (np.$prev) { + this.$target.removeClass(np.$prev.data('class' || "")); + } + if (np.$next) { + this.$target.addClass(np.$next.data('class') || ""); + } + }, + /* preview + * called when a user is on mouse over or mouse out of an item + * variables: np = {$next, $prev} + * $next is false if they are no next item selected + * $prev is false if they are no previous item selected + */ + preview: function (event, np) { + var self = this; + + // add or remove html class + if (np.$prev) { + this.$target.removeClass(np.$prev.data('class') || ""); + } + if (np.$next) { + this.$target.addClass(np.$next.data('class') || ""); + } + }, + /* set_active + * select and set item active or not (add highlight item and his parents) + * called before start + */ + set_active: function () { + var self = this; + this.$el.find('li').removeClass("active"); + var $active = this.$el.find('li[data-class]') + .filter(function () { + var $li = $(this); + return ($li.data('class') && self.$target.hasClass($li.data('class'))); + }) + .first() + .addClass("active"); + this.$el.find('li:has(li[data-class].active)').addClass("active"); + } + }); + + + website.snippet.styleRegistry['size'] = website.snippet.StyleEditor.extend({ + select: function(event, np) { + this._super(event, np); + this.parent.parent.cover_target(this.$overlay, this.$target); + }, + preview: function (event, np) { + this._super(event, np); + this.parent.parent.cover_target(this.$overlay, this.$target); + } + }); + + website.snippet.styleRegistry.background = website.snippet.StyleEditor.extend({ + _get_bg: function () { + return this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, ""); + }, + _set_bg: function (src) { + this.$target.css("background-image", src && src !== "" ? 'url(' + src + ')' : ""); + }, + start: function () { + this._super(); + var src = this._get_bg(); + this.$el.find("li[data-class].active.oe_custom_bg").data("src", src); + }, + select: function(event, np) { + var self = this; + this._super(event, np); + if (np.$next) { + if (np.$next.hasClass("oe_custom_bg")) { + var editor = new website.editor.ImageDialog(); + editor.on('start', self, function (o) {o.url = np.$prev && np.$prev.data("src") || "";}); + editor.on('save', self, function (o) { + self._set_bg(o.url); + np.$next.data("src", o.url); + self.$target.trigger("snippet-style-change", [self, np]); + }); + editor.on('cancel', self, function () { + if (!np.$prev || np.$prev.data("src") === "") { + np.$next.removeClass('active'); + self.$target.removeClass(np.$next.data("class")); + self.$target.trigger("snippet-style-change", [self, np]); + } + }); + editor.appendTo($('body')); + } else { + this._set_bg(np.$next.data("src")); + } + } else { + this._set_bg(false); + } + }, + preview: function (event, np) { + this._super(event, np); + if (np.$next) { + this._set_bg(np.$next.data("src")); + } + }, + set_active: function () { + var self = this; + var bg = self.$target.css("background-image"); + this.$el.find('li').removeClass("active"); + var $active = this.$el.find('li[data-class]') + .filter(function () { + var $li = $(this); + return ($li.data('src') && bg.indexOf($li.data('src')) >= 0) || + (!$li.data('src') && self.$target.hasClass($li.data('class'))); + }) + .first(); + if (!$active.length) { + $active = this.$target.css("background-image") !== 'none' ? + this.$el.find('li[data-class].oe_custom_bg') : + this.$el.find('li[data-class=""]'); + } + $active.addClass("active"); + this.$el.find('li:has(li[data-class].active)').addClass("active"); + } + }); + + website.snippet.editorRegistry = {}; website.snippet.Editor = openerp.Class.extend({ - init: function (parent, dom, force_snippet_id) { + init: function (parent, dom) { this.parent = parent; this.$target = $(dom); this.$overlay = this.$target.data('overlay'); - this.snippet_id = force_snippet_id || this.$target.data("snippet-id"); + this.snippet_id = this.$target.data("snippet-id"); this._readXMLData(); this.load_style_options(); this.get_parent_block(); @@ -652,90 +849,21 @@ }, load_style_options: function () { - if (this.$target.data('snippetStyles')) { - return; - } - var self = this; var $styles = this.$overlay.find('.oe_options'); var $ul = $styles.find('ul:first'); - _.each(this.parent.style_templates, function (val) { + _.each(website.snippet.styles, function (val) { if (!self.parent.dom_filter(val.selector).is(self.$target)) { return; } - var $li = $("
  • ").data(val); - $li.append($('').text(val.label)); - if (self.$target.hasClass( val.class )) { - $li.addClass("active"); - } - - if (val.group.length) { - var $group = $ul.find("li.dropdown-submenu").filter(function () { - return $("a:first", this).text() === val.group; - }); - if (!$group.length) { - $group = $('