Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

medium-editor

Package Overview
Dependencies
Maintainers
6
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.14.0 to 5.14.1

.node-version

7

bower.json

@@ -34,5 +34,8 @@ {

"src",
"README.md",
"CHANGES.md"
"CHANGES.md",
"RELEASE-PROCESS.md",
"CODE_OF_CONDUCT.md",
"CONTRIBUTING.md",
"UPGRADE-5.md"
]
}

@@ -0,1 +1,10 @@

5.14.1 / 2016-02-05
==================
* Fix issue with saving selection after newline and whitespace text nodes
* Fix import/export selection to prefer start of nodes over end of nodes
* Fix for getClosestBlockContainer utility function
* Fix for getTopBlockContainer utility function
* Deprecate getFirstTextNode utility function
5.14.0 / 2016-01-31

@@ -2,0 +11,0 @@ ==================

{
"name": "medium-editor",
"version": "5.14.0",
"version": "5.14.1",
"author": "Davi Ferreira <hi@daviferreira.com>",

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

@@ -67,2 +67,6 @@ /*global atob, unescape, Uint8Array, Blob*/

function isSafari() {
return navigator.userAgent.toLowerCase().indexOf('safari') !== -1;
}
function dataURItoBlob(dataURI) {

@@ -69,0 +73,0 @@ // convert base64/URLEncoded data component to raw binary data held in a string

@@ -1,2 +0,2 @@

/*global selectElementContents, placeCursorInsideElement */
/*global selectElementContents, placeCursorInsideElement, isSafari */

@@ -160,2 +160,3 @@ describe('MediumEditor.selection TestCase', function () {

placeCursorInsideElement(link.childNodes[0], link.childNodes[0].nodeValue.length);
expect(MediumEditor.util.isDescendant(link, window.getSelection().getRangeAt(0).startContainer, true)).toBe(true);

@@ -166,2 +167,9 @@ var exportedSelection = MediumEditor.selection.exportSelection(this.el, document);

node = range.startContainer;
// For some reason, Safari mucks with the selection range and makes this case not hold
// since we only really care about whether this works in IE, and it works as expected
// in other browsers, just skip this assertion for Safari
if (!isSafari()) {
expect(MediumEditor.util.isDescendant(link, node, true)).toBe(false);
}
// Even though we set the range to use the P tag as the start container, Safari normalizes the range

@@ -331,3 +339,3 @@ // down to the text node. Setting the range to use the P tag for the start is necessary to support

var selectionData = MediumEditor.selection.exportSelection(this.el, document);
expect(selectionData.emptyBlocksIndex).toBe(0);
expect(selectionData.emptyBlocksIndex).toBeUndefined();

@@ -344,3 +352,3 @@ MediumEditor.selection.importSelection(selectionData, this.el, document);

this.el.innerHTML = '<p>lorem ipsum <a href="#"><img src="../demo/img/medium-editor.jpg" /></a> dolor</p>';
MediumEditor.selection.importSelection({ start: 12, end: 12, trailingImageCount: 1 }, this.el, document);
MediumEditor.selection.importSelection({ start: 12, end: 12, startsWithImage: true, trailingImageCount: 1 }, this.el, document);
var range = window.getSelection().getRangeAt(0);

@@ -353,3 +361,3 @@ expect(range.toString()).toBe('');

this.el.innerHTML = '<p>lorem ipsum <a href="#"><img src="../demo/img/medium-editor.jpg" />img</a> dolor</p>';
MediumEditor.selection.importSelection({ start: 12, end: 15 }, this.el, document);
MediumEditor.selection.importSelection({ start: 12, end: 15, startsWithImage: true }, this.el, document);
var range = window.getSelection().getRangeAt(0);

@@ -373,2 +381,35 @@ expect(range.toString()).toBe('img');

});
// https://github.com/yabwe/medium-editor/issues/935
it('should support a selection that is after white-space at the beginning of a paragraph', function () {
this.el.innerHTML = ' <p>one two<br><a href="transindex.hu">three</a><br></p><p><a href="amazon.com">one</a> two three</p>';
this.newMediumEditor(this.el);
var firstText = this.el.querySelector('p').firstChild;
MediumEditor.selection.select(document, firstText, 0, firstText, 'one'.length);
var exported = MediumEditor.selection.exportSelection(this.el, document);
MediumEditor.selection.importSelection(exported, this.el, document);
var range = window.getSelection().getRangeAt(0);
expect(range.toString()).toBe('one');
});
it('should support importing a collapsed selection at the end of all content', function () {
this.el.innerHTML = '<p>lorem ipsum <b>dolor</b></p>';
var boldText = this.el.querySelector('b').firstChild;
placeCursorInsideElement(boldText, boldText.length);
var range = window.getSelection().getRangeAt(0);
expect(range.collapsed).toBe(true);
expect(MediumEditor.util.isDescendant(boldText.parentNode, range.startContainer, true)).toBe(true);
expect(MediumEditor.util.isDescendant(boldText.parentNode, range.endContainer, true)).toBe(true);
var exported = MediumEditor.selection.exportSelection(this.el, document);
expect(exported.start).toBe('lorem ipsum dolor'.length);
expect(exported.end).toBe('lorem ipsum dolor'.length);
MediumEditor.selection.importSelection(exported, this.el, document);
range = window.getSelection().getRangeAt(0);
expect(range.collapsed).toBe(true);
expect(MediumEditor.util.isDescendant(boldText.parentNode, range.startContainer, true)).toBe(true);
expect(MediumEditor.util.isDescendant(boldText.parentNode, range.endContainer, true)).toBe(true);
});
});

