[ADD] theme saas-12.3
|
After Width: | Height: | Size: 997 KiB |
|
After Width: | Height: | Size: 135 B |
|
After Width: | Height: | Size: 821 B |
|
After Width: | Height: | Size: 977 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 932 B |
|
After Width: | Height: | Size: 921 B |
|
After Width: | Height: | Size: 174 B |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 915 B |
|
After Width: | Height: | Size: 255 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1022 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 597 B |
|
After Width: | Height: | Size: 974 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 386 B |
|
After Width: | Height: | Size: 285 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 391 B |
|
After Width: | Height: | Size: 333 B |
|
After Width: | Height: | Size: 404 B |
|
After Width: | Height: | Size: 468 B |
|
After Width: | Height: | Size: 157 B |
|
After Width: | Height: | Size: 979 B |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 389 B |
|
After Width: | Height: | Size: 961 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 180 B |
|
After Width: | Height: | Size: 994 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1018 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 922 B |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 234 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 292 B |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,19 @@
|
||||
odoo.define('theme_common.compatibility_editor', function (require) {
|
||||
'use strict';
|
||||
|
||||
var sOptions = require('web_editor.snippets.options');
|
||||
|
||||
sOptions.registry.background.include({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
background: function (previewMode, value, $opt) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
var customClass = this.$target.attr('class').match(/\b(bg-img-\d+)\b/);
|
||||
if (customClass) {
|
||||
this.$target.removeClass(customClass[1]);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
odoo.define('theme_common.s_animated_boxes_editor', function (require) {
|
||||
'use strict';
|
||||
|
||||
var s_options = require('web_editor.snippets.options');
|
||||
|
||||
s_options.registry.s_animated_boxes = s_options.Class.extend({
|
||||
start: function () {
|
||||
var self = this;
|
||||
self.add_btn_edit_slide();
|
||||
setTimeout(function () {
|
||||
if (self.$target.find(".slide").length > 0) {
|
||||
self.$overlay.find(".createSlide").addClass("hide");
|
||||
self.$overlay.find(".deleteSlide").removeClass("hide");
|
||||
self.$overlay.find(".btn-showSlide").removeClass("hide");
|
||||
} else {
|
||||
self.$overlay.find(".createSlide").removeClass("hide");
|
||||
self.$overlay.find(".deleteSlide").addClass("hide");
|
||||
self.$overlay.find(".btn-showSlide").addClass("hide");
|
||||
}
|
||||
}, 100);
|
||||
},
|
||||
|
||||
add_btn_edit_slide: function () {
|
||||
var self = this;
|
||||
self.$overlay.find(".oe_options").after('<a href="#" class="btn btn-secondary btn-showSlide" title="Lock slide" style="width: auto; padding: 0 5px;"><i class="fa fa-toggle-off"></i> Edit Slide</a> ');
|
||||
self.$overlay.on('click', '.btn-showSlide', _.bind(this.toggle_slide, this));
|
||||
},
|
||||
|
||||
create_slide: function (previewMode, value, $li) {
|
||||
var self = this,
|
||||
slide = '<div class="slide editable bg-success animated text-center dur-200ms insetShadow" anim="o_anim_fade_in_down" del="0" dur="200"><div class="v-align"><h3>Your new slide</h3></div></div>';
|
||||
self.$target.append(slide);
|
||||
self.$overlay.find(".btn-showSlide").removeClass("hide");
|
||||
self.$overlay.find(".createSlide").addClass("hide");
|
||||
self.$overlay.find(".deleteSlide").removeClass("hide");
|
||||
},
|
||||
|
||||
delete_slide: function (previewMode, value, $li) {
|
||||
var self = this;
|
||||
self.$target.find(".slide").remove();
|
||||
self.$overlay.find(".btn-showSlide").addClass("hide");
|
||||
self.$overlay.find(".createSlide").removeClass("hide");
|
||||
self.$overlay.find(".deleteSlide").addClass("hide");
|
||||
},
|
||||
|
||||
toggle_slide: function () {
|
||||
var a = this.$overlay.find(".btn-showSlide"),
|
||||
i = a.find("i"),
|
||||
t = this.$target,
|
||||
s = t.find(".slide");
|
||||
if (!t.hasClass("showSlide")) {
|
||||
t.addClass("showSlide"); i.addClass("fa-toggle-on").removeClass("fa-toggle-off"); i.css("color", "#FFF");
|
||||
s.addClass("visible");
|
||||
} else {
|
||||
t.removeClass("showSlide"); i.removeClass("fa-toggle-on").addClass("fa-toggle-off"); i.css("color", "");
|
||||
s.removeClass("visible");
|
||||
}
|
||||
},
|
||||
onFocus: function () {
|
||||
this._adapt_items_height();
|
||||
},
|
||||
onBlur: function () {
|
||||
this._adapt_items_height();
|
||||
},
|
||||
cleanForSave: function() {
|
||||
this.$target.removeClass("showSlide")
|
||||
.find(".slide").removeClass("visible");
|
||||
|
||||
this._adapt_items_height();
|
||||
},
|
||||
_adapt_items_height: function () {
|
||||
var $items = this.$target.closest(".s_animated_boxes").find(".item");
|
||||
var min_height = _.reduce($items.find(".v-align"), function (memo, item) {
|
||||
return Math.max(memo, $(item).outerHeight(true) + 40);
|
||||
}, 200);
|
||||
$items.css("height", min_height);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
odoo.define('theme_common.s_animated_boxes_frontend', function (require) {
|
||||
'use strict';
|
||||
|
||||
var publicWidget = require('web.public.widget');
|
||||
|
||||
/**
|
||||
* This is a fix for some apple devices.
|
||||
* The slide of animated boxes widget is shown on hover or click on mobile
|
||||
* devices, but iOS only activates :hover if the element targeted is
|
||||
* clickable.
|
||||
*/
|
||||
publicWidget.registry._fixAnimatedBoxesAppleClick = publicWidget.registry._fixAppleCollapse.extend({
|
||||
selector: '.s_animated_boxes .item',
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
odoo.define('theme_common.s_css_slider_editor', function (require) {
|
||||
'use strict';
|
||||
|
||||
var s_options = require('web_editor.snippets.options');
|
||||
|
||||
s_options.registry.s_css_slider_slide = s_options.Class.extend({
|
||||
newSlideUrl: "/web/image/theme_common.image_content_23",
|
||||
|
||||
start: function () {
|
||||
var self = this;
|
||||
|
||||
self.reset();
|
||||
this.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: this.$target,
|
||||
});
|
||||
|
||||
setTimeout(function () {
|
||||
self.$overlay.find(".js_s_css_slider_addSlide").on('click', function () { self.addSlide(); });
|
||||
self.$overlay.find(".js_s_css_slider_removeSlide").on('click', function () { self.removeSlide(); });
|
||||
}, 100);
|
||||
|
||||
$(document.body).on("media-saved", function () {
|
||||
self.resizeImgsEditor();
|
||||
});
|
||||
},
|
||||
|
||||
resizeImgsEditor: function () {
|
||||
var self = this;
|
||||
setTimeout(function () {
|
||||
self.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: self.$target,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
var self= this;
|
||||
self.$target.find(".s_css_slider_pagination").remove().end()
|
||||
.find("span").unbind("click.s_css");
|
||||
},
|
||||
|
||||
addSlide: function () {
|
||||
var self = this;
|
||||
var $currentSlide = self.$target.find(".slider .slide.selected");
|
||||
$currentSlide.clone().insertAfter($currentSlide).removeClass("selected");
|
||||
|
||||
_.defer(function () {
|
||||
self.reset();
|
||||
self.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: self.$target,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
removeSlide: function () {
|
||||
var self = this;
|
||||
var $currentSlide = self.$target.find(".slider .slide.selected");
|
||||
|
||||
$currentSlide.remove();
|
||||
_.defer(function () {
|
||||
self.reset();
|
||||
self.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: self.$target,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
cleanForSave: function () {
|
||||
var self = this;
|
||||
self.$target
|
||||
.find(".s_css_slider_pagination").remove().end()
|
||||
.find(".navigation .prev").addClass("inactive").end()
|
||||
.find(".navigation .next").removeClass("inactive").end()
|
||||
.find(".slider .slide").removeClass("selected move-left").end()
|
||||
.find(".slider .slide").first().addClass("selected");
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,122 @@
|
||||
odoo.define('theme_common.s_css_slider_frontend', function (require) {
|
||||
'use strict';
|
||||
|
||||
var publicWidget = require('web.public.widget');
|
||||
|
||||
publicWidget.registry.s_css_slider = publicWidget.Widget.extend({
|
||||
selector: ".s_css_slider",
|
||||
disabledInEditableMode: false,
|
||||
|
||||
start: function () {
|
||||
var self = this;
|
||||
var $container = self.$target;
|
||||
$container.find(".s_css_slider_pagination").remove();
|
||||
// create slider pagination
|
||||
var sliderPagination = self.createSliderPagination($container);
|
||||
self.bindEvents($container, sliderPagination);
|
||||
$(window).on("resize", function () {
|
||||
self.resizeImgs($container);
|
||||
}).trigger("resize");
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
bindEvents: function ($container, sliderPagination) {
|
||||
var self = this,
|
||||
$next_btn = $container.find('.next'),
|
||||
$prev_btn = $container.find('.prev');
|
||||
|
||||
$next_btn.on('click.s_css', function (e) {
|
||||
self.nextSlide($container, sliderPagination);
|
||||
});
|
||||
|
||||
$prev_btn.on('click.s_css', function (e) {
|
||||
self.prevSlide($container, sliderPagination);
|
||||
});
|
||||
|
||||
if ($container.hasClass("autoplay") && this.editableMode !== true) {
|
||||
var interval;
|
||||
var autoplay = function () {
|
||||
interval = setInterval(function () {
|
||||
if (!$next_btn.hasClass("inactive")) {
|
||||
self.nextSlide($container, sliderPagination);
|
||||
} else {
|
||||
self.prevSlide($container, sliderPagination, 0);
|
||||
}
|
||||
}, 3000);
|
||||
};
|
||||
autoplay();
|
||||
$container.hover(function () { clearInterval(interval); });
|
||||
$container.mouseleave(function () { autoplay(); });
|
||||
}
|
||||
|
||||
sliderPagination.on('click.s_css', function () {
|
||||
var selectedDot = $(this);
|
||||
if (!selectedDot.hasClass('selected')) {
|
||||
var selectedPosition = selectedDot.index(),
|
||||
activePosition = $container.find('.slider .selected').index();
|
||||
if ( activePosition < selectedPosition) {
|
||||
self.nextSlide($container, sliderPagination, selectedPosition);
|
||||
} else {
|
||||
self.prevSlide($container, sliderPagination, selectedPosition);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
resizeImgs: function ($container) {
|
||||
var cont_h = $container.height(),
|
||||
imgs = $container.find(".slide img");
|
||||
|
||||
imgs.each(function () {
|
||||
var $img = $(this),
|
||||
img_h = $img.height();
|
||||
if (img_h > cont_h) {
|
||||
$img.css("width", "100%");
|
||||
$img.css("margin-top", (cont_h - img_h)/2);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
createSliderPagination: function ($container) {
|
||||
var wrapper = $('<ul class="s_css_slider_pagination"></ul>').insertAfter($container.find('.navigation'));
|
||||
$container.find('.slider .slide').each(function (index) {
|
||||
var dotWrapper = (index === 0) ? $('<li class="selected"></li>') : $('<li></li>'),
|
||||
dot = $('<a href="#0"></a>').appendTo(dotWrapper);
|
||||
dotWrapper.appendTo(wrapper);
|
||||
dot.text(index+1);
|
||||
});
|
||||
return wrapper.children('li');
|
||||
},
|
||||
|
||||
nextSlide: function ($container, $pagination, $n) {
|
||||
var self = this,
|
||||
visibleSlide = $container.find('.slider .selected'),
|
||||
navigationDot = $container.find('.s_css_slider_pagination .selected');
|
||||
|
||||
if (typeof $n === 'undefined') $n = visibleSlide.index() + 1;
|
||||
visibleSlide.removeClass('selected');
|
||||
$container.find('.slider .slide').eq($n).addClass('selected').prevAll().addClass('move-left');
|
||||
navigationDot.removeClass('selected');
|
||||
$pagination.eq($n).addClass('selected');
|
||||
self.updateNavigation($container, $container.find('.slider .slide').eq($n));
|
||||
},
|
||||
|
||||
prevSlide: function ($container, $pagination, $n) {
|
||||
var self = this,
|
||||
visibleSlide = $container.find('.slider .selected'),
|
||||
navigationDot = $container.find('.s_css_slider_pagination .selected');
|
||||
|
||||
if (typeof $n === 'undefined') $n = visibleSlide.index() - 1;
|
||||
visibleSlide.removeClass('selected');
|
||||
$container.find('.slider .slide').eq($n).addClass('selected').removeClass('move-left').nextAll().removeClass('move-left');
|
||||
navigationDot.removeClass('selected');
|
||||
$pagination.eq($n).addClass('selected');
|
||||
self.updateNavigation($container, $container.find('.slider .slide').eq($n));
|
||||
},
|
||||
|
||||
updateNavigation: function ($container, $active) {
|
||||
$container.find('.prev').toggleClass('inactive', $active.is(':first-child'));
|
||||
$container.find('.next').toggleClass('inactive', $active.is(':last-child'));
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,149 @@
|
||||
odoo.define('theme_common.s_google_map_editor', function (require) {
|
||||
'use strict';
|
||||
|
||||
var Dialog = require('web_editor.widget').Dialog;
|
||||
var core = require('web.core');
|
||||
var sOptions = require('web_editor.snippets.options');
|
||||
var googleScriptLoaded = require('theme_common.s_google_map_frontend').googleScriptLoaded;
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
sOptions.registry.map = sOptions.Class.extend({
|
||||
xmlDependencies: ['/theme_common/static/src/xml/s_google_map_modal.xml'],
|
||||
defaultLocation: '(50.854975,4.3753899)',
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
onBuilt: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.map('click', null, null);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Options
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Opens the customization dialog.
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
map: function (previewMode, value, $opt) {
|
||||
var self = this;
|
||||
|
||||
this.dialog = new Dialog(this, {
|
||||
size: 'medium',
|
||||
title: _t("Customize your map"),
|
||||
buttons: [
|
||||
{text: _t("Save"), classes: 'btn-primary', close: true, click: function () {
|
||||
if (!this.$('#placeBk').val()) {
|
||||
this.$('#placeBk').val(self.defaultLocation);
|
||||
}
|
||||
self.$target.attr({
|
||||
'data-map-gps': this.$('#placeBk').val(),
|
||||
'data-pin-style': this.$('#pin_style').val(),
|
||||
'data-pin-address': this.$('#pin_address').val(),
|
||||
});
|
||||
self.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: self.$target,
|
||||
});
|
||||
}},
|
||||
{text: _t("Cancel"), close: true}
|
||||
],
|
||||
$content: $(core.qweb.render('theme_common.s_google_map_modal'))
|
||||
});
|
||||
|
||||
this.dialog.opened().then((function () {
|
||||
this.$('#pin_address').val(self.$target.attr('data-pin-address'));
|
||||
this.$('#pin_style').val(self.$target.attr('data-pin-style'));
|
||||
this.$('#placeBk').val(self.$target.attr('data-map-gps'));
|
||||
var autocomplete = new google.maps.places.Autocomplete(this.$('#pin_address').get(0), {types: ['geocode']});
|
||||
google.maps.event.addListener(autocomplete, 'place_changed', function () {
|
||||
var place = autocomplete.getPlace();
|
||||
self.dialog.$('#placeBk').val(place.geometry ? place.geometry.location : self.defaultLocation);
|
||||
});
|
||||
}).bind(this.dialog));
|
||||
|
||||
googleScriptLoaded.then(function () {
|
||||
self.dialog.open();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Adapts map's type.
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
mapType: function (previewMode, value, $opt) {
|
||||
this.$target.attr('data-map-type', value);
|
||||
this.$target.attr('data-map-color', '');
|
||||
this.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: this.$target,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Adapts map's color.
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
mapColor: function (previewMode, value, $opt) {
|
||||
this.$target.attr('data-map-color', value);
|
||||
this.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: this.$target,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Adapts map's zoom.
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
mapZoom: function (previewMode, value, $opt) {
|
||||
this.$target.attr('data-map-zoom', value);
|
||||
this.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: this.$target,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Adapts map's location.
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
mapGps: function (previewMode, value, $opt) {
|
||||
this.$target.attr('data-map-gps', value);
|
||||
this.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: this.$target,
|
||||
});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this.$el.find('[data-map-type]')
|
||||
.removeClass('active')
|
||||
.filter('[data-map-type="' + this.$target.attr('data-map-type') + '"]')
|
||||
.addClass('active');
|
||||
this.$el.find('[data-map-color]')
|
||||
.removeClass('active')
|
||||
.filter('[data-map-color="' + this.$target.attr('data-map-color') + '"]')
|
||||
.addClass('active');
|
||||
this.$el.find('[data-map-zoom]')
|
||||
.removeClass('active')
|
||||
.filter('[data-map-zoom="' + this.$target.attr('data-map-zoom') + '"]')
|
||||
.addClass('active');
|
||||
this.$el.find('[data-map-gps]')
|
||||
.removeClass('active')
|
||||
.filter('[data-map-gps="' + this.$target.attr('data-map-gps') + '"]')
|
||||
.addClass('active');
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
odoo.define('theme_common.s_google_map_frontend', function (require) {
|
||||
'use strict';
|
||||
|
||||
var core = require('web.core');
|
||||
var publicWidget = require('web.public.widget');
|
||||
var googleScriptLoaded = $.Deferred();
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
publicWidget.registry.s_google_map = publicWidget.Widget.extend({
|
||||
selector: '.s_google_map',
|
||||
disabledInEditableMode: false,
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
var defs = [this._super.apply(this, arguments)];
|
||||
|
||||
if (typeof google !== 'object' || typeof google.maps !== 'object') {
|
||||
if (!publicWidget.registry.s_google_map.isScriptLoading) {
|
||||
publicWidget.registry.s_google_map.isScriptLoading = true;
|
||||
window.odoo_s_google_map_redraw_all = function odoo_s_google_map_redraw_all() {
|
||||
self.trigger_up('widgets_start_request', {
|
||||
editableMode: self.editableMode,
|
||||
$target: $('section.s_google_map'),
|
||||
});
|
||||
publicWidget.registry.s_google_map.isScriptLoading = false;
|
||||
googleScriptLoaded.resolve();
|
||||
};
|
||||
|
||||
defs.push(this._rpc({
|
||||
route: '/theme_common/google_maps_api_key',
|
||||
}).then(function (data) {
|
||||
var key_param = '';
|
||||
var data_json = JSON.parse(data);
|
||||
if (!data_json.google_maps_api_key) {
|
||||
publicWidget.registry.s_google_map.isScriptLoading = false;
|
||||
if (self.editableMode) {
|
||||
self.$loadingWarning = $('<div/>', {
|
||||
class: 'alert alert-warning alert-link',
|
||||
}).append($('<a/>', {
|
||||
href: "#",
|
||||
text: _t("Cannot load google map, check your configuration !"),
|
||||
})).click(function (e) {
|
||||
window.location.href = "/web#action=website.action_website_configuration";
|
||||
});
|
||||
self.$('.map_container').append(self.$loadingWarning);
|
||||
}
|
||||
return;
|
||||
}
|
||||
key_param = '&key=' + data_json.google_maps_api_key;
|
||||
$('head').append($('<script/>', {
|
||||
type: "text/javascript",
|
||||
src: "https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=places&callback=odoo_s_google_map_redraw_all" + key_param,
|
||||
}));
|
||||
}));
|
||||
}
|
||||
return $.when.apply($, defs);
|
||||
}
|
||||
|
||||
// Define a default map's colors set
|
||||
var std = [];
|
||||
new google.maps.StyledMapType(std, {name: "Std Map"});
|
||||
|
||||
// Default options, will be overwritten by the user
|
||||
var myOptions = {
|
||||
zoom: 12,
|
||||
center: new google.maps.LatLng(50.854975,4.3753899),
|
||||
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
||||
panControl: false,
|
||||
zoomControl: false,
|
||||
mapTypeControl: false,
|
||||
streetViewControl: false,
|
||||
scrollwheel: false,
|
||||
mapTypeControlOptions: {
|
||||
mapTypeIds: [google.maps.MapTypeId.ROADMAP, 'map_style']
|
||||
}
|
||||
};
|
||||
|
||||
// Render Map
|
||||
var mapC = this.$target.find('.map_container');
|
||||
var map = new google.maps.Map(mapC.get(0), myOptions);
|
||||
|
||||
// Update GPS position
|
||||
var p = this.$target.attr('data-map-gps').substring(1).slice(0, -1).split(',');
|
||||
var gps = new google.maps.LatLng(p[0], p[1]);
|
||||
map.setCenter(gps);
|
||||
|
||||
// Update Map on screen resize
|
||||
google.maps.event.addDomListener(window, 'resize', function () {
|
||||
map.setCenter(gps);
|
||||
});
|
||||
|
||||
// Create Marker & Infowindow
|
||||
if (this.$target.attr('data-pin-style') === "flat") {
|
||||
new google.maps.Marker({
|
||||
map: map,
|
||||
animation: google.maps.Animation.DROP,
|
||||
position: new google.maps.LatLng(p[0],p[1]),
|
||||
icon: "/theme_common/static/src/img/snippets/s_google_map_marker.png",
|
||||
});
|
||||
} else {
|
||||
new google.maps.Marker({
|
||||
map: map,
|
||||
animation: google.maps.Animation.DROP,
|
||||
position: new google.maps.LatLng(p[0], p[1])
|
||||
});
|
||||
}
|
||||
|
||||
map.setMapTypeId(google.maps.MapTypeId[this.$target.attr('data-map-type')]); // Update Map Type
|
||||
map.setZoom(parseInt(this.$target.attr('data-map-zoom'))); // Update Map Zoom
|
||||
|
||||
// Update Map Color
|
||||
if (this.$target.attr('data-map-color') !== "") {
|
||||
var mapColor = eval("this." + this.$target.attr('data-map-color'));
|
||||
var thisMap = new google.maps.StyledMapType(mapColor, {name: "Styled Map"});
|
||||
map.mapTypes.set('map_style', thisMap);
|
||||
map.setMapTypeId('map_style');
|
||||
}
|
||||
|
||||
return $.when.apply($, defs);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy: function () {
|
||||
this._super.apply(this, arguments);
|
||||
if (this.$loadingWarning) {
|
||||
this.$loadingWarning.remove();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// This allows to save if the google maps script is already loading or not.
|
||||
// This script cannot be loaded 2 times, so if we have 2 snippets in the same
|
||||
// page we don't want to load the script 2 times.
|
||||
// And we cannot put the script in an asset because we don't want to load it
|
||||
// on each page, but only on pages with a google maps snippet...
|
||||
publicWidget.registry.s_google_map.prototype.isScriptLoading = false;
|
||||
|
||||
return {
|
||||
googleScriptLoaded: googleScriptLoaded,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,106 @@
|
||||
odoo.define('theme_common.s_media_block_editor', function (require) {
|
||||
'use strict';
|
||||
|
||||
var core = require('web.core');
|
||||
var s_options = require('web_editor.snippets.options');
|
||||
require("theme_common.s_media_block_frontend");
|
||||
|
||||
var QWeb = core.qweb;
|
||||
|
||||
// MEDIABLOCK EDITOR
|
||||
|
||||
// Image background - Fix for mediablock
|
||||
s_options.registry.background.include({
|
||||
selectClass: function (previewMode, value, $li) {
|
||||
var self = this;
|
||||
this._super(previewMode, value, $li);
|
||||
if (this.$target.hasClass("s_media_block")) {
|
||||
var objs = self.$target.find(".videoBox, > iframe, .yt_video_container");
|
||||
objs.stop(true,true)
|
||||
.fadeOut(200)
|
||||
.delay(300)
|
||||
.fadeIn(2000);
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
s_options.registry.s_media_block = s_options.Class.extend({
|
||||
xmlDependencies: ['/theme_common/static/src/xml/s_media_block_modal.xml'],
|
||||
videoOpt: function (previewMode, value, $opt) {
|
||||
var self = this;
|
||||
self.trigger_up('widgets_stop_request', {
|
||||
editableMode: true,
|
||||
$target: self.$target,
|
||||
});
|
||||
self.$modal = $(QWeb.render("theme_common.s_media_block_modal"));
|
||||
self.$modal.appendTo('body').modal();
|
||||
|
||||
var $src = self.$modal.find('#media_block_src'),
|
||||
$cancel = self.$modal.find("#cancel"),
|
||||
$sub_data = self.$modal.find("#sub_data");
|
||||
var field_names = ['src', 'muted', 'loop', 'controls', 'autoplay', 'opacity', 'background', 'iframefit'];
|
||||
|
||||
_.each(field_names, function (field_name) {
|
||||
var value = self.$target.attr(field_name);
|
||||
var $fields = self.$modal.find('[name="'+field_name+'"]');
|
||||
$fields.each(function (i, field) {
|
||||
var $field = $(field);
|
||||
if ($field.is(':radio, :checkbox')) {
|
||||
$field.prop('checked', $field.val() === value);
|
||||
} else if (!_.isUndefined(value)) {
|
||||
$field.val(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!$src.val()) {
|
||||
// if custom code is empty,provide an example video
|
||||
$src.val('<iframe src="//player.vimeo.com/video/105977011?title=0&byline=0&portrait=0&color=24a8bf&autoplay=1&loop=1" width="500" height="264" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>');
|
||||
self.$target.attr('src', $src.val());
|
||||
}
|
||||
|
||||
$sub_data.on('click', function () {
|
||||
_.each(field_names, function (field_name) {
|
||||
var field = self.$modal.find('[name="'+field_name+'"]').filter(':checked, :not(:checkbox, :radio)');
|
||||
self.$target.removeAttr(field_name).attr(field_name, field.val());
|
||||
});
|
||||
self.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: self.$target,
|
||||
});
|
||||
});
|
||||
|
||||
$cancel.on('click', function () {
|
||||
self.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: self.$target,
|
||||
});
|
||||
});
|
||||
|
||||
var update_form = function () {
|
||||
self.$target.attr('src', this.value);
|
||||
self.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: self.$target,
|
||||
});
|
||||
self.$modal.find('form').attr('data-video-type', self.$target.data('video-type'));
|
||||
};
|
||||
|
||||
$src
|
||||
.on('keyup', _.throttle(update_form, 700))
|
||||
.on('change', function () {
|
||||
update_form.call(this);
|
||||
this.value = self.$target.attr('src');
|
||||
})
|
||||
.keyup();
|
||||
|
||||
this.$modal.on('hidden.bs.modal', function () {
|
||||
self.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: self.$target,
|
||||
});
|
||||
$(this).remove();
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,280 @@
|
||||
odoo.define('theme_common.s_media_block_frontend', function (require) {
|
||||
'use strict';
|
||||
|
||||
var publicWidget = require('web.public.widget');
|
||||
|
||||
// MEDIABLOCK EDITOR
|
||||
publicWidget.registry.s_media_block = publicWidget.Widget.extend({
|
||||
selector : "section.s_media_block",
|
||||
|
||||
start: function () {
|
||||
this.start_video();
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.stop_video();
|
||||
},
|
||||
|
||||
update_video_type: function () {
|
||||
var regex_url = '((?:https?:)?//([^\\s\'"<>\\\\/]+)/([^\\s\'"<>\\\\]+))';
|
||||
var match = this.src.match(new RegExp('\\ssrc=[\'"]?' + regex_url));
|
||||
match = match || this.src.match(new RegExp('^\\s*' + regex_url));
|
||||
if (!match) {
|
||||
this.video_type = "image";
|
||||
this.src = "";
|
||||
return;
|
||||
}
|
||||
|
||||
var url = match[1];
|
||||
var domain = match[2];
|
||||
var path = match[3];
|
||||
|
||||
match = undefined;
|
||||
|
||||
var services_prefix = {
|
||||
youtube: 'https://youtu.be/',
|
||||
vimeo: 'https://vimeo.com/',
|
||||
dailymotion: 'http://dai.ly/',
|
||||
};
|
||||
|
||||
if (/\.youtube(-nocookie)?\./.test(domain)) {
|
||||
this.video_type = 'youtube';
|
||||
match = path.match(/^(?:embed\/|watch\?v=)?([^\/?&#]+)/i);
|
||||
} else if (domain === "youtu.be") {
|
||||
this.video_type = 'youtube';
|
||||
match = path.match(/^([^\/?&#]+)/);
|
||||
} else if (_.str.include(domain, "vimeo.")) {
|
||||
this.video_type = 'vimeo';
|
||||
match = path.match(/^(?:video\/)?([^?&#]+)/i);
|
||||
} else if (_.str.include(domain, ".dailymotion.")) {
|
||||
this.video_type = "dailymotion";
|
||||
match = path.match(/(?:embed\/)?(?:video\/)?([^\/?&#_]+)/i);
|
||||
} else if (domain === "dai.ly") {
|
||||
this.video_type = "dailymotion";
|
||||
match = path.match(/^([^\/?&#]+)/);
|
||||
}
|
||||
|
||||
if (match) {
|
||||
this.src = services_prefix[this.video_type] + match[1];
|
||||
} else if (!/\ssrc=/.test(this.src)) {
|
||||
this.src = url;
|
||||
this.video_type = 'html5';
|
||||
} else {
|
||||
this.video_type = 'other';
|
||||
}
|
||||
|
||||
this.$target.data('video-type', this.video_type);
|
||||
},
|
||||
|
||||
start_video: function () {
|
||||
var self = this;
|
||||
var video_url;
|
||||
|
||||
// mobile phones are not supported, just show the background image.
|
||||
if (jQuery.browser.mobile) { return; }
|
||||
|
||||
if (!self.video_type || self.src !== self.$target.attr('src')) {
|
||||
self.src = self.$target.attr('src');
|
||||
if (!self.src) {
|
||||
return;
|
||||
}
|
||||
self.update_video_type();
|
||||
}
|
||||
|
||||
var params = _.chain(['muted', 'loop', 'autoplay', 'controls']).map(function (attribute) {
|
||||
var value = self.$target.attr(attribute);
|
||||
return [attribute, value ? 1 : value];
|
||||
}).object().value();
|
||||
|
||||
var video_id = self.src.split('/')[3];
|
||||
|
||||
switch (self.video_type) {
|
||||
case 'html5':
|
||||
video_url = self.src;
|
||||
break;
|
||||
case 'youtube':
|
||||
video_url = "https://www.youtube.com/embed/" + video_id;
|
||||
if ('muted' in params) params['mute'] = 1;
|
||||
if ('loop' in params) params['playlist'] = video_id;
|
||||
_.extend(params, {iv_load_policy: 3, rel: 0, showinfo: 0});
|
||||
break;
|
||||
case 'vimeo':
|
||||
video_url = "https://player.vimeo.com/video/" + video_id;
|
||||
_.extend(params, {color: 'fff', title: 0, byline: 0, portrait: 0, badge: 0});
|
||||
break;
|
||||
case 'dailymotion':
|
||||
video_url = "https://www.dailymotion.com/embed/video/" + video_id;
|
||||
if (!params["controls"]) params["chromeless"] = 1;
|
||||
_.extend(params, {logo: 0, info: 0, related: 0, wmode: 'opaque'});
|
||||
break;
|
||||
}
|
||||
|
||||
var whenPlayerReady = (self['create_' + self.video_type + '_video'] || self['create_video']).call(self, self.$target, video_url, params);
|
||||
|
||||
whenPlayerReady.then(function ($player) {
|
||||
$player.fadeTo(0, 0);
|
||||
$player.addClass('o_player').parent().addClass('o_player_wrapper');
|
||||
self.ratio = ($player.width() / $player.height());
|
||||
|
||||
if (self.$target.attr('iframefit') === 'fitCont') {
|
||||
$player.parentsUntil(self.$target).css({width: '100%', height: 'auto'});
|
||||
if ($player.is('iframe')) {
|
||||
$player.css('width', '100%');
|
||||
$player.height($player.width() / self.ratio);
|
||||
} else {
|
||||
$player.css({width: '100%', height: 'auto'});
|
||||
}
|
||||
} else if (self.$target.attr('iframefit') === 'fitIframe') {
|
||||
$player.parentsUntil(self.$target).andSelf().css({width: '100%', height: '100%'});
|
||||
}
|
||||
|
||||
$player.fadeTo("slow", 1);
|
||||
$player.removeClass('o_media_loading');
|
||||
$(window).trigger('resize');
|
||||
});
|
||||
},
|
||||
|
||||
stop_video: function () {
|
||||
this.$target.find('.videoBox, iframe').remove();
|
||||
$(window).unbind('resize.s_media_block');
|
||||
},
|
||||
|
||||
create_video: function ($container, video_url, params) {
|
||||
var def = $.Deferred();
|
||||
var $iframe;
|
||||
|
||||
if (video_url) {
|
||||
$iframe = $('<iframe/>', {
|
||||
frameborder: "0",
|
||||
allowfullscreen: "allowfullscreen",
|
||||
src: video_url + '?' + $.param(params),
|
||||
});
|
||||
} else {
|
||||
$iframe = $('<div/>').html(this.src).find('iframe:first').css({
|
||||
height: "100%", width: "100%", top: 0, position: "absolute",
|
||||
});
|
||||
if ($iframe.length) {
|
||||
$container.css('max-height', '');
|
||||
} else {
|
||||
return def.reject();
|
||||
}
|
||||
}
|
||||
|
||||
$iframe.addClass('o_media_loading o_temp_auto_element');
|
||||
$iframe.on('load', function () {
|
||||
def.resolve($iframe);
|
||||
});
|
||||
$container.append($iframe);
|
||||
|
||||
return def;
|
||||
},
|
||||
|
||||
create_html5_video: function ($container, video_url, params) {
|
||||
var def = $.Deferred();
|
||||
var $video_content = $('<video/>', {'class': 'videoBox d-none d-md-block o_temp_auto_element', 'preload': 'metadata'}).attr(params).attr('src', video_url);
|
||||
|
||||
$video_content.addClass('o_media_loading');
|
||||
$video_content.on('canplay', function () {
|
||||
def.resolve($video_content);
|
||||
});
|
||||
$container.append($video_content);
|
||||
|
||||
$video_content.get(0).load();
|
||||
if (params['autoplay']) {
|
||||
$video_content.get(0).play();
|
||||
}
|
||||
|
||||
return def;
|
||||
},
|
||||
|
||||
// When the visitor is logged in YouTube, the console return "Failed to execute 'postMessage'";
|
||||
// That is a known error from the side of the Youtube API.
|
||||
// Fortunately it's not blocking but unfortunatly the fix should be done by the Google team.
|
||||
YTPlayer_video: function ($container, video_url, _params) {
|
||||
var video_id = this.src.split('/')[3];
|
||||
var params = _.mapObject(_params, function (v) { return !!v; });
|
||||
|
||||
var opacity = this.$target.attr('opacity');
|
||||
var background = this.$target.attr('background') || '';
|
||||
|
||||
var timeStamp = Date.now();
|
||||
|
||||
var $video_container = $('<div/>', {
|
||||
'class': 'yt_video_container '+ background,
|
||||
id: "s_media_block_" + timeStamp,
|
||||
});
|
||||
var playerParams = {
|
||||
videoURL: video_id, containment: '#s_media_block_' + timeStamp, mute: params['muted'], loop: params['loop'],
|
||||
stopMovieOnBlur: false, autoPlay: true, showYTLogo: false, opacity: opacity, showControls: false,
|
||||
};
|
||||
var $el = $('<div/>', {'class': 'player', 'data-property': JSON.stringify(playerParams)});
|
||||
var $loader = $("<span class='yt-loader'><span/></span>");
|
||||
$video_container.append($el).append($loader);
|
||||
|
||||
var interval = null;
|
||||
if ($("#oe_main_menu_navbar").length > 0) { $loader.css("top", $("#oe_main_menu_navbar").outerHeight()+1); }
|
||||
$loader.animate({width: "45%"}, 800, function () {
|
||||
var el = $loader;
|
||||
interval = setInterval(function () { timer(); }, 300);
|
||||
function timer() { var w = el.width(); el.width(w + 5); }
|
||||
});
|
||||
|
||||
if (!params['autoplay']) {
|
||||
$el.one('YTPStart', function () {
|
||||
$el.YTPPause();
|
||||
});
|
||||
}
|
||||
|
||||
var def = $.Deferred();
|
||||
$el.on('YTPReady', function () {
|
||||
clearInterval(interval);
|
||||
$loader.css("width", "100%").fadeOut(500);
|
||||
|
||||
def.resolve($video_container.find('iframe'));
|
||||
|
||||
if (!params['controls'] && params['autoplay']) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $controls = $("<span/>", {'class': 'controls'}).appendTo($video_container);
|
||||
|
||||
var $btnplay = $("<span/>", {'class': 'fa fa-fw'}).appendTo($controls);
|
||||
var playing = params['autoplay'];
|
||||
$btnplay.toggleClass("fa-pause", playing).toggleClass("fa-play", !playing);
|
||||
$btnplay.on("click", play_callback);
|
||||
if (!params['controls']) {
|
||||
$btnplay.one('click', function () {
|
||||
$controls.remove();
|
||||
});
|
||||
}
|
||||
|
||||
if (!params['muted'] && params['controls']) {
|
||||
var $btnMute = $("<span/>", {'class': 'fa fa-fw fa-volume-up'}).appendTo($controls);
|
||||
$btnMute.on("click", mute_callback);
|
||||
}
|
||||
|
||||
function play_callback() {
|
||||
if (playing) {
|
||||
$el.YTPPause();
|
||||
} else {
|
||||
$el.YTPPlay();
|
||||
}
|
||||
playing = !playing;
|
||||
|
||||
$btnplay.toggleClass("fa-pause", playing).toggleClass("fa-play", !playing);
|
||||
}
|
||||
function mute_callback() {
|
||||
$el.YTPToggleVolume();
|
||||
$btnMute.toggleClass("fa-volume-up").toggleClass("fa-volume-off");
|
||||
}
|
||||
});
|
||||
|
||||
$container.append($video_container);
|
||||
$el.YTPlayer();
|
||||
|
||||
return def;
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
odoo.define('theme_common.s_mini_nav_bar', function (require) {
|
||||
'use strict';
|
||||
|
||||
var publicWidget = require('web.public.widget');
|
||||
|
||||
publicWidget.registry.miniNavbarScroll = publicWidget.Widget.extend({
|
||||
selector: '.s_mini_nav_bar a[href*="#"]:not([href="#"])',
|
||||
events: {
|
||||
'click': '_onClick',
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Handlers
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Called on click on the mini navbar link -> scroll to the section.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onClick: function (ev) {
|
||||
var index = $(this).parent().index();
|
||||
var target = $('.o_scroll_nav').get(index);
|
||||
if (target) {
|
||||
ev.preventDefault();
|
||||
$('html,body').animate({ scrollTop: $(target).offset().top }, 1000);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
odoo.define('theme_common.s_progress_bar', function (require) {
|
||||
'use strict';
|
||||
|
||||
var s_options = require('web_editor.snippets.options');
|
||||
|
||||
s_options.registry.progress = s_options.Class.extend({
|
||||
start: function () {
|
||||
if (this.$overlay) {
|
||||
this.bind_move_progress();
|
||||
}
|
||||
this._super();
|
||||
},
|
||||
|
||||
bind_move_progress: function () {
|
||||
var self = this;
|
||||
var $e = this.$overlay.find(".o_handle.e").removeClass("readonly");
|
||||
var $parent = this.$target.parent();
|
||||
var pos = $parent.offset();
|
||||
var width = $parent.width();
|
||||
var time;
|
||||
function move(event) {
|
||||
clearTimeout(time);
|
||||
time = setTimeout(function () {
|
||||
var value = (event.clientX - pos.left)/width*100 | 0;
|
||||
if (value > 100 ) value = 100;
|
||||
if (value < 0 ) value = 0;
|
||||
self.$target.text( self.$target.text().replace(/[0-9]+/, value) );
|
||||
self.$target.attr("data-value", value);
|
||||
if (value < 3 ) value = 3;
|
||||
self.$target.css("width", value + "%");
|
||||
},5);
|
||||
}
|
||||
$e.on("mousedown", function () {
|
||||
self.$overlay.addClass('d-none');
|
||||
$(document)
|
||||
.on("mousemove", move)
|
||||
.one("mouseup", function () {
|
||||
self.$overlay.removeClass('d-none');
|
||||
$(document).off("mousemove", move);
|
||||
self.trigger_up('cover_update');
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
odoo.define('theme_common.s_showcase_editor.js', function (require) {
|
||||
'use strict';
|
||||
|
||||
var s_options = require('web_editor.snippets.options');
|
||||
|
||||
s_options.registry.s_showcase = s_options.Class.extend({
|
||||
onClone: function () {
|
||||
this._reset_alignment();
|
||||
},
|
||||
|
||||
onRemove: function () {
|
||||
this._reset_alignment(this.$target);
|
||||
},
|
||||
|
||||
_reset_alignment: function ($exclude) {
|
||||
var $features = this.$target.closest(".s_showcase").find(".feature").not($exclude);
|
||||
|
||||
// Remove classes used in the previus implementation
|
||||
$features.removeClass('offset-md-2');
|
||||
|
||||
// Wait until the $clone element is in place
|
||||
setTimeout(function () {
|
||||
$features.unwrap('.row');
|
||||
|
||||
_.each($features, function (feat, i) {
|
||||
var even = (i % 2 === 0);
|
||||
$(feat)
|
||||
.toggleClass('text-right', even)
|
||||
.toggleClass('text-left', !even)
|
||||
.toggleClass('offset-lg-2', !even);
|
||||
});
|
||||
|
||||
// Re-wrap in rows
|
||||
for (var i = 0; i < $features.length; i+=2) {
|
||||
$features.slice(i, i + 2).wrapAll("<div class='row'/>");
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
odoo.define('theme_common.s_showcase_slider_editor.js', function (require) {
|
||||
'use strict';
|
||||
|
||||
var s_options = require('web_editor.snippets.options');
|
||||
require("theme_common.s_showcase_slider_frontend");
|
||||
|
||||
s_options.registry.s_showcase_slider = s_options.Class.extend({
|
||||
start: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.$target.on("transitionIsFinished", (function () {
|
||||
this.trigger_up('cover_update');
|
||||
}).bind(this));
|
||||
|
||||
_.delay((function () { // FIXME ...
|
||||
this.$overlay.find(".js_s_ss_addSlide").on('click', this.addSlide.bind(this));
|
||||
this.$overlay.find(".js_s_ss_removeSlide").on('click', this.removeSlide.bind(this));
|
||||
}).bind(this), 100);
|
||||
},
|
||||
|
||||
resetSlides: function (active) {
|
||||
this.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: this.$target,
|
||||
});
|
||||
this.$target.toggleClass("active", !!active);
|
||||
},
|
||||
|
||||
addSlide: function () {
|
||||
var $currentSlide = this.$target.find(".s_ss_slider > .selected");
|
||||
$currentSlide.after("<li><img class=\"o_not-animable\" src=\"/web/image/theme_common.image_content_23\"/></li>");
|
||||
this.resetSlides(true);
|
||||
},
|
||||
|
||||
removeSlide: function () {
|
||||
if (this.$target.find(".s_ss_slider").children().length <= 1) return;
|
||||
|
||||
var $currentSlide = this.$target.find(".s_ss_slider > .selected");
|
||||
$currentSlide.remove();
|
||||
this.resetSlides(true);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,130 @@
|
||||
odoo.define("theme_common.s_showcase_slider_frontend", function (require) {
|
||||
"use strict";
|
||||
|
||||
var publicWidget = require('web.public.widget');
|
||||
|
||||
publicWidget.registry.s_showcase_slider = publicWidget.Widget.extend({
|
||||
selector: ".s_showcase_slider",
|
||||
|
||||
start: function () {
|
||||
_.defer(this.bindEvents.bind(this)); // FIXME delayed to counter a web_editor bug which off all click event
|
||||
this.createPagination();
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this._super.apply(this, arguments);
|
||||
_.defer(this.unbindEvents.bind(this)); // FIXME delayed to counter a web_editor bug which off all click event
|
||||
this.destroyPagination();
|
||||
this.$target.removeClass("active");
|
||||
},
|
||||
|
||||
bindEvents: function () {
|
||||
// Enlarge image on click if not already enlarged
|
||||
this.$target.on("click.s_showcase_slider", ".s_ss_slider", (function (e) {
|
||||
if (this.$target.hasClass("active")) return;
|
||||
|
||||
this.$target
|
||||
.addClass("active")
|
||||
.one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend", (function () {
|
||||
$("body, html").animate({
|
||||
scrollTop: this.$target.offset().top - 70
|
||||
}, 200);
|
||||
this.$target.trigger("transitionIsFinished");
|
||||
}).bind(this));
|
||||
}).bind(this));
|
||||
|
||||
// Close the enlarged image on close icon click
|
||||
this.$target.on("click.s_showcase_slider", ".s_ss_close", (function (e) {
|
||||
this.$target.removeClass("active");
|
||||
}).bind(this));
|
||||
|
||||
// Handle click navigation
|
||||
this.$target.on("click.s_showcase_slider", ".s_ss_prev", this.prevSlide.bind(this));
|
||||
this.$target.on("click.s_showcase_slider", ".s_ss_next", this.nextSlide.bind(this));
|
||||
this.$target.on("click.s_showcase_slider", ".s_ss_slider_pagination > li > a", (function (e) {
|
||||
e.preventDefault();
|
||||
var $selectedDot = $(e.currentTarget).parent();
|
||||
if ($selectedDot.hasClass("selected")) return;
|
||||
this.changeSlide($selectedDot.index());
|
||||
}).bind(this));
|
||||
|
||||
// Keyboard slider navigation
|
||||
$(document).on("keyup.s_showcase_slider", (function (e) {
|
||||
if (!this.$target.hasClass("active")) return;
|
||||
|
||||
switch (e.which) {
|
||||
case $.ui.keyCode.LEFT:
|
||||
this.prevSlide();
|
||||
break;
|
||||
case $.ui.keyCode.RIGHT:
|
||||
this.nextSlide();
|
||||
break;
|
||||
case $.ui.keyCode.ESCAPE:
|
||||
this.$target.removeClass("active");
|
||||
break;
|
||||
}
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
unbindEvents: function () {
|
||||
this.$target.off(".s_showcase_slider");
|
||||
$(document).off(".s_showcase_slider");
|
||||
},
|
||||
|
||||
createPagination: function () { // FIXME pagination should be saved with editor but keep this for compatibility
|
||||
this.$target.find(".s_ss_slider_pagination").remove(); // Remove saved-with-editor pagination
|
||||
|
||||
this.$pagination = $("<ul/>", {class: "s_ss_slider_pagination"});
|
||||
this.$pagination.insertAfter(this.$target.find(".s_ss_slider_navigation"));
|
||||
|
||||
var nbSlides = this.$target.find(".s_ss_slider").children().length;
|
||||
for (var i = 0 ; i < nbSlides ; i++) {
|
||||
this.$pagination.append("<li><a href=\"#\"></a></li>");
|
||||
}
|
||||
|
||||
this.$pagination.children().eq(this.getCurrentIndex()).addClass("selected");
|
||||
},
|
||||
|
||||
destroyPagination: function () {
|
||||
if (this.$pagination) {
|
||||
this.$pagination.remove();
|
||||
this.$pagination = null;
|
||||
}
|
||||
},
|
||||
|
||||
prevSlide: function () {
|
||||
var nbSlides = this.$target.find(".s_ss_slider").children().length;
|
||||
var currentIndex = this.getCurrentIndex();
|
||||
this.changeSlide(currentIndex > 0 ? (currentIndex - 1) : (nbSlides - 1));
|
||||
},
|
||||
|
||||
nextSlide: function () {
|
||||
var nbSlides = this.$target.find(".s_ss_slider").children().length;
|
||||
var currentIndex = this.getCurrentIndex();
|
||||
this.changeSlide((currentIndex + 1) % nbSlides);
|
||||
},
|
||||
|
||||
getCurrentIndex: function () {
|
||||
return this.$target.find(".s_ss_slider > .selected").index();
|
||||
},
|
||||
|
||||
changeSlide: function (n) {
|
||||
var $slides = this.$target.find(".s_ss_slider > li").removeClass("selected");
|
||||
this.$target.find(".s_ss_slider_pagination > li").removeClass("selected");
|
||||
|
||||
var $slide = $slides.eq(n).addClass("selected");
|
||||
$slides.removeClass("move-left");
|
||||
$slide.prevAll().addClass("move-left");
|
||||
|
||||
this.$pagination.children().eq(n).addClass("selected");
|
||||
this.updateNavigation();
|
||||
},
|
||||
|
||||
updateNavigation: function () {
|
||||
var $active = this.$target.find(".s_ss_slider > .selected");
|
||||
this.$target.find(".s_ss_prev").toggleClass("inactive", $active.is(":first-child"));
|
||||
this.$target.find(".s_ss_next").toggleClass("inactive", $active.is(":last-child"));
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
// This file is for compatibility since saas-10 web_editor refactoring.
|
||||
// As a fix to work properly in the editor, some classes have been renamed.
|
||||
// To allow a smooth transition, old classes alias are created here.
|
||||
|
||||
@mixin bg-compatibility-mixin($bg, $color) {
|
||||
background-color: $bg;
|
||||
color: $color;
|
||||
.text-muted {
|
||||
color: rgba($color, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.black { @include bg-compatibility-mixin($black, gray('600')); }
|
||||
.darkgray { @include bg-compatibility-mixin(gray('800'), $white); }
|
||||
.gray { @include bg-compatibility-mixin(gray('700'), $white); }
|
||||
.lightgray { @include bg-compatibility-mixin(gray('200'), gray('700')); }
|
||||
.white { @include bg-compatibility-mixin($white, gray('700')); }
|
||||