@ckeditor/ckeditor5-source-editing 48.2.0 → 48.3.0-alpha.1
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/dist/augmentation.d.ts +16 -16
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +334 -389
- package/dist/index.js.map +1 -1
- package/dist/sourceediting.d.ts +115 -115
- package/dist/sourceeditingconfig.d.ts +29 -29
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -2,400 +2,345 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { IconSource } from
|
|
7
|
-
import { ButtonView, MenuBarMenuListItemButtonView } from
|
|
8
|
-
import {
|
|
5
|
+
import { PendingActions, Plugin } from "@ckeditor/ckeditor5-core";
|
|
6
|
+
import { IconSource } from "@ckeditor/ckeditor5-icons";
|
|
7
|
+
import { ButtonView, MenuBarMenuListItemButtonView } from "@ckeditor/ckeditor5-ui";
|
|
8
|
+
import { CKEditorError, ElementReplacer, createElement, env, formatHtml } from "@ckeditor/ckeditor5-utils";
|
|
9
9
|
|
|
10
|
-
const COMMAND_FORCE_DISABLE_ID = 'SourceEditingMode';
|
|
11
10
|
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
* @param isReadOnly Indicates whether all textarea elements should be read-only.
|
|
322
|
-
*/ _handleReadOnlyMode(isReadOnly) {
|
|
323
|
-
if (!this.isSourceEditingMode) {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
for (const [, domSourceEditingElementWrapper] of this._replacedRoots){
|
|
327
|
-
domSourceEditingElementWrapper.querySelector('textarea').readOnly = isReadOnly;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Checks, if the plugin is allowed to handle the source editing mode by itself. Currently, the source editing mode is supported only
|
|
332
|
-
* for the {@link module:editor-classic/classiceditor~ClassicEditor classic editor}.
|
|
333
|
-
*/ _isAllowedToHandleSourceEditingMode() {
|
|
334
|
-
const editor = this.editor;
|
|
335
|
-
const editable = editor.ui.view.editable;
|
|
336
|
-
// Checks, if the editor's editable belongs to the editor's DOM tree.
|
|
337
|
-
return editable && !editable.hasExternalElement;
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* If any {@link module:ui/dialog/dialogview~DialogView editor dialog} is currently visible, hide it.
|
|
341
|
-
*/ _hideVisibleDialog() {
|
|
342
|
-
if (this.editor.plugins.has('Dialog')) {
|
|
343
|
-
const dialogPlugin = this.editor.plugins.get('Dialog');
|
|
344
|
-
if (dialogPlugin.isOpen) {
|
|
345
|
-
dialogPlugin.hide();
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
_createButton(ButtonClass) {
|
|
350
|
-
const editor = this.editor;
|
|
351
|
-
const buttonView = new ButtonClass(editor.locale);
|
|
352
|
-
buttonView.set({
|
|
353
|
-
withText: true,
|
|
354
|
-
isToggleable: true
|
|
355
|
-
});
|
|
356
|
-
buttonView.bind('isOn').to(this, 'isSourceEditingMode');
|
|
357
|
-
// The button should be disabled if one of the following conditions is met:
|
|
358
|
-
buttonView.bind('isEnabled').to(this, 'isEnabled', editor, 'isReadOnly', editor.plugins.get(PendingActions), 'hasAny', (isEnabled, isEditorReadOnly, hasAnyPendingActions)=>{
|
|
359
|
-
// (1) The plugin itself is disabled.
|
|
360
|
-
if (!isEnabled) {
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
363
|
-
// (2) The editor is in read-only mode.
|
|
364
|
-
if (isEditorReadOnly) {
|
|
365
|
-
return false;
|
|
366
|
-
}
|
|
367
|
-
// (3) Any pending action is scheduled. It may change the model, so modifying the document source should be prevented
|
|
368
|
-
// until the model is finally set.
|
|
369
|
-
if (hasAnyPendingActions) {
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
return true;
|
|
373
|
-
});
|
|
374
|
-
this.listenTo(buttonView, 'execute', ()=>{
|
|
375
|
-
this.isSourceEditingMode = !this.isSourceEditingMode;
|
|
376
|
-
});
|
|
377
|
-
return buttonView;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
11
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
12
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* @module source-editing/sourceediting
|
|
16
|
+
*/
|
|
17
|
+
const COMMAND_FORCE_DISABLE_ID = "SourceEditingMode";
|
|
18
|
+
/**
|
|
19
|
+
* The source editing feature.
|
|
20
|
+
*
|
|
21
|
+
* It provides the possibility to view and edit the source of the document.
|
|
22
|
+
*
|
|
23
|
+
* For a detailed overview, check the {@glink features/source-editing/source-editing source editing feature documentation} and the
|
|
24
|
+
* {@glink api/source-editing package page}.
|
|
25
|
+
*/
|
|
26
|
+
var SourceEditing = class extends Plugin {
|
|
27
|
+
/**
|
|
28
|
+
* @inheritDoc
|
|
29
|
+
*/
|
|
30
|
+
static get pluginName() {
|
|
31
|
+
return "SourceEditing";
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @inheritDoc
|
|
35
|
+
*/
|
|
36
|
+
static get isOfficialPlugin() {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @inheritDoc
|
|
41
|
+
*/
|
|
42
|
+
static get requires() {
|
|
43
|
+
return [PendingActions];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The element replacer instance used to replace the editing roots with the wrapper elements containing the document source.
|
|
47
|
+
*/
|
|
48
|
+
_elementReplacer;
|
|
49
|
+
/**
|
|
50
|
+
* Maps all root names to wrapper elements containing the document source.
|
|
51
|
+
*/
|
|
52
|
+
_replacedRoots;
|
|
53
|
+
/**
|
|
54
|
+
* Maps all root names to their document data.
|
|
55
|
+
*/
|
|
56
|
+
_dataFromRoots;
|
|
57
|
+
/**
|
|
58
|
+
* @inheritDoc
|
|
59
|
+
*/
|
|
60
|
+
constructor(editor) {
|
|
61
|
+
super(editor);
|
|
62
|
+
this.set("isSourceEditingMode", false);
|
|
63
|
+
this._elementReplacer = new ElementReplacer();
|
|
64
|
+
this._replacedRoots = /* @__PURE__ */ new Map();
|
|
65
|
+
this._dataFromRoots = /* @__PURE__ */ new Map();
|
|
66
|
+
editor.config.define("sourceEditing.allowCollaborationFeatures", false);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* @inheritDoc
|
|
70
|
+
*/
|
|
71
|
+
init() {
|
|
72
|
+
this._checkCompatibility();
|
|
73
|
+
const editor = this.editor;
|
|
74
|
+
const t = editor.locale.t;
|
|
75
|
+
editor.ui.componentFactory.add("sourceEditing", () => {
|
|
76
|
+
const buttonView = this._createButton(ButtonView);
|
|
77
|
+
buttonView.set({
|
|
78
|
+
label: t("Source"),
|
|
79
|
+
icon: IconSource,
|
|
80
|
+
tooltip: true,
|
|
81
|
+
class: "ck-source-editing-button"
|
|
82
|
+
});
|
|
83
|
+
return buttonView;
|
|
84
|
+
});
|
|
85
|
+
editor.ui.componentFactory.add("menuBar:sourceEditing", () => {
|
|
86
|
+
const buttonView = this._createButton(MenuBarMenuListItemButtonView);
|
|
87
|
+
buttonView.set({
|
|
88
|
+
label: t("Show source"),
|
|
89
|
+
role: "menuitemcheckbox"
|
|
90
|
+
});
|
|
91
|
+
return buttonView;
|
|
92
|
+
});
|
|
93
|
+
if (this._isAllowedToHandleSourceEditingMode()) {
|
|
94
|
+
this.on("change:isSourceEditingMode", (evt, name, isSourceEditingMode) => {
|
|
95
|
+
if (isSourceEditingMode) {
|
|
96
|
+
this._hideVisibleDialog();
|
|
97
|
+
this._showSourceEditing();
|
|
98
|
+
this._disableCommands();
|
|
99
|
+
} else {
|
|
100
|
+
this._hideSourceEditing();
|
|
101
|
+
this._enableCommands();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
this.on("change:isEnabled", (evt, name, isEnabled) => this._handleReadOnlyMode(!isEnabled));
|
|
105
|
+
this.listenTo(editor, "change:isReadOnly", (evt, name, isReadOnly) => this._handleReadOnlyMode(isReadOnly));
|
|
106
|
+
}
|
|
107
|
+
editor.data.on("get", () => {
|
|
108
|
+
if (this.isSourceEditingMode) this.updateEditorData();
|
|
109
|
+
}, { priority: "high" });
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Updates the source data in all hidden editing roots.
|
|
113
|
+
*/
|
|
114
|
+
updateEditorData() {
|
|
115
|
+
const editor = this.editor;
|
|
116
|
+
const data = {};
|
|
117
|
+
for (const [rootName, domSourceEditingElementWrapper] of this._replacedRoots) {
|
|
118
|
+
const oldData = this._dataFromRoots.get(rootName);
|
|
119
|
+
const newData = domSourceEditingElementWrapper.dataset.value;
|
|
120
|
+
if (oldData !== newData) {
|
|
121
|
+
data[rootName] = newData;
|
|
122
|
+
this._dataFromRoots.set(rootName, newData);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (Object.keys(data).length) editor.data.set(data, {
|
|
126
|
+
batchType: { isUndoable: true },
|
|
127
|
+
suppressErrorInCollaboration: true
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
_checkCompatibility() {
|
|
131
|
+
const editor = this.editor;
|
|
132
|
+
const allowCollaboration = editor.config.get("sourceEditing.allowCollaborationFeatures");
|
|
133
|
+
if (!allowCollaboration && editor.plugins.has("RealTimeCollaborativeEditing"))
|
|
134
|
+
/**
|
|
135
|
+
* Source editing feature is not fully compatible with real-time collaboration,
|
|
136
|
+
* and using it may lead to data loss. Please read
|
|
137
|
+
* {@glink features/source-editing/source-editing#limitations-and-incompatibilities source editing feature guide} to learn more.
|
|
138
|
+
*
|
|
139
|
+
* If you understand the possible risk of data loss, you can enable the source editing
|
|
140
|
+
* by setting the
|
|
141
|
+
* {@link module:source-editing/sourceeditingconfig~SourceEditingConfig#allowCollaborationFeatures}
|
|
142
|
+
* configuration flag to `true`.
|
|
143
|
+
*
|
|
144
|
+
* @error source-editing-incompatible-with-real-time-collaboration
|
|
145
|
+
*/
|
|
146
|
+
throw new CKEditorError("source-editing-incompatible-with-real-time-collaboration", null);
|
|
147
|
+
if (!allowCollaboration && [
|
|
148
|
+
"CommentsEditing",
|
|
149
|
+
"TrackChangesEditing",
|
|
150
|
+
"RevisionHistory"
|
|
151
|
+
].some((pluginName) => editor.plugins.has(pluginName))) console.warn("You initialized the editor with the source editing feature and at least one of the collaboration features. Please be advised that the source editing feature may not work, and be careful when editing document source that contains markers created by the collaboration features.");
|
|
152
|
+
if (editor.plugins.has("RestrictedEditingModeEditing")) console.warn("You initialized the editor with the source editing feature and restricted editing feature. Please be advised that the source editing feature may not work, and be careful when editing document source that contains markers created by the restricted editing feature.");
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Creates source editing wrappers that replace each editing root. Each wrapper contains the document source from the corresponding
|
|
156
|
+
* root.
|
|
157
|
+
*
|
|
158
|
+
* The wrapper element contains a textarea and it solves the problem, that the textarea element cannot auto expand its height based on
|
|
159
|
+
* 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
|
|
160
|
+
* needs to, in order to display the whole document source without scrolling. The wrapper element is a parent for the textarea and for
|
|
161
|
+
* the pseudo-element `::after`, that replicates the look, content, and position of the textarea. The pseudo-element replica is hidden,
|
|
162
|
+
* 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
|
|
163
|
+
* of its children (the textarea and the `::after` pseudo-element) are positioned within a CSS grid to occupy the same grid cell. The
|
|
164
|
+
* content in the pseudo-element `::after` is set in CSS and it stretches the grid to the appropriate size based on the textarea value.
|
|
165
|
+
* Since both children occupy the same grid cell, both have always the same height.
|
|
166
|
+
*/
|
|
167
|
+
_showSourceEditing() {
|
|
168
|
+
const editor = this.editor;
|
|
169
|
+
const editingView = editor.editing.view;
|
|
170
|
+
const model = editor.model;
|
|
171
|
+
model.change((writer) => {
|
|
172
|
+
writer.setSelection(null);
|
|
173
|
+
writer.removeSelectionAttribute(model.document.selection.getAttributeKeys());
|
|
174
|
+
});
|
|
175
|
+
for (const [rootName, domRootElement] of editingView.domRoots) {
|
|
176
|
+
const data = formatSource(editor.data.get({ rootName }));
|
|
177
|
+
const domSourceEditingElementTextarea = createElement(domRootElement.ownerDocument, "textarea", {
|
|
178
|
+
rows: "1",
|
|
179
|
+
"aria-label": "Source code editing area"
|
|
180
|
+
});
|
|
181
|
+
const domSourceEditingElementWrapper = createElement(domRootElement.ownerDocument, "div", {
|
|
182
|
+
class: "ck-source-editing-area",
|
|
183
|
+
"data-value": data
|
|
184
|
+
}, [domSourceEditingElementTextarea]);
|
|
185
|
+
domSourceEditingElementTextarea.value = data;
|
|
186
|
+
domSourceEditingElementTextarea.setSelectionRange(0, 0);
|
|
187
|
+
domSourceEditingElementTextarea.addEventListener("input", () => {
|
|
188
|
+
domSourceEditingElementWrapper.dataset.value = domSourceEditingElementTextarea.value;
|
|
189
|
+
editor.ui.update();
|
|
190
|
+
});
|
|
191
|
+
domSourceEditingElementTextarea.addEventListener("keydown", (evt) => {
|
|
192
|
+
const key = evt.key.toLowerCase();
|
|
193
|
+
if ((evt.ctrlKey || evt.metaKey) && !evt.altKey && (key === "z" || key === "y")) {
|
|
194
|
+
evt.stopImmediatePropagation();
|
|
195
|
+
if (env.isMac && key === "y") {
|
|
196
|
+
evt.preventDefault();
|
|
197
|
+
domRootElement.ownerDocument.execCommand("redo");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
editingView.change((writer) => {
|
|
202
|
+
const viewRoot = editingView.document.getRoot(rootName);
|
|
203
|
+
writer.addClass("ck-hidden", viewRoot);
|
|
204
|
+
});
|
|
205
|
+
editor.ui.setEditableElement("sourceEditing:" + rootName, domSourceEditingElementTextarea);
|
|
206
|
+
this._replacedRoots.set(rootName, domSourceEditingElementWrapper);
|
|
207
|
+
this._elementReplacer.replace(domRootElement, domSourceEditingElementWrapper);
|
|
208
|
+
this._dataFromRoots.set(rootName, data);
|
|
209
|
+
}
|
|
210
|
+
this._hideDocumentOutline();
|
|
211
|
+
this._refreshAnnotationsVisibility();
|
|
212
|
+
this._focusSourceEditing();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Restores all hidden editing roots and sets the source data in them.
|
|
216
|
+
*/
|
|
217
|
+
_hideSourceEditing() {
|
|
218
|
+
const editingView = this.editor.editing.view;
|
|
219
|
+
this.updateEditorData();
|
|
220
|
+
editingView.change((writer) => {
|
|
221
|
+
for (const [rootName] of this._replacedRoots) writer.removeClass("ck-hidden", editingView.document.getRoot(rootName));
|
|
222
|
+
});
|
|
223
|
+
this._elementReplacer.restore();
|
|
224
|
+
this._replacedRoots.clear();
|
|
225
|
+
this._dataFromRoots.clear();
|
|
226
|
+
this._showDocumentOutline();
|
|
227
|
+
this._refreshAnnotationsVisibility();
|
|
228
|
+
editingView.focus();
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Hides the document outline if it is configured.
|
|
232
|
+
*/
|
|
233
|
+
_hideDocumentOutline() {
|
|
234
|
+
if (this.editor.plugins.has("DocumentOutlineUI")) this.editor.plugins.get("DocumentOutlineUI").view.element.style.display = "none";
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Shows the document outline if it was hidden when entering the source editing.
|
|
238
|
+
*/
|
|
239
|
+
_showDocumentOutline() {
|
|
240
|
+
if (this.editor.plugins.has("DocumentOutlineUI")) this.editor.plugins.get("DocumentOutlineUI").view.element.style.display = "";
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Hides the annotations when entering the source editing mode and shows back them after leaving it.
|
|
244
|
+
*/
|
|
245
|
+
_refreshAnnotationsVisibility() {
|
|
246
|
+
if (this.editor.plugins.has("Annotations")) this.editor.plugins.get("Annotations").refreshVisibility();
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Focuses the textarea containing document source from the first editing root.
|
|
250
|
+
*/
|
|
251
|
+
_focusSourceEditing() {
|
|
252
|
+
const editor = this.editor;
|
|
253
|
+
const [domSourceEditingElementWrapper] = this._replacedRoots.values();
|
|
254
|
+
const textarea = domSourceEditingElementWrapper.querySelector("textarea");
|
|
255
|
+
editor.editing.view.document.isFocused = false;
|
|
256
|
+
textarea.focus();
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Disables all commands.
|
|
260
|
+
*/
|
|
261
|
+
_disableCommands() {
|
|
262
|
+
const editor = this.editor;
|
|
263
|
+
for (const command of editor.commands.commands()) command.forceDisabled(COMMAND_FORCE_DISABLE_ID);
|
|
264
|
+
if (editor.plugins.has("CommentsArchiveUI")) editor.plugins.get("CommentsArchiveUI").forceDisabled(COMMAND_FORCE_DISABLE_ID);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Clears forced disable for all commands, that was previously set through {@link #_disableCommands}.
|
|
268
|
+
*/
|
|
269
|
+
_enableCommands() {
|
|
270
|
+
const editor = this.editor;
|
|
271
|
+
for (const command of editor.commands.commands()) command.clearForceDisabled(COMMAND_FORCE_DISABLE_ID);
|
|
272
|
+
if (editor.plugins.has("CommentsArchiveUI")) editor.plugins.get("CommentsArchiveUI").clearForceDisabled(COMMAND_FORCE_DISABLE_ID);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Adds or removes the `readonly` attribute from the textarea from all roots, if document source mode is active.
|
|
276
|
+
*
|
|
277
|
+
* @param isReadOnly Indicates whether all textarea elements should be read-only.
|
|
278
|
+
*/
|
|
279
|
+
_handleReadOnlyMode(isReadOnly) {
|
|
280
|
+
if (!this.isSourceEditingMode) return;
|
|
281
|
+
for (const [, domSourceEditingElementWrapper] of this._replacedRoots) domSourceEditingElementWrapper.querySelector("textarea").readOnly = isReadOnly;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Checks, if the plugin is allowed to handle the source editing mode by itself. Currently, the source editing mode is supported only
|
|
285
|
+
* for the {@link module:editor-classic/classiceditor~ClassicEditor classic editor}.
|
|
286
|
+
*/
|
|
287
|
+
_isAllowedToHandleSourceEditingMode() {
|
|
288
|
+
const editable = this.editor.ui.view.editable;
|
|
289
|
+
return editable && !editable.hasExternalElement;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* If any {@link module:ui/dialog/dialogview~DialogView editor dialog} is currently visible, hide it.
|
|
293
|
+
*/
|
|
294
|
+
_hideVisibleDialog() {
|
|
295
|
+
if (this.editor.plugins.has("Dialog")) {
|
|
296
|
+
const dialogPlugin = this.editor.plugins.get("Dialog");
|
|
297
|
+
if (dialogPlugin.isOpen) dialogPlugin.hide();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
_createButton(ButtonClass) {
|
|
301
|
+
const editor = this.editor;
|
|
302
|
+
const buttonView = new ButtonClass(editor.locale);
|
|
303
|
+
buttonView.set({
|
|
304
|
+
withText: true,
|
|
305
|
+
isToggleable: true
|
|
306
|
+
});
|
|
307
|
+
buttonView.bind("isOn").to(this, "isSourceEditingMode");
|
|
308
|
+
buttonView.bind("isEnabled").to(this, "isEnabled", editor, "isReadOnly", editor.plugins.get(PendingActions), "hasAny", (isEnabled, isEditorReadOnly, hasAnyPendingActions) => {
|
|
309
|
+
if (!isEnabled) return false;
|
|
310
|
+
if (isEditorReadOnly) return false;
|
|
311
|
+
if (hasAnyPendingActions) return false;
|
|
312
|
+
return true;
|
|
313
|
+
});
|
|
314
|
+
this.listenTo(buttonView, "execute", () => {
|
|
315
|
+
this.isSourceEditingMode = !this.isSourceEditingMode;
|
|
316
|
+
});
|
|
317
|
+
return buttonView;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
380
320
|
/**
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
return formatHtml(input);
|
|
321
|
+
* Formats the content for a better readability.
|
|
322
|
+
*
|
|
323
|
+
* For a non-HTML source the unchanged input string is returned.
|
|
324
|
+
*
|
|
325
|
+
* @param input Input string to check.
|
|
326
|
+
*/
|
|
327
|
+
function formatSource(input) {
|
|
328
|
+
if (!isHtml(input)) return input;
|
|
329
|
+
return formatHtml(input);
|
|
391
330
|
}
|
|
392
331
|
/**
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
332
|
+
* Checks, if the document source is HTML. It is sufficient to just check the first character from the document data.
|
|
333
|
+
*
|
|
334
|
+
* @param input Input string to check.
|
|
335
|
+
*/
|
|
336
|
+
function isHtml(input) {
|
|
337
|
+
return input.startsWith("<");
|
|
398
338
|
}
|
|
399
339
|
|
|
340
|
+
/**
|
|
341
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
342
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
343
|
+
*/
|
|
344
|
+
|
|
400
345
|
export { SourceEditing };
|
|
401
|
-
//# sourceMappingURL=index.js.map
|
|
346
|
+
//# sourceMappingURL=index.js.map
|