@ckeditor/ckeditor5-source-editing 38.1.0 → 38.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,298 +1,298 @@
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 source-editing/sourceediting
7
- */
8
- /* global console */
9
- import { Plugin, PendingActions } from 'ckeditor5/src/core';
10
- import { ButtonView } from 'ckeditor5/src/ui';
11
- import { createElement, ElementReplacer } from 'ckeditor5/src/utils';
12
- import { formatHtml } from './utils/formathtml';
13
- import '../theme/sourceediting.css';
14
- import sourceEditingIcon from '../theme/icons/source-editing.svg';
15
- const COMMAND_FORCE_DISABLE_ID = 'SourceEditingMode';
16
- /**
17
- * The source editing feature.
18
- *
19
- * It provides the possibility to view and edit the source of the document.
20
- *
21
- * For a detailed overview, check the {@glink features/source-editing source editing feature documentation} and the
22
- * {@glink api/source-editing package page}.
23
- */
24
- export default class SourceEditing extends Plugin {
25
- /**
26
- * @inheritDoc
27
- */
28
- static get pluginName() {
29
- return 'SourceEditing';
30
- }
31
- /**
32
- * @inheritDoc
33
- */
34
- static get requires() {
35
- return [PendingActions];
36
- }
37
- /**
38
- * @inheritDoc
39
- */
40
- constructor(editor) {
41
- super(editor);
42
- this.set('isSourceEditingMode', false);
43
- this._elementReplacer = new ElementReplacer();
44
- this._replacedRoots = new Map();
45
- this._dataFromRoots = new Map();
46
- }
47
- /**
48
- * @inheritDoc
49
- */
50
- init() {
51
- const editor = this.editor;
52
- const t = editor.t;
53
- editor.ui.componentFactory.add('sourceEditing', locale => {
54
- const buttonView = new ButtonView(locale);
55
- buttonView.set({
56
- label: t('Source'),
57
- icon: sourceEditingIcon,
58
- tooltip: true,
59
- withText: true,
60
- class: 'ck-source-editing-button'
61
- });
62
- buttonView.bind('isOn').to(this, 'isSourceEditingMode');
63
- // The button should be disabled if one of the following conditions is met:
64
- buttonView.bind('isEnabled').to(this, 'isEnabled', editor, 'isReadOnly', editor.plugins.get(PendingActions), 'hasAny', (isEnabled, isEditorReadOnly, hasAnyPendingActions) => {
65
- // (1) The plugin itself is disabled.
66
- if (!isEnabled) {
67
- return false;
68
- }
69
- // (2) The editor is in read-only mode.
70
- if (isEditorReadOnly) {
71
- return false;
72
- }
73
- // (3) Any pending action is scheduled. It may change the model, so modifying the document source should be prevented
74
- // until the model is finally set.
75
- if (hasAnyPendingActions) {
76
- return false;
77
- }
78
- return true;
79
- });
80
- this.listenTo(buttonView, 'execute', () => {
81
- this.isSourceEditingMode = !this.isSourceEditingMode;
82
- });
83
- return buttonView;
84
- });
85
- // Currently, the plugin handles the source editing mode by itself only for the classic editor. To use this plugin with other
86
- // integrations, listen to the `change:isSourceEditingMode` event and act accordingly.
87
- if (this._isAllowedToHandleSourceEditingMode()) {
88
- this.on('change:isSourceEditingMode', (evt, name, isSourceEditingMode) => {
89
- if (isSourceEditingMode) {
90
- this._showSourceEditing();
91
- this._disableCommands();
92
- }
93
- else {
94
- this._hideSourceEditing();
95
- this._enableCommands();
96
- }
97
- });
98
- this.on('change:isEnabled', (evt, name, isEnabled) => this._handleReadOnlyMode(!isEnabled));
99
- this.listenTo(editor, 'change:isReadOnly', (evt, name, isReadOnly) => this._handleReadOnlyMode(isReadOnly));
100
- }
101
- // Update the editor data while calling editor.getData() in the source editing mode.
102
- editor.data.on('get', () => {
103
- if (this.isSourceEditingMode) {
104
- this.updateEditorData();
105
- }
106
- }, { priority: 'high' });
107
- }
108
- /**
109
- * @inheritDoc
110
- */
111
- afterInit() {
112
- const editor = this.editor;
113
- const collaborationPluginNamesToWarn = [
114
- 'RealTimeCollaborativeEditing',
115
- 'CommentsEditing',
116
- 'TrackChangesEditing',
117
- 'RevisionHistory'
118
- ];
119
- // Currently, the basic integration with Collaboration Features is to display a warning in the console.
120
- if (collaborationPluginNamesToWarn.some(pluginName => editor.plugins.has(pluginName))) {
121
- console.warn('You initialized the editor with the source editing feature and at least one of the collaboration features. ' +
122
- 'Please be advised that the source editing feature may not work, and be careful when editing document source ' +
123
- 'that contains markers created by the collaboration features.');
124
- }
125
- // Restricted Editing integration can also lead to problems. Warn the user accordingly.
126
- if (editor.plugins.has('RestrictedEditingModeEditing')) {
127
- console.warn('You initialized the editor with the source editing feature and restricted editing feature. ' +
128
- 'Please be advised that the source editing feature may not work, and be careful when editing document source ' +
129
- 'that contains markers created by the restricted editing feature.');
130
- }
131
- }
132
- /**
133
- * Updates the source data in all hidden editing roots.
134
- */
135
- updateEditorData() {
136
- const editor = this.editor;
137
- const data = {};
138
- for (const [rootName, domSourceEditingElementWrapper] of this._replacedRoots) {
139
- const oldData = this._dataFromRoots.get(rootName);
140
- const newData = domSourceEditingElementWrapper.dataset.value;
141
- // Do not set the data unless some changes have been made in the meantime.
142
- // This prevents empty undo steps after switching to the normal editor.
143
- if (oldData !== newData) {
144
- data[rootName] = newData;
145
- }
146
- }
147
- if (Object.keys(data).length) {
148
- editor.data.set(data, { batchType: { isUndoable: true } });
149
- }
150
- }
151
- /**
152
- * Creates source editing wrappers that replace each editing root. Each wrapper contains the document source from the corresponding
153
- * root.
154
- *
155
- * The wrapper element contains a textarea and it solves the problem, that the textarea element cannot auto expand its height based on
156
- * 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
157
- * needs to, in order to display the whole document source without scrolling. The wrapper element is a parent for the textarea and for
158
- * the pseudo-element `::after`, that replicates the look, content, and position of the textarea. The pseudo-element replica is hidden,
159
- * 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
160
- * of its children (the textarea and the `::after` pseudo-element) are positioned within a CSS grid to occupy the same grid cell. The
161
- * content in the pseudo-element `::after` is set in CSS and it stretches the grid to the appropriate size based on the textarea value.
162
- * Since both children occupy the same grid cell, both have always the same height.
163
- */
164
- _showSourceEditing() {
165
- const editor = this.editor;
166
- const editingView = editor.editing.view;
167
- const model = editor.model;
168
- model.change(writer => {
169
- writer.setSelection(null);
170
- writer.removeSelectionAttribute(model.document.selection.getAttributeKeys());
171
- });
172
- // It is not needed to iterate through all editing roots, as currently the plugin supports only the Classic Editor with a single
173
- // main root, but this code may help understand and use this feature in external integrations.
174
- for (const [rootName, domRootElement] of editingView.domRoots) {
175
- const data = formatSource(editor.data.get({ rootName }));
176
- const domSourceEditingElementTextarea = createElement(domRootElement.ownerDocument, 'textarea', {
177
- rows: '1',
178
- 'aria-label': 'Source code editing area'
179
- });
180
- const domSourceEditingElementWrapper = createElement(domRootElement.ownerDocument, 'div', {
181
- class: 'ck-source-editing-area',
182
- 'data-value': data
183
- }, [domSourceEditingElementTextarea]);
184
- domSourceEditingElementTextarea.value = data;
185
- // Setting a value to textarea moves the input cursor to the end. We want the selection at the beginning.
186
- domSourceEditingElementTextarea.setSelectionRange(0, 0);
187
- // Bind the textarea's value to the wrapper's `data-value` property. Each change of the textarea's value updates the
188
- // wrapper's `data-value` property.
189
- domSourceEditingElementTextarea.addEventListener('input', () => {
190
- domSourceEditingElementWrapper.dataset.value = domSourceEditingElementTextarea.value;
191
- editor.ui.update();
192
- });
193
- editingView.change(writer => {
194
- const viewRoot = editingView.document.getRoot(rootName);
195
- writer.addClass('ck-hidden', viewRoot);
196
- });
197
- // Register the element so it becomes available for Alt+F10 and Esc navigation.
198
- editor.ui.setEditableElement('sourceEditing:' + rootName, domSourceEditingElementTextarea);
199
- this._replacedRoots.set(rootName, domSourceEditingElementWrapper);
200
- this._elementReplacer.replace(domRootElement, domSourceEditingElementWrapper);
201
- this._dataFromRoots.set(rootName, data);
202
- }
203
- this._focusSourceEditing();
204
- }
205
- /**
206
- * Restores all hidden editing roots and sets the source data in them.
207
- */
208
- _hideSourceEditing() {
209
- const editor = this.editor;
210
- const editingView = editor.editing.view;
211
- this.updateEditorData();
212
- editingView.change(writer => {
213
- for (const [rootName] of this._replacedRoots) {
214
- writer.removeClass('ck-hidden', editingView.document.getRoot(rootName));
215
- }
216
- });
217
- this._elementReplacer.restore();
218
- this._replacedRoots.clear();
219
- this._dataFromRoots.clear();
220
- editingView.focus();
221
- }
222
- /**
223
- * Focuses the textarea containing document source from the first editing root.
224
- */
225
- _focusSourceEditing() {
226
- const editor = this.editor;
227
- const [domSourceEditingElementWrapper] = this._replacedRoots.values();
228
- const textarea = domSourceEditingElementWrapper.querySelector('textarea');
229
- // The FocusObserver was disabled by View.render() while the DOM root was getting hidden and the replacer
230
- // revealed the textarea. So it couldn't notice that the DOM root got blurred in the process.
231
- // Let's sync this state manually here because otherwise Renderer will attempt to render selection
232
- // in an invisible DOM root.
233
- editor.editing.view.document.isFocused = false;
234
- textarea.focus();
235
- }
236
- /**
237
- * Disables all commands.
238
- */
239
- _disableCommands() {
240
- const editor = this.editor;
241
- for (const command of editor.commands.commands()) {
242
- command.forceDisabled(COMMAND_FORCE_DISABLE_ID);
243
- }
244
- }
245
- /**
246
- * Clears forced disable for all commands, that was previously set through {@link #_disableCommands}.
247
- */
248
- _enableCommands() {
249
- const editor = this.editor;
250
- for (const command of editor.commands.commands()) {
251
- command.clearForceDisabled(COMMAND_FORCE_DISABLE_ID);
252
- }
253
- }
254
- /**
255
- * Adds or removes the `readonly` attribute from the textarea from all roots, if document source mode is active.
256
- *
257
- * @param isReadOnly Indicates whether all textarea elements should be read-only.
258
- */
259
- _handleReadOnlyMode(isReadOnly) {
260
- if (!this.isSourceEditingMode) {
261
- return;
262
- }
263
- for (const [, domSourceEditingElementWrapper] of this._replacedRoots) {
264
- domSourceEditingElementWrapper.querySelector('textarea').readOnly = isReadOnly;
265
- }
266
- }
267
- /**
268
- * Checks, if the plugin is allowed to handle the source editing mode by itself. Currently, the source editing mode is supported only
269
- * for the {@link module:editor-classic/classiceditor~ClassicEditor classic editor}.
270
- */
271
- _isAllowedToHandleSourceEditingMode() {
272
- const editor = this.editor;
273
- const editable = editor.ui.view.editable;
274
- // Checks, if the editor's editable belongs to the editor's DOM tree.
275
- return editable && !editable.hasExternalElement;
276
- }
277
- }
278
- /**
279
- * Formats the content for a better readability.
280
- *
281
- * For a non-HTML source the unchanged input string is returned.
282
- *
283
- * @param input Input string to check.
284
- */
285
- function formatSource(input) {
286
- if (!isHtml(input)) {
287
- return input;
288
- }
289
- return formatHtml(input);
290
- }
291
- /**
292
- * Checks, if the document source is HTML. It is sufficient to just check the first character from the document data.
293
- *
294
- * @param input Input string to check.
295
- */
296
- function isHtml(input) {
297
- return input.startsWith('<');
298
- }
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 source-editing/sourceediting
7
+ */
8
+ /* global console */
9
+ import { Plugin, PendingActions } from 'ckeditor5/src/core';
10
+ import { ButtonView } from 'ckeditor5/src/ui';
11
+ import { createElement, ElementReplacer } from 'ckeditor5/src/utils';
12
+ import { formatHtml } from './utils/formathtml';
13
+ import '../theme/sourceediting.css';
14
+ import sourceEditingIcon from '../theme/icons/source-editing.svg';
15
+ const COMMAND_FORCE_DISABLE_ID = 'SourceEditingMode';
16
+ /**
17
+ * The source editing feature.
18
+ *
19
+ * It provides the possibility to view and edit the source of the document.
20
+ *
21
+ * For a detailed overview, check the {@glink features/source-editing source editing feature documentation} and the
22
+ * {@glink api/source-editing package page}.
23
+ */
24
+ export default class SourceEditing extends Plugin {
25
+ /**
26
+ * @inheritDoc
27
+ */
28
+ static get pluginName() {
29
+ return 'SourceEditing';
30
+ }
31
+ /**
32
+ * @inheritDoc
33
+ */
34
+ static get requires() {
35
+ return [PendingActions];
36
+ }
37
+ /**
38
+ * @inheritDoc
39
+ */
40
+ constructor(editor) {
41
+ super(editor);
42
+ this.set('isSourceEditingMode', false);
43
+ this._elementReplacer = new ElementReplacer();
44
+ this._replacedRoots = new Map();
45
+ this._dataFromRoots = new Map();
46
+ }
47
+ /**
48
+ * @inheritDoc
49
+ */
50
+ init() {
51
+ const editor = this.editor;
52
+ const t = editor.t;
53
+ editor.ui.componentFactory.add('sourceEditing', locale => {
54
+ const buttonView = new ButtonView(locale);
55
+ buttonView.set({
56
+ label: t('Source'),
57
+ icon: sourceEditingIcon,
58
+ tooltip: true,
59
+ withText: true,
60
+ class: 'ck-source-editing-button'
61
+ });
62
+ buttonView.bind('isOn').to(this, 'isSourceEditingMode');
63
+ // The button should be disabled if one of the following conditions is met:
64
+ buttonView.bind('isEnabled').to(this, 'isEnabled', editor, 'isReadOnly', editor.plugins.get(PendingActions), 'hasAny', (isEnabled, isEditorReadOnly, hasAnyPendingActions) => {
65
+ // (1) The plugin itself is disabled.
66
+ if (!isEnabled) {
67
+ return false;
68
+ }
69
+ // (2) The editor is in read-only mode.
70
+ if (isEditorReadOnly) {
71
+ return false;
72
+ }
73
+ // (3) Any pending action is scheduled. It may change the model, so modifying the document source should be prevented
74
+ // until the model is finally set.
75
+ if (hasAnyPendingActions) {
76
+ return false;
77
+ }
78
+ return true;
79
+ });
80
+ this.listenTo(buttonView, 'execute', () => {
81
+ this.isSourceEditingMode = !this.isSourceEditingMode;
82
+ });
83
+ return buttonView;
84
+ });
85
+ // Currently, the plugin handles the source editing mode by itself only for the classic editor. To use this plugin with other
86
+ // integrations, listen to the `change:isSourceEditingMode` event and act accordingly.
87
+ if (this._isAllowedToHandleSourceEditingMode()) {
88
+ this.on('change:isSourceEditingMode', (evt, name, isSourceEditingMode) => {
89
+ if (isSourceEditingMode) {
90
+ this._showSourceEditing();
91
+ this._disableCommands();
92
+ }
93
+ else {
94
+ this._hideSourceEditing();
95
+ this._enableCommands();
96
+ }
97
+ });
98
+ this.on('change:isEnabled', (evt, name, isEnabled) => this._handleReadOnlyMode(!isEnabled));
99
+ this.listenTo(editor, 'change:isReadOnly', (evt, name, isReadOnly) => this._handleReadOnlyMode(isReadOnly));
100
+ }
101
+ // Update the editor data while calling editor.getData() in the source editing mode.
102
+ editor.data.on('get', () => {
103
+ if (this.isSourceEditingMode) {
104
+ this.updateEditorData();
105
+ }
106
+ }, { priority: 'high' });
107
+ }
108
+ /**
109
+ * @inheritDoc
110
+ */
111
+ afterInit() {
112
+ const editor = this.editor;
113
+ const collaborationPluginNamesToWarn = [
114
+ 'RealTimeCollaborativeEditing',
115
+ 'CommentsEditing',
116
+ 'TrackChangesEditing',
117
+ 'RevisionHistory'
118
+ ];
119
+ // Currently, the basic integration with Collaboration Features is to display a warning in the console.
120
+ if (collaborationPluginNamesToWarn.some(pluginName => editor.plugins.has(pluginName))) {
121
+ console.warn('You initialized the editor with the source editing feature and at least one of the collaboration features. ' +
122
+ 'Please be advised that the source editing feature may not work, and be careful when editing document source ' +
123
+ 'that contains markers created by the collaboration features.');
124
+ }
125
+ // Restricted Editing integration can also lead to problems. Warn the user accordingly.
126
+ if (editor.plugins.has('RestrictedEditingModeEditing')) {
127
+ console.warn('You initialized the editor with the source editing feature and restricted editing feature. ' +
128
+ 'Please be advised that the source editing feature may not work, and be careful when editing document source ' +
129
+ 'that contains markers created by the restricted editing feature.');
130
+ }
131
+ }
132
+ /**
133
+ * Updates the source data in all hidden editing roots.
134
+ */
135
+ updateEditorData() {
136
+ const editor = this.editor;
137
+ const data = {};
138
+ for (const [rootName, domSourceEditingElementWrapper] of this._replacedRoots) {
139
+ const oldData = this._dataFromRoots.get(rootName);
140
+ const newData = domSourceEditingElementWrapper.dataset.value;
141
+ // Do not set the data unless some changes have been made in the meantime.
142
+ // This prevents empty undo steps after switching to the normal editor.
143
+ if (oldData !== newData) {
144
+ data[rootName] = newData;
145
+ }
146
+ }
147
+ if (Object.keys(data).length) {
148
+ editor.data.set(data, { batchType: { isUndoable: true } });
149
+ }
150
+ }
151
+ /**
152
+ * Creates source editing wrappers that replace each editing root. Each wrapper contains the document source from the corresponding
153
+ * root.
154
+ *
155
+ * The wrapper element contains a textarea and it solves the problem, that the textarea element cannot auto expand its height based on
156
+ * 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
157
+ * needs to, in order to display the whole document source without scrolling. The wrapper element is a parent for the textarea and for
158
+ * the pseudo-element `::after`, that replicates the look, content, and position of the textarea. The pseudo-element replica is hidden,
159
+ * 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
160
+ * of its children (the textarea and the `::after` pseudo-element) are positioned within a CSS grid to occupy the same grid cell. The
161
+ * content in the pseudo-element `::after` is set in CSS and it stretches the grid to the appropriate size based on the textarea value.
162
+ * Since both children occupy the same grid cell, both have always the same height.
163
+ */
164
+ _showSourceEditing() {
165
+ const editor = this.editor;
166
+ const editingView = editor.editing.view;
167
+ const model = editor.model;
168
+ model.change(writer => {
169
+ writer.setSelection(null);
170
+ writer.removeSelectionAttribute(model.document.selection.getAttributeKeys());
171
+ });
172
+ // It is not needed to iterate through all editing roots, as currently the plugin supports only the Classic Editor with a single
173
+ // main root, but this code may help understand and use this feature in external integrations.
174
+ for (const [rootName, domRootElement] of editingView.domRoots) {
175
+ const data = formatSource(editor.data.get({ rootName }));
176
+ const domSourceEditingElementTextarea = createElement(domRootElement.ownerDocument, 'textarea', {
177
+ rows: '1',
178
+ 'aria-label': 'Source code editing area'
179
+ });
180
+ const domSourceEditingElementWrapper = createElement(domRootElement.ownerDocument, 'div', {
181
+ class: 'ck-source-editing-area',
182
+ 'data-value': data
183
+ }, [domSourceEditingElementTextarea]);
184
+ domSourceEditingElementTextarea.value = data;
185
+ // Setting a value to textarea moves the input cursor to the end. We want the selection at the beginning.
186
+ domSourceEditingElementTextarea.setSelectionRange(0, 0);
187
+ // Bind the textarea's value to the wrapper's `data-value` property. Each change of the textarea's value updates the
188
+ // wrapper's `data-value` property.
189
+ domSourceEditingElementTextarea.addEventListener('input', () => {
190
+ domSourceEditingElementWrapper.dataset.value = domSourceEditingElementTextarea.value;
191
+ editor.ui.update();
192
+ });
193
+ editingView.change(writer => {
194
+ const viewRoot = editingView.document.getRoot(rootName);
195
+ writer.addClass('ck-hidden', viewRoot);
196
+ });
197
+ // Register the element so it becomes available for Alt+F10 and Esc navigation.
198
+ editor.ui.setEditableElement('sourceEditing:' + rootName, domSourceEditingElementTextarea);
199
+ this._replacedRoots.set(rootName, domSourceEditingElementWrapper);
200
+ this._elementReplacer.replace(domRootElement, domSourceEditingElementWrapper);
201
+ this._dataFromRoots.set(rootName, data);
202
+ }
203
+ this._focusSourceEditing();
204
+ }
205
+ /**
206
+ * Restores all hidden editing roots and sets the source data in them.
207
+ */
208
+ _hideSourceEditing() {
209
+ const editor = this.editor;
210
+ const editingView = editor.editing.view;
211
+ this.updateEditorData();
212
+ editingView.change(writer => {
213
+ for (const [rootName] of this._replacedRoots) {
214
+ writer.removeClass('ck-hidden', editingView.document.getRoot(rootName));
215
+ }
216
+ });
217
+ this._elementReplacer.restore();
218
+ this._replacedRoots.clear();
219
+ this._dataFromRoots.clear();
220
+ editingView.focus();
221
+ }
222
+ /**
223
+ * Focuses the textarea containing document source from the first editing root.
224
+ */
225
+ _focusSourceEditing() {
226
+ const editor = this.editor;
227
+ const [domSourceEditingElementWrapper] = this._replacedRoots.values();
228
+ const textarea = domSourceEditingElementWrapper.querySelector('textarea');
229
+ // The FocusObserver was disabled by View.render() while the DOM root was getting hidden and the replacer
230
+ // revealed the textarea. So it couldn't notice that the DOM root got blurred in the process.
231
+ // Let's sync this state manually here because otherwise Renderer will attempt to render selection
232
+ // in an invisible DOM root.
233
+ editor.editing.view.document.isFocused = false;
234
+ textarea.focus();
235
+ }
236
+ /**
237
+ * Disables all commands.
238
+ */
239
+ _disableCommands() {
240
+ const editor = this.editor;
241
+ for (const command of editor.commands.commands()) {
242
+ command.forceDisabled(COMMAND_FORCE_DISABLE_ID);
243
+ }
244
+ }
245
+ /**
246
+ * Clears forced disable for all commands, that was previously set through {@link #_disableCommands}.
247
+ */
248
+ _enableCommands() {
249
+ const editor = this.editor;
250
+ for (const command of editor.commands.commands()) {
251
+ command.clearForceDisabled(COMMAND_FORCE_DISABLE_ID);
252
+ }
253
+ }
254
+ /**
255
+ * Adds or removes the `readonly` attribute from the textarea from all roots, if document source mode is active.
256
+ *
257
+ * @param isReadOnly Indicates whether all textarea elements should be read-only.
258
+ */
259
+ _handleReadOnlyMode(isReadOnly) {
260
+ if (!this.isSourceEditingMode) {
261
+ return;
262
+ }
263
+ for (const [, domSourceEditingElementWrapper] of this._replacedRoots) {
264
+ domSourceEditingElementWrapper.querySelector('textarea').readOnly = isReadOnly;
265
+ }
266
+ }
267
+ /**
268
+ * Checks, if the plugin is allowed to handle the source editing mode by itself. Currently, the source editing mode is supported only
269
+ * for the {@link module:editor-classic/classiceditor~ClassicEditor classic editor}.
270
+ */
271
+ _isAllowedToHandleSourceEditingMode() {
272
+ const editor = this.editor;
273
+ const editable = editor.ui.view.editable;
274
+ // Checks, if the editor's editable belongs to the editor's DOM tree.
275
+ return editable && !editable.hasExternalElement;
276
+ }
277
+ }
278
+ /**
279
+ * Formats the content for a better readability.
280
+ *
281
+ * For a non-HTML source the unchanged input string is returned.
282
+ *
283
+ * @param input Input string to check.
284
+ */
285
+ function formatSource(input) {
286
+ if (!isHtml(input)) {
287
+ return input;
288
+ }
289
+ return formatHtml(input);
290
+ }
291
+ /**
292
+ * Checks, if the document source is HTML. It is sufficient to just check the first character from the document data.
293
+ *
294
+ * @param input Input string to check.
295
+ */
296
+ function isHtml(input) {
297
+ return input.startsWith('<');
298
+ }
@@ -1,19 +1,19 @@
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 source-editing/utils/formathtml
7
- */
8
- /**
9
- * A simple (and naive) HTML code formatter that returns a formatted HTML markup that can be easily
10
- * parsed by human eyes. It beautifies the HTML code by adding new lines between elements that behave like block elements
11
- * (https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
12
- * and a few more like `tr`, `td`, and similar ones) and inserting indents for nested content.
13
- *
14
- * WARNING: This function works only on a text that does not contain any indentations or new lines.
15
- * Calling this function on the already formatted text will damage the formatting.
16
- *
17
- * @param input An HTML string to format.
18
- */
19
- export declare function formatHtml(input: string): string;
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 source-editing/utils/formathtml
7
+ */
8
+ /**
9
+ * A simple (and naive) HTML code formatter that returns a formatted HTML markup that can be easily
10
+ * parsed by human eyes. It beautifies the HTML code by adding new lines between elements that behave like block elements
11
+ * (https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
12
+ * and a few more like `tr`, `td`, and similar ones) and inserting indents for nested content.
13
+ *
14
+ * WARNING: This function works only on a text that does not contain any indentations or new lines.
15
+ * Calling this function on the already formatted text will damage the formatting.
16
+ *
17
+ * @param input An HTML string to format.
18
+ */
19
+ export declare function formatHtml(input: string): string;