@syncfusion/ej2-richtexteditor
Version:
Essential JS 2 RichTextEditor component
589 lines (588 loc) • 28 kB
JavaScript
import * as CONSTANT from './../base/constant';
import { NodeSelection } from '../../selection/selection';
import { NodeCutter } from './nodecutter';
import { InsertHtml } from './inserthtml';
import { createElement, isNullOrUndefined as isNOU, closest } from '@syncfusion/ej2-base';
import * as EVENTS from './../../common/constant';
import { DOMMethods } from './dom-tree';
import { InsertMethods } from './insert-methods';
import { IsFormatted } from './isformatted';
/**
* Link internal component
*
* @hidden
* @private
*/
var LinkCommand = /** @class */ (function () {
/**
* Constructor for creating the Formats plugin
*
* @param {IEditorModel} parent - specifies the editor manager
* @hidden
* @private
*/
function LinkCommand(parent) {
this.parent = parent;
this.addEventListener();
}
LinkCommand.prototype.addEventListener = function () {
this.parent.observer.on(CONSTANT.LINK, this.linkCommand, this);
this.parent.observer.on(EVENTS.INTERNAL_DESTROY, this.destroy, this);
var dropElement = this.parent.editableElement;
if (dropElement) {
this.parent.observer.on(EVENTS.dropEventHandler, this.dragDrop, this);
this.parent.observer.on(EVENTS.dragEnterEvent, this.dragEnter, this);
this.parent.observer.on(EVENTS.dragOverEvent, this.dragStart, this);
}
};
LinkCommand.prototype.removeEventListener = function () {
this.parent.observer.off(CONSTANT.LINK, this.linkCommand);
this.parent.observer.off(EVENTS.INTERNAL_DESTROY, this.destroy);
var dropElement = this.parent.editableElement;
if (dropElement) {
this.parent.observer.off(EVENTS.dropEventHandler, this.dragDrop);
this.parent.observer.off(EVENTS.dragEnterEvent, this.dragEnter);
this.parent.observer.off(EVENTS.dragOverEvent, this.dragStart);
}
};
LinkCommand.prototype.linkCommand = function (e) {
switch (e.value.toString().toLocaleLowerCase()) {
case 'createlink':
case 'editlink':
this.createLink(e);
break;
case 'openlink':
this.openLink(e);
break;
case 'removelink':
this.removeLink(e);
break;
}
};
LinkCommand.prototype.dragStart = function (event) {
var range = this.parent.nodeSelection.getRange(this.parent.currentDocument);
if (range) {
var startContainer = range.startContainer;
var endContainer = range.endContainer;
var startAnchor = null;
var endAnchor = null;
if (startContainer.nodeType === Node.ELEMENT_NODE) {
startAnchor = startContainer.closest('a');
}
else {
var parentElement = startContainer.parentElement;
if (parentElement) {
startAnchor = parentElement.closest('a');
}
}
if (endContainer.nodeType === Node.ELEMENT_NODE) {
endAnchor = endContainer.closest('a');
}
else {
var parentElement = endContainer.parentElement;
if (parentElement) {
endAnchor = parentElement.closest('a');
}
}
if (event.target.nodeName === 'A' || startAnchor || endAnchor) {
this.dragSelectionRange = range.cloneRange();
}
}
};
LinkCommand.prototype.dragEnter = function (event) {
event.dataTransfer.dropEffect = 'copy';
event.preventDefault();
};
LinkCommand.prototype.dragDrop = function (event) {
var html = event.dataTransfer.getData('text/html');
if (html && /^<img[\s\S]*?>$/i.test(html.trim())) {
return;
}
if (this.dragSelectionRange) {
event.preventDefault();
var range = void 0;
if (this.parent.currentDocument.caretRangeFromPoint) { //For chrome and safari
range = this.parent.currentDocument.caretRangeFromPoint(event.clientX, event.clientY);
}
else if ((event.rangeParent)) { //For mozilla firefox
range = this.parent.currentDocument.createRange();
range.setStart(event.rangeParent, event.rangeOffset);
}
if (html) {
var anchorElement = null;
if (range.startContainer && range.startContainer.nodeType === Node.TEXT_NODE) {
anchorElement = range.startContainer.parentNode;
}
else if (range.startContainer instanceof HTMLAnchorElement) {
anchorElement = range.startContainer;
}
if (!anchorElement) {
if (range.collapsed) {
var node = range.startContainer;
if (node) {
var parentAnchor = node.closest('a');
if (parentAnchor) {
anchorElement = parentAnchor;
}
}
}
}
else if (anchorElement && anchorElement.nodeName !== 'A') {
anchorElement = anchorElement.closest('a');
}
if (anchorElement) {
var tempDiv = createElement('div', { innerHTML: html });
var anchors = tempDiv.querySelectorAll('a');
anchors.forEach(function (anchor) {
while (anchor.firstChild) {
anchor.parentNode.insertBefore(anchor.firstChild, anchor);
}
anchor.remove();
});
html = tempDiv.innerHTML;
}
range.deleteContents();
var fragment = range.createContextualFragment(html);
var anchorEle = fragment.querySelectorAll('a');
anchorEle.forEach(function (anchor) {
anchor.style.textDecoration = '';
});
if (this.dragSelectionRange) {
this.dragSelectionRange.deleteContents();
this.normalizeEmptyLinks();
this.dragSelectionRange = null;
}
this.parent.nodeSelection.setRange(this.parent.currentDocument, range);
InsertHtml.Insert(this.parent.currentDocument, fragment, this.parent.editableElement, true);
if (anchorElement) {
anchorElement.normalize();
}
}
}
};
LinkCommand.prototype.normalizeEmptyLinks = function () {
if (!this.dragSelectionRange) {
return;
}
var commonAncestor = this.dragSelectionRange.commonAncestorContainer;
var parentElement = commonAncestor.nodeType === Node.TEXT_NODE
? commonAncestor.parentElement
: commonAncestor;
if (parentElement && CONSTANT.BLOCK_TAGS.indexOf(parentElement.nodeName.toLocaleLowerCase()) === -1) {
parentElement = this.parent.domNode.getImmediateBlockNode(parentElement);
}
if (parentElement) {
var emptyLinks = parentElement.querySelectorAll('a:empty');
emptyLinks.forEach(function (link) {
if (link.textContent.trim() === '' && !link.querySelector('img') && !link.querySelector('video')) {
if (link.parentNode) {
link.parentNode.removeChild(link);
}
}
});
}
};
LinkCommand.prototype.createLink = function (e) {
var closestAnchor = (!isNOU(e.item.selectParent) && e.item.selectParent.length === 1) &&
closest(e.item.selectParent[0], 'a');
closestAnchor = !isNOU(closestAnchor) ? closestAnchor :
(!isNOU(e.item.selectParent) && e.item.selectParent.length === 1) ?
(e.item.selectParent[0]) : null;
if (!isNOU(closestAnchor) && closestAnchor.tagName === 'A') {
var anchorEle = closestAnchor;
var linkText = '';
if (!isNOU(e.item.url)) {
anchorEle.setAttribute('href', e.item.url);
}
if (!isNOU(e.item.title)) {
anchorEle.setAttribute('title', e.item.title);
}
if (!isNOU(e.item.text) && e.item.text !== '') {
linkText = anchorEle.innerText;
var walker = document.createTreeWalker(anchorEle, NodeFilter.SHOW_TEXT, null);
var anchorTextnode = walker.nextNode();
if (anchorTextnode) {
anchorTextnode.textContent = e.item.text;
}
}
if (!isNOU(e.item.target)) {
anchorEle.setAttribute('target', e.item.target);
anchorEle.setAttribute('aria-label', e.item.ariaLabel);
}
else {
anchorEle.removeAttribute('target');
anchorEle.removeAttribute('aria-label');
}
if (isNOU(e.item.selection)) {
return;
}
if (linkText === e.item.text) {
e.item.selection.setSelectionText(this.parent.currentDocument, anchorEle, anchorEle, 1, 1);
e.item.selection.restore();
}
else {
var startIndex = e.item.action === 'Paste' ? anchorEle.childNodes[0].textContent.length : 0;
var endIndex = anchorEle.firstChild.nodeName === '#text' ? anchorEle.childNodes[0].textContent.length : anchorEle.childNodes.length;
e.item.selection.setSelectionText(this.parent.currentDocument, anchorEle.childNodes[0], anchorEle.childNodes[0], startIndex, endIndex);
}
}
else {
var domSelection = new NodeSelection(this.parent.editableElement);
var range = domSelection.getRange(this.parent.currentDocument);
if (range.endContainer.nodeName === '#text' && range.startContainer.textContent.length === (range.endOffset + 1) &&
range.endContainer.textContent.charAt(range.endOffset) === ' ' && (!isNOU(range.endContainer.nextSibling) && range.endContainer.nextSibling.nodeName === 'A')) {
domSelection.setSelectionText(this.parent.currentDocument, range.startContainer, range.endContainer, range.startOffset, range.endOffset + 1);
range = domSelection.getRange(this.parent.currentDocument);
}
var text = isNOU(e.item.text) ? true : e.item.text.replace(/ /g, '').localeCompare(range.toString()
.replace(/\n/g, ' ').replace(/ /g, '')) < 0;
if (e.event && e.event.type === 'keydown' && (e.event.keyCode === 32
|| e.event.keyCode === 13) || e.item.action === 'Paste' || range.collapsed || text) {
var anchor = this.createAchorNode(e);
anchor.innerText = e.item.text === '' ? e.item.url : e.item.text;
var text_1 = anchor.innerText;
// Replace spaces with non-breaking spaces
var modifiedText = text_1.replace(/ +/g, function (match) {
return '\u00A0'.repeat(match.length);
});
anchor.innerText = modifiedText;
if (isNOU(e.item.selection)) {
return;
}
e.item.selection.restore();
InsertHtml.Insert(this.parent.currentDocument, anchor, this.parent.editableElement);
if (!isNOU(anchor.parentElement) && anchor.parentElement.nodeName === 'LI') {
if (!isNOU(anchor.parentNode.childNodes) && anchor.parentNode.childNodes[0].textContent === '') {
anchor.parentNode.removeChild(anchor.parentNode.childNodes[0]);
}
}
var regex = /[^\w\s\\/\\.\\:]/g;
if (e.event && e.event.type === 'keydown' && (e.event.keyCode === 32
|| e.event.keyCode === 13 || regex.test(e.event.key))) {
var startContainer = e.item.selection.range.startContainer;
startContainer.textContent = this.removeText(startContainer.textContent, e.item.text);
}
else {
var startIndex = e.item.action === 'Paste' ? anchor.childNodes[0].textContent.length : 0;
e.item.selection.setSelectionText(this.parent.currentDocument, anchor.childNodes[0], anchor.childNodes[0], startIndex, anchor.childNodes[0].textContent.length);
}
}
else {
this.handleLinkFormat(e);
}
}
if (e.callBack) {
e.callBack({
requestType: 'Links',
editorMode: 'HTML',
event: e.event,
range: this.parent.nodeSelection.getRange(this.parent.currentDocument),
elements: this.parent.nodeSelection.getSelectedNodes(this.parent.currentDocument)
});
}
};
LinkCommand.prototype.createAchorNode = function (e) {
var anchorEle = createElement('a', {
className: 'e-rte-anchor',
attrs: {
href: e.item.url,
title: isNOU(e.item.title) || e.item.title === '' ? e.item.url : e.item.title
}
});
if (!isNOU(e.item.target)) {
anchorEle.setAttribute('target', e.item.target);
}
if (!isNOU(e.item.ariaLabel)) {
anchorEle.setAttribute('aria-label', e.item.ariaLabel);
}
return anchorEle;
};
LinkCommand.prototype.removeText = function (text, val) {
var arr = text.split(' ');
for (var i = 0; i < arr.length; i++) {
if (arr[i] === val) {
arr.splice(i, 1);
i--;
}
}
return arr.join(' ') + ' ';
};
LinkCommand.prototype.openLink = function (e) {
document.defaultView.open(e.item.url, e.item.target);
this.callBack(e);
};
LinkCommand.prototype.removeLink = function (e) {
var blockNodes = this.parent.domNode.blockNodes();
if (blockNodes.length < 2) {
this.parent.domNode.setMarker(e.item.selection);
var closestAnchor = closest(e.item.selectParent[0], 'a');
var selectParent = closestAnchor ? closestAnchor : e.item.selectParent[0];
var parent_1 = selectParent.parentNode;
var child = [];
for (; selectParent.firstChild; null) {
if (parent_1) {
child.push(parent_1.insertBefore(selectParent.firstChild, selectParent));
}
else {
break;
}
}
parent_1.removeChild(selectParent);
if (child && child.length === 1) {
e.item.selection.startContainer = e.item.selection.getNodeArray(child[child.length - 1], true);
e.item.selection.endContainer = e.item.selection.startContainer;
e.item.selection.startOffset = 0;
e.item.selection.endOffset = child[child.length - 1].textContent.length;
}
e.item.selection = this.parent.domNode.saveMarker(e.item.selection);
}
else {
for (var i = 0; i < blockNodes.length; i++) {
var linkNode = blockNodes[i].querySelectorAll('a');
for (var j = 0; j < linkNode.length; j++) {
if (this.parent.currentDocument.getSelection().containsNode(linkNode[j], true)) {
linkNode[j].outerHTML = linkNode[j].innerHTML;
}
}
}
}
e.item.selection.restore();
this.callBack(e);
};
LinkCommand.prototype.callBack = function (e) {
if (e.callBack) {
e.callBack({
requestType: e.item.subCommand,
editorMode: 'HTML',
event: e.event,
range: this.parent.nodeSelection.getRange(this.parent.currentDocument),
elements: this.parent.nodeSelection.getSelectedNodes(this.parent.currentDocument)
});
}
};
LinkCommand.prototype.destroy = function () {
this.removeEventListener();
};
LinkCommand.prototype.handleLinkFormat = function (e) {
var editableElement = this.parent.editableElement;
var range = this.parent.nodeSelection.getRange(editableElement.ownerDocument);
var selection = this.parent.currentDocument.getSelection();
if (!selection || selection.rangeCount === 0) {
return;
}
var domMethods = new DOMMethods(editableElement);
var blockNodes = e.enterAction === 'BR' ? [this.parent.editableElement] : domMethods.getBlockNode();
var appliedNodes = [];
var inlineMediaTags = ['IMG', 'AUDIO', 'VIDEO'];
var mediaStart;
var mediaEnd;
if (range.startContainer.nodeType === 1) {
var mediaNode = range.startContainer.childNodes[range.startOffset];
mediaStart = mediaNode && inlineMediaTags.indexOf(mediaNode.nodeName) > -1 ? mediaNode : null;
}
if (range.endContainer.nodeType === 1) {
var mediaNode = range.endContainer.childNodes[range.endContainer.childNodes.length > 1 ?
range.endOffset - 1 : range.endOffset];
mediaEnd = mediaNode && inlineMediaTags.indexOf(mediaNode.nodeName) > -1 ? mediaNode : null;
}
var staticRange = {
startContainer: range.startContainer,
endContainer: range.endContainer,
endOffset: range.endOffset,
startOffset: range.startOffset,
collapsed: range.collapsed
};
for (var i = 0; i < blockNodes.length; i++) {
var currentNode = blockNodes[i];
this.unwrapLink(currentNode);
currentNode.normalize();
this.applyLinkToBlockNode(currentNode, e, appliedNodes);
if (blockNodes.length <= 1 && appliedNodes.length <= 1) {
var currentText = appliedNodes[appliedNodes.length - 1].textContent.trim();
var newText = e.item.text.trim();
if (currentText !== newText) {
appliedNodes[appliedNodes.length - 1].textContent = newText;
}
}
}
if (appliedNodes.length === 0) {
return;
}
if (mediaStart || mediaEnd) {
var start = mediaStart ? mediaStart.parentElement : staticRange.startContainer;
var end = mediaEnd ? mediaEnd.parentElement : staticRange.endContainer;
var startOffset = mediaStart ? 0 : staticRange.startOffset;
var endOffset = staticRange.endOffset;
this.parent.nodeSelection.setSelectionText(this.parent.currentDocument, start, end, startOffset, endOffset);
}
else {
if (appliedNodes.length === 1) {
this.parent.nodeSelection.setSelectionContents(this.parent.currentDocument, appliedNodes[0]);
}
else {
this.parent.nodeSelection.setSelectionText(this.parent.currentDocument, appliedNodes[0], // Start Node
appliedNodes[appliedNodes.length - 1], // end Node
0, // start offset
appliedNodes[appliedNodes.length - 1].textContent.length // end offset
);
}
}
};
LinkCommand.prototype.applyLinkToBlockNode = function (blockNode, e, appliedNode) {
var domMethods = new DOMMethods(this.parent.editableElement);
var textNodes = domMethods.getTextNodes(blockNode);
var inlineNodes = blockNode.querySelectorAll('*');
var range = this.parent.nodeSelection.getRange(this.parent.currentDocument);
// eslint-disable-next-line prefer-const
var complexFormatNodes = [];
var hasOnlyTextNode = inlineNodes.length === 0;
for (var i = 0; i < textNodes.length; i++) {
var splitNode = void 0;
var currentTextNode = textNodes[i];
var fontColorNode = new IsFormatted().getFormattedNode(currentTextNode, 'fontcolor', blockNode);
if (hasOnlyTextNode) { // Only text node case.
splitNode = this.getSplitNode(currentTextNode, range);
appliedNode.push(InsertMethods.Wrap(splitNode, this.createAchorNode(e)));
}
else {
if (fontColorNode) { // Font color foramt case.
if (complexFormatNodes.length > 0) {
this.replaceElementsWithAnchor(complexFormatNodes, this.createAchorNode(e), e.enterAction);
}
splitNode = this.getSplitNode(fontColorNode, range);
if (range.intersectsNode(fontColorNode)) {
InsertMethods.Wrap(fontColorNode.firstChild, this.createAchorNode(e));
appliedNode.push(currentTextNode);
}
}
else { // Partial selection of Inline nodes.
var partialStart = range.startContainer.nodeName === '#text' &&
range.startContainer === currentTextNode && range.startOffset !== 0;
var partialEnd = range.endContainer.nodeName === '#text' &&
range.endContainer === currentTextNode && range.endOffset !== range.startContainer.textContent.length;
if (i > 0) {
var currentParent = e.enterAction === 'BR' ? this.parent.editableElement : domMethods.getParentBlockNode(currentTextNode);
if (currentParent !== blockNode) {
this.replaceElementsWithAnchor(complexFormatNodes, this.createAchorNode(e), e.enterAction);
}
}
if (partialStart || partialEnd) {
var topMostFormatNode = domMethods.getTopMostNode(currentTextNode);
splitNode = this.getSplitNode(topMostFormatNode, range);
appliedNode.push(currentTextNode);
complexFormatNodes.push(currentTextNode);
}
else {
appliedNode.push(currentTextNode);
complexFormatNodes.push(currentTextNode);
}
if (i === textNodes.length - 1) {
this.replaceElementsWithAnchor(complexFormatNodes, this.createAchorNode(e), e.enterAction);
}
}
}
}
};
LinkCommand.prototype.unwrapLink = function (elem) {
var links = elem.querySelectorAll('a');
if (links.length === 0) {
return;
}
var range = this.parent.nodeSelection.getRange(this.parent.currentDocument);
var startContainer = range.startContainer;
var endContainer = range.endContainer;
var startOffset = range.startOffset;
var endOffset = range.endOffset;
this.parent.nodeSelection.save(range, this.parent.currentDocument);
var selection = this.parent.nodeSelection.get(this.parent.currentDocument);
for (var i = 0; i < links.length; i++) {
if (range.intersectsNode(links[i])) {
if (selection.containsNode(links[i], false)) {
InsertMethods.unwrap(links[i]);
}
else {
var linkText = links[i] && links[i].textContent;
if (linkText && range.startContainer.textContent &&
linkText.indexOf(range.startContainer.textContent) !== -1) {
startOffset = 0;
}
var splitNode = this.getSplitNode(links[i], range);
InsertMethods.unwrap(splitNode);
}
}
}
range.setStart(startContainer, startOffset);
range.setEnd(endContainer, endOffset);
};
LinkCommand.prototype.replaceElementsWithAnchor = function (complexFormatNodes, anchor, enterAction) {
var domMethods = new DOMMethods(this.parent.editableElement);
var processedNodes = [];
for (var j = 0; j < complexFormatNodes.length; j++) {
var currentText = complexFormatNodes[j];
processedNodes.push(domMethods.getTopMostNode(currentText));
}
complexFormatNodes.length = 0;
var firstNode = processedNodes[0];
var cloneNode = anchor.cloneNode(true);
firstNode.parentElement.insertBefore(anchor, firstNode);
var previousBRAnchor;
for (var i = 0; i < processedNodes.length; i++) {
var node = processedNodes[i];
if (enterAction === 'BR') {
if (i === 0) {
anchor.appendChild(node);
}
else {
if (isNOU(previousBRAnchor)) {
var anchorElem = cloneNode.cloneNode(true);
node.parentElement.insertBefore(anchorElem, node);
anchorElem.appendChild(node);
previousBRAnchor = anchorElem;
}
else {
var isNextSiblingBlockOrBR = (node.nextSibling && node.nextSibling.nodeName === 'BR') || domMethods.isBlockNode(node.nextSibling);
var isPrevSiblingBlockOrBR = (node.previousSibling && node.previousSibling.nodeName === 'BR') || domMethods.isBlockNode(node.previousSibling);
var isLastElement = this.parent.editableElement.lastChild === node;
var isBlockParent = domMethods.isBlockNode(node.parentElement);
if (isNextSiblingBlockOrBR && isPrevSiblingBlockOrBR) {
var anchorElem = cloneNode.cloneNode(true);
node.parentElement.insertBefore(anchorElem, node);
anchorElem.appendChild(node);
previousBRAnchor = anchorElem;
}
else if (isLastElement) {
var anchorElem = cloneNode.cloneNode(true);
node.parentElement.insertBefore(anchorElem, node);
anchorElem.appendChild(node);
}
else if (isBlockParent) {
var anchorElem = cloneNode.cloneNode(true);
node.parentElement.insertBefore(anchorElem, node);
anchorElem.appendChild(node);
previousBRAnchor = anchorElem;
}
else {
previousBRAnchor.appendChild(node);
}
}
}
}
else {
anchor.appendChild(node);
}
}
};
LinkCommand.prototype.getSplitNode = function (node, range) {
var nodeCutter = new NodeCutter();
var splitNode;
if (range.collapsed) {
splitNode = nodeCutter.SplitNode(range, node, true);
}
else {
splitNode = nodeCutter.GetSpliceNode(range, node);
}
return splitNode;
};
return LinkCommand;
}());
export { LinkCommand };