diff --git a/addons/im_livechat/views/im_livechat_channel_templates.xml b/addons/im_livechat/views/im_livechat_channel_templates.xml
index 90199615161..6cc5f093969 100644
--- a/addons/im_livechat/views/im_livechat_channel_templates.xml
+++ b/addons/im_livechat/views/im_livechat_channel_templates.xml
@@ -147,7 +147,9 @@
-
+
+
+
diff --git a/addons/survey/views/assets.xml b/addons/survey/views/assets.xml
index 90db69d356e..e292f49ca66 100644
--- a/addons/survey/views/assets.xml
+++ b/addons/survey/views/assets.xml
@@ -5,7 +5,9 @@
-
+
+
+
diff --git a/addons/web/static/src/js/public/public_root.js b/addons/web/static/src/js/public/public_root.js
new file mode 100644
index 00000000000..10a2d20efc3
--- /dev/null
+++ b/addons/web/static/src/js/public/public_root.js
@@ -0,0 +1,289 @@
+odoo.define('web.public.root', function (require) {
+'use strict';
+
+var ajax = require('web.ajax');
+var ServiceProviderMixin = require('web.ServiceProviderMixin');
+var session = require('web.session');
+var utils = require('web.utils');
+var publicWidget = require('web.public.widget');
+
+var publicRootRegistry = new publicWidget.RootWidgetRegistry();
+
+// Load localizations outside the PublicRoot to not wait for DOM ready (but
+// wait for them in PublicRoot)
+function getLang() {
+ var html = document.documentElement;
+ return (html.getAttribute('lang') || 'en_US').replace('-', '_');
+}
+var lang = utils.get_cookie('frontend_lang') || getLang(); // FIXME the cookie value should maybe be in the ctx?
+var localeDef = ajax.loadJS('/web/webclient/locale/' + lang.replace('-', '_'));
+
+/**
+ * Element which is designed to be unique and that will be the top-most element
+ * in the widget hierarchy. So, all other widgets will be indirectly linked to
+ * this Class instance. Its main role will be to retrieve RPC demands from its
+ * children and handle them.
+ */
+var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, {
+ events: _.extend({}, publicWidget.RootWidget.prototype.events || {}, {
+ 'submit .js_website_submit_form': '_onWebsiteFormSubmit',
+ 'click .js_disable_on_click': '_onDisableOnClick',
+ }),
+ custom_events: _.extend({}, publicWidget.RootWidget.prototype.custom_events || {}, {
+ 'animation_start_demand': '_onAnimationStartDemand',
+ 'animation_stop_demand': '_onAnimationStopDemand',
+ 'context_get': '_onContextGet',
+ 'main_object_request': '_onMainObjectRequest',
+ }),
+
+ /**
+ * @constructor
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ ServiceProviderMixin.init.call(this);
+ this.animations = [];
+ },
+ /**
+ * @override
+ */
+ willStart: function () {
+ // TODO would be even greater to wait for localeDef only when necessary
+ return $.when(
+ this._super.apply(this, arguments),
+ session.is_bound,
+ localeDef
+ );
+ },
+ /**
+ * @override
+ */
+ start: function () {
+ var defs = [this._super.apply(this, arguments)];
+
+ // Animations
+ defs.push(this._startAnimations());
+
+ // Display image thumbnail
+ this.$(".o_image[data-mimetype^='image']").each(function () {
+ var $img = $(this);
+ if (/gif|jpe|jpg|png/.test($img.data('mimetype')) && $img.data('src')) {
+ $img.css('background-image', "url('" + $img.data('src') + "')");
+ }
+ });
+
+ // Auto scroll
+ if (window.location.hash.indexOf("scrollTop=") > -1) {
+ this.el.scrollTop = +window.location.hash.match(/scrollTop=([0-9]+)/)[1];
+ }
+
+ // Fix for IE:
+ if ($.fn.placeholder) {
+ $('input, textarea').placeholder();
+ }
+
+ return $.when.apply($, defs);
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Automatically adds the global context to RPCs.
+ *
+ * @override
+ */
+ _call_service: function (ev) {
+ if (ev.data.service === 'ajax' && ev.data.method === 'rpc') {
+ var route = ev.data.args[0];
+ if (_.str.startsWith(route, '/web/dataset/call_kw/')) {
+ var params = ev.data.args[1];
+ var options = ev.data.args[2];
+ params.kwargs.context = _.extend({}, this._getContext(), params.kwargs.context || {});
+ if (options) {
+ params.kwargs.context = _.omit(params.kwargs.context, options.noContextKeys);
+ ev.data.args[2] = _.omit(options, 'noContextKeys');
+ }
+ params.kwargs.context = JSON.parse(JSON.stringify(params.kwargs.context));
+ }
+ }
+ return ServiceProviderMixin._call_service.apply(this, arguments);
+ },
+ /**
+ * Retrieves the global context of the public environment. This is the
+ * context which is automatically added to each RPC.
+ *
+ * @private
+ * @param {Object} [context]
+ * @returns {Object}
+ */
+ _getContext: function (context) {
+ return _.extend({
+ 'lang': getLang(),
+ }, context || {});
+ },
+ /**
+ * Retrieves the global context of the public environment (as
+ * @see _getContext) but with extra informations that would be useless to
+ * send with each RPC.
+ *
+ * @private
+ * @param {Object} [context]
+ * @returns {Object}
+ */
+ _getExtraContext: function (context) {
+ return this._getContext(context);
+ },
+ /**
+ * As the root instance is designed to be unique, the associated
+ * registry has been instantiated outside of the class and is simply
+ * returned here.
+ *
+ * @private
+ * @override
+ */
+ _getRegistry: function () {
+ return publicRootRegistry;
+ },
+ /**
+ * Creates an PublicWidget instance for each DOM element which matches the
+ * `selector` key of one of the registered widgets
+ * (@see PublicWidget.selector).
+ *
+ * @private
+ * @param {jQuery} [$from]
+ * only initialize the animations whose `selector` matches the
+ * element or one of its descendant (default to the wrapwrap element)
+ * @param {Object} [options]
+ * @returns {Deferred}
+ */
+ _startAnimations: function ($from, options) {
+ var self = this;
+
+ if ($from === undefined) {
+ $from = this.$('#wrapwrap');
+ }
+ if (options === undefined) {
+ options = {};
+ }
+
+ this._stopAnimations($from);
+
+ var defs = _.map(publicWidget.registry, function (WebsiteWidget) {
+ var selector = WebsiteWidget.prototype.selector || '';
+ var $target = $from.find(selector).addBack(selector);
+
+ var defs = _.map($target, function (el) {
+ var animation = new WebsiteWidget(self, options);
+ self.animations.push(animation);
+ return animation.attachTo($(el));
+ });
+ return $.when.apply($, defs);
+ });
+ return $.when.apply($, defs);
+ },
+ /**
+ * Destroys all registered widget instances. Website would need this before
+ * saving while in edition mode for example.
+ *
+ * @private
+ * @param {jQuery} [$from]
+ * only stop the animations linked to the given element(s) or one of
+ * its descendants
+ */
+ _stopAnimations: function ($from) {
+ var removedAnimations = _.map(this.animations, function (animation) {
+ if (!$from
+ || $from.filter(animation.el).length
+ || $from.find(animation.el).length) {
+ animation.destroy();
+ return animation;
+ }
+ return null;
+ });
+ this.animations = _.difference(this.animations, removedAnimations);
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Called when the root is notified that the animations have to be
+ * (re)started.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onAnimationStartDemand: function (ev) {
+ this._startAnimations(ev.data.$target, ev.data.options)
+ .done(ev.data.onSuccess)
+ .fail(ev.data.onFailure);
+ },
+ /**
+ * Called when the root is notified that the animations have to be
+ * stopped.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onAnimationStopDemand: function (ev) {
+ this._stopAnimations(ev.data.$target);
+ },
+ /**
+ * Called when someone asked for the global public context.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onContextGet: function (ev) {
+ if (ev.data.extra) {
+ ev.data.callback(this._getExtraContext(ev.data.context));
+ } else {
+ ev.data.callback(this._getContext(ev.data.context));
+ }
+ },
+ /**
+ * Checks information about the page main object.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onMainObjectRequest: function (ev) {
+ var repr = $('html').data('main-object');
+ var m = repr.match(/(.+)\((\d+),(.*)\)/);
+ ev.data.callback({
+ model: m[1],
+ id: m[2] | 0,
+ });
+ },
+ /**
+ * @todo review
+ * @private
+ */
+ _onWebsiteFormSubmit: function (ev) {
+ var $buttons = $(ev.currentTarget).find('button[type="submit"], a.a-submit');
+ _.each($buttons, function (btn) {
+ var $btn = $(btn);
+ $btn.attr('data-loading-text', ' ' + $(btn).text());
+ $btn.button('loading');
+ });
+ },
+ /**
+ * Called when the root is notified that the button should be
+ * disabled after the first click.
+ *
+ * @private
+ * @param {Event} ev
+ */
+ _onDisableOnClick: function (ev) {
+ $(ev.currentTarget).addClass('disabled');
+ },
+});
+
+return {
+ PublicRoot: PublicRoot,
+ publicRootRegistry: publicRootRegistry,
+};
+});
diff --git a/addons/web/static/src/js/public/public_root_instance.js b/addons/web/static/src/js/public/public_root_instance.js
index ce0cfa61bc6..8202e011cc3 100644
--- a/addons/web/static/src/js/public/public_root_instance.js
+++ b/addons/web/static/src/js/public/public_root_instance.js
@@ -1,23 +1,17 @@
odoo.define('root.widget', function (require) {
-"use strict";
+'use strict';
-var ServiceProviderMixin = require('web.ServiceProviderMixin');
-var Widget = require('web.Widget');
+require('web.dom_ready');
+var rootData = require('web.public.root');
/**
- * This is the root widget in the frontend bundle when website is not installed.
- *
* This widget is important, because the tour manager needs a root widget in
* order to work. The root widget must be a service provider with the ajax
* service, so that the tour manager can let the server know when tours have
* been consumed.
*/
-var PublicRootWidget = Widget.extend(ServiceProviderMixin, {
- init: function () {
- this._super.apply(this, arguments);
- ServiceProviderMixin.init.call(this);
- }
+var publicRoot = new rootData.PublicRoot(null);
+return publicRoot.attachTo(document.body).then(function () {
+ return publicRoot;
});
-
-return new PublicRootWidget();
});
diff --git a/addons/web/static/src/js/public/public_widget.js b/addons/web/static/src/js/public/public_widget.js
index 462dcd9bae9..2f720c019f1 100644
--- a/addons/web/static/src/js/public/public_widget.js
+++ b/addons/web/static/src/js/public/public_widget.js
@@ -1,6 +1,10 @@
-odoo.define('web_editor.root_widget', function (require) {
+odoo.define('web.public.widget', function (require) {
'use strict';
+/**
+ * Provides a way to start JS code for snippets' initialization and animations.
+ */
+
var Class = require('web.Class');
var dom = require('web.dom');
var mixins = require('web.mixins');
@@ -12,11 +16,13 @@ var Widget = require('web.Widget');
* to internal DOM elements once it is started. The widgets to instantiate are
* known thanks to a linked registry which contains info about the widget
* classes and jQuery selectors to use to find the elements to attach them to.
+ *
+ * @todo Merge with 'PublicWidget' ?
*/
var RootWidget = Widget.extend({
custom_events: _.extend({}, Widget.prototype.custom_events || {}, {
- registry_update: '_onRegistryUpdate',
- get_session: '_onGetSession',
+ 'registry_update': '_onRegistryUpdate',
+ 'get_session': '_onGetSession',
}),
/**
* @constructor
@@ -160,8 +166,219 @@ var RootWidgetRegistry = Class.extend(mixins.EventDispatcherMixin, {
},
});
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+/**
+ * Provides a way for executing code once a website DOM element is loaded in the
+ * dom.
+ */
+var PublicWidget = Widget.extend({
+ /**
+ * The selector attribute, if defined, allows to automatically create an
+ * instance of this animation on page load for each DOM element which
+ * matches this selector. The `Animation.$target` element will then be that
+ * particular DOM element. This should be the main way of instantiating
+ * `Animation` elements.
+ *
+ * @todo do not make this part of the Widget but rather an info to give when
+ * registering the widget.
+ */
+ selector: false,
+ /**
+ * Extension of @see Widget.events
+ *
+ * A description of the event handlers to bind/delegate once the widget
+ * has been rendered::
+ *
+ * 'click .hello .world': 'async _onHelloWorldClick',
+ * _^_ _^_ _^_ _^_
+ * | | | |
+ * | (Optional) jQuery | Handler method name
+ * | delegate selector |
+ * | |_ (Optional) space separated options
+ * | * async: use the automatic system
+ * |_ Event name with making handlers promise-ready (see
+ * potential jQuery makeButtonHandler, makeAsyncHandler)
+ * namespaces
+ *
+ * Note: the values may be replaced by a function declaration. This is
+ * however a deprecated behavior.
+ *
+ * @type {Object}
+ */
+ events: {},
+ /**
+ * Acts as @see Widget.events except that the events are only binded if the
+ * Animation instance is instanciated in edit mode.
+ */
+ edit_events: {},
+ /**
+ * Acts as @see Widget.events except that the events are only binded if the
+ * Animation instance is instanciated in readonly mode.
+ */
+ read_events: {},
+
+ /**
+ * 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 - true if the page is in edition mode
+ */
+ init: function (parent, options) {
+ this._super.apply(this, arguments);
+
+ // Note that the notion of editable mode is kept here for now even
+ // though nothing can be edited here. This allows to have a readable
+ // code for frontend widgets and mainly to prevent having loading
+ // problems an "include" could have caused.
+ this.options = options || {};
+ this.editableMode = this.options.editableMode || false;
+
+ if (this.editableMode) {
+ this.events = _.extend({}, this.events || {}, this.edit_events || {});
+ } else {
+ this.events = _.extend({}, this.events || {}, this.read_events || {});
+ }
+ },
+ /**
+ * Destroys the animation and basically restores the target to the state it
+ * was before the start method was called (unlike standard widget, the
+ * associated $el DOM is not removed).
+ *
+ * Also stops animation effects and destroys them if any.
+ */
+ destroy: function () {
+ if (this.selector) {
+ var $oldel = this.$el;
+ // The difference with the default behavior is that we unset the
+ // associated element first so that:
+ // 1) its events are unbinded
+ // 2) it is not removed from the DOM
+ this.setElement(null);
+ }
+
+ this._super.apply(this, arguments);
+
+ if (this.selector) {
+ // Reassign the variables afterwards to allow extensions to use them
+ // after calling the _super method
+ this.$el = $oldel;
+ this.el = $oldel[0];
+ this.$target = this.$el;
+ this.target = this.el;
+ }
+ },
+ /**
+ * @override
+ */
+ setElement: function () {
+ this._super.apply(this, arguments);
+ if (this.selector) {
+ this.$target = this.$el;
+ this.target = this.el;
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @see this.events
+ * @override
+ */
+ _delegateEvents: function () {
+ var self = this;
+ var originalEvents = this.events;
+
+ var events = {};
+ _.each(this.events, function (method, event) {
+ // If the method is a function, use the default Widget system
+ if (typeof method !== 'string') {
+ events[event] = method;
+ return;
+ }
+ // If the method is only a function name without options, use the
+ // default Widget system
+ var methodOptions = method.split(' ');
+ if (methodOptions.length <= 1) {
+ events[event] = method;
+ return;
+ }
+ // If the method has no meaningful options, use the default Widget
+ // system
+ var isAsync = _.contains(methodOptions, 'async');
+ if (!isAsync) {
+ events[event] = method;
+ return;
+ }
+
+ method = self.proxy(methodOptions[methodOptions.length - 1]);
+ if (_.str.startsWith(event, 'click')) {
+ // Protect click handler to be called multiple times by
+ // mistake by the user and add a visual disabling effect
+ // for buttons.
+ method = dom.makeButtonHandler(method);
+ } else {
+ // Protect all handlers to be recalled while the previous
+ // async handler call is not finished.
+ method = dom.makeAsyncHandler(method);
+ }
+ events[event] = method;
+ });
+
+ this.events = events;
+ this._super.apply(this, arguments);
+ this.events = originalEvents;
+ },
+ /**
+ * @private
+ * @param {boolean} [extra=false]
+ * @param {Object} [extraContext]
+ * @returns {Object}
+ */
+ _getContext: function (extra, extraContext) {
+ var context;
+ this.trigger_up('context_get', {
+ extra: extra || false,
+ context: extraContext,
+ callback: function (ctx) {
+ context = ctx;
+ },
+ });
+ return context;
+ },
+});
+
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+/**
+ * The registry object contains the list of available animations.
+ */
+var registry = {};
+
+/**
+ * This is a fix for apple device (<= IPhone 4, IPad 2)
+ * Standard bootstrap requires data-toggle='collapse' element to be tags.
+ * Unfortunatly one snippet uses a tag instead. The fix forces an empty
+ * click handler on these div, which allows standard bootstrap to work.
+ *
+ * This should be removed in a future odoo snippets refactoring.
+ */
+registry._fixAppleCollapse = PublicWidget.extend({
+ selector: 'div[data-toggle="collapse"]',
+ events: {
+ 'click': function () {},
+ },
+});
+
return {
RootWidget: RootWidget,
RootWidgetRegistry: RootWidgetRegistry,
+ Widget: PublicWidget,
+ registry: registry,
};
});
diff --git a/addons/web/views/report_templates.xml b/addons/web/views/report_templates.xml
index f53a56f51ff..ea963fb926b 100644
--- a/addons/web/views/report_templates.xml
+++ b/addons/web/views/report_templates.xml
@@ -23,8 +23,10 @@
-
+
+
+
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index 8ac3e7671fe..ccf02f5c743 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -340,7 +340,8 @@
-
+
+
diff --git a/addons/website/static/src/js/content/lazy_template_call.js b/addons/website/static/src/js/content/lazy_template_call.js
index 99319e8e414..b354a4edd00 100644
--- a/addons/website/static/src/js/content/lazy_template_call.js
+++ b/addons/website/static/src/js/content/lazy_template_call.js
@@ -2,7 +2,7 @@ odoo.define('website.content.lazy_template_call', function (require) {
'use strict';
var Widget = require('web.Widget');
-var websiteRootData = require('website.WebsiteRoot');
+var websiteRootData = require('website.root');
var LazyTemplateRenderer = Widget.extend({
/**
diff --git a/addons/website/static/src/js/content/snippets.animation.js b/addons/website/static/src/js/content/snippets.animation.js
index 2c322012d1c..d2ef26a1bf6 100644
--- a/addons/website/static/src/js/content/snippets.animation.js
+++ b/addons/website/static/src/js/content/snippets.animation.js
@@ -7,10 +7,9 @@ odoo.define('website.content.snippets.animation', function (require) {
var Class = require('web.Class');
var core = require('web.core');
-var dom = require('web.dom');
var mixins = require('web.mixins');
var utils = require('web.utils');
-var Widget = require('web.Widget');
+var publicWidget = require('web.public.widget');
var qweb = core.qweb;
@@ -20,16 +19,18 @@ window.requestAnimationFrame = window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.msRequestAnimationFrame
- || window.oRequestAnimationFrame
- || function (callback) { setTimeout(callback, 10); };
+ || window.oRequestAnimationFrame;
window.cancelAnimationFrame = window.cancelAnimationFrame
|| window.webkitCancelAnimationFrame
|| window.mozCancelAnimationFrame
|| window.msCancelAnimationFrame
- || window.oCancelAnimationFrame
- || function (id) { clearTimeout(id); };
+ || window.oCancelAnimationFrame;
if (!window.performance || !window.performance.now) {
- window.performance = {now: function () { return Date.now(); }};
+ window.performance = {
+ now: function () {
+ return Date.now();
+ }
+ };
}
/**
@@ -74,10 +75,24 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
this._updateCallback = updateCallback;
this.startEvents = startEvents || 'scroll';
this.$startTarget = $($startTarget || window);
- this._getStateCallback = options.getStateCallback
- || ((this.startEvents === 'scroll' && this.$startTarget[0] === window) ? function () { return window.pageYOffset; } : false)
- || ((this.startEvents === 'resize' && this.$startTarget[0] === window) ? function () { return {width: window.innerWidth, height: window.innerHeight}; } : false)
- || function () { return undefined; };
+ 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;
@@ -88,7 +103,7 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
this._uid = '_animationEffect' + _.uniqueId();
this.startEvents = _processEvents(this.startEvents, this._uid);
if (this.endEvents) {
- this.endEvents = _processEvents(this.endEvents, this._uid);
+ this.endEvents = _processEvents(this.endEvents, this._uid);
}
function _processEvents(events, namespace) {
@@ -181,7 +196,9 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
*/
play: function (e) {
this._newEvent = e;
- if (!this._paused) return;
+ if (!this._paused) {
+ return;
+ }
this._paused = false;
this._rafID = window.requestAnimationFrame(this._update.bind(this));
this._lastUpdateTimestamp = undefined;
@@ -190,7 +207,9 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
* Forces the requestAnimationFrame loop to stop.
*/
pause: function () {
- if (this._paused) return;
+ if (this._paused) {
+ return;
+ }
this._paused = true;
window.cancelAnimationFrame(this._rafID);
this._lastUpdateTimestamp = undefined;
@@ -210,7 +229,9 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
* @param {DOMHighResTimeStamp} timestamp
*/
_update: function (timestamp) {
- if (this._paused) return;
+ if (this._paused) {
+ return;
+ }
this._rafID = window.requestAnimationFrame(this._update.bind(this));
// Check the elapsed time since the last update callback call.
@@ -219,7 +240,9 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
var elapsedTime = 0;
if (this._lastUpdateTimestamp) {
elapsedTime = timestamp - this._lastUpdateTimestamp;
- if (elapsedTime < this._minFrameTime) return;
+ if (elapsedTime < this._minFrameTime) {
+ return;
+ }
}
// Check the new animation state thanks to the get state callback and
@@ -242,53 +265,9 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
});
/**
- * Provides a way for executing code once a website DOM element is loaded in the
- * dom and handle the case where the website edit mode is triggered.
- *
* Also register AnimationEffect automatically (@see effects, _prepareEffects).
*/
-var Animation = Widget.extend({
- /**
- * The selector attribute, if defined, allows to automatically create an
- * instance of this animation on page load for each DOM element which
- * matches this selector. The `Animation.$target` element will then be that
- * particular DOM element. This should be the main way of instantiating
- * `Animation` elements.
- */
- selector: false,
- /**
- * Extension of @see Widget.events
- *
- * A description of the event handlers to bind/delegate once the widget
- * has been rendered::
- *
- * 'click .hello .world': 'async _onHelloWorldClick',
- * _^_ _^_ _^_ _^_
- * | | | |
- * | (Optional) jQuery | Handler method name
- * | delegate selector |
- * | |_ (Optional) space separated options
- * | * async: use the automatic system
- * |_ Event name with making handlers promise-ready (see
- * potential jQuery makeButtonHandler, makeAsyncHandler)
- * namespaces
- *
- * Note: the values may be replaced by a function declaration. This is
- * however a deprecated behavior.
- *
- * @type {Object}
- */
- events: {},
- /**
- * Acts as @see Widget.events except that the events are only binded if the
- * Animation instance is instanciated in edit mode.
- */
- edit_events: {},
- /**
- * Acts as @see Widget.events except that the events are only binded if the
- * Animation instance is instanciated in readonly mode.
- */
- read_events: {},
+var Animation = publicWidget.Widget.extend({
/**
* The max FPS at which all the automatic animation effects will be
* running by default.
@@ -328,23 +307,6 @@ var Animation = Widget.extend({
*/
effects: [],
- /**
- * Initializes the events that will need to be binded according to the
- * given mode.
- *
- * @constructor
- * @param {Object} parent
- * @param {boolean} editableMode - true if the page is in edition mode
- */
- init: function (parent, editableMode) {
- this._super.apply(this, arguments);
- this.editableMode = editableMode;
- if (editableMode) {
- this.events = _.extend({}, this.events || {}, this.edit_events || {});
- } else {
- this.events = _.extend({}, this.events || {}, this.read_events || {});
- }
- },
/**
* Initializes the animation. The method should not be called directly as
* called automatically on animation instantiation and on restart.
@@ -360,89 +322,11 @@ var Animation = Widget.extend({
});
return this._super.apply(this, arguments);
},
- /**
- * Destroys the animation and basically restores the target to the state it
- * was before the start method was called (unlike standard widget, the
- * associated $el DOM is not removed).
- *
- * Also stops animation effects and destroys them if any.
- */
- destroy: function () {
- var $oldel = this.$el;
- // The difference with the default behavior is that we unset the
- // associated element first so that:
- // 1) its events are unbinded
- // 2) it is not removed from the DOM
- this.setElement(null);
- this._super.apply(this, arguments);
- // Reassign the variables afterwards to allow extensions to use them
- // after calling the _super method
- this.$el = $oldel;
- this.el = $oldel[0];
- this.$target = this.$el;
- this.target = this.el;
- },
- /**
- * @override
- */
- setElement: function () {
- this._super.apply(this, arguments);
- this.$target = this.$el;
- this.target = this.el;
- },
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
- /**
- * @see this.events
- * @override
- */
- _delegateEvents: function () {
- var self = this;
- var originalEvents = this.events;
-
- var events = {};
- _.each(this.events, function (method, event) {
- // If the method is a function, use the default Widget system
- if (typeof method !== 'string') {
- events[event] = method;
- return;
- }
- // If the method is only a function name without options, use the
- // default Widget system
- var methodOptions = method.split(' ');
- if (methodOptions.length <= 1) {
- events[event] = method;
- return;
- }
- // If the method has no meaningful options, use the default Widget
- // system
- var isAsync = _.contains(methodOptions, 'async');
- if (!isAsync) {
- events[event] = method;
- return;
- }
-
- method = self.proxy(methodOptions[methodOptions.length - 1]);
- if (_.str.startsWith(event, 'click')) {
- // Protect click handler to be called multiple times by
- // mistake by the user and add a visual disabling effect
- // for buttons.
- method = dom.makeButtonHandler(method);
- } else {
- // Protect all handlers to be recalled while the previous
- // async handler call is not finished.
- method = dom.makeAsyncHandler(method);
- }
- events[event] = method;
- });
-
- this.events = events;
- this._super.apply(this, arguments);
- this.events = originalEvents;
- },
/**
* Registers `AnimationEffect` instances.
*
@@ -487,33 +371,13 @@ var Animation = Widget.extend({
new AnimationEffect(this, updateCallback, startEvents, $startTarget, options)
);
},
- /**
- * @private
- * @param {boolean} [extra=false]
- * @param {Object} [extraContext]
- * @returns {Object}
- */
- _getContext: function (extra, extraContext) {
- var context;
- this.trigger_up('context_get', {
- extra: extra || false,
- context: extraContext,
- callback: function (ctx) {
- context = ctx;
- },
- });
- return context;
- },
});
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-/**
- * The registry object contains the list of available animations.
- */
-var registry = {};
+var registry = publicWidget.registry;
-registry.slider = Animation.extend({
+registry.slider = publicWidget.Widget.extend({
selector: '.carousel',
edit_events: {
'slid.bs.carousel': '_onEditionSlide',
@@ -651,8 +515,8 @@ registry.parallax = Animation.extend({
// Initialize parallax data according to snippet and viewport dimensions
this.viewport = document.body.clientHeight - $('#wrapwrap').position().top;
- this.visible_area = [this.$target.offset().top];
- this.visible_area.push(this.visible_area[0] + this.$target.innerHeight() + this.viewport);
+ 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
@@ -680,43 +544,43 @@ registry.parallax = Animation.extend({
// Perform translation if the element is visible only
var vpEndOffset = scrollOffset + this.viewport;
- if (vpEndOffset >= this.visible_area[0]
- && vpEndOffset <= this.visible_area[1]) {
+ 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.visible_area[1]) / (this.visible_area[0] - this.visible_area[1]);
+ 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.share = Animation.extend({
+registry.share = publicWidget.Widget.extend({
selector: '.s_share, .oe_share', // oe_share for compatibility
/**
* @override
*/
start: function () {
- var url_regex = /(\?(?:|.*&)(?:u|url|body)=)(.*?)(&|#|$)/;
- var title_regex = /(\?(?:|.*&)(?:title|text|subject)=)(.*?)(&|#|$)/;
+ var urlRegex = /(\?(?:|.*&)(?:u|url|body)=)(.*?)(&|#|$)/;
+ var titleRegex = /(\?(?:|.*&)(?:title|text|subject)=)(.*?)(&|#|$)/;
var url = encodeURIComponent(window.location.href);
var title = encodeURIComponent($('title').text());
this.$('a').each(function () {
var $a = $(this);
$a.attr('href', function (i, href) {
- return href.replace(url_regex, function (match, a, b, c) {
+ return href.replace(urlRegex, function (match, a, b, c) {
return a + url + c;
- }).replace(title_regex, function (match, a, b, c) {
+ }).replace(titleRegex, function (match, a, b, c) {
return a + title + c;
});
});
if ($a.attr('target') && $a.attr('target').match(/_blank/i) && !$a.closest('.o_editable').length) {
$a.on('click', function () {
- window.open(this.href,'','menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=550,width=600');
+ window.open(this.href, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=550,width=600');
return false;
});
}
@@ -726,7 +590,7 @@ registry.share = Animation.extend({
},
});
-registry.mediaVideo = Animation.extend({
+registry.mediaVideo = publicWidget.Widget.extend({
selector: '.media_iframe_video',
/**
@@ -767,7 +631,7 @@ registry.mediaVideo = Animation.extend({
},
});
-registry.ul = Animation.extend({
+registry.ul = publicWidget.Widget.extend({
selector: 'ul.o_ul_folded, ol.o_ul_folded',
events: {
'click .o_ul_toggle_next': '_onToggleNextClick',
@@ -802,7 +666,7 @@ registry.ul = Animation.extend({
},
});
-registry.gallery = Animation.extend({
+registry.gallery = publicWidget.Widget.extend({
selector: '.o_gallery:not(.o_slideshow)',
xmlDependencies: ['/website/static/src/xml/website.gallery.xml'],
read_events: {
@@ -831,12 +695,12 @@ registry.gallery = Animation.extend({
var $images = $cur.closest('.o_gallery').find('img');
var size = 0.8;
var dimensions = {
- min_width : Math.round( window.innerWidth * size*0.9),
- min_height : Math.round( window.innerHeight * size),
- max_width : Math.round( window.innerWidth * size*0.9),
- max_height : Math.round( window.innerHeight * size),
- width : Math.round( window.innerWidth * size*0.9),
- height : Math.round( window.innerHeight * size)
+ min_width: Math.round(window.innerWidth * size * 0.9),
+ min_height: Math.round(window.innerHeight * size),
+ max_width: Math.round(window.innerWidth * size * 0.9),
+ max_height: Math.round(window.innerHeight * size),
+ width: Math.round(window.innerWidth * size * 0.9),
+ height: Math.round(window.innerHeight * size)
};
$images.each(function () {
@@ -846,14 +710,13 @@ registry.gallery = Animation.extend({
idx = urls.indexOf($img.attr('src'));
milliseconds = $cur.closest('.o_gallery').data('interval') || false;
- params = {
- srcs : urls,
+ var $modal = $(qweb.render('website.gallery.slideshow.lightbox', {
+ srcs: urls,
index: idx,
- dim : dimensions,
- interval : milliseconds,
- id: _.uniqueId('slideshow_')
- };
- var $modal = $(qweb.render('website.gallery.slideshow.lightbox', params));
+ dim: dimensions,
+ interval: milliseconds,
+ id: _.uniqueId('slideshow_'),
+ }));
$modal.modal({
keyboard: true,
backdrop: true,
@@ -875,7 +738,7 @@ registry.gallery = Animation.extend({
},
});
-registry.gallerySlider = Animation.extend({
+registry.gallerySlider = publicWidget.Widget.extend({
selector: '.o_slideshow',
xmlDependencies: ['/website/static/src/xml/website.gallery.xml'],
@@ -899,7 +762,7 @@ registry.gallerySlider = Animation.extend({
function hide() {
$lis.each(function (i) {
- $(this).toggleClass('d-none', !(i >= page*nbPerPage && i < (page+1)*nbPerPage));
+ $(this).toggleClass('d-none', i < page * nbPerPage || i >= (page + 1) * nbPerPage);
});
if (self.editableMode) { // do not remove DOM in edit mode
return;
@@ -927,7 +790,7 @@ registry.gallerySlider = Animation.extend({
var $item = self.$carousel.find('.carousel-inner .carousel-item-prev, .carousel-inner .carousel-item-next');
var index = $item.index();
$lis.removeClass('active')
- .filter('[data-slide-to="'+index+'"]')
+ .filter('[data-slide-to="' + index + '"]')
.addClass('active');
}, 0);
});
@@ -954,7 +817,7 @@ registry.gallerySlider = Animation.extend({
},
});
-registry.socialShare = Animation.extend({
+registry.socialShare = publicWidget.Widget.extend({
selector: '.oe_social_share',
xmlDependencies: ['/website/static/src/xml/website.share.xml'],
read_events: {
@@ -978,16 +841,16 @@ registry.socialShare = Animation.extend({
* @private
*/
_render: function () {
- this.$target.popover({
+ this.$el.popover({
content: qweb.render('website.social_hover', {medias: this.socialList}),
placement: 'bottom',
- container: this.$target,
+ container: this.$el,
html: true,
trigger: 'manual',
animation: false,
}).popover("show");
- this.$target.off('mouseleave.socialShare').on('mouseleave.socialShare', function () {
+ this.$el.off('mouseleave.socialShare').on('mouseleave.socialShare', function () {
var self = this;
setTimeout(function () {
if (!$(".popover:hover").length) {
@@ -1003,19 +866,19 @@ registry.socialShare = Animation.extend({
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 social_network = {
+ 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),
'google-plus': 'https://plus.google.com/share?url=' + url,
};
- if (!_.contains(_.keys(social_network), social)) {
+ if (!_.contains(_.keys(socialNetworks), social)) {
return;
}
var wHeight = 500;
var wWidth = 500;
- window.open(social_network[social], '', 'menubar=no, toolbar=no, resizable=yes, scrollbar=yes, height=' + wHeight + ',width=' + wWidth);
+ window.open(socialNetworks[social], '', 'menubar=no, toolbar=no, resizable=yes, scrollbar=yes, height=' + wHeight + ',width=' + wWidth);
},
//--------------------------------------------------------------------------
@@ -1029,16 +892,16 @@ registry.socialShare = Animation.extend({
* @private
*/
_onMouseEnter: function () {
- var social = this.$target.data('social');
+ var social = this.$el.data('social');
this.socialList = social ? social.split(',') : ['facebook', 'twitter', 'linkedin', 'google-plus'];
- this.hashtags = this.$target.data('hashtags') || '';
+ this.hashtags = this.$el.data('hashtags') || '';
this._render();
this._bindSocialEvent();
},
});
-registry.facebookPage = Animation.extend({
+registry.facebookPage = publicWidget.Widget.extend({
selector: '.o_facebook_page',
/**
@@ -1082,22 +945,7 @@ registry.facebookPage = Animation.extend({
},
});
-/**
- * This is a fix for apple device (<= IPhone 4, IPad 2)
- * Standard bootstrap requires data-toggle='collapse' element to be tags.
- * Unfortunatly one snippet uses a
tag instead. The fix forces an empty
- * click handler on these div, which allows standard bootstrap to work.
- *
- * This should be removed in a future odoo snippets refactoring.
- */
-registry._fixAppleCollapse = Animation.extend({
- selector: '.s_faq_collapse [data-toggle="collapse"]',
- events: {
- 'click': function () {},
- },
-});
-
-registry.anchorSlide = Animation.extend({
+registry.anchorSlide = publicWidget.Widget.extend({
selector: 'a[href^="/"][href*="#"], a[href^="#"]',
read_events: {
'click': '_onAnimateClick',
@@ -1130,7 +978,10 @@ registry.anchorSlide = Animation.extend({
});
return {
- Class: Animation,
+ Widget: publicWidget.Widget,
+ Animation: Animation,
registry: registry,
+
+ Class: Animation, // Deprecated
};
});
diff --git a/addons/website/static/src/js/content/website_root.js b/addons/website/static/src/js/content/website_root.js
index 1abef2589a4..ff398b2ff2d 100644
--- a/addons/website/static/src/js/content/website_root.js
+++ b/addons/website/static/src/js/content/website_root.js
@@ -1,80 +1,30 @@
-odoo.define('website.WebsiteRoot', function (require) {
+odoo.define('website.root', function (require) {
'use strict';
-var ajax = require('web.ajax');
var core = require('web.core');
var Dialog = require('web.Dialog');
-var ServiceProviderMixin = require('web.ServiceProviderMixin');
-var session = require('web.session');
-var utils = require('web.utils');
-var rootWidget = require('web_editor.root_widget');
-var sAnimation = require('website.content.snippets.animation');
-require("website.content.zoomodoo");
+var publicRootData = require('web.public.root');
+require('website.content.zoomodoo');
var _t = core._t;
-var websiteRootRegistry = new rootWidget.RootWidgetRegistry();
+var websiteRootRegistry = publicRootData.publicRootRegistry;
-// Load localizations outside the WebsiteRoot to not wait for DOM ready (but
-// wait for them in WebsiteRoot)
-function getLang() {
- var html = document.documentElement;
- return (html.getAttribute('lang') || 'en_US').replace('-', '_');
-}
-var lang = utils.get_cookie('frontend_lang') || getLang(); // FIXME the cookie value should maybe be in the ctx?
-var localeDef = ajax.loadJS('/web/webclient/locale/' + lang.replace('-', '_'));
-
-/**
- * Element which is designed to be unique and that will be the top-most element
- * in the widget hierarchy. So, all other widgets will be indirectly linked to
- * this Class instance. Its main role will be to retrieve RPC demands from its
- * children and handle them.
- */
-var WebsiteRoot = rootWidget.RootWidget.extend(ServiceProviderMixin, {
- events: _.extend({}, rootWidget.RootWidget.prototype.events || {}, {
+var WebsiteRoot = publicRootData.PublicRoot.extend({
+ events: _.extend({}, publicRootData.PublicRoot.prototype.events || {}, {
'click .js_change_lang': '_onLangChangeClick',
'click .js_publish_management .js_publish_btn': '_onPublishBtnClick',
- 'submit .js_website_submit_form': '_onWebsiteFormSubmit',
- 'click .js_disable_on_click': '_onDisableOnClick',
- 'click .js_multi_website_switch': '_multiWebsiteSwitch',
- 'click .js_multi_company_switch': '_multiCompanySwitch',
+ 'click .js_multi_website_switch': '_onWebsiteSwitch',
+ 'click .js_multi_company_switch': '_onCompanySwitch',
}),
- custom_events: _.extend({}, rootWidget.RootWidget.prototype.custom_events || {}, {
- animation_start_demand: '_onAnimationStartDemand',
- animation_stop_demand: '_onAnimationStopDemand',
- context_get: '_onContextGet',
- main_object_request: '_onMainObjectRequest',
- ready_to_clean_for_save: '_onAnimationStopDemand',
+ custom_events: _.extend({}, publicRootData.PublicRoot.prototype.custom_events || {}, {
+ 'ready_to_clean_for_save': '_onAnimationStopDemand',
}),
- /**
- * @constructor
- */
- init: function () {
- this._super.apply(this, arguments);
- ServiceProviderMixin.init.call(this);
- this.animations = [];
- },
- /**
- * @override
- */
- willStart: function () {
- // TODO would be even greater to wait for those only if necessary
- return $.when(
- this._super.apply(this, arguments),
- session.is_bound,
- localeDef
- );
- },
/**
* @override
*/
start: function () {
- var defs = [this._super.apply(this, arguments)];
-
- // Animations
- defs.push(this._startAnimations());
-
// Compatibility lang change ?
if (!this.$('.js_change_lang').length) {
var $links = this.$('ul.js_language_selector li a:not([data-oe-id])');
@@ -89,28 +39,10 @@ var WebsiteRoot = rootWidget.RootWidget.extend(ServiceProviderMixin, {
});
}
- // Display image thumbnail
- this.$(".o_image[data-mimetype^='image']").each(function () {
- var $img = $(this);
- if (/gif|jpe|jpg|png/.test($img.data('mimetype')) && $img.data('src')) {
- $img.css('background-image', "url('" + $img.data('src') + "')");
- }
- });
-
// Enable magnify on zommable img
this.$('.zoomable img[data-zoom]').zoomOdoo();
- // Auto scroll
- if (window.location.hash.indexOf("scrollTop=") > -1) {
- this.el.scrollTop = +window.location.hash.match(/scrollTop=([0-9]+)/)[1];
- }
-
- // Fix for IE:
- if ($.fn.placeholder) {
- $('input, textarea').placeholder();
- }
-
- return $.when.apply($, defs);
+ return this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
@@ -118,124 +50,24 @@ var WebsiteRoot = rootWidget.RootWidget.extend(ServiceProviderMixin, {
//--------------------------------------------------------------------------
/**
- * Automatically add the global context to RPC commands.
- *
* @override
*/
- _call_service: function (ev) {
- if (ev.data.service === 'ajax' && ev.data.method === 'rpc') {
- var route = ev.data.args[0];
- if (_.str.startsWith(route, '/web/dataset/call_kw/')) {
- var params = ev.data.args[1];
- var options = ev.data.args[2];
- params.kwargs.context = _.extend({}, this._getContext(), params.kwargs.context || {});
- if (options) {
- params.kwargs.context = _.omit(params.kwargs.context, options.noContextKeys);
- ev.data.args[2] = _.omit(options, 'noContextKeys');
- }
- params.kwargs.context = JSON.parse(JSON.stringify(params.kwargs.context));
- }
- }
- return ServiceProviderMixin._call_service.apply(this, arguments);
- },
- /**
- * Retrieves the global context of the public environment. This is the
- * context which is automatically added to each RPC.
- *
- * @private
- * @param {Object} [context]
- * @returns {Object}
- */
_getContext: function (context) {
var html = document.documentElement;
return _.extend({
- 'lang': getLang(),
'website_id': html.getAttribute('data-website-id') | 0,
- }, context || {});
+ }, this._super.apply(this, arguments));
},
/**
- * Retrieves the global context of the public environment (as
- * @see _getContext) but with extra informations that would be useless to
- * send with each RPC.
- *
- * @private
- * @param {Object} [context]
- * @returns {Object}
+ * @override
*/
_getExtraContext: function (context) {
var html = document.documentElement;
- return _.extend(this._getContext(), {
+ return _.extend({
'editable': !!(html.dataset.editable || $('[data-oe-model]').length), // temporary hack, this should be done in python
'translatable': !!html.dataset.translatable,
'edit_translations': !!html.dataset.edit_translations,
- }, context || {});
- },
- /**
- * As the WebsiteRoot instance is designed to be unique, the associated
- * registry has been instantiated outside of the class and is simply
- * returned here.
- *
- * @private
- * @override
- */
- _getRegistry: function () {
- return websiteRootRegistry;
- },
- /**
- * Creates an Animation instance for each DOM element which matches the
- * `selector` key of one of the registered animations
- * (@see Animation.selector).
- *
- * @private
- * @param {boolean} [editableMode=false] - true if the page is in edition mode
- * @param {jQuery} [$from]
- * only initialize the animations whose `selector` matches the
- * element or one of its descendant (default to the wrapwrap element)
- * @returns {Deferred}
- */
- _startAnimations: function (editableMode, $from) {
- var self = this;
-
- editableMode = editableMode || false;
- if ($from === undefined) {
- $from = this.$('#wrapwrap');
- }
-
- this._stopAnimations($from);
-
- var defs = _.map(sAnimation.registry, function (Animation, animationName) {
- var selector = Animation.prototype.selector || '';
- var $target = $from.find(selector).addBack(selector);
-
- var defs = _.map($target, function (el) {
- var animation = new Animation(self, editableMode);
- self.animations.push(animation);
- return animation.attachTo($(el));
- });
- return $.when.apply($, defs);
- });
- return $.when.apply($, defs);
- },
- /**
- * Destroys all animation instances. Especially needed before saving while
- * in edition mode for example.
- *
- * @private
- * @param {jQuery} [$from]
- * only stop the animations linked to the given element(s) or one of
- * its descendants
- */
- _stopAnimations: function ($from) {
- var removedAnimations = _.map(this.animations, function (animation) {
- if (!$from
- || $from.filter(animation.el).length
- || $from.find(animation.el).length) {
- animation.destroy();
- return animation;
- }
- return null;
- });
- this.animations = _.difference(this.animations, removedAnimations);
+ }, this._super.apply(this, arguments));
},
//--------------------------------------------------------------------------
@@ -243,39 +75,12 @@ var WebsiteRoot = rootWidget.RootWidget.extend(ServiceProviderMixin, {
//--------------------------------------------------------------------------
/**
- * Called when the root is notified that the animations have to be
- * (re)started.
- *
- * @private
- * @param {OdooEvent} ev
+ * @override
*/
_onAnimationStartDemand: function (ev) {
- this._startAnimations(ev.data.editableMode, ev.data.$target)
- .done(ev.data.onSuccess)
- .fail(ev.data.onFailure);
- },
- /**
- * Called when the root is notified that the animations have to be
- * stopped.
- *
- * @private
- * @param {OdooEvent} ev
- */
- _onAnimationStopDemand: function (ev) {
- this._stopAnimations(ev.data.$target);
- },
- /**
- * Called when someone asked for the global public context.
- *
- * @private
- * @param {OdooEvent} ev
- */
- _onContextGet: function (ev) {
- if (ev.data.extra) {
- ev.data.callback(this._getExtraContext(ev.data.context));
- } else {
- ev.data.callback(this._getContext(ev.data.context));
- }
+ ev.data.options = _.clone(ev.data.options || {});
+ ev.data.options.editableMode = ev.data.editableMode;
+ this._super.apply(this, arguments);
},
/**
* @todo review
@@ -293,20 +98,6 @@ var WebsiteRoot = rootWidget.RootWidget.extend(ServiceProviderMixin, {
};
window.location.href = _.str.sprintf("/website/lang/%(lang)s?r=%(url)s%(hash)s", redirect);
},
- /**
- * Checks information about the page main object.
- *
- * @private
- * @param {OdooEvent} ev
- */
- _onMainObjectRequest: function (ev) {
- var repr = $('html').data('main-object');
- var m = repr.match(/(.+)\((\d+),(.*)\)/);
- ev.data.callback({
- model: m[1],
- id: m[2] | 0,
- });
- },
/**
* @todo review
* @private
@@ -343,56 +134,35 @@ var WebsiteRoot = rootWidget.RootWidget.extend(ServiceProviderMixin, {
});
},
/**
- * @todo review
- * @private
- */
- _onWebsiteFormSubmit: function (ev) {
- var $buttons = $(ev.currentTarget).find('button[type="submit"], a.a-submit');
- _.each($buttons, function (btn) {
- var $btn = $(btn);
- $btn.attr('data-loading-text', ' ' + $(btn).text());
- $btn.button('loading');
- });
- },
- /**
- * Called when the root is notified that the button should be
- * disabled after the first click.
- *
* @private
* @param {Event} ev
*/
- _onDisableOnClick: function (ev) {
- $(ev.currentTarget).addClass('disabled');
- },
-
- /**
- * Called when clicking on the multi-website switcher.
- *
- * @param {OdooEvent} ev
- */
- _multiWebsiteSwitch: function (ev) {
- var website_id_to_switch_to = ev.currentTarget.getAttribute('website-id');
+ _onWebsiteSwitch: function (ev) {
+ var websiteID = ev.currentTarget.getAttribute('website-id');
// need to force in each case, even if domain is set
// Website 1: localhost; Website 2: 0.0.0.0; website 3: -
// when you switch 3 <--> 1, you need to force the website
- var website_domain = ev.currentTarget.getAttribute('domain');
- var url = $.param.querystring(window.location.href, {'fw': website_id_to_switch_to});
- if (website_domain && window.location.hostname !== website_domain) {
+ var websiteDomain = ev.currentTarget.getAttribute('domain');
+ var url = $.param.querystring(window.location.href, {fw: websiteID});
+ if (websiteDomain && window.location.hostname !== websiteDomain) {
// if domain unchanged, this line will do a nop while we need to refresh
// the page to load the new forced website.
url = new URL(url);
- url.hostname = website_domain;
+ url.hostname = websiteDomain;
}
window.location.href = url;
},
-
- _multiCompanySwitch: function (ev) {
- var company_id_to_switch_to = ev.currentTarget.getAttribute('company-id');
+ /**
+ * @private
+ * @param {Event} ev
+ */
+ _onCompanySwitch: function (ev) {
+ var companyID = parseInt(ev.currentTarget.getAttribute('company-id'), 10);
this._rpc({model: 'res.users',
method: 'write',
- args: [odoo.session_info.user_id, {'company_id': parseInt(company_id_to_switch_to, 10)}],
+ args: [odoo.session_info.user_id, {'company_id': companyID}],
}).then(function () {
window.location.reload(true);
});
diff --git a/addons/website/static/src/js/content/website_root_instance.js b/addons/website/static/src/js/content/website_root_instance.js
index d7d3f2f3f2a..48faa115bac 100644
--- a/addons/website/static/src/js/content/website_root_instance.js
+++ b/addons/website/static/src/js/content/website_root_instance.js
@@ -2,7 +2,7 @@ odoo.define('root.widget', function (require) {
'use strict';
require('web.dom_ready');
-var websiteRootData = require('website.WebsiteRoot');
+var websiteRootData = require('website.root');
var websiteRoot = new websiteRootData.WebsiteRoot(null);
return websiteRoot.attachTo(document.body).then(function () {
diff --git a/addons/website/static/src/js/menu/content.js b/addons/website/static/src/js/menu/content.js
index 9bb2f996595..e18de94e084 100644
--- a/addons/website/static/src/js/menu/content.js
+++ b/addons/website/static/src/js/menu/content.js
@@ -7,7 +7,7 @@ var Dialog = require('web.Dialog');
var time = require('web.time');
var weWidgets = require('wysiwyg.widgets');
var websiteNavbarData = require('website.navbar');
-var websiteRootData = require('website.WebsiteRoot');
+var websiteRootData = require('website.root');
var Widget = require('web.Widget');
var _t = core._t;
@@ -406,7 +406,7 @@ var MenuEntryDialog = weWidgets.LinkDialog.extend({
save: function () {
var $e = this.$('#o_link_dialog_label_input');
if (!$e.val() || !$e[0].checkValidity()) {
- $e.closest('.form-group').addClass('o_has_error').find('.form-control, .custom-select').addClass('is-invalid')
+ $e.closest('.form-group').addClass('o_has_error').find('.form-control, .custom-select').addClass('is-invalid');
$e.focus();
return;
}
diff --git a/addons/website/static/src/js/menu/navbar.js b/addons/website/static/src/js/menu/navbar.js
index aa1646b2eaa..b132b0e2d51 100644
--- a/addons/website/static/src/js/menu/navbar.js
+++ b/addons/website/static/src/js/menu/navbar.js
@@ -1,24 +1,24 @@
odoo.define('website.navbar', function (require) {
'use strict';
-var rootWidget = require('web_editor.root_widget');
+var publicWidget = require('web.public.widget');
var concurrency = require('web.concurrency');
var Widget = require('web.Widget');
-var websiteRootData = require('website.WebsiteRoot');
+var websiteRootData = require('website.root');
-var websiteNavbarRegistry = new rootWidget.RootWidgetRegistry();
+var websiteNavbarRegistry = new publicWidget.RootWidgetRegistry();
-var WebsiteNavbar = rootWidget.RootWidget.extend({
- events: _.extend({}, rootWidget.RootWidget.prototype.events || {}, {
+var WebsiteNavbar = publicWidget.RootWidget.extend({
+ events: _.extend({}, publicWidget.RootWidget.prototype.events || {}, {
'click [data-action]': '_onActionMenuClick',
'mouseover > ul > li.dropdown:not(.show)': '_onMenuHovered',
'click .o_mobile_menu_toggle': '_onMobileMenuToggleClick',
}),
- custom_events: _.extend({}, rootWidget.RootWidget.prototype.custom_events || {}, {
- action_demand: '_onActionDemand',
- edit_mode: '_onEditMode',
- readonly_mode: '_onReadonlyMode',
- ready_to_save: '_onSave',
+ custom_events: _.extend({}, publicWidget.RootWidget.prototype.custom_events || {}, {
+ 'action_demand': '_onActionDemand',
+ 'edit_mode': '_onEditMode',
+ 'readonly_mode': '_onReadonlyMode',
+ 'ready_to_save': '_onSave',
}),
/**
diff --git a/addons/website_forum/static/src/js/website_forum.editor.js b/addons/website_forum/static/src/js/website_forum.editor.js
index f9e03422119..870878f0331 100644
--- a/addons/website_forum/static/src/js/website_forum.editor.js
+++ b/addons/website_forum/static/src/js/website_forum.editor.js
@@ -2,10 +2,8 @@ odoo.define('website_forum.editor', function (require) {
"use strict";
var core = require('web.core');
-var Widget = require('web.Widget');
var WebsiteNewMenu = require('website.newMenu');
var wUtils = require('website.utils');
-var websiteRootData = require('website.WebsiteRoot');
var _t = core._t;
@@ -62,5 +60,4 @@ WebsiteNewMenu.include({
});
},
});
-
});