summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/core.js
diff options
context:
space:
mode:
authorGéry Debongnie <ged@odoo.com>2015-03-09 16:34:27 +0100
committerGéry Debongnie <ged@odoo.com>2015-03-18 09:01:49 +0100
commite8a00bc50da435d3859118ca127d20a2eb9a0cab (patch)
tree039f6ec40b37392e76c8830a8e518ac40f7e7fec /addons/web/static/src/js/core.js
parent9036084f065c7aca3b2199903f4da67db3501ba7 (diff)
[REF] web: add module system to the web client
This commit introduces a new module system for the javascript code. Instead of using global (namespaced) variables everywhere (like openerp.web.whatever, ...), each file declare one (or more) module with its dependencies. Each module will be loaded after its dependencies, and will be given a reference to the return values. This change is a massive departure from the way the client worked previously. It will sadly break some/most history, but I am sure that the future git ecosystem will allow you to easily browse the history past this revision (one can dream). This commit feature: * main namespace is now odoo instead of openerp * a way to define modules and dependencies (inspired by requirejs) * many files have been splitted in simpler subfiles * basic support for debugging in odoo.__DEBUG__ * no global variable (i.e. nothing in window.openerp) * many bug fixes, many new bugs Future vision: this modularization should simplify future code development, simplify debugging and will allow a serious testing effort
Diffstat (limited to 'addons/web/static/src/js/core.js')
-rw-r--r--addons/web/static/src/js/core.js787
1 files changed, 0 insertions, 787 deletions
diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js
deleted file mode 100644
index ef7523ee440..00000000000
--- a/addons/web/static/src/js/core.js
+++ /dev/null
@@ -1,787 +0,0 @@
-
-(function() {
-
-if (typeof(console) === "undefined") {
- // Even IE9 only exposes console object if debug window opened
- window.console = {};
- ('log error debug info warn assert clear dir dirxml trace group'
- + ' groupCollapsed groupEnd time timeEnd profile profileEnd count'
- + ' exception').split(/\s+/).forEach(function(property) {
- console[property] = _.identity;
- });
-}
-
-var instance = openerp;
-openerp.web.core = {};
-
-var ControllerMixin = {
- /**
- * Informs the action manager to do an action. This supposes that
- * the action manager can be found amongst the ancestors of the current widget.
- * If that's not the case this method will simply return `false`.
- */
- do_action: function() {
- var parent = this.getParent();
- if (parent) {
- return parent.do_action.apply(parent, arguments);
- }
- return false;
- },
- do_notify: function() {
- if (this.getParent()) {
- return this.getParent().do_notify.apply(this,arguments);
- }
- return false;
- },
- do_warn: function() {
- if (this.getParent()) {
- return this.getParent().do_warn.apply(this,arguments);
- }
- return false;
- },
- rpc: function(url, data, options) {
- return this.alive(openerp.session.rpc(url, data, options));
- }
-};
-
-/**
- A class containing common utility methods useful when working with OpenERP as well as the PropertiesMixin.
-*/
-openerp.web.Controller = openerp.web.Class.extend(openerp.web.PropertiesMixin, ControllerMixin, {
- /**
- * Constructs the object and sets its parent if a parent is given.
- *
- * @param {openerp.web.Controller} parent Binds the current instance to the given Controller instance.
- * When that controller is destroyed by calling destroy(), the current instance will be
- * destroyed too. Can be null.
- */
- init: function(parent) {
- openerp.web.PropertiesMixin.init.call(this);
- this.setParent(parent);
- this.session = openerp.session;
- },
-});
-
-openerp.web.Widget.include(_.extend({}, ControllerMixin, {
- init: function() {
- this._super.apply(this, arguments);
- this.session = openerp.session;
- },
-}));
-
-instance.web.Registry = instance.web.Class.extend({
- /**
- * Stores a mapping of arbitrary key (strings) to object paths (as strings
- * as well).
- *
- * Resolves those paths at query time in order to always fetch the correct
- * object, even if those objects have been overloaded/replaced after the
- * registry was created.
- *
- * An object path is simply a dotted name from the instance root to the
- * object pointed to (e.g. ``"instance.web.Session"`` for an OpenERP
- * session object).
- *
- * @constructs instance.web.Registry
- * @param {Object} mapping a mapping of keys to object-paths
- */
- init: function (mapping) {
- this.parent = null;
- this.map = mapping || {};
- },
- /**
- * Retrieves the object matching the provided key string.
- *
- * @param {String} key the key to fetch the object for
- * @param {Boolean} [silent_error=false] returns undefined if the key or object is not found, rather than throwing an exception
- * @returns {Class} the stored class, to initialize or null if not found
- */
- get_object: function (key, silent_error) {
- var path_string = this.map[key];
- if (path_string === undefined) {
- if (this.parent) {
- return this.parent.get_object(key, silent_error);
- }
- if (silent_error) { return void 'nooo'; }
- return null;
- }
-
- var object_match = instance;
- var path = path_string.split('.');
- // ignore first section
- for(var i=1; i<path.length; ++i) {
- object_match = object_match[path[i]];
-
- if (object_match === undefined) {
- if (silent_error) { return void 'noooooo'; }
- return null;
- }
- }
- return object_match;
- },
- /**
- * Checks if the registry contains an object mapping for this key.
- *
- * @param {String} key key to look for
- */
- contains: function (key) {
- if (key === undefined) { return false; }
- if (key in this.map) {
- return true;
- }
- if (this.parent) {
- return this.parent.contains(key);
- }
- return false;
- },
- /**
- * Tries a number of keys, and returns the first object matching one of
- * the keys.
- *
- * @param {Array} keys a sequence of keys to fetch the object for
- * @returns {Class} the first class found matching an object
- */
- get_any: function (keys) {
- for (var i=0; i<keys.length; ++i) {
- var key = keys[i];
- if (!this.contains(key)) {
- continue;
- }
-
- return this.get_object(key);
- }
- return null;
- },
- /**
- * Adds a new key and value to the registry.
- *
- * This method can be chained.
- *
- * @param {String} key
- * @param {String} object_path fully qualified dotted object path
- * @returns {instance.web.Registry} itself
- */
- add: function (key, object_path) {
- this.map[key] = object_path;
- return this;
- },
- /**
- * Creates and returns a copy of the current mapping, with the provided
- * mapping argument added in (replacing existing keys if needed)
- *
- * Parent and child remain linked, a new key in the parent (which is not
- * overwritten by the child) will appear in the child.
- *
- * @param {Object} [mapping={}] a mapping of keys to object-paths
- */
- extend: function (mapping) {
- var child = new instance.web.Registry(mapping);
- child.parent = this;
- return child;
- },
- /**
- * @deprecated use Registry#extend
- */
- clone: function (mapping) {
- console.warn('Registry#clone is deprecated, use Registry#extend');
- return this.extend(mapping);
- }
-});
-
-instance.web.py_eval = function(expr, context) {
- return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
-};
-
-/*
- Some retro-compatibility.
-*/
-instance.web.JsonRPC = instance.web.Session;
-
-/** Session openerp specific RPC class */
-instance.web.Session.include( /** @lends instance.web.Session# */{
- init: function() {
- this._super.apply(this, arguments);
- this.debug = ($.deparam($.param.querystring()).debug !== undefined);
- // TODO: session store in cookie should be optional
- this.name = instance._session_id;
- this.qweb_mutex = new $.Mutex();
- },
- /**
- * Setup a sessionm
- */
- session_bind: function(origin) {
- var self = this;
- this.setup(origin);
- instance.web.qweb.default_dict['_s'] = this.origin;
- this.uid = null;
- this.username = null;
- this.user_context= {};
- this.db = null;
- this.module_list = instance._modules.slice();
- this.module_loaded = {};
- _(this.module_list).each(function (mod) {
- self.module_loaded[mod] = true;
- });
- this.active_id = null;
- return this.session_init();
- },
- /**
- * Init a session, reloads from cookie, if it exists
- */
- session_init: function () {
- var self = this;
- return this.session_reload().then(function(result) {
- var modules = instance._modules.join(',');
- var deferred = self.load_qweb(modules);
- if(self.session_is_valid()) {
- return deferred.then(function() { return self.load_modules(); });
- }
- return $.when(
- deferred,
- self.rpc('/web/webclient/bootstrap_translations', {mods: instance._modules}).then(function(trans) {
- instance.web._t.database.set_bundle(trans);
- })
- );
- });
- },
- session_is_valid: function() {
- var db = $.deparam.querystring().db;
- if (db && this.db !== db) {
- return false;
- }
- return !!this.uid;
- },
- /**
- * The session is validated by restoration of a previous session
- */
- session_authenticate: function() {
- var self = this;
- return $.when(this._super.apply(this, arguments)).then(function() {
- return self.load_modules();
- });
- },
- session_logout: function() {
- $.bbq.removeState();
- return this.rpc("/web/session/destroy", {});
- },
- get_cookie: function (name) {
- if (!this.name) { return null; }
- var nameEQ = this.name + '|' + name + '=';
- var cookies = document.cookie.split(';');
- for(var i=0; i<cookies.length; ++i) {
- var cookie = cookies[i].replace(/^\s*/, '');
- if(cookie.indexOf(nameEQ) === 0) {
- try {
- return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
- } catch(err) {
- // wrong cookie, delete it
- this.set_cookie(name, '', -1);
- }
- }
- }
- return null;
- },
- /**
- * Create a new cookie with the provided name and value
- *
- * @private
- * @param name the cookie's name
- * @param value the cookie's value
- * @param ttl the cookie's time to live, 1 year by default, set to -1 to delete
- */
- set_cookie: function (name, value, ttl) {
- if (!this.name) { return; }
- ttl = ttl || 24*60*60*365;
- document.cookie = [
- this.name + '|' + name + '=' + encodeURIComponent(JSON.stringify(value)),
- 'path=/',
- 'max-age=' + ttl,
- 'expires=' + new Date(new Date().getTime() + ttl*1000).toGMTString()
- ].join(';');
- },
- /**
- * Load additional web addons of that instance and init them
- *
- */
- load_modules: function() {
- var self = this;
- return this.rpc('/web/session/modules', {}).then(function(result) {
- var all_modules = _.uniq(self.module_list.concat(result));
- var to_load = _.difference(result, self.module_list).join(',');
- self.module_list = all_modules;
-
- var loaded = self.load_translations();
- var locale = "/web/webclient/locale/" + self.user_context.lang || 'en_US';
- var file_list = [ locale ];
- if(to_load.length) {
- loaded = $.when(
- loaded,
- self.rpc('/web/webclient/csslist', {mods: to_load}).done(self.load_css.bind(self)),
- self.load_qweb(to_load),
- self.rpc('/web/webclient/jslist', {mods: to_load}).done(function(files) {
- file_list = file_list.concat(files);
- })
- );
- }
- return loaded.then(function () {
- return self.load_js(file_list);
- }).done(function() {
- self.on_modules_loaded();
- self.trigger('module_loaded');
- });
- });
- },
- load_translations: function() {
- return instance.web._t.database.load_translations(this, this.module_list, this.user_context.lang);
- },
- load_css: function (files) {
- var self = this;
- _.each(files, function (file) {
- openerp.loadCSS(self.url(file, null));
- });
- },
- load_js: function(files) {
- var self = this;
- var d = $.Deferred();
- if (files.length !== 0) {
- var file = files.shift();
- var url = self.url(file, null);
- openerp.loadJS(url).done(d.resolve);
- } else {
- d.resolve();
- }
- return d;
- },
- load_qweb: function(mods) {
- var self = this;
- self.qweb_mutex.exec(function() {
- return self.rpc('/web/proxy/load', {path: '/web/webclient/qweb?mods=' + mods}).then(function(xml) {
- if (!xml) { return; }
- instance.web.qweb.add_template(_.str.trim(xml));
- });
- });
- return self.qweb_mutex.def;
- },
- on_modules_loaded: function() {
- for(var j=0; j<this.module_list.length; j++) {
- var mod = this.module_list[j];
- if(this.module_loaded[mod])
- continue;
- instance[mod] = {};
- // init module mod
- var fct = instance._openerp[mod];
- if(typeof(fct) === "function") {
- instance._openerp[mod] = {};
- for (var k in fct) {
- instance._openerp[mod][k] = fct[k];
- }
- fct(instance, instance._openerp[mod]);
- }
- this.module_loaded[mod] = true;
- }
- },
- /**
- * Cooperative file download implementation, for ajaxy APIs.
- *
- * Requires that the server side implements an httprequest correctly
- * setting the `fileToken` cookie to the value provided as the `token`
- * parameter. The cookie *must* be set on the `/` path and *must not* be
- * `httpOnly`.
- *
- * It would probably also be a good idea for the response to use a
- * `Content-Disposition: attachment` header, especially if the MIME is a
- * "known" type (e.g. text/plain, or for some browsers application/json
- *
- * @param {Object} options
- * @param {String} [options.url] used to dynamically create a form
- * @param {Object} [options.data] data to add to the form submission. If can be used without a form, in which case a form is created from scratch. Otherwise, added to form data
- * @param {HTMLFormElement} [options.form] the form to submit in order to fetch the file
- * @param {Function} [options.success] callback in case of download success
- * @param {Function} [options.error] callback in case of request error, provided with the error body
- * @param {Function} [options.complete] called after both ``success`` and ``error` callbacks have executed
- */
- get_file: function (options) {
- // need to detect when the file is done downloading (not used
- // yet, but we'll need it to fix the UI e.g. with a throbber
- // while dump is being generated), iframe load event only fires
- // when the iframe content loads, so we need to go smarter:
- // http://geekswithblogs.net/GruffCode/archive/2010/10/28/detecting-the-file-download-dialog-in-the-browser.aspx
- var timer, token = new Date().getTime(),
- cookie_name = 'fileToken', cookie_length = cookie_name.length,
- CHECK_INTERVAL = 1000, id = _.uniqueId('get_file_frame'),
- remove_form = false;
-
-
- // iOS devices doesn't allow iframe use the way we do it,
- // opening a new window seems the best way to workaround
- if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
- var params = _.extend({}, options.data || {}, {token: token});
- var url = this.url(options.url, params);
- instance.web.unblockUI();
- return window.open(url);
- }
-
- var $form, $form_data = $('<div>');
-
- var complete = function () {
- if (options.complete) { options.complete(); }
- clearTimeout(timer);
- $form_data.remove();
- $target.remove();
- if (remove_form && $form) { $form.remove(); }
- };
- var $target = $('<iframe style="display: none;">')
- .attr({id: id, name: id})
- .appendTo(document.body)
- .load(function () {
- try {
- if (options.error) {
- var body = this.contentDocument.body;
- var nodes = body.children.length === 0 ? body.childNodes : body.children;
- var node = nodes[1] || nodes[0];
- options.error(JSON.parse(node.textContent));
- }
- } finally {
- complete();
- }
- });
-
- if (options.form) {
- $form = $(options.form);
- } else {
- remove_form = true;
- $form = $('<form>', {
- action: options.url,
- method: 'POST'
- }).appendTo(document.body);
- }
-
- var hparams = _.extend({}, options.data || {}, {token: token});
- if (this.override_session)
- hparams.session_id = this.session_id;
- _.each(hparams, function (value, key) {
- var $input = $form.find('[name=' + key +']');
- if (!$input.length) {
- $input = $('<input type="hidden" name="' + key + '">')
- .appendTo($form_data);
- }
- $input.val(value);
- });
-
- $form
- .append($form_data)
- .attr('target', id)
- .get(0).submit();
-
- var waitLoop = function () {
- var cookies = document.cookie.split(';');
- // setup next check
- timer = setTimeout(waitLoop, CHECK_INTERVAL);
- for (var i=0; i<cookies.length; ++i) {
- var cookie = cookies[i].replace(/^\s*/, '');
- if (!cookie.indexOf(cookie_name === 0)) { continue; }
- var cookie_val = cookie.substring(cookie_length + 1);
- if (parseInt(cookie_val, 10) !== token) { continue; }
-
- // clear cookie
- document.cookie = _.str.sprintf("%s=;expires=%s;path=/",
- cookie_name, new Date().toGMTString());
- if (options.success) { options.success(); }
- complete();
- return;
- }
- };
- timer = setTimeout(waitLoop, CHECK_INTERVAL);
- },
- synchronized_mode: function(to_execute) {
- var synch = this.synch;
- this.synch = true;
- try {
- return to_execute();
- } finally {
- this.synch = synch;
- }
- }
-});
-
-
-/**
- * Event Bus used to bind events scoped in the current instance
- */
-instance.web.Bus = instance.web.Class.extend(instance.web.EventDispatcherMixin, {
- init: function() {
- instance.web.EventDispatcherMixin.init.call(this, parent);
- var self = this;
- // TODO fme: allow user to bind keys for some global actions.
- // check gtk bindings
- // http://unixpapa.com/js/key.html
- _.each('click,dblclick,keydown,keypress,keyup'.split(','), function(evtype) {
- $('html').on(evtype, function(ev) {
- self.trigger(evtype, ev);
- });
- });
- _.each('resize,scroll'.split(','), function(evtype) {
- $(window).on(evtype, function(ev) {
- self.trigger(evtype, ev);
- });
- });
- }
-});
-instance.web.bus = new instance.web.Bus();
-
-instance.web.TranslationDataBase.include({
- set_bundle: function(translation_bundle) {
- this._super(translation_bundle);
- if (translation_bundle.lang_parameters) {
- this.parameters.grouping = py.eval(this.parameters.grouping);
- }
- },
-});
-
-/** Custom jQuery plugins */
-$.fn.getAttributes = function() {
- var o = {};
- if (this.length) {
- for (var attr, i = 0, attrs = this[0].attributes, l = attrs.length; i < l; i++) {
- attr = attrs.item(i);
- o[attr.nodeName] = attr.value;
- }
- }
- return o;
-};
-$.fn.openerpClass = function(additionalClass) {
- // This plugin should be applied on top level elements
- additionalClass = additionalClass || '';
- if (!!$.browser.msie) {
- additionalClass += ' openerp_ie';
- }
- return this.each(function() {
- $(this).addClass('openerp ' + additionalClass);
- });
-};
-$.fn.openerpBounce = function() {
- return this.each(function() {
- $(this).css('box-sizing', 'content-box').effect('bounce', {distance: 18, times: 5}, 250);
- });
-};
-
-/** Jquery extentions */
-$.Mutex = openerp.Mutex;
-
-$.async_when = function() {
- var async = false;
- var def = $.Deferred();
- $.when.apply($, arguments).done(function() {
- var args = arguments;
- var action = function() {
- def.resolve.apply(def, args);
- };
- if (async)
- action();
- else
- setTimeout(action, 0);
- }).fail(function() {
- var args = arguments;
- var action = function() {
- def.reject.apply(def, args);
- };
- if (async)
- action();
- else
- setTimeout(action, 0);
- });
- async = true;
- return def;
-};
-
-// special tweak for the web client
-var old_async_when = $.async_when;
-$.async_when = function() {
- if (instance.session.synch)
- return $.when.apply(this, arguments);
- else
- return old_async_when.apply(this, arguments);
-};
-
-/** Setup default session */
-instance.session = new instance.web.Session();
-
-/**
- * Lazy translation function, only performs the translation when actually
- * printed (e.g. inserted into a template)
- *
- * Useful when defining translatable strings in code evaluated before the
- * translation database is loaded, as class attributes or at the top-level of
- * an OpenERP Web module
- *
- * @param {String} s string to translate
- * @returns {Object} lazy translation object
- */
-instance.web._lt = function (s) {
- return {toString: function () { return instance.web._t(s); }};
-};
-instance.web.qweb.debug = instance.session.debug;
-_.extend(instance.web.qweb.default_dict, {
- '__debug__': instance.session.debug,
- 'moment': function(date) { return new moment(date); },
-});
-instance.web.qweb.preprocess_node = function() {
- // Note that 'this' is the Qweb Node
- switch (this.node.nodeType) {
- case Node.TEXT_NODE:
- case Node.CDATA_SECTION_NODE:
- // Text and CDATAs
- var translation = this.node.parentNode.attributes['t-translation'];
- if (translation && translation.value === 'off') {
- return;
- }
- var match = /^(\s*)([\s\S]+?)(\s*)$/.exec(this.node.data);
- if (match) {
- this.node.data = match[1] + instance.web._t(match[2]) + match[3];
- }
- break;
- case Node.ELEMENT_NODE:
- // Element
- var attr, attrs = ['label', 'title', 'alt', 'placeholder'];
- while ((attr = attrs.pop())) {
- if (this.attributes[attr]) {
- this.attributes[attr] = instance.web._t(this.attributes[attr]);
- }
- }
- }
-};
-
-/** Setup jQuery timeago */
-var _t = instance.web._t;
-/*
- * Strings in timeago are "composed" with prefixes, words and suffixes. This
- * makes their detection by our translating system impossible. Use all literal
- * strings we're using with a translation mark here so the extractor can do its
- * job.
- */
-{
- _t('less than a minute ago');
- _t('about a minute ago');
- _t('%d minutes ago');
- _t('about an hour ago');
- _t('%d hours ago');
- _t('a day ago');
- _t('%d days ago');
- _t('about a month ago');
- _t('%d months ago');
- _t('about a year ago');
- _t('%d years ago');
-}
-
-instance.session.on('module_loaded', this, function () {
- // provide timeago.js with our own translator method
- $.timeago.settings.translator = instance.web._t;
-});
-
-/** Setup blockui */
-if ($.blockUI) {
- $.blockUI.defaults.baseZ = 1100;
- $.blockUI.defaults.message = '<div class="openerp oe_blockui_spin_container" style="background-color: transparent;">';
- $.blockUI.defaults.css.border = '0';
- $.blockUI.defaults.css["background-color"] = '';
-}
-
-var messages_by_seconds = function() {
- return [
- [0, _t("Loading...")],
- [20, _t("Still loading...")],
- [60, _t("Still loading...<br />Please be patient.")],
- [120, _t("Don't leave yet,<br />it's still loading...")],
- [300, _t("You may not believe it,<br />but the application is actually loading...")],
- [420, _t("Take a minute to get a coffee,<br />because it's loading...")],
- [3600, _t("Maybe you should consider reloading the application by pressing F5...")]
- ];
-};
-
-instance.web.Throbber = instance.web.Widget.extend({
- template: "Throbber",
- start: function() {
- var opts = {
- lines: 13, // The number of lines to draw
- length: 7, // The length of each line
- width: 4, // The line thickness
- radius: 10, // The radius of the inner circle
- rotate: 0, // The rotation offset
- color: '#FFF', // #rgb or #rrggbb
- speed: 1, // Rounds per second
- trail: 60, // Afterglow percentage
- shadow: false, // Whether to render a shadow
- hwaccel: false, // Whether to use hardware acceleration
- className: 'spinner', // The CSS class to assign to the spinner
- zIndex: 2e9, // The z-index (defaults to 2000000000)
- top: 'auto', // Top position relative to parent in px
- left: 'auto' // Left position relative to parent in px
- };
- this.spin = new Spinner(opts).spin(this.$el[0]);
- this.start_time = new Date().getTime();
- this.act_message();
- },
- act_message: function() {
- var self = this;
- setTimeout(function() {
- if (self.isDestroyed())
- return;
- var seconds = (new Date().getTime() - self.start_time) / 1000;
- var mes;
- _.each(messages_by_seconds(), function(el) {
- if (seconds >= el[0])
- mes = el[1];
- });
- self.$(".oe_throbber_message").html(mes);
- self.act_message();
- }, 1000);
- },
- destroy: function() {
- if (this.spin)
- this.spin.stop();
- this._super();
- },
-});
-instance.web.Throbber.throbbers = [];
-
-instance.web.blockUI = function() {
- var tmp = $.blockUI.apply($, arguments);
- var throbber = new instance.web.Throbber();
- instance.web.Throbber.throbbers.push(throbber);
- throbber.appendTo($(".oe_blockui_spin_container"));
- return tmp;
-};
-instance.web.unblockUI = function() {
- _.each(instance.web.Throbber.throbbers, function(el) {
- el.destroy();
- });
- return $.unblockUI.apply($, arguments);
-};
-
-
-/* Bootstrap defaults overwrite */
-$.fn.tooltip.Constructor.DEFAULTS.placement = 'auto top';
-$.fn.tooltip.Constructor.DEFAULTS.html = true;
-$.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover focus click';
-$.fn.tooltip.Constructor.DEFAULTS.container = 'body';
-//overwrite bootstrap tooltip method to prevent showing 2 tooltip at the same time
-var bootstrap_show_function = $.fn.tooltip.Constructor.prototype.show;
-$.fn.modal.Constructor.prototype.enforceFocus = function () { };
-$.fn.tooltip.Constructor.prototype.show = function () {
- $('.tooltip').remove();
- //the following fix the bug when using placement
- //auto and the parent element does not exist anymore resulting in
- //an error. This should be remove once we updade bootstrap to a version that fix the bug
- //edit: bug has been fixed here : https://github.com/twbs/bootstrap/pull/13752
- var e = $.Event('show.bs.' + this.type);
- var inDom = $.contains(document.documentElement, this.$element[0]);
- if (e.isDefaultPrevented() || !inDom) return;
- return bootstrap_show_function.call(this);
-};
-
-/**
- * Registry for all the client actions key: tag value: widget
- */
-instance.web.client_actions = new instance.web.Registry();
-
-})();