Permalink
Browse files

refactor

- Emitting TEXT_CHANGE is moved from Editor to Quill so Editor does not
  need to depend on Emitter.
- Cursor index is passed as parameter to Editor.update so Editor does
  not need to depend on Selection
- Editor.enable is moved into Scroll
- Quill top level modifcation APIs are all using modify now
  • Loading branch information...
1 parent cad7490 commit ac63543b20562dbe68f5d504903da58ff1fe5d92 @jhchen jhchen committed Oct 20, 2016
Showing with 145 additions and 149 deletions.
  1. +5 −0 blots/scroll.js
  2. +18 −36 core/editor.js
  3. +74 −65 core/quill.js
  4. +5 −5 test/helpers/unit.js
  5. +0 −43 test/unit/core/editor.js
  6. +43 −0 test/unit/core/quill.js
View
@@ -22,6 +22,7 @@ class Scroll extends Parchment.Scroll {
}, {});
}
this.optimize();
+ this.enable();
}
deleteAt(index, length) {
@@ -40,6 +41,10 @@ class Scroll extends Parchment.Scroll {
this.optimize();
}
+ enable(enabled = true) {
+ this.domNode.setAttribute('contenteditable', enabled);
+ }
+
formatAt(index, length, format, value) {
if (this.whitelist != null && !this.whitelist[format]) return;
super.formatAt(index, length, format, value);
View
@@ -1,6 +1,5 @@
import Delta from 'quill-delta';
import DeltaOp from 'quill-delta/lib/op';
-import Emitter from './emitter';
import Parchment from 'parchment';
import CodeBlock from '../formats/code';
import CursorBlot from '../blots/cursor';
@@ -11,16 +10,12 @@ import extend from 'extend';
class Editor {
- constructor(scroll, emitter, selection) {
+ constructor(scroll) {
this.scroll = scroll;
- this.selection = selection;
- this.emitter = emitter;
- this.emitter.on(Emitter.events.SCROLL_UPDATE, this.update.bind(this, null));
this.delta = this.getDelta();
- this.enable();
}
- applyDelta(delta, source = Emitter.sources.API) {
+ applyDelta(delta) {
let consumeNextNewline = false;
this.scroll.update();
let scrollLength = this.scroll.length();
@@ -68,19 +63,15 @@ class Editor {
}, 0);
this.scroll.batch = false;
this.scroll.optimize();
- return this.update(delta, source);
+ return this.update(delta);
}
- deleteText(index, length, source = Emitter.sources.API) {
+ deleteText(index, length) {
this.scroll.deleteAt(index, length);
- return this.update(new Delta().retain(index).delete(length), source);
+ return this.update(new Delta().retain(index).delete(length));
}
- enable(enabled = true) {
- this.scroll.domNode.setAttribute('contenteditable', enabled);
- }
-
- formatLine(index, length, formats = {}, source = Emitter.sources.API) {
+ formatLine(index, length, formats = {}) {
this.scroll.update();
Object.keys(formats).forEach((format) => {
let lines = this.scroll.lines(index, Math.max(length, 1));
@@ -98,14 +89,14 @@ class Editor {
});
});
this.scroll.optimize();
- return this.update(new Delta().retain(index).retain(length, clone(formats)), source);
+ return this.update(new Delta().retain(index).retain(length, clone(formats)));
}
- formatText(index, length, formats = {}, source = Emitter.sources.API) {
+ formatText(index, length, formats = {}) {
Object.keys(formats).forEach((format) => {
this.scroll.formatAt(index, length, format, formats[format]);
});
- return this.update(new Delta().retain(index).retain(length, clone(formats)), source);
+ return this.update(new Delta().retain(index).retain(length, clone(formats)));
}
getContents(index, length) {
@@ -154,18 +145,18 @@ class Editor {
}).join('');
}
- insertEmbed(index, embed, value, source = Emitter.sources.API) {
+ insertEmbed(index, embed, value) {
this.scroll.insertAt(index, embed, value);
- return this.update(new Delta().retain(index).insert({ [embed]: value }), source);
+ return this.update(new Delta().retain(index).insert({ [embed]: value }));
}
- insertText(index, text, formats = {}, source = Emitter.sources.API) {
+ insertText(index, text, formats = {}) {
text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
this.scroll.insertAt(index, text);
Object.keys(formats).forEach((format) => {
this.scroll.formatAt(index, text.length, format, formats[format]);
});
- return this.update(new Delta().retain(index).insert(text, clone(formats)), source)
+ return this.update(new Delta().retain(index).insert(text, clone(formats)));
}
isBlank() {
@@ -175,7 +166,7 @@ class Editor {
return child.length() <= 1 && Object.keys(child.formats()).length == 0;
}
- removeFormat(index, length, source) {
+ removeFormat(index, length) {
let text = this.getText(index, length);
let [line, offset] = this.scroll.line(index + length);
let suffixLength = 0, suffix = new Delta();
@@ -190,13 +181,11 @@ class Editor {
let contents = this.getContents(index, length + suffixLength);
let diff = contents.diff(new Delta().insert(text).concat(suffix));
let delta = new Delta().retain(index).concat(diff);
- return this.applyDelta(delta, source);
+ return this.applyDelta(delta);
}
- update(change, source = Emitter.sources.USER, mutations = []) {
+ update(change, mutations = [], cursorIndex = undefined) {
let oldDelta = this.delta;
- let range = this.selection.lastRange;
- let index = range && range.length === 0 ? range.index : undefined;
if (mutations.length === 1 &&
mutations[0].type === 'characterData' &&
Parchment.find(mutations[0].target)) {
@@ -207,7 +196,7 @@ class Editor {
let oldValue = mutations[0].oldValue.replace(CursorBlot.CONTENTS, '');
let oldText = new Delta().insert(oldValue);
let newText = new Delta().insert(textBlot.value());
- let diffDelta = new Delta().retain(index).concat(oldText.diff(newText, index));
+ let diffDelta = new Delta().retain(index).concat(oldText.diff(newText, cursorIndex));
change = diffDelta.reduce(function(delta, op) {
if (op.insert) {
return delta.insert(op.insert, formats);
@@ -219,14 +208,7 @@ class Editor {
} else {
this.delta = this.getDelta();
if (!change || !equal(oldDelta.compose(change), this.delta)) {
- change = oldDelta.diff(this.delta, index);
- }
- }
- if (change.length() > 0) {
- let args = [Emitter.events.TEXT_CHANGE, change, oldDelta, source];
- this.emitter.emit(Emitter.events.EDITOR_CHANGE, ...args);
- if (source !== Emitter.sources.SILENT) {
- this.emitter.emit(...args);
+ change = oldDelta.diff(this.delta, cursorIndex);
}
}
return change;
View
@@ -65,8 +65,8 @@ class Quill {
emitter: this.emitter,
whitelist: this.options.formats
});
+ this.editor = new Editor(this.scroll);
this.selection = new Selection(this.scroll, this.emitter);
- this.editor = new Editor(this.scroll, this.emitter, this.selection);
this.theme = new this.options.theme(this, this.options);
this.keyboard = this.theme.addModule('keyboard');
this.clipboard = this.theme.addModule('clipboard');
@@ -77,6 +77,13 @@ class Quill {
this.root.classList.toggle('ql-blank', this.editor.isBlank());
}
});
+ this.emitter.on(Emitter.events.SCROLL_UPDATE, (source, mutations) => {
+ let range = this.selection.lastRange;
+ let index = range && range.length === 0 ? range.index : undefined;
+ modify.call(this, () => {
+ return this.editor.update(null, mutations, index);
+ }, source);
+ });
let contents = this.clipboard.convert(`<div class='ql-editor' style="white-space: normal;">${html}<p><br></p></div>`);
this.setContents(contents);
this.history.clear();
@@ -104,17 +111,17 @@ class Quill {
deleteText(index, length, source) {
[index, length, , source] = overload(index, length, source);
- return modify.call(this, source, index, -1*length, () => {
- return this.editor.deleteText(index, length, source);
- });
+ return modify.call(this, () => {
+ return this.editor.deleteText(index, length);
+ }, source, index, -1*length);
}
disable() {
this.enable(false);
}
enable(enabled = true) {
- this.editor.enable(enabled);
+ this.scroll.enable(enabled);
this.container.classList.toggle('ql-disabled', !enabled);
if (!enabled) {
this.blur();
@@ -127,38 +134,38 @@ class Quill {
}
format(name, value, source = Emitter.sources.API) {
- if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
- return new Delta();
- }
- let range = this.getSelection(true);
- let change = new Delta();
- if (range == null) return change;
- if (Parchment.query(name, Parchment.Scope.BLOCK)) {
- change = this.formatLine(range, name, value, source);
- } else if (range.length === 0) {
- this.selection.format(name, value);
+ return modify.call(this, () => {
+ let range = this.getSelection(true);
+ let change = new Delta();
+ if (range == null) {
+ return change;
+ } else if (Parchment.query(name, Parchment.Scope.BLOCK)) {
+ change = this.editor.formatLine(range.index, range.length, { [name]: value });
+ } else if (range.length === 0) {
+ this.selection.format(name, value);
+ return change;
+ } else {
+ change = this.editor.formatText(range.index, range.length, { [name]: value });
+ }
+ this.setSelection(range, Emitter.sources.SILENT);
return change;
- } else {
- change = this.formatText(range, name, value, source);
- }
- this.setSelection(range, Emitter.sources.SILENT);
- return change;
+ }, source);
}
formatLine(index, length, name, value, source) {
let formats;
[index, length, formats, source] = overload(index, length, name, value, source);
- return modify.call(this, source, index, 0, () => {
- return this.editor.formatLine(index, length, formats, source);
- });
+ return modify.call(this, () => {
+ return this.editor.formatLine(index, length, formats);
+ }, source, index, 0);
}
formatText(index, length, name, value, source) {
let formats;
[index, length, formats, source] = overload(index, length, name, value, source);
- return modify.call(this, source, index, 0, () => {
- return this.editor.formatText(index, length, formats, source);
- });
+ return modify.call(this, () => {
+ return this.editor.formatText(index, length, formats);
+ }, source, index, 0);
}
getBounds(index, length = 0) {
@@ -206,17 +213,17 @@ class Quill {
}
insertEmbed(index, embed, value, source = Quill.sources.API) {
- return modify.call(this, source, index, null, () => {
- return this.editor.insertEmbed(index, embed, value, source);
- });
+ return modify.call(this, () => {
+ return this.editor.insertEmbed(index, embed, value);
+ }, source, index);
}
insertText(index, text, name, value, source) {
let formats;
[index, , formats, source] = overload(index, 0, name, value, source);
- return modify.call(this, source, index, text.length, () => {
- return this.editor.insertText(index, text, formats, source);
- });
+ return modify.call(this, () => {
+ return this.editor.insertText(index, text, formats);
+ }, source, index, text.length);
}
isEnabled() {
@@ -241,23 +248,22 @@ class Quill {
removeFormat(index, length, source) {
[index, length, , source] = overload(index, length, source);
- return modify.call(this, source, index, null, () => {
- return this.editor.removeFormat(index, length, source);
- });
+ return modify.call(this, () => {
+ return this.editor.removeFormat(index, length);
+ }, source, index);
}
setContents(delta, source = Emitter.sources.API) {
- if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
- return new Delta();
- }
- delta = new Delta(delta).slice();
- let lastOp = delta.ops[delta.ops.length - 1];
- // Quill contents must always end with newline
- if (lastOp == null || lastOp.insert[lastOp.insert.length-1] !== '\n') {
- delta.insert('\n');
- }
- delta.delete(this.getLength());
- return this.editor.applyDelta(delta, source);
+ return modify.call(this, () => {
+ delta = new Delta(delta).slice();
+ let lastOp = delta.ops[delta.ops.length - 1];
+ // Quill contents must always end with newline
+ if (lastOp == null || lastOp.insert[lastOp.insert.length-1] !== '\n') {
+ delta.insert('\n');
+ }
+ delta.delete(this.getLength());
+ return this.editor.applyDelta(delta);
+ }, source);
}
setSelection(index, length, source) {
@@ -282,19 +288,12 @@ class Quill {
}
updateContents(delta, source = Emitter.sources.API) {
- if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
- return new Delta();
- }
- let range = this.getSelection();
- if (Array.isArray(delta)) {
- delta = new Delta(delta.slice());
- }
- let change = this.editor.applyDelta(delta, source);
- if (range != null) {
- range = shiftRange(range, change, source);
- this.setSelection(range, Emitter.sources.SILENT);
- }
- return change;
+ return modify.call(this, () => {
+ if (Array.isArray(delta)) {
+ delta = new Delta(delta.slice());
+ }
+ return this.editor.applyDelta(delta, source);
+ }, source, true);
}
}
Quill.DEFAULTS = {
@@ -377,21 +376,31 @@ function expandConfig(container, userConfig) {
return userConfig;
}
-function modify(source, index, shift, modifier) {
- let change = new Delta();
+// Handle selection preservation and TEXT_CHANGE emission
+// common to modification APIs
+function modify(modifier, source, index, shift) {
if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
return new Delta();
}
- let range = this.getSelection();
- change = modifier();
+ let range = index == null ? null : this.getSelection();
+ let oldDelta = this.editor.delta;
+ let change = modifier();
if (range != null) {
- if (shift === null) {
- range = shiftRange(range, index, change, source);
+ if (index === true) index = range.index;
+ if (shift == null) {
+ range = shiftRange(range, change, source);
} else if (shift !== 0) {
range = shiftRange(range, index, shift, source);
}
this.setSelection(range, Emitter.sources.SILENT);
}
+ if (change.length() > 0) {
+ let args = [Emitter.events.TEXT_CHANGE, change, oldDelta, source];
+ this.emitter.emit(Emitter.events.EDITOR_CHANGE, ...args);
+ if (source !== Emitter.sources.SILENT) {
+ this.emitter.emit(...args);
+ }
+ }
return change;
}
Oops, something went wrong.

0 comments on commit ac63543

Please sign in to comment.