diff options
| author | Christophe Matthieu <chm@odoo.com> | 2019-01-28 09:39:56 +0000 |
|---|---|---|
| committer | Christophe Matthieu <chm@odoo.com> | 2019-02-14 15:11:19 +0000 |
| commit | b49745e52d7a7bc27afd416206dcd5fa242aa07a (patch) | |
| tree | ae6a047da7e77b951db241e1342278ce543972e2 /addons/web_editor/static | |
| parent | 7daa85dd12ff522be510bc2f8db1d968cae7d891 (diff) | |
[IMP] web_editor: lazy load the wysiwyg
Issue: wysiwyg asset slow down the loading of the website, error
inadvertently introduced: https://github.com/odoo/odoo/pull/29775
The assets are now loaded assynchroneously, when the editor is needed,
its assets will be loaded.
closes odoo/odoo#30700
Diffstat (limited to 'addons/web_editor/static')
12 files changed, 200 insertions, 114 deletions
diff --git a/addons/web_editor/static/src/js/backend/convert_inline.js b/addons/web_editor/static/src/js/backend/convert_inline.js index 3b09bef5f31..b6b36a388ef 100644 --- a/addons/web_editor/static/src/js/backend/convert_inline.js +++ b/addons/web_editor/static/src/js/backend/convert_inline.js @@ -1,7 +1,6 @@ odoo.define('web_editor.convertInline', function (require) { 'use strict'; -var fonts = require('wysiwyg.fonts'); var FieldHtml = require('web_editor.field.html'); /** @@ -189,6 +188,8 @@ function getMatchedCSSRules(a) { * converted to images */ function fontToImg($editable) { + var fonts = odoo.__DEBUG__.services["wysiwyg.fonts"]; + $editable.find('.fa').each(function () { var $font = $(this); var icon, content; diff --git a/addons/web_editor/static/src/js/backend/field_html.js b/addons/web_editor/static/src/js/backend/field_html.js index 69e32fb815e..b78fcc1d35f 100644 --- a/addons/web_editor/static/src/js/backend/field_html.js +++ b/addons/web_editor/static/src/js/backend/field_html.js @@ -4,12 +4,12 @@ odoo.define('web_editor.field.html', function (require) { var ajax = require('web.ajax'); var basic_fields = require('web.basic_fields'); var core = require('web.core'); -var Wysiwyg = require('web_editor.wysiwyg'); +var Wysiwyg = require('web_editor.wysiwyg.root'); var field_registry = require('web.field_registry'); var TranslatableFieldMixin = basic_fields.TranslatableFieldMixin; - var QWeb = core.qweb; +var assetsLoaded = false; /** * FieldHtml Widget @@ -40,8 +40,23 @@ var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, { */ willStart: function () { this._onUpdateIframeId = 'onLoad_' + _.uniqueId('FieldHtml'); - var defAsset = this.nodeOptions.cssReadonly && ajax.loadAsset(this.nodeOptions.cssReadonly); - return $.when(this._super().then(Wysiwyg.prepare.bind(Wysiwyg, this)), defAsset); + var defAsset = null; + if (this.nodeOptions.cssReadonly) { + defAsset = ajax.loadAsset(this.nodeOptions.cssReadonly); + } + + if (!assetsLoaded) { // avoid flickering when begin to edit + assetsLoaded = $.Deferred(); + var wysiwyg = new Wysiwyg(this, {}); + wysiwyg.attachTo($('<textarea>')).then(function () { + wysiwyg.destroy(); + var def = assetsLoaded; + assetsLoaded = true; + def.resolve(); + }); + } + + return $.when(this._super(), assetsLoaded, defAsset); }, /** * @override @@ -147,7 +162,7 @@ var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, { // by default this is synchronous because the assets are already loaded in willStart // but it can be async in the case of options such as iframe, snippets... return this.wysiwyg.attachTo(this.$target).then(function () { - self.$content = self.wysiwyg.$el; + self.$content = self.wysiwyg.$editor; self._onLoadWysiwyg(); }); }, @@ -444,7 +459,7 @@ var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, { position: 'absolute', right: '+5px', }); - var $toolbar = this.$content.closest('.note-editor').find('.note-toolbar'); + var $toolbar = this.$content.find('.note-toolbar'); $toolbar.css('position', 'relative'); $toolbar.append($button); }, diff --git a/addons/web_editor/static/src/js/common/ace.js b/addons/web_editor/static/src/js/common/ace.js index f242129c4a8..2f9108b4d0d 100644 --- a/addons/web_editor/static/src/js/common/ace.js +++ b/addons/web_editor/static/src/js/common/ace.js @@ -191,7 +191,6 @@ var ViewEditor = Widget.extend({ willStart: function () { return $.when( this._super.apply(this, arguments), - ajax.loadLibs(this), this._loadResources() ); }, diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/font.js b/addons/web_editor/static/src/js/wysiwyg/plugin/font.js index c52712d7a02..cd4d80a9a45 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/font.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/font.js @@ -534,6 +534,11 @@ registry.addJob(function (wysiwyg) { if ('web_editor.colorpicker' in QWeb.templates) { return; } + + if (wysiwyg.isDestroyed()) { + throw new Error('The Wysiwyg are destroyed before this loading'); + } + var options = {}; wysiwyg.trigger_up('getRecordInfo', { recordInfo: options, diff --git a/addons/web_editor/static/src/js/wysiwyg/root.js b/addons/web_editor/static/src/js/wysiwyg/root.js new file mode 100644 index 00000000000..5dd48b6acf7 --- /dev/null +++ b/addons/web_editor/static/src/js/wysiwyg/root.js @@ -0,0 +1,62 @@ +odoo.define('web_editor.wysiwyg.root', function (require) { +'use strict'; + +var Widget = require('web.Widget'); + +var assetsLoaded = false; + +var WysiwygRoot = Widget.extend({ + assetLibs: ['web_editor.compiled_assets_wysiwyg'], + + publicMethods: ['isDirty', 'save', 'getValue', 'setValue', 'getEditable', 'on', 'trigger'], + + /** + * @see 'web_editor.wysiwyg' module + **/ + init: function (parent, params) { + this._super.apply(this, arguments); + this._params = params; + this.$editor = null; + }, + /** + * Load assets + * + * @override + **/ + willStart: function () { + var self = this; + + var $target = this.$el; + this.$el = null; + + return this._super().then(function () { + if (!assetsLoaded) { + var Wysiwyg = odoo.__DEBUG__.services['web_editor.wysiwyg']; + _.each(['getRange', 'setRange', 'setRangeFromNode'], function (methodName) { + WysiwygRoot[methodName] = Wysiwyg[methodName].bind(Wysiwyg); + }); + assetsLoaded = true; + } + + var Wysiwyg = self._getWysiwygContructor(); + var instance = new Wysiwyg(self, self._params); + self._params = null; + + _.each(self.publicMethods, function (methodName) { + self[methodName] = instance[methodName].bind(instance); + }); + + return instance.attachTo($target).then(function () { + self.$editor = instance.$el; + }); + }); + }, + + _getWysiwygContructor: function () { + return odoo.__DEBUG__.services['web_editor.wysiwyg']; + } +}); + +return WysiwygRoot; + +}); diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js index 8cf1026158d..44f7dd2c6a8 100644 --- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js +++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js @@ -19,7 +19,10 @@ var Wysiwyg = Widget.extend({ wysiwyg_blur: '_onWysiwygBlur', }, defaultOptions: { - codeview: config.debug + codeview: config.debug, + recordInfo: { + context: {}, + }, }, /** @@ -694,41 +697,6 @@ var Wysiwyg = Widget.extend({ //-------------------------------------------------------------------------- /** - * Load wysiwyg assets if needed. - * - * @see Wysiwyg.createReadyFunction - * @param {Widget} parent - * @returns {$.Promise} - */ -Wysiwyg.prepare = (function () { - var assetsLoaded = false; - var def; - return function prepare(parent) { - if (assetsLoaded) { - return $.when(); - } - if (def) { - return def; - } - def = $.Deferred(); - var timeout = setTimeout(function () { - throw _t("Can't load assets of the wysiwyg editor"); - }, 10000); - var wysiwyg = new Wysiwyg(parent, { - recordInfo: { - context: {}, - } - }); - wysiwyg.attachTo($('<textarea>')).then(function () { - assetsLoaded = true; - clearTimeout(timeout); - wysiwyg.destroy(); - def.resolve(); - }); - return def; - }; -})(); -/** * @param {Node} node (editable or node inside) * @returns {Object} * @returns {Node} sc - start container diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js index 7bfe76e3d24..9de5c6b4cb7 100644 --- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js +++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js @@ -3,7 +3,9 @@ odoo.define('web_editor.wysiwyg.iframe', function (require) { var Wysiwyg = require('web_editor.wysiwyg'); var ajax = require('web.ajax'); +var core = require('web.core'); +var qweb = core.qweb; var _fnSummernoteMaster = $.fn.summernote; var _summernoteMaster = $.summernote; @@ -165,48 +167,14 @@ Wysiwyg.include({ console.warn('Wysiwyg immediate iframe double load detected'); return; } - var cwindow = this.$iframe[0].contentWindow; - cwindow.document + var iframeContent = qweb.render('wysiwyg.iframeContent', { + asset: asset, + updateIframeId: this._onUpdateIframeId, + avoidDoubleLoad: _avoidDoubleLoad + }); + this.$iframe[0].contentWindow.document .open("text/html", "replace") - .write( - '<head>' + - '<meta charset="utf-8">' + - '<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>\n' + - '<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>\n' + - _.map(asset.cssLibs, function (cssLib) { - return '<link type="text/css" rel="stylesheet" href="' + cssLib + '"/>'; - }).join('\n') + '\n' + - _.map(asset.cssContents, function (cssContent) { - return '<style type="text/css">' + cssContent + '</style>'; - }).join('\n') + '\n' + - _.map(asset.jsContents, function (jsContent) { - if (jsContent.indexOf('<inline asset>') !== -1) { - return '<script type="text/javascript">' + jsContent + '</script>'; - } - }).join('\n') + '\n' + - '</head>\n' + - '<body class="o_in_iframe">\n' + - '<div id="iframe_target" style="height: calc(100vh - 6px);"></div>\n' + - '<script type="text/javascript">' + - 'window.$ = window.jQuery = window.top.jQuery;' + - 'var _summernoteMaster = $.summernote;' + - 'var _fnSummernoteMaster = $.fn.summernote;' + - 'delete $.summernote;' + - 'delete $.fn.summernote;' + - '</script>\n' + - '<script type="text/javascript" src="/web_editor/static/lib/summernote/summernote.js"></script>\n' + - '<script type="text/javascript">' + - 'window._summernoteSlave = $.summernote;' + - 'window._summernoteSlave.iframe = true;' + - 'window._summernoteSlave.lang = _summernoteMaster.lang;' + - 'window._fnSummenoteSlave = $.fn.summernote;' + - '$.summernote = _summernoteMaster;' + - '$.fn.summernote = _fnSummernoteMaster;' + - 'if (window.top.' + this._onUpdateIframeId + ') {' + - 'window.top.' + this._onUpdateIframeId + '(' + _avoidDoubleLoad + ')' + - '}' + - '</script>\n' + - '</body>'); + .write(iframeContent); }.bind(this)); }.bind(this)); diff --git a/addons/web_editor/static/src/js/wysiwyg_snippets/wysiwyg_snippets.js b/addons/web_editor/static/src/js/wysiwyg_snippets/wysiwyg_snippets.js index 0f411343486..5df48ebf2f9 100644 --- a/addons/web_editor/static/src/js/wysiwyg_snippets/wysiwyg_snippets.js +++ b/addons/web_editor/static/src/js/wysiwyg_snippets/wysiwyg_snippets.js @@ -15,6 +15,7 @@ Wysiwyg.include({ snippet_cloned: '_onSnippetCloned', snippet_dropped: '_onSnippetDropped', snippet_focused: '_onSnippetFocused', + reload_snippet_dropzones: '_onReloadSnippetDropzones', }), selectorEditableArea: '.note-editable', @@ -173,6 +174,14 @@ Wysiwyg.include({ context.invoke('MediaPlugin.hidePopovers'); }, /** + * trigger reload_snippet_dropzones on snippets + * + * @private + */ + _onReloadSnippetDropzones: function () { + this.snippets.trigger('reload_snippet_dropzones'); + }, + /** * Triggered when a snippet is cloned in the editable area * * @private @@ -258,4 +267,6 @@ $.fn.extend({ }, }); +return Wysiwyg; + }); diff --git a/addons/web_editor/static/src/xml/wysiwyg.xml b/addons/web_editor/static/src/xml/wysiwyg.xml index 5d7754dd7c3..d28efeee92b 100644 --- a/addons/web_editor/static/src/xml/wysiwyg.xml +++ b/addons/web_editor/static/src/xml/wysiwyg.xml @@ -2,6 +2,48 @@ <templates id="template" xml:space="preserve"> <!--=================--> + <!-- Edition Iframe --> + <!--=================--> + + <t t-name="wysiwyg.iframeContent"><head> + <meta charset="utf-8"/> + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> + <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/> + <t t-foreach="asset.cssLibs || []" t-as="cssLib"> + <link type="text/css" rel="stylesheet" t-att-href="cssLib"/> + </t> + <t t-foreach="asset.cssContents || []" t-as="cssContent"> + <style type="text/css" t-raw="cssContent"/> + </t> + <t t-foreach="asset.jsContents || []" t-as="jsContent" t-if="jsContent.indexOf('inline asset') !== -1"> + <script type="text/javascript" t-raw="jsContent"/> + </t> + </head> + <body class="o_in_iframe"> + <div id="iframe_target" style="height: calc(100vh - 6px);"/> + <script type="text/javascript"> + window.$ = window.jQuery = window.top.jQuery; + var _summernoteMaster = $.summernote; + var _fnSummernoteMaster = $.fn.summernote; + delete $.summernote; + delete $.fn.summernote; + </script> + <script type="text/javascript" src="/web_editor/static/lib/summernote/summernote.js"/> + <script type="text/javascript"> + window._summernoteSlave = $.summernote; + window._summernoteSlave.iframe = true; + window._summernoteSlave.lang = _summernoteMaster.lang; + window._fnSummenoteSlave = $.fn.summernote; + $.summernote = _summernoteMaster; + $.fn.summernote = _fnSummernoteMaster; + if (window.top.<t t-esc="updateIframeId"/>) { + window.top.<t t-esc="updateIframeId"/>(<t t-esc="avoidDoubleLoad"/>); + } + </script> + </body> + </t> + + <!--=================--> <!-- Edition Dialogs --> <!--=================--> diff --git a/addons/web_editor/static/tests/field_html_tests.js b/addons/web_editor/static/tests/field_html_tests.js index b01ab48586b..46e66eed81e 100644 --- a/addons/web_editor/static/tests/field_html_tests.js +++ b/addons/web_editor/static/tests/field_html_tests.js @@ -67,7 +67,7 @@ QUnit.module('field html', { } if (xmlId === 'template.assets_all_style') { return $.when({ - cssLibs: $('head link[href]:not([type="image/x-icon"])').map(function () { + cssLibs: $('link[href]:not([type="image/x-icon"])').map(function () { return $(this).attr('href'); }).get(), cssContents: ['body {background-color: red;}'] @@ -815,7 +815,7 @@ QUnit.test('field html translatable', function (assert) { this.data['note.note'].fields.body.translate = true; - testUtils.createAsyncView({ + return testUtils.createAsyncView({ View: FormView, model: 'note.note', data: this.data, @@ -841,7 +841,6 @@ QUnit.test('field html translatable', function (assert) { form.destroy(); _t.database.multi_lang = multiLang; - }); }); diff --git a/addons/web_editor/static/tests/test_utils.js b/addons/web_editor/static/tests/test_utils.js index ff740e52f36..9f89fd2f3a7 100644 --- a/addons/web_editor/static/tests/test_utils.js +++ b/addons/web_editor/static/tests/test_utils.js @@ -101,7 +101,7 @@ function patch() { } if (xmlId === 'template.assets_all_style') { return $.when({ - cssLibs: $('head link[href]:not([type="image/x-icon"])').map(function () { + cssLibs: $('link[href]:not([type="image/x-icon"])').map(function () { return $(this).attr('href'); }).get(), cssContents: ['body {background-color: red;}'] diff --git a/addons/web_editor/static/tests/wysiwyg_tests.js b/addons/web_editor/static/tests/wysiwyg_tests.js index c2033848e67..3146f908520 100644 --- a/addons/web_editor/static/tests/wysiwyg_tests.js +++ b/addons/web_editor/static/tests/wysiwyg_tests.js @@ -3039,8 +3039,10 @@ QUnit.test('CodeView', function (assert) { var defMediaDialogInit; var defMediaDialogSave; +var defMediaDialogClose; var defCropDialogInit; var defCropDialogSave; +var defCropDialogClose; var defAltDialogInit; var defAltDialogSave; var imgWidth = 10; @@ -3051,26 +3053,34 @@ QUnit.module('Media', { testUtils.mock.patch(MediaDialog, { init: function () { this._super.apply(this, arguments); + this.uid = _.uniqueId(); defMediaDialogInit = $.Deferred(); defMediaDialogSave = $.Deferred(); + defMediaDialogClose = $.Deferred(); this.opened(defMediaDialogInit.resolve.bind(defMediaDialogInit)); - }, - save: function () { - $.when(this._super.apply(this, arguments)).then(function () { - var def = defMediaDialogSave; + this.on('closed', this, function () { + var def = defMediaDialogClose; defMediaDialogInit = null; defMediaDialogSave = null; + defMediaDialogClose = null; def.resolve(); }); }, + save: function () { + var def = defMediaDialogSave; + $.when(this._super.apply(this, arguments)) + .then(def.resolve.bind(def)); + }, }); testUtils.mock.patch(CropDialog, { init: function () { $(arguments[2]).attr('src', $(arguments[2]).data('src')); this._super.apply(this, arguments); var self = this; + this.uid = _.uniqueId(); defCropDialogInit = $.Deferred(); defCropDialogSave = $.Deferred(); + defCropDialogClose = $.Deferred(); this.opened(function () { var cropper = self.$cropperImage.data('cropper'); cropper.clone(); @@ -3084,15 +3094,19 @@ QUnit.module('Media', { cropper.render(); defCropDialogInit.resolve(); }); - }, - save: function () { - $.when(this._super.apply(this, arguments)).then(function () { - var def = defCropDialogSave; + this.on('closed', this, function () { + var def = defCropDialogClose; defCropDialogInit = null; defCropDialogSave = null; + defCropDialogClose = null; def.resolve(); }); }, + save: function () { + var def = defCropDialogSave; + $.when(this._super.apply(this, arguments)) + .then(def.resolve.bind(def)); + }, }); testUtils.mock.patch(AltDialog, { init: function () { @@ -3166,24 +3180,23 @@ QUnit.module('Media', { var _clickMedia = function (wysiwyg, assert, callbackInit, test) { wysiwyg.$('.note-insert .fa-file-image-o').mousedown().click(); - defMediaDialogSave.then(function () { + defMediaDialogInit.then(function () { + return callbackInit(); + }); + return $.when(defMediaDialogSave, defMediaDialogClose).then(function () { + var def = $.when(); if (test.check) { - test.check(); - } - if (test.content) { - assert.deepEqual(wysiwyg.getValue(), test.content, testName); + def = $.when(test.check()); } - if (test.start) { - var range = weTestUtils.select(test.start, test.end, $editable); - assert.deepEqual(Wysiwyg.getRange($editable[0]), range, testName + carretTestSuffix); - } - }); - return defMediaDialogInit.then(function () { - callbackInit(); - var def = $.Deferred(); - setTimeout(function () { - def.resolve(); - }, 0); + def.then(function () { + if (test.content) { + assert.deepEqual(wysiwyg.getValue(), test.content, testName); + } + if (test.start) { + var range = weTestUtils.select(test.start, test.end, $editable); + assert.deepEqual(Wysiwyg.getRange($editable[0]), range, testName + carretTestSuffix); + } + }); return def; }); }; @@ -3494,6 +3507,7 @@ QUnit.test('Image crop', function (assert) { $zoomBtn.mousedown().click(); $('.modal-dialog .modal-footer .btn.btn-primary:contains("Save")').mousedown().click(); }); + return $.when(defCropDialogClose, defCropDialogSave); }, }, }, @@ -3519,6 +3533,7 @@ QUnit.test('Image crop', function (assert) { $('.o_crop_image_dialog .o_crop_options .btn:has(.fa-arrows-h)').mousedown().click(); $('.modal-dialog .modal-footer .btn.btn-primary:contains("Save")').mousedown().click(); }); + return $.when(defCropDialogClose, defCropDialogSave); }, }, }, @@ -3554,6 +3569,7 @@ QUnit.test('Image crop', function (assert) { $pointW.trigger('pointerup'); $('.modal-dialog .modal-footer .btn.btn-primary:contains("Save")').mousedown().click(); }); + return $.when(defCropDialogClose, defCropDialogSave); }, }, }, @@ -3659,7 +3675,7 @@ QUnit.test('Pictogram (fontawesome)', function (assert) { }, test: { check: function () { - _clickMedia(wysiwyg, assert, function () { + return _clickMedia(wysiwyg, assert, function () { _insertPictogram('fa-heart'); }, { content: '<p>\u200B<span class="fa fa-heart"></span>\u200B</p>', |
