summaryrefslogtreecommitdiff
path: root/addons/web_editor/static/src
diff options
context:
space:
mode:
authorGorash <chm@odoo.com>2019-08-08 15:08:09 +0000
committerDavid Monjoie <dmo@odoo.com>2019-08-22 06:41:05 +0000
commit56d0c1ed97784a2d2e6608caf8feafc5fa611d68 (patch)
tree0ef4b5e9db4cbe39ca2d411792dd450ce5adf13c /addons/web_editor/static/src
parent1eb1c2b029c7216c70becbf4cd0d9e6535deea96 (diff)
[REF] web_editor, mass_mailing: new iframe mechanism
Since we reverted the saas-12.2 editor in favor of the 12.0 one, we faced a dilemna regarding how the editor iframe is handled in mass_mailing. The 12.0 version was using a special controller to get the website assets required for mass_mailing while the saas-12.2 version used a completely different mechanism that was, obviously, not handled by the 12.0 version. We did not want to reintroduce the old controllers who were considered to be a hack to load the assets in the mass_mailing iframe. However, we were very cautious about changing the editor itself to avoid introducing new bugs in the editor core of 13.0. The approach we chose at the end, is to use the loadAsset mechanism introduced in saas-12.2 to load the necessary assets and inject them in the mass_mailing iframe. This did not reintroduce the controllers nor did it require to change the code of the editor itself. However, it required moderate changes to the wysiwyg interface. Part of PR 35677. Co-authored-by: Nicolas Bayet <nby@odoo.com> Co-authored-by: Antoine Guenet <age@odoo.com> Co-authored-by: Christophe Matthieu <chm@odoo.com> Co-authored-by: David Monjoie <dmo@odoo.com>
Diffstat (limited to 'addons/web_editor/static/src')
-rw-r--r--addons/web_editor/static/src/js/backend/convert_inline.js (renamed from addons/web_editor/static/src/js/editor/transcoder.js)109
-rw-r--r--addons/web_editor/static/src/js/backend/field_html.js3
-rw-r--r--addons/web_editor/static/src/js/editor/editor.js31
-rw-r--r--addons/web_editor/static/src/js/editor/snippets.editor.js3
-rw-r--r--addons/web_editor/static/src/js/wysiwyg/root.js2
-rw-r--r--addons/web_editor/static/src/js/wysiwyg/wysiwyg.js77
-rw-r--r--addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js136
-rw-r--r--addons/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js33
-rw-r--r--addons/web_editor/static/src/scss/wysiwyg.scss74
-rw-r--r--addons/web_editor/static/src/scss/wysiwyg_iframe.scss10
-rw-r--r--addons/web_editor/static/src/scss/wysiwyg_snippets.scss15
-rw-r--r--addons/web_editor/static/src/xml/wysiwyg.xml68
12 files changed, 446 insertions, 115 deletions
diff --git a/addons/web_editor/static/src/js/editor/transcoder.js b/addons/web_editor/static/src/js/backend/convert_inline.js
index 745e8492b11..4482a8f67ba 100644
--- a/addons/web_editor/static/src/js/editor/transcoder.js
+++ b/addons/web_editor/static/src/js/backend/convert_inline.js
@@ -1,9 +1,7 @@
-odoo.define('web_editor.transcoder', function (require) {
+odoo.define('web_editor.convertInline', function (require) {
'use strict';
-var base = require('web_editor.base');
-
-var rulesCache = [];
+var FieldHtml = require('web_editor.field.html');
/**
* Returns the css rules which applies on an element, tweaked so that they are
@@ -14,8 +12,11 @@ var rulesCache = [];
*/
function getMatchedCSSRules(a) {
var i, r, k;
+ var doc = a.ownerDocument;
+ var rulesCache = a.ownerDocument._rulesCache || (a.ownerDocument._rulesCache = []);
+
if (!rulesCache.length) {
- var sheets = document.styleSheets;
+ var sheets = doc.styleSheets;
for (i = sheets.length-1 ; i >= 0 ; i--) {
var rules;
// try...catch because browser may not able to enumerate rules for cross-domain sheets
@@ -187,11 +188,13 @@ 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;
- _.find(base.fontIcons, function (font) {
- return _.find(base.getCssSelectors(font.parser), function (data) {
+ _.find(fonts.fontIcons, function (font) {
+ return _.find(fonts.getCssSelectors(font.parser), function (data) {
if ($font.is(data.selector.replace(/::?before/g, ''))) {
icon = data.names[0].split('-').shift();
content = data.css.match(/content:\s*['"]?(.)['"]?/)[1];
@@ -265,9 +268,6 @@ function applyOverDescendants(node, func) {
* @param {jQuery} $editable
*/
function classToStyle($editable) {
- if (!rulesCache.length) {
- getMatchedCSSRules($editable[0]);
- }
applyOverDescendants($editable[0], function (node) {
var $target = $(node);
var css = getMatchedCSSRules(node);
@@ -328,9 +328,7 @@ function styleToClass($editable) {
$(this).after($('a,img', this));
}).remove();
- getMatchedCSSRules($editable[0]);
-
- var $c = $('<span/>').appendTo(document.body);
+ var $c = $('<span/>').appendTo($editable[0].ownerDocument.body);
applyOverDescendants($editable[0], function (node) {
var $target = $(node);
@@ -342,7 +340,7 @@ function styleToClass($editable) {
}
});
css = ($c.attr('style', style).attr('style') || '').split(/\s*;\s*/);
- style = $target.attr('style') || '';
+ style = ($target.attr('style') || '').replace(/\s*:\s*/, ':').replace(/\s*;\s*/, ';');
_.each(css, function (v) {
style = style.replace(v, '');
});
@@ -364,13 +362,13 @@ function styleToClass($editable) {
* @param {jQuery} $editable
*/
function attachmentThumbnailToLinkImg($editable) {
- $editable.find('a[href*="/web/content/"][data-mimetype]:empty').each(function () {
+ $editable.find('a[href*="/web/content/"][data-mimetype]').filter(':empty, :containsExact( )').each(function () {
var $link = $(this);
var $img = $('<img/>')
.attr('src', $link.css('background-image').replace(/(^url\(['"])|(['"]\)$)/g, ''))
.css('height', Math.max(1, $link.height()) + 'px')
.css('width', Math.max(1, $link.width()) + 'px');
- $link.append($img);
+ $link.prepend($img);
});
}
@@ -384,6 +382,83 @@ function linkImgToAttachmentThumbnail($editable) {
$editable.find('a[href*="/web/content/"][data-mimetype] > img').remove();
}
+
+//--------------------------------------------------------------------------
+//--------------------------------------------------------------------------
+
+
+FieldHtml.include({
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ commitChanges: function () {
+ if (this.nodeOptions['style-inline'] && this.mode === "edit") {
+ this._toInline();
+ }
+ return this._super();
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Converts CSS dependencies to CSS-independent HTML.
+ * - CSS display for attachment link -> real image
+ * - Font icons -> images
+ * - CSS styles -> inline styles
+ *
+ * @private
+ */
+ _toInline: function () {
+ var $editable = this.wysiwyg.getEditable();
+ var html = this.wysiwyg.getValue();
+ $editable.html(html);
+
+ attachmentThumbnailToLinkImg($editable);
+ fontToImg($editable);
+ classToStyle($editable);
+ this.wysiwyg.setValue($editable.html(), {
+ notifyChange: false,
+ });
+ },
+ /**
+ * Revert _toInline changes.
+ *
+ * @private
+ */
+ _fromInline: function () {
+ var $editable = this.wysiwyg.getEditable();
+ var html = this.wysiwyg.getValue();
+ $editable.html(html);
+
+ styleToClass($editable);
+ imgToFont($editable);
+ linkImgToAttachmentThumbnail($editable);
+ this.wysiwyg.setValue($editable.html(), {
+ notifyChange: false,
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Handler
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _onLoadWysiwyg: function () {
+ if (this.nodeOptions['style-inline'] && this.mode === "edit") {
+ this._fromInline();
+ }
+ this._super();
+ },
+});
+
return {
fontToImg: fontToImg,
imgToFont: imgToFont,
@@ -392,4 +467,4 @@ return {
attachmentThumbnailToLinkImg: attachmentThumbnailToLinkImg,
linkImgToAttachmentThumbnail: linkImgToAttachmentThumbnail,
};
-});
+}); \ No newline at end of file
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 f6d86c9020c..0be20743132 100644
--- a/addons/web_editor/static/src/js/backend/field_html.js
+++ b/addons/web_editor/static/src/js/backend/field_html.js
@@ -179,8 +179,6 @@ var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, {
* @returns {Object}
*/
_getWysiwygOptions: function () {
- var iPhone = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
-
return Object.assign({}, this.nodeOptions, {
recordInfo: {
context: this.record.getContext(this.recordParams),
@@ -193,6 +191,7 @@ var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, {
snippets: this.nodeOptions.snippets,
tabsize: 0,
+ height: 180,
generateOptions: function (options) {
var para = _.find(options.toolbar, function (item) {
return item[0] === 'para';
diff --git a/addons/web_editor/static/src/js/editor/editor.js b/addons/web_editor/static/src/js/editor/editor.js
index dc0f53c8e57..07947db56e4 100644
--- a/addons/web_editor/static/src/js/editor/editor.js
+++ b/addons/web_editor/static/src/js/editor/editor.js
@@ -6,6 +6,7 @@ var Widget = require('web.Widget');
var core = require('web.core');
var rte = require('web_editor.rte');
var snippetsEditor = require('web_editor.snippet.editor');
+var summernoteCustomColors = require('web_editor.rte.summernote_custom_colors');
var _t = core._t;
@@ -26,7 +27,7 @@ var EditorMenuBar = Widget.extend({
*
* @constructor
*/
- init: function (parent) {
+ init: function (parent, options) {
var self = this;
var res = this._super.apply(this, arguments);
var Editor = options.Editor || rte.Class;
@@ -48,8 +49,6 @@ var EditorMenuBar = Widget.extend({
var $editable = this.rte.editable();
window.__EditorMenuBar_$editable = $editable; // TODO remove this hack asap
-
- var options = this.getParent().params;
if (options.snippets) {
this.snippetsMenu = new snippetsEditor.Class(this, Object.assign({
$el: $editable,
@@ -186,6 +185,32 @@ var EditorMenuBar = Widget.extend({
//--------------------------------------------------------------------------
/**
+ * @private
+ */
+ _getDefaultConfig: function ($editable) {
+ return {
+ 'airMode' : true,
+ 'focus': false,
+ 'airPopover': [
+ ['style', ['style']],
+ ['font', ['bold', 'italic', 'underline', 'clear']],
+ ['fontsize', ['fontsize']],
+ ['color', ['color']],
+ ['para', ['ul', 'ol', 'paragraph']],
+ ['table', ['table']],
+ ['insert', ['link', 'picture']],
+ ['history', ['undo', 'redo']],
+ ],
+ 'styleWithSpan': false,
+ 'inlinemedia' : ['p'],
+ 'lang': 'odoo',
+ 'onChange': function (html, $editable) {
+ $editable.trigger('content_changed');
+ },
+ 'colors': summernoteCustomColors,
+ };
+ },
+ /**
* Reloads the page in non-editable mode, with the right scrolling.
*
* @private
diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index 2f2e55a0084..4fba95f2490 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -67,7 +67,7 @@ var SnippetEditor = Widget.extend({
defs.push(this._initializeOptions());
// Initialize move/clone/remove buttons
- if (!this.options.isEditableNode(this.$target[0])) {
+ if (!this.$target.is(':o_editable')) {
this.$el.find('.oe_snippet_move, .oe_snippet_clone, .oe_snippet_remove').remove();
} else {
this.dropped = false;
@@ -1249,6 +1249,7 @@ var SnippetsMenu = Widget.extend({
this.$el.addClass('o_loaded');
$('body.editor_enable').addClass('editor_has_snippets');
+ this.trigger_up('snippets_loaded', self.$el);
},
/**
* Creates a snippet editor to associated to the given snippet. If the given
diff --git a/addons/web_editor/static/src/js/wysiwyg/root.js b/addons/web_editor/static/src/js/wysiwyg/root.js
index ab531d086f7..c2b3d703733 100644
--- a/addons/web_editor/static/src/js/wysiwyg/root.js
+++ b/addons/web_editor/static/src/js/wysiwyg/root.js
@@ -47,7 +47,7 @@ var WysiwygRoot = Widget.extend({
});
return instance.attachTo($target).then(function () {
- self.$editor = instance.$el;
+ self.$editor = instance.$editor || instance.$el;
});
});
},
diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
index cb0db917e54..d33f5455da4 100644
--- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
+++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
@@ -2,7 +2,6 @@ odoo.define('web_editor.wysiwyg', function (require) {
'use strict';
var Widget = require('web.Widget');
var SummernoteManager = require('web_editor.rte.summernote');
-var transcoder = require('web_editor.transcoder');
var summernoteCustomColors = require('web_editor.rte.summernote_custom_colors');
var id = 0;
@@ -32,28 +31,26 @@ var Wysiwyg = Widget.extend({
},
},
/**
- * @params {Object} params
- * @params {Object} params.recordInfo
- * @params {Object} params.recordInfo.context
- * @params {String} [params.recordInfo.context]
- * @params {integer} [params.recordInfo.res_id]
- * @params {String} [params.recordInfo.data_res_model]
- * @params {integer} [params.recordInfo.data_res_id]
+ * @options {Object} options
+ * @options {Object} options.recordInfo
+ * @options {Object} options.recordInfo.context
+ * @options {String} [options.recordInfo.context]
+ * @options {integer} [options.recordInfo.res_id]
+ * @options {String} [options.recordInfo.data_res_model]
+ * @options {integer} [options.recordInfo.data_res_id]
* @see _onGetRecordInfo
* @see _getAttachmentsDomain in /wysiwyg/widgets/media.js
- * @params {Object} params.attachments
+ * @options {Object} options.attachments
* @see _onGetRecordInfo
* @see _getAttachmentsDomain in /wysiwyg/widgets/media.js (for attachmentIDs)
- * @params {function} params.generateOptions
+ * @options {function} options.generateOptions
* called with the summernote configuration object used before sending to summernote
* @see _editorOptions
**/
- init: function (parent, params) {
+ init: function (parent, options) {
this._super.apply(this, arguments);
- this.params = params;
- this.params.isEditableNode = function (node) {
- return $(node).is(':o_editable');
- };
+ this.id = ++id;
+ this.options = options;
},
/**
* Load assets and color picker template then call summernote API
@@ -64,14 +61,13 @@ var Wysiwyg = Widget.extend({
willStart: function () {
new SummernoteManager(this);
this.$target = this.$el;
- return this._super.apply(this, arguments);
+ return this._super();
},
/**
*
* @override
*/
start: function () {
- var self = this;
this.$target.wrap('<odoo-wysiwyg-container>');
this.$el = this.$target.parent();
var options = this._editorOptions();
@@ -80,7 +76,6 @@ var Wysiwyg = Widget.extend({
this.$editor.data('wysiwyg', this);
this.$editor.data('oe-model', options.recordInfo.res_model);
this.$editor.data('oe-id', options.recordInfo.res_id);
- var $wysiwyg = this.$editor.closest('odoo-wysiwyg-container');
$(document).on('mousedown', this._blur);
this._value = this.$target.html() || this.$target.val();
return this._super.apply(this, arguments).then(() => {
@@ -106,7 +101,7 @@ var Wysiwyg = Widget.extend({
* @returns {jQuery}
*/
getEditable: function () {
- console.log('getEditable');
+ return this.$editor;
},
/**
* Return true if the content has changed.
@@ -117,27 +112,6 @@ var Wysiwyg = Widget.extend({
return this._value !== (this.$editor.html() || this.$editor.val());
},
/**
- * Return true if the current node is unbreakable.
- * An unbreakable node can be removed or added but can't by split into
- * different nodes (for keypress and selection).
- * An unbreakable node can contain nodes that can be edited.
- *
- * @param {Node} node
- * @returns {Boolean}
- */
- isUnbreakableNode: function (node) {
- console.log('isUnbreakableNode');
- },
- /**
- * Return true if the current node is editable (for keypress and selection).
- *
- * @param {Node} node
- * @returns {Boolean}
- */
- isEditableNode: function (node) {
- console.log('isEditableNode');
- },
- /**
* Set the focus on the element.
*/
focus: function () {
@@ -193,12 +167,6 @@ var Wysiwyg = Widget.extend({
this.$target.html(value);
}
this.$editor.html(value);
- if (this.params['style-inline']) {
- transcoder.styleToClass(this.$editor);
- transcoder.imgToFont(this.$editor);
- transcoder.linkImgToAttachmentThumbnail(this.$editor);
- }
- this._value = (this.$target.html() || this.$target.val());
},
//--------------------------------------------------------------------------
// Private
@@ -246,8 +214,21 @@ Wysiwyg.getRange = function (node) {
eo: range.eo,
};
};
-Wysiwyg.setRange = function () {
- console.log('setRange');
+/**
+ * @param {Node} startNode
+ * @param {Number} startOffset
+ * @param {Node} endNode
+ * @param {Number} endOffset
+ */
+Wysiwyg.setRange = function (startNode, startOffset, endNode, endOffset) {
+ $(startNode).focus();
+ if (endNode) {
+ $.summernote.core.range.create(startNode, startOffset, endNode, endOffset).select();
+ } else {
+ $.summernote.core.range.create(startNode, startOffset).select();
+ }
+ // trigger for Unbreakable
+ $(startNode.tagName ? startNode : startNode.parentNode).trigger('wysiwyg.range');
};
/**
* @param {Node} node - dom node
diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js
new file mode 100644
index 00000000000..64b7c6a8f8c
--- /dev/null
+++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js
@@ -0,0 +1,136 @@
+odoo.define('web_editor.wysiwyg.iframe', function (require) {
+'use strict';
+
+var Wysiwyg = require('web_editor.wysiwyg');
+var ajax = require('web.ajax');
+var core = require('web.core');
+
+var qweb = core.qweb;
+var promiseCommon;
+var promiseWysiwyg;
+
+
+/**
+ * Add option (inIframe) to load Wysiwyg in an iframe.
+ **/
+Wysiwyg.include({
+ /**
+ * Add options to load Wysiwyg in an iframe.
+ *
+ * @override
+ * @param {boolean} options.inIframe
+ **/
+ init: function (parent, options) {
+ this._super.apply(this, arguments);
+ if (this.options.inIframe) {
+ if (!this.options.iframeCssAssets) {
+ this.options.iframeCssAssets = 'web_editor.wysiwyg_iframe_css_assets';
+ }
+ this._onUpdateIframeId = 'onLoad_' + this.id;
+ }
+ },
+ /**
+ * Load assets to inject into iframe.
+ *
+ * @override
+ **/
+ willStart: function () {
+ if (!this.options.inIframe) {
+ return this._super();
+ }
+ var debug = odoo.debug;
+ odoo.debug = false;
+
+ var defAsset;
+ if (this.options.iframeCssAssets) {
+ defAsset = ajax.loadAsset(this.options.iframeCssAssets);
+ } else {
+ defAsset = Promise.resolve({
+ cssLibs: [],
+ cssContents: []
+ });
+ }
+
+ promiseWysiwyg = promiseWysiwyg || ajax.loadAsset('web_editor.wysiwyg_iframe_editor_assets');
+ odoo.debug = debug;
+
+ this.defAsset = Promise.all([promiseWysiwyg, defAsset]);
+
+ this.$target = this.$el;
+ return this.defAsset
+ .then(this._loadIframe.bind(this))
+ .then(this._super.bind(this));
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Create iframe, inject css and create a link with the content,
+ * then inject the target inside.
+ *
+ * @private
+ * @returns {Promise}
+ */
+ _loadIframe: function () {
+ var self = this;
+ this.$iframe = $('<iframe class="wysiwyg_iframe">').css({
+ 'min-height': '400px',
+ width: '100%'
+ });
+ var avoidDoubleLoad = 0; // this bug only appears on some configurations.
+
+ // resolve promise on load
+ var def = new Promise(function (resolve) {
+ window.top[self._onUpdateIframeId] = function (Editor, _avoidDoubleLoad) {
+ if (_avoidDoubleLoad !== avoidDoubleLoad) {
+ console.warn('Wysiwyg iframe double load detected');
+ return;
+ }
+ delete window.top[self._onUpdateIframeId];
+ var $iframeTarget = self.$iframe.contents().find('#iframe_target');
+ $iframeTarget.find('.o_editable').html(self.$target.val());
+ self.options.toolbarHandler = $('#web_editor-top-edit', self.$iframe[0].contentWindow.document);
+ $(qweb.render('web_editor.FieldTextHtml.fullscreen'))
+ .appendTo(self.options.toolbarHandler)
+ .on('click', '.o_fullscreen', function () {
+ $("body").toggleClass("o_field_widgetTextHtml_fullscreen");
+ var full = $("body").hasClass("o_field_widgetTextHtml_fullscreen");
+ self.$iframe.parents().toggleClass('o_form_fullscreen_ancestor', full);
+ $(window).trigger("resize"); // induce a resize() call and let other backend elements know (the navbar extra items management relies on this)
+ });
+ self.Editor = Editor;
+ resolve();
+ };
+ });
+ this.$iframe.data('loadDef', def); // for unit test
+
+ // inject content in iframe
+
+ this.$iframe.on('load', function onLoad (ev) {
+ var _avoidDoubleLoad = ++avoidDoubleLoad;
+ self.defAsset.then(function (assets) {
+ if (_avoidDoubleLoad !== avoidDoubleLoad) {
+ console.warn('Wysiwyg immediate iframe double load detected');
+ return;
+ }
+
+ var iframeContent = qweb.render('wysiwyg.iframeContent', {
+ assets: assets,
+ updateIframeId: self._onUpdateIframeId,
+ avoidDoubleLoad: _avoidDoubleLoad
+ });
+ self.$iframe[0].contentWindow.document
+ .open("text/html", "replace")
+ .write(iframeContent);
+ });
+ });
+
+ this.$iframe.insertAfter(this.$target);
+
+ return def;
+ },
+});
+
+}); \ No newline at end of file
diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js
new file mode 100644
index 00000000000..4b095029432
--- /dev/null
+++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js
@@ -0,0 +1,33 @@
+odoo.define('web_editor.wysiwyg.snippets', function (require) {
+'use strict';
+var editor = require('web_editor.editor');
+var Wysiwyg = require('web_editor.wysiwyg');
+
+
+Wysiwyg.include({
+ init: function (parent, options) {
+ this._super.apply(this, arguments);
+ this.Editor = editor.Class;
+ if (!this.options.toolbarHandler) {
+ this.options.toolbarHandler = $('#web_editor-top-edit');
+ }
+ },
+ start: async function () {
+ if (this.options.snippets) {
+ var self = this;
+ this.editor = new (this.Editor)(this, this.options);
+ this.$editor = this.editor.rte.editable();
+ await this.editor.prependTo(this.$editor[0].ownerDocument.body);
+ this.options.toolbarHandler.append(this.editor.$el);
+ this.$el.on('content_changed', function (e) {
+ var $editable = $(e.target).closest('[data-oe-model], .o_editable');
+ $editable.addClass('o_dirty');
+ self.trigger_up('wysiwyg_change');
+ });
+ } else {
+ return this._super();
+ }
+ },
+});
+
+});
diff --git a/addons/web_editor/static/src/scss/wysiwyg.scss b/addons/web_editor/static/src/scss/wysiwyg.scss
index 58fe5f7ec42..cd8fa7bcf44 100644
--- a/addons/web_editor/static/src/scss/wysiwyg.scss
+++ b/addons/web_editor/static/src/scss/wysiwyg.scss
@@ -1,5 +1,37 @@
// NOTE EDITOR
+body.editor_enable {
+ padding-top: $o-wysiwyg-toolbar-height !important;
+}
+
+#wrapwrap {
+ height: calc(100vh - #{$o-wysiwyg-toolbar-height});
+ border: 1px solid $o-wysiwyg-border-color;
+ border-left: 0;
+ border-top: 0;
+
+ .btn {
+ -webkit-user-select: none;
+ }
+}
+
+// TOP BAR (EDIT)
+#web_editor-top-edit {
+ height: $o-wysiwyg-toolbar-height;
+ background-color: $o-wysiwyg-bg-color;
+ z-index: $o-wysiwyg-zindex - 2;
+ position: fixed;
+ top: 0px;
+ right: 0px;
+ left: 0px;
+ white-space: nowrap;
+ border: 1px solid $o-wysiwyg-border-color;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.125);
+
+ transition: background-color 400ms $o-we-md-ease 0s;
+ font-family: $o-we-font-family;
+}
+
.note-editor.note-frame {
background-color: inherit;
@@ -15,25 +47,36 @@
}
}
-.note-popover.popover {
- z-index: $o-wysiwyg-zindex;
- height: auto;
+.note-editor:not(.note-frame) .note-editing-area {
+ overflow: visible;
+}
+
+#web_editor-top-edit .note-popover .popover {
+ height: $o-wysiwyg-toolbar-height - 2;
+ top: 0 !important;
+ left: 0 !important;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ width: auto;
+ text-align: center;
+ box-shadow: none;
background-color: $o-wysiwyg-bg-color;
+ z-index: $o-wysiwyg-zindex;
- .popover-content {
- padding-bottom: 0;
- max-width: 334px;
+ .popover-body {
+ height: $o-wysiwyg-toolbar-height - 2;
- >note-btn-group {
- white-space: nowrap;
+ > .btn-group {
+ height: $o-wysiwyg-toolbar-height - 2;
+ margin-top: 0;
}
- }
-}
-.note-editor .note-toolbar {
- white-space: nowrap;
- z-index: $o-wysiwyg-zindex + 10;
- height: $o-wysiwyg-toolbar-height;
+ .btn {
+ height: $o-wysiwyg-toolbar-height - 2;
+ border-top: 0;
+ }
+ }
}
#web_editor-toolbars .popover,
@@ -57,12 +100,10 @@
}
.btn {
- height: $o-wysiwyg-toolbar-height;
border: none;
border-radius: 0;
background-color: inherit;
color: inherit;
- border-top: 2px solid transparent;
transition: all 0.3s ease 0s;
>.fa {
@@ -84,7 +125,6 @@
}
&.active {
- border-top: 2px solid white;
background-color: inherit;
color: inherit;
box-shadow: inset 0 3px 10px rgba(0, 0, 0, 0.5);
diff --git a/addons/web_editor/static/src/scss/wysiwyg_iframe.scss b/addons/web_editor/static/src/scss/wysiwyg_iframe.scss
index 1124d903353..e1af34e41fa 100644
--- a/addons/web_editor/static/src/scss/wysiwyg_iframe.scss
+++ b/addons/web_editor/static/src/scss/wysiwyg_iframe.scss
@@ -16,7 +16,7 @@ iframe.wysiwyg_iframe.o_fullscreen {
body.o_in_iframe {
background-color: $o-view-background-color;
- overflow: hidden;
+ overflow: auto;
.note-editable {
height: calc(100vh - #{$o-wysiwyg-toolbar-height} - 2px);
@@ -25,4 +25,12 @@ body.o_in_iframe {
.note-statusbar {
display: none;
}
+
+ #web_editor-toolbars .popover .btn {
+ font-size: 1rem;
+ }
+
+ .o_mail_theme_selector .btn {
+ font-size: 1rem;
+ }
}
diff --git a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss
index b693fd38544..6b576acecba 100644
--- a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss
+++ b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss
@@ -13,6 +13,14 @@ $o-wysiwyg-handle-border-width: 1px !default;
$o-wysiwyg-handles-offset-to-hide: 10000px !default;
+// OVERRIDES FOR EDITOR WITH SNIPPETS
+body.editor_enable.editor_has_snippets {
+ padding-left: $o-we-sidebar-width !important;
+
+ #web_editor-top-edit .note-popover .popover {
+ margin: 0 0 0 $o-we-sidebar-width;
+ }
+}
// NOTE EDITOR
.note-editor.o_snippets_loaded {
@@ -38,8 +46,8 @@ $o-wysiwyg-handles-offset-to-hide: 10000px !default;
display: flex;
flex-flow: column nowrap;
- @include o-position-absolute(0, auto, 0, -$o-wysiwyg-sidebar-width);
- position: absolute;
+ position: fixed;
+ top: 0;
width: $o-wysiwyg-sidebar-width;
z-index: $o-wysiwyg-zindex - 1;
@@ -52,7 +60,7 @@ $o-wysiwyg-handles-offset-to-hide: 10000px !default;
#snippets_menu {
flex: 0 0 auto;
- height: $o-wysiwyg-sidebar-height;
+ height: $o-wysiwyg-toolbar-height;
line-height: $o-wysiwyg-sidebar-height;
margin: 0;
padding: 0;
@@ -341,6 +349,7 @@ $o-wysiwyg-handles-offset-to-hide: 10000px !default;
// MANIPULATORS
#oe_manipulators {
position: relative;
+ z-index: 1;
// SNIPPET MANIPULATORS
.oe_overlay {
diff --git a/addons/web_editor/static/src/xml/wysiwyg.xml b/addons/web_editor/static/src/xml/wysiwyg.xml
index b5372892fe5..b9b29a8984e 100644
--- a/addons/web_editor/static/src/xml/wysiwyg.xml
+++ b/addons/web_editor/static/src/xml/wysiwyg.xml
@@ -9,32 +9,56 @@
<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 t-foreach="assets || []" t-as="asset">
+ <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.jsLibs || []" t-as="jsLib">
+ <script type="text/javascript" t-att-src="jsLib"/>
+ </t>
+ <t t-foreach="asset.jsContents || []" t-as="jsContent" t-if="jsContent.indexOf('inline asset') !== -1">
+ <script type="text/javascript" t-raw="jsContent"/>
+ </t>
</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;
+ odoo.define('web.session', function () {
+ return window.top.odoo.__DEBUG__.services['web.session'];
+ });
+
+ odoo.define('root.widget', function (require) {
+ 'use strict';
+ var Widget = require('web.Widget');
+ var widget = new Widget();
+ widget.appendTo(document.body);
+ return widget;
+ });
+
+ odoo.define('web.core.top', function (require) {
+ var core = require('web.core');
+ core.qweb.templates = window.top.odoo.__DEBUG__.services['web.core'].qweb.templates;
+ });
</script>
- <script type="text/javascript" src="/web_editor/static/lib/summernote/summernote.js"/>
+ </head>
+ <body id="iframe_target" class="o_in_iframe">
+ <div id="web_editor-top-edit"></div>
+ <div id="wrapwrap">
+ <div data-oe-model="model" data-oe-type="html" class="o_editable oe_structure"></div>
+ </div>
<script type="text/javascript">
- window._summernoteSlave = $.summernote;
- window._summernoteSlave.iframe = true;
- window._summernoteSlave.lang = window.top._summernoteMaster.lang;
- window._fnSummenoteSlave = $.fn.summernote;
- $.summernote = window.top._summernoteMaster;
- $.fn.summernote = window.top._fnSummernoteMaster;
- if (window.top.<t t-esc="updateIframeId"/>) {
- window.top.<t t-esc="updateIframeId"/>(<t t-esc="avoidDoubleLoad"/>);
- }
+ odoo.define('web_editor.wysiwyg.iniframe', function (require) {
+ 'use strict';
+ var editor = require('web_editor.editor');
+
+ window._summernoteSlave = $.summernote;
+ window._summernoteSlave.iframe = true;
+ window._summernoteSlave.lang = window.top.$.summernote.lang;
+ if (window.top.<t t-esc="updateIframeId"/>) {
+ window.top.<t t-esc="updateIframeId"/>(editor.Class, <t t-esc="avoidDoubleLoad"/>);
+ }
+ });
</script>
</body>
</t>