@ckeditor/ckeditor5-block-quote 38.1.0 → 38.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,172 +1,172 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module block-quote/blockquotecommand
7
- */
8
- import { Command } from 'ckeditor5/src/core';
9
- import { first } from 'ckeditor5/src/utils';
10
- /**
11
- * The block quote command plugin.
12
- *
13
- * @extends module:core/command~Command
14
- */
15
- export default class BlockQuoteCommand extends Command {
16
- /**
17
- * @inheritDoc
18
- */
19
- refresh() {
20
- this.value = this._getValue();
21
- this.isEnabled = this._checkEnabled();
22
- }
23
- /**
24
- * Executes the command. When the command {@link #value is on}, all top-most block quotes within
25
- * the selection will be removed. If it is off, all selected blocks will be wrapped with
26
- * a block quote.
27
- *
28
- * @fires execute
29
- * @param options Command options.
30
- * @param options.forceValue If set, it will force the command behavior. If `true`, the command will apply a block quote,
31
- * otherwise the command will remove the block quote. If not set, the command will act basing on its current value.
32
- */
33
- execute(options = {}) {
34
- const model = this.editor.model;
35
- const schema = model.schema;
36
- const selection = model.document.selection;
37
- const blocks = Array.from(selection.getSelectedBlocks());
38
- const value = (options.forceValue === undefined) ? !this.value : options.forceValue;
39
- model.change(writer => {
40
- if (!value) {
41
- this._removeQuote(writer, blocks.filter(findQuote));
42
- }
43
- else {
44
- const blocksToQuote = blocks.filter(block => {
45
- // Already quoted blocks needs to be considered while quoting too
46
- // in order to reuse their <bQ> elements.
47
- return findQuote(block) || checkCanBeQuoted(schema, block);
48
- });
49
- this._applyQuote(writer, blocksToQuote);
50
- }
51
- });
52
- }
53
- /**
54
- * Checks the command's {@link #value}.
55
- */
56
- _getValue() {
57
- const selection = this.editor.model.document.selection;
58
- const firstBlock = first(selection.getSelectedBlocks());
59
- // In the current implementation, the block quote must be an immediate parent of a block element.
60
- return !!(firstBlock && findQuote(firstBlock));
61
- }
62
- /**
63
- * Checks whether the command can be enabled in the current context.
64
- *
65
- * @returns Whether the command should be enabled.
66
- */
67
- _checkEnabled() {
68
- if (this.value) {
69
- return true;
70
- }
71
- const selection = this.editor.model.document.selection;
72
- const schema = this.editor.model.schema;
73
- const firstBlock = first(selection.getSelectedBlocks());
74
- if (!firstBlock) {
75
- return false;
76
- }
77
- return checkCanBeQuoted(schema, firstBlock);
78
- }
79
- /**
80
- * Removes the quote from given blocks.
81
- *
82
- * If blocks which are supposed to be "unquoted" are in the middle of a quote,
83
- * start it or end it, then the quote will be split (if needed) and the blocks
84
- * will be moved out of it, so other quoted blocks remained quoted.
85
- */
86
- _removeQuote(writer, blocks) {
87
- // Unquote all groups of block. Iterate in the reverse order to not break following ranges.
88
- getRangesOfBlockGroups(writer, blocks).reverse().forEach(groupRange => {
89
- if (groupRange.start.isAtStart && groupRange.end.isAtEnd) {
90
- writer.unwrap(groupRange.start.parent);
91
- return;
92
- }
93
- // The group of blocks are at the beginning of an <bQ> so let's move them left (out of the <bQ>).
94
- if (groupRange.start.isAtStart) {
95
- const positionBefore = writer.createPositionBefore(groupRange.start.parent);
96
- writer.move(groupRange, positionBefore);
97
- return;
98
- }
99
- // The blocks are in the middle of an <bQ> so we need to split the <bQ> after the last block
100
- // so we move the items there.
101
- if (!groupRange.end.isAtEnd) {
102
- writer.split(groupRange.end);
103
- }
104
- // Now we are sure that groupRange.end.isAtEnd is true, so let's move the blocks right.
105
- const positionAfter = writer.createPositionAfter(groupRange.end.parent);
106
- writer.move(groupRange, positionAfter);
107
- });
108
- }
109
- /**
110
- * Applies the quote to given blocks.
111
- */
112
- _applyQuote(writer, blocks) {
113
- const quotesToMerge = [];
114
- // Quote all groups of block. Iterate in the reverse order to not break following ranges.
115
- getRangesOfBlockGroups(writer, blocks).reverse().forEach(groupRange => {
116
- let quote = findQuote(groupRange.start);
117
- if (!quote) {
118
- quote = writer.createElement('blockQuote');
119
- writer.wrap(groupRange, quote);
120
- }
121
- quotesToMerge.push(quote);
122
- });
123
- // Merge subsequent <bQ> elements. Reverse the order again because this time we want to go through
124
- // the <bQ> elements in the source order (due to how merge works – it moves the right element's content
125
- // to the first element and removes the right one. Since we may need to merge a couple of subsequent `<bQ>` elements
126
- // we want to keep the reference to the first (furthest left) one.
127
- quotesToMerge.reverse().reduce((currentQuote, nextQuote) => {
128
- if (currentQuote.nextSibling == nextQuote) {
129
- writer.merge(writer.createPositionAfter(currentQuote));
130
- return currentQuote;
131
- }
132
- return nextQuote;
133
- });
134
- }
135
- }
136
- function findQuote(elementOrPosition) {
137
- return elementOrPosition.parent.name == 'blockQuote' ? elementOrPosition.parent : null;
138
- }
139
- /**
140
- * Returns a minimal array of ranges containing groups of subsequent blocks.
141
- *
142
- * content: abcdefgh
143
- * blocks: [ a, b, d, f, g, h ]
144
- * output ranges: [ab]c[d]e[fgh]
145
- */
146
- function getRangesOfBlockGroups(writer, blocks) {
147
- let startPosition;
148
- let i = 0;
149
- const ranges = [];
150
- while (i < blocks.length) {
151
- const block = blocks[i];
152
- const nextBlock = blocks[i + 1];
153
- if (!startPosition) {
154
- startPosition = writer.createPositionBefore(block);
155
- }
156
- if (!nextBlock || block.nextSibling != nextBlock) {
157
- ranges.push(writer.createRange(startPosition, writer.createPositionAfter(block)));
158
- startPosition = null;
159
- }
160
- i++;
161
- }
162
- return ranges;
163
- }
164
- /**
165
- * Checks whether <bQ> can wrap the block.
166
- */
167
- function checkCanBeQuoted(schema, block) {
168
- // TMP will be replaced with schema.checkWrap().
169
- const isBQAllowed = schema.checkChild(block.parent, 'blockQuote');
170
- const isBlockAllowedInBQ = schema.checkChild(['$root', 'blockQuote'], block);
171
- return isBQAllowed && isBlockAllowedInBQ;
172
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module block-quote/blockquotecommand
7
+ */
8
+ import { Command } from 'ckeditor5/src/core';
9
+ import { first } from 'ckeditor5/src/utils';
10
+ /**
11
+ * The block quote command plugin.
12
+ *
13
+ * @extends module:core/command~Command
14
+ */
15
+ export default class BlockQuoteCommand extends Command {
16
+ /**
17
+ * @inheritDoc
18
+ */
19
+ refresh() {
20
+ this.value = this._getValue();
21
+ this.isEnabled = this._checkEnabled();
22
+ }
23
+ /**
24
+ * Executes the command. When the command {@link #value is on}, all top-most block quotes within
25
+ * the selection will be removed. If it is off, all selected blocks will be wrapped with
26
+ * a block quote.
27
+ *
28
+ * @fires execute
29
+ * @param options Command options.
30
+ * @param options.forceValue If set, it will force the command behavior. If `true`, the command will apply a block quote,
31
+ * otherwise the command will remove the block quote. If not set, the command will act basing on its current value.
32
+ */
33
+ execute(options = {}) {
34
+ const model = this.editor.model;
35
+ const schema = model.schema;
36
+ const selection = model.document.selection;
37
+ const blocks = Array.from(selection.getSelectedBlocks());
38
+ const value = (options.forceValue === undefined) ? !this.value : options.forceValue;
39
+ model.change(writer => {
40
+ if (!value) {
41
+ this._removeQuote(writer, blocks.filter(findQuote));
42
+ }
43
+ else {
44
+ const blocksToQuote = blocks.filter(block => {
45
+ // Already quoted blocks needs to be considered while quoting too
46
+ // in order to reuse their <bQ> elements.
47
+ return findQuote(block) || checkCanBeQuoted(schema, block);
48
+ });
49
+ this._applyQuote(writer, blocksToQuote);
50
+ }
51
+ });
52
+ }
53
+ /**
54
+ * Checks the command's {@link #value}.
55
+ */
56
+ _getValue() {
57
+ const selection = this.editor.model.document.selection;
58
+ const firstBlock = first(selection.getSelectedBlocks());
59
+ // In the current implementation, the block quote must be an immediate parent of a block element.
60
+ return !!(firstBlock && findQuote(firstBlock));
61
+ }
62
+ /**
63
+ * Checks whether the command can be enabled in the current context.
64
+ *
65
+ * @returns Whether the command should be enabled.
66
+ */
67
+ _checkEnabled() {
68
+ if (this.value) {
69
+ return true;
70
+ }
71
+ const selection = this.editor.model.document.selection;
72
+ const schema = this.editor.model.schema;
73
+ const firstBlock = first(selection.getSelectedBlocks());
74
+ if (!firstBlock) {
75
+ return false;
76
+ }
77
+ return checkCanBeQuoted(schema, firstBlock);
78
+ }
79
+ /**
80
+ * Removes the quote from given blocks.
81
+ *
82
+ * If blocks which are supposed to be "unquoted" are in the middle of a quote,
83
+ * start it or end it, then the quote will be split (if needed) and the blocks
84
+ * will be moved out of it, so other quoted blocks remained quoted.
85
+ */
86
+ _removeQuote(writer, blocks) {
87
+ // Unquote all groups of block. Iterate in the reverse order to not break following ranges.
88
+ getRangesOfBlockGroups(writer, blocks).reverse().forEach(groupRange => {
89
+ if (groupRange.start.isAtStart && groupRange.end.isAtEnd) {
90
+ writer.unwrap(groupRange.start.parent);
91
+ return;
92
+ }
93
+ // The group of blocks are at the beginning of an <bQ> so let's move them left (out of the <bQ>).
94
+ if (groupRange.start.isAtStart) {
95
+ const positionBefore = writer.createPositionBefore(groupRange.start.parent);
96
+ writer.move(groupRange, positionBefore);
97
+ return;
98
+ }
99
+ // The blocks are in the middle of an <bQ> so we need to split the <bQ> after the last block
100
+ // so we move the items there.
101
+ if (!groupRange.end.isAtEnd) {
102
+ writer.split(groupRange.end);
103
+ }
104
+ // Now we are sure that groupRange.end.isAtEnd is true, so let's move the blocks right.
105
+ const positionAfter = writer.createPositionAfter(groupRange.end.parent);
106
+ writer.move(groupRange, positionAfter);
107
+ });
108
+ }
109
+ /**
110
+ * Applies the quote to given blocks.
111
+ */
112
+ _applyQuote(writer, blocks) {
113
+ const quotesToMerge = [];
114
+ // Quote all groups of block. Iterate in the reverse order to not break following ranges.
115
+ getRangesOfBlockGroups(writer, blocks).reverse().forEach(groupRange => {
116
+ let quote = findQuote(groupRange.start);
117
+ if (!quote) {
118
+ quote = writer.createElement('blockQuote');
119
+ writer.wrap(groupRange, quote);
120
+ }
121
+ quotesToMerge.push(quote);
122
+ });
123
+ // Merge subsequent <bQ> elements. Reverse the order again because this time we want to go through
124
+ // the <bQ> elements in the source order (due to how merge works – it moves the right element's content
125
+ // to the first element and removes the right one. Since we may need to merge a couple of subsequent `<bQ>` elements
126
+ // we want to keep the reference to the first (furthest left) one.
127
+ quotesToMerge.reverse().reduce((currentQuote, nextQuote) => {
128
+ if (currentQuote.nextSibling == nextQuote) {
129
+ writer.merge(writer.createPositionAfter(currentQuote));
130
+ return currentQuote;
131
+ }
132
+ return nextQuote;
133
+ });
134
+ }
135
+ }
136
+ function findQuote(elementOrPosition) {
137
+ return elementOrPosition.parent.name == 'blockQuote' ? elementOrPosition.parent : null;
138
+ }
139
+ /**
140
+ * Returns a minimal array of ranges containing groups of subsequent blocks.
141
+ *
142
+ * content: abcdefgh
143
+ * blocks: [ a, b, d, f, g, h ]
144
+ * output ranges: [ab]c[d]e[fgh]
145
+ */
146
+ function getRangesOfBlockGroups(writer, blocks) {
147
+ let startPosition;
148
+ let i = 0;
149
+ const ranges = [];
150
+ while (i < blocks.length) {
151
+ const block = blocks[i];
152
+ const nextBlock = blocks[i + 1];
153
+ if (!startPosition) {
154
+ startPosition = writer.createPositionBefore(block);
155
+ }
156
+ if (!nextBlock || block.nextSibling != nextBlock) {
157
+ ranges.push(writer.createRange(startPosition, writer.createPositionAfter(block)));
158
+ startPosition = null;
159
+ }
160
+ i++;
161
+ }
162
+ return ranges;
163
+ }
164
+ /**
165
+ * Checks whether <bQ> can wrap the block.
166
+ */
167
+ function checkCanBeQuoted(schema, block) {
168
+ // TMP will be replaced with schema.checkWrap().
169
+ const isBQAllowed = schema.checkChild(block.parent, 'blockQuote');
170
+ const isBlockAllowedInBQ = schema.checkChild(['$root', 'blockQuote'], block);
171
+ return isBQAllowed && isBlockAllowedInBQ;
172
+ }
@@ -1,31 +1,31 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module block-quote/blockquoteediting
7
- */
8
- import { Plugin } from 'ckeditor5/src/core';
9
- import { Enter } from 'ckeditor5/src/enter';
10
- import { Delete } from 'ckeditor5/src/typing';
11
- /**
12
- * The block quote editing.
13
- *
14
- * Introduces the `'blockQuote'` command and the `'blockQuote'` model element.
15
- *
16
- * @extends module:core/plugin~Plugin
17
- */
18
- export default class BlockQuoteEditing extends Plugin {
19
- /**
20
- * @inheritDoc
21
- */
22
- static get pluginName(): "BlockQuoteEditing";
23
- /**
24
- * @inheritDoc
25
- */
26
- static get requires(): readonly [typeof Enter, typeof Delete];
27
- /**
28
- * @inheritDoc
29
- */
30
- init(): void;
31
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module block-quote/blockquoteediting
7
+ */
8
+ import { Plugin } from 'ckeditor5/src/core';
9
+ import { Enter } from 'ckeditor5/src/enter';
10
+ import { Delete } from 'ckeditor5/src/typing';
11
+ /**
12
+ * The block quote editing.
13
+ *
14
+ * Introduces the `'blockQuote'` command and the `'blockQuote'` model element.
15
+ *
16
+ * @extends module:core/plugin~Plugin
17
+ */
18
+ export default class BlockQuoteEditing extends Plugin {
19
+ /**
20
+ * @inheritDoc
21
+ */
22
+ static get pluginName(): "BlockQuoteEditing";
23
+ /**
24
+ * @inheritDoc
25
+ */
26
+ static get requires(): readonly [typeof Enter, typeof Delete];
27
+ /**
28
+ * @inheritDoc
29
+ */
30
+ init(): void;
31
+ }
@@ -1,118 +1,118 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module block-quote/blockquoteediting
7
- */
8
- import { Plugin } from 'ckeditor5/src/core';
9
- import { Enter } from 'ckeditor5/src/enter';
10
- import { Delete } from 'ckeditor5/src/typing';
11
- import BlockQuoteCommand from './blockquotecommand';
12
- /**
13
- * The block quote editing.
14
- *
15
- * Introduces the `'blockQuote'` command and the `'blockQuote'` model element.
16
- *
17
- * @extends module:core/plugin~Plugin
18
- */
19
- export default class BlockQuoteEditing extends Plugin {
20
- /**
21
- * @inheritDoc
22
- */
23
- static get pluginName() {
24
- return 'BlockQuoteEditing';
25
- }
26
- /**
27
- * @inheritDoc
28
- */
29
- static get requires() {
30
- return [Enter, Delete];
31
- }
32
- /**
33
- * @inheritDoc
34
- */
35
- init() {
36
- const editor = this.editor;
37
- const schema = editor.model.schema;
38
- editor.commands.add('blockQuote', new BlockQuoteCommand(editor));
39
- schema.register('blockQuote', {
40
- inheritAllFrom: '$container'
41
- });
42
- editor.conversion.elementToElement({ model: 'blockQuote', view: 'blockquote' });
43
- // Postfixer which cleans incorrect model states connected with block quotes.
44
- editor.model.document.registerPostFixer(writer => {
45
- const changes = editor.model.document.differ.getChanges();
46
- for (const entry of changes) {
47
- if (entry.type == 'insert') {
48
- const element = entry.position.nodeAfter;
49
- if (!element) {
50
- // We are inside a text node.
51
- continue;
52
- }
53
- if (element.is('element', 'blockQuote') && element.isEmpty) {
54
- // Added an empty blockQuote - remove it.
55
- writer.remove(element);
56
- return true;
57
- }
58
- else if (element.is('element', 'blockQuote') && !schema.checkChild(entry.position, element)) {
59
- // Added a blockQuote in incorrect place. Unwrap it so the content inside is not lost.
60
- writer.unwrap(element);
61
- return true;
62
- }
63
- else if (element.is('element')) {
64
- // Just added an element. Check that all children meet the scheme rules.
65
- const range = writer.createRangeIn(element);
66
- for (const child of range.getItems()) {
67
- if (child.is('element', 'blockQuote') &&
68
- !schema.checkChild(writer.createPositionBefore(child), child)) {
69
- writer.unwrap(child);
70
- return true;
71
- }
72
- }
73
- }
74
- }
75
- else if (entry.type == 'remove') {
76
- const parent = entry.position.parent;
77
- if (parent.is('element', 'blockQuote') && parent.isEmpty) {
78
- // Something got removed and now blockQuote is empty. Remove the blockQuote as well.
79
- writer.remove(parent);
80
- return true;
81
- }
82
- }
83
- }
84
- return false;
85
- });
86
- const viewDocument = this.editor.editing.view.document;
87
- const selection = editor.model.document.selection;
88
- const blockQuoteCommand = editor.commands.get('blockQuote');
89
- // Overwrite default Enter key behavior.
90
- // If Enter key is pressed with selection collapsed in empty block inside a quote, break the quote.
91
- this.listenTo(viewDocument, 'enter', (evt, data) => {
92
- if (!selection.isCollapsed || !blockQuoteCommand.value) {
93
- return;
94
- }
95
- const positionParent = selection.getLastPosition().parent;
96
- if (positionParent.isEmpty) {
97
- editor.execute('blockQuote');
98
- editor.editing.view.scrollToTheSelection();
99
- data.preventDefault();
100
- evt.stop();
101
- }
102
- }, { context: 'blockquote' });
103
- // Overwrite default Backspace key behavior.
104
- // If Backspace key is pressed with selection collapsed in first empty block inside a quote, break the quote.
105
- this.listenTo(viewDocument, 'delete', (evt, data) => {
106
- if (data.direction != 'backward' || !selection.isCollapsed || !blockQuoteCommand.value) {
107
- return;
108
- }
109
- const positionParent = selection.getLastPosition().parent;
110
- if (positionParent.isEmpty && !positionParent.previousSibling) {
111
- editor.execute('blockQuote');
112
- editor.editing.view.scrollToTheSelection();
113
- data.preventDefault();
114
- evt.stop();
115
- }
116
- }, { context: 'blockquote' });
117
- }
118
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module block-quote/blockquoteediting
7
+ */
8
+ import { Plugin } from 'ckeditor5/src/core';
9
+ import { Enter } from 'ckeditor5/src/enter';
10
+ import { Delete } from 'ckeditor5/src/typing';
11
+ import BlockQuoteCommand from './blockquotecommand';
12
+ /**
13
+ * The block quote editing.
14
+ *
15
+ * Introduces the `'blockQuote'` command and the `'blockQuote'` model element.
16
+ *
17
+ * @extends module:core/plugin~Plugin
18
+ */
19
+ export default class BlockQuoteEditing extends Plugin {
20
+ /**
21
+ * @inheritDoc
22
+ */
23
+ static get pluginName() {
24
+ return 'BlockQuoteEditing';
25
+ }
26
+ /**
27
+ * @inheritDoc
28
+ */
29
+ static get requires() {
30
+ return [Enter, Delete];
31
+ }
32
+ /**
33
+ * @inheritDoc
34
+ */
35
+ init() {
36
+ const editor = this.editor;
37
+ const schema = editor.model.schema;
38
+ editor.commands.add('blockQuote', new BlockQuoteCommand(editor));
39
+ schema.register('blockQuote', {
40
+ inheritAllFrom: '$container'
41
+ });
42
+ editor.conversion.elementToElement({ model: 'blockQuote', view: 'blockquote' });
43
+ // Postfixer which cleans incorrect model states connected with block quotes.
44
+ editor.model.document.registerPostFixer(writer => {
45
+ const changes = editor.model.document.differ.getChanges();
46
+ for (const entry of changes) {
47
+ if (entry.type == 'insert') {
48
+ const element = entry.position.nodeAfter;
49
+ if (!element) {
50
+ // We are inside a text node.
51
+ continue;
52
+ }
53
+ if (element.is('element', 'blockQuote') && element.isEmpty) {
54
+ // Added an empty blockQuote - remove it.
55
+ writer.remove(element);
56
+ return true;
57
+ }
58
+ else if (element.is('element', 'blockQuote') && !schema.checkChild(entry.position, element)) {
59
+ // Added a blockQuote in incorrect place. Unwrap it so the content inside is not lost.
60
+ writer.unwrap(element);
61
+ return true;
62
+ }
63
+ else if (element.is('element')) {
64
+ // Just added an element. Check that all children meet the scheme rules.
65
+ const range = writer.createRangeIn(element);
66
+ for (const child of range.getItems()) {
67
+ if (child.is('element', 'blockQuote') &&
68
+ !schema.checkChild(writer.createPositionBefore(child), child)) {
69
+ writer.unwrap(child);
70
+ return true;
71
+ }
72
+ }
73
+ }
74
+ }
75
+ else if (entry.type == 'remove') {
76
+ const parent = entry.position.parent;
77
+ if (parent.is('element', 'blockQuote') && parent.isEmpty) {
78
+ // Something got removed and now blockQuote is empty. Remove the blockQuote as well.
79
+ writer.remove(parent);
80
+ return true;
81
+ }
82
+ }
83
+ }
84
+ return false;
85
+ });
86
+ const viewDocument = this.editor.editing.view.document;
87
+ const selection = editor.model.document.selection;
88
+ const blockQuoteCommand = editor.commands.get('blockQuote');
89
+ // Overwrite default Enter key behavior.
90
+ // If Enter key is pressed with selection collapsed in empty block inside a quote, break the quote.
91
+ this.listenTo(viewDocument, 'enter', (evt, data) => {
92
+ if (!selection.isCollapsed || !blockQuoteCommand.value) {
93
+ return;
94
+ }
95
+ const positionParent = selection.getLastPosition().parent;
96
+ if (positionParent.isEmpty) {
97
+ editor.execute('blockQuote');
98
+ editor.editing.view.scrollToTheSelection();
99
+ data.preventDefault();
100
+ evt.stop();
101
+ }
102
+ }, { context: 'blockquote' });
103
+ // Overwrite default Backspace key behavior.
104
+ // If Backspace key is pressed with selection collapsed in first empty block inside a quote, break the quote.
105
+ this.listenTo(viewDocument, 'delete', (evt, data) => {
106
+ if (data.direction != 'backward' || !selection.isCollapsed || !blockQuoteCommand.value) {
107
+ return;
108
+ }
109
+ const positionParent = selection.getLastPosition().parent;
110
+ if (positionParent.isEmpty && !positionParent.previousSibling) {
111
+ editor.execute('blockQuote');
112
+ editor.editing.view.scrollToTheSelection();
113
+ data.preventDefault();
114
+ evt.stop();
115
+ }
116
+ }, { context: 'blockquote' });
117
+ }
118
+ }