diff --git a/spec/anchor.spec.js b/spec/anchor.spec.js index 1cda00386..44269288e 100644 --- a/spec/anchor.spec.js +++ b/spec/anchor.spec.js @@ -316,7 +316,85 @@ describe('Anchor Button TestCase', function () { expect(link.classList.contains('btn-default')).toBe(true); }); - it('should not select empty paragraphs when link is created at beginning of paragraph', function () { + // https://github.com/yabwe/medium-editor/issues/757 + it('should not select empty paragraphs when link is created at beginning of paragraph after empty paragraphs', function () { + spyOn(MediumEditor.prototype, 'createLink').and.callThrough(); + this.el.innerHTML = '

Some text



link text more text

'; + var editor = this.newMediumEditor('.editor'), + lastP = this.el.lastChild, + anchorExtension = editor.getExtensionByName('anchor'); + + // Select the text 'link text' in the last paragraph + MediumEditor.selection.select(document, lastP.firstChild, 0, lastP.firstChild, 'link text'.length); + fireEvent(editor.elements[0], 'focus'); + jasmine.clock().tick(1); + + // Click the 'anchor' button in the toolbar + fireEvent(editor.toolbar.getToolbarElement().querySelector('[data-action="createLink"]'), 'click'); + + // Input a url and save + var input = anchorExtension.getInput(); + input.value = 'http://www.example.com'; + fireEvent(input, 'keyup', { + keyCode: Util.keyCode.ENTER + }); + + expect(editor.createLink).toHaveBeenCalledWith({ + url: 'http://www.example.com', + target: '_self' + }); + + // Make sure the

wasn't removed, and the was added to the end + expect(this.el.lastChild).toBe(lastP); + expect(lastP.firstChild.nodeName.toLowerCase()).toBe('a'); + + // Make sure selection is only the link + var range = window.getSelection().getRangeAt(0); + expect(MediumEditor.util.isDescendant(lastP, range.startContainer, true)).toBe(true, 'The start of the selection is incorrect'); + expect(range.startOffset).toBe(0); + expect(MediumEditor.util.isDescendant(lastP.firstChild, range.endContainer, true)).toBe(true, 'The end of the selection is not contained within the link'); + }); + + // https://github.com/yabwe/medium-editor/issues/757 + it('should not select empty paragraphs when link is created at beginning of paragraph after another paragraph', function () { + spyOn(MediumEditor.prototype, 'createLink').and.callThrough(); + this.el.innerHTML = '

Some text

link text more text

'; + var editor = this.newMediumEditor('.editor'), + lastP = this.el.lastChild, + anchorExtension = editor.getExtensionByName('anchor'); + + // Select the text 'link text' in the last paragraph + MediumEditor.selection.select(document, lastP.firstChild, 0, lastP.firstChild, 'link text'.length); + fireEvent(editor.elements[0], 'focus'); + jasmine.clock().tick(1); + + // Click the 'anchor' button in the toolbar + fireEvent(editor.toolbar.getToolbarElement().querySelector('[data-action="createLink"]'), 'click'); + + // Input a url and save + var input = anchorExtension.getInput(); + input.value = 'http://www.example.com'; + fireEvent(input, 'keyup', { + keyCode: Util.keyCode.ENTER + }); + + expect(editor.createLink).toHaveBeenCalledWith({ + url: 'http://www.example.com', + target: '_self' + }); + + // Make sure the

wasn't removed, and the was added to the end + expect(this.el.lastChild).toBe(lastP); + expect(lastP.firstChild.nodeName.toLowerCase()).toBe('a'); + + // Make sure selection is only the link + var range = window.getSelection().getRangeAt(0); + expect(MediumEditor.util.isDescendant(lastP, range.startContainer, true)).toBe(true, 'The start of the selection is incorrect'); + expect(range.startOffset).toBe(0); + expect(MediumEditor.util.isDescendant(lastP.firstChild, range.endContainer, true)).toBe(true, 'The end of the selection is not contained within the link'); + }); + + it('should not remove the

container when adding a link inside a top-level

with a single text-node child', function () { spyOn(MediumEditor.prototype, 'createLink').and.callThrough(); this.el.innerHTML = '

Some text



link text more text

'; var editor = this.newMediumEditor('.editor'), diff --git a/spec/selection.spec.js b/spec/selection.spec.js index d1dabfc23..3ef1d7aa2 100644 --- a/spec/selection.spec.js +++ b/spec/selection.spec.js @@ -106,6 +106,16 @@ describe('Selection TestCase', function () { expect(exportedSelection.emptyBlocksIndex).toEqual(undefined); }); + it('should export a position indicating the cursor is at the beginning of a paragraph', function () { + this.el.innerHTML = '

www.google.com

Whatever

'; + var editor = this.newMediumEditor('.editor', { + buttons: ['italic', 'underline', 'strikethrough'] + }); + placeCursorInsideElement(editor.elements[0].querySelector('b'), 0); // beginning of tag + var exportedSelection = editor.exportSelection(); + expect(exportedSelection.emptyBlocksIndex).toEqual(0); + }); + it('should not export a position indicating the cursor is after an empty paragraph', function () { this.el.innerHTML = '

www.google.com


' + '

Whatever

'; diff --git a/src/js/core.js b/src/js/core.js index 3c0bd5b89..b6368ef55 100644 --- a/src/js/core.js +++ b/src/js/core.js @@ -924,7 +924,7 @@ function MediumEditor(elements, options) { this.elements[editableElementIndex], range.startContainer, range.startOffset); - if (emptyBlocksIndex !== 0) { + if (emptyBlocksIndex !== -1) { selectionState.emptyBlocksIndex = emptyBlocksIndex; } } @@ -1002,11 +1002,11 @@ function MediumEditor(elements, options) { } } - if (inSelectionState.emptyBlocksIndex) { + if (typeof inSelectionState.emptyBlocksIndex !== 'undefined') { var targetNode = Util.getBlockContainer(range.startContainer), index = 0; // Skip over empty blocks until we hit the block we want the selection to be in - while (index < inSelectionState.emptyBlocksIndex && targetNode.nextSibling) { + while ((index === 0 || index < inSelectionState.emptyBlocksIndex) && targetNode.nextSibling) { targetNode = targetNode.nextSibling; index++; // If we find a non-empty block, ignore the emptyBlocksIndex and just put selection here diff --git a/src/js/selection.js b/src/js/selection.js index ec34a60c6..cd796d02f 100644 --- a/src/js/selection.js +++ b/src/js/selection.js @@ -67,23 +67,24 @@ var Selection; return range; }, - // Returns 0 unless the cursor is within or preceded by empty paragraphs/blocks, - // in which case it returns the count of such preceding paragraphs, including - // the empty paragraph in which the cursor itself may be embedded. + // Returns -1 unless the cursor is at the beginning of a paragraph/block + // If the paragraph/block is preceeded 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: function (doc, root, cursorContainer, cursorOffset) { // If there is text in front of the cursor, that means there isn't only empty blocks before it - if (cursorContainer.nodeType === 3 && cursorOffset > 0) { - return 0; + 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 var node = cursorContainer; if (node.nodeType !== 3) { - //node = cursorContainer.childNodes.length === cursorOffset ? null : cursorContainer.childNodes[cursorOffset]; node = cursorContainer.childNodes[cursorOffset]; } if (node && !Util.isElementAtBeginningOfBlock(node)) { - return 0; + return -1; } // Walk over block elements, counting number of empty blocks between last piece of text