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.6.0 to 5.6.1

7

CHANGES.md

@@ -0,1 +1,8 @@

5.6.1 / 2015-08-10
==================
* Fix issue with creating anchors and restoring selection at the beginning of paragraphs
* Fix issue with creating anchors and restoring selection within list items and nested blocks
* Ensure CTRL + M is respected as a way to insert new lines
5.6.0 / 2015-08-07

@@ -2,0 +9,0 @@ ==================

2

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

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

@@ -538,3 +538,3 @@ # MediumEditor

* __.serialize()__: returns a JSON object with elements contents
* __.setContent(html, element)__: sets the `innerHTML` to `html` of the element at `index`
* __.setContent(html, index)__: sets the `innerHTML` to `html` of the element at `index`

@@ -541,0 +541,0 @@ ## Capturing DOM changes

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

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();

@@ -349,2 +350,42 @@ this.el.innerHTML = '<p>Some text</p><p><br/></p><p><br/></p><p>link text more text</p>';

// 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 = '<p>Some text</p><p>link text more text</p>';
var editor = this.newMediumEditor('.editor'),
lastP = this.el.lastChild,
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
// 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(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 <p> wasn't removed, and the <a> 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 <p> container when adding a link inside a top-level <p> with a single text-node child', function () {

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

@@ -215,2 +215,107 @@ /*global describe, it, expect, spyOn,

describe('when the ctrl key and m key is pressed', function () {
it('should prevent new lines from being inserted when disableReturn options is true', function () {
this.el.innerHTML = 'lorem ipsum';
var editor = this.newMediumEditor('.editor', { disableReturn: true }),
evt;
placeCursorInsideElement(editor.elements[0], 0);
evt = prepareEvent(editor.elements[0], 'keydown', {
ctrlKey: true,
keyCode: Util.keyCode.M
});
spyOn(evt, 'preventDefault').and.callThrough();
firePreparedEvent(evt, editor.elements[0], 'keydown');
expect(evt.preventDefault).toHaveBeenCalled();
});
it('should prevent new lines from being inserted when data-disable-return is defined', function () {
this.el.innerHTML = 'lorem ipsum';
this.el.setAttribute('data-disable-return', true);
var editor = this.newMediumEditor('.editor'),
evt;
placeCursorInsideElement(editor.elements[0], 0);
evt = prepareEvent(editor.elements[0], 'keydown', {
ctrlKey: true,
keyCode: Util.keyCode.M
});
spyOn(evt, 'preventDefault').and.callThrough();
firePreparedEvent(evt, editor.elements[0], 'keydown');
expect(evt.preventDefault).toHaveBeenCalled();
});
it('should prevent consecutive new lines from being inserted when disableDoubleReturn is true', function () {
this.el.innerHTML = '<p><br></p>';
var editor = this.newMediumEditor('.editor', { disableDoubleReturn: true }),
p = editor.elements[0].querySelector('p'),
evt;
placeCursorInsideElement(p, 0);
evt = prepareEvent(p, 'keydown', {
ctrlKey: true,
keyCode: Util.keyCode.M
});
spyOn(evt, 'preventDefault').and.callThrough();
firePreparedEvent(evt, p, 'keydown');
expect(evt.preventDefault).toHaveBeenCalled();
});
it('should prevent consecutive new lines from being inserted when data-disable-double-return is defined', function () {
this.el.innerHTML = '<p><br></p>';
this.el.setAttribute('data-disable-double-return', true);
var editor = this.newMediumEditor('.editor'),
p = editor.elements[0].querySelector('p'),
evt;
placeCursorInsideElement(p, 0);
evt = prepareEvent(p, 'keydown', {
ctrlKey: true,
keyCode: Util.keyCode.M
});
spyOn(evt, 'preventDefault').and.callThrough();
firePreparedEvent(evt, p, 'keydown');
expect(evt.preventDefault).toHaveBeenCalled();
});
it('should prevent consecutive new lines from being inserted inside a sentence when disableDoubleReturn is true', function () {
this.el.innerHTML = '<p>hello</p><p><br></p><p>word</p>';
var editor = this.newMediumEditor('.editor', { disableDoubleReturn: true }),
p = editor.elements[0].getElementsByTagName('p')[2],
evt;
placeCursorInsideElement(p, 0);
evt = prepareEvent(p, 'keydown', {
ctrlKey: true,
keyCode: Util.keyCode.M
});
spyOn(evt, 'preventDefault').and.callThrough();
firePreparedEvent(evt, p, 'keydown');
expect(evt.preventDefault).toHaveBeenCalled();
});
});
describe('should unlink anchors', function () {

@@ -217,0 +322,0 @@ it('when the user presses enter inside an anchor', function () {

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

@@ -116,2 +116,12 @@ selectElementContentsAndFire, Selection,

it('should export a position indicating the cursor is at the beginning of a paragraph', function () {
this.el.innerHTML = '<p><span>www.google.com</span></p><p><b>Whatever</b></p>';
var editor = this.newMediumEditor('.editor', {
buttons: ['italic', 'underline', 'strikethrough']
});
placeCursorInsideElement(editor.elements[0].querySelector('b'), 0); // beginning of <b> 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 () {

@@ -196,3 +206,3 @@ this.el.innerHTML = '<p><span>www.google.com</span></p><p><br /></p>' +

var startParagraph = Util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'p');
var startParagraph = MediumEditor.util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'p');
expect(startParagraph).toBe(editor.elements[0].getElementsByTagName('p')[1], 'empty paragraph');

@@ -212,3 +222,3 @@ });

var startParagraph = Util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'p');
var startParagraph = MediumEditor.util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'p');
expect(startParagraph).toBe(editor.elements[0].getElementsByTagName('p')[2], 'paragraph after empty paragraph');

@@ -229,3 +239,3 @@ });

var startParagraph = Util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'p');
var startParagraph = MediumEditor.util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'p');
expect(startParagraph).toBe(editor.elements[1].getElementsByTagName('p')[2], 'paragraph after empty paragraph');

@@ -245,3 +255,3 @@ });

var startParagraph = Util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'h2');
var startParagraph = MediumEditor.util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'h2');
expect(startParagraph).toBe(editor.elements[0].querySelector('h2'), 'block element after empty block element');

@@ -261,3 +271,3 @@ });

var startParagraph = Util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'h2');
var startParagraph = MediumEditor.util.getClosestTag(window.getSelection().getRangeAt(0).startContainer, 'h2');
expect(startParagraph).toBe(editor.elements[0].querySelector('h2'), 'block element after empty block element');

@@ -278,3 +288,3 @@ });

var innerElement = window.getSelection().getRangeAt(0).startContainer;
expect(Util.isDescendant(editor.elements[0].querySelector('i'), innerElement, true)).toBe(true, 'nested inline elment inside block element after empty block element');
expect(MediumEditor.util.isDescendant(editor.elements[0].querySelector('i'), innerElement, true)).toBe(true, 'nested inline elment inside block element after empty block element');
});

@@ -317,3 +327,3 @@

// The behavior varies from browser to browser for this case, some select TH, some #textNode
expect(Util.isDescendant(editor.elements[0].querySelector('th'), innerElement, true))
expect(MediumEditor.util.isDescendant(editor.elements[0].querySelector('th'), innerElement, true))
.toBe(true, 'expect selection to be of TH or a descendant');

@@ -336,5 +346,29 @@ expect(innerElement).toBe(window.getSelection().getRangeAt(0).endContainer);

var innerElement = window.getSelection().getRangeAt(0).startContainer;
expect(Util.isDescendant(editor.elements[0].querySelectorAll('p')[1], innerElement, true)).toBe(false, 'moved selection beyond non-empty block element');
expect(Util.isDescendant(editor.elements[0].querySelector('h2'), innerElement, true)).toBe(true, 'moved selection to element to incorrect block element');
expect(MediumEditor.util.isDescendant(editor.elements[0].querySelectorAll('p')[1], innerElement, true)).toBe(false, 'moved selection beyond non-empty block element');
expect(MediumEditor.util.isDescendant(editor.elements[0].querySelector('h2'), innerElement, true)).toBe(true, 'moved selection to element to incorrect block element');
});
// https://github.com/yabwe/medium-editor/issues/732
it('should support a selection correctly when space + newlines are separating block elements', function () {
this.el.innerHTML = '<ul>\n' +
' <li><a href="#">a link</a></li>\n' +
' <li>a list item</li>\n' +
' <li>target</li>\n' +
'</ul>';
var editor = this.newMediumEditor('.editor'),
lastLi = this.el.querySelectorAll('ul > li')[2];
// Select the <li> with 'target'
selectElementContents(lastLi.firstChild);
var selectionData = editor.exportSelection();
expect(selectionData.emptyBlocksIndex).toBe(0);
editor.importSelection(selectionData);
var range = window.getSelection().getRangeAt(0);
expect(range.toString()).toBe('target', 'The selection is around the wrong element');
expect(MediumEditor.util.isDescendant(lastLi, range.startContainer, true)).toBe(true, 'The start of the selection is invalid');
expect(MediumEditor.util.isDescendant(lastLi, range.endContainer, true)).toBe(true, 'The end of the selection is invalid');
});
});

@@ -459,3 +493,3 @@

it('no selected elements on empty selection', function () {
var elements = Selection.getSelectedElements(document);
var elements = MediumEditor.selection.getSelectedElements(document);

@@ -471,3 +505,3 @@ expect(elements.length).toBe(0);

selectElementContents(editor.elements[0].querySelector('i').firstChild);
elements = Selection.getSelectedElements(document);
elements = MediumEditor.selection.getSelectedElements(document);

@@ -484,3 +518,3 @@ expect(elements.length).toBe(1);

selectElementContents(this.el);
elements = Selection.getSelectedElements(document);
elements = MediumEditor.selection.getSelectedElements(document);

@@ -495,4 +529,4 @@ expect(elements.length).toBe(1);

it('should return null on bad range', function () {
expect(Selection.getSelectedParentElement(null)).toBe(null);
expect(Selection.getSelectedParentElement(false)).toBe(null);
expect(MediumEditor.selection.getSelectedParentElement(null)).toBe(null);
expect(MediumEditor.selection.getSelectedParentElement(false)).toBe(null);
});

@@ -512,3 +546,3 @@

element = Selection.getSelectedParentElement(range);
element = MediumEditor.selection.getSelectedParentElement(range);

@@ -515,0 +549,0 @@ expect(element).toBe(document);

@@ -490,3 +490,3 @@ /*global Util*/

