From ae1687c6ab89f15ef95c479ac42f71a714c62e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Mon, 26 Aug 2013 16:39:21 +0200 Subject: [PATCH 1/9] [WIP] website: trying to make inpage snippet drag and drop work bzr revid: fva@openerp.com-20130826143921-myctckmddoexa1fv --- addons/website/static/src/js/website.js | 95 +++++++++++++++++-------- addons/website/views/views.xml | 4 +- 2 files changed, 66 insertions(+), 33 deletions(-) diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index c71126816c1..69797d44020 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -350,7 +350,9 @@ dataType: "text", success: function(snippets){ self.$el.html(snippets); - self.start_snippets(); + self.$('.oe_snippet').each(function(index,snippet){ + self.make_snippet_draggable($(snippet)); + }); }, }); @@ -364,26 +366,25 @@ return obj; }, - // setup widget and drag and drop - start_snippets: function(){ + // activate drag and drop for the snippets in the snippet toolbar + make_snippet_draggable: function($snippet){ var self = this; - - this.$('.oe_snippet').draggable({ + + $snippet.draggable({ helper: 'clone', zIndex: '1000', appendTo: 'body', start: function(){ - var snippet = $(this); - var action = snippet.data('action'); + var action = $snippet.data('action'); self.deactivate_snippet_manipulators(); if( action === 'insert'){ self.activate_insertion_zones({ - siblings: snippet.data('selector-siblings'), - childs: snippet.data('selector-childs') + siblings: $snippet.data('selector-siblings'), + childs: $snippet.data('selector-childs') }); }else if( action === 'mutate' ){ - self.activate_overlay_zones(snippet.data('selector')); + self.activate_overlay_zones($snippet.data('selector')); } $('.oe_drop_zone').droppable({ @@ -398,11 +399,13 @@ }, drop: function(){ if( action === 'insert' ){ - $(".oe_drop_zone.oe_hover") - .replaceWith(snippet.find('.oe_snippet_body').clone()) - .removeClass('oe_snippet_body'); + var $toInsert = $snippet.find('.oe_snippet_body').clone(); + $toInsert.removeClass('oe_snippet_body'); + $toInsert.addClass('oe_snippet_instance'); + $toInsert.data('snippet-id',$snippet.data('snippet-id')); + $(".oe_drop_zone.oe_hover").replaceWith($toInsert); }else if( action === 'mutate' ){ - self.path_eval(snippet.data('action-function'))( $(".oe_drop_zone.oe_hover").data('target') ); + self.path_eval($snippet.data('action-function'))( $(".oe_drop_zone.oe_hover").data('target') ); } }, }); @@ -412,8 +415,38 @@ self.activate_snippet_manipulators(); }, }); - }, + + // return the original snippet in the editor bar from a snippet id (string) + get_snippet_from_id: function(id){ + return $('.oe_snippet').filter(function(){ + return $(this).data('snippet-id') === id; + }).first(); + }, + // WIP + make_draggable_instance: function($instance){ + var self = this; + var $snippet = get_snippet_from_id($instance.data('snippet-id')); + + $instance.draggable({ + helper: 'clone', + zIndex: '1000', + appendTo: 'body', + start: function(){ + var action = $snippet.data('action'); + if(action === 'insert'){ + + self.deactivate_snippet_manipulators(); + self.activate_insertion_zones({ + siblings: $snippet.data('selector-siblings'), + child: $snippet.data('selector-childs') + }); + + } + } + }); + }, + // Create element insertion drop zones. two css selectors can be provided // selector.childs -> will insert drop zones as direct child of the selected elements // in case the selected elements have children themselves, dropzones will be interleaved @@ -490,6 +523,7 @@ $('.oe_drop_zone').remove(); }, + // generate drop zones covering the elements selected by the selector activate_overlay_zones: function(selector){ var $targets = $(selector); @@ -519,6 +553,8 @@ $zone.data('target',$target); } }, + + // puts $el at the same absolute position as $target cover_target: function($el, $target){ $el.css({ 'position': 'absolute', @@ -527,28 +563,25 @@ }); $el.css($target.offset()); }, + + // activates the manipulator boxes (with resizing handles) for all snippets activate_snippet_manipulators: function(){ var self = this; this.activate_overlay_zones('#wrap .container'); - var $snippets = $('.oe_drop_zone'); - - for(var i = 0, len = $snippets.length; i < len; i++){ - var $snippet = $snippets.eq(i); + + $('.oe_drop_zone').each(function(){; + + var $zone = $(this); + var $snippet = $zone.data('target'); var $manipulator = $(openerp.qweb.render('website.snippet_manipulator')); - $manipulator.css({ - 'top': $snippet.css('top'), - 'left': $snippet.css('left'), - 'width': $snippet.css('width'), - 'height': $snippet.css('height'), - }); - $manipulator.data('target',$snippet.data('target')); + + self.cover_target($manipulator, $zone); + $manipulator.data('target',$snippet); $manipulator.appendTo('body'); - $snippet.remove(); + $zone.remove(); $manipulator.find('.oe_handle').mousedown(function(event){ var $handle = $(this); - var $manipulator = $handle.parent(); - var $snippet = $manipulator.data('target'); var x = event.pageX; var y = event.pageY; @@ -568,11 +601,11 @@ $snippet.css('padding-top',pt-dy+'px'); self.cover_target($manipulator,$snippet); }else if($handle.hasClass('s') || $handle.hasClass('sw') || $handle.hasClass('se')){ - $snippet.css('padding-bottom',pb+dy+'px'); self.cover_target($manipulator,$snippet); } }); + $('body').mouseup(function(){ $('body').unbind('mousemove'); $('body').unbind('mouseup'); @@ -581,7 +614,7 @@ }); }); - } + }); }, deactivate_snippet_manipulators: function(){ $('.oe_snippet_manipulator').remove(); diff --git a/addons/website/views/views.xml b/addons/website/views/views.xml index b9cbe035d3d..41899b7ccfc 100644 --- a/addons/website/views/views.xml +++ b/addons/website/views/views.xml @@ -428,7 +428,7 @@ -
+
@@ -470,7 +470,7 @@
-
+
From c5fa23bffa8ccbc3db6a88be822c4f9209701ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Mon, 2 Sep 2013 11:10:05 +0200 Subject: [PATCH 2/9] [IMP] website editor: allow click trough on snippet manipulators bzr revid: fva@openerp.com-20130902091005-pnhb05fxk29z5q0r --- addons/website/static/src/css/editor.css | 38 +++++++++++++++++++---- addons/website/static/src/css/editor.sass | 32 ++++++++++++++++--- addons/website/static/src/js/website.js | 38 +++++++++++++++++++++-- addons/website/static/src/xml/website.xml | 4 +++ 4 files changed, 99 insertions(+), 13 deletions(-) diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 8a20e94bec0..3e678efe612 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -96,6 +96,7 @@ line-height: 120px; color: white; text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95); + box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset; } .oe_snippet .oe_snippet_body { display: none; @@ -161,31 +162,56 @@ .oe_snippet_manipulator { display: block; position: absolute; - background: rgba(153, 0, 255, 0.1); + background: transparent; border-radius: 3px; - border: solid 1px rgba(153, 0, 255, 0.5); opacity: 0; transition: opacity 100ms linear; + box-sizing: border-box; } .oe_snippet_manipulator > .oe_handle { display: none; } -.oe_snippet_manipulator:hover, .oe_snippet_manipulator.oe_hover { +.oe_snippet_manipulator.oe_hover { opacity: 1; + cursor: pointer; } -.oe_snippet_manipulator:hover > .oe_handle, .oe_snippet_manipulator.oe_hover > .oe_handle { +.oe_snippet_manipulator.oe_selected { + opacity: 1; + pointer-events: none; + background: transparent; + border: solid 1px rgba(153, 0, 255, 0.5); +} +.oe_snippet_manipulator.oe_selected > * { + pointer-events: auto; +} +.oe_snippet_manipulator.oe_selected > .oe_handle { display: block; } +.oe_snippet_manipulator .oe_buttons { + position: absolute; + right: 16px; + top: 16px; + background: #ad00ff; + color: white; + padding: 0px 2px; + border-radius: 3px; +} +.oe_snippet_manipulator .oe_buttons .oe_icon { + display: inline-block; + padding: 8px; + cursor: pointer; +} .oe_handle { + pointer-events: auto; position: absolute; top: 50%; left: 50%; display: block; background: white; - border: solid 1px rgba(153, 0, 255, 0.5); + border: solid 1px #9900ff; box-sizing: border-box; - border-radius: 2px; + border-radius: 8px; width: 16px; height: 16px; margin: -8px; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 29621f95e4b..feb7432821c 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -89,6 +89,7 @@ line-height: 120px color: white text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95) + box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset .oe_snippet_body display: none @@ -146,27 +147,48 @@ .oe_snippet_manipulator display: block position: absolute - background: rgba(153, 0, 255, 0.1) + background: transparent //rgba(153, 0, 255, 0.05) border-radius: 3px - border: solid 1px rgba(153, 0, 255, 0.5) opacity: 0 transition: opacity 100ms linear + box-sizing: border-box & > .oe_handle display: none - &:hover,&.oe_hover + &.oe_hover opacity: 1 + cursor: pointer + &.oe_selected + opacity: 1 + pointer-events: none + background: transparent + border: solid 1px rgba(153, 0, 255, 0.5) + & > * + pointer-events: auto & > .oe_handle display: block + .oe_buttons + position: absolute + right: 16px + top: 16px + background: rgb(173, 0, 255) + color: white + padding: 0px 2px + border-radius: 3px + .oe_icon + display: inline-block + padding: 8px + cursor: pointer .oe_handle + pointer-events: auto position: absolute top: 50% left: 50% display: block background: rgba(255, 255, 255, 1) - border: solid 1px rgba(153, 0, 255, 0.5) + border: solid 1px rgba(153, 0, 255, 1) box-sizing: border-box - border-radius: 2px + border-radius: 8px width: 16px height: 16px margin: -8px diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index 69797d44020..2969e52ff63 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -340,6 +340,9 @@ template: 'website.snippets', init: function () { this._super.apply(this, arguments); + if(!$('#oe_manipulators').length){ + $("
").appendTo('body'); + } }, start: function() { var self = this; @@ -549,7 +552,7 @@ var $target = $targets.eq(i); var $zone = $(zone_template); this.cover_target($zone,$target); - $zone.appendTo('body'); + $zone.appendTo('#oe_manipulators'); $zone.data('target',$target); } }, @@ -567,8 +570,13 @@ // activates the manipulator boxes (with resizing handles) for all snippets activate_snippet_manipulators: function(){ var self = this; + // we generate overlay drop zones only to get an idea of where the snippet are, the drop + // zones are replaced by manipulators this.activate_overlay_zones('#wrap .container'); + var $active_manipulator = null; + var locked = false; + $('.oe_drop_zone').each(function(){; var $zone = $(this); @@ -577,10 +585,35 @@ self.cover_target($manipulator, $zone); $manipulator.data('target',$snippet); - $manipulator.appendTo('body'); + $manipulator.appendTo('#oe_manipulators'); $zone.remove(); + $manipulator.mouseover(function(){ + if(!locked && $active_manipulator != $manipulator){ + if($active_manipulator){ + $active_manipulator.removeClass('oe_selected'); + } + $active_manipulator = $manipulator; + $manipulator.addClass('oe_selected'); + } + }); + /*$manipulator.mouseleave(function(){ + if(!locked && $active_manipulator){ + $active_manipulator.removeClass('oe_selected'); + $active_manipulator = null; + } + });*/ + /* + $manipulator.click(function(){ + if($active_manipulator === $manipulator){ + selected = !selected; + $manipulator.toggleClass('oe_selected',selected); + } + }); + */ + $manipulator.find('.oe_handle').mousedown(function(event){ + locked = true; var $handle = $(this); var x = event.pageX; var y = event.pageY; @@ -607,6 +640,7 @@ }); $('body').mouseup(function(){ + locked = false; $('body').unbind('mousemove'); $('body').unbind('mouseup'); self.deactivate_snippet_manipulators(); diff --git a/addons/website/static/src/xml/website.xml b/addons/website/static/src/xml/website.xml index 37a3bc453f3..ab2f622429c 100644 --- a/addons/website/static/src/xml/website.xml +++ b/addons/website/static/src/xml/website.xml @@ -69,6 +69,10 @@
+
+ + +
From 6f6c59ec1de1881a70e4bf728ea5d8b56bd609f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Mon, 2 Sep 2013 11:20:38 +0200 Subject: [PATCH 3/9] [IMP] website: made widget manipulator visible on dark areas bzr revid: fva@openerp.com-20130902092038-2dpxy9fidznaeclz --- addons/website/static/src/css/editor.css | 3 ++- addons/website/static/src/css/editor.sass | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 3e678efe612..987b229d91a 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -179,7 +179,8 @@ opacity: 1; pointer-events: none; background: transparent; - border: solid 1px rgba(153, 0, 255, 0.5); + border: dashed 1px rgba(153, 0, 255, 0.5); + box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset; } .oe_snippet_manipulator.oe_selected > * { pointer-events: auto; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index feb7432821c..2f236c38aab 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -161,7 +161,8 @@ opacity: 1 pointer-events: none background: transparent - border: solid 1px rgba(153, 0, 255, 0.5) + border: dashed 1px rgba(153, 0, 255, 0.5) + box-shadow: 0px 0px 0px 1px rgba(255,255,255,0.3), 0px 0px 0px 1px rgba(255,255,255,0.3) inset & > * pointer-events: auto & > .oe_handle From 4d1cc6a4ed499ddd8541fb37810a6728da66f9d1 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 6 Sep 2013 17:04:08 +0200 Subject: [PATCH 4/9] [WIP] website: snippets bzr revid: chm@openerp.com-20130906150408-81qzb2492lwqf7lb --- addons/website/static/src/css/snippets.css | 26 + .../website/static/src/js/website.editor.js | 6 + .../website/static/src/js/website.snippets.js | 470 +++++++++++++----- .../static/src/xml/website.snippets.xml | 21 + addons/website/views/views.xml | 19 +- 5 files changed, 403 insertions(+), 139 deletions(-) create mode 100644 addons/website/static/src/xml/website.snippets.xml diff --git a/addons/website/static/src/css/snippets.css b/addons/website/static/src/css/snippets.css index c9b726affde..1d99722e48b 100644 --- a/addons/website/static/src/css/snippets.css +++ b/addons/website/static/src/css/snippets.css @@ -176,3 +176,29 @@ margin-left: 0px !important; } */ + +#website-top-edit-snippet-option { + padding: 1px 8px 2px; + font: normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif; + float: left; + margin-top: 6px; + border: 1px solid #a6a6a6; + border-bottom-color: #979797; + background: #eeeeee; + border-radius: 3px; +} +#website-top-edit-snippet-option > * { + display: inline-block; + height: 22px; + padding: 4px 6px; + outline: 0; + border: 0; +} +#website-top-edit-snippet-option a.button .icon { + cursor: inherit; + background-repeat: no-repeat; + margin-top: 1px; + width: 16px; + height: 16px; + display: inline-block; +} \ 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 32e77acedab..b4c35042350 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -220,6 +220,11 @@ // editor.ui.items -> possible commands &al // editor.applyStyle(new CKEDITOR.style({element: "span",styles: {color: "#(color)"},overrides: [{element: "font",attributes: {color: null}}]}, {color: '#ff0000'})); + init: function (EditorBar) { + this.EditorBar = EditorBar; + this._super.apply(this, arguments); + }, + start_edition: function ($elements) { var self = this; $elements @@ -229,6 +234,7 @@ var $node = $(node); var editor = CKEDITOR.inline(this, self._config()); editor.on('instanceReady', function () { + self.trigger('instanceReady'); observer.observe(node, OBSERVER_CONFIG); }); $node.one('content_changed', function () { diff --git a/addons/website/static/src/js/website.snippets.js b/addons/website/static/src/js/website.snippets.js index 5ebf7aecc8d..8662bc1522d 100644 --- a/addons/website/static/src/js/website.snippets.js +++ b/addons/website/static/src/js/website.snippets.js @@ -2,84 +2,106 @@ 'use strict'; var website = openerp.website; + website.templates.push('/website/static/src/xml/website.snippets.xml'); + website.EditorBar.include({ events: _.extend({}, website.EditorBar.prototype.events, { 'click button[data-action=snippet]': 'snippet', }), start: function () { return this._super().then(function () { + this.$buttons.snippet = this.$('button[data-action=snippet]'); - window.snippets = this.snippets = new website.Snippets(); + window.snippets = this.snippets = new website.snippet.BuildingBlock(); this.snippets.appendTo($(document.body)); + }.bind(this)); }, + edit: function () { + var res = this._super.apply(this, arguments); + var self = this; + var instanceReady = false; + this.rte.on('instanceReady', this, function () { + clearTimeout(instanceReady); + instanceReady = setTimeout(function () { self.snippetBind(); }, 0); + }); + return res; + }, snippet: function (ev) { this.snippets.toggle(); }, - }); - website.RTE.include({ - start_edition: function ($elements) { - this.snippet_carousel(); - return this._super($elements); - }, - // TODO clean - snippet_carousel: function () { + snippetBind: function () { var self = this; - $('.carousel').each(function () { - var $carousel = $(this); - var $options = $('.js_carousel_options', $carousel); - $options.on('click', '.label', function (e) { - e.preventDefault(); - var $button = $(e.currentTarget); - var $c = $button.parents(".carousel:first"); - var $carousel_inner = $c.find('.carousel-inner'); + var $snipped_id = false; + var snipped_event_flag = false; + $("[data-snippet-id]") + .filter(function() { + return !!website.snippet.editorRegistry[$(this).data("snippet-id")]; + }) + .on('click', function (event) { + if (snipped_event_flag) { + return; + } - if($button.hasClass("js_add")) { - var cycle = $carousel_inner.find('.item').size(); - $carousel_inner.append(openerp.qweb.render('website.carousel')); - $c.carousel(cycle); + snipped_event_flag = true; + setTimeout(function () {snipped_event_flag = false;}, 0); + + if ($snipped_id && $snipped_id.get(0) == event.currentTarget) { + return; } - else if ($carousel_inner.find('.item').size() > 1) { - $carousel_inner - .find('.item.active').remove().end() - .find('.item:first').addClass('active'); - $c.carousel(0); - self.trigger('change', self, null); + + self.snippetblur($snipped_id); + + $snipped_id = $(event.currentTarget); + + if (typeof $snipped_id.data("snippet-editor") === 'undefined' && + website.snippet.editorRegistry[$snipped_id.data("snippet-id")]) { + $snipped_id.data("snippet-editor", new website.snippet.editorRegistry[$snipped_id.data("snippet-id")](self, $snipped_id)); + } + self.snippetFocus($snipped_id); + }); + $("body > :not(:has(#website-top-view))").on('click', function (ev) { + if (!snipped_event_flag && $snipped_id) { + self.snippetblur($snipped_id); + $snipped_id = false; } }); - $options.on('change', 'select[name="carousel-background"]', function () { - $('.carousel-inner .item.active', $carousel).css('background-image', 'url(' + $(this).val() + ')'); - $(this).val(""); - }); - $options.on('change', 'select[name="carousel-style"]', function () { - var $container = $('.carousel-inner .item.active .container', $carousel); - $('.content_image', $container).remove(); - switch ($(this).val()) { - case 'no_image': - $('.content', $container).attr("class", "content"); - break; - case 'image_left': - $('.content', $container).attr("class", "content col-md-6") - .before('
'); - break; - case 'image_right': - $('.content', $container).attr("class", "content col-md-6") - .after('
'); - break; - } - $(this).val(""); - }); - $options.show(); - }); + }, + + snippetblur: function ($snipped_id) { + if ($snipped_id) { + if ($snipped_id.data("snippet-editor")) { + $snipped_id.data("snippet-editor").onBlur(); + } + if ($snipped_id.data("snippet-view")) { + $snipped_id.data("snippet-view").onBlurEdit(); + } + } + }, + snippetFocus: function ($snipped_id) { + if ($snipped_id) { + if ($snipped_id.data("snippet-view")) { + $snipped_id.data("snippet-view").onFocusEdit(); + } + if ($snipped_id.data("snippet-editor")) { + $snipped_id.data("snippet-editor").onFocus(); + } + } }, }); /* ----- SNIPPET SELECTOR ---- */ - website.Snippets = openerp.Widget.extend({ + + website.snippet = {}; + + website.snippet.BuildingBlock = openerp.Widget.extend({ template: 'website.snippets', init: function () { this._super.apply(this, arguments); + if(!$('#oe_manipulators').length){ + $("
").appendTo('body'); + } }, start: function() { var self = this; @@ -90,7 +112,9 @@ dataType: "text", success: function(snippets){ self.$el.html(snippets); - self.start_snippets(); + self.$('.oe_snippet').each(function(index,snippet){ + self.make_snippet_draggable($(snippet)); + }); }, }); @@ -104,26 +128,25 @@ return obj; }, - // setup widget and drag and drop - start_snippets: function(){ + // activate drag and drop for the snippets in the snippet toolbar + make_snippet_draggable: function($snippet){ var self = this; - this.$('.oe_snippet').draggable({ + $snippet.draggable({ helper: 'clone', zIndex: '1000', appendTo: 'body', start: function(){ - var snippet = $(this); - var action = snippet.data('action'); + var action = $snippet.data('action'); self.deactivate_snippet_manipulators(); if( action === 'insert'){ self.activate_insertion_zones({ - siblings: snippet.data('selector-siblings'), - childs: snippet.data('selector-childs') + siblings: $snippet.data('selector-siblings'), + childs: $snippet.data('selector-childs') }); }else if( action === 'mutate' ){ - self.activate_overlay_zones(snippet.data('selector')); + self.activate_overlay_zones($snippet.data('selector')); } $('.oe_drop_zone').droppable({ @@ -138,11 +161,13 @@ }, drop: function(){ if( action === 'insert' ){ - $(".oe_drop_zone.oe_hover") - .replaceWith(snippet.find('.oe_snippet_body').clone()) - .removeClass('oe_snippet_body'); + var $toInsert = $snippet.find('.oe_snippet_body').clone(); + $toInsert.removeClass('oe_snippet_body'); + $toInsert.addClass('oe_snippet_instance'); + $toInsert.data('snippet-id',$snippet.data('snippet-id')); + $(".oe_drop_zone.oe_hover").replaceWith($toInsert); }else if( action === 'mutate' ){ - self.path_eval(snippet.data('action-function'))( $(".oe_drop_zone.oe_hover").data('target') ); + self.path_eval($snippet.data('action-function'))( $(".oe_drop_zone.oe_hover").data('target') ); } }, }); @@ -152,8 +177,38 @@ self.activate_snippet_manipulators(); }, }); - }, + + // return the original snippet in the editor bar from a snippet id (string) + get_snippet_from_id: function(id){ + return $('.oe_snippet').filter(function(){ + return $(this).data('snippet-id') === id; + }).first(); + }, + // WIP + make_draggable_instance: function($instance){ + var self = this; + var $snippet = get_snippet_from_id($instance.data('snippet-id')); + + $instance.draggable({ + helper: 'clone', + zIndex: '1000', + appendTo: 'body', + start: function(){ + var action = $snippet.data('action'); + if(action === 'insert'){ + + self.deactivate_snippet_manipulators(); + self.activate_insertion_zones({ + siblings: $snippet.data('selector-siblings'), + child: $snippet.data('selector-childs') + }); + + } + } + }); + }, + // Create element insertion drop zones. two css selectors can be provided // selector.childs -> will insert drop zones as direct child of the selected elements // in case the selected elements have children themselves, dropzones will be interleaved @@ -161,25 +216,23 @@ // selector.siblings -> will insert drop zones after and before selected elements activate_insertion_zones: function(selector){ var self = this; - var i, len, $zones; var child_selector = selector.childs || ''; var sibling_selector = selector.siblings || ''; var zone_template = "
"; - var $drop_zone = $('.oe_drop_zone'); - $drop_zone.remove(); + $('.oe_drop_zone').remove(); if(child_selector){ - $zones = $(child_selector); - for(i = 0, len = $zones.length; i < len; i++ ){ + var $zones = $(child_selector); + for( var i = 0, len = $zones.length; i < len; i++ ){ $zones.eq(i).find('> *:not(.oe_drop_zone)').after(zone_template); $zones.eq(i).prepend(zone_template); } } if(sibling_selector){ - $zones = $(sibling_selector); - for(i = 0, len = $zones.length; i < len; i++ ){ + var $zones = $(sibling_selector); + for( var i = 0, len = $zones.length; i < len; i++ ){ if($zones.eq(i).prev('.oe_drop_zone').length === 0){ $zones.eq(i).before(zone_template); } @@ -191,12 +244,12 @@ // Cleaning up unnecessary zones $('.oe_snippets .oe_drop_zone').remove(); // no zone in the snippet selector ... - $('#website-top-view').find('.oe_drop_zone').remove(); // no zone in the top bars ... - $('#website-top-edit').find('.oe_drop_zone').remove(); + $('#website-top-view .oe_drop_zone').remove(); // no zone in the top bars ... + $('#website-top-edit .oe_drop_zone').remove(); var count; do { count = 0; - $zones = $('.oe_drop_zone + .oe_drop_zone'); // no two consecutive zones + var $zones = $('.oe_drop_zone + .oe_drop_zone'); // no two consecutive zones count += $zones.length; $zones.remove(); @@ -206,23 +259,25 @@ }while(count > 0); // Cleaning up zones placed between floating or inline elements. We do not like these kind of zones. - $zones = $drop_zone; - for(i = 0, len = $zones.length; i < len; i++ ){ + var $zones = $('.oe_drop_zone'); + for( var i = 0, len = $zones.length; i < len; i++ ){ var zone = $zones.eq(i); var prev = zone.prev(); var next = zone.next(); - var float_prev = prev.css('float') || 'none'; - var float_next = next.css('float') || 'none'; - var disp_prev = prev.css('display') || null; - var disp_next = next.css('display') || null; + var float_prev = zone.prev().css('float') || 'none'; + var float_next = zone.next().css('float') || 'none'; + var disp_prev = zone.prev().css('display') || null; + var disp_next = zone.next().css('display') || null; if( (float_prev === 'left' || float_prev === 'right') && (float_next === 'left' || float_next === 'right') ){ zone.remove(); + continue; }else if( !( disp_prev === null || disp_next === null || disp_prev === 'block' || disp_next === 'block' )){ zone.remove(); + continue; } } }, @@ -230,9 +285,10 @@ $('.oe_drop_zone').remove(); }, + // generate drop zones covering the elements selected by the selector activate_overlay_zones: function(selector){ var $targets = $(selector); - + function is_visible($el){ return $el.css('display') != 'none' && $el.css('opacity') != '0' @@ -247,7 +303,7 @@ var parents = $(this).parents().filter(function(){ return !is_visible($(this)); }); return parents.length === 0; }); - + var zone_template = "
"; $('.oe_drop_zone').remove(); @@ -255,10 +311,12 @@ var $target = $targets.eq(i); var $zone = $(zone_template); this.cover_target($zone,$target); - $zone.appendTo('body'); + $zone.appendTo('#oe_manipulators'); $zone.data('target',$target); } }, + + // puts $el at the same absolute position as $target cover_target: function($el, $target){ $el.css({ 'position': 'absolute', @@ -267,62 +325,89 @@ }); $el.css($target.offset()); }, + + // activates the manipulator boxes (with resizing handles) for all snippets activate_snippet_manipulators: function(){ var self = this; + // we generate overlay drop zones only to get an idea of where the snippet are, the drop + // zones are replaced by manipulators this.activate_overlay_zones('#wrap .container'); - var $snippets = $('.oe_drop_zone'); - for(var i = 0, len = $snippets.length; i < len; i++){ - var $snippet = $snippets.eq(i); + var $active_manipulator = null; + var locked = false; + + $('.oe_drop_zone').each(function(){; + + var $zone = $(this); + var $snippet = $zone.data('target'); var $manipulator = $(openerp.qweb.render('website.snippet_manipulator')); - $manipulator.css({ - 'top': $snippet.css('top'), - 'left': $snippet.css('left'), - 'width': $snippet.css('width'), - 'height': $snippet.css('height'), + + self.cover_target($manipulator, $zone); + $manipulator.data('target',$snippet); + $manipulator.appendTo('#oe_manipulators'); + $zone.remove(); + + $manipulator.mouseover(function(){ + if(!locked && $active_manipulator != $manipulator){ + if($active_manipulator){ + $active_manipulator.removeClass('oe_selected'); + } + $active_manipulator = $manipulator; + $manipulator.addClass('oe_selected'); + } }); - $manipulator.data('target',$snippet.data('target')); - $manipulator.appendTo('body'); - $snippet.remove(); + /*$manipulator.mouseleave(function(){ + if(!locked && $active_manipulator){ + $active_manipulator.removeClass('oe_selected'); + $active_manipulator = null; + } + });*/ + /* + $manipulator.click(function(){ + if($active_manipulator === $manipulator){ + selected = !selected; + $manipulator.toggleClass('oe_selected',selected); + } + }); + */ $manipulator.find('.oe_handle').mousedown(function(event){ + locked = true; var $handle = $(this); - var $manipulator = $handle.parent(); - var $snippet = $manipulator.data('target'); var x = event.pageX; var y = event.pageY; var pt = $snippet.css('padding-top'); - var pb = $snippet.css('padding-bottom'); - pt = Number(pt.slice(0, -2)) || 0; //FIXME something cleaner to remove 'px' - pb = Number(pb.slice(0, -2)) || 0; - + var pb = $snippet.css('padding-bottom'); + pt = Number(pt.slice(0,pt.length - 2)) || 0; //FIXME something cleaner to remove 'px' + pb = Number(pb.slice(0,pb.length - 2)) || 0; + $manipulator.addClass('oe_hover'); event.preventDefault(); - $(document.body).on({ - mousemove: function(event){ - var dx = event.pageX - x; - var dy = event.pageY - y; - event.preventDefault(); - if($handle.hasClass('n') || $handle.hasClass('nw') || $handle.hasClass('ne')){ - $snippet.css('padding-top',pt-dy+'px'); - self.cover_target($manipulator,$snippet); - }else if($handle.hasClass('s') || $handle.hasClass('sw') || $handle.hasClass('se')){ - - $snippet.css('padding-bottom',pb+dy+'px'); - self.cover_target($manipulator,$snippet); - } - }, - mouseup: function(){ - $body.off('mouseup mousemove'); - self.deactivate_snippet_manipulators(); - self.activate_snippet_manipulators(); + $('body').mousemove(function(event){ + var dx = event.pageX - x; + var dy = event.pageY - y; + event.preventDefault(); + if($handle.hasClass('n') || $handle.hasClass('nw') || $handle.hasClass('ne')){ + $snippet.css('padding-top',pt-dy+'px'); + self.cover_target($manipulator,$snippet); + }else if($handle.hasClass('s') || $handle.hasClass('sw') || $handle.hasClass('se')){ + $snippet.css('padding-bottom',pb+dy+'px'); + self.cover_target($manipulator,$snippet); } }); - }); - } + $('body').mouseup(function(){ + locked = false; + $('body').unbind('mousemove'); + $('body').unbind('mouseup'); + self.deactivate_snippet_manipulators(); + self.activate_snippet_manipulators(); + }); + }); + + }); }, deactivate_snippet_manipulators: function(){ $('.oe_snippet_manipulator').remove(); @@ -337,4 +422,145 @@ } }, }); + + + website.snippet.viewRegistry = {}; + website.snippet.View = openerp.Class.extend({ + $: function () { + return this.$el.find.apply(this.$el, arguments); + }, + init: function (dom) { + this.$el = $(dom); + this._super.apply(this, arguments); + }, + /* onFocusEdit + * if they are an editor for this snippet-id + * Called before onFocus of snippet editor + */ + onFocusEdit : function () {}, + + /* onBlurEdit + * if they are an editor for this snippet-id + * Called after onBlur of snippet editor + */ + onBlurEdit : function () {}, + + /* getOptions + * get the options saved in the html view + */ + getOptions: function () { + var options = this.$el.data("snippet-options"); + return options ? JSON.parse(options) : undefined; + }, + }); + + + website.snippet.editorRegistry = {}; + website.snippet.Editor = openerp.Widget.extend({ + init: function (parent, dom) { + this.$target = $(dom); + this.parent = parent; + this._super.apply(this, arguments); + this.renderElement(); + this.start(); + }, + + renderElement: function() { + var $el; + if (this.template) { + $el = $(openerp.qweb.render(this.template, {widget: this}).trim()); + } else { + $el = this._make_descriptive(); + } + $el = $('
  • ').append($el); + this.replaceElement($el); + }, + + /* onFocus + * called when the user click inside the snippet dom + */ + onFocus : function () { + this.$el.prependTo(this.parent.$('#website-top-edit .nav.pull-right')); + }, + + /* onFocus + * called when the user click outide the snippet dom + */ + onBlur : function () { + this.$el.detach(); + }, + + /* setOptions + * saved the options in the html view + */ + setOptions: function (options) { + $target.attr("data-snippet-options", JSON.stringify(options)); + }, + + /* getOptions + * get the options saved in the html view + */ + getOptions: function () { + var options = this.$target.data("snippet-options"); + return options ? JSON.parse(options) : undefined; + }, + }); + + + website.snippet.editorRegistry.carousel = website.snippet.Editor.extend({ + template : "website.snippets.EditorBar.carousel", + start : function () { + var self = this; + + self.$(".js_add").on('click', function (e) { + e.preventDefault(); + var $inner = self.$target.find('.carousel-inner'); + var cycle = $inner.find('.item').size(); + $inner.append(openerp.qweb.render('website.carousel')); + self.$target.carousel(cycle); + }); + + + self.$(".js_remove").on('click', function (e) { + e.preventDefault(); + var $inner = self.$target.find('.carousel-inner'); + if ($inner.find('.item').size() > 1) { + $inner + .find('.item.active').remove().end() + .find('.item:first').addClass('active'); + self.$target.carousel(0); + } + }); + + + var bg = this.$target.find('.carousel-inner .item.active').css('background-image').replace(/url\((.*)\)/g, '\$1'); + this.$( 'select[name="carousel-background"] option[value="'+bg+'"], select[name="carousel-background"] option[value="'+bg.replace(window.location.protocol+'//'+window.location.host, '')+'"]') + .prop('selected', true); + self.$('select[name="carousel-background"]').on('change', function () { + self.$target.find('.carousel-inner .item.active').css('background-image', 'url(' + $(this).val() + ')'); + $(this).val(""); + }); + + + self.$('select[name="carousel-style"]').on('change', function () { + var $container = self.$target.find('.carousel-inner .item.active .container'); + $('.content_image', $container).remove(); + switch ($(this).val()) { + case 'no_image': + $('.content', $container).attr("class", "content"); + break; + case 'image_left': + $('.content', $container).attr("class", "content col-md-6") + .before('
    '); + break; + case 'image_right': + $('.content', $container).attr("class", "content col-md-6") + .after('
    '); + break; + } + $(this).val(""); + }); + } + }); + })(); diff --git a/addons/website/static/src/xml/website.snippets.xml b/addons/website/static/src/xml/website.snippets.xml new file mode 100644 index 00000000000..8665a1ce06e --- /dev/null +++ b/addons/website/static/src/xml/website.snippets.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/addons/website/views/views.xml b/addons/website/views/views.xml index 25f3869826e..a48068b82ed 100644 --- a/addons/website/views/views.xml +++ b/addons/website/views/views.xml @@ -156,24 +156,9 @@