diff --git a/examples/index.html b/examples/index.html index b3a812d1..d935d90e 100755 --- a/examples/index.html +++ b/examples/index.html @@ -58,6 +58,26 @@

HTML

+ +
+
+

Plain Text

+ +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero.

+
+ +
+

Plain text blocks don't allow any markup. Newlines are replaced with spaces.

+

Example:

+

+editable.add('.example', {plainText: true})
+          
+
+ +
+
+
diff --git a/examples/index.js b/examples/index.js index c52ad9fd..188eae60 100644 --- a/examples/index.js +++ b/examples/index.js @@ -8,14 +8,17 @@ const editable = new Editable({browserSpellcheck: false}) // Paragraph // --------- -editable.enable('.paragraph-example p', true) +editable.enable('.paragraph-example p', {normalize: true}) eventList(editable) // Text formatting toolbar -editable.enable('.formatting-example p', true) +editable.enable('.formatting-example p', {normalize: true}) setupTooltip() -editable.enable('.styling-example p', true) +// Plain Text +editable.enable('.plain-text-example.example-sheet', {plainText: true}) + +editable.enable('.styling-example p', {normalize: true}) const secondExample = document.querySelector('.formatting-example p') updateCode(secondExample) diff --git a/spec/selection.spec.js b/spec/selection.spec.js index d9a4254a..9a22150c 100644 --- a/spec/selection.spec.js +++ b/spec/selection.spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai' import rangy from 'rangy' +import {Editable} from '../src/core' import Selection from '../src/selection' import Cursor from '../src/cursor' import config from '../src/config' @@ -467,6 +468,47 @@ describe('Selection', function () { expect('isAtEnd' in Selection.prototype).to.equal(true) }) }) + + describe('plain text host', function () { + beforeEach(function () { + this.editable = new Editable() + }) + + describe('with regular text', function () { + beforeEach(function () { + this.div = createElement('
regular text
') + const range = rangy.createRange() + range.selectNodeContents(this.div) + this.selection = new Selection(this.div, range) + + this.editable.enable(this.div, {plainText: true}) + }) + + it('should not make regular text bold on toggle', function () { + this.selection.toggleBold() + expect(this.div.innerHTML).to.equal('regular text') + }) + + it('should not make regular text bold on forceWrap', function () { + this.selection.makeBold() + expect(this.div.innerHTML).to.equal('regular text') + }) + + it('should not make regular text italic on toggle', function () { + this.selection.toggleEmphasis() + expect(this.div.innerHTML).to.equal('regular text') + }) + + it('should not make regular text italic on forceWrap', function () { + this.selection.giveEmphasis() + expect(this.div.innerHTML).to.equal('regular text') + }) + }) + + afterEach(function () { + this.editable.disable(this.div) + }) + }) }) const getHtml = function (tag) { diff --git a/src/block.js b/src/block.js index da42ad07..4e970b12 100644 --- a/src/block.js +++ b/src/block.js @@ -7,11 +7,12 @@ const state = {} export const next = getSibling('nextElementSibling') export const previous = getSibling('previousElementSibling') -export function init (elem, {normalize, shouldSpellcheck}) { +export function init (elem, {normalize, plainText, shouldSpellcheck}) { setBlockId(elem) elem.setAttribute('contenteditable', true) elem.setAttribute('spellcheck', Boolean(shouldSpellcheck)) + elem.setAttribute('data-plaintext', Boolean(plainText)) elem.classList.remove(config.editableDisabledClass) elem.classList.add(config.editableClass) @@ -23,6 +24,7 @@ export function init (elem, {normalize, shouldSpellcheck}) { export function disable (elem) { elem.removeAttribute('contenteditable') elem.removeAttribute('spellcheck') + elem.removeAttribute('data-plaintext') setState(elem, undefined) @@ -30,6 +32,9 @@ export function disable (elem) { elem.classList.add(config.editableDisabledClass) } +export function isPlainTextBlock (elem) { + return elem.getAttribute('data-plaintext') === 'true' +} export function setBlockId (elem) { if (!elem.hasAttribute('data-editable')) { diff --git a/src/core.js b/src/core.js index 57b9e412..d9fbd828 100644 --- a/src/core.js +++ b/src/core.js @@ -93,10 +93,11 @@ export class Editable { * @param {HTMLElement|Array(HTMLElement)|String} target A HTMLElement, an * array of HTMLElement or a query selector representing the target where * the API should be added on. + * @param {normalize, plainText} options Block specific configuration * @chainable */ - add (target) { - this.enable(target) + add (target, options) { + this.enable(target, options) // TODO check css whitespace settings return this } @@ -143,19 +144,26 @@ export class Editable { } /** - * Adds the Editable.JS API to the given target elements. - * - * @method enable - * @param { HTMLElement | undefined } target editable root element(s) - * If no param is specified all editables marked as disabled are enabled. - * @chainable - */ - enable (target, normalize) { + * Adds the Editable.JS API to the given target elements. + * + * @method enable + * @param { HTMLElement | undefined } target editable root element(s) + * If no param is specified all editables marked as disabled are enabled. + * @param {boolean} normalize normalizes target content (legacy param) + * @param {boolean} options.normalize normalizes target content + * @param {boolean} options.plainText prevents text formatting for block + * @chainable + */ + enable (target, options) { + const { + normalize = typeof options === 'boolean' ? options : false, + plainText = false + } = options ?? {} const shouldSpellcheck = this.config.browserSpellcheck const targets = domArray(target || `.${config.editableDisabledClass}`, this.win.document) for (const element of targets) { - block.init(element, {normalize, shouldSpellcheck}) + block.init(element, {normalize, plainText, shouldSpellcheck}) this.dispatcher.notify('init', element) } diff --git a/src/selection.js b/src/selection.js index f19b556d..136ad90a 100644 --- a/src/selection.js +++ b/src/selection.js @@ -3,6 +3,7 @@ import 'rangy/lib/rangy-textrange' import Cursor from './cursor' import * as content from './content' import * as parser from './parser' +import * as block from './block' import config from './config' import highlightSupport from './highlight-support' import highlightText from './highlight-text' @@ -144,6 +145,7 @@ export default class Selection extends Cursor { // toggle('') toggle (elem) { + if (block.isPlainTextBlock(this.host)) return elem = this.adoptElement(elem) this.range = content.toggleTag(this.host, this.range, elem) this.setSelection() @@ -307,6 +309,7 @@ export default class Selection extends Cursor { // the same tagName is affecting the selection this tag will be // remove first. forceWrap (elem) { + if (block.isPlainTextBlock(this.host)) return elem = this.adoptElement(elem) this.range = content.forceWrap(this.host, this.range, elem) this.setSelection()