summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/core/dom.js91
1 files changed, 90 insertions, 1 deletions
diff --git a/addons/web/static/src/js/core/dom.js b/addons/web/static/src/js/core/dom.js
index 2a961596862..848e406c4b2 100644
--- a/addons/web/static/src/js/core/dom.js
+++ b/addons/web/static/src/js/core/dom.js
@@ -20,6 +20,7 @@ odoo.define('web.dom', function (require) {
* something happens in the DOM.
*/
+var concurrency = require('web.concurrency');
var config = require('web.config');
var core = require('web.core');
var _t = core._t;
@@ -40,7 +41,9 @@ function _notify(content, callbacks) {
core.bus.trigger('DOM_updated', content);
}
-return {
+var dom = {
+ DEBOUNCE: 400,
+
/**
* Appends content in a jQuery object and optionnally triggers an event
*
@@ -209,6 +212,91 @@ return {
return position;
},
/**
+ * Protects a function which is to be used as a handler by preventing its
+ * execution for the duration of a previous call to it (including async
+ * parts of that call).
+ *
+ * Limitation: as the handler is ignored during async actions,
+ * the 'preventDefault' or 'stopPropagation' calls it may want to do
+ * will be ignored too. Using the 'preventDefault' and 'stopPropagation'
+ * arguments solves that problem.
+ *
+ * @param {function} fct
+ * The function which is to be used as a handler. If a promise
+ * is returned, it is used to determine when the handler's action is
+ * finished. Otherwise, the return is used as jQuery uses it.
+ * @param {function|boolean} preventDefault
+ * @param {function|boolean} stopPropagation
+ */
+ makeAsyncHandler: function (fct, preventDefault, stopPropagation) {
+ // Create a deferred indicating if a previous call to this handler is
+ // still pending.
+ var def = $.when();
+
+ return function (ev) {
+ if (preventDefault === true || preventDefault && preventDefault()) {
+ ev.preventDefault();
+ }
+ if (stopPropagation === true || stopPropagation && stopPropagation()) {
+ ev.stopPropagation();
+ }
+
+ if (def.state() === 'pending') {
+ // If a previous call to this handler is still pending, ignore
+ // the new call.
+ return;
+ }
+ var result = fct.apply(this, arguments);
+ def = $.when(result);
+ return result;
+ };
+ },
+ /**
+ * Creates a debounced version of a function to be used as a button click
+ * handler. Also improves the handler to disable the button for the time of
+ * the debounce and/or the time of the async actions it performs.
+ *
+ * Limitation: if two handlers are put on the same button, the button will
+ * become enabled again once any handler's action finishes (multiple click
+ * handlers should however not be binded to the same button).
+ *
+ * @param {function} fct
+ * The function which is to be used as a button click handler. If a
+ * promise is returned, it is used to determine when the button can be
+ * re-enabled. Otherwise, the return is used as jQuery uses it.
+ */
+ makeButtonHandler: function (fct) {
+ // Fallback: if the final handler is not binded to a button, at least
+ // make it an async handler (also handles the case where some events
+ // might ignore the disabled state of the button).
+ fct = dom.makeAsyncHandler(fct);
+
+ return function (ev) {
+ var result = fct.apply(this, arguments);
+
+ var $button = $(ev.target).closest('.btn');
+ if (!$button.length) {
+ return result;
+ }
+
+ // Disable the button for the duration of the handler's action
+ // or at least for the duration of the click debounce. This makes
+ // a 'real' debounce creation useless. Also, during the debouncing
+ // part, the button is disabled without any visual effect.
+ $button.addClass('o_debounce_disabled');
+ $.when(dom.DEBOUNCE && concurrency.delay(dom.DEBOUNCE)).then(function () {
+ $button.addClass('disabled').prop('disabled', true);
+ $button.removeClass('o_debounce_disabled');
+
+ return $.when(result).always(function () {
+ $button.removeClass('disabled').prop('disabled', false);
+ });
+ });
+
+ return result;
+ };
+ },
+ /**
* Prepends content in a jQuery object and optionnally triggers an event
*
* @param {jQuery} [$target] the node where content will be prepended
@@ -460,4 +548,5 @@ return {
}
},
};
+return dom;
});