Socket
Socket
Sign inDemoInstall

medium-editor

Package Overview
Dependencies
Maintainers
4
Versions
125
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

medium-editor - npm Package Compare versions

Comparing version 5.1.0 to 5.2.0

demo/button-example.html

12

CHANGES.md

@@ -1,3 +0,13 @@

5.1.0 / 2015-06-18
5.2.0 / 2015-06-29
==================
* Move allowMultiParagraphSelection into toolbar options
* Deprecate global allowMultiParagraphSelection option
* Fix issue with allowMultiParagraphSelection option over empty elements
* Fix issue with creating links producing multiple anchor tags
* Fix issue where anchor preview displays while toolbar is visible
* Add demo pages for example extension and example button
5.1.0 / 2015-06-26
==================
* Add showToolbarDefaultAction helper method to form extension

@@ -4,0 +14,0 @@ * Ensure elements generated for textareas have a unique id

2

package.json
{
"name": "medium-editor",
"version": "5.1.0",
"version": "5.2.0",
"author": "Davi Ferreira <hi@daviferreira.com>",

@@ -5,0 +5,0 @@ "contributors": [

@@ -109,3 +109,2 @@ # MediumEditor

* __activeButtonClass__: CSS class added to active buttons in the toolbar. Default: `'medium-editor-button-active'`
* __allowMultiParagraphSelection__: enables the toolbar when selecting multiple paragraphs/block elements. Default: `true`
* __buttonLabels__: type of labels on the buttons. Values: `false` | 'fontawesome'. Default: `false`

@@ -135,2 +134,3 @@

if nothing is passed this is what is used */
allowMultiParagraphSelection: true,
buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote'],

@@ -152,2 +152,3 @@ diffLeft: 0,

* __allowMultiParagraphSelection__: enables/disables whether the toolbar should be displayed when selecting multiple paragraphs/block elements. Default: `true`
* __buttons__: the set of buttons to display on the toolbar. Default: `['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote']`

@@ -154,0 +155,0 @@ * See [Button Options](#button-options) for details on more button options

/*global MediumEditor, describe, it, expect, spyOn,
afterEach, beforeEach, selectElementContents,
jasmine, fireEvent, Util, setupTestHelpers,
selectElementContentsAndFire, AnchorForm */
selectElementContentsAndFire, AnchorForm,
Selection */

@@ -73,5 +74,63 @@ describe('Anchor Button TestCase', function () {

expect(editor.createLink).toHaveBeenCalled();
expect(this.el.innerHTML).toBe('<a href="test">lorem ipsum</a>');
// A trailing <br> may be added when insertHTML is used to add the link internally.
expect(this.el.innerHTML.indexOf('<a href="test">lorem ipsum</a>')).toBe(0);
});
it('should create only one anchor tag when the user selects across a boundary', function () {
this.el.innerHTML = 'Hello world, this <strong>will become a link, but this part won\'t.</strong>';
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
button, input;
Selection.select(document, this.el.childNodes[0], 'Hello world, '.length,
this.el.childNodes[1].childNodes[0], 'will become a link'.length);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
input = editor.getExtensionByName('anchor').getInput();
input.value = 'test';
fireEvent(input, 'keyup', {
keyCode: Util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalled();
var suffix;
if (this.el.innerHTML.indexOf('<br') !== -1) {
suffix = '<br>';
} else {
suffix = '<strong></strong>';
}
expect(this.el.innerHTML).toBe('Hello world, <a href="test">this <strong>will become a link</strong></a>' +
'<strong>, but this part won\'t.</strong>' + suffix);
});
it('should create a link when the user selects text within two paragraphs', function () {
this.el.innerHTML = '<p>Hello <span>world</span>.</p>' +
'<p><strong>Let us make a link</strong> across paragraphs.</p>';
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
button, input;
Selection.select(document, this.el.querySelector('span'), 0,
this.el.querySelector('strong'), 1);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
input = editor.getExtensionByName('anchor').getInput();
input.value = 'test';
fireEvent(input, 'keyup', {
keyCode: Util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalled();
var expectedHTML = '<p>Hello <a href="test"><span>world</span>.</a></p>';
// Different browser's native createLink implement this differently.
if (this.el.innerHTML.indexOf('<strong><a') !== -1) {
expectedHTML += '<p><strong><a href="test">Let us make a link</a></strong> across paragraphs.</p>';
} else {
expectedHTML += '<p><a href="test"><strong>Let us make a link</strong></a> across paragraphs.</p>';
}
expect(this.el.innerHTML).toBe(expectedHTML);
});
it('shouldn\'t create a link when user presses enter without value', function () {

@@ -78,0 +137,0 @@ spyOn(MediumEditor.prototype, 'createLink').and.callThrough();

@@ -110,3 +110,2 @@ /*global MediumEditor, describe, it, expect, spyOn,

ownerDocument: document,
allowMultiParagraphSelection: true,
buttonLabels: false,

@@ -113,0 +112,0 @@ targetBlank: false,

@@ -376,3 +376,3 @@ /*global MediumEditor, describe, it, expect, spyOn,

it('should hide the toolbar when selecting multiple paragraphs and the allowMultiParagraphSelection option is false', function () {
it('should hide the toolbar when selecting multiple paragraphs and the deprecated allowMultiParagraphSelection option is false', function () {
this.el.innerHTML = '<p id="p-one">lorem ipsum</p><p id="p-two">lorem ipsum</p>';

@@ -379,0 +379,0 @@ var editor = this.newMediumEditor('.editor', {

@@ -206,2 +206,16 @@ /*global MediumEditor, describe, it, expect, spyOn,

});
it('should hide the toolbar when selecting multiple paragraphs and the allowMultiParagraphSelection option is false', function () {
this.el.innerHTML = '<p id="p-one">lorem ipsum</p><p id="p-two">lorem ipsum</p>';
var editor = this.newMediumEditor('.editor', {
toolbar: {
allowMultiParagraphSelection: false
}
}),
toolbar = editor.getExtensionByName('toolbar');
selectElementContentsAndFire(document.getElementById('p-one'), { eventToFire: 'focus' });
expect(toolbar.getToolbarElement().classList.contains('medium-editor-toolbar-active')).toBe(true);
selectElementContentsAndFire(this.el, { eventToFire: 'mouseup' });
expect(toolbar.getToolbarElement().classList.contains('medium-editor-toolbar-active')).toBe(false);
});
});

@@ -208,0 +222,0 @@

@@ -407,3 +407,7 @@ /*global Util, Selection, Extension,

if (!toolbarExtension && isToolbarEnabled.call(this)) {
toolbarExtension = new MediumEditor.extensions.toolbar(this.options.toolbar);
// Backwards compatability
var toolbarOptions = Util.extend({}, this.options.toolbar, {
allowMultiParagraphSelection: this.options.allowMultiParagraphSelection // deprecated
});
toolbarExtension = new MediumEditor.extensions.toolbar(toolbarOptions);
}

@@ -420,3 +424,3 @@

var deprecatedProperties = [
// ['forcePlainText', 'paste.forcePlainText'],
['allowMultiParagraphSelection', 'toolbar.allowMultiParagraphSelection']
];

@@ -577,2 +581,4 @@ // warn about using deprecated properties

this.events.destroy();
this.elements.forEach(function (element) {

@@ -587,3 +593,2 @@ // Reset elements content, fix for issue where after editor destroyed the red underlines on spelling errors are left

element.removeAttribute('spellcheck');
element.removeAttribute('data-medium-focused');
element.removeAttribute('data-medium-editor-element');

@@ -608,3 +613,2 @@ element.removeAttribute('role');

this.events.destroy();
removeFromEditors.call(this, this.options.contentWindow);

@@ -994,10 +998,70 @@ },

if (opts.url && opts.url.trim().length > 0) {
this.options.ownerDocument.execCommand('createLink', false, opts.url);
var currentSelection = this.options.contentWindow.getSelection();
if (currentSelection) {
var exportedSelection,
startContainerParentElement,
endContainerParentElement,
textNodes;
if (this.options.targetBlank || opts.target === '_blank') {
Util.setTargetBlank(Selection.getSelectionStart(this.options.ownerDocument), opts.url);
}
startContainerParentElement = Util.getClosestBlockContainer(
currentSelection.getRangeAt(0).startContainer);
endContainerParentElement = Util.getClosestBlockContainer(
currentSelection.getRangeAt(0).endContainer);
if (opts.buttonClass) {
Util.addClassToAnchors(Selection.getSelectionStart(this.options.ownerDocument), opts.buttonClass);
if (startContainerParentElement === endContainerParentElement) {
var currentEditor = Selection.getSelectionElement(this.options.contentWindow),
parentElement = (startContainerParentElement || currentEditor),
fragment = this.options.ownerDocument.createDocumentFragment();
exportedSelection = this.exportSelection();
fragment.appendChild(parentElement.cloneNode(true));
if (currentEditor === parentElement) {
// We have to avoid the editor itself being wiped out when it's the only block element,
// as our reference inside this.elements gets detached from the page when insertHTML runs.
// If we just use [parentElement, 0] and [parentElement, parentElement.childNodes.length]
// as the range boundaries, this happens whenever parentElement === currentEditor.
// The tradeoff to this workaround is that a orphaned tag can sometimes be left behind at
// the end of the editor's content.
// In Gecko:
// as an empty <strong></strong> if parentElement.lastChild is a <strong> tag.
// In WebKit:
// an invented <br /> tag at the end in the same situation
Selection.select(this.options.ownerDocument,
parentElement.firstChild, 0,
parentElement.lastChild, parentElement.lastChild.nodeType === 3 ?
parentElement.lastChild.nodeValue.length : parentElement.lastChild.childNodes.length);
} else {
Selection.select(this.options.ownerDocument,
parentElement, 0,
parentElement, parentElement.childNodes.length);
}
var modifiedExportedSelection = this.exportSelection();
textNodes = Util.findOrCreateMatchingTextNodes(this.options.ownerDocument,
fragment,
{
start: exportedSelection.start - modifiedExportedSelection.start,
end: exportedSelection.end - modifiedExportedSelection.start,
editableElementIndex: exportedSelection.editableElementIndex
});
// Creates the link in the document fragment
Util.createLink(this.options.ownerDocument, textNodes, opts.url.trim());
// Chrome trims the leading whitespaces when inserting HTML, which messes up restoring the selection.
var leadingWhitespacesCount = (fragment.firstChild.innerHTML.match(/^\s+/) || [''])[0].length;
// Now move the created link back into the original document in a way to preserve undo/redo history
Util.insertHTMLCommand(this.options.ownerDocument,
fragment.firstChild.innerHTML.replace(/^\s+/, ''));
exportedSelection.start -= leadingWhitespacesCount;
exportedSelection.end -= leadingWhitespacesCount;
this.importSelection(exportedSelection);
} else {
this.options.ownerDocument.execCommand('createLink', false, opts.url);
}
if (this.options.targetBlank || opts.target === '_blank') {
Util.setTargetBlank(Selection.getSelectionStart(this.options.ownerDocument), opts.url);
}
if (opts.buttonClass) {
Util.addClassToAnchors(Selection.getSelectionStart(this.options.ownerDocument), opts.buttonClass);
}
}

@@ -1004,0 +1068,0 @@ }

@@ -7,3 +7,2 @@ var editorDefaults;

activeButtonClass: 'medium-editor-button-active',
allowMultiParagraphSelection: true,
buttonLabels: false,

@@ -10,0 +9,0 @@ delay: 0,

@@ -98,2 +98,8 @@ /*global Util*/

this.detachExecCommand();
if (this.base.elements) {
this.base.elements.forEach(function (element) {
element.removeAttribute('data-medium-focused');
});
}
},

@@ -242,29 +248,19 @@

// Detecting click in the contenteditables
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, 'click', this.handleClick.bind(this));
}.bind(this));
this.attachToEachElement('click', this.handleClick);
break;
case 'editableBlur':
// Detecting blur in the contenteditables
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, 'blur', this.handleBlur.bind(this));
}.bind(this));
this.attachToEachElement('blur', this.handleBlur);
break;
case 'editableKeypress':
// Detecting keypress in the contenteditables
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, 'keypress', this.handleKeypress.bind(this));
}.bind(this));
this.attachToEachElement('keypress', this.handleKeypress);
break;
case 'editableKeyup':
// Detecting keyup in the contenteditables
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, 'keyup', this.handleKeyup.bind(this));
}.bind(this));
this.attachToEachElement('keyup', this.handleKeyup);
break;
case 'editableKeydown':
// Detecting keydown on the contenteditables
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, 'keydown', this.handleKeydown.bind(this));
}.bind(this));
this.attachToEachElement('keydown', this.handleKeydown);
break;

