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.2.0 to 5.3.0

src/js/extensions/deprecated/image-dragging.js

9

CHANGES.md

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

5.3.0 / 2015-07-07
==================
* Fix issue with disabling image drag & drop via imageDragging option
* Deprecate image-dragging extension
* Introduce file-dragging extension
* Ensure autolink urls respect targetBlank option
* Expose importSelection and exportSelection as generic Selection helpers
5.2.0 / 2015-06-29

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

3

Gruntfile.js

@@ -27,3 +27,3 @@ /*global module, require, process*/

'src/js/extensions/auto-link.js',
'src/js/extensions/image-dragging.js',
'src/js/extensions/file-dragging.js',
'src/js/extensions/keyboard-commands.js',

@@ -34,2 +34,3 @@ 'src/js/extensions/fontsize.js',

'src/js/extensions/toolbar.js',
'src/js/extensions/deprecated/image-dragging.js',
'src/js/defaults/options.js',

@@ -36,0 +37,0 @@ 'src/js/defaults/extensions.js',

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

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

@@ -401,2 +401,18 @@ /*global describe, it, expect, beforeEach, afterEach,

it('should create a link with target="_blank" when respective option is set to true', function () {
this.el = this.createElement('div', 'editor-blank', '');
this.newMediumEditor('.editor-blank', {
autoLink: true,
targetBlank: true
});
this.el.innerHTML = 'http://www.example.com';
selectElementContentsAndFire(this.el);
triggerAutolinking(this.el);
var links = this.el.getElementsByTagName('a');
expect(links.length).toBe(1);
expect(links[0].getAttribute('href')).toBe('http://www.example.com');
expect(links[0].target).toBe('_blank');
});
it('should stop attempting to auto-link on keypress if an error is encountered', function () {

@@ -403,0 +419,0 @@ var spy = spyOn(MediumEditor.extensions.autoLink.prototype, 'performLinking').and.throwError('DOM ERROR');

@@ -17,20 +17,24 @@ /*global describe, it, expect, spyOn,

it('should do nothing when option is false', function () {
var editor = this.newMediumEditor(this.el, { imageDragging: false });
fireEvent(editor.elements[0], 'dragover');
expect(editor.elements[0].className).not.toContain('medium-editor-dragover');
});
describe('drag', function () {
it('should add medium-editor-dragover class', function () {
var editor = this.newMediumEditor(this.el);
fireEvent(editor.elements[0], 'dragover');
expect(editor.elements[0].className).toContain('medium-editor-dragover');
});
it('should add medium-editor-dragover class on drag over', function () {
var editor = this.newMediumEditor(this.el);
fireEvent(editor.elements[0], 'dragover');
expect(editor.elements[0].className).toContain('medium-editor-dragover');
});
it('should add medium-editor-dragover class even when data is invalid', function () {
var editor = this.newMediumEditor(this.el, {
imageDragging: false
});
fireEvent(editor.elements[0], 'dragover');
expect(editor.elements[0].className).toContain('medium-editor-dragover');
});
it('should remove medium-editor-dragover class on drag leave', function () {
var editor = this.newMediumEditor(this.el);
fireEvent(editor.elements[0], 'dragover');
expect(editor.elements[0].className).toContain('medium-editor-dragover');
fireEvent(editor.elements[0], 'dragleave');
expect(editor.elements[0].className).not.toContain('medium-editor-dragover');
it('should remove medium-editor-dragover class on drag leave', function () {
var editor = this.newMediumEditor(this.el);
fireEvent(editor.elements[0], 'dragover');
expect(editor.elements[0].className).toContain('medium-editor-dragover');
fireEvent(editor.elements[0], 'dragleave');
expect(editor.elements[0].className).not.toContain('medium-editor-dragover');
});
});

@@ -51,3 +55,13 @@

});
it('should remove medium-editor-dragover class and NOT add the image to the editor content', function () {
spyOn(Util, 'insertHTMLCommand').and.callThrough();
var editor = this.newMediumEditor(this.el, { imageDragging: false });
fireEvent(editor.elements[0], 'dragover');
expect(editor.elements[0].className).toContain('medium-editor-dragover');
fireEvent(editor.elements[0], 'drop');
expect(editor.elements[0].className).not.toContain('medium-editor-dragover');
expect(Util.insertHTMLCommand).not.toHaveBeenCalled();
});
});
});

@@ -166,6 +166,6 @@ /*global MediumEditor, describe, it, expect, spyOn,

it('should not add default extensions', function () {
it('should not add default extensions when overriden', function () {
var editor,
Preview, Placeholder, AutoLink, ImageDragging,
extPreview, extPlaceholder, extAutoLink, extImageDragging;
Preview, Placeholder, AutoLink, FileDragging,
extPreview, extPlaceholder, extAutoLink, extFileDragging;

@@ -175,3 +175,3 @@ Preview = Extension.extend({ name: 'anchor-preview' });

AutoLink = Extension.extend({ name: 'auto-link' });
ImageDragging = Extension.extend({ name: 'image-dragging' });
FileDragging = Extension.extend({ name: 'fileDragging' });

@@ -181,3 +181,3 @@ extPreview = new Preview();

extAutoLink = new AutoLink();
extImageDragging = new ImageDragging();
extFileDragging = new FileDragging();

@@ -189,3 +189,3 @@ editor = this.newMediumEditor('.editor', {

'auto-link': extAutoLink,
'image-dragging': extImageDragging
'fileDragging': extFileDragging
}

@@ -197,3 +197,3 @@ });

expect(editor.getExtensionByName('auto-link')).toBe(extAutoLink);
expect(editor.getExtensionByName('image-dragging')).toBe(extImageDragging);
expect(editor.getExtensionByName('fileDragging')).toBe(extFileDragging);
});

@@ -200,0 +200,0 @@

@@ -161,4 +161,9 @@ /*global atob, unescape, Uint8Array, Blob*/

};
if (!isIE9()) {
evt.dataTransfer.files = [dataURItoBlob('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7')];
// File API only allows access to 'files' on drop, not on any other event
if (!isIE9() && eventName === 'drop') {
var file = dataURItoBlob('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
if (!file.type) {
file.type = 'image/gif';
}
evt.dataTransfer.files = [file];
}

@@ -165,0 +170,0 @@ }

@@ -286,2 +286,9 @@ /*global Util, Selection, Extension,

function shouldUseFileDraggingExtension() {
// Since the file-dragging extension replaces the image-dragging extension,
// we need to check if the user passed an overrided image-dragging extension.
// If they have, to avoid breaking users, we won't use file-dragging extension.
return !this.options.extensions['imageDragging'];
}
function createContentEditable(textarea, id) {

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

// 4 Cases for imageDragging + fileDragging extensons:
//
// 1. ImageDragging ON + No Custom Image Dragging Extension:
// * Use fileDragging extension (default options)
// 2. ImageDragging OFF + No Custom Image Dragging Extension:
// * Use fileDragging extension w/ images turned off
// 3. ImageDragging ON + Custom Image Dragging Extension:
// * Don't use fileDragging (could interfere with custom image dragging extension)
// 4. ImageDragging OFF + Custom Image Dragging:
// * Don't use fileDragging (could interfere with custom image dragging extension)
if (shouldUseFileDraggingExtension.call(this)) {
var opts = this.options.fileDragging;
if (!opts) {
opts = {};
// Image is in the 'allowedTypes' list by default.
// If imageDragging is off override the 'allowedTypes' list with an empty one
if (!isImageDraggingEnabled.call(this)) {
opts.allowedTypes = [];
}
}
this.addBuiltInExtension('fileDragging', opts);
}
// Built-in extensions

@@ -394,3 +425,2 @@ var builtIns = {

autoLink: isAutoLinkEnabled.call(this),
imageDragging: isImageDraggingEnabled.call(this),
keyboardCommands: isKeyboardCommandsEnabled.call(this),

@@ -692,8 +722,8 @@ placeholder: isPlaceholderEnabled.call(this)

break;
case 'fileDragging':
extension = new MediumEditor.extensions.fileDragging(opts);
break;
case 'fontsize':
extension = new MediumEditor.extensions.fontSize(opts);
break;
case 'imageDragging':
extension = new MediumEditor.extensions.imageDragging();
break;
case 'keyboardCommands':

@@ -841,52 +871,15 @@ extension = new MediumEditor.extensions.keyboardCommands(this.options.keyboardCommands);

// http://stackoverflow.com/questions/17678843/cant-restore-selection-after-html-modify-even-if-its-the-same-html
// Tim Down
// TODO: move to selection.js and clean up old methods there
// Export the state of the selection in respect to one of this
// instance of MediumEditor's elements
exportSelection: function () {
var selectionState = null,
selection = this.options.contentWindow.getSelection(),
range,
preSelectionRange,
start,
editableElementIndex = -1;
var selectionElement = Selection.getSelectionElement(this.options.contentWindow),
editableElementIndex = this.elements.indexOf(selectionElement),
selectionState = null;
if (selection.rangeCount > 0) {
range = selection.getRangeAt(0);
preSelectionRange = range.cloneRange();
// Find element current selection is inside
this.elements.some(function (el, index) {
if (el === range.startContainer || Util.isDescendant(el, range.startContainer)) {
editableElementIndex = index;
return true;
}
return false;
});
if (editableElementIndex > -1) {
preSelectionRange.selectNodeContents(this.elements[editableElementIndex]);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
start = preSelectionRange.toString().length;
selectionState = {
start: start,
end: start + range.toString().length,
editableElementIndex: editableElementIndex
};
// If start = 0 there may still be an empty paragraph before it, but we don't care.
if (start !== 0) {
var emptyBlocksIndex = Selection.getIndexRelativeToAdjacentEmptyBlocks(
this.options.ownerDocument,
this.elements[editableElementIndex],
range.startContainer,
range.startOffset);
if (emptyBlocksIndex !== 0) {
selectionState.emptyBlocksIndex = emptyBlocksIndex;
}
}
}
if (editableElementIndex >= 0) {
selectionState = Selection.exportSelection(selectionElement, this.options.ownerDocument);
}
if (selectionState !== null && selectionState.editableElementIndex === 0) {
delete selectionState.editableElementIndex;
if (selectionState !== null && editableElementIndex !== 0) {
selectionState.editableElementIndex = editableElementIndex;
}

@@ -901,88 +894,11 @@

// http://stackoverflow.com/questions/17678843/cant-restore-selection-after-html-modify-even-if-its-the-same-html
// Tim Down
// TODO: move to selection.js and clean up old methods there
//
// {object} inSelectionState - the selection to import
// {boolean} [favorLaterSelectionAnchor] - defaults to false. If true, import the cursor immediately
// subsequent to an anchor tag if it would otherwise be placed right at the trailing edge inside the
// anchor. This cursor positioning, even though visually equivalent to the user, can affect behavior
// in MS IE.
importSelection: function (inSelectionState, favorLaterSelectionAnchor) {
if (!inSelectionState) {
// Restore a selection based on a selectionState returned by a call
// to MediumEditor.exportSelection
importSelection: function (selectionState, favorLaterSelectionAnchor) {
if (!selectionState) {
return;
}
var editableElementIndex = inSelectionState.editableElementIndex === undefined ?
0 : inSelectionState.editableElementIndex,
selectionState = {
editableElementIndex: editableElementIndex,
start: inSelectionState.start,
end: inSelectionState.end
},
editableElement = this.elements[selectionState.editableElementIndex],
charIndex = 0,
range = this.options.ownerDocument.createRange(),
nodeStack = [editableElement],
node,
foundStart = false,
stop = false,
i,
sel,
nextCharIndex;
range.setStart(editableElement, 0);
range.collapse(true);
node = nodeStack.pop();
while (!stop && node) {
if (node.nodeType === 3) {
nextCharIndex = charIndex + node.length;
if (!foundStart && selectionState.start >= charIndex && selectionState.start <= nextCharIndex) {
range.setStart(node, selectionState.start - charIndex);
foundStart = true;
}
if (foundStart && selectionState.end >= charIndex && selectionState.end <= nextCharIndex) {
range.setEnd(node, selectionState.end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
i = node.childNodes.length - 1;
while (i >= 0) {
nodeStack.push(node.childNodes[i]);
i -= 1;
}
}
if (!stop) {
node = nodeStack.pop();
}
}
if (inSelectionState.emptyBlocksIndex && selectionState.end === nextCharIndex) {
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 < inSelectionState.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);
range.collapse(true);
}
// If the selection is right at the ending edge of a link, put it outside the anchor tag instead of inside.
if (favorLaterSelectionAnchor) {
range = Selection.importSelectionMoveCursorPastAnchor(selectionState, range);
}
sel = this.options.contentWindow.getSelection();
sel.removeAllRanges();
sel.addRange(range);
var editableElement = this.elements[selectionState.editableElementIndex || 0];
Selection.importSelection(selectionState, editableElement, this.options.ownerDocument, favorLaterSelectionAnchor);
},

@@ -989,0 +905,0 @@

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

/*global Button, FormExtension,
AnchorForm, AnchorPreview, AutoLink,
/*global Button, FormExtension, AnchorForm,
AnchorPreview, AutoLink, FileDragging,
FontSizeForm, KeyboardCommands, ImageDragging,

@@ -17,4 +17,5 @@ PasteHandler, Placeholder, Toolbar */

autoLink: AutoLink,
fileDragging: FileDragging,
fontSize: FontSizeForm,
imageDragging: ImageDragging,
imageDragging: ImageDragging, // deprecated
keyboardCommands: KeyboardCommands,

@@ -21,0 +22,0 @@ paste: PasteHandler,

@@ -208,3 +208,3 @@ /*global Extension, Util */

href = Util.ensureUrlHasProtocol(href);
var anchor = Util.createLink(this.document, textNodes, href),
var anchor = Util.createLink(this.document, textNodes, href, this.getEditorOption('targetBlank') ? '_blank' : null),
span = this.document.createElement('span');

@@ -211,0 +211,0 @@ span.setAttribute('data-auto-link', 'true');

@@ -37,2 +37,116 @@ /*global Util */

// http://stackoverflow.com/questions/17678843/cant-restore-selection-after-html-modify-even-if-its-the-same-html
// Tim Down
exportSelection: function (root, doc) {
if (!root) {
return null;
}
var selectionState = null,
selection = doc.getSelection();
if (selection.rangeCount > 0) {
var range = selection.getRangeAt(0),
preSelectionRange = range.cloneRange(),
start;
preSelectionRange.selectNodeContents(root);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
start = preSelectionRange.toString().length;
selectionState = {
start: start,
end: start + range.toString().length
};
// If start = 0 there may still be an empty paragraph before it, but we don't care.
if (start !== 0) {
var emptyBlocksIndex = this.getIndexRelativeToAdjacentEmptyBlocks(doc, root, range.startContainer, range.startOffset);
if (emptyBlocksIndex !== 0) {
selectionState.emptyBlocksIndex = emptyBlocksIndex;
}
}
}
return selectionState;
},
// http://stackoverflow.com/questions/17678843/cant-restore-selection-after-html-modify-even-if-its-the-same-html
// Tim Down
//
// {object} selectionState - the selection to import
// {DOMElement} root - the root element the selection is being restored inside of
// {Document} doc - the document to use for managing selection
// {boolean} [favorLaterSelectionAnchor] - defaults to false. If true, import the cursor immediately
// subsequent to an anchor tag if it would otherwise be placed right at the trailing edge inside the
// anchor. This cursor positioning, even though visually equivalent to the user, can affect behavior
// in MS IE.
importSelection: function (selectionState, root, doc, favorLaterSelectionAnchor) {
if (!selectionState || !root) {
return;
}
var range = doc.createRange();
range.setStart(root, 0);
range.collapse(true);
var node = root,
nodeStack = [],
charIndex = 0,
foundStart = false,
stop = false,
nextCharIndex;
while (!stop && node) {
if (node.nodeType === 3) {
nextCharIndex = charIndex + node.length;
if (!foundStart && selectionState.start >= charIndex && selectionState.start <= nextCharIndex) {
range.setStart(node, selectionState.start - charIndex);
foundStart = true;
}
if (foundStart && selectionState.end >= charIndex && selectionState.end <= nextCharIndex) {
range.setEnd(node, selectionState.end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
var i = node.childNodes.length - 1;
while (i >= 0) {
nodeStack.push(node.childNodes[i]);
i -= 1;
}
}
if (!stop) {
node = nodeStack.pop();
}
}
if (selectionState.emptyBlocksIndex && selectionState.end === nextCharIndex) {
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);
range.collapse(true);
}
// If the selection is right at the ending edge of a link, put it outside the anchor tag instead of inside.
if (favorLaterSelectionAnchor) {
range = Selection.importSelectionMoveCursorPastAnchor(selectionState, range);
}
var sel = doc.getSelection();
sel.removeAllRanges();
sel.addRange(range);
},
// Utility method called from importSelection only

@@ -39,0 +153,0 @@ importSelectionMoveCursorPastAnchor: function (selectionState, range) {

@@ -109,6 +109,9 @@ /*global NodeFilter, Selection*/

*/
createLink: function (document, textNodes, href) {
createLink: function (document, textNodes, href, target) {
var anchor = document.createElement('a');
Util.moveTextRangeIntoElement(textNodes[0], textNodes[textNodes.length - 1], anchor);
anchor.setAttribute('href', href);
if (target) {
anchor.setAttribute('target', target);
}
return anchor;

@@ -728,2 +731,8 @@ },

getContainerEditorElement: function (element) {
return Util.traverseUp(element, function (node) {
return Util.isMediumEditorElement(node);
});
},
isBlockContainer: function (element) {

@@ -730,0 +739,0 @@ return element && element.nodeType !== 3 && this.blockContainerElementNames.indexOf(element.nodeName.toLowerCase()) !== -1;

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

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

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

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

Sorry, the diff of this file is not supported yet

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