if (Util.isKey(event, Util.keyCode.ENTER)) {
if (Util.isKey(event, Util.keyCode.ENTER) || (event.ctrlKey && Util.isKey(event, Util.keyCode.M))) {
return this.triggerCustomEvent('editableKeydownEnter', event, event.currentTarget);

@@ -493,0 +493,0 @@ }

@@ -63,3 +63,3 @@ /*global Util */

var emptyBlocksIndex = this.getIndexRelativeToAdjacentEmptyBlocks(doc, root, range.startContainer, range.startOffset);
if (emptyBlocksIndex !== 0) {
if (emptyBlocksIndex !== -1) {
selectionState.emptyBlocksIndex = emptyBlocksIndex;

@@ -123,18 +123,4 @@ }

if (selectionState.emptyBlocksIndex) {
var targetNode = Util.getTopBlockContainer(range.startContainer),
index = 0;
// Skip over empty blocks until we hit the block we want the selection to be in
while (index < selectionState.emptyBlocksIndex && targetNode.nextSibling) {
targetNode = targetNode.nextSibling;
index++;
// If we find a non-empty block, ignore the emptyBlocksIndex and just put selection here
if (targetNode.textContent.length > 0) {
break;
}
}
// We're selecting a high-level block node, so make sure the cursor gets moved into the deepest
// element at the beginning of the block
range.setStart(Util.getFirstSelectableLeafNode(targetNode), 0);
if (typeof selectionState.emptyBlocksIndex !== 'undefined') {
range = Selection.importSelectionMoveCursorPastBlocks(doc, root, selectionState.emptyBlocksIndex, range);
}

@@ -185,9 +171,59 @@

// 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.
// Uses the emptyBlocksIndex calculated by getIndexRelativeToAdjacentEmptyBlocks
// to move the cursor back to the start of the correct paragraph
importSelectionMoveCursorPastBlocks: function (doc, root, index, range) {
var treeWalker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filterOnlyParentElements, false),
startContainer = range.startContainer,
startBlock,
targetNode,
currIndex = 0;
index = index || 1; // If index is 0, we still want to move to the next block
// Chrome counts newlines and spaces that separate block elements as actual elements.
// If the selection is inside one of these text nodes, and it has a previous sibling
// which is a block element, we want the treewalker to start at the previous sibling
// and NOT at the parent of the textnode
if (startContainer.nodeType === 3 && Util.isBlockContainer(startContainer.previousSibling)) {
startBlock = startContainer.previousSibling;
} else {
startBlock = Util.getClosestBlockContainer(startContainer);
}
// Skip over empty blocks until we hit the block we want the selection to be in
while (treeWalker.nextNode()) {
if (!targetNode) {
// Loop through all blocks until we hit the starting block element
if (startBlock === treeWalker.currentNode) {
targetNode = treeWalker.currentNode;
}
} else {
targetNode = treeWalker.currentNode;
currIndex++;
// We hit the target index, bail
if (currIndex === index) {
break;
}
// If we find a non-empty block, ignore the emptyBlocksIndex and just put selection here
if (targetNode.textContent.length > 0) {
break;
}
}
}
// We're selecting a high-level block node, so make sure the cursor gets moved into the deepest
// element at the beginning of the block
range.setStart(Util.getFirstSelectableLeafNode(targetNode), 0);
return range;
},
// 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;
}

@@ -198,7 +234,6 @@

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;
}

