diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js index eb2fe57f114..1285f26a4d8 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js @@ -135,9 +135,9 @@ function hasColor(element, mode) { } } return ( - (style[mode] && style[mode] !== 'inherit' && style[mode] !== parent.style[mode]) || + (style[mode] && style[mode] !== 'inherit' && (!parent || style[mode] !== parent.style[mode])) || (classRegex.test(element.className) && - getComputedStyle(element)[mode] !== getComputedStyle(parent)[mode]) + (!parent || getComputedStyle(element)[mode] !== getComputedStyle(parent)[mode])) ); } @@ -598,59 +598,69 @@ export const editorCommands = { .map(n => closestElement(n, "*[t-field],*[t-out],*[t-esc]")) .filter(Boolean)); - const fonts = selectedNodes.flatMap(node => { - let font = closestElement(node, 'font') || closestElement(node, 'span'); - const children = font && descendants(font); - if (font && (font.nodeName === 'FONT' || (font.nodeName === 'SPAN' && font.style[mode]))) { - // Partially selected : split it. - const selectedChildren = children.filter(child => selectedNodes.includes(child)); - if (selectedChildren.length) { - font = splitAroundUntil(selectedChildren, font); + function getFonts(selectedNodes) { + return selectedNodes.flatMap(node => { + let font = closestElement(node, 'font') || closestElement(node, 'span'); + const children = font && descendants(font); + if (font && (font.nodeName === 'FONT' || (font.nodeName === 'SPAN' && font.style[mode]))) { + // Partially selected : split it. + const selectedChildren = children.filter(child => selectedNodes.includes(child)); + if (selectedChildren.length) { + font = splitAroundUntil(selectedChildren, font); + } else { + font = []; + } + } else if ((node.nodeType === Node.TEXT_NODE && !isWhitespace(node)) + || (node.nodeName === 'BR' && isEmptyBlock(node.parentNode)) + || (node.nodeType === Node.ELEMENT_NODE && + ['inline', 'inline-block'].includes(getComputedStyle(node).display) && + !isWhitespace(node.textContent) && + !node.classList.contains('btn') && + !node.querySelector('font'))) { + // Node is a visible text or inline node without font nor a button: + // wrap it in a . + const previous = node.previousSibling; + const classRegex = mode === 'color' ? BG_CLASSES_REGEX : TEXT_CLASSES_REGEX; + if ( + previous && + previous.nodeName === 'FONT' && + !previous.style[mode === 'color' ? 'backgroundColor' : 'color'] && + !classRegex.test(previous.className) && + selectedNodes.includes(previous.firstChild) && + selectedNodes.includes(previous.lastChild) + ) { + // Directly follows a fully selected that isn't + // colored in the other mode: append to that. + font = previous; + } else { + // No found: insert a new one. + font = document.createElement('font'); + node.after(font); + } + if (node.textContent) { + font.appendChild(node); + } else { + fillEmpty(font); + } } else { - font = []; + font = []; // Ignore non-text or invisible text nodes. } - } else if ((node.nodeType === Node.TEXT_NODE && !isWhitespace(node)) - || (node.nodeName === 'BR' && isEmptyBlock(node.parentNode)) - || (node.nodeType === Node.ELEMENT_NODE && - ['inline', 'inline-block'].includes(getComputedStyle(node).display) && - !isWhitespace(node.textContent) && - !node.classList.contains('btn') && - !node.querySelector('font'))) { - // Node is a visible text or inline node without font nor a button: - // wrap it in a . - const previous = node.previousSibling; - const classRegex = mode === 'color' ? BG_CLASSES_REGEX : TEXT_CLASSES_REGEX; - if ( - previous && - previous.nodeName === 'FONT' && - !previous.style[mode === 'color' ? 'backgroundColor' : 'color'] && - !classRegex.test(previous.className) && - selectedNodes.includes(previous.firstChild) && - selectedNodes.includes(previous.lastChild) - ) { - // Directly follows a fully selected that isn't - // colored in the other mode: append to that. - font = previous; - } else { - // No found: insert a new one. - font = document.createElement('font'); - node.after(font); - } - if (node.textContent) { - font.appendChild(node); - } else { - fillEmpty(font); - } - } else { - font = []; // Ignore non-text or invisible text nodes. - } - return font; - }); + return font; + }); + } for (const fieldNode of selectedFieldNodes) { colorElement(fieldNode, color, mode); } + let fonts = getFonts(selectedNodes); + // Dirty fix as the previous call could have unconnected elements + // because of the `splitAroundUntil`. Another call should provide he + // correct list of fonts. + if (!fonts.every((font) => font.isConnected)) { + fonts = getFonts(selectedNodes); + } + // Color the selected s and remove uncolored fonts. const fontsSet = new Set(fonts); for (const font of fontsSet) {