diff --git a/spec/extension.spec.js b/spec/extension.spec.js
index 0ef322567..de05357ae 100644
--- a/spec/extension.spec.js
+++ b/spec/extension.spec.js
@@ -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', '
replace
'),
+ editor = this.newMediumEditor('.editable', {
+ extensions: {
+ 'temp-extension': new TempExtension()
+ }
+ });
+
+ expect(editable.innerHTML).toBe('*******
');
+ expect(editor.getContent()).toBe('replace
');
+ });
+
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'),
diff --git a/spec/toolbar.spec.js b/spec/toolbar.spec.js
index 50fcc7a2e..a405e5f1f 100644
--- a/spec/toolbar.spec.js
+++ b/spec/toolbar.spec.js
@@ -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'),
diff --git a/spec/util.spec.js b/spec/util.spec.js
index de688b2bc..568103798 100644
--- a/spec/util.spec.js
+++ b/spec/util.spec.js
@@ -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', '', 'Some Text
');
+ el = this.createElement('div', 'editable', 'Some Text
');
+ this.newMediumEditor('.editable');
MediumEditor.util.isIE = true;
- el.setAttribute('contenteditable', true);
+ //el.setAttribute('contenteditable', true);
selectElementContents(el.querySelector('b'));
spyOn(document, 'execCommand');
@@ -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', ' text
'),
- emptyTextNode = el.firstChild;
+ var el = this.createElement('div', 'editable', ' text
');
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', '', 'paragraph
'),
- span = el.querySelector('span'),
- container = MediumEditor.util.getTopBlockContainer(span);
+ var el = this.createElement('div', 'editable', 'paragraph
');
+ 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', ' text
'),
- emptyTextNode = el.firstChild;
+ var el = this.createElement('div', 'editable', ' text
');
this.newMediumEditor('.editable');
- var container = MediumEditor.util.getTopBlockContainer(emptyTextNode);
+ var container = MediumEditor.util.getTopBlockContainer(el.firstChild);
expect(container).toBe(el);
});
});
@@ -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', 'first second third
'),
- first = el.querySelector('p').firstChild;
+ var el = this.createElement('div', 'editable', 'first second third
');
this.newMediumEditor('.editable');
- var prevSibling = MediumEditor.util.findPreviousSibling(first);
+ var first = el.querySelector('p').firstChild,
+ prevSibling = MediumEditor.util.findPreviousSibling(first);
expect(prevSibling).toBeFalsy();
});
});
diff --git a/src/js/core.js b/src/js/core.js
index e71455b95..b4f13391e 100644
--- a/src/js/core.js
+++ b/src/js/core.js
@@ -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);
+ }
}
}
@@ -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;
}
@@ -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 () {
@@ -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');
@@ -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;
@@ -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);
}
},
@@ -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;
},
@@ -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);
diff --git a/src/js/util.js b/src/js/util.js
index f3007be27..3644642da 100644
--- a/src/js/util.js
+++ b/src/js/util.js
@@ -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'
],
@@ -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') {
@@ -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