@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 CHANGED
@@ -823,7 +823,11 @@
823
823
  data.status = notes[index].isWrappable;
824
824
  }
825
825
  var status = notes[index].isWrappable;
826
- if (status !== data.status){
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
- return wrap('ins', val, opIndex, dataPrefix, className);
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
@@ -2,7 +2,7 @@
2
2
  "name": "@matrixreq/htmldiff",
3
3
  "description": "Diff and markup HTML with <ins> and <del> tags",
4
4
  "companyname": "Matrix Requirements",
5
- "version": "1.1.0",
5
+ "version": "1.3.0",
6
6
  "keywords": [
7
7
  "diff",
8
8
  "html",
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')
@@ -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<iframe src="source.html"></iframe></del>' +
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
  });