@@ -375,0 +416,0 @@

@@ -509,2 +509,62 @@ /*global selectElementContents*/

describe('getClosestBlockContainer', function () {
it('should return the closest block container', function () {
var el = this.createElement('div', '', '<blockquote><p>paragraph</p><ul><li><span>list item</span></li></ul></blockquote>'),
span = el.querySelector('span'),
container = MediumEditor.util.getClosestBlockContainer(span);
expect(container).toBe(el.querySelector('li'));
});
it('should return the parent editable if element is a text node child of the editor', function () {
var el = this.createElement('div', 'editable', ' <p>text</p>'),
emptyTextNode = el.firstChild;
this.newMediumEditor('.editable');
var container = MediumEditor.util.getClosestBlockContainer(emptyTextNode);
expect(container).toBe(el);
});
});
describe('getTopBlockContainer', function () {
it('should return the highest level block container', function () {
var el = this.createElement('div', '', '<blockquote><p>paragraph</p><ul><li><span>list item</span></li></ul></blockquote>'),
span = el.querySelector('span'),
container = MediumEditor.util.getTopBlockContainer(span);
expect(container).toBe(el.querySelector('blockquote'));
});
it('should return the parent editable if element is a text node child of the editor', function () {
var el = this.createElement('div', 'editable', ' <p>text</p>'),
emptyTextNode = el.firstChild;
this.newMediumEditor('.editable');
var container = MediumEditor.util.getTopBlockContainer(emptyTextNode);
expect(container).toBe(el);
});
});
describe('findPreviousSibling', function () {
it('should return the previous sibling of an element if it exists', function () {
var el = this.createElement('div', '', '<p>first <b>second </b><i>third</i></p><ul><li>fourth</li></ul>'),
second = el.querySelector('b'),
third = el.querySelector('i'),
prevSibling = MediumEditor.util.findPreviousSibling(third);
expect(prevSibling).toBe(second);
});
it('should return the previous sibling on an ancestor if a previous sibling does not exist', function () {
var el = this.createElement('div', '', '<p>first <b>second </b><i>third</i></p><ul><li>fourth</li></ul>'),
fourth = el.querySelector('li').firstChild,
p = el.querySelector('p'),
prevSibling = MediumEditor.util.findPreviousSibling(fourth);
expect(prevSibling).toBe(p);
});
it('should not find a previous sibling if the element is at the beginning of an editor element', function () {
var el = this.createElement('div', 'editable', '<p>first <b>second </b><i>third</i></p><ul><li>fourth</li></ul>'),
first = el.querySelector('p').firstChild;
this.newMediumEditor('.editable');
var prevSibling = MediumEditor.util.findPreviousSibling(first);
expect(prevSibling).toBeFalsy();
});
});
describe('findOrCreateMatchingTextNodes', function () {

@@ -581,2 +641,28 @@ it('should return text nodes within an element', function () {

});
// TODO: Remove these tests when getFirstTextNode is deprecated in 6.0.0
describe('getFirstTextNode', function () {
it('should find the first text node within an element', function () {
var el = this.createElement('div', '', '<p><b><i><u><a href="#">First</a> text</u> in</i> editor</b>!</p>'),
anchorText = el.querySelector('a').firstChild,
firstText = MediumEditor.util.getFirstTextNode(el);
expect(firstText).toBe(anchorText);
});
it('should return the text node if passed a text node', function () {
var el = this.createElement('div', '', '<p>text</p>'),
textNode = el.querySelector('p').firstChild,
firstText = MediumEditor.util.getFirstTextNode(textNode);
expect(firstText).toBe(textNode);
});
it('should return null if no text node exists in element', function () {
var el = this.createElement('div'),
firstText = MediumEditor.util.getFirstTextNode(el);
expect(firstText).toBeNull();
});
});
});

