[FIX] mass_mailing: properly manage td/th elements

Refactore width-x snippet option and create another option to handle
cloning and removing td elements properly.

* on_focus: the handle to resize are now hidden when the td/th is alone
on its row

* on_clone: when cloning a td element which is alone on its row, it is
instead cloned in a new row. If not, empty td are added on the whole
column to maintain table integrity.

* on_remove: handle removing td in complex table
This commit is contained in:
qsm-odoo
2016-09-23 11:16:16 +02:00
parent d12dc84209
commit 3f5a27e286
2 changed files with 69 additions and 62 deletions
@@ -16,18 +16,16 @@ if ($editable_area.length === 0 || !$editable_area.is(".o_mail_area")) {
// Snippet option for resizing image and column width inline like excel
options.registry["width-x"] = options.Class.extend({
start: function () {
this.container_width = 600;
var parent = this.$target.closest('[data-max-width]');
if (parent.length) {
this.container_width = parseInt(parent.attr('data-max-width'));
}
this.container_width = this.$target.parent().closest("td, table, div").width();
var self = this;
var offset, sib_offset, target_width, sib_width;
var $body = $(document.body);
this.is_image = false;
this._super();
this._super.apply(this, arguments);
this.$overlay.find(".oe_handle.e, .oe_handle.w").removeClass("readonly");
if (this.$target.is('img')) {
if (this.$target.is("img")) {
this.$overlay.find(".oe_handle.w").addClass("readonly");
this.$overlay.find(".oe_snippet_move, .oe_snippet_clone").addClass("hidden");
this.is_image=true;
@@ -44,88 +42,95 @@ options.registry["width-x"] = options.Class.extend({
if (self.is_image) { compass = "image"; }
self.buildingBlock.editor_busy = true;
var $body = $(document.body);
var body_mousemove = function (event) {
$body.on("mousemove.mass_mailing_width_x", function (event) {
event.preventDefault();
offset = self.$target.offset().left;
target_width = self.get_max_width(self.$target);
if (compass === 'e' && self.$target.next().offset()) {
sib_width = self.get_max_width(self.$target.next());
sib_offset = self.$target.next().offset().left;
self.change_width(event, self.$target, target_width, offset ,'plus');
self.change_width(event, self.$target.next(), sib_width, sib_offset ,'minus');
self.change_width(event, self.$target, target_width, offset, true);
self.change_width(event, self.$target.next(), sib_width, sib_offset, false);
}
if (compass === 'w' && self.$target.prev().offset()) {
sib_width = self.get_max_width(self.$target.prev());
sib_offset = self.$target.prev().offset().left;
self.change_width(event, self.$target, target_width, offset ,'minus');
self.change_width(event, self.$target.prev(), sib_width, sib_offset, 'plus');
self.change_width(event, self.$target, target_width, offset, false);
self.change_width(event, self.$target.prev(), sib_width, sib_offset, true);
}
if (compass === 'image') {
self.change_width(event, self.$target, target_width, offset ,'plus');
self.change_width(event, self.$target, target_width, offset, true);
}
};
var body_mouseup = function () {
$body.unbind('mousemove', body_mousemove);
$body.unbind('mouseup', body_mouseup);
});
$body.on("mouseup.mass_mailing_width_x", function () {
$body.off('.mass_mailing_width_x');
self.buildingBlock.editor_busy = false;
self.$target.removeClass("resize_editor_busy");
};
$body.mousemove(body_mousemove);
$body.mouseup(body_mouseup);
});
});
},
change_width: function (event, target ,target_width, offset, type) {
var self = this;
var width;
if (type === 'plus') {
width = event.pageX-offset;
} else {
width = offset + target_width - event.pageX;
}
target.css("width", width + "px");
self.buildingBlock.cover_target(self.$overlay, self.$target);
return;
change_width: function (event, target, target_width, offset, grow) {
target.css("width", grow ? (event.pageX - offset) : (offset + target_width - event.pageX));
this.buildingBlock.cover_target(this.$overlay, this.$target);
},
get_int_width: function ($el) {
var el_width = $el.css('width');
return parseInt(el_width);
get_int_width: function (el) {
return parseInt($(el).css("width"), 10);
},
get_max_width: function ($el) {
var max_width = 0;
var self = this;
_.each($el.siblings(),function(sib){
max_width += self.get_int_width($(sib));
});
return this.container_width - max_width;
return this.container_width - _.reduce(_.map($el.siblings(), this.get_int_width), function (memo, w) { return memo + w; });
},
on_focus: function () {
this._super.apply(this, arguments);
if (this.$target.is("td, th")) {
this.$overlay.find(".oe_handle.e, .oe_handle.w").toggleClass("readonly", this.$target.siblings().length === 0);
}
},
});
options.registry.table_item = options.Class.extend({
on_clone: function ($clone) {
var clone_index = $(this.$target).index();
var $table = this.$target.parents('table[data-max-width]');
if ($table.length === 1){
_.each($table.find('tbody>tr'),function(row){
var clone_selector = 'td:eq(' + clone_index + ')';
var $col_to_clone = $(row).find(clone_selector);
if ($col_to_clone.length !== 0){
$col_to_clone.after($col_to_clone.clone());
}
this._super.apply(this, arguments);
// If we cloned a td or th element...
if (this.$target.is("td, th")) {
// ... and that the td or th element was alone on its row ...
if (this.$target.siblings().length === 1) {
var $tr = $clone.parent();
$tr.clone().empty().insertAfter($tr).append($clone); // ... move the clone in a new row instead
return;
}
// ... if not, if the clone neighbor is an empty cell, remove this empty cell (like if the clone content had been put in that cell)
var $next = $clone.next();
if ($next.length && $next.text().trim() === "") {
$next.remove();
return;
}
// ... if not, insert an empty col in each other row, at the index of the clone
var width = $clone.width();
var $trs = this.$target.closest("table").children("thead, tbody, tfoot").addBack().children("tr").not(this.$target.parent());
_.each($trs.children(":nth-child(" + this.$target.index() + ")"), function (col) {
$(col).after($("<td/>", {style: "width: " + width + "px;"}));
});
}
this._super($clone);
this.buildingBlock.cover_target(this.$overlay, this.$target);
},
on_remove: function () {
var remove_index = $(this.$target).index();
var $table = this.$target.parents('table[data-max-width]');
if ($table.length === 1){
_.each($table.find('tbody>tr'),function(row){
var remove_selector = 'td:eq(' + remove_index + ')';
$(row).find(remove_selector).remove();
});
}
this._super.apply(this, arguments);
this.buildingBlock.cover_target(this.$overlay, this.$target);
// If we are removing a td or th element which was not alone on its row ...
if (this.$target.is("td, th") && this.$target.siblings().length > 0) {
var $trs = this.$target.closest("table").children("thead, tbody, tfoot").addBack().children("tr").not(this.$target.parent());
if ($trs.length) { // ... if there are other rows in the table ...
var $last_tds = $trs.children(":last-child");
if (_.reduce($last_tds, function (memo, td) { return memo + (td.innerHTML || ""); }, "").trim() === "") {
$last_tds.remove(); // ... remove the potential full empty column in the table
} else {
this.$target.parent().append("<td/>"); // ... else, if there is no full empty column, append an empty col in the current row
}
}
}
},
});
@@ -9,6 +9,8 @@
data-selector="img, .mv, .col_mv, td, th">
</div>
<div data-js="table_item" data-selector="td, th"/>
<div data-js="table_row"
data-selector="tr:has(> .row), tr:has(> .col_mv)"
data-drop-near="tr:has(> .row), tr:has(> .col_mv)">