Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow extensions to hook into the get/set content #1349

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions spec/extension.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,26 @@ describe('Extensions TestCase', function () {
expect(tempExtension.getEditorOption('spellcheck')).toBe(editor.options.spellcheck);
});

it('should be able to manipulate the html via setContent() and getContent()', function () {
var TempExtension = MediumEditor.Extension.extend({
setContent: function (html) {
return html.replace(/replace/g, '*******');
},
getContent: function (html) {
return html.replace(/\*\*\*\*\*\*\*/g, 'replace');
}
}),
editable = this.createElement('div', 'editable', '<p>replace</p>'),
editor = this.newMediumEditor('.editable', {
extensions: {
'temp-extension': new TempExtension()
}
});

expect(editable.innerHTML).toBe('<p>*******</p>');
expect(editor.getContent()).toBe('<p>replace</p>');
});

it('should be able to prevent blur on the editor when user iteracts with extension elements', function () {
var sampleElOne = this.createElement('button', null, 'Test Button'),
sampleElTwo = this.createElement('textarea', null, 'Test Div'),
Expand Down
3 changes: 3 additions & 0 deletions spec/toolbar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,15 @@ describe('MediumEditor.extensions.toolbar TestCase', function () {
expect(document.getElementsByClassName('medium-editor-toolbar-actions').length).toBe(0);
});

/*
// Only disable using options, it is possible to add elements later who don't have the disable-toolbar attr and will not work
it('should not create the toolbar if all elements has data attr of disable-toolbar', function () {
this.el.setAttribute('data-disable-toolbar', 'true');
var editor = this.newMediumEditor('.editor');
expect(document.getElementsByClassName('medium-editor-toolbar-actions').length).toBe(0);
expect(editor.getExtensionByName('toolbar')).toBeUndefined();
});
*/

it('should not show the toolbar when one element has a data attr of disable-toolbar set and text is selected', function () {
var element = this.createElement('div', 'editor', 'lorem ipsum'),
Expand Down
27 changes: 13 additions & 14 deletions spec/util.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,9 +389,10 @@ describe('MediumEditor.util', function () {

it('should execute indent command when called with blockquote when isIE is true', function () {
var origIsIE = MediumEditor.util.isIE,
el = this.createElement('div', '', '<p>Some <b>Text</b></p>');
el = this.createElement('div', 'editable', '<p>Some <b>Text</b></p>');
this.newMediumEditor('.editable');
MediumEditor.util.isIE = true;
el.setAttribute('contenteditable', true);
//el.setAttribute('contenteditable', true);
selectElementContents(el.querySelector('b'));
spyOn(document, 'execCommand');

Expand Down Expand Up @@ -570,27 +571,25 @@ describe('MediumEditor.util', function () {
});

it('should return the parent editable if element is a text node child of the editor', function () {
var el = this.createElement('div', 'editable', ' <p>text</p>'),
emptyTextNode = el.firstChild;
var el = this.createElement('div', 'editable', ' <p>text</p>');
this.newMediumEditor('.editable');
var container = MediumEditor.util.getClosestBlockContainer(emptyTextNode);
var container = MediumEditor.util.getClosestBlockContainer(el.firstChild);
expect(container).toBe(el);
});
});

describe('getTopBlockContainer', function () {
it('should return the highest level block container', function () {
var el = this.createElement('div', '', '<blockquote><p>paragraph</p><ul><li><span>list item</span></li></ul></blockquote>'),
span = el.querySelector('span'),
container = MediumEditor.util.getTopBlockContainer(span);
var el = this.createElement('div', 'editable', '<blockquote><p>paragraph</p><ul><li><span>list item</span></li></ul></blockquote>');
this.newMediumEditor('.editable');
var container = MediumEditor.util.getTopBlockContainer(el.querySelector('span'));
expect(container).toBe(el.querySelector('blockquote'));
});

it('should return the parent editable if element is a text node child of the editor', function () {
var el = this.createElement('div', 'editable', ' <p>text</p>'),
emptyTextNode = el.firstChild;
var el = this.createElement('div', 'editable', ' <p>text</p>');
this.newMediumEditor('.editable');
var container = MediumEditor.util.getTopBlockContainer(emptyTextNode);
var container = MediumEditor.util.getTopBlockContainer(el.firstChild);
expect(container).toBe(el);
});
});
Expand All @@ -613,10 +612,10 @@ describe('MediumEditor.util', function () {
});

it('should not find a previous sibling if the element is at the beginning of an editor element', function () {
var el = this.createElement('div', 'editable', '<p>first <b>second </b><i>third</i></p><ul><li>fourth</li></ul>'),
first = el.querySelector('p').firstChild;
var el = this.createElement('div', 'editable', '<p>first <b>second </b><i>third</i></p><ul><li>fourth</li></ul>');
this.newMediumEditor('.editable');
var prevSibling = MediumEditor.util.findPreviousSibling(first);
var first = el.querySelector('p').firstChild,
prevSibling = MediumEditor.util.findPreviousSibling(first);
expect(prevSibling).toBeFalsy();
});
});
Expand Down
60 changes: 36 additions & 24 deletions src/js/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,19 @@
this.options.ownerDocument.execCommand('formatBlock', false, 'p');
}
}

if (MediumEditor.util.isKey(event, MediumEditor.util.keyCode.ENTER) && node.nodeName.toLowerCase() === 'div') {
this.options.ownerDocument.execCommand('formatBlock', false, 'p');
}
}

function handleEditableInput(event, editable) {
var textarea = editable.parentNode.querySelector('textarea[medium-editor-textarea-id="' + editable.getAttribute('medium-editor-textarea-id') + '"]');
if (textarea) {
textarea.value = editable.innerHTML.trim();
var index = this.elements.indexOf(editable);
if (index !== -1) {
textarea.value = this.getContent(index);
}
}
}

Expand Down Expand Up @@ -315,14 +322,6 @@
}

