import { DOMUtils, ContenteditableExtension } from 'mailspring-exports'; export default class TemplateEditor extends ContenteditableExtension { static onContentChanged = ({ editor }) => { // Run through and remove all code nodes that are invalid const codeNodes = editor.rootNode.querySelectorAll('code.var.empty'); for (let ii = 0; ii < codeNodes.length; ii++) { const codeNode = codeNodes[ii]; // remove any style that was added by contenteditable codeNode.removeAttribute('style'); // grab the text content and the indexable text content const codeNodeText = codeNode.textContent; const indexText = DOMUtils.getIndexedTextContent(codeNode) .map(({ text }) => text) .join(''); // unwrap any code nodes that don't start/end with {{}}, and any with line breaks inside if ( !codeNodeText.startsWith('{{') || !codeNodeText.endsWith('}}') || indexText.indexOf('\n') > -1 ) { editor.whilePreservingSelection(() => { DOMUtils.unwrapNode(codeNode); }); } } // Attempt to sanitize extra nodes that may have been created by contenteditable on certain text editing // operations (insertion/deletion of line breaks, etc.). These are generally , but can also be // , , and possibly others. The extra nodes often grab CSS styles from neighboring elements // as inline style, including the yellow text from nodes that we insert. This is contenteditable // trying to be "smart" and preserve styles, which is very undesirable for the node styles. The // below code is a hack to prevent yellow text from appearing. const starNodes = editor.rootNode.querySelectorAll('*'); for (let ii = 0; ii < starNodes.length; ii++) { const node = starNodes[ii]; if (!node.className && node.style.color === '#c79b11') { editor.whilePreservingSelection(() => { DOMUtils.unwrapNode(node); }); } } const fontNodes = editor.rootNode.querySelectorAll('font'); for (let ii = 0; ii < fontNodes.length; ii++) { const node = fontNodes[ii]; if (node.color === '#c79b11') { editor.whilePreservingSelection(() => { DOMUtils.unwrapNode(node); }); } } // Find all {{}} and wrap them in code nodes if they aren't already // Regex finds any {{ }} that doesn't contain {, }, or \n // https://regex101.com/r/jF2oF4/1 for (const range of editor.regExpSelectorAll(/\{\{[^\n{}]*?\}\}/g)) { if (!DOMUtils.isWrapped(range, 'CODE')) { // Preserve the selection based on text index within the range matched by the regex const selIndex = editor.getSelectionTextIndex(range); const codeNode = DOMUtils.wrap(range, 'CODE'); codeNode.className = 'var empty'; // Sets node contents to just its textContent, strips HTML codeNode.textContent = codeNode.textContent; if (selIndex != null) { editor.restoreSelectionByTextIndex(codeNode, selIndex.startIndex, selIndex.endIndex); } } } }; }