From 85d1e76ddbac9aeedfba4710443b74cbcc6586be Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Tue, 20 Jun 2017 16:27:28 +0200 Subject: [PATCH] [IMP] web, *: reorganize our libraries' extensions * web_editor, web_tour, website_event_track Before this commit, one JS file was responsible for all the library extensions and fixes. There were two main problems with this system: - The file was only available in the backend (as some extensions are made on backend-only libraries). - Some other modules were doing library customizations. This commit creates one file per library to regroup their associated customization and adds them in the right asset. These are placed in the web module and every other module should avoid doing customizations. This allows to have libraries consistency accross all modules and between the backend and the frontend. --- addons/web/static/src/js/libs.js | 138 ------------------ addons/web/static/src/js/libs/autocomplete.js | 38 +++++ addons/web/static/src/js/libs/bootstrap.js | 29 ++++ addons/web/static/src/js/libs/jquery.js | 98 +++++++++++++ addons/web/static/src/js/libs/nvd3.js | 49 +++++++ .../src/js/views/form/form_controller.js | 2 +- addons/web/views/webclient_templates.xml | 5 +- .../static/src/js/snippets.editor.js | 9 -- addons/web_editor/static/src/js/translator.js | 15 -- addons/web_tour/static/src/js/tour_manager.js | 28 ---- .../static/src/js/website_event_track.js | 4 +- 11 files changed, 221 insertions(+), 194 deletions(-) delete mode 100644 addons/web/static/src/js/libs.js create mode 100644 addons/web/static/src/js/libs/autocomplete.js create mode 100644 addons/web/static/src/js/libs/bootstrap.js create mode 100644 addons/web/static/src/js/libs/jquery.js create mode 100644 addons/web/static/src/js/libs/nvd3.js diff --git a/addons/web/static/src/js/libs.js b/addons/web/static/src/js/libs.js deleted file mode 100644 index 1a058aee761..00000000000 --- a/addons/web/static/src/js/libs.js +++ /dev/null @@ -1,138 +0,0 @@ -odoo.define('web.libs', function () { -"use strict"; - -/** - * External Libraries Customization - * - * This is the place where we do some slightly crappy libraries customization. - * We are not proud of this, but at least, we try to avoid patching them in - * place. - * - * In an ideal world, this file would not be needed, because external libraries - * would not have bugs, their APIS would be done in a way that we could use them - * as is, and their distribution process would make sure that they are properly - * configured. However, we do not live in such a world, so this file is - * necessary. - */ - -// nvd3 customization -//------------------------------------------------------------------------- -if ('nv' in window) { - nv.dev = false; // sets nvd3 library in production mode - - // monkey patch nvd3 to allow removing eventhandler on windowresize events - // see https://github.com/novus/nvd3/pull/396 for more details - - // Adds a resize listener to the window. - nv.utils.onWindowResize = function(fun) { - if (fun === null) return; - window.addEventListener('resize', fun); - }; - - // Backwards compatibility with current API. - nv.utils.windowResize = nv.utils.onWindowResize; - - // Removes a resize listener from the window. - nv.utils.offWindowResize = function(fun) { - if (fun === null) return; - window.removeEventListener('resize', fun); - }; - - // monkey patch nvd3 to prevent crashes when user changes view and nvd3 - // tries to remove tooltips after 500 ms... seriously nvd3, what were you - // thinking? - nv.tooltip.cleanup = function () { - $('.nvtooltip').remove(); - }; - - // monkey patch nvd3 to prevent it to display a tooltip (position: absolute) - // with a negative `top`; with this patch the highest tooltip's position is - // still in the graph - var originalCalcTooltipPosition = nv.tooltip.calcTooltipPosition; - nv.tooltip.calcTooltipPosition = function () { - var container = originalCalcTooltipPosition.apply(this, arguments); - container.style.top = container.style.top.split('px')[0] < 0 ? 0 + 'px' : container.style.top; - return container; - }; -} - -// Bootstrap customization -//------------------------------------------------------------------------- -/* 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'; -$.fn.tooltip.Constructor.DEFAULTS.delay = { show: 1000, hide: 0 }; -//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); -}; - -// jquery customization -//------------------------------------------------------------------------- -jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) { - return function( elem ) { - return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0; - }; -}); - -/** 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.openerpBounce = function() { - return this.each(function() { - $(this).css('box-sizing', 'content-box').effect('bounce', {distance: 18, times: 5}, 250); - }); -}; - -// jquery autocomplete tweak to allow html and classnames -var proto = $.ui.autocomplete.prototype; -var initSource = proto._initSource; - -function filter( array, term ) { - var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" ); - return $.grep( array, function(value_) { - return matcher.test( $( "
" ).html( value_.label || value_.value || value_ ).text() ); - }); -} - -$.extend( proto, { - _initSource: function() { - if ( this.options.html && $.isArray(this.options.source) ) { - this.source = function( request, response ) { - response( filter( this.options.source, request.term ) ); - }; - } else { - initSource.call( this ); - } - }, - - _renderItem: function( ul, item) { - return $( "
  • " ) - .data( "item.autocomplete", item ) - .append( $( "" )[ this.options.html ? "html" : "text" ]( item.label ) ) - .appendTo( ul ) - .addClass(item.classname); - } -}); - -}); diff --git a/addons/web/static/src/js/libs/autocomplete.js b/addons/web/static/src/js/libs/autocomplete.js new file mode 100644 index 00000000000..72f3ee8911b --- /dev/null +++ b/addons/web/static/src/js/libs/autocomplete.js @@ -0,0 +1,38 @@ +odoo.define('web.autocomplete.extensions', function () { +'use strict'; + +/** + * The jquery autocomplete library extensions and fixes should be done here to + * avoid patching in place. + */ + +// jquery autocomplete tweak to allow html and classnames +var proto = $.ui.autocomplete.prototype; +var initSource = proto._initSource; + +function filter( array, term ) { + var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" ); + return $.grep( array, function (value_) { + return matcher.test( $( "
    " ).html( value_.label || value_.value || value_ ).text() ); + }); +} + +$.extend(proto, { + _initSource: function () { + if ( this.options.html && $.isArray(this.options.source) ) { + this.source = function (request, response) { + response( filter( this.options.source, request.term ) ); + }; + } else { + initSource.call( this ); + } + }, + _renderItem: function (ul, item) { + return $( "
  • " ) + .data( "item.autocomplete", item ) + .append( $( "" )[ this.options.html ? "html" : "text" ]( item.label ) ) + .appendTo( ul ) + .addClass(item.classname); + }, +}); +}); diff --git a/addons/web/static/src/js/libs/bootstrap.js b/addons/web/static/src/js/libs/bootstrap.js new file mode 100644 index 00000000000..04ca2381d7d --- /dev/null +++ b/addons/web/static/src/js/libs/bootstrap.js @@ -0,0 +1,29 @@ +odoo.define('web.bootstrap.extensions', function () { +'use strict'; + +/** + * The bootstrap library extensions and fixes should be done here to avoid + * patching in place. + */ + +/* 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'; +$.fn.tooltip.Constructor.DEFAULTS.delay = { show: 1000, hide: 0 }; +//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); +}; +}); diff --git a/addons/web/static/src/js/libs/jquery.js b/addons/web/static/src/js/libs/jquery.js new file mode 100644 index 00000000000..ace99a0f8d3 --- /dev/null +++ b/addons/web/static/src/js/libs/jquery.js @@ -0,0 +1,98 @@ +odoo.define('web.jquery.extensions', function () { +'use strict'; + +/** + * The jquery library extensions and fixes should be done here to avoid patching + * in place. + */ + +// jQuery selectors extensions +$.extend($.expr[':'], { + containsLike: function (element, index, matches){ + return element.innerHTML.toUpperCase().indexOf(matches[3].toUpperCase()) >= 0; + }, + containsExact: function (element, index, matches){ + return $.trim(element.innerHTML) === matches[3]; + }, + /** + * Note all escaped characters need to be double escaped inside of the + * expression, so "\(" needs to be "\\(" + */ + containsRegex: function (element, index, matches){ + var regreg = /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})$/, + reg = regreg.exec(matches[3]); + return reg ? new RegExp(reg[1], reg[2]).test($.trim(element.innerHTML)) : false; + }, + propChecked: function (element, index, matches) { + return $(element).prop("checked") === true; + }, + propSelected: function (element, index, matches) { + return $(element).prop("selected") === true; + }, + propValue: function (element, index, matches) { + return $(element).prop("value") === matches[3]; + }, + propValueContains: function (element, index, matches) { + return $(element).prop("value") && $(element).prop("value").indexOf(matches[3]) !== -1; + }, + hasData: function (element) { + return !!_.toArray(element.dataset).length; + }, + data: function (element, index, matches) { + return $(element).data(matches[3]); + }, +}); + +// jQuery functions extensions +$.fn.extend({ + /** + * Returns all the attributes of a DOM element (first one in the jQuery + * set). + * + * @returns {Object} attribute name -> attribute value + */ + getAttributes: function () { + var o = {}; + if (this.length) { + var attrs = this[0].attributes; + for (var i = 0, l = attrs.length ; i < l ; i++) { + var attr = attrs.item(i); + o[attr.name] = attr.value; + } + } + return o; + }, + /** + * Makes DOM elements bounce the way Odoo decided it. + */ + odooBounce: function () { + return this.each(function () { + var $elem = $(this); + $elem + .css('box-sizing', 'content-box') + .effect('bounce', {distance: 18, times: 5}, 250, function () { + $elem.css('box-sizing', ''); + }); + }); + }, + /** + * Allows to bind events to a handler just as the standard `$.on` function + * but binds the handler so that it is executed before any already-attached + * handler for the same events. + * + * @see jQuery.on + */ + prependEvent: function (events, selector, data, handler) { + this.on.apply(this, arguments); + + events = events.split(' '); + return this.each(function () { + var el = this; + _.each(events, function (evName) { + var handler = $._data(el, 'events')[evName].pop(); + $._data(el, 'events')[evName].unshift(handler); + }); + }); + }, +}); +}); diff --git a/addons/web/static/src/js/libs/nvd3.js b/addons/web/static/src/js/libs/nvd3.js new file mode 100644 index 00000000000..963a1454c3d --- /dev/null +++ b/addons/web/static/src/js/libs/nvd3.js @@ -0,0 +1,49 @@ +odoo.define('web.nvd3.extensions', function () { +'use strict'; + +/** + * The nvd3 library extensions and fixes should be done here to avoid patching + * in place. + */ + +if (!('nv' in window)) { + return; +} + +nv.dev = false; // sets nvd3 library in production mode + +// monkey patch nvd3 to allow removing eventhandler on windowresize events +// see https://github.com/novus/nvd3/pull/396 for more details + +// Adds a resize listener to the window. +nv.utils.onWindowResize = function (fun) { + if (fun === null) return; + window.addEventListener('resize', fun); +}; + +// Backwards compatibility with current API. +nv.utils.windowResize = nv.utils.onWindowResize; + +// Removes a resize listener from the window. +nv.utils.offWindowResize = function (fun) { + if (fun === null) return; + window.removeEventListener('resize', fun); +}; + +// monkey patch nvd3 to prevent crashes when user changes view and nvd3 +// tries to remove tooltips after 500 ms... seriously nvd3, what were you +// thinking? +nv.tooltip.cleanup = function () { + $('.nvtooltip').remove(); +}; + +// monkey patch nvd3 to prevent it to display a tooltip (position: absolute) +// with a negative `top`; with this patch the highest tooltip's position is +// still in the graph +var originalCalcTooltipPosition = nv.tooltip.calcTooltipPosition; +nv.tooltip.calcTooltipPosition = function () { + var container = originalCalcTooltipPosition.apply(this, arguments); + container.style.top = container.style.top.split('px')[0] < 0 ? 0 + 'px' : container.style.top; + return container; +}; +}); diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index 0b85ee8cf42..e9bc8cf2a2a 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -339,7 +339,7 @@ var FormController = BasicController.extend({ */ _onBounceEdit: function () { if (this.$buttons) { - this.$buttons.find('.o_form_button_edit').openerpBounce(); + this.$buttons.find('.o_form_button_edit').odooBounce(); } }, /** diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 02ddcb28dde..fe23ff89679 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -59,6 +59,9 @@ + + + @@ -136,6 +139,7 @@ + @@ -171,7 +175,6 @@ - diff --git a/addons/web_editor/static/src/js/snippets.editor.js b/addons/web_editor/static/src/js/snippets.editor.js index f68bf8756bb..e11e3205815 100644 --- a/addons/web_editor/static/src/js/snippets.editor.js +++ b/addons/web_editor/static/src/js/snippets.editor.js @@ -18,15 +18,6 @@ var data = {}; /* ----- SNIPPET SELECTOR ---- */ -$.extend($.expr[':'], { - hasData: function (node) { - return !!_.toArray(node.dataset).length; - }, - data: function (node, i, m) { - return $(node).data(m[3]); - } -}); - data.globalSelector = { closest: function () { return $(); }, all: function () { return $(); }, diff --git a/addons/web_editor/static/src/js/translator.js b/addons/web_editor/static/src/js/translator.js index b2e7aa483fb..9d1ebe94082 100644 --- a/addons/web_editor/static/src/js/translator.js +++ b/addons/web_editor/static/src/js/translator.js @@ -13,21 +13,6 @@ var _t = core._t; var translatable = !!$('html').data('translatable'); var edit_translations = !!$('html').data('edit_translations'); -$.fn.extend({ - prependEvent: function (events, selector, data, handler) { - this.on(events, selector, data, handler); - events = events.split(' '); - this.each(function () { - var el = this; - _.each(events, function (event) { - var handler = $._data(el, 'events')[event].pop(); - $._data(el, 'events')[event].unshift(handler); - }); - }); - return this; - } -}); - var RTE_Translate = rte.Class.extend({ saveElement: function ($el, context) { // remove multi edition diff --git a/addons/web_tour/static/src/js/tour_manager.js b/addons/web_tour/static/src/js/tour_manager.js index 829c06e7dc5..26628e57384 100644 --- a/addons/web_tour/static/src/js/tour_manager.js +++ b/addons/web_tour/static/src/js/tour_manager.js @@ -12,34 +12,6 @@ var _t = core._t; var RUNNING_TOUR_TIMEOUT = 10000; -$.extend($.expr[':'],{ - containsExact: function(element, index, matches){ - return $.trim(element.innerHTML.toLowerCase()) === matches[3].toLowerCase(); - }, - containsExactCase: function(element, index, matches){ - return $.trim(element.innerHTML) === matches[3]; - }, - // Note all escaped characters need to be double escaped - // inside of the containsRegex, so "\(" needs to be "\\(" - containsRegex: function(element, index, matches){ - var regreg = /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})$/, - reg = regreg.exec(matches[3]); - return reg ? new RegExp(reg[1], reg[2]).test($.trim(element.innerHTML)) : false; - }, - propChecked: function(element, index, matches) { - return $(element).prop("checked") === true; - }, - propSelected: function(element, index, matches) { - return $(element).prop("selected") === true; - }, - propValue: function(element, index, matches) { - return $(element).prop("value") === matches[3]; - }, - propValueContains: function(element, index, matches) { - return $(element).prop("value") && $(element).prop("value").indexOf(matches[3]) !== -1; - }, -}); - function get_step_key(name) { return 'tour_' + name + '_step'; } diff --git a/addons/website_event_track/static/src/js/website_event_track.js b/addons/website_event_track/static/src/js/website_event_track.js index ccb080270aa..4fb9baa8215 100644 --- a/addons/website_event_track/static/src/js/website_event_track.js +++ b/addons/website_event_track/static/src/js/website_event_track.js @@ -9,8 +9,8 @@ $(document).ready(function() { $("#search_summary").removeClass('invisible'); if (change_text) { - $("#search_number").text($(".event_track:Contains("+change_text+")").length); - $(".event_track:not(:Contains("+change_text+"))").addClass('invisible'); + $("#search_number").text($(".event_track:containsLike("+change_text+")").length); + $(".event_track:not(:containsLike("+change_text+"))").addClass('invisible'); } else { $("#search_number").text(30); }