Permalink
Browse files

[IMP] web_editor: lazy load the wysiwyg

Issue: wysiwyg asset slow down the loading of the website, error
inadvertently introduced: #29775

The assets are now loaded assynchroneously, when the editor is needed,
its assets will be loaded.
  • Loading branch information...
Gorash committed Jan 28, 2019
1 parent 50a41c9 commit ef9ca178dc9f147bd208692539c22a4215b5e77d
Showing with 849 additions and 724 deletions.
  1. +2 −2 addons/mass_mailing/static/src/js/mass_mailing_widget.js
  2. +1 −1 addons/mass_mailing/static/tests/mass_mailing_html_tests.js
  3. +5 −3 addons/mass_mailing/views/editor_field_html.xml
  4. +33 −4 addons/web/static/src/js/core/widget.js
  5. +0 −10 addons/web/static/src/js/fields/abstract_field.js
  6. +2 −1 addons/web_editor/static/src/js/backend/convert_inline.js
  7. +21 −6 addons/web_editor/static/src/js/backend/field_html.js
  8. +0 −1 addons/web_editor/static/src/js/common/ace.js
  9. +5 −0 addons/web_editor/static/src/js/wysiwyg/plugin/font.js
  10. +62 −0 addons/web_editor/static/src/js/wysiwyg/root.js
  11. +4 −36 addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
  12. +9 −41 addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js
  13. +9 −0 addons/web_editor/static/src/js/wysiwyg_snippets/wysiwyg_snippets.js
  14. +42 −0 addons/web_editor/static/src/xml/wysiwyg.xml
  15. +2 −3 addons/web_editor/static/tests/field_html_tests.js
  16. +1 −1 addons/web_editor/static/tests/test_utils.js
  17. +12 −8 addons/web_editor/static/tests/wysiwyg_tests.js
  18. +23 −3 addons/web_editor/views/editor.xml
  19. +1 −1 addons/web_unsplash/views/web_unsplash_templates.xml
  20. +1 −1 addons/website/static/src/js/backend/dashboard.js
  21. +8 −1 addons/website/static/src/js/editor/editor_menu.js
  22. +8 −1 addons/website/static/src/js/editor/editor_menu_translate.js
  23. +0 −2 addons/website/static/src/js/editor/wysiwyg_multizone.js
  24. +0 −1 addons/website/static/src/js/menu/content.js
  25. +2 −0 addons/website/static/src/js/menu/edit.js
  26. +1 −1 addons/website/static/src/js/tours/rte.js
  27. +578 −588 addons/website/static/src/scss/website.editor.ui.scss
  28. +16 −6 addons/website/views/website_templates.xml
  29. +0 −1 addons/website_forum/static/src/js/website_forum.editor.js
  30. +1 −1 addons/website_forum/static/src/js/website_forum.js
@@ -305,7 +305,7 @@ var MassMailingFieldHtml = FieldHtml.extend({
this.$content.focusIn();
}
}
this.wysiwyg.snippets.trigger('reload_snippet_dropzones');
this.wysiwyg.trigger('reload_snippet_dropzones');
},

//--------------------------------------------------------------------------
@@ -442,7 +442,7 @@ var MassMailingFieldHtml = FieldHtml.extend({
selectedTheme = themeParams;

// Notify form view
self.wysiwyg._onChange();
self.wysiwyg.getEditable().trigger('change');
$dropdown.find('.dropdown-menu').removeClass('show');
$dropdown.find('.dropdown-item.selected').removeClass('selected');
$dropdown.find('.dropdown-item:eq(' + themesParams.indexOf(selectedTheme) + ')').addClass('selected');
@@ -46,7 +46,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: ['.field_body {background-color: red;}']
@@ -7,15 +7,17 @@
</template>

<template id="assets_mail_themes_edition"> <!-- maybe to remove and convert into a field dumy with attr invisible if the template is not selected -->
<t t-call="web._assets_helpers"/>
<t t-call="web._assets_helpers">
<link rel="stylesheet" type="text/scss" href="/web_editor/static/src/scss/wysiwyg_variables.scss"/>
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/mass_mailing.ui.scss"/>
</t>
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/webclient.scss"/>
<link rel="stylesheet" type="text/scss" href="/web_editor/static/src/scss/wysiwyg_variables.scss"/>
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/mass_mailing.ui.scss"/>
</template>

<template id="iframe_css_assets_edit">
<t t-call-assets="web.assets_common" t-js="false"/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
<t t-call-assets="web_editor.assets_wysiwyg" t-js="false"/>
<t t-call-assets="mass_mailing.assets_mail_themes" t-js="false"/>
<t t-call-assets="mass_mailing.assets_mail_themes_edition" t-js="false"/>
</template>
@@ -84,6 +84,32 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
* @type {null|string[]}
*/
xmlDependencies: null,
/**
* List of paths to css files that need to be loaded before the widget can
* be rendered. This will not induce loading anything that has already been
* loaded.
*
* @type {null|string[]}
*/
cssLibs: null,
/**
* List of paths to js files that need to be loaded before the widget can
* be rendered. This will not induce loading anything that has already been
* loaded.
*
* @type {null|string[]}
*/
jsLibs: null,
/**
* List of xmlID that need to be loaded before the widget can be rendered.
* The content css (link file or style tag) and js (file or inline) of the
* assets are loaded.
* This will not induce loading anything that has already been
* loaded.
*
* @type {null|string[]}
*/
assetLibs: null,

/**
* Constructs the widget and sets its parent if a parent is given.
@@ -115,13 +141,16 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
* @returns {Deferred}
*/
willStart: function () {
var defs = [];
if (this.xmlDependencies) {
var defs = _.map(this.xmlDependencies, function (xmlPath) {
defs.push.apply(defs, _.map(this.xmlDependencies, function (xmlPath) {
return ajax.loadXML(xmlPath, core.qweb);
});
return $.when.apply($, defs);
}));
}
return $.when();
if (this.jsLibs || this.cssLibs || this.assetLibs) {
defs.push(ajax.loadLibs(this));
}
return $.when.apply($, defs);
},
/**
* Method called after rendering. Mostly used to bind actions, perform
@@ -35,8 +35,6 @@ var field_utils = require('web.field_utils');
var Widget = require('web.Widget');

var AbstractField = Widget.extend({
cssLibs: [],
jsLibs: [],
events: {
'keydown': '_onKeydown',
},
@@ -182,14 +180,6 @@ var AbstractField = Widget.extend({
this.resetOnAnyFieldChange = true;
}
},
/**
* Loads the libraries listed in this.jsLibs and this.cssLibs
*
* @override
*/
willStart: function () {
return $.when(ajax.loadLibs(this), this._super.apply(this, arguments));
},
/**
* When a field widget is appended to the DOM, its start method is called,
* and will automatically call render. Most widgets should not override this.
@@ -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;
@@ -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);
},
@@ -191,7 +191,6 @@ var ViewEditor = Widget.extend({
willStart: function () {
return $.when(
this._super.apply(this, arguments),
ajax.loadLibs(this),
this._loadResources()
);
},
@@ -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,
@@ -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.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;

});
@@ -19,7 +19,10 @@ var Wysiwyg = Widget.extend({
wysiwyg_blur: '_onWysiwygBlur',
},
defaultOptions: {
codeview: config.debug
codeview: config.debug,
recordInfo: {
context: {},
},
},

/**
@@ -693,41 +696,6 @@ var Wysiwyg = Widget.extend({
// Public helper
//--------------------------------------------------------------------------

/**
* 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}
Oops, something went wrong.

0 comments on commit ef9ca17

Please sign in to comment.