diff --git a/README.md b/README.md index e67301f22..bcdd5fa0c 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,10 @@ var editor = new ContentKit.Editor(element, options); * `editor.serialize()` - serialize the current post for persistence. Returns Mobiledoc. * `editor.destroy()` - teardown the editor event listeners, free memory etc. +* `editor.disableEditing()` - stop the user from being able to edit the + current post with their cursor. Programmatic edits are still allowed. +* `editor.enableEditing()` - allow the user to make direct edits directly + to a post's text. ### Programmatic Post Editing diff --git a/src/js/editor/editor.js b/src/js/editor/editor.js index c33550884..d451cf451 100644 --- a/src/js/editor/editor.js +++ b/src/js/editor/editor.js @@ -51,7 +51,6 @@ const defaults = { spellcheck: true, autofocus: true, post: null, - serverHost: '', // FIXME PhantomJS has 'ontouchstart' in window, // causing the stickyToolbar to accidentally be auto-activated // in tests @@ -69,7 +68,9 @@ const defaults = { ], cards: [], cardOptions: {}, - unknownCardHandler: () => { throw new Error('Unknown card encountered'); }, + unknownCardHandler: () => { + throw new Error('Unknown card encountered'); + }, mobiledoc: null }; @@ -133,6 +134,9 @@ function bindSelectionEvent(editor) { } function bindKeyListeners(editor) { + if (!editor.isEditable) { + return; + } editor.addEventListener(document, 'keyup', (event) => { const key = Key.fromEvent(event); if (key.isEscape()) { @@ -141,6 +145,9 @@ function bindKeyListeners(editor) { }); editor.addEventListener(document, 'keydown', (event) => { + if (!editor.isEditable) { + return; + } const key = Key.fromEvent(event); if (key.isDelete()) { @@ -235,7 +242,7 @@ class Editor { this.applyPlaceholder(); element.spellcheck = this.spellcheck; - element.setAttribute('contentEditable', true); + this.enableEditing(); if (this.mobiledoc) { this.post = new MobiledocParser(this.builder).parse(this.mobiledoc); @@ -602,6 +609,31 @@ class Editor { this.removeAllViews(); } + /** + * Keep the user from directly editing the post. Modification via the + * programmatic API is still permitted. + * + * @method disableEditing + * @return undefined + * @public + */ + disableEditing() { + this.isEditable = false; + this.element.setAttribute('contentEditable', false); + } + + /** + * Allow the user to directly interact with editing a post via a cursor. + * + * @method enableEditing + * @return undefined + * @public + */ + enableEditing() { + this.isEditable = true; + this.element.setAttribute('contentEditable', true); + } + /** * Run a new post editing session. Yields a block with a new `postEditor` * instance. This instance can be used to interact with the post abstract, diff --git a/tests/acceptance/basic-editor-test.js b/tests/acceptance/basic-editor-test.js index f061c889a..58e0296b4 100644 --- a/tests/acceptance/basic-editor-test.js +++ b/tests/acceptance/basic-editor-test.js @@ -29,3 +29,21 @@ test('sets element as contenteditable', (assert) => { assert.equal(editorElement.firstChild.tagName, 'P', `editor element has a P as its first child`); }); + +test('#disableEditing and #enableEditing toggle contenteditable', (assert) => { + let innerHTML = `

Hello

`; + editorElement.innerHTML = innerHTML; + editor = new Editor(document.getElementById('editor')); + + assert.equal(editorElement.getAttribute('contenteditable'), + 'true', + 'element is contenteditable'); + editor.disableEditing(); + assert.equal(editorElement.getAttribute('contenteditable'), + 'false', + 'element is not contenteditable'); + editor.enableEditing(); + assert.equal(editorElement.getAttribute('contenteditable'), + 'true', + 'element is contenteditable'); +}); diff --git a/tests/acceptance/editor-sections-test.js b/tests/acceptance/editor-sections-test.js index fb55fd116..156d1d283 100644 --- a/tests/acceptance/editor-sections-test.js +++ b/tests/acceptance/editor-sections-test.js @@ -167,7 +167,22 @@ test('hitting enter at end of a section creates new empty section', (assert) => offset: 0}); }); -test('deleting across 0 sections merges them', (assert) => { +// Phantom does not recognize toggling contenteditable off +Helpers.skipInPhantom('deleting across 2 sections does nothing if editing is disabled', (assert) => { + editor = new Editor(editorElement, {mobiledoc: mobileDocWith2Sections}); + editor.disableEditing(); + assert.equal($('#editor p').length, 2, 'precond - has 2 sections to start'); + + const p0 = $('#editor p:eq(0)')[0], + p1 = $('#editor p:eq(1)')[0]; + + Helpers.dom.selectText('tion', p0, 'sec', p1); + Helpers.dom.triggerDelete(editor); + + assert.equal($('#editor p').length, 2, 'still has 2 sections'); +}); + +test('deleting across 2 sections merges them', (assert) => { editor = new Editor(editorElement, {mobiledoc: mobileDocWith2Sections}); assert.equal($('#editor p').length, 2, 'precond - has 2 sections to start');