[IMP] website, *: allow to set background video to snippets
* web_editor This commit adds support to set background videos to all the snippets that already support setting background images (except on parallax background for now). task-1920630 closes odoo/odoo#36261 Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com> Co-authored-by: Varun Raval <var@odoo.com> Co-authored-by: qsm-odoo <qsm@odoo.com>
This commit is contained in:
committed by
qsm-odoo
co-authored by
Varun Raval
qsm-odoo
parent
ee8106bd7a
commit
17237ff31c
@@ -838,23 +838,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 +866,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
|
||||
@@ -896,6 +882,37 @@ registry.background = SnippetOption.extend({
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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 +958,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);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -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') + '">' +
|
||||
@@ -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'
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -383,7 +383,7 @@
|
||||
<textarea class="form-control" id="o_video_text" placeholder="Copy-paste your URL or embed code here"/>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<small class="text-muted">Accepts <b><i>Youtube</i></b>, <b><i>Instagram</i></b>, <b><i>Vine.co</i></b>, <b><i>Vimeo</i></b>, <b><i>Dailymotion</i></b> and <b><i>Youku</i></b> videos</small>
|
||||
<small class="text-muted">Accepts <b><i>Youtube</i></b>, <b><i>Vimeo</i></b>, <b><i>Dailymotion</i></b> and <b><i>Youku</i></b> videos</small>
|
||||
</div>
|
||||
<div class="o_video_dialog_options d-none mt-4">
|
||||
<div class="o_yt_option o_vim_option o_dm_option">
|
||||
|
||||
@@ -6,9 +6,9 @@ odoo.define('website.content.snippets.animation', function (require) {
|
||||
*/
|
||||
|
||||
var Class = require('web.Class');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var mixins = require('web.mixins');
|
||||
var utils = require('web.utils');
|
||||
var publicWidget = require('web.public.widget');
|
||||
var utils = require('web.utils');
|
||||
|
||||
@@ -681,6 +681,113 @@ registry.mediaVideo = publicWidget.Widget.extend({
|
||||
},
|
||||
});
|
||||
|
||||
registry.backgroundVideo = publicWidget.Widget.extend({
|
||||
selector: '.o_background_video',
|
||||
xmlDependencies: ['/website/static/src/xml/website.background.video.xml'],
|
||||
disabledInEditableMode: false,
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var proms = [this._super(...arguments)];
|
||||
|
||||
this.videoSrc = this.el.dataset.bgVideoSrc;
|
||||
this.iframeID = _.uniqueId('o_bg_video_iframe_');
|
||||
|
||||
this.isYoutubeVideo = this.videoSrc.indexOf('youtube') >= 0;
|
||||
this.isMobileEnv = config.device.size_class <= config.device.SIZES.LG && config.device.touch;
|
||||
if (this.isYoutubeVideo && this.isMobileEnv) {
|
||||
this.videoSrc = this.videoSrc + "&enablejsapi=1";
|
||||
|
||||
if (!window.YT) {
|
||||
proms.push(new Promise(resolve => {
|
||||
window.onYouTubeIframeAPIReady = () => resolve();
|
||||
}));
|
||||
$('<script/>', {
|
||||
src: 'https://www.youtube.com/iframe_api',
|
||||
}).appendTo('head');
|
||||
}
|
||||
}
|
||||
|
||||
$(window).on('resize.' + this.iframeID, _.throttle(() => this._adjustIframe(), 50));
|
||||
|
||||
return Promise.all(proms).then(() => this._appendBgVideo());
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
$(window).off('resize.' + this.iframeID);
|
||||
|
||||
if (this.$bgVideoContainer) {
|
||||
this.$bgVideoContainer.remove();
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Adjusts iframe sizes and position so that it fills the container and so
|
||||
* that it is centered in it.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_adjustIframe: function () {
|
||||
// Adjust the iframe
|
||||
var wrapperWidth = this.$target.innerWidth();
|
||||
var wrapperHeight = this.$target.innerHeight();
|
||||
var relativeRatio = (wrapperWidth / wrapperHeight) / (16 / 9);
|
||||
var style = {};
|
||||
if (relativeRatio >= 1.0) {
|
||||
style['width'] = '100%';
|
||||
style['height'] = (relativeRatio * 100) + '%';
|
||||
style['left'] = '0';
|
||||
style['top'] = (-(relativeRatio - 1.0) / 2 * 100) + '%';
|
||||
} else {
|
||||
style['width'] = ((1 / relativeRatio) * 100) + '%';
|
||||
style['height'] = '100%';
|
||||
style['left'] = (-((1 / relativeRatio) - 1.0) / 2 * 100) + '%';
|
||||
style['top'] = '0';
|
||||
}
|
||||
this.$iframe.css(style);
|
||||
},
|
||||
/**
|
||||
* Append background video related elements to the target.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_appendBgVideo: function () {
|
||||
var $oldContainer = this.$bgVideoContainer || this.$('> .o_bg_video_container');
|
||||
this.$bgVideoContainer = $(qweb.render('website.background.video', {
|
||||
videoSrc: this.videoSrc,
|
||||
iframeID: this.iframeID,
|
||||
}));
|
||||
this.$iframe = this.$bgVideoContainer.find('.o_bg_video_iframe');
|
||||
this.$iframe.one('load', () => {
|
||||
this.$bgVideoContainer.find('.o_bg_video_loading').remove();
|
||||
});
|
||||
this.$bgVideoContainer.prependTo(this.$target);
|
||||
$oldContainer.remove();
|
||||
|
||||
this._adjustIframe();
|
||||
|
||||
// YouTube does not allow to auto-play video in mobile devices, so we
|
||||
// have to play the video manually.
|
||||
if (this.isMobileEnv && this.isYoutubeVideo) {
|
||||
new window.YT.Player(this.iframeID, {
|
||||
events: {
|
||||
onReady: ev => ev.target.playVideo(),
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
registry.ul = publicWidget.Widget.extend({
|
||||
selector: 'ul.o_ul_folded, ol.o_ul_folded',
|
||||
events: {
|
||||
|
||||
@@ -29,6 +29,102 @@ options.Class.include({
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.background.include({
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_getEditableMedia: function () {
|
||||
if (!this._hasBgvideo()) {
|
||||
return this._super(...arguments);
|
||||
}
|
||||
return this.$('.o_bg_video_iframe')[0];
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_getMediaDialogOptions: function () {
|
||||
return _.extend(this._super(...arguments), {
|
||||
// For now, disable the possibility to have a parallax video bg
|
||||
noVideos: this.$target.is('.parallax, .s_parallax_bg'),
|
||||
isForBgVideo: true,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super(...arguments);
|
||||
if (this._hasBgvideo()) {
|
||||
this.$el.find('[data-choose-image]').addClass('active');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Updates the background video used by the snippet.
|
||||
*
|
||||
* @private
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
_setBgVideo: function (previewMode, value) {
|
||||
this.$('> .o_bg_video_container').toggleClass('d-none', previewMode === true);
|
||||
|
||||
if (previewMode !== false) {
|
||||
return;
|
||||
}
|
||||
|
||||
var target = this.$target[0];
|
||||
target.classList.toggle('o_background_video', !!(value && value.length));
|
||||
if (value && value.length) {
|
||||
target.dataset.bgVideoSrc = value;
|
||||
} else {
|
||||
delete target.dataset.bgVideoSrc;
|
||||
}
|
||||
this._refreshPublicWidgets();
|
||||
this._setActive();
|
||||
},
|
||||
/**
|
||||
* Returns whether the current target has a background video or not.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_hasBgvideo: function () {
|
||||
return this.$target[0].classList.contains('o_background_video');
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_onBackgroundColorUpdate: function (ev, previewMode) {
|
||||
var ret = this._super(...arguments);
|
||||
if (ret) {
|
||||
this._setBgVideo(previewMode);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_onSaveMediaDialog: function (data) {
|
||||
if (!data.bgVideoSrc) {
|
||||
this._setBgVideo(false);
|
||||
this._super(...arguments);
|
||||
return;
|
||||
}
|
||||
// if the user chose a video, only add the video without removing the
|
||||
// background
|
||||
this._setBgVideo(false, data.bgVideoSrc);
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.menu_data = options.Class.extend({
|
||||
xmlDependencies: ['/website/static/src/xml/website.editor.xml'],
|
||||
|
||||
|
||||
@@ -257,6 +257,26 @@ font[class*='bg-'] {
|
||||
}
|
||||
}
|
||||
|
||||
// Background videos
|
||||
.o_background_video {
|
||||
position: relative;
|
||||
|
||||
> * {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
.o_bg_video_container {
|
||||
@include o-position-absolute(0, 0 ,0 ,0);
|
||||
overflow: hidden;
|
||||
}
|
||||
.o_bg_video_iframe {
|
||||
position: relative;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
.o_bg_video_loading {
|
||||
@include o-position-absolute(0, 0 ,0 ,0);
|
||||
}
|
||||
|
||||
// Probably outdated
|
||||
// Lists
|
||||
.o_ul_toggle {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="website.background.video">
|
||||
<div class="o_bg_video_container">
|
||||
<div class="o_bg_video_loading d-flex justify-content-center align-items-center text-primary">
|
||||
<div class="spinner-border" style="width: 4em; height: 4em;" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<iframe t-att-id="iframeID"
|
||||
class="o_bg_video_iframe"
|
||||
frameBorder="0"
|
||||
t-att-src="videoSrc"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -1317,7 +1317,7 @@
|
||||
data-selector="section, .parallax, :not(.o_gallery > .container) > .carousel"
|
||||
data-exclude=".s_hr, .s_image_gallery">
|
||||
<we-button data-choose-image="true" data-no-preview="true">
|
||||
<i class="fa fa-fw fa-picture-o"/> Background Image
|
||||
<i class="fa fa-fw fa-picture-o"/> Background
|
||||
</we-button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user