function isToolbarEnabled() {
// If any of the elements don't have the toolbar disabled
// We need a toolbar
if (this.elements.every(function (element) {
return !!element.getAttribute('data-disable-toolbar');
})) {
return false;
}

return this.options.toolbar !== false;
}

Expand Down Expand Up @@ -694,17 +693,13 @@
this.events = new MediumEditor.Events(this);
this.elements = [];

this.addElements(this.origElements);

if (this.elements.length === 0) {
return;
}

this.isActive = true;

// Call initialization helpers
initExtensions.call(this);
attachHandlers.call(this);

this.addElements(this.origElements);

this.isActive = true;
},

destroy: function () {
Expand All @@ -722,11 +717,9 @@

this.events.destroy();

this.elements.forEach(function (element) {
// Reset elements content, fix for issue where after editor destroyed the red underlines on spelling errors are left
if (this.options.spellcheck) {
element.innerHTML = element.innerHTML;
}
this.elements.forEach(function (element, i) {
// Call the getContent to set innerHTML, extensions can do some cleanup
element.innerHTML = this.getContent(i);

// cleanup extra added attributes
element.removeAttribute('contentEditable');
Expand Down Expand Up @@ -798,7 +791,7 @@
for (i = 0; i < len; i += 1) {
elementid = (this.elements[i].id !== '') ? this.elements[i].id : 'element-' + i;
content[elementid] = {
value: this.elements[i].innerHTML.trim()
value: this.getContent(i)
};
}
return content;
Expand Down Expand Up @@ -1190,7 +1183,15 @@

if (this.elements[index]) {
var target = this.elements[index];

this.extensions.forEach(function (extension) {
if (typeof extension.setContent === 'function') {
html = extension.setContent(html);
}
}, this);

target.innerHTML = html;

this.checkContentChanged(target);
}
},
Expand All @@ -1199,8 +1200,17 @@
index = index || 0;

if (this.elements[index]) {
return this.elements[index].innerHTML.trim();
var html = this.elements[index].innerHTML.trim();

this.extensions.forEach(function (extension) {
if (typeof extension.getContent === 'function') {
html = extension.getContent(html);
}
}, this);

return html;
}

return null;
},

Expand Down Expand Up @@ -1243,6 +1253,8 @@
// Add new elements to our internal elements array
this.elements.push(element);

this.setContent(element.innerHTML, this.elements.length - 1);

// Trigger event so extensions can know when an element has been added
this.trigger('addElement', { target: element, currentTarget: element }, element);
}, this);
Expand Down
19 changes: 17 additions & 2 deletions src/js/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
// all other known block elements
'address', 'article', 'aside', 'audio', 'canvas', 'dd', 'dl', 'dt', 'fieldset',
'figcaption', 'figure', 'footer', 'form', 'header', 'hgroup', 'main', 'nav',
'noscript', 'output', 'section', 'video',
'noscript', 'output', 'section', 'video', 'div',
'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td'
],

Expand Down Expand Up @@ -545,6 +545,21 @@
tagName = '<' + tagName + '>';
}

if (blockContainer) {
var blockTagName = blockContainer.nodeName.toLowerCase();

if (tagName !== blockTagName) {
// Changing list items to something else breaks, remove list item first
if (blockTagName === 'ul') {
doc.execCommand('insertunorderedlist', false);
}

if (blockTagName === 'ol') {
doc.execCommand('insertorderedlist', false);
}
}
}

// When FF, IE and Edge, we have to handle blockquote node seperately as 'formatblock' does not work.
// https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand#Commands
if (blockContainer && blockContainer.nodeName.toLowerCase() === 'blockquote') {
Expand Down Expand Up @@ -926,7 +941,7 @@
},

isBlockContainer: function (element) {
return element && element.nodeType !== 3 && Util.blockContainerElementNames.indexOf(element.nodeName.toLowerCase()) !== -1;
return element && element.nodeType !== 3 && !Util.isMediumEditorElement(element) && Util.blockContainerElementNames.indexOf(element.nodeName.toLowerCase()) !== -1;
},

/* Finds the closest ancestor which is a block container element
Expand Down