@@ -285,24 +281,16 @@ case 'editableKeydownEnter':

// Detecting mouseover on the contenteditables
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, 'mouseover', this.handleMouseover.bind(this));
}, this);
this.attachToEachElement('mouseover', this.handleMouseover);
break;
case 'editableDrag':
// Detecting dragover and dragleave on the contenteditables
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, 'dragover', this.handleDragging.bind(this));
this.attachDOMEvent(element, 'dragleave', this.handleDragging.bind(this));
}, this);
this.attachToEachElement('dragover', this.handleDragging);
this.attachToEachElement('dragleave', this.handleDragging);
break;
case 'editableDrop':
// Detecting drop on the contenteditables
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, 'drop', this.handleDrop.bind(this));
}, this);
this.attachToEachElement('drop', this.handleDrop);
break;
case 'editablePaste':
// Detecting paste on the contenteditables
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, 'paste', this.handlePaste.bind(this));
}, this);
this.attachToEachElement('paste', this.handlePaste);
break;

@@ -313,2 +301,8 @@ }

attachToEachElement: function (name, handler) {
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, name, handler.bind(this));
}, this);
},
focusElement: function (element) {

@@ -315,0 +309,0 @@ element.focus();

@@ -166,5 +166,5 @@ var AnchorPreview;

// only show when hovering on anchors
if (this.base.toolbar && this.base.toolbar.isDisplayed()) {
// only show when toolbar is not present
// only show when toolbar is not present
var toolbar = this.base.getExtensionByName('toolbar');
if (toolbar && toolbar.isDisplayed && toolbar.isDisplayed()) {
return true;

@@ -171,0 +171,0 @@ }

@@ -100,24 +100,2 @@ /*global Extension, Util */

splitStartNodeIfNeeded: function (currentNode, matchStartIndex, currentTextIndex) {
if (matchStartIndex !== currentTextIndex) {
return currentNode.splitText(matchStartIndex - currentTextIndex);
}
return null;
},
splitEndNodeIfNeeded: function (currentNode, newNode, matchEndIndex, currentTextIndex) {
var textIndexOfEndOfFarthestNode,
endSplitPoint;
textIndexOfEndOfFarthestNode = currentTextIndex + (newNode || currentNode).nodeValue.length +
(newNode ? currentNode.nodeValue.length : 0) -
1;
endSplitPoint = (newNode || currentNode).nodeValue.length -
(textIndexOfEndOfFarthestNode + 1 - matchEndIndex);
if (textIndexOfEndOfFarthestNode >= matchEndIndex &&
currentTextIndex !== textIndexOfEndOfFarthestNode &&
endSplitPoint !== 0) {
(newNode || currentNode).splitText(endSplitPoint);
}
},
removeObsoleteAutoLinkSpans: function (element) {

@@ -180,4 +158,8 @@ var spans = element.querySelectorAll('span[data-auto-link="true"]'),

for (var matchIndex = 0; matchIndex < matches.length; matchIndex++) {
linkCreated = this.createLink(this.findOrCreateMatchingTextNodes(element, matches[matchIndex]),
matches[matchIndex].href) || linkCreated;
var matchingTextNodes = Util.findOrCreateMatchingTextNodes(this.document, element,
matches[matchIndex]);
if (this.shouldNotLink(matchingTextNodes)) {
continue;
}
this.createAutoLink(matchingTextNodes, matches[matchIndex].href);
}

@@ -187,2 +169,14 @@ return linkCreated;

shouldNotLink: function (textNodes) {
var shouldNotLink = false;
for (var i = 0; i < textNodes.length && shouldNotLink === false; i++) {
// Do not link if the text node is either inside an anchor or inside span[data-auto-link]
shouldNotLink = !!Util.traverseUp(textNodes[i], function (node) {
return node.nodeName.toLowerCase() === 'a' ||
(node.getAttribute && node.getAttribute('data-auto-link') === 'true');
});
}
return shouldNotLink;
},
findLinkableText: function (contenteditable) {

@@ -215,64 +209,15 @@ var linkRegExp = new RegExp(LINK_REGEXP_TEXT, 'gi'),

findOrCreateMatchingTextNodes: function (element, match) {
var treeWalker = this.document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false),
matchedNodes = [],
currentTextIndex = 0,
startReached = false,
currentNode = null,
newNode = null;
while ((currentNode = treeWalker.nextNode()) !== null) {
if (!startReached && match.start < (currentTextIndex + currentNode.nodeValue.length)) {
startReached = true;
newNode = this.splitStartNodeIfNeeded(currentNode, match.start, currentTextIndex);
}
if (startReached) {
this.splitEndNodeIfNeeded(currentNode, newNode, match.end, currentTextIndex);
}
if (startReached && currentTextIndex === match.end) {
break; // Found the node(s) corresponding to the link. Break out and move on to the next.
} else if (startReached && currentTextIndex > (match.end + 1)) {
throw new Error('PerformLinking overshot the target!'); // should never happen...
}
if (startReached) {
matchedNodes.push(newNode || currentNode);
}
currentTextIndex += currentNode.nodeValue.length;
if (newNode !== null) {
currentTextIndex += newNode.nodeValue.length;
// Skip the newNode as we'll already have pushed it to the matches
treeWalker.nextNode();
}
newNode = null;
createAutoLink: function (textNodes, href) {
href = Util.ensureUrlHasProtocol(href);
var anchor = Util.createLink(this.document, textNodes, href),
span = this.document.createElement('span');
span.setAttribute('data-auto-link', 'true');
span.setAttribute('data-href', href);
anchor.insertBefore(span, anchor.firstChild);
while (anchor.childNodes.length > 1) {
span.appendChild(anchor.childNodes[1]);
}
return matchedNodes;
},
}
createLink: function (textNodes, href) {
var shouldNotLink = false;
for (var i = 0; i < textNodes.length && shouldNotLink === false; i++) {
// Do not link if the text node is either inside an anchor or inside span[data-auto-link]
shouldNotLink = !!Util.traverseUp(textNodes[i], function (node) {
return node.nodeName.toLowerCase() === 'a' ||
(node.getAttribute && node.getAttribute('data-auto-link') === 'true');
});
}
if (shouldNotLink) {
return false;
}
var anchor = this.document.createElement('a'),
span = this.document.createElement('span'),
hrefWithProtocol = Util.ensureUrlHasProtocol(href);
Util.moveTextRangeIntoElement(textNodes[0], textNodes[textNodes.length - 1], span);
span.setAttribute('data-auto-link', 'true');
span.setAttribute('data-href', hrefWithProtocol);
anchor.setAttribute('href', hrefWithProtocol);
span.parentNode.insertBefore(anchor, span);
anchor.appendChild(span);
return true;
}
});
}());

