summaryrefslogtreecommitdiff
path: root/addons/website/static/src
diff options
context:
space:
mode:
Diffstat (limited to 'addons/website/static/src')
-rw-r--r--addons/website/static/src/css/snippets.css10
-rw-r--r--addons/website/static/src/css/snippets.sass9
-rw-r--r--addons/website/static/src/css/website.css108
-rw-r--r--addons/website/static/src/css/website.sass96
-rw-r--r--addons/website/static/src/js/website.editor.js4
-rw-r--r--addons/website/static/src/js/website.snippets.animation.js77
-rw-r--r--addons/website/static/src/js/website.snippets.editor.js953
-rw-r--r--addons/website/static/src/js/website.snippets.gallery.js348
-rw-r--r--addons/website/static/src/js/website.theme.js15
-rw-r--r--addons/website/static/src/xml/website.editor.xml2
-rw-r--r--addons/website/static/src/xml/website.gallery.xml102
-rw-r--r--addons/website/static/src/xml/website.snippets.xml11
-rw-r--r--addons/website/static/src/xml/website.theme.xml159
13 files changed, 1200 insertions, 694 deletions
diff --git a/addons/website/static/src/css/snippets.css b/addons/website/static/src/css/snippets.css
index a1c42b0b814..ebe87769794 100644
--- a/addons/website/static/src/css/snippets.css
+++ b/addons/website/static/src/css/snippets.css
@@ -46,6 +46,7 @@
display: inline-block;
white-space: nowrap;
background: black;
+ margin-right: 60px;
}
#oe_snippets .tab-content > div {
background: black;
@@ -119,6 +120,12 @@
.oe_snippet > :not(.oe_snippet_thumbnail) {
display: none !important;
}
+.oe_snippet.disable .oe_snippet_thumbnail {
+ background-color: rgba(255, 0, 0, 0.75);
+}
+.oe_snippet.disable .oe_snippet_thumbnail_img {
+ opacity: 0.8;
+}
#oe_snippets .oe_snippet_thumbnail {
background: #747474, -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 100, color-stop(0%, rgba(0, 0, 0, 0.25)), color-stop(100%, rgba(0, 0, 0, 0.4)));
@@ -381,6 +388,9 @@
float: none;
margin: 0 -3px;
}
+.oe_overlay .oe_overlay_options > .btn-group > div {
+ float: none;
+}
.oe_overlay .oe_overlay_options .btn, .oe_overlay .oe_overlay_options a {
cursor: pointer;
}
diff --git a/addons/website/static/src/css/snippets.sass b/addons/website/static/src/css/snippets.sass
index 71613f8dfbe..a94b8665e0c 100644
--- a/addons/website/static/src/css/snippets.sass
+++ b/addons/website/static/src/css/snippets.sass
@@ -43,6 +43,7 @@
display: inline-block
white-space: nowrap
background: black
+ margin-right: 60px
> div
background: rgb(0,0,0)
label
@@ -87,7 +88,11 @@
line-height: 18px
& > :not(.oe_snippet_thumbnail)
display: none !important
-
+ &.disable
+ .oe_snippet_thumbnail
+ background-color: rgba(255, 0, 0, 0.75)
+ .oe_snippet_thumbnail_img
+ opacity: 0.8
#oe_snippets .oe_snippet_thumbnail
@include background(#747474, radial-gradient(rgba(0,0,0,0.25),rgba(0,0,0,0.4)))
@@ -286,6 +291,8 @@
display: inline-block
float: none
margin: 0 -3px
+ > div
+ float: none
.btn, a
cursor: pointer
.dropdown
diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css
index c5d403bfdda..055ac7fb4e3 100644
--- a/addons/website/static/src/css/website.css
+++ b/addons/website/static/src/css/website.css
@@ -356,6 +356,114 @@ ul.nav-stacked > li > a {
padding: 4px 0;
}
+/* gallery */
+.o_gallery.o_grid.o_spc-none div.row, .o_gallery.o_masonry.o_spc-none div.row {
+ margin: 0;
+}
+.o_gallery.o_grid.o_spc-none div.row > *, .o_gallery.o_masonry.o_spc-none div.row > * {
+ padding: 0;
+}
+.o_gallery.o_grid.o_spc-small div.row, .o_gallery.o_masonry.o_spc-small div.row {
+ margin: 5px 0;
+}
+.o_gallery.o_grid.o_spc-small div.row > *, .o_gallery.o_masonry.o_spc-small div.row > * {
+ padding: 0 5px;
+}
+.o_gallery.o_grid.o_spc-medium div.row, .o_gallery.o_masonry.o_spc-medium div.row {
+ margin: 10px 0;
+}
+.o_gallery.o_grid.o_spc-medium div.row > *, .o_gallery.o_masonry.o_spc-medium div.row > * {
+ padding: 0 10px;
+}
+.o_gallery.o_grid.o_spc-big div.row, .o_gallery.o_masonry.o_spc-big div.row {
+ margin: 15px 0;
+}
+.o_gallery.o_grid.o_spc-big div.row > *, .o_gallery.o_masonry.o_spc-big div.row > * {
+ padding: 0 15px;
+}
+.o_gallery.o_grid .img, .o_gallery.o_masonry .img {
+ width: 100%;
+}
+.o_gallery.o_grid.size-auto .row {
+ height: auto;
+}
+.o_gallery.o_grid.size-small .row {
+ height: 100px;
+}
+.o_gallery.o_grid.size-medium .row {
+ height: 250px;
+}
+.o_gallery.o_grid.size-big .row {
+ height: 400px;
+}
+.o_gallery.o_grid.size-small .img, .o_gallery.o_grid.size-medium .img, .o_gallery.o_grid.size-big .img {
+ height: 100%;
+}
+.o_gallery.o_nomode.o_spc-none .img {
+ padding: 0;
+}
+.o_gallery.o_nomode.o_spc-small .img {
+ padding: 5px;
+}
+.o_gallery.o_nomode.o_spc-medium .img {
+ padding: 10px;
+}
+.o_gallery.o_nomode.o_spc-big .img {
+ padding: 15px;
+}
+.o_gallery.o_slideshow .carousel ul.carousel-indicators li {
+ border: 1px solid #aaaaaa;
+}
+.o_gallery .carousel-inner .item img {
+ max-width: none;
+}
+
+.o_gallery.o_slideshow > .container {
+ height: 100%;
+}
+
+.o_gallery.o_slideshow .carousel, .modal-body.o_slideshow .carousel {
+ height: 100%;
+}
+.o_gallery.o_slideshow .carousel img, .modal-body.o_slideshow .carousel img {
+ max-height: 100%;
+ max-width: 100%;
+ margin: auto;
+ position: relative;
+ top: 50%;
+ -webkit-transform: translateY(-50%);
+ -ms-transform: translateY(-50%);
+ transform: translateY(-50%);
+}
+.o_gallery.o_slideshow .carousel ul.carousel-indicators, .modal-body.o_slideshow .carousel ul.carousel-indicators {
+ display: block;
+ height: auto;
+ padding: 0;
+ border-width: 0;
+ position: absolute;
+ bottom: 0;
+}
+.o_gallery.o_slideshow .carousel ul.carousel-indicators li, .modal-body.o_slideshow .carousel ul.carousel-indicators li {
+ list-style-image: none;
+ display: inline-block;
+ width: 35px;
+ height: 35px;
+ margin: 0 0px 5px 5px;
+ padding: 0;
+ border: 1px solid #aaaaaa;
+ text-indent: initial;
+ background-size: cover;
+ opacity: 0.5;
+ background-color: black;
+}
+.o_gallery.o_slideshow .carousel ul.carousel-indicators li.active, .modal-body.o_slideshow .carousel ul.carousel-indicators li.active {
+ opacity: 1;
+}
+.o_gallery.o_slideshow .carousel .carousel-control.left, .o_gallery.o_slideshow .carousel .carousel-control.right, .modal-body.o_slideshow .carousel .carousel-control.left, .modal-body.o_slideshow .carousel .carousel-control.right {
+ background-image: none;
+ background-color: transparent;
+}
+
/* Parallax Theme */
div.carousel .carousel-indicators li {
border: 1px solid grey;
diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass
index f4cf73437f8..ba47a12c90f 100644
--- a/addons/website/static/src/css/website.sass
+++ b/addons/website/static/src/css/website.sass
@@ -285,6 +285,102 @@ ul.nav-stacked > li > a
.hr
padding: 4px 0
+/* gallery */
+
+.o_gallery
+ &.o_grid, &.o_masonry
+ &.o_spc-none
+ div.row
+ margin: 0
+ div.row > *
+ padding: 0
+ &.o_spc-small
+ div.row
+ margin: 5px 0
+ div.row > *
+ padding: 0 5px
+ &.o_spc-medium
+ div.row
+ margin: 10px 0
+ div.row > *
+ padding: 0 10px
+ &.o_spc-big
+ div.row
+ margin: 15px 0
+ div.row > *
+ padding: 0 15px
+ .img
+ width: 100%
+ &.o_grid
+ &.size-auto .row
+ height: auto
+ &.size-small .row
+ height: 100px
+ &.size-medium .row
+ height: 250px
+ &.size-big .row
+ height: 400px
+ &.size-small, &.size-medium, &.size-big
+ .img
+ height: 100%
+ &.o_nomode
+ &.o_spc-none
+ .img
+ padding: 0
+ &.o_spc-small
+ .img
+ padding: 5px
+ &.o_spc-medium
+ .img
+ padding: 10px
+ &.o_spc-big
+ .img
+ padding: 15px
+
+ &.o_slideshow .carousel ul.carousel-indicators li
+ border: 1px solid #aaa
+ .carousel-inner .item img
+ max-width: none
+
+.o_gallery.o_slideshow > .container
+ height: 100%
+
+.o_gallery.o_slideshow .carousel, .modal-body.o_slideshow .carousel
+ height: 100%
+ img
+ max-height: 100%
+ max-width: 100%
+ margin: auto
+ position: relative
+ top: 50%
+ -webkit-transform: translateY(-50%)
+ -ms-transform: translateY(-50%)
+ transform: translateY(-50%)
+ ul.carousel-indicators
+ display: block
+ height: auto
+ padding: 0
+ border-width: 0
+ position: absolute
+ bottom: 0
+ li
+ list-style-image: none
+ display: inline-block
+ width: 35px
+ height: 35px
+ margin: 0 0px 5px 5px
+ padding: 0
+ border: 1px solid #aaa
+ text-indent: initial
+ background-size: cover
+ opacity: 0.5
+ background-color: #000
+ li.active
+ opacity: 1
+ .carousel-control.left, .carousel-control.right
+ background-image: none
+ background-color: transparent
+
/* Parallax Theme */
div.carousel
diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js
index 9b86002626c..205d8dde076 100644
--- a/addons/website/static/src/js/website.editor.js
+++ b/addons/website/static/src/js/website.editor.js
@@ -1568,9 +1568,9 @@
}
var callback = _.uniqueId('func_');
this.$('input[name=func]').val(callback);
- window[callback] = function (url, error) {
+ window[callback] = function (attachments, error) {
delete window[callback];
- self.file_selected(url, error);
+ self.file_selected(attachments[0]['website_url'], error);
};
},
file_selection: function () {
diff --git a/addons/website/static/src/js/website.snippets.animation.js b/addons/website/static/src/js/website.snippets.animation.js
index c72e2c3b84e..785027795ce 100644
--- a/addons/website/static/src/js/website.snippets.animation.js
+++ b/addons/website/static/src/js/website.snippets.animation.js
@@ -1,7 +1,9 @@
(function () {
'use strict';
+
var website = openerp.website;
+
if (!website.snippet) website.snippet = {};
website.snippet.readyAnimation = [];
@@ -37,7 +39,14 @@
}
});
};
+
+
$(document).ready(function () {
+ if ($(".o_gallery:not(.oe_slideshow)").size()) {
+ // load gallery modal template
+ website.add_template_file('/website/static/src/xml/website.gallery.xml');
+ }
+
website.snippet.start_animation();
});
@@ -69,7 +78,7 @@
website.snippet.animationRegistry.slider = website.snippet.Animation.extend({
selector: ".carousel",
start: function () {
- this.$target.carousel({interval: 10000});
+ this.$target.carousel();
},
stop: function () {
this.$target.carousel('pause');
@@ -154,4 +163,70 @@
this.$target.html('<div class="css_editable_mode_display">&nbsp;</div><iframe src="'+this.$target.data("src")+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>');
},
});
+
+ /* -------------------------------------------------------------------------
+ Gallery Animation
+
+ This ads a Modal window containing a slider when an image is clicked
+ inside a gallery
+ -------------------------------------------------------------------------*/
+ website.snippet.animationRegistry.gallery = website.snippet.Animation.extend({
+ selector: ".o_gallery:not(.o_slideshow)",
+ start: function() {
+ var self = this;
+ this.$el.on("click", "img", this.click_handler);
+ },
+ click_handler : function(event) {
+ var self = this;
+ var $cur = $(event.currentTarget);
+ var edition_mode = ($cur.closest("[contenteditable='true']").size() !== 0);
+
+ // show it only if not in edition mode
+ if (!edition_mode) {
+ var urls = [],
+ idx = undefined,
+ milliseconds = undefined,
+ params = undefined,
+ $images = $cur.closest(".o_gallery").find("img"),
+ dimensions = {
+ min_width : Math.round( window.innerWidth * 0.7),
+ min_height : Math.round( window.innerHeight * 0.7),
+ max_width : Math.round( window.innerWidth * 0.7),
+ max_height : Math.round( window.innerHeight * 0.7),
+ height : Math.round( window.innerHeight * 0.7)
+ };
+
+ $images.each(function() {
+ urls.push($(this).attr("src"));
+ });
+ var $img = ($cur.is("img") === true) ? $cur : $cur.closest("img");
+ idx = urls.indexOf($img.attr("src"));
+
+ milliseconds = $cur.closest(".o_gallery").data("interval") || false;
+ var params = {
+ srcs : urls,
+ index: idx,
+ dim : dimensions,
+ interval : milliseconds,
+ id: _.uniqueId("slideshow_")
+ };
+ var $modal = $(openerp.qweb.render('website.gallery.slideshow.lightbox', params));
+ $modal.modal({
+ keyboard : true,
+ backdrop : true
+ });
+ $modal.on('hidden.bs.modal', function() {
+ $(this).hide();
+ $(this).siblings().filter(".modal-backdrop").remove(); // bootstrap leaves a modal-backdrop
+ $(this).remove();
+
+ });
+ $modal.find(".modal-content, .modal-body.o_slideshow").css("height", "100%");
+
+ $modal.appendTo(document.body);
+ $modal.find(".carousel").carousel();
+ }
+ } // click_handler
+ });
+
})();
diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js
index 4630206748f..c4ca26f2f74 100644
--- a/addons/website/static/src/js/website.snippets.editor.js
+++ b/addons/website/static/src/js/website.snippets.editor.js
@@ -1,135 +1,6 @@
(function () {
'use strict';
-/* Building block / Snippet Editor
-
- The building blocks appear in the edit bar website. These prebuilt html block
- allowing the designer to easily generate content on a page (drag and drop).
- Options allow snippets to add customizations part html code according to their
- selector (jQuery) and javascript object.
-
- How to create content?
-
- Designers can add their own html block in the "snippets" (/website/views/snippets.xml).
- The block must be added in one of four menus (structure, content, feature or effect).
- Structure:
- <div>
- <div class="oe_snippet_thumbnail">
- <img class="oe_snippet_thumbnail_img" src="...image src..."/>
- <span class="oe_snippet_thumbnail_title">...Block Name...</span>
- </div>
- <div class="oe_snippet_body">
- ...
- <!--
- The block with class 'oe_snippet_body' is inserted in the page.
- This class is removed when the block is dropped.
- The block can be made of any html tag and content. -->
- </div>
- </div>
-
- How to create options?
-
- Designers can add their own html block in the "snippet_options" (/website/views/snippets.xml).
- Structure:
-
- <div data-snippet-option-id='...' <!-- Required: javascript object id (but javascript
- for this option object is not required) -->
- data-selector="..." <!-- Required: jQuery selector.
- Apply options on all The part of html who
- match with this jQuery selector.
- E.g.: If the selector is div, all div will be selected
- and can be highlighted and assigned an editor. -->
- data-selector-siblings="..." <!-- Optional: jQuery selector.
- The html part can be insert or move beside
- the selected html block -->
- data-selector-children="..." <!-- Optional: jQuery selector.
- The html part can be insert or move inside
- the selected html block -->
- data-selector-vertical-children='...'> <!-- Optional: jQuery selector.
- The html part can be insert or move inside
- the selected html block. The drop zone is
- displayed vertically -->
- ...
- <li><a href="#">...</a></li> <!-- Optional: html li list.
- List of menu items displayed in customize
- menu. If the li tag have 'data-class', the
- class is automaticcally added or removed to
- the html content when the user select this item. -->
- ...
- <li class="dropdown-submenu" <!-- Optional: html li list exemple. !-->
- data-required="true"> <!-- Optional: if only one item can be selected
- and can't be unselect. !-->
- <a tabindex="-1" href="#">...</a> <!-- bootstrap dropdown button !-->
- <ul class="dropdown-menu">
- <li data-value="text_only"><a>...</a></li> <!-- by default data-value is apply
- like a class to html block !-->
- </ul>
- </li>
- </div>
-
- How to create a javascript object for an options?
-
- openerp.website.snippet.options["...option-id..."] = website.snippet.Option.extend({
- // start is called when the user click into a block or when the user drop a block
- // into the page (just after the init method).
- // start is usually used to bind event.
- //
- // this.$target: block html inserted inside the page
- // this.$el: html li list of this options
- // this.$overlay: html editor overlay who content resize bar, customize menu...
- start: function () {},
-
-
- // onFocus is called when the user click inside the block inserted in page
- // and when the user drop on block into the page
- onFocus : function () {},
-
-
- // onBlur is called when the user click outside the block inserted in page, if
- // the block is focused
- onBlur : function () {},
-
-
- // on_clone is called when the snippet is duplicate
- // @variables: $clone is allready inserted is the page
- on_clone: function ($clone) {},
-
-
- // on_remove is called when the snippet is removed (dom is removing after this tigger)
- on_remove: function () {},
-
-
- // drop_and_build_snippet is called just after that a thumbnail is drag and dropped
- // into a drop zone. The content is already inserted in the page.
- drop_and_build_snippet: function () {},
-
- // select is called when a user select an item in the li list of options
- // By default, if the li item have a data-value attribute, the data-vlue it's apply
- // like a class to the html block (this.$target)
- // @variables: next_previous = {$next, $prev}
- // $next = next item selected or false
- // $prev = previous item selected or false
- select: function (event, next_previous) {}
-
- // preview is called when a user is on mouse over or mouse out of an item
- // variables: next_previous = {$next, $prev}
- // $next = next item selected or false
- // $prev = previous item selected or false
- preview: function (event, next_previous) {}
-
- // clean_for_save
- // clean_for_save is called just before to save the vue
- // Sometime it's important to remove or add some datas (contentEditable, added
- // classes to a running animation...)
- clean_for_save: function () {}
- });
-
-
- // 'snippet-dropped' is triggered on '#oe_snippets' whith $target as attribute when a snippet is dropped
- // 'snippet-activated' is triggered on '#oe_snippets' (and on snippet) when a snippet is activated
-
-*/
-
var dummy = function () {};
var website = openerp.website;
@@ -177,8 +48,25 @@
}
});
+ $.extend($.expr[':'],{
+ checkData: function(node,i,m){
+ var dataName = m[3];
+ while (node) {
+ if (node.dataset && node.dataset[dataName]) {
+ return true;
+ } else {
+ node = node.parentNode;
+ }
+ }
+ return false;
+ },
+ hasData: function(node,i,m){
+ return !!_.toArray(node.dataset).length;
+ },
+ });
+
if (!website.snippet) website.snippet = {};
- website.snippet.templateOptions = {};
+ website.snippet.templateOptions = [];
website.snippet.globalSelector = "";
website.snippet.selector = [];
website.snippet.BuildingBlock = openerp.Widget.extend({
@@ -198,25 +86,6 @@
subtree: true,
});
},
- dom_filter: function (dom, sibling) {
- if (typeof dom === "string") {
- var include = "[data-oe-model]";
- var sdom = dom.split(',');
- dom = "";
- _.each(sdom, function (val) {
- val = val.replace(/^\s+|\s+$/g, '');
- dom += include + " " + val + ", ";
- if (!sibling) {
- val = val.split(" ");
- dom += val.shift() + include + val.join(" ") + ", ";
- }
- });
- dom = dom.replace(/,\s*$/g, '');
- return $(dom);
- } else {
- return (!sibling && $(dom).is("[data-oe-model]")) || $(dom).parents("[data-oe-model]").length ? $(dom) : $("");
- }
- },
start: function() {
var self = this;
@@ -224,18 +93,16 @@
.prependTo(this.parent.$("#website-top-edit ul"))
.find("button");
- this.$button.click(function () {
- self.make_active(false);
- self.$el.toggleClass("hidden");
- });
- $("#wrapwrap").click(function () {
+ this.$button.click(_.bind(this.show_blocks, this));
+
+ this.$snippet = $("#oe_snippets");
+ this.$wrapwrap = $("#wrapwrap");
+ this.$wrapwrap.click(function () {
self.$el.addClass("hidden");
});
this.fetch_snippet_templates();
-
this.bind_snippet_click_editor();
-
this.$el.addClass("hidden");
$(document).on('click', '.dropdown-submenu a[tabindex]', function (e) {
@@ -245,11 +112,69 @@
this.getParent().on('change:height', this, function (editor) {
self.$el.css('top', editor.get('height'));
});
- self.$el.css('top', this.parent.get('height'));
+ this.$el.css('top', this.parent.get('height'));
+ },
+ show_blocks: function () {
+ var self = this;
+ this.make_active(false);
+ this.$el.toggleClass("hidden");
+ if (this.$el.hasClass("hidden")) {
+ return;
+ }
+
+ //this.enable_snippets( this.$snippet.find(".tab-pane.active") );
+ var categories = this.$snippet.find(".tab-pane.active")
+ .add(this.$snippet.find(".tab-pane:not(.active)"))
+ .get().reverse();
+ function enable() {
+ self.enable_snippets( $(categories.pop()) );
+ if (categories.length) {
+ setTimeout(enable,10);
+ }
+ }
+ setTimeout(enable,0);
+ },
+ enable_snippets: function ($category) {
+ var self = this;
+ $category.find(".oe_snippet_body").each(function () {
+ var $snippet = $(this);
+
+ if (!$snippet.data('selectors')) {
+ var selectors = [];
+ for (var k in website.snippet.templateOptions) {
+ var option = website.snippet.templateOptions[k];
+ if ($snippet.is(option.base_selector)) {
+
+ var dropzone = [];
+ if (option['drop-near']) dropzone.push(option['drop-near']);
+ if (option['drop-in']) dropzone.push(option['drop-in']);
+ if (option['drop-in-vertical']) dropzone.push(option['drop-in-vertical']);
+ selectors = selectors.concat(dropzone);
+ }
+ }
+ $snippet.data('selectors', selectors.length ? selectors.join(":first, ") + ":first" : "");
+ }
+
+ if ($snippet.data('selectors').length && self.$wrapwrap.find($snippet.data('selectors')).size()) {
+ $snippet.closest(".oe_snippet").removeClass("disable");
+ } else {
+ $snippet.closest(".oe_snippet").addClass("disable");
+ }
+ });
+ $('#oe_snippets .scroll a[data-toggle="tab"][href="#' + $category.attr("id") + '"]')
+ .toggle(!!$category.find(".oe_snippet:not(.disable)").size());
},
_get_snippet_url: function () {
return '/website/snippets';
},
+ _add_check_selector : function (selector, no_check) {
+ var data = selector.split(",");
+ var selectors = [];
+ for (var k in data) {
+ selectors.push(data[k].replace(/^\s+|\s+$/g, '') + (no_check ? "" : ":checkData(oeModel)"));
+ }
+ return selectors.join(", ");
+ },
fetch_snippet_templates: function () {
var self = this;
@@ -258,34 +183,30 @@
var $html = $(html);
var selector = [];
- var $styles = $html.find("[data-snippet-option-id]");
+ var $styles = $html.find("[data-option], [data-selector]");
$styles.each(function () {
var $style = $(this);
- var style_id = $style.data('snippet-option-id');
- website.snippet.templateOptions[style_id] = {
- 'snippet-option-id' : style_id,
- 'selector': $style.data('selector'),
+ var no_check = $style.data('no-check');
+ var option_id = $style.data('option');
+ var option = {
+ 'option' : option_id,
+ 'base_selector': $style.data('selector'),
+ 'selector': self._add_check_selector($style.data('selector'), no_check),
'$el': $style,
- 'selector-siblings': $style.data('selector-siblings'),
- 'selector-children': $style.data('selector-children'),
- 'selector-vertical-children': $style.data('selector-vertical-children'),
+ 'drop-near': $style.data('drop-near') && self._add_check_selector($style.data('drop-near'), no_check),
+ 'drop-in': $style.data('drop-in') && self._add_check_selector($style.data('drop-in'), no_check),
+ 'drop-in-vertical': $style.data('drop-in-vertical') && self._add_check_selector($style.data('drop-in-vertical'), no_check),
'data': $style.data()
};
- selector.push($style.data('selector'));
+ website.snippet.templateOptions.push(option);
+ selector.push(option.selector);
});
$styles.addClass("hidden");
website.snippet.globalSelector = selector.join(",");
self.$snippets = $html.find(".tab-content > div > div").addClass("oe_snippet");
- self.$el.append($html);
-
- var snippets = 0;
- self.$snippets.each(function () {
- if (self.snippet_have_dropzone($(this)))
- snippets++;
- });
- if (!snippets) self.$button.css("display", "none");
+ self.$el.append($html);
self.make_snippet_draggable(self.$snippets);
});
@@ -310,17 +231,10 @@
hide: function () {
this.$el.addClass("hidden");
},
-
- snippet_have_dropzone: function ($snippet) {
- return (($snippet.data('selector-siblings') && this.dom_filter($snippet.data('selector-siblings')).size() > 0) ||
- ($snippet.data('selector-children') && this.dom_filter($snippet.data('selector-children')).size() > 0) ||
- ($snippet.data('selector-vertical-children') && this.dom_filter($snippet.data('selector-vertical-children')).size() > 0));
- },
-
bind_snippet_click_editor: function () {
var self = this;
var snipped_event_flag;
- $("#wrapwrap").on('click', function (event) {
+ self.$wrapwrap.on('click', function (event) {
if (snipped_event_flag || !event.srcElement) {
return;
}
@@ -337,9 +251,6 @@
$target = $target.parents(website.snippet.globalSelector).first();
}
- if (!self.dom_filter($target).length) {
- $target = false;
- }
if (self.$active_snipped_id && self.$active_snipped_id.is($target)) {
return;
}
@@ -349,14 +260,14 @@
snippet_blur: function ($snippet) {
if ($snippet) {
if ($snippet.data("snippet-editor")) {
- $snippet.data("snippet-editor").onBlur();
+ $snippet.data("snippet-editor").on_blur();
}
}
},
snippet_focus: function ($snippet) {
if ($snippet) {
if ($snippet.data("snippet-editor")) {
- $snippet.data("snippet-editor").onFocus();
+ $snippet.data("snippet-editor").on_focus();
}
}
},
@@ -364,15 +275,15 @@
var self = this;
var options = website.snippet.options;
var template = website.snippet.templateOptions;
- for (var k in options) {
- if (template[k] && options[k].prototype.clean_for_save !== dummy) {
- var $snippet = this.dom_filter(template[k].selector);
- $snippet.each(function () {
- new options[k](self, null, $(this), k).clean_for_save();
+ for (var k in template) {
+ var Option = options[template[k]['option']];
+ if (Option && Option.prototype.clean_for_save !== dummy) {
+ self.$wrapwrap.find(template[k].selector).each(function () {
+ new Option(self, null, $(this), k).clean_for_save();
});
}
}
- $("*[contentEditable], *[attributeEditable]")
+ self.$wrapwrap.find("*[contentEditable], *[attributeEditable]")
.removeAttr('contentEditable')
.removeAttr('attributeEditable');
},
@@ -392,7 +303,7 @@
this.create_overlay(this.$active_snipped_id);
this.snippet_focus($snippet);
}
- $("#oe_snippets").trigger('snippet-activated', $snippet);
+ this.$snippet.trigger('snippet-activated', $snippet);
if ($snippet) {
$snippet.trigger('snippet-activated', $snippet);
}
@@ -406,15 +317,6 @@
this.cover_target($snippet.data('overlay'), $snippet);
},
- path_eval: function(path){
- var obj = window;
- path = path.split('.');
- do{
- obj = obj[path.shift()];
- }while(path.length && obj);
- return obj;
- },
-
// activate drag and drop for the snippets in the snippet toolbar
make_snippet_draggable: function($snippets){
var self = this;
@@ -437,32 +339,32 @@
start: function(){
self.hide();
dropped = false;
- // snippet_selectors => to get selector-siblings, selector-children, selector-vertical-children
+ // snippet_selectors => to get drop-near, drop-in, drop-in-vertical
$snippet = $(this);
- $toInsert = $snippet.find('.oe_snippet_body').clone();
-
+ var $base_body = $snippet.find('.oe_snippet_body');
var selector = [];
var selector_siblings = [];
var selector_children = [];
var selector_vertical_children = [];
for (var k in website.snippet.templateOptions) {
- if ($toInsert.is(website.snippet.templateOptions[k].selector)) {
- selector.push(website.snippet.templateOptions[k].selector);
- if (website.snippet.templateOptions[k]['selector-siblings'])
- selector_siblings.push(website.snippet.templateOptions[k]['selector-siblings']);
- if (website.snippet.templateOptions[k]['selector-children'])
- selector_children.push(website.snippet.templateOptions[k]['selector-children']);
- if (website.snippet.templateOptions[k]['selector-vertical-children'])
- selector_vertical_children.push(website.snippet.templateOptions[k]['selector-vertical-children']);
+ if ($base_body.is(website.snippet.templateOptions[k].base_selector)) {
+ selector.push(website.snippet.templateOptions[k].base_selector);
+ if (website.snippet.templateOptions[k]['drop-near'])
+ selector_siblings.push(website.snippet.templateOptions[k]['drop-near']);
+ if (website.snippet.templateOptions[k]['drop-in'])
+ selector_children.push(website.snippet.templateOptions[k]['drop-in']);
+ if (website.snippet.templateOptions[k]['drop-in-vertical'])
+ selector_vertical_children.push(website.snippet.templateOptions[k]['drop-in-vertical']);
}
}
+ $toInsert = $base_body.clone();
action = $snippet.find('.oe_snippet_body').size() ? 'insert' : 'mutate';
if( action === 'insert'){
if (!selector_siblings.length && !selector_children.length && !selector_vertical_children.length) {
- console.debug($snippet.data("snippet-id") + " have oe_snippet_body class and have not for insert action"+
- "data-selector-siblings, data-selector-children or data-selector-vertical-children tag for mutate action");
+ console.debug($snippet.find(".oe_snippet_thumbnail_title").text() + " have not insert action: "+
+ "data-drop-near, data-drop-in or data-drop-in-vertical");
return;
}
self.activate_insertion_zones({
@@ -473,7 +375,7 @@
} else if( action === 'mutate' ){
if (!$snippet.data('selector')) {
- console.debug($snippet.data("snippet-id") + " have not oe_snippet_body class and have not data-selector tag");
+ console.debug($snippet.data("option") + " have not oe_snippet_body class and have not data-selector tag");
return;
}
var $targets = self.activate_overlay_zones(selector_children.join(","));
@@ -504,38 +406,24 @@
stop: function(ev, ui){
$toInsert.removeClass('oe_snippet_body');
- if (action === 'insert' && ! dropped && $('.oe_drop_zone') && ui.position.top > 3) {
- var el = $('.oe_drop_zone').nearest({x: ui.position.left, y: ui.position.top}).first();
+ if (action === 'insert' && ! dropped && self.$wrapwrap.find('.oe_drop_zone') && ui.position.top > 3) {
+ var el = self.$wrapwrap.find('.oe_drop_zone').nearest({x: ui.position.left, y: ui.position.top}).first();
if (el.length) {
el.after($toInsert);
dropped = true;
}
}
- $('.oe_drop_zone').droppable('destroy').remove();
+ self.$wrapwrap.find('.oe_drop_zone').droppable('destroy').remove();
if (dropped) {
var $target = false;
$target = $toInsert;
setTimeout(function () {
- $("#oe_snippets").trigger('snippet-dropped', $target);
+ self.$snippet.trigger('snippet-dropped', $target);
website.snippet.start_animation(true, $target);
- // drop_and_build_snippet
- self.create_overlay($target);
- if ($target.data("snippet-editor")) {
- $target.data("snippet-editor").drop_and_build_snippet();
- }
- for (var k in website.snippet.templateOptions) {
- $target.find(website.snippet.templateOptions[k].selector).each(function () {
- var $snippet = $(this);
- self.create_overlay($snippet);
- if ($snippet.data("snippet-editor")) {
- $snippet.data("snippet-editor").drop_and_build_snippet();
- }
- });
- }
// reset snippet for rte
$target.removeData("snippet-editor");
@@ -551,7 +439,22 @@
$snippet.removeData("overlay");
}
});
+ // end
+
+ // drop_and_build_snippet
self.create_overlay($target);
+ if ($target.data("snippet-editor")) {
+ $target.data("snippet-editor").drop_and_build_snippet();
+ }
+ for (var k in website.snippet.templateOptions) {
+ $target.find(website.snippet.templateOptions[k].selector).each(function () {
+ var $snippet = $(this);
+ self.create_overlay($snippet);
+ if ($snippet.data("snippet-editor")) {
+ $snippet.data("snippet-editor").drop_and_build_snippet();
+ }
+ });
+ }
// end
self.make_active($target);
@@ -566,7 +469,7 @@
// 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;
+ return $(this).data('option') === id;
}).first();
},
@@ -584,7 +487,7 @@
var zone_template = "<div class='oe_drop_zone oe_insert'></div>";
if(child_selector){
- self.dom_filter(child_selector).each(function (){
+ self.$wrapwrap.find(child_selector).each(function (){
var $zone = $(this);
$zone.find('> *:not(.oe_drop_zone):visible').after(zone_template);
$zone.prepend(zone_template);
@@ -592,7 +495,7 @@
}
if(vertical_child_selector){
- self.dom_filter(vertical_child_selector).each(function (){
+ self.$wrapwrap.find(vertical_child_selector).each(function (){
var $zone = $(this);
var $template = $(zone_template).addClass("oe_vertical");
var nb = 0;
@@ -623,7 +526,7 @@
}
if(sibling_selector){
- self.dom_filter(sibling_selector, true).each(function (){
+ self.$wrapwrap.find(sibling_selector, true).each(function (){
var $zone = $(this);
if($zone.prev('.oe_drop_zone:visible').length === 0){
$zone.before(zone_template);
@@ -641,13 +544,13 @@
// count += $zones.length;
// $zones.remove();
- $zones = $('.oe_drop_zone > .oe_drop_zone:not(.oe_vertical)').remove(); // no recursive zones
+ $zones = self.$wrapwrap.find('.oe_drop_zone > .oe_drop_zone:not(.oe_vertical)').remove(); // no recursive zones
count += $zones.length;
$zones.remove();
} while (count > 0);
// Cleaning up zones placed between floating or inline elements. We do not like these kind of zones.
- var $zones = $('.oe_drop_zone:not(.oe_vertical)');
+ var $zones = self.$wrapwrap.find('.oe_drop_zone:not(.oe_vertical)');
$zones.each(function (){
var zone = $(this);
var prev = zone.prev();
@@ -671,14 +574,9 @@
// 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(selector){
- var $targets = this.dom_filter(selector);
+ var $targets = typeof selector === "string" ? this.$wrapwrap.find(selector) : selector;
var self = this;
- if (typeof selector !== 'string' && !$targets.length) {
- console.debug( "A good node must have a [data-oe-model] attribute or must have at least one parent with [data-oe-model] attribute.");
- console.debug( "Wrong node(s): ", selector);
- }
-
function is_visible($el){
return $el.css('display') != 'none'
&& $el.css('opacity') != '0'
@@ -719,9 +617,14 @@
$target.on("DOMNodeInserted DOMNodeRemoved DOMSubtreeModified", function () {
self.cover_target($zone, $target);
});
- $('body').on("resize", function () {
- self.cover_target($zone, $target);
- });
+ var resize = function () {
+ if ($zone.parent().length) {
+ self.cover_target($zone, $target);
+ } else {
+ $('body').off("resize", resize);
+ }
+ };
+ $('body').on("resize", resize);
}
self.cover_target($target.data('overlay'), $target);
});
@@ -733,97 +636,86 @@
website.snippet.options = {};
website.snippet.Option = openerp.Class.extend({
// initialisation (don't overwrite)
- init: function (BuildingBlock, editor, $target, snippet_id) {
+ init: function (BuildingBlock, editor, $target, option_id) {
this.BuildingBlock = BuildingBlock;
this.editor = editor;
this.$target = $target;
+ var option = website.snippet.templateOptions[option_id];
var styles = this.$target.data("snippet-option-ids") || {};
- styles[snippet_id] = this;
+ styles[option_id] = this;
this.$target.data("snippet-option-ids", styles);
this.$overlay = this.$target.data('overlay');
- this['snippet-option-id'] = snippet_id;
- var $option = website.snippet.templateOptions[snippet_id].$el;
- this.$el = $option.find(">li").clone();
- this.data = $option.data();
-
- this.required = this.$el.data("required");
+ this.option= option_id;
+ this.$el = option.$el.find(">li").clone();
+ this.data = option.$el.data();
this.set_active();
- this.$el.find('li[data-value] a').on('mouseenter mouseleave click', _.bind(this._mouse, this));
- this.$el.not(':not([data-value])').find("a").on('mouseenter mouseleave click', _.bind(this._mouse, this));
- this.$target.on('snippet-style-reset', _.bind(this.set_active, this));
+ this.$target.on('snippet-option-reset', _.bind(this.set_active, this));
+ this._bind_li_menu();
this.start();
},
- _mouse: function (event) {
- var self = this;
- if (event.type === 'mouseleave') {
- if (!this.over) return;
- this.over = false;
- } else if (event.type === 'click') {
- this.over = false;
- }else {
- this.over = true;
- }
+ _bind_li_menu: function () {
+ this.$el.filter("li:hasData").find('a:first')
+ .off('mouseenter click')
+ .on('mouseenter click', _.bind(this._mouse, this));
- var $prev, $next;
- if (event.type === 'mouseleave') {
- $prev = $(event.currentTarget).parent();
- $next = this.$el.find("li[data-value].active");
- } else {
- $prev = this.$el.find("li[data-value].active");
- $next = $(event.currentTarget).parent();
- }
- if (!$prev.length) {
- $prev = false;
- }
- if ($prev && $prev[0] === $next[0]) {
- $next = false;
- if (this.required) {
- return;
- }
- }
+ 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));
- var np = {'$next': $next, '$prev': $prev};
+ this.reset_methods = [];
+ this.reset_time = null;
+ },
+
+ /**
+ * 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') {
- setTimeout(function () {
- self.set_active();
- self.$target.trigger("snippet-style-change", [self, np]);
- },0);
- this.select({'$next': $next, '$prev': $prev});
+ this.set_active();
+ this.$target.trigger("snippet-option-change", [this]);
} else {
- setTimeout(function () {
- self.$target.trigger("snippet-style-preview", [self, np]);
- },0);
- this.preview(np);
+ this.$target.trigger("snippet-option-preview", [this]);
}
},
- /* 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-value]')
- .filter(function () {
- var $li = $(this);
- return ($li.data('value') && self.$target.hasClass($li.data('value')));
- })
- .first()
+ var classes = _.uniq((this.$target.attr("class") || '').split(/\s+/));
+ this.$el.find('[data-check_class], [data-select_class]')
+ .add(this.$el)
+ .filter('[data-check_class], [data-select_class]')
+ .removeClass("active")
+ .filter('[data-check_class="' + classes.join('"], [data-check_class="') + '"] ,'+
+ '[data-select_class="' + classes.join('"], [data-select_class="') + '"]')
.addClass("active");
- this.$el.find('li:has(li[data-value].active)').addClass("active");
},
start: function () {
},
- onFocus : function () {
+ on_focus : function () {
+ this._bind_li_menu();
},
- onBlur : function () {
+ on_blur : function () {
},
on_clone: function ($clone) {
@@ -835,110 +727,167 @@
drop_and_build_snippet: function () {
},
- select: function (np) {
- var self = this;
- // add or remove html class
- if (np.$prev && this.required) {
- this.$target.removeClass(np.$prev.data('value' || ""));
+ reset: function (event) {
+ var self = this, $el, method;
+ for (var k in self.reset_methods) {
+ method = self.reset_methods[k];
+ var $li = self.$el.find('[data-'+method+']').add(self.$el).filter('.active, :has(.active)');
+ if ($li.size()) {
+ $li.each(function () {
+ self.select("reset", $(this));
+ });
+ } else {
+ self[method]("reset", null, null);
+ }
}
- if (np.$next) {
- this.$target.addClass(np.$next.data('value') || "");
+ self.reset_methods = [];
+ self.$target.trigger("snippet-option-reset", [this]);
+ },
+
+ // call data-method args as method
+ select: function (type, $li) {
+ var self = this,
+ $methods = [],
+ el = $li[0],
+ $el;
+ clearTimeout(this.reset_time);
+
+ function filter (k) { return k !== 'oeId' && k !== 'oeModel' && k !== 'oeField' && k !== 'oeXpath' && k !== 'oeSourceId';}
+ function hasData(el) {
+ for (var k in el.dataset) {
+ if (filter (k)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ function method(el) {
+ var data = {};
+ for (var k in el.dataset) {
+ if (filter (k)) {
+ data[k] = el.dataset[k];
+ }
+ }
+ return data;
+ }
+
+ while (el && this.$el.is(el) || _.some(this.$el.map(function () {return $.contains(this, el);}).get()) ) {
+ if (hasData(el)) {
+ $methods.push(el);
+ }
+ el = el.parentNode;
}
+
+ $methods.reverse();
+
+ _.each($methods, function (el) {
+ var $el = $(el);
+ var methods = method(el);
+ for (var k in methods) {
+ if (self[k]) {
+ if (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+"'");
+ }
+ }
+ });
},
- preview: function (np) {
- var self = this;
+ // default method for snippet
+ check_class: function (type, value, $li) {
+ var $lis = this.$el.find('[data-check_class]').add(this.$el).filter('[data-check_class]');
- // add or remove html class
- if (np.$prev) {
- this.$target.removeClass(np.$prev.data('value') || "");
+ function map ($lis) {
+ return $lis.map(function () {return $(this).data("check_class");}).get().join(" ");
}
- if (np.$next) {
- this.$target.addClass(np.$next.data('value') || "");
+ var classes = map($lis);
+ var active_classes = map($lis.filter('.active, :has(.active)'));
+
+ this.$target.removeClass(classes);
+ this.$target.addClass(active_classes);
+
+ if (type !== 'reset') {
+ this.$target.toggleClass(value);
}
},
+ select_class: function (type, value, $li) {
+ var $lis = this.$el.find('[data-select_class]').add(this.$el).filter('[data-select_class]');
+
+ var classes = $lis.map(function () {return $(this).data('select_class');}).get();
+
+ this.$target.removeClass(classes.join(" "));
+ if(value) this.$target.addClass(value);
+ },
clean_for_save: dummy
});
-
website.snippet.options.background = website.snippet.Option.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-value].active.oe_custom_bg").data("src", src);
+ 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);
+ }
},
- select: function(np) {
- var self = this;
- this._super(np);
- if (np.$next) {
- if (np.$next.hasClass("oe_custom_bg")) {
- var $image = $('<img class="hidden"/>');
- $image.attr("src", np.$prev ? np.$prev.data("src") : '');
- $image.appendTo(self.$target);
-
- self.element = new CKEDITOR.dom.element($image[0]);
- var editor = new website.editor.MediaDialog(self, self.element);
- editor.appendTo(document.body);
- editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden');
-
- $image.on('saved', self, function (o) {
- var src = $image.attr("src");
- self._set_bg(src);
- np.$next.data("src", src);
- self.$target.trigger("snippet-style-change", [self, np]);
- $image.remove();
- });
- editor.on('cancel', self, function () {
- if (!np.$prev || np.$prev.data("src") === "") {
- self.$target.removeClass(np.$next.data("value"));
- self.$target.trigger("snippet-style-change", [self, np]);
- }
- $image.remove();
- });
- } else {
- this._set_bg(np.$next.data("src"));
- }
+ background: function(type, value, $li) {
+ if (value && value.length) {
+ this.$target.css("background-image", 'url(' + value + ')');
} else {
- this._set_bg(false);
- this.$target.removeClass(np.$prev.data("value"));
+ this.$target.css("background-image", "").removeClass("oe_img_bg");
}
},
- preview: function (np) {
- this._super(np);
- if (np.$next) {
- this._set_bg(np.$next.data("src"));
- }
+ 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);
+
+ self.element = new CKEDITOR.dom.element($image[0]);
+ var editor = new website.editor.MediaDialog(self, self.element);
+ editor.appendTo(document.body);
+ editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden');
+
+ $image.on('saved', self, function (o) {
+ var value = $image.attr("src");
+ self.$target.css("background-image", 'url(' + value + ')');
+ self.$el.find('li[data-select_class="oe_custom_bg"]').data("src", value);
+ self.$target.trigger("snippet-option-change", [self]);
+ $image.remove();
+ self.$target.addClass('oe_custom_bg').removeClass('oe_img_bg');
+ self.set_active();
+ });
+ editor.on('cancel', self, function () {
+ self.$target.removeClass("oe_img_bg")
+ .trigger("snippet-option-change", [self]);
+ $image.remove();
+ });
},
set_active: function () {
var self = this;
- var bg = self.$target.css("background-image");
- this.$el.find('li').removeClass("active");
- this.$el.find('li').removeClass("btn-primary");
- var $active = this.$el.find('li[data-value]')
- .filter(function () {
- var $li = $(this);
- return ($li.data('src') && bg.indexOf($li.data('src')) >= 0) ||
- (!$li.data('src') && self.$target.hasClass($li.data('value')));
- })
- .first();
- if (!$active.length) {
- $active = this.$target.css("background-image") !== 'none' ?
- this.$el.find('li[data-value].oe_custom_bg') :
- this.$el.find('li[data-value=""]');
- }
+ var src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, "");
+ this._super();
- //don't set active on an OpenDialog link, else it not possible to click on it again after.
- // TODO in Saas-4 - Once bootstrap is in less
- // - add a class active-style to get the same display but without the active behaviour used by bootstrap in JS.
- var classStr = _.string.contains($active[0].className, "oe_custom_bg") ? "btn-primary" : "active";
- $active.addClass(classStr);
- this.$el.find('li:has(li[data-value].active)').addClass(classStr);
+ this.$el.find('li[data-background]')
+ .filter(':not([data-background=""])')
+ .removeClass("active")
+ .each(function () {
+ var background = $(this).data("background");
+ if ((src.length && background.length && src.indexOf(background) !== -1) || (!src.length && !background.length)) {
+ $(this).addClass("active");
+ }
+ });
+ if(!this.$el.find('li.active').size()) {
+ this.$el.find('li[data-select_class=""][data-background=""], '+
+ 'li[data-select_class=""] li[data-background=""], '+
+ 'li[data-background=""] li[data-select_class=""]').addClass("active");
+ this.$el.filter('li[data-select_class=""]').find('li[data-background=""]').addClass("active");
+ this.$el.filter('li[data-background=""]').find('li[data-select_class=""]').addClass("active");
+ }
}
});
@@ -983,7 +932,6 @@
}
});
-
website.snippet.options.slider = website.snippet.Option.extend({
unique_id: function () {
var id = 0;
@@ -1029,14 +977,12 @@
this.id = this.$target.attr("id");
this.$inner = this.$target.find('.carousel-inner');
this.$indicators = this.$target.find('.carousel-indicators');
-
- this.$el.find(".js_add").on('click', function () {self.on_add_slide(); return false;});
- this.$el.find(".js_remove").on('click', function () {self.on_remove_slide(); return false;});
-
this.$target.carousel('pause');
this.rebind_event();
},
- on_add_slide: function () {
+ add_slide: function (type, value) {
+ if(type !== "click") return;
+
var self = this;
var cycle = this.$inner.find('.item').length;
var $active = this.$inner.find('.item.active, .item.prev, .item.next').first();
@@ -1054,7 +1000,9 @@
},0);
return $clone;
},
- on_remove_slide: function () {
+ remove_slide: function (type, value) {
+ if(type !== "click") return;
+
if (this.remove_process) {
return;
}
@@ -1083,6 +1031,13 @@
this.$target.find('.carousel-control, .carousel-indicators').addClass("hidden");
}
},
+ interval : function(type, value) {
+ this.$target.attr("data-interval", value);
+ },
+ set_active: function () {
+ this.$el.find('li[data-interval]').removeClass("active")
+ .filter('li[data-interval='+this.$target.attr("data-interval")+']').addClass("active");
+ },
});
website.snippet.options.carousel = website.snippet.options.slider.extend({
getSize: function () {
@@ -1092,12 +1047,11 @@
},
clean_for_save: function () {
this._super();
- this.$target.css("background-image", "");
- this.$target.removeClass(this._class);
+ this.$target.removeClass('oe_img_bg ' + this._class).css("background-image", "");
},
load_style_options : function () {
this._super();
- $(".snippet-style-size li[data-value='']").remove();
+ $(".snippet-option-size li[data-value='']").remove();
},
start : function () {
var self = this;
@@ -1108,46 +1062,30 @@
if (c) self._class = (self._class || "").replace(new RegExp("[ ]+" + c.replace(" ", "|[ ]+")), '') + ' ' + c;
return self._class || "";
};
- this.$target.on('snippet-style-change snippet-style-preview', function (event, style, np) {
- var $active = self.$target.find(".item.active");
- if (style['snippet-option-id'] === "size") return;
- if (style['snippet-option-id'] === "background") {
- $active.css("background-image", self.$target.css("background-image"));
- }
- if (np.$prev) {
- $active.removeClass(np.$prev.data("value"));
- }
- if (np.$next) {
- $active.addClass(np.$next.data("value"));
- add_class(np.$next.data("value"));
+ this.$target.on('slid.bs.carousel', function () {
+ if(self.editor && self.editor.styles.background) {
+ self.editor.styles.background.$target = self.$target.find(".item.active");
+ self.editor.styles.background.set_active();
}
- });
- this.$target.on('slid', function () { // slide.bs.carousel
- var $active = self.$target.find(".item.active");
- self.$target
- .css("background-image", $active.css("background-image"))
- .removeClass(add_class($active.attr("class")))
- .addClass($active.attr("class"))
- .trigger("snippet-style-reset");
-
self.$target.carousel("pause");
});
- this.$target.trigger('slid');
+ this.$target.trigger('slid.bs.carousel');
},
- on_add_slide: function () {
- var $clone = this._super();
+ add_slide: function (type, data) {
+ if(type !== "click") return;
+ var $clone = this._super(type, data);
// choose an other background
var bg = this.$target.data("snippet-option-ids").background;
if (!bg) return $clone;
- var $styles = bg.$el.find("li[data-value]:not(.oe_custom_bg)");
- var styles_index = $styles.index($styles.filter(".active")[0]);
- $styles.removeClass("active");
- var $select = $($styles[styles_index >= $styles.length-1 ? 0 : styles_index+1]);
+ var $styles = bg.$el.find("li[data-background]");
+ var $select = $styles.filter(".active").removeClass("active").next("li[data-background]");
+ if (!$select.length) {
+ $select = $styles.first();
+ }
$select.addClass("active");
- $clone.css("background-image", $select.data("src") ? "url('"+ $select.data("src") +"')" : "");
- $clone.addClass($select.data("value") || "");
+ $clone.css("background-image", $select.data("background") ? "url('"+ $select.data("background") +"')" : "");
return $clone;
},
@@ -1162,7 +1100,6 @@
this._super();
},
});
-
website.snippet.options.marginAndResize = website.snippet.Option.extend({
start: function () {
var self = this;
@@ -1290,7 +1227,7 @@
return this.grid;
},
- onFocus : function () {
+ on_focus : function () {
this._super();
this.change_cursor();
},
@@ -1390,7 +1327,7 @@
hide_remove_button: function() {
this.$overlay.find('.oe_snippet_remove').toggleClass("hidden", !this.$target.siblings().length);
},
- onFocus : function () {
+ on_focus : function () {
this._super();
this.hide_remove_button();
},
@@ -1401,20 +1338,8 @@
return false;
},
on_remove: function () {
- if (!this.$target.siblings().length) {
- var $parent = this.$target.parents(".row:first");
- if($parent.find("[class*='col-md']").length > 1) {
- return false;
- } else {
- if (!$parent.data("snippet-editor")) {
- this.BuildingBlock.create_overlay($parent);
- }
- $parent.data("snippet-editor").on_remove();
- }
- }
this._super();
this.hide_remove_button();
- return false;
},
on_resize: function (compass, beginClass, current) {
if (compass === 'w') {
@@ -1441,7 +1366,7 @@
},
});
- website.snippet.options["resize"] = website.snippet.options.marginAndResize.extend({
+ website.snippet.options.resize = website.snippet.options.marginAndResize.extend({
getSize: function () {
this.grid = this._super();
this.grid.size = 8;
@@ -1465,7 +1390,7 @@
this.$target.data("snippet-view", new website.snippet.animationRegistry.parallax(this.$target));
}
this.scroll();
- this.$target.on('snippet-style-change snippet-style-preview', function () {
+ this.$target.on('snippet-option-change snippet-option-preview', function () {
self.$target.data("snippet-view").set_values();
});
this.$target.attr('contentEditable', 'false');
@@ -1474,22 +1399,15 @@
this.$target.find('> div > div:not(.oe_structure) > .oe_structure').attr('contentEditable', 'true');
},
- scroll: function () {
- var self = this;
- var $ul = this.$el.find('ul[name="parallax-scroll"]');
- var $li = $ul.find("li");
- var speed = this.$target.data('scroll-background-ratio') || 0.6 ;
- $ul.find('[data-value="' + speed + '"]').addClass('active');
- $li.on('click', function (event) {
- $li.removeClass("active");
- $(this).addClass("active");
- var speed = $(this).data('value');
- self.$target.attr('data-scroll-background-ratio', speed);
- self.$target.data("snippet-view").set_values();
- return false;
- });
+ scroll: function (type, value) {
+ this.$target.attr('data-scroll-background-ratio', value);
this.$target.data("snippet-view").set_values();
},
+ set_active: function () {
+ var value = this.$target.attr('data-scroll-background-ratio') || 0;
+ this.$el.find('[data-scroll]').removeClass("active")
+ .filter('[data-scroll="' + (this.$target.attr('data-scroll-background-ratio') || 0) + '"]').addClass("active");
+ },
clean_for_save: function () {
this._super();
this.$target.find(".parallax")
@@ -1502,24 +1420,22 @@
start: function () {
var self = this;
this._super();
-
- this.$el.find(".clear-style").click(function (event) {
- self.$target.removeClass("fa-spin").attr("style", "");
- self.resetTransfo();
- });
-
- this.$el.find(".style").click(function (event) {
- var settings = self.$target.data("transfo").settings;
- self.$target.transfo({ hide: (settings.hide = !settings.hide) });
- });
-
this.$overlay.find('.oe_snippet_clone, .oe_handles').addClass('hidden');
-
this.$overlay.find('[data-toggle="dropdown"]')
.on("mousedown", function () {
self.$target.transfo("hide");
});
},
+ style: function (type, value) {
+ if (type !== 'click') return;
+ var settings = this.$target.data("transfo").settings;
+ this.$target.transfo({ hide: (settings.hide = !settings.hide) });
+ },
+ clear_style: function (type, value) {
+ if (type !== 'click') return;
+ this.$target.removeClass("fa-spin").attr("style", "");
+ this.resetTransfo();
+ },
resetTransfo: function () {
var self = this;
this.$target.transfo("destroy");
@@ -1541,10 +1457,10 @@
})
.mouseover();
},
- onFocus : function () {
+ on_focus : function () {
this.resetTransfo();
},
- onBlur : function () {
+ on_blur : function () {
this.$target.transfo("hide");
},
});
@@ -1557,7 +1473,7 @@
website.snippet.start_animation(true, this.$target);
$(document.body).on("media-saved", self, function (event, prev , item) {
- self.editor.onBlur();
+ self.editor.on_blur();
self.BuildingBlock.make_active(false);
if (self.$target.parent().data("oe-field") !== "image") {
self.BuildingBlock.make_active($(item));
@@ -1571,7 +1487,7 @@
new website.editor.MediaDialog(self, self.element).appendTo(document.body);
});
},
- onFocus : function () {
+ on_focus : function () {
var self = this;
if (this.$target.parent().data("oe-field") === "image") {
this.$overlay.addClass("hidden");
@@ -1582,39 +1498,16 @@
},
});
-
website.snippet.Editor = openerp.Class.extend({
init: function (BuildingBlock, dom) {
this.BuildingBlock = BuildingBlock;
this.$target = $(dom);
this.$overlay = this.$target.data('overlay');
- this.snippet_id = this.$target.data("snippet-id");
- this._readXMLData();
this.load_style_options();
this.get_parent_block();
this.start();
},
- /*
- * _readXMLData
- * Read data XML and set value into:
- * this.$el :
- * all xml data
- * this.$overlay :
- * Dom hover the $target who content options
- */
- _readXMLData: function() {
- var self = this;
- if(this && this.BuildingBlock && this.BuildingBlock.$snippets) {
- this.$el = this.BuildingBlock.$snippets.filter(function () { return $(this).data("snippet-id") == self.snippet_id; }).clone();
- }
- var $options = this.$overlay.find(".oe_overlay_options");
- if ($options.find(".oe_options ul li").length) {
- $options.find(".oe_options").removeClass("hidden");
- }
- },
-
-
// activate drag and drop for the snippets in the snippet toolbar
_drag_and_drop: function(){
var self = this;
@@ -1700,18 +1593,18 @@
this.selector_siblings = [];
this.selector_children = [];
this.selector_vertical_children = [];
- _.each(website.snippet.templateOptions, function (val) {
+ _.each(website.snippet.templateOptions, function (val, option_id) {
if (!self.$target.is(val.selector)) {
return;
}
- if (val['selector-siblings']) self.selector_siblings.push(val['selector-siblings']);
- if (val['selector-children']) self.selector_children.push(val['selector-children']);
- if (val['selector-vertical-children']) self.selector_vertical_children.push(val['selector-vertical-children']);
-
- var style = val['snippet-option-id'];
- var Editor = website.snippet.options[style] || website.snippet.Option;
- var editor = self.styles[style] = new Editor(self.BuildingBlock, self, self.$target, style);
- $ul.append(editor.$el.addClass("snippet-style-" + style));
+ if (val['drop-near']) self.selector_siblings.push(val['drop-near']);
+ if (val['drop-in']) self.selector_children.push(val['drop-in']);
+ if (val['drop-in-vertical']) self.selector_vertical_children.push(val['drop-in-vertical']);
+
+ var option = val['option'];
+ var Editor = website.snippet.options[option] || website.snippet.Option;
+ var editor = self.styles[option] = new Editor(self.BuildingBlock, self, self.$target, option_id);
+ $ul.append(editor.$el.addClass("snippet-option-" + option));
});
this.selector_siblings = this.selector_siblings.join(",");
if (this.selector_siblings === "")
@@ -1724,7 +1617,7 @@
this.selector_vertical_children = false;
if (!this.selector_siblings && !this.selector_children && !this.selector_vertical_children) {
- this.$overlay.find(".oe_snippet_move").addClass('hidden');
+ this.$overlay.find(".oe_snippet_move, .oe_snippet_clone, .oe_snippet_remove").addClass('hidden');
}
@@ -1772,13 +1665,35 @@
},
on_remove: function () {
- this.onBlur();
+ this.on_blur();
var index = _.indexOf(this.BuildingBlock.snippets, this.$target.get(0));
for (var i in this.styles){
this.styles[i].on_remove();
}
delete this.BuildingBlock.snippets[index];
+
+ // remove node and his empty
+ var parent,
+ node = this.$target.parent()[0];
+
this.$target.remove();
+ function check(node) {
+ if ($(node).outerHeight() > 8) {
+ return false;
+ }
+ for (var k=0; k<node.children.length; k++) {
+ if (node.children[k].tagName || node.children[k].textContent.match(/[^\s]/)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ while (check(node)) {
+ parent = node.parentNode;
+ parent.removeChild(node);
+ node = parent;
+ }
+
this.$overlay.remove();
return false;
},
@@ -1794,22 +1709,22 @@
}
},
- /* onFocus
+ /* on_focus
* This method is called when the user click inside the snippet in the dom
*/
- onFocus : function () {
+ on_focus : function () {
this.$overlay.addClass('oe_active');
for (var i in this.styles){
- this.styles[i].onFocus();
+ this.styles[i].on_focus();
}
},
- /* onFocus
+ /* on_focus
* This method is called when the user click outside the snippet in the dom, after a focus
*/
- onBlur : function () {
+ on_blur : function () {
for (var i in this.styles){
- this.styles[i].onBlur();
+ this.styles[i].on_blur();
}
this.$overlay.removeClass('oe_active');
},
diff --git a/addons/website/static/src/js/website.snippets.gallery.js b/addons/website/static/src/js/website.snippets.gallery.js
new file mode 100644
index 00000000000..ed700bdd371
--- /dev/null
+++ b/addons/website/static/src/js/website.snippets.gallery.js
@@ -0,0 +1,348 @@
+(function() {
+ 'use strict';
+
+ var website = openerp.website,
+ _t = openerp._t;
+
+ /*--------------------------------------------------------------------------
+ Template files to load
+ --------------------------------------------------------------------------*/
+ website.add_template_file('/website/static/src/xml/website.gallery.xml');
+
+ /*--------------------------------------------------------------------------
+ Gallery Snippet
+
+ This is the snippet responsible for configuring the image galleries.
+ Look at /website/views/snippets.xml for the available options
+ ------------------------------------------------------------------------*/
+ website.snippet.options.gallery = website.snippet.Option.extend({
+ start : function() {
+ this._super();
+ this.bind_change();
+ },
+ styling : function(type, value) {
+ var classes = this.$el.find('li[data-styling]').map(function () {
+ return $(this).data('styling');
+ }).get().join(' ');
+ this.$target.find("img").removeClass(classes).addClass(value);
+ },
+ interval : function(type, value) {
+ this.$target.find('.carousel:first').attr("data-interval", value);
+ },
+ reapply : function() {
+ var self = this,
+ modes = [ 'o_nomode', 'o_grid', 'o_masonry', 'o_slideshow' ],
+ classes = this.$target.attr("class").split(/\s+/);
+ if (this.block) return;
+ modes.forEach(function(mode) {
+ if (classes.indexOf(mode) != -1) {
+ self.mode("reapply", mode);
+ return;
+ }
+ });
+ },
+ bind_change: function () {
+ var self = this;
+ return this.$target.find("img").off('saved').on('saved', function (event, img) {
+ var $parent = $(img).parent();
+ $parent.addClass("saved_active");
+ var index = self.$target.find(".item.saved_active").index();
+ $parent.removeClass("saved_active");
+ self.$target.find(".carousel:first li[data-target]:eq("+index+")").css("background-image", "url("+$(img).attr("src")+")");
+ });
+ },
+ get_imgs: function () {
+ return this.$target.find("img").addClass("img img-thumbnail img-responsive mb8 mt8").detach();
+ },
+ mode: function (type, value) {
+ if (this.block) return;
+ if (!value) value = 'nomode';
+ this[value]();
+ this.$target.removeClass('o_nomode o_masonry o_grid o_slideshow').addClass("o_"+value);
+ this.bind_change();
+ },
+ replace: function ($content) {
+ var $container = this.$target.find(".container:first");
+ $container.empty().append($content);
+ return $container;
+ },
+ nomode : function() {
+ var self = this,
+ $row = $('<div class="row"></div>'),
+ $imgs = this.get_imgs().wrap('<div>').parent();
+
+ this.replace($row);
+
+ $imgs.each(function () {
+ var img = this.childNodes[0];
+ if (img.width >= img.height * 2) {
+ $(this).addClass("col-md-6");
+ } else if (img.width > 600) {
+ $(this).addClass("col-md-6");
+ } else {
+ $(this).addClass("col-md-3");
+ }
+ });
+ $row.append($imgs);
+ this.$target.css("height", "");
+ },
+ masonry : function() {
+ var self = this,
+ $imgs = this.get_imgs(),
+ columns = this.get_columns(),
+ colClass = undefined,
+ $cols = [];
+
+ var $row = $("<div class='row'/>");
+ this.replace($row);
+
+ // if no columns let's default to 3, here we must update the DOM accordingly :'(
+ if (columns === 0) {
+ columns = 3;
+ this.$target.attr("data-columns", columns);
+ }
+ colClass = "col-md-"+(12/columns);
+
+ // create columns
+ for (var c = 0; c < columns; c++) {
+ var $col = $('<div class="col"></div>').addClass(colClass);
+ $row.append($col);
+ $cols.push($col.get()[0]);
+ }
+
+ this.block = true;
+ $cols = $($cols);
+ var imgs = $imgs.get();
+ function add() {
+ self.lowest($cols).append(imgs.pop());
+ if (imgs.length) setTimeout(add, 0);
+ else self.block = false;
+ }
+ if (imgs.length) add();
+ this.$target.css("height", "");
+ },
+ grid : function() {
+ var self = this,
+ $cols = this.get_imgs().wrap('<div>').parent(),
+ $col, $img,
+ $row = $('<div class="row"></div>'),
+ columns = this.get_columns() || 3,
+ colClass = "col-md-"+(12/columns),
+ $container = this.replace($row);
+
+ $cols.each(function(index) { // 0 based index
+ $col = $(this);
+ $img = $col.find('img');
+ self.img_preserve_styles($img);
+ self.img_responsive($img);
+ $col.addClass(colClass);
+ $col.appendTo($row);
+ if ( (index+1) % columns === 0) {
+ $row = $('<div class="row"></div>');
+ $row.appendTo($container);
+ }
+ });
+ this.$target.css("height", "");
+ },
+ slideshow :function () {
+ var self = this;
+ var $imgs = this.$target.find("img").detach(),
+ urls = $imgs.map(function() { return $(this).attr("src"); } ).get(),
+ params = {
+ srcs : urls,
+ index: 1,
+ title: "",
+ interval : this.$target.data("interval") || false,
+ id: _.uniqueId("slideshow_")
+ },
+ $slideshow = $(openerp.qweb.render('website.gallery.slideshow', params));
+ this.replace($slideshow);
+ this.$target.css("height", Math.round(window.innerHeight*0.7));
+ },
+ columns : function(type, value) {
+ if (this.block) return;
+ this.$target.attr("data-columns", value);
+ this.reapply();
+ },
+ albumimages : function(type, value) {
+ if(type === "click") this[value]();
+ },
+ images_add : function() {
+ /* will be moved in ImageDialog from MediaManager */
+ var self = this,
+ $container = this.$target.find(".container:first"),
+ $upload_form = $(openerp.qweb.render('website.gallery.dialog.upload')),
+ $progress = $upload_form.find(".fa");
+
+ $upload_form.appendTo(document.body);
+
+ $upload_form.on('modal.bs.hide', function() { $(this).remove(); } );
+
+ $upload_form.find(".alert-success").hide();
+ $upload_form.find(".alert-danger").hide();
+
+ $upload_form.on("change", 'input[name="upload"]', function(event) {
+ $upload_form.find('input[type="submit"]').parent().removeClass("hidden");
+ $upload_form.find(".alert").hide();
+ });
+
+ $upload_form.find("form").on("submit", function(event) {
+ event.preventDefault();
+ var files = $(this).find('input[type="file"]')[0].files;
+ var formData = new FormData();
+ for (var i = 0; i < files.length; i++ ) {
+ var file = files[i];
+ formData.append('upload', file, file.name);
+ }
+
+ /*
+ * hide submit button
+ */
+ $('input[name="upload"], input[type="submit"]', this).parent().addClass("hidden");
+
+ /*
+ * show progress
+ */
+ $progress.removeClass("hidden");
+
+ /*
+ * Images upload callback
+ */
+ var callback = _.uniqueId('func_');
+ formData.append("func", callback);
+ window[callback] = function (attachments, error) {
+ delete window[callback];
+
+ $('input[name="upload"]', this).parent().removeClass("hidden");
+
+ if (error) { /* failure */
+
+ $upload_form.find(".alert-danger").show();
+
+ } else { /* success */
+
+ $upload_form.find(".alert-success").show();
+ for (var i = 0 ; i < attachments.length; i++) {
+ $('<img class="img img-thumbnail img-responsive mb8 mt8"/>')
+ .attr("src", attachments[i].website_url)
+ .appendTo($container);
+ }
+ $progress.addClass("hidden");
+ $upload_form.remove();
+ self.reapply(); // refresh the $target
+ }
+ };
+
+ /*
+ * Images upload : don't change order of contentType & processData
+ * and don't change their values, otherwise the XHR will be
+ * wrongly conceived by jQuery.
+ *
+ * (missing boundary in the content-type header field)
+ * Leading to an upload failure.
+ */
+ $.ajax('/website/attach', {
+ type: 'POST',
+ data: formData,
+ contentType: false, /* multipart/form-data for files */
+ processData: false,
+ dataType: 'text'
+ }).done(function (script) {
+ $(script).appendTo('head').remove();
+ });
+ });
+ $upload_form.modal({ backdrop : false });
+
+ $upload_form.find('input[name="upload"]').click();
+ },
+ images_rm : function() {
+ this.replace($('<div class="alert alert-info css_editable_mode_display" style="display: none;"/>').text(_t("Add Images from the 'Customize' menu")));
+ },
+ sizing : function() { // done via css, keep it to avoid undefined error
+ },
+ /*
+ * helpers
+ */
+ styles_to_preserve : function($img) {
+ var styles = [ 'img-rounded', 'img-thumbnail', 'img-circle', 'shadow', 'fa-spin' ];
+ var preserved = [];
+
+ for (var style in styles) {
+ if ($img.hasClass(style)) {
+ preserved.push(style);
+ }
+ }
+ return preserved.join(' ');
+ },
+ img_preserve_styles : function($img) {
+ var classes = this.styles_to_preserve($img);
+ $img.removeAttr("class");
+ $img.addClass(classes);
+ return $img;
+ },
+ img_from_src : function(src) {
+ var self = this;
+ var $img = $("<img/>").attr("src", src);
+ return $img;
+ },
+ img_responsive : function(img) {
+ img.addClass("img img-responsive");
+ return img;
+ },
+ lowest : function($cols) {
+ var height = 0, min = -1, col=0, lowest = undefined;
+ $cols.each(function () {
+ var $col = $(this);
+ height = $col.height();
+ if (min === -1 || height < min) {
+ min = height;
+ lowest = $col;
+ }
+ });
+ return lowest;
+ },
+ get_columns : function() {
+ return parseInt(this.$target.attr("data-columns") || 3);
+ },
+
+ clean_for_save: function() {
+ var self = this;
+ if (this.$target.hasClass("slideshow")) {
+ this.$target.removeAttr("style");
+ }
+ },
+
+ set_active: function () {
+ this._super();
+ var classes = _.uniq((this.$target.attr("class").replace(/(^|\s)o_/g, ' ') || '').split(/\s+/));
+ var $li = this.$el.find('[data-mode]')
+ .removeClass("active")
+ .filter('[data-mode="' + classes.join('"], [data-mode="') + '"]').addClass("active");
+ var mode = this.$el.find('[data-mode].active').data('mode');
+
+ var classes = _.uniq((this.$target.find("img:first").attr("class") || '').split(/\s+/));
+ var $li = this.$el.find('[data-styling]')
+ .removeClass("active")
+ .filter('[data-styling="' + classes.join('"], [data-styling="') + '"]').addClass("active");
+
+ this.$el.find('li[data-interval]').removeClass("active")
+ .filter('li[data-interval='+this.$target.find(".carousel:first").attr("data-interval")+']')
+ .addClass("active");
+
+ var interval = this.$target.find('.carousel:first').attr("data-interval");
+ var $li = this.$el.find('[data-interval]')
+ .removeClass("active")
+ .filter('[data-interval=' + interval + ']').addClass("active");
+
+ var columns = this.get_columns();
+ var $li = this.$el.find('[data-columns]')
+ .removeClass("active")
+ .filter('[data-columns=' + columns + ']').addClass("active");
+
+ this.$el.find('[data-columns]:first, [data-select_class="spc-none"]')
+ .parent().parent().toggle(["grid", "masonry"].indexOf(mode) !== -1);
+ this.$el.find('[data-interval]:first').parent().parent().toggle(mode === "slideshow");
+ },
+ }); // website.snippet.Option.extend
+
+})(); // anonymous function
diff --git a/addons/website/static/src/js/website.theme.js b/addons/website/static/src/js/website.theme.js
index 61655ea3e7b..26bce16c0b6 100644
--- a/addons/website/static/src/js/website.theme.js
+++ b/addons/website/static/src/js/website.theme.js
@@ -1,7 +1,20 @@
(function () {
'use strict';
- openerp.website.add_template_file('/website/static/src/xml/website.theme.xml');
+ openerp.jsonRpc('/web/dataset/call', 'call', {
+ 'model': 'ir.ui.view',
+ 'method': 'read_template',
+ 'args': ['website.theme_customize', openerp.website.get_context()]
+ }).done(function (data) {
+ openerp.qweb.add_template(data);
+ });
+ openerp.jsonRpc('/web/dataset/call', 'call', {
+ 'model': 'ir.ui.view',
+ 'method': 'read_template',
+ 'args': ['website.colorpicker', openerp.website.get_context()]
+ }).done(function (data) {
+ openerp.qweb.add_template(data);
+ });
openerp.website.Theme = openerp.Widget.extend({
template: 'website.theme_customize',
diff --git a/addons/website/static/src/xml/website.editor.xml b/addons/website/static/src/xml/website.editor.xml
index 24d54ec017e..607759cb0c2 100644
--- a/addons/website/static/src/xml/website.editor.xml
+++ b/addons/website/static/src/xml/website.editor.xml
@@ -203,7 +203,7 @@
class="form-inline">
<div class="well">
<div class="form-group pull-left">
- <input type="file" name="upload" accept="image/*" style="position: absolute; opacity: 0; width: 1px; height: 1px;"/>
+ <input type="file" name="upload" accept="image/*" multiple="multiple" style="position: absolute; opacity: 0; width: 1px; height: 1px;"/>
<button type="button" class="btn btn-primary filepicker">Upload an image from your computer</button>
<button type="button" class="btn hidden wait" disabled="disabled">Uploading...</button>
</div>
diff --git a/addons/website/static/src/xml/website.gallery.xml b/addons/website/static/src/xml/website.gallery.xml
new file mode 100644
index 00000000000..cefe50a5319
--- /dev/null
+++ b/addons/website/static/src/xml/website.gallery.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates xml:space="preserve">
+ <!--
+ =======================================================================
+ Gallery Dialog
+
+ This template is shown when creating a gallery, allowing users to upload
+ images.
+ =======================================================================
+ -->
+
+ <t t-name="website.gallery.dialog.upload">
+ <div class="modal fade" >
+ <div class="modal-dialog" style="max-width: 50%;">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true"><![CDATA[&times;]]></button>
+ <h4 class="modal-title">Add images</h4>
+ </div>
+ <div class="modal-body">
+ <form action="/website/attach" method="post" enctype="multipart/form-data" >
+ <div class="form-group">
+ <input name="upload" type="file" value="Choose images" multiple="multiple" accept="image/*"/>
+ </div>
+ <div class="form-group hidden">
+ <input type="submit" value="Upload images" class="btn btn-default" />
+ </div>
+ </form>
+ <div class="text-center"><span class="fa fa-refresh fa-5x fa-spin hidden"></span></div>
+ <div class="alert alert-success" role="alert">Upload successful.</div>
+ <div class="alert alert-danger" role="alert">Upload failed, some images might not have been uploaded. Check your network connectivity.</div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+ </t>
+
+ <!--
+ ========================================================================
+ Gallery Slideshow
+
+ This template is used to display a slideshow of images inside a
+ bootstrap carousel.
+
+ ========================================================================
+ -->
+
+ <t t-name="website.gallery.slideshow">
+ <div t-attf-id="#{id}" class="carousel slide" data-ride="carousel" t-attf-data-interval="#{interval}">
+ <div class="carousel-inner container">
+ <t t-foreach="srcs" t-as="src">
+ <div t-attf-class="item #{src_index == index and 'active' or ''}">
+ <img class="img" t-att-src="src"/>
+ </div>
+ </t>
+ </div>
+
+ <ul class="carousel-indicators">
+ <t t-foreach="srcs" t-as="src">
+ <li t-attf-data-target="##{id}" t-att-data-slide-to="src_index" t-att-class="src_index == index and 'active'" t-attf-style="background-image: url(#{src})"></li>
+ </t>
+ </ul>
+
+ <a class="carousel-control left" t-attf-href="##{id}" data-slide="prev"><span class="glyphicon glyphicon-chevron-left" /></a>
+ <a class="carousel-control right" t-attf-href="##{id}" data-slide="next"><span class="glyphicon glyphicon-chevron-right" /></a>
+ </div> <!-- carousel-id -->
+ <script type="text/javascript">
+ (function() {
+ $(".slideshow").css("height", Math.round(window.innerHeight*0.7));
+ $(window).on("resize", function() { $(".slideshow").css("height", Math.round(window.innerHeight*0.7)); });
+ })();
+ </script>
+ </t>
+
+ <!--
+ ========================================================================
+ Gallery Slideshow LightBox
+
+ This template is used to display a lightbox with a slideshow.
+
+ This template wraps website.gallery.slideshow in a bootstrap modal
+ dialog.
+ ========================================================================
+ -->
+ <t t-name="website.gallery.slideshow.lightbox">
+
+ <div class="modal fade" aria-labbelledby="Image Gallery Dialog" aria-hidden="true">
+
+ <div class="modal-dialog modal-lg" role="Picture Gallery"
+ t-attf-style="min-width: #{dim.min_width}px ; min-height: #{dim.min_height}px ; max-width: #{dim.max_width}px ; max-height: #{dim.max_height}px ; height: #{dim.height}px ;">
+ <div class="modal-content">
+ <div class="modal-body o_slideshow">
+ <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&amp;times;</span><span class="sr-only">Close</span></button>
+ <t t-call="website.gallery.slideshow"></t>
+ </div>
+
+ </div>
+ </div>
+ </div>
+ </t>
+</templates>
diff --git a/addons/website/static/src/xml/website.snippets.xml b/addons/website/static/src/xml/website.snippets.xml
index 4b37ccc5e65..1db2433a4d4 100644
--- a/addons/website/static/src/xml/website.snippets.xml
+++ b/addons/website/static/src/xml/website.snippets.xml
@@ -4,7 +4,7 @@
<!-- Snippet loader -->
<t t-name="website.snippets_button">
<li class="navbar-form js_hide_on_translate" style="margin-top: 6px;">
- <button type="button" data-action="snippet" class="hidden btn btn-primary btn-xs">Insert Blocks</button>
+ <button type="button" data-action="snippet" class="btn btn-primary btn-xs">Insert Blocks</button>
</li>
</t>
<t t-name="website.snippets_style">
@@ -53,13 +53,4 @@
<div class='oe_snippet_manipulator'></div>
</t>
- <!-- snippet objects -->
-
- <div t-name="website.snippets.resize" data-snippet-id='resize'>
- <!-- custom data for the widget -->
- <div class='oe_snippet_thumbnail'>Margin resize</div>
- </div>
-
- <div t-name="website.snippets.colmd" data-snippet-id='colmd' data-selector-vertical-children='.row'></div>
-
</templates>
diff --git a/addons/website/static/src/xml/website.theme.xml b/addons/website/static/src/xml/website.theme.xml
deleted file mode 100644
index 16f4961d367..00000000000
--- a/addons/website/static/src/xml/website.theme.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<templates id="template" xml:space="preserve">
-
-<!--
- Customize Themes
-
- Use INPUT type 'checkbox' or 'radio' or use OPTION in select box
- 'data-xmlid' (optional) xml id of the template to add if the input is checked.
- You can set a list of xml id separate by comma (all template is enable or
- disable in same time)
- 'data-enable' (optional) to checked one or more HTML ids, or list separate by comma
- 'data-disable' (optional) to unchecked one or more HTML ids, or list separate by comma
- 'data-reload="/"' (optional) force the reloading of the page if the url match with
- the string ( = regexp).
- Otherwise, only the '/web/css/website.assets_frontend' is reloaded
-
- For the sets (data-enable and/or data-disable without data-xmlid), the set is
- automatically checked if:
- - all related fields are enabled for data-enable
- - all related fields are disabled for data-disable
- else unchecked
-
- HTML apply classes:
- - 'checked': on the parent label when input is checked
- - 'in': on the container (e.g.: bootstrap modal) after added in DOM (removed together
- out is added)
- - 'out': on the container 1 second before removed from ths DOM
- - 'loading': on the container/modal when the new css is loading
--->
-
-<t t-name="website.theme_customize">
- <div id="theme_customize_modal" class="modal fade">
- <div class="modal-dialog">
- <div class="modal-content">
- <input id="less" data-xmlid="website.option_bootstrap_less" style="display: none;"/>
-
- <div class="loading_backdrop"></div>
- <div class="modal-header">
- <button type="button" class="close">×</button>
- <h4 class="modal-title" id="mySmallModalLabel">Customize your theme</h4>
- </div>
- <div class="modal-body">
- <h5 class="modal-h5">LAYOUT</h5>
- <table name="layout">
- <tr>
- <td class=" text-center" width="50%">
- <label class=" center-block">
- <div class="text-center" style="background-image: url(/website/static/src/img/theme/layout-full.gif); background-size: 100%; line-height: 40px;">FULL WIDTH</div>
- <input name="layoutvar" type="radio" data-xmlid=""/>
- </label>
- </td>
- <td class=" text-center">
- <label class=" center-block">
- <div class="text-center" style="background-image: url(/website/static/src/img/theme/layout-boxed.gif); background-size: 100%; line-height: 40px;">BOXED</div>
- <input name="layoutvar" type="radio" data-xmlid="website.option_layout_boxed" data-enable="less"/>
- </label>
- </td>
- </tr>
- </table>
-
- <h5 class="modal-h5">MAIN COLOR</h5>
- <table name="color">
- <tr>
- <td>
- <label class="chd-color-combi">
- <img src="/website/static/src/img/theme/variant-stone.gif" alt="Stone" class="chd-color-combi-img"/>
- <input name="colorvar" type="radio" data-xmlid="" data-disable="less"/>
- </label>
- </td>
- <td>
- <label class="chd-color-combi">
- <img src="/website/static/src/img/theme/variant-emerald.gif" alt="Emerald" class="chd-color-combi-img"/>
- <input name="colorvar" type="radio" data-xmlid="website.option_color_emerald" data-enable="less"/>
- </label>
- </td>
- <td>
- <label class="chd-color-combi">
- <img src="/website/static/src/img/theme/variant-cobalt.gif" alt="Cobalt" class="chd-color-combi-img"/>
- <input name="colorvar" type="radio" data-xmlid="website.option_color_cobalt" data-enable="less"/>
- </label>
- </td>
- <td>
- <label class="chd-color-combi">
- <img src="/website/static/src/img/theme/variant-amethyst.gif" alt="Amethyst" class="chd-color-combi-img"/>
- <input name="colorvar" type="radio" data-xmlid="website.option_color_amethyst" data-enable="less"/>
- </label>
- </td>
- <td>
- <label class="chd-color-combi">
- <img src="/website/static/src/img/theme/variant-ruby.gif" alt="Blue" class="chd-color-combi-img"/>
- <input name="colorvar" type="radio" data-xmlid="website.option_color_ruby" data-enable="less"/>
- </label>
- </td>
- <td>
- <label class="chd-color-combi">
- <img src="/website/static/src/img/theme/variant-gold.gif" alt="Gold" class="chd-color-combi-img"/>
- <input name="colorvar" type="radio" data-xmlid="website.option_color_gold" data-enable="less"/>
- </label>
- </td>
- </tr>
- </table>
-
- <h5 class="modal-h5">FONTS COMBINATIONS</h5>
- <table name="font">
- <tr>
- <td width="50%">
- <label>
- <div>
- <span style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:11px">Helvetica</span>
- <span style="font-family:Georgia, 'Times New Roman', Times, serif; font-size:11px" >/ Georgia</span>
- </div>
- <input name="theme" type="radio" data-xmlid=""/>
- </label>
- </td>
- <td width="50%">
- <label>
- <div>
- <span style="font-family:Georgia, 'Times New Roman', Times, serif; font-size:11px" >Georgia</span>
- <span style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:11px">/ Helvetica</span>
- <input name="theme" type="radio" data-xmlid="website.option_font" data-enable="less"/>
- </div>
- </label>
- </td>
- </tr>
- </table>
- </div>
- </div>
- </div>
- </div>
-</t>
-
-<!-- color-picker -->
-<!-- HTML class to hide option for one mode : only-text, only-bg -->
-
-<t t-name="website.colorpicker">
- <table class="colorpicker">
- <tr>
- <td><button class="automatic-color" title="Automatic Color"/></td>
- <td><button class="bg-primary" title="Primary Color"/></td>
- <td><button class="bg-success" title="Success Color"/></td>
- <td><button class="bg-info" title="Info Color"/></td>
- <td><button class="bg-warning" title="Warning Color"/></td>
- <td><button class="bg-danger" title="Danger Color"/></td>
- </tr>
- <tr><td colspan="8"><hr style="width: 100%; height: 1px;"/></td></tr>
- <tr>
- <td><button class="bg-blue"/></td>
- <td><button class="bg-turquoise"/></td>
- <td><button class="bg-green"/></td>
- <td><button class="bg-yellow"/></td>
- <td><button class="bg-red"/></td>
- <td><button class="bg-pink"/></td>
- <td><button class="bg-purple"/></td>
- <td><button class="bg-brown"/></td>
- </tr>
- </table>
-</t>
-
-</templates> \ No newline at end of file