@ckeditor/ckeditor5-engine 34.0.0 → 35.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +823 -0
- package/LICENSE.md +6 -2
- package/package.json +24 -23
- package/src/conversion/upcastdispatcher.js +4 -4
- package/src/conversion/upcasthelpers.js +23 -1
- package/src/dataprocessor/htmldataprocessor.js +2 -2
- package/src/dataprocessor/xmldataprocessor.js +2 -2
- package/src/index.js +5 -0
- package/src/model/document.js +20 -28
- package/src/model/history.js +160 -22
- package/src/model/model.js +2 -2
- package/src/model/schema.js +2 -2
- package/src/model/utils/findoptimalinsertionrange.js +5 -5
- package/src/model/utils/insertobject.js +5 -5
- package/src/view/domconverter.js +43 -30
- package/src/view/element.js +3 -0
- package/src/view/filler.js +1 -1
- package/src/view/observer/tabobserver.js +2 -2
- package/src/view/renderer.js +13 -5
package/LICENSE.md
CHANGED
|
@@ -2,7 +2,7 @@ Software License Agreement
|
|
|
2
2
|
==========================
|
|
3
3
|
|
|
4
4
|
**CKEditor 5 editing engine** – https://github.com/ckeditor/ckeditor5-engine <br>
|
|
5
|
-
Copyright (c) 2003-2022, [CKSource
|
|
5
|
+
Copyright (c) 2003-2022, [CKSource Holding sp. z o.o.](https://cksource.com) All rights reserved.
|
|
6
6
|
|
|
7
7
|
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
|
|
8
8
|
|
|
@@ -11,7 +11,11 @@ Sources of Intellectual Property Included in CKEditor
|
|
|
11
11
|
|
|
12
12
|
Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
|
|
13
13
|
|
|
14
|
+
The following libraries are included in CKEditor under the [MIT license](https://opensource.org/licenses/MIT):
|
|
15
|
+
|
|
16
|
+
* lodash - Copyright (c) JS Foundation and other contributors https://js.foundation/. Based on Underscore.js, copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors http://underscorejs.org/.
|
|
17
|
+
|
|
14
18
|
Trademarks
|
|
15
19
|
----------
|
|
16
20
|
|
|
17
|
-
**CKEditor** is a trademark of [CKSource
|
|
21
|
+
**CKEditor** is a trademark of [CKSource Holding sp. z o.o.](https://cksource.com) All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-engine",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "35.0.0",
|
|
4
4
|
"description": "The editing engine of CKEditor 5 – the best browser-based rich text editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wysiwyg",
|
|
@@ -23,30 +23,30 @@
|
|
|
23
23
|
],
|
|
24
24
|
"main": "src/index.js",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@ckeditor/ckeditor5-utils": "^
|
|
26
|
+
"@ckeditor/ckeditor5-utils": "^35.0.0",
|
|
27
27
|
"lodash-es": "^4.17.15"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@ckeditor/ckeditor5-basic-styles": "^
|
|
31
|
-
"@ckeditor/ckeditor5-block-quote": "^
|
|
32
|
-
"@ckeditor/ckeditor5-clipboard": "^
|
|
33
|
-
"@ckeditor/ckeditor5-cloud-services": "^
|
|
34
|
-
"@ckeditor/ckeditor5-core": "^
|
|
35
|
-
"@ckeditor/ckeditor5-editor-classic": "^
|
|
36
|
-
"@ckeditor/ckeditor5-enter": "^
|
|
37
|
-
"@ckeditor/ckeditor5-essentials": "^
|
|
38
|
-
"@ckeditor/ckeditor5-heading": "^
|
|
39
|
-
"@ckeditor/ckeditor5-image": "^
|
|
40
|
-
"@ckeditor/ckeditor5-link": "^
|
|
41
|
-
"@ckeditor/ckeditor5-list": "^
|
|
42
|
-
"@ckeditor/ckeditor5-mention": "^
|
|
43
|
-
"@ckeditor/ckeditor5-paragraph": "^
|
|
44
|
-
"@ckeditor/ckeditor5-table": "^
|
|
45
|
-
"@ckeditor/ckeditor5-theme-lark": "^
|
|
46
|
-
"@ckeditor/ckeditor5-typing": "^
|
|
47
|
-
"@ckeditor/ckeditor5-ui": "^
|
|
48
|
-
"@ckeditor/ckeditor5-undo": "^
|
|
49
|
-
"@ckeditor/ckeditor5-widget": "^
|
|
30
|
+
"@ckeditor/ckeditor5-basic-styles": "^35.0.0",
|
|
31
|
+
"@ckeditor/ckeditor5-block-quote": "^35.0.0",
|
|
32
|
+
"@ckeditor/ckeditor5-clipboard": "^35.0.0",
|
|
33
|
+
"@ckeditor/ckeditor5-cloud-services": "^35.0.0",
|
|
34
|
+
"@ckeditor/ckeditor5-core": "^35.0.0",
|
|
35
|
+
"@ckeditor/ckeditor5-editor-classic": "^35.0.0",
|
|
36
|
+
"@ckeditor/ckeditor5-enter": "^35.0.0",
|
|
37
|
+
"@ckeditor/ckeditor5-essentials": "^35.0.0",
|
|
38
|
+
"@ckeditor/ckeditor5-heading": "^35.0.0",
|
|
39
|
+
"@ckeditor/ckeditor5-image": "^35.0.0",
|
|
40
|
+
"@ckeditor/ckeditor5-link": "^35.0.0",
|
|
41
|
+
"@ckeditor/ckeditor5-list": "^35.0.0",
|
|
42
|
+
"@ckeditor/ckeditor5-mention": "^35.0.0",
|
|
43
|
+
"@ckeditor/ckeditor5-paragraph": "^35.0.0",
|
|
44
|
+
"@ckeditor/ckeditor5-table": "^35.0.0",
|
|
45
|
+
"@ckeditor/ckeditor5-theme-lark": "^35.0.0",
|
|
46
|
+
"@ckeditor/ckeditor5-typing": "^35.0.0",
|
|
47
|
+
"@ckeditor/ckeditor5-ui": "^35.0.0",
|
|
48
|
+
"@ckeditor/ckeditor5-undo": "^35.0.0",
|
|
49
|
+
"@ckeditor/ckeditor5-widget": "^35.0.0",
|
|
50
50
|
"webpack": "^5.58.1",
|
|
51
51
|
"webpack-cli": "^4.9.0"
|
|
52
52
|
},
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"lang",
|
|
68
68
|
"src",
|
|
69
69
|
"theme",
|
|
70
|
-
"ckeditor5-metadata.json"
|
|
70
|
+
"ckeditor5-metadata.json",
|
|
71
|
+
"CHANGELOG.md"
|
|
71
72
|
]
|
|
72
73
|
}
|
|
@@ -152,9 +152,9 @@ export default class UpcastDispatcher {
|
|
|
152
152
|
this._modelCursor = null;
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
|
-
* The list of elements that were created during splitting but should not get removed on conversion end even if they are empty.
|
|
155
|
+
* The list of elements that were created during the splitting but should not get removed on conversion end even if they are empty.
|
|
156
156
|
*
|
|
157
|
-
*
|
|
157
|
+
* The list is cleared after the conversion process.
|
|
158
158
|
*
|
|
159
159
|
* @private
|
|
160
160
|
* @type {Set.<module:engine/model/element~Element>}
|
|
@@ -464,7 +464,7 @@ export default class UpcastDispatcher {
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
/**
|
|
467
|
-
* Mark an element that were created during splitting
|
|
467
|
+
* Mark an element that were created during the splitting to not get removed on conversion end even if it is empty.
|
|
468
468
|
*
|
|
469
469
|
* @private
|
|
470
470
|
*/
|
|
@@ -779,7 +779,7 @@ function createContextTree( contextDefinition, writer ) {
|
|
|
779
779
|
*/
|
|
780
780
|
|
|
781
781
|
/**
|
|
782
|
-
* Mark an element that was created during splitting
|
|
782
|
+
* Mark an element that was created during splitting to not get removed on conversion end even if it is empty.
|
|
783
783
|
*
|
|
784
784
|
* **Note:** This is an advanced method. For most cases you will not need to keep the split empty element.
|
|
785
785
|
*
|
|
@@ -465,6 +465,11 @@ export function convertText() {
|
|
|
465
465
|
return;
|
|
466
466
|
}
|
|
467
467
|
|
|
468
|
+
// Do not auto-paragraph whitespaces.
|
|
469
|
+
if ( data.viewItem.data.trim().length == 0 ) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
468
473
|
position = wrapInParagraph( position, writer );
|
|
469
474
|
}
|
|
470
475
|
|
|
@@ -867,6 +872,13 @@ function prepareToAttributeConverter( config, shallow ) {
|
|
|
867
872
|
const matcher = new Matcher( config.view );
|
|
868
873
|
|
|
869
874
|
return ( evt, data, conversionApi ) => {
|
|
875
|
+
// Converting an attribute of an element that has not been converted to anything does not make sense
|
|
876
|
+
// because there will be nowhere to set that attribute on. At this stage, the element should've already
|
|
877
|
+
// been converted (https://github.com/ckeditor/ckeditor5/issues/11000).
|
|
878
|
+
if ( !data.modelRange && shallow ) {
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
|
|
870
882
|
const match = matcher.match( data.viewItem );
|
|
871
883
|
|
|
872
884
|
// If there is no match, this callback should not do anything.
|
|
@@ -877,7 +889,8 @@ function prepareToAttributeConverter( config, shallow ) {
|
|
|
877
889
|
if ( onlyViewNameIsDefined( config.view, data.viewItem ) ) {
|
|
878
890
|
match.match.name = true;
|
|
879
891
|
} else {
|
|
880
|
-
// Do not test
|
|
892
|
+
// Do not test `name` consumable because it could get consumed already while upcasting some other attribute
|
|
893
|
+
// on the same element (for example <span class="big" style="color: red">foo</span>).
|
|
881
894
|
delete match.match.name;
|
|
882
895
|
}
|
|
883
896
|
|
|
@@ -908,6 +921,15 @@ function prepareToAttributeConverter( config, shallow ) {
|
|
|
908
921
|
// It may happen that a converter will try to set an attribute that is not allowed in the given context.
|
|
909
922
|
// In such a situation we cannot consume the attribute. See: https://github.com/ckeditor/ckeditor5/pull/9249#issuecomment-815658459.
|
|
910
923
|
if ( attributeWasSet ) {
|
|
924
|
+
// Verify if the element itself wasn't consumed yet. It could be consumed already while upcasting some other attribute
|
|
925
|
+
// on the same element (for example <span class="big" style="color: red">foo</span>).
|
|
926
|
+
// We need to consume it so other features (especially GHS) won't try to convert it.
|
|
927
|
+
// Note that it's not tested by the other element-to-attribute converters whether an element was consumed before
|
|
928
|
+
// (in case of converters that the element itself is just a context and not the primary information to convert).
|
|
929
|
+
if ( conversionApi.consumable.test( data.viewItem, { name: true } ) ) {
|
|
930
|
+
match.match.name = true;
|
|
931
|
+
}
|
|
932
|
+
|
|
911
933
|
conversionApi.consumable.consume( data.viewItem, match.match );
|
|
912
934
|
}
|
|
913
935
|
};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module engine/dataprocessor/htmldataprocessor
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
/* globals
|
|
10
|
+
/* globals DOMParser */
|
|
11
11
|
|
|
12
12
|
import BasicHtmlWriter from './basichtmlwriter';
|
|
13
13
|
import DomConverter from '../view/domconverter';
|
|
@@ -56,7 +56,7 @@ export default class HtmlDataProcessor {
|
|
|
56
56
|
*/
|
|
57
57
|
toData( viewFragment ) {
|
|
58
58
|
// Convert view DocumentFragment to DOM DocumentFragment.
|
|
59
|
-
const domFragment = this.domConverter.viewToDom( viewFragment
|
|
59
|
+
const domFragment = this.domConverter.viewToDom( viewFragment );
|
|
60
60
|
|
|
61
61
|
// Convert DOM DocumentFragment to HTML output.
|
|
62
62
|
return this.htmlWriter.getHtml( domFragment );
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module engine/dataprocessor/xmldataprocessor
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
/* globals DOMParser
|
|
10
|
+
/* globals DOMParser */
|
|
11
11
|
|
|
12
12
|
import BasicHtmlWriter from './basichtmlwriter';
|
|
13
13
|
import DomConverter from '../view/domconverter';
|
|
@@ -71,7 +71,7 @@ export default class XmlDataProcessor {
|
|
|
71
71
|
*/
|
|
72
72
|
toData( viewFragment ) {
|
|
73
73
|
// Convert view DocumentFragment to DOM DocumentFragment.
|
|
74
|
-
const domFragment = this.domConverter.viewToDom( viewFragment
|
|
74
|
+
const domFragment = this.domConverter.viewToDom( viewFragment );
|
|
75
75
|
|
|
76
76
|
// Convert DOM DocumentFragment to XML output.
|
|
77
77
|
// There is no need to use dedicated for XML serializing method because BasicHtmlWriter works well in this case.
|
package/src/index.js
CHANGED
|
@@ -38,6 +38,11 @@ export { default as Renderer } from './view/renderer';
|
|
|
38
38
|
export { default as ViewDocument } from './view/document';
|
|
39
39
|
export { default as ViewText } from './view/text';
|
|
40
40
|
export { default as ViewElement } from './view/element';
|
|
41
|
+
export { default as ViewContainerElement } from './view/containerelement';
|
|
42
|
+
export { default as ViewAttributeElement } from './view/attributeelement';
|
|
43
|
+
export { default as ViewEmptyElement } from './view/emptyelement';
|
|
44
|
+
export { default as ViewRawElement } from './view/rawelement';
|
|
45
|
+
export { default as ViewUIElement } from './view/uielement';
|
|
41
46
|
export { default as ViewDocumentFragment } from './view/documentfragment';
|
|
42
47
|
|
|
43
48
|
export { getFillerOffset } from './view/containerelement';
|
package/src/model/document.js
CHANGED
|
@@ -52,24 +52,13 @@ export default class Document {
|
|
|
52
52
|
*/
|
|
53
53
|
this.model = model;
|
|
54
54
|
|
|
55
|
-
/**
|
|
56
|
-
* The document version. It starts from `0` and every operation increases the version number. It is used to ensure that
|
|
57
|
-
* operations are applied on a proper document version.
|
|
58
|
-
*
|
|
59
|
-
* If the {@link module:engine/model/operation/operation~Operation#baseVersion base version} does not match the document version,
|
|
60
|
-
* a {@link module:utils/ckeditorerror~CKEditorError model-document-applyoperation-wrong-version} error is thrown.
|
|
61
|
-
*
|
|
62
|
-
* @type {Number}
|
|
63
|
-
*/
|
|
64
|
-
this.version = 0;
|
|
65
|
-
|
|
66
55
|
/**
|
|
67
56
|
* The document's history.
|
|
68
57
|
*
|
|
69
58
|
* @readonly
|
|
70
59
|
* @type {module:engine/model/history~History}
|
|
71
60
|
*/
|
|
72
|
-
this.history = new History(
|
|
61
|
+
this.history = new History();
|
|
73
62
|
|
|
74
63
|
/**
|
|
75
64
|
* The selection in this document.
|
|
@@ -115,21 +104,6 @@ export default class Document {
|
|
|
115
104
|
// Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
|
|
116
105
|
this.createRoot( '$root', graveyardName );
|
|
117
106
|
|
|
118
|
-
// First, if the operation is a document operation check if it's base version is correct.
|
|
119
|
-
this.listenTo( model, 'applyOperation', ( evt, args ) => {
|
|
120
|
-
const operation = args[ 0 ];
|
|
121
|
-
|
|
122
|
-
if ( operation.isDocumentOperation && operation.baseVersion !== this.version ) {
|
|
123
|
-
/**
|
|
124
|
-
* Only operations with matching versions can be applied.
|
|
125
|
-
*
|
|
126
|
-
* @error model-document-applyoperation-wrong-version
|
|
127
|
-
* @param {module:engine/model/operation/operation~Operation} operation
|
|
128
|
-
*/
|
|
129
|
-
throw new CKEditorError( 'model-document-applyoperation-wrong-version', this, { operation } );
|
|
130
|
-
}
|
|
131
|
-
}, { priority: 'highest' } );
|
|
132
|
-
|
|
133
107
|
// Then, still before an operation is applied on model, buffer the change in differ.
|
|
134
108
|
this.listenTo( model, 'applyOperation', ( evt, args ) => {
|
|
135
109
|
const operation = args[ 0 ];
|
|
@@ -144,7 +118,6 @@ export default class Document {
|
|
|
144
118
|
const operation = args[ 0 ];
|
|
145
119
|
|
|
146
120
|
if ( operation.isDocumentOperation ) {
|
|
147
|
-
this.version++;
|
|
148
121
|
this.history.addOperation( operation );
|
|
149
122
|
}
|
|
150
123
|
}, { priority: 'low' } );
|
|
@@ -179,6 +152,25 @@ export default class Document {
|
|
|
179
152
|
} );
|
|
180
153
|
}
|
|
181
154
|
|
|
155
|
+
/**
|
|
156
|
+
* The document version. Every applied operation increases the version number. It is used to
|
|
157
|
+
* ensure that operations are applied on a proper document version.
|
|
158
|
+
*
|
|
159
|
+
* This property is equal to {@link module:engine/model/history~History#version `model.Document#history#version`}.
|
|
160
|
+
*
|
|
161
|
+
* If the {@link module:engine/model/operation/operation~Operation#baseVersion base version} does not match the document version,
|
|
162
|
+
* a {@link module:utils/ckeditorerror~CKEditorError model-document-applyoperation-wrong-version} error is thrown.
|
|
163
|
+
*
|
|
164
|
+
* @type {Number}
|
|
165
|
+
*/
|
|
166
|
+
get version() {
|
|
167
|
+
return this.history.version;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
set version( version ) {
|
|
171
|
+
this.history.version = version;
|
|
172
|
+
}
|
|
173
|
+
|
|
182
174
|
/**
|
|
183
175
|
* The graveyard tree root. A document always has a graveyard root that stores removed nodes.
|
|
184
176
|
*
|
package/src/model/history.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { CKEditorError } from '@ckeditor/ckeditor5-utils';
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* @module engine/model/history
|
|
8
10
|
*/
|
|
@@ -18,8 +20,9 @@ export default class History {
|
|
|
18
20
|
/**
|
|
19
21
|
* Operations added to the history.
|
|
20
22
|
*
|
|
21
|
-
* @
|
|
22
|
-
* @
|
|
23
|
+
* @private
|
|
24
|
+
* @readonly
|
|
25
|
+
* @type {Array.<module:engine/model/operation/operation~Operation>}
|
|
23
26
|
*/
|
|
24
27
|
this._operations = [];
|
|
25
28
|
|
|
@@ -39,43 +42,164 @@ export default class History {
|
|
|
39
42
|
* Holds all undone operations.
|
|
40
43
|
*
|
|
41
44
|
* @private
|
|
42
|
-
* @
|
|
45
|
+
* @type {Set.<module:engine/model/operation/operation~Operation>}
|
|
43
46
|
*/
|
|
44
47
|
this._undoneOperations = new Set();
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A map that allows retrieving the operations fast based on the given base version.
|
|
51
|
+
*
|
|
52
|
+
* @private
|
|
53
|
+
* @type Map.<Number,Number>
|
|
54
|
+
*/
|
|
55
|
+
this._baseVersionToOperationIndex = new Map();
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The history version.
|
|
59
|
+
*
|
|
60
|
+
* @private
|
|
61
|
+
* @type {Number}
|
|
62
|
+
*/
|
|
63
|
+
this._version = 0;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The gap pairs kept in the <from,to> format.
|
|
67
|
+
*
|
|
68
|
+
* Anytime the `history.version` is set to a version larger than `history.version + 1`,
|
|
69
|
+
* a new <lastHistoryVersion, newHistoryVersion> entry is added to the map.
|
|
70
|
+
*
|
|
71
|
+
* @private
|
|
72
|
+
* @type Map.<number,number>
|
|
73
|
+
*/
|
|
74
|
+
this._gaps = new Map();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The version of the last operation in the history.
|
|
79
|
+
*
|
|
80
|
+
* The history version is incremented automatically when a new operation is added to the history.
|
|
81
|
+
* Setting the version manually should be done only in rare circumstances when a gap is planned
|
|
82
|
+
* between history versions. When doing so, a gap will be created and the history will accept adding
|
|
83
|
+
* an operation with base version equal to the new history version.
|
|
84
|
+
*
|
|
85
|
+
* @type {Number}
|
|
86
|
+
*/
|
|
87
|
+
get version() {
|
|
88
|
+
return this._version;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
set version( version ) {
|
|
92
|
+
// Store a gap if there are some operations already in the history and the
|
|
93
|
+
// new version does not increment the latest one.
|
|
94
|
+
if ( this._operations.length && version > this._version + 1 ) {
|
|
95
|
+
this._gaps.set( this._version, version );
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this._version = version;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The last history operation.
|
|
103
|
+
*
|
|
104
|
+
* @readonly
|
|
105
|
+
* @type {module:engine/model/operation/operation~Operation|undefined}
|
|
106
|
+
*/
|
|
107
|
+
get lastOperation() {
|
|
108
|
+
return this._operations[ this._operations.length - 1 ];
|
|
45
109
|
}
|
|
46
110
|
|
|
47
111
|
/**
|
|
48
|
-
* Adds an operation to the history.
|
|
112
|
+
* Adds an operation to the history and increments the history version.
|
|
113
|
+
*
|
|
114
|
+
* The operation's base version should be equal to the history version. Otherwise an error is thrown.
|
|
49
115
|
*
|
|
50
116
|
* @param {module:engine/model/operation/operation~Operation} operation Operation to add.
|
|
51
117
|
*/
|
|
52
118
|
addOperation( operation ) {
|
|
53
|
-
if (
|
|
54
|
-
|
|
119
|
+
if ( operation.baseVersion !== this.version ) {
|
|
120
|
+
/**
|
|
121
|
+
* Only operations with matching versions can be added to the history.
|
|
122
|
+
*
|
|
123
|
+
* @error model-document-history-addoperation-incorrect-version
|
|
124
|
+
* @param {Object} errorData The operation and the current document history version.
|
|
125
|
+
*/
|
|
126
|
+
throw new CKEditorError( 'model-document-history-addoperation-incorrect-version', this, {
|
|
127
|
+
operation,
|
|
128
|
+
historyVersion: this.version
|
|
129
|
+
} );
|
|
55
130
|
}
|
|
56
131
|
|
|
57
132
|
this._operations.push( operation );
|
|
133
|
+
this._version++;
|
|
134
|
+
|
|
135
|
+
this._baseVersionToOperationIndex.set( operation.baseVersion, this._operations.length - 1 );
|
|
58
136
|
}
|
|
59
137
|
|
|
60
138
|
/**
|
|
61
|
-
* Returns operations added to the history.
|
|
139
|
+
* Returns operations from the given range of operation base versions that were added to the history.
|
|
62
140
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* @param {Number} [
|
|
66
|
-
*
|
|
67
|
-
|
|
141
|
+
* Note that there may be gaps in operations base versions.
|
|
142
|
+
*
|
|
143
|
+
* @param {Number} [fromBaseVersion] Base version from which operations should be returned (inclusive).
|
|
144
|
+
* @param {Number} [toBaseVersion] Base version up to which operations should be returned (exclusive).
|
|
145
|
+
* @returns {Array.<module:engine/model/operation/operation~Operation>} History operations for the given range, in chronological order.
|
|
68
146
|
*/
|
|
69
|
-
getOperations(
|
|
70
|
-
|
|
147
|
+
getOperations( fromBaseVersion, toBaseVersion = this.version ) {
|
|
148
|
+
// When there is no operation in the history, return an empty array.
|
|
149
|
+
// After that we can be sure that `firstOperation`, `lastOperation` are not nullish.
|
|
150
|
+
if ( !this._operations.length ) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const firstOperation = this._operations[ 0 ];
|
|
155
|
+
|
|
156
|
+
if ( fromBaseVersion === undefined ) {
|
|
157
|
+
fromBaseVersion = firstOperation.baseVersion;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Change exclusive `toBaseVersion` to inclusive, so it will refer to the actual index.
|
|
161
|
+
// Thanks to that mapping from base versions to operation indexes are possible.
|
|
162
|
+
let inclusiveTo = toBaseVersion - 1;
|
|
163
|
+
|
|
164
|
+
// Check if "from" or "to" point to a gap between versions.
|
|
165
|
+
// If yes, then change the incorrect position to the proper side of the gap.
|
|
166
|
+
// Thanks to it, it will be possible to get index of the operation.
|
|
167
|
+
for ( const [ gapFrom, gapTo ] of this._gaps ) {
|
|
168
|
+
if ( fromBaseVersion > gapFrom && fromBaseVersion < gapTo ) {
|
|
169
|
+
fromBaseVersion = gapTo;
|
|
170
|
+
}
|
|
71
171
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
operations.push( operation );
|
|
172
|
+
if ( inclusiveTo > gapFrom && inclusiveTo < gapTo ) {
|
|
173
|
+
inclusiveTo = gapFrom - 1;
|
|
75
174
|
}
|
|
76
175
|
}
|
|
77
176
|
|
|
78
|
-
return
|
|
177
|
+
// If the whole range is outside of the operation versions, then return an empty array.
|
|
178
|
+
if ( inclusiveTo < firstOperation.baseVersion || fromBaseVersion > this.lastOperation.baseVersion ) {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let fromIndex = this._baseVersionToOperationIndex.get( fromBaseVersion );
|
|
183
|
+
|
|
184
|
+
// If the range starts before the first operation, then use the first operation as the range's start.
|
|
185
|
+
if ( fromIndex === undefined ) {
|
|
186
|
+
fromIndex = 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let toIndex = this._baseVersionToOperationIndex.get( inclusiveTo );
|
|
190
|
+
|
|
191
|
+
// If the range ends after the last operation, then use the last operation as the range's end.
|
|
192
|
+
if ( toIndex === undefined ) {
|
|
193
|
+
toIndex = this._operations.length - 1;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Return the part of the history operations based on the calculated start index and end index.
|
|
197
|
+
return this._operations.slice(
|
|
198
|
+
fromIndex,
|
|
199
|
+
|
|
200
|
+
// The `toIndex` should be included in the returned operations, so add `1`.
|
|
201
|
+
toIndex + 1
|
|
202
|
+
);
|
|
79
203
|
}
|
|
80
204
|
|
|
81
205
|
/**
|
|
@@ -86,11 +210,13 @@ export default class History {
|
|
|
86
210
|
* there is no such operation in history.
|
|
87
211
|
*/
|
|
88
212
|
getOperation( baseVersion ) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
213
|
+
const operationIndex = this._baseVersionToOperationIndex.get( baseVersion );
|
|
214
|
+
|
|
215
|
+
if ( operationIndex === undefined ) {
|
|
216
|
+
return;
|
|
93
217
|
}
|
|
218
|
+
|
|
219
|
+
return this._operations[ operationIndex ];
|
|
94
220
|
}
|
|
95
221
|
|
|
96
222
|
/**
|
|
@@ -135,4 +261,16 @@ export default class History {
|
|
|
135
261
|
getUndoneOperation( undoingOperation ) {
|
|
136
262
|
return this._undoPairs.get( undoingOperation );
|
|
137
263
|
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Resets the history of operations.
|
|
267
|
+
*/
|
|
268
|
+
reset() {
|
|
269
|
+
this._version = 0;
|
|
270
|
+
this._undoPairs = new Map();
|
|
271
|
+
this._operations = [];
|
|
272
|
+
this._undoneOperations = new Set();
|
|
273
|
+
this._gaps = new Map();
|
|
274
|
+
this._baseVersionToOperationIndex = new Map();
|
|
275
|
+
}
|
|
138
276
|
}
|
package/src/model/model.js
CHANGED
|
@@ -525,7 +525,7 @@ export default class Model {
|
|
|
525
525
|
* * When `'before'`, the closest position before `selectable` will be used that will not result in content splitting.
|
|
526
526
|
* * When `'after'`, the closest position after `selectable` will be used that will not result in content splitting.
|
|
527
527
|
*
|
|
528
|
-
* Note that this option works
|
|
528
|
+
* Note that this option only works for block objects. Inline objects are inserted into text and do not split blocks.
|
|
529
529
|
* @param {'on'|'after'} [options.setSelection] An option that, when set, moves the
|
|
530
530
|
* {@link module:engine/model/document~Document#selection document selection} after inserting the object.
|
|
531
531
|
* * When `'on'`, the document selection will be set on the inserted object.
|
|
@@ -1014,7 +1014,7 @@ export default class Model {
|
|
|
1014
1014
|
*/
|
|
1015
1015
|
|
|
1016
1016
|
/**
|
|
1017
|
-
* Event fired when {@link #insertObject} method is called.
|
|
1017
|
+
* Event fired when the {@link #insertObject} method is called.
|
|
1018
1018
|
*
|
|
1019
1019
|
* The {@link #insertObject default action of that method} is implemented as a
|
|
1020
1020
|
* listener to this event so it can be fully customized by the features.
|
package/src/model/schema.js
CHANGED
|
@@ -835,7 +835,7 @@ export default class Schema {
|
|
|
835
835
|
}
|
|
836
836
|
|
|
837
837
|
/**
|
|
838
|
-
* Sets attributes allowed by the schema on given node.
|
|
838
|
+
* Sets attributes allowed by the schema on a given node.
|
|
839
839
|
*
|
|
840
840
|
* @param {module:engine/model/node~Node} node A node to set attributes on.
|
|
841
841
|
* @param {Object} attributes Attributes keys and values.
|
|
@@ -881,7 +881,7 @@ export default class Schema {
|
|
|
881
881
|
}
|
|
882
882
|
|
|
883
883
|
/**
|
|
884
|
-
* Gets attributes of a node that have given property.
|
|
884
|
+
* Gets attributes of a node that have a given property.
|
|
885
885
|
*
|
|
886
886
|
* @param {module:engine/model/node~Node} node Node to get attributes from.
|
|
887
887
|
* @param {String} propertyName Name of the property that attribute must have to return it.
|
|
@@ -21,16 +21,16 @@ import first from '@ckeditor/ckeditor5-utils/src/first';
|
|
|
21
21
|
//
|
|
22
22
|
// **Note:** Use {@link module:widget/utils#findOptimalInsertionRange} instead of this function outside engine.
|
|
23
23
|
// This function is only exposed to be used by {@link module:widget/utils#findOptimalInsertionRange findOptimalInsertionRange()}
|
|
24
|
-
// in `widget` package and inside `engine` package.
|
|
24
|
+
// in the `widget` package and inside the `engine` package.
|
|
25
25
|
//
|
|
26
26
|
// @private
|
|
27
27
|
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
|
|
28
28
|
// The selection based on which the insertion position should be calculated.
|
|
29
29
|
// @param {module:engine/model/model~Model} model Model instance.
|
|
30
|
-
// @param {'auto'|'before'|'after'} [place='auto']
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
30
|
+
// @param {'auto'|'before'|'after'} [place='auto'] The place where to look for optimal insertion range.
|
|
31
|
+
// The default `auto` value will determine itself the best position for insertion.
|
|
32
|
+
// The `before` value will try to find a position before selection.
|
|
33
|
+
// The `after` value will try to find a position after selection.
|
|
34
34
|
// @returns {module:engine/model/range~Range} The optimal range.
|
|
35
35
|
export function findOptimalInsertionRange( selection, model, place = 'auto' ) {
|
|
36
36
|
const selectedElement = selection.getSelectedElement();
|
|
@@ -129,9 +129,9 @@ export default function insertObject( model, object, selectable, placeOrOffset,
|
|
|
129
129
|
//
|
|
130
130
|
// @private
|
|
131
131
|
// @param {module:engine/model/writer~Writer} writer An instance of the model writer.
|
|
132
|
-
// @param {module:engine/model/element~Element} contextElement An element to set attributes on.
|
|
133
|
-
// @param {'on'|'after'} place
|
|
134
|
-
// Value `on` will set selection on passed `contextElement`. Value `after` will set selection after `contextElement`.
|
|
132
|
+
// @param {module:engine/model/element~Element} contextElement An element to set the attributes on.
|
|
133
|
+
// @param {'on'|'after'} place The place where selection should be set in relation to the `contextElement` element.
|
|
134
|
+
// Value `on` will set selection on the passed `contextElement`. Value `after` will set selection after `contextElement`.
|
|
135
135
|
// @param {Object} attributes Attributes keys and values to set on a paragraph that this function can create when
|
|
136
136
|
// `place` parameter is equal to `after` but there is no element with `$text` node to set selection in.
|
|
137
137
|
function updateSelection( writer, contextElement, place, paragraphAttributes ) {
|
|
@@ -161,9 +161,9 @@ function updateSelection( writer, contextElement, place, paragraphAttributes ) {
|
|
|
161
161
|
}
|
|
162
162
|
else {
|
|
163
163
|
/**
|
|
164
|
-
*
|
|
164
|
+
* The unsupported `options.setSelection` parameter was passed
|
|
165
165
|
* to the {@link module:engine/model/utils/insertobject insertObject()} function.
|
|
166
|
-
* Check {@link module:engine/model/utils/insertobject insertObject()} API documentation for allowed
|
|
166
|
+
* Check the {@link module:engine/model/utils/insertobject insertObject()} API documentation for allowed
|
|
167
167
|
* `options.setSelection` parameter values.
|
|
168
168
|
*
|
|
169
169
|
* @error insertobject-invalid-place-parameter-value
|