diff --git a/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js b/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js index 8934a046a4c..ce447e3110e 100644 --- a/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js +++ b/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js @@ -30,6 +30,7 @@ import { splitElement, splitTextNode, startPos, + nodeSize, } from '../utils/utils.js'; const TEXT_CLASSES_REGEX = /\btext-[^\s]*\b/g; diff --git a/addons/web_editor/static/lib/odoo-editor/src/commands/deleteForward.js b/addons/web_editor/static/lib/odoo-editor/src/commands/deleteForward.js index 50f9206b084..e05d8aebb00 100644 --- a/addons/web_editor/static/lib/odoo-editor/src/commands/deleteForward.js +++ b/addons/web_editor/static/lib/odoo-editor/src/commands/deleteForward.js @@ -11,8 +11,9 @@ import { CTYPES, leftPos, isFontAwesome, - rightLeafOnlyNotBlockPath, - rightLeafOnlyPath, + rightLeafOnlyNotBlockNotEditablePath, + rightLeafOnlyPathNotBlockNotEditablePath, + isNotEditableNode, } from '../utils/utils.js'; Text.prototype.oDeleteForward = function (offset) { @@ -24,23 +25,27 @@ Text.prototype.oDeleteForward = function (offset) { }; HTMLElement.prototype.oDeleteForward = function (offset) { - const filterFunc = node => isVisibleEmpty(node) || isContentTextNode(node); + const filterFunc = node => + isVisibleEmpty(node) || isContentTextNode(node) || isNotEditableNode(node); - const firstInlineNode = findNode(rightLeafOnlyNotBlockPath(this, offset), filterFunc); - if (isFontAwesome(firstInlineNode && firstInlineNode.parentElement)) { - firstInlineNode.parentElement.remove(); + const firstLeafNode = findNode(rightLeafOnlyNotBlockNotEditablePath(this, offset), filterFunc); + + if (firstLeafNode && (isFontAwesome(firstLeafNode) || isNotEditableNode(firstLeafNode))) { + firstLeafNode.remove(); return; } if ( - firstInlineNode && - (firstInlineNode.nodeName !== 'BR' || - getState(...rightPos(firstInlineNode), DIRECTIONS.RIGHT).cType !== CTYPES.BLOCK_INSIDE) + firstLeafNode && + (firstLeafNode.nodeName !== 'BR' || + getState(...rightPos(firstLeafNode), DIRECTIONS.RIGHT).cType !== CTYPES.BLOCK_INSIDE) ) { - firstInlineNode.oDeleteBackward(Math.min(1, nodeSize(firstInlineNode))); + firstLeafNode.oDeleteBackward(Math.min(1, nodeSize(firstLeafNode))); return; } const firstOutNode = findNode( - rightLeafOnlyPath(...(firstInlineNode ? rightPos(firstInlineNode) : [this, offset])), + rightLeafOnlyPathNotBlockNotEditablePath( + ...(firstLeafNode ? rightPos(firstLeafNode) : [this, offset]), + ), filterFunc, ); if (firstOutNode) { diff --git a/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js b/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js index 48ce436d0a8..68ffd2f8535 100644 --- a/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js +++ b/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js @@ -133,11 +133,11 @@ const PATH_END_REASONS = { * * @see leftLeafFirstPath * @see leftLeafOnlyNotBlockPath - * @see leftLeafOnlyInScopeNotBlockPath - * @see rightLeafOnlyNotBlockPath - * @see rightLeafOnlyPath - * @see rightLeafOnlyNotBlockInScopePath + * @see leftLeafOnlyInScopeNotBlockNoEditablePath * @see rightLeafOnlyNotBlockPath + * @see rightLeafOnlyPathNotBlockNotEditablePath + * @see rightLeafOnlyInScopeNotBlockPath + * @see rightLeafOnlyNotBlockNotEditablePath * * @param {number} direction * @param {boolean} [options.leafOnly] if true, do not yield any non-leaf node @@ -456,7 +456,8 @@ export function getNormalizedCursorPosition(node, offset, full = true) { } } if (el) { - const leftInlineNode = leftLeafOnlyInScopeNotBlockPath(el, elOffset).next().value; + const leftInlineNode = leftLeafOnlyInScopeNotBlockNoEditablePath(el, elOffset).next() + .value; let leftVisibleEmpty = false; if (leftInlineNode) { leftVisibleEmpty = @@ -467,7 +468,7 @@ export function getNormalizedCursorPosition(node, offset, full = true) { : endPos(leftInlineNode); } if (!leftInlineNode || leftVisibleEmpty) { - const rightInlineNode = rightLeafOnlyNotBlockInScopePath(el, elOffset).next().value; + const rightInlineNode = rightLeafOnlyInScopeNotBlockPath(el, elOffset).next().value; if (rightInlineNode) { const rightVisibleEmpty = isVisibleEmpty(rightInlineNode) || @@ -1889,17 +1890,23 @@ export function getRangePosition(el, document, options = {}) { return offset; } + +export const isNotEditableNode = node => + node.getAttribute && + node.getAttribute('contenteditable') && + node.getAttribute('contenteditable').toLowerCase() === 'false'; + export const leftLeafFirstPath = createDOMPathGenerator(DIRECTIONS.LEFT); export const leftLeafOnlyNotBlockPath = createDOMPathGenerator(DIRECTIONS.LEFT, { leafOnly: true, stopTraverseFunction: isBlock, stopFunction: isBlock, }); -export const leftLeafOnlyInScopeNotBlockPath = createDOMPathGenerator(DIRECTIONS.LEFT, { +export const leftLeafOnlyInScopeNotBlockNoEditablePath = createDOMPathGenerator(DIRECTIONS.LEFT, { leafOnly: true, inScope: true, - stopTraverseFunction: isBlock, - stopFunction: isBlock, + stopTraverseFunction: node => isNotEditableNode(node) || isBlock(node), + stopFunction: node => isNotEditableNode(node) || isBlock(node), }); export const rightLeafOnlyNotBlockPath = createDOMPathGenerator(DIRECTIONS.RIGHT, { @@ -1908,12 +1915,17 @@ export const rightLeafOnlyNotBlockPath = createDOMPathGenerator(DIRECTIONS.RIGHT stopFunction: isBlock, }); -export const rightLeafOnlyPath = createDOMPathGenerator(DIRECTIONS.RIGHT, { +export const rightLeafOnlyPathNotBlockNotEditablePath = createDOMPathGenerator(DIRECTIONS.RIGHT, { leafOnly: true, }); -export const rightLeafOnlyNotBlockInScopePath = createDOMPathGenerator(DIRECTIONS.RIGHT, { +export const rightLeafOnlyInScopeNotBlockPath = createDOMPathGenerator(DIRECTIONS.RIGHT, { leafOnly: true, inScope: true, stopTraverseFunction: isBlock, stopFunction: isBlock, }); +export const rightLeafOnlyNotBlockNotEditablePath = createDOMPathGenerator(DIRECTIONS.RIGHT, { + leafOnly: true, + stopTraverseFunction: node => isNotEditableNode(node) || isBlock(node), + stopFunction: node => isBlock(node) && !isNotEditableNode(node), +}); diff --git a/addons/web_editor/static/lib/odoo-editor/test/spec/editor.test.js b/addons/web_editor/static/lib/odoo-editor/test/spec/editor.test.js index 58f3b0dace2..657d98a10f9 100644 --- a/addons/web_editor/static/lib/odoo-editor/test/spec/editor.test.js +++ b/addons/web_editor/static/lib/odoo-editor/test/spec/editor.test.js @@ -157,18 +157,18 @@ X[] }); it('should remove contenteditable="false"', async () => { await testEditor(BasicEditor, { - contentBefore: `
mollis.[]Pelentesque
`, }); }); + it('should remove contenteditable="false"', async () => { + await testEditor(BasicEditor, { + contentBefore: `