easy-richtextarea 3.0.154 → 4.0.3
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/README.md +8 -0
- package/example.js +836 -1
- package/lib/browser.js +33 -1
- package/lib/constants.js +13 -0
- package/lib/content/transform.js +17 -0
- package/lib/main.js +45 -4
- package/lib/operation/delete.js +240 -0
- package/lib/operation/empty.js +101 -0
- package/lib/operation/insert.js +198 -0
- package/lib/operations/fromJSON.js +40 -0
- package/lib/operations/generate.js +42 -0
- package/lib/operations/toJSON.js +17 -0
- package/lib/operations/transform.js +35 -0
- package/lib/stringCompare.js +33 -0
- package/lib/types.js +34 -0
- package/package.json +6 -2
- package/src/browser.js +10 -1
- package/src/constants.js +3 -0
- package/src/content/transform.js +5 -0
- package/src/main.js +10 -0
- package/src/operation/delete.js +206 -0
- package/src/operation/empty.js +71 -0
- package/src/operation/insert.js +163 -0
- package/src/operations/fromJSON.js +34 -0
- package/src/operations/generate.js +42 -0
- package/src/operations/toJSON.js +5 -0
- package/src/operations/transform.js +57 -0
- package/src/stringCompare.js +33 -0
- package/src/types.js +11 -0
- package/test/content/transform.js +59 -0
- package/test/helpers.js +85 -0
- package/test/operation/delete.js +245 -0
- package/test/operation/empty.js +83 -0
- package/test/operation/insert.js +201 -0
- package/test/operations/generate.js +81 -0
- package/test/operations/transform.js +130 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import EmptyOperation from "../operation/empty";
|
|
4
|
+
|
|
5
|
+
import { insertType, deleteType, emptyType } from "../types";
|
|
6
|
+
|
|
7
|
+
export default class DeleteOperation {
|
|
8
|
+
constructor(type, length, position) {
|
|
9
|
+
this.type = type;
|
|
10
|
+
this.length = length;
|
|
11
|
+
this.position = position;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
clone() {
|
|
15
|
+
return DeleteOperation.fromLengthAndPosition(this.length, this.position);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
transformOperation(operation) {
|
|
19
|
+
switch (operation.type) {
|
|
20
|
+
case insertType:
|
|
21
|
+
return ((tau, rho) => {
|
|
22
|
+
|
|
23
|
+
if (tau.position <= rho.position) {
|
|
24
|
+
return [tau.clone()];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (tau.position > rho.position) {
|
|
28
|
+
if (tau.position < rho.length + rho.position) {
|
|
29
|
+
return [tau.left(rho).shift(tau)];
|
|
30
|
+
}
|
|
31
|
+
if (tau.position >= rho.length + rho.position) {
|
|
32
|
+
return [rho.shift(tau)];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
})(operation, this);
|
|
37
|
+
|
|
38
|
+
case deleteType:
|
|
39
|
+
return ((tau, rho) => {
|
|
40
|
+
|
|
41
|
+
if (tau.position < rho.position) {
|
|
42
|
+
if (tau.length + tau.position <= rho.position) {
|
|
43
|
+
return [tau.clone()];
|
|
44
|
+
}
|
|
45
|
+
if (tau.length + tau.position <= rho.length + rho.position) {
|
|
46
|
+
return [rho.takenFrom(tau)];
|
|
47
|
+
}
|
|
48
|
+
if (tau.length + tau.position > rho.length + rho.position) {
|
|
49
|
+
return [rho.split(tau)];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (tau.position === rho.position) {
|
|
54
|
+
if (tau.length + tau.position <= rho.length + rho.position) {
|
|
55
|
+
return [EmptyOperation.fromNothing()];
|
|
56
|
+
}
|
|
57
|
+
if (tau.length + tau.position > rho.length + rho.position) {
|
|
58
|
+
return [rho.shift(rho.takenFrom(tau))];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (tau.position >= rho.length + rho.position) {
|
|
63
|
+
return [rho.shift(tau)];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (tau.position > rho.position) {
|
|
67
|
+
if (tau.length + tau.position <= rho.length + rho.position) {
|
|
68
|
+
return [EmptyOperation.fromNothing()];
|
|
69
|
+
}
|
|
70
|
+
if (tau.length + tau.position > rho.length + rho.position) {
|
|
71
|
+
return [rho.shift(rho.takenFrom(tau))];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
})(operation, this);
|
|
76
|
+
|
|
77
|
+
case emptyType:
|
|
78
|
+
return ((tau, rho) => {
|
|
79
|
+
|
|
80
|
+
return [tau.clone()];
|
|
81
|
+
|
|
82
|
+
})(operation, this);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
transformContent(content) {
|
|
87
|
+
return content.substring(0, this.position) + content.substring(this.length + this.position);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
transformSelection(selection) {
|
|
91
|
+
let transformedSelection;
|
|
92
|
+
|
|
93
|
+
const length = this.length, ///
|
|
94
|
+
startPosition = this.position, ///
|
|
95
|
+
endPosition = startPosition + length,
|
|
96
|
+
selectionStartPosition = selection.getStartPosition(),
|
|
97
|
+
selectionEndPosition = selection.getEndPosition();
|
|
98
|
+
|
|
99
|
+
let offset,
|
|
100
|
+
endOffset;
|
|
101
|
+
|
|
102
|
+
if (false) {
|
|
103
|
+
///
|
|
104
|
+
} else if (selectionStartPosition >= endPosition) {
|
|
105
|
+
offset = -length;
|
|
106
|
+
|
|
107
|
+
transformedSelection = selection.shifted(offset);
|
|
108
|
+
} else if (selectionStartPosition >= startPosition) {
|
|
109
|
+
if (selectionEndPosition > endPosition) {
|
|
110
|
+
offset = startPosition - selectionStartPosition;
|
|
111
|
+
endOffset = selectionStartPosition - endPosition;
|
|
112
|
+
|
|
113
|
+
transformedSelection = selection.shifted(offset).endPositionShifted(endOffset);
|
|
114
|
+
} else {
|
|
115
|
+
const Selection = selection.constructor; ///
|
|
116
|
+
|
|
117
|
+
transformedSelection = new Selection(startPosition, startPosition);
|
|
118
|
+
}
|
|
119
|
+
} else if (selectionEndPosition > endPosition) {
|
|
120
|
+
endOffset = -length;
|
|
121
|
+
|
|
122
|
+
transformedSelection = selection.endPositionShifted(endOffset);
|
|
123
|
+
} else if (selectionEndPosition > startPosition) {
|
|
124
|
+
endOffset = startPosition - selectionEndPosition;
|
|
125
|
+
|
|
126
|
+
transformedSelection = selection.endPositionShifted(endOffset);
|
|
127
|
+
} else {
|
|
128
|
+
transformedSelection = selection.clone();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return transformedSelection;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
shifted(offset) {
|
|
135
|
+
const length = this.length,
|
|
136
|
+
position = this.position + offset,
|
|
137
|
+
deleteOperation = DeleteOperation.fromLengthAndPosition(length, position);
|
|
138
|
+
|
|
139
|
+
return deleteOperation;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
shift(operation) {
|
|
143
|
+
const offset = -this.length,
|
|
144
|
+
shiftedOperation = operation.shifted(offset);
|
|
145
|
+
|
|
146
|
+
return shiftedOperation
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
takenFrom(deleteOperation) {
|
|
150
|
+
let length = deleteOperation.length,
|
|
151
|
+
position = deleteOperation.position;
|
|
152
|
+
|
|
153
|
+
if (this.position > position && this.length + this.position >= length + position) {
|
|
154
|
+
length = this.position - position;
|
|
155
|
+
|
|
156
|
+
deleteOperation = DeleteOperation.fromLengthAndPosition(length, position);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (this.position <= position && this.length + this.position < length + position) {
|
|
160
|
+
length = length + position - this.position - this.length;
|
|
161
|
+
position = this.length + this.position;
|
|
162
|
+
|
|
163
|
+
deleteOperation = DeleteOperation.fromLengthAndPosition(length, position);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return deleteOperation;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
split(deleteOperation) {
|
|
170
|
+
let length = deleteOperation.length,
|
|
171
|
+
position = deleteOperation.position;
|
|
172
|
+
|
|
173
|
+
length = length - this.length;
|
|
174
|
+
|
|
175
|
+
deleteOperation = DeleteOperation.fromLengthAndPosition(length, position);
|
|
176
|
+
|
|
177
|
+
return deleteOperation;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
toJSON() {
|
|
181
|
+
const type = this.type,
|
|
182
|
+
length = this.length,
|
|
183
|
+
position = this.position,
|
|
184
|
+
json = {
|
|
185
|
+
type,
|
|
186
|
+
length,
|
|
187
|
+
position,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return json;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static fromJSON(json) {
|
|
194
|
+
const { type, length, position } = json,
|
|
195
|
+
deleteOperation = new DeleteOperation(type, length, position);
|
|
196
|
+
|
|
197
|
+
return deleteOperation;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static fromLengthAndPosition(length, position) {
|
|
201
|
+
const type = deleteType, ///
|
|
202
|
+
deleteOperation = new DeleteOperation(type, length, position);
|
|
203
|
+
|
|
204
|
+
return deleteOperation;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { emptyType } from "../types";
|
|
4
|
+
|
|
5
|
+
export default class EmptyOperation {
|
|
6
|
+
constructor(type) {
|
|
7
|
+
this.type = type;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
clone() {
|
|
13
|
+
return EmptyOperation.fromNothing();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
transformOperation(operation) {
|
|
17
|
+
return ((tau, rho) => {
|
|
18
|
+
|
|
19
|
+
return [tau.clone()];
|
|
20
|
+
|
|
21
|
+
})(operation, this);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
transformContent(content) {
|
|
25
|
+
return content;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
transformSelection(selection) {
|
|
29
|
+
const transformedSelection = selection.clone();
|
|
30
|
+
|
|
31
|
+
return transformedSelection;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
shift(operation) {
|
|
37
|
+
const offset = 0,
|
|
38
|
+
shiftedOperation = operation.shifted(offset);
|
|
39
|
+
|
|
40
|
+
return shiftedOperation;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
toJSON() {
|
|
48
|
+
const type = this.type,
|
|
49
|
+
json = {
|
|
50
|
+
type
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
return json;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static fromJSON(json) {
|
|
59
|
+
const { type } = json,
|
|
60
|
+
emptyOperation = new EmptyOperation(type);
|
|
61
|
+
|
|
62
|
+
return emptyOperation;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static fromNothing() {
|
|
66
|
+
const type = emptyType, ///
|
|
67
|
+
emptyOperation = new EmptyOperation(type);
|
|
68
|
+
|
|
69
|
+
return emptyOperation;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import stringCompare from "../stringCompare";
|
|
4
|
+
import DeleteOperation from "../operation/delete";
|
|
5
|
+
|
|
6
|
+
import { insertType, deleteType, emptyType } from "../types";
|
|
7
|
+
|
|
8
|
+
export default class InsertOperation {
|
|
9
|
+
constructor(type, string, position) {
|
|
10
|
+
this.type = type;
|
|
11
|
+
this.string = string;
|
|
12
|
+
this.position = position;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
clone() {
|
|
16
|
+
return InsertOperation.fromStringAndPosition(this.string, this.position);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
transformOperation(operation) {
|
|
20
|
+
switch (operation.type) {
|
|
21
|
+
case insertType:
|
|
22
|
+
return ((tau, rho) => {
|
|
23
|
+
|
|
24
|
+
if (tau.position < rho.position) {
|
|
25
|
+
return [tau.clone()];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (tau.position === rho.position) {
|
|
29
|
+
if (tau.string === rho.string) {
|
|
30
|
+
return [tau.clone()];
|
|
31
|
+
}
|
|
32
|
+
if (tau.string !== rho.string) {
|
|
33
|
+
if (stringCompare(rho.string, tau.string)) {
|
|
34
|
+
return [rho.shift(tau)];
|
|
35
|
+
} else {
|
|
36
|
+
return [tau.clone()];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (tau.position > rho.position) {
|
|
42
|
+
return [rho.shift(tau)];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
})(operation, this);
|
|
46
|
+
|
|
47
|
+
case deleteType:
|
|
48
|
+
return ((tau, rho) => {
|
|
49
|
+
|
|
50
|
+
if (tau.position + tau.length <= rho.position) {
|
|
51
|
+
return [tau.clone()];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (tau.position < rho.position) {
|
|
55
|
+
return [rho.left(tau), rho.left(tau).shift(rho.shift(rho.right(tau)))];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (tau.position >= rho.position) {
|
|
59
|
+
return [rho.shift(tau)];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
})(operation, this);
|
|
63
|
+
|
|
64
|
+
case emptyType:
|
|
65
|
+
return ((tau, rho) => {
|
|
66
|
+
|
|
67
|
+
return [tau.clone()];
|
|
68
|
+
|
|
69
|
+
})(operation, this);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
transformContent(content) {
|
|
74
|
+
return content.substring(0, this.position) + this.string + content.substring(this.position);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
transformSelection(selection) {
|
|
78
|
+
let transformedSelection;
|
|
79
|
+
|
|
80
|
+
const startPosition = this.position, ///
|
|
81
|
+
length = this.string.length,
|
|
82
|
+
selectionStartPosition = selection.getStartPosition(),
|
|
83
|
+
selectionEndPosition = selection.getEndPosition(),
|
|
84
|
+
offset = length, ///
|
|
85
|
+
endOffset = offset; ///
|
|
86
|
+
|
|
87
|
+
if (false) {
|
|
88
|
+
|
|
89
|
+
} else if (selectionStartPosition >= startPosition) {
|
|
90
|
+
transformedSelection = selection.shifted(offset);
|
|
91
|
+
} else if (selectionEndPosition > startPosition) {
|
|
92
|
+
transformedSelection = selection.endPositionShifted(endOffset);
|
|
93
|
+
} else {
|
|
94
|
+
transformedSelection = selection.clone();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return transformedSelection;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
shifted(offset) {
|
|
101
|
+
const string = this.string,
|
|
102
|
+
position = this.position + offset,
|
|
103
|
+
insertOperation = InsertOperation.fromStringAndPosition(string, position);
|
|
104
|
+
|
|
105
|
+
return insertOperation;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
shift(operation) {
|
|
109
|
+
const length = this.string.length,
|
|
110
|
+
offset = length, ///
|
|
111
|
+
shiftedOperation = operation.shifted(offset);
|
|
112
|
+
|
|
113
|
+
return shiftedOperation;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
left(deleteOperation) {
|
|
117
|
+
const position = deleteOperation.position,
|
|
118
|
+
length = this.position - position;
|
|
119
|
+
|
|
120
|
+
deleteOperation = DeleteOperation.fromLengthAndPosition(length, position);
|
|
121
|
+
|
|
122
|
+
return deleteOperation;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
right(deleteOperation) {
|
|
126
|
+
let length = deleteOperation.length,
|
|
127
|
+
position = deleteOperation.position;
|
|
128
|
+
|
|
129
|
+
length = length - this.position + position;
|
|
130
|
+
position = this.position;
|
|
131
|
+
|
|
132
|
+
deleteOperation = DeleteOperation.fromLengthAndPosition(length, position);
|
|
133
|
+
|
|
134
|
+
return deleteOperation;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
toJSON() {
|
|
138
|
+
const type = this.type,
|
|
139
|
+
string = this.string,
|
|
140
|
+
position = this.position,
|
|
141
|
+
json = {
|
|
142
|
+
type,
|
|
143
|
+
string,
|
|
144
|
+
position
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return json;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static fromJSON(json) {
|
|
151
|
+
const { type, string, position } = json,
|
|
152
|
+
insertOperation = new InsertOperation(type, string, position);
|
|
153
|
+
|
|
154
|
+
return insertOperation;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static fromStringAndPosition(string, position) {
|
|
158
|
+
const type = insertType, ///
|
|
159
|
+
insertOperation = new InsertOperation(type, string, position);
|
|
160
|
+
|
|
161
|
+
return insertOperation;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import EmptyOperation from "../operation/empty";
|
|
4
|
+
import DeleteOperation from "../operation/delete";
|
|
5
|
+
import InsertOperation from "../operation/insert";
|
|
6
|
+
|
|
7
|
+
import { emptyType, deleteType, insertType } from "../types";
|
|
8
|
+
|
|
9
|
+
export default function operationsFromJSON(operationsJSON) {
|
|
10
|
+
const operations = operationsJSON.map((operationJSON) => {
|
|
11
|
+
let operation;
|
|
12
|
+
|
|
13
|
+
const json = operationJSON, ///
|
|
14
|
+
{ type } = json;
|
|
15
|
+
|
|
16
|
+
switch (type) {
|
|
17
|
+
case emptyType:
|
|
18
|
+
operation = EmptyOperation.fromJSON(json);
|
|
19
|
+
break;
|
|
20
|
+
|
|
21
|
+
case deleteType:
|
|
22
|
+
operation = DeleteOperation.fromJSON(json);
|
|
23
|
+
break;
|
|
24
|
+
|
|
25
|
+
case insertType:
|
|
26
|
+
operation = InsertOperation.fromJSON(json);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return operation;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return operations;
|
|
34
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import InsertOperation from "../operation/insert";
|
|
4
|
+
import DeleteOperation from "../operation/delete";
|
|
5
|
+
|
|
6
|
+
export default function generateOperations(contentA, contentB) {
|
|
7
|
+
const operations = [],
|
|
8
|
+
contentALength = contentA.length,
|
|
9
|
+
contentBLength = contentB.length;
|
|
10
|
+
|
|
11
|
+
let left, right;
|
|
12
|
+
|
|
13
|
+
for (left = 0; (left < contentALength) && (left < contentBLength); left++) {
|
|
14
|
+
if (contentA[left] !== contentB[left]) {
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for (right = 0; (right < contentALength - left) && (right < contentBLength - left); right++) {
|
|
20
|
+
if (contentA[contentALength - right - 1] !== contentB[contentBLength - right - 1]) {
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (left + right !== contentALength) {
|
|
26
|
+
const length = contentALength - left - right, ///
|
|
27
|
+
position = left, ///
|
|
28
|
+
deleteOperation = DeleteOperation.fromLengthAndPosition(length, position);
|
|
29
|
+
|
|
30
|
+
operations.push(deleteOperation);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (left + right !== contentBLength) {
|
|
34
|
+
const string = contentB.substring(left, contentBLength - right), ///
|
|
35
|
+
position = left, ///
|
|
36
|
+
insertOperation = InsertOperation.fromStringAndPosition(string, position);
|
|
37
|
+
|
|
38
|
+
operations.push(insertOperation);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return operations;
|
|
42
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { arrayUtilities } from "necessary";
|
|
4
|
+
|
|
5
|
+
const { first, tail } = arrayUtilities;
|
|
6
|
+
|
|
7
|
+
export default function transformOperations(tau, rho) {
|
|
8
|
+
if ((tau.length === 0) || (rho.length === 0)) {
|
|
9
|
+
return tau;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const tau1 = [first(tau)],
|
|
13
|
+
tau2 = tail(tau),
|
|
14
|
+
rho1 = [first(rho)],
|
|
15
|
+
rho2 = tail(rho);
|
|
16
|
+
|
|
17
|
+
if (tau.length > 1 && rho.length > 1) {
|
|
18
|
+
return (
|
|
19
|
+
transformOperations(
|
|
20
|
+
transformOperations(tau1, rho1),
|
|
21
|
+
rho2
|
|
22
|
+
).concat(
|
|
23
|
+
transformOperations(
|
|
24
|
+
transformOperations(
|
|
25
|
+
tau2,
|
|
26
|
+
transformOperations(rho1, tau1)
|
|
27
|
+
),
|
|
28
|
+
transformOperations(
|
|
29
|
+
rho2,
|
|
30
|
+
transformOperations(tau1, rho1)
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
);
|
|
35
|
+
} else if ((tau.length > 1) && (rho.length === 1)) {
|
|
36
|
+
return (
|
|
37
|
+
transformOperations(tau1, rho).concat(
|
|
38
|
+
transformOperations(
|
|
39
|
+
tau2,
|
|
40
|
+
transformOperations(rho, tau1)
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
);
|
|
44
|
+
} else if ((tau.length === 1) && (rho.length > 1)) {
|
|
45
|
+
return (
|
|
46
|
+
transformOperations(
|
|
47
|
+
transformOperations(tau, rho1),
|
|
48
|
+
rho2
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
} else {
|
|
52
|
+
tau = first(tau); ///
|
|
53
|
+
rho = first(rho); ///
|
|
54
|
+
|
|
55
|
+
return rho.transformOperation(tau);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { EMPTY_STRING } from "./constants";
|
|
4
|
+
|
|
5
|
+
export default function stringCompare(stringA, stringB) {
|
|
6
|
+
if ((stringA === EMPTY_STRING) && (stringB === EMPTY_STRING)) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (stringA === EMPTY_STRING) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (stringB === EMPTY_STRING) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const codePointA = stringA.codePointAt(0),
|
|
19
|
+
codePointB = stringB.codePointAt(0);
|
|
20
|
+
|
|
21
|
+
if (codePointA < codePointB) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (codePointA > codePointB) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const subStringA = stringA.substring(1),
|
|
30
|
+
subStringB = stringB.substring(1);
|
|
31
|
+
|
|
32
|
+
return stringCompare(subStringA, subStringB);
|
|
33
|
+
}
|
package/src/types.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { assert } = require("chai"),
|
|
4
|
+
{ DeleteOperation, InsertOperation, transformContent, generateOperations } = require("../../lib/main"); ///
|
|
5
|
+
|
|
6
|
+
const helpers = require("../helpers");
|
|
7
|
+
|
|
8
|
+
describe("transformContent", () => {
|
|
9
|
+
describe("the array of operations is of zero length", () => {
|
|
10
|
+
it("leaves the content unchanged", () => {
|
|
11
|
+
const content = helpers.content(),
|
|
12
|
+
operations = [],
|
|
13
|
+
transformedContent = transformContent(content, operations),
|
|
14
|
+
expectedContent = content; ///
|
|
15
|
+
|
|
16
|
+
assert.equal(transformedContent, expectedContent);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("the array of operations is contains a single delete operation", () => {
|
|
21
|
+
it("deletes the requisite characters", () => {
|
|
22
|
+
const content = helpers.content(),
|
|
23
|
+
deleteOperation = DeleteOperation.fromLengthAndPosition(2, 0),
|
|
24
|
+
operations = [
|
|
25
|
+
deleteOperation
|
|
26
|
+
],
|
|
27
|
+
transformedContent = transformContent(content, operations),
|
|
28
|
+
expectedContent = content.substring(2);
|
|
29
|
+
|
|
30
|
+
assert.equal(transformedContent, expectedContent);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("the array of operations is contains a single insert operation", () => {
|
|
35
|
+
it("inserts the requisite characters", () => {
|
|
36
|
+
const content = helpers.content(),
|
|
37
|
+
insertOperation = InsertOperation.fromStringAndPosition("xyz", 0),
|
|
38
|
+
operations = [
|
|
39
|
+
insertOperation
|
|
40
|
+
],
|
|
41
|
+
transformedContent = transformContent(content, operations),
|
|
42
|
+
expectedContent = `xyz${content}`;
|
|
43
|
+
|
|
44
|
+
assert.equal(transformedContent, expectedContent);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("the array of operations is generated", () => {
|
|
49
|
+
it("returns the result of applying those operations", () => {
|
|
50
|
+
const contentA = helpers.content(),
|
|
51
|
+
contentB = helpers.content(),
|
|
52
|
+
operations = generateOperations(contentA, contentB),
|
|
53
|
+
transformedContentA = transformContent(contentA, operations),
|
|
54
|
+
expectedContent = contentB; ///
|
|
55
|
+
|
|
56
|
+
assert.equal(transformedContentA, expectedContent);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|