summaryrefslogtreecommitdiff
path: root/addons/website/static/src/js/content
diff options
context:
space:
mode:
authorqsm-odoo <qsm@odoo.com>2019-12-18 03:05:35 +0100
committerqsm-odoo <qsm@odoo.com>2019-12-18 03:05:35 +0100
commit30aa2ee339d37cb08363b25efd5efe91465d4c54 (patch)
treedeed65374e2f8e078b0f8f4898bd44f1226af98d /addons/website/static/src/js/content
parente0b97cdb8775b285874437428e9a37a59dc42126 (diff)
[REF] fake move of snippets.xml and options/animations.js
Diffstat (limited to 'addons/website/static/src/js/content')
-rw-r--r--addons/website/static/src/js/content/snippets.animation.js1061
1 files changed, 0 insertions, 1061 deletions
diff --git a/addons/website/static/src/js/content/snippets.animation.js b/addons/website/static/src/js/content/snippets.animation.js
deleted file mode 100644
index b9ca31ee248..00000000000
--- a/addons/website/static/src/js/content/snippets.animation.js
+++ /dev/null
@@ -1,1061 +0,0 @@
-odoo.define('website.content.snippets.animation', function (require) {
-'use strict';
-
-/**
- * Provides a way to start JS code for snippets' initialization and animations.
- */
-
-var Class = require('web.Class');
-const ColorpickerDialog = require('web.ColorpickerDialog');
-var config = require('web.config');
-var core = require('web.core');
-var mixins = require('web.mixins');
-var publicWidget = require('web.public.widget');
-var utils = require('web.utils');
-
-var qweb = core.qweb;
-const _t = core._t;
-
-// Initialize fallbacks for the use of requestAnimationFrame,
-// cancelAnimationFrame and performance.now()
-window.requestAnimationFrame = window.requestAnimationFrame
- || window.webkitRequestAnimationFrame
- || window.mozRequestAnimationFrame
- || window.msRequestAnimationFrame
- || window.oRequestAnimationFrame;
-window.cancelAnimationFrame = window.cancelAnimationFrame
- || window.webkitCancelAnimationFrame
- || window.mozCancelAnimationFrame
- || window.msCancelAnimationFrame
- || window.oCancelAnimationFrame;
-if (!window.performance || !window.performance.now) {
- window.performance = {
- now: function () {
- return Date.now();
- }
- };
-}
-
-/**
- * Add the notion of edit mode to public widgets.
- */
-publicWidget.Widget.include({
- /**
- * Indicates if the widget should not be instantiated in edit. The default
- * is true, indeed most (all?) defined widgets only want to initialize
- * events and states which should not be active in edit mode (this is
- * especially true for non-website widgets).
- *
- * @type {boolean}
- */
- disabledInEditableMode: true,
- /**
- * Acts as @see Widget.events except that the events are only binded if the
- * Widget instance is instanciated in edit mode. The property is not
- * considered if @see disabledInEditableMode is false.
- */
- edit_events: null,
- /**
- * Acts as @see Widget.events except that the events are only binded if the
- * Widget instance is instanciated in readonly mode. The property only
- * makes sense if @see disabledInEditableMode is false, you should simply
- * use @see Widget.events otherwise.
- */
- read_events: null,
-
- /**
- * Initializes the events that will need to be binded according to the
- * given mode.
- *
- * @constructor
- * @param {Object} parent
- * @param {Object} [options]
- * @param {boolean} [options.editableMode=false]
- * true if the page is in edition mode
- */
- init: function (parent, options) {
- this._super.apply(this, arguments);
-
- this.editableMode = this.options.editableMode || false;
- var extraEvents = this.editableMode ? this.edit_events : this.read_events;
- if (extraEvents) {
- this.events = _.extend({}, this.events || {}, extraEvents);
- }
- },
-});
-
-/**
- * In charge of handling one animation loop using the requestAnimationFrame
- * feature. This is used by the `Animation` class below and should not be called
- * directly by an end developer.
- *
- * This uses a simple API: it can be started, stopped, played and paused.
- */
-var AnimationEffect = Class.extend(mixins.ParentedMixin, {
- /**
- * @constructor
- * @param {Object} parent
- * @param {function} updateCallback - the animation update callback
- * @param {string} [startEvents=scroll]
- * space separated list of events which starts the animation loop
- * @param {jQuery|DOMElement} [$startTarget=window]
- * the element(s) on which the startEvents are listened
- * @param {Object} [options]
- * @param {function} [options.getStateCallback]
- * a function which returns a value which represents the state of the
- * animation, i.e. for two same value, no refreshing of the animation
- * is needed. Can be used for optimization. If the $startTarget is
- * the window element, this defaults to returning the current
- * scoll offset of the window or the size of the window for the
- * scroll and resize events respectively.
- * @param {string} [options.endEvents]
- * space separated list of events which pause the animation loop. If
- * not given, the animation is stopped after a while (if no
- * startEvents is received again)
- * @param {jQuery|DOMElement} [options.$endTarget=$startTarget]
- * the element(s) on which the endEvents are listened
- */
- init: function (parent, updateCallback, startEvents, $startTarget, options) {
- mixins.ParentedMixin.init.call(this);
- this.setParent(parent);
-
- options = options || {};
- this._minFrameTime = 1000 / (options.maxFPS || 100);
-
- // Initialize the animation startEvents, startTarget, endEvents, endTarget and callbacks
- this._updateCallback = updateCallback;
- this.startEvents = startEvents || 'scroll';
- this.$startTarget = $($startTarget || window);
- if (options.getStateCallback) {
- this._getStateCallback = options.getStateCallback;
- } else if (this.startEvents === 'scroll' && this.$startTarget[0] === window) {
- this._getStateCallback = function () {
- return window.pageYOffset;
- };
- } else if (this.startEvents === 'resize' && this.$startTarget[0] === window) {
- this._getStateCallback = function () {
- return {
- width: window.innerWidth,
- height: window.innerHeight,
- };
- };
- } else {
- this._getStateCallback = function () {
- return undefined;
- };
- }
- this.endEvents = options.endEvents || false;
- this.$endTarget = options.$endTarget ? $(options.$endTarget) : this.$startTarget;
-
- this._updateCallback = this._updateCallback.bind(parent);
- this._getStateCallback = this._getStateCallback.bind(parent);
-
- // Add a namespace to events using the generated uid
- this._uid = '_animationEffect' + _.uniqueId();
- this.startEvents = _processEvents(this.startEvents, this._uid);
- if (this.endEvents) {
- this.endEvents = _processEvents(this.endEvents, this._uid);
- }
-
- function _processEvents(events, namespace) {
- events = events.split(' ');
- return _.each(events, function (e, index) {
- events[index] += ('.' + namespace);
- }).join(' ');
- }
- },
- /**
- * @override
- */
- destroy: function () {
- mixins.ParentedMixin.destroy.call(this);
- this.stop();
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * Initializes when the animation must be played and paused and initializes
- * the animation first frame.
- */
- start: function () {
- // Initialize the animation first frame
- this._paused = false;
- this._rafID = window.requestAnimationFrame((function (t) {
- this._update(t);
- this._paused = true;
- }).bind(this));
-
- // Initialize the animation play/pause events
- if (this.endEvents) {
- /**
- * If there are endEvents, the animation should begin playing when
- * the startEvents are triggered on the $startTarget and pause when
- * the endEvents are triggered on the $endTarget.
- */
- this.$startTarget.on(this.startEvents, (function (e) {
- if (this._paused) {
- _.defer(this.play.bind(this, e));
- }
- }).bind(this));
- this.$endTarget.on(this.endEvents, (function () {
- if (!this._paused) {
- _.defer(this.pause.bind(this));
- }
- }).bind(this));
- } else {
- /**
- * Else, if there is no endEvents, the animation should begin playing
- * when the startEvents are *continuously* triggered on the
- * $startTarget or fully played once. To achieve this, the animation
- * begins playing and is scheduled to pause after 2 seconds. If the
- * startEvents are triggered during that time, this is not paused
- * for another 2 seconds. This allows to describe an "effect"
- * animation (which lasts less than 2 seconds) or an animation which
- * must be playing *during* an event (scroll, mousemove, resize,
- * repeated clicks, ...).
- */
- var pauseTimer = null;
- this.$startTarget.on(this.startEvents, _.throttle((function (e) {
- this.play(e);
-
- clearTimeout(pauseTimer);
- pauseTimer = _.delay((function () {
- this.pause();
- pauseTimer = null;
- }).bind(this), 2000);
- }).bind(this), 250, {trailing: false}));
- }
- },
- /**
- * Pauses the animation and destroys the attached events which trigger the
- * animation to be played or paused.
- */
- stop: function () {
- this.$startTarget.off(this.startEvents);
- if (this.endEvents) {
- this.$endTarget.off(this.endEvents);
- }
- this.pause();
- },
- /**
- * Forces the requestAnimationFrame loop to start.
- *
- * @param {Event} e - the event which triggered the animation to play
- */
- play: function (e) {
- this._newEvent = e;
- if (!this._paused) {
- return;
- }
- this._paused = false;
- this._rafID = window.requestAnimationFrame(this._update.bind(this));
- this._lastUpdateTimestamp = undefined;
- },
- /**
- * Forces the requestAnimationFrame loop to stop.
- */
- pause: function () {
- if (this._paused) {
- return;
- }
- this._paused = true;
- window.cancelAnimationFrame(this._rafID);
- this._lastUpdateTimestamp = undefined;
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Callback which is repeatedly called by the requestAnimationFrame loop.
- * It controls the max fps at which the animation is running and initializes
- * the values that the update callback needs to describe the animation
- * (state, elapsedTime, triggered event).
- *
- * @private
- * @param {DOMHighResTimeStamp} timestamp
- */
- _update: function (timestamp) {
- if (this._paused) {
- return;
- }
- this._rafID = window.requestAnimationFrame(this._update.bind(this));
-
- // Check the elapsed time since the last update callback call.
- // Consider it 0 if there is no info of last timestamp and leave this
- // _update call if it was called too soon (would overflow the set max FPS).
- var elapsedTime = 0;
- if (this._lastUpdateTimestamp) {
- elapsedTime = timestamp - this._lastUpdateTimestamp;
- if (elapsedTime < this._minFrameTime) {
- return;
- }
- }
-
- // Check the new animation state thanks to the get state callback and
- // store its new value. If the state is the same as the previous one,
- // leave this _update call, except if there is an event which triggered
- // the "play" method again.
- var animationState = this._getStateCallback(elapsedTime, this._newEvent);
- if (!this._newEvent
- && animationState !== undefined
- && _.isEqual(animationState, this._animationLastState)) {
- return;
- }
- this._animationLastState = animationState;
-
- // Call the update callback with frame parameters
- this._updateCallback(this._animationLastState, elapsedTime, this._newEvent);
- this._lastUpdateTimestamp = timestamp; // Save the timestamp at which the update callback was really called
- this._newEvent = undefined; // Forget the event which triggered the last "play" call
- },
-});
-
-/**
- * Also register AnimationEffect automatically (@see effects, _prepareEffects).
- */
-var Animation = publicWidget.Widget.extend({
- /**
- * The max FPS at which all the automatic animation effects will be
- * running by default.
- */
- maxFPS: 100,
- /**
- * @see this._prepareEffects
- *
- * @type {Object[]}
- * @type {string} startEvents
- * The names of the events which trigger the effect to begin playing.
- * @type {string} [startTarget]
- * A selector to find the target where to listen for the start events
- * (if no selector, the window target will be used). If the whole
- * $target of the animation should be used, use the 'selector' string.
- * @type {string} [endEvents]
- * The name of the events which trigger the end of the effect (if none
- * is defined, the animation will stop after a while
- * @see AnimationEffect.start).
- * @type {string} [endTarget]
- * A selector to find the target where to listen for the end events
- * (if no selector, the startTarget will be used). If the whole
- * $target of the animation should be used, use the 'selector' string.
- * @type {string} update
- * A string which refers to a method which will be used as the update
- * callback for the effect. It receives 3 arguments: the animation
- * state, the elapsedTime since last update and the event which
- * triggered the animation (undefined if just a new update call
- * without trigger).
- * @type {string} [getState]
- * The animation state is undefined by default, the scroll offset for
- * the particular {startEvents: 'scroll'} effect and an object with
- * width and height for the particular {startEvents: 'resize'} effect.
- * There is the possibility to define the getState callback of the
- * animation effect with this key. This allows to improve performance
- * even further in some cases.
- */
- effects: [],
-
- /**
- * Initializes the animation. The method should not be called directly as
- * called automatically on animation instantiation and on restart.
- *
- * Also, prepares animation's effects and start them if any.
- *
- * @override
- */
- start: function () {
- this._prepareEffects();
- _.each(this._animationEffects, function (effect) {
- effect.start();
- });
- return this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Registers `AnimationEffect` instances.
- *
- * This can be done by extending this method and calling the @see _addEffect
- * method in it or, better, by filling the @see effects property.
- *
- * @private
- */
- _prepareEffects: function () {
- this._animationEffects = [];
-
- var self = this;
- _.each(this.effects, function (desc) {
- self._addEffect(self[desc.update], desc.startEvents, _findTarget(desc.startTarget), {
- getStateCallback: desc.getState && self[desc.getState],
- endEvents: desc.endEvents || undefined,
- $endTarget: _findTarget(desc.endTarget),
- maxFPS: self.maxFPS,
- });
-
- // Return the DOM element matching the selector in the form
- // described above.
- function _findTarget(selector) {
- if (selector) {
- if (selector === 'selector') {
- return self.$target;
- }
- return self.$(selector);
- }
- return undefined;
- }
- });
- },
- /**
- * Registers a new `AnimationEffect` according to given parameters.
- *
- * @private
- * @see AnimationEffect.init
- */
- _addEffect: function (updateCallback, startEvents, $startTarget, options) {
- this._animationEffects.push(
- new AnimationEffect(this, updateCallback, startEvents, $startTarget, options)
- );
- },
-});
-
-//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-
-var registry = publicWidget.registry;
-
-registry.slider = publicWidget.Widget.extend({
- selector: '.carousel',
- disabledInEditableMode: false,
- edit_events: {
- 'content_changed': '_onContentChanged',
- },
-
- /**
- * @override
- */
- start: function () {
- this.$('img').on('load.slider', () => this._computeHeights());
- this._computeHeights();
- // Initialize carousel and pause if in edit mode.
- this.$target.carousel(this.editableMode ? 'pause' : undefined);
- return this._super.apply(this, arguments);
- },
- /**
- * @override
- */
- destroy: function () {
- this._super.apply(this, arguments);
- this.$('img').off('.slider');
- this.$target.carousel('pause');
- this.$target.removeData('bs.carousel');
- _.each(this.$('.carousel-item'), function (el) {
- $(el).css('min-height', '');
- });
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @private
- */
- _computeHeights: function () {
- var maxHeight = 0;
- var $items = this.$('.carousel-item');
- $items.css('min-height', '');
- _.each($items, function (el) {
- var $item = $(el);
- var isActive = $item.hasClass('active');
- $item.addClass('active');
- var height = $item.outerHeight();
- if (height > maxHeight) {
- maxHeight = height;
- }
- $item.toggleClass('active', isActive);
- });
- $items.css('min-height', maxHeight);
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * @private
- */
- _onContentChanged: function (ev) {
- this._computeHeights();
- },
-});
-
-registry.parallax = Animation.extend({
- selector: '.parallax',
- disabledInEditableMode: false,
- effects: [{
- startEvents: 'scroll',
- update: '_onWindowScroll',
- }],
-
- /**
- * @override
- */
- start: function () {
- this._rebuild();
- $(window).on('resize.animation_parallax', _.debounce(this._rebuild.bind(this), 500));
- return this._super.apply(this, arguments);
- },
- /**
- * @override
- */
- destroy: function () {
- this._super.apply(this, arguments);
- $(window).off('.animation_parallax');
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Prepares the background element which will scroll at a different speed
- * according to the viewport dimensions and other snippet parameters.
- *
- * @private
- */
- _rebuild: function () {
- // Add/find bg DOM element to hold the parallax bg (support old v10.0 parallax)
- if (!this.$bg || !this.$bg.length) {
- this.$bg = this.$('> .s_parallax_bg');
- if (!this.$bg.length) {
- this.$bg = $('<span/>', {
- class: 's_parallax_bg' + (this.$target.hasClass('oe_custom_bg') ? ' oe_custom_bg' : ''),
- }).prependTo(this.$target);
- }
- }
- var urlTarget = this.$target.css('background-image');
- if (urlTarget !== 'none') {
- this.$bg.css('background-image', urlTarget);
- }
- this.$target.css('background-image', 'none');
-
- // Get parallax speed
- this.speed = parseFloat(this.$target.attr('data-scroll-background-ratio') || 0);
-
- // Reset offset if parallax effect will not be performed and leave
- this.$target.toggleClass('s_parallax_is_fixed', this.speed === 1);
- var noParallaxSpeed = (this.speed === 0 || this.speed === 1);
- this.$target.toggleClass('s_parallax_no_overflow_hidden', noParallaxSpeed);
- if (noParallaxSpeed) {
- this.$bg.css({
- transform: '',
- top: '',
- bottom: '',
- });
- return;
- }
-
- // Initialize parallax data according to snippet and viewport dimensions
- this.viewport = document.body.clientHeight - $('#wrapwrap').position().top;
- this.visibleArea = [this.$target.offset().top];
- this.visibleArea.push(this.visibleArea[0] + this.$target.innerHeight() + this.viewport);
- this.ratio = this.speed * (this.viewport / 10);
-
- // Provide a "safe-area" to limit parallax
- this.$bg.css({
- top: -this.ratio,
- bottom: -this.ratio,
- });
- },
-
- //--------------------------------------------------------------------------
- // Effects
- //--------------------------------------------------------------------------
-
- /**
- * Describes how to update the snippet when the window scrolls.
- *
- * @private
- * @param {integer} scrollOffset
- */
- _onWindowScroll: function (scrollOffset) {
- // Speed == 0 is no effect and speed == 1 is handled by CSS only
- if (this.speed === 0 || this.speed === 1) {
- return;
- }
-
- // Perform translation if the element is visible only
- var vpEndOffset = scrollOffset + this.viewport;
- if (vpEndOffset >= this.visibleArea[0]
- && vpEndOffset <= this.visibleArea[1]) {
- this.$bg.css('transform', 'translateY(' + _getNormalizedPosition.call(this, vpEndOffset) + 'px)');
- }
-
- function _getNormalizedPosition(pos) {
- // Normalize scroll in a 1 to 0 range
- var r = (pos - this.visibleArea[1]) / (this.visibleArea[0] - this.visibleArea[1]);
- // Normalize accordingly to current options
- return Math.round(this.ratio * (2 * r - 1));
- }
- },
-});
-
-registry.mediaVideo = publicWidget.Widget.extend({
- selector: '.media_iframe_video',
-
- /**
- * @override
- */
- start: function () {
- // TODO: this code should be refactored to make more sense and be better
- // integrated with Odoo (this refactoring should be done in master).
-
- var def = this._super.apply(this, arguments);
- if (this.$target.children('iframe').length) {
- // There already is an <iframe/>, do nothing
- return def;
- }
-
- // Bug fix / compatibility: empty the <div/> element as all information
- // to rebuild the iframe should have been saved on the <div/> element
- this.$target.empty();
-
- // Add extra content for size / edition
- this.$target.append(
- '<div class="css_editable_mode_display">&nbsp;</div>' +
- '<div class="media_iframe_video_size">&nbsp;</div>'
- );
-
- // Rebuild the iframe. Depending on version / compatibility / instance,
- // the src is saved in the 'data-src' attribute or the
- // 'data-oe-expression' one (the latter is used as a workaround in 10.0
- // system but should obviously be reviewed in master).
- this.$target.append($('<iframe/>', {
- src: _.escape(this.$target.data('oe-expression') || this.$target.data('src')),
- frameborder: '0',
- allowfullscreen: 'allowfullscreen',
- sandbox: 'allow-scripts allow-same-origin', // https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
- }));
-
- return def;
- },
-});
-
-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');
- }
- }
-
- var throttledUpdate = _.throttle(() => this._adjustIframe(), 50);
-
- var $dropdownMenu = this.$el.closest('.dropdown-menu');
- if ($dropdownMenu.length) {
- this.$dropdownParent = $dropdownMenu.parent();
- this.$dropdownParent.on('shown.bs.dropdown.backgroundVideo', throttledUpdate);
- }
-
- $(window).on('resize.' + this.iframeID, throttledUpdate);
-
- return Promise.all(proms).then(() => this._appendBgVideo());
- },
- /**
- * @override
- */
- destroy: function () {
- this._super.apply(this, arguments);
-
- if (this.$dropdownParent) {
- this.$dropdownParent.off('.backgroundVideo');
- }
-
- $(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 () {
- this.$iframe.removeClass('show');
-
- // 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);
-
- void this.$iframe[0].offsetWidth; // Force style addition
- this.$iframe.addClass('show');
- },
- /**
- * 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: {
- 'click .o_ul_toggle_next': '_onToggleNextClick',
- 'click .o_ul_toggle_self': '_onToggleSelfClick',
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Called when a "toggle next" ul is clicked.
- *
- * @private
- */
- _onToggleNextClick: function (ev) {
- ev.preventDefault();
- var $target = $(ev.currentTarget);
- $target.toggleClass('o_open');
- $target.closest('li').next().toggleClass('o_close');
- },
- /**
- * Called when a "toggle self" ul is clicked.
- *
- * @private
- */
- _onToggleSelfClick: function (ev) {
- ev.preventDefault();
- var $target = $(ev.currentTarget);
- $target.toggleClass('o_open');
- $target.closest('li').find('ul,ol').toggleClass('o_close');
- },
-});
-
-registry.socialShare = publicWidget.Widget.extend({
- selector: '.oe_social_share',
- xmlDependencies: ['/website/static/src/xml/website.share.xml'],
- events: {
- 'mouseenter': '_onMouseEnter',
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @private
- */
- _bindSocialEvent: function () {
- this.$('.oe_social_facebook').click($.proxy(this._renderSocial, this, 'facebook'));
- this.$('.oe_social_twitter').click($.proxy(this._renderSocial, this, 'twitter'));
- this.$('.oe_social_linkedin').click($.proxy(this._renderSocial, this, 'linkedin'));
- },
- /**
- * @private
- */
- _render: function () {
- this.$el.popover({
- content: qweb.render('website.social_hover', {medias: this.socialList}),
- placement: 'bottom',
- container: this.$el,
- html: true,
- trigger: 'manual',
- animation: false,
- }).popover("show");
-
- this.$el.off('mouseleave.socialShare').on('mouseleave.socialShare', function () {
- var self = this;
- setTimeout(function () {
- if (!$(".popover:hover").length) {
- $(self).popover('dispose');
- }
- }, 200);
- });
- },
- /**
- * @private
- */
- _renderSocial: function (social) {
- var url = this.$el.data('urlshare') || document.URL.split(/[?#]/)[0];
- url = encodeURIComponent(url);
- var title = document.title.split(" | ")[0]; // get the page title without the company name
- var hashtags = ' #' + document.title.split(" | ")[1].replace(' ', '') + ' ' + this.hashtags; // company name without spaces (for hashtag)
- var socialNetworks = {
- 'facebook': 'https://www.facebook.com/sharer/sharer.php?u=' + url,
- 'twitter': 'https://twitter.com/intent/tweet?original_referer=' + url + '&text=' + encodeURIComponent(title + hashtags + ' - ') + url,
- 'linkedin': 'https://www.linkedin.com/shareArticle?mini=true&url=' + url + '&title=' + encodeURIComponent(title),
- };
- if (!_.contains(_.keys(socialNetworks), social)) {
- return;
- }
- var wHeight = 500;
- var wWidth = 500;
- window.open(socialNetworks[social], '', 'menubar=no, toolbar=no, resizable=yes, scrollbar=yes, height=' + wHeight + ',width=' + wWidth);
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Called when the user hovers the animation element -> open the social
- * links popover.
- *
- * @private
- */
- _onMouseEnter: function () {
- var social = this.$el.data('social');
- this.socialList = social ? social.split(',') : ['facebook', 'twitter', 'linkedin'];
- this.hashtags = this.$el.data('hashtags') || '';
-
- this._render();
- this._bindSocialEvent();
- },
-});
-
-registry.anchorSlide = publicWidget.Widget.extend({
- selector: 'a[href^="/"][href*="#"], a[href^="#"]',
- events: {
- 'click': '_onAnimateClick',
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * @private
- */
- _onAnimateClick: function (ev) {
- if (this.$target[0].pathname !== window.location.pathname) {
- return;
- }
- var hash = this.$target[0].hash;
- if (!utils.isValidAnchor(hash)) {
- return;
- }
- var $anchor = $(hash);
- if (!$anchor.length || !$anchor.attr('data-anchor')) {
- return;
- }
- ev.preventDefault();
- $('html, body').animate({
- scrollTop: $anchor.offset().top,
- }, 500);
- },
-});
-
-registry.chart = publicWidget.Widget.extend({
- selector: '.s_chart',
- disabledInEditableMode: false,
- jsLibs: [
- '/web/static/lib/Chart/Chart.js',
- ],
-
- /**
- * @override
- * @param {Object} parent
- * @param {Object} options The default value of the chartbar.
- */
- init: function (parent, options) {
- this._super.apply(this, arguments);
- this.style = window.getComputedStyle(document.documentElement);
- },
- /**
- * @override
- */
- start: function () {
- // Convert Theme colors to css color
- const data = JSON.parse(this.el.dataset.data);
- data.datasets.forEach(el => {
- if (Array.isArray(el.backgroundColor)) {
- el.backgroundColor = el.backgroundColor.map(el => this._convertToCssColor(el));
- el.borderColor = el.borderColor.map(el => this._convertToCssColor(el));
- } else {
- el.backgroundColor = this._convertToCssColor(el.backgroundColor);
- el.borderColor = this._convertToCssColor(el.borderColor);
- }
- el.borderWidth = this.el.dataset.borderWidth;
- });
-
- // Make chart data
- const chartData = {
- type: this.el.dataset.type,
- data: data,
- options: {
- legend: {
- display: this.el.dataset.legendPosition !== 'none',
- position: this.el.dataset.legendPosition,
- },
- tooltips: {
- enabled: this.el.dataset.tooltipDisplay === 'true',
- },
- title: {
- display: !!this.el.dataset.title,
- text: this.el.dataset.title,
- },
- },
- };
-
- // Add type specific options
- if (this.el.dataset.type === 'radar') {
- chartData.options.scale = {
- ticks: {
- beginAtZero: true,
- }
- };
- } else if (['pie', 'doughnut'].includes(this.el.dataset.type)) {
- chartData.options.tooltips.callbacks = {
- label: (tooltipItem, data) => {
- const label = data.datasets[tooltipItem.datasetIndex].label;
- const secondLabel = data.labels[tooltipItem.index];
- let final = label;
- if (label) {
- if (secondLabel) {
- final = label + ' - ' + secondLabel;
- }
- } else if (secondLabel) {
- final = secondLabel;
- }
- return final + ':' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
- },
- };
- } else {
- chartData.options.scales = {
- xAxes: [{
- stacked: this.el.dataset.stacked === 'true',
- ticks: {
- beginAtZero: true
- },
- }],
- yAxes: [{
- stacked: this.el.dataset.stacked === 'true',
- ticks: {
- beginAtZero: true
- },
- }],
- };
- }
-
- // Disable animation in edit mode
- if (this.editableMode) {
- chartData.options.animation = {
- duration: 0,
- };
- }
-
- const canvas = this.el.querySelector('canvas');
- this.chart = new window.Chart(canvas, chartData);
- return this._super.apply(this, arguments);
- },
- /**
- * @override
- * Discard all library changes to reset the state of the Html.
- */
- destroy: function () {
- if (this.chart) { // The widget can be destroyed before start has completed
- this.chart.destroy();
- this.el.querySelectorAll('.chartjs-size-monitor').forEach(el => el.remove());
- }
- this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @private
- * @param {string} color A css color or theme color string
- * @returns {string} Css color
- */
- _convertToCssColor: function (color) {
- if (!color) {
- return 'transparent';
- }
- return this.style.getPropertyValue(`--${color}`).trim() || color;
- },
-});
-
-return {
- Widget: publicWidget.Widget,
- Animation: Animation,
- registry: registry,
-
- Class: Animation, // Deprecated
-};
-});