@@ -18,2 +18,8 @@ var Toolbar;

/* allowMultiParagraphSelection: [boolean]
* enables/disables whether the toolbar should be displayed when
* selecting multiple paragraphs/block elements
*/
allowMultiParagraphSelection: true,
/* buttons: [Array]

@@ -326,6 +332,6 @@ * the names of the set of buttons to display on the toolbar.

multipleBlockElementsSelected: function () {
/*jslint regexp: true*/
var selectionHtml = Selection.getSelectionHtml(this.document).replace(/<[\S]+><\/[\S]+>/gim, ''),
hasMultiParagraphs = selectionHtml.match(/<(p|h[1-6]|blockquote)[^>]*>/g);
/*jslint regexp: false*/
var regexEmptyHTMLTags = /<[^\/>][^>]*><\/[^>]+>/gim, // http://stackoverflow.com/questions/3129738/remove-empty-tags-using-regex
regexBlockElements = new RegExp('<(' + Util.blockContainerElementNames.join('|') + ')[^>]*>', 'g'),
selectionHTML = Selection.getSelectionHtml(this.document).replace(regexEmptyHTMLTags, ''), // Filter out empty blocks from selection
hasMultiParagraphs = selectionHTML.match(regexBlockElements); // Find how many block elements are within the html

@@ -364,8 +370,4 @@ return !!hasMultiParagraphs && hasMultiParagraphs.length > 1;

}
var newRange = this.document.createRange();
newRange.setStart(adjacentNode, offset);
newRange.setEnd(selectionRange.endContainer, selectionRange.endOffset);
selection.removeAllRanges();
selection.addRange(newRange);
selectionRange = newRange;
selectionRange = Selection.select(this.document, adjacentNode, offset,
selectionRange.endContainer, selectionRange.offset);
}

