@ckeditor/ckeditor5-utils 34.2.0 → 35.1.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 +324 -0
- package/LICENSE.md +1 -1
- package/package.json +19 -8
- package/src/areconnectedthroughproperties.js +54 -71
- package/src/ckeditorerror.js +92 -114
- package/src/collection.js +594 -762
- package/src/comparearrays.js +22 -28
- package/src/config.js +193 -223
- package/src/count.js +8 -12
- package/src/diff.js +85 -110
- package/src/difftochanges.js +47 -57
- package/src/dom/createelement.js +17 -25
- package/src/dom/emittermixin.js +202 -263
- package/src/dom/getancestors.js +9 -13
- package/src/dom/getborderwidths.js +10 -13
- package/src/dom/getcommonancestor.js +9 -15
- package/src/dom/getdatafromelement.js +5 -9
- package/src/dom/getpositionedancestor.js +9 -14
- package/src/dom/global.js +15 -4
- package/src/dom/indexof.js +7 -11
- package/src/dom/insertat.js +2 -4
- package/src/dom/iscomment.js +2 -5
- package/src/dom/isnode.js +10 -12
- package/src/dom/isrange.js +2 -4
- package/src/dom/istext.js +2 -4
- package/src/dom/isvisible.js +2 -4
- package/src/dom/iswindow.js +11 -16
- package/src/dom/position.js +220 -410
- package/src/dom/rect.js +335 -414
- package/src/dom/remove.js +5 -8
- package/src/dom/resizeobserver.js +109 -342
- package/src/dom/scroll.js +151 -183
- package/src/dom/setdatainelement.js +5 -9
- package/src/dom/tounit.js +10 -12
- package/src/elementreplacer.js +30 -44
- package/src/emittermixin.js +368 -634
- package/src/env.js +109 -116
- package/src/eventinfo.js +12 -65
- package/src/fastdiff.js +96 -128
- package/src/first.js +8 -12
- package/src/focustracker.js +77 -133
- package/src/index.js +0 -9
- package/src/inserttopriorityarray.js +9 -30
- package/src/isiterable.js +2 -4
- package/src/keyboard.js +117 -196
- package/src/keystrokehandler.js +72 -88
- package/src/language.js +9 -15
- package/src/locale.js +61 -158
- package/src/mapsequal.js +12 -17
- package/src/mix.js +17 -16
- package/src/nth.js +8 -11
- package/src/objecttomap.js +7 -11
- package/src/observablemixin.js +474 -778
- package/src/priorities.js +20 -32
- package/src/spy.js +3 -6
- package/src/toarray.js +2 -13
- package/src/tomap.js +8 -10
- package/src/translation-service.js +57 -93
- package/src/uid.js +34 -38
- package/src/unicode.js +28 -43
- package/src/version.js +134 -143
package/src/env.js
CHANGED
|
@@ -2,170 +2,165 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
/* globals navigator:false */
|
|
7
|
-
|
|
8
6
|
/**
|
|
9
7
|
* @module utils/env
|
|
10
8
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Safely returns `userAgent` from browser's navigator API in a lower case.
|
|
11
|
+
* If navigator API is not available it will return an empty string.
|
|
12
|
+
*
|
|
13
|
+
* @returns {String}
|
|
14
|
+
*/
|
|
15
|
+
export function getUserAgent() {
|
|
16
|
+
// In some environments navigator API might not be available.
|
|
17
|
+
try {
|
|
18
|
+
return navigator.userAgent.toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const userAgent = getUserAgent();
|
|
14
25
|
/**
|
|
15
26
|
* A namespace containing environment and browser information.
|
|
16
27
|
*
|
|
17
28
|
* @namespace
|
|
18
29
|
*/
|
|
19
30
|
const env = {
|
|
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
|
-
* More information about unicode properties might be found
|
|
86
|
-
* [in Unicode Standard Annex #44](https://www.unicode.org/reports/tr44/#GC_Values_Table).
|
|
87
|
-
*
|
|
88
|
-
* @type {Boolean}
|
|
89
|
-
*/
|
|
90
|
-
isRegExpUnicodePropertySupported: isRegExpUnicodePropertySupported()
|
|
91
|
-
}
|
|
31
|
+
/**
|
|
32
|
+
* Indicates that the application is running on Macintosh.
|
|
33
|
+
*
|
|
34
|
+
* @static
|
|
35
|
+
* @type {Boolean}
|
|
36
|
+
*/
|
|
37
|
+
isMac: isMac(userAgent),
|
|
38
|
+
/**
|
|
39
|
+
* Indicates that the application is running on Windows.
|
|
40
|
+
*
|
|
41
|
+
* @static
|
|
42
|
+
* @type {Boolean}
|
|
43
|
+
*/
|
|
44
|
+
isWindows: isWindows(userAgent),
|
|
45
|
+
/**
|
|
46
|
+
* Indicates that the application is running in Firefox (Gecko).
|
|
47
|
+
*
|
|
48
|
+
* @static
|
|
49
|
+
* @type {Boolean}
|
|
50
|
+
*/
|
|
51
|
+
isGecko: isGecko(userAgent),
|
|
52
|
+
/**
|
|
53
|
+
* Indicates that the application is running in Safari.
|
|
54
|
+
*
|
|
55
|
+
* @static
|
|
56
|
+
* @type {Boolean}
|
|
57
|
+
*/
|
|
58
|
+
isSafari: isSafari(userAgent),
|
|
59
|
+
/**
|
|
60
|
+
* Indicates the the application is running in iOS.
|
|
61
|
+
*
|
|
62
|
+
* @static
|
|
63
|
+
* @type {Boolean}
|
|
64
|
+
*/
|
|
65
|
+
isiOS: isiOS(userAgent),
|
|
66
|
+
/**
|
|
67
|
+
* Indicates that the application is running on Android mobile device.
|
|
68
|
+
*
|
|
69
|
+
* @static
|
|
70
|
+
* @type {Boolean}
|
|
71
|
+
*/
|
|
72
|
+
isAndroid: isAndroid(userAgent),
|
|
73
|
+
/**
|
|
74
|
+
* Indicates that the application is running in a browser using the Blink engine.
|
|
75
|
+
*
|
|
76
|
+
* @static
|
|
77
|
+
* @type {Boolean}
|
|
78
|
+
*/
|
|
79
|
+
isBlink: isBlink(userAgent),
|
|
80
|
+
/**
|
|
81
|
+
* Environment features information.
|
|
82
|
+
*
|
|
83
|
+
* @memberOf module:utils/env~env
|
|
84
|
+
* @namespace
|
|
85
|
+
*/
|
|
86
|
+
features: {
|
|
87
|
+
/**
|
|
88
|
+
* Indicates that the environment supports ES2018 Unicode property escapes — like `\p{P}` or `\p{L}`.
|
|
89
|
+
* More information about unicode properties might be found
|
|
90
|
+
* [in Unicode Standard Annex #44](https://www.unicode.org/reports/tr44/#GC_Values_Table).
|
|
91
|
+
*
|
|
92
|
+
* @type {Boolean}
|
|
93
|
+
*/
|
|
94
|
+
isRegExpUnicodePropertySupported: isRegExpUnicodePropertySupported()
|
|
95
|
+
}
|
|
92
96
|
};
|
|
93
|
-
|
|
94
97
|
export default env;
|
|
95
|
-
|
|
96
98
|
/**
|
|
97
99
|
* Checks if User Agent represented by the string is running on Macintosh.
|
|
98
100
|
*
|
|
99
101
|
* @param {String} userAgent **Lowercase** `navigator.userAgent` string.
|
|
100
102
|
* @returns {Boolean} Whether User Agent is running on Macintosh or not.
|
|
101
103
|
*/
|
|
102
|
-
export function isMac(
|
|
103
|
-
|
|
104
|
+
export function isMac(userAgent) {
|
|
105
|
+
return userAgent.indexOf('macintosh') > -1;
|
|
104
106
|
}
|
|
105
|
-
|
|
106
107
|
/**
|
|
107
108
|
* Checks if User Agent represented by the string is running on Windows.
|
|
108
109
|
*
|
|
109
110
|
* @param {String} userAgent **Lowercase** `navigator.userAgent` string.
|
|
110
111
|
* @returns {Boolean} Whether User Agent is running on Windows or not.
|
|
111
112
|
*/
|
|
112
|
-
export function isWindows(
|
|
113
|
-
|
|
113
|
+
export function isWindows(userAgent) {
|
|
114
|
+
return userAgent.indexOf('windows') > -1;
|
|
114
115
|
}
|
|
115
|
-
|
|
116
116
|
/**
|
|
117
117
|
* Checks if User Agent represented by the string is Firefox (Gecko).
|
|
118
118
|
*
|
|
119
119
|
* @param {String} userAgent **Lowercase** `navigator.userAgent` string.
|
|
120
120
|
* @returns {Boolean} Whether User Agent is Firefox or not.
|
|
121
121
|
*/
|
|
122
|
-
export function isGecko(
|
|
123
|
-
|
|
122
|
+
export function isGecko(userAgent) {
|
|
123
|
+
return !!userAgent.match(/gecko\/\d+/);
|
|
124
124
|
}
|
|
125
|
-
|
|
126
125
|
/**
|
|
127
126
|
* Checks if User Agent represented by the string is Safari.
|
|
128
127
|
*
|
|
129
128
|
* @param {String} userAgent **Lowercase** `navigator.userAgent` string.
|
|
130
129
|
* @returns {Boolean} Whether User Agent is Safari or not.
|
|
131
130
|
*/
|
|
132
|
-
export function isSafari(
|
|
133
|
-
|
|
131
|
+
export function isSafari(userAgent) {
|
|
132
|
+
return userAgent.indexOf(' applewebkit/') > -1 && userAgent.indexOf('chrome') === -1;
|
|
134
133
|
}
|
|
135
|
-
|
|
136
134
|
/**
|
|
137
135
|
* Checks if User Agent represented by the string is running in iOS.
|
|
138
136
|
*
|
|
139
137
|
* @param {String} userAgent **Lowercase** `navigator.userAgent` string.
|
|
140
138
|
* @returns {Boolean} Whether User Agent is running in iOS or not.
|
|
141
139
|
*/
|
|
142
|
-
export function isiOS(
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
export function isiOS(userAgent) {
|
|
141
|
+
// "Request mobile site" || "Request desktop site".
|
|
142
|
+
return !!userAgent.match(/iphone|ipad/i) || (isMac(userAgent) && navigator.maxTouchPoints > 0);
|
|
145
143
|
}
|
|
146
|
-
|
|
147
144
|
/**
|
|
148
145
|
* Checks if User Agent represented by the string is Android mobile device.
|
|
149
146
|
*
|
|
150
147
|
* @param {String} userAgent **Lowercase** `navigator.userAgent` string.
|
|
151
148
|
* @returns {Boolean} Whether User Agent is Safari or not.
|
|
152
149
|
*/
|
|
153
|
-
export function isAndroid(
|
|
154
|
-
|
|
150
|
+
export function isAndroid(userAgent) {
|
|
151
|
+
return userAgent.indexOf('android') > -1;
|
|
155
152
|
}
|
|
156
|
-
|
|
157
153
|
/**
|
|
158
154
|
* Checks if User Agent represented by the string is Blink engine.
|
|
159
155
|
*
|
|
160
156
|
* @param {String} userAgent **Lowercase** `navigator.userAgent` string.
|
|
161
157
|
* @returns {Boolean} Whether User Agent is Blink engine or not.
|
|
162
158
|
*/
|
|
163
|
-
export function isBlink(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
159
|
+
export function isBlink(userAgent) {
|
|
160
|
+
// The Edge browser before switching to the Blink engine used to report itself as Chrome (and "Edge/")
|
|
161
|
+
// but after switching to the Blink it replaced "Edge/" with "Edg/".
|
|
162
|
+
return userAgent.indexOf('chrome/') > -1 && userAgent.indexOf('edge/') < 0;
|
|
167
163
|
}
|
|
168
|
-
|
|
169
164
|
/**
|
|
170
165
|
* Checks if the current environment supports ES2018 Unicode properties like `\p{P}` or `\p{L}`.
|
|
171
166
|
* More information about unicode properties might be found
|
|
@@ -174,17 +169,15 @@ export function isBlink( userAgent ) {
|
|
|
174
169
|
* @returns {Boolean}
|
|
175
170
|
*/
|
|
176
171
|
export function isRegExpUnicodePropertySupported() {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return isSupported;
|
|
172
|
+
let isSupported = false;
|
|
173
|
+
// Feature detection for Unicode properties. Added in ES2018. Currently Firefox does not support it.
|
|
174
|
+
// See https://github.com/ckeditor/ckeditor5-mention/issues/44#issuecomment-487002174.
|
|
175
|
+
try {
|
|
176
|
+
// Usage of regular expression literal cause error during build (ckeditor/ckeditor5-dev#534).
|
|
177
|
+
isSupported = 'ć'.search(new RegExp('[\\p{L}]', 'u')) === 0;
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
// Firefox throws a SyntaxError when the group is unsupported.
|
|
181
|
+
}
|
|
182
|
+
return isSupported;
|
|
190
183
|
}
|
package/src/eventinfo.js
CHANGED
|
@@ -2,78 +2,25 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
/**
|
|
7
6
|
* @module utils/eventinfo
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
8
|
import spy from './spy';
|
|
11
|
-
|
|
12
9
|
/**
|
|
13
10
|
* The event object passed to event callbacks. It is used to provide information about the event as well as a tool to
|
|
14
11
|
* manipulate it.
|
|
15
12
|
*/
|
|
16
13
|
export default class EventInfo {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* The event name.
|
|
32
|
-
*
|
|
33
|
-
* @readonly
|
|
34
|
-
* @member {String}
|
|
35
|
-
*/
|
|
36
|
-
this.name = name;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Path this event has followed. See {@link module:utils/emittermixin~EmitterMixin#delegate}.
|
|
40
|
-
*
|
|
41
|
-
* @readonly
|
|
42
|
-
* @member {Array.<Object>}
|
|
43
|
-
*/
|
|
44
|
-
this.path = [];
|
|
45
|
-
|
|
46
|
-
// The following methods are defined in the constructor because they must be re-created per instance.
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Stops the event emitter to call further callbacks for this event interaction.
|
|
50
|
-
*
|
|
51
|
-
* @method #stop
|
|
52
|
-
*/
|
|
53
|
-
this.stop = spy();
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Removes the current callback from future interactions of this event.
|
|
57
|
-
*
|
|
58
|
-
* @method #off
|
|
59
|
-
*/
|
|
60
|
-
this.off = spy();
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The value which will be returned by {@link module:utils/emittermixin~EmitterMixin#fire}.
|
|
64
|
-
*
|
|
65
|
-
* It's `undefined` by default and can be changed by an event listener:
|
|
66
|
-
*
|
|
67
|
-
* dataController.fire( 'getSelectedContent', ( evt ) => {
|
|
68
|
-
* // This listener will make `dataController.fire( 'getSelectedContent' )`
|
|
69
|
-
* // always return an empty DocumentFragment.
|
|
70
|
-
* evt.return = new DocumentFragment();
|
|
71
|
-
*
|
|
72
|
-
* // Make sure no other listeners are executed.
|
|
73
|
-
* evt.stop();
|
|
74
|
-
* } );
|
|
75
|
-
*
|
|
76
|
-
* @member #return
|
|
77
|
-
*/
|
|
78
|
-
}
|
|
14
|
+
/**
|
|
15
|
+
* @param {Object} source The emitter.
|
|
16
|
+
* @param {String} name The event name.
|
|
17
|
+
*/
|
|
18
|
+
constructor(source, name) {
|
|
19
|
+
this.source = source;
|
|
20
|
+
this.name = name;
|
|
21
|
+
this.path = [];
|
|
22
|
+
// The following methods are defined in the constructor because they must be re-created per instance.
|
|
23
|
+
this.stop = spy();
|
|
24
|
+
this.off = spy();
|
|
25
|
+
}
|
|
79
26
|
}
|
package/src/fastdiff.js
CHANGED
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @module utils/fastdiff
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
5
|
/**
|
|
11
6
|
* Finds positions of the first and last change in the given string/array and generates a set of changes:
|
|
12
7
|
*
|
|
@@ -94,33 +89,24 @@
|
|
|
94
89
|
* be returned instead of changes set. This makes this function compatible with {@link module:utils/diff~diff `diff()`}.
|
|
95
90
|
* @returns {Array} Array of changes.
|
|
96
91
|
*/
|
|
97
|
-
export default function fastDiff(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
b = Array.prototype.slice.call( b );
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Find first and last change.
|
|
118
|
-
const changeIndexes = findChangeBoundaryIndexes( a, b, cmp );
|
|
119
|
-
|
|
120
|
-
// Transform into changes array.
|
|
121
|
-
return atomicChanges ? changeIndexesToAtomicChanges( changeIndexes, b.length ) : changeIndexesToChanges( b, changeIndexes );
|
|
92
|
+
export default function fastDiff(a, b, cmp, atomicChanges = false) {
|
|
93
|
+
// Set the comparator function.
|
|
94
|
+
cmp = cmp || function (a, b) {
|
|
95
|
+
return a === b;
|
|
96
|
+
};
|
|
97
|
+
// Convert the string (or any array-like object - eg. NodeList) to an array by using the slice() method because,
|
|
98
|
+
// unlike Array.from(), it returns array of UTF-16 code units instead of the code points of a string.
|
|
99
|
+
// One code point might be a surrogate pair of two code units. All text offsets are expected to be in code units.
|
|
100
|
+
// See ckeditor/ckeditor5#3147.
|
|
101
|
+
//
|
|
102
|
+
// We need to make sure here that fastDiff() works identical to diff().
|
|
103
|
+
const arrayA = Array.isArray(a) ? a : Array.prototype.slice.call(a);
|
|
104
|
+
const arrayB = Array.isArray(b) ? b : Array.prototype.slice.call(b);
|
|
105
|
+
// Find first and last change.
|
|
106
|
+
const changeIndexes = findChangeBoundaryIndexes(arrayA, arrayB, cmp);
|
|
107
|
+
// Transform into changes array.
|
|
108
|
+
return atomicChanges ? changeIndexesToAtomicChanges(changeIndexes, arrayB.length) : changeIndexesToChanges(arrayB, changeIndexes);
|
|
122
109
|
}
|
|
123
|
-
|
|
124
110
|
// Finds position of the first and last change in the given arrays. For example:
|
|
125
111
|
//
|
|
126
112
|
// const indexes = findChangeBoundaryIndexes( [ '1', '2', '3', '4' ], [ '1', '3', '4', '2', '4' ] );
|
|
@@ -137,125 +123,107 @@ export default function fastDiff( a, b, cmp, atomicChanges = false ) {
|
|
|
137
123
|
// @returns {Number} return.firstIndex Index of the first change in both values (always the same for both).
|
|
138
124
|
// @returns {Number} result.lastIndexOld Index of the last common value in `arr1`.
|
|
139
125
|
// @returns {Number} result.lastIndexNew Index of the last common value in `arr2`.
|
|
140
|
-
function findChangeBoundaryIndexes(
|
|
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
|
-
// Use `lastIndex` to calculate proper offset, starting from the beginning (`lastIndex` kind of starts from the end).
|
|
167
|
-
const lastIndexOld = arr1.length - lastIndex;
|
|
168
|
-
const lastIndexNew = arr2.length - lastIndex;
|
|
169
|
-
|
|
170
|
-
return { firstIndex, lastIndexOld, lastIndexNew };
|
|
126
|
+
function findChangeBoundaryIndexes(arr1, arr2, cmp) {
|
|
127
|
+
// Find the first difference between passed values.
|
|
128
|
+
const firstIndex = findFirstDifferenceIndex(arr1, arr2, cmp);
|
|
129
|
+
// If arrays are equal return -1 indexes object.
|
|
130
|
+
if (firstIndex === -1) {
|
|
131
|
+
return { firstIndex: -1, lastIndexOld: -1, lastIndexNew: -1 };
|
|
132
|
+
}
|
|
133
|
+
// Remove the common part of each value and reverse them to make it simpler to find the last difference between them.
|
|
134
|
+
const oldArrayReversed = cutAndReverse(arr1, firstIndex);
|
|
135
|
+
const newArrayReversed = cutAndReverse(arr2, firstIndex);
|
|
136
|
+
// Find the first difference between reversed values.
|
|
137
|
+
// It should be treated as "how many elements from the end the last difference occurred".
|
|
138
|
+
//
|
|
139
|
+
// For example:
|
|
140
|
+
//
|
|
141
|
+
// initial -> after cut -> reversed:
|
|
142
|
+
// oldValue: '321ba' -> '21ba' -> 'ab12'
|
|
143
|
+
// newValue: '31xba' -> '1xba' -> 'abx1'
|
|
144
|
+
// lastIndex: -> 2
|
|
145
|
+
//
|
|
146
|
+
// So the last change occurred two characters from the end of the arrays.
|
|
147
|
+
const lastIndex = findFirstDifferenceIndex(oldArrayReversed, newArrayReversed, cmp);
|
|
148
|
+
// Use `lastIndex` to calculate proper offset, starting from the beginning (`lastIndex` kind of starts from the end).
|
|
149
|
+
const lastIndexOld = arr1.length - lastIndex;
|
|
150
|
+
const lastIndexNew = arr2.length - lastIndex;
|
|
151
|
+
return { firstIndex, lastIndexOld, lastIndexNew };
|
|
171
152
|
}
|
|
172
|
-
|
|
173
153
|
// Returns a first index on which given arrays differ. If both arrays are the same, -1 is returned.
|
|
174
154
|
//
|
|
175
155
|
// @param {Array} arr1
|
|
176
156
|
// @param {Array} arr2
|
|
177
157
|
// @param {Function} cmp Comparator function.
|
|
178
158
|
// @returns {Number}
|
|
179
|
-
function findFirstDifferenceIndex(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return -1; // Return -1 if arrays are equal.
|
|
159
|
+
function findFirstDifferenceIndex(arr1, arr2, cmp) {
|
|
160
|
+
for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
|
|
161
|
+
if (arr1[i] === undefined || arr2[i] === undefined || !cmp(arr1[i], arr2[i])) {
|
|
162
|
+
return i;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return -1; // Return -1 if arrays are equal.
|
|
187
166
|
}
|
|
188
|
-
|
|
189
167
|
// Returns a copy of the given array with `howMany` elements removed starting from the beginning and in reversed order.
|
|
190
168
|
//
|
|
191
169
|
// @param {Array} arr Array to be processed.
|
|
192
170
|
// @param {Number} howMany How many elements from array beginning to remove.
|
|
193
171
|
// @returns {Array} Shortened and reversed array.
|
|
194
|
-
function cutAndReverse(
|
|
195
|
-
|
|
172
|
+
function cutAndReverse(arr, howMany) {
|
|
173
|
+
return arr.slice(howMany).reverse();
|
|
196
174
|
}
|
|
197
|
-
|
|
198
175
|
// Generates changes array based on change indexes from `findChangeBoundaryIndexes` function. This function will
|
|
199
176
|
// generate array with 0 (no changes), 1 (deletion or insertion) or 2 records (insertion and deletion).
|
|
200
177
|
//
|
|
201
178
|
// @param {Array} newArray New array for which change indexes were calculated.
|
|
202
179
|
// @param {Object} changeIndexes Change indexes object from `findChangeBoundaryIndexes` function.
|
|
203
|
-
// @returns {Array.<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return result;
|
|
180
|
+
// @returns {Array.<module:utils/difftochanges~Change>} Array of changes compatible with
|
|
181
|
+
// {@link module:utils/difftochanges~diffToChanges} format.
|
|
182
|
+
function changeIndexesToChanges(newArray, changeIndexes) {
|
|
183
|
+
const result = [];
|
|
184
|
+
const { firstIndex, lastIndexOld, lastIndexNew } = changeIndexes;
|
|
185
|
+
// Order operations as 'insert', 'delete' array to keep compatibility with {@link module:utils/difftochanges~diffToChanges}
|
|
186
|
+
// in most cases. However, 'diffToChanges' does not stick to any order so in some cases
|
|
187
|
+
// (for example replacing '12345' with 'abcd') it will generate 'delete', 'insert' order.
|
|
188
|
+
if (lastIndexNew - firstIndex > 0) {
|
|
189
|
+
result.push({
|
|
190
|
+
index: firstIndex,
|
|
191
|
+
type: 'insert',
|
|
192
|
+
values: newArray.slice(firstIndex, lastIndexNew)
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (lastIndexOld - firstIndex > 0) {
|
|
196
|
+
result.push({
|
|
197
|
+
index: firstIndex + (lastIndexNew - firstIndex),
|
|
198
|
+
type: 'delete',
|
|
199
|
+
howMany: lastIndexOld - firstIndex
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
228
203
|
}
|
|
229
|
-
|
|
230
204
|
// Generates array with set `equal|insert|delete` operations based on change indexes from `findChangeBoundaryIndexes` function.
|
|
231
205
|
//
|
|
232
206
|
// @param {Object} changeIndexes Change indexes object from `findChangeBoundaryIndexes` function.
|
|
233
207
|
// @param {Number} newLength Length of the new array on which `findChangeBoundaryIndexes` calculated change indexes.
|
|
234
|
-
// @returns {Array.<
|
|
235
|
-
function changeIndexesToAtomicChanges(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if ( lastIndexNew < newLength ) {
|
|
257
|
-
result = result.concat( Array( newLength - lastIndexNew ).fill( 'equal' ) );
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return result;
|
|
208
|
+
// @returns {Array.<module:utils/diff~DiffResult>} Array of changes compatible with {@link module:utils/diff~diff} format.
|
|
209
|
+
function changeIndexesToAtomicChanges(changeIndexes, newLength) {
|
|
210
|
+
const { firstIndex, lastIndexOld, lastIndexNew } = changeIndexes;
|
|
211
|
+
// No changes.
|
|
212
|
+
if (firstIndex === -1) {
|
|
213
|
+
return Array(newLength).fill('equal');
|
|
214
|
+
}
|
|
215
|
+
let result = [];
|
|
216
|
+
if (firstIndex > 0) {
|
|
217
|
+
result = result.concat(Array(firstIndex).fill('equal'));
|
|
218
|
+
}
|
|
219
|
+
if (lastIndexNew - firstIndex > 0) {
|
|
220
|
+
result = result.concat(Array(lastIndexNew - firstIndex).fill('insert'));
|
|
221
|
+
}
|
|
222
|
+
if (lastIndexOld - firstIndex > 0) {
|
|
223
|
+
result = result.concat(Array(lastIndexOld - firstIndex).fill('delete'));
|
|
224
|
+
}
|
|
225
|
+
if (lastIndexNew < newLength) {
|
|
226
|
+
result = result.concat(Array(newLength - lastIndexNew).fill('equal'));
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
261
229
|
}
|