summaryrefslogtreecommitdiff
path: root/addons/web_editor/static/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web_editor/static/src/js')
-rw-r--r--addons/web_editor/static/src/js/backend/field_html.js2
-rw-r--r--addons/web_editor/static/src/js/editor/rte.js22
-rw-r--r--addons/web_editor/static/src/js/editor/rte.summernote.js3
-rw-r--r--addons/web_editor/static/src/js/editor/snippets.editor.js11
-rw-r--r--addons/web_editor/static/src/js/editor/snippets.options.js112
-rw-r--r--addons/web_editor/static/src/js/editor/summernote.js9
-rw-r--r--addons/web_editor/static/src/js/wysiwyg/widgets/crop_dialog.js11
-rw-r--r--addons/web_editor/static/src/js/wysiwyg/widgets/media.js29
-rw-r--r--addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js2
-rw-r--r--addons/web_editor/static/src/js/wysiwyg/wysiwyg.js4
-rw-r--r--addons/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js2
11 files changed, 150 insertions, 57 deletions
diff --git a/addons/web_editor/static/src/js/backend/field_html.js b/addons/web_editor/static/src/js/backend/field_html.js
index 0be20743132..cc06bc53c96 100644
--- a/addons/web_editor/static/src/js/backend/field_html.js
+++ b/addons/web_editor/static/src/js/backend/field_html.js
@@ -432,6 +432,8 @@ var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, {
if (ev.offsetX > 0) {
return;
}
+ ev.stopPropagation();
+ ev.preventDefault();
var checked = $(ev.target).hasClass('o_checked');
var checklistId = parseInt(($(ev.target).attr('id') || '0').replace(/^checklist-id-/, ''));
diff --git a/addons/web_editor/static/src/js/editor/rte.js b/addons/web_editor/static/src/js/editor/rte.js
index bfd7994b161..529bf68887e 100644
--- a/addons/web_editor/static/src/js/editor/rte.js
+++ b/addons/web_editor/static/src/js/editor/rte.js
@@ -323,11 +323,20 @@ var RTEWidget = Widget.extend({
});
// start element observation
- $(document).on('content_changed', '.o_editable', function (ev) {
+ $(document).on('content_changed', function (ev) {
self.trigger_up('rte_change', {target: ev.target});
+
+ // Add the dirty flag to the element that changed by either adding
+ // it on the highest editable ancestor or, if there is no editable
+ // ancestor, on the element itself (that element may not be editable
+ // but if it received a content_changed event, it should be marked
+ // as dirty to allow for custom savings).
if (!ev.__isDirtyHandled) {
- $(this).addClass('o_dirty');
ev.__isDirtyHandled = true;
+
+ var el = ev.target;
+ var dirty = el.closest('.o_editable') || el;
+ dirty.classList.add('o_dirty');
}
});
@@ -586,11 +595,16 @@ var RTEWidget = Widget.extend({
* page element)
*/
_saveElement: function ($el, context, withLang) {
+ var viewID = $el.data('oe-id');
+ if (!viewID) {
+ return Promise.resolve();
+ }
+
return this._rpc({
model: 'ir.ui.view',
method: 'save',
args: [
- $el.data('oe-id'),
+ viewID,
this._getEscapedElement($el).prop('outerHTML'),
$el.data('oe-xpath') || null,
],
@@ -720,7 +734,7 @@ var RTEWidget = Widget.extend({
// selected too)
//
// The triple click behavior is reimplemented for all browsers here
- if (ev.originalEvent.detail === 3) {
+ if (ev.originalEvent && ev.originalEvent.detail === 3) {
// Select the whole content inside the deepest DOM element that was
// triple-clicked
range.create(ev.target, 0, ev.target, ev.target.childNodes.length).select();
diff --git a/addons/web_editor/static/src/js/editor/rte.summernote.js b/addons/web_editor/static/src/js/editor/rte.summernote.js
index b5b74f64466..95ce86572ae 100644
--- a/addons/web_editor/static/src/js/editor/rte.summernote.js
+++ b/addons/web_editor/static/src/js/editor/rte.summernote.js
@@ -452,6 +452,9 @@ eventHandler.modules.linkDialog.showLinkDialog = function ($editable, $dialog, l
$editable.data('range').select();
$editable.data('NoteHistory').recordUndo();
+ var link = linkInfo.range.commonAncestor().closest('a');
+ linkInfo.className = link && link.className;
+
var def = new $.Deferred();
core.bus.trigger('link_dialog_demand', {
$editable: $editable,
diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index c9571f59c51..b94c7108122 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -77,7 +77,16 @@ var SnippetEditor = Widget.extend({
return $clone;
},
start: this._onDragAndDropStart.bind(this),
- stop: this._onDragAndDropStop.bind(this),
+ stop: function () {
+ // Delay our stop handler so that some summernote handlers
+ // which occur on mouseup (and are themself delayed) are
+ // executed first (this prevents the library to crash
+ // because our stop handler may change the DOM).
+ var args = arguments;
+ setTimeout(function () {
+ self._onDragAndDropStop.apply(self, args);
+ }, 0);
+ },
});
} else {
this.$('.o_move_handle').addClass('d-none');
diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js
index f124d1841f0..eec85058ebc 100644
--- a/addons/web_editor/static/src/js/editor/snippets.options.js
+++ b/addons/web_editor/static/src/js/editor/snippets.options.js
@@ -700,6 +700,17 @@ registry.colorpicker = SnippetOption.extend({
this.$el.find('we-collapse').append($pt);
}
+
+ // TODO refactor in master
+ // The primary and secondary are hardcoded here (but marked as hidden)
+ // so they can be removed from snippets when selecting another color.
+ // Normally, the chosable colors do not contain them, which prevents
+ // them to be removed. Indeed, normally, the 'alpha' and 'beta' colors
+ // (which are the same) are displayed instead... but not for all themes.
+ var $colorpicker = this.$el.find('.colorpicker');
+ $colorpicker.append($('<button/>', {'class': 'd-none', 'data-color': 'primary'}));
+ $colorpicker.append($('<button/>', {'class': 'd-none', 'data-color': 'secondary'}));
+
var classes = [];
this.$el.find('.colorpicker button').each(function () {
var $color = $(this);
@@ -838,23 +849,18 @@ registry.background = SnippetOption.extend({
* @see this.selectClass for parameters
*/
chooseImage: function (previewMode, value, $opt) {
- // Put fake image in the DOM, edit it and use it as background-image
- var $image = $('<img/>', {class: 'd-none', src: value === 'true' ? '' : value}).appendTo(this.$target);
+ var options = this._getMediaDialogOptions();
+ var media = this._getEditableMedia();
- var $editable = this.$target.closest('.o_editable');
- var _editor = new weWidgets.MediaDialog(this, {
- 'onlyImages': true,
- 'firstFilters': ['background'],
- 'res_model': $editable.data('oe-model'),
- 'res_id': $editable.data('oe-id'),
- }, $image[0]).open();
-
- _editor.on('save', this, function () {
- this._setCustomBackground($image.attr('src'));
+ var _editor = new weWidgets.MediaDialog(this, options, media).open();
+ _editor.on('save', this, data => {
+ this._onSaveMediaDialog(data);
this.$target.trigger('content_changed');
});
- _editor.on('closed', this, function () {
- $image.remove();
+ _editor.on('closed', this, () => {
+ if (media.classList.contains('o_we_fake_image')) {
+ media.parentNode.removeChild(media);
+ }
});
},
@@ -871,16 +877,7 @@ registry.background = SnippetOption.extend({
return;
}
this.$target.off('.background-option')
- .on('background-color-event.background-option', (function (e, previewMode) {
- e.stopPropagation();
- if (e.currentTarget !== e.target) {
- return;
- }
- if (previewMode === false) {
- this.__customImageSrc = undefined;
- }
- this.background(previewMode);
- }).bind(this));
+ .on('background-color-event.background-option', this._onBackgroundColorUpdate.bind(this));
},
/**
* @override
@@ -897,6 +894,37 @@ registry.background = SnippetOption.extend({
//--------------------------------------------------------------------------
/**
+ * Returns a media element the media dialog will be able to edit to use
+ * the result as the snippet's background somehow.
+ *
+ * @private
+ * @returns {HTMLElement}
+ */
+ _getEditableMedia: function () {
+ var $image = $('<img/>', {
+ class: 'd-none o_we_fake_image',
+ }).appendTo(this.$target);
+ return $image[0];
+ },
+ /**
+ * Returns the options to be given to the MediaDialog instance when choosing
+ * a snippet's background.
+ *
+ * @private
+ * @returns {Object}
+ */
+ _getMediaDialogOptions: function () {
+ var $editable = this.$target.closest('.o_editable');
+ return {
+ noDocuments: true,
+ noIcons: true,
+ noVideos: true,
+ firstFilters: ['background'],
+ res_model: $editable.data('oe-model'),
+ res_id: $editable.data('oe-id'),
+ };
+ },
+ /**
* Returns the src value from a css value related to a background image
* (e.g. "url('blabla')" => "blabla" / "none" => "").
*
@@ -941,6 +969,42 @@ registry.background = SnippetOption.extend({
this._setActive();
this.$target.trigger('snippet-option-change', [this]);
},
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Called on background-color update (useful to remove the background to be
+ * able to see the chosen color).
+ *
+ * @private
+ * @param {Event} ev
+ * @param {boolean|string} previewMode
+ * @returns {boolean} true if the color has been applied (removing the
+ * background)
+ */
+ _onBackgroundColorUpdate: function (ev, previewMode) {
+ ev.stopPropagation();
+ if (ev.currentTarget !== ev.target) {
+ return false;
+ }
+ if (previewMode === false) {
+ this.__customImageSrc = undefined;
+ }
+ this.background(previewMode);
+ return true;
+ },
+ /**
+ * Called on media dialog save (when choosing a snippet's background) ->
+ * sets the resulting media as the snippet's background somehow.
+ *
+ * @private
+ * @param {Object} data
+ */
+ _onSaveMediaDialog: function (data) {
+ this._setCustomBackground(data.src);
+ },
});
/**
diff --git a/addons/web_editor/static/src/js/editor/summernote.js b/addons/web_editor/static/src/js/editor/summernote.js
index 6c62758142d..c2cf0d68d6c 100644
--- a/addons/web_editor/static/src/js/editor/summernote.js
+++ b/addons/web_editor/static/src/js/editor/summernote.js
@@ -1790,6 +1790,7 @@ $.summernote.pluginEvents.insertUnorderedList = function (event, editor, layoutI
}
r.clean().select();
event.preventDefault();
+
return false;
};
$.summernote.pluginEvents.insertOrderedList = function (event, editor, layoutInfo) {
@@ -1797,6 +1798,7 @@ $.summernote.pluginEvents.insertOrderedList = function (event, editor, layoutInf
};
$.summernote.pluginEvents.insertCheckList = function (event, editor, layoutInfo) {
$.summernote.pluginEvents.insertUnorderedList(event, editor, layoutInfo, "checklist");
+ $(range.create().sc.parentNode).trigger('input'); // to update checklist-id
};
$.summernote.pluginEvents.indent = function (event, editor, layoutInfo, outdent) {
var $editable = layoutInfo.editable();
@@ -2469,6 +2471,7 @@ function mouseDownChecklist (e) {
if (!dom.isLi(e.target) || !$(e.target).parent('ul.o_checklist').length || e.offsetX > 0) {
return;
}
+ e.stopPropagation();
e.preventDefault();
var checked = $(e.target).hasClass('o_checked');
$(e.target).toggleClass('o_checked', !checked);
@@ -2478,12 +2481,14 @@ function mouseDownChecklist (e) {
});
if (checked) {
$sublevel.removeClass('o_checked');
- $parents.prev('ul.o_checklist li').removeClass('o_checked');
+ do {
+ $parents = $parents.prev('ul.o_checklist li').removeClass('o_checked');
+ } while ($parents.length);
} else {
$sublevel.addClass('o_checked');
var $lis;
do {
- $lis = $parents.not(':has(li:not(.o_checked))').prev('ul.o_checklist li:not(.o_checked)');
+ $lis = $parents.not(':has(li[id^="checklist-id"]:not(.o_checked))').prev('ul.o_checklist li:not(.o_checked)');
$lis.addClass('o_checked');
} while ($lis.length);
}
diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/crop_dialog.js b/addons/web_editor/static/src/js/wysiwyg/widgets/crop_dialog.js
index 19623d05f5e..9f827410221 100644
--- a/addons/web_editor/static/src/js/wysiwyg/widgets/crop_dialog.js
+++ b/addons/web_editor/static/src/js/wysiwyg/widgets/crop_dialog.js
@@ -14,12 +14,6 @@ var CropImageDialog = Dialog.extend({
xmlDependencies: Dialog.prototype.xmlDependencies.concat(
['/web_editor/static/src/xml/wysiwyg.xml']
),
- jsLibs: [
- '/web_editor/static/lib/cropper/js/cropper.js',
- ],
- cssLibs: [
- '/web_editor/static/lib/cropper/css/cropper.css',
- ],
events: _.extend({}, Dialog.prototype.events, {
'click .o_crop_options [data-event]': '_onCropOptionClick',
}),
@@ -116,8 +110,9 @@ var CropImageDialog = Dialog.extend({
}
}
this.$cropperImage.cropper({
- viewMode: 1,
- autoCropArea: 1,
+ viewMode: 2,
+ dragMode: 'move',
+ autoCropArea: 1.0,
aspectRatio: ratio,
data: _.pick(data, 'x', 'y', 'width', 'height', 'rotate', 'scaleX', 'scaleY')
});
diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/media.js b/addons/web_editor/static/src/js/wysiwyg/widgets/media.js
index cbb39ea6173..c291a00a0c7 100644
--- a/addons/web_editor/static/src/js/wysiwyg/widgets/media.js
+++ b/addons/web_editor/static/src/js/wysiwyg/widgets/media.js
@@ -1009,8 +1009,9 @@ var VideoWidget = MediaWidget.extend({
/**
* @constructor
*/
- init: function (parent, media) {
+ init: function (parent, media, options) {
this._super.apply(this, arguments);
+ this.isForBgVideo = !!options.isForBgVideo;
this._onVideoCodeInput = _.debounce(this._onVideoCodeInput, 1000);
},
/**
@@ -1021,7 +1022,7 @@ var VideoWidget = MediaWidget.extend({
if (this.media) {
var $media = $(this.media);
- var src = $media.data('oe-expression') || $media.data('src') || '';
+ var src = $media.data('oe-expression') || $media.data('src') || ($media.is('iframe') ? $media.attr('src') : '') || '';
this.$('textarea#o_video_text').val(src);
this.$('input#o_video_autoplay').prop('checked', src.indexOf('autoplay=1') >= 0);
@@ -1047,6 +1048,9 @@ var VideoWidget = MediaWidget.extend({
*/
save: function () {
this._updateVideo();
+ if (this.isForBgVideo) {
+ return Promise.resolve({bgVideoSrc: this.$content.attr('src')});
+ }
if (this.$('.o_video_dialog_iframe').is('iframe')) {
this.$media = $(
'<div class="media_iframe_video" data-oe-expression="' + this.$content.attr('src') + '">' +
@@ -1135,7 +1139,7 @@ var VideoWidget = MediaWidget.extend({
$video.attr('src', vinMatch[0] + '/embed/simple');
videoType = 'vin';
} else if (vimMatch && vimMatch[3].length) {
- $video.attr('src', '//player.vimeo.com/video/' + vimMatch[3] + autoplay);
+ $video.attr('src', '//player.vimeo.com/video/' + vimMatch[3] + autoplay.replace('mute', 'muted'));
videoType = 'vim';
} else if (dmMatch && dmMatch[2].length) {
var justId = dmMatch[2].replace('video/', '');
@@ -1200,13 +1204,13 @@ var VideoWidget = MediaWidget.extend({
var url = embedMatch ? embedMatch[1] : code;
var query = this._createVideoNode(url, {
- autoplay: this.$('input#o_video_autoplay').is(':checked'),
- hide_controls: this.$('input#o_video_hide_controls').is(':checked'),
- loop: this.$('input#o_video_loop').is(':checked'),
- hide_fullscreen: this.$('input#o_video_hide_fullscreen').is(':checked'),
- hide_yt_logo: this.$('input#o_video_hide_yt_logo').is(':checked'),
- hide_dm_logo: this.$('input#o_video_hide_dm_logo').is(':checked'),
- hide_dm_share: this.$('input#o_video_hide_dm_share').is(':checked'),
+ 'autoplay': this.isForBgVideo || this.$('input#o_video_autoplay').is(':checked'),
+ 'hide_controls': this.isForBgVideo || this.$('input#o_video_hide_controls').is(':checked'),
+ 'loop': this.isForBgVideo || this.$('input#o_video_loop').is(':checked'),
+ 'hide_fullscreen': this.isForBgVideo || this.$('input#o_video_hide_fullscreen').is(':checked'),
+ 'hide_yt_logo': this.isForBgVideo || this.$('input#o_video_hide_yt_logo').is(':checked'),
+ 'hide_dm_logo': this.isForBgVideo || this.$('input#o_video_hide_dm_logo').is(':checked'),
+ 'hide_dm_share': this.isForBgVideo || this.$('input#o_video_hide_dm_share').is(':checked'),
});
var $optBox = this.$('.o_video_dialog_options');
@@ -1222,8 +1226,9 @@ var VideoWidget = MediaWidget.extend({
// Individually show / hide options base on the video provider
$optBox.find('div.o_' + query.type + '_option').removeClass('d-none');
- // Hide the entire options box if no options are available
- $optBox.toggleClass('d-none', $optBox.find('div:not(.d-none)').length === 0);
+ // Hide the entire options box if no options are available or if the
+ // dialog is opened for a background-video
+ $optBox.toggleClass('d-none', this.isForBgVideo || $optBox.find('div:not(.d-none)').length === 0);
if (query.type === 'yt') {
// Youtube only: If 'hide controls' is checked, hide 'fullscreen'
diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js b/addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js
index 84dd8e7f650..6d1f2b642c7 100644
--- a/addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js
+++ b/addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js
@@ -76,7 +76,7 @@ var MediaDialog = Dialog.extend({
this.activeWidget = this.imageWidget;
} else if (this.documentWidget && $media.is('a.o_image')) {
this.activeWidget = this.documentWidget;
- } else if (this.videoWidget && $media.hasClass('media_iframe_video')) {
+ } else if (this.videoWidget && $media.is('.media_iframe_video, .o_bg_video_iframe')) {
this.activeWidget = this.videoWidget;
} else if (this.iconWidget && $media.is('span, i')) {
this.activeWidget = this.iconWidget;
diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
index d33f5455da4..361355c4cab 100644
--- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
+++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
@@ -78,9 +78,7 @@ var Wysiwyg = Widget.extend({
this.$editor.data('oe-id', options.recordInfo.res_id);
$(document).on('mousedown', this._blur);
this._value = this.$target.html() || this.$target.val();
- return this._super.apply(this, arguments).then(() => {
- this.$editor.trigger('mouseup');
- });
+ return this._super.apply(this, arguments);
},
/**
* @override
diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js
index 4b095029432..df6d243ffc6 100644
--- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js
+++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js
@@ -20,8 +20,6 @@ Wysiwyg.include({
await this.editor.prependTo(this.$editor[0].ownerDocument.body);
this.options.toolbarHandler.append(this.editor.$el);
this.$el.on('content_changed', function (e) {
- var $editable = $(e.target).closest('[data-oe-model], .o_editable');
- $editable.addClass('o_dirty');
self.trigger_up('wysiwyg_change');
});
} else {