@@ -406,3 +408,3 @@ }

if (this.window.getSelection().toString().trim() === '' ||
(this.getEditorOption('allowMultiParagraphSelection') === false && this.multipleBlockElementsSelected())) {
(this.allowMultiParagraphSelection === false && this.multipleBlockElementsSelected())) {
return this.hideToolbar();

@@ -409,0 +411,0 @@ }

@@ -236,2 +236,15 @@ /*global Util */

select: function (doc, startNode, startOffset, endNode, endOffset) {
doc.getSelection().removeAllRanges();
var range = doc.createRange();
range.setStart(startNode, startOffset);
if (endNode) {
range.setEnd(endNode, endOffset);
} else {
range.collapse(true);
}
doc.getSelection().addRange(range);
return range;
},
/**

@@ -245,13 +258,3 @@ * Move cursor to the given node with the given offset.

moveCursor: function (doc, node, offset) {
var range, sel,
startOffset = offset || 0;
range = doc.createRange();
sel = doc.getSelection();
range.setStart(node, startOffset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
this.select(doc, node, offset);
},

@@ -258,0 +261,0 @@

@@ -104,2 +104,95 @@ /*global NodeFilter, Selection*/

/*
* Create a link around the provided text nodes which must be adjacent to each other and all be
* descendants of the same closest block container. If the preconditions are not met, unexpected
* behavior will result.
*/
createLink: function (document, textNodes, href) {
var anchor = document.createElement('a');
Util.moveTextRangeIntoElement(textNodes[0], textNodes[textNodes.length - 1], anchor);
anchor.setAttribute('href', href);
return anchor;
},
/*
* Given the provided match in the format {start: 1, end: 2} where start and end are indices into the
* textContent of the provided element argument, modify the DOM inside element to ensure that the text
* identified by the provided match can be returned as text nodes that contain exactly that text, without
* any additional text at the beginning or end of the returned array of adjacent text nodes.
*
* The only DOM manipulation performed by this function is splitting the text nodes, non-text nodes are
* not affected in any way.
*/
findOrCreateMatchingTextNodes: function (document, element, match) {
var treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false),
matchedNodes = [],
currentTextIndex = 0,
startReached = false,
currentNode = null,
newNode = null;
while ((currentNode = treeWalker.nextNode()) !== null) {
if (!startReached && match.start < (currentTextIndex + currentNode.nodeValue.length)) {
startReached = true;
newNode = Util.splitStartNodeIfNeeded(currentNode, match.start, currentTextIndex);
}
if (startReached) {
Util.splitEndNodeIfNeeded(currentNode, newNode, match.end, currentTextIndex);
}
if (startReached && currentTextIndex === match.end) {
break; // Found the node(s) corresponding to the link. Break out and move on to the next.
} else if (startReached && currentTextIndex > (match.end + 1)) {
throw new Error('PerformLinking overshot the target!'); // should never happen...
}
if (startReached) {
matchedNodes.push(newNode || currentNode);
}
currentTextIndex += currentNode.nodeValue.length;
if (newNode !== null) {
currentTextIndex += newNode.nodeValue.length;
// Skip the newNode as we'll already have pushed it to the matches
treeWalker.nextNode();
}
newNode = null;
}
return matchedNodes;
},
/*
* Given the provided text node and text coordinates, split the text node if needed to make it align
* precisely with the coordinates.
*
* This function is intended to be called from Util.findOrCreateMatchingTextNodes.
*/
splitStartNodeIfNeeded: function (currentNode, matchStartIndex, currentTextIndex) {
if (matchStartIndex !== currentTextIndex) {
return currentNode.splitText(matchStartIndex - currentTextIndex);
}
return null;
},
/*
* Given the provided text node and text coordinates, split the text node if needed to make it align
* precisely with the coordinates. The newNode argument should from the result of Util.splitStartNodeIfNeeded,
* if that function has been called on the same currentNode.
*
* This function is intended to be called from Util.findOrCreateMatchingTextNodes.
*/
splitEndNodeIfNeeded: function (currentNode, newNode, matchEndIndex, currentTextIndex) {
var textIndexOfEndOfFarthestNode,
endSplitPoint;
textIndexOfEndOfFarthestNode = currentTextIndex + (newNode || currentNode).nodeValue.length +
(newNode ? currentNode.nodeValue.length : 0) -
1;
endSplitPoint = (newNode || currentNode).nodeValue.length -
(textIndexOfEndOfFarthestNode + 1 - matchEndIndex);
if (textIndexOfEndOfFarthestNode >= matchEndIndex &&
currentTextIndex !== textIndexOfEndOfFarthestNode &&
endSplitPoint !== 0) {
(newNode || currentNode).splitText(endSplitPoint);
}
},
// Find the next node in the DOM tree that represents any text that is being

@@ -639,2 +732,8 @@ // displayed directly next to the targetNode (passed as an argument)

getClosestBlockContainer: function (node) {
return Util.traverseUp(node, function (node) {
return Util.isBlockContainer(node);
});
},
getTopBlockContainer: function (element) {

@@ -641,0 +740,0 @@ var topBlock = element;

@@ -20,3 +20,3 @@ /*global MediumEditor */

// grunt-bump looks for this:
'version': '5.1.0'
'version': '5.2.0'
}).version);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc