@syncfusion/ej2-richtexteditor
Version:
Essential JS 2 RichTextEditor component
537 lines (514 loc) • 20 kB
JavaScript
import { isNullOrUndefined } from '@syncfusion/ej2-base';
import * as CONSTANT from './../editor-manager/base';
/**
* `Selection` module is used to handle RTE Selections.
*/
var NodeSelection = /** @class */ (function () {
function NodeSelection(editElement) {
this.startNodeName = [];
this.endNodeName = [];
this.editableElement = editElement;
}
NodeSelection.prototype.saveInstance = function (range, body) {
this.range = range.cloneRange();
this.rootNode = this.documentFromRange(range);
this.body = body;
this.startContainer = this.getNodeArray(range.startContainer, true);
this.endContainer = this.getNodeArray(range.endContainer, false);
this.startOffset = range.startOffset;
this.endOffset = range.endOffset;
this.html = this.body.innerHTML;
return this;
};
NodeSelection.prototype.documentFromRange = function (range) {
return (9 === range.startContainer.nodeType) ? range.startContainer : range.startContainer.ownerDocument;
};
NodeSelection.prototype.getRange = function (docElement) {
var select = this.get(docElement);
var range = select && select.rangeCount > 0 ? select.getRangeAt(select.rangeCount - 1) : docElement.createRange();
return (range.startContainer !== docElement || range.endContainer !== docElement
|| range.startOffset || range.endOffset || (range.setStart(docElement.body, 0),
range.collapse(!0)),
range);
};
/**
* get method
*
* @param {Document} docElement - specifies the get function
* @returns {void}
* @hidden
*/
NodeSelection.prototype.get = function (docElement) {
return docElement.defaultView.getSelection();
};
/**
* save method
*
* @param {Range} range - range value.
* @param {Document} docElement - specifies the document.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.save = function (range, docElement) {
range = (range) ? range.cloneRange() : this.getRange(docElement);
return this.saveInstance(range, docElement.body);
};
/**
* getIndex method
*
* @param {Node} node - specifies the node value.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getIndex = function (node) {
var index;
var num = 0;
node = !node.previousSibling && node.tagName === 'BR' ? node : node.previousSibling;
if (node) {
for (var type = node.nodeType; node; null) {
index = node.nodeType;
num++;
//eslint-disable-next-line
type = index;
node = node.previousSibling;
}
}
return num;
};
NodeSelection.prototype.isChildNode = function (nodeCollection, parentNode) {
for (var index = 0; index < parentNode.childNodes.length; index++) {
if (nodeCollection.indexOf(parentNode.childNodes[index]) > -1) {
return true;
}
}
return false;
};
NodeSelection.prototype.getNode = function (startNode, endNode, nodeCollection) {
if (this.editableElement && (!this.editableElement.contains(startNode) || this.editableElement === startNode)) {
return null;
}
if (endNode === startNode &&
(startNode.nodeType === 3 || !startNode.firstChild || nodeCollection.indexOf(startNode.firstChild) !== -1
|| this.isChildNode(nodeCollection, startNode))) {
return null;
}
if (startNode.nodeType === 3 && startNode.previousSibling === endNode && endNode.nodeName === 'IMG') {
return null;
}
if (nodeCollection.indexOf(startNode.firstChild) === -1 && startNode.firstChild && !this.isChildNode(nodeCollection, startNode)) {
return startNode.firstChild;
}
if (startNode.nextSibling) {
return startNode.nextSibling;
}
if (!startNode.parentNode) {
return null;
}
else {
return startNode.parentNode;
}
};
/**
* getNodeCollection method
*
* @param {Range} range -specifies the range.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getNodeCollection = function (range) {
var startNode = range.startContainer.childNodes[range.startOffset]
|| range.startContainer;
var endNode = range.endContainer.childNodes[(range.endOffset > 0) ? (range.endOffset - 1) : range.endOffset]
|| range.endContainer;
var tableCursor = this.processedTableImageCursor(range);
if (tableCursor.start || tableCursor.end) {
if (tableCursor.startName === 'TABLE' || tableCursor.endName === 'TABLE') {
var tableNode = tableCursor.start ? tableCursor.startNode : tableCursor.endNode;
return [tableNode];
}
}
if ((startNode === endNode || (startNode.nodeName === 'BR' && startNode === range.endContainer.childNodes[range.endOffset])) &&
startNode.childNodes.length === 0) {
return [startNode];
}
if (range.startOffset === range.endOffset && range.startOffset !== 0 && range.startContainer.nodeName === 'PRE') {
return [startNode.nodeName === 'BR' || startNode.nodeName === '#text' ? startNode : startNode.childNodes[0]];
}
var nodeCollection = [];
do {
if (nodeCollection.indexOf(startNode) === -1) {
nodeCollection.push(startNode);
}
startNode = this.getNode(startNode, endNode, nodeCollection);
} while (startNode);
return nodeCollection;
};
/**
* getParentNodeCollection method
*
* @param {Range} range - specifies the range value.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getParentNodeCollection = function (range) {
return this.getParentNodes(this.getNodeCollection(range), range);
};
/**
* getParentNodes method
*
* @param {Node[]} nodeCollection - specifies the collection of nodes.
* @param {Range} range - specifies the range values.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getParentNodes = function (nodeCollection, range) {
nodeCollection = nodeCollection.reverse();
for (var index = 0; index < nodeCollection.length; index++) {
if ((nodeCollection.indexOf(nodeCollection[index].parentNode) !== -1)
|| (nodeCollection[index].nodeType === 3 &&
range.startContainer !== range.endContainer &&
range.startContainer.parentNode !== range.endContainer.parentNode) &&
range.startContainer.parentNode.tagName && range.endContainer.parentNode.tagName &&
CONSTANT.BLOCK_TAGS.indexOf(range.startContainer.parentNode.tagName.toLowerCase()) !== -1
&& CONSTANT.BLOCK_TAGS.indexOf(range.endContainer.parentNode.tagName.toLowerCase()) !== -1) {
nodeCollection.splice(index, 1);
index--;
}
else if (nodeCollection[index].nodeType === 3) {
nodeCollection[index] = nodeCollection[index].parentNode;
}
}
return nodeCollection;
};
/**
* getSelectionNodeCollection method
*
* @param {Range} range - specifies the range value.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getSelectionNodeCollection = function (range) {
return this.getSelectionNodes(this.getNodeCollection(range));
};
/**
* getSelectionNodeCollection along with BR node method
*
* @param {Range} range - specifies the range value.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getSelectionNodeCollectionBr = function (range) {
return this.getSelectionNodesBr(this.getNodeCollection(range));
};
/**
* getParentNodes method
*
* @param {Node[]} nodeCollection - specifies the collection of nodes.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getSelectionNodes = function (nodeCollection) {
nodeCollection = nodeCollection.reverse();
var regEx = new RegExp('\u200B', 'g');
for (var index = 0; index < nodeCollection.length; index++) {
if (nodeCollection[index].nodeType !== 3 || (nodeCollection[index].textContent.trim() === '' ||
(nodeCollection[index].textContent.length === 1 && nodeCollection[index].textContent.match(regEx)))) {
nodeCollection.splice(index, 1);
index--;
}
}
return nodeCollection.reverse();
};
/**
* Get selection text nodes with br method.
*
* @param {Node[]} nodeCollection - specifies the collection of nodes.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getSelectionNodesBr = function (nodeCollection) {
nodeCollection = nodeCollection.reverse();
for (var index = 0; index < nodeCollection.length; index++) {
if (nodeCollection[index].nodeName !== 'BR' &&
(nodeCollection[index].nodeType !== 3 || (nodeCollection[index].textContent.trim() === ''))) {
nodeCollection.splice(index, 1);
index--;
}
}
return nodeCollection.reverse();
};
/**
* getInsertNodeCollection method
*
* @param {Range} range - specifies the range value.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getInsertNodeCollection = function (range) {
return this.getInsertNodes(this.getNodeCollection(range));
};
/**
* getInsertNodes method
*
* @param {Node[]} nodeCollection - specifies the collection of nodes.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getInsertNodes = function (nodeCollection) {
nodeCollection = nodeCollection.reverse();
for (var index = 0; index < nodeCollection.length; index++) {
if ((nodeCollection[index].childNodes.length !== 0 &&
nodeCollection[index].nodeType !== 3) ||
(nodeCollection[index].nodeType === 3 &&
nodeCollection[index].textContent === '')) {
nodeCollection.splice(index, 1);
index--;
}
}
return nodeCollection.reverse();
};
/**
* getNodeArray method
*
* @param {Node} node - specifies the node content.
* @param {boolean} isStart - specifies the boolean value.
* @param {Document} root - specifies the root document.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getNodeArray = function (node, isStart, root) {
var array = [];
// eslint-disable-next-line
((isStart) ? (this.startNodeName = []) : (this.endNodeName = []));
for (; node !== (root ? root : this.rootNode); null) {
if (isNullOrUndefined(node)) {
break;
}
// eslint-disable-next-line
(isStart) ? this.startNodeName.push(node.nodeName.toLowerCase()) : this.endNodeName.push(node.nodeName.toLowerCase());
array.push(this.getIndex(node));
node = node.parentNode;
}
return array;
};
NodeSelection.prototype.setRangePoint = function (range, isvalid, num, size) {
var node = this.rootNode;
var index = num.length;
var constant = size;
for (; index--; null) {
node = node && node.childNodes[num[index]];
}
if (node && constant >= 0 && node.nodeName !== 'html') {
if (node.nodeType === 3 && node.nodeValue.replace(/\u00a0/g, ' ') === ' ') {
constant = node.textContent.length;
}
else if (node.nodeType !== 3) {
constant = Math.min(constant, node.childNodes.length);
}
range[isvalid ? 'setStart' : 'setEnd'](node, constant);
}
return range;
};
/**
* restore method
*
* @returns {void}
* @hidden
*/
NodeSelection.prototype.restore = function () {
var range = this.range.cloneRange();
range = this.setRangePoint(range, true, this.startContainer, this.startOffset);
range = this.setRangePoint(range, false, this.endContainer, this.endOffset);
this.selectRange(this.rootNode, range);
return range;
};
NodeSelection.prototype.selectRange = function (docElement, range) {
this.setRange(docElement, range);
this.save(range, docElement);
};
/**
* setRange method
*
* @param {Document} docElement - specifies the document.
* @param {Range} range - specifies the range.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.setRange = function (docElement, range) {
var selection = this.get(docElement);
selection.removeAllRanges();
selection.addRange(range);
};
/**
* setSelectionText method
*
* @param {Document} docElement - specifies the documrent
* @param {Node} startNode - specifies the starting node.
* @param {Node} endNode - specifies the the end node.
* @param {number} startIndex - specifies the starting index.
* @param {number} endIndex - specifies the end index.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.setSelectionText = function (docElement, startNode, endNode, startIndex, endIndex) {
var range = docElement.createRange();
range.setStart(startNode, startIndex);
range.setEnd(endNode, endIndex);
this.setRange(docElement, range);
};
/**
* setSelectionContents method
*
* @param {Document} docElement - specifies the document.
* @param {Node} element - specifies the node.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.setSelectionContents = function (docElement, element) {
var range = docElement.createRange();
range.selectNode(element);
this.setRange(docElement, range);
};
/**
* setSelectionNode method
*
* @param {Document} docElement - specifies the document.
* @param {Node} element - specifies the node.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.setSelectionNode = function (docElement, element) {
var range = docElement.createRange();
range.selectNodeContents(element);
this.setRange(docElement, range);
};
/**
* getSelectedNodes method
*
* @param {Document} docElement - specifies the document.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.getSelectedNodes = function (docElement) {
return this.getNodeCollection(this.getRange(docElement));
};
/**
* Clear method
*
* @param {Document} docElement - specifies the document.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.Clear = function (docElement) {
this.get(docElement).removeAllRanges();
};
/**
* insertParentNode method
*
* @param {Document} docElement - specifies the document.
* @param {Node} newNode - specicfies the new node.
* @param {Range} range - specifies the range.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.insertParentNode = function (docElement, newNode, range) {
range.surroundContents(newNode);
this.selectRange(docElement, range);
};
/**
* setCursorPoint method
*
* @param {Document} docElement - specifies the document.
* @param {Element} element - specifies the element.
* @param {number} point - specifies the point.
* @returns {void}
* @hidden
*/
NodeSelection.prototype.setCursorPoint = function (docElement, element, point) {
var range = docElement.createRange();
var selection = docElement.defaultView.getSelection();
range.setStart(element, point);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
};
NodeSelection.prototype.isTableOrImageStart = function (range) {
var customHandlerElements = ['TABLE'];
var startContainer = range.startContainer;
var startOffset = range.startOffset;
var startNode = startContainer.childNodes[startOffset];
var isCursorAtStart = range.collapsed && (startContainer.nodeType === 1) &&
startContainer.isContentEditable && startNode &&
(customHandlerElements.indexOf(startNode.nodeName) > -1);
if (isCursorAtStart) {
return { start: isCursorAtStart, startNodeName: startNode.nodeName, startNode: startNode };
}
else {
return { start: false, startNodeName: '', startNode: undefined };
}
};
NodeSelection.prototype.isTableOrImageEnd = function (range) {
var customHandlerElements = ['TABLE'];
var startContainer = range.startContainer;
var startOffset = range.startOffset;
var endNode = startContainer.childNodes[startOffset - 1];
var isCursorAtEnd = range.collapsed && (startContainer.nodeType === 1) &&
startContainer.isContentEditable && endNode &&
(customHandlerElements.indexOf(endNode.nodeName) > -1);
if (isCursorAtEnd) {
return { end: isCursorAtEnd, endNodeName: endNode.nodeName, endNode: endNode };
}
else {
return { end: false, endNodeName: '', endNode: undefined };
}
};
NodeSelection.prototype.processedTableImageCursor = function (range) {
var _a = this.isTableOrImageStart(range), start = _a.start, startNodeName = _a.startNodeName, startNode = _a.startNode;
var _b = this.isTableOrImageEnd(range), end = _b.end, endNodeName = _b.endNodeName, endNode = _b.endNode;
return { start: start, startName: startNodeName, end: end, endName: endNodeName, startNode: startNode, endNode: endNode };
};
NodeSelection.prototype.findLastTextPosition = function (element) {
if (element.nodeType === Node.TEXT_NODE) {
return { node: element, offset: element.textContent ? element.textContent.length : 0 };
}
if (element.nodeName === 'BR') {
return { node: element, offset: 0 };
}
for (var i = element.childNodes.length - 1; i >= 0; i--) {
var lastPosition = this.findLastTextPosition(element.childNodes[i]);
if (lastPosition) {
return lastPosition;
}
}
return null;
};
NodeSelection.prototype.findFirstTextNode = function (node) {
if (node.nodeType === Node.TEXT_NODE) {
return node;
}
for (var i = 0; i < node.childNodes.length; i++) {
var textNode = this.findFirstTextNode(node.childNodes[i]);
if (!isNullOrUndefined(textNode)) {
return textNode;
}
}
return null;
};
NodeSelection.prototype.findFirstContentNode = function (node) {
if (node.nodeType === Node.TEXT_NODE) {
return { node: node, position: 0 };
}
if (node.nodeName === 'BR') {
return { node: node, position: 0 };
}
for (var i = 0; i < node.childNodes.length; i++) {
var result = this.findFirstContentNode(node.childNodes[i]);
if (result.node !== null) {
return result;
}
}
return { node: node, position: 0 };
};
return NodeSelection;
}());
export { NodeSelection };