UNPKG

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