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

medium-editor

Package Overview
Dependencies
Maintainers
3
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 4.8.1 to 4.9.0

demo/auto-link.html

2

bower.json
{
"name": "medium-editor",
"version": "4.8.1",
"version": "4.9.0",
"homepage": "http://daviferreira.github.io/medium-editor/",

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

@@ -0,1 +1,9 @@

4.9.0 / 2015-05-18
==================
* New auto-link support for detecting urls and converting them to links
* Fix target _blank issue for links in Firefox
* Don't show placeholders for empty lists
* Allow for overriding image drag and drop via extension
4.8.1 / 2015-05-13

@@ -2,0 +10,0 @@ ==================

@@ -23,6 +23,8 @@ /*global module, require, process*/

'src/js/button.js',
'src/js/paste.js',
'src/js/extensions/anchor.js',
'src/js/extensions/anchor-preview.js',
'src/js/extensions/auto-link.js',
'src/js/extensions/image-dragging.js',
'src/js/extensions/fontsize.js',
'src/js/extensions/paste.js',
'src/js/toolbar.js',

@@ -29,0 +31,0 @@ 'src/js/placeholders.js',

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

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

@@ -15,3 +15,3 @@ # MediumEditor

[![NPM info](https://nodei.co/npm/medium-editor.png?downloads=true)](https://nodei.co/npm/medium-editor.png?downloads=true)
[![NPM info](https://nodei.co/npm/medium-editor.png?downloads=true)](https://www.npmjs.com/package/medium-editor)

@@ -78,2 +78,3 @@ [![Travis build status](http://img.shields.io/travis/daviferreira/medium-editor.svg?style=flat-square)](https://travis-ci.org/daviferreira/medium-editor)

* __disableAnchorPreview__: enables/disables the anchor preview element, which appears when hovering links and allows the user to edit the link when clicking. If toolbar is diabled (via __disableToolbar__ or `data-disable-toolbar attribute`) the anchor preview is always disabled so this option will be ignored. Default: false
* __autoLink__: enables/disables the auto-link feature, which automatically turns URLs entered into the text field into HTML anchor tags (similar to the functionality of Markdown). Default: false
* __disableReturn__: enables/disables the use of the return-key. You can also set specific element behavior by using setting a data-disable-return attribute. Default: false

@@ -80,0 +81,0 @@ * __disableDoubleReturn__: allows/disallows two (or more) empty new lines. You can also set specific element behavior by using setting a data-disable-double-return attribute. Default: false

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

/*global describe, it, expect, afterEach,
/*global describe, it, expect, afterEach, Util,
beforeEach, fireEvent, setupTestHelpers */

@@ -31,3 +31,5 @@

// hit return
fireEvent(editor.elements[0], 'keypress', 13);
fireEvent(editor.elements[0], 'keypress', {
keyCode: Util.keyCode.ENTER
});

@@ -51,3 +53,5 @@ el = document.getElementById('header');

// hit return
fireEvent(editor.elements[0], 'keypress', 13);
fireEvent(editor.elements[0], 'keypress', {
keyCode: Util.keyCode.ENTER
});

@@ -72,3 +76,5 @@ el = document.getElementById('header');

// hit backspace
fireEvent(editor.elements[0].querySelector(el.tagName.toLowerCase()), 'keydown', { keyCode: 8 });
fireEvent(editor.elements[0].querySelector(el.tagName.toLowerCase()), 'keydown', {
keyCode: Util.keyCode.BACKSPACE
});

@@ -75,0 +81,0 @@ el = document.getElementById('header');

@@ -11,3 +11,5 @@ /*global atob, unescape, Uint8Array, Blob*/

el.innerHTML = html || '';
el.className = className;
if (className) {
el.className = className;
}
this.elements.push(el);

@@ -14,0 +16,0 @@ if (!dontAppend) {

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

disablePlaceholders: false,
autoLink: false,
toolbarAlign: 'center',

@@ -105,0 +106,0 @@ elementsContainer: document.body,

@@ -33,2 +33,14 @@ /*global describe, it, expect,

it('should not set a placeholder for elements with unorderedlist', function () {
this.el.innerHTML = '<ul><li></li></ul>';
var editor = this.newMediumEditor('.editor');
expect(editor.elements[0].className).not.toContain('medium-editor-placeholder');
});
it('should not set a placeholder for elements with orderedlist', function () {
this.el.innerHTML = '<ol><li></li></ol>';
var editor = this.newMediumEditor('.editor');
expect(editor.elements[0].className).not.toContain('medium-editor-placeholder');
});
it('should set placeholder for elements with empty children', function () {

@@ -35,0 +47,0 @@ this.el.innerHTML = '<p><br></p><div class="empty"></div>';

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

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

@@ -6,2 +7,10 @@ describe('Util', function () {

beforeEach(function () {
setupTestHelpers.call(this);
});
afterEach(function () {
this.cleanupTest();
});
describe('Exposure', function () {

@@ -104,2 +113,26 @@

describe('settargetblank', function () {
it('sets target blank on a A element from a A element', function () {
var el = this.createElement('a', '', 'lorem ipsum');
el.attributes.href = 'http://0.0.0.0/bar.html';
Util.setTargetBlank(el);
expect(el.target).toBe('_blank');
});
it('sets target blank on a A element from a DIV element', function () {
var el = this.createElement('div', '', '<a href="http://1.1.1.1/foo.html">foo</a> <a href="http://0.0.0.0/bar.html">bar</a>');
Util.setTargetBlank(el, 'http://0.0.0.0/bar.html');
var nodes = el.getElementsByTagName('a');
expect(nodes[0].target).not.toBe('_blank');
expect(nodes[1].target).toBe('_blank');
});
});
describe('warn', function () {

@@ -131,2 +164,101 @@

});
describe('splitOffDOMTree', function () {
/* start:
*
* <div>
* / | \
* <span> <span> <span>
* / \ / \ / \
* 1 2 3 4 5 6
*
* result:
*
* <div> <div>'
* / \ / \
* <span> <span> <span>' <span>
* / \ | | / \
* 1 2 3 4 5 6
*/
it('should split a complex tree correctly when splitting off right part of tree', function () {
var el = this.createElement('div', '',
'<span><b>1</b><i>2</i></span><span><b>3</b><u>4</u></span><span><b>5</b><i>6</i></span>'),
splitOn = el.querySelector('u').firstChild,
result = Util.splitOffDOMTree(el, splitOn);
expect(el.outerHTML).toBe('<div><span><b>1</b><i>2</i></span><span><b>3</b></span></div>');
expect(result.outerHTML).toBe('<div><span><u>4</u></span><span><b>5</b><i>6</i></span></div>');
});
/* start:
*
* <div>
* / | \
* <span> <span> <span>
* / \ / \ / \
* 1 2 3 4 5 6
*
* result:
*
* <div>' <div>
* / \ |
* <span> <span> <span>
* /\ /\ /\
* 1 2 3 4 5 6
*/
it('should split a complex tree correctly when splitting off left part of tree', function () {
var el = this.createElement('div', '',
'<span><b>1</b><i>2</i></span><span><b>3</b><u>4</u></span><span><b>5</b><i>6</i></span>'),
splitOn = el.querySelector('u').firstChild,
result = Util.splitOffDOMTree(el, splitOn, true);
expect(el.outerHTML).toBe('<div><span><b>5</b><i>6</i></span></div>');
expect(result.outerHTML).toBe('<div><span><b>1</b><i>2</i></span><span><b>3</b><u>4</u></span></div>');
});
});
describe('moveTextRangeIntoElement', function () {
it('should return false and bail if no elements are passed', function () {
expect(Util.moveTextRangeIntoElement(null, null)).toBe(false);
});
it('should return false and bail if elemenets do not share a root', function () {
var el = this.createElement('div', '', 'text'),
elTwo = this.createElement('div', '', 'more text', true),
temp = this.createElement('div', '');
expect(Util.moveTextRangeIntoElement(el, elTwo, temp)).toBe(false);
expect(temp.innerHTML).toBe('');
});
it('should create a parent element that spans multiple root elements', function () {
var el = this.createElement('div', '',
'<span>Link = http</span>' +
'<span>://</span>' +
'<span>www.exam</span>' +
'<span>ple.com</span>' +
'<span>:443/</span>' +
'<span>path/to</span>' +
'<span>somewhere#</span>' +
'<span>index notLink</span>'),
firstText = el.firstChild.firstChild.splitText('Link = '.length),
lastText = el.lastChild.firstChild,
para = this.createElement('p', '');
lastText.splitText('index'.length);
Util.moveTextRangeIntoElement(firstText, lastText, para);
expect(el.innerHTML).toBe(
'<span>Link = </span>' +
'<p>' +
'<span>http</span>' +
'<span>://</span>' +
'<span>www.exam</span>' +
'<span>ple.com</span>' +
'<span>:443/</span>' +
'<span>path/to</span>' +
'<span>somewhere#</span>' +
'<span>index</span>' +
'</p>' +
'<span> notLink</span>'
);
});
});
});

@@ -1,4 +0,5 @@

/*global FileReader, Util, ButtonsData, DefaultButton,
/*global Util, ButtonsData, DefaultButton,
Selection, AnchorExtension, FontSizeExtension, Extension, extensionDefaults,
Toolbar, AnchorPreview, Events, Placeholders, editorDefaults */
Toolbar, AnchorPreview, AutoLink, ImageDragging,
Events, Placeholders, editorDefaults */

@@ -142,47 +143,2 @@ function MediumEditor(elements, options) {

function handleDrag(event) {
var className = 'medium-editor-dragover';
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
if (event.type === 'dragover') {
event.target.classList.add(className);
} else if (event.type === 'dragleave') {
event.target.classList.remove(className);
}
}
function handleDrop(event) {
var className = 'medium-editor-dragover',
files;
event.preventDefault();
event.stopPropagation();
// IE9 does not support the File API, so prevent file from opening in a new window
// but also don't try to actually get the file
if (event.dataTransfer.files) {
files = Array.prototype.slice.call(event.dataTransfer.files, 0);
files.some(function (file) {
if (file.type.match('image')) {
var fileReader, id;
fileReader = new FileReader();
fileReader.readAsDataURL(file);
id = 'medium-img-' + (+new Date());
Util.insertHTMLCommand(this.options.ownerDocument, '<img class="medium-image-loading" id="' + id + '" />');
fileReader.onload = function () {
var img = this.options.ownerDocument.getElementById(id);
if (img) {
img.removeAttribute('id');
img.removeAttribute('class');
img.src = fileReader.result;
}
}.bind(this);
}
}.bind(this));
}
event.target.classList.remove(className);
}
function handleKeyup(event) {

@@ -282,2 +238,18 @@ var node = Util.getSelectionStart(this.options.ownerDocument),

function shouldAddDefaultAutoLink() {
if (this.options.extensions['auto-link']) {
return false;
}
return !!this.options.autoLink;
}
function shouldAddDefaultImageDragging() {
if (this.options.extensions['image-dragging']) {
return false;
}
return !!this.options.imageDragging;
}
function createContentEditable(textarea) {

@@ -388,8 +360,2 @@ var div = this.options.ownerDocument.createElement('div'),

}
// drag and drop of images
if (this.options.imageDragging) {
this.subscribe('editableDrag', handleDrag.bind(this));
this.subscribe('editableDrop', handleDrop.bind(this));
}
}

@@ -452,2 +418,10 @@

}
if (shouldAddDefaultAutoLink.call(this)) {
this.commands.push(initExtension(new AutoLink(), 'auto-link', this));
}
if (shouldAddDefaultImageDragging.call(this)) {
this.commands.push(initExtension(new ImageDragging(), 'image-dragging', this));
}
}

@@ -937,3 +911,3 @@

if (this.options.targetBlank || opts.target === '_blank') {
Util.setTargetBlank(Util.getSelectionStart(this.options.ownerDocument));
Util.setTargetBlank(Util.getSelectionStart(this.options.ownerDocument), opts.url);
}

@@ -940,0 +914,0 @@

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

/*global PasteHandler */
/*global PasteHandler, AutoLink, ImageDragging */

@@ -8,4 +8,6 @@ var extensionDefaults;

extensionDefaults = {
autoLink: AutoLink,
imageDragging: ImageDragging,
paste: PasteHandler
};
})();

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

disablePlaceholders: false,
autoLink: false,
toolbarAlign: 'center',

@@ -26,0 +27,0 @@ elementsContainer: false,

@@ -25,4 +25,4 @@ /* global Util */

//
// var thingOne = new Thing(); // foo === bar
// var thingTwo = new ThingTwo(); // foo == baz
// var thingOne = new Thing(); // foo === "bar"
// var thingTwo = new ThingTwo(); // foo === "baz"
//

@@ -111,3 +111,3 @@ // which seems like some simply shallow copy nonsense

* 2) Call checkState on the extension, passing the node as an argument
* 3) Get tha parent node of the previous node
* 3) Get the parent node of the previous node
* 4) Repeat steps #2 and #3 until we move outside the parent contenteditable

