medium-editor
Advanced tools
Comparing version 5.21.1 to 5.22.0
@@ -402,3 +402,3 @@ # MediumEditor Object API (v5.0.0) | ||
* Optional overrides for `cleanTags` and/or `cleanAttrs` for removing specific element types (`cleanTags`) or specific attributes (`cleanAttrs`) from the inserted HTML. See [cleanTags](OPTIONS.md#cleantags) and [cleanAttrs](OPTIONS.md#cleanattrs) in OPTIONS.md for more information. | ||
* Optional overrides for `cleanTags`, `unwrapTags`, and/or `cleanAttrs` for removing/unwrapping specific element types (`cleanTags`/`unwrapTags`), or removing specific attributes (`cleanAttrs`) from the inserted HTML. See [cleanTags](OPTIONS.md#cleantags), [unwrapTags](OPTIONS.md#unwraptags), and [cleanAttrs](OPTIONS.md#cleanattrs) in OPTIONS.md for more information. | ||
@@ -408,3 +408,3 @@ ##### Example | ||
```js | ||
editor.pasteHTML('<p class="classy"><strong>Some Custom HTML</strong></p>', { cleanAttrs: ['class'], cleanTags: ['strong']}); | ||
editor.pasteHTML('<p class="classy"><strong>Some Custom HTML</strong></p>', { cleanAttrs: ['class'], cleanTags: ['strong'], unwrapTags: ['em']}); | ||
``` | ||
@@ -411,0 +411,0 @@ |
@@ -301,3 +301,5 @@ /*global module, require, process*/ | ||
build: process.env.TRAVIS_JOB_ID, | ||
name: 'medium-editor-tests' | ||
name: 'medium-editor-tests', | ||
maxDuration: 900, | ||
idleTimeout: 600 | ||
} | ||
@@ -304,0 +306,0 @@ } |
@@ -14,2 +14,3 @@ # MediumEditor Options (v5.0.0) | ||
- [Core Options](#core-options) | ||
@@ -36,2 +37,3 @@ - [`activeButtonClass`](#activebuttonclass) | ||
- [`lastButtonClass`](#lastbuttonclass) | ||
- [`relativeContainer`](#relativecontainer) | ||
- [`standardizeSelectionStart`](#standardizeselectionstart) | ||
@@ -42,2 +44,3 @@ - [`static`](#static) | ||
- [`sticky`](#sticky) | ||
- [`stickyTopOffset`](#stickytopoffset) | ||
- [`updateOnEmptySelection`](#updateonemptyselection) | ||
@@ -49,2 +52,3 @@ - [Disabling Toolbar](#disabling-toolbar) | ||
- [`showOnEmptyLinks`](#showonemptylinks) | ||
- [`showWhenToolbarIsVisible`](#showwhentoolbarisvisible) | ||
- [Disabling Anchor Preview](#disabling-anchor-preview) | ||
@@ -68,2 +72,3 @@ - [Placeholder Options](#placeholder-options) | ||
- [`cleanTags`](#cleantags) | ||
- [`unwrapTags`](#unwraptags) | ||
- [Disabling Paste Handling](#disabling-paste-handling) | ||
@@ -529,2 +534,9 @@ - [KeyboardCommands Options](#keyboardcommands-options) | ||
*** | ||
#### `unwrapTags` | ||
**Default:** `[]` | ||
List of element tag names to unwrap (remove the element tag but retain its child elements) during paste when __cleanPastedHTML__ is `true` or when calling `cleanPaste(text)` or `pasteHTML(html,options)` helper methods. | ||
*** | ||
### Disabling Paste Handling | ||
@@ -531,0 +543,0 @@ |
{ | ||
"name": "medium-editor", | ||
"version": "5.21.1", | ||
"version": "5.22.0", | ||
"author": "Davi Ferreira <hi@daviferreira.com>", | ||
@@ -51,3 +51,3 @@ "contributors": [ | ||
"grunt-contrib-cssmin": "0.14.0", | ||
"grunt-contrib-jasmine": "0.9.2", | ||
"grunt-contrib-jasmine": "1.0.3", | ||
"grunt-contrib-jshint": "0.11.3", | ||
@@ -54,0 +54,0 @@ "grunt-contrib-uglify": "0.11.0", |
@@ -340,3 +340,4 @@ # MediumEditor | ||
cleanAttrs: ['class', 'style', 'dir'], | ||
cleanTags: ['meta'] | ||
cleanTags: ['meta'], | ||
unwrapTags: [] | ||
} | ||
@@ -352,2 +353,3 @@ }); | ||
* __cleanTags__: list of element tag names to remove during paste when __cleanPastedHTML__ is `true` or when calling `cleanPaste(text)` or `pasteHTML(html,options)` helper methods. Default: `['meta']` | ||
* __unwrapTags__: list of element tag names to unwrap (remove the element tag but retain its child elements) during paste when __cleanPastedHTML__ is `true` or when calling `cleanPaste(text)` or `pasteHTML(html,options)` helper methods. Default: `[]` | ||
@@ -460,3 +462,4 @@ ### KeyboardCommands Options | ||
cleanAttrs: ['style', 'dir'], | ||
cleanTags: ['label', 'meta'] | ||
cleanTags: ['label', 'meta'], | ||
unwrapTags: ['sub', 'sup'] | ||
}, | ||
@@ -463,0 +466,0 @@ anchorPreview: { |
@@ -655,2 +655,18 @@ /*global fireEvent, firePreparedEvent, | ||
}); | ||
describe('within an empty paragraph which is the first element of the editor', function () { | ||
it('should delete the paragraph and place the caret to the next paragraph', function () { | ||
this.el.innerHTML = '<p class=""><br></p><p>test</p>'; | ||
var editor = this.newMediumEditor('.editor'), | ||
target = editor.elements[0].querySelector('p'), | ||
range; | ||
placeCursorInsideElement(target, 0); | ||
fireEvent(target, 'keydown', { | ||
keyCode: MediumEditor.util.keyCode.BACKSPACE | ||
}); | ||
expect(this.el.innerHTML).toBe('<p>test</p>'); | ||
range = document.getSelection().getRangeAt(0); | ||
expect(range.commonAncestorContainer.textContent).toBe('test'); | ||
}); | ||
}); | ||
}); | ||
@@ -657,0 +673,0 @@ |
@@ -719,2 +719,12 @@ /*global selectElementContents, | ||
it('should accept a list of tags to unwrap', function () { | ||
var editor = this.newMediumEditor('.editor'); | ||
selectElementContents(this.el.firstChild); | ||
editor.pasteHTML( | ||
'<div><i>test</i><sub><b>test</b></sub><sup>test</sup></div>', | ||
{ unwrapTags: ['sub', 'sup'] } | ||
); | ||
expect(editor.elements[0].innerHTML).toBe('<div><i>test</i><b>test</b>test</div>'); | ||
}); | ||
it('should respect custom clean up options passed during instantiation', function () { | ||
@@ -724,3 +734,4 @@ var editor = this.newMediumEditor('.editor', { | ||
cleanAttrs: ['style', 'dir'], | ||
cleanTags: ['meta', 'b'] | ||
cleanTags: ['meta', 'b'], | ||
unwrapTags: ['sub', 'sup'] | ||
} | ||
@@ -731,7 +742,9 @@ }); | ||
'<table class="medium-editor-table" dir="ltr" style="border: 1px solid red;"><tbody><tr><td>test</td></tr></tbody></table>' + | ||
'<div><i>test</i><meta name="description" content="test" /><b>test</b></div>' | ||
'<div><i>test</i><meta name="description" content="test" /><b>test</b></div>' + | ||
'<div><i>test</i><sub><b>test</b></sub><sup>test</sup></div>' | ||
); | ||
expect(editor.elements[0].innerHTML).toBe( | ||
'<table class="medium-editor-table"><tbody><tr><td>test</td></tr></tbody></table>' + | ||
'<div><i>test</i></div>' | ||
'<div><i>test</i></div>' + | ||
'<div><i>test</i>test</div>' | ||
); | ||
@@ -738,0 +751,0 @@ }); |
@@ -155,2 +155,13 @@ (function () { | ||
event.preventDefault(); | ||
} else if (MediumEditor.util.isKey(event, MediumEditor.util.keyCode.BACKSPACE) && | ||
MediumEditor.util.isMediumEditorElement(node.parentElement) && | ||
!node.previousElementSibling && | ||
node.nextElementSibling && | ||
isEmpty.test(node.innerHTML)) { | ||
// when cursor is in the first element, it's empty and user presses backspace, | ||
// do delete action instead to get rid of the first element and move caret to 2nd | ||
event.preventDefault(); | ||
MediumEditor.selection.moveCursor(this.options.ownerDocument, node.nextSibling); | ||
node.parentElement.removeChild(node); | ||
} | ||
@@ -157,0 +168,0 @@ } |
@@ -39,4 +39,7 @@ (function () { | ||
attachDOMEvent: function (targets, event, listener, useCapture) { | ||
targets = MediumEditor.util.isElement(targets) || [window, document].indexOf(targets) > -1 ? [targets] : targets; | ||
var win = this.base.options.contentWindow, | ||
doc = this.base.options.ownerDocument; | ||
targets = MediumEditor.util.isElement(targets) || [win, doc].indexOf(targets) > -1 ? [targets] : targets; | ||
Array.prototype.forEach.call(targets, function (target) { | ||
@@ -49,5 +52,8 @@ target.addEventListener(event, listener, useCapture); | ||
detachDOMEvent: function (targets, event, listener, useCapture) { | ||
var index, e; | ||
targets = MediumEditor.util.isElement(targets) || [window, document].indexOf(targets) > -1 ? [targets] : targets; | ||
var index, e, | ||
win = this.base.options.contentWindow, | ||
doc = this.base.options.ownerDocument; | ||
targets = MediumEditor.util.isElement(targets) || [win, doc].indexOf(targets) > -1 ? [targets] : targets; | ||
Array.prototype.forEach.call(targets, function (target) { | ||
@@ -219,19 +225,19 @@ index = this.indexOfListener(target, event, listener, useCapture); | ||
var callListeners = function (args, result) { | ||
if (doc.execCommand.listeners) { | ||
doc.execCommand.listeners.forEach(function (listener) { | ||
listener({ | ||
command: args[0], | ||
value: args[2], | ||
args: args, | ||
result: result | ||
}); | ||
if (doc.execCommand.listeners) { | ||
doc.execCommand.listeners.forEach(function (listener) { | ||
listener({ | ||
command: args[0], | ||
value: args[2], | ||
args: args, | ||
result: result | ||
}); | ||
} | ||
}, | ||
}); | ||
} | ||
}, | ||
// Create a wrapper method for execCommand which will: | ||
// 1) Call document.execCommand with the correct arguments | ||
// 2) Loop through any listeners and notify them that execCommand was called | ||
// passing extra info on the call | ||
// 3) Return the result | ||
// Create a wrapper method for execCommand which will: | ||
// 1) Call document.execCommand with the correct arguments | ||
// 2) Loop through any listeners and notify them that execCommand was called | ||
// passing extra info on the call | ||
// 3) Return the result | ||
wrapper = function () { | ||
@@ -411,6 +417,6 @@ var result = doc.execCommand.orig.apply(this, arguments); | ||
if (hadFocus && | ||
eventObj.type === 'click' && | ||
this.lastMousedownTarget && | ||
(MediumEditor.util.isDescendant(hadFocus, this.lastMousedownTarget, true) || | ||
isElementDescendantOfExtension(this.base.extensions, this.lastMousedownTarget))) { | ||
eventObj.type === 'click' && | ||
this.lastMousedownTarget && | ||
(MediumEditor.util.isDescendant(hadFocus, this.lastMousedownTarget, true) || | ||
isElementDescendantOfExtension(this.base.extensions, this.lastMousedownTarget))) { | ||
toFocus = hadFocus; | ||
@@ -433,3 +439,3 @@ } | ||
var externalEvent = !MediumEditor.util.isDescendant(hadFocus, target, true) && | ||
!isElementDescendantOfExtension(this.base.extensions, target); | ||
!isElementDescendantOfExtension(this.base.extensions, target); | ||
@@ -436,0 +442,0 @@ if (toFocus !== hadFocus) { |
@@ -142,2 +142,9 @@ (function () { | ||
/* unwrapTags: [Array] | ||
* list of element tag names to unwrap (remove the element tag but retain its child elements) | ||
* during paste when __cleanPastedHTML__ is `true` or when | ||
* calling `cleanPaste(text)` or `pasteHTML(html, options)` helper methods. | ||
*/ | ||
unwrapTags: [], | ||
init: function () { | ||
@@ -426,3 +433,4 @@ MediumEditor.Extension.prototype.init.apply(this, arguments); | ||
cleanAttrs: this.cleanAttrs, | ||
cleanTags: this.cleanTags | ||
cleanTags: this.cleanTags, | ||
unwrapTags: this.unwrapTags | ||
}); | ||
@@ -449,2 +457,3 @@ | ||
MediumEditor.util.cleanupTags(workEl, options.cleanTags); | ||
MediumEditor.util.unwrapTags(workEl, options.unwrapTags); | ||
} | ||
@@ -451,0 +460,0 @@ |
@@ -94,4 +94,13 @@ # Extensions | ||
* A Medium Editor extension for converting `1.` and `*` into ordered lists and unordered lists. | ||
* [MediumEditor Toolbar States](https://github.com/davideanderson/medium-editor-toolbar-states) | ||
* An Extension for the medium-editor which allows different toolbar configurations based on the selected element(s). | ||
* [MediumEditor AutoFocus](https://github.com/dazorni/medium-editor-autofocus) | ||
* Autofocus plugin for medium-editor | ||
* [MediumEditor Thaana Keyboard](https://github.com/jawish/medium-editor-thaanakbd) | ||
* Thaana Keyboard extension for medium-editor | ||
* [MediumEditor Merge Fields Plugin](https://github.com/epascarello/merge-fields-plugin-for-medium-editor) | ||
* Add merge fields for medium-editor | ||
* [MediumEditor Google Docs Anchor Preview](https://github.com/patternhq/MediumTools/tree/master/GdocMediumAnchorPreview) | ||
* Google Doc style link preview for medium-editor | ||
## What is a Button? | ||
@@ -127,4 +136,5 @@ **Buttons** are a specific type of Extension which have a contract with the MediumEditor toolbar. Buttons have specific lifecycle methods that MediumEditor and the toolbar use to interact with these specific types of Extensions. These contract create easy hooks, allowing custom buttons to: | ||
* Adds a "Add Paragraph" button which allows for inserting customized paragraphs to MediumEditor | ||
* [MediumEditor Embed Button](https://github.com/orhanveli/medium-editor-embed-button) | ||
* oEmbed based embedding button extension to add rich embeds to your document. | ||
## What is a Form Extension? | ||
@@ -131,0 +141,0 @@ **Form Extensions** are a specific type of Button Extension which collect input from the user via the toolbar. Form Extensions extend from Button, and thus inherit all of the lifecycle methods of a Button. In addition, Form Extensions have some additional methods exposed to interact with MediumEditor and provide some common functionality. |
@@ -1027,9 +1027,13 @@ /*global NodeFilter*/ | ||
cleanupTags: function (el, tags) { | ||
tags.forEach(function (tag) { | ||
if (el.nodeName.toLowerCase() === tag) { | ||
el.parentNode.removeChild(el); | ||
} | ||
}); | ||
if (tags.indexOf(el.nodeName.toLowerCase()) !== -1) { | ||
el.parentNode.removeChild(el); | ||
} | ||
}, | ||
unwrapTags: function (el, tags) { | ||
if (tags.indexOf(el.nodeName.toLowerCase()) !== -1) { | ||
MediumEditor.util.unwrap(el, document); | ||
} | ||
}, | ||
// get the closest parent | ||
@@ -1036,0 +1040,0 @@ getClosestTag: function (el, tag) { |
@@ -18,3 +18,3 @@ MediumEditor.parseVersionString = function (release) { | ||
// grunt-bump looks for this: | ||
'version': '5.21.1' | ||
'version': '5.22.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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1937256
24966
714