[FIX] web_editor: restore selection after paste and undo

Before this commit, after pasting a valid URL on a non-collapsed selection
followed by UNDO, the selection was not restored to the original range.
This was due to not recording the selection in the current history step
before creating the link.
After pasting text composed of a valid URL between text fragments on a
non-collapsed selection followed by UNDO, the selection was again not
properly restored. This time, because each text fragment insertion was
done via execCommand, which calls _computeHistorySelection and thus
overwrites the current step's selection.

This commit:
- refactors code for link creation into a single function;
- wraps the link creation at `_onPaste` in a 'insertHTML' command called
via `_applyCommand`, which records the selection properly in the history
step;
- replaces `execCommand` by `_applyCommand` for text fragments
insertion.

task-3099012

X-original-commit: 20bb1a2090c90f87a819fdc1c3bbb332cae3720e
Part-of: odoo/odoo#118204
This commit is contained in:
Rodolpho Lima
2023-04-12 12:26:07 +02:00
parent 794d1407e0
commit d49da8aae4
@@ -3244,12 +3244,16 @@ export class OdooEditor extends EventTarget {
if (lastWordMatch) {
const matches = getUrlsInfosInString(textSliced);
const match = matches[matches.length - 1];
this._createLinkWithUrlInTextNode(
selection.anchorNode,
match.url,
match.index,
match.length,
);
const cloneRange = selection.getRangeAt(0).cloneRange();
const range = this.document.createRange();
range.setStart(selection.anchorNode, match.index);
range.setEnd(selection.anchorNode, match.index + match.length);
const link = this._createLink(range.extractContents().textContent, match.url);
range.insertNode(link);
// Inserting an element into a range clears the selection in Safari
// Hence, use the cloned range to reselect it.
selection.removeAllRanges();
selection.addRange(cloneRange);
}
selection.collapseToEnd();
}
@@ -4237,33 +4241,18 @@ export class OdooEditor extends EventTarget {
}
/**
* Create a Link in the node text based on the given data
*
* @param {Node} textNode
* @param {String} label
* @param {String} url
* @param {int} index
* @param {int} length
*/
_createLinkWithUrlInTextNode(textNode, url, index, length) {
const selection = this.document.getSelection();
const cloneRange = selection.getRangeAt(0).cloneRange();
_createLink(label, url) {
const link = this.document.createElement('a');
link.setAttribute('href', url);
for (const [param, value] of Object.entries(this.options.defaultLinkAttributes)) {
link.setAttribute(param, `${value}`);
}
const range = this.document.createRange();
range.setStart(textNode, index);
range.setEnd(textNode, index + length);
link.appendChild(range.extractContents());
range.insertNode(link);
// Inserting an element into a range clears the selection in Safari
// Hence, use the cloned range to reselect it.
selection.removeAllRanges();
selection.addRange(cloneRange);
link.innerText = label;
return link;
}
/**
* Add images inside the editable at the current selection.
*
@@ -4313,10 +4302,8 @@ export class OdooEditor extends EventTarget {
this.execCommand('insert', this._prepareClipboardData(clipboardHtml));
} else {
const text = ev.clipboardData.getData('text/plain');
let splitAroundUrl = [text];
const linkAttributes = this.options.defaultLinkAttributes || {};
const selectionIsInsideALink = !!closestElement(sel.anchorNode, 'a');
let splitAroundUrl = [text];
// Avoid transforming dynamic placeholder pattern to url.
if(!text.match(/\${.*}/gi)) {
splitAroundUrl = text.split(URL_REGEX);
@@ -4340,20 +4327,7 @@ export class OdooEditor extends EventTarget {
fontawesome: 'fa-link',
callback: () => {
this.historyUndo();
const link = document.createElement('A');
link.setAttribute('href', url);
for (const attribute in linkAttributes) {
link.setAttribute(attribute, linkAttributes[attribute]);
}
link.innerText = splitAroundUrl[i];
const sel = this.document.getSelection();
if (!sel.isCollapsed) {
this.deleteRange(sel);
}
if (sel.rangeCount) {
sel.getRangeAt(0).insertNode(link);
sel.collapseToEnd();
}
this._applyRawCommand('insert', this._createLink(splitAroundUrl[i], url));
},
},
{
@@ -4447,26 +4421,13 @@ export class OdooEditor extends EventTarget {
...baseEmbedCommand,
]);
} else {
const link = document.createElement('A');
link.setAttribute('href', url);
for (const attribute in linkAttributes) {
link.setAttribute(attribute, linkAttributes[attribute]);
}
link.innerText = splitAroundUrl[i];
const sel = this.document.getSelection();
if (!sel.isCollapsed) {
this.deleteRange(sel);
}
if (sel.rangeCount) {
sel.getRangeAt(0).insertNode(link);
sel.collapseToEnd();
}
this._applyCommand('insert', this._createLink(splitAroundUrl[i], url));
}
} else if (splitAroundUrl[i] !== '') {
const textFragments = splitAroundUrl[i].split(/\r?\n/);
let textIndex = 1;
for (const textFragment of textFragments) {
this.execCommand('insert', textFragment);
this._applyCommand('insert', textFragment);
if (textIndex < textFragments.length) {
this._applyCommand('oShiftEnter');
}