@@ -208,3 +243,4 @@

// and the block the cursor is in
var treeWalker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filterOnlyParentElements, false),
var closestBlock = Util.getClosestBlockContainer(cursorContainer),
treeWalker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filterOnlyParentElements, false),
emptyBlocksCount = 0;

@@ -216,3 +252,3 @@ while (treeWalker.nextNode()) {

}
if (Util.isDescendant(treeWalker.currentNode, cursorContainer, true)) {
if (treeWalker.currentNode === closestBlock) {
return emptyBlocksCount;

@@ -219,0 +255,0 @@ }

@@ -44,3 +44,4 @@ /*global NodeFilter, Selection*/

DELETE: 46,
K: 75 // K keycode, and not k
K: 75, // K keycode, and not k
M: 77
},

@@ -92,3 +93,11 @@

blockContainerElementNames: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre'],
blockContainerElementNames: [
// elements our editor generates
'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'ul', 'li', 'ol',
// all other known block elements
'address', 'article', 'aside', 'audio', 'canvas', 'dd', 'dl', 'dt', 'fieldset',
'figcaption', 'figure', 'footer', 'form', 'header', 'hgroup', 'main', 'nav',
'noscript', 'output', 'section', 'table', 'tbody', 'tfoot', 'video'
],
emptyElementNames: ['br', 'col', 'colgroup', 'hr', 'img', 'input', 'source', 'wbr'],

@@ -465,3 +474,3 @@

tagName = parentNode.nodeName.toLowerCase();
while (!this.isBlockContainer(parentNode) && tagName !== 'div') {
while (tagName === 'li' || (!this.isBlockContainer(parentNode) && tagName !== 'div')) {
if (tagName === 'li') {

@@ -468,0 +477,0 @@ return true;

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

// grunt-bump looks for this:
'version': '5.6.0'
'version': '5.6.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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc