117 lines
3.1 KiB
JavaScript
117 lines
3.1 KiB
JavaScript
import Parchment from 'parchment';
|
|
import TextBlot from './text';
|
|
|
|
|
|
class Cursor extends Parchment.Embed {
|
|
static value() {
|
|
return undefined;
|
|
}
|
|
|
|
constructor(domNode, selection) {
|
|
super(domNode);
|
|
this.selection = selection;
|
|
this.textNode = document.createTextNode(Cursor.CONTENTS);
|
|
this.domNode.appendChild(this.textNode);
|
|
this._length = 0;
|
|
}
|
|
|
|
detach() {
|
|
// super.detach() will also clear domNode.__blot
|
|
if (this.parent != null) this.parent.removeChild(this);
|
|
}
|
|
|
|
format(name, value) {
|
|
if (this._length !== 0) {
|
|
return super.format(name, value);
|
|
}
|
|
let target = this, index = 0;
|
|
while (target != null && target.statics.scope !== Parchment.Scope.BLOCK_BLOT) {
|
|
index += target.offset(target.parent);
|
|
target = target.parent;
|
|
}
|
|
if (target != null) {
|
|
this._length = Cursor.CONTENTS.length;
|
|
target.optimize();
|
|
target.formatAt(index, Cursor.CONTENTS.length, name, value);
|
|
this._length = 0;
|
|
}
|
|
}
|
|
|
|
index(node, offset) {
|
|
if (node === this.textNode) return 0;
|
|
return super.index(node, offset);
|
|
}
|
|
|
|
length() {
|
|
return this._length;
|
|
}
|
|
|
|
position() {
|
|
return [this.textNode, this.textNode.data.length];
|
|
}
|
|
|
|
remove() {
|
|
super.remove();
|
|
this.parent = null;
|
|
}
|
|
|
|
restore() {
|
|
if (this.selection.composing || this.parent == null) return;
|
|
let textNode = this.textNode;
|
|
let range = this.selection.getNativeRange();
|
|
let restoreText, start, end;
|
|
if (range != null && range.start.node === textNode && range.end.node === textNode) {
|
|
[restoreText, start, end] = [textNode, range.start.offset, range.end.offset];
|
|
}
|
|
// Link format will insert text outside of anchor tag
|
|
while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) {
|
|
this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode);
|
|
}
|
|
if (this.textNode.data !== Cursor.CONTENTS) {
|
|
let text = this.textNode.data.split(Cursor.CONTENTS).join('');
|
|
if (this.next instanceof TextBlot) {
|
|
restoreText = this.next.domNode;
|
|
this.next.insertAt(0, text);
|
|
this.textNode.data = Cursor.CONTENTS;
|
|
} else {
|
|
this.textNode.data = text;
|
|
this.parent.insertBefore(Parchment.create(this.textNode), this);
|
|
this.textNode = document.createTextNode(Cursor.CONTENTS);
|
|
this.domNode.appendChild(this.textNode);
|
|
}
|
|
}
|
|
this.remove();
|
|
if (start != null) {
|
|
[start, end] = [start, end].map(function(offset) {
|
|
return Math.max(0, Math.min(restoreText.data.length, offset - 1));
|
|
});
|
|
return {
|
|
startNode: restoreText,
|
|
startOffset: start,
|
|
endNode: restoreText,
|
|
endOffset: end
|
|
};
|
|
}
|
|
}
|
|
|
|
update(mutations, context) {
|
|
if (mutations.some((mutation) => {
|
|
return mutation.type === 'characterData' && mutation.target === this.textNode;
|
|
})) {
|
|
let range = this.restore();
|
|
if (range) context.range = range;
|
|
}
|
|
}
|
|
|
|
value() {
|
|
return '';
|
|
}
|
|
}
|
|
Cursor.blotName = 'cursor';
|
|
Cursor.className = 'ql-cursor';
|
|
Cursor.tagName = 'span';
|
|
Cursor.CONTENTS = "\uFEFF"; // Zero width no break space
|
|
|
|
|
|
export default Cursor;
|