UNPKG

@syncfusion/ej2-richtexteditor

Version:
685 lines (684 loc) 31.7 kB
import { closest, createElement, detach, isNullOrUndefined as isNOU, removeClass } from '@syncfusion/ej2-base'; import * as EVENTS from '../../common/constant'; import { SelectionCommands } from '../plugin'; var FormatPainterActions = /** @class */ (function () { function FormatPainterActions(parent, options) { this.INVALID_TAGS = ['A', 'AUDIO', 'IMG', 'VIDEO', 'IFRAME']; this.parent = parent; this.settings = options; this.addEventListener(); this.setDeniedFormats(); } FormatPainterActions.prototype.addEventListener = function () { this.parent.observer.on(EVENTS.FORMAT_PAINTER_ACTIONS, this.actionHandler, this); this.parent.observer.on(EVENTS.MODEL_CHANGED_PLUGIN, this.onPropertyChanged, this); this.parent.observer.on(EVENTS.INTERNAL_DESTROY, this.destroy, this); }; FormatPainterActions.prototype.onPropertyChanged = function (prop) { if (prop && prop.module === 'formatPainter') { if (!isNOU(prop.newProp.formatPainterSettings.allowedFormats)) { this.settings.allowedFormats = prop.newProp.formatPainterSettings.allowedFormats; } if (!isNOU(prop.newProp.formatPainterSettings.deniedFormats)) { this.settings.deniedFormats = prop.newProp.formatPainterSettings.deniedFormats; this.setDeniedFormats(); } } }; FormatPainterActions.prototype.removeEventListener = function () { this.parent.observer.off(EVENTS.FORMAT_PAINTER_ACTIONS, this.actionHandler); this.parent.observer.off(EVENTS.MODEL_CHANGED_PLUGIN, this.onPropertyChanged); this.parent.observer.off(EVENTS.INTERNAL_DESTROY, this.destroy); }; /** * Destroys the format painter. * * @function destroy * @returns {void} * @hidden * @private */ FormatPainterActions.prototype.destroy = function () { this.removeEventListener(); this.INVALID_TAGS = null; this.copyCollection = null; this.deniedFormatsCollection = null; this.newElem = null; this.newElemLastChild = null; this.settings = null; this.parent = null; }; FormatPainterActions.prototype.actionHandler = function (args) { if (!isNOU(args) && !isNOU(args.item) && !isNOU(args.item.formatPainterAction)) { switch (args.item.formatPainterAction) { case 'format-copy': this.copyAction(); break; case 'format-paste': this.pasteAction(); break; case 'escape': this.escapeAction(); break; } this.callBack(args); } }; FormatPainterActions.prototype.callBack = function (event) { if (event.callBack) { event.callBack({ requestType: 'FormatPainter', action: event.item.formatPainterAction, event: event.event, editorMode: 'HTML', range: this.parent.nodeSelection.getRange(this.parent.currentDocument), elements: this.parent.nodeSelection.getSelectedNodes(this.parent.currentDocument) }); } }; FormatPainterActions.prototype.generateElement = function () { var copyCollection = this.copyCollection.slice(); // To clone without reversing the collcection array copyCollection.reverse(); var length = copyCollection.length; var elemCollection = createElement('div', { className: 'e-format-paste-wrapper' }); var lastAppendChild; for (var i = 0; i < length; i++) { var _a = copyCollection[i], attrs = _a.attrs, className = _a.className, styles = _a.styles, tagName = _a.tagName; var elem = createElement(tagName); if (className !== '') { elem.className = className; } for (var i_1 = 0; i_1 < attrs.length; i_1++) { var property = attrs[i_1].name; var value = attrs[i_1].value; elem.setAttribute(property, value); } for (var i_2 = 0; i_2 < styles.length; i_2++) { var property = styles[i_2].property; var value = styles[i_2].value; var priority = styles[i_2].priority; elem.style.setProperty(property, value, priority); } if (elemCollection.childElementCount === 0) { elemCollection.append(elem); lastAppendChild = elem; } else { lastAppendChild.append(elem); lastAppendChild = elem; } } var elemChild = this.removeDeniedFormats(elemCollection); var currentElem = elemChild; while (currentElem) { if (currentElem.firstChild === null) { lastAppendChild = currentElem; currentElem = undefined; } else { currentElem = currentElem.firstChild; } } this.newElem = elemChild; this.newElemLastChild = lastAppendChild; }; FormatPainterActions.prototype.pasteAction = function () { if (isNOU(this.copyCollection) || this.copyCollection.length === 0) { this.paintPlainTextFormat(); return; } this.insertFormatNode(this.newElem, this.newElemLastChild); }; FormatPainterActions.prototype.removeDeniedFormats = function (parentElement) { var deniedPropArray = this.deniedFormatsCollection; if (!isNOU(deniedPropArray) && deniedPropArray.length > 0) { var length_1 = deniedPropArray.length; for (var i = 0; i < length_1; i++) { var currentRule = deniedPropArray[i]; var tag = currentRule.tag; if (!tag) { continue; } var elementsList = parentElement.querySelectorAll(tag); for (var j = 0; j < elementsList.length; j++) { var currentElement = elementsList[j]; var classes = currentRule.classes; var styles = currentRule.styles; var attributes = currentRule.attributes; // Step 1: Check class condition var classConditionMet = true; if (classes.length > 0 && classes[j].trim() !== '') { classConditionMet = true; for (var k = 0; k < classes.length; k++) { if (currentElement.classList.contains(classes[k].trim())) { classConditionMet = true; } else { classConditionMet = false; } } } // Step 2: Check if styles or attributes are targeted var hasExplicitTargets = (styles.length > 0 && styles[j].trim() !== '') || (attributes.length > 0 && attributes[j].trim() !== ''); // Step 3: Apply denial logic if (classConditionMet) { // Case A: Remove styles if (hasExplicitTargets) { for (var k = 0; k < styles.length; k++) { var styleName = styles[k].trim(); if (styleName) { currentElement.style.removeProperty(styleName); } } for (var k = 0; k < attributes.length; k++) { var attrName = attributes[k].trim(); if (attrName) { currentElement.removeAttribute(attrName); } } } // Case B: Remove classes if (classes.length > 0) { for (var k = 0; k < classes.length; k++) { var className = classes[k].trim(); if (className) { removeClass([currentElement], className); } } } // Final cleanup if (currentElement.style.length === 0) { currentElement.removeAttribute('style'); } if (currentElement.classList.length === 0) { currentElement.removeAttribute('class'); } } } } } return parentElement.firstElementChild; }; FormatPainterActions.prototype.copyAction = function () { var copyCollection = []; var range = this.parent.nodeSelection.getRange(this.parent.currentDocument); var domSelection = this.parent.nodeSelection; var nodes = range.collapsed ? domSelection.getSelectionNodeCollection(range) : domSelection.getSelectionNodeCollectionBr(range); if (nodes.length === 0 && domSelection.getSelectionNodeCollectionBr(range).length === 0) { return; } else { nodes = nodes.length === 0 ? domSelection.getSelectionNodeCollectionBr(range) : nodes; } var parentElem = nodes[0].parentElement; var currentContext = this.findCurrentContext(parentElem); var allowedRulesArray = this.settings.allowedFormats.indexOf(';') > -1 ? this.settings.allowedFormats.split(';') : [this.settings.allowedFormats]; for (var i = 0; i < allowedRulesArray.length; i++) { allowedRulesArray[i] = allowedRulesArray[i].trim(); } var _a = this.getRangeParentElem(currentContext, parentElem), rangeParentElem = _a[0], context = _a[1]; if (currentContext === null) { currentContext = context; } if (!isNOU(currentContext)) { if (range.startContainer.nodeName === '#text') { parentElem = range.startContainer.parentElement; } var lastElement = parentElem; do { if (allowedRulesArray.indexOf(parentElem.nodeName.toLowerCase()) > -1) { var allAttributes = parentElem.attributes; var attribute = []; for (var i = 0; i < allAttributes.length; i++) { if (allAttributes[i].name !== 'class' && allAttributes[i].name !== 'style') { attribute.push(allAttributes[i]); } } var classes = parentElem.className; var allStyles = parentElem.style; var styleProp = []; for (var i = 0; i < allStyles.length; i++) { var property = allStyles[i]; var value = allStyles.getPropertyValue(property); var priority = allStyles.getPropertyPriority(property); styleProp.push({ property: property, value: value, priority: priority }); } copyCollection.push({ attrs: attribute, className: classes, styles: styleProp, tagName: parentElem.nodeName }); } if (rangeParentElem === parentElem) { parentElem = undefined; } else if (!isNOU(parentElem.parentElement)) { parentElem = parentElem.parentElement; } if (lastElement === parentElem) { break; } } while (!isNOU(parentElem) || parentElem === this.parent.editableElement); this.copyCollection = copyCollection; } this.generateElement(); }; FormatPainterActions.prototype.getRangeParentElem = function (currentContext, rangeParent) { var startContainer = rangeParent; var rangeParentELem; if (startContainer.nodeType === 3) { startContainer = startContainer.parentElement; } switch (currentContext) { case 'Table': rangeParentELem = closest(startContainer, 'td'); if (isNOU(rangeParentELem)) { rangeParentELem = closest(startContainer, 'th'); } break; case 'List': rangeParentELem = closest(startContainer, 'li'); break; case 'Text': rangeParentELem = closest(startContainer, 'p'); break; } if (isNOU(rangeParentELem)) { var nearBlockParentName = this.getNearestBlockParentElement(rangeParent); if (!isNOU(nearBlockParentName) && nearBlockParentName !== 'UL' && nearBlockParentName !== 'OL' && nearBlockParentName !== 'LI') { rangeParentELem = closest(startContainer, nearBlockParentName); currentContext = 'Text'; } } if (currentContext === 'List') { rangeParentELem = rangeParentELem.parentElement; } return [rangeParentELem, currentContext]; }; FormatPainterActions.prototype.getNearestBlockParentElement = function (rangeParent) { var node = rangeParent; if (node.nodeType === 3) { node = node.parentNode; } // iterate untill the content editable div while (node && node !== this.parent.editableElement) { // If true return the block node name. if (!isNOU(node) && this.isBlockElement(node)) { return node.nodeName; } // if false re assign node to parent node node = node.parentNode; } return null; }; FormatPainterActions.prototype.isBlockElement = function (node) { var blockTags = ['P', 'DIV', 'UL', 'OL', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'ADDRESS', 'ARTICLE', 'ASIDE', 'BLOCKQUOTE', 'FIGCAPTION', 'FIGURE', 'FOOTER', 'HEADER', 'HR', 'MAIN', 'NAV', 'SECTION', 'SUMMARY', 'PRE']; return blockTags.indexOf(node.nodeName) > -1; }; FormatPainterActions.prototype.escapeAction = function () { this.copyCollection = []; }; FormatPainterActions.prototype.paintPlainTextFormat = function () { var range = this.parent.nodeSelection.getRange(this.parent.currentDocument); var domSelection = this.parent.nodeSelection; var nodes = range.collapsed ? domSelection.getSelectionNodeCollection(range) : domSelection.getSelectionNodeCollectionBr(range); var isInValid; if (nodes.length > 1) { for (var i = 0; i < nodes.length; i++) { isInValid = this.validateELementTag(nodes[i]); } } else { isInValid = this.validateELementTag(range.startContainer) && this.validateELementTag(range.endContainer); } if (!isInValid) { this.parent.execCommand('Clear', 'ClearFormat', null, null); } }; FormatPainterActions.prototype.validateELementTag = function (node) { if (node.nodeType === 3) { node = node.parentElement; } return this.INVALID_TAGS.indexOf(node.tagName) > -1; }; FormatPainterActions.prototype.findCurrentContext = function (parentElem) { var closestParagraph = closest(parentElem, 'p'); var closestList = closest(parentElem, 'li'); if (closestParagraph && !closestList) { return 'Text'; } else if (closest(parentElem, 'li')) { if (!isNOU(closestParagraph) && !isNOU(closestList) && closestParagraph.textContent.trim() !== closestList.textContent.trim()) { return 'Text'; } return 'List'; } else if (closest(parentElem, 'td') || closest(parentElem, 'tr') || closest(parentElem, 'th')) { return 'Table'; } return null; }; FormatPainterActions.prototype.insertFormatNode = function (elem, lastChild) { var clonedElem = elem.cloneNode(true); if (!this.isBlockElement(elem)) { var newBlockElem = createElement('P'); newBlockElem.appendChild(elem); clonedElem = newBlockElem.cloneNode(true); } var endNode = this.parent.editableElement; var docElement = this.parent.currentDocument; var childElem = clonedElem.firstChild; var inlineElement; while (childElem) { if (this.isBlockElement(childElem)) { childElem = childElem.firstChild; } else { inlineElement = childElem.parentNode.removeChild(childElem); break; } } var formatValues = { element: inlineElement, lastChild: lastChild }; SelectionCommands.applyFormat(docElement, null, endNode, 'P', null, 'formatPainter', null, formatValues); var range = this.parent.nodeSelection.getRange(docElement); var isCollapsed = range.collapsed; var blockNodes = this.parent.domNode.blockNodes(); var isListCopied = this.isListCopied(); if (isListCopied) { for (var i = 0; i < blockNodes.length; i++) { if (closest(blockNodes[i], 'li')) { blockNodes[i] = closest(blockNodes[i], 'li'); } } } var isFullNodeSelected = false; if (blockNodes.length === 1) { isFullNodeSelected = blockNodes[0].textContent.trim() === range.toString().trim(); } if (this.isBlockElement(clonedElem) && isCollapsed || blockNodes.length > 1 || isFullNodeSelected) { this.insertBlockNode(clonedElem, range, docElement, blockNodes); } }; FormatPainterActions.prototype.isListCopied = function () { var isListCopied = false; for (var i = 0; i < this.copyCollection.length; i++) { if (this.copyCollection[i].tagName === 'OL' || this.copyCollection[i].tagName === 'UL') { isListCopied = true; break; } } return isListCopied; }; FormatPainterActions.prototype.insertBlockNode = function (element, range, docElement, nodes) { var domSelection = this.parent.nodeSelection; var saveSelection = domSelection.save(range, docElement); this.parent.domNode.setMarker(saveSelection); var listElement; // To clone to multiple list elements var cloneListParentNode; var sameListType = false; if (element.nodeName === 'UL' || element.nodeName === 'OL') { cloneListParentNode = element.cloneNode(true); listElement = cloneListParentNode.firstChild; } var cloneElementNode = isNOU(cloneListParentNode) ? element : element.firstChild; for (var index = 0; index < nodes.length; index++) { if (this.INVALID_TAGS.indexOf(nodes[index].nodeName) > -1 || nodes[index].querySelectorAll('img,audio,video,iframe').length > 0) { continue; } var cloneParentNode = cloneElementNode.cloneNode(false); // Appending all the child elements while (nodes[index].firstChild) { if (nodes[index].textContent.trim().length !== 0) { cloneParentNode.appendChild(nodes[index].firstChild); } else { nodes[index].removeChild(nodes[index].firstChild); } } if (nodes[index].nodeName === 'TD' || nodes[index].nodeName === 'TH') { if (isNOU(cloneListParentNode)) { nodes[index].appendChild(cloneParentNode); continue; } else if (index === 0 && !isNOU(cloneListParentNode)) { nodes[index].appendChild(cloneListParentNode); cloneListParentNode.appendChild(cloneParentNode); continue; } else { nodes[index].appendChild(cloneParentNode); continue; } } if (!isNOU(cloneListParentNode)) { sameListType = this.isSameListType(element, nodes[index]); } if (cloneParentNode.nodeName === 'LI' && !sameListType) { this.insertNewList(range, nodes, index, cloneListParentNode, cloneParentNode); } else if (sameListType) { this.insertSameList(nodes, index, cloneListParentNode, cloneParentNode); } else { nodes[index].parentNode.replaceChild(cloneParentNode, nodes[index]); } /**Removing the inserted block node in list and appending to previous element sibling */ if (cloneParentNode.nodeName !== 'LI' && (cloneParentNode.parentElement.nodeName === 'OL' || cloneParentNode.parentElement.nodeName === 'UL')) { var parent_1 = cloneParentNode.parentElement; // Cutting single ul or ol to two ul or ol based on the range this.parent.nodeCutter.SplitNode(range, parent_1, true); if (!isNOU(parent_1.previousElementSibling)) { parent_1.previousElementSibling.after(cloneParentNode); // To remove the nested list items out of the block element if (cloneParentNode.childNodes.length > 1) { for (var j = 0; j < cloneParentNode.childNodes.length; j++) { var currentChild = cloneParentNode.childNodes[j]; if (currentChild.nodeName === 'OL' || currentChild.nodeName === 'UL') { cloneParentNode.after(currentChild); } } } } else { parent_1.parentElement.prepend(cloneParentNode); } } } // eslint-disable-next-line @typescript-eslint/no-unused-expressions !isNOU(listElement) ? detach(listElement) : false; this.cleanEmptyLists(); var save = this.parent.domNode.saveMarker(saveSelection); save.restore(); }; FormatPainterActions.prototype.insertNewList = function (range, nodes, index, cloneListParentNode, cloneParentNode) { // Appending the li nodes to the ol or ul node if (index === 0) { var nodeName = nodes[index].nodeName; nodes[index] = nodes[index].parentNode.replaceChild(cloneListParentNode, nodes[index]); var parent_2 = nodeName === 'LI' ? cloneListParentNode.parentElement : cloneListParentNode; // Splicing and then inserting the node to previous element sibling of the Listparent.parent this.parent.nodeCutter.SplitNode(range, parent_2, true); if (nodes[index].nodeName === 'LI' && !isNOU(parent_2)) { cloneListParentNode.append(cloneParentNode); if (!isNOU(parent_2.parentNode)) { parent_2.parentNode.insertBefore(cloneListParentNode, parent_2); } } else { if (!isNOU(parent_2)) { if (!isNOU(parent_2.previousElementSibling) && parent_2.previousElementSibling.nodeName === cloneListParentNode.nodeName) { var currentParent = parent_2.previousElementSibling; currentParent.append(cloneParentNode); while (currentParent.firstChild) { cloneListParentNode.append(currentParent.firstChild); } } else if (!isNOU(parent_2.nextElementSibling) && parent_2.nextElementSibling.nodeName === cloneListParentNode.nodeName) { var currentParent = parent_2.nextElementSibling; currentParent.prepend(cloneParentNode); while (currentParent.firstChild) { cloneListParentNode.append(currentParent.firstChild); } } else { cloneListParentNode.append(cloneParentNode); } } else { cloneListParentNode.append(cloneParentNode); } } } else { cloneListParentNode.append(cloneParentNode); } this.detachEmptyBlockNodes(nodes[index]); }; FormatPainterActions.prototype.insertSameList = function (nodes, index, cloneListParentNode, cloneParentNode) { if (index === 0) { if (!isNOU(nodes[index].parentNode) && (nodes[index].parentNode.nodeName === 'UL' || nodes[index].parentNode.nodeName === 'OL')) { // append the nodes[index].parentNode.childNodes to the clonelistparentnode if (nodes.length === 1) { // When clicked with cursor in the single list item while (cloneParentNode.firstChild) { nodes[index].append(cloneParentNode.firstChild); } for (var i = 0; i < nodes[index].parentNode.childNodes.length; i++) { var currentChild = nodes[index].parentNode.childNodes[i]; cloneListParentNode.append(currentChild.cloneNode(true)); } } else { cloneListParentNode.append(cloneParentNode); } var childNodesTextContent = void 0; if (nodes[index].parentNode) { childNodesTextContent = Array.from(nodes[index].parentNode.childNodes).map(function (node) { return node.textContent.trim(); }).join(''); } var nodesTextContent = Array.from(nodes).map(function (node) { return node.textContent.trim(); }).join(''); if (childNodesTextContent === nodesTextContent || (nodes[index].parentNode && nodes[index].parentNode.textContent === cloneListParentNode.textContent)) { // replace the older ol and ul with new ol and ul of clonelistparentnode nodes[index].parentNode.parentNode.replaceChild(cloneListParentNode, nodes[index].parentNode); } else { nodes[index].parentNode.replaceChild(cloneParentNode, nodes[index]); } } } else { if (cloneListParentNode.parentNode) { cloneListParentNode.append(cloneParentNode); } else { nodes[index].parentNode.replaceChild(cloneParentNode, nodes[index]); } } this.detachEmptyBlockNodes(nodes[index]); }; FormatPainterActions.prototype.isSameListType = function (element, node) { var isSameListType = false; var nearestListNode = closest(node, 'ol, ul'); if (!isNOU(nearestListNode) && nearestListNode.querySelectorAll('li').length > 0) { if (nearestListNode.nodeName === element.nodeName) { isSameListType = true; } else { isSameListType = false; } } return isSameListType; }; FormatPainterActions.prototype.cleanEmptyLists = function () { var listElem = this.parent.editableElement.querySelectorAll('ol, ul'); for (var i = 0; i < listElem.length; i++) { if (listElem[i].textContent.trim() === '') { detach(listElem[i]); } } }; FormatPainterActions.prototype.setDeniedFormats = function () { var deniedFormatsCollection = []; if (isNOU(this.settings) || isNOU(this.settings.deniedFormats)) { return; } var deniedFormats = this.settings.deniedFormats.indexOf(';') > -1 ? this.settings.deniedFormats.split(';') : [this.settings.deniedFormats]; var length = deniedFormats.length; for (var i = 0; i < length; i++) { var formatString = deniedFormats[i]; if (formatString !== '') { formatString.trim(); var collection = this.makeDeniedFormatsCollection(formatString); if (!isNOU(collection)) { deniedFormatsCollection.push(collection); } } } this.deniedFormatsCollection = deniedFormatsCollection; }; FormatPainterActions.prototype.detachEmptyBlockNodes = function (node) { if (!isNOU(node) && node.textContent.trim() === '') { detach(node); } }; FormatPainterActions.prototype.makeDeniedFormatsCollection = function (value) { var openParenIndex = value.indexOf('('); var closeParenIndex = value.indexOf(')'); var openBracketIndex = value.indexOf('['); var closeBracketIndex = value.indexOf(']'); var openBraceIndex = value.indexOf('{'); var closeBraceIndex = value.indexOf('}'); var classes = []; var attributes = ''; var styles = ''; var tagName = ''; var classList = []; var attributesList = []; var stylesList = []; if (openParenIndex > -1 && closeParenIndex > -1) { classes = value.substring(openParenIndex + 1, closeParenIndex).split(' '); classList = classes[0].split(')')[0].split(','); } if (openBracketIndex > -1 && closeBracketIndex > -1) { attributes = value.substring(openBracketIndex + 1, closeBracketIndex); attributesList = attributes.split(','); } if (openBraceIndex > -1 && closeBraceIndex > -1) { styles = value.substring(openBraceIndex + 1, closeBraceIndex); stylesList = styles.split(','); } var openIndexArray = [openParenIndex, openBracketIndex, openBraceIndex]; openIndexArray = openIndexArray.filter(function (index) { return index > -1; }); var len = openIndexArray.length; var min; if (len === 1) { min = openIndexArray[0]; } else if (len === 2) { min = Math.min(openIndexArray[0], openIndexArray[1]); } else if (len === 3) { min = Math.min(openIndexArray[0], openIndexArray[1], openIndexArray[2]); } tagName = value.substring(0, min); tagName = tagName.trim(); return ({ tag: tagName, styles: stylesList, classes: classList, attributes: attributesList }); }; return FormatPainterActions; }()); export { FormatPainterActions };