@@ -136,3 +136,3 @@ */

*
* If this function returns a non-null value, the exntesion will
* If this function returns a non-null value, the extension will
* be ignored as the code climbs the dom tree.

@@ -158,3 +158,3 @@ *

* If implemented, this function is similar to checkState() in
* that it will be calle repeatedly as MediumEditor moves up
* that it will be called repeatedly as MediumEditor moves up
* the DOM to update the editor & toolbar after a state change.

@@ -161,0 +161,0 @@ *

@@ -19,5 +19,4 @@ /*global Util*/

init: function (instance) {
init: function () {
this.base = instance;
this.anchorPreview = this.createPreview();

@@ -24,0 +23,0 @@ this.base.options.elementsContainer.appendChild(this.anchorPreview);

@@ -34,5 +34,4 @@ var Placeholders;

updatePlaceholder: function (el) {
if (!(el.querySelector('img')) &&
!(el.querySelector('blockquote')) &&
el.textContent.replace(/^\s+|\s+$/g, '') === '') {
// if one of these element ('img, blockquote, ul, ol') are found inside the given element, we won't display the placeholder
if (!(el.querySelector('img, blockquote, ul, ol')) && el.textContent.replace(/^\s+|\s+$/g, '') === '') {
this.showPlaceholder(el);

@@ -39,0 +38,0 @@ } else {

@@ -338,5 +338,14 @@ /*global NodeFilter, console*/

// TODO: not sure if this should be here
setTargetBlank: function (el) {
var i;
/**
* Set target to blank on the given el element
*
* TODO: not sure if this should be here
*
* When creating a link (using core -> createLink) the selection returned by Firefox will be the parent of the created link
* instead of the created link itself (as it is for Chrome for example), so we retrieve all "a" children to grab the good one by
* using `anchorUrl` to ensure that we are adding target="_blank" on the good one.
* This isn't a bulletproof solution anyway ..
*/
setTargetBlank: function (el, anchorUrl) {
var i, url = anchorUrl || false;
if (el.tagName.toLowerCase() === 'a') {

@@ -348,3 +357,5 @@ el.target = '_blank';

for (i = 0; i < el.length; i += 1) {
el[i].target = '_blank';
if (false === url || url === el[i].attributes.href.value) {
el[i].target = '_blank';
}
}

@@ -417,2 +428,226 @@ }

/* splitDOMTree
*
* Given a root element some descendant element, split the root element
* into its own element containing the descendant element and all elements
* on the left or right side of the descendant ('right' is default)
*
* example:
*
* <div>
* / | \
* <span> <span> <span>
* / \ / \ / \
* 1 2 3 4 5 6
*
* If I wanted to split this tree given the <div> as the root and "4" as the leaf
* the result would be (the prime ' marks indicates nodes that are created as clones):
*
* SPLITTING OFF 'RIGHT' TREE SPLITTING OFF 'LEFT' TREE
*
* <div> <div>' <div>' <div>
* / \ / \ / \ |
* <span> <span> <span>' <span> <span> <span> <span>
* / \ | | / \ /\ /\ /\
* 1 2 3 4 5 6 1 2 3 4 5 6
*
* The above example represents splitting off the 'right' or 'left' part of a tree, where
* the <div>' would be returned as an element not appended to the DOM, and the <div>
* would remain in place where it was
*
*/
splitOffDOMTree: function (rootNode, leafNode, splitLeft) {
var splitOnNode = leafNode,
createdNode = null,
splitRight = !splitLeft;
// loop until we hit the root
while (splitOnNode !== rootNode) {
var currParent = splitOnNode.parentNode,
newParent = currParent.cloneNode(false),
targetNode = (splitRight ? splitOnNode : currParent.firstChild),
appendLast;
// Create a new parent element which is a clone of the current parent
if (createdNode) {
if (splitRight) {
// If we're splitting right, add previous created element before siblings
newParent.appendChild(createdNode);
} else {
// If we're splitting left, add previous created element last
appendLast = createdNode;
}
}
createdNode = newParent;
while (targetNode) {
var sibling = targetNode.nextSibling;
// Special handling for the 'splitNode'
if (targetNode === splitOnNode) {
if (!targetNode.hasChildNodes()) {
targetNode.parentNode.removeChild(targetNode);
} else {
// For the node we're splitting on, if it has children, we need to clone it
// and not just move it
targetNode = targetNode.cloneNode(false);
}
// If the resulting split node has content, add it
if (targetNode.textContent) {
createdNode.appendChild(targetNode);
}
targetNode = (splitRight ? sibling : null);
} else {
// For general case, just remove the element and only
// add it to the split tree if it contains something
targetNode.parentNode.removeChild(targetNode);
if (targetNode.hasChildNodes() || targetNode.textContent) {
createdNode.appendChild(targetNode);
}
targetNode = sibling;
}
}
// If we had an element we wanted to append at the end, do that now
if (appendLast) {
createdNode.appendChild(appendLast);
}
splitOnNode = currParent;
}
return createdNode;
},
moveTextRangeIntoElement: function (startNode, endNode, newElement) {
if (!startNode || !endNode) {
return false;
}
var rootNode = this.findCommonRoot(startNode, endNode);
if (!rootNode) {
return false;
}
if (endNode === startNode) {
var temp = startNode.parentNode,
sibling = startNode.nextSibling;
temp.removeChild(startNode);
newElement.appendChild(startNode);
if (sibling) {
temp.insertBefore(newElement, sibling);
} else {
temp.appendChild(newElement);
}
return newElement.hasChildNodes();
}
// create rootChildren array which includes all the children
// we care about
var rootChildren = [],
firstChild,
lastChild,
nextNode;
for (var i = 0; i < rootNode.childNodes.length; i++) {
nextNode = rootNode.childNodes[i];
if (!firstChild) {
if (Util.isDescendant(nextNode, startNode, true)) {
firstChild = nextNode;
}
} else {
if (this.isDescendant(nextNode, endNode, true)) {
lastChild = nextNode;
break;
} else {
rootChildren.push(nextNode);
}
}
}
var afterLast = lastChild.nextSibling,
fragment = document.createDocumentFragment();
// build up fragment on startNode side of tree
if (firstChild === startNode) {
firstChild.parentNode.removeChild(firstChild);
fragment.appendChild(firstChild);
} else {
fragment.appendChild(this.splitOffDOMTree(firstChild, startNode));
}
// add any elements between firstChild & lastChild
rootChildren.forEach(function (element) {
element.parentNode.removeChild(element);
fragment.appendChild(element);
});
// build up fragment on endNode side of the tree
if (lastChild === endNode) {
lastChild.parentNode.removeChild(lastChild);
fragment.appendChild(lastChild);
} else {
fragment.appendChild(this.splitOffDOMTree(lastChild, endNode, true));
}
// Add fragment into passed in element
newElement.appendChild(fragment);
if (lastChild.parentNode === rootNode) {
// If last child is in the root, insert newElement in front of it
rootNode.insertBefore(newElement, lastChild);
} else if (afterLast) {
// If last child was removed, but it had a sibling, insert in front of it
rootNode.insertBefore(newElement, afterLast);
} else {
// lastChild was removed and was the last actual element just append
rootNode.appendChild(newElement);
}
return newElement.hasChildNodes();
},
/* based on http://stackoverflow.com/a/6183069 */
depthOfNode: function (inNode) {
var theDepth = 0,
node = inNode;
while (node.parentNode !== null) {
node = node.parentNode;
theDepth++;
}
return theDepth;
},
findCommonRoot: function (inNode1, inNode2) {
var depth1 = this.depthOfNode(inNode1),
depth2 = this.depthOfNode(inNode2),
node1 = inNode1,
node2 = inNode2;
while (depth1 !== depth2) {
if (depth1 > depth2) {
node1 = node1.parentNode;
depth1 -= 1;
} else {
node2 = node2.parentNode;
depth2 -= 1;
}
}
while (node1 !== node2) {
node1 = node1.parentNode;
node2 = node2.parentNode;
}
return node1;
},
/* END - based on http://stackoverflow.com/a/6183069 */
ensureUrlHasProtocol: function (url) {
if (url.indexOf('://') === -1) {
return 'http://' + url;
}
return url;
},
warn: function () {

@@ -419,0 +654,0 @@ if (window.console !== undefined && typeof window.console.warn === 'function') {

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

// grunt-bump looks for this:
'version': '4.8.1'
'version': '4.9.0'
}).version.split('.'));

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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