@ckeditor/ckeditor5-typing 40.0.0 → 40.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -27
- package/LICENSE.md +3 -3
- package/package.json +4 -4
- package/src/augmentation.d.ts +27 -27
- package/src/augmentation.js +5 -5
- package/src/delete.d.ts +32 -32
- package/src/delete.js +82 -82
- package/src/deletecommand.d.ts +83 -83
- package/src/deletecommand.js +201 -201
- package/src/deleteobserver.d.ts +55 -55
- package/src/deleteobserver.js +261 -261
- package/src/index.d.ts +24 -24
- package/src/index.js +18 -18
- package/src/input.d.ts +21 -21
- package/src/input.js +141 -141
- package/src/inserttextcommand.d.ts +76 -76
- package/src/inserttextcommand.js +83 -80
- package/src/inserttextobserver.d.ts +59 -59
- package/src/inserttextobserver.js +108 -108
- package/src/texttransformation.d.ts +33 -33
- package/src/texttransformation.js +228 -228
- package/src/textwatcher.d.ts +138 -138
- package/src/textwatcher.js +105 -105
- package/src/twostepcaretmovement.d.ts +232 -199
- package/src/twostepcaretmovement.js +622 -435
- package/src/typing.d.ts +23 -23
- package/src/typing.js +27 -27
- package/src/typingconfig.d.ts +204 -204
- package/src/typingconfig.js +5 -5
- package/src/utils/changebuffer.d.ts +103 -103
- package/src/utils/changebuffer.js +123 -123
- package/src/utils/findattributerange.d.ts +33 -33
- package/src/utils/findattributerange.js +41 -41
- package/src/utils/getlasttextline.d.ts +49 -49
- package/src/utils/getlasttextline.js +43 -43
- package/src/utils/inlinehighlight.d.ts +33 -33
- package/src/utils/inlinehighlight.js +74 -74
|
@@ -1,228 +1,228 @@
|
|
|
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 typing/texttransformation
|
|
7
|
-
*/
|
|
8
|
-
import { Plugin } from '@ckeditor/ckeditor5-core';
|
|
9
|
-
import TextWatcher from './textwatcher';
|
|
10
|
-
import { escapeRegExp } from 'lodash-es';
|
|
11
|
-
// All named transformations.
|
|
12
|
-
const TRANSFORMATIONS = {
|
|
13
|
-
// Common symbols:
|
|
14
|
-
copyright: { from: '(c)', to: '©' },
|
|
15
|
-
registeredTrademark: { from: '(r)', to: '®' },
|
|
16
|
-
trademark: { from: '(tm)', to: '™' },
|
|
17
|
-
// Mathematical:
|
|
18
|
-
oneHalf: { from: /(^|[^/a-z0-9])(1\/2)([^/a-z0-9])$/i, to: [null, '½', null] },
|
|
19
|
-
oneThird: { from: /(^|[^/a-z0-9])(1\/3)([^/a-z0-9])$/i, to: [null, '⅓', null] },
|
|
20
|
-
twoThirds: { from: /(^|[^/a-z0-9])(2\/3)([^/a-z0-9])$/i, to: [null, '⅔', null] },
|
|
21
|
-
oneForth: { from: /(^|[^/a-z0-9])(1\/4)([^/a-z0-9])$/i, to: [null, '¼', null] },
|
|
22
|
-
threeQuarters: { from: /(^|[^/a-z0-9])(3\/4)([^/a-z0-9])$/i, to: [null, '¾', null] },
|
|
23
|
-
lessThanOrEqual: { from: '<=', to: '≤' },
|
|
24
|
-
greaterThanOrEqual: { from: '>=', to: '≥' },
|
|
25
|
-
notEqual: { from: '!=', to: '≠' },
|
|
26
|
-
arrowLeft: { from: '<-', to: '←' },
|
|
27
|
-
arrowRight: { from: '->', to: '→' },
|
|
28
|
-
// Typography:
|
|
29
|
-
horizontalEllipsis: { from: '...', to: '…' },
|
|
30
|
-
enDash: { from: /(^| )(--)( )$/, to: [null, '–', null] },
|
|
31
|
-
emDash: { from: /(^| )(---)( )$/, to: [null, '—', null] },
|
|
32
|
-
// Quotations:
|
|
33
|
-
// English, US
|
|
34
|
-
quotesPrimary: { from: buildQuotesRegExp('"'), to: [null, '“', null, '”'] },
|
|
35
|
-
quotesSecondary: { from: buildQuotesRegExp('\''), to: [null, '‘', null, '’'] },
|
|
36
|
-
// English, UK
|
|
37
|
-
quotesPrimaryEnGb: { from: buildQuotesRegExp('\''), to: [null, '‘', null, '’'] },
|
|
38
|
-
quotesSecondaryEnGb: { from: buildQuotesRegExp('"'), to: [null, '“', null, '”'] },
|
|
39
|
-
// Polish
|
|
40
|
-
quotesPrimaryPl: { from: buildQuotesRegExp('"'), to: [null, '„', null, '”'] },
|
|
41
|
-
quotesSecondaryPl: { from: buildQuotesRegExp('\''), to: [null, '‚', null, '’'] }
|
|
42
|
-
};
|
|
43
|
-
// Transformation groups.
|
|
44
|
-
const TRANSFORMATION_GROUPS = {
|
|
45
|
-
symbols: ['copyright', 'registeredTrademark', 'trademark'],
|
|
46
|
-
mathematical: [
|
|
47
|
-
'oneHalf', 'oneThird', 'twoThirds', 'oneForth', 'threeQuarters',
|
|
48
|
-
'lessThanOrEqual', 'greaterThanOrEqual', 'notEqual',
|
|
49
|
-
'arrowLeft', 'arrowRight'
|
|
50
|
-
],
|
|
51
|
-
typography: ['horizontalEllipsis', 'enDash', 'emDash'],
|
|
52
|
-
quotes: ['quotesPrimary', 'quotesSecondary']
|
|
53
|
-
};
|
|
54
|
-
// A set of default transformations provided by the feature.
|
|
55
|
-
const DEFAULT_TRANSFORMATIONS = [
|
|
56
|
-
'symbols',
|
|
57
|
-
'mathematical',
|
|
58
|
-
'typography',
|
|
59
|
-
'quotes'
|
|
60
|
-
];
|
|
61
|
-
/**
|
|
62
|
-
* The text transformation plugin.
|
|
63
|
-
*/
|
|
64
|
-
export default class TextTransformation extends Plugin {
|
|
65
|
-
/**
|
|
66
|
-
* @inheritDoc
|
|
67
|
-
*/
|
|
68
|
-
static get requires() {
|
|
69
|
-
return ['Delete', 'Input'];
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* @inheritDoc
|
|
73
|
-
*/
|
|
74
|
-
static get pluginName() {
|
|
75
|
-
return 'TextTransformation';
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* @inheritDoc
|
|
79
|
-
*/
|
|
80
|
-
constructor(editor) {
|
|
81
|
-
super(editor);
|
|
82
|
-
editor.config.define('typing', {
|
|
83
|
-
transformations: {
|
|
84
|
-
include: DEFAULT_TRANSFORMATIONS
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* @inheritDoc
|
|
90
|
-
*/
|
|
91
|
-
init() {
|
|
92
|
-
const model = this.editor.model;
|
|
93
|
-
const modelSelection = model.document.selection;
|
|
94
|
-
modelSelection.on('change:range', () => {
|
|
95
|
-
// Disable plugin when selection is inside a code block.
|
|
96
|
-
this.isEnabled = !modelSelection.anchor.parent.is('element', 'codeBlock');
|
|
97
|
-
});
|
|
98
|
-
this._enableTransformationWatchers();
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Create new TextWatcher listening to the editor for typing and selection events.
|
|
102
|
-
*/
|
|
103
|
-
_enableTransformationWatchers() {
|
|
104
|
-
const editor = this.editor;
|
|
105
|
-
const model = editor.model;
|
|
106
|
-
const deletePlugin = editor.plugins.get('Delete');
|
|
107
|
-
const normalizedTransformations = normalizeTransformations(editor.config.get('typing.transformations'));
|
|
108
|
-
const testCallback = (text) => {
|
|
109
|
-
for (const normalizedTransformation of normalizedTransformations) {
|
|
110
|
-
const from = normalizedTransformation.from;
|
|
111
|
-
const match = from.test(text);
|
|
112
|
-
if (match) {
|
|
113
|
-
return { normalizedTransformation };
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
const watcher = new TextWatcher(editor.model, testCallback);
|
|
118
|
-
watcher.on('matched:data', (evt, data) => {
|
|
119
|
-
if (!data.batch.isTyping) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const { from, to } = data.normalizedTransformation;
|
|
123
|
-
const matches = from.exec(data.text);
|
|
124
|
-
const replaces = to(matches.slice(1));
|
|
125
|
-
const matchedRange = data.range;
|
|
126
|
-
let changeIndex = matches.index;
|
|
127
|
-
model.enqueueChange(writer => {
|
|
128
|
-
for (let i = 1; i < matches.length; i++) {
|
|
129
|
-
const match = matches[i];
|
|
130
|
-
const replaceWith = replaces[i - 1];
|
|
131
|
-
if (replaceWith == null) {
|
|
132
|
-
changeIndex += match.length;
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
const replacePosition = matchedRange.start.getShiftedBy(changeIndex);
|
|
136
|
-
const replaceRange = model.createRange(replacePosition, replacePosition.getShiftedBy(match.length));
|
|
137
|
-
const attributes = getTextAttributesAfterPosition(replacePosition);
|
|
138
|
-
model.insertContent(writer.createText(replaceWith, attributes), replaceRange);
|
|
139
|
-
changeIndex += replaceWith.length;
|
|
140
|
-
}
|
|
141
|
-
model.enqueueChange(() => {
|
|
142
|
-
deletePlugin.requestUndoOnBackspace();
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
watcher.bind('isEnabled').to(this);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Normalizes the configuration `from` parameter value.
|
|
151
|
-
* The normalized value for the `from` parameter is a RegExp instance. If the passed `from` is already a RegExp instance,
|
|
152
|
-
* it is returned unchanged.
|
|
153
|
-
*/
|
|
154
|
-
function normalizeFrom(from) {
|
|
155
|
-
if (typeof from == 'string') {
|
|
156
|
-
return new RegExp(`(${escapeRegExp(from)})$`);
|
|
157
|
-
}
|
|
158
|
-
// `from` is already a regular expression.
|
|
159
|
-
return from;
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Normalizes the configuration `to` parameter value.
|
|
163
|
-
* The normalized value for the `to` parameter is a function that takes an array and returns an array. See more in the
|
|
164
|
-
* configuration description. If the passed `to` is already a function, it is returned unchanged.
|
|
165
|
-
*/
|
|
166
|
-
function normalizeTo(to) {
|
|
167
|
-
if (typeof to == 'string') {
|
|
168
|
-
return () => [to];
|
|
169
|
-
}
|
|
170
|
-
else if (to instanceof Array) {
|
|
171
|
-
return () => to;
|
|
172
|
-
}
|
|
173
|
-
// `to` is already a function.
|
|
174
|
-
return to;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* For given `position` returns attributes for the text that is after that position.
|
|
178
|
-
* The text can be in the same text node as the position (`foo[]bar`) or in the next text node (`foo[]<$text bold="true">bar</$text>`).
|
|
179
|
-
*/
|
|
180
|
-
function getTextAttributesAfterPosition(position) {
|
|
181
|
-
const textNode = position.textNode ? position.textNode : position.nodeAfter;
|
|
182
|
-
return textNode.getAttributes();
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Returns a RegExp pattern string that detects a sentence inside a quote.
|
|
186
|
-
*
|
|
187
|
-
* @param quoteCharacter The character to create a pattern for.
|
|
188
|
-
*/
|
|
189
|
-
function buildQuotesRegExp(quoteCharacter) {
|
|
190
|
-
return new RegExp(`(^|\\s)(${quoteCharacter})([^${quoteCharacter}]*)(${quoteCharacter})$`);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Reads text transformation config and returns normalized array of transformations objects.
|
|
194
|
-
*/
|
|
195
|
-
function normalizeTransformations(config) {
|
|
196
|
-
const extra = config.extra || [];
|
|
197
|
-
const remove = config.remove || [];
|
|
198
|
-
const isNotRemoved = (transformation) => !remove.includes(transformation);
|
|
199
|
-
const configured = config.include.concat(extra).filter(isNotRemoved);
|
|
200
|
-
return expandGroupsAndRemoveDuplicates(configured)
|
|
201
|
-
.filter(isNotRemoved) // Filter out 'remove' transformations as they might be set in group.
|
|
202
|
-
.map(transformation => (typeof transformation == 'string' && TRANSFORMATIONS[transformation] ? TRANSFORMATIONS[transformation] : transformation))
|
|
203
|
-
// Filter out transformations set as string that has not been found.
|
|
204
|
-
.filter((transformation) => typeof transformation === 'object')
|
|
205
|
-
.map(transformation => ({
|
|
206
|
-
from: normalizeFrom(transformation.from),
|
|
207
|
-
to: normalizeTo(transformation.to)
|
|
208
|
-
}));
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Reads definitions and expands named groups if needed to transformation names.
|
|
212
|
-
* This method also removes duplicated named transformations if any.
|
|
213
|
-
*/
|
|
214
|
-
function expandGroupsAndRemoveDuplicates(definitions) {
|
|
215
|
-
// Set is using to make sure that transformation names are not duplicated.
|
|
216
|
-
const definedTransformations = new Set();
|
|
217
|
-
for (const transformationOrGroup of definitions) {
|
|
218
|
-
if (typeof transformationOrGroup == 'string' && TRANSFORMATION_GROUPS[transformationOrGroup]) {
|
|
219
|
-
for (const transformation of TRANSFORMATION_GROUPS[transformationOrGroup]) {
|
|
220
|
-
definedTransformations.add(transformation);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
definedTransformations.add(transformationOrGroup);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return Array.from(definedTransformations);
|
|
228
|
-
}
|
|
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 typing/texttransformation
|
|
7
|
+
*/
|
|
8
|
+
import { Plugin } from '@ckeditor/ckeditor5-core';
|
|
9
|
+
import TextWatcher from './textwatcher';
|
|
10
|
+
import { escapeRegExp } from 'lodash-es';
|
|
11
|
+
// All named transformations.
|
|
12
|
+
const TRANSFORMATIONS = {
|
|
13
|
+
// Common symbols:
|
|
14
|
+
copyright: { from: '(c)', to: '©' },
|
|
15
|
+
registeredTrademark: { from: '(r)', to: '®' },
|
|
16
|
+
trademark: { from: '(tm)', to: '™' },
|
|
17
|
+
// Mathematical:
|
|
18
|
+
oneHalf: { from: /(^|[^/a-z0-9])(1\/2)([^/a-z0-9])$/i, to: [null, '½', null] },
|
|
19
|
+
oneThird: { from: /(^|[^/a-z0-9])(1\/3)([^/a-z0-9])$/i, to: [null, '⅓', null] },
|
|
20
|
+
twoThirds: { from: /(^|[^/a-z0-9])(2\/3)([^/a-z0-9])$/i, to: [null, '⅔', null] },
|
|
21
|
+
oneForth: { from: /(^|[^/a-z0-9])(1\/4)([^/a-z0-9])$/i, to: [null, '¼', null] },
|
|
22
|
+
threeQuarters: { from: /(^|[^/a-z0-9])(3\/4)([^/a-z0-9])$/i, to: [null, '¾', null] },
|
|
23
|
+
lessThanOrEqual: { from: '<=', to: '≤' },
|
|
24
|
+
greaterThanOrEqual: { from: '>=', to: '≥' },
|
|
25
|
+
notEqual: { from: '!=', to: '≠' },
|
|
26
|
+
arrowLeft: { from: '<-', to: '←' },
|
|
27
|
+
arrowRight: { from: '->', to: '→' },
|
|
28
|
+
// Typography:
|
|
29
|
+
horizontalEllipsis: { from: '...', to: '…' },
|
|
30
|
+
enDash: { from: /(^| )(--)( )$/, to: [null, '–', null] },
|
|
31
|
+
emDash: { from: /(^| )(---)( )$/, to: [null, '—', null] },
|
|
32
|
+
// Quotations:
|
|
33
|
+
// English, US
|
|
34
|
+
quotesPrimary: { from: buildQuotesRegExp('"'), to: [null, '“', null, '”'] },
|
|
35
|
+
quotesSecondary: { from: buildQuotesRegExp('\''), to: [null, '‘', null, '’'] },
|
|
36
|
+
// English, UK
|
|
37
|
+
quotesPrimaryEnGb: { from: buildQuotesRegExp('\''), to: [null, '‘', null, '’'] },
|
|
38
|
+
quotesSecondaryEnGb: { from: buildQuotesRegExp('"'), to: [null, '“', null, '”'] },
|
|
39
|
+
// Polish
|
|
40
|
+
quotesPrimaryPl: { from: buildQuotesRegExp('"'), to: [null, '„', null, '”'] },
|
|
41
|
+
quotesSecondaryPl: { from: buildQuotesRegExp('\''), to: [null, '‚', null, '’'] }
|
|
42
|
+
};
|
|
43
|
+
// Transformation groups.
|
|
44
|
+
const TRANSFORMATION_GROUPS = {
|
|
45
|
+
symbols: ['copyright', 'registeredTrademark', 'trademark'],
|
|
46
|
+
mathematical: [
|
|
47
|
+
'oneHalf', 'oneThird', 'twoThirds', 'oneForth', 'threeQuarters',
|
|
48
|
+
'lessThanOrEqual', 'greaterThanOrEqual', 'notEqual',
|
|
49
|
+
'arrowLeft', 'arrowRight'
|
|
50
|
+
],
|
|
51
|
+
typography: ['horizontalEllipsis', 'enDash', 'emDash'],
|
|
52
|
+
quotes: ['quotesPrimary', 'quotesSecondary']
|
|
53
|
+
};
|
|
54
|
+
// A set of default transformations provided by the feature.
|
|
55
|
+
const DEFAULT_TRANSFORMATIONS = [
|
|
56
|
+
'symbols',
|
|
57
|
+
'mathematical',
|
|
58
|
+
'typography',
|
|
59
|
+
'quotes'
|
|
60
|
+
];
|
|
61
|
+
/**
|
|
62
|
+
* The text transformation plugin.
|
|
63
|
+
*/
|
|
64
|
+
export default class TextTransformation extends Plugin {
|
|
65
|
+
/**
|
|
66
|
+
* @inheritDoc
|
|
67
|
+
*/
|
|
68
|
+
static get requires() {
|
|
69
|
+
return ['Delete', 'Input'];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @inheritDoc
|
|
73
|
+
*/
|
|
74
|
+
static get pluginName() {
|
|
75
|
+
return 'TextTransformation';
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* @inheritDoc
|
|
79
|
+
*/
|
|
80
|
+
constructor(editor) {
|
|
81
|
+
super(editor);
|
|
82
|
+
editor.config.define('typing', {
|
|
83
|
+
transformations: {
|
|
84
|
+
include: DEFAULT_TRANSFORMATIONS
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* @inheritDoc
|
|
90
|
+
*/
|
|
91
|
+
init() {
|
|
92
|
+
const model = this.editor.model;
|
|
93
|
+
const modelSelection = model.document.selection;
|
|
94
|
+
modelSelection.on('change:range', () => {
|
|
95
|
+
// Disable plugin when selection is inside a code block.
|
|
96
|
+
this.isEnabled = !modelSelection.anchor.parent.is('element', 'codeBlock');
|
|
97
|
+
});
|
|
98
|
+
this._enableTransformationWatchers();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Create new TextWatcher listening to the editor for typing and selection events.
|
|
102
|
+
*/
|
|
103
|
+
_enableTransformationWatchers() {
|
|
104
|
+
const editor = this.editor;
|
|
105
|
+
const model = editor.model;
|
|
106
|
+
const deletePlugin = editor.plugins.get('Delete');
|
|
107
|
+
const normalizedTransformations = normalizeTransformations(editor.config.get('typing.transformations'));
|
|
108
|
+
const testCallback = (text) => {
|
|
109
|
+
for (const normalizedTransformation of normalizedTransformations) {
|
|
110
|
+
const from = normalizedTransformation.from;
|
|
111
|
+
const match = from.test(text);
|
|
112
|
+
if (match) {
|
|
113
|
+
return { normalizedTransformation };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const watcher = new TextWatcher(editor.model, testCallback);
|
|
118
|
+
watcher.on('matched:data', (evt, data) => {
|
|
119
|
+
if (!data.batch.isTyping) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const { from, to } = data.normalizedTransformation;
|
|
123
|
+
const matches = from.exec(data.text);
|
|
124
|
+
const replaces = to(matches.slice(1));
|
|
125
|
+
const matchedRange = data.range;
|
|
126
|
+
let changeIndex = matches.index;
|
|
127
|
+
model.enqueueChange(writer => {
|
|
128
|
+
for (let i = 1; i < matches.length; i++) {
|
|
129
|
+
const match = matches[i];
|
|
130
|
+
const replaceWith = replaces[i - 1];
|
|
131
|
+
if (replaceWith == null) {
|
|
132
|
+
changeIndex += match.length;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const replacePosition = matchedRange.start.getShiftedBy(changeIndex);
|
|
136
|
+
const replaceRange = model.createRange(replacePosition, replacePosition.getShiftedBy(match.length));
|
|
137
|
+
const attributes = getTextAttributesAfterPosition(replacePosition);
|
|
138
|
+
model.insertContent(writer.createText(replaceWith, attributes), replaceRange);
|
|
139
|
+
changeIndex += replaceWith.length;
|
|
140
|
+
}
|
|
141
|
+
model.enqueueChange(() => {
|
|
142
|
+
deletePlugin.requestUndoOnBackspace();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
watcher.bind('isEnabled').to(this);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Normalizes the configuration `from` parameter value.
|
|
151
|
+
* The normalized value for the `from` parameter is a RegExp instance. If the passed `from` is already a RegExp instance,
|
|
152
|
+
* it is returned unchanged.
|
|
153
|
+
*/
|
|
154
|
+
function normalizeFrom(from) {
|
|
155
|
+
if (typeof from == 'string') {
|
|
156
|
+
return new RegExp(`(${escapeRegExp(from)})$`);
|
|
157
|
+
}
|
|
158
|
+
// `from` is already a regular expression.
|
|
159
|
+
return from;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Normalizes the configuration `to` parameter value.
|
|
163
|
+
* The normalized value for the `to` parameter is a function that takes an array and returns an array. See more in the
|
|
164
|
+
* configuration description. If the passed `to` is already a function, it is returned unchanged.
|
|
165
|
+
*/
|
|
166
|
+
function normalizeTo(to) {
|
|
167
|
+
if (typeof to == 'string') {
|
|
168
|
+
return () => [to];
|
|
169
|
+
}
|
|
170
|
+
else if (to instanceof Array) {
|
|
171
|
+
return () => to;
|
|
172
|
+
}
|
|
173
|
+
// `to` is already a function.
|
|
174
|
+
return to;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* For given `position` returns attributes for the text that is after that position.
|
|
178
|
+
* The text can be in the same text node as the position (`foo[]bar`) or in the next text node (`foo[]<$text bold="true">bar</$text>`).
|
|
179
|
+
*/
|
|
180
|
+
function getTextAttributesAfterPosition(position) {
|
|
181
|
+
const textNode = position.textNode ? position.textNode : position.nodeAfter;
|
|
182
|
+
return textNode.getAttributes();
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Returns a RegExp pattern string that detects a sentence inside a quote.
|
|
186
|
+
*
|
|
187
|
+
* @param quoteCharacter The character to create a pattern for.
|
|
188
|
+
*/
|
|
189
|
+
function buildQuotesRegExp(quoteCharacter) {
|
|
190
|
+
return new RegExp(`(^|\\s)(${quoteCharacter})([^${quoteCharacter}]*)(${quoteCharacter})$`);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Reads text transformation config and returns normalized array of transformations objects.
|
|
194
|
+
*/
|
|
195
|
+
function normalizeTransformations(config) {
|
|
196
|
+
const extra = config.extra || [];
|
|
197
|
+
const remove = config.remove || [];
|
|
198
|
+
const isNotRemoved = (transformation) => !remove.includes(transformation);
|
|
199
|
+
const configured = config.include.concat(extra).filter(isNotRemoved);
|
|
200
|
+
return expandGroupsAndRemoveDuplicates(configured)
|
|
201
|
+
.filter(isNotRemoved) // Filter out 'remove' transformations as they might be set in group.
|
|
202
|
+
.map(transformation => (typeof transformation == 'string' && TRANSFORMATIONS[transformation] ? TRANSFORMATIONS[transformation] : transformation))
|
|
203
|
+
// Filter out transformations set as string that has not been found.
|
|
204
|
+
.filter((transformation) => typeof transformation === 'object')
|
|
205
|
+
.map(transformation => ({
|
|
206
|
+
from: normalizeFrom(transformation.from),
|
|
207
|
+
to: normalizeTo(transformation.to)
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Reads definitions and expands named groups if needed to transformation names.
|
|
212
|
+
* This method also removes duplicated named transformations if any.
|
|
213
|
+
*/
|
|
214
|
+
function expandGroupsAndRemoveDuplicates(definitions) {
|
|
215
|
+
// Set is using to make sure that transformation names are not duplicated.
|
|
216
|
+
const definedTransformations = new Set();
|
|
217
|
+
for (const transformationOrGroup of definitions) {
|
|
218
|
+
if (typeof transformationOrGroup == 'string' && TRANSFORMATION_GROUPS[transformationOrGroup]) {
|
|
219
|
+
for (const transformation of TRANSFORMATION_GROUPS[transformationOrGroup]) {
|
|
220
|
+
definedTransformations.add(transformation);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
definedTransformations.add(transformationOrGroup);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return Array.from(definedTransformations);
|
|
228
|
+
}
|