From d496a1c4c769839eb90e68c9b7519a3e2d1c9811 Mon Sep 17 00:00:00 2001 From: Nate Mielnik Date: Thu, 2 Jun 2016 11:54:16 -0400 Subject: [PATCH] 5.20.0 --- CHANGES.md | 9 +++ dist/js/medium-editor.js | 140 +++++++++++++++++++++++++++++------ dist/js/medium-editor.min.js | 8 +- package.json | 2 +- src/js/version.js | 2 +- 5 files changed, 131 insertions(+), 30 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d345e1c61..52ef0123d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,12 @@ +5.20.0 / 2016-06-02 +================== +* Fix anchor-preview bug where click preview no longer prefills href into anchor form +* Add getEditorFromElement for retrieving an editor instance from an editor element +* Respect form.reset for textarea elements within forms passed into the editor +* Add getContent + resetContent helpers for retrieving/reverting content of editors +* Add support for extensions preventing blur on editor when user interacts with extension elements + + 5.19.1 / 2016-05-28 ================== * Add feature for toggling anchor preview for empty or # links diff --git a/dist/js/medium-editor.js b/dist/js/medium-editor.js index 544b84e33..d1e51b1f7 100644 --- a/dist/js/medium-editor.js +++ b/dist/js/medium-editor.js @@ -1653,6 +1653,19 @@ MediumEditor.extensions = {}; */ setInactive: undefined, + /* getInteractionElements: [function ()] + * + * If the extension renders any elements that the user can interact with, + * this method should be implemented and return the root element or an array + * containing all of the root elements. MediumEditor will call this function + * during interaction to see if the user clicked on something outside of the editor. + * The elements are used to check if the target element of a click or + * other user event is a descendant of any extension elements. + * This way, the editor can also count user interaction within editor elements as + * interactions with the editor, and thus not trigger 'blur' + */ + getInteractionElements: undefined, + /************************ Helpers ************************ * The following are helpers that are either set by MediumEditor * during initialization, or are helper methods which either @@ -2416,6 +2429,26 @@ MediumEditor.extensions = {}; (function () { 'use strict'; + function isElementDescendantOfExtension(extensions, element) { + return extensions.some(function (extension) { + if (typeof extension.getInteractionElements !== 'function') { + return false; + } + + var extensionElements = extension.getInteractionElements(); + if (!extensionElements) { + return false; + } + + if (!Array.isArray(extensionElements)) { + extensionElements = [extensionElements]; + } + return extensionElements.some(function (el) { + return MediumEditor.util.isDescendant(el, element, true); + }); + }); + } + var Events = function (instance) { this.base = instance; this.options = this.base.options; @@ -2792,21 +2825,16 @@ MediumEditor.extensions = {}; }, updateFocus: function (target, eventObj) { - var toolbar = this.base.getExtensionByName('toolbar'), - toolbarEl = toolbar ? toolbar.getToolbarElement() : null, - anchorPreview = this.base.getExtensionByName('anchor-preview'), - previewEl = (anchorPreview && anchorPreview.getPreviewElement) ? anchorPreview.getPreviewElement() : null, - hadFocus = this.base.getFocusedElement(), + var hadFocus = this.base.getFocusedElement(), toFocus; - // For clicks, we need to know if the mousedown that caused the click happened inside the existing focused element. - // If so, we don't want to focus another element + // For clicks, we need to know if the mousedown that caused the click happened inside the existing focused element + // or one of the extension elements. If so, we don't want to focus another element if (hadFocus && eventObj.type === 'click' && this.lastMousedownTarget && (MediumEditor.util.isDescendant(hadFocus, this.lastMousedownTarget, true) || - MediumEditor.util.isDescendant(toolbarEl, this.lastMousedownTarget, true) || - MediumEditor.util.isDescendant(previewEl, this.lastMousedownTarget, true))) { + isElementDescendantOfExtension(this.base.extensions, this.lastMousedownTarget))) { toFocus = hadFocus; } @@ -2822,10 +2850,9 @@ MediumEditor.extensions = {}; }, this); } - // Check if the target is external (not part of the editor, toolbar, or anchorpreview) + // Check if the target is external (not part of the editor, toolbar, or any other extension) var externalEvent = !MediumEditor.util.isDescendant(hadFocus, target, true) && - !MediumEditor.util.isDescendant(toolbarEl, target, true) && - !MediumEditor.util.isDescendant(previewEl, target, true); + !isElementDescendantOfExtension(this.base.extensions, target); if (toFocus !== hadFocus) { // If element has focus, and focus is going outside of editor @@ -3961,6 +3988,11 @@ MediumEditor.extensions = {}; this.attachToEditables(); }, + getInteractionElements: function () { + return this.getPreviewElement(); + }, + + // TODO: Remove this function in 6.0.0 getPreviewElement: function () { return this.anchorPreview; }, @@ -4082,7 +4114,7 @@ MediumEditor.extensions = {}; this.base.delay(function () { if (activeAnchor) { var opts = { - url: activeAnchor.attributes.href.value, + value: activeAnchor.attributes.href.value, target: activeAnchor.getAttribute('target'), buttonClass: activeAnchor.getAttribute('class') }; @@ -5807,6 +5839,10 @@ MediumEditor.extensions = {}; // Toolbar accessors + getInteractionElements: function () { + return this.getToolbarElement(); + }, + getToolbarElement: function () { if (!this.toolbar) { this.toolbar = this.createToolbar(); @@ -6660,17 +6696,17 @@ MediumEditor.extensions = {}; return !this.options.extensions['imageDragging']; } - function createContentEditable(textarea, id, doc) { - var div = doc.createElement('div'), + function createContentEditable(textarea) { + var div = this.options.ownerDocument.createElement('div'), now = Date.now(), - uniqueId = 'medium-editor-' + now + '-' + id, + uniqueId = 'medium-editor-' + now, atts = textarea.attributes; // Some browsers can move pretty fast, since we're using a timestamp // to make a unique-id, ensure that the id is actually unique on the page - while (doc.getElementById(uniqueId)) { + while (this.options.ownerDocument.getElementById(uniqueId)) { now++; - uniqueId = 'medium-editor-' + now + '-' + id; + uniqueId = 'medium-editor-' + now; } div.className = textarea.className; @@ -6687,6 +6723,16 @@ MediumEditor.extensions = {}; } } + // If textarea has a form, listen for reset on the form to clear + // the content of the created div + if (textarea.form) { + this.on(textarea.form, 'reset', function (event) { + if (!event.defaultPrevented) { + this.resetContent(this.options.ownerDocument.getElementById(uniqueId)); + } + }.bind(this)); + } + textarea.classList.add('medium-editor-hidden'); textarea.parentNode.insertBefore( div, @@ -6696,10 +6742,10 @@ MediumEditor.extensions = {}; return div; } - function initElement(element, id) { + function initElement(element, editorId) { if (!element.getAttribute('data-medium-editor-element')) { if (element.nodeName.toLowerCase() === 'textarea') { - element = createContentEditable(element, id, this.options.ownerDocument); + element = createContentEditable.call(this, element); // Make sure we only attach to editableInput once for