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.15.1 to 5.16.0

.node-version

20

API.md

@@ -12,4 +12,4 @@ # MediumEditor Object API (v5.0.0)

- [Event Functions](#event-functions)
- [`on(target, event, listener, useCapture)`](#ontarget-event-listener-usecapture)
- [`off(target, event, listener, useCapture)`](#offtarget-event-listener-usecapture)
- [`on(targets, event, listener, useCapture)`](#ontargets-event-listener-usecapture)
- [`off(targets, event, listener, useCapture)`](#offtargets-event-listener-usecapture)
- [`subscribe(name, listener)`](#subscribename-listener)

@@ -87,11 +87,11 @@ - [`unsubscribe(name, listener)`](#unsubscribename-listener)

### `on(target, event, listener, useCapture)`
### `on(targets, event, listener, useCapture)`
Attaches an event listener to specific element via the browser's built-in `addEventListener(type, listener, useCapture)` API. However, this helper method also ensures that when MediumEditor is destroyed, this event listener will be automatically be detached from the DOM.
Attaches an event listener to a specific element or elements via the browser's built-in `addEventListener(type, listener, useCapture)` API. However, this helper method also ensures that when MediumEditor is destroyed, this event listener will be automatically be detached from the DOM.
**Arguments**
1. _**target** (`HTMLElement`)_:
1. _**targets** (`HTMLElement` / `NodeList`)_:
* Element to attach listener to via `addEventListener(type, listener, useCapture)`
* Element or elements to attach listener to via `addEventListener(type, listener, useCapture)`

@@ -111,11 +111,11 @@ 2. _**event** (`String`)_:

***
### `off(target, event, listener, useCapture)`
### `off(targets, event, listener, useCapture)`
Detach an event listener from a specific element via the browser's built-in `removeEventListener(type, listener, useCapture)` API.
Detach an event listener from a specific element or elements via the browser's built-in `removeEventListener(type, listener, useCapture)` API.
**Arguments**
1. _**target** (`HTMLElement`)_:
1. _**targets** (`HTMLElement` / `NodeList`)_:
* Element to detach listener from via `removeEventListener(type, listener, useCapture)`
* Element or elements to detach listener from via `removeEventListener(type, listener, useCapture)`

@@ -122,0 +122,0 @@ 2. _**event** (`String`)_:

@@ -0,4 +1,12 @@

5.16.0 / 2016-04-12
==================
* Add support for multiple targets for attaching/detach event handlers
* Add support for chaining calls to attach/detach events
* Fix issue with click anchor-preview when using showWhenToolbarIsVisible
* Fix IE issue with line-breaking within editor
* Fix Firefox error when using elements other than divs as editor
5.15.1 / 2016-04-05
==================
* Fix link validation in anchor extension

@@ -8,2 +16,3 @@ * Improve performance when dealing with a lot of data

5.15.0 / 2016-03-23

@@ -10,0 +19,0 @@ ==================

@@ -26,2 +26,3 @@ # MediumEditor Custom Events (v5.0.0)

- [`positionToolbar`](#positiontoolbar)
- [`positionedToolbar`](#positionedtoolbar)
- [`showToolbar`](#showtoolbar)

@@ -156,2 +157,5 @@ - [Proxied Custom Events](#proxied-custom-events)

### `positionedToolbar`
`positionedToolbar` is triggered each time the current selection is checked, the toolbar is displayed, and the toolbar's position was updated. This differs from the `positionToolbar` event in that the visibility and location of the toolbar has already been changed (as opposed to the event triggering before those changes occur). This event will be triggered even if nothing was changed about the toolbar's appearance.
### `showToolbar`

@@ -158,0 +162,0 @@ `showToolbar` is triggered whenever the toolbar was hidden and has just been displayed.

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

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

@@ -240,12 +240,12 @@ /*global fireEvent, selectElementContentsAndFire */

var editor = this.newMediumEditor('.editor', {
delay: 200,
anchorPreview: {
showWhenToolbarIsVisible: true
},
toolbar: {
static: true
}
}),
anchorPreview = editor.getExtensionByName('anchor-preview'),
toolbar = editor.getExtensionByName('toolbar');
delay: 200,
anchorPreview: {
showWhenToolbarIsVisible: true
},
toolbar: {
static: true
}
}),
anchorPreview = editor.getExtensionByName('anchor-preview'),
toolbar = editor.getExtensionByName('toolbar');

@@ -262,17 +262,17 @@ selectElementContentsAndFire(editor.elements[0].firstChild);

expect(toolbar.isDisplayed()).toBe(true);
expect(anchorPreview.getPreviewElement().classList.contains('medium-toolbar-arrow-over')).toBe(true);
expect(anchorPreview.getPreviewElement().classList.contains('medium-editor-anchor-preview-active')).toBe(true);
});
it('should be displayed when the option showWhenToolbarIsVisible is set to true and toolbar is visible', function () {
it('should NOT be displayed when the option showWhenToolbarIsVisible is set to false and toolbar is visible', function () {
var editor = this.newMediumEditor('.editor', {
delay: 200,
anchorPreview: {
showWhenToolbarIsVisible: false
},
toolbar: {
static: true
}
}),
anchorPreview = editor.getExtensionByName('anchor-preview'),
toolbar = editor.getExtensionByName('toolbar');
delay: 200,
anchorPreview: {
showWhenToolbarIsVisible: false
},
toolbar: {
static: true
}
}),
anchorPreview = editor.getExtensionByName('anchor-preview'),
toolbar = editor.getExtensionByName('toolbar');

@@ -287,8 +287,56 @@ selectElementContentsAndFire(editor.elements[0].firstChild);

jasmine.clock().tick(250);
expect(anchorPreview.showPreview).not.toHaveBeenCalled();
expect(toolbar.isDisplayed()).toBe(true);
expect(anchorPreview.getPreviewElement().classList.contains('medium-toolbar-arrow-over')).toBe(false);
expect(anchorPreview.getPreviewElement().classList.contains('medium-editor-anchor-preview-active')).toBe(false);
});
// https://github.com/yabwe/medium-editor/issues/1047
it('should display the anchor form in the toolbar when clicked when showWhenToolbarIsVisible is set to true adn toolbar is visible', function () {
var editor = this.newMediumEditor('.editor', {
anchorPreview: {
showWhenToolbarIsVisible: true
},
toolbar: {
static: true
}
}),
anchorPreview = editor.getExtensionByName('anchor-preview'),
anchor = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
// show toolbar
selectElementContentsAndFire(editor.elements[0].firstChild);
jasmine.clock().tick(1);
expect(toolbar.isDisplayed()).toBe(true);
// show preview
fireEvent(document.getElementById('test-link'), 'mouseover');
// load into editor
jasmine.clock().tick(1);
expect(anchorPreview.getPreviewElement().classList.contains('medium-editor-anchor-preview-active')).toBe(true);
var clickEvent = {
defaultPrevented: false,
preventDefault: function () {
this.defaultPrevented = true;
}
};
// trigger all events toolbar is listening to on clicks
fireEvent(anchorPreview.getPreviewElement(), 'mousedown');
fireEvent(anchorPreview.getPreviewElement(), 'mouseup');
anchorPreview.handleClick(clickEvent);
jasmine.clock().tick(1);
// click on the link should have called `preventDefault` to stop from navigating away
expect(clickEvent.defaultPrevented).toBe(true, 'link navigation was not prevented on click of the anchor-preview');
// anchor form should be visible in toolbar
expect(toolbar.isDisplayed()).toBe(true);
expect(anchor.isDisplayed()).toBe(true, 'anchor form to edit link is not visible');
expect(anchorPreview.getPreviewElement().classList.contains('medium-editor-anchor-preview-active')).toBe(false,
'anchor-preview is still visible after being clicked');
});
it('should NOT be present when anchorPreview option is set to false', function () {

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

@@ -91,2 +91,13 @@ /*global fireEvent, firePreparedEvent,

});
it('should insert a space when within a pre node', function () {
this.el.innerHTML = '<pre>lorem ipsum</pre>';
var editor = this.newMediumEditor('.editor'),
targetNode = editor.elements[0].querySelector('pre');
placeCursorInsideElement(targetNode, 0);
fireEvent(targetNode, 'keydown', {
keyCode: MediumEditor.util.keyCode.TAB
});
expect(this.el.innerHTML).toBe('<pre> lorem ipsum</pre>');
});
});

@@ -403,2 +414,14 @@

});
it('inside an anchor the anchors should be unlinked', function () {
this.el.innerHTML = '<a href="#">test</a>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('a');
spyOn(document, 'execCommand').and.callThrough();
placeCursorInsideElement(target, 1);
fireEvent(target, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(document.execCommand).toHaveBeenCalledWith('unlink', false, null);
});
});

@@ -550,14 +573,57 @@

describe('should unlink anchors', function () {
it('when the user presses enter inside an anchor', function () {
this.el.innerHTML = '<a href="#">test</a>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('a');
spyOn(document, 'execCommand').and.callThrough();
placeCursorInsideElement(target, 1);
fireEvent(target, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
describe('when backspace is pressed', function () {
describe('within a blockquote element', function () {
it('at the start of the blockquote, the blockquote tag should be replaced with a p tag', function () {
this.el.innerHTML = '<blockquote>lorem ipsum</blockquote>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('blockquote');
placeCursorInsideElement(target, 0);
fireEvent(target, 'keydown', {
keyCode: MediumEditor.util.keyCode.BACKSPACE
});
expect(this.el.innerHTML).toBe('<p>lorem ipsum</p>');
});
expect(document.execCommand).toHaveBeenCalledWith('unlink', false, null);
it('NOT at the start of the blockqoute, no formatting should be changed', function () {
this.el.innerHTML = '<blockquote>lorem ipsum</blockquote>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('blockquote');
placeCursorInsideElement(target, 1);
fireEvent(target, 'keydown', {
keyCode: MediumEditor.util.keyCode.BACKSPACE
});
expect(this.el.innerHTML).toBe('<blockquote>lorem ipsum</blockquote>');
});
});
describe('within an empty first list item', function () {
it('should insert a paragraph before the list if it is the first element in the editor', function () {
this.el.innerHTML = '<ul><li></li><li>lorem ipsum</li></ul>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('li'),
range;
placeCursorInsideElement(target, 0);
fireEvent(target, 'keydown', {
keyCode: MediumEditor.util.keyCode.BACKSPACE
});
expect(this.el.innerHTML).toBe('<p><br></p><ul><li>lorem ipsum</li></ul>');
range = document.getSelection().getRangeAt(0);
expect(range.commonAncestorContainer.nodeName.toLowerCase()).toBe('p');
});
it('should not insert a paragraph before the list if it is NOT the first element in the editor', function () {
this.el.innerHTML = '<p>lorem ipsum</p><ul><li></li><li>lorem ipsum</li></ul>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('li');
placeCursorInsideElement(target, 0);
fireEvent(target, 'keydown', {
keyCode: MediumEditor.util.keyCode.BACKSPACE
});
expect(this.el.innerHTML).toBe('<p>lorem ipsum</p><ul><li></li><li>lorem ipsum</li></ul>');
});
});
});

@@ -602,13 +668,2 @@

it('should insert a space when hitting tab key within a pre node', function () {
this.el.innerHTML = '<pre>lorem ipsum</pre>';
var editor = this.newMediumEditor('.editor'),
targetNode = editor.elements[0].querySelector('pre');
placeCursorInsideElement(targetNode, 0);
fireEvent(targetNode, 'keydown', {
keyCode: MediumEditor.util.keyCode.TAB
});
expect(this.el.innerHTML).toBe('<pre> lorem ipsum</pre>');
});
it('should call formatBlock when a keyup results in an empty element', function () {

@@ -629,53 +684,30 @@ this.el.innerHTML = ' ';

describe('when pressing backspace key on blockquote element', function () {
it('should remove the blockquote tag and replace it with p tag when cursor is at the start of the blockquote content', function () {
this.el.innerHTML = '<blockquote>lorem ipsum</blockquote>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('blockquote');
// https://github.com/yabwe/medium-editor/issues/994
it('should not throw an error when keyup occurs within a non-div editor', function () {
var origEC = document.execCommand,
errorCount = 0;
// Wrap document.execCommand so we can detect browser errors when it's called
document.execCommand = function () {
try {
origEC.apply(document, arguments);
} catch (err) {
errorCount++;
throw err;
}
};
placeCursorInsideElement(target, 0);
fireEvent(target, 'keydown', {
keyCode: MediumEditor.util.keyCode.BACKSPACE
});
expect(this.el.innerHTML).toBe('<p>lorem ipsum</p>');
});
this.el.parentNode.removeChild(this.el);
this.el = this.createElement('h1', 'editor', 'M');
it('should not change any formatting when cursor is not at the start of the blockquote content', function () {
this.el.innerHTML = '<blockquote>lorem ipsum</blockquote>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('blockquote');
placeCursorInsideElement(target, 1);
fireEvent(target, 'keydown', {
keyCode: MediumEditor.util.keyCode.BACKSPACE
});
expect(this.el.innerHTML).toBe('<blockquote>lorem ipsum</blockquote>');
var editor = this.newMediumEditor('h1.editor');
editor.elements[0].focus();
selectElementContentsAndFire(editor.elements[0]);
jasmine.clock().tick(1);
fireEvent(editor.elements[0], 'keyup', {
keyCode: MediumEditor.util.keyCode.M
});
});
describe('when deleting an empty first list item via backspace', function () {
it('should insert a paragraph before the list if it is the first element in the editor', function () {
this.el.innerHTML = '<ul><li></li><li>lorem ipsum</li></ul>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('li'),
range;
placeCursorInsideElement(target, 0);
fireEvent(target, 'keydown', {
keyCode: MediumEditor.util.keyCode.BACKSPACE
});
expect(this.el.innerHTML).toBe('<p><br></p><ul><li>lorem ipsum</li></ul>');
range = document.getSelection().getRangeAt(0);
expect(range.commonAncestorContainer.nodeName.toLowerCase()).toBe('p');
});
it('should not insert a paragraph before the list if it is NOT the first element in the editor', function () {
this.el.innerHTML = '<p>lorem ipsum</p><ul><li></li><li>lorem ipsum</li></ul>';
var editor = this.newMediumEditor('.editor'),
target = editor.elements[0].querySelector('li');
placeCursorInsideElement(target, 0);
fireEvent(target, 'keydown', {
keyCode: MediumEditor.util.keyCode.BACKSPACE
});
expect(this.el.innerHTML).toBe('<p>lorem ipsum</p><ul><li></li><li>lorem ipsum</li></ul>');
});
// Restore original document.execCommand
document.execCommand = origEC;
expect(errorCount).toBe(0, 'there was an error thrown when calling document.execCommand()');
});

@@ -682,0 +714,0 @@

@@ -27,2 +27,17 @@ /*global fireEvent, selectElementContents,

});
it('should bind listener even to list of elements', function () {
var el1, el2, elements, editor, spy;
el1 = this.createElement('div');
el1.classList.add('test-element');
el2 = this.createElement('div');
el2.classList.add('test-element');
elements = document.getElementsByClassName('test-element');
spy = jasmine.createSpy('handler');
editor = this.newMediumEditor('.editor');
editor.on(elements, 'click', spy);
fireEvent(el1, 'click');
jasmine.clock().tick(1);
expect(spy).toHaveBeenCalled();
});
});

@@ -42,2 +57,18 @@

});
it('should unbind listener even from list of elements', function () {
var el1, el2, elements, editor, spy;
el1 = this.createElement('div');
el1.classList.add('test-element');
el2 = this.createElement('div');
el2.classList.add('test-element');
elements = document.getElementsByClassName('test-element');
spy = jasmine.createSpy('handler');
editor = this.newMediumEditor('.editor');
editor.on(elements, 'click', spy);
editor.off(elements, 'click', spy);
fireEvent(el1, 'click');
jasmine.clock().tick(1);
expect(spy).not.toHaveBeenCalled();
});
});

@@ -44,0 +75,0 @@

@@ -99,3 +99,14 @@ /*global fireEvent, selectElementContents,

expect(callback).toHaveBeenCalledWith({}, this.el);
});
it('should trigger positionedToolbar custom event when toolbar is moved', function () {
var editor = this.newMediumEditor('.editor'),
callback = jasmine.createSpy();
this.el.innerHTML = 'specOnUpdateToolbarTest';
editor.subscribe('positionedToolbar', callback);
selectElementContentsAndFire(this.el);
expect(callback).toHaveBeenCalledWith({}, this.el);
});

@@ -135,2 +146,25 @@

it('should trigger positionedToolbar after setToolbarPosition and showToolbar is called', function () {
this.el.innerHTML = 'position sanity check';
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
temp = {
update: function () {
expect(toolbar.setToolbarPosition).toHaveBeenCalled();
expect(toolbar.showToolbar).toHaveBeenCalled();
}
};
selectElementContents(this.el);
jasmine.clock().tick(1);
spyOn(toolbar, 'setToolbarPosition').and.callThrough();
spyOn(toolbar, 'showToolbar').and.callThrough();
spyOn(temp, 'update').and.callThrough();
editor.subscribe('positionedToolbar', temp.update);
selectElementContentsAndFire(this.el);
expect(temp.update).toHaveBeenCalled();
expect(toolbar.setToolbarPosition).toHaveBeenCalled();
});
it('should trigger the hideToolbar custom event when toolbar is hidden', function () {

@@ -137,0 +171,0 @@ var editor = this.newMediumEditor('.editor'),

@@ -152,3 +152,5 @@ (function () {

if (MediumEditor.util.isMediumEditorElement(node) && node.children.length === 0) {
// https://github.com/yabwe/medium-editor/issues/994
// Firefox thrown an error when calling `formatBlock` on an empty editable blockContainer that's not a <div>
if (MediumEditor.util.isMediumEditorElement(node) && node.children.length === 0 && !MediumEditor.util.isBlockContainer(node)) {
this.options.ownerDocument.execCommand('formatBlock', false, 'p');

@@ -669,2 +671,4 @@ }

this.events.attachDOMEvent(target, event, listener, useCapture);
return this;
},

@@ -674,2 +678,4 @@

this.events.detachDOMEvent(target, event, listener, useCapture);
return this;
},

@@ -679,2 +685,4 @@

this.events.attachCustomEvent(event, listener);
return this;
},

@@ -684,2 +692,4 @@

this.events.detachCustomEvent(event, listener);
return this;
},

@@ -689,2 +699,4 @@

this.events.triggerCustomEvent(name, data, editable);
return this;
},

@@ -691,0 +703,0 @@

@@ -18,14 +18,22 @@ (function () {

attachDOMEvent: function (target, event, listener, useCapture) {
target.addEventListener(event, listener, useCapture);
this.events.push([target, event, listener, useCapture]);
attachDOMEvent: function (targets, event, listener, useCapture) {
targets = MediumEditor.util.isElement(targets) || [window, document].indexOf(targets) > -1 ? [targets] : targets;
Array.prototype.forEach.call(targets, function (target) {
target.addEventListener(event, listener, useCapture);
this.events.push([target, event, listener, useCapture]);
}.bind(this));
},
detachDOMEvent: function (target, event, listener, useCapture) {
var index = this.indexOfListener(target, event, listener, useCapture),
e;
if (index !== -1) {
e = this.events.splice(index, 1)[0];
e[0].removeEventListener(e[1], e[2], e[3]);
}
detachDOMEvent: function (targets, event, listener, useCapture) {
var index, e;
targets = MediumEditor.util.isElement(targets) || [window, document].indexOf(targets) > -1 ? [targets] : targets;
Array.prototype.forEach.call(targets, function (target) {
index = this.indexOfListener(target, event, listener, useCapture);
if (index !== -1) {
e = this.events.splice(index, 1)[0];
e[0].removeEventListener(e[1], e[2], e[3]);
}
}.bind(this));
},

@@ -32,0 +40,0 @@

@@ -128,4 +128,13 @@ (function () {

this.subscribe('editableMouseover', this.handleEditableMouseover.bind(this));
this.subscribe('positionedToolbar', this.handlePositionedToolbar.bind(this));
},
handlePositionedToolbar: function () {
// If the toolbar is visible and positioned, we don't need to hide the preview
// when showWhenToolbarIsVisible is true
if (!this.showWhenToolbarIsVisible) {
this.hidePreview();
}
},
handleClick: function (event) {

@@ -132,0 +141,0 @@ var anchorExtension = this.base.getExtensionByName('anchor'),

@@ -517,4 +517,3 @@ (function () {

var container = this.base.getFocusedElement(),
selection = this.window.getSelection(),
anchorPreview;
selection = this.window.getSelection();

@@ -526,18 +525,15 @@ // If there isn't a valid selection, bail

if (this.static && !this.relativeContainer) {
if (this.static || !selection.isCollapsed) {
this.showToolbar();
this.positionStaticToolbar(container);
} else if (!selection.isCollapsed) {
this.showToolbar();
// we don't need any absolute positioning if relativeContainer is set
if (!this.relativeContainer) {
this.positionToolbar(selection);
if (this.static) {
this.positionStaticToolbar(container);
} else {
this.positionToolbar(selection);
}
}
}
anchorPreview = this.base.getExtensionByName('anchor-preview');
if (anchorPreview && typeof anchorPreview.hidePreview === 'function') {
anchorPreview.hidePreview();
this.trigger('positionedToolbar', {}, this.base.getFocusedElement());
}

@@ -544,0 +540,0 @@ },

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

// grunt-bump looks for this:
'version': '5.15.1'
'version': '5.16.0'
}).version);

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

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