@ckeditor/ckeditor5-source-editing 41.3.0-alpha.1 → 41.3.0-alpha.3
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/translations/ar.d.ts +8 -0
- package/dist/translations/ar.js +5 -0
- package/dist/translations/bg.d.ts +8 -0
- package/dist/translations/bg.js +5 -0
- package/dist/translations/bn.d.ts +8 -0
- package/dist/translations/bn.js +5 -0
- package/dist/translations/ca.d.ts +8 -0
- package/dist/translations/ca.js +5 -0
- package/dist/translations/cs.d.ts +8 -0
- package/dist/translations/cs.js +5 -0
- package/dist/translations/da.d.ts +8 -0
- package/dist/translations/da.js +5 -0
- package/dist/translations/de.d.ts +8 -0
- package/dist/translations/de.js +5 -0
- package/dist/translations/el.d.ts +8 -0
- package/dist/translations/el.js +5 -0
- package/dist/translations/en-au.d.ts +8 -0
- package/dist/translations/en-au.js +5 -0
- package/dist/translations/en.d.ts +8 -0
- package/dist/translations/en.js +5 -0
- package/dist/translations/es.d.ts +8 -0
- package/dist/translations/es.js +5 -0
- package/dist/translations/et.d.ts +8 -0
- package/dist/translations/et.js +5 -0
- package/dist/translations/fi.d.ts +8 -0
- package/dist/translations/fi.js +5 -0
- package/dist/translations/fr.d.ts +8 -0
- package/dist/translations/fr.js +5 -0
- package/dist/translations/gl.d.ts +8 -0
- package/dist/translations/gl.js +5 -0
- package/dist/translations/he.d.ts +8 -0
- package/dist/translations/he.js +5 -0
- package/dist/translations/hi.d.ts +8 -0
- package/dist/translations/hi.js +5 -0
- package/dist/translations/hr.d.ts +8 -0
- package/dist/translations/hr.js +5 -0
- package/dist/translations/hu.d.ts +8 -0
- package/dist/translations/hu.js +5 -0
- package/dist/translations/id.d.ts +8 -0
- package/dist/translations/id.js +5 -0
- package/dist/translations/it.d.ts +8 -0
- package/dist/translations/it.js +5 -0
- package/dist/translations/ja.d.ts +8 -0
- package/dist/translations/ja.js +5 -0
- package/dist/translations/ko.d.ts +8 -0
- package/dist/translations/ko.js +5 -0
- package/dist/translations/lt.d.ts +8 -0
- package/dist/translations/lt.js +5 -0
- package/dist/translations/lv.d.ts +8 -0
- package/dist/translations/lv.js +5 -0
- package/dist/translations/ms.d.ts +8 -0
- package/dist/translations/ms.js +5 -0
- package/dist/translations/nl.d.ts +8 -0
- package/dist/translations/nl.js +5 -0
- package/dist/translations/no.d.ts +8 -0
- package/dist/translations/no.js +5 -0
- package/dist/translations/pl.d.ts +8 -0
- package/dist/translations/pl.js +5 -0
- package/dist/translations/pt-br.d.ts +8 -0
- package/dist/translations/pt-br.js +5 -0
- package/dist/translations/pt.d.ts +8 -0
- package/dist/translations/pt.js +5 -0
- package/dist/translations/ro.d.ts +8 -0
- package/dist/translations/ro.js +5 -0
- package/dist/translations/ru.d.ts +8 -0
- package/dist/translations/ru.js +5 -0
- package/dist/translations/sk.d.ts +8 -0
- package/dist/translations/sk.js +5 -0
- package/dist/translations/sr-latn.d.ts +8 -0
- package/dist/translations/sr-latn.js +5 -0
- package/dist/translations/sr.d.ts +8 -0
- package/dist/translations/sr.js +5 -0
- package/dist/translations/sv.d.ts +8 -0
- package/dist/translations/sv.js +5 -0
- package/dist/translations/th.d.ts +8 -0
- package/dist/translations/th.js +5 -0
- package/dist/translations/tr.d.ts +8 -0
- package/dist/translations/tr.js +5 -0
- package/dist/translations/ug.d.ts +8 -0
- package/dist/translations/ug.js +5 -0
- package/dist/translations/uk.d.ts +8 -0
- package/dist/translations/uk.js +5 -0
- package/dist/translations/ur.d.ts +8 -0
- package/dist/translations/ur.js +5 -0
- package/dist/translations/vi.d.ts +8 -0
- package/dist/translations/vi.js +5 -0
- package/dist/translations/zh-cn.d.ts +8 -0
- package/dist/translations/zh-cn.js +5 -0
- package/dist/translations/zh.d.ts +8 -0
- package/dist/translations/zh.js +5 -0
- package/dist/types/augmentation.d.ts +4 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/sourceediting.d.ts +4 -0
- package/dist/types/sourceeditingconfig.d.ts +4 -0
- package/dist/types/utils/formathtml.d.ts +4 -0
- package/package.json +3 -3
- package/dist/index.js +0 -490
- package/dist/index.js.map +0 -1
package/dist/index.js
DELETED
@@ -1,490 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @license Copyright (c) 2003-2024, 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
|
-
import { Plugin, PendingActions } from '@ckeditor/ckeditor5-core/dist/index.js';
|
6
|
-
import { ButtonView } from '@ckeditor/ckeditor5-ui/dist/index.js';
|
7
|
-
import { ElementReplacer, CKEditorError, createElement } from '@ckeditor/ckeditor5-utils/dist/index.js';
|
8
|
-
|
9
|
-
/**
|
10
|
-
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
11
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
12
|
-
*/
|
13
|
-
/**
|
14
|
-
* @module source-editing/utils/formathtml
|
15
|
-
*/
|
16
|
-
/**
|
17
|
-
* A simple (and naive) HTML code formatter that returns a formatted HTML markup that can be easily
|
18
|
-
* parsed by human eyes. It beautifies the HTML code by adding new lines between elements that behave like block elements
|
19
|
-
* (https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
|
20
|
-
* and a few more like `tr`, `td`, and similar ones) and inserting indents for nested content.
|
21
|
-
*
|
22
|
-
* WARNING: This function works only on a text that does not contain any indentations or new lines.
|
23
|
-
* Calling this function on the already formatted text will damage the formatting.
|
24
|
-
*
|
25
|
-
* @param input An HTML string to format.
|
26
|
-
*/
|
27
|
-
function formatHtml(input) {
|
28
|
-
// A list of block-like elements around which the new lines should be inserted, and within which
|
29
|
-
// the indentation of their children should be increased.
|
30
|
-
// The list is partially based on https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements that contains
|
31
|
-
// a full list of HTML block-level elements.
|
32
|
-
// A void element is an element that cannot have any child - https://html.spec.whatwg.org/multipage/syntax.html#void-elements.
|
33
|
-
// Note that <pre> element is not listed on this list to avoid breaking whitespace formatting.
|
34
|
-
// Note that <br> element is not listed and handled separately so no additional white spaces are injected.
|
35
|
-
const elementsToFormat = [
|
36
|
-
{ name: 'address', isVoid: false },
|
37
|
-
{ name: 'article', isVoid: false },
|
38
|
-
{ name: 'aside', isVoid: false },
|
39
|
-
{ name: 'blockquote', isVoid: false },
|
40
|
-
{ name: 'details', isVoid: false },
|
41
|
-
{ name: 'dialog', isVoid: false },
|
42
|
-
{ name: 'dd', isVoid: false },
|
43
|
-
{ name: 'div', isVoid: false },
|
44
|
-
{ name: 'dl', isVoid: false },
|
45
|
-
{ name: 'dt', isVoid: false },
|
46
|
-
{ name: 'fieldset', isVoid: false },
|
47
|
-
{ name: 'figcaption', isVoid: false },
|
48
|
-
{ name: 'figure', isVoid: false },
|
49
|
-
{ name: 'footer', isVoid: false },
|
50
|
-
{ name: 'form', isVoid: false },
|
51
|
-
{ name: 'h1', isVoid: false },
|
52
|
-
{ name: 'h2', isVoid: false },
|
53
|
-
{ name: 'h3', isVoid: false },
|
54
|
-
{ name: 'h4', isVoid: false },
|
55
|
-
{ name: 'h5', isVoid: false },
|
56
|
-
{ name: 'h6', isVoid: false },
|
57
|
-
{ name: 'header', isVoid: false },
|
58
|
-
{ name: 'hgroup', isVoid: false },
|
59
|
-
{ name: 'hr', isVoid: true },
|
60
|
-
{ name: 'li', isVoid: false },
|
61
|
-
{ name: 'main', isVoid: false },
|
62
|
-
{ name: 'nav', isVoid: false },
|
63
|
-
{ name: 'ol', isVoid: false },
|
64
|
-
{ name: 'p', isVoid: false },
|
65
|
-
{ name: 'section', isVoid: false },
|
66
|
-
{ name: 'table', isVoid: false },
|
67
|
-
{ name: 'tbody', isVoid: false },
|
68
|
-
{ name: 'td', isVoid: false },
|
69
|
-
{ name: 'th', isVoid: false },
|
70
|
-
{ name: 'thead', isVoid: false },
|
71
|
-
{ name: 'tr', isVoid: false },
|
72
|
-
{ name: 'ul', isVoid: false }
|
73
|
-
];
|
74
|
-
const elementNamesToFormat = elementsToFormat.map(element => element.name).join('|');
|
75
|
-
// It is not the fastest way to format the HTML markup but the performance should be good enough.
|
76
|
-
const lines = input
|
77
|
-
// Add new line before and after `<tag>` and `</tag>`.
|
78
|
-
// It may separate individual elements with two new lines, but this will be fixed below.
|
79
|
-
.replace(new RegExp(`</?(${elementNamesToFormat})( .*?)?>`, 'g'), '\n$&\n')
|
80
|
-
// Keep `<br>`s at the end of line to avoid adding additional whitespaces before `<br>`.
|
81
|
-
.replace(/<br[^>]*>/g, '$&\n')
|
82
|
-
// Divide input string into lines, which start with either an opening tag, a closing tag, or just a text.
|
83
|
-
.split('\n');
|
84
|
-
let indentCount = 0;
|
85
|
-
let isPreformattedLine = false;
|
86
|
-
return lines
|
87
|
-
.filter(line => line.length)
|
88
|
-
.map(line => {
|
89
|
-
isPreformattedLine = isPreformattedBlockLine(line, isPreformattedLine);
|
90
|
-
if (isNonVoidOpeningTag(line, elementsToFormat)) {
|
91
|
-
return indentLine(line, indentCount++);
|
92
|
-
}
|
93
|
-
if (isClosingTag(line, elementsToFormat)) {
|
94
|
-
return indentLine(line, --indentCount);
|
95
|
-
}
|
96
|
-
if (isPreformattedLine === 'middle' || isPreformattedLine === 'last') {
|
97
|
-
return line;
|
98
|
-
}
|
99
|
-
return indentLine(line, indentCount);
|
100
|
-
})
|
101
|
-
.join('\n');
|
102
|
-
}
|
103
|
-
/**
|
104
|
-
* Checks, if an argument is an opening tag of a non-void element to be formatted.
|
105
|
-
*
|
106
|
-
* @param line String to check.
|
107
|
-
* @param elementsToFormat Elements to be formatted.
|
108
|
-
*/
|
109
|
-
function isNonVoidOpeningTag(line, elementsToFormat) {
|
110
|
-
return elementsToFormat.some(element => {
|
111
|
-
if (element.isVoid) {
|
112
|
-
return false;
|
113
|
-
}
|
114
|
-
if (!new RegExp(`<${element.name}( .*?)?>`).test(line)) {
|
115
|
-
return false;
|
116
|
-
}
|
117
|
-
return true;
|
118
|
-
});
|
119
|
-
}
|
120
|
-
/**
|
121
|
-
* Checks, if an argument is a closing tag.
|
122
|
-
*
|
123
|
-
* @param line String to check.
|
124
|
-
* @param elementsToFormat Elements to be formatted.
|
125
|
-
*/
|
126
|
-
function isClosingTag(line, elementsToFormat) {
|
127
|
-
return elementsToFormat.some(element => {
|
128
|
-
return new RegExp(`</${element.name}>`).test(line);
|
129
|
-
});
|
130
|
-
}
|
131
|
-
/**
|
132
|
-
* Indents a line by a specified number of characters.
|
133
|
-
*
|
134
|
-
* @param line Line to indent.
|
135
|
-
* @param indentCount Number of characters to use for indentation.
|
136
|
-
* @param indentChar Indentation character(s). 4 spaces by default.
|
137
|
-
*/
|
138
|
-
function indentLine(line, indentCount, indentChar = ' ') {
|
139
|
-
// More about Math.max() here in https://github.com/ckeditor/ckeditor5/issues/10698.
|
140
|
-
return `${indentChar.repeat(Math.max(0, indentCount))}${line}`;
|
141
|
-
}
|
142
|
-
/**
|
143
|
-
* Checks whether a line belongs to a preformatted (`<pre>`) block.
|
144
|
-
*
|
145
|
-
* @param line Line to check.
|
146
|
-
* @param isPreviousLinePreFormatted Information on whether the previous line was preformatted (and how).
|
147
|
-
*/
|
148
|
-
function isPreformattedBlockLine(line, isPreviousLinePreFormatted) {
|
149
|
-
if (new RegExp('<pre( .*?)?>').test(line)) {
|
150
|
-
return 'first';
|
151
|
-
}
|
152
|
-
else if (new RegExp('</pre>').test(line)) {
|
153
|
-
return 'last';
|
154
|
-
}
|
155
|
-
else if (isPreviousLinePreFormatted === 'first' || isPreviousLinePreFormatted === 'middle') {
|
156
|
-
return 'middle';
|
157
|
-
}
|
158
|
-
else {
|
159
|
-
return false;
|
160
|
-
}
|
161
|
-
}
|
162
|
-
|
163
|
-
var sourceEditingIcon = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m12.5 0 5 4.5v15.003h-16V0h11zM3 1.5v3.25l-1.497 1-.003 8 1.5 1v3.254L7.685 18l-.001 1.504H17.5V8.002L16 9.428l-.004-4.22-4.222-3.692L3 1.5z\"/><path d=\"M4.06 6.64a.75.75 0 0 1 .958 1.15l-.085.07L2.29 9.75l2.646 1.89c.302.216.4.62.232.951l-.058.095a.75.75 0 0 1-.951.232l-.095-.058-3.5-2.5V9.14l3.496-2.5zm4.194 6.22a.75.75 0 0 1-.958-1.149l.085-.07 2.643-1.89-2.646-1.89a.75.75 0 0 1-.232-.952l.058-.095a.75.75 0 0 1 .95-.232l.096.058 3.5 2.5v1.22l-3.496 2.5zm7.644-.836 2.122 2.122-5.825 5.809-2.125-.005.003-2.116zm2.539-1.847 1.414 1.414a.5.5 0 0 1 0 .707l-1.06 1.06-2.122-2.12 1.061-1.061a.5.5 0 0 1 .707 0z\"/></svg>";
|
164
|
-
|
165
|
-
/**
|
166
|
-
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
167
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
168
|
-
*/
|
169
|
-
/**
|
170
|
-
* @module source-editing/sourceediting
|
171
|
-
*/
|
172
|
-
/* global console */
|
173
|
-
const COMMAND_FORCE_DISABLE_ID = 'SourceEditingMode';
|
174
|
-
/**
|
175
|
-
* The source editing feature.
|
176
|
-
*
|
177
|
-
* It provides the possibility to view and edit the source of the document.
|
178
|
-
*
|
179
|
-
* For a detailed overview, check the {@glink features/source-editing source editing feature documentation} and the
|
180
|
-
* {@glink api/source-editing package page}.
|
181
|
-
*/
|
182
|
-
class SourceEditing extends Plugin {
|
183
|
-
/**
|
184
|
-
* @inheritDoc
|
185
|
-
*/
|
186
|
-
static get pluginName() {
|
187
|
-
return 'SourceEditing';
|
188
|
-
}
|
189
|
-
/**
|
190
|
-
* @inheritDoc
|
191
|
-
*/
|
192
|
-
static get requires() {
|
193
|
-
return [PendingActions];
|
194
|
-
}
|
195
|
-
/**
|
196
|
-
* @inheritDoc
|
197
|
-
*/
|
198
|
-
constructor(editor) {
|
199
|
-
super(editor);
|
200
|
-
this.set('isSourceEditingMode', false);
|
201
|
-
this._elementReplacer = new ElementReplacer();
|
202
|
-
this._replacedRoots = new Map();
|
203
|
-
this._dataFromRoots = new Map();
|
204
|
-
editor.config.define('sourceEditing.allowCollaborationFeatures', false);
|
205
|
-
}
|
206
|
-
/**
|
207
|
-
* @inheritDoc
|
208
|
-
*/
|
209
|
-
init() {
|
210
|
-
this._checkCompatibility();
|
211
|
-
const editor = this.editor;
|
212
|
-
const t = editor.t;
|
213
|
-
editor.ui.componentFactory.add('sourceEditing', locale => {
|
214
|
-
const buttonView = new ButtonView(locale);
|
215
|
-
buttonView.set({
|
216
|
-
label: t('Source'),
|
217
|
-
icon: sourceEditingIcon,
|
218
|
-
tooltip: true,
|
219
|
-
withText: true,
|
220
|
-
class: 'ck-source-editing-button'
|
221
|
-
});
|
222
|
-
buttonView.bind('isOn').to(this, 'isSourceEditingMode');
|
223
|
-
// The button should be disabled if one of the following conditions is met:
|
224
|
-
buttonView.bind('isEnabled').to(this, 'isEnabled', editor, 'isReadOnly', editor.plugins.get(PendingActions), 'hasAny', (isEnabled, isEditorReadOnly, hasAnyPendingActions) => {
|
225
|
-
// (1) The plugin itself is disabled.
|
226
|
-
if (!isEnabled) {
|
227
|
-
return false;
|
228
|
-
}
|
229
|
-
// (2) The editor is in read-only mode.
|
230
|
-
if (isEditorReadOnly) {
|
231
|
-
return false;
|
232
|
-
}
|
233
|
-
// (3) Any pending action is scheduled. It may change the model, so modifying the document source should be prevented
|
234
|
-
// until the model is finally set.
|
235
|
-
if (hasAnyPendingActions) {
|
236
|
-
return false;
|
237
|
-
}
|
238
|
-
return true;
|
239
|
-
});
|
240
|
-
this.listenTo(buttonView, 'execute', () => {
|
241
|
-
this.isSourceEditingMode = !this.isSourceEditingMode;
|
242
|
-
});
|
243
|
-
return buttonView;
|
244
|
-
});
|
245
|
-
// Currently, the plugin handles the source editing mode by itself only for the classic editor. To use this plugin with other
|
246
|
-
// integrations, listen to the `change:isSourceEditingMode` event and act accordingly.
|
247
|
-
if (this._isAllowedToHandleSourceEditingMode()) {
|
248
|
-
this.on('change:isSourceEditingMode', (evt, name, isSourceEditingMode) => {
|
249
|
-
if (isSourceEditingMode) {
|
250
|
-
this._hideVisibleDialog();
|
251
|
-
this._showSourceEditing();
|
252
|
-
this._disableCommands();
|
253
|
-
}
|
254
|
-
else {
|
255
|
-
this._hideSourceEditing();
|
256
|
-
this._enableCommands();
|
257
|
-
}
|
258
|
-
});
|
259
|
-
this.on('change:isEnabled', (evt, name, isEnabled) => this._handleReadOnlyMode(!isEnabled));
|
260
|
-
this.listenTo(editor, 'change:isReadOnly', (evt, name, isReadOnly) => this._handleReadOnlyMode(isReadOnly));
|
261
|
-
}
|
262
|
-
// Update the editor data while calling editor.getData() in the source editing mode.
|
263
|
-
editor.data.on('get', () => {
|
264
|
-
if (this.isSourceEditingMode) {
|
265
|
-
this.updateEditorData();
|
266
|
-
}
|
267
|
-
}, { priority: 'high' });
|
268
|
-
}
|
269
|
-
/**
|
270
|
-
* Updates the source data in all hidden editing roots.
|
271
|
-
*/
|
272
|
-
updateEditorData() {
|
273
|
-
const editor = this.editor;
|
274
|
-
const data = {};
|
275
|
-
for (const [rootName, domSourceEditingElementWrapper] of this._replacedRoots) {
|
276
|
-
const oldData = this._dataFromRoots.get(rootName);
|
277
|
-
const newData = domSourceEditingElementWrapper.dataset.value;
|
278
|
-
// Do not set the data unless some changes have been made in the meantime.
|
279
|
-
// This prevents empty undo steps after switching to the normal editor.
|
280
|
-
if (oldData !== newData) {
|
281
|
-
data[rootName] = newData;
|
282
|
-
this._dataFromRoots.set(rootName, newData);
|
283
|
-
}
|
284
|
-
}
|
285
|
-
if (Object.keys(data).length) {
|
286
|
-
editor.data.set(data, { batchType: { isUndoable: true }, suppressErrorInCollaboration: true });
|
287
|
-
}
|
288
|
-
}
|
289
|
-
_checkCompatibility() {
|
290
|
-
const editor = this.editor;
|
291
|
-
const allowCollaboration = editor.config.get('sourceEditing.allowCollaborationFeatures');
|
292
|
-
if (!allowCollaboration && editor.plugins.has('RealTimeCollaborativeEditing')) {
|
293
|
-
/**
|
294
|
-
* Source editing feature is not fully compatible with real-time collaboration,
|
295
|
-
* and using it may lead to data loss. Please read
|
296
|
-
* {@glink features/source-editing#limitations-and-incompatibilities source editing feature guide} to learn more.
|
297
|
-
*
|
298
|
-
* If you understand the possible risk of data loss, you can enable the source editing
|
299
|
-
* by setting the
|
300
|
-
* {@link module:source-editing/sourceeditingconfig~SourceEditingConfig#allowCollaborationFeatures}
|
301
|
-
* configuration flag to `true`.
|
302
|
-
*
|
303
|
-
* @error source-editing-incompatible-with-real-time-collaboration
|
304
|
-
*/
|
305
|
-
throw new CKEditorError('source-editing-incompatible-with-real-time-collaboration', null);
|
306
|
-
}
|
307
|
-
const collaborationPluginNamesToWarn = [
|
308
|
-
'CommentsEditing',
|
309
|
-
'TrackChangesEditing',
|
310
|
-
'RevisionHistory'
|
311
|
-
];
|
312
|
-
// Currently, the basic integration with Collaboration Features is to display a warning in the console.
|
313
|
-
//
|
314
|
-
// If `allowCollaboration` flag is set, do not show these warnings. If the flag is set, we assume that the integrator read
|
315
|
-
// appropriate section of the guide so there's no use to spam the console with warnings.
|
316
|
-
//
|
317
|
-
if (!allowCollaboration && collaborationPluginNamesToWarn.some(pluginName => editor.plugins.has(pluginName))) {
|
318
|
-
console.warn('You initialized the editor with the source editing feature and at least one of the collaboration features. ' +
|
319
|
-
'Please be advised that the source editing feature may not work, and be careful when editing document source ' +
|
320
|
-
'that contains markers created by the collaboration features.');
|
321
|
-
}
|
322
|
-
// Restricted Editing integration can also lead to problems. Warn the user accordingly.
|
323
|
-
if (editor.plugins.has('RestrictedEditingModeEditing')) {
|
324
|
-
console.warn('You initialized the editor with the source editing feature and restricted editing feature. ' +
|
325
|
-
'Please be advised that the source editing feature may not work, and be careful when editing document source ' +
|
326
|
-
'that contains markers created by the restricted editing feature.');
|
327
|
-
}
|
328
|
-
}
|
329
|
-
/**
|
330
|
-
* Creates source editing wrappers that replace each editing root. Each wrapper contains the document source from the corresponding
|
331
|
-
* root.
|
332
|
-
*
|
333
|
-
* The wrapper element contains a textarea and it solves the problem, that the textarea element cannot auto expand its height based on
|
334
|
-
* the content it contains. The solution is to make the textarea more like a plain div element, which expands in height as much as it
|
335
|
-
* needs to, in order to display the whole document source without scrolling. The wrapper element is a parent for the textarea and for
|
336
|
-
* the pseudo-element `::after`, that replicates the look, content, and position of the textarea. The pseudo-element replica is hidden,
|
337
|
-
* but it is styled to be an identical visual copy of the textarea with the same content. Then, the wrapper is a grid container and both
|
338
|
-
* of its children (the textarea and the `::after` pseudo-element) are positioned within a CSS grid to occupy the same grid cell. The
|
339
|
-
* content in the pseudo-element `::after` is set in CSS and it stretches the grid to the appropriate size based on the textarea value.
|
340
|
-
* Since both children occupy the same grid cell, both have always the same height.
|
341
|
-
*/
|
342
|
-
_showSourceEditing() {
|
343
|
-
const editor = this.editor;
|
344
|
-
const editingView = editor.editing.view;
|
345
|
-
const model = editor.model;
|
346
|
-
model.change(writer => {
|
347
|
-
writer.setSelection(null);
|
348
|
-
writer.removeSelectionAttribute(model.document.selection.getAttributeKeys());
|
349
|
-
});
|
350
|
-
// It is not needed to iterate through all editing roots, as currently the plugin supports only the Classic Editor with a single
|
351
|
-
// main root, but this code may help understand and use this feature in external integrations.
|
352
|
-
for (const [rootName, domRootElement] of editingView.domRoots) {
|
353
|
-
const data = formatSource(editor.data.get({ rootName }));
|
354
|
-
const domSourceEditingElementTextarea = createElement(domRootElement.ownerDocument, 'textarea', {
|
355
|
-
rows: '1',
|
356
|
-
'aria-label': 'Source code editing area'
|
357
|
-
});
|
358
|
-
const domSourceEditingElementWrapper = createElement(domRootElement.ownerDocument, 'div', {
|
359
|
-
class: 'ck-source-editing-area',
|
360
|
-
'data-value': data
|
361
|
-
}, [domSourceEditingElementTextarea]);
|
362
|
-
domSourceEditingElementTextarea.value = data;
|
363
|
-
// Setting a value to textarea moves the input cursor to the end. We want the selection at the beginning.
|
364
|
-
domSourceEditingElementTextarea.setSelectionRange(0, 0);
|
365
|
-
// Bind the textarea's value to the wrapper's `data-value` property. Each change of the textarea's value updates the
|
366
|
-
// wrapper's `data-value` property.
|
367
|
-
domSourceEditingElementTextarea.addEventListener('input', () => {
|
368
|
-
domSourceEditingElementWrapper.dataset.value = domSourceEditingElementTextarea.value;
|
369
|
-
editor.ui.update();
|
370
|
-
});
|
371
|
-
editingView.change(writer => {
|
372
|
-
const viewRoot = editingView.document.getRoot(rootName);
|
373
|
-
writer.addClass('ck-hidden', viewRoot);
|
374
|
-
});
|
375
|
-
// Register the element so it becomes available for Alt+F10 and Esc navigation.
|
376
|
-
editor.ui.setEditableElement('sourceEditing:' + rootName, domSourceEditingElementTextarea);
|
377
|
-
this._replacedRoots.set(rootName, domSourceEditingElementWrapper);
|
378
|
-
this._elementReplacer.replace(domRootElement, domSourceEditingElementWrapper);
|
379
|
-
this._dataFromRoots.set(rootName, data);
|
380
|
-
}
|
381
|
-
this._focusSourceEditing();
|
382
|
-
}
|
383
|
-
/**
|
384
|
-
* Restores all hidden editing roots and sets the source data in them.
|
385
|
-
*/
|
386
|
-
_hideSourceEditing() {
|
387
|
-
const editor = this.editor;
|
388
|
-
const editingView = editor.editing.view;
|
389
|
-
this.updateEditorData();
|
390
|
-
editingView.change(writer => {
|
391
|
-
for (const [rootName] of this._replacedRoots) {
|
392
|
-
writer.removeClass('ck-hidden', editingView.document.getRoot(rootName));
|
393
|
-
}
|
394
|
-
});
|
395
|
-
this._elementReplacer.restore();
|
396
|
-
this._replacedRoots.clear();
|
397
|
-
this._dataFromRoots.clear();
|
398
|
-
editingView.focus();
|
399
|
-
}
|
400
|
-
/**
|
401
|
-
* Focuses the textarea containing document source from the first editing root.
|
402
|
-
*/
|
403
|
-
_focusSourceEditing() {
|
404
|
-
const editor = this.editor;
|
405
|
-
const [domSourceEditingElementWrapper] = this._replacedRoots.values();
|
406
|
-
const textarea = domSourceEditingElementWrapper.querySelector('textarea');
|
407
|
-
// The FocusObserver was disabled by View.render() while the DOM root was getting hidden and the replacer
|
408
|
-
// revealed the textarea. So it couldn't notice that the DOM root got blurred in the process.
|
409
|
-
// Let's sync this state manually here because otherwise Renderer will attempt to render selection
|
410
|
-
// in an invisible DOM root.
|
411
|
-
editor.editing.view.document.isFocused = false;
|
412
|
-
textarea.focus();
|
413
|
-
}
|
414
|
-
/**
|
415
|
-
* Disables all commands.
|
416
|
-
*/
|
417
|
-
_disableCommands() {
|
418
|
-
const editor = this.editor;
|
419
|
-
for (const command of editor.commands.commands()) {
|
420
|
-
command.forceDisabled(COMMAND_FORCE_DISABLE_ID);
|
421
|
-
}
|
422
|
-
}
|
423
|
-
/**
|
424
|
-
* Clears forced disable for all commands, that was previously set through {@link #_disableCommands}.
|
425
|
-
*/
|
426
|
-
_enableCommands() {
|
427
|
-
const editor = this.editor;
|
428
|
-
for (const command of editor.commands.commands()) {
|
429
|
-
command.clearForceDisabled(COMMAND_FORCE_DISABLE_ID);
|
430
|
-
}
|
431
|
-
}
|
432
|
-
/**
|
433
|
-
* Adds or removes the `readonly` attribute from the textarea from all roots, if document source mode is active.
|
434
|
-
*
|
435
|
-
* @param isReadOnly Indicates whether all textarea elements should be read-only.
|
436
|
-
*/
|
437
|
-
_handleReadOnlyMode(isReadOnly) {
|
438
|
-
if (!this.isSourceEditingMode) {
|
439
|
-
return;
|
440
|
-
}
|
441
|
-
for (const [, domSourceEditingElementWrapper] of this._replacedRoots) {
|
442
|
-
domSourceEditingElementWrapper.querySelector('textarea').readOnly = isReadOnly;
|
443
|
-
}
|
444
|
-
}
|
445
|
-
/**
|
446
|
-
* Checks, if the plugin is allowed to handle the source editing mode by itself. Currently, the source editing mode is supported only
|
447
|
-
* for the {@link module:editor-classic/classiceditor~ClassicEditor classic editor}.
|
448
|
-
*/
|
449
|
-
_isAllowedToHandleSourceEditingMode() {
|
450
|
-
const editor = this.editor;
|
451
|
-
const editable = editor.ui.view.editable;
|
452
|
-
// Checks, if the editor's editable belongs to the editor's DOM tree.
|
453
|
-
return editable && !editable.hasExternalElement;
|
454
|
-
}
|
455
|
-
/**
|
456
|
-
* If any {@link module:ui/dialog/dialogview~DialogView editor dialog} is currently visible, hide it.
|
457
|
-
*/
|
458
|
-
_hideVisibleDialog() {
|
459
|
-
if (this.editor.plugins.has('Dialog')) {
|
460
|
-
const dialogPlugin = this.editor.plugins.get('Dialog');
|
461
|
-
if (dialogPlugin.isOpen) {
|
462
|
-
dialogPlugin.hide();
|
463
|
-
}
|
464
|
-
}
|
465
|
-
}
|
466
|
-
}
|
467
|
-
/**
|
468
|
-
* Formats the content for a better readability.
|
469
|
-
*
|
470
|
-
* For a non-HTML source the unchanged input string is returned.
|
471
|
-
*
|
472
|
-
* @param input Input string to check.
|
473
|
-
*/
|
474
|
-
function formatSource(input) {
|
475
|
-
if (!isHtml(input)) {
|
476
|
-
return input;
|
477
|
-
}
|
478
|
-
return formatHtml(input);
|
479
|
-
}
|
480
|
-
/**
|
481
|
-
* Checks, if the document source is HTML. It is sufficient to just check the first character from the document data.
|
482
|
-
*
|
483
|
-
* @param input Input string to check.
|
484
|
-
*/
|
485
|
-
function isHtml(input) {
|
486
|
-
return input.startsWith('<');
|
487
|
-
}
|
488
|
-
|
489
|
-
export { SourceEditing };
|
490
|
-
//# sourceMappingURL=index.js.map
|