@@ -58,10 +58,16 @@ (function () {

// Range contains an image, check to see if the selection ends with that image
if (range.endOffset !== 0 && (range.endContainer.nodeName.toLowerCase() === 'img' || (range.endContainer.nodeType === 1 && range.endContainer.querySelector('img')))) {
var trailingImageCount = this.getTrailingImageCount(root, selectionState, range.endContainer, range.endOffset);
if (trailingImageCount) {
selectionState.trailingImageCount = trailingImageCount;
}
// 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, doc)) {
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
var 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.

@@ -105,4 +111,23 @@ if (start !== 0) {

stop = false,
nextCharIndex;
nextCharIndex,
allowRangeToStartAtEndOfNode = false,
lastTextNode = null;
// When importing selection, the start of the selection may lie at the end of an element
// or at the beginning of an element. Since visually there is no difference between these 2
// we will try to move the selection to the beginning of an element since this is generally
// what users will expect and it's a more predictable behavior.
//
// However, there are some specific cases when we don't want to do this:
// 1) We're attempting to move the cursor outside of the end of an anchor [favorLaterSelectionAnchor = true]
// 2) The selection starts with an image, which is special since an image doesn't have any 'content'
// as far as selection and ranges are concerned
// 3) The selection starts after a specified number of empty block elements (selectionState.emptyBlocksIndex)
//
// For these cases, we want the selection to start at a very specific location, so we should NOT
// automatically move the cursor to the beginning of the first actual chunk of text
if (favorLaterSelectionAnchor || selectionState.startsWithImage || typeof selectionState.emptyBlocksIndex !== 'undefined') {
allowRangeToStartAtEndOfNode = true;
}
while (!stop && node) {

@@ -118,6 +143,19 @@ // Only iterate over elements and text nodes

nextCharIndex = charIndex + node.length;
// Check if we're at or beyond the start of the selection we're importing
if (!foundStart && selectionState.start >= charIndex && selectionState.start <= nextCharIndex) {
range.setStart(node, selectionState.start - charIndex);
foundStart = true;
// NOTE: We only want to allow a selection to start at the END of an element if
// allowRangeToStartAtEndOfNode is true
if (allowRangeToStartAtEndOfNode || selectionState.start < nextCharIndex) {
range.setStart(node, selectionState.start - charIndex);
foundStart = true;
}
// We're at the end of a text node where the selection could start but we shouldn't
// make the selection start here because allowRangeToStartAtEndOfNode is false.
// However, we should keep a reference to this node in case there aren't any more
// text nodes after this, so that we have somewhere to import the selection to
else {
lastTextNode = node;
}
}
// We've found the start of the selection, check if we're at or beyond the end of the selection we're importing
if (foundStart && selectionState.end >= charIndex && selectionState.end <= nextCharIndex) {

@@ -164,2 +202,10 @@ if (!selectionState.trailingImageCount) {

// If we've gone through the entire text but didn't find the beginning of a text node
// to make the selection start at, we should fall back to starting the selection
// at the END of the last text node we found
if (!foundStart && lastTextNode) {
range.setStart(lastTextNode, lastTextNode.length);
range.setEnd(lastTextNode, lastTextNode.length);
}
if (typeof selectionState.emptyBlocksIndex !== 'undefined') {

@@ -253,2 +299,6 @@ range = this.importSelectionMoveCursorPastBlocks(doc, root, selectionState.emptyBlocksIndex, range);

if (!targetNode) {
targetNode = startBlock;
}
// We're selecting a high-level block node, so make sure the cursor gets moved into the deepest

@@ -277,4 +327,17 @@ // element at the beginning of the block

}
if (node && !MediumEditor.util.isElementAtBeginningOfBlock(node)) {
return -1;
if (node) {
// The element isn't at the beginning of a block, so it has content before it
if (!MediumEditor.util.isElementAtBeginningOfBlock(node)) {
return -1;
}
var previousSibling = MediumEditor.util.findPreviousSibling(node);
// If there is no previous sibling, this is the first text element in the editor
if (!previousSibling) {
return -1;
}
// If the previous sibling has text, then there are no empty blocks before this
else if (previousSibling.nodeValue) {
return -1;
}
}

@@ -303,3 +366,49 @@

// Returns true if the selection range begins with an image tag
// Returns false if the range starts with any non empty text nodes
doesRangeStartWithImages: function (range, doc) {
if (range.startOffset !== 0 || range.startContainer.nodeType !== 1) {
return false;
}
if (range.startContainer.nodeName.toLowerCase() === 'img') {
return true;
}
var img = range.startContainer.querySelector('img');
if (!img) {
return false;
}
var treeWalker = doc.createTreeWalker(range.startContainer, NodeFilter.SHOW_ALL, null, false);
while (treeWalker.nextNode()) {
var 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: function (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;
}
var lastNode = endContainer.childNodes[endOffset - 1];

@@ -363,3 +472,3 @@ while (lastNode.hasChildNodes()) {

// determine if the current selection contains any 'content'
// content being and non-white space text or an image
// content being any non-white space text or an image
selectionContainsContent: function (doc) {

@@ -366,0 +475,0 @@ var sel = doc.getSelection();

@@ -319,2 +319,18 @@ /*global NodeFilter*/

// Find an element's previous sibling within a medium-editor element
// If one doesn't exist, find the closest ancestor's previous sibling
findPreviousSibling: function (node) {
if (!node || Util.isMediumEditorElement(node)) {
return false;
}
var previousSibling = node.previousSibling;
while (!previousSibling && !Util.isMediumEditorElement(node.parentNode)) {
node = node.parentNode;
previousSibling = node.previousSibling;
}
return previousSibling;
},
isDescendant: function isDescendant(parent, child, checkEquality) {

@@ -868,10 +884,18 @@ if (!parent || !child) {

/* Finds the closest ancestor which is a block container element
* If element is within editor element but not within any other block element,
* the editor element is returned
*/
getClosestBlockContainer: function (node) {
return Util.traverseUp(node, function (node) {
return Util.isBlockContainer(node);
return Util.isBlockContainer(node) || Util.isMediumEditorElement(node);
});
},
/* Finds highest level ancestor element which is a block container element
* If element is within editor element but not within any other block element,
* the editor element is returned
*/
getTopBlockContainer: function (element) {
var topBlock = element;
var topBlock = Util.isBlockContainer(element) ? element : false;
Util.traverseUp(element, function (el) {

@@ -881,2 +905,6 @@ if (Util.isBlockContainer(el)) {

}
if (!topBlock && Util.isMediumEditorElement(el)) {
topBlock = el;
return true;
}
return false;

@@ -906,3 +934,9 @@ });

// TODO: remove getFirstTextNode AND _getFirstTextNode when jumping in 6.0.0 (no code references)
getFirstTextNode: function (element) {
Util.warn('getFirstTextNode is deprecated and will be removed in version 6.0.0');
return Util._getFirstTextNode(element);
},
_getFirstTextNode: function (element) {
if (element.nodeType === 3) {

@@ -913,3 +947,3 @@ return element;

for (var i = 0; i < element.childNodes.length; i++) {
var textNode = Util.getFirstTextNode(element.childNodes[i]);
var textNode = Util._getFirstTextNode(element.childNodes[i]);
if (textNode !== null) {

@@ -916,0 +950,0 @@ return textNode;

@@ -18,3 +18,3 @@ MediumEditor.parseVersionString = function (release) {

// grunt-bump looks for this:
'version': '5.14.0'
'version': '5.14.1'
}).version);

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