@ckeditor/ckeditor5-watchdog 47.6.1 → 48.0.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/LICENSE.md +1 -1
- package/ckeditor5-metadata.json +1 -1
- package/{src → dist}/contextwatchdog.d.ts +13 -8
- package/{src → dist}/editorwatchdog.d.ts +37 -4
- package/dist/index.css +3 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +209 -59
- package/dist/index.js.map +1 -1
- package/dist/utils/normalizerootsconfig.d.ts +18 -0
- package/package.json +22 -42
- package/src/actionsrecorder.js +0 -627
- package/src/actionsrecorderconfig.js +0 -5
- package/src/augmentation.js +0 -5
- package/src/contextwatchdog.js +0 -423
- package/src/editorwatchdog.js +0 -470
- package/src/index.js +0 -12
- package/src/utils/areconnectedthroughproperties.js +0 -59
- package/src/utils/getsubnodes.js +0 -79
- package/src/watchdog.js +0 -201
- /package/{src → dist}/actionsrecorder.d.ts +0 -0
- /package/{src → dist}/actionsrecorderconfig.d.ts +0 -0
- /package/{src → dist}/augmentation.d.ts +0 -0
- /package/{src → dist}/index.d.ts +0 -0
- /package/{src → dist}/utils/areconnectedthroughproperties.d.ts +0 -0
- /package/{src → dist}/utils/getsubnodes.d.ts +0 -0
- /package/{src → dist}/watchdog.d.ts +0 -0
package/LICENSE.md
CHANGED
|
@@ -18,7 +18,7 @@ Where not otherwise indicated, all CKEditor 5 content is authored by CKSour
|
|
|
18
18
|
|
|
19
19
|
The following libraries are included in CKEditor 5 under the [MIT license](https://opensource.org/licenses/MIT):
|
|
20
20
|
|
|
21
|
-
* es-toolkit - Copyright (c) 2024 Viva Republica, Inc.
|
|
21
|
+
* es-toolkit - Copyright (c) 2024 Viva Republica, Inc and Copyright OpenJS Foundation and other contributors.
|
|
22
22
|
|
|
23
23
|
Trademarks
|
|
24
24
|
----------
|
package/ckeditor5-metadata.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"className": "EditorWatchdog",
|
|
6
6
|
"description": "A utility that detects if an editor crashed, destroys it, and automatically creates a new instance of that editor with the content of the previous editor.",
|
|
7
7
|
"docs": "features/watchdog.html",
|
|
8
|
-
"path": "src/editorwatchdog.
|
|
8
|
+
"path": "src/editorwatchdog.ts"
|
|
9
9
|
}
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -152,12 +152,12 @@ export declare class ContextWatchdog<TContext extends Context = Context> extends
|
|
|
152
152
|
* await watchdog.add( [ {
|
|
153
153
|
* id: 'editor1',
|
|
154
154
|
* type: 'editor',
|
|
155
|
-
* sourceElementOrData: document.querySelector( '#editor' ),
|
|
156
155
|
* config: {
|
|
156
|
+
* attachTo: document.querySelector( '#editor' ),
|
|
157
157
|
* plugins: [ Essentials, Paragraph, Bold, Italic ],
|
|
158
158
|
* toolbar: [ 'bold', 'italic', 'alignment' ]
|
|
159
159
|
* },
|
|
160
|
-
* creator:
|
|
160
|
+
* creator: config => ClassicEditor.create( config )
|
|
161
161
|
* } ] );
|
|
162
162
|
* ```
|
|
163
163
|
*
|
|
@@ -167,13 +167,13 @@ export declare class ContextWatchdog<TContext extends Context = Context> extends
|
|
|
167
167
|
* await watchdog.add( {
|
|
168
168
|
* id: 'editor1',
|
|
169
169
|
* type: 'editor',
|
|
170
|
-
* sourceElementOrData: document.querySelector( '#editor' ),
|
|
171
170
|
* config: {
|
|
171
|
+
* attachTo: document.querySelector( '#editor' ),
|
|
172
172
|
* plugins: [ Essentials, Paragraph, Bold, Italic ],
|
|
173
173
|
* toolbar: [ 'bold', 'italic', 'alignment' ]
|
|
174
174
|
* },
|
|
175
|
-
* creator:
|
|
176
|
-
*
|
|
175
|
+
* creator: config => ClassicEditor.create( config )
|
|
176
|
+
* } );
|
|
177
177
|
* ```
|
|
178
178
|
*
|
|
179
179
|
* Then an instance can be retrieved using the {@link #getItem} method:
|
|
@@ -312,8 +312,8 @@ export interface ContextWatchdogItemConfiguration {
|
|
|
312
312
|
*/
|
|
313
313
|
type: 'editor';
|
|
314
314
|
/**
|
|
315
|
-
* A function that initializes the item (the editor). The function takes editor
|
|
316
|
-
* and should return a promise. For example: `
|
|
315
|
+
* A function that initializes the item (the editor). The function takes the editor configuration
|
|
316
|
+
* and should return a promise. For example: `config => ClassicEditor.create( config )`.
|
|
317
317
|
*/
|
|
318
318
|
creator: EditorWatchdogCreatorFunction;
|
|
319
319
|
/**
|
|
@@ -324,8 +324,13 @@ export interface ContextWatchdogItemConfiguration {
|
|
|
324
324
|
/**
|
|
325
325
|
* The source element or data that will be passed
|
|
326
326
|
* as the first argument to the `Editor.create()` method.
|
|
327
|
+
*
|
|
328
|
+
* When not provided, the watchdog will use config-based creator mode, passing only the {@link #config}
|
|
329
|
+
* to the editor creator.
|
|
330
|
+
*
|
|
331
|
+
* @deprecated
|
|
327
332
|
*/
|
|
328
|
-
sourceElementOrData
|
|
333
|
+
sourceElementOrData?: string | HTMLElement;
|
|
329
334
|
/**
|
|
330
335
|
* An editor configuration.
|
|
331
336
|
*/
|
|
@@ -41,9 +41,19 @@ export declare class EditorWatchdog<TEditor extends Editor = Editor> extends Wat
|
|
|
41
41
|
*/
|
|
42
42
|
private _elementOrData?;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* Stores the original DOM element for single-root editors.
|
|
45
45
|
*/
|
|
46
|
-
private
|
|
46
|
+
private _editorAttachTo;
|
|
47
|
+
/**
|
|
48
|
+
* Specifies whether the editor is a single-root editor (e.g. ClassicEditor) or a multi-root editor (e.g. MultiRootEditor).
|
|
49
|
+
*/
|
|
50
|
+
private _isSingleRootEditor;
|
|
51
|
+
/**
|
|
52
|
+
* Specifies whether the editor was created using config-based creator mode (without a source element or data as the first argument).
|
|
53
|
+
*
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
_isUsingConfigBasedCreator: boolean;
|
|
47
57
|
/**
|
|
48
58
|
* The latest record of the editor editable elements. Used to restart the editor.
|
|
49
59
|
*/
|
|
@@ -84,6 +94,14 @@ export declare class EditorWatchdog<TEditor extends Editor = Editor> extends Wat
|
|
|
84
94
|
* Sets the function that is responsible for the editor creation.
|
|
85
95
|
* It expects a function that should return a promise.
|
|
86
96
|
*
|
|
97
|
+
* For config-based editor creation:
|
|
98
|
+
*
|
|
99
|
+
* ```ts
|
|
100
|
+
* watchdog.setCreator( config => ClassicEditor.create( config ) );
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* For legacy editor creation (with element or data as the first argument):
|
|
104
|
+
*
|
|
87
105
|
* ```ts
|
|
88
106
|
* watchdog.setCreator( ( element, config ) => ClassicEditor.create( element, config ) );
|
|
89
107
|
* ```
|
|
@@ -117,11 +135,21 @@ export declare class EditorWatchdog<TEditor extends Editor = Editor> extends Wat
|
|
|
117
135
|
/**
|
|
118
136
|
* Creates the editor instance and keeps it running, using the defined creator and destructor.
|
|
119
137
|
*
|
|
138
|
+
* @param config The editor configuration.
|
|
139
|
+
* @param context A context for the editor.
|
|
140
|
+
*/
|
|
141
|
+
create(config: EditorConfig, context?: Context): Promise<unknown>;
|
|
142
|
+
/**
|
|
143
|
+
* Creates the editor instance and keeps it running, using the defined creator and destructor.
|
|
144
|
+
*
|
|
145
|
+
* **Note**: This method signature is deprecated and will be removed in the future release.
|
|
146
|
+
*
|
|
147
|
+
* @deprecated
|
|
120
148
|
* @param elementOrData The editor source element or the editor data.
|
|
121
149
|
* @param config The editor configuration.
|
|
122
150
|
* @param context A context for the editor.
|
|
123
151
|
*/
|
|
124
|
-
create(elementOrData
|
|
152
|
+
create(elementOrData: HTMLElement | string | Record<string, string> | Record<string, HTMLElement>, config: EditorConfig, context?: Context): Promise<unknown>;
|
|
125
153
|
/**
|
|
126
154
|
* Destroys the watchdog and the current editor instance. It fires the callback
|
|
127
155
|
* registered in {@link #setDestructor `setDestructor()`} and uses it to destroy the editor instance.
|
|
@@ -153,6 +181,11 @@ export declare class EditorWatchdog<TEditor extends Editor = Editor> extends Wat
|
|
|
153
181
|
* @internal
|
|
154
182
|
*/
|
|
155
183
|
_isErrorComingFromThisItem(error: CKEditorError): boolean;
|
|
184
|
+
/**
|
|
185
|
+
* Detects whether the `create()` call was made in config-based creator mode
|
|
186
|
+
* (i.e., the first argument is a config object rather than a source element or data).
|
|
187
|
+
*/
|
|
188
|
+
private _detectConfigBasedCreator;
|
|
156
189
|
/**
|
|
157
190
|
* Clones the editor configuration.
|
|
158
191
|
*/
|
|
@@ -188,4 +221,4 @@ export type EditorWatchdogRestartEvent = {
|
|
|
188
221
|
args: [];
|
|
189
222
|
return: undefined;
|
|
190
223
|
};
|
|
191
|
-
export type EditorWatchdogCreatorFunction<TEditor = Editor> = (elementOrData: HTMLElement | string | Record<string, string> | Record<string, HTMLElement
|
|
224
|
+
export type EditorWatchdogCreatorFunction<TEditor = Editor> = ((config: EditorConfig) => Promise<TEditor>) | ((elementOrData: HTMLElement | string | Record<string, string> | Record<string, HTMLElement> | undefined, config: EditorConfig) => Promise<TEditor>);
|
package/dist/index.css
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["index.css"],"names":[],"mappings":";;;;;;AAEA,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC","file":"index.css.map","sourcesContent":["\n\n/*# sourceMappingURL=index.css.map */"]}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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 {
|
|
5
|
+
import { isElement as isElement$2, throttle, cloneDeepWith, isPlainObject as isPlainObject$1 } from 'es-toolkit/compat';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
@@ -274,6 +274,82 @@ function isObject(structure) {
|
|
|
274
274
|
return typeof structure === 'object' && structure !== null;
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
+
// Note: This file is a copy of core/editor/utils/normalizerootsconfig with some adjustments.
|
|
278
|
+
// The main difference is that it does not use the `Config` class from `@ckeditor/ckeditor5-utils` and instead works
|
|
279
|
+
// with a plain `EditorConfig` object. It also does not throw `CKEditorError` but a generic `Error` with a specific message.
|
|
280
|
+
// It resolves root configuration for the editor, but does not care about real data in initialData as the watchdog does not need it.
|
|
281
|
+
// It just needs to ensure that initialData is defined for all roots, so the editor features can work with it without additional checks.
|
|
282
|
+
// Note that this helper modifies the provided config object.
|
|
283
|
+
/**
|
|
284
|
+
* Normalizes the editor roots configuration. It ensures that all root configurations are defined in `config.roots`
|
|
285
|
+
* and that they have `initialData` defined.
|
|
286
|
+
*
|
|
287
|
+
* It normalizes a single-root configuration (where `config.root` is used) to a multi-root configuration
|
|
288
|
+
* (where all roots are defined in `config.roots`). This is considered a standard configuration format,
|
|
289
|
+
* so the editor features can always expect roots to be defined in `config.roots`.
|
|
290
|
+
*
|
|
291
|
+
* It also handles legacy configuration options, such as `config.initialData`, `config.placeholder`, and `config.label`.
|
|
292
|
+
*
|
|
293
|
+
* @internal
|
|
294
|
+
*/ function normalizeRootsConfig(sourceElementsOrData, config, defaultRootName) {
|
|
295
|
+
const mainRootConfig = config.root;
|
|
296
|
+
const rootsConfig = config.roots || Object.create(null);
|
|
297
|
+
// Move `config.root` to `config.roots.main`.
|
|
298
|
+
// This makes access to root configuration more consistent as all roots will be defined in `config.roots`.
|
|
299
|
+
if (defaultRootName && !rootsConfig[defaultRootName]) {
|
|
300
|
+
rootsConfig[defaultRootName] = mainRootConfig || Object.create(null);
|
|
301
|
+
}
|
|
302
|
+
const sourceElementIsPlainObject = isPlainObject(sourceElementsOrData);
|
|
303
|
+
// Collect legacy configuration values for `initialData`, `placeholder`, and `label` from the config.
|
|
304
|
+
const legacyInitialData = getLegacyInitialData(config, sourceElementIsPlainObject, defaultRootName);
|
|
305
|
+
// Collect root names. This includes root names from the source element (if it's an object),
|
|
306
|
+
// from `config.roots`, and from legacy config. This ensures that all roots are processed in the next step.
|
|
307
|
+
const rootNames = Array.from(new Set([
|
|
308
|
+
...sourceElementIsPlainObject ? Object.keys(sourceElementsOrData) : [],
|
|
309
|
+
...Object.keys(rootsConfig),
|
|
310
|
+
...Object.keys(legacyInitialData)
|
|
311
|
+
]));
|
|
312
|
+
// Ensure that all roots have `initialData` defined. If not, try to get it from the source element or data.
|
|
313
|
+
for (const rootName of rootNames){
|
|
314
|
+
const rootConfig = rootsConfig[rootName] || Object.create(null);
|
|
315
|
+
// Watchdog does not need HTML, it uses dedicated plugin for restoring the model.
|
|
316
|
+
// As watchdog uses only DOM element or map of DOM elements for sourceElementsOrData,
|
|
317
|
+
// it can not use string as initial data, so it can be safely set to an empty string.
|
|
318
|
+
rootConfig.initialData = '';
|
|
319
|
+
// Handle legacy `config.placeholder` and `config.label` for the root.
|
|
320
|
+
rootConfig.placeholder ??= getLegacyPlainConfigValue(config, 'placeholder', rootName);
|
|
321
|
+
rootConfig.label ??= getLegacyPlainConfigValue(config, 'label', rootName);
|
|
322
|
+
rootsConfig[rootName] = rootConfig;
|
|
323
|
+
}
|
|
324
|
+
config.roots = rootsConfig;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Type guard to check if the provided value is a plain object.
|
|
328
|
+
*/ function isPlainObject(sourceElementsOrData) {
|
|
329
|
+
return !!sourceElementsOrData && typeof sourceElementsOrData == 'object' && !Array.isArray(sourceElementsOrData) && !isElement$1(sourceElementsOrData);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Retrieve legacy configuration value for `initialData` from the config.
|
|
333
|
+
* Normalize single-root config so returned value is always an object with root names as keys.
|
|
334
|
+
*/ function getLegacyInitialData(config, sourceElementIsObject, defaultRootName) {
|
|
335
|
+
return sourceElementIsObject || !defaultRootName ? config.initialData || Object.create(null) : {
|
|
336
|
+
[defaultRootName]: config.initialData
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Retrieve legacy configuration value for `placeholder` or `label` from the config for a specific root.
|
|
341
|
+
*/ function getLegacyPlainConfigValue(config, key, rootName) {
|
|
342
|
+
const legacyValue = config[key];
|
|
343
|
+
if (legacyValue) {
|
|
344
|
+
return typeof legacyValue == 'string' ? legacyValue : legacyValue[rootName];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* An alias for `isElement` from `es-toolkit/compat` with additional type guard.
|
|
349
|
+
*/ function isElement$1(value) {
|
|
350
|
+
return isElement$2(value);
|
|
351
|
+
}
|
|
352
|
+
|
|
277
353
|
/**
|
|
278
354
|
* A watchdog for CKEditor 5 editors.
|
|
279
355
|
*
|
|
@@ -303,8 +379,16 @@ function isObject(structure) {
|
|
|
303
379
|
* The editor source element or data.
|
|
304
380
|
*/ _elementOrData;
|
|
305
381
|
/**
|
|
306
|
-
*
|
|
307
|
-
*/
|
|
382
|
+
* Stores the original DOM element for single-root editors.
|
|
383
|
+
*/ _editorAttachTo = null;
|
|
384
|
+
/**
|
|
385
|
+
* Specifies whether the editor is a single-root editor (e.g. ClassicEditor) or a multi-root editor (e.g. MultiRootEditor).
|
|
386
|
+
*/ _isSingleRootEditor = true;
|
|
387
|
+
/**
|
|
388
|
+
* Specifies whether the editor was created using config-based creator mode (without a source element or data as the first argument).
|
|
389
|
+
*
|
|
390
|
+
* @internal
|
|
391
|
+
*/ _isUsingConfigBasedCreator = false;
|
|
308
392
|
/**
|
|
309
393
|
* The latest record of the editor editable elements. Used to restart the editor.
|
|
310
394
|
*/ _editables = {};
|
|
@@ -321,7 +405,14 @@ function isObject(structure) {
|
|
|
321
405
|
this._throttledSave = throttle(this._save.bind(this), typeof watchdogConfig.saveInterval === 'number' ? watchdogConfig.saveInterval : 5000);
|
|
322
406
|
// Set default creator and destructor functions:
|
|
323
407
|
if (Editor) {
|
|
324
|
-
this._creator = (
|
|
408
|
+
this._creator = (elementOrDataOrConfig, config)=>{
|
|
409
|
+
if (config === undefined) {
|
|
410
|
+
// Config-based mode: first argument is the config.
|
|
411
|
+
return Editor.create(elementOrDataOrConfig);
|
|
412
|
+
}
|
|
413
|
+
// Legacy mode: first argument is element/data, second is config.
|
|
414
|
+
return Editor.create(elementOrDataOrConfig, config);
|
|
415
|
+
};
|
|
325
416
|
}
|
|
326
417
|
this._destructor = (editor)=>editor.destroy();
|
|
327
418
|
}
|
|
@@ -339,6 +430,14 @@ function isObject(structure) {
|
|
|
339
430
|
* Sets the function that is responsible for the editor creation.
|
|
340
431
|
* It expects a function that should return a promise.
|
|
341
432
|
*
|
|
433
|
+
* For config-based editor creation:
|
|
434
|
+
*
|
|
435
|
+
* ```ts
|
|
436
|
+
* watchdog.setCreator( config => ClassicEditor.create( config ) );
|
|
437
|
+
* ```
|
|
438
|
+
*
|
|
439
|
+
* For legacy editor creation (with element or data as the first argument):
|
|
440
|
+
*
|
|
342
441
|
* ```ts
|
|
343
442
|
* watchdog.setCreator( ( element, config ) => ClassicEditor.create( element, config ) );
|
|
344
443
|
* ```
|
|
@@ -379,80 +478,98 @@ function isObject(structure) {
|
|
|
379
478
|
}).then(()=>{
|
|
380
479
|
// Pre-process some data from the original editor config.
|
|
381
480
|
// Our goal here is to make sure that the restarted editor will be reinitialized with correct set of roots.
|
|
382
|
-
// We are not interested in any data set in config
|
|
481
|
+
// We are not interested in any data set in config. It will be replaced anyway.
|
|
383
482
|
// But we need to set them correctly to make sure that proper roots are created.
|
|
384
483
|
//
|
|
385
|
-
// Since a different set of roots will be created,
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const rootsAttributes = {};
|
|
394
|
-
// Traverse through the roots saved when the editor crashed and set up the discussed values.
|
|
395
|
-
for (const [rootName, rootData] of Object.entries(this._data.roots)){
|
|
396
|
-
if (rootData.isLoaded) {
|
|
397
|
-
existingRoots[rootName] = '';
|
|
398
|
-
rootsAttributes[rootName] = oldRootsAttributes[rootName] || {};
|
|
399
|
-
} else {
|
|
400
|
-
lazyRoots.push(rootName);
|
|
401
|
-
}
|
|
484
|
+
// Since a different set of roots will be created, lazy-roots and roots-attributes must be managed too.
|
|
485
|
+
if (this._isUsingConfigBasedCreator) {
|
|
486
|
+
// In config-based creator mode, normalize using an empty source to ensure `config.root` is moved
|
|
487
|
+
// to `config.roots.main` and other legacy config properties are handled.
|
|
488
|
+
normalizeRootsConfig(this._isSingleRootEditor ? '' : {}, this._config, this._isSingleRootEditor ? 'main' : false);
|
|
489
|
+
} else {
|
|
490
|
+
// Normalize the roots configuration based on the editor source element or data and the editor configuration.
|
|
491
|
+
normalizeRootsConfig(this._isSingleRootEditor ? this._editorAttachTo || '' : this._editables, this._config, this._isSingleRootEditor ? 'main' : false);
|
|
402
492
|
}
|
|
403
493
|
const updatedConfig = {
|
|
404
494
|
...this._config,
|
|
405
495
|
extraPlugins: this._config.extraPlugins || [],
|
|
406
|
-
lazyRoots,
|
|
407
|
-
rootsAttributes,
|
|
408
496
|
_watchdogInitialData: this._data
|
|
409
497
|
};
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
delete updatedConfig.initialData;
|
|
498
|
+
// Add content loading plugin to the editor configuration.
|
|
499
|
+
// This plugin will be responsible for loading the editor data into the editor after it is restarted.
|
|
413
500
|
updatedConfig.extraPlugins.push(EditorWatchdogInitPlugin);
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
return this.create(this._elementOrData, updatedConfig, updatedConfig.context);
|
|
501
|
+
// Collect existing roots configuration and update it. This will ensure that the same set of roots
|
|
502
|
+
// will be created after the restart, and they will have correct lazy loading and attributes configuration.
|
|
503
|
+
const updatedRootsConfig = {};
|
|
504
|
+
for (const [rootName, rootData] of Object.entries(this._data.roots)){
|
|
505
|
+
const rootConfig = updatedConfig.roots[rootName] || Object.create(null);
|
|
506
|
+
// Delete `initialData` as it is not needed. Data will be set by the watchdog based on `_watchdogInitialData`.
|
|
507
|
+
rootConfig.initialData = '';
|
|
508
|
+
if (rootData.isLoaded) {
|
|
509
|
+
rootConfig.lazyLoad = false;
|
|
424
510
|
} else {
|
|
425
|
-
|
|
511
|
+
delete rootConfig.modelAttributes;
|
|
426
512
|
}
|
|
513
|
+
updatedRootsConfig[rootName] = rootConfig;
|
|
427
514
|
}
|
|
515
|
+
updatedConfig.roots = updatedRootsConfig;
|
|
516
|
+
// Delete `initialData` as it is not needed. Data will be set by the watchdog based on `_watchdogInitialData`.
|
|
517
|
+
delete updatedConfig.initialData;
|
|
518
|
+
// Also alias for main root should not provide initial data.
|
|
519
|
+
// In fact, it should not provide any data as it is only an alias for the root defined in `config.roots`
|
|
520
|
+
// and it is the `config.roots` that should be used to set up the initial data for the main root.
|
|
521
|
+
// This would cause a crash while normalizing conflict when left as is.
|
|
522
|
+
delete updatedConfig.root;
|
|
523
|
+
if (this._isUsingConfigBasedCreator) {
|
|
524
|
+
return this.create(updatedConfig, updatedConfig.context);
|
|
525
|
+
}
|
|
526
|
+
const elementOrData = this._isSingleRootEditor ? this._editorAttachTo || '' : this._editables;
|
|
527
|
+
return this.create(elementOrData, updatedConfig, updatedConfig.context);
|
|
428
528
|
}).then(()=>{
|
|
429
529
|
this._fire('restart');
|
|
430
530
|
});
|
|
431
531
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
532
|
+
create(elementOrDataOrConfig = this._isUsingConfigBasedCreator ? this._config : this._elementOrData, configOrContext = this._isUsingConfigBasedCreator ? undefined : this._config, context) {
|
|
533
|
+
// Detect config-based creator mode: first argument is a config object (not an element, string, or record of strings/elements).
|
|
534
|
+
// The detection is skipped during restart (when `_elementOrData` or `_config` is already set).
|
|
535
|
+
const isUsingConfigBasedCreator = this._detectConfigBasedCreator(elementOrDataOrConfig, configOrContext);
|
|
536
|
+
const elementOrData = isUsingConfigBasedCreator ? undefined : elementOrDataOrConfig;
|
|
537
|
+
const config = isUsingConfigBasedCreator ? elementOrDataOrConfig : configOrContext;
|
|
538
|
+
const resolvedContext = isUsingConfigBasedCreator ? configOrContext : context;
|
|
439
539
|
this._lifecyclePromise = Promise.resolve(this._lifecyclePromise).then(()=>{
|
|
440
540
|
super._startErrorHandling();
|
|
541
|
+
this._isUsingConfigBasedCreator = isUsingConfigBasedCreator;
|
|
441
542
|
this._elementOrData = elementOrData;
|
|
442
|
-
// Use document data in the first parameter of the editor `.create()` call only if it was used like this originally.
|
|
443
|
-
// Use document data if a string or object with strings was passed.
|
|
444
|
-
this._initUsingData = typeof elementOrData == 'string' || Object.keys(elementOrData).length > 0 && typeof Object.values(elementOrData)[0] == 'string';
|
|
445
543
|
// Clone configuration because it might be shared within multiple watchdog instances. Otherwise,
|
|
446
544
|
// when an error occurs in one of these editors, the watchdog will restart all of them.
|
|
447
|
-
this._config = this._cloneEditorConfiguration(config
|
|
448
|
-
this._config.context =
|
|
545
|
+
this._config = this._cloneEditorConfiguration(config || {});
|
|
546
|
+
this._config.context = resolvedContext;
|
|
547
|
+
// Store the original DOM element for single-root editors. We can't use editable elements as ClassicEditor
|
|
548
|
+
// expects the attachment element.
|
|
549
|
+
if (isUsingConfigBasedCreator) {
|
|
550
|
+
// In config-based creator mode, element references are already in the config
|
|
551
|
+
// (`config.attachTo` or `config.roots.*.element`), so there's no need to store them separately.
|
|
552
|
+
this._editorAttachTo = null;
|
|
553
|
+
// Detect single-root vs multi-root from config. The config might use `config.root` (alias),
|
|
554
|
+
// `config.roots`, `config.attachTo`, or legacy `config.initialData`.
|
|
555
|
+
const rootsCount = this._config.roots ? Object.keys(this._config.roots).length : 0;
|
|
556
|
+
const legacyInitialData = this._config.initialData;
|
|
557
|
+
const isMultiRootFromLegacy = legacyInitialData && typeof legacyInitialData === 'object';
|
|
558
|
+
this._isSingleRootEditor = !isMultiRootFromLegacy && rootsCount <= 1;
|
|
559
|
+
} else {
|
|
560
|
+
this._editorAttachTo = isElement(elementOrData) ? elementOrData : null;
|
|
561
|
+
this._isSingleRootEditor = isElement(elementOrData) || typeof elementOrData == 'string';
|
|
562
|
+
}
|
|
563
|
+
if (isUsingConfigBasedCreator) {
|
|
564
|
+
return this._creator(this._config);
|
|
565
|
+
}
|
|
449
566
|
return this._creator(elementOrData, this._config);
|
|
450
567
|
}).then((editor)=>{
|
|
451
568
|
this._editor = editor;
|
|
452
569
|
editor.model.document.on('change:data', this._throttledSave);
|
|
453
570
|
this._lastDocumentVersion = editor.model.document.version;
|
|
454
571
|
this._data = this._getData();
|
|
455
|
-
if (!this.
|
|
572
|
+
if (!this._editorAttachTo) {
|
|
456
573
|
this._editables = this._getEditables();
|
|
457
574
|
}
|
|
458
575
|
this.state = 'ready';
|
|
@@ -497,7 +614,7 @@ function isObject(structure) {
|
|
|
497
614
|
const version = this._editor.model.document.version;
|
|
498
615
|
try {
|
|
499
616
|
this._data = this._getData();
|
|
500
|
-
if (!this.
|
|
617
|
+
if (!this._editorAttachTo) {
|
|
501
618
|
this._editables = this._getEditables();
|
|
502
619
|
}
|
|
503
620
|
this._lastDocumentVersion = version;
|
|
@@ -576,6 +693,28 @@ function isObject(structure) {
|
|
|
576
693
|
*/ _isErrorComingFromThisItem(error) {
|
|
577
694
|
return areConnectedThroughProperties(this._editor, error.context, this._excludedProps);
|
|
578
695
|
}
|
|
696
|
+
/**
|
|
697
|
+
* Detects whether the `create()` call was made in config-based creator mode
|
|
698
|
+
* (i.e., the first argument is a config object rather than a source element or data).
|
|
699
|
+
*/ _detectConfigBasedCreator(elementOrDataOrConfig, configOrContext) {
|
|
700
|
+
// A string or DOM element is clearly the legacy signature.
|
|
701
|
+
if (typeof elementOrDataOrConfig === 'string' || isElement(elementOrDataOrConfig)) {
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
// If the second argument is a plain object with keys, it's a config → legacy signature.
|
|
705
|
+
if (configOrContext && typeof configOrContext === 'object' && !('destroy' in configOrContext) && Object.keys(configOrContext).length > 0) {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
// If the first argument is an object where all values are strings or elements, it's multi-root legacy.
|
|
709
|
+
if (elementOrDataOrConfig && typeof elementOrDataOrConfig === 'object') {
|
|
710
|
+
const values = Object.values(elementOrDataOrConfig);
|
|
711
|
+
if (values.length > 0 && values.every((v)=>typeof v === 'string' || isElement(v))) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
// Otherwise, it's config-based.
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
579
718
|
/**
|
|
580
719
|
* Clones the editor configuration.
|
|
581
720
|
*/ _cloneEditorConfiguration(config) {
|
|
@@ -705,6 +844,11 @@ function isObject(structure) {
|
|
|
705
844
|
}
|
|
706
845
|
}
|
|
707
846
|
}
|
|
847
|
+
/**
|
|
848
|
+
* An alias for `isElement` from `es-toolkit/compat` with additional type guard.
|
|
849
|
+
*/ function isElement(value) {
|
|
850
|
+
return isElement$2(value);
|
|
851
|
+
}
|
|
708
852
|
|
|
709
853
|
const mainQueueId = Symbol('MainQueueId');
|
|
710
854
|
/**
|
|
@@ -850,12 +994,12 @@ const mainQueueId = Symbol('MainQueueId');
|
|
|
850
994
|
* await watchdog.add( [ {
|
|
851
995
|
* id: 'editor1',
|
|
852
996
|
* type: 'editor',
|
|
853
|
-
* sourceElementOrData: document.querySelector( '#editor' ),
|
|
854
997
|
* config: {
|
|
998
|
+
* attachTo: document.querySelector( '#editor' ),
|
|
855
999
|
* plugins: [ Essentials, Paragraph, Bold, Italic ],
|
|
856
1000
|
* toolbar: [ 'bold', 'italic', 'alignment' ]
|
|
857
1001
|
* },
|
|
858
|
-
* creator:
|
|
1002
|
+
* creator: config => ClassicEditor.create( config )
|
|
859
1003
|
* } ] );
|
|
860
1004
|
* ```
|
|
861
1005
|
*
|
|
@@ -865,13 +1009,13 @@ const mainQueueId = Symbol('MainQueueId');
|
|
|
865
1009
|
* await watchdog.add( {
|
|
866
1010
|
* id: 'editor1',
|
|
867
1011
|
* type: 'editor',
|
|
868
|
-
* sourceElementOrData: document.querySelector( '#editor' ),
|
|
869
1012
|
* config: {
|
|
1013
|
+
* attachTo: document.querySelector( '#editor' ),
|
|
870
1014
|
* plugins: [ Essentials, Paragraph, Bold, Italic ],
|
|
871
1015
|
* toolbar: [ 'bold', 'italic', 'alignment' ]
|
|
872
1016
|
* },
|
|
873
|
-
* creator:
|
|
874
|
-
*
|
|
1017
|
+
* creator: config => ClassicEditor.create( config )
|
|
1018
|
+
* } );
|
|
875
1019
|
* ```
|
|
876
1020
|
*
|
|
877
1021
|
* Then an instance can be retrieved using the {@link #getItem} method:
|
|
@@ -928,7 +1072,10 @@ const mainQueueId = Symbol('MainQueueId');
|
|
|
928
1072
|
watchdog.on('restart', rethrowRestartEventOnce);
|
|
929
1073
|
}));
|
|
930
1074
|
});
|
|
931
|
-
|
|
1075
|
+
if (item.sourceElementOrData !== undefined) {
|
|
1076
|
+
return watchdog.create(item.sourceElementOrData, item.config, this._context);
|
|
1077
|
+
}
|
|
1078
|
+
return watchdog.create(item.config, this._context);
|
|
932
1079
|
} else {
|
|
933
1080
|
throw new Error(`Not supported item type: '${item.type}'.`);
|
|
934
1081
|
}
|
|
@@ -996,6 +1143,9 @@ const mainQueueId = Symbol('MainQueueId');
|
|
|
996
1143
|
this._contextProps = getSubNodes(this._context);
|
|
997
1144
|
return Promise.all(Array.from(this._watchdogs.values()).map((watchdog)=>{
|
|
998
1145
|
watchdog._setExcludedProperties(this._contextProps);
|
|
1146
|
+
if (watchdog._isUsingConfigBasedCreator) {
|
|
1147
|
+
return watchdog.create(undefined, this._context);
|
|
1148
|
+
}
|
|
999
1149
|
return watchdog.create(undefined, undefined, this._context);
|
|
1000
1150
|
}));
|
|
1001
1151
|
});
|
|
@@ -1552,7 +1702,7 @@ const mainQueueId = Symbol('MainQueueId');
|
|
|
1552
1702
|
if (typeof value.toJSON == 'function') {
|
|
1553
1703
|
const jsonData = value.toJSON();
|
|
1554
1704
|
// Make sure that toJSON returns plain object, otherwise it could be just a clone with circular references.
|
|
1555
|
-
if (isPlainObject(jsonData) || Array.isArray(jsonData) || [
|
|
1705
|
+
if (isPlainObject$1(jsonData) || Array.isArray(jsonData) || [
|
|
1556
1706
|
'string',
|
|
1557
1707
|
'number',
|
|
1558
1708
|
'boolean'
|