diff --git a/src/muya/lib/parser/render/renderInlines/highlight.js b/src/muya/lib/parser/render/renderInlines/highlight.js index 61af74b1..35c80568 100644 --- a/src/muya/lib/parser/render/renderInlines/highlight.js +++ b/src/muya/lib/parser/render/renderInlines/highlight.js @@ -5,7 +5,7 @@ export default function highlight (h, block, rStart, rEnd, token) { const { text } = block const { highlights } = token let result = [] - let unions = [] + const unions = [] let pos = rStart if (highlights) { diff --git a/src/muya/lib/selection/dom.js b/src/muya/lib/selection/dom.js index 92086846..84d65718 100644 --- a/src/muya/lib/selection/dom.js +++ b/src/muya/lib/selection/dom.js @@ -89,37 +89,6 @@ export const getFirstSelectableLeafNode = element => { return element } -export const isElementAtBeginningOfBlock = node => { - let textVal - let sibling - while (!isBlockContainer(node) && !isAganippeEditorElement(node)) { - sibling = node.previousSibling - while (sibling) { - textVal = sibling.nodeType === 3 ? sibling.nodeValue : sibling.textContent - if (textVal.length > 0) { - return false - } - sibling = sibling.previousSibling - } - node = node.parentNode - } - return true -} - -export const findPreviousSibling = node => { - if (!node || isAganippeEditorElement(node)) { - return false - } - - let previousSibling = node.previousSibling - while (!previousSibling && !isAganippeEditorElement(node.parentNode)) { - node = node.parentNode - previousSibling = node.previousSibling - } - - return previousSibling -} - export const getClosestBlockContainer = node => { return traverseUp(node, node => { return isBlockContainer(node) || isAganippeEditorElement(node) diff --git a/src/muya/lib/selection/index.js b/src/muya/lib/selection/index.js index 9f40297d..a6cf5565 100644 --- a/src/muya/lib/selection/index.js +++ b/src/muya/lib/selection/index.js @@ -5,10 +5,7 @@ import { isBlockContainer, traverseUp, - isAganippeEditorElement, getFirstSelectableLeafNode, - isElementAtBeginningOfBlock, - findPreviousSibling, getClosestBlockContainer, getCursorPositionWithinMarkedText, compareParagraphsOrder, @@ -42,60 +39,6 @@ class Selection { return traverseUp(current, testElementFunction) } - getSelectionElement (contentWindow) { - return this.findMatchingSelectionParent(el => { - return isAganippeEditorElement(el) - }, contentWindow) - } - - // https://stackoverflow.com/questions/17678843/cant-restore-selection-after-html-modify-even-if-its-the-same-html - // Tim Down - exportSelection (root) { - if (!root) { - return null - } - - let selectionState = null - const selection = this.doc.getSelection() - - if (selection.rangeCount > 0) { - const range = selection.getRangeAt(0) - const preSelectionRange = range.cloneRange() - - preSelectionRange.selectNodeContents(root) - preSelectionRange.setEnd(range.startContainer, range.startOffset) - - const start = preSelectionRange.toString().length - selectionState = { - start, - end: start + range.toString().length - } - - // Check to see if the selection starts with any images - // if so we need to make sure the the beginning of the selection is - // set correctly when importing selection - if (this.doesRangeStartWithImages(range)) { - selectionState.startsWithImage = true - } - - // Check to see if the selection has any trailing images - // if so, this this means we need to look for them when we import selection - const trailingImageCount = this.getTrailingImageCount(root, selectionState, range.endContainer, range.endOffset) - if (trailingImageCount) { - selectionState.trailingImageCount = trailingImageCount - } - - // If start = 0 there may still be an empty paragraph before it, but we don't care. - if (start !== 0) { - const emptyBlocksIndex = this.getIndexRelativeToAdjacentEmptyBlocks(root, range.startContainer, range.startOffset) - if (emptyBlocksIndex !== -1) { - selectionState.emptyBlocksIndex = emptyBlocksIndex - } - } - } - return selectionState - } - // https://stackoverflow.com/questions/17678843/cant-restore-selection-after-html-modify-even-if-its-the-same-html // Tim Down // @@ -314,207 +257,6 @@ class Selection { return range } - // Returns -1 unless the cursor is at the beginning of a paragraph/block - // If the paragraph/block is preceded by empty paragraphs/block (with no text) - // it will return the number of empty paragraphs before the cursor. - // Otherwise, it will return 0, which indicates the cursor is at the beginning - // of a paragraph/block, and not at the end of the paragraph/block before it - getIndexRelativeToAdjacentEmptyBlocks (root, cursorContainer, cursorOffset) { - // If there is text in front of the cursor, that means there isn't only empty blocks before it - if (cursorContainer.textContent.length > 0 && cursorOffset > 0) { - return -1 - } - - // Check if the block that contains the cursor has any other text in front of the cursor - let node = cursorContainer - if (node.nodeType !== 3) { - node = cursorContainer.childNodes[cursorOffset] - } - if (node) { - // The element isn't at the beginning of a block, so it has content before it - if (!isElementAtBeginningOfBlock(node)) { - return -1 - } - - const previousSibling = findPreviousSibling(node) - // If there is no previous sibling, this is the first text element in the editor - if (!previousSibling) { - return -1 - } else if (previousSibling.nodeValue) { - // If the previous sibling has text, then there are no empty blocks before this - return -1 - } - } - - // Walk over block elements, counting number of empty blocks between last piece of text - // and the block the cursor is in - const closestBlock = getClosestBlockContainer(cursorContainer) - const treeWalker = this.doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filterOnlyParentElements, false) - let emptyBlocksCount = 0 - while (treeWalker.nextNode()) { - const blockIsEmpty = treeWalker.currentNode.textContent === '' - if (blockIsEmpty || emptyBlocksCount > 0) { - emptyBlocksCount += 1 - } - if (treeWalker.currentNode === closestBlock) { - return emptyBlocksCount - } - if (!blockIsEmpty) { - emptyBlocksCount = 0 - } - } - - return emptyBlocksCount - } - - // Returns true if the selection range begins with an image tag - // Returns false if the range starts with any non empty text nodes - doesRangeStartWithImages (range) { - if (range.startOffset !== 0 || range.startContainer.nodeType !== 1) { - return false - } - - if (range.startContainer.nodeName.toLowerCase() === 'img') { - return true - } - - const img = range.startContainer.querySelector('img') - if (!img) { - return false - } - - const treeWalker = this.doc.createTreeWalker(range.startContainer, NodeFilter.SHOW_ALL, null, false) - while (treeWalker.nextNode()) { - const next = treeWalker.currentNode - // If we hit the image, then there isn't any text before the image so - // the image is at the beginning of the range - if (next === img) { - break - } - // If we haven't hit the iamge, but found text that contains content - // then the range doesn't start with an image - if (next.nodeValue) { - return false - } - } - - return true - } - - getTrailingImageCount (root, selectionState, endContainer, endOffset) { - // If the endOffset of a range is 0, the endContainer doesn't contain images - // If the endContainer is a text node, there are no trailing images - if (endOffset === 0 || endContainer.nodeType !== 1) { - return 0 - } - - // If the endContainer isn't an image, and doesn't have an image descendants - // there are no trailing images - if (endContainer.nodeName.toLowerCase() !== 'img' && !endContainer.querySelector('img')) { - return 0 - } - - let lastNode = endContainer.childNodes[endOffset - 1] - while (lastNode.hasChildNodes()) { - lastNode = lastNode.lastChild - } - - let node = root - const nodeStack = [] - let charIndex = 0 - let foundStart = false - let foundEnd = false - let stop = false - let nextCharIndex - let trailingImages = 0 - - while (!stop && node) { - // Only iterate over elements and text nodes - if (node.nodeType > 3) { - node = nodeStack.pop() - continue - } - - if (node.nodeType === 3 && !foundEnd) { - trailingImages = 0 - nextCharIndex = charIndex + node.length - if (!foundStart && selectionState.start >= charIndex && selectionState.start <= nextCharIndex) { - foundStart = true - } - if (foundStart && selectionState.end >= charIndex && selectionState.end <= nextCharIndex) { - foundEnd = true - } - charIndex = nextCharIndex - } else { - if (node.nodeName.toLowerCase() === 'img') { - trailingImages++ - } - - if (node === lastNode) { - stop = true - } else if (node.nodeType === 1) { - // this is an element - // add all its children to the stack - let i = node.childNodes.length - 1 - while (i >= 0) { - nodeStack.push(node.childNodes[i]) - i -= 1 - } - } - } - - if (!stop) { - node = nodeStack.pop() - } - } - - return trailingImages - } - - // determine if the current selection contains any 'content' - // content being any non-white space text or an image - selectionContainsContent () { - const sel = this.doc.getSelection() - - // collapsed selection or selection withour range doesn't contain content - if (!sel || sel.isCollapsed || !sel.rangeCount) { - return false - } - - // if toString() contains any text, the selection contains some content - if (sel.toString().trim() !== '') { - return true - } - - // if selection contains only image(s), it will return empty for toString() - // so check for an image manually - const selectionNode = this.getSelectedParentElement(sel.getRangeAt(0)) - if (selectionNode) { - if (selectionNode.nodeName.toLowerCase() === 'img' || - (selectionNode.nodeType === 1 && selectionNode.querySelector('img'))) { - return true - } - } - - return false - } - - selectionInContentEditableFalse (contentWindow) { - // determine if the current selection is exclusively inside - // a contenteditable="false", though treat the case of an - // explicit contenteditable="true" inside a "false" as false. - let sawtrue - const sawfalse = this.findMatchingSelectionParent(function (el) { - const ce = el && el.getAttribute('contenteditable') - if (ce === 'true') { - sawtrue = true - } - return el.nodeName !== '#text' && ce === 'false' - }, contentWindow) - - return !sawtrue && sawfalse - } - // https://stackoverflow.com/questions/4176923/html-of-selected-text // by Tim Down getSelectionHtml () { @@ -593,61 +335,6 @@ class Selection { } } - // https://stackoverflow.com/questions/15867542/range-object-get-selection-parent-node-chrome-vs-firefox - rangeSelectsSingleNode (range) { - const startNode = range.startContainer - return startNode === range.endContainer && - startNode.hasChildNodes() && - range.endOffset === range.startOffset + 1 - } - - getSelectedParentElement (range) { - if (!range) { - return null - } - - // Selection encompasses a single element - if (this.rangeSelectsSingleNode(range) && range.startContainer.childNodes[range.startOffset].nodeType !== 3) { - return range.startContainer.childNodes[range.startOffset] - } - - // Selection range starts inside a text node, so get its parent - if (range.startContainer.nodeType === 3) { - return range.startContainer.parentNode - } - - // Selection starts inside an element - return range.startContainer - } - - getSelectedElements () { - const selection = this.doc.getSelection() - let range - let toRet - let currNode - - if (!selection.rangeCount || selection.isCollapsed || !selection.getRangeAt(0).commonAncestorContainer) { - return [] - } - - range = selection.getRangeAt(0) - - if (range.commonAncestorContainer.nodeType === 3) { - toRet = [] - currNode = range.commonAncestorContainer - while (currNode.parentNode && currNode.parentNode.childNodes.length === 1) { - toRet.push(currNode.parentNode) - currNode = currNode.parentNode - } - - return toRet - } - - return [].filter.call(range.commonAncestorContainer.getElementsByTagName('*'), function (el) { - return (typeof selection.containsNode === 'function') ? selection.containsNode(el, true) : true - }) - } - selectNode (node) { const range = this.doc.createRange() range.selectNodeContents(node)