@matrixreq/htmldiff 1.1.0 → 1.3.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/js/htmldiff.js +19 -4
- package/package.json +1 -1
- package/test/diff.spec.js +50 -1
- package/test/edge_cases.spec.js +10 -2
- package/test/render_operations.spec.js +3 -2
package/js/htmldiff.js
CHANGED
|
@@ -823,7 +823,11 @@
|
|
|
823
823
|
data.status = notes[index].isWrappable;
|
|
824
824
|
}
|
|
825
825
|
var status = notes[index].isWrappable;
|
|
826
|
-
|
|
826
|
+
|
|
827
|
+
// Handling atomic tags wrapping independently
|
|
828
|
+
// each atomic tag is wrapped with their own ins/del tags
|
|
829
|
+
var isAtomic = isStartOfAtomicTag(token);
|
|
830
|
+
if (status !== data.status || (isAtomic && index > data.lastIndex) || data.lastWasAtomic){
|
|
827
831
|
data.list.push({
|
|
828
832
|
isWrappable: data.status,
|
|
829
833
|
tokens: tokens.slice(data.lastIndex, index)
|
|
@@ -831,6 +835,9 @@
|
|
|
831
835
|
data.lastIndex = index;
|
|
832
836
|
data.status = status;
|
|
833
837
|
}
|
|
838
|
+
// tracking if the last token was an atomic tag
|
|
839
|
+
// if so then we break the segment and wrap them
|
|
840
|
+
data.lastWasAtomic = isAtomic;
|
|
834
841
|
if (index === tokens.length - 1){
|
|
835
842
|
data.list.push({
|
|
836
843
|
isWrappable: data.status,
|
|
@@ -838,7 +845,7 @@
|
|
|
838
845
|
});
|
|
839
846
|
}
|
|
840
847
|
return data;
|
|
841
|
-
}, {list: [], status: null, lastIndex: 0}).list;
|
|
848
|
+
}, {list: [], status: null, lastIndex: 0, lastWasAtomic: false}).list;
|
|
842
849
|
|
|
843
850
|
return segments.map(mapFn).join('');
|
|
844
851
|
};
|
|
@@ -910,7 +917,15 @@
|
|
|
910
917
|
var val = tokens.map(function(token){
|
|
911
918
|
return token.string;
|
|
912
919
|
});
|
|
913
|
-
|
|
920
|
+
|
|
921
|
+
const res = wrap('ins', val, opIndex, dataPrefix, className);
|
|
922
|
+
|
|
923
|
+
// handling inserted tags, see https://matrixreq.atlassian.net/browse/MATRIX-7876
|
|
924
|
+
if (/^<[^.\/]+?>$/.exec(res)) {
|
|
925
|
+
return `${res.slice(0, res.length - 1)} data-inserted="true">`;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
return res;
|
|
914
929
|
},
|
|
915
930
|
'delete': function(op, beforeTokens, afterTokens, opIndex, dataPrefix, className){
|
|
916
931
|
var tokens = beforeTokens.slice(op.startInBefore, op.endInBefore + 1);
|
|
@@ -919,7 +934,7 @@
|
|
|
919
934
|
});
|
|
920
935
|
const res = wrap("del", val, opIndex, dataPrefix, className);
|
|
921
936
|
|
|
922
|
-
// handling cases like deleted </p><p
|
|
937
|
+
// handling cases like deleted </p><p>, see https://matrixreq.atlassian.net/browse/MATRIX-7688
|
|
923
938
|
if (/^<\/.+?><.+?>$/.exec(res) && !res.includes("del")) {
|
|
924
939
|
return `<del>${val.slice(1, val.length - 1).join("")}</del>`;
|
|
925
940
|
}
|
package/package.json
CHANGED
package/test/diff.spec.js
CHANGED
|
@@ -206,5 +206,54 @@ describe('Diff', function(){
|
|
|
206
206
|
});
|
|
207
207
|
});
|
|
208
208
|
}); // describe('iframe Differences')
|
|
209
|
-
|
|
209
|
+
|
|
210
|
+
describe('Adjacent atomic tag combining', function(){
|
|
211
|
+
it('should wrap each inserted atomic tag independently when multiple are inserted', function(){
|
|
212
|
+
var result = cut('', '<iframe src="a.html"></iframe><iframe src="b.html"></iframe>');
|
|
213
|
+
expect(result).to.equal(
|
|
214
|
+
'<ins data-operation-index="0"><iframe src="a.html"></iframe></ins>' +
|
|
215
|
+
'<ins data-operation-index="0"><iframe src="b.html"></iframe></ins>'
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should wrap each deleted atomic tag independently when multiple are deleted', function(){
|
|
220
|
+
var result = cut('<iframe src="a.html"></iframe><iframe src="b.html"></iframe>', '');
|
|
221
|
+
expect(result).to.equal(
|
|
222
|
+
'<del data-operation-index="0"><iframe src="a.html"></iframe></del>' +
|
|
223
|
+
'<del data-operation-index="0"><iframe src="b.html"></iframe></del>'
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should not merge atomic tag with adjacent text in same ins/del', function(){
|
|
228
|
+
var result = cut('hello world', 'hello<iframe src="a.html"></iframe>world');
|
|
229
|
+
expect(result).to.equal('hello<ins data-operation-index="1"><iframe src="a.html"></iframe></ins>world');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should wrap inserted <b> content inside the tag, not in a standalone ins', function(){
|
|
233
|
+
var result = cut('', '<b>hello</b>');
|
|
234
|
+
expect(result).to.equal(
|
|
235
|
+
'<b data-diff-node="ins" data-operation-index="0"><ins data-operation-index="0">hello</ins></b>'
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should mark non-atomic container tags with data-diff-node rather than wrapping with ins', function(){
|
|
240
|
+
var result = cut('', '<li><div>content</div></li>');
|
|
241
|
+
expect(result).to.equal(
|
|
242
|
+
'<li data-diff-node="ins" data-operation-index="0">' +
|
|
243
|
+
'<div data-diff-node="ins" data-operation-index="0">' +
|
|
244
|
+
'<ins data-operation-index="0">content</ins>' +
|
|
245
|
+
'</div>' +
|
|
246
|
+
'</li>'
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should keep adjacent inserted <b> tags as separate segments', function(){
|
|
251
|
+
var result = cut('', '<b>hello</b><b>world</b>');
|
|
252
|
+
expect(result).to.equal(
|
|
253
|
+
'<b data-diff-node="ins" data-operation-index="0"><ins data-operation-index="0">hello</ins></b>' +
|
|
254
|
+
'<b data-diff-node="ins" data-operation-index="0"><ins data-operation-index="0">world</ins></b>'
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
}); // describe('Adjacent atomic tag combining')
|
|
258
|
+
|
|
210
259
|
}); // describe('Diff')
|
package/test/edge_cases.spec.js
CHANGED
|
@@ -29,5 +29,13 @@ BNXOISAN'FOSER3154
|
|
|
29
29
|
5SSDC</ins></div>`;
|
|
30
30
|
|
|
31
31
|
expect(diff(before, after)).to.eql(res);
|
|
32
|
-
})
|
|
33
|
-
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('adds data-inserted="true" to inserted tags', () => {
|
|
35
|
+
const before = `<div>some content</div>`;
|
|
36
|
+
const after = `<div><strong>some content</strong></div>`;
|
|
37
|
+
const res = `<div><strong data-inserted="true">some content</strong></div>`;
|
|
38
|
+
|
|
39
|
+
expect(diff(before, after)).to.eql(res);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -163,14 +163,15 @@ describe('renderOperations', function(){
|
|
|
163
163
|
'<ins data-operation-index="0">new<br/></ins> text');
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
-
it('should wrap atomic tags', function(){
|
|
166
|
+
it('should wrap atomic tags independently', function(){
|
|
167
167
|
var before = tokenize(['old', '<iframe src="source.html"></iframe>', ' ', 'text']);
|
|
168
168
|
var after = tokenize(['new', ' ', 'text']);
|
|
169
169
|
|
|
170
170
|
res = cut(before, after);
|
|
171
171
|
|
|
172
172
|
expect(res).to.equal(
|
|
173
|
-
'<del data-operation-index="0">old
|
|
173
|
+
'<del data-operation-index="0">old</del>' +
|
|
174
|
+
'<del data-operation-index="0"><iframe src="source.html"></iframe></del>' +
|
|
174
175
|
'<ins data-operation-index="0">new</ins> text');
|
|
175
176
|
});
|
|
176
177
|
});
|