summaryrefslogtreecommitdiff
path: root/addons/web_editor
diff options
context:
space:
mode:
authorqsm-odoo <qsm@odoo.com>2018-04-27 12:28:57 +0200
committerqsm-odoo <qsm@odoo.com>2018-04-27 12:28:57 +0200
commitce6f8632cc272ca616ebc6bdedfa6347f4592b25 (patch)
treea691acff358bab3a90e3ebd3ec68e28a41ee0a43 /addons/web_editor
parent409970c188adaf79bc45c7e29b3b2e111c2c7775 (diff)
[FIX] web_editor: update summernote copy-paste feature
The copy-paste feature of summernote was messy and led most of the time to breaking the template you were trying to edit. While it is difficult to solve as it mostly comes from the lib itself and that the lib is in a complicated state as Odoo had to overridde part of its internal code for its own purposes, this commit hopes to solve the problem by updating only the copy-paste feature to the latest summernote version. Of course, as summernote changed a lot meanwhile, it was not possible to just take the up-to-date summernote file. This commit refactores the up-to-date summernote clipboard code so that it fits the old summernote version Odoo is using. Note: the main problems occurred while using Windows/Mac and not Linux. opw-1837818
Diffstat (limited to 'addons/web_editor')
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js287
1 files changed, 36 insertions, 251 deletions
diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js b/addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js
index 955b49437a8..4cd9088da75 100644
--- a/addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js
+++ b/addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js
@@ -1,256 +1,41 @@
define([
- 'summernote/core/list',
- 'summernote/core/dom',
- 'summernote/core/key',
- 'summernote/core/agent',
- 'summernote/core/range'
+ 'summernote/core/list',
+ 'summernote/core/dom',
+ 'summernote/core/key',
+ 'summernote/core/agent',
+ 'summernote/core/range'
], function (list, dom, key, agent, range) {
- var Clipboard = function (handler) {
- var $paste;
-
- this.attach = function (layoutInfo) {
- // [workaround] getting image from clipboard
- // - IE11 and Firefox: CTRL+v hook
- // - Webkit: event.clipboardData
- if (agent.isMSIE && agent.browserVersion > 10) {
- $paste = $('<div />').attr('contenteditable', true).css({
- position : 'absolute',
- left : -100000,
- opacity : 0
- });
-
- layoutInfo.editable().on('keydown', function (e) {
- if (e.ctrlKey && e.keyCode === key.code.V) {
- handler.invoke('saveRange', layoutInfo.editable());
- $paste.focus();
-
- setTimeout(function () {
- pasteByHook(layoutInfo);
- }, 0);
- }
- });
-
- layoutInfo.editable().before($paste);
- } else {
- layoutInfo.editable().on('paste', pasteByEvent);
- }
- };
-
- var pasteByHook = function (layoutInfo) {
- var $editable = layoutInfo.editable();
- var node = $paste[0].firstChild;
-
- if (dom.isImg(node)) {
- var dataURI = node.src;
- var decodedData = atob(dataURI.split(',')[1]);
- var array = new Uint8Array(decodedData.length);
- for (var i = 0; i < decodedData.length; i++) {
- array[i] = decodedData.charCodeAt(i);
- }
-
- var blob = new Blob([array], { type : 'image/png' });
- blob.name = 'clipboard.png';
-
- handler.invoke('restoreRange', $editable);
- handler.invoke('focus', $editable);
- handler.insertImages(layoutInfo, [blob]);
- } else {
- var pasteContent = $('<div />').html($paste.html()).html();
- handler.invoke('restoreRange', $editable);
- handler.invoke('focus', $editable);
-
- if (pasteContent) {
- handler.invoke('pasteHTML', $editable, pasteContent);
- }
- }
-
- $paste.empty();
- };
-
- /**
- * paste by clipboard event
- *
- * @param {Event} event
- */
- var pasteByEvent = function (event) {
- var clipboardData = event.originalEvent.clipboardData;
- var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target);
- var $editable = layoutInfo.editable();
-
- if (["INPUT", "TEXTAREA"].indexOf(event.target.tagName) !== -1) {
- return;
- }
-
- if (clipboardData && clipboardData.items && clipboardData.items.length) {
- var item = list.head(clipboardData.items);
- if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {
- handler.insertImages(layoutInfo, [item.getAsFile()]);
- }
- handler.invoke('editor.afterCommand', $editable);
- }
-
- event.preventDefault();
-
- var html = clipboardData.getData("text/html");
- var $node = $('<div/>').html(html);
- // if copying source did not provide html, default to plain text
- if(!html) {
- $node.text(clipboardData.getData("text/plain")).html(function(_, html){
- return html.replace(/\r?\n/g,'<br>');
- });
- }
- pasteContent($node, layoutInfo, $editable);
- };
-
- /*
- remove undesirable tag
- filter classes and style attributes
- remove undesirable attributes
- */
- var filter_tag = function ($nodes, $editable) {
- return $nodes.each(function() {
- var $node = $(this);
-
- if ($node.attr('style')) {
- var style = _.filter(_.compact($node.attr('style').split(/\s*;\s*/)), function (style) {
- style = style.split(/\s*:\s*/);
- return /width|height|color|background-color|font-weight|text-align|font-style|text-decoration/i.test(style[0]) &&
- !(style[1] === 'initial' || style[1] === 'inherit' || $node.css(style[0]) === $editable.css(style[0]) ||
- (style[0] === 'background-color' && style[1] === 'rgb(255, 255, 255)') ||
- (style[0] === 'color' && style[1] === 'rgb(0, 0, 0)'));
- }).join(';');
- if (style.length) {
- $node.attr('style', style);
- } else {
- $node.removeAttr('style');
- }
- }
-
- if ($node.attr('class')) {
- var classes = _.filter($node.attr('class').split(/\s+/), function (style) {
- return /(^|\s)(fa|pull|text|bg)(\s|-|$)/.test(style);
- }).join(' ');
- if (classes.length) {
- $node.attr('class', classes);
- } else {
- $node.removeAttr('class');
- }
- }
- });
- };
-
- var pasteContent = function ($node, layoutInfo, $editable) {
- $node.find('meta, script, style').remove();
- filter_tag($node.find('*'), $editable).removeAttr('title', 'alt', 'id', 'contenteditable');
-
- /*
- remove unless span and unwant font
- */
- $node.find('span, font').filter(':not([class]):not([style])').each(function () {
- $(this).replaceWith($(this).contents());
- });
- $node.find('span + span').each(function () {
-
- if (dom.isText(this.previousSibling)) {
- if (dom.isVisibleText(this.previousSibling)) {
- return;
- } else { // keep space between 2 tags, but can merge the both tags
- $(this).prev().append(this.previousSibling);
- }
- }
- if ($(this).attr('class') === $(this).prev().attr('class') && $(this).attr('style') === $(this).prev().attr('style')) {
- $(this).prev().append($(this).contents());
- $(this).remove();
- }
- });
-
- // remove empty table row and td
- var $tdr;
- while(($tdr = $node.find('tr:empty, td:empty, th:empty, tbody:empty, t-head:empty, table:empty')) && $tdr.length) {
- $tdr.remove();
- }
-
- /*
- reset architecture HTML node and add <p> tag
- */
- var $arch = $('<div/>');
- var $last = $arch;
- $node.contents().each(function () {
- if (dom.isBR(this)) {
- $(this).remove();
- $last = $('<p/>');
- $arch.append($last);
- } else if (/h[0-9]+|li|table|p/i.test(this.tagName)) {
- $last = $('<p/>');
- $arch.append(this).append($last);
- } else if ($arch.is(':empty') && dom.isText(this)) {
- $last = $('<p/>').append(this);
- $arch.append($last);
- } else if (this.nodeType !== Node.COMMENT_NODE) {
- $last.append(this);
- }
- });
- $arch.find(':not([class]):not([style]):empty, p:empty').remove();
-
- /*
- history
- */
- $editable.data('NoteHistory').recordUndo($editable, "paste");
-
- /*
- remove selected content
- */
- var r = range.create();
- if (!r.isCollapsed()) {
- r = r.deleteContents();
- r.select();
- }
-
- // If only pasting a <p/> element in an unique <p/> element, only paste
- // the <p/> element text
- var $p = $arch.children('p');
- var onlyAP = ($p.length === 1 && $arch.children().length === 1);
- if (onlyAP) {
- var $p1 = $(r.sc).closest('p');
- var $p2 = $(r.ec).closest('p');
- if ($p1.length && $p2.length && $p1[0] === $p2[0]) {
- $arch.html($p.text());
- }
- }
-
- /*
- insert content
- */
- var $nodes = $();
- $editable.on('DOMNodeInserted', function (event) {
- $nodes = $nodes.add(event.originalEvent.target);
- });
- window.document.execCommand('insertHTML', false, $arch.html());
- $editable.off('DOMNodeInserted');
-
- /*
- clean insterted content
- */
- var $span = $nodes.filter('span');
- $span = $span.first().add($span.last());
- $span = $span.add($span.prev('span'));
- $span = $span.add($span.next('span'));
- filter_tag($span, $editable);
- $span.not('[span], [style]').each(function () {
- _.each(this.childNodes, function (node) {
- $(node.parentNode).after(node);
- });
- $(this).remove();
- });
- r = range.create();
- if (!dom.isText(r.ec)) {
- r = range.create(r.sc.childNodes[r.so], dom.nodeLength(r.sc.childNodes[r.so]));
- }
- r.clean().select();
-
- $editable.trigger('content_changed');
+ // ODOO override: use 0.8.10 version of this, adapted for the old summernote
+ // version odoo is using
+ var Clipboard = function (handler) {
+ /**
+ * paste by clipboard event
+ *
+ * @param {Event} event
+ */
+ var pasteByEvent = function (event) {
+ if (["INPUT", "TEXTAREA"].indexOf(event.target.tagName) !== -1) {
+ // ODOO override: from old summernote version
+ return;
+ }
+
+ var clipboardData = event.originalEvent.clipboardData;
+ var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target);
+ var $editable = layoutInfo.editable();
+
+ if (clipboardData && clipboardData.items && clipboardData.items.length) {
+ var item = list.head(clipboardData.items);
+ if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {
+ handler.insertImages(layoutInfo, [item.getAsFile()]);
+ }
+ handler.invoke('editor.afterCommand', $editable);
+ }
+ };
+
+ this.attach = function (layoutInfo) {
+ layoutInfo.editable().on('paste', pasteByEvent);
+ };
};
- };
- return Clipboard;
+ return Clipboard;
});