odoo/addons/website/static/src/js/website.snippets.editor.js

1786 lines
76 KiB
JavaScript

(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;
website.add_template_file('/website/static/src/xml/website.snippets.xml');
website.EditorBar.include({
start: function () {
var self = this;
$("[data-oe-model]").on('click', function (event) {
var $this = $(event.srcElement);
var tag = $this[0] && $this[0].tagName.toLowerCase();
if (!(tag === 'a' || tag === "button") && !$this.parents("a, button").length) {
self.$('[data-action="edit"]').parent().effect('bounce', {distance: 18, times: 5}, 250);
}
});
return this._super();
},
edit: function () {
var self = this;
$("[data-oe-model] *, [data-oe-type=html] *").off('click');
window.snippets = this.snippets = new website.snippet.BuildingBlock(this);
this.snippets.appendTo(this.$el);
website.snippet.stop_animation();
this.on('rte:ready', this, function () {
self.snippets.$button.removeClass("hidden");
website.snippet.start_animation(true);
$("#wrapwrap *").off('mousedown mouseup click');
});
return this._super.apply(this, arguments);
},
save: function () {
this.snippets.clean_for_save();
this._super();
},
});
/* ----- SNIPPET SELECTOR ---- */
var observer = new website.Observer(function (mutations) {
if (!_(mutations).find(function (m) {
return m.type === 'childList' && m.addedNodes.length > 0;
})) {
return;
}
});
if (!website.snippet) website.snippet = {};
website.snippet.templateOptions = {};
website.snippet.globalSelector = "";
website.snippet.selector = [];
website.snippet.BuildingBlock = openerp.Widget.extend({
template: 'website.snippets',
activeSnippets: [],
init: function (parent) {
this.parent = parent;
this._super.apply(this, arguments);
if(!$('#oe_manipulators').length){
$("<div id='oe_manipulators'></div>").appendTo('body');
}
this.$active_snipped_id = false;
this.snippets = [];
observer.observe(document.body, {
childList: true,
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;
this.$button = $(openerp.qweb.render('website.snippets_button'))
.prependTo(this.parent.$("#website-top-edit ul"))
.find("button");
this.$button.click(function () {
self.make_active(false);
self.$el.toggleClass("hidden");
});
$("#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) {
e.preventDefault();
});
this.getParent().on('change:height', this, function (editor) {
self.$el.css('top', editor.get('height'));
});
self.$el.css('top', this.parent.get('height'));
},
_get_snippet_url: function () {
return '/website/snippets';
},
fetch_snippet_templates: function () {
var self = this;
openerp.jsonRpc(this._get_snippet_url(), 'call', {})
.then(function (html) {
var $html = $(html);
var selector = [];
var $styles = $html.find("[data-snippet-option-id]");
$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'),
'$el': $style,
'selector-siblings': $style.data('selector-siblings'),
'selector-children': $style.data('selector-children'),
'selector-vertical-children': $style.data('selector-vertical-children'),
'data': $style.data()
};
selector.push($style.data('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.make_snippet_draggable(self.$snippets);
});
},
cover_target: function ($el, $target){
var pos = $target.offset();
var mt = parseInt($target.css("margin-top") || 0);
var mb = parseInt($target.css("margin-bottom") || 0);
$el.css({
'width': $target.outerWidth(),
'top': pos.top - mt - 5,
'left': pos.left
});
$el.find(".oe_handle.e,.oe_handle.w").css({'height': $target.outerHeight() + mt + mb+1});
$el.find(".oe_handle.s").css({'top': $target.outerHeight() + mt + mb});
$el.find(".oe_handle.size").css({'top': $target.outerHeight() + mt});
$el.find(".oe_handle.s,.oe_handle.n").css({'width': $target.outerWidth()-2});
},
show: function () {
this.$el.removeClass("hidden");
},
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) {
var srcElement = event.srcElement || (event.originalEvent && (event.originalEvent.originalTarget || event.originalEvent.target));
if (snipped_event_flag || !srcElement) {
return;
}
snipped_event_flag = true;
setTimeout(function () {snipped_event_flag = false;}, 0);
var $target = $(srcElement);
if ($target.parents(".oe_overlay").length) {
return;
}
if (!$target.is(website.snippet.globalSelector)) {
$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;
}
self.make_active($target);
});
},
snippet_blur: function ($snippet) {
if ($snippet) {
if ($snippet.data("snippet-editor")) {
$snippet.data("snippet-editor").onBlur();
}
}
},
snippet_focus: function ($snippet) {
if ($snippet) {
if ($snippet.data("snippet-editor")) {
$snippet.data("snippet-editor").onFocus();
}
}
},
clean_for_save: function () {
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();
});
}
}
$("*[contentEditable], *[attributeEditable]")
.removeAttr('contentEditable')
.removeAttr('attributeEditable');
},
make_active: function ($snippet) {
if ($snippet && this.$active_snipped_id && this.$active_snipped_id.get(0) === $snippet.get(0)) {
return;
}
if (this.$active_snipped_id) {
this.snippet_blur(this.$active_snipped_id);
this.$active_snipped_id = false;
}
if ($snippet && $snippet.length) {
if(_.indexOf(this.snippets, $snippet.get(0)) === -1) {
this.snippets.push($snippet.get(0));
}
this.$active_snipped_id = $snippet;
this.create_overlay(this.$active_snipped_id);
this.snippet_focus($snippet);
}
$("#oe_snippets").trigger('snippet-activated', $snippet);
if ($snippet) {
$snippet.trigger('snippet-activated', $snippet);
}
},
create_overlay: function ($snippet) {
if (typeof $snippet.data("snippet-editor") === 'undefined') {
var $targets = this.activate_overlay_zones($snippet);
if (!$targets.length) return;
$snippet.data("snippet-editor", new website.snippet.Editor(this, $snippet));
}
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;
var $tumb = $snippets.find(".oe_snippet_thumbnail:first");
var left = $tumb.outerWidth()/2;
var top = $tumb.outerHeight()/2;
var $toInsert, dropped, $snippet, action, snipped_id;
$snippets.draggable({
greedy: true,
helper: 'clone',
zIndex: '1000',
appendTo: 'body',
cursor: "move",
handle: ".oe_snippet_thumbnail",
cursorAt: {
'left': left,
'top': top
},
start: function(){
self.hide();
dropped = false;
// snippet_selectors => to get selector-siblings, selector-children, selector-vertical-children
$snippet = $(this);
$toInsert = $snippet.find('.oe_snippet_body').clone();
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']);
}
}
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");
return;
}
self.activate_insertion_zones({
siblings: selector_siblings.join(","),
children: selector_children.join(","),
vertical_children: selector_vertical_children.join(","),
});
} 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");
return;
}
var $targets = self.activate_overlay_zones(selector_children.join(","));
$targets.each(function(){
var $clone = $(this).data('overlay').clone();
$clone.addClass("oe_drop_zone").data('target', $(this));
$(this).data('overlay').after($clone);
});
}
$('.oe_drop_zone').droppable({
over: function(){
if( action === 'insert'){
dropped = true;
$(this).first().after($toInsert);
}
},
out: function(){
var prev = $toInsert.prev();
if( action === 'insert' && this === prev[0]){
dropped = false;
$toInsert.detach();
}
}
});
},
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 (el.length) {
el.after($toInsert);
dropped = true;
}
}
$('.oe_drop_zone').droppable('destroy').remove();
if (dropped) {
var $target = false;
$target = $toInsert;
setTimeout(function () {
$("#oe_snippets").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");
if ($target.data("overlay")) {
$target.data("overlay").remove();
$target.removeData("overlay");
}
$target.find(website.snippet.globalSelector).each(function () {
var $snippet = $(this);
$snippet.removeData("snippet-editor");
if ($snippet.data("overlay")) {
$snippet.data("overlay").remove();
$snippet.removeData("overlay");
}
});
self.create_overlay($target);
// end
self.make_active($target);
},0);
} else {
$toInsert.remove();
}
},
});
},
// 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();
},
// Create element insertion drop zones. two css selectors can be provided
// selector.children -> will insert drop zones as direct child of the selected elements
// in case the selected elements have children themselves, dropzones will be interleaved
// with them.
// selector.siblings -> will insert drop zones after and before selected elements
activate_insertion_zones: function(selector){
var self = this;
var child_selector = selector.children;
var sibling_selector = selector.siblings;
var vertical_child_selector = selector.vertical_children;
var zone_template = "<div class='oe_drop_zone oe_insert'></div>";
if(child_selector){
self.dom_filter(child_selector).each(function (){
var $zone = $(this);
$zone.find('> *:not(.oe_drop_zone):visible').after(zone_template);
$zone.prepend(zone_template);
});
}
if(vertical_child_selector){
self.dom_filter(vertical_child_selector).each(function (){
var $zone = $(this);
var $template = $(zone_template).addClass("oe_vertical");
var nb = 0;
var $lastinsert = false;
var left = 0;
var temp_left = 0;
$zone.find('> *:not(.oe_drop_zone):visible').each(function () {
var $col = $(this);
$template.css('height', ($col.outerHeight() + parseInt($col.css("margin-top")) + parseInt($col.css("margin-bottom")))+'px');
$lastinsert = $template.clone();
$(this).after($lastinsert);
temp_left = $col.position().left;
if (left === temp_left) {
$col.prev(".oe_drop_zone.oe_vertical").remove();
$col.before($template.clone().css("clear", "left"));
}
else if (!nb) {
$col.before($template.clone());
}
left = temp_left;
nb ++;
});
if (!nb) {
$zone.prepend($template.css('height', $zone.outerHeight()+'px'));
}
});
}
if(sibling_selector){
self.dom_filter(sibling_selector, true).each(function (){
var $zone = $(this);
if($zone.prev('.oe_drop_zone:visible').length === 0){
$zone.before(zone_template);
}
if($zone.next('.oe_drop_zone:visible').length === 0){
$zone.after(zone_template);
}
});
}
var count;
do {
count = 0;
// var $zones = $('.oe_drop_zone + .oe_drop_zone'); // no two consecutive zones
// count += $zones.length;
// $zones.remove();
$zones = $('.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)');
$zones.each(function (){
var zone = $(this);
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;
if( (float_prev === 'left' || float_prev === 'right')
&& (float_next === 'left' || float_next === 'right') ){
zone.remove();
}else if( !( disp_prev === null
|| disp_next === null
|| disp_prev === 'block'
|| disp_next === 'block' )){
zone.remove();
}
});
},
// 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 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'
&& $el.css('visibility') != 'hidden';
}
// filter out invisible elements
$targets = $targets.filter(function(){ return is_visible($(this)); });
// filter out elements with invisible parents
$targets = $targets.filter(function(){
var parents = $(this).parents().filter(function(){ return !is_visible($(this)); });
return parents.length === 0;
});
$targets.each(function () {
var $target = $(this);
if (!$target.data('overlay')) {
var $zone = $(openerp.qweb.render('website.snippet_overlay'));
// fix for pointer-events: none with ie9
if (document.body && document.body.addEventListener) {
$zone.on("click mousedown mousedown", function passThrough(event) {
event.preventDefault();
$target.each(function() {
// check if clicked point (taken from event) is inside element
event.srcElement = this;
$(this).trigger(event.type);
});
return false;
});
}
$zone.appendTo('#oe_manipulators');
$zone.data('target',$target);
$target.data('overlay',$zone);
$target.on("DOMNodeInserted DOMNodeRemoved DOMSubtreeModified", function () {
self.cover_target($zone, $target);
});
$('body').on("resize", function () {
self.cover_target($zone, $target);
});
}
self.cover_target($target.data('overlay'), $target);
});
return $targets;
}
});
website.snippet.options = {};
website.snippet.Option = openerp.Class.extend({
// initialisation (don't overwrite)
init: function (BuildingBlock, editor, $target, snippet_id) {
this.BuildingBlock = BuildingBlock;
this.editor = editor;
this.$target = $target;
var styles = this.$target.data("snippet-option-ids") || {};
styles[snippet_id] = this;
this.$target.data("snippet-option-ids", styles);
this.$overlay = this.$target.data('overlay') || $('<div>');
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.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.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;
}
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;
}
}
var np = {'$next': $next, '$prev': $prev};
if (event.type === 'click') {
setTimeout(function () {
self.set_active();
self.$target.trigger("snippet-style-change", [self, np]);
},0);
this.select({'$next': $next, '$prev': $prev});
} else {
setTimeout(function () {
self.$target.trigger("snippet-style-preview", [self, np]);
},0);
this.preview(np);
}
},
/* 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()
.addClass("active");
this.$el.find('li:has(li[data-value].active)').addClass("active");
},
start: function () {
},
onFocus : function () {
},
onBlur : function () {
},
on_clone: function ($clone) {
},
on_remove: function () {
},
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' || ""));
}
if (np.$next) {
this.$target.addClass(np.$next.data('value') || "");
}
},
preview: function (np) {
var self = this;
// add or remove html class
if (np.$prev) {
this.$target.removeClass(np.$prev.data('value') || "");
}
if (np.$next) {
this.$target.addClass(np.$next.data('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);
},
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"));
}
} else {
this._set_bg(false);
this.$target.removeClass(np.$prev.data("value"));
}
},
preview: function (np) {
this._super(np);
if (np.$next) {
this._set_bg(np.$next.data("src"));
}
},
set_active: function () {
var self = this;
var bg = self.$target.css("background-image");
this.$el.find('li').removeClass("active");
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=""]');
}
//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);
}
});
website.snippet.options.slider = website.snippet.Option.extend({
drop_and_build_snippet: function() {
this.id = "myCarousel_" + new Date().getTime();
this.$target.attr("id", this.id);
this.$target.find("[data-slide]").attr("data-cke-saved-href", "#" + this.id);
this.$target.find("[data-target]").attr("data-target", "#" + this.id);
this.rebind_event();
},
on_clone: function ($clone) {
var id = "myCarousel_" + new Date().getTime();
$clone.attr("id", id);
$clone.find("[data-slide]").attr("href", "#" + id);
$clone.find("[data-slide-to]").attr("data-target", "#" + id);
},
// rebind event to active carousel on edit mode
rebind_event: function () {
var self = this;
this.$target.find('.carousel-indicators [data-slide-to]').off('click').on('click', function () {
self.$target.carousel(+$(this).data('slide-to')); });
this.$target.attr('contentEditable', 'false');
this.$target.find('.oe_structure, .content.row, [data-slide], .row > blockquote').attr('contentEditable', 'true');
},
clean_for_save: function () {
this._super();
this.$target.find(".item").removeClass("next prev left right active");
this.$target.find(".item:first").addClass("active");
this.$indicators.find('li').removeClass('active');
this.$indicators.find('li:first').addClass('active');
},
start : function () {
var self = this;
this._super();
this.$target.carousel({interval: false});
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 () {
var self = this;
var cycle = this.$inner.find('.item').length;
var $active = this.$inner.find('.item.active, .item.prev, .item.next').first();
var index = $active.index();
this.$target.find('.carousel-control, .carousel-indicators').removeClass("hidden");
this.$indicators.append('<li data-target="#' + this.id + '" data-slide-to="' + cycle + '"></li>');
// clone the best candidate from template to use new features
var $snippets = this.BuildingBlock.$snippets.find('.oe_snippet_body.carousel');
var point = 0;
var selection;
var className = _.compact(this.$target.attr("class").split(" "));
$snippets.each(function () {
var len = _.intersection(_.compact(this.className.split(" ")), className).length;
if (len > point) {
point = len;
selection = this;
}
});
var $clone = $(selection).find('.item:first').clone();
// insert
$clone.removeClass('active').insertAfter($active);
setTimeout(function() {
self.$target.carousel().carousel(++index);
self.rebind_event();
},0);
return $clone;
},
on_remove_slide: function () {
if (this.remove_process) {
return;
}
var self = this;
var new_index = 0;
var cycle = this.$inner.find('.item').length - 1;
var index = this.$inner.find('.item.active').index();
if (cycle > 0) {
this.remove_process = true;
var $el = this.$inner.find('.item.active');
self.$target.on('slid.bs.carousel', function (event) {
$el.remove();
self.$indicators.find("li:last").remove();
self.$target.off('slid.bs.carousel');
self.rebind_event();
self.remove_process = false;
if (cycle == 1) {
self.on_remove_slide(event);
}
});
setTimeout(function () {
self.$target.carousel( index > 0 ? --index : cycle );
}, 500);
} else {
this.$target.find('.carousel-control, .carousel-indicators').addClass("hidden");
}
},
});
website.snippet.options.carousel = website.snippet.options.slider.extend({
getSize: function () {
this.grid = this._super();
this.grid.size = 8;
return this.grid;
},
clean_for_save: function () {
this._super();
this.$target.css("background-image", "");
this.$target.removeClass(this._class);
},
load_style_options : function () {
this._super();
$(".snippet-style-size li[data-value='']").remove();
},
start : function () {
var self = this;
this._super();
// set background and prepare to clean for save
var add_class = function (c){
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', 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');
},
on_add_slide: function () {
var $clone = this._super();
// 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]);
$select.addClass("active");
$clone.css("background-image", $select.data("src") ? "url('"+ $select.data("src") +"')" : "");
$clone.addClass($select.data("value") || "");
return $clone;
},
// rebind event to active carousel on edit mode
rebind_event: function () {
var self = this;
this.$target.find('.carousel-control').off('click').on('click', function () {
self.$target.carousel( $(this).data('slide')); });
this._super();
/* Fix: backward compatibility saas-3 */
this.$target.find('.item.text_image, .item.image_text, .item.text_only').find('.container > .carousel-caption > div, .container > img.carousel-image').attr('contentEditable', 'true');
},
});
website.snippet.options.marginAndResize = website.snippet.Option.extend({
start: function () {
var self = this;
this._super();
var resize_values = this.getSize();
if (resize_values.n) this.$overlay.find(".oe_handle.n").removeClass("readonly");
if (resize_values.s) this.$overlay.find(".oe_handle.s").removeClass("readonly");
if (resize_values.e) this.$overlay.find(".oe_handle.e").removeClass("readonly");
if (resize_values.w) this.$overlay.find(".oe_handle.w").removeClass("readonly");
if (resize_values.size) this.$overlay.find(".oe_handle.size").removeClass("readonly");
this.$overlay.find(".oe_handle:not(.size), .oe_handle.size .size").on('mousedown', function (event){
event.preventDefault();
var $handle = $(this);
var resize_values = self.getSize();
var compass = false;
var XY = false;
if ($handle.hasClass('n')) {
compass = 'n';
XY = 'Y';
}
else if ($handle.hasClass('s')) {
compass = 's';
XY = 'Y';
}
else if ($handle.hasClass('e')) {
compass = 'e';
XY = 'X';
}
else if ($handle.hasClass('w')) {
compass = 'w';
XY = 'X';
}
else if ($handle.hasClass('size')) {
compass = 'size';
XY = 'Y';
}
var resize = resize_values[compass];
if (!resize) return;
if (compass === 'size') {
var offset = self.$target.offset().top;
if (self.$target.css("background").match(/rgba\(0, 0, 0, 0\)/)) {
self.$target.addClass("resize_editor_busy");
}
} else {
var xy = event['page'+XY];
var current = resize[2] || 0;
_.each(resize[0], function (val, key) {
if (self.$target.hasClass(val)) {
current = key;
}
});
var begin = current;
var beginClass = self.$target.attr("class");
var regClass = new RegExp("\\s*" + resize[0][begin].replace(/[-]*[0-9]+/, '[-]*[0-9]+'), 'g');
}
self.BuildingBlock.editor_busy = true;
var cursor = $handle.css("cursor")+'-important';
var $body = $(document.body);
$body.addClass(cursor);
var body_mousemove = function (event){
event.preventDefault();
if (compass === 'size') {
var dy = event.pageY-offset;
dy = dy - dy%resize;
if (dy <= 0) dy = resize;
self.$target.css("height", dy+"px");
self.$target.css("overflow", "hidden");
self.on_resize(compass, null, dy);
self.BuildingBlock.cover_target(self.$overlay, self.$target);
return;
}
var dd = event['page'+XY] - xy + resize[1][begin];
var next = current+1 === resize[1].length ? current : (current+1);
var prev = current ? (current-1) : 0;
var change = false;
if (dd > (2*resize[1][next] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][next]);
current = next;
change = true;
}
if (prev != current && dd < (2*resize[1][prev] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][prev]);
current = prev;
change = true;
}
if (change) {
self.on_resize(compass, beginClass, current);
self.BuildingBlock.cover_target(self.$overlay, self.$target);
}
};
var body_mouseup = function(){
$body.unbind('mousemove', body_mousemove);
$body.unbind('mouseup', body_mouseup);
$body.removeClass(cursor);
self.BuildingBlock.editor_busy = false;
self.$target.removeClass("resize_editor_busy");
};
$body.mousemove(body_mousemove);
$body.mouseup(body_mouseup);
});
this.$overlay.find(".oe_handle.size .auto_size").on('click', function (event){
self.$target.css("height", "");
self.$target.css("overflow", "");
self.BuildingBlock.cover_target(self.$overlay, self.$target);
return false;
});
},
getSize: function () {
this.grid = {};
return this.grid;
},
onFocus : function () {
this._super();
this.change_cursor();
},
change_cursor : function () {
var _class = this.$target.attr("class") || "";
var col = _class.match(/col-md-([0-9-]+)/i);
col = col ? +col[1] : 0;
var offset = _class.match(/col-md-offset-([0-9-]+)/i);
offset = offset ? +offset[1] : 0;
var overlay_class = this.$overlay.attr("class").replace(/(^|\s+)block-[^\s]*/gi, '');
if (col+offset >= 12) overlay_class+= " block-e-right";
if (col === 1) overlay_class+= " block-w-right block-e-left";
if (offset === 0) overlay_class+= " block-w-left";
var mb = _class.match(/mb([0-9-]+)/i);
mb = mb ? +mb[1] : 0;
if (mb >= 128) overlay_class+= " block-s-bottom";
else if (!mb) overlay_class+= " block-s-top";
var mt = _class.match(/mt([0-9-]+)/i);
mt = mt ? +mt[1] : 0;
if (mt >= 128) overlay_class+= " block-n-top";
else if (!mt) overlay_class+= " block-n-bottom";
this.$overlay.attr("class", overlay_class);
},
/* on_resize
* called when the box is resizing and the class change, before the cover_target
* @compass: resize direction : 'n', 's', 'e', 'w'
* @beginClass: attributes class at the begin
* @current: curent increment in this.grid
*/
on_resize: function (compass, beginClass, current) {
this.change_cursor();
}
});
website.snippet.options["margin-y"] = website.snippet.options.marginAndResize.extend({
getSize: function () {
this.grid = this._super();
var grid = [0,4,8,16,32,48,64,92,128];
this.grid = {
// list of class (Array), grid (Array), default value (INT)
n: [_.map(grid, function (v) {return 'mt'+v;}), grid],
s: [_.map(grid, function (v) {return 'mb'+v;}), grid],
// INT if the user can resize the snippet (resizing per INT px)
size: null
};
return this.grid;
},
});
website.snippet.options["margin-x"] = website.snippet.options.marginAndResize.extend({
getSize: function () {
this.grid = this._super();
var width = this.$target.parents(".row:first").first().outerWidth();
var grid = [1,2,3,4,5,6,7,8,9,10,11,12];
this.grid.e = [_.map(grid, function (v) {return 'col-md-'+v;}), _.map(grid, function (v) {return width/12*v;})];
var grid = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11];
this.grid.w = [_.map(grid, function (v) {return 'col-md-offset-'+v;}), _.map(grid, function (v) {return width/12*v;}), 12];
return this.grid;
},
_drag_and_drop_after_insert_dropzone: function(){
var self = this;
var $zones = $(".row:has(> .oe_drop_zone)").each(function () {
var $row = $(this);
var width = $row.innerWidth();
var pos = 0;
while (width > pos + self.size.width) {
var $last = $row.find("> .oe_drop_zone:last");
$last.each(function () {
pos = $(this).position().left;
});
if (width > pos + self.size.width) {
$row.append("<div class='col-md-1 oe_drop_to_remove'/>");
var $add_drop = $last.clone();
$row.append($add_drop);
self._drag_and_drop_active_drop_zone($add_drop);
}
}
});
},
_drag_and_drop_start: function () {
this._super();
this.$target.attr("class",this.$target.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-)([0-9-]+)/g, ''));
},
_drag_and_drop_stop: function () {
this.$target.addClass("col-md-offset-" + this.$target.prevAll(".oe_drop_to_remove").length);
this._super();
},
hide_remove_button: function() {
this.$overlay.find('.oe_snippet_remove').toggleClass("hidden", !this.$target.siblings().length);
},
onFocus : function () {
this._super();
this.hide_remove_button();
},
on_clone: function ($clone) {
var _class = $clone.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-)([0-9-]+)/g, '');
$clone.attr("class", _class);
this.hide_remove_button();
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') {
// don't change the right border position when we change the offset (replace col size)
var beginCol = Number(beginClass.match(/col-md-([0-9]+)|$/)[1] || 0);
var beginOffset = Number(beginClass.match(/col-md-offset-([0-9-]+)|$/)[1] || beginClass.match(/col-lg-offset-([0-9-]+)|$/)[1] || 0);
var offset = Number(this.grid.w[0][current].match(/col-md-offset-([0-9-]+)|$/)[1] || 0);
if (offset < 0) {
offset = 0;
}
var colSize = beginCol - (offset - beginOffset);
if (colSize <= 0) {
colSize = 1;
offset = beginOffset + beginCol - 1;
}
this.$target.attr("class",this.$target.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-|col-md-)([0-9-]+)/g, ''));
this.$target.addClass('col-md-' + (colSize > 12 ? 12 : colSize));
if (offset > 0) {
this.$target.addClass('col-md-offset-' + offset);
}
}
this._super(compass, beginClass, current);
},
});
website.snippet.options["resize"] = website.snippet.options.marginAndResize.extend({
getSize: function () {
this.grid = this._super();
this.grid.size = 8;
return this.grid;
},
});
website.snippet.options.parallax = website.snippet.Option.extend({
getSize: function () {
this.grid = this._super();
this.grid.size = 8;
return this.grid;
},
on_resize: function (compass, beginClass, current) {
this.$target.data("snippet-view").set_values();
},
start : function () {
var self = this;
this._super();
if (!self.$target.data("snippet-view")) {
this.$target.data("snippet-view", new website.snippet.animationRegistry.parallax(this.$target));
}
this.scroll();
this.$target.on('snippet-style-change snippet-style-preview', function () {
self.$target.data("snippet-view").set_values();
});
this.$target.attr('contentEditable', 'false');
this.$target.find('> div > .oe_structure').attr('contentEditable', 'true'); // saas-3 retro-compatibility
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;
});
this.$target.data("snippet-view").set_values();
},
clean_for_save: function () {
this._super();
this.$target.find(".parallax")
.css("background-position", '')
.removeAttr("data-scroll-background-offset");
}
});
website.snippet.options.transform = website.snippet.Option.extend({
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");
});
},
resetTransfo: function () {
var self = this;
this.$target.transfo("destroy");
this.$target.transfo({
hide: true,
callback: function () {
var center = $(this).data("transfo").$markup.find('.transfo-scaler-mc').offset();
var $option = self.$overlay.find('.btn-group:first');
self.$overlay.css({
'top': center.top - $option.height()/2,
'left': center.left,
'position': 'absolute',
});
self.$overlay.find(".oe_overlay_options").attr("style", "width:0; left:0!important; top:0;");
self.$overlay.find(".oe_overlay_options > .btn-group").attr("style", "width:160px; left:-80px;");
}});
this.$target.data('transfo').$markup
.on("mouseover", function () {
self.$target.trigger("mouseover");
})
.mouseover();
},
onFocus : function () {
this.resetTransfo();
},
onBlur : function () {
this.$target.transfo("hide");
},
});
website.snippet.options.media = website.snippet.Option.extend({
start: function () {
var self = this;
this._super();
website.snippet.start_animation(true, this.$target);
$(document.body).on("media-saved", self, function (event, prev , item) {
self.editor.onBlur();
self.BuildingBlock.make_active(false);
if (self.$target.parent().data("oe-field") !== "image") {
self.BuildingBlock.make_active($(item));
}
});
this.$el.find(".edition").click(function (event) {
event.preventDefault();
event.stopPropagation();
self.element = new CKEDITOR.dom.element(self.$target[0]);
new website.editor.MediaDialog(self, self.element).appendTo(document.body);
});
},
onFocus : function () {
var self = this;
if (this.$target.parent().data("oe-field") === "image") {
this.$overlay.addClass("hidden");
self.element = new CKEDITOR.dom.element(self.$target[0]);
new website.editor.MediaDialog(self, self.element).appendTo(document.body);
self.BuildingBlock.make_active(false);
}
setTimeout(function () {
self.$target.find(".css_editable_mode_display").removeAttr("_moz_abspos");
},0);
},
});
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;
this.dropped = false;
this.$overlay.draggable({
greedy: true,
appendTo: 'body',
cursor: "move",
handle: ".oe_snippet_move",
cursorAt: {
left: 18,
top: 14
},
helper: function() {
var $clone = $(this).clone().css({width: "24px", height: "24px", border: 0});
$clone.find(".oe_overlay_options >:not(:contains(.oe_snippet_move)), .oe_handle").remove();
$clone.find(":not(.glyphicon)").css({position: 'absolute', top: 0, left: 0});
$clone.appendTo("body").removeClass("hidden");
return $clone;
},
start: _.bind(self._drag_and_drop_start, self),
stop: _.bind(self._drag_and_drop_stop, self)
});
},
_drag_and_drop_after_insert_dropzone: function (){},
_drag_and_drop_active_drop_zone: function ($zones){
var self = this;
$zones.droppable({
over: function(){
$(".oe_drop_zone.hide").removeClass("hide");
$(this).addClass("hide").first().after(self.$target);
self.dropped = true;
},
out: function(){
$(this).removeClass("hide");
self.$target.detach();
self.dropped = false;
},
});
},
_drag_and_drop_start: function (){
var self = this;
self.BuildingBlock.hide();
self.BuildingBlock.editor_busy = true;
self.size = {
width: self.$target.width(),
height: self.$target.height()
};
self.$target.after("<div class='oe_drop_clone' style='display: none;'/>");
self.$target.detach();
self.$overlay.addClass("hidden");
self.BuildingBlock.activate_insertion_zones({
siblings: self.selector_siblings,
children: self.selector_children,
vertical_children: self.selector_vertical_children,
});
$("body").addClass('move-important');
self._drag_and_drop_after_insert_dropzone();
self._drag_and_drop_active_drop_zone($('.oe_drop_zone'));
},
_drag_and_drop_stop: function (){
var self = this;
if (!self.dropped) {
$(".oe_drop_clone").after(self.$target);
}
self.$overlay.removeClass("hidden");
$("body").removeClass('move-important');
$('.oe_drop_zone').droppable('destroy').remove();
$(".oe_drop_clone, .oe_drop_to_remove").remove();
self.BuildingBlock.editor_busy = false;
self.get_parent_block();
setTimeout(function () {self.BuildingBlock.create_overlay(self.$target);},0);
},
load_style_options: function () {
var self = this;
var $styles = this.$overlay.find('.oe_options');
var $ul = $styles.find('ul:first');
this.styles = {};
this.selector_siblings = [];
this.selector_children = [];
this.selector_vertical_children = [];
_.each(website.snippet.templateOptions, function (val) {
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));
});
this.selector_siblings = this.selector_siblings.join(",");
if (this.selector_siblings === "")
this.selector_siblings = false;
this.selector_children = this.selector_children.join(",");
if (this.selector_children === "")
this.selector_children = false;
this.selector_vertical_children = this.selector_vertical_children.join(",");
if (this.selector_vertical_children === "")
this.selector_vertical_children = false;
if (!this.selector_siblings && !this.selector_children && !this.selector_vertical_children) {
this.$overlay.find(".oe_snippet_move").addClass('hidden');
}
if ($ul.find("li").length) {
$styles.removeClass("hidden");
}
this.$overlay.find('[data-toggle="dropdown"]').dropdown();
},
get_parent_block: function () {
var self = this;
var $button = this.$overlay.find('.oe_snippet_parent');
var $parent = this.$target.parents(website.snippet.globalSelector).first();
if ($parent.length) {
$button.removeClass("hidden");
$button.off("click").on('click', function (event) {
event.preventDefault();
setTimeout(function () {
self.BuildingBlock.make_active($parent);
}, 0);
});
} else {
$button.addClass("hidden");
}
},
/*
* start
* This method is called after init and _readXMLData
*/
start: function () {
var self = this;
this.$overlay.on('click', '.oe_snippet_clone', _.bind(this.on_clone, this));
this.$overlay.on('click', '.oe_snippet_remove', _.bind(this.on_remove, this));
this._drag_and_drop();
},
on_clone: function () {
var $clone = this.$target.clone(false);
this.$target.after($clone);
for (var i in this.styles){
this.styles[i].on_clone($clone);
}
return false;
},
on_remove: function () {
this.onBlur();
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];
this.$target.remove();
this.$overlay.remove();
return false;
},
/*
* drop_and_build_snippet
* This method is called just after that a thumbnail is drag and dropped into a drop zone
* (after the insertion of this.$body, if this.$body exists)
*/
drop_and_build_snippet: function () {
for (var i in this.styles){
this.styles[i].drop_and_build_snippet();
}
},
/* onFocus
* This method is called when the user click inside the snippet in the dom
*/
onFocus : function () {
this.$overlay.addClass('oe_active');
for (var i in this.styles){
this.styles[i].onFocus();
}
},
/* onFocus
* This method is called when the user click outside the snippet in the dom, after a focus
*/
onBlur : function () {
for (var i in this.styles){
this.styles[i].onBlur();
}
this.$overlay.removeClass('oe_active');
},
});
})();