From 95c2bd33a2e4bc5995b77c64c961e97db809e7f2 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 31 Aug 2017 14:39:52 +0200 Subject: [PATCH] [IMP] web: lazy load nvd3, ace and fullCalendar lib to reduce asset size --- addons/web/static/lib/ace/ace.odoo-custom.js | 13 ++--- addons/web/static/lib/ace/mode-less.js | 2 + addons/web/static/lib/ace/mode-python.js | 4 +- addons/web/static/lib/ace/mode-xml.js | 4 +- addons/web/static/lib/ace/theme-monokai.js | 2 +- addons/web/static/lib/ace/worker-xml.js | 4 +- addons/web/static/src/js/core/ajax.js | 39 +++++++++++++- .../static/src/js/fields/abstract_field.js | 11 ++++ .../web/static/src/js/fields/basic_fields.js | 35 ++++++------- addons/web/static/src/js/libs/nvd3.js | 4 -- .../web/static/src/js/views/abstract_view.js | 51 ++++--------------- .../src/js/views/calendar/calendar_view.js | 2 + .../static/src/js/views/graph/graph_view.js | 8 +++ .../static/tests/views/abstract_view_tests.js | 26 +++++----- addons/web/views/webclient_templates.xml | 19 ++++--- .../web_diagram/static/src/js/diagram_view.js | 18 +++---- .../web_editor/static/src/js/widgets/ace.js | 21 +++++--- .../static/src/js/kanban_gauge.js | 8 +++ .../static/src/js/backend/dashboard.js | 15 ++++-- 19 files changed, 168 insertions(+), 118 deletions(-) diff --git a/addons/web/static/lib/ace/ace.odoo-custom.js b/addons/web/static/lib/ace/ace.odoo-custom.js index 628464abe25..d8f84294316 100644 --- a/addons/web/static/lib/ace/ace.odoo-custom.js +++ b/addons/web/static/lib/ace/ace.odoo-custom.js @@ -6,10 +6,8 @@ if (!i.require || !i.require.packaged) r.original = i.require, i.require = r, i.require.packaged = !0 } var ACE_NAMESPACE = "", - e = function() { - return this - }(); - !e && typeof window != "undefined" && (e = window); + e = {}; + window.ace = e; if (!ACE_NAMESPACE && typeof requirejs != "undefined") return; var t = function(e, n, r) { if (typeof e != "string") { @@ -76,7 +74,9 @@ return s }; o(ACE_NAMESPACE) -})(), define("ace/lib/regexp", ["require", "exports", "module"], function(e, t, n) { +})(); +(function() {var define = ace.define, require = ace.require; +define("ace/lib/regexp", ["require", "exports", "module"], function(e, t, n) { "use strict"; function o(e) { @@ -9826,7 +9826,7 @@ }, t.EditSession = o, t.UndoManager = u }); (function() { - window.require(["ace/ace"], function(a) { + require(["ace/ace"], function(a) { a && a.config.init(true); if (!window.ace) window.ace = a; @@ -9835,3 +9835,4 @@ window.ace[key] = a[key]; }); })(); +})(); diff --git a/addons/web/static/lib/ace/mode-less.js b/addons/web/static/lib/ace/mode-less.js index 8ee94129b2b..6581141bb5c 100644 --- a/addons/web/static/lib/ace/mode-less.js +++ b/addons/web/static/lib/ace/mode-less.js @@ -1,3 +1,4 @@ +(function() {var define = ace.define, require = ace.require; define("ace/mode/less", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text", "ace/tokenizer", "ace/mode/less_highlight_rules", "ace/mode/matching_brace_outdent", "ace/mode/folding/cstyle"], function(e, t) { var r = e("../lib/oop"), o = e("./text").Mode, @@ -204,3 +205,4 @@ define("ace/mode/less", ["require", "exports", "module", "ace/lib/oop", "ace/mod } }).call(o.prototype) }); +})(); diff --git a/addons/web/static/lib/ace/mode-python.js b/addons/web/static/lib/ace/mode-python.js index fd0e436ae50..65087ebd86d 100644 --- a/addons/web/static/lib/ace/mode-python.js +++ b/addons/web/static/lib/ace/mode-python.js @@ -1,3 +1,4 @@ +(function() {var define = ace.define, require = ace.require; define("ace/mode/python_highlight_rules", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text_highlight_rules"], function(e, t, n) { "use strict"; var r = e("../lib/oop"), @@ -182,4 +183,5 @@ define("ace/mode/python_highlight_rules", ["require", "exports", "module", "ace/ r.slice(-i.length) == i && t.remove(new u(n, r.length - i.length, n, r.length)) }, this.$id = "ace/mode/python" }.call(a.prototype), t.Mode = a -}); \ No newline at end of file +}); +})(); diff --git a/addons/web/static/lib/ace/mode-xml.js b/addons/web/static/lib/ace/mode-xml.js index f3bdebf8357..f1bd6dadaa3 100644 --- a/addons/web/static/lib/ace/mode-xml.js +++ b/addons/web/static/lib/ace/mode-xml.js @@ -1,3 +1,4 @@ +(function() {var define = ace.define, require = ace.require; define("ace/mode/xml_highlight_rules", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text_highlight_rules"], function(e, t, n) { "use strict"; var r = e("../lib/oop"), @@ -499,4 +500,5 @@ define("ace/mode/xml_highlight_rules", ["require", "exports", "module", "ace/lib }), t }, this.$id = "ace/mode/xml" }.call(l.prototype), t.Mode = l -}); \ No newline at end of file +}); +})(); diff --git a/addons/web/static/lib/ace/theme-monokai.js b/addons/web/static/lib/ace/theme-monokai.js index b2f9df674f3..322c2fa88d8 100644 --- a/addons/web/static/lib/ace/theme-monokai.js +++ b/addons/web/static/lib/ace/theme-monokai.js @@ -1,4 +1,4 @@ -define("ace/theme/monokai",["require","exports","module","ace/lib/dom"], function(require, exports, module) { +ace.define("ace/theme/monokai",["require","exports","module","ace/lib/dom"], function(require, exports, module) { exports.isDark = true; exports.cssClass = "ace-monokai"; diff --git a/addons/web/static/lib/ace/worker-xml.js b/addons/web/static/lib/ace/worker-xml.js index 7947df70be3..8ddf14e7e8d 100644 --- a/addons/web/static/lib/ace/worker-xml.js +++ b/addons/web/static/lib/ace/worker-xml.js @@ -2,8 +2,8 @@ ;(function(window) { if (typeof window.window != "undefined" && window.document) return; -if (window.require && window.define) - return; + +var define = ace.define, require = ace.require; if (!window.console) { window.console = function() { diff --git a/addons/web/static/src/js/core/ajax.js b/addons/web/static/src/js/core/ajax.js index 63ee1f47c40..5bab96a0c1e 100644 --- a/addons/web/static/src/js/core/ajax.js +++ b/addons/web/static/src/js/core/ajax.js @@ -433,15 +433,52 @@ var loadXML = (function () { }; })(); -return { + +/** + * Loads the given js and css libraries. Note that the ajax loadJS and loadCSS methods + * don't do anything if the given file is already loaded. + * + * @param {Object} libs + * @Param {Array | Array} [libs.jsLibs=[]] The list of JS files that we want to + * load. The list may contain strings (the files to load), or lists of strings. The + * first level is loaded sequentially, and files listed in inner lists are loaded in + * parallel. + * @param {Array} [libs.cssLibs=[]] A list of css files, to be loaded in + * parallel + * + * @returns {Deferred} + */ +function loadLibs (libs) { + var defs = []; + _.each(libs.jsLibs || [], function (urls) { + defs.push($.when.apply($, defs).then(function () { + if (typeof(urls) === 'string') { + return ajax.loadJS(urls); + } else { + return $.when.apply($, _.map(urls, function (url) { + return ajax.loadJS(url); + })); + } + })); + }); + _.each(libs.cssLibs || [], function (url) { + defs.push(ajax.loadCSS(url)); + }); + return $.when.apply($, defs); +} + +var ajax = { jsonRpc: jsonRpc, jsonpRpc: jsonpRpc, rpc: rpc, loadCSS: loadCSS, loadJS: loadJS, loadXML: loadXML, + loadLibs: loadLibs, get_file: get_file, post: post, }; +return ajax; + }); diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js index 9b484354544..bdb745298b2 100644 --- a/addons/web/static/src/js/fields/abstract_field.js +++ b/addons/web/static/src/js/fields/abstract_field.js @@ -30,10 +30,13 @@ odoo.define('web.AbstractField', function (require) { * @module web.AbstractField */ +var ajax = require('web.ajax'); var field_utils = require('web.field_utils'); var Widget = require('web.Widget'); var AbstractField = Widget.extend({ + cssLibs: [], + jsLibs: [], events: { 'keydown': '_onKeydown', }, @@ -173,6 +176,14 @@ var AbstractField = Widget.extend({ return self._render(); }); }, + /** + * Loads the libraries listed in this.jsLibs and this.cssLibs + * + * @override + */ + willStart: function () { + return $.when(ajax.loadLibs(this), this._super.apply(this, arguments)); + }, //-------------------------------------------------------------------------- // Public diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 4c4671275f2..7b15d30202d 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -8,7 +8,6 @@ odoo.define('web.basic_fields', function (require) { */ var AbstractField = require('web.AbstractField'); -var ajax = require('web.ajax'); var config = require('web.config'); var core = require('web.core'); var crash_manager = require('web.crash_manager'); @@ -1949,6 +1948,14 @@ var FieldToggleBoolean = AbstractField.extend({ var JournalDashboardGraph = AbstractField.extend({ className: "o_dashboard_graph", + cssLibs: [ + '/web/static/lib/nvd3/nv.d3.css' + ], + jsLibs: [ + '/web/static/lib/nvd3/d3.v3.js', + '/web/static/lib/nvd3/nv.d3.js', + '/web/static/src/js/libs/nvd3.js' + ], init: function () { this._super.apply(this, arguments); this.graph_type = this.attrs.graph_type; @@ -2069,7 +2076,6 @@ var JournalDashboardGraph = AbstractField.extend({ this._customizeChart(); } }, - }); /** @@ -2282,25 +2288,14 @@ var FieldDomain = AbstractField.extend({ */ var AceEditor = DebouncedField.extend({ template: "AceEditor", + jsLibs: [ + '/web/static/lib/ace/ace.odoo-custom.js', + [ + '/web/static/lib/ace/mode-python.js', + '/web/static/lib/ace/mode-xml.js' + ] + ], events: {}, // events are triggered manually for this debounced widget - /** - * @override willStart from AbstractField (Widget) - * Loads the ace library if not already loaded. - * - * @returns {Deferred} - */ - willStart: function () { - var loadJSDef; - if (!window.ace) { - loadJSDef = ajax.loadJS('/web/static/lib/ace/ace.odoo-custom.js').then(function () { - return $.when( - ajax.loadJS('/web/static/lib/ace/mode-python.js'), - ajax.loadJS('/web/static/lib/ace/mode-xml.js') - ); - }); - } - return $.when(this._super.apply(this, arguments), loadJSDef); - }, /** * @override start from AbstractField (Widget) * diff --git a/addons/web/static/src/js/libs/nvd3.js b/addons/web/static/src/js/libs/nvd3.js index 963a1454c3d..eebee6c9459 100644 --- a/addons/web/static/src/js/libs/nvd3.js +++ b/addons/web/static/src/js/libs/nvd3.js @@ -6,10 +6,6 @@ odoo.define('web.nvd3.extensions', function () { * 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 diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js index 644ba8ac985..873fcc27b0d 100644 --- a/addons/web/static/src/js/views/abstract_view.js +++ b/addons/web/static/src/js/views/abstract_view.js @@ -23,13 +23,12 @@ odoo.define('web.AbstractView', function (require) { * in most case discarded. */ +var ajax = require('web.ajax'); var Class = require('web.Class'); var Context = require('web.Context'); -var ajax = require('web.ajax'); var AbstractModel = require('web.AbstractModel'); var AbstractRenderer = require('web.AbstractRenderer'); var AbstractController = require('web.AbstractController'); -var Context = require('web.Context'); var AbstractView = Class.extend({ // name displayed in view switchers @@ -51,8 +50,6 @@ var AbstractView = Class.extend({ Model: AbstractModel, Renderer: AbstractRenderer, Controller: AbstractController, - js_libs: [], - css_libs: [], }, /** @@ -125,7 +122,15 @@ var AbstractView = Class.extend({ //-------------------------------------------------------------------------- /** - * Main method of the view class. + * Main method of the view class. Create a controller, and make sure that + * data and libraries are loaded. + * + * There is a unusual thing going in this method with parents: we create + * renderer/model with parent as parent, then we have to reassign them at + * the end to make sure that we have the proper relationships. This is + * necessary to solve the problem that the controller need the model and the + * renderer to be instantiated, but the model need a parent to be able to + * load itself, and the renderer needs the data in its constructor. * * @param {Widget} parent The parent of the resulting Controller (most * likely a view manager) @@ -133,7 +138,7 @@ var AbstractView = Class.extend({ */ getController: function (parent) { var self = this; - return $.when(this._loadData(parent), this._loadLibs()).then(function () { + return $.when(this._loadData(parent), ajax.loadLibs(this)).then(function () { var model = self.getModel(); var state = model.get(arguments[0]); var renderer = self.getRenderer(parent, state); @@ -202,40 +207,6 @@ var AbstractView = Class.extend({ var model = this.getModel(parent); return model.load(this.loadParams); }, - /** - * Makes sure that the js_libs and css_libs are properly loaded. Note that - * the ajax loadJS and loadCSS methods don't do anything if the given file - * is already loaded. - * - * @private - * @returns {Deferred} - */ - _loadLibs: function () { - var defs = []; - var jsDefs; - _.each(this.config.js_libs, function (urls) { - if (typeof(urls) === 'string') { - // js_libs is an array of urls: those urls can be loaded in - // parallel - defs.push(ajax.loadJS(urls)); - } else { - // js_libs is an array of arrays of urls: those arrays of urls - // must be loaded sequentially, but the urls inside each - // sub-array can be loaded in parallel - defs.push($.when.apply($, jsDefs).then(function () { - jsDefs = []; - _.each(urls, function (url) { - jsDefs.push(ajax.loadJS(url)); - }); - return $.when.apply($, jsDefs); - })); - } - }); - _.each(this.config.css_libs, function (url) { - defs.push(ajax.loadCSS(url)); - }); - return $.when.apply($, defs); - }, /** * Loads the subviews for x2many fields when they are not inline * diff --git a/addons/web/static/src/js/views/calendar/calendar_view.js b/addons/web/static/src/js/views/calendar/calendar_view.js index 71a6c30b756..47c16300508 100644 --- a/addons/web/static/src/js/views/calendar/calendar_view.js +++ b/addons/web/static/src/js/views/calendar/calendar_view.js @@ -21,6 +21,8 @@ var fieldsToGather = [ var CalendarView = AbstractView.extend({ display_name: _lt('Calendar'), icon: 'fa-calendar', + jsLibs: ['/web/static/lib/fullcalendar/js/fullcalendar.js'], + cssLibs: ['/web/static/lib/fullcalendar/css/fullcalendar.css'], config: { Model: CalendarModel, Controller: CalendarController, diff --git a/addons/web/static/src/js/views/graph/graph_view.js b/addons/web/static/src/js/views/graph/graph_view.js index 423d52ca64c..a5ad21e1816 100644 --- a/addons/web/static/src/js/views/graph/graph_view.js +++ b/addons/web/static/src/js/views/graph/graph_view.js @@ -19,6 +19,14 @@ var _lt = core._lt; var GraphView = AbstractView.extend({ display_name: _lt('Graph'), icon: 'fa-bar-chart', + cssLibs: [ + '/web/static/lib/nvd3/nv.d3.css' + ], + jsLibs: [ + '/web/static/lib/nvd3/d3.v3.js', + '/web/static/lib/nvd3/nv.d3.js', + '/web/static/src/js/libs/nvd3.js' + ], config: { Model: GraphModel, Controller: Controller, diff --git a/addons/web/static/tests/views/abstract_view_tests.js b/addons/web/static/tests/views/abstract_view_tests.js index 1c85b390cc6..b958550a8e1 100644 --- a/addons/web/static/tests/views/abstract_view_tests.js +++ b/addons/web/static/tests/views/abstract_view_tests.js @@ -32,10 +32,9 @@ QUnit.module('Views', { assert.step(url + ' loaded'); }); }; + var View = AbstractView.extend({ - config: _.extend({}, AbstractView.prototype.config, { - js_libs: ['a', 'b'], - }), + jsLibs: [['a', 'b']], }); createAsyncView({ View: View, @@ -71,13 +70,12 @@ QUnit.module('Views', { assert.step(url + ' loaded'); }); }; + var View = AbstractView.extend({ - config: _.extend({}, AbstractView.prototype.config, { - js_libs: [ - ['a', 'b'], - ['c'], - ], - }), + jsLibs: [ + ['a', 'b'], + 'c', + ], }); createAsyncView({ View: View, @@ -85,7 +83,7 @@ QUnit.module('Views', { data: this.data, model: 'fake_model', }).then(function (view) { - assert.verifySteps(['a', 'b', 'a loaded', 'b loaded', 'c', 'c loaded'], + assert.verifySteps(['a', 'b', 'b loaded', 'a loaded', 'c', 'c loaded'], "should for all libs to be loaded"); ajax.loadJS = loadJS; view.destroy(); @@ -94,11 +92,11 @@ QUnit.module('Views', { assert.verifySteps(['a', 'b'], "libs 'a' and 'b' should be loaded in parallel"); - defs.a.resolve(); - assert.verifySteps(['a', 'b', 'a loaded'], - "should wait for 'a' and 'b' to be loaded before loading 'c'"); defs.b.resolve(); - assert.verifySteps(['a', 'b', 'a loaded', 'b loaded', 'c'], + assert.verifySteps(['a', 'b', 'b loaded'], + "should wait for 'a' and 'b' to be loaded before loading 'c'"); + defs.a.resolve(); + assert.verifySteps(['a', 'b', 'b loaded', 'a loaded', 'c'], "should load 'c' when 'a' and 'b' are loaded"); defs.c.resolve(); }); diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 51ef31df56f..1848d9a101d 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -95,7 +95,6 @@ - @@ -129,8 +128,6 @@ - - @@ -142,7 +139,6 @@ - @@ -222,9 +218,6 @@ - - - @@ -408,6 +401,18 @@ + + + + + + + + + + + +