1 | import { AttributorStore, BlockBlot, EmbedBlot, LeafBlot, Scope } from 'parchment';
|
2 | import Delta from 'quill-delta';
|
3 | import Break from './break.js';
|
4 | import Inline from './inline.js';
|
5 | import TextBlot from './text.js';
|
6 | const NEWLINE_LENGTH = 1;
|
7 | class Block extends BlockBlot {
|
8 | cache = {};
|
9 | delta() {
|
10 | if (this.cache.delta == null) {
|
11 | this.cache.delta = blockDelta(this);
|
12 | }
|
13 | return this.cache.delta;
|
14 | }
|
15 | deleteAt(index, length) {
|
16 | super.deleteAt(index, length);
|
17 | this.cache = {};
|
18 | }
|
19 | formatAt(index, length, name, value) {
|
20 | if (length <= 0) return;
|
21 | if (this.scroll.query(name, Scope.BLOCK)) {
|
22 | if (index + length === this.length()) {
|
23 | this.format(name, value);
|
24 | }
|
25 | } else {
|
26 | super.formatAt(index, Math.min(length, this.length() - index - 1), name, value);
|
27 | }
|
28 | this.cache = {};
|
29 | }
|
30 | insertAt(index, value, def) {
|
31 | if (def != null) {
|
32 | super.insertAt(index, value, def);
|
33 | this.cache = {};
|
34 | return;
|
35 | }
|
36 | if (value.length === 0) return;
|
37 | const lines = value.split('\n');
|
38 | const text = lines.shift();
|
39 | if (text.length > 0) {
|
40 | if (index < this.length() - 1 || this.children.tail == null) {
|
41 | super.insertAt(Math.min(index, this.length() - 1), text);
|
42 | } else {
|
43 | this.children.tail.insertAt(this.children.tail.length(), text);
|
44 | }
|
45 | this.cache = {};
|
46 | }
|
47 |
|
48 |
|
49 | let block = this;
|
50 | lines.reduce((lineIndex, line) => {
|
51 |
|
52 | block = block.split(lineIndex, true);
|
53 | block.insertAt(0, line);
|
54 | return line.length;
|
55 | }, index + text.length);
|
56 | }
|
57 | insertBefore(blot, ref) {
|
58 | const {
|
59 | head
|
60 | } = this.children;
|
61 | super.insertBefore(blot, ref);
|
62 | if (head instanceof Break) {
|
63 | head.remove();
|
64 | }
|
65 | this.cache = {};
|
66 | }
|
67 | length() {
|
68 | if (this.cache.length == null) {
|
69 | this.cache.length = super.length() + NEWLINE_LENGTH;
|
70 | }
|
71 | return this.cache.length;
|
72 | }
|
73 | moveChildren(target, ref) {
|
74 | super.moveChildren(target, ref);
|
75 | this.cache = {};
|
76 | }
|
77 | optimize(context) {
|
78 | super.optimize(context);
|
79 | this.cache = {};
|
80 | }
|
81 | path(index) {
|
82 | return super.path(index, true);
|
83 | }
|
84 | removeChild(child) {
|
85 | super.removeChild(child);
|
86 | this.cache = {};
|
87 | }
|
88 | split(index) {
|
89 | let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
90 | if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) {
|
91 | const clone = this.clone();
|
92 | if (index === 0) {
|
93 | this.parent.insertBefore(clone, this);
|
94 | return this;
|
95 | }
|
96 | this.parent.insertBefore(clone, this.next);
|
97 | return clone;
|
98 | }
|
99 | const next = super.split(index, force);
|
100 | this.cache = {};
|
101 | return next;
|
102 | }
|
103 | }
|
104 | Block.blotName = 'block';
|
105 | Block.tagName = 'P';
|
106 | Block.defaultChild = Break;
|
107 | Block.allowedChildren = [Break, Inline, EmbedBlot, TextBlot];
|
108 | class BlockEmbed extends EmbedBlot {
|
109 | attach() {
|
110 | super.attach();
|
111 | this.attributes = new AttributorStore(this.domNode);
|
112 | }
|
113 | delta() {
|
114 | return new Delta().insert(this.value(), {
|
115 | ...this.formats(),
|
116 | ...this.attributes.values()
|
117 | });
|
118 | }
|
119 | format(name, value) {
|
120 | const attribute = this.scroll.query(name, Scope.BLOCK_ATTRIBUTE);
|
121 | if (attribute != null) {
|
122 |
|
123 | this.attributes.attribute(attribute, value);
|
124 | }
|
125 | }
|
126 | formatAt(index, length, name, value) {
|
127 | this.format(name, value);
|
128 | }
|
129 | insertAt(index, value, def) {
|
130 | if (def != null) {
|
131 | super.insertAt(index, value, def);
|
132 | return;
|
133 | }
|
134 | const lines = value.split('\n');
|
135 | const text = lines.pop();
|
136 | const blocks = lines.map(line => {
|
137 | const block = this.scroll.create(Block.blotName);
|
138 | block.insertAt(0, line);
|
139 | return block;
|
140 | });
|
141 | const ref = this.split(index);
|
142 | blocks.forEach(block => {
|
143 | this.parent.insertBefore(block, ref);
|
144 | });
|
145 | if (text) {
|
146 | this.parent.insertBefore(this.scroll.create('text', text), ref);
|
147 | }
|
148 | }
|
149 | }
|
150 | BlockEmbed.scope = Scope.BLOCK_BLOT;
|
151 |
|
152 |
|
153 | function blockDelta(blot) {
|
154 | let filter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
155 | return blot.descendants(LeafBlot).reduce((delta, leaf) => {
|
156 | if (leaf.length() === 0) {
|
157 | return delta;
|
158 | }
|
159 | return delta.insert(leaf.value(), bubbleFormats(leaf, {}, filter));
|
160 | }, new Delta()).insert('\n', bubbleFormats(blot));
|
161 | }
|
162 | function bubbleFormats(blot) {
|
163 | let formats = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
164 | let filter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
165 | if (blot == null) return formats;
|
166 | if ('formats' in blot && typeof blot.formats === 'function') {
|
167 | formats = {
|
168 | ...formats,
|
169 | ...blot.formats()
|
170 | };
|
171 | if (filter) {
|
172 |
|
173 | delete formats['code-token'];
|
174 | }
|
175 | }
|
176 | if (blot.parent == null || blot.parent.statics.blotName === 'scroll' || blot.parent.statics.scope !== blot.statics.scope) {
|
177 | return formats;
|
178 | }
|
179 | return bubbleFormats(blot.parent, formats, filter);
|
180 | }
|
181 | export { blockDelta, bubbleFormats, BlockEmbed, Block as default };
|
182 |
|
\ | No newline at end of file |