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;