1 | import Parchment from 'parchment';
|
2 | import TextBlot from './text';
|
3 |
|
4 |
|
5 | class Cursor extends Parchment.Embed {
|
6 | static value() {
|
7 | return undefined;
|
8 | }
|
9 |
|
10 | constructor(domNode, selection) {
|
11 | super(domNode);
|
12 | this.selection = selection;
|
13 | this.textNode = document.createTextNode(Cursor.CONTENTS);
|
14 | this.domNode.appendChild(this.textNode);
|
15 | this._length = 0;
|
16 | }
|
17 |
|
18 | detach() {
|
19 |
|
20 | if (this.parent != null) this.parent.removeChild(this);
|
21 | }
|
22 |
|
23 | format(name, value) {
|
24 | if (this._length !== 0) {
|
25 | return super.format(name, value);
|
26 | }
|
27 | let target = this, index = 0;
|
28 | while (target != null && target.statics.scope !== Parchment.Scope.BLOCK_BLOT) {
|
29 | index += target.offset(target.parent);
|
30 | target = target.parent;
|
31 | }
|
32 | if (target != null) {
|
33 | this._length = Cursor.CONTENTS.length;
|
34 | target.optimize();
|
35 | target.formatAt(index, Cursor.CONTENTS.length, name, value);
|
36 | this._length = 0;
|
37 | }
|
38 | }
|
39 |
|
40 | index(node, offset) {
|
41 | if (node === this.textNode) return 0;
|
42 | return super.index(node, offset);
|
43 | }
|
44 |
|
45 | length() {
|
46 | return this._length;
|
47 | }
|
48 |
|
49 | position() {
|
50 | return [this.textNode, this.textNode.data.length];
|
51 | }
|
52 |
|
53 | remove() {
|
54 | super.remove();
|
55 | this.parent = null;
|
56 | }
|
57 |
|
58 | restore() {
|
59 | if (this.selection.composing || this.parent == null) return;
|
60 | let textNode = this.textNode;
|
61 | let range = this.selection.getNativeRange();
|
62 | let restoreText, start, end;
|
63 | if (range != null && range.start.node === textNode && range.end.node === textNode) {
|
64 | [restoreText, start, end] = [textNode, range.start.offset, range.end.offset];
|
65 | }
|
66 |
|
67 | while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) {
|
68 | this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode);
|
69 | }
|
70 | if (this.textNode.data !== Cursor.CONTENTS) {
|
71 | let text = this.textNode.data.split(Cursor.CONTENTS).join('');
|
72 | if (this.next instanceof TextBlot) {
|
73 | restoreText = this.next.domNode;
|
74 | this.next.insertAt(0, text);
|
75 | this.textNode.data = Cursor.CONTENTS;
|
76 | } else {
|
77 | this.textNode.data = text;
|
78 | this.parent.insertBefore(Parchment.create(this.textNode), this);
|
79 | this.textNode = document.createTextNode(Cursor.CONTENTS);
|
80 | this.domNode.appendChild(this.textNode);
|
81 | }
|
82 | }
|
83 | this.remove();
|
84 | if (start != null) {
|
85 | [start, end] = [start, end].map(function(offset) {
|
86 | return Math.max(0, Math.min(restoreText.data.length, offset - 1));
|
87 | });
|
88 | return {
|
89 | startNode: restoreText,
|
90 | startOffset: start,
|
91 | endNode: restoreText,
|
92 | endOffset: end
|
93 | };
|
94 | }
|
95 | }
|
96 |
|
97 | update(mutations, context) {
|
98 | if (mutations.some((mutation) => {
|
99 | return mutation.type === 'characterData' && mutation.target === this.textNode;
|
100 | })) {
|
101 | let range = this.restore();
|
102 | if (range) context.range = range;
|
103 | }
|
104 | }
|
105 |
|
106 | value() {
|
107 | return '';
|
108 | }
|
109 | }
|
110 | Cursor.blotName = 'cursor';
|
111 | Cursor.className = 'ql-cursor';
|
112 | Cursor.tagName = 'span';
|
113 | Cursor.CONTENTS = "\uFEFF";
|
114 |
|
115 |
|
116 | export default Cursor;
|