From b88c91dac138533af5d7e4cfaf100a847a21423e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ne=C5=A1et=C5=99il?= Date: Thu, 5 Apr 2012 16:39:44 +0300 Subject: [PATCH 001/527] Added support for [`diff_match_patch`-style](http://code.google.com/p/google-diff-match-patch/wiki/API) output --- diff.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/diff.js b/diff.js index 6b7ce244..ebe33a57 100644 --- a/diff.js +++ b/diff.js @@ -279,6 +279,15 @@ var JsDiff = (function() { } return ret.join(""); } + + convertChangesToDMP: function(changes){ + var ret = [], change; + for ( var i = 0; i < changes.length; i++) { + change = changes[i]; + ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); + } + return ret; + } }; })(); From 6b72ccc103ce731275a721a4f6622113b4ed3e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Nes=CC=8Cetr=CC=8Cil?= Date: Thu, 5 Apr 2012 15:58:26 +0200 Subject: [PATCH 002/527] Added test for DMP formatting (and fixed a comma typo) --- diff.js | 2 +- test/diffTest.js | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/diff.js b/diff.js index ebe33a57..8e6a1c03 100644 --- a/diff.js +++ b/diff.js @@ -278,7 +278,7 @@ var JsDiff = (function() { } } return ret.join(""); - } + }, convertChangesToDMP: function(changes){ var ret = [], change; diff --git a/test/diffTest.js b/test/diffTest.js index 311b48b0..62d915d1 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -614,3 +614,12 @@ exports['Patch'] = function() { diffResult, "Patch same diffResult Value"); }; + +exports['convertToDMP'] = function() { + diffResult = diff.diffWords("New Value ", "New ValueMoreData "); + + assert.deepEqual( + [[0,'New '],[1,'ValueMoreData'],[-1,'Value'],[0,' ']], + diff.convertChangesToDMP(diffResult), + "DMP conversion of diffResult"); +}; From 5b5b6ac22c4a6fa4a02a50c384633714207d2d27 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 15 Apr 2012 19:12:05 -0500 Subject: [PATCH 003/527] 1.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aa0abb08..c437c157 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "1.0.2", + "version": "1.0.3", "description": "A javascript text diff implementation.", "keywords": [ "diff", From 9c1bc6a890ba0939ee939584ff18a2d58b5dd0d2 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Thu, 3 May 2012 12:26:21 -0500 Subject: [PATCH 004/527] Add travis config --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..ad74a3fe --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - 0.4 + - 0.6 + - 0.7 + From 14dda46358f355f52feed76047146cc156b4196f Mon Sep 17 00:00:00 2001 From: kpdecker Date: Thu, 3 May 2012 12:30:56 -0500 Subject: [PATCH 005/527] Add expressor to dev dependencies --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c437c157..31f52960 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,10 @@ }, "main": "./diff", "scripts": { - "test": "expresso test/*" + "test": "node_modules/.bin/expresso test/*" }, "dependencies": {}, - "devDependencies": {} -} \ No newline at end of file + "devDependencies": { + "expresso": "~0.9" + } +} From 205547ab0093ff2afb1f7d8e9311a41993a55e5d Mon Sep 17 00:00:00 2001 From: kpdecker Date: Thu, 3 May 2012 12:35:07 -0500 Subject: [PATCH 006/527] Add travis image to docs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2ed7ee10..15aab7ad 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # jsdiff +[![Build Status](https://secure.travis-ci.org/kpdecker/jsdiff.png)](http://travis-ci.org/kpdecker/jsdiff) + A javascript text differencing implementation. Based on the algorithm proposed in From 7c543475cafbb4bc1d53b486dcc03708adfe6903 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Tue, 7 Aug 2012 22:33:25 -0500 Subject: [PATCH 007/527] Add 0.8 travis tests --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad74a3fe..2a9b9e68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,4 @@ language: node_js node_js: - 0.4 - 0.6 - - 0.7 - + - 0.8 From 1242d7341d3ca2da299d8709a0e2d5063745d500 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Tue, 7 Aug 2012 22:33:57 -0500 Subject: [PATCH 008/527] gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..93f13619 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log From 2e6900696acf691317068a068bf295f64fe9ac1c Mon Sep 17 00:00:00 2001 From: "vprimachenko@ya.ru" Date: Fri, 31 Aug 2012 14:10:38 +0200 Subject: [PATCH 009/527] implement applyPath, fixes #4 --- diff.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/diff.js b/diff.js index 8e6a1c03..230e7f02 100644 --- a/diff.js +++ b/diff.js @@ -259,6 +259,52 @@ var JsDiff = (function() { return ret.join('\n') + '\n'; }, + applyPatch: function(oldStr, uniDiff) { + var diffstr = uniDiff.split("\n"); + var diff = []; + var remEOFNL = false, + addEOFNL = false; + for (var i = (diffstr[0][0]=="I"?4:0); i < diffstr.length; i++) { + if(diffstr[i][0] == "@") { + var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); + diff.unshift({ + start:meh[3], + oldlength:meh[2], + oldlines:[], + newlength:meh[4], + newlines:[] + }); + } else + if(diffstr[i][0] == "+") { + diff[0].newlines.push(diffstr[i].substr(1)); + } else + if(diffstr[i][0] == "-") { + diff[0].oldlines.push(diffstr[i].substr(1)); + } else + if(diffstr[i][0] == " ") { + diff[0].newlines.push(diffstr[i].substr(1)); + diff[0].oldlines.push(diffstr[i].substr(1)); + } else + if(diffstr[i][0] == "\\") { + if(diffstr[i-1][0] == "+") remEOFNL = true; + if(diffstr[i-1][0] == "-") addEOFNL = true; + } + }; + var str = oldStr.split("\n"); + for (var i = diff.length - 1; i >= 0; i--) { + var d = diff[i]; + for (var j = 0; j < d.oldlength; j++) { + if(str[d.start-1+j] != d.oldlines[j]) { + return false; + } + }; + Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); + } + if(remEOFNL) while(str[str.length-1]=="") str.pop(); + else if(addEOFNL) str.push(""); + return str.join("\n"); + }, + convertChangesToXML: function(changes){ var ret = []; for ( var i = 0; i < changes.length; i++) { From 863a581f69aa95b2837ae6e9e1ca23a640d40981 Mon Sep 17 00:00:00 2001 From: "vprimachenko@ya.ru" Date: Fri, 31 Aug 2012 14:11:04 +0200 Subject: [PATCH 010/527] add tests for applyPatch function --- test/diffTest.js | 265 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) diff --git a/test/diffTest.js b/test/diffTest.js index 62d915d1..a71f48ad 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -623,3 +623,268 @@ exports['convertToDMP'] = function() { diff.convertChangesToDMP(diffResult), "DMP conversion of diffResult"); }; + + + + +exports['lastLineChangedMergeTest'] = function() { + assert.equal( + 'line2\n'+ + 'line3\n'+ + 'line4\n'+ + 'line5\n', + diff.applyPatch( + 'line2\n'+ + 'line3\n'+ + 'line5\n' + , + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,3 +1,4 @@\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + ' line5\n') + ); + + assert.equal( + 'line2\nline3\nline4\nline5\n', + diff.applyPatch('line2\nline3\nline4\n','Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,3 +1,4 @@\n' + + ' line2\n' + + ' line3\n' + + ' line4\n' + + '+line5\n')); + + assert.equal('line1\nline2\nline3\nline44\n', + diff.applyPatch('line1\nline2\nline3\nline4\n', + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line44\n' + + '-line4\n')); + + assert.equal('line1\nline2\nline3\nline44\nline5\n', + diff.applyPatch('line1\nline2\nline3\nline4\n', + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,5 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line44\n' + + '+line5\n' + + '-line4\n')); +}; + +exports['EOFNLMergeTest'] = function() { + assert.equal('line1\nline2\nline3\nline4', + diff.applyPatch('line1\nline2\nline3\nline4\n', + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + '\\ No newline at end of file\n' + + '-line4\n')); + + assert.equal('line1\nline2\nline3\nline4\n', + diff.applyPatch('line1\nline2\nline3\nline4', + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + '-line4\n' + + '\\ No newline at end of file\n')); + + assert.equal('line1\nline2\nline3\nline4', + diff.applyPatch('line11\nline2\nline3\nline4', + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + '+line1\n' + + '-line11\n' + + ' line2\n' + + ' line3\n' + + ' line4\n' + + '\\ No newline at end of file\n')); + + assert.equal('line1\nline2\nline3\nline4\nline4\nline4\nline4', + diff.applyPatch('line11\nline2\nline3\nline4\nline4\nline4\nline4', + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,5 +1,5 @@\n' + + '+line1\n' + + '-line11\n' + + ' line2\n' + + ' line3\n' + + ' line4\n' + + ' line4\n')); +}; + +exports['PatchMerge'] = function() { + // Create patch + var oldFile = + "value\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "remove value\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "remove value\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "value\n" + + "context\n" + + "context"; + var newFile = + "new value\n" + + "new value 2\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "add value\n" + + "context\n" + + "context\n" + + "context\n" + + "context\n" + + "new value\n" + + "new value 2\n" + + "context\n" + + "context"; + var diffFile = + "Index: testFileName\n" + + "===================================================================\n" + + "--- testFileName\tOld Header\n" + + "+++ testFileName\tNew Header\n" + + "@@ -1,5 +1,6 @@\n" + + "+new value\n" + + "+new value 2\n" + + "-value\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + "@@ -7,9 +8,8 @@\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + "-remove value\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + "@@ -17,20 +17,21 @@\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + "-remove value\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + "+add value\n" + + " context\n" + + " context\n" + + " context\n" + + " context\n" + + "+new value\n" + + "+new value 2\n" + + "-value\n" + + " context\n" + + " context\n" + + "\\ No newline at end of file\n"; + + assert.equal( + newFile, + diff.applyPatch(oldFile, diffFile)); + + diffFile = + "Index: testFileName\n" + + "===================================================================\n" + + "--- testFileName\tOld Header\n" + + "+++ testFileName\tNew Header\n"; + assert.equal( + oldFile, + diff.applyPatch(oldFile, diffFile), + "Patch same diffResult Value"); + +}; From 617d3650c32192d4947fd5e2d0cc802e1d2480ec Mon Sep 17 00:00:00 2001 From: "vprimachenko@ya.ru" Date: Fri, 31 Aug 2012 14:28:20 +0200 Subject: [PATCH 011/527] add new functions to display --- index.html | 70 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/index.html b/index.html index ecb22fac..33bb403b 100644 --- a/index.html +++ b/index.html @@ -6,12 +6,13 @@ -

Diff

+ +
github.com/kpdecker/jsdiff @@ -20,7 +21,7 @@

Diff

restaurant aura -

+		
 	
 
 
@@ -31,31 +32,37 @@ 

Diff

var result = document.getElementById('result'); function changed() { - var diff = JsDiff[window.diffType](a.textContent, b.textContent); - var fragment = document.createDocumentFragment(); - for (var i=0; i < diff.length; i++) { + if(window.diffType == 'applyPatch') { + b.textContent = JsDiff.applyPatch(a.textContent, result.textContent); + } else if(window.diffType == 'createPatch') { + result.textContent = JsDiff.createPatch('filename',a.textContent, b.textContent,'left','right'); + } else { + var diff = JsDiff[window.diffType](a.textContent, b.textContent); + var fragment = document.createDocumentFragment(); + for (var i=0; i < diff.length; i++) { - if (diff[i].added && diff[i + 1] && diff[i + 1].removed) { - var swap = diff[i]; - diff[i] = diff[i + 1]; - diff[i + 1] = swap; - } + if (diff[i].added && diff[i + 1] && diff[i + 1].removed) { + var swap = diff[i]; + diff[i] = diff[i + 1]; + diff[i + 1] = swap; + } - var node; - if (diff[i].removed) { - node = document.createElement('del'); - node.appendChild(document.createTextNode(diff[i].value)); - } else if (diff[i].added) { - node = document.createElement('ins'); - node.appendChild(document.createTextNode(diff[i].value)); - } else { - node = document.createTextNode(diff[i].value); + var node; + if (diff[i].removed) { + node = document.createElement('del'); + node.appendChild(document.createTextNode(diff[i].value)); + } else if (diff[i].added) { + node = document.createElement('ins'); + node.appendChild(document.createTextNode(diff[i].value)); + } else { + node = document.createTextNode(diff[i].value); + } + fragment.appendChild(node); } - fragment.appendChild(node); - } - result.textContent = ''; - result.appendChild(fragment); + result.textContent = ''; + result.appendChild(fragment); + } } window.onload = function() { @@ -64,17 +71,26 @@

Diff

}; a.onpaste = a.onchange = -b.onpaste = b.onchange = changed; +b.onpaste = b.onchange = +result.onpaste = result.onchange = + changed; if ('oninput' in a) { - a.oninput = b.oninput = changed; + a.oninput = b.oninput = result.oninput = changed; } else { - a.onkeyup = b.onkeyup = changed; + a.onkeyup = b.onkeyup = result.onkeyup = changed; } function onDiffTypeChange(radio) { window.diffType = radio.value; - document.title = "Diff " + radio.value.slice(4); + document.title = "Diff " + radio.parentNode.innerText; + if(window.diffType == "applyPatch") { + b.removeAttribute('contenteditable'); + result.setAttribute('contenteditable','true'); + } else { + result.removeAttribute('contenteditable'); + b.setAttribute('contenteditable','true'); + } } var radio = document.getElementsByName('diff_type'); From b9822903a29ae0060ae6499bbb0ea7caa33b6954 Mon Sep 17 00:00:00 2001 From: "vprimachenko@ya.ru" Date: Fri, 31 Aug 2012 23:44:12 +0200 Subject: [PATCH 012/527] Document addition to API --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 15aab7ad..95bd8da2 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,11 @@ or * oldHeader : Additional information to include in the old file header * newHeader : Additional information to include in thew new file header +* JsDiff.applyPatch(oldStr, diffStr) + Applies a unified diff patch. + + Return a string containing new version of provided data. + * convertChangesToXML(changes) Converts a list of changes to a serialized XML format From c6ffaaff837db26c54a766ed32be1758b37cc9cd Mon Sep 17 00:00:00 2001 From: John Arthorne Date: Wed, 17 Oct 2012 17:05:18 -0300 Subject: [PATCH 013/527] Corrected location of license file --- diff.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diff.js b/diff.js index 8e6a1c03..48009843 100644 --- a/diff.js +++ b/diff.js @@ -1,4 +1,4 @@ -/* See license.txt for terms of usage */ +/* See LICENSE file for terms of use */ /* * Text diff implementation. From b220d3b997e770021bd459f5235d0f44b3004535 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 19 Oct 2012 19:30:47 -0500 Subject: [PATCH 014/527] Add comment --- diff.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/diff.js b/diff.js index 561606dc..5f03f815 100644 --- a/diff.js +++ b/diff.js @@ -325,7 +325,8 @@ var JsDiff = (function() { } return ret.join(""); }, - + + // See: http://code.google.com/p/google-diff-match-patch/wiki/API convertChangesToDMP: function(changes){ var ret = [], change; for ( var i = 0; i < changes.length; i++) { From c3d4a31ef2b6c08110201d391257695156665cd8 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 19 Oct 2012 19:30:56 -0500 Subject: [PATCH 015/527] jshint --- .jshintrc | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .jshintrc diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..b8ab2b7a --- /dev/null +++ b/.jshintrc @@ -0,0 +1,35 @@ +{ + "predef": [ + "describe", "it", "before", "after", "beforeEach", "afterEach" + ], + + "node": true, + "es5": true, + "esnext": true, + "browser": false, + "jquery": false, + + "bitwise": true, + "curly": true, + "eqeqeq": true, + "evil": false, + "forin": true, + "immed": true, + "latedef": false, + "laxbreak": true, + "newcap": true, + "noarg": true, + "noempty": false, + "nonew": true, + "plusplus": false, + "undef": true, + "strict": false, + "shadow": true, + "trailing": false, + "globalstrict": true, + "nonstandard": true, + "white": false, + "indent": 2, + + "onevar": false +} From 6209298de4653fcbc8f04e3e6cbdfcb23cabff93 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 19 Oct 2012 19:31:19 -0500 Subject: [PATCH 016/527] Update to mocha --- package.json | 5 +- test/applyPatch.js | 272 +++++++++++++ test/createPatch.js | 488 ++++++++++++++++++++++ test/diffTest.js | 972 +++++--------------------------------------- test/mocha.opts | 2 + 5 files changed, 860 insertions(+), 879 deletions(-) create mode 100644 test/applyPatch.js create mode 100644 test/createPatch.js create mode 100644 test/mocha.opts diff --git a/package.json b/package.json index 31f52960..a5046757 100644 --- a/package.json +++ b/package.json @@ -28,10 +28,11 @@ }, "main": "./diff", "scripts": { - "test": "node_modules/.bin/expresso test/*" + "test": "node_modules/.bin/mocha test/*.js" }, "dependencies": {}, "devDependencies": { - "expresso": "~0.9" + "mocha": "~1.6", + "should": "~1.2" } } diff --git a/test/applyPatch.js b/test/applyPatch.js new file mode 100644 index 00000000..a21ce9c7 --- /dev/null +++ b/test/applyPatch.js @@ -0,0 +1,272 @@ +var diff = require('../diff'); + +describe('#applyPatch', function() { + it('should apply patches that change the last line', function() { + diff.applyPatch( + 'line2\n'+ + 'line3\n'+ + 'line5\n', + + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,3 +1,4 @@\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + ' line5\n') + .should.equal( + 'line2\n' + + 'line3\n' + + 'line4\n' + + 'line5\n'); + + diff.applyPatch( + 'line2\nline3\nline4\n', + + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,3 +1,4 @@\n' + + ' line2\n' + + ' line3\n' + + ' line4\n' + + '+line5\n') + .should.equal('line2\nline3\nline4\nline5\n'); + + diff.applyPatch( + 'line1\nline2\nline3\nline4\n', + + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line44\n' + + '-line4\n') + .should.equal('line1\nline2\nline3\nline44\n'); + + diff.applyPatch( + 'line1\nline2\nline3\nline4\n', + + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,5 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line44\n' + + '+line5\n' + + '-line4\n') + .should.equal('line1\nline2\nline3\nline44\nline5\n'); + }); + + it('should merge EOFNL', function() { + diff.applyPatch( + 'line1\nline2\nline3\nline4\n', + + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + '\\ No newline at end of file\n' + + '-line4\n') + .should.equal('line1\nline2\nline3\nline4'); + + diff.applyPatch( + 'line1\nline2\nline3\nline4', + + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + '-line4\n' + + '\\ No newline at end of file\n') + .should.equal('line1\nline2\nline3\nline4\n'); + + diff.applyPatch( + 'line11\nline2\nline3\nline4', + + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + '+line1\n' + + '-line11\n' + + ' line2\n' + + ' line3\n' + + ' line4\n' + + '\\ No newline at end of file\n') + .should.equal('line1\nline2\nline3\nline4'); + + diff.applyPatch( + 'line11\nline2\nline3\nline4\nline4\nline4\nline4', + + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,5 +1,5 @@\n' + + '+line1\n' + + '-line11\n' + + ' line2\n' + + ' line3\n' + + ' line4\n' + + ' line4\n') + .should.equal('line1\nline2\nline3\nline4\nline4\nline4\nline4'); + }); + + it('should apply patches', function() { + // Create patch + var oldFile = + 'value\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'remove value\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'remove value\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'value\n' + + 'context\n' + + 'context'; + var newFile = + 'new value\n' + + 'new value 2\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'add value\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'new value\n' + + 'new value 2\n' + + 'context\n' + + 'context'; + var diffFile = + 'Index: testFileName\n' + + '===================================================================\n' + + '--- testFileName\tOld Header\n' + + '+++ testFileName\tNew Header\n' + + '@@ -1,5 +1,6 @@\n' + + '+new value\n' + + '+new value 2\n' + + '-value\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '@@ -7,9 +8,8 @@\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '-remove value\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '@@ -17,20 +17,21 @@\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '-remove value\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '+add value\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '+new value\n' + + '+new value 2\n' + + '-value\n' + + ' context\n' + + ' context\n' + + '\\ No newline at end of file\n'; + + diff.applyPatch(oldFile, diffFile).should.equal(newFile); + + diffFile = + 'Index: testFileName\n' + + '===================================================================\n' + + '--- testFileName\tOld Header\n' + + '+++ testFileName\tNew Header\n'; + diff.applyPatch(oldFile, diffFile).should.equal(oldFile); + }); +}); diff --git a/test/createPatch.js b/test/createPatch.js new file mode 100644 index 00000000..067f6e18 --- /dev/null +++ b/test/createPatch.js @@ -0,0 +1,488 @@ +const VERBOSE = false; + +var diff = require('../diff'); + +function log() { + VERBOSE && console.log.apply(console, arguments); +} + +describe('#createPatch', function() { + it('should handle files with the last line changed', function() { + diff.createPatch('test', 'line2\nline3\nline5\n', 'line2\nline3\nline4\nline5\n', 'header1', 'header2').should.equal( + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,3 +1,4 @@\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + ' line5\n'); + + diff.createPatch('test', 'line2\nline3\nline4\n', 'line2\nline3\nline4\nline5\n', 'header1', 'header2').should.equal( + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,3 +1,4 @@\n' + + ' line2\n' + + ' line3\n' + + ' line4\n' + + '+line5\n'); + + diff.createPatch('test', 'line1\nline2\nline3\nline4\n', 'line1\nline2\nline3\nline44\n', 'header1', 'header2').should.equal( + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line44\n' + + '-line4\n'); + + diff.createPatch('test', 'line1\nline2\nline3\nline4\n', 'line1\nline2\nline3\nline44\nline5\n', 'header1', 'header2').should.equal( + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,5 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line44\n' + + '+line5\n' + + '-line4\n'); + }); + + it('should output no newline at end of file message', function() { + diff.createPatch('test', 'line1\nline2\nline3\nline4\n', 'line1\nline2\nline3\nline4', 'header1', 'header2').should.equal( + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + '\\ No newline at end of file\n' + + '-line4\n'); + + diff.createPatch('test', 'line1\nline2\nline3\nline4', 'line1\nline2\nline3\nline4\n', 'header1', 'header2').should.equal( + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + ' line1\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + '-line4\n' + + '\\ No newline at end of file\n'); + + diff.createPatch('test', 'line11\nline2\nline3\nline4', 'line1\nline2\nline3\nline4', 'header1', 'header2').should.equal( + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,4 +1,4 @@\n' + + '+line1\n' + + '-line11\n' + + ' line2\n' + + ' line3\n' + + ' line4\n' + + '\\ No newline at end of file\n'); + + diff.createPatch('test', 'line11\nline2\nline3\nline4\nline4\nline4\nline4', 'line1\nline2\nline3\nline4\nline4\nline4\nline4', 'header1', 'header2').should.equal( + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,5 +1,5 @@\n' + + '+line1\n' + + '-line11\n' + + ' line2\n' + + ' line3\n' + + ' line4\n' + + ' line4\n'); + }); + + it('should diff large content blocks', function() { + var random = 42; + var mult = 134775813, range = Math.pow(2, 32); + function nextRandom() { + random = ((random * mult) + 1) % range; + return random; + } + var largeTest = '.hbh9asgiidc {ehaahc9:ses;bhg9hc:ses;idgaag-hi9aa:cdca;ihgd9gdgca-gdadg:ighchehgaci;ggghdg:edhciag;daagsada:ahhhiaa;ahai7:hgid;}.hbh9asgiidc.hchgihaa {ggghdg:hgid;}.igiidchbh9ah {ihgd9gdgca-hbh9a:gga(' + + 'hbh9ah/igiidcfhbh9ah.9hs);ihgd9gdgca-gaeahi:cd-gaeahi;7ah97i:7des;bhg9hc-gh97i:ses;ahai7:7des;ihgd9gdgca-edhhihdc:ses ses;}.igiidcfgde9 {ihgd9gdgca-edhhihdc:ses ses;}.bdghadaag .igiidcfgde9 {ihgd9gdgc' + + 'a-edhhihdc:-7des ses;}.hchgihaa .igiidcfgde9 {ihgd9gdgca-edhhihdc:-dses ses;}.igiidcfaaaaia {ihgd9gdgca-edhhihdc:-bdes ses;}.bdghadaag .igiidcfaaaaia {ihgd9gdgca-edhhihdc:-9sses ses;}.hchgihaa .igiidc' + + 'faaaaia {ihgd9gdgca-edhhihdc:-97des ses;}.igiidcfadacadha {ihgd9gdgca-edhhihdc:-9dses ses;}.bdghadaag .igiidcfadacadha {ihgd9gdgca-edhhihdc:-9bdes ses;}.hchgihaa .igiidcfadacadha {ihgd9gdgca-edhhihdc:' + + '-7sses ses;}.igiidcfabhha {ihgd9gdgca-edhhihdc:-77des ses;}.bdghadaag .igiidcfabhha {ihgd9gdgca-edhhihdc:-7dses ses;}.hchgihaa .igiidcfabhha {ihgd9gdgca-edhhihdc:-7bdes ses;}.igiidcfbdaa {ihgd9gdgca-e' + + 'dhhihdc:-d7des ses;}.bdghadaag .igiidcfbdaa {ihgd9gdgca-edhhihdc:-ddses ses;}.hchgihaa .igiidcfbdaa {ihgd9gdgca-edhhihdc:-dbdes ses;}.igiidcfcaasdaaag {ihgd9gdgca-edhhihdc:-abdes ses;}.bdghadaag .igii' + + 'dcfcaasdaaag {ihgd9gdgca-edhhihdc:-bsses ses;}.hchgihaa .igiidcfcaasdaaag {ihgd9gdgca-edhhihdc:-b7des ses;}.igiidcfgachba {ihgd9gdgca-edhhihdc:-dbdes ses;}.bdghadaag .igiidcfgachba {ihgd9gdgca-edhhihd' + + 'c:-9ssses ses;}.hchgihaa .igiidcfgachba {ihgd9gdgca-edhhihdc:-9s7des ses;}.igiidcfghh {ihgd9gdgca-edhhihdc:-9sdses ses;}.bdghadaag .igiidcfghh {ihgd9gdgca-edhhihdc:-9sbdes ses;}.hchgihaa .igiidcfghh {' + + 'ihgd9gdgca-edhhihdc:-99sses ses;}.igiidcfh7hga {ihgd9gdgca-edhhihdc:-97bdes ses;}.bdghadaag .igiidcfh7hga {ihgd9gdgca-edhhihdc:-9hsses ses;}.hchgihaa .igiidcfh7hga {ihgd9gdgca-edhhihdc:-9h7des ses;}.i' + + 'giidcfgeadha {ihgd9gdgca-edhhihdc:-9hdses ses;}.bdghadaag .igiidcfgeadha {ihgd9gdgca-edhhihdc:-9hbdes ses;}.hchgihaa .igiidcfgeadha {ihgd9gdgca-edhhihdc:-9gsses ses;}.igiidcfaaisdaaag {ihgd9gdgca-edhh' + + 'ihdc:-9dsses ses;}.bdghadaag .igiidcfaaisdaaag {ihgd9gdgca-edhhihdc:-9d7des ses;}.hchgihaa .igiidcfaaisdaaag {ihgd9gdgca-edhhihdc:-9ddses ses;}.igiidcfabei9ighh7 {ihgd9gdgca-edhhihdc:-hsses ses;}.bdgh' + + 'adaag .igiidcfabei9ighh7 {ihgd9gdgca-edhhihdc:-h7des ses;}.hchgihaa .igiidcfabei9ighh7 {ihgd9gdgca-edhhihdc:-hdses ses;}.igiidcfadgd {ihgd9gdgca-edhhihdc:-hbdes ses;}.bdghadaag .igiidcfadgd {ihgd9gdgc' + + 'a-edhhihdc:-gsses ses;}.hchgihaa .igiidcfadgd {ihgd9gdgca-edhhihdc:-g7des ses;}.igiidcfbhch9a {ihgd9gdgca-edhhihdc:-gdses ses;}.bdghadaag .igiidcfbhch9a {ihgd9gdgca-edhhihdc:-gbdes ses;}.hchgihaa .igi' + + 'idcfbhch9a {ihgd9gdgca-edhhihdc:-dsses ses;}.igiidcfcaaahdh {ihgd9gdgca-edhhihdc:-bdses ses;}.bdghadaag .igiidcfcaaahdh {ihgd9gdgca-edhhihdc:-bbdes ses;}.hchgihaa .igiidcfcaaahdh {ihgd9gdgca-edhhihdc:' + + '-csses ses;}.igiidcfhhaahahgg7gahgaih {ihgd9gdgca-edhhihdc:-97sses ses;}.bdghadaag .igiidcfhhaahahgg7gahgaih {ihgd9gdgca-edhhihdc:-977des ses;}.hchgihaa .igiidcfhhaahahgg7gahgaih {ihgd9gdgca-edhhihdc:' + + '-97dses ses;}.igiidcfhhaahahgg7 {ihgd9gdgca-edhhihdc:-997des ses;}.bdghadaag .igiidcfhhaahahgg7 {ihgd9gdgca-edhhihdc:-99dses ses;}.hchgihaa .igiidcfhhaahahgg7 {ihgd9gdgca-edhhihdc:-99bdes ses;}.igiidc' + + 'fcaaidddbhgd {ihgd9gdgca-edhhihdc:-asses ses;}.bdghadaag .igiidcfcaaidddbhgd {ihgd9gdgca-edhhihdc:-a7des ses;}.hchgihaa .igiidcfcaaidddbhgd {ihgd9gdgca-edhhihdc:-adses ses;}.igiidcfdeac {ihgd9gdgca-ed' + + 'hhihdc:-c7des ses;}.bdghadaag .igiidcfdeac {ihgd9gdgca-edhhihdc:-cdses ses;}.hchgihaa .igiidcfdeac {ihgd9gdgca-edhhihdc:-cbdes ses;}.igiidcfdaagaghia {ihgd9gdgca-edhhihdc:-9hdses ses;}.bdghadaag .igii' + + 'dcfdaagaghia {ihgd9gdgca-edhhihdc:-9hbdes ses;}.hchgihaa .igiidcfdaagaghia {ihgd9gdgca-edhhihdc:-9gsses ses;}.igiidcfahaa {ihgd9gdgca-edhhihdc:-9g7des ses;}.bdghadaag .igiidcfahaa {ihgd9gdgca-edhhihdc' + + ':-9gdses ses;}.hchgihaa .igiidcfahaa {ihgd9gdgca-edhhihdc:-9gbdes ses;}.igiidcfih9 {ihgd9gdgca-edhhihdc:-9dbdes ses;}.bdghadaag .igiidcfih9 {ihgd9gdgca-edhhihdc:-9asses ses;}.hchgihaa .igiidcfih9 {ihg' + + 'd9gdgca-edhhihdc:-9a7des ses;}.igiidcfhgihgghia {ihgd9gdgca-edhhihdc:-9b7des ses;}.bdghadaag .igiidcfhgihgghia {ihgd9gdgca-edhhihdc:-9bdses ses;}.hchgihaa .igiidcfhgihgghia {ihgd9gdgca-edhhihdc:-9bbde' + + 's ses;}.igiidcfhgeaggaaa {ihgd9gdgca-edhhihdc:-9csses ses;}.bdghadaag .igiidcfhgeaggaaa {ihgd9gdgca-edhhihdc:-9c7des ses;}.hchgihaa .igiidcfhgeaggaaa {ihgd9gdgca-edhhihdc:-9cdses ses;}.igiidcfggahiaha' + + 'ghah {ihgd9gdgca-edhhihdc:-9csses ses;}.bdghadaag .igiidcfggahiahaghah {ihgd9gdgca-edhhihdc:-9c7des ses;}.hchgihaa .igiidcfggahiahaghah {ihgd9gdgca-edhhihdc:-9cdses ses;}.igiidcfggahiagagdgaghia9dg9 {' + + 'ihgd9gdgca-edhhihdc:-9cbdes ses;}.bdghadaag .igiidcfggahiagagdgaghia9dg9 {ihgd9gdgca-edhhihdc:-9dsses ses;}.hchgihaa .igiidcfggahiagagdgaghia9dg9 {ihgd9gdgca-edhhihdc:-9d7des ses;}.igiidcfggahiagagdga' + + 'sdaaag {ihgd9gdgca-edhhihdc:-9ddses ses;}.bdghadaag .igiidcfggahiagagdgasdaaag {ihgd9gdgca-edhhihdc:-9dbdes ses;}.hchgihaa .igiidcfggahiagagdgasdaaag {ihgd9gdgca-edhhihdc:-7ssses ses;}.igiidcfggahiaga' + + 'gdga {ihgd9gdgca-edhhihdc:-7s7des ses;}.bdghadaag .igiidcfggahiagagdga {ihgd9gdgca-edhhihdc:-7sdses ses;}.hchgihaa .igiidcfggahiagagdga {ihgd9gdgca-edhhihdc:-7sbdes ses;}.igiidcfggahiae79hhghagagdga {' + + 'ihgd9gdgca-edhhihdc:-79bdes ses;}.bdghadaag .igiidcfggahiae79hhghagagdga {ihgd9gdgca-edhhihdc:-77sses ses;}.hchgihaa .igiidcfggahiae79hhghagagdga {ihgd9gdgca-edhhihdc:-777des ses;}.igiidcfbhdagagdga {' + + 'ihgd9gdgca-edhhihdc:-77dses ses;}.bdghadaag .igiidcfbhdagagdga {ihgd9gdgca-edhhihdc:-77bdes ses;}.hchgihaa .igiidcfbhdagagdga {ihgd9gdgca-edhhihdc:-7hsses ses;}.igiidcfggahiagagdgaaaghhdc {ihgd9gdgca-' + + 'edhhihdc:-79sses ses;}.bdghadaag .igiidcfggahiagagdgaaaghhdc {ihgd9gdgca-edhhihdc:-797des ses;}.hchgihaa .igiidcfggahiagagdgaaaghhdc {ihgd9gdgca-edhhihdc:-79dses ses;}.igiidcfgaea9abhha {ihgd9gdgca-ed' + + 'hhihdc:-7h7des ses;}.bdghadaag .igiidcfgaea9abhha {ihgd9gdgca-edhhihdc:-7hdses ses;}.hchgihaa .igiidcfgaea9abhha {ihgd9gdgca-edhhihdc:-7hbdes ses;}.igiidcfsdgahgaabhha {ihgd9gdgca-edhhihdc:-9b7des ses' + + ';}.bdghadaag .igiidcfsdgahgaabhha {ihgd9gdgca-edhhihdc:-9bdses ses;}.hchgihaa .igiidcfsdgahgaabhha {ihgd9gdgca-edhhihdc:-9bbdes ses;}.igiidcfegiihgdabhha {ihgd9gdgca-edhhihdc:-7d7des ses;}.bdghadaag .' + + 'igiidcfegiihgdabhha {ihgd9gdgca-edhhihdc:-7ddses ses;}.hchgihaa .igiidcfegiihgdabhha {ihgd9gdgca-edhhihdc:-7dbdes ses;}.igiidcfhhaa {ihgd9gdgca-edhhihdc:-7a7des ses;}.bdghadaag .igiidcfhhaa {ihgd9gdgc' + + 'a-edhhihdc:-7adses ses;}.hchgihaa .igiidcfhhaa {ihgd9gdgca-edhhihdc:-7abdes ses;}.igiidcfhhaa {ihgd9gdgca-edhhihdc:-7a7des ses;}.bdghadaag .igiidcfhhaa {ihgd9gdgca-edhhihdc:-7adses ses;}.hchgihaa .igi' + + 'idcfhhaa {ihgd9gdgca-edhhihdc:-7abdes ses;}.igiidcfaahi {ihgd9gdgca-edhhihdc:-h9shes ses;}.bdghadaag .igiidcfaahi {ihgd9gdgca-edhhihdc:-h97ces ses;}.hchgihaa .igiidcfaahi {ihgd9gdgca-edhhihdc:-h9dhes ' + + 'ses;}.igiidcfhaagdaa {ihgd9gdgca-edhhihdc:-h9bces ses;}.bdghadaag .igiidcfhaagdaa {ihgd9gdgca-edhhihdc:-h7shes ses;}.hchgihaa .igiidcfhaagdaa {ihgd9gdgca-edhhihdc:-h77ces ses;}.igiidcfcaagdcihgi {ihgd' + + '9gdgca-edhhihdc:-h7dhes ses;}.bdghadaag .igiidcfcaagdcihgi {ihgd9gdgca-edhhihdc:-h7bces ses;}.hchgihaa .igiidcfcaagdcihgi {ihgd9gdgca-edhhihdc:-hhshes ses;}.igiidcfcaa9gdge {ihgd9gdgca-edhhihdc:-hh7ce' + + 's ses;}.bdghadaag .igiidcfcaa9gdge {ihgd9gdgca-edhhihdc:-hhdhes ses;}.hchgihaa .igiidcfcaa9gdge {ihgd9gdgca-edhhihdc:-hhbces ses;}.igiidcf7aae {ihgd9gdgca-edhhihdc:-hgshes ses;}.bdghadaag .igiidcf7aae' + + ' {ihgd9gdgca-edhhihdc:-hg7ces ses;}.igiidcfagdebacg {ihgd9gdgca-edhhihdc:-hscces ses;ahai7:9ges;}.bdghadaag .igiidcfagdebacg {ihgd9gdgca-edhhihdc:-hsbges ses;}.hchgihaa .igiidcfagdebacg {ihgd9gdgca-ed' + + 'hhihdc:-hscces ses;}.igiidcfighchsaghc {ihgd9gdgca-edhhihdc:-7bbdes ses;}.bdghadaag .igiidcfighchsaghc {ihgd9gdgca-edhhihdc:-7csses ses;}.hchgihaa .igiidcfighchsaghc {ihgd9gdgca-edhhihdc:-7c7des ses;}' + + '.igiidcfhihgiadgdsada {ihgd9gdgca-edhhihdc:-hgdhes ses;}.bdghadaag .igiidcfhihgiadgdsada {ihgd9gdgca-edhhihdc:-hgbces ses;}.hchgihaa .igiidcfhihgiadgdsada {ihgd9gdgca-edhhihdc:-hdshes ses;}.igiidcfgas' + + 'gah7 {ihgd9gdgca-edhhihdc:-hd7ces ses;}.bdghadaag .igiidcfgasgah7 {ihgd9gdgca-edhhihdc:-hddhes ses;}.hchgihaa .igiidcfgasgah7 {ihgd9gdgca-edhhihdc:-hdbces ses;}.igiidcfgadhaagdeids {ihgd9gdgca-edhhihd' + + 'c:-hashes ses;}.bdghadaag .igiidcfgadhaagdeids {ihgd9gdgca-edhhihdc:-ha7ces ses;}.hchgihaa .igiidcfgadhaagdeids {ihgd9gdgca-edhhihdc:-hadhes ses;}.igiidcfdeacagdeids {ihgd9gdgca-edhhihdc:-habces ses;}' + + '.bdghadaag .igiidcfdeacagdeids {ihgd9gdgca-edhhihdc:-hbshes ses;}.hchgihaa .igiidcfdeacagdeids {ihgd9gdgca-edhhihdc:-hb7ces ses;}.igiidcfcaaagdeids {ihgd9gdgca-edhhihdc:-hbdges ses;}.bdghadaag .igiidc' + + 'fcaaagdeids {ihgd9gdgca-edhhihdc:-hbbdes ses;}.hchgihaa .igiidcfcaaagdeids {ihgd9gdgca-edhhihdc:-hcsges ses;}.igiidcfighh7gahidga {ihgd9gdgca-edhhihdc:-hc7des ses;}.bdghadaag .igiidcfighh7gahidga {ihg' + + 'd9gdgca-edhhihdc:-hcdges ses;}.hchgihaa .igiidcfighh7gahidga {ihgd9gdgca-edhhihdc:-hcbdes ses;}.hgdchbh9ah {ihgd9gdgca-hbh9a:gga(hbh9ah/hgdcfhbh9ah.9hs);ihgd9gdgca-gaeahi:cd-gaeahi;7ah97i:77es;ahca-7a' + + 'h97i:77es;bhg9hc-gh97i:ses;ahai7:7des;ihgd9gdgca-edhhihdc:ses ses;}.hgdcfidddbhgdggggaci {ihgd9gdgca-edhhihdc:-ses ses;}.hgdcfidddbhgdggggacif7daag {ihgd9gdgca-edhhihdc:-7des ses;}.hgdcfidddbhgdggggac' + + 'ifhchgihaa {ihgd9gdgca-edhhihdc:-dses ses;}.hgdcfasehca {ihgd9gdgca-edhhihdc:-bdes ses;}.hgdcfasehcaf7daag {ihgd9gdgca-edhhihdc:-9sses ses;}.hgdcfbhch9aggggaci {ihgd9gdgca-edhhihdc:-97des ses;}.hgdcfb' + + 'hch9aggggacif7daag {ihgd9gdgca-edhhihdc:-9dses ses;}.hgdcfbhch9aggggacifhchgihaa {ihgd9gdgca-edhhihdc:-9bges ses;}.hgdcfgasgah7ggggaci {ihgd9gdgca-edhhihdc:-7sses ses;}.hgdcfgasgah7ggggacifhchgihaa {i' + + 'hgd9gdgca-edhhihdc:-ah9es ses;}.hgdcfgasgah7ggggacif7daag {ihgd9gdgca-edhhihdc:-77des ses;}.hgdcfidddbhgdh {ahai7:7ses;ihgd9gdgca-edhhihdc:-7dses ses;}.hgdcfidddbhgdhf7daag {ahai7:7ses;ihgd9gdgca-edhh' + + 'ihdc:-7bses ses;}.hgdcfge {ahai7:7ses;ihgd9gdgca-edhhihdc:-7cdes ses;}.hgdcfgef7daag {ahai7:7ses;ihgd9gdgca-edhhihdc:-hsdes ses;}.hgdcfgefhchgihaa {ahai7:7ses;ihgd9gdgca-edhhihdc:-h7des ses;}.hgdcfhah' + + 'gg7 {ihgd9gdgca-edhhihdc:-hgdes ses;}.hgdcfhahgg7fhchgihaa {ihgd9gdgca-edhhihdc:-asaes ses;}.hgdcfhahgg7f7daag {ihgd9gdgca-edhhihdc:-hbges ses;}.hgdcfgdaaheha {ihgd9gdgca-edhhihdc:-gaaes ses;}.hgdcfgd' + + 'aahehaf7daag {ihgd9gdgca-edhhihdc:-gd9es ses;}.hgdcfaggdg {ihgd9gdgca-edhhihdc:-g9hes ses;7ah97i:9ges;}.hgdcf7aae {ihgd9gdgca-edhhihdc:-gh9es ses;7ah97i:9ces;}.hgdcfhcsd {ihgd9gdgca-edhhihdc:-ggdes se' + + 's;7ah97i:9ges;}.hgdcfgddisdaaag {ihgd9gdgca-edhhihdc:-gades ses;7ah97i:7ses;}.hgdcfihgd {ihgd9gdgca-edhhihdc:-d9aes ses;ahai7 :7ses;}.hgdcfihgdf7daag {ihgd9gdgca-edhhihdc:-dhaes ses;ahai7 :7ses;}.hgdc' + + 'fgadhafhahgg7 {ihgd9gdgca-edhhihdc:-ddaes ses;ahai7 :7des;}.hgdcfgadhafhahgg7f7daag {ihgd9gdgca-edhhihdc:-dc9es ses;ahai7:7des;}.gbhgdc {ihgd9gdgca-hbh9a:gga(hbh9ah/hgdchfaf.9hs);ihgd9gdgca-gaeahi:cd-' + + 'gaeahi;7ah97i:7ses;bhg9hc-gh97i:ses;aagihgha-hah9c:bhaaaa;ahai7:7ses;ihgd9gdgca-edhhihdc:ses ses;}.haghah {ihgd9gdgca-edhhihdc:-ses ses;}.gagfghia9dg9 {ihgd9gdgca-edhhihdc:-7ses ses;}.gagfsdaaag {ihgd' + + '9gdgca-edhhihdc:-gses ses;}.gagfsdaaagfhahhifsa {ihgd9gdgca-edhhihdc:-9gses ses;}.gagfsdaaagfahhedhaa {ihgd9gdgca-edhhihdc:-9ases ses;}.gagfsdaaagfsgd7ac {ihgd9gdgca-edhhihdc:-97ses ses;}.gagdgafe79hh' + + 'gha {ihgd9gdgca-edhhihdc:-9sses ses;}.gagdgafabhha {ihgd9gdgca-edhhihdc:-cses ses;}.gagdga {ihgd9gdgca-edhhihdc:-ases ses;}.gagdgafhahhifsa {ihgd9gdgca-edhhihdc:-7sses ses;}.gagdgafahhedhaa {ihgd9gdgc' + + 'a-edhhihdc:-77ses ses;}.gagdgafsgd7ac {ihgd9gdgca-edhhihdc:-9cses ses;}.gbhgdc {ihgd9gdgca-hbh9a:gga(hbh9ah/hgdchfaf.9hs);ihgd9gdgca-gaeahi:cd-gaeahi;7ah97i:7ses;bhg9hc-gh97i:ses;aagihgha-hah9c:bhaaaa' + + ';ahai7:7ses;ihgd9gdgca-edhhihdc:ses ses;}.haghah {ihgd9gdgca-edhhihdc:-ses ses;}.gagfghia9dg9 {ihgd9gdgca-edhhihdc:-7ses ses;}.gagfsdaaag {ihgd9gdgca-edhhihdc:-gses ses;}.gagfsdaaagfhahhifsa {ihgd9gdg' + + 'ca-edhhihdc:-9gses ses;}.gagfsdaaagfahhedhaa {ihgd9gdgca-edhhihdc:-9ases ses;}.gagfsdaaagfsgd7ac {ihgd9gdgca-edhhihdc:-97ses ses;}.gagdgafe79hhgha {ihgd9gdgca-edhhihdc:-9sses ses;}.gagdgafabhha {ihgd9' + + 'gdgca-edhhihdc:-cses ses;}.gagdga {ihgd9gdgca-edhhihdc:-ases ses;}.gagdgafhahhifsa {ihgd9gdgca-edhhihdc:-7sses ses;}.gagdgafahhedhaa {ihgd9gdgca-edhhihdc:-77ses ses;}.gagdgafsgd7ac {ihgd9gdgca-edhhihd' + + 'c:-9cses ses;}.shaahgdc {ihgd9gdgca-hbh9a:gga(hbh9ah/gdcghiachihdc.9hs);ihgd9gdgca-gaeahi:cd-gaeahi;7ah97i:7ses;bhg9hc-gh97i:ses;aagihgha-hah9c:bhaaaa;ahai7:7ses;ihgd9gdgca-edhhihdc:ses ses;}.shaa{ihg' + + 'd9gdgca-edhhihdc:ses ses;}.shaafhggdihi{ihgd9gdgca-edhhihdc:-7ses ses;}.shaafheeaa{ihgd9gdgca-edhhihdc:-gses ses;}.shaafheeahghihdc{ihgd9gdgca-edhhihdc:-ases ses;}.shaafhgahd{ihgd9gdgca-edhhihdc:-cses' + + ' ses;}.shaafasgaa{ihgd9gdgca-edhhihdc:-9sses ses;}.shaaf7iba{ihgd9gdgca-edhhihdc:-97ses ses;}.shaafhbh9a{ihgd9gdgca-edhhihdc:-9gses ses;}.shaafghah{ihgd9gdgca-edhhihdc:-9ases ses;}.shaafghahhgghei{ihg' + + 'd9gdgca-edhhihdc:-9cses ses;}.shaafbhadga{ihgd9gdgca-edhhihdc:-7sses ses;}.shaafeei{ihgd9gdgca-edhhihdc:-77ses ses;}.shaafegdgagi{ihgd9gdgca-edhhihdc:-7gses ses;}.shaaffghgdihba{ihgd9gdgca-edhhihdc:-7' + + 'ases ses;}.shaafiasi{ihgd9gdgca-edhhihdc:-7cses ses;}.shaafahaad{ihgd9gdgca-edhhihdc:-hsses ses;}.ahdh{ihgd9gdgca-edhhihdc:-hcses ses;}.shaafsba{ihgd9gdgca-edhhihdc:-h7ses ses;}.shaaf7he{ihgd9gdgca-ed' + + 'hhihdc:-hgses ses;}.sdaaag{ihgd9gdgca-edhhihdc:-hases ses;}.shaafagdeids {ihgd9gdgca-edhhihdc:-gsses ses;}.aaaaiafhbhaa{ihgd9gdgca-edhhihdc:-ggses ses;ggghdg:edhciag;}.egiahgfh7hga{ihgd9gdgca-edhhihdc' + + ':-gases ses;}.h7hga{ihgd9gdgca-edhhihdc:-gcses ses;}.eghahia{ihgd9gdgca-edhhihdc:-dsses ses;}.gddisdaaag{ihgd9gdgca-edhhihdc:-dcses ses;}.adacfhggda{ihgd9gdgca-edhhihdc:-a77es ses;ahai7:9ces;}.7aae{ih' + + 'gd9gdgca-edhhihdc:-agses ses;}.ighh7 {ihgd9gdgca-edhhihdc:-acses ses;}.hggdafgh97i {ihgd9gdgca-edhhihdc:-bs7es ses;ahai7:9ces;}.igahagggbih {a7hia-hehga:cdaghe;}.igahagggbih .igahagggbihfgddi {aagihgh' + + 'a-hah9c:bhaaaa;ehaahc9:s;}.igahagggbih .igahagggbihfhggda {aagihgha-hah9c:bhaaaa;ehaahc9-aasi:7es;}.igahagggbih .igahagggbihfahgchba {aagihgha-hah9c:bhaaaa;ehaahc9-aasi:7es;sdci-shbha9:aghha,aaaaaihgh' + + ',hhch-haghs;sdci-hh7a:9ab;}.igahagggbih .igahagggbihfahgchba hcegi {ihgd9gdgca:ighchehgaci;idgaag:cdca;ehaahc9:ses;bhg9hc:ses;ahai7:9ss%;}a {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;}a:ahcd {iasi' + + '-aagdghihdc:cdca;gdadg:#ssssss;}a:ahhhiaa {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:hgihaa {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:ahcd.iddaihgfigiidcfahcd {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:ahhhiaa.i' + + 'ddaihgfigiidcfahcd {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:hgihaa.iddaihgfigiidcfahcd {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:7daag.iddaihgfigiidcfahcd {iasi-aagdghihdc:cdca;gdadg:#ggbggg;ggghdg:edhc' + + 'iag;}a:ahcd.aaaaa9fiasi {iasi-aagdghihdc:cdca;gdadg:#gggggg;sdci-hh7a:dd%;}a:ahhhiaa.aaaaa9fiasi {iasi-aagdghihdc:cdca;gdadg:#gggggg;sdci-hh7a:dd%;}a:hgihaa.aaaaa9fiasi {iasi-aagdghihdc:gcaagahca;gdad' + + 'g:#ssssss;sdci-hh7a:dd%;}a:7daag.aaaaa9fiasi {iasi-aagdghihdc:cdca;gdadg:#ggbggg;sdci-hh7a:dd%;ggghdg:edhciag;}a:ahcd.aaaaa9fhgihaafhagihdc {iasi-aagdghihdc:cdca;gdadg:#gggggg;sdci-aah97i:idaa;sdci-hh' + + '7a:dd%;}a:ahhhiaa.aaaaa9fhgihaafhagihdc {iasi-aagdghihdc:cdca;gdadg:#gggggg;sdci-aah97i:idaa;sdci-hh7a:dd%;}a:hgihaa.aaaaa9fhgihaafhagihdc {iasi-aagdghihdc:gcaagahca;gdadg:#ssssss;sdci-aah97i:idaa;sdc' + + 'i-hh7a:dd%;}a:7daag.aaaaa9fhgihaafhagihdc {iasi-aagdghihdc:cdca;gdadg:#ggbggg;sdci-hh7a:dd%;sdci-aah97i:idaa;ggghdg:edhciag;}a:ahcd.aaaaa7fiasi {iasi-aagdghihdc:cdca;gdadg:#cdcdcd;}a:ahhhiaa.aaaaa7fia' + + 'si {iasi-aagdghihdc:cdca;gdadg:#cdcdcd;}a:hgihaa.aaaaa7fiasi {iasi-aagdghihdc:gcaagahca;gdadg:#cdcdcd;}a:7daag.aaaaa7fiasi {iasi-aagdghihdc:gcaagahca;gdadg:#ggbggg;ggghdg:edhciag;}a:ahcd.aaaaahfiasi {' + + 'iasi-aagdghihdc:cdca;gdadg:#cdcdcd;ahca-7ah97i:7ses;}a:ahhhiaa.aaaaahfiasi {iasi-aagdghihdc:cdca;gdadg:#cdcdcd;ahca-7ah97i:7ses;}a:hgihaa.aaaaahfiasi {iasi-aagdghihdc:gcaagahca;gdadg:#cdcdcd;ahca-7ah9' + + '7i:7ses;}a:7daag.aaaaahfiasi {iasi-aagdghihdc:cdca;gdadg:#ggbggg;ggghdg:edhciag;ahca-7ah97i:7ses;}a:ahcd.gcaagahca {gdadg:#ssssss;iasi-aagdghihdc:gcaagahca;ggghdg:edhciag;}a:ahhhiaa.gcaagahca {gdadg:#' + + 'ssssss;iasi-aagdghihdc:gcaagahca;}a:hgihaa.gcaagahca {iasi-aagdghihdc:gcaagahca;gdadg:#ssssss;}a:7daag.gcaagahca {iasi-aagdghihdc:gcaagahca;gdadg:#ggbggg;}.igahagggbih hcegi {ggghdg:edhciag;}.igahaggg' + + 'bih hcegi:7daag.hchgihaa {ggghdg:aashgai;gdadg:#adadad;}a:7daag,a.sdggh,a:ahcd.sdggh,a:ahhhiaa.sdggh,a:7daag.sdggh,a:hgihaa.sdggh,.ihiehhi igiidc:7daag,.igahagggbih hcegi:7daag {iasi-aagdghihdc:cdca;g' + + 'dadg:#ggbggg;ggghdg:edhciag;}h hb9 {idgaag:cdca;}ida9 {ehaahc9-gh97i:ses;ehaahc9-aasi:ses;ehaahc9-idiidb:ses;ehaahc9-ide:ses;bhg9hc-gh97i:ses;bhg9hc-aasi:ses;bhg9hc-ide:ses;bhg9hc-idiidb:ses;ihgd9gdgc' + + 'a-gdadg:#gggggg;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:bg%;}ihiaa {sdci-hh7a:9ab;}ihiaa ihiaa {sdci-hh7a:9ss%;}.aasigadhi {sadhi:aasi;}i {sdci-aah97i:idaa;}.igiidc {sdci-shbha9:sh7d' + + 'bh,aghha,aaaaaihgh,hhch-haghs;ehaahc9:des;iasi-hah9c:gaciag;sadhi:aasi;}.ihhhgsgiidc {ehaahc9:ses;bhg9hc:ses;idgaag-hi9aa:cdca;ihgd9gdgca-gdadg:ighchehgaci;ggghdg:edhciag;}.ihhhgsgiidc .igiidcfiasi {a' + + 'hca-7ah97i:7ses;sdci-hh7a:9ab;}.ihhhgsgiidc.bdghadaag .igiidcfiasi {gdadg:#ggbggg;}.ihhhgsgiidc.hchgihaa .igiidcfiasi {gdadg:#sgsgsg;ggghdg:aashgai;}.aasifigiidc {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hh' + + 'ch-haghs;sdci-hh7a:s.dab;ehaahc9-ide:des;ehaahc9-aasi:des;ehaahc9-gh97i:des;iasi-hah9c:gaciag;sadhi:aasi;bhc-ahai7:dses;}.gh97ifigiidc {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:s.dab;eh' + + 'aahc9:des;iasi-hah9c:gaciag;sadhi:gh97i;}.gdagbcf7ahaag {sdci-aah97i:idaa;sdci-hh7a:dd%;ahca-7ah97i:9des;gdadg:#ssssss;ehaahc9-ide:7es;ehaahc9-aasi:7es;ehaahc9-gh97i:7es;a7hia-hehga:cdaghe;iasi-hah9c:' + + 'aasi;}.hgdcfgdagbcf7ahaag {ehaahc9-aasi:7des;}.ahihfgda i7 {iasi-hah9c:aasi;sdci-aah97i:cdgbha;}.ahihfgda .cd7hih {iasi-hah9c:gaciag;}#idddbhgdfihiaa i7 h {ehaahc9-aasi:9ses;}.ahihfgda {7ah97i:7des;ih' + + 'gd9gdgca-gdadg:#gggggg;}.ahihfahhi9dagbc {ehaahc9-aasi:des;ehaahc9-gh97i:des;ahca-7ah97i:7des;}.agdeadac {idgaag-idiidb:9es iahgd;idgaag-gh97i:9es iahgd;idgaag-aasi:9es #9s9s9s;idgaag-ide:9es #9s9s' + + '9s;ahhhihahi9:7haaac;edhhihdc:hihdagia;ahai7:7sses;7-hcaas:9;ehaahc9:ses;ggghdg:edhciag;}.haaagi {ihgd9gdgca-gdadg:#g9gdga;sadhi:aasi;}haaagi.gghiaghh {sdci-hh7a:s.dab;ahai7:9cses;}.hbhaa {sdci-hh7a:d' + + 'd%;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;}.ihiaaaehgag {ahai7:7ses;}.iasihcegi {ehaahc9:des;bhg9hc:ses;aagihgha-hah9c:ide;}i7 {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-aah97i:idaa;' + + 'sdci-hh7a:dd%;}.ihiaa {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;sdci-aah97i:idaa;}.ahaga {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;}.aaaaafiddaihg {gaahg:idi7;a' + + 'hai7:hgid;7ah97i:gces;ihgd9gdgca-gdadg:#d77ahc;}.iddaihgfaasi {sadhi:aasi;ahai7:hgid;iasi-hah9c:gaciag;}.iddaihgfgh97i {sadhi:gh97i;ahai7:hgid;iasi-hah9c:gaciag;}aha.iddaihg {sadhi:aasi;}.iddaihgfid9f' + + 'aasi {sadhi:aasi;}.iddaihgfid9 {gdadg:#ssssss;sadhi:aasi;}.iddaihgfid9fgh97i {sadhi:aasi;}.iddaihgfigiidc {sadhi:aasi;iasi-hah9c:gaciag;ggghdg:edhciag;ehaahc9:ces ses ces ses;bhg9hc:ses;idgaag:ses cdc' + + 'a;ihgd9gdgca-gdadg:ighchehgaci;}.iddaihgfigiidc.hchgihaa {ggghdg:hgid;}.iddaihgfigiidcfhgdc {gaahg:gh97i;sadhi:cdca;bhg9hc-aasi:hgid;bhg9hc-gh97i:hgid;idgaag:ses;}.iddaihgfigiidc .igiidcfiasi,.iddaihg' + + 'figiidc hehc {ahheah9:iadgd;sdci-hh7a:s.cab;ehaahc9-aasi:des;ehaahc9-gh97i:des;sdci-shbha9:aghha,aaaaaihgh,hhch-haghs;ahca-7ah97i:s.cab;iasi-aagdghihdc:cdca;}.iddaihgfigiidc.bdghadaag .igiidcfiasi {gd' + + 'adg:#ggbggg;}.iddaihgfigiidc.hchgihaa .igiidcfiasi {gdadg:9gh9;}.iicfahih9dcigdafaasi {sadhi:aasi;ehaahc9-aasi:des;}.cdaghe,.cdaghe ia,.iaehiaa {a7hia-hehga:cdaghe;}ihiaa.cdaghe,ihiaa.cdaghe ig,ia.ag' + + 'he {a7hia-hehga:cdgbha;}.ihifhbhaafaasi {sadhi:aasi;}.ihifhbhaafgh97i {sadhi:aasi;}.ihifhbhaafid9 {sadhi:aasi;sdci-hh7a:dd%;}.ihifhbhaafid9 h {ahca-7ah97i:7ses;ehaahc9-ide:7es;}.ihifhbhaafdssfaasi {' + + 'sadhi:aasi;ahai7:7ses;ehaahc9-ide:7es;}.ihifhbhaafdssfgh97i {sadhi:aasi;ehaahc9-ide:7es;}.ihifhbhaafdssfid9 {sadhi:aasi;sdci-hh7a:dd%;ehaahc9-ide:7es;}aha.ihifhbhaa {idgaag-aasi:9es hdaha #7a77hh;id' + + 'gaag-gh97i:9es hdaha #7a77hh;idgaag-idiidb:9es hdaha #7a77hh;}aha.aaaaa9 {ahai7:hgid;gaahg:idi7;}aha.aaaaa9faasi {sadhi:aasi;ehaahc9-aasi:des;ehaahc9-ide:7es;ehaahc9-idiidb:7es;7ah97i:9ges;}aha.aaaaa' + + '9fgh97i {sadhi:gh97i;ehaahc9-aasi:des;ehaahc9-ide:7es;ehaahc9-idiidb:7es;7ah97i:9ges;}.aaaaa9fiasi {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;gdadg:#gggggg;sdci-hh7a:dd%;ehaahc9-aasi:7es;ehaahc9-g' + + 'h97i:7es;}.aaaaa9fad9hc {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;gdadg:#gggggg;sdci-hh7a:dd%;sdci-aah97i:idaa;ehaahc9-aasi:des;ehaahc9-gh97i:des;}.aaaaa9fad9dgi {bhg9hc-gh97i:7es;}aha.ad9d {sad' + + 'hi:aasi;}.aaaaa7fiasi {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;gdadg:#i7i7i7;sdci-aah97i:idaa;ehaahc9-aasi:des;ehaahc9-gh97i:des;ahca-7ah97i:hhes;}.aaaaafadghihdc {gaahg:idi7;ahai7:hgid;7ah97i:7' + + 'ces;ihgd9gdgca-gdadg:#d77ahc;edhhihdc:gaahihaa;}.adghihdcfaasi {bhg9hc-aasi:hes;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;}.aaaaafigahagggbi {gaahg:idi7;ahai7:hgid;7ah97i:7ces;ihgd9g' + + 'dgca-gdadg:#d77ahc;}.igahagggbifaasi {ahai7:hgid;bhg9hc-gh97i:7dses;}.igahagggbifgh97i {sadhi:gh97i;ahai7:hgid;ahai7:7gces;}.igahagggbifigiidc {sadhi:aasi;bhg9hc-aasi:ses;bhg9hc-gh97i:ses;bhg9hc-ide:9' + + 'es;ggghdg:edhciag;7ah97i:7ces;ihgd9gdgca-gdadg:ighchehgaci;idgaag:cdca;ehaahc9:ses;}.igahagggbifhbh9a {bhg9hc-aasi:hes;bhg9hc-gh97i:hes;bhg9hc-ide:9es;ggghdg:edhciag;7ah97i:7ces;idgaag:cdca;}.igahaggg' + + 'bifehi7fgdcihhcag {ihgd9gdgca-gdadg:a7hia;ahai7:hgid;ehaahc9-ide:ses;ehaahc9-idiidb:ses;}.igahagggbihcah {ahai7:bes;7ah97i:7ces;ihgd9gdgca-gdadg:#d77ahc;}.igahagggbifehi7figiidc {sadhi:aasi;bhg9hc-gh9' + + '7i:9es;bhg9hc-ide:9es;bhg9hc-aasi:ses;}.cdsdgaag {idgaag:cdca;}.igahagggbifehi7fahcd {idgaag:cdca;}.igahagggbifehi7 {sadhi:aasi;}.igahagggbifehi7faasi,.igahagggbifehi7fgh97i {sdci-shbha9:sh7dbh,aghha,' + + 'aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;}.igahagggbifehi7fiasi {iasi-aagdghihdc:cdca;gdadg:#ssssss;sdci-hh7a:s.dab;ahca-7ah97i:7ges;bhg9hc-aasi:hes;}.hahgg7fehi7fgdcihhcag7 {sadhi:gh97i;ihgd9gdgca-gdadg:a7' + + 'hia;gdadg:9gh9;ehaahc9-ide:ses;ehaahc9-idiidb:ses;7ah97i:7ges;idgaag:7es hdaha #d77ahc;}.hahgg7fhcegi7 {sadhi:aasi;ahai7:9gces;aagihgha-hah9c:bhaaaa;bhg9hc-aasi:9es;idgaag:7es hdaha a7hia;ihgd9gdgca-g' + + 'dadg:a7hia;}#hahgg7iasi {sdci-shbha9:sh7dbh,dacaah,hhch-haghs;sdci-hh7a:9ab;}#hahgg7sddaihg {sdci-hh7a:9ab;}.hahgg7fehi7figiidc {sadhi:aasi;bhg9hc-aasi:ses;bhg9hc-ide:9es;bhg9hc-gh97i:9es;ggghdg:edhci' + + 'ag;idgaag:cdca;}.ahgagidg9fehi7 {sadhi:aasi;}aha.ehi7faasi,aha.ehi7fgh97i {sadhi:aasi;sdci-aah97i:cdgbha;bhg9hc:ses;ahca-7ah97i:7des;ihgd9gdgca-gdadg:#d77ahc;}aha.ggggacifehi7figiidch {ihgd9gdgca-gd' + + 'adg:#d77ahc;sadhi:aasi;}aha.idddbhgdh {sadhi:aasi;ihgd9gdgca-gdadg:#d77ahc;7ah97i:7des;ahca-7ah97i:7des;}ga.idddbhgd {ihgd9gdgca-gdadg:#gsg9g9;}ah.idddbhgd {ahhi-hi9aa:cdca;ehaahc9-aasi:des;ehaahc9-id' + + 'e:7es;ehaahc9-idiidb:ses;ehaahc9-gh97i:des;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:s.dab;}ah.bhch9afidddbhgdh {ahhi-hi9aa:cdca;ehaahc9-aasi:des;ehaahc9-ide:7es;ehaahc9-idiidb:ses;ehaah' + + 'c9-gh97i:des;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;idgaag-ide:9es hdaha #7d7d7d;sdci-hh7a:s.dab;}aha.hahgg7 {sadhi:aasi;ahai7:hgid;ihgd9gdgca-gdadg:#d77ahc;}aha.9dd9aafhahgg7 {sadhi:gh97i;ihg' + + 'd9gdgca-gdadg:#d77ahc;}aha.hahgg7fids {sdci-aah97i:cdgbha;bhg9hc:ses;ahca-7ah97i:7des;sadhi:aasi;ihgd9gdgca-gdadg:#d77ahc;}.bacg {idgaag-idiidb:9es hdaha #cscscs;idgaag-gh97i:9es hdaha #cscscs;idgaag' + + '-ide:9es hdaha #gggggg;idgaag-aasi:9es hdaha #gggggg;ihgd9gdgca-gdadg:#gggggg;sdci-shbha9:aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:s.dab;ehaahc9:ses;}aha.shaafbhch9ag {ehaahc9:ses;ihgd9gdgca-gdadg:#s7sca' + + '7;idgaag:9es hdaha #sgsgsg;ahai7:9ss%;sadhi:aasi;}aha.sddiag {ahai7:hgid;sadhi:gh97i;ehaahc9-ide:7es;ehaahc9-idiidb:ses;}aha.bahhh9a {sadhi:aasi;sdci-aah97i:cdgbha;sdci-hh7a:dd%;gdadg:iahgd;bhg9hc:se' + + 's;ahca-7ah97i:77es;ahai7:hgid;ehaahc9:7es;}.gh97ifgahgd {idgaag:9es hdaha #9s9s9s;ihgd9gdgca-gdadg:#gsgggg;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:s.dab;ahhhihahi9:7haaac;edhhihdc:hihd' + + 'agia;ahai7:97ses;7-hcaas:9;ehaahc9:9ses;ggghdg:edhciag;}aha.ehgdehhi {ihgd9gdgca-gdadg:#gggggg;idgaag:9es hdaha #b99h7h;ehaahc9:des;}aha.ehgdehhiagah {gaahg:idi7;ehaahc9:9ses;}.bhch9afeh9a {ihgd9gdgca' + + '-gdadg:#ghghgh;}.gdbbaci {edhhihdc:gaahihaa;bhg9hc-ide:7es;ehaahc9:9ses;gaahg:gh97i;}.aaac9dbbaci {ihgd9gdgca:#gggggg;}.daa9dbbaci {ihgd9gdgca-gdadg:#ghghgh;idgaag-ide:#sgsgsg;idgaag-idiidb:#sgsgsg;}.' + + 'haa9dbbaciagah {bhg9hc-idiidb:des;}.gdbbaciagi7dg {sdci-hh7a:9ab;sdci-aah97i:idaa;ehaahc9-idiidb:des;}.gdbbacisasi {sdci-hh7a:dd%;ehaahc9-idiidb:9es;}.gdbbacishbahihbe {sdci-hh7a:s.cab;}.gdbbaci7aaaia' + + '97agdids {edhhihdc:hihdagia;gh97i:9ses;ide:9ses;ggghdg:edhciag;}aha.eh9afihiaa {gaahg:idi7;ahai7:hgid;}aha.hhaafchah9hihdc {bhg9hc-ide:ses;}ia.hhaafchah9hihdc {ihgd9gdgca-hbh9a:gga(hi9aah/hhaafchafid' + + '9.ge9);ihgd9gdgca-gaeahi:gaeahi-s;ahai7:9bdes;idgaag-gh97i:9es hdaha #sgsgsg;}.ihiehhi {bhg9hc-aasi:7ses;bhg9hc-ide:7ses;bhg9hc-idiidb:7ses;ehaahc9-aasi:9bes;}.ihiehhi igiidc {ihgd9gdgca-gdadg:ighcheh' + + 'gaci;idgaag:ses cdca;bhg9hc:ses;ehaahc9:ses;ggghdg:edhciag;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;iasi-hah9c:aasi;}.ihiehhi ah {ehaahc9-aasi:ses;ehaahc9-ide:ses;ehaahc9-gh97i:ses;' + + 'ehaahc9-idiidb:ges;ahhi-hi9aa-i9ea:cdca;sdci-hh7a:s.dab;}.aaihfhiabfhgihaa {ahhi-hi9aa-hbh9a:gga(hbh9ah/haaagiaafhgihdc.9hs);}.aaihfhiabfhgihaa igiidc {sdci-aah97i:idaa;}aha.gh97ifeh9afgdciaci {ahai7' + + ':hgid;ihgd9gdgca-gdadg:a7hia;}aha.hbhaafhgifiddaihg {ihgd9gdgca-gdadg:#9s9s9s;7ah97i:7des;ehaahc9-idiidb:des;}aha.hgifigiidc {sdci-hh7a:s.dab;ehaahc9-aasi:7es;ehaahc9-gh97i:7es;sadhi:aasi;iasi-hah9c:' + + 'gaciag;}aha.eh9afihiaa {bhg9hc:ses;}.eh9afihiaa hcegi,.eh9afihiaa hgibhi,.eh9afihiaa haaagi,.eh9afihiaa iasihgah,.sdgbfgdciaci iasihgah {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;}a' + + 'ha.eh9afahih {bhg9hc:9ses;sadhi:aasi;}aha.bdahafsdgb {idgaag:9es hdaha #a9a9a9;bhc-ahai7:gases;}aha.sdgbfgdciaci {bhg9hc:7as;gaahg:idi7;ahai7:hgid;}.sdgbfgdciaci h {sdci-hh7a:9ab;}aha.sdgbfihiaafihg' + + ' {idgaag-aasi:9es hdaha #b99h7h;idgaag-gh97i:9es hdaha #b99h7h;gaahg:idi7;}.sdgbfihiaafiasi {ahca-7ah97i:7aes;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;sdci-aah97i:idaa;gdadg:#hhhh' + + 'hh;sdci-aah97i:idaa;aagihgha-hah9c:ihhaahca;bhg9hc:ses;ehaahc9-aasi:des;}.iicfeh9a9dcigdafaasi {ehaahc9-ide:ges;sadhi:aasi;}.iicfeh9a9dcigdafgh97i {ehaahc9-ide:ges;sadhi:gh97i;}aha.sdgbfgdcigdah {gaa' + + 'hg:idi7;}aha.ghahdfigiidch {ehaahc9:des;}.aaig9sdgaag {idgaag:9es hdaha gaa;}.aaig9 {idgaag:9es hdaha #iii;}.haahcgaafhahgg7 {ahai7:hgid;gaahg:idi7;bhg9hc:9ses;ihgd9gdgca-gdadg:#ac7ahs;edhhihdc:gaah' + + 'ihaa;}aha.hahgg7fid9 {edhhihdc:gaahihaa;ihgd9gdgca-gdadg:#h9h9h9;idgaag-idiidb:7es hdaha #d9a9sb;}.hahgg7fgdcihhcag {ahai7:hgid;bhg9hc-aasi:7ses;ehaahc9-ide:9ses;}aha.hahgg7fgefaasifgdgcag {sadhi:aa' + + 'si;}aha.hahgg7fgefgh97ifgdgcag {sadhi:gh97i;}.hahgg7fihiaa {sdci-hh7a:dd%;sdci-aah97i:idaa;ehaahc9-aasi:des;ehaahc9-ide:des;}.hahgg7fgghiaghh {ehaahc9-aasi:9ses;ehaahc9-gh97i:ses;ehaahc9-ide:des;sdci-' + + 'hh7a:dd%;ihgd9gdgca-gdadg:#gggggg;idgaag:9es hdaha #sgsgsg;}aha.hahgg7figiidcfgda {ehaahc9-ide:des;idgaag-ide:9es hdaha #sgsgsg;sadhi:aasi;ahai7:9ss%;}aha.hahgg7figiidcfgdcihhcag {sadhi:gh97i;}aha.hah' + + 'gg7fhaa9ghiaghhfgda {idgaag-idiidb:9es hdaha #sgsgsg;sadhi:aasi;ahai7:9ss%;7ah97i:7des;}aha.hahgg7fgghiaghhfgdcihhcag {idgaag:7es hdaha #d9a9sb;ehaahc9:des;}.hgdcehiaafiasi {sadhi:aasi;bhg9hc:ses;7ah9' + + '7i:7des;ahca-7ah97i:7des;ehaahc9-gh97i:des;sdci-hh7a:dd%;sdci-aah97i:idaa;}aha.hgdc {ahai7:7des;7ah97i:7des;sadhi:aasi;}a:ahcd.hahgg79ghiaghhfiasi {iasi-aagdghihdc:gcaagahca;gdadg:#ssssss;sdci-hh7a' + + '{sadhi:aasi;ahhi-hi9aa:cdca;bhg9hc-ide:hes;}.7aae9dcihhcag {edhhihdc:hihdagia;ide:7es;gh97i:7es;bhg9hc:ses;ehaahc9:ses;idgaag:ses cdca;7-hcaas:g;ggghdg:edhciag;ahhhihahi9:hc7aghi;ihgd9gdgca-gdadg:ighc' + + 'hehgaci;}.7aae9dcihhcagaihihg {bhg9hc:ses;ehaahc9:ses;ggghdg:edhciag;ahhhihahi9:hc7aghi;}.9gh-bdagaa .7aae9dcihhcag {gh97i:ges;}.9gh-ahhad9 .9gh-ehcaa .7aae9dcihhcag {gh97i:7ses;}.sdgb7hih {sdci-hh7a' + + ':9ab;}ihiaa.cdihs9shiaa {idgaag:ses;}ihiaa.cdihs9shiaa .aasi9dagbc {iasi-hah9c:gh97i;aagihgha-hah9c:ide;sdci-aah97i:idaa;}ihiaa.cdihs9shiaa .gh97i9dagbc {iasi-hah9c:aasi;}ihiaa.hgeagihiaa {bhg9hc:s s ' + + 's 7es;}ihiaa.hgeagihiaa iida9 ig ia.geadhaf7ahaag {idgaag-ide:9es hdaha g9i(9g7,9g7,9g7);idgaag-gh97i:9es hdaha g9i(9g7,9g7,9g7);idgaag-idiidb:cdca;idgaag-aasi:9es hdaha g9i(9g7,9g7,9g7);ihgd9gdgca:g9' + + 'i(77d,77d,77d);ehaahc9:s;sdci-hh7a:s;7ah97i:hses;}ihiaa.hgeagihiaa iida9 ig ia.geadhaf7ahaag ihiaa {sdci-aah97i:ass;sdci-hh7a:9ab;ihgd9gdgca:ighchehgaci;gdadg:iahgd;ahai7:9ss%;bhg9hc:s;}ihiaa.hgeagihi' + + 'aa iida9 ig ia.gdcihhchfgeadha {bhg9hc:s;ehaahc9-aasi:s;}aha.geadha {bhg9hc:s;ehaahc9:s;daagsada:hgid;7ah97i:7hses;ahai7:asses;idgaag:9es hdaha g9i(9g7,9g7,9g7);ihgd9gdgca:a7hia;}aha.geadha ihiaa {7ah' + + '97i:9ss%;idgaag:cdca;}.geadha-daagaghia {}aha.geadha ihiaa iida9 ig {7ah97i:7des;}aha.geadha ihiaa iida9 ig#geadhafiahcdgda {7ah97i:hgid;}.geadhafsddiag {idgaag-ide:cdca;idgaag-gh97i:9es hdaha g9i(9g7' + + ',9g7,9g7);idgaag-idiidb:9es hdaha g9i(9g7,9g7,9g7);idgaag-aasi:9es hdaha g9i(9g7,9g7,9g7);7ah97i:hses;ihgd9gdgca:g9i(77d,77d,77d);}.iaehiaa {sdci-aah97i:idaa;}.ihiaaf7ahaag {sdci-aah97i:idaa;sdci-hh7a' + + ':9ab;gdadg:#ssssss;ahca-7ah97i:7hes;ahai7:9ss%;}.gh97ifihiaaf7ahaag {ahca-7ah97i:7hes;ahai7:hes;sdci-aah97i:idaa;gdadg:#ssssss;sdci-hh7a:9ab;}.aasifihiaaf7ahaag {ahca-7ah97i:7hes;ahai7:ges;sdci-aah97i' + + '{ihgd9gdgca-gdadg:#gggggg;ia77bgd-abdas:ses;ia77bgd-ehgs:ses;ia77bgd-shsshf:ses;ia77bgd-shi:hces;faadbg-abdas:des;faadbg-ehgs:des;faadbg-shi:hces;faadbg-shsshf:ses;ghgs-abah:9ss%;}.aabhcfhaiahiaa {' + + 'sdci-hh7a:9ab;}#shaahgdgb {edhhihdc:hihdagia;ide:ses;aasi:ses;ahai7:ses;7ah97i:ses;}.aaaaafhgiihih {ihgd9gdgca-gdadg:#gggggg;7ah97i:7ses;}.gdagbcf7ahaagfadc9 {sdci-hh7a:dd%;7ah97i:hses;gdadg:#sssss' + + 's;}aha.ihifhaafeagbhhhhdch,aha.ihifihhhgfeagbhhhhdch {sadhi:aasi;ahai7:hgid;iasi-hah9c:gaciag;}aha.eagbhhhhdchbhhaehhi {bhg9hc:ses;ehaahc9-aasi:7ses;ehaahc9-ide:des;ehaahc9-idiidb:des;}aha.eagbhhhhdch' + + 'bhhaehhi ga {bhg9hc:ses;ehaahc9:ses ses ses 9ses;}aha.hgdcehiaafiasi {sadhi:aasi;bhg9hc:ses;7ah97i:7des;ahca-7ah97i:7des;ehaahc9-gh97i:des;sdci-aah97i:idaa;}aha.hgdc {ahai7:7des;7ah97i:7des;sadhi:a' + + 'asi;}a.ahia {gdadg:#ggssss;sdci-aah97i:cdgbha;}a:ahhhiaa.ahia {gdadg:#ggssss;sdci-aah97i:cdgbha;}a:7daag.ahia {gdadg:#ggssss;sdci-aah97i:cdgbha;}a:hgihaa.ahia {gdadg:#ggssss;sdci-aah97i:cdgbha;}.ahia' + + '{9heha:#ggssss;ghgs-hhbdas:idaa;}.gh97ifiddaihg {sadhi:gh97i;ehaahc9-gh97i:des;}aha.asfhahgg7fid9 {bhg9hc-aasi:hes;bhg9hc-gh97i:hes;bhg9hc-idiidb:9ses;idgaag-idiidb:7es hdaha #d9a9sb;}.asfhahgg7 {ah' + + 'ai7:hgid;gaahg:idi7;7ah97i:9hses;ehaahc9-ide:ses;bhg9hc:ses;ihgd9gdgca-gdadg:#d77ahc;}aha.asfhahgg7faasiaca {sadhi:aasi;}aha.asfhahgg7fgh97iaca {sadhi:gh97i;}.ahih h {sdci-hh7a:9ab;}.asfhahgg7fgdcihh' + + 'cag {sadhi:aasi;bhg9hc-ide:des;}ia.asfhahgg7fiasi {sdci-hh7a:s.dab;}.asfhahgg7fiasi hcegi {sdci-hh7a:s.dab;}.asfhahgg7fiasi haaagi {sdci-hh7a:s.dab;}ia.asfihiaaaehgag {ahai7:9ses;}.asfihiaa9aaa {ehaah' + + 'c9-aasi:9ses;ehaahc9-gh97i:des;}aha.hgihdcfhiae {ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:des;ehaahc9-gh97i:9ses;bhg9hc-gh97i:9des;ihgd9gdgca-gdadg:a7hia;idgaag-idiidb:7es dgihai;idgaag-gh97i:7' + + 'es dgihai;ahai7:dd%;}aha.aaghhhdcfhiae {ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:des;ehaahc9-gh97i:des;ihgd9gdgca-gdadg:a7hia;idgaag-idiidb:7es dgihai;idgaag-gh97i:7es dgihai;ahai7:dd%;}aha.i7' + + 'ac {ihgd9gdgca:#hhgggh;ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:7es;ehaahc9-gh97i:des;idgaag-idiidb:7es dgihai;idgaag-gh97i:7es dgihai;ahai7:dd%;}aha.i7acfida9 {ehaahc9-ide:des;ehaahc9-idiidb:' + + 'des;ehaahc9-aasi:9ses;ehaahc9-gh97i:des;ahai7:dd%;}aha.aaha {ihgd9gdgca:#hhgggh;ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:7es;ehaahc9-gh97i:des;idgaag-idiidb:7es dgihai;idgaag-gh97i:7es dgihai;a' + + 'hai7:dd%;}aha.aahafida9 {ehaahc9-ide:des;ehaahc9-idiidb:des;ehaahc9-aasi:9ses;ehaahc9-gh97i:des;ahai7:dd%;}aha.haafhiae {ehaahc9-ide:9ses;ehaahc9-idiidb:9ses;iasi-hah9c:gaciag;}aha.casifhiae {ihgd9gd' + + 'gca:#hhgggh;ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:7es;ehaahc9-gh97i:des;idgaag-idiidb:7es dgihai;idgaag-gh97i:7es dgihai;ahai7:dd%;}aha.hsfida9 {ehaahc9-ide:des;ehaahc9-idiidb:des;ehaahc9-aa' + + 'si:9ses;ehaahc9-gh97i:des;ahai7:dd%;}aha.ehghbaiagh {ehaahc9-ide:des;ehaahc9-idiidb:des;ehaahc9-aasi:9ses;ehaahc9-gh97i:des;ahai7:dd%;}aha.eh9afida9 {ehaahc9-ide:9ses;ehaahc9-aasi:9ses;}aha.h7dgifhgi' + + 'hdcfchba {sdci-hh7a:9ss%;}aha.h7dgifaaghhhdcfchba {sdci-hh7a:9ss%;}.gahaagfcdihshghihdc {ahheah9:cdca;}.gahaaghaabaci {edhhihdc:hihdagia;ide:-9es;aasi:-9ses;7ah97i:9es;ahca-7ah97i:9es;idgaag:cdca;eh' + + 'aahc9:ses;bhg9hc:ses;ahai7:9es;daagsada:7haaac;}ihiaa.dig i7 aha {edhhihdc:gaahihaa;ide:-9ses;7ah97i:9es;daagsada:7haaac;}.idaaehiaah ahiaa,.ahiaa {sdci-aah97i:idaa;sdci-hh7a:9ab;}shaaahai.ghahddgdge ' + + '{ehaahc9:ses;idgaag:cdca;iasi-hah9c:aasi;}shaaahai.ghahddgdge aa9aca {ehaahc9:ses;}shaaahai.ghahddgdge aha {ehaahc9-aasi:7ses;ehaahc9-idiidb:des;}shaaahai.ghahddgdge ahiaa {ahheah9:iadgd;bhg9hc-aasi:9' + + '79.ihiaa,77.ihiaa {bhg9hc:ses;}.9gh-bdagaa,.ahdhfihi7haa9dciaci,.hahgg7fid9,.haahcgaafhahgg7 {7ddb:9;}.eghcghehaehhi {ahhi-hi9aa-i9ea:cdca;}.ihiehhi igiidc,.aaaaa7fbacg igiidc,.aaaaahfbacg igiidc,.ihh' + + 'hgsgiidc,.iddaihgfigiidc,.igahagggbihfahgchba hcegi,.shaaa9hiabsgdahagsgaa hcegi {ahai7:hgid;daagsada:ahhhiaa;}.gbaiaeaahahc9h {ehaahc9-aasi:7ses;sdci-aah97i:idaa;sdci-hh7a:9ss%;}.9ghaids {ahai7:9ss%;' + + 'sdci-shbha9:sh7dbh,dacaah,hhch-haghs;sdci-hh7a:9ss%;ehaahc9:ses;bhg9hc:ses;idgaag-idiidb:ses;idgaag-aasi:ses;idgaag-gh97i:ses;ihgd9gdgca-gdadg:#gshghg;daagsada:7haaac;}.digids hcegi {ggghdg:aashgai;}.' + + 'bgaihehhi {sadhi:aasi;ahheah9:hcahca;ehaahc9:s 7ses;bhg9hc:s;}.bgaihehhi .cd7hagah {ahhi-hi9aa:cdca;}.s9fgaciagidhhihdc {bhg9hc:ses hgid;}.ahdhfeh9a9dciaci {ihgd9gdgca-hbh9a:gga(hi9aah/ahdhfid9.ge9);i' + + 'hgd9gdgca-gaeahi:gaeahi-9;}shca9haghaaaaagihdc,.asfhahgg7fid9,#aaaaa7,.aaaaa7fihifshaafaasi,.iddaihg,.iddaihgfid9,.aaaaa7fihih,.aaaaa7fbacg .ihifbha,.aaaaa7fihifshaafgh97i,aha.aaaaa7fhgihaa .ihifbha,.' + + 'ahdhfaaaaa9,.gdagbcf7ahaagfadc9,.ahdhfeh9ashiaa,.ahdhfeh9agddiag,.igahagggbifehi7fgdcihhcag,.ggggacifehi7figiidch,.hahgg7fids,#aaaaah,.aaaaahfchah9hihdc,.aaaaahfhgihaa .ihifbha,.ahdhfihi .ihisd9,.ahdh' + + 'fihi .haaagiaa .ihisd9,.ahdhfihighaa,#aaaaa9,.aaaaa9,aha.9ghaids .s7ag,aha.9ghaids .sig,aha.9ghaids .sig ia,.gdagbcf7ahaag,.ihifhbhaafid9,.ihifhbhaafdssfid9,.eagbfihifhaaagiaa .ihifhbhaafid9,.ahdhfaaa' + + 'aah {ihgd9gdgca-hbh9a:gga(heghiahfgfd.ec9);ihgd9gdgca-gaeahi:gaeahi-s;}shca9haghaaaaagihdc {ihgd9gdgca-edhhihdc:-ses -ses;7ah97i:9gces;}.asfhahgg7fid9 {ihgd9gdgca-edhhihdc:-ses -9gces;7ah97i:97ses;}#a' + + 'aaaa7,.aaaaa7fihifshaafaasi {ihgd9gdgca-edhhihdc:-ses -7aces;7ah97i:gces;}.iddaihg,.iddaihgfid9 {ihgd9gdgca-edhhihdc:-ses -h9aes;7ah97i:gces;}a.aaaaa7fihih,.aaaaa7fbacg .ihifbha,.aaaaa7fihifshaafgh97i' + + ' {ihgd9gdgca-edhhihdc:-ses -hages;7ah97i:hhes;}aha.aaaaa7fhgihaa .ihifbha {ihgd9gdgca-edhhihdc:-ses -hdbes;7ah97i:hhes;}.ahdhfaaaaa9 {ihgd9gdgca-edhhihdc:-ses -ghses;7ah97i:h7es;}.gdagbcf7ahaagfadc9 {' + + 'ihgd9gdgca-edhhihdc:-ses -ga7es;7ah97i:hses;}.ahdhfeh9ashiaa {ihgd9gdgca-edhhihdc:-ses -gd7es;7ah97i:7des;}.hahgg7fgefgh97ifgdgcag {ihgd9gdgca-edhhihdc:-h9es -99ges;ahai7:9ses;7ah97i:ces;}.hahgg7fid9 ' + + '{ihgd9gdgca-hbh9a:gga(hi9aah/haahcgaafehcaafid9.ge9);ihgd9gdgca-gaeahi:gaeahi-s;}.eh9afihiaa,aha.eh9afgdcigdah,aha.sdgbfgdcigdah,aha.9gh-ahhad9 aha.9gh-bdagaa aha.si,aha.gh97ifeh9afgdciaci aha.9gh-bda' + + 'gaa aha.7a,aha.ggahiahh7hga9dciaciagah aha.9gh-bdagaa aha.si,aha.cdihs99dciaciagah aha.9gh-bdagaa aha.si,aha.agdesdshh7hga9dciaciagah aha.9gh-bdagaa aha.si,.sdgbfihiaafihg,aha.9gh-ahhad9 aha.9gh-bdaga' + + 'a aha.7a,.hcaggaacfihiaashg,.ihiaaf7ahaag,.gh97ifihiaaf7ahaag,.aasifihiaaf7ahaag,.igiidcfid9,.hchgihaa .igiidcfid9 {ihgd9gdgca-hbh9a:gga(ihhaaeghiahfgfd.ec9);ihgd9gdgca-gaeahi:gaeahi-s;}.eh9afihiaa,ah' + + 'a.eh9afgdcigdah,aha.sdgbfgdcigdah,aha.9gh-ahhad9 aha.9gh-bdagaa aha.si,aha.gh97ifeh9afgdciaci aha.9gh-bdagaa aha.7a,aha.ggahiahh7hga9dciaciagah aha.9gh-bdagaa aha.si,aha.cdihs99dciaciagah aha.9gh-bdag' + + 'aa aha.si,aha.agdesdshh7hga9dciaciagah aha.9gh-bdagaa aha.si {ihgd9gdgca-edhhihdc:-ses -ses;7ah97i:7des;}.sdgbfihiaafihg,aha.9gh-ahhad9 aha.9gh-bdagaa aha.7a,.hcaggaacfihiaashg,.ihiaaf7ahaag,.gh97ifih' + + 'iaaf7ahaag,.aasifihiaaf7ahaag {ihgd9gdgca-edhhihdc:-ses -7des;7ah97i:7aes;}.igiidcfid9 {edhhihdc:gaahihaa;bhg9hc-gh97i:7es;ehaahc9-gh97i:9hes;ehaahc9-aasi:9hes;bhg9hc-aasi:7es;a7hia-hehga:cdaghe;ihgd9' + + 'gdgca-edhhihdc:-ses -ddes;7ah97i:77es;}.hchgihaa .igiidcfid9 {ihgd9gdgca-edhhihdc:-ses -bbes;7ah97i:77es;}.aashgaifad9d,.hchgihaa .igiidcfaasi,.igiidcfaasi,.igiidcfgh97i,.hchgihaa .igiidcfgh97i,.hgdcf' + + 'hcsd,.hgdcfaggdg {ihgd9gdgca-hbh9a:gga(ihhaaeghiahfghgh.ec9);ihgd9gdgca-gaeahi:cd-gaeahi;}.aashgaifad9d {ihgd9gdgca-edhhihdc:-ses -ses;ahai7:c7es;7ah97i:hdes;}.hchgihaa .igiidcfaasi {ihgd9gdgca-edhhih' + + 'dc:-c7es -ses;ahai7:9ges;7ah97i:77es;}.igiidcfaasi {edhhihdc:hihdagia;ide:ses;aasi:-7es;ihgd9gdgca-edhhihdc:-daes -ses;ahai7:9hes;7ah97i:77es;}.igiidcfgh97i {edhhihdc:hihdagia;ide:ses;gh97i:-7es;ihgd9' + + 'gdgca-edhhihdc:-9sdes -ses;ahai7:9hes;7ah97i:77es;}.hchgihaa .igiidcfgh97i {ihgd9gdgca-edhhihdc:-977es -ses;ahai7:9hes;7ah97i:77es;}.hgdcfhcsd {ihgd9gdgca-edhhihdc:-c7es -77es;ahai7:9ges;7ah97i:9des;}' + + '.hgdcfaggdg {ihgd9gdgca-edhhihdc:-daes -77es;ahai7:9ges;7ah97i:9ges;}'; + + var largeNewValue = largeTest, + len = largeTest.length, + count = nextRandom() % 20, + removeBound = len-(count*100), + logData = []; + for (; count > 0; count--) { + var removePos = nextRandom() % removeBound, + removeLength = 1+nextRandom()%100; + logData.push('(' + removePos + ', ' + removeLength + ')'); + largeNewValue = largeNewValue.substring(0, removePos) + + largeNewValue.substring(removePos + removeLength); + } + log('len: ' + len + ' count: ' + count + ' removed ( ' + logData.join(', ') + ' )'); + + var diffResult = diff.diffWords(largeTest, largeNewValue); + log('diffResult length: ' + diffResult.length); + var removeCount = 0; + var removeChanges = [], addChanges = []; + for (var i = 0; i < diffResult.length; i++) { + if (diffResult[i].removed) { + log('remove Change ' + i, diffResult[i]); + removeChanges.push(diffResult[i].value); + } else if (diffResult[i].added) { + log('add Change ' + i, diffResult[i]); + addChanges.push(diffResult[i].value); + } else { + log('no Change ' + i, diffResult[i]); + removeChanges.push(diffResult[i].value); + addChanges.push(diffResult[i].value); + } + } + + log('diffResult remove length: ' + removeCount); + function stripSpace(value) { + return value.replace(/s+/g, ''); + } + stripSpace(removeChanges.join('')).should.equal(stripSpace(largeTest)); + stripSpace(addChanges.join('')).should.equal(stripSpace(largeNewValue)); + }); + + // Create patch + var oldFile = + 'value\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'remove value\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'remove value\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'value\n' + + 'context\n' + + 'context'; + var newFile = + 'new value\n' + + 'new value 2\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'add value\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'context\n' + + 'new value\n' + + 'new value 2\n' + + 'context\n' + + 'context'; + + it('should generate a patch', function() { + var expectedResult = + 'Index: testFileName\n' + + '===================================================================\n' + + '--- testFileName\tOld Header\n' + + '+++ testFileName\tNew Header\n' + + '@@ -1,5 +1,6 @@\n' + + '+new value\n' + + '+new value 2\n' + + '-value\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '@@ -7,9 +8,8 @@\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '-remove value\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '@@ -17,20 +17,21 @@\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '-remove value\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '+add value\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '+new value\n' + + '+new value 2\n' + + '-value\n' + + ' context\n' + + ' context\n' + + '\\ No newline at end of file\n'; + + var diffResult = diff.createPatch('testFileName', oldFile, newFile, 'Old Header', 'New Header'); + diffResult.should.equal(expectedResult); + }); + + it('should output headers only for identical files', function() { + var expectedResult = + 'Index: testFileName\n' + + '===================================================================\n' + + '--- testFileName\tOld Header\n' + + '+++ testFileName\tNew Header\n'; + var diffResult = diff.createPatch('testFileName', oldFile, oldFile, 'Old Header', 'New Header'); + diffResult.should.equal(expectedResult); + }); +}); diff --git a/test/diffTest.js b/test/diffTest.js index a71f48ad..91c0765b 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -7,884 +7,102 @@ function log() { VERBOSE && console.log.apply(console, arguments); } -exports['Whitespace diff'] = function() { - diffResult = diff.diffWords("New Value", "New ValueMoreData"); - assert.equal( - "New ValueMoreDataValue", - diff.convertChangesToXML(diffResult), - "Single whitespace diffResult Value"); - - diffResult = diff.diffWords("New Value ", "New ValueMoreData "); - assert.equal( - "New ValueMoreDataValue ", - diff.convertChangesToXML(diffResult), - "Multiple whitespace diffResult Value"); -}; - -// Diff on word boundary -exports['Word Diff'] = function() { - diffResult = diff.diffWords("New :Value:Test", "New ValueMoreData "); - assert.equal( - "New ValueMoreData :Value:Test", - diff.convertChangesToXML(diffResult), - "Nonmatching word boundary diffResult Value"); - diffResult = diff.diffWords("New Value:Test", "New Value:MoreData "); - assert.equal( - "New Value:MoreData Test", - diff.convertChangesToXML(diffResult), - "Word boundary diffResult Value"); - diffResult = diff.diffWords("New Value-Test", "New Value:MoreData "); - assert.equal( - "New Value:MoreData -Test", - diff.convertChangesToXML(diffResult), - "Uninque boundary diffResult Value"); - diffResult = diff.diffWords("New Value", "New Value:MoreData "); - assert.equal( - "New Value:MoreData ", - diff.convertChangesToXML(diffResult), - "Word boundary diffResult Value"); -}; - -// Diff without changes -exports['Diff without changes'] = function() { - diffResult = diff.diffWords("New Value", "New Value"); - assert.equal( - "New Value", - diff.convertChangesToXML(diffResult), - "No changes diffResult Value"); - diffResult = diff.diffWords("New Value", "New Value"); - assert.equal( - "New Value", - diff.convertChangesToXML(diffResult), - "No changes whitespace diffResult Value"); - diffResult = diff.diffWords("", ""); - assert.equal( - "", - diff.convertChangesToXML(diffResult), - "Empty no changes diffResult Value"); -}; - -// Empty diffs -exports['Empty diffs'] = function() { - diffResult = diff.diffWords("New Value", ""); - assert.equal(1, diffResult.length, "Empty diff result length"); - assert.equal( - "New Value", - diff.convertChangesToXML(diffResult), - "Empty diffResult Value"); - diffResult = diff.diffWords("", "New Value"); - assert.equal( - "New Value", - diff.convertChangesToXML(diffResult), - "Empty diffResult Value"); -}; - -// With without anchor (the Heckel algorithm error case) -exports['No anchor'] = function() { - diffResult = diff.diffWords("New Value New Value", "Value Value New New"); - assert.eql( - "ValueNew Value New NewValue", - diff.convertChangesToXML(diffResult), - "No anchor diffResult Value"); -}; +describe('#diffWords', function() { + it('should diff whitespace', function() { + var diffResult = diff.diffWords('New Value', 'New ValueMoreData'); + diff.convertChangesToXML(diffResult).should.equal('New ValueMoreDataValue'); + }); + + it('should diff multiple whitespace values', function() { + var diffResult = diff.diffWords('New Value ', 'New ValueMoreData '); + diff.convertChangesToXML(diffResult).should.equal('New ValueMoreDataValue '); + }); + + // Diff on word boundary + it('should diff on word boundaries', function() { + var diffResult = diff.diffWords('New :Value:Test', 'New ValueMoreData '); + diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData :Value:Test'); + + diffResult = diff.diffWords('New Value:Test', 'New Value:MoreData '); + diff.convertChangesToXML(diffResult).should.equal('New Value:MoreData Test'); + + diffResult = diff.diffWords('New Value-Test', 'New Value:MoreData '); + diff.convertChangesToXML(diffResult).should.equal('New Value:MoreData -Test'); + + diffResult = diff.diffWords('New Value', 'New Value:MoreData '); + diff.convertChangesToXML(diffResult).should.equal('New Value:MoreData '); + }); + + // Diff without changes + exports['Diff without changes'] = function() { + var diffResult = diff.diffWords('New Value', 'New Value'); + diff.convertChangesToXML(diffResult).should.equal('New Value'); + + diffResult = diff.diffWords('New Value', 'New Value'); + diff.convertChangesToXML(diffResult).should.equal('New Value'); + + diffResult = diff.diffWords('', ''); + diff.convertChangesToXML(diffResult).should.equal(''); + }; + + // Empty diffs + it('should diff empty content', function() { + var diffResult = diff.diffWords('New Value', ''); + diffResult.length.should.equal(1); + diff.convertChangesToXML(diffResult).should.equal('New Value'); + + diffResult = diff.diffWords('', 'New Value'); + diff.convertChangesToXML(diffResult).should.equal('New Value'); + }); + + // With without anchor (the Heckel algorithm error case) + it('should diff when there is no anchor value', function() { + var diffResult = diff.diffWords('New Value New Value', 'Value Value New New'); + diff.convertChangesToXML(diffResult).should.equal('ValueNew Value New NewValue'); + }); +}); // CSS Diff -exports['CSS diffs'] = function() { - diffResult = diff.diffCss( - ".test,#value .test{margin-left:50px;margin-right:-40px}", - ".test2, #value2 .test {\nmargin-top:50px;\nmargin-right:-400px;\n}"); - assert.equal( - ".test2.test,#value #value2 .test {\n" - + "margin-topmargin-left:50px;\n" - + "margin-right:-400px;\n-40px}", - diff.convertChangesToXML(diffResult), - "CSS diffResult Value"); -}; +describe('#diffCss', function() { + it('should diff css', function() { + var diffResult = diff.diffCss( + '.test,#value .test{margin-left:50px;margin-right:-40px}', + '.test2, #value2 .test {\nmargin-top:50px;\nmargin-right:-400px;\n}'); + diff.convertChangesToXML(diffResult).should.equal( + '.test2.test,#value #value2 .test {\n' + + 'margin-topmargin-left:50px;\n' + + 'margin-right:-400px;\n-40px}'); + }); +}); // Line Diff -exports['Line diffs'] = function() { - diffResult = diff.diffLines( - "line\nold value\nline", - "line\nnew value\nline"); - assert.equal( - "line\nnew value\nold value\nline", - diff.convertChangesToXML(diffResult), - "Line diffResult Value"); - diffResult = diff.diffLines( - "line\nvalue\nline", - "line\nvalue\nline"); - assert.equal( - "line\nvalue\nline", - diff.convertChangesToXML(diffResult), - "Line same diffResult Value"); - diffResult = diff.diffLines( - "line\nvalue \nline", - "line\nvalue\nline"); - log("diffResult", diffResult); - log("diffResult", diff.convertChangesToXML(diffResult)); - assert.equal( - "line\nvalue\nvalue \nline", - diff.convertChangesToXML(diffResult), - "Line whitespace diffResult Value"); -}; - -// Patch creation with diff at EOF -exports['lastLineChanged'] = function() { - assert.eql( - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,3 +1,4 @@\n' - + ' line2\n' - + ' line3\n' - + '+line4\n' - + ' line5\n', - diff.createPatch('test', 'line2\nline3\nline5\n', 'line2\nline3\nline4\nline5\n', 'header1', 'header2')); - - assert.eql( - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,3 +1,4 @@\n' - + ' line2\n' - + ' line3\n' - + ' line4\n' - + '+line5\n', - diff.createPatch('test', 'line2\nline3\nline4\n', 'line2\nline3\nline4\nline5\n', 'header1', 'header2')); - - assert.eql( - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,4 @@\n' - + ' line1\n' - + ' line2\n' - + ' line3\n' - + '+line44\n' - + '-line4\n', - diff.createPatch('test', 'line1\nline2\nline3\nline4\n', 'line1\nline2\nline3\nline44\n', 'header1', 'header2')); - - assert.eql( - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,5 @@\n' - + ' line1\n' - + ' line2\n' - + ' line3\n' - + '+line44\n' - + '+line5\n' - + '-line4\n', - diff.createPatch('test', 'line1\nline2\nline3\nline4\n', 'line1\nline2\nline3\nline44\nline5\n', 'header1', 'header2')); -}; - -exports['EOFNL'] = function() { - assert.eql( - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,4 @@\n' - + ' line1\n' - + ' line2\n' - + ' line3\n' - + '+line4\n' - + '\\ No newline at end of file\n' - + '-line4\n', - diff.createPatch('test', 'line1\nline2\nline3\nline4\n', 'line1\nline2\nline3\nline4', 'header1', 'header2')); - - assert.eql( - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,4 @@\n' - + ' line1\n' - + ' line2\n' - + ' line3\n' - + '+line4\n' - + '-line4\n' - + '\\ No newline at end of file\n', - diff.createPatch('test', 'line1\nline2\nline3\nline4', 'line1\nline2\nline3\nline4\n', 'header1', 'header2')); - - assert.eql( - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,4 @@\n' - + '+line1\n' - + '-line11\n' - + ' line2\n' - + ' line3\n' - + ' line4\n' - + '\\ No newline at end of file\n', - diff.createPatch('test', 'line11\nline2\nline3\nline4', 'line1\nline2\nline3\nline4', 'header1', 'header2')); - - assert.eql( - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,5 +1,5 @@\n' - + '+line1\n' - + '-line11\n' - + ' line2\n' - + ' line3\n' - + ' line4\n' - + ' line4\n', - diff.createPatch('test', 'line11\nline2\nline3\nline4\nline4\nline4\nline4', 'line1\nline2\nline3\nline4\nline4\nline4\nline4', 'header1', 'header2')); -}; - -exports['Large Test'] = function() { - var random = 42; - var mult = 134775813, range = Math.pow(2, 32); - function nextRandom() { - random = ((random * mult) + 1) % range; - return random; - } - var largeTest = ".hbh9asgiidc {ehaahc9:ses;bhg9hc:ses;idgaag-hi9aa:cdca;ihgd9gdgca-gdadg:ighchehgaci;ggghdg:edhciag;daagsada:ahhhiaa;ahai7:hgid;}.hbh9asgiidc.hchgihaa {ggghdg:hgid;}.igiidchbh9ah {ihgd9gdgca-hbh9a:gga(" - + "hbh9ah/igiidcfhbh9ah.9hs);ihgd9gdgca-gaeahi:cd-gaeahi;7ah97i:7des;bhg9hc-gh97i:ses;ahai7:7des;ihgd9gdgca-edhhihdc:ses ses;}.igiidcfgde9 {ihgd9gdgca-edhhihdc:ses ses;}.bdghadaag .igiidcfgde9 {ihgd9gdgc" - + "a-edhhihdc:-7des ses;}.hchgihaa .igiidcfgde9 {ihgd9gdgca-edhhihdc:-dses ses;}.igiidcfaaaaia {ihgd9gdgca-edhhihdc:-bdes ses;}.bdghadaag .igiidcfaaaaia {ihgd9gdgca-edhhihdc:-9sses ses;}.hchgihaa .igiidc" - + "faaaaia {ihgd9gdgca-edhhihdc:-97des ses;}.igiidcfadacadha {ihgd9gdgca-edhhihdc:-9dses ses;}.bdghadaag .igiidcfadacadha {ihgd9gdgca-edhhihdc:-9bdes ses;}.hchgihaa .igiidcfadacadha {ihgd9gdgca-edhhihdc:" - + "-7sses ses;}.igiidcfabhha {ihgd9gdgca-edhhihdc:-77des ses;}.bdghadaag .igiidcfabhha {ihgd9gdgca-edhhihdc:-7dses ses;}.hchgihaa .igiidcfabhha {ihgd9gdgca-edhhihdc:-7bdes ses;}.igiidcfbdaa {ihgd9gdgca-e" - + "dhhihdc:-d7des ses;}.bdghadaag .igiidcfbdaa {ihgd9gdgca-edhhihdc:-ddses ses;}.hchgihaa .igiidcfbdaa {ihgd9gdgca-edhhihdc:-dbdes ses;}.igiidcfcaasdaaag {ihgd9gdgca-edhhihdc:-abdes ses;}.bdghadaag .igii" - + "dcfcaasdaaag {ihgd9gdgca-edhhihdc:-bsses ses;}.hchgihaa .igiidcfcaasdaaag {ihgd9gdgca-edhhihdc:-b7des ses;}.igiidcfgachba {ihgd9gdgca-edhhihdc:-dbdes ses;}.bdghadaag .igiidcfgachba {ihgd9gdgca-edhhihd" - + "c:-9ssses ses;}.hchgihaa .igiidcfgachba {ihgd9gdgca-edhhihdc:-9s7des ses;}.igiidcfghh {ihgd9gdgca-edhhihdc:-9sdses ses;}.bdghadaag .igiidcfghh {ihgd9gdgca-edhhihdc:-9sbdes ses;}.hchgihaa .igiidcfghh {" - + "ihgd9gdgca-edhhihdc:-99sses ses;}.igiidcfh7hga {ihgd9gdgca-edhhihdc:-97bdes ses;}.bdghadaag .igiidcfh7hga {ihgd9gdgca-edhhihdc:-9hsses ses;}.hchgihaa .igiidcfh7hga {ihgd9gdgca-edhhihdc:-9h7des ses;}.i" - + "giidcfgeadha {ihgd9gdgca-edhhihdc:-9hdses ses;}.bdghadaag .igiidcfgeadha {ihgd9gdgca-edhhihdc:-9hbdes ses;}.hchgihaa .igiidcfgeadha {ihgd9gdgca-edhhihdc:-9gsses ses;}.igiidcfaaisdaaag {ihgd9gdgca-edhh" - + "ihdc:-9dsses ses;}.bdghadaag .igiidcfaaisdaaag {ihgd9gdgca-edhhihdc:-9d7des ses;}.hchgihaa .igiidcfaaisdaaag {ihgd9gdgca-edhhihdc:-9ddses ses;}.igiidcfabei9ighh7 {ihgd9gdgca-edhhihdc:-hsses ses;}.bdgh" - + "adaag .igiidcfabei9ighh7 {ihgd9gdgca-edhhihdc:-h7des ses;}.hchgihaa .igiidcfabei9ighh7 {ihgd9gdgca-edhhihdc:-hdses ses;}.igiidcfadgd {ihgd9gdgca-edhhihdc:-hbdes ses;}.bdghadaag .igiidcfadgd {ihgd9gdgc" - + "a-edhhihdc:-gsses ses;}.hchgihaa .igiidcfadgd {ihgd9gdgca-edhhihdc:-g7des ses;}.igiidcfbhch9a {ihgd9gdgca-edhhihdc:-gdses ses;}.bdghadaag .igiidcfbhch9a {ihgd9gdgca-edhhihdc:-gbdes ses;}.hchgihaa .igi" - + "idcfbhch9a {ihgd9gdgca-edhhihdc:-dsses ses;}.igiidcfcaaahdh {ihgd9gdgca-edhhihdc:-bdses ses;}.bdghadaag .igiidcfcaaahdh {ihgd9gdgca-edhhihdc:-bbdes ses;}.hchgihaa .igiidcfcaaahdh {ihgd9gdgca-edhhihdc:" - + "-csses ses;}.igiidcfhhaahahgg7gahgaih {ihgd9gdgca-edhhihdc:-97sses ses;}.bdghadaag .igiidcfhhaahahgg7gahgaih {ihgd9gdgca-edhhihdc:-977des ses;}.hchgihaa .igiidcfhhaahahgg7gahgaih {ihgd9gdgca-edhhihdc:" - + "-97dses ses;}.igiidcfhhaahahgg7 {ihgd9gdgca-edhhihdc:-997des ses;}.bdghadaag .igiidcfhhaahahgg7 {ihgd9gdgca-edhhihdc:-99dses ses;}.hchgihaa .igiidcfhhaahahgg7 {ihgd9gdgca-edhhihdc:-99bdes ses;}.igiidc" - + "fcaaidddbhgd {ihgd9gdgca-edhhihdc:-asses ses;}.bdghadaag .igiidcfcaaidddbhgd {ihgd9gdgca-edhhihdc:-a7des ses;}.hchgihaa .igiidcfcaaidddbhgd {ihgd9gdgca-edhhihdc:-adses ses;}.igiidcfdeac {ihgd9gdgca-ed" - + "hhihdc:-c7des ses;}.bdghadaag .igiidcfdeac {ihgd9gdgca-edhhihdc:-cdses ses;}.hchgihaa .igiidcfdeac {ihgd9gdgca-edhhihdc:-cbdes ses;}.igiidcfdaagaghia {ihgd9gdgca-edhhihdc:-9hdses ses;}.bdghadaag .igii" - + "dcfdaagaghia {ihgd9gdgca-edhhihdc:-9hbdes ses;}.hchgihaa .igiidcfdaagaghia {ihgd9gdgca-edhhihdc:-9gsses ses;}.igiidcfahaa {ihgd9gdgca-edhhihdc:-9g7des ses;}.bdghadaag .igiidcfahaa {ihgd9gdgca-edhhihdc" - + ":-9gdses ses;}.hchgihaa .igiidcfahaa {ihgd9gdgca-edhhihdc:-9gbdes ses;}.igiidcfih9 {ihgd9gdgca-edhhihdc:-9dbdes ses;}.bdghadaag .igiidcfih9 {ihgd9gdgca-edhhihdc:-9asses ses;}.hchgihaa .igiidcfih9 {ihg" - + "d9gdgca-edhhihdc:-9a7des ses;}.igiidcfhgihgghia {ihgd9gdgca-edhhihdc:-9b7des ses;}.bdghadaag .igiidcfhgihgghia {ihgd9gdgca-edhhihdc:-9bdses ses;}.hchgihaa .igiidcfhgihgghia {ihgd9gdgca-edhhihdc:-9bbde" - + "s ses;}.igiidcfhgeaggaaa {ihgd9gdgca-edhhihdc:-9csses ses;}.bdghadaag .igiidcfhgeaggaaa {ihgd9gdgca-edhhihdc:-9c7des ses;}.hchgihaa .igiidcfhgeaggaaa {ihgd9gdgca-edhhihdc:-9cdses ses;}.igiidcfggahiaha" - + "ghah {ihgd9gdgca-edhhihdc:-9csses ses;}.bdghadaag .igiidcfggahiahaghah {ihgd9gdgca-edhhihdc:-9c7des ses;}.hchgihaa .igiidcfggahiahaghah {ihgd9gdgca-edhhihdc:-9cdses ses;}.igiidcfggahiagagdgaghia9dg9 {" - + "ihgd9gdgca-edhhihdc:-9cbdes ses;}.bdghadaag .igiidcfggahiagagdgaghia9dg9 {ihgd9gdgca-edhhihdc:-9dsses ses;}.hchgihaa .igiidcfggahiagagdgaghia9dg9 {ihgd9gdgca-edhhihdc:-9d7des ses;}.igiidcfggahiagagdga" - + "sdaaag {ihgd9gdgca-edhhihdc:-9ddses ses;}.bdghadaag .igiidcfggahiagagdgasdaaag {ihgd9gdgca-edhhihdc:-9dbdes ses;}.hchgihaa .igiidcfggahiagagdgasdaaag {ihgd9gdgca-edhhihdc:-7ssses ses;}.igiidcfggahiaga" - + "gdga {ihgd9gdgca-edhhihdc:-7s7des ses;}.bdghadaag .igiidcfggahiagagdga {ihgd9gdgca-edhhihdc:-7sdses ses;}.hchgihaa .igiidcfggahiagagdga {ihgd9gdgca-edhhihdc:-7sbdes ses;}.igiidcfggahiae79hhghagagdga {" - + "ihgd9gdgca-edhhihdc:-79bdes ses;}.bdghadaag .igiidcfggahiae79hhghagagdga {ihgd9gdgca-edhhihdc:-77sses ses;}.hchgihaa .igiidcfggahiae79hhghagagdga {ihgd9gdgca-edhhihdc:-777des ses;}.igiidcfbhdagagdga {" - + "ihgd9gdgca-edhhihdc:-77dses ses;}.bdghadaag .igiidcfbhdagagdga {ihgd9gdgca-edhhihdc:-77bdes ses;}.hchgihaa .igiidcfbhdagagdga {ihgd9gdgca-edhhihdc:-7hsses ses;}.igiidcfggahiagagdgaaaghhdc {ihgd9gdgca-" - + "edhhihdc:-79sses ses;}.bdghadaag .igiidcfggahiagagdgaaaghhdc {ihgd9gdgca-edhhihdc:-797des ses;}.hchgihaa .igiidcfggahiagagdgaaaghhdc {ihgd9gdgca-edhhihdc:-79dses ses;}.igiidcfgaea9abhha {ihgd9gdgca-ed" - + "hhihdc:-7h7des ses;}.bdghadaag .igiidcfgaea9abhha {ihgd9gdgca-edhhihdc:-7hdses ses;}.hchgihaa .igiidcfgaea9abhha {ihgd9gdgca-edhhihdc:-7hbdes ses;}.igiidcfsdgahgaabhha {ihgd9gdgca-edhhihdc:-9b7des ses" - + ";}.bdghadaag .igiidcfsdgahgaabhha {ihgd9gdgca-edhhihdc:-9bdses ses;}.hchgihaa .igiidcfsdgahgaabhha {ihgd9gdgca-edhhihdc:-9bbdes ses;}.igiidcfegiihgdabhha {ihgd9gdgca-edhhihdc:-7d7des ses;}.bdghadaag ." - + "igiidcfegiihgdabhha {ihgd9gdgca-edhhihdc:-7ddses ses;}.hchgihaa .igiidcfegiihgdabhha {ihgd9gdgca-edhhihdc:-7dbdes ses;}.igiidcfhhaa {ihgd9gdgca-edhhihdc:-7a7des ses;}.bdghadaag .igiidcfhhaa {ihgd9gdgc" - + "a-edhhihdc:-7adses ses;}.hchgihaa .igiidcfhhaa {ihgd9gdgca-edhhihdc:-7abdes ses;}.igiidcfhhaa {ihgd9gdgca-edhhihdc:-7a7des ses;}.bdghadaag .igiidcfhhaa {ihgd9gdgca-edhhihdc:-7adses ses;}.hchgihaa .igi" - + "idcfhhaa {ihgd9gdgca-edhhihdc:-7abdes ses;}.igiidcfaahi {ihgd9gdgca-edhhihdc:-h9shes ses;}.bdghadaag .igiidcfaahi {ihgd9gdgca-edhhihdc:-h97ces ses;}.hchgihaa .igiidcfaahi {ihgd9gdgca-edhhihdc:-h9dhes " - + "ses;}.igiidcfhaagdaa {ihgd9gdgca-edhhihdc:-h9bces ses;}.bdghadaag .igiidcfhaagdaa {ihgd9gdgca-edhhihdc:-h7shes ses;}.hchgihaa .igiidcfhaagdaa {ihgd9gdgca-edhhihdc:-h77ces ses;}.igiidcfcaagdcihgi {ihgd" - + "9gdgca-edhhihdc:-h7dhes ses;}.bdghadaag .igiidcfcaagdcihgi {ihgd9gdgca-edhhihdc:-h7bces ses;}.hchgihaa .igiidcfcaagdcihgi {ihgd9gdgca-edhhihdc:-hhshes ses;}.igiidcfcaa9gdge {ihgd9gdgca-edhhihdc:-hh7ce" - + "s ses;}.bdghadaag .igiidcfcaa9gdge {ihgd9gdgca-edhhihdc:-hhdhes ses;}.hchgihaa .igiidcfcaa9gdge {ihgd9gdgca-edhhihdc:-hhbces ses;}.igiidcf7aae {ihgd9gdgca-edhhihdc:-hgshes ses;}.bdghadaag .igiidcf7aae" - + " {ihgd9gdgca-edhhihdc:-hg7ces ses;}.igiidcfagdebacg {ihgd9gdgca-edhhihdc:-hscces ses;ahai7:9ges;}.bdghadaag .igiidcfagdebacg {ihgd9gdgca-edhhihdc:-hsbges ses;}.hchgihaa .igiidcfagdebacg {ihgd9gdgca-ed" - + "hhihdc:-hscces ses;}.igiidcfighchsaghc {ihgd9gdgca-edhhihdc:-7bbdes ses;}.bdghadaag .igiidcfighchsaghc {ihgd9gdgca-edhhihdc:-7csses ses;}.hchgihaa .igiidcfighchsaghc {ihgd9gdgca-edhhihdc:-7c7des ses;}" - + ".igiidcfhihgiadgdsada {ihgd9gdgca-edhhihdc:-hgdhes ses;}.bdghadaag .igiidcfhihgiadgdsada {ihgd9gdgca-edhhihdc:-hgbces ses;}.hchgihaa .igiidcfhihgiadgdsada {ihgd9gdgca-edhhihdc:-hdshes ses;}.igiidcfgas" - + "gah7 {ihgd9gdgca-edhhihdc:-hd7ces ses;}.bdghadaag .igiidcfgasgah7 {ihgd9gdgca-edhhihdc:-hddhes ses;}.hchgihaa .igiidcfgasgah7 {ihgd9gdgca-edhhihdc:-hdbces ses;}.igiidcfgadhaagdeids {ihgd9gdgca-edhhihd" - + "c:-hashes ses;}.bdghadaag .igiidcfgadhaagdeids {ihgd9gdgca-edhhihdc:-ha7ces ses;}.hchgihaa .igiidcfgadhaagdeids {ihgd9gdgca-edhhihdc:-hadhes ses;}.igiidcfdeacagdeids {ihgd9gdgca-edhhihdc:-habces ses;}" - + ".bdghadaag .igiidcfdeacagdeids {ihgd9gdgca-edhhihdc:-hbshes ses;}.hchgihaa .igiidcfdeacagdeids {ihgd9gdgca-edhhihdc:-hb7ces ses;}.igiidcfcaaagdeids {ihgd9gdgca-edhhihdc:-hbdges ses;}.bdghadaag .igiidc" - + "fcaaagdeids {ihgd9gdgca-edhhihdc:-hbbdes ses;}.hchgihaa .igiidcfcaaagdeids {ihgd9gdgca-edhhihdc:-hcsges ses;}.igiidcfighh7gahidga {ihgd9gdgca-edhhihdc:-hc7des ses;}.bdghadaag .igiidcfighh7gahidga {ihg" - + "d9gdgca-edhhihdc:-hcdges ses;}.hchgihaa .igiidcfighh7gahidga {ihgd9gdgca-edhhihdc:-hcbdes ses;}.hgdchbh9ah {ihgd9gdgca-hbh9a:gga(hbh9ah/hgdcfhbh9ah.9hs);ihgd9gdgca-gaeahi:cd-gaeahi;7ah97i:77es;ahca-7a" - + "h97i:77es;bhg9hc-gh97i:ses;ahai7:7des;ihgd9gdgca-edhhihdc:ses ses;}.hgdcfidddbhgdggggaci {ihgd9gdgca-edhhihdc:-ses ses;}.hgdcfidddbhgdggggacif7daag {ihgd9gdgca-edhhihdc:-7des ses;}.hgdcfidddbhgdggggac" - + "ifhchgihaa {ihgd9gdgca-edhhihdc:-dses ses;}.hgdcfasehca {ihgd9gdgca-edhhihdc:-bdes ses;}.hgdcfasehcaf7daag {ihgd9gdgca-edhhihdc:-9sses ses;}.hgdcfbhch9aggggaci {ihgd9gdgca-edhhihdc:-97des ses;}.hgdcfb" - + "hch9aggggacif7daag {ihgd9gdgca-edhhihdc:-9dses ses;}.hgdcfbhch9aggggacifhchgihaa {ihgd9gdgca-edhhihdc:-9bges ses;}.hgdcfgasgah7ggggaci {ihgd9gdgca-edhhihdc:-7sses ses;}.hgdcfgasgah7ggggacifhchgihaa {i" - + "hgd9gdgca-edhhihdc:-ah9es ses;}.hgdcfgasgah7ggggacif7daag {ihgd9gdgca-edhhihdc:-77des ses;}.hgdcfidddbhgdh {ahai7:7ses;ihgd9gdgca-edhhihdc:-7dses ses;}.hgdcfidddbhgdhf7daag {ahai7:7ses;ihgd9gdgca-edhh" - + "ihdc:-7bses ses;}.hgdcfge {ahai7:7ses;ihgd9gdgca-edhhihdc:-7cdes ses;}.hgdcfgef7daag {ahai7:7ses;ihgd9gdgca-edhhihdc:-hsdes ses;}.hgdcfgefhchgihaa {ahai7:7ses;ihgd9gdgca-edhhihdc:-h7des ses;}.hgdcfhah" - + "gg7 {ihgd9gdgca-edhhihdc:-hgdes ses;}.hgdcfhahgg7fhchgihaa {ihgd9gdgca-edhhihdc:-asaes ses;}.hgdcfhahgg7f7daag {ihgd9gdgca-edhhihdc:-hbges ses;}.hgdcfgdaaheha {ihgd9gdgca-edhhihdc:-gaaes ses;}.hgdcfgd" - + "aahehaf7daag {ihgd9gdgca-edhhihdc:-gd9es ses;}.hgdcfaggdg {ihgd9gdgca-edhhihdc:-g9hes ses;7ah97i:9ges;}.hgdcf7aae {ihgd9gdgca-edhhihdc:-gh9es ses;7ah97i:9ces;}.hgdcfhcsd {ihgd9gdgca-edhhihdc:-ggdes se" - + "s;7ah97i:9ges;}.hgdcfgddisdaaag {ihgd9gdgca-edhhihdc:-gades ses;7ah97i:7ses;}.hgdcfihgd {ihgd9gdgca-edhhihdc:-d9aes ses;ahai7 :7ses;}.hgdcfihgdf7daag {ihgd9gdgca-edhhihdc:-dhaes ses;ahai7 :7ses;}.hgdc" - + "fgadhafhahgg7 {ihgd9gdgca-edhhihdc:-ddaes ses;ahai7 :7des;}.hgdcfgadhafhahgg7f7daag {ihgd9gdgca-edhhihdc:-dc9es ses;ahai7:7des;}.gbhgdc {ihgd9gdgca-hbh9a:gga(hbh9ah/hgdchfaf.9hs);ihgd9gdgca-gaeahi:cd-" - + "gaeahi;7ah97i:7ses;bhg9hc-gh97i:ses;aagihgha-hah9c:bhaaaa;ahai7:7ses;ihgd9gdgca-edhhihdc:ses ses;}.haghah {ihgd9gdgca-edhhihdc:-ses ses;}.gagfghia9dg9 {ihgd9gdgca-edhhihdc:-7ses ses;}.gagfsdaaag {ihgd" - + "9gdgca-edhhihdc:-gses ses;}.gagfsdaaagfhahhifsa {ihgd9gdgca-edhhihdc:-9gses ses;}.gagfsdaaagfahhedhaa {ihgd9gdgca-edhhihdc:-9ases ses;}.gagfsdaaagfsgd7ac {ihgd9gdgca-edhhihdc:-97ses ses;}.gagdgafe79hh" - + "gha {ihgd9gdgca-edhhihdc:-9sses ses;}.gagdgafabhha {ihgd9gdgca-edhhihdc:-cses ses;}.gagdga {ihgd9gdgca-edhhihdc:-ases ses;}.gagdgafhahhifsa {ihgd9gdgca-edhhihdc:-7sses ses;}.gagdgafahhedhaa {ihgd9gdgc" - + "a-edhhihdc:-77ses ses;}.gagdgafsgd7ac {ihgd9gdgca-edhhihdc:-9cses ses;}.gbhgdc {ihgd9gdgca-hbh9a:gga(hbh9ah/hgdchfaf.9hs);ihgd9gdgca-gaeahi:cd-gaeahi;7ah97i:7ses;bhg9hc-gh97i:ses;aagihgha-hah9c:bhaaaa" - + ";ahai7:7ses;ihgd9gdgca-edhhihdc:ses ses;}.haghah {ihgd9gdgca-edhhihdc:-ses ses;}.gagfghia9dg9 {ihgd9gdgca-edhhihdc:-7ses ses;}.gagfsdaaag {ihgd9gdgca-edhhihdc:-gses ses;}.gagfsdaaagfhahhifsa {ihgd9gdg" - + "ca-edhhihdc:-9gses ses;}.gagfsdaaagfahhedhaa {ihgd9gdgca-edhhihdc:-9ases ses;}.gagfsdaaagfsgd7ac {ihgd9gdgca-edhhihdc:-97ses ses;}.gagdgafe79hhgha {ihgd9gdgca-edhhihdc:-9sses ses;}.gagdgafabhha {ihgd9" - + "gdgca-edhhihdc:-cses ses;}.gagdga {ihgd9gdgca-edhhihdc:-ases ses;}.gagdgafhahhifsa {ihgd9gdgca-edhhihdc:-7sses ses;}.gagdgafahhedhaa {ihgd9gdgca-edhhihdc:-77ses ses;}.gagdgafsgd7ac {ihgd9gdgca-edhhihd" - + "c:-9cses ses;}.shaahgdc {ihgd9gdgca-hbh9a:gga(hbh9ah/gdcghiachihdc.9hs);ihgd9gdgca-gaeahi:cd-gaeahi;7ah97i:7ses;bhg9hc-gh97i:ses;aagihgha-hah9c:bhaaaa;ahai7:7ses;ihgd9gdgca-edhhihdc:ses ses;}.shaa{ihg" - + "d9gdgca-edhhihdc:ses ses;}.shaafhggdihi{ihgd9gdgca-edhhihdc:-7ses ses;}.shaafheeaa{ihgd9gdgca-edhhihdc:-gses ses;}.shaafheeahghihdc{ihgd9gdgca-edhhihdc:-ases ses;}.shaafhgahd{ihgd9gdgca-edhhihdc:-cses" - + " ses;}.shaafasgaa{ihgd9gdgca-edhhihdc:-9sses ses;}.shaaf7iba{ihgd9gdgca-edhhihdc:-97ses ses;}.shaafhbh9a{ihgd9gdgca-edhhihdc:-9gses ses;}.shaafghah{ihgd9gdgca-edhhihdc:-9ases ses;}.shaafghahhgghei{ihg" - + "d9gdgca-edhhihdc:-9cses ses;}.shaafbhadga{ihgd9gdgca-edhhihdc:-7sses ses;}.shaafeei{ihgd9gdgca-edhhihdc:-77ses ses;}.shaafegdgagi{ihgd9gdgca-edhhihdc:-7gses ses;}.shaaffghgdihba{ihgd9gdgca-edhhihdc:-7" - + "ases ses;}.shaafiasi{ihgd9gdgca-edhhihdc:-7cses ses;}.shaafahaad{ihgd9gdgca-edhhihdc:-hsses ses;}.ahdh{ihgd9gdgca-edhhihdc:-hcses ses;}.shaafsba{ihgd9gdgca-edhhihdc:-h7ses ses;}.shaaf7he{ihgd9gdgca-ed" - + "hhihdc:-hgses ses;}.sdaaag{ihgd9gdgca-edhhihdc:-hases ses;}.shaafagdeids {ihgd9gdgca-edhhihdc:-gsses ses;}.aaaaiafhbhaa{ihgd9gdgca-edhhihdc:-ggses ses;ggghdg:edhciag;}.egiahgfh7hga{ihgd9gdgca-edhhihdc" - + ":-gases ses;}.h7hga{ihgd9gdgca-edhhihdc:-gcses ses;}.eghahia{ihgd9gdgca-edhhihdc:-dsses ses;}.gddisdaaag{ihgd9gdgca-edhhihdc:-dcses ses;}.adacfhggda{ihgd9gdgca-edhhihdc:-a77es ses;ahai7:9ces;}.7aae{ih" - + "gd9gdgca-edhhihdc:-agses ses;}.ighh7 {ihgd9gdgca-edhhihdc:-acses ses;}.hggdafgh97i {ihgd9gdgca-edhhihdc:-bs7es ses;ahai7:9ces;}.igahagggbih {a7hia-hehga:cdaghe;}.igahagggbih .igahagggbihfgddi {aagihgh" - + "a-hah9c:bhaaaa;ehaahc9:s;}.igahagggbih .igahagggbihfhggda {aagihgha-hah9c:bhaaaa;ehaahc9-aasi:7es;}.igahagggbih .igahagggbihfahgchba {aagihgha-hah9c:bhaaaa;ehaahc9-aasi:7es;sdci-shbha9:aghha,aaaaaihgh" - + ",hhch-haghs;sdci-hh7a:9ab;}.igahagggbih .igahagggbihfahgchba hcegi {ihgd9gdgca:ighchehgaci;idgaag:cdca;ehaahc9:ses;bhg9hc:ses;ahai7:9ss%;}a {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;}a:ahcd {iasi" - + "-aagdghihdc:cdca;gdadg:#ssssss;}a:ahhhiaa {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:hgihaa {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:ahcd.iddaihgfigiidcfahcd {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:ahhhiaa.i" - + "ddaihgfigiidcfahcd {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:hgihaa.iddaihgfigiidcfahcd {iasi-aagdghihdc:cdca;gdadg:#ssssss;}a:7daag.iddaihgfigiidcfahcd {iasi-aagdghihdc:cdca;gdadg:#ggbggg;ggghdg:edhc" - + "iag;}a:ahcd.aaaaa9fiasi {iasi-aagdghihdc:cdca;gdadg:#gggggg;sdci-hh7a:dd%;}a:ahhhiaa.aaaaa9fiasi {iasi-aagdghihdc:cdca;gdadg:#gggggg;sdci-hh7a:dd%;}a:hgihaa.aaaaa9fiasi {iasi-aagdghihdc:gcaagahca;gdad" - + "g:#ssssss;sdci-hh7a:dd%;}a:7daag.aaaaa9fiasi {iasi-aagdghihdc:cdca;gdadg:#ggbggg;sdci-hh7a:dd%;ggghdg:edhciag;}a:ahcd.aaaaa9fhgihaafhagihdc {iasi-aagdghihdc:cdca;gdadg:#gggggg;sdci-aah97i:idaa;sdci-hh" - + "7a:dd%;}a:ahhhiaa.aaaaa9fhgihaafhagihdc {iasi-aagdghihdc:cdca;gdadg:#gggggg;sdci-aah97i:idaa;sdci-hh7a:dd%;}a:hgihaa.aaaaa9fhgihaafhagihdc {iasi-aagdghihdc:gcaagahca;gdadg:#ssssss;sdci-aah97i:idaa;sdc" - + "i-hh7a:dd%;}a:7daag.aaaaa9fhgihaafhagihdc {iasi-aagdghihdc:cdca;gdadg:#ggbggg;sdci-hh7a:dd%;sdci-aah97i:idaa;ggghdg:edhciag;}a:ahcd.aaaaa7fiasi {iasi-aagdghihdc:cdca;gdadg:#cdcdcd;}a:ahhhiaa.aaaaa7fia" - + "si {iasi-aagdghihdc:cdca;gdadg:#cdcdcd;}a:hgihaa.aaaaa7fiasi {iasi-aagdghihdc:gcaagahca;gdadg:#cdcdcd;}a:7daag.aaaaa7fiasi {iasi-aagdghihdc:gcaagahca;gdadg:#ggbggg;ggghdg:edhciag;}a:ahcd.aaaaahfiasi {" - + "iasi-aagdghihdc:cdca;gdadg:#cdcdcd;ahca-7ah97i:7ses;}a:ahhhiaa.aaaaahfiasi {iasi-aagdghihdc:cdca;gdadg:#cdcdcd;ahca-7ah97i:7ses;}a:hgihaa.aaaaahfiasi {iasi-aagdghihdc:gcaagahca;gdadg:#cdcdcd;ahca-7ah9" - + "7i:7ses;}a:7daag.aaaaahfiasi {iasi-aagdghihdc:cdca;gdadg:#ggbggg;ggghdg:edhciag;ahca-7ah97i:7ses;}a:ahcd.gcaagahca {gdadg:#ssssss;iasi-aagdghihdc:gcaagahca;ggghdg:edhciag;}a:ahhhiaa.gcaagahca {gdadg:#" - + "ssssss;iasi-aagdghihdc:gcaagahca;}a:hgihaa.gcaagahca {iasi-aagdghihdc:gcaagahca;gdadg:#ssssss;}a:7daag.gcaagahca {iasi-aagdghihdc:gcaagahca;gdadg:#ggbggg;}.igahagggbih hcegi {ggghdg:edhciag;}.igahaggg" - + "bih hcegi:7daag.hchgihaa {ggghdg:aashgai;gdadg:#adadad;}a:7daag,a.sdggh,a:ahcd.sdggh,a:ahhhiaa.sdggh,a:7daag.sdggh,a:hgihaa.sdggh,.ihiehhi igiidc:7daag,.igahagggbih hcegi:7daag {iasi-aagdghihdc:cdca;g" - + "dadg:#ggbggg;ggghdg:edhciag;}h hb9 {idgaag:cdca;}ida9 {ehaahc9-gh97i:ses;ehaahc9-aasi:ses;ehaahc9-idiidb:ses;ehaahc9-ide:ses;bhg9hc-gh97i:ses;bhg9hc-aasi:ses;bhg9hc-ide:ses;bhg9hc-idiidb:ses;ihgd9gdgc" - + "a-gdadg:#gggggg;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:bg%;}ihiaa {sdci-hh7a:9ab;}ihiaa ihiaa {sdci-hh7a:9ss%;}.aasigadhi {sadhi:aasi;}i {sdci-aah97i:idaa;}.igiidc {sdci-shbha9:sh7d" - + "bh,aghha,aaaaaihgh,hhch-haghs;ehaahc9:des;iasi-hah9c:gaciag;sadhi:aasi;}.ihhhgsgiidc {ehaahc9:ses;bhg9hc:ses;idgaag-hi9aa:cdca;ihgd9gdgca-gdadg:ighchehgaci;ggghdg:edhciag;}.ihhhgsgiidc .igiidcfiasi {a" - + "hca-7ah97i:7ses;sdci-hh7a:9ab;}.ihhhgsgiidc.bdghadaag .igiidcfiasi {gdadg:#ggbggg;}.ihhhgsgiidc.hchgihaa .igiidcfiasi {gdadg:#sgsgsg;ggghdg:aashgai;}.aasifigiidc {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hh" - + "ch-haghs;sdci-hh7a:s.dab;ehaahc9-ide:des;ehaahc9-aasi:des;ehaahc9-gh97i:des;iasi-hah9c:gaciag;sadhi:aasi;bhc-ahai7:dses;}.gh97ifigiidc {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:s.dab;eh" - + "aahc9:des;iasi-hah9c:gaciag;sadhi:gh97i;}.gdagbcf7ahaag {sdci-aah97i:idaa;sdci-hh7a:dd%;ahca-7ah97i:9des;gdadg:#ssssss;ehaahc9-ide:7es;ehaahc9-aasi:7es;ehaahc9-gh97i:7es;a7hia-hehga:cdaghe;iasi-hah9c:" - + "aasi;}.hgdcfgdagbcf7ahaag {ehaahc9-aasi:7des;}.ahihfgda i7 {iasi-hah9c:aasi;sdci-aah97i:cdgbha;}.ahihfgda .cd7hih {iasi-hah9c:gaciag;}#idddbhgdfihiaa i7 h {ehaahc9-aasi:9ses;}.ahihfgda {7ah97i:7des;ih" - + "gd9gdgca-gdadg:#gggggg;}.ahihfahhi9dagbc {ehaahc9-aasi:des;ehaahc9-gh97i:des;ahca-7ah97i:7des;}.agdeadac {idgaag-idiidb:9es iahgd;idgaag-gh97i:9es iahgd;idgaag-aasi:9es #9s9s9s;idgaag-ide:9es #9s9s" - + "9s;ahhhihahi9:7haaac;edhhihdc:hihdagia;ahai7:7sses;7-hcaas:9;ehaahc9:ses;ggghdg:edhciag;}.haaagi {ihgd9gdgca-gdadg:#g9gdga;sadhi:aasi;}haaagi.gghiaghh {sdci-hh7a:s.dab;ahai7:9cses;}.hbhaa {sdci-hh7a:d" - + "d%;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;}.ihiaaaehgag {ahai7:7ses;}.iasihcegi {ehaahc9:des;bhg9hc:ses;aagihgha-hah9c:ide;}i7 {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-aah97i:idaa;" - + "sdci-hh7a:dd%;}.ihiaa {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;sdci-aah97i:idaa;}.ahaga {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;}.aaaaafiddaihg {gaahg:idi7;a" - + "hai7:hgid;7ah97i:gces;ihgd9gdgca-gdadg:#d77ahc;}.iddaihgfaasi {sadhi:aasi;ahai7:hgid;iasi-hah9c:gaciag;}.iddaihgfgh97i {sadhi:gh97i;ahai7:hgid;iasi-hah9c:gaciag;}aha.iddaihg {sadhi:aasi;}.iddaihgfid9f" - + "aasi {sadhi:aasi;}.iddaihgfid9 {gdadg:#ssssss;sadhi:aasi;}.iddaihgfid9fgh97i {sadhi:aasi;}.iddaihgfigiidc {sadhi:aasi;iasi-hah9c:gaciag;ggghdg:edhciag;ehaahc9:ces ses ces ses;bhg9hc:ses;idgaag:ses cdc" - + "a;ihgd9gdgca-gdadg:ighchehgaci;}.iddaihgfigiidc.hchgihaa {ggghdg:hgid;}.iddaihgfigiidcfhgdc {gaahg:gh97i;sadhi:cdca;bhg9hc-aasi:hgid;bhg9hc-gh97i:hgid;idgaag:ses;}.iddaihgfigiidc .igiidcfiasi,.iddaihg" - + "figiidc hehc {ahheah9:iadgd;sdci-hh7a:s.cab;ehaahc9-aasi:des;ehaahc9-gh97i:des;sdci-shbha9:aghha,aaaaaihgh,hhch-haghs;ahca-7ah97i:s.cab;iasi-aagdghihdc:cdca;}.iddaihgfigiidc.bdghadaag .igiidcfiasi {gd" - + "adg:#ggbggg;}.iddaihgfigiidc.hchgihaa .igiidcfiasi {gdadg:9gh9;}.iicfahih9dcigdafaasi {sadhi:aasi;ehaahc9-aasi:des;}.cdaghe,.cdaghe ia,.iaehiaa {a7hia-hehga:cdaghe;}ihiaa.cdaghe,ihiaa.cdaghe ig,ia.ag" - + "he {a7hia-hehga:cdgbha;}.ihifhbhaafaasi {sadhi:aasi;}.ihifhbhaafgh97i {sadhi:aasi;}.ihifhbhaafid9 {sadhi:aasi;sdci-hh7a:dd%;}.ihifhbhaafid9 h {ahca-7ah97i:7ses;ehaahc9-ide:7es;}.ihifhbhaafdssfaasi {" - + "sadhi:aasi;ahai7:7ses;ehaahc9-ide:7es;}.ihifhbhaafdssfgh97i {sadhi:aasi;ehaahc9-ide:7es;}.ihifhbhaafdssfid9 {sadhi:aasi;sdci-hh7a:dd%;ehaahc9-ide:7es;}aha.ihifhbhaa {idgaag-aasi:9es hdaha #7a77hh;id" - + "gaag-gh97i:9es hdaha #7a77hh;idgaag-idiidb:9es hdaha #7a77hh;}aha.aaaaa9 {ahai7:hgid;gaahg:idi7;}aha.aaaaa9faasi {sadhi:aasi;ehaahc9-aasi:des;ehaahc9-ide:7es;ehaahc9-idiidb:7es;7ah97i:9ges;}aha.aaaaa" - + "9fgh97i {sadhi:gh97i;ehaahc9-aasi:des;ehaahc9-ide:7es;ehaahc9-idiidb:7es;7ah97i:9ges;}.aaaaa9fiasi {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;gdadg:#gggggg;sdci-hh7a:dd%;ehaahc9-aasi:7es;ehaahc9-g" - + "h97i:7es;}.aaaaa9fad9hc {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;gdadg:#gggggg;sdci-hh7a:dd%;sdci-aah97i:idaa;ehaahc9-aasi:des;ehaahc9-gh97i:des;}.aaaaa9fad9dgi {bhg9hc-gh97i:7es;}aha.ad9d {sad" - + "hi:aasi;}.aaaaa7fiasi {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;gdadg:#i7i7i7;sdci-aah97i:idaa;ehaahc9-aasi:des;ehaahc9-gh97i:des;ahca-7ah97i:hhes;}.aaaaafadghihdc {gaahg:idi7;ahai7:hgid;7ah97i:7" - + "ces;ihgd9gdgca-gdadg:#d77ahc;edhhihdc:gaahihaa;}.adghihdcfaasi {bhg9hc-aasi:hes;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;}.aaaaafigahagggbi {gaahg:idi7;ahai7:hgid;7ah97i:7ces;ihgd9g" - + "dgca-gdadg:#d77ahc;}.igahagggbifaasi {ahai7:hgid;bhg9hc-gh97i:7dses;}.igahagggbifgh97i {sadhi:gh97i;ahai7:hgid;ahai7:7gces;}.igahagggbifigiidc {sadhi:aasi;bhg9hc-aasi:ses;bhg9hc-gh97i:ses;bhg9hc-ide:9" - + "es;ggghdg:edhciag;7ah97i:7ces;ihgd9gdgca-gdadg:ighchehgaci;idgaag:cdca;ehaahc9:ses;}.igahagggbifhbh9a {bhg9hc-aasi:hes;bhg9hc-gh97i:hes;bhg9hc-ide:9es;ggghdg:edhciag;7ah97i:7ces;idgaag:cdca;}.igahaggg" - + "bifehi7fgdcihhcag {ihgd9gdgca-gdadg:a7hia;ahai7:hgid;ehaahc9-ide:ses;ehaahc9-idiidb:ses;}.igahagggbihcah {ahai7:bes;7ah97i:7ces;ihgd9gdgca-gdadg:#d77ahc;}.igahagggbifehi7figiidc {sadhi:aasi;bhg9hc-gh9" - + "7i:9es;bhg9hc-ide:9es;bhg9hc-aasi:ses;}.cdsdgaag {idgaag:cdca;}.igahagggbifehi7fahcd {idgaag:cdca;}.igahagggbifehi7 {sadhi:aasi;}.igahagggbifehi7faasi,.igahagggbifehi7fgh97i {sdci-shbha9:sh7dbh,aghha," - + "aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;}.igahagggbifehi7fiasi {iasi-aagdghihdc:cdca;gdadg:#ssssss;sdci-hh7a:s.dab;ahca-7ah97i:7ges;bhg9hc-aasi:hes;}.hahgg7fehi7fgdcihhcag7 {sadhi:gh97i;ihgd9gdgca-gdadg:a7" - + "hia;gdadg:9gh9;ehaahc9-ide:ses;ehaahc9-idiidb:ses;7ah97i:7ges;idgaag:7es hdaha #d77ahc;}.hahgg7fhcegi7 {sadhi:aasi;ahai7:9gces;aagihgha-hah9c:bhaaaa;bhg9hc-aasi:9es;idgaag:7es hdaha a7hia;ihgd9gdgca-g" - + "dadg:a7hia;}#hahgg7iasi {sdci-shbha9:sh7dbh,dacaah,hhch-haghs;sdci-hh7a:9ab;}#hahgg7sddaihg {sdci-hh7a:9ab;}.hahgg7fehi7figiidc {sadhi:aasi;bhg9hc-aasi:ses;bhg9hc-ide:9es;bhg9hc-gh97i:9es;ggghdg:edhci" - + "ag;idgaag:cdca;}.ahgagidg9fehi7 {sadhi:aasi;}aha.ehi7faasi,aha.ehi7fgh97i {sadhi:aasi;sdci-aah97i:cdgbha;bhg9hc:ses;ahca-7ah97i:7des;ihgd9gdgca-gdadg:#d77ahc;}aha.ggggacifehi7figiidch {ihgd9gdgca-gd" - + "adg:#d77ahc;sadhi:aasi;}aha.idddbhgdh {sadhi:aasi;ihgd9gdgca-gdadg:#d77ahc;7ah97i:7des;ahca-7ah97i:7des;}ga.idddbhgd {ihgd9gdgca-gdadg:#gsg9g9;}ah.idddbhgd {ahhi-hi9aa:cdca;ehaahc9-aasi:des;ehaahc9-id" - + "e:7es;ehaahc9-idiidb:ses;ehaahc9-gh97i:des;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:s.dab;}ah.bhch9afidddbhgdh {ahhi-hi9aa:cdca;ehaahc9-aasi:des;ehaahc9-ide:7es;ehaahc9-idiidb:ses;ehaah" - + "c9-gh97i:des;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;idgaag-ide:9es hdaha #7d7d7d;sdci-hh7a:s.dab;}aha.hahgg7 {sadhi:aasi;ahai7:hgid;ihgd9gdgca-gdadg:#d77ahc;}aha.9dd9aafhahgg7 {sadhi:gh97i;ihg" - + "d9gdgca-gdadg:#d77ahc;}aha.hahgg7fids {sdci-aah97i:cdgbha;bhg9hc:ses;ahca-7ah97i:7des;sadhi:aasi;ihgd9gdgca-gdadg:#d77ahc;}.bacg {idgaag-idiidb:9es hdaha #cscscs;idgaag-gh97i:9es hdaha #cscscs;idgaag" - + "-ide:9es hdaha #gggggg;idgaag-aasi:9es hdaha #gggggg;ihgd9gdgca-gdadg:#gggggg;sdci-shbha9:aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:s.dab;ehaahc9:ses;}aha.shaafbhch9ag {ehaahc9:ses;ihgd9gdgca-gdadg:#s7sca" - + "7;idgaag:9es hdaha #sgsgsg;ahai7:9ss%;sadhi:aasi;}aha.sddiag {ahai7:hgid;sadhi:gh97i;ehaahc9-ide:7es;ehaahc9-idiidb:ses;}aha.bahhh9a {sadhi:aasi;sdci-aah97i:cdgbha;sdci-hh7a:dd%;gdadg:iahgd;bhg9hc:se" - + "s;ahca-7ah97i:77es;ahai7:hgid;ehaahc9:7es;}.gh97ifgahgd {idgaag:9es hdaha #9s9s9s;ihgd9gdgca-gdadg:#gsgggg;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:s.dab;ahhhihahi9:7haaac;edhhihdc:hihd" - + "agia;ahai7:97ses;7-hcaas:9;ehaahc9:9ses;ggghdg:edhciag;}aha.ehgdehhi {ihgd9gdgca-gdadg:#gggggg;idgaag:9es hdaha #b99h7h;ehaahc9:des;}aha.ehgdehhiagah {gaahg:idi7;ehaahc9:9ses;}.bhch9afeh9a {ihgd9gdgca" - + "-gdadg:#ghghgh;}.gdbbaci {edhhihdc:gaahihaa;bhg9hc-ide:7es;ehaahc9:9ses;gaahg:gh97i;}.aaac9dbbaci {ihgd9gdgca:#gggggg;}.daa9dbbaci {ihgd9gdgca-gdadg:#ghghgh;idgaag-ide:#sgsgsg;idgaag-idiidb:#sgsgsg;}." - + "haa9dbbaciagah {bhg9hc-idiidb:des;}.gdbbaciagi7dg {sdci-hh7a:9ab;sdci-aah97i:idaa;ehaahc9-idiidb:des;}.gdbbacisasi {sdci-hh7a:dd%;ehaahc9-idiidb:9es;}.gdbbacishbahihbe {sdci-hh7a:s.cab;}.gdbbaci7aaaia" - + "97agdids {edhhihdc:hihdagia;gh97i:9ses;ide:9ses;ggghdg:edhciag;}aha.eh9afihiaa {gaahg:idi7;ahai7:hgid;}aha.hhaafchah9hihdc {bhg9hc-ide:ses;}ia.hhaafchah9hihdc {ihgd9gdgca-hbh9a:gga(hi9aah/hhaafchafid" - + "9.ge9);ihgd9gdgca-gaeahi:gaeahi-s;ahai7:9bdes;idgaag-gh97i:9es hdaha #sgsgsg;}.ihiehhi {bhg9hc-aasi:7ses;bhg9hc-ide:7ses;bhg9hc-idiidb:7ses;ehaahc9-aasi:9bes;}.ihiehhi igiidc {ihgd9gdgca-gdadg:ighcheh" - + "gaci;idgaag:ses cdca;bhg9hc:ses;ehaahc9:ses;ggghdg:edhciag;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;iasi-hah9c:aasi;}.ihiehhi ah {ehaahc9-aasi:ses;ehaahc9-ide:ses;ehaahc9-gh97i:ses;" - + "ehaahc9-idiidb:ges;ahhi-hi9aa-i9ea:cdca;sdci-hh7a:s.dab;}.aaihfhiabfhgihaa {ahhi-hi9aa-hbh9a:gga(hbh9ah/haaagiaafhgihdc.9hs);}.aaihfhiabfhgihaa igiidc {sdci-aah97i:idaa;}aha.gh97ifeh9afgdciaci {ahai7" - + ":hgid;ihgd9gdgca-gdadg:a7hia;}aha.hbhaafhgifiddaihg {ihgd9gdgca-gdadg:#9s9s9s;7ah97i:7des;ehaahc9-idiidb:des;}aha.hgifigiidc {sdci-hh7a:s.dab;ehaahc9-aasi:7es;ehaahc9-gh97i:7es;sadhi:aasi;iasi-hah9c:" - + "gaciag;}aha.eh9afihiaa {bhg9hc:ses;}.eh9afihiaa hcegi,.eh9afihiaa hgibhi,.eh9afihiaa haaagi,.eh9afihiaa iasihgah,.sdgbfgdciaci iasihgah {sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;}a" - + "ha.eh9afahih {bhg9hc:9ses;sadhi:aasi;}aha.bdahafsdgb {idgaag:9es hdaha #a9a9a9;bhc-ahai7:gases;}aha.sdgbfgdciaci {bhg9hc:7as;gaahg:idi7;ahai7:hgid;}.sdgbfgdciaci h {sdci-hh7a:9ab;}aha.sdgbfihiaafihg" - + " {idgaag-aasi:9es hdaha #b99h7h;idgaag-gh97i:9es hdaha #b99h7h;gaahg:idi7;}.sdgbfihiaafiasi {ahca-7ah97i:7aes;sdci-shbha9:sh7dbh,aghha,aaaaaihgh,hhch-haghs;sdci-hh7a:9ab;sdci-aah97i:idaa;gdadg:#hhhh" - + "hh;sdci-aah97i:idaa;aagihgha-hah9c:ihhaahca;bhg9hc:ses;ehaahc9-aasi:des;}.iicfeh9a9dcigdafaasi {ehaahc9-ide:ges;sadhi:aasi;}.iicfeh9a9dcigdafgh97i {ehaahc9-ide:ges;sadhi:gh97i;}aha.sdgbfgdcigdah {gaa" - + "hg:idi7;}aha.ghahdfigiidch {ehaahc9:des;}.aaig9sdgaag {idgaag:9es hdaha gaa;}.aaig9 {idgaag:9es hdaha #iii;}.haahcgaafhahgg7 {ahai7:hgid;gaahg:idi7;bhg9hc:9ses;ihgd9gdgca-gdadg:#ac7ahs;edhhihdc:gaah" - + "ihaa;}aha.hahgg7fid9 {edhhihdc:gaahihaa;ihgd9gdgca-gdadg:#h9h9h9;idgaag-idiidb:7es hdaha #d9a9sb;}.hahgg7fgdcihhcag {ahai7:hgid;bhg9hc-aasi:7ses;ehaahc9-ide:9ses;}aha.hahgg7fgefaasifgdgcag {sadhi:aa" - + "si;}aha.hahgg7fgefgh97ifgdgcag {sadhi:gh97i;}.hahgg7fihiaa {sdci-hh7a:dd%;sdci-aah97i:idaa;ehaahc9-aasi:des;ehaahc9-ide:des;}.hahgg7fgghiaghh {ehaahc9-aasi:9ses;ehaahc9-gh97i:ses;ehaahc9-ide:des;sdci-" - + "hh7a:dd%;ihgd9gdgca-gdadg:#gggggg;idgaag:9es hdaha #sgsgsg;}aha.hahgg7figiidcfgda {ehaahc9-ide:des;idgaag-ide:9es hdaha #sgsgsg;sadhi:aasi;ahai7:9ss%;}aha.hahgg7figiidcfgdcihhcag {sadhi:gh97i;}aha.hah" - + "gg7fhaa9ghiaghhfgda {idgaag-idiidb:9es hdaha #sgsgsg;sadhi:aasi;ahai7:9ss%;7ah97i:7des;}aha.hahgg7fgghiaghhfgdcihhcag {idgaag:7es hdaha #d9a9sb;ehaahc9:des;}.hgdcehiaafiasi {sadhi:aasi;bhg9hc:ses;7ah9" - + "7i:7des;ahca-7ah97i:7des;ehaahc9-gh97i:des;sdci-hh7a:dd%;sdci-aah97i:idaa;}aha.hgdc {ahai7:7des;7ah97i:7des;sadhi:aasi;}a:ahcd.hahgg79ghiaghhfiasi {iasi-aagdghihdc:gcaagahca;gdadg:#ssssss;sdci-hh7a" - + "{sadhi:aasi;ahhi-hi9aa:cdca;bhg9hc-ide:hes;}.7aae9dcihhcag {edhhihdc:hihdagia;ide:7es;gh97i:7es;bhg9hc:ses;ehaahc9:ses;idgaag:ses cdca;7-hcaas:g;ggghdg:edhciag;ahhhihahi9:hc7aghi;ihgd9gdgca-gdadg:ighc" - + "hehgaci;}.7aae9dcihhcagaihihg {bhg9hc:ses;ehaahc9:ses;ggghdg:edhciag;ahhhihahi9:hc7aghi;}.9gh-bdagaa .7aae9dcihhcag {gh97i:ges;}.9gh-ahhad9 .9gh-ehcaa .7aae9dcihhcag {gh97i:7ses;}.sdgb7hih {sdci-hh7a" - + ":9ab;}ihiaa.cdihs9shiaa {idgaag:ses;}ihiaa.cdihs9shiaa .aasi9dagbc {iasi-hah9c:gh97i;aagihgha-hah9c:ide;sdci-aah97i:idaa;}ihiaa.cdihs9shiaa .gh97i9dagbc {iasi-hah9c:aasi;}ihiaa.hgeagihiaa {bhg9hc:s s " - + "s 7es;}ihiaa.hgeagihiaa iida9 ig ia.geadhaf7ahaag {idgaag-ide:9es hdaha g9i(9g7,9g7,9g7);idgaag-gh97i:9es hdaha g9i(9g7,9g7,9g7);idgaag-idiidb:cdca;idgaag-aasi:9es hdaha g9i(9g7,9g7,9g7);ihgd9gdgca:g9" - + "i(77d,77d,77d);ehaahc9:s;sdci-hh7a:s;7ah97i:hses;}ihiaa.hgeagihiaa iida9 ig ia.geadhaf7ahaag ihiaa {sdci-aah97i:ass;sdci-hh7a:9ab;ihgd9gdgca:ighchehgaci;gdadg:iahgd;ahai7:9ss%;bhg9hc:s;}ihiaa.hgeagihi" - + "aa iida9 ig ia.gdcihhchfgeadha {bhg9hc:s;ehaahc9-aasi:s;}aha.geadha {bhg9hc:s;ehaahc9:s;daagsada:hgid;7ah97i:7hses;ahai7:asses;idgaag:9es hdaha g9i(9g7,9g7,9g7);ihgd9gdgca:a7hia;}aha.geadha ihiaa {7ah" - + "97i:9ss%;idgaag:cdca;}.geadha-daagaghia {}aha.geadha ihiaa iida9 ig {7ah97i:7des;}aha.geadha ihiaa iida9 ig#geadhafiahcdgda {7ah97i:hgid;}.geadhafsddiag {idgaag-ide:cdca;idgaag-gh97i:9es hdaha g9i(9g7" - + ",9g7,9g7);idgaag-idiidb:9es hdaha g9i(9g7,9g7,9g7);idgaag-aasi:9es hdaha g9i(9g7,9g7,9g7);7ah97i:hses;ihgd9gdgca:g9i(77d,77d,77d);}.iaehiaa {sdci-aah97i:idaa;}.ihiaaf7ahaag {sdci-aah97i:idaa;sdci-hh7a" - + ":9ab;gdadg:#ssssss;ahca-7ah97i:7hes;ahai7:9ss%;}.gh97ifihiaaf7ahaag {ahca-7ah97i:7hes;ahai7:hes;sdci-aah97i:idaa;gdadg:#ssssss;sdci-hh7a:9ab;}.aasifihiaaf7ahaag {ahca-7ah97i:7hes;ahai7:ges;sdci-aah97i" - + "{ihgd9gdgca-gdadg:#gggggg;ia77bgd-abdas:ses;ia77bgd-ehgs:ses;ia77bgd-shsshf:ses;ia77bgd-shi:hces;faadbg-abdas:des;faadbg-ehgs:des;faadbg-shi:hces;faadbg-shsshf:ses;ghgs-abah:9ss%;}.aabhcfhaiahiaa {" - + "sdci-hh7a:9ab;}#shaahgdgb {edhhihdc:hihdagia;ide:ses;aasi:ses;ahai7:ses;7ah97i:ses;}.aaaaafhgiihih {ihgd9gdgca-gdadg:#gggggg;7ah97i:7ses;}.gdagbcf7ahaagfadc9 {sdci-hh7a:dd%;7ah97i:hses;gdadg:#sssss" - + "s;}aha.ihifhaafeagbhhhhdch,aha.ihifihhhgfeagbhhhhdch {sadhi:aasi;ahai7:hgid;iasi-hah9c:gaciag;}aha.eagbhhhhdchbhhaehhi {bhg9hc:ses;ehaahc9-aasi:7ses;ehaahc9-ide:des;ehaahc9-idiidb:des;}aha.eagbhhhhdch" - + "bhhaehhi ga {bhg9hc:ses;ehaahc9:ses ses ses 9ses;}aha.hgdcehiaafiasi {sadhi:aasi;bhg9hc:ses;7ah97i:7des;ahca-7ah97i:7des;ehaahc9-gh97i:des;sdci-aah97i:idaa;}aha.hgdc {ahai7:7des;7ah97i:7des;sadhi:a" - + "asi;}a.ahia {gdadg:#ggssss;sdci-aah97i:cdgbha;}a:ahhhiaa.ahia {gdadg:#ggssss;sdci-aah97i:cdgbha;}a:7daag.ahia {gdadg:#ggssss;sdci-aah97i:cdgbha;}a:hgihaa.ahia {gdadg:#ggssss;sdci-aah97i:cdgbha;}.ahia" - + "{9heha:#ggssss;ghgs-hhbdas:idaa;}.gh97ifiddaihg {sadhi:gh97i;ehaahc9-gh97i:des;}aha.asfhahgg7fid9 {bhg9hc-aasi:hes;bhg9hc-gh97i:hes;bhg9hc-idiidb:9ses;idgaag-idiidb:7es hdaha #d9a9sb;}.asfhahgg7 {ah" - + "ai7:hgid;gaahg:idi7;7ah97i:9hses;ehaahc9-ide:ses;bhg9hc:ses;ihgd9gdgca-gdadg:#d77ahc;}aha.asfhahgg7faasiaca {sadhi:aasi;}aha.asfhahgg7fgh97iaca {sadhi:gh97i;}.ahih h {sdci-hh7a:9ab;}.asfhahgg7fgdcihh" - + "cag {sadhi:aasi;bhg9hc-ide:des;}ia.asfhahgg7fiasi {sdci-hh7a:s.dab;}.asfhahgg7fiasi hcegi {sdci-hh7a:s.dab;}.asfhahgg7fiasi haaagi {sdci-hh7a:s.dab;}ia.asfihiaaaehgag {ahai7:9ses;}.asfihiaa9aaa {ehaah" - + "c9-aasi:9ses;ehaahc9-gh97i:des;}aha.hgihdcfhiae {ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:des;ehaahc9-gh97i:9ses;bhg9hc-gh97i:9des;ihgd9gdgca-gdadg:a7hia;idgaag-idiidb:7es dgihai;idgaag-gh97i:7" - + "es dgihai;ahai7:dd%;}aha.aaghhhdcfhiae {ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:des;ehaahc9-gh97i:des;ihgd9gdgca-gdadg:a7hia;idgaag-idiidb:7es dgihai;idgaag-gh97i:7es dgihai;ahai7:dd%;}aha.i7" - + "ac {ihgd9gdgca:#hhgggh;ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:7es;ehaahc9-gh97i:des;idgaag-idiidb:7es dgihai;idgaag-gh97i:7es dgihai;ahai7:dd%;}aha.i7acfida9 {ehaahc9-ide:des;ehaahc9-idiidb:" - + "des;ehaahc9-aasi:9ses;ehaahc9-gh97i:des;ahai7:dd%;}aha.aaha {ihgd9gdgca:#hhgggh;ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:7es;ehaahc9-gh97i:des;idgaag-idiidb:7es dgihai;idgaag-gh97i:7es dgihai;a" - + "hai7:dd%;}aha.aahafida9 {ehaahc9-ide:des;ehaahc9-idiidb:des;ehaahc9-aasi:9ses;ehaahc9-gh97i:des;ahai7:dd%;}aha.haafhiae {ehaahc9-ide:9ses;ehaahc9-idiidb:9ses;iasi-hah9c:gaciag;}aha.casifhiae {ihgd9gd" - + "gca:#hhgggh;ehaahc9-ide:7es;ehaahc9-idiidb:7es;ehaahc9-aasi:7es;ehaahc9-gh97i:des;idgaag-idiidb:7es dgihai;idgaag-gh97i:7es dgihai;ahai7:dd%;}aha.hsfida9 {ehaahc9-ide:des;ehaahc9-idiidb:des;ehaahc9-aa" - + "si:9ses;ehaahc9-gh97i:des;ahai7:dd%;}aha.ehghbaiagh {ehaahc9-ide:des;ehaahc9-idiidb:des;ehaahc9-aasi:9ses;ehaahc9-gh97i:des;ahai7:dd%;}aha.eh9afida9 {ehaahc9-ide:9ses;ehaahc9-aasi:9ses;}aha.h7dgifhgi" - + "hdcfchba {sdci-hh7a:9ss%;}aha.h7dgifaaghhhdcfchba {sdci-hh7a:9ss%;}.gahaagfcdihshghihdc {ahheah9:cdca;}.gahaaghaabaci {edhhihdc:hihdagia;ide:-9es;aasi:-9ses;7ah97i:9es;ahca-7ah97i:9es;idgaag:cdca;eh" - + "aahc9:ses;bhg9hc:ses;ahai7:9es;daagsada:7haaac;}ihiaa.dig i7 aha {edhhihdc:gaahihaa;ide:-9ses;7ah97i:9es;daagsada:7haaac;}.idaaehiaah ahiaa,.ahiaa {sdci-aah97i:idaa;sdci-hh7a:9ab;}shaaahai.ghahddgdge " - + "{ehaahc9:ses;idgaag:cdca;iasi-hah9c:aasi;}shaaahai.ghahddgdge aa9aca {ehaahc9:ses;}shaaahai.ghahddgdge aha {ehaahc9-aasi:7ses;ehaahc9-idiidb:des;}shaaahai.ghahddgdge ahiaa {ahheah9:iadgd;bhg9hc-aasi:9" - + "79.ihiaa,77.ihiaa {bhg9hc:ses;}.9gh-bdagaa,.ahdhfihi7haa9dciaci,.hahgg7fid9,.haahcgaafhahgg7 {7ddb:9;}.eghcghehaehhi {ahhi-hi9aa-i9ea:cdca;}.ihiehhi igiidc,.aaaaa7fbacg igiidc,.aaaaahfbacg igiidc,.ihh" - + "hgsgiidc,.iddaihgfigiidc,.igahagggbihfahgchba hcegi,.shaaa9hiabsgdahagsgaa hcegi {ahai7:hgid;daagsada:ahhhiaa;}.gbaiaeaahahc9h {ehaahc9-aasi:7ses;sdci-aah97i:idaa;sdci-hh7a:9ss%;}.9ghaids {ahai7:9ss%;" - + "sdci-shbha9:sh7dbh,dacaah,hhch-haghs;sdci-hh7a:9ss%;ehaahc9:ses;bhg9hc:ses;idgaag-idiidb:ses;idgaag-aasi:ses;idgaag-gh97i:ses;ihgd9gdgca-gdadg:#gshghg;daagsada:7haaac;}.digids hcegi {ggghdg:aashgai;}." - + "bgaihehhi {sadhi:aasi;ahheah9:hcahca;ehaahc9:s 7ses;bhg9hc:s;}.bgaihehhi .cd7hagah {ahhi-hi9aa:cdca;}.s9fgaciagidhhihdc {bhg9hc:ses hgid;}.ahdhfeh9a9dciaci {ihgd9gdgca-hbh9a:gga(hi9aah/ahdhfid9.ge9);i" - + "hgd9gdgca-gaeahi:gaeahi-9;}shca9haghaaaaagihdc,.asfhahgg7fid9,#aaaaa7,.aaaaa7fihifshaafaasi,.iddaihg,.iddaihgfid9,.aaaaa7fihih,.aaaaa7fbacg .ihifbha,.aaaaa7fihifshaafgh97i,aha.aaaaa7fhgihaa .ihifbha,." - + "ahdhfaaaaa9,.gdagbcf7ahaagfadc9,.ahdhfeh9ashiaa,.ahdhfeh9agddiag,.igahagggbifehi7fgdcihhcag,.ggggacifehi7figiidch,.hahgg7fids,#aaaaah,.aaaaahfchah9hihdc,.aaaaahfhgihaa .ihifbha,.ahdhfihi .ihisd9,.ahdh" - + "fihi .haaagiaa .ihisd9,.ahdhfihighaa,#aaaaa9,.aaaaa9,aha.9ghaids .s7ag,aha.9ghaids .sig,aha.9ghaids .sig ia,.gdagbcf7ahaag,.ihifhbhaafid9,.ihifhbhaafdssfid9,.eagbfihifhaaagiaa .ihifhbhaafid9,.ahdhfaaa" - + "aah {ihgd9gdgca-hbh9a:gga(heghiahfgfd.ec9);ihgd9gdgca-gaeahi:gaeahi-s;}shca9haghaaaaagihdc {ihgd9gdgca-edhhihdc:-ses -ses;7ah97i:9gces;}.asfhahgg7fid9 {ihgd9gdgca-edhhihdc:-ses -9gces;7ah97i:97ses;}#a" - + "aaaa7,.aaaaa7fihifshaafaasi {ihgd9gdgca-edhhihdc:-ses -7aces;7ah97i:gces;}.iddaihg,.iddaihgfid9 {ihgd9gdgca-edhhihdc:-ses -h9aes;7ah97i:gces;}a.aaaaa7fihih,.aaaaa7fbacg .ihifbha,.aaaaa7fihifshaafgh97i" - + " {ihgd9gdgca-edhhihdc:-ses -hages;7ah97i:hhes;}aha.aaaaa7fhgihaa .ihifbha {ihgd9gdgca-edhhihdc:-ses -hdbes;7ah97i:hhes;}.ahdhfaaaaa9 {ihgd9gdgca-edhhihdc:-ses -ghses;7ah97i:h7es;}.gdagbcf7ahaagfadc9 {" - + "ihgd9gdgca-edhhihdc:-ses -ga7es;7ah97i:hses;}.ahdhfeh9ashiaa {ihgd9gdgca-edhhihdc:-ses -gd7es;7ah97i:7des;}.hahgg7fgefgh97ifgdgcag {ihgd9gdgca-edhhihdc:-h9es -99ges;ahai7:9ses;7ah97i:ces;}.hahgg7fid9 " - + "{ihgd9gdgca-hbh9a:gga(hi9aah/haahcgaafehcaafid9.ge9);ihgd9gdgca-gaeahi:gaeahi-s;}.eh9afihiaa,aha.eh9afgdcigdah,aha.sdgbfgdcigdah,aha.9gh-ahhad9 aha.9gh-bdagaa aha.si,aha.gh97ifeh9afgdciaci aha.9gh-bda" - + "gaa aha.7a,aha.ggahiahh7hga9dciaciagah aha.9gh-bdagaa aha.si,aha.cdihs99dciaciagah aha.9gh-bdagaa aha.si,aha.agdesdshh7hga9dciaciagah aha.9gh-bdagaa aha.si,.sdgbfihiaafihg,aha.9gh-ahhad9 aha.9gh-bdaga" - + "a aha.7a,.hcaggaacfihiaashg,.ihiaaf7ahaag,.gh97ifihiaaf7ahaag,.aasifihiaaf7ahaag,.igiidcfid9,.hchgihaa .igiidcfid9 {ihgd9gdgca-hbh9a:gga(ihhaaeghiahfgfd.ec9);ihgd9gdgca-gaeahi:gaeahi-s;}.eh9afihiaa,ah" - + "a.eh9afgdcigdah,aha.sdgbfgdcigdah,aha.9gh-ahhad9 aha.9gh-bdagaa aha.si,aha.gh97ifeh9afgdciaci aha.9gh-bdagaa aha.7a,aha.ggahiahh7hga9dciaciagah aha.9gh-bdagaa aha.si,aha.cdihs99dciaciagah aha.9gh-bdag" - + "aa aha.si,aha.agdesdshh7hga9dciaciagah aha.9gh-bdagaa aha.si {ihgd9gdgca-edhhihdc:-ses -ses;7ah97i:7des;}.sdgbfihiaafihg,aha.9gh-ahhad9 aha.9gh-bdagaa aha.7a,.hcaggaacfihiaashg,.ihiaaf7ahaag,.gh97ifih" - + "iaaf7ahaag,.aasifihiaaf7ahaag {ihgd9gdgca-edhhihdc:-ses -7des;7ah97i:7aes;}.igiidcfid9 {edhhihdc:gaahihaa;bhg9hc-gh97i:7es;ehaahc9-gh97i:9hes;ehaahc9-aasi:9hes;bhg9hc-aasi:7es;a7hia-hehga:cdaghe;ihgd9" - + "gdgca-edhhihdc:-ses -ddes;7ah97i:77es;}.hchgihaa .igiidcfid9 {ihgd9gdgca-edhhihdc:-ses -bbes;7ah97i:77es;}.aashgaifad9d,.hchgihaa .igiidcfaasi,.igiidcfaasi,.igiidcfgh97i,.hchgihaa .igiidcfgh97i,.hgdcf" - + "hcsd,.hgdcfaggdg {ihgd9gdgca-hbh9a:gga(ihhaaeghiahfghgh.ec9);ihgd9gdgca-gaeahi:cd-gaeahi;}.aashgaifad9d {ihgd9gdgca-edhhihdc:-ses -ses;ahai7:c7es;7ah97i:hdes;}.hchgihaa .igiidcfaasi {ihgd9gdgca-edhhih" - + "dc:-c7es -ses;ahai7:9ges;7ah97i:77es;}.igiidcfaasi {edhhihdc:hihdagia;ide:ses;aasi:-7es;ihgd9gdgca-edhhihdc:-daes -ses;ahai7:9hes;7ah97i:77es;}.igiidcfgh97i {edhhihdc:hihdagia;ide:ses;gh97i:-7es;ihgd9" - + "gdgca-edhhihdc:-9sdes -ses;ahai7:9hes;7ah97i:77es;}.hchgihaa .igiidcfgh97i {ihgd9gdgca-edhhihdc:-977es -ses;ahai7:9hes;7ah97i:77es;}.hgdcfhcsd {ihgd9gdgca-edhhihdc:-c7es -77es;ahai7:9ges;7ah97i:9des;}" - + ".hgdcfaggdg {ihgd9gdgca-edhhihdc:-daes -77es;ahai7:9ges;7ah97i:9ges;}"; - - var largeNewValue = largeTest, - len = largeTest.length, - count = nextRandom() % 20, - removeBound = len-(count*100), - logData = []; - for (; count > 0; count--) { - var removePos = nextRandom() % removeBound; - var removeLength = 1+nextRandom()%100; - logData.push("(" + removePos + ", " + removeLength + ")"); - largeNewValue = largeNewValue.substring(0, removePos) - + largeNewValue.substring(removePos + removeLength); - } - log("len: " + len + " count: " + count + " removed ( " + logData.join(", ") + " )"); - - diffResult = diff.diffWords(largeTest, largeNewValue); - log("diffResult length: " + diffResult.length); - var removeCount = 0; - var removeChanges = [], addChanges = [], testChanges = []; - for (var i = 0; i < diffResult.length; i++) { - if (diffResult[i].removed) { - log("remove Change " + i, diffResult[i]); - removeChanges.push(diffResult[i].value); - } else if (diffResult[i].added) { - log("add Change " + i, diffResult[i]); - addChanges.push(diffResult[i].value); - } else { - log("no Change " + i, diffResult[i]); - removeChanges.push(diffResult[i].value); - addChanges.push(diffResult[i].value); - } - } - - log("diffResult remove length: " + removeCount); - assert.equal(largeTest.replace(/s+/g, ""), removeChanges.join("").replace(/s+/g, ""), "New Diff results match"); - assert.equal(largeNewValue.replace(/s+/g, ""), addChanges.join("").replace(/s+/g, ""), "Old Diff results match"); -}; - -exports['Patch'] = function() { - // Create patch - var oldFile = - "value\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "remove value\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "remove value\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "value\n" - + "context\n" - + "context"; - var newFile = - "new value\n" - + "new value 2\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "add value\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "new value\n" - + "new value 2\n" - + "context\n" - + "context"; - var expectedResult = - "Index: testFileName\n" - + "===================================================================\n" - + "--- testFileName\tOld Header\n" - + "+++ testFileName\tNew Header\n" - + "@@ -1,5 +1,6 @@\n" - + "+new value\n" - + "+new value 2\n" - + "-value\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "@@ -7,9 +8,8 @@\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "-remove value\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "@@ -17,20 +17,21 @@\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "-remove value\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "+add value\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "+new value\n" - + "+new value 2\n" - + "-value\n" - + " context\n" - + " context\n" - + "\\ No newline at end of file\n"; - - diffResult = diff.createPatch("testFileName", oldFile, newFile, "Old Header", "New Header"); - assert.equal( - expectedResult, - diffResult); - - expectedResult = - "Index: testFileName\n" - + "===================================================================\n" - + "--- testFileName\tOld Header\n" - + "+++ testFileName\tNew Header\n"; - diffResult = diff.createPatch("testFileName", oldFile, oldFile, "Old Header", "New Header"); - assert.equal( - expectedResult, - diffResult, - "Patch same diffResult Value"); -}; - -exports['convertToDMP'] = function() { - diffResult = diff.diffWords("New Value ", "New ValueMoreData "); - - assert.deepEqual( - [[0,'New '],[1,'ValueMoreData'],[-1,'Value'],[0,' ']], - diff.convertChangesToDMP(diffResult), - "DMP conversion of diffResult"); -}; - - - - -exports['lastLineChangedMergeTest'] = function() { - assert.equal( - 'line2\n'+ - 'line3\n'+ - 'line4\n'+ - 'line5\n', - diff.applyPatch( - 'line2\n'+ - 'line3\n'+ - 'line5\n' - , - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,3 +1,4 @@\n' - + ' line2\n' - + ' line3\n' - + '+line4\n' - + ' line5\n') - ); - - assert.equal( - 'line2\nline3\nline4\nline5\n', - diff.applyPatch('line2\nline3\nline4\n','Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,3 +1,4 @@\n' - + ' line2\n' - + ' line3\n' - + ' line4\n' - + '+line5\n')); - - assert.equal('line1\nline2\nline3\nline44\n', - diff.applyPatch('line1\nline2\nline3\nline4\n', - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,4 @@\n' - + ' line1\n' - + ' line2\n' - + ' line3\n' - + '+line44\n' - + '-line4\n')); - - assert.equal('line1\nline2\nline3\nline44\nline5\n', - diff.applyPatch('line1\nline2\nline3\nline4\n', - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,5 @@\n' - + ' line1\n' - + ' line2\n' - + ' line3\n' - + '+line44\n' - + '+line5\n' - + '-line4\n')); -}; - -exports['EOFNLMergeTest'] = function() { - assert.equal('line1\nline2\nline3\nline4', - diff.applyPatch('line1\nline2\nline3\nline4\n', - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,4 @@\n' - + ' line1\n' - + ' line2\n' - + ' line3\n' - + '+line4\n' - + '\\ No newline at end of file\n' - + '-line4\n')); - - assert.equal('line1\nline2\nline3\nline4\n', - diff.applyPatch('line1\nline2\nline3\nline4', - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,4 @@\n' - + ' line1\n' - + ' line2\n' - + ' line3\n' - + '+line4\n' - + '-line4\n' - + '\\ No newline at end of file\n')); - - assert.equal('line1\nline2\nline3\nline4', - diff.applyPatch('line11\nline2\nline3\nline4', - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,4 +1,4 @@\n' - + '+line1\n' - + '-line11\n' - + ' line2\n' - + ' line3\n' - + ' line4\n' - + '\\ No newline at end of file\n')); - - assert.equal('line1\nline2\nline3\nline4\nline4\nline4\nline4', - diff.applyPatch('line11\nline2\nline3\nline4\nline4\nline4\nline4', - 'Index: test\n' - + '===================================================================\n' - + '--- test\theader1\n' - + '+++ test\theader2\n' - + '@@ -1,5 +1,5 @@\n' - + '+line1\n' - + '-line11\n' - + ' line2\n' - + ' line3\n' - + ' line4\n' - + ' line4\n')); -}; - -exports['PatchMerge'] = function() { - // Create patch - var oldFile = - "value\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "remove value\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "remove value\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "value\n" - + "context\n" - + "context"; - var newFile = - "new value\n" - + "new value 2\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "add value\n" - + "context\n" - + "context\n" - + "context\n" - + "context\n" - + "new value\n" - + "new value 2\n" - + "context\n" - + "context"; - var diffFile = - "Index: testFileName\n" - + "===================================================================\n" - + "--- testFileName\tOld Header\n" - + "+++ testFileName\tNew Header\n" - + "@@ -1,5 +1,6 @@\n" - + "+new value\n" - + "+new value 2\n" - + "-value\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "@@ -7,9 +8,8 @@\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "-remove value\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "@@ -17,20 +17,21 @@\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "-remove value\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "+add value\n" - + " context\n" - + " context\n" - + " context\n" - + " context\n" - + "+new value\n" - + "+new value 2\n" - + "-value\n" - + " context\n" - + " context\n" - + "\\ No newline at end of file\n"; - - assert.equal( - newFile, - diff.applyPatch(oldFile, diffFile)); - - diffFile = - "Index: testFileName\n" - + "===================================================================\n" - + "--- testFileName\tOld Header\n" - + "+++ testFileName\tNew Header\n"; - assert.equal( - oldFile, - diff.applyPatch(oldFile, diffFile), - "Patch same diffResult Value"); - -}; +describe('#diffLines', function() { + it('should diff lines', function() { + var diffResult = diff.diffLines( + 'line\nold value\nline', + 'line\nnew value\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\nnew value\nold value\nline'); + }); + it('should the same lines in diff', function() { + var diffResult = diff.diffLines( + 'line\nvalue\nline', + 'line\nvalue\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\nvalue\nline'); + }); + + it('should handle shorespace', function() { + var diffResult = diff.diffLines( + 'line\nvalue \nline', + 'line\nvalue\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\nvalue\nvalue \nline'); + }); +}); + +describe('convertToDMP', function() { + it('should output diff-match-patch format', function() { + var diffResult = diff.diffWords('New Value ', 'New ValueMoreData '); + + diff.convertChangesToDMP(diffResult).should.eql( + [[0,'New '],[1,'ValueMoreData'],[-1,'Value'],[0,' ']]); + }); +}); diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 00000000..9c3fba06 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,2 @@ +--require should +--reporter spec From 54a326b3215944c8258d622079e30b53f0864045 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 19 Oct 2012 19:33:08 -0500 Subject: [PATCH 017/527] Cleanup whitespace --- diff.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/diff.js b/diff.js index 5f03f815..b9417b9a 100644 --- a/diff.js +++ b/diff.js @@ -37,7 +37,6 @@ var JsDiff = (function() { return n; } - var fbDiff = function(ignoreWhitespace) { this.ignoreWhitespace = ignoreWhitespace; }; @@ -127,7 +126,7 @@ var JsDiff = (function() { while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { newPos++; oldPos++; - + this.pushComponent(basePath.components, newString[newPos], undefined, undefined); } basePath.newPos = newPos; @@ -149,24 +148,24 @@ var JsDiff = (function() { return value; } }; - + var CharDiff = new fbDiff(); - + var WordDiff = new fbDiff(true); WordDiff.tokenize = function(value) { return removeEmpty(value.split(/(\s+|\b)/)); }; - + var CssDiff = new fbDiff(true); CssDiff.tokenize = function(value) { return removeEmpty(value.split(/([{}:;,]|\s+)/)); }; - + var LineDiff = new fbDiff(); LineDiff.tokenize = function(value) { return value.split(/^/m); }; - + return { diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, @@ -214,7 +213,7 @@ var JsDiff = (function() { var prev = diff[i-1]; oldRangeStart = oldLine; newRangeStart = newLine; - + if (prev) { curRange = contextLines(prev.lines.slice(-4)); oldRangeStart -= curRange.length; From acd68e691e0699ebd644bbfd26f3d31c54abdd45 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 19 Oct 2012 21:18:54 -0500 Subject: [PATCH 018/527] Style updates --- diff.js | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/diff.js b/diff.js index b9417b9a..8cab52c2 100644 --- a/diff.js +++ b/diff.js @@ -193,7 +193,7 @@ var JsDiff = (function() { function eofNL(curRange, i, current) { var last = diff[diff.length-2], isLast = i === diff.length-2, - isLastOfType = i === diff.length-3 && (current.added === !last.added || current.removed === !last.removed); + isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); // Figure out if this is the last line for the given file and missing NL if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { @@ -263,6 +263,7 @@ var JsDiff = (function() { var diff = []; var remEOFNL = false, addEOFNL = false; + for (var i = (diffstr[0][0]=="I"?4:0); i < diffstr.length; i++) { if(diffstr[i][0] == "@") { var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); @@ -273,22 +274,22 @@ var JsDiff = (function() { newlength:meh[4], newlines:[] }); - } else - if(diffstr[i][0] == "+") { + } else if(diffstr[i][0] == '+') { diff[0].newlines.push(diffstr[i].substr(1)); - } else - if(diffstr[i][0] == "-") { + } else if(diffstr[i][0] == '-') { diff[0].oldlines.push(diffstr[i].substr(1)); - } else - if(diffstr[i][0] == " ") { + } else if(diffstr[i][0] == ' ') { diff[0].newlines.push(diffstr[i].substr(1)); diff[0].oldlines.push(diffstr[i].substr(1)); - } else - if(diffstr[i][0] == "\\") { - if(diffstr[i-1][0] == "+") remEOFNL = true; - if(diffstr[i-1][0] == "-") addEOFNL = true; + } else if(diffstr[i][0] == '\\') { + if (diffstr[i-1][0] == '+') { + remEOFNL = true; + } else if(diffstr[i-1][0] == '-') { + addEOFNL = true; + } } - }; + } + var str = oldStr.split("\n"); for (var i = diff.length - 1; i >= 0; i--) { var d = diff[i]; @@ -296,12 +297,18 @@ var JsDiff = (function() { if(str[d.start-1+j] != d.oldlines[j]) { return false; } - }; + } Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); } - if(remEOFNL) while(str[str.length-1]=="") str.pop(); - else if(addEOFNL) str.push(""); - return str.join("\n"); + + if (remEOFNL) { + while (!str[str.length-1]) { + str.pop(); + } + } else if (addEOFNL) { + str.push(''); + } + return str.join('\n'); }, convertChangesToXML: function(changes){ From bf0075e1d4cfa0663bc20b8eb341f0e3f103bb4e Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 19 Oct 2012 21:19:21 -0500 Subject: [PATCH 019/527] 1.0.4 --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a5046757..91957d07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "1.0.3", + "version": "1.0.4", "description": "A javascript text diff implementation.", "keywords": [ "diff", @@ -34,5 +34,6 @@ "devDependencies": { "mocha": "~1.6", "should": "~1.2" - } + }, + "optionalDependencies": {} } From c1f3f5992e1818f8b069c7cc30198648bb4c403d Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 6 Jan 2013 17:55:56 -0600 Subject: [PATCH 020/527] Update for jshint --- .jshintrc | 42 +++++++++++++---------- diff.js | 85 +++++++++++++++++++++++----------------------- test/applyPatch.js | 2 +- test/diffTest.js | 7 +--- 4 files changed, 69 insertions(+), 67 deletions(-) diff --git a/.jshintrc b/.jshintrc index b8ab2b7a..d57cb294 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,35 +1,41 @@ +/*dotfiles node, mocha*/ { - "predef": [ - "describe", "it", "before", "after", "beforeEach", "afterEach" - ], - "node": true, "es5": true, "esnext": true, "browser": false, - "jquery": false, - "bitwise": true, "curly": true, "eqeqeq": true, - "evil": false, "forin": true, - "immed": true, + "immed": false, "latedef": false, - "laxbreak": true, "newcap": true, "noarg": true, - "noempty": false, - "nonew": true, + "noempty": true, + "nonew": false, "plusplus": false, + "quotmark": "single", + "regexp": false, "undef": true, + "unused": true, "strict": false, + "trailing": true, + "maxparams": 4, + "asi": false, + "boss": false, + "expr": true, + "laxbreak": true, + "loopfunc": true, "shadow": true, - "trailing": false, - "globalstrict": true, "nonstandard": true, - "white": false, - "indent": 2, - - "onevar": false -} + "onevar": false, + "predef": [ + "describe", + "it", + "before", + "after", + "beforeEach", + "afterEach" + ] +} \ No newline at end of file diff --git a/diff.js b/diff.js index 8cab52c2..1924c389 100644 --- a/diff.js +++ b/diff.js @@ -2,19 +2,20 @@ /* * Text diff implementation. - * + * * This library supports the following APIS: * JsDiff.diffChars: Character by character diff * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace * JsDiff.diffLines: Line based diff - * + * * JsDiff.diffCss: Diff targeted at CSS content - * + * * These methods are based on the implementation proposed in * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 */ var JsDiff = (function() { + /*jshint maxparams: 5*/ function clonePath(path) { return { newPos: path.newPos, components: path.components.slice(0) }; } @@ -29,21 +30,21 @@ var JsDiff = (function() { } function escapeHTML(s) { var n = s; - n = n.replace(/&/g, "&"); - n = n.replace(//g, ">"); - n = n.replace(/"/g, """); + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); return n; } - var fbDiff = function(ignoreWhitespace) { + var Diff = function(ignoreWhitespace) { this.ignoreWhitespace = ignoreWhitespace; }; - fbDiff.prototype = { + Diff.prototype = { diff: function(oldString, newString) { // Handle the identity case (this is due to unrolling editLength == 0 - if (newString == oldString) { + if (newString === oldString) { return [{ value: newString }]; } if (!newString) { @@ -138,7 +139,7 @@ var JsDiff = (function() { if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { return true; } else { - return left == right; + return left === right; } }, join: function(left, right) { @@ -149,19 +150,19 @@ var JsDiff = (function() { } }; - var CharDiff = new fbDiff(); + var CharDiff = new Diff(); - var WordDiff = new fbDiff(true); + var WordDiff = new Diff(true); WordDiff.tokenize = function(value) { return removeEmpty(value.split(/(\s+|\b)/)); }; - var CssDiff = new fbDiff(true); + var CssDiff = new Diff(true); CssDiff.tokenize = function(value) { return removeEmpty(value.split(/([{}:;,]|\s+)/)); }; - var LineDiff = new fbDiff(); + var LineDiff = new Diff(); LineDiff.tokenize = function(value) { return value.split(/^/m); }; @@ -176,16 +177,16 @@ var JsDiff = (function() { createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { var ret = []; - ret.push("Index: " + fileName); - ret.push("==================================================================="); - ret.push("--- " + fileName + (typeof oldHeader === "undefined" ? "" : "\t" + oldHeader)); - ret.push("+++ " + fileName + (typeof newHeader === "undefined" ? "" : "\t" + newHeader)); + ret.push('Index: ' + fileName); + ret.push('==================================================================='); + ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); + ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); var diff = LineDiff.diff(oldStr, newStr); if (!diff[diff.length-1].value) { diff.pop(); // Remove trailing newline add } - diff.push({value: "", lines: []}); // Append an empty value to make cleanup easier + diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier function contextLines(lines) { return lines.map(function(entry) { return ' ' + entry; }); @@ -205,7 +206,7 @@ var JsDiff = (function() { oldLine = 1, newLine = 1; for (var i = 0; i < diff.length; i++) { var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, "").split("\n"); + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); current.lines = lines; if (current.added || current.removed) { @@ -220,7 +221,7 @@ var JsDiff = (function() { newRangeStart -= curRange.length; } } - curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?"+":"-") + entry; })); + curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; })); eofNL(curRange, i, current); if (current.added) { @@ -238,9 +239,9 @@ var JsDiff = (function() { // end the range and output var contextSize = Math.min(lines.length, 4); ret.push( - "@@ -" + oldRangeStart + "," + (oldLine-oldRangeStart+contextSize) - + " +" + newRangeStart + "," + (newLine-newRangeStart+contextSize) - + " @@"); + '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) + + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) + + ' @@'); ret.push.apply(ret, curRange); ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); if (lines.length <= 4) { @@ -259,13 +260,13 @@ var JsDiff = (function() { }, applyPatch: function(oldStr, uniDiff) { - var diffstr = uniDiff.split("\n"); + var diffstr = uniDiff.split('\n'); var diff = []; var remEOFNL = false, addEOFNL = false; - for (var i = (diffstr[0][0]=="I"?4:0); i < diffstr.length; i++) { - if(diffstr[i][0] == "@") { + for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { + if(diffstr[i][0] === '@') { var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); diff.unshift({ start:meh[3], @@ -274,27 +275,27 @@ var JsDiff = (function() { newlength:meh[4], newlines:[] }); - } else if(diffstr[i][0] == '+') { + } else if(diffstr[i][0] === '+') { diff[0].newlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] == '-') { + } else if(diffstr[i][0] === '-') { diff[0].oldlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] == ' ') { + } else if(diffstr[i][0] === ' ') { diff[0].newlines.push(diffstr[i].substr(1)); diff[0].oldlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] == '\\') { - if (diffstr[i-1][0] == '+') { + } else if(diffstr[i][0] === '\\') { + if (diffstr[i-1][0] === '+') { remEOFNL = true; - } else if(diffstr[i-1][0] == '-') { + } else if(diffstr[i-1][0] === '-') { addEOFNL = true; } } } - var str = oldStr.split("\n"); + var str = oldStr.split('\n'); for (var i = diff.length - 1; i >= 0; i--) { var d = diff[i]; for (var j = 0; j < d.oldlength; j++) { - if(str[d.start-1+j] != d.oldlines[j]) { + if(str[d.start-1+j] !== d.oldlines[j]) { return false; } } @@ -316,20 +317,20 @@ var JsDiff = (function() { for ( var i = 0; i < changes.length; i++) { var change = changes[i]; if (change.added) { - ret.push(""); + ret.push(''); } else if (change.removed) { - ret.push(""); + ret.push(''); } ret.push(escapeHTML(change.value)); if (change.added) { - ret.push(""); + ret.push(''); } else if (change.removed) { - ret.push(""); + ret.push(''); } } - return ret.join(""); + return ret.join(''); }, // See: http://code.google.com/p/google-diff-match-patch/wiki/API @@ -344,6 +345,6 @@ var JsDiff = (function() { }; })(); -if (typeof module !== "undefined") { +if (typeof module !== 'undefined') { module.exports = JsDiff; } diff --git a/test/applyPatch.js b/test/applyPatch.js index a21ce9c7..b7d0207e 100644 --- a/test/applyPatch.js +++ b/test/applyPatch.js @@ -173,7 +173,7 @@ describe('#applyPatch', function() { + 'value\n' + 'context\n' + 'context'; - var newFile = + var newFile = 'new value\n' + 'new value 2\n' + 'context\n' diff --git a/test/diffTest.js b/test/diffTest.js index 91c0765b..36a071d7 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -1,11 +1,6 @@ const VERBOSE = false; -var assert = require('assert'), - diff = require('../diff'); - -function log() { - VERBOSE && console.log.apply(console, arguments); -} +var diff = require('../diff'); describe('#diffWords', function() { it('should diff whitespace', function() { From 1f4bf7a708fc1121bb49e3b10d2ec7218c52a210 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 6 Jan 2013 17:57:06 -0600 Subject: [PATCH 021/527] Expose Diff object Fixes #15 --- diff.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/diff.js b/diff.js index 1924c389..4cfe05ba 100644 --- a/diff.js +++ b/diff.js @@ -168,6 +168,8 @@ var JsDiff = (function() { }; return { + Diff: Diff, + diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, From 77c3a11a8d57906c2b848421790b6f4477d9dd1e Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Fri, 28 Jun 2013 11:14:19 -0400 Subject: [PATCH 022/527] adding an `.npmignore` file should slightly help speed up `npm i` time, and is generally a good practice --- .npmignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .npmignore diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..e95a11e5 --- /dev/null +++ b/.npmignore @@ -0,0 +1,5 @@ +test/ +.jshintrc +.travis.yml +index.html +style.css \ No newline at end of file From 4661b9ddb6d3f995b29cc6cb4523ce20d9b28212 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 1 Jul 2013 23:00:46 -0500 Subject: [PATCH 023/527] 1.0.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91957d07..e8ec3c3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "1.0.4", + "version": "1.0.5", "description": "A javascript text diff implementation.", "keywords": [ "diff", From f807c204cc60279aa6805b00ebd575724869af96 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 20 Jul 2013 13:42:03 +0200 Subject: [PATCH 024/527] Explicitly define contents of npm package Every byte counts for popular packages like this one. --- .npmignore | 5 ----- package.json | 5 ++++- 2 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 .npmignore diff --git a/.npmignore b/.npmignore deleted file mode 100644 index e95a11e5..00000000 --- a/.npmignore +++ /dev/null @@ -1,5 +0,0 @@ -test/ -.jshintrc -.travis.yml -index.html -style.css \ No newline at end of file diff --git a/package.json b/package.json index e8ec3c3c..035dbaf8 100644 --- a/package.json +++ b/package.json @@ -35,5 +35,8 @@ "mocha": "~1.6", "should": "~1.2" }, - "optionalDependencies": {} + "optionalDependencies": {}, + "files": [ + "diff.js" + ] } From bb96fe9e0b565a7343bc5a2ce62628fe43bee5fe Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 30 Aug 2013 01:37:27 -0500 Subject: [PATCH 025/527] Add release notes --- release-notes.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 release-notes.md diff --git a/release-notes.md b/release-notes.md new file mode 100644 index 00000000..e80b8b3d --- /dev/null +++ b/release-notes.md @@ -0,0 +1,11 @@ +# Release Notes + +## Development + +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.6...master) + +## v1.0.6 - August 30th, 2013 + +- [#19](https://github.com/kpdecker/jsdiff/pull/19) - Explicitly define contents of npm package ([@sindresorhus](https://api.github.com/users/sindresorhus) + +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.5...v1.0.6) From 95dfbd1b0ced4b4b3ad3853685dd516a8f308dd4 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 30 Aug 2013 01:37:36 -0500 Subject: [PATCH 026/527] 1.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 035dbaf8..e3a1d187 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "1.0.5", + "version": "1.0.6", "description": "A javascript text diff implementation.", "keywords": [ "diff", From 887e2cde1c7df4948e5e5541ff1a9597dcdd0045 Mon Sep 17 00:00:00 2001 From: Andreas Lind Petersen Date: Wed, 11 Sep 2013 22:37:48 +0200 Subject: [PATCH 027/527] Added WordWithSpaceDiff. Compares word by word, but doesn't ignore whitespace differences in between. --- diff.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/diff.js b/diff.js index 4cfe05ba..a34c22a0 100644 --- a/diff.js +++ b/diff.js @@ -153,7 +153,8 @@ var JsDiff = (function() { var CharDiff = new Diff(); var WordDiff = new Diff(true); - WordDiff.tokenize = function(value) { + var WordWithSpaceDiff = new Diff(); + WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { return removeEmpty(value.split(/(\s+|\b)/)); }; @@ -172,6 +173,7 @@ var JsDiff = (function() { diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, + diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); }, diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); }, From 243a52602f41af49c79901633341e2597343384a Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 11 Sep 2013 23:22:19 -0500 Subject: [PATCH 028/527] Add 0.10 to travis tests --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2a9b9e68..4585bf2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,4 @@ node_js: - 0.4 - 0.6 - 0.8 + - "0.10" From c95c1e9136c7b7804501f078af363bbfa4c30133 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 11 Sep 2013 23:26:50 -0500 Subject: [PATCH 029/527] Add tests for diffWordsWithSpace --- test/diffTest.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/diffTest.js b/test/diffTest.js index 36a071d7..91dafe1c 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -57,6 +57,18 @@ describe('#diffWords', function() { }); }); +describe('#diffWordsWithSpace', function() { + it('should diff whitespace', function() { + var diffResult = diff.diffWordsWithSpace('New Value', 'New ValueMoreData'); + diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData Value'); + }); + + it('should diff multiple whitespace values', function() { + var diffResult = diff.diffWordsWithSpace('New Value ', 'New ValueMoreData '); + diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData Value '); + }); +}); + // CSS Diff describe('#diffCss', function() { it('should diff css', function() { From f6c414a76cf9d14d93af194a4f7c048b3a9e76ab Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 11 Sep 2013 23:27:47 -0500 Subject: [PATCH 030/527] Update release notes --- release-notes.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/release-notes.md b/release-notes.md index e80b8b3d..6989e2bb 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,15 @@ ## Development -[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.6...master) +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.7...master) + +## v1.0.7 - September 11th, 2013 + +- [#22](https://github.com/kpdecker/jsdiff/pull/22) - Added variant of WordDiff that doesn't ignore whitespace differences ([@papandreou](https://api.github.com/users/papandreou) + +- Add 0.10 to travis tests - 243a526 + +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.6...v1.0.7) ## v1.0.6 - August 30th, 2013 From c6614d942c91cb0bbed977d90eb55d6c2d73a449 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 11 Sep 2013 23:28:00 -0500 Subject: [PATCH 031/527] v1.0.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e3a1d187..4c5d6fc0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "1.0.6", + "version": "1.0.7", "description": "A javascript text diff implementation.", "keywords": [ "diff", From 2552511cfd2ad9985f8668a2b3304597c6913dea Mon Sep 17 00:00:00 2001 From: airportyh Date: Tue, 5 Nov 2013 14:22:15 -0500 Subject: [PATCH 032/527] Code formatting. --- README.md | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 95bd8da2..56934d7e 100644 --- a/README.md +++ b/README.md @@ -17,50 +17,43 @@ or ## API -* JsDiff.diffChars(oldStr, newStr) - Diffs two blocks of text, comparing character by character. +* `JsDiff.diffChars(oldStr, newStr)` - diffs two blocks of text, comparing character by character. Returns a list of change objects (See below). -* JsDiff.diffWords(oldStr, newStr) - Diffs two blocks of text, comparing word by word. +* `JsDiff.diffWords(oldStr, newStr)` - diffs two blocks of text, comparing word by word. Returns a list of change objects (See below). -* JsDiff.diffLines(oldStr, newStr) - Diffs two blocks of text, comparing line by line. +* `JsDiff.diffLines(oldStr, newStr)` - diffs two blocks of text, comparing line by line. Returns a list of change objects (See below). -* JsDiff.diffCss(oldStr, newStr) - Diffs two blocks of text, comparing CSS tokens. +* `JsDiff.diffCss(oldStr, newStr)` - diffs two blocks of text, comparing CSS tokens. Returns a list of change objects (See below). -* JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader) - Creates a unified diff patch. +* `JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader)` - creates a unified diff patch. Parameters: - * fileName : String to be output in the filename sections of the patch - * oldStr : Original string value - * newStr : New string value - * oldHeader : Additional information to include in the old file header - * newHeader : Additional information to include in thew new file header + * `fileName` : String to be output in the filename sections of the patch + * `oldStr` : Original string value + * `newStr` : New string value + * `oldHeader` : Additional information to include in the old file header + * `newHeader` : Additional information to include in thew new file header -* JsDiff.applyPatch(oldStr, diffStr) - Applies a unified diff patch. +* `JsDiff.applyPatch(oldStr, diffStr)` - applies a unified diff patch. Return a string containing new version of provided data. -* convertChangesToXML(changes) - Converts a list of changes to a serialized XML format +* `convertChangesToXML(changes)` - converts a list of changes to a serialized XML format ### Change Objects Many of the methods above return change objects. These objects are consist of the following fields: -* value: Text content -* added: True if the value was inserted into the new string -* removed: True of the value was removed from the old string +* `value`: Text content +* `added`: True if the value was inserted into the new string +* `removed`: True of the value was removed from the old string Note that some cases may omit a particular flag field. Comparison on the flag fields should always be done in a truthy or falsy manner. From f07f830470a366d4e53eb681900b741f0b81cc05 Mon Sep 17 00:00:00 2001 From: airportyh Date: Tue, 5 Nov 2013 14:39:36 -0500 Subject: [PATCH 033/527] Added Node example. --- README.md | 29 ++++++++++++++++++++++++++++- examples/node_example.js | 17 +++++++++++++++++ examples/one.txt | 1 + examples/other.txt | 1 + images/node_example.png | Bin 0 -> 5654 bytes package.json | 3 ++- 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 examples/node_example.js create mode 100644 examples/one.txt create mode 100644 examples/other.txt create mode 100644 images/node_example.png diff --git a/README.md b/README.md index 56934d7e..a138c73f 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,34 @@ Many of the methods above return change objects. These objects are consist of th Note that some cases may omit a particular flag field. Comparison on the flag fields should always be done in a truthy or falsy manner. -## [Example](http://kpdecker.github.com/jsdiff) +## Examples + +Using in Node + +```js +require('colors') +var jsdiff = require('diff'); + +var one = 'beep boop' +var other = 'beep boob blah' + +var diff = jsdiff.diffChars(one, other) + +diff.forEach(function(part){ + // green for additions, red for deletions + // grey for common parts + var color = part.added ? 'green' : + part.removed ? 'red' : 'grey'; + process.stderr.write(part.value[color]); +}); + +console.log() +``` +Running the above program should yield + +Node Example + +* [Full online demo](http://kpdecker.github.com/jsdiff) ## License diff --git a/examples/node_example.js b/examples/node_example.js new file mode 100644 index 00000000..7cadcf5a --- /dev/null +++ b/examples/node_example.js @@ -0,0 +1,17 @@ +require('colors') +var jsdiff = require('../diff'); + +var one = 'beep boop' +var other = 'beep boob blah' + +var diff = jsdiff.diffChars(one, other) + +diff.forEach(function(part){ + // green for additions, red for deletions + // grey for common parts + var color = part.added ? 'green' : + part.removed ? 'red' : 'grey'; + process.stderr.write(part.value[color]); +}); + +console.log() \ No newline at end of file diff --git a/examples/one.txt b/examples/one.txt new file mode 100644 index 00000000..1c337236 --- /dev/null +++ b/examples/one.txt @@ -0,0 +1 @@ +blah blah \ No newline at end of file diff --git a/examples/other.txt b/examples/other.txt new file mode 100644 index 00000000..8e92e3a1 --- /dev/null +++ b/examples/other.txt @@ -0,0 +1 @@ +bee blah boop \ No newline at end of file diff --git a/images/node_example.png b/images/node_example.png new file mode 100644 index 0000000000000000000000000000000000000000..eb87034ab4b06f3a95efaf40743c3753c6c2c7ee GIT binary patch literal 5654 zcmV+x7U}7UP)4Tx07wn3mUmne*%rs|OnRdwgd!#Mj&uS@uK^UKgN={`LJJ`zh=_{F;tEz& zM6gj-nu2QqWG#p&wnag)>w+u_>T|ImDk^URkGP-rdHdhH^SSqa=XcMXIrq%m&*uO@ zu4fB{X|MtS=>oAR%-@R<6C20CJpoui1SDV%m~2j#&@(hN82n@Vco#s@#oHEAU;k+K zzZVp_yetj?P$;sTz|G>MBYYC+TRB3J7yy(HqU)~|3#H6Qm?lC3L^w~%Nwd6N$_cYv zJ7W_O=8f321Ar&9*`g!>NZp9e$mS#=en@{JY$f0(3jiR~5O(A6*<6Gp5H?Rw&yaFH z!o~@I^ON*9X0sDyKH2Of8PDzoc@lZCPjZ$pjlF8-=KndQr%8}}%``1!Bxj|D2O%>Z z$ypAE?HfMFd>%{s&1Q6=*eh&~lf^9QK4)~kBp`B*C8?2~bDWwHB(oPJEDD*^Ia%IF z?q{FP<$Euhvq@nGO279^3~UiX|6yL5zwEt2ai}a# zLE0kOUJ^xqG8P5R6<55DsjR%L+IFBm-hzj##<@?+oE8QF0Pr% zp`{ZBIji!32GoES&;!Q60*TH6xBw3%;y@4rB0wxy4mf}h(t!x91bJXRC;}y5JJ<~> zz(G(0>cLTP9JGNm-~zY|u7g`(06YSNUUpKqXKqQ~^~%4bZR9Y3KrU73zZ?Kts?N^bYz0qhK;jgEe78 zcs|U8J>eiY0$v6u!I^L_TnKN4%i(JH2;2%^fUm;?@F4sOoHu%VzF2{)(Go}4Zy}? zQ?RSCo3Z<`E!a+MA9ff!fy3d{aAr7HTnLVh6XS|-dvQl_=W+eG5!@#{39pT}!TaIk z@B(}Rz6{@pKZozfzrars6bJ?cXF>>pOIStNMyMrp5N;Ai2vbBwq7l)R7(q-St|yig zn~9f*kBRR|B$6)4iL{uMOv)#fla7(DkcLQ~$x38XvNw4tSwh}Qt|NDnACTWuC=^4A z2PKXorfj7&P%cuQQa;I1RR$wcvRj5=rt#D6aLXoOytr()1uDDsTQSqwcs1i=eP{~(`t5m2|qjXVen2Mt6 zQN5`g>IP~p^%8Z2hNT(NShN&c3GFDYmo~1fq->`grJSQ&seD#>kdC4o(pmI0`ZoG; z`d#`L6-^aS6`sl_m1dQGl}XiksvfF5)ne5a)dAHnY78}BwN$koYHeyy)Y0mu>WkG^ zsvl6lr2cvyZJx_K&b;Dz$LBrNfHh1t!ZcQE)M(t$n9$VH^w$(>?$x}g`C5yv<)M|L zwM*-q)|fU`+f6%Jdzbck?UxK?h9@J9QO@XQjO%FV1n5X~s&smFrgV*TBXtXOTXdi3 z5%rjQiF&*Ay7b2Nwe=V3=jk`;KQtg1Fb((ydklIECJl`YqYXD2wi%8ZsT#42a*djd z9vf4P-HkJitBvoNU`?1NsV4hPZkfWS_NIK(O4FNWu$hBdve|yKK68w@lexgW+Wejc z*}~Hz+oH*0$dYauY*}F0X8C5m{`|Q4JLdPy|88Y(m1b3I^~hSuI?%eny2JXNjj0XC zrqbq)EydQ)cAagz?OQuDJD%MEyZiQ3`-S!!?Yr#1IXF6q9GV@*9Q7UJ9rrojW73$R z%+1UzP8cUIr*%$eoIX1{IE$T+JOAZk;gago=rX#%XaR3Q?Sf%fU01eimFu7z!!6$J zklUcUjyv1E+Wnb_o(I?Cu*VBe6VDXSBc5-(th_{Cr@SV;oxRt1clw}w{C$djdVQ6A zBYi7;pZe+gCHXb^jr-gCulDZ@zyt&Z>yL(!>q&d!g|7~;VZ%$ z!#_rNMQn?B6lokOiR_A!k6Iel5H%U?6I~iT7&AX+P0aOJ_1NUt_BdQzRNUdXi6y>E zb}t!OYQMB->D^^U%W{@oU9P@7ZTZ<1@+;UYPQ+v4qvIRnzp_Kv)$EA`R>Ho7agH}< z59bxvgS(46#&hMB@?IpmCYC0SCb=fG51895n!LNj5Ja4^$3b9d&rC`fcT3(8uS)h?zX#mfzevE(Po zP_|okW%j3)Q7cd6DCP)qZmcq0wQ1FhT)*63R-;#QR(Iv;*4_M)Ljh>(>+4r>_6Kz^b6M;KPRK4IPD=h53cUKl%UER3uj8*)dd$&1kJFp$QU9kP`4)-1PrHZAw zrNcWH?rhz~*j2pi!|oNkua?=B9o$3QBiS=p9$enOSAXyJyvUm6apA1*oktxixk zSRYp3-Qd{J(5TV4tqE?DG>siudgRtopQG)~7R^;H^p=ui;F#pt%U|Pvy?Z?9c-IN$ ziRP0AC-xAEHs+hg1NP6wXu>Tu~e^_%5yb!T+WRGw8nyX_qDT;aLt z^SS3II)$CDFC<+U?po3H=wi&pJKc-BdoKlDx^mg?^2Hv{p7U2+ubjE+eD(A-=C!u# z4%b_6*xxwyyZ!H{dL4ROZ#v#=zvXnRqi;dqxqkQl3%7l4U%tb-bA4dZK;PZSyZ7%c zy*GHDdw=vn+JmM1gN!e44r`3O${LwP#FnD&zXXwVW@Mn*PIm561 z%=+`|Na1tJ^RgG(FB(Q|M$e4-joo^=Z}gabL^7nSAT`9`yb3 zbo%tPkS$`*)B-4EgcB11cvlAiB^ChcEdYpevo-IG1vFcyBP^}grCr+oi)ZWNnH2{B zYB~Xk$Oj-8nfD^IA=2eN0Fah%5gq`xwUu>5k+!TvTbmhx$ReaP`7}NKUK0RZ2LRth z)6-M?r>DQwBDD@u+jM5m*4k2Qg(CnQ@^*}{i@dxgCV%GnzYYHeaXt2Sf0s5T000XS zNklmNU49OT8hk*>3nrTBjp-h`;J3KIL zC)3W*bee}wAKJ8KrW0VAhBSfRLJcI`L$P6i;6MWShK+6cDofUVrCn{$*_GBSS*w*T zUm8aHAfG+^o&W#NIp6vJb9RkpWo04BD>(uQi~yEE+ho(^2*43YYAC6LL`NX0p+rY8 z**>YEqy`cl0V2WxgQacT(_ECQboRF%`tY-er~fCn3qlgB|BYU2#4VkUD{pyc)p2PH zS#_G)?!n#Tyl@9}^713NtvXSD~?vX`P>oB!;OX4Y^fP9(6A8bT0-Mx{V8WOCRn z(R48%F91nYp~X;~vG!22?WnYclsp2%QB2KyK>1Rf1wx6SI7&MFGg4lh#^Z!d*ANnj zv+1J37Jwv^a0ewVbR4tyo<{suree3ga#NJKNSMMx82GyKUWl<-2p^2k3f=fUl6qSR zHnZ~~sv(+o2JnJ4tMhOa@j3=?ebGDJL1ER(Y#r)j2y&$RZr^zP08FhmC=ttqo58Yc zo?4(KL=gP4Gv>${#qHH@HD6f_3KR4sl zmbe83LG$Y6at$iiAoc;j`LpSX3rf)Rv|L|+;WFgmX)no26I6msw}ikRk)R2-DN}90 zF_(`zIqv5AJ}1AT8qyWK@Wz@P6&lRY>(`o3zjy4OGsvFEt=+w~&ZxpeB-TG~?LG6s z@lIzj$;1Ev-R#`coTFmQ-90IVC0Za-_S&$1^umevZkcB=Nm3+N?tJ;VEUi35vfeRt z_aE<{8Vv*&J&`K`01ZvcbQO&L@@Fbq9^c!XVAc2KD8G6B8YP754M$+@&hTLz^o_t27cpE4xdnzuh1HyKQ!7 zg~-l|p6%0eb5$s19UFC!aKv$4PQz!E-ca;Ud!V9Wt3m4?F2J740= zO9EKgU^ykFTAXs)ogTPK5LB5_zw2A2geWD>xbQa?$&9soo9a0MIc*jP>q3{ky!pG0 zDt4$*DFEllSPBHh*d9@ScIhfpW%a*K6-=m*PV{mj7GwrxRRa_d?PDxmKHDR>L^TeX#MRXFz;nyd(cU{1-0plsvn=aZNFyFI$sK zv<_0S0au4Y+jfS$-E#QsSJ1`k#yxfA=?rSDdvQhAe?E`57h)vO_HW*@1_n{abF<~> z1=huLukNiaOvO^mx2!Gx=<2Wp;JaqhiHE{{&+ci z3x2D2=&lMwylrm>cTD9?DOC+}MXF3+jjXP*B-{N&X36Qlbcj=D4RH41fVdKRr_Q&HaP?QhfsI1Y3GlPQA9Se|K$4d8hSU7&%9 zj^6QtJGNV2GyfSVwO>~8172?AW)=^4*kZj$S>DvQ0b0XbN( zJK-lLYT{79C3x{x zXvRnTkk|mO3Hux7V$1L$AL0Y<$?!9?m32XToB?i(#Q|)< z;(P|h#NB{*df<}lLI(k^R^oT|7Ifvx4L{WiH`GyGVMLgVs2E{okC#ov4C0AztqR*t zFY~h>`G^Ku5L_fHbf0Z_M20}V4B2+<4_5}+sS?0I5zi}%4M4DLu0@0kjRMXOis1zG z5Yl0|@rb(!3p~j&G8G~*wJ{Y#LT5=(p;bW_xJrxpSmxpYb!bv(e;5;rJ<2$TXYgJ) z0SkEDekdS@4s@JxnQ!_j598{`8xHB^Q?IsRMn|E@nr;6SC^T;#aB}Q_n zXBe)4aFXCF+sg7xP)%9}?{yEw2AD&Go?ao``r!IJIATobX*@J%Z9!KdXSKpye`n&} zF>a}j6`W0gdHLJ>_BJbTy z73pBQ9PaokJpaJwVwV_6Wb8(t4UUeG5U=CR>ru(L}V5OSGbsm`n8MhUMwn5u+> zkMv$-Ka++qvH!+zfr&ioalsHZlm^p`b{kw-E?#cF1;QQRkD`l_O>rh$g+QuJ0X}7s)ozVbw z&XiTAf6_Ka#Rjxm`0mN7Hts5{brNc=oSi1d-FyB@5C2%YAWrh+sn+l9T&H45{;=9k zDN?N(lhMbsA8>o_alPMKdD{r8kG!(|a!7NXd3V)I!PjlqVYH;Jpsa zD_~oxmTfs=1ZBU+cV?g6@xVvA)nMmV-k{H}@zWHh&*xdi8k?Z>!XRjEzUj|d27`Oj`_8;lBg+XR}BNB&vqc?7Kf@lx6%^YjTK>Lv+r#LUD{nnvFCwndMh>5FGjI4lY zujP8n`#?aTQWC(~u?-J<0*qQ46p&}U`^2A)^DmJ(OswCSBxe>{3_$PQDMOdAx? zMBmLrM_RZzCkX%zX&=?`2@E;BfGl0^3kRl7tg+~^&*HHFK1ZM-ZE$67mO)~__)7=L zzVguJ$XEd}WA}aR(VcOP!ay5fLzmYUrs)8CC)~$b+~g!p>*NF{AP-6Y_H*-txkzMy z0~+Ih@=;B8Rf!37&^6i>^-Gh+SWu9uK?x$@9>3RRo<5*a0U+@;f4@0fiS}JR`eCbo zMNJWbP;Se>{l2LHHQkh0jU>tR!lgNBYMiFV`|tEmgpD<4QxYIE;F&AoLCoIopX?A$ zjTouO`UEYcRm(Icl=cNIcPaiuSfr2W_t-CkO z3ui?M1s6$z{=?b6B@HUgxj_ zUmsK4_p*8AEBbzDUcGue9ty|3B;2eduX!B-m!I~@15WrKEc10OLN1%Ht|aX)(g-B8 w$|4Q?W8^lep~q-47pW^r4K32ZKSpl<2OR-(lkpeLm;e9(07*qoM6N<$g3d9~!2kdN literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 4c5d6fc0..7fa14afb 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "dependencies": {}, "devDependencies": { "mocha": "~1.6", - "should": "~1.2" + "should": "~1.2", + "colors": "~0.6.2" }, "optionalDependencies": {}, "files": [ From 16c90ca05673ab173a9389a17767c6c295d0b6d2 Mon Sep 17 00:00:00 2001 From: airportyh Date: Tue, 5 Nov 2013 14:47:02 -0500 Subject: [PATCH 034/527] Added web example. --- README.md | 37 +++++++++++++++++++++++++++++++++---- examples/node_example.js | 8 ++++---- examples/one.txt | 1 - examples/other.txt | 1 - examples/web_example.html | 20 ++++++++++++++++++++ images/web_example.png | Bin 0 -> 4372 bytes 6 files changed, 57 insertions(+), 10 deletions(-) delete mode 100644 examples/one.txt delete mode 100644 examples/other.txt create mode 100644 examples/web_example.html create mode 100644 images/web_example.png diff --git a/README.md b/README.md index a138c73f..4f7790e2 100644 --- a/README.md +++ b/README.md @@ -59,16 +59,16 @@ Note that some cases may omit a particular flag field. Comparison on the flag fi ## Examples -Using in Node +Basic example in Node ```js require('colors') var jsdiff = require('diff'); -var one = 'beep boop' -var other = 'beep boob blah' +var one = 'beep boop'; +var other = 'beep boob blah'; -var diff = jsdiff.diffChars(one, other) +var diff = jsdiff.diffChars(one, other); diff.forEach(function(part){ // green for additions, red for deletions @@ -84,6 +84,35 @@ Running the above program should yield Node Example +Basic example in a web page + +```html +

+
+
+```
+
+Open the above .html file in a browser and you should see
+
+Node Example
+
 * [Full online demo](http://kpdecker.github.com/jsdiff)
 
 ## License
diff --git a/examples/node_example.js b/examples/node_example.js
index 7cadcf5a..0c62ca1b 100644
--- a/examples/node_example.js
+++ b/examples/node_example.js
@@ -1,10 +1,10 @@
 require('colors')
 var jsdiff = require('../diff');
 
-var one = 'beep boop'
-var other = 'beep boob blah'
+var one = 'beep boop';
+var other = 'beep boob blah';
 
-var diff = jsdiff.diffChars(one, other)
+var diff = jsdiff.diffChars(one, other);
 
 diff.forEach(function(part){
   // green for additions, red for deletions
@@ -14,4 +14,4 @@ diff.forEach(function(part){
   process.stderr.write(part.value[color]);
 });
 
-console.log()
\ No newline at end of file
+console.log();
\ No newline at end of file
diff --git a/examples/one.txt b/examples/one.txt
deleted file mode 100644
index 1c337236..00000000
--- a/examples/one.txt
+++ /dev/null
@@ -1 +0,0 @@
-blah blah
\ No newline at end of file
diff --git a/examples/other.txt b/examples/other.txt
deleted file mode 100644
index 8e92e3a1..00000000
--- a/examples/other.txt
+++ /dev/null
@@ -1 +0,0 @@
-bee blah boop
\ No newline at end of file
diff --git a/examples/web_example.html b/examples/web_example.html
new file mode 100644
index 00000000..fc58df7e
--- /dev/null
+++ b/examples/web_example.html
@@ -0,0 +1,20 @@
+

+
+
\ No newline at end of file
diff --git a/images/web_example.png b/images/web_example.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa7e0def703af2ee6892bba93bc54474a18c982c
GIT binary patch
literal 4372
zcmV+v5$o=WP)4Tx07wn3mUmne*%rs|OnRdwgd!#Mj&uS@uK^UKgN={`LJJ`zh=_{F;tEz&
zM6gj-nu2QqWG#p&wnag)>w+u_>T|ImDk^URkGP-rdHdhH^SSqa=XcMXIrq%m&*uO@
zu4fB{X|MtS=>oAR%-@R<6C20CJpoui1SDV%m~2j#&@(hN82n@Vco#s@#oHEAU;k+K
zzZVp_yetj?P$;sTz|G>MBYYC+TRB3J7yy(HqU)~|3#H6Qm?lC3L^w~%Nwd6N$_cYv
zJ7W_O=8f321Ar&9*`g!>NZp9e$mS#=en@{JY$f0(3jiR~5O(A6*<6Gp5H?Rw&yaFH
z!o~@I^ON*9X0sDyKH2Of8PDzoc@lZCPjZ$pjlF8-=KndQr%8}}%``1!Bxj|D2O%>Z
z$ypAE?HfMFd>%{s&1Q6=*eh&~lf^9QK4)~kBp`B*C8?2~bDWwHB(oPJEDD*^Ia%IF
z?q{FP<$Euhvq@nGO279^3~UiX|6yL5zwEt2ai}a#
zLE0kOUJ^xqG8P5R6<55DsjR%L+IFBm-hzj##<@?+oE8QF0Pr%
zp`{ZBIji!32GoES&;!Q60*TH6xBw3%;y@4rB0wxy4mf}h(t!x91bJXRC;}y5JJ<~>
zz(G(0>cLTP9JGNm-~zY|u7g`(06YSNUUpKqXKqQ~^~%4bZR9Y3KrU73zZ?Kts?N^bYz0qhK;jgEe78
zcs|U8J>eiY0$v6u!I^L_TnKN4%i(JH2;2%^fUm;?@F4sOoHu%VzF2{)(Go}4Zy}?
zQ?RSCo3Z<`E!a+MA9ff!fy3d{aAr7HTnLVh6XS|-dvQl_=W+eG5!@#{39pT}!TaIk
z@B(}Rz6{@pKZozfzrars6bJ?cXF>>pOIStNMyMrp5N;Ai2vbBwq7l)R7(q-St|yig
zn~9f*kBRR|B$6)4iL{uMOv)#fla7(DkcLQ~$x38XvNw4tSwh}Qt|NDnACTWuC=^4A
z2PKXorfj7&P%cuQQa;I1RR$wcvRj5=rt#D6aLXoOytr()1uDDsTQSqwcs1i=eP{~(`t5m2|qjXVen2Mt6
zQN5`g>IP~p^%8Z2hNT(NShN&c3GFDYmo~1fq->`grJSQ&seD#>kdC4o(pmI0`ZoG;
z`d#`L6-^aS6`sl_m1dQGl}XiksvfF5)ne5a)dAHnY78}BwN$koYHeyy)Y0mu>WkG^
zsvl6lr2cvyZJx_K&b;Dz$LBrNfHh1t!ZcQE)M(t$n9$VH^w$(>?$x}g`C5yv<)M|L
zwM*-q)|fU`+f6%Jdzbck?UxK?h9@J9QO@XQjO%FV1n5X~s&smFrgV*TBXtXOTXdi3
z5%rjQiF&*Ay7b2Nwe=V3=jk`;KQtg1Fb((ydklIECJl`YqYXD2wi%8ZsT#42a*djd
z9vf4P-HkJitBvoNU`?1NsV4hPZkfWS_NIK(O4FNWu$hBdve|yKK68w@lexgW+Wejc
z*}~Hz+oH*0$dYauY*}F0X8C5m{`|Q4JLdPy|88Y(m1b3I^~hSuI?%eny2JXNjj0XC
zrqbq)EydQ)cAagz?OQuDJD%MEyZiQ3`-S!!?Yr#1IXF6q9GV@*9Q7UJ9rrojW73$R
z%+1UzP8cUIr*%$eoIX1{IE$T+JOAZk;gago=rX#%XaR3Q?Sf%fU01eimFu7z!!6$J
zklUcUjyv1E+Wnb_o(I?Cu*VBe6VDXSBc5-(th_{Cr@SV;oxRt1clw}w{C$djdVQ6A
zBYi7;pZe+gCHXb^jr-gCulDZ@zyt&Z>yL(!>q&d!g|7~;VZ%$
z!#_rNMQn?B6lokOiR_A!k6Iel5H%U?6I~iT7&AX+P0aOJ_1NUt_BdQzRNUdXi6y>E
zb}t!OYQMB->D^^U%W{@oU9P@7ZTZ<1@+;UYPQ+v4qvIRnzp_Kv)$EA`R>Ho7agH}<
z59bxvgS(46#&hMB@?IpmCYC0SCb=fG51895n!LNj5Ja4^$3b9d&rC`fcT3(8uS)h?zX#mfzevE(Po
zP_|okW%j3)Q7cd6DCP)qZmcq0wQ1FhT)*63R-;#QR(Iv;*4_M)Ljh>(>+4r>_6Kz^b6M;KPRK4IPD=h53cUKl%UER3uj8*)dd$&1kJFp$QU9kP`4)-1PrHZAw
zrNcWH?rhz~*j2pi!|oNkua?=B9o$3QBiS=p9$enOSAXyJyvUm6apA1*oktxixk
zSRYp3-Qd{J(5TV4tqE?DG>siudgRtopQG)~7R^;H^p=ui;F#pt%U|Pvy?Z?9c-IN$
ziRP0AC-xAEHs+hg1NP6wXu>Tu~e^_%5yb!T+WRGw8nyX_qDT;aLt
z^SS3II)$CDFC<+U?po3H=wi&pJKc-BdoKlDx^mg?^2Hv{p7U2+ubjE+eD(A-=C!u#
z4%b_6*xxwyyZ!H{dL4ROZ#v#=zvXnRqi;dqxqkQl3%7l4U%tb-bA4dZK;PZSyZ7%c
zy*GHDdw=vn+JmM1gN!e44r`3O${LwP#FnD&zXXwVW@Mn*PIm561
z%=+`|Na1tJ^RgG(FB(Q|M$e4-joo^=Z}gabL^7nSAT`9`yb3
zbo%tPkS$`*)B-4EgcB11cvlAiB^ChcEdYpevo-IG1vFcyBP^}grCr+oi)ZWNnH2{B
zYB~Xk$Oj-8nfD^IA=2eN0Fah%5gq`xwUu>5k+!TvTbmhx$ReaP`7}NKUK0RZ2LRth
z)6-M?r>DQwBDD@u+jM5m*4k2Qg(CnQ@^*}{i@dxgCV%GnzYYHeaXt2Sf0s5T000IL
zNkl5oEB
zq5ae2hp@%EuG&TD5sP?$Y;{vLQbpXdR^yR)Rm3ClK9aakb|jW$W|DX;3_C*z-#qht
z&-?nm^S(nwYB5p<2#Yw#@^kstDII|uf5(e-eIE;Ec}7l
zfm&tltv_zP`umktwTXp^dw<@m{HxL`b0-8x+Reei!G3;zw3qf2+%3B&gHImzJ8ZkF
zEd1l&9>?cK;|*KX!o}g~Vd=Iocfz618KpQmIjK-6(60gm15;B|-QC@R&(6-))zwiY
z7!nd9kw{!!U77IC&Q7^po|2N%)6=8TXbv7cc;dtfN{7X1TDvI0`uh4WU%m(gg3!>=
zQ>RY3xw%1v3CE>Rlb@a_o}lj?4Lo``^{$V*4~tQI`F+!UIkFs;M&;@3c_sczVN#(<
zylbK>;g{fwaedMj`58N@(M!Bl@H4|aiNeD7Lwdt
za_kr>DItA*q{G&6Yf`2bY`u|cKHv$3LT6`Z+})@rM@L6*Z*OX|NF<7kjC62tSX*18
z#VMLe2OK9SCy*(e>D$CN+12ayZ~*D2wm<|%;S3W_(L`<{YT+Vu@o@H7T3=$axD7rU
zV|H}*>#W!4jb|&)>J3{(1B4QEb#z5Kw~UH5{UMkp7&PguYgslhPATN-X>)Kf-A1~LSdp`js}OojzEB6;!g@yW@_
zEcnZpFMWM|(L(X?a^S#$w6ruzht?q$yDF8cy1E*D7`cZI+N@YCh6pWQ+EseB=4w_{
z7T%UtbSuH4;L?oJ_<(p?qX1d>xz)LK{dIGzb3%a-#R?Hww(-xu-E_H5fZ
zGlri1z=MM&0eAh<(vmHBWo0F%jwRD7LTh(nVPScB*>W!vj@E>y14}*=Vi$jLZP5<#
zbLTc18=36dg|p2w>aN7trxss-dwOoR?5{zr&2T)q%+1XW3=H5KlMge=jL&jPYuCfW
zgLVrOjvF6EK)Zz&w2SZVWPJIt=};=k$Ox&gC!?d}!UeK#pG~_MNNn?rVT&Cw$`S8y
z*nqKT_0hu)UCvSQsC?!Oo}tO-&t!3NhwjtQvmJ`^+xIjAds}CF>iYjyoX;8eQqSj%
z&l&e3z&(%oI^%Q3y$Ep6W4_M#oN+G#-1C^PGd^eBivagL=Ie}mIr|4bv&bj!S{@nz
O0000
Date: Tue, 5 Nov 2013 14:50:12 -0500
Subject: [PATCH 035/527] Made online demo bigger.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 4f7790e2..26e7df13 100644
--- a/README.md
+++ b/README.md
@@ -113,7 +113,7 @@ Open the above .html file in a browser and you should see
 
 Node Example
 
-* [Full online demo](http://kpdecker.github.com/jsdiff)
+**[Full online demo](http://kpdecker.github.com/jsdiff)**
 
 ## License
 

From 9969bb7b245bf191b8d7568e9baca61ccb88557e Mon Sep 17 00:00:00 2001
From: Ben Ogle 
Date: Wed, 11 Dec 2013 16:50:14 -0800
Subject: [PATCH 036/527] Handle windows newlines on non windows machines.

`value.split(/^/m)` splits the `some\r\ntext` into 3 different elements:

```
['some\r', '\n', 'text']
```

Not good for diffing!
---
 diff.js          | 15 ++++++++++++++-
 test/diffTest.js |  7 +++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/diff.js b/diff.js
index a34c22a0..efab725e 100644
--- a/diff.js
+++ b/diff.js
@@ -165,7 +165,20 @@ var JsDiff = (function() {
 
   var LineDiff = new Diff();
   LineDiff.tokenize = function(value) {
-    return value.split(/^/m);
+    var retLines = [];
+    var lines = value.split(/^/m);
+
+    for(var i = 0; i < lines.length; i++) {
+      var line = lines[i];
+      var lastLine = lines[i - 1];
+
+      if(line == '\n' && lastLine && lastLine.indexOf('\r') == lastLine.length - 1)
+        retLines[retLines.length - 1] += '\n';
+      else if(line)
+        retLines.push(line);
+    }
+
+    return retLines;
   };
 
   return {
diff --git a/test/diffTest.js b/test/diffTest.js
index 91dafe1c..e2aa8eab 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -103,6 +103,13 @@ describe('#diffLines', function() {
       'line\nvalue\nline');
     diff.convertChangesToXML(diffResult).should.equal('line\nvalue\nvalue \nline');
   });
+
+  it('should handle windows line endings', function() {
+    var diffResult = diff.diffLines(
+      'line\r\nold value \r\nline',
+      'line\r\nnew value\r\nline');
+    diff.convertChangesToXML(diffResult).should.equal('line\r\nnew value\r\nold value \r\nline');
+  });
 });
 
 describe('convertToDMP', function() {

From 9b5c39fc985a1e074d0880fddd6ae29390ae0ade Mon Sep 17 00:00:00 2001
From: Bitdeli Chef 
Date: Mon, 16 Dec 2013 21:09:06 +0000
Subject: [PATCH 037/527] Add a Bitdeli badge to README

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index 26e7df13..1bc32d5a 100644
--- a/README.md
+++ b/README.md
@@ -148,3 +148,7 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVI
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/kpdecker/jsdiff/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
+

From 8665995a0c5625708eee59fdf2f9d9a4ba6690e1 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sun, 22 Dec 2013 20:03:43 -0600
Subject: [PATCH 038/527] Update style

---
 diff.js | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/diff.js b/diff.js
index efab725e..c084609a 100644
--- a/diff.js
+++ b/diff.js
@@ -165,17 +165,19 @@ var JsDiff = (function() {
 
   var LineDiff = new Diff();
   LineDiff.tokenize = function(value) {
-    var retLines = [];
-    var lines = value.split(/^/m);
+    var retLines = [],
+        lines = value.split(/^/m);
 
     for(var i = 0; i < lines.length; i++) {
-      var line = lines[i];
-      var lastLine = lines[i - 1];
+      var line = lines[i],
+          lastLine = lines[i - 1];
 
-      if(line == '\n' && lastLine && lastLine.indexOf('\r') == lastLine.length - 1)
+      // Merge lines that may contain windows new lines
+      if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') {
         retLines[retLines.length - 1] += '\n';
-      else if(line)
+      } else if (line) {
         retLines.push(line);
+      }
     }
 
     return retLines;

From d02d887735f977320349e371356a0d27c98e62aa Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sun, 22 Dec 2013 20:03:50 -0600
Subject: [PATCH 039/527] Remove deprecated es flag

---
 .jshintrc | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/.jshintrc b/.jshintrc
index d57cb294..3db30150 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -1,7 +1,6 @@
 /*dotfiles node, mocha*/
 {
   "node": true,
-  "es5": true,
   "esnext": true,
   "browser": false,
   "bitwise": true,
@@ -38,4 +37,4 @@
     "beforeEach",
     "afterEach"
   ]
-}
\ No newline at end of file
+}

From 71d31480ca41ebbb8b60a5aa17903f5b2b7ed5bd Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sun, 22 Dec 2013 20:05:18 -0600
Subject: [PATCH 040/527] Update release notes

---
 release-notes.md | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/release-notes.md b/release-notes.md
index 6989e2bb..4667f8b8 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -2,7 +2,13 @@
 
 ## Development
 
-[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.7...master)
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.8...master)
+
+## v1.0.8 - December 22nd, 2013
+- [#24](https://github.com/kpdecker/jsdiff/pull/24) - Handle windows newlines on non windows machines. ([@benogle](https://api.github.com/users/benogle))
+- [#23](https://github.com/kpdecker/jsdiff/pull/23) - Prettied up the API formatting a little, and added basic node and web examples ([@airportyh](https://api.github.com/users/airportyh))
+
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.7...v1.0.8)
 
 ## v1.0.7 - September 11th, 2013
 

From ae33afbe1b525b2f86141c97524296632811d099 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sun, 22 Dec 2013 20:05:22 -0600
Subject: [PATCH 041/527] v1.0.8

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 7fa14afb..7f5318fa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "diff",
-  "version": "1.0.7",
+  "version": "1.0.8",
   "description": "A javascript text diff implementation.",
   "keywords": [
     "diff",

From 2b2557ba05c2eb7ce66870f6060f19b2875252d5 Mon Sep 17 00:00:00 2001
From: Andreas Lind Petersen 
Date: Thu, 3 Apr 2014 23:10:16 +0200
Subject: [PATCH 042/527] Implemented diffJson.

It takes two objects, serializes them as canonical JSON, then does a line-based diff that ignores differences in trailing commas.

See discussion here: https://github.com/visionmedia/mocha/pull/1182
---
 diff.js          | 60 ++++++++++++++++++++++++++++++++++++++++++++++--
 test/diffTest.js | 16 +++++++++++++
 2 files changed, 74 insertions(+), 2 deletions(-)

diff --git a/diff.js b/diff.js
index c084609a..e63ab21b 100644
--- a/diff.js
+++ b/diff.js
@@ -127,8 +127,11 @@ var JsDiff = (function() {
         while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) {
           newPos++;
           oldPos++;
-
-          this.pushComponent(basePath.components, newString[newPos], undefined, undefined);
+          var value = newString[newPos];
+          if (this.useLongestToken && oldString[oldPos].length > value.length) {
+            value = oldString[oldPos];
+          }
+          this.pushComponent(basePath.components, value, undefined, undefined);
         }
         basePath.newPos = newPos;
         return oldPos;
@@ -183,6 +186,52 @@ var JsDiff = (function() {
     return retLines;
   };
 
+  JsonDiff = new Diff();
+  JsonDiff.useLongestToken = true;
+  JsonDiff.tokenize = LineDiff.tokenize;
+  JsonDiff.equals = function(left, right) {
+    return LineDiff.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'));
+  };
+
+  function canonicalize(obj, stack) {
+    stack = stack || [];
+
+    var i;
+
+    for (var i = 0 ; i < stack.length ; i += 1) {
+      if (stack[i] === obj) {
+        return obj;
+      }
+    }
+
+    var canonicalizedObj;
+
+    if ('[object Array]' == {}.toString.call(obj)) {
+      stack.push(obj);
+      canonicalizedObj = new Array(obj.length);
+      for (i = 0 ; i < obj.length ; i += 1) {
+        canonicalizedObj[i] = canonicalize(obj[i], stack);
+      }
+      stack.pop();
+    } else if (typeof obj === 'object' && obj !== null) {
+      stack.push(obj);
+      canonicalizedObj = {};
+      var sortedKeys = [];
+      for (var key in obj) {
+        sortedKeys.push(key);
+      }
+      sortedKeys.sort();
+      for (i = 0 ; i < sortedKeys.length ; i += 1) {
+        var key = sortedKeys[i];
+        canonicalizedObj[key] = canonicalize(obj[key], stack);
+      }
+      stack.pop();
+    } else {
+      canonicalizedObj = obj;
+    }
+    return canonicalizedObj;
+  }
+
   return {
     Diff: Diff,
 
@@ -191,6 +240,13 @@ var JsDiff = (function() {
     diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); },
     diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); },
 
+    diffJson: function(oldObj, newObj) {
+      return JsonDiff.diff(
+        JSON.stringify(canonicalize(oldObj), undefined, "  "),
+        JSON.stringify(canonicalize(newObj), undefined, "  ")
+      );
+    },
+
     diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); },
 
     createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) {
diff --git a/test/diffTest.js b/test/diffTest.js
index e2aa8eab..d4d009dc 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -112,6 +112,22 @@ describe('#diffLines', function() {
   });
 });
 
+describe('#diffJson', function () {
+  it('should ignore trailing comma on the previous line when the property has been removed', function() {
+    var diffResult = diff.diffJson(
+      {a: 123, b: 456, c: 789},
+      {a: 123, b: 456});
+    diff.convertChangesToXML(diffResult).should.equal('{\n  "a": 123,\n  "b": 456,\n  "c": 789\n}');
+  });
+
+  it('should ignore the missing trailing comma on the last line when a property has been added after it', function() {
+    var diffResult = diff.diffJson(
+      {a: 123, b: 456},
+      {a: 123, b: 456, c: 789});
+    diff.convertChangesToXML(diffResult).should.equal('{\n  "a": 123,\n  "b": 456,\n  "c": 789\n}');
+  });
+});
+
 describe('convertToDMP', function() {
   it('should output diff-match-patch format', function() {
     var diffResult = diff.diffWords('New Value  ', 'New  ValueMoreData ');

From e119b1a110c144d969c9d8a855f0c2aab3d99fbc Mon Sep 17 00:00:00 2001
From: Andreas Lind 
Date: Fri, 8 Aug 2014 22:56:11 +0200
Subject: [PATCH 043/527] diffJson: Assume already stringified JSON when
 receiving a string.

---
 diff.js          |  4 ++--
 test/diffTest.js | 24 +++++++++++++++++++++++-
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/diff.js b/diff.js
index e63ab21b..22db075b 100644
--- a/diff.js
+++ b/diff.js
@@ -242,8 +242,8 @@ var JsDiff = (function() {
 
     diffJson: function(oldObj, newObj) {
       return JsonDiff.diff(
-        JSON.stringify(canonicalize(oldObj), undefined, "  "),
-        JSON.stringify(canonicalize(newObj), undefined, "  ")
+        typeof oldObj === 'string' ? oldObj : JSON.stringify(canonicalize(oldObj), undefined, "  "),
+        typeof newObj === 'string' ? newObj : JSON.stringify(canonicalize(newObj), undefined, "  ")
       );
     },
 
diff --git a/test/diffTest.js b/test/diffTest.js
index d4d009dc..8a6811ff 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -112,7 +112,29 @@ describe('#diffLines', function() {
   });
 });
 
-describe('#diffJson', function () {
+describe('#diffJson', function() {
+  it('should accept objects', function() {
+    diff.diffJson(
+      {a: 123, b: 456, c: 789},
+      {a: 123, b: 456}
+    ).should.eql([
+      { value: '{\n  "a": 123,\n  "b": 456,\n', added: undefined, removed: undefined },
+      { value: '  "c": 789\n', added: undefined, removed: true },
+      { value: '}', added: undefined, removed: undefined }
+    ]);
+  });
+
+  it('should accept already stringified JSON', function() {
+    diff.diffJson(
+      JSON.stringify({a: 123, b: 456, c: 789}, undefined, "  "),
+      JSON.stringify({a: 123, b: 456}, undefined, "  ")
+    ).should.eql([
+      { value: '{\n  "a": 123,\n  "b": 456,\n', added: undefined, removed: undefined },
+      { value: '  "c": 789\n', added: undefined, removed: true },
+      { value: '}', added: undefined, removed: undefined }
+    ]);
+  });
+
   it('should ignore trailing comma on the previous line when the property has been removed', function() {
     var diffResult = diff.diffJson(
       {a: 123, b: 456, c: 789},

From fe13ae1fbd62edbc38875746f0c6851870fae0d3 Mon Sep 17 00:00:00 2001
From: vmariano 
Date: Tue, 12 Aug 2014 11:26:17 -0300
Subject: [PATCH 044/527] Add support for component

---
 component.json | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 component.json

diff --git a/component.json b/component.json
new file mode 100644
index 00000000..965bf2bb
--- /dev/null
+++ b/component.json
@@ -0,0 +1,13 @@
+{
+  "name": "jsdiff",
+  "repo": "kpdecker/jsdiff",
+  "description": "A javascript text diff implementation.",
+  "keywords": [
+    "diff",
+    "text"
+  ],
+  "version": "0.0.1",
+  "scripts": [ "diff.js" ],
+  "main":  "diff.js",
+  "license": "BSD"
+}
\ No newline at end of file

From df86e5d74c5cf07ac691ca53b28f8bdd0413f553 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Mon, 1 Sep 2014 22:04:10 -0500
Subject: [PATCH 045/527] Drop unsupported travis node version

---
 .travis.yml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index 4585bf2e..c693a939 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
 language: node_js
 node_js:
-  - 0.4
   - 0.6
   - 0.8
   - "0.10"

From 90e6979075825eeae9f089fbfa02a262df1ec32d Mon Sep 17 00:00:00 2001
From: Andreas Lind Petersen 
Date: Tue, 2 Sep 2014 09:38:18 +0200
Subject: [PATCH 046/527] Added a diffJson test with a nested array to gain
 more coverage of canonicalize.

---
 test/diffTest.js | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/test/diffTest.js b/test/diffTest.js
index 8a6811ff..18b35971 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -124,6 +124,17 @@ describe('#diffJson', function() {
     ]);
   });
 
+  it('should accept objects with nested structures', function() {
+    diff.diffJson(
+      {a: 123, b: 456, c: [1, 2, {foo: 'bar'}, 4]},
+      {a: 123, b: 456, c: [1, {foo: 'bar'}, 4]}
+    ).should.eql([
+      { value: '{\n  "a": 123,\n  "b": 456,\n  "c": [\n    1,\n', added: undefined, removed: undefined },
+      { value: '    2,\n', added: undefined, removed: true },
+      { value: '    {\n      "foo": "bar"\n    },\n    4\n  ]\n}', added: undefined, removed: undefined }
+    ]);
+  });
+
   it('should accept already stringified JSON', function() {
     diff.diffJson(
       JSON.stringify({a: 123, b: 456, c: 789}, undefined, "  "),

From c7a5bab406a48ad9bc2c5f9079ea7aeee30d8095 Mon Sep 17 00:00:00 2001
From: Andreas Lind Petersen 
Date: Tue, 2 Sep 2014 09:38:46 +0200
Subject: [PATCH 047/527] Put Object.prototype.toString into a var to avoid
 creating an empty object each time it's used.

---
 diff.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/diff.js b/diff.js
index 22db075b..4e412fda 100644
--- a/diff.js
+++ b/diff.js
@@ -193,6 +193,8 @@ var JsDiff = (function() {
     return LineDiff.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'));
   };
 
+  var objectPrototypeToString = Object.prototype.toString;
+
   function canonicalize(obj, stack) {
     stack = stack || [];
 
@@ -206,7 +208,7 @@ var JsDiff = (function() {
 
     var canonicalizedObj;
 
-    if ('[object Array]' == {}.toString.call(obj)) {
+    if ('[object Array]' === objectPrototypeToString.call(obj)) {
       stack.push(obj);
       canonicalizedObj = new Array(obj.length);
       for (i = 0 ; i < obj.length ; i += 1) {

From 619fe4615713ab496ba573c4e446fbe2f8900643 Mon Sep 17 00:00:00 2001
From: ovcharik 
Date: Thu, 4 Sep 2014 15:17:14 +0600
Subject: [PATCH 048/527] AMD and global exports

---
 diff.js | 618 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 313 insertions(+), 305 deletions(-)

diff --git a/diff.js b/diff.js
index c084609a..2118d0ac 100644
--- a/diff.js
+++ b/diff.js
@@ -14,356 +14,364 @@
  * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986).
  * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
  */
-var JsDiff = (function() {
-  /*jshint maxparams: 5*/
-  function clonePath(path) {
-    return { newPos: path.newPos, components: path.components.slice(0) };
-  }
-  function removeEmpty(array) {
-    var ret = [];
-    for (var i = 0; i < array.length; i++) {
-      if (array[i]) {
-        ret.push(array[i]);
+(function(global, undefined) {
+  var JsDiff = (function() {
+    /*jshint maxparams: 5*/
+    function clonePath(path) {
+      return { newPos: path.newPos, components: path.components.slice(0) };
+    }
+    function removeEmpty(array) {
+      var ret = [];
+      for (var i = 0; i < array.length; i++) {
+        if (array[i]) {
+          ret.push(array[i]);
+        }
       }
+      return ret;
+    }
+    function escapeHTML(s) {
+      var n = s;
+      n = n.replace(/&/g, '&');
+      n = n.replace(//g, '>');
+      n = n.replace(/"/g, '"');
+
+      return n;
     }
-    return ret;
-  }
-  function escapeHTML(s) {
-    var n = s;
-    n = n.replace(/&/g, '&');
-    n = n.replace(//g, '>');
-    n = n.replace(/"/g, '"');
-
-    return n;
-  }
 
-  var Diff = function(ignoreWhitespace) {
-    this.ignoreWhitespace = ignoreWhitespace;
-  };
-  Diff.prototype = {
-      diff: function(oldString, newString) {
-        // Handle the identity case (this is due to unrolling editLength == 0
-        if (newString === oldString) {
-          return [{ value: newString }];
-        }
-        if (!newString) {
-          return [{ value: oldString, removed: true }];
-        }
-        if (!oldString) {
-          return [{ value: newString, added: true }];
-        }
+    var Diff = function(ignoreWhitespace) {
+      this.ignoreWhitespace = ignoreWhitespace;
+    };
+    Diff.prototype = {
+        diff: function(oldString, newString) {
+          // Handle the identity case (this is due to unrolling editLength == 0
+          if (newString === oldString) {
+            return [{ value: newString }];
+          }
+          if (!newString) {
+            return [{ value: oldString, removed: true }];
+          }
+          if (!oldString) {
+            return [{ value: newString, added: true }];
+          }
 
-        newString = this.tokenize(newString);
-        oldString = this.tokenize(oldString);
+          newString = this.tokenize(newString);
+          oldString = this.tokenize(oldString);
 
-        var newLen = newString.length, oldLen = oldString.length;
-        var maxEditLength = newLen + oldLen;
-        var bestPath = [{ newPos: -1, components: [] }];
+          var newLen = newString.length, oldLen = oldString.length;
+          var maxEditLength = newLen + oldLen;
+          var bestPath = [{ newPos: -1, components: [] }];
 
-        // Seed editLength = 0
-        var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
-        if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) {
-          return bestPath[0].components;
-        }
+          // Seed editLength = 0
+          var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
+          if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) {
+            return bestPath[0].components;
+          }
 
-        for (var editLength = 1; editLength <= maxEditLength; editLength++) {
-          for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) {
-            var basePath;
-            var addPath = bestPath[diagonalPath-1],
-                removePath = bestPath[diagonalPath+1];
-            oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
-            if (addPath) {
-              // No one else is going to attempt to use this value, clear it
-              bestPath[diagonalPath-1] = undefined;
-            }
+          for (var editLength = 1; editLength <= maxEditLength; editLength++) {
+            for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) {
+              var basePath;
+              var addPath = bestPath[diagonalPath-1],
+                  removePath = bestPath[diagonalPath+1];
+              oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
+              if (addPath) {
+                // No one else is going to attempt to use this value, clear it
+                bestPath[diagonalPath-1] = undefined;
+              }
 
-            var canAdd = addPath && addPath.newPos+1 < newLen;
-            var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
-            if (!canAdd && !canRemove) {
-              bestPath[diagonalPath] = undefined;
-              continue;
-            }
+              var canAdd = addPath && addPath.newPos+1 < newLen;
+              var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
+              if (!canAdd && !canRemove) {
+                bestPath[diagonalPath] = undefined;
+                continue;
+              }
 
-            // Select the diagonal that we want to branch from. We select the prior
-            // path whose position in the new string is the farthest from the origin
-            // and does not pass the bounds of the diff graph
-            if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
-              basePath = clonePath(removePath);
-              this.pushComponent(basePath.components, oldString[oldPos], undefined, true);
-            } else {
-              basePath = clonePath(addPath);
-              basePath.newPos++;
-              this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined);
-            }
+              // Select the diagonal that we want to branch from. We select the prior
+              // path whose position in the new string is the farthest from the origin
+              // and does not pass the bounds of the diff graph
+              if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
+                basePath = clonePath(removePath);
+                this.pushComponent(basePath.components, oldString[oldPos], undefined, true);
+              } else {
+                basePath = clonePath(addPath);
+                basePath.newPos++;
+                this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined);
+              }
 
-            var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath);
+              var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath);
 
-            if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) {
-              return basePath.components;
-            } else {
-              bestPath[diagonalPath] = basePath;
+              if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) {
+                return basePath.components;
+              } else {
+                bestPath[diagonalPath] = basePath;
+              }
             }
           }
+        },
+
+        pushComponent: function(components, value, added, removed) {
+          var last = components[components.length-1];
+          if (last && last.added === added && last.removed === removed) {
+            // We need to clone here as the component clone operation is just
+            // as shallow array clone
+            components[components.length-1] =
+              {value: this.join(last.value, value), added: added, removed: removed };
+          } else {
+            components.push({value: value, added: added, removed: removed });
+          }
+        },
+        extractCommon: function(basePath, newString, oldString, diagonalPath) {
+          var newLen = newString.length,
+              oldLen = oldString.length,
+              newPos = basePath.newPos,
+              oldPos = newPos - diagonalPath;
+          while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) {
+            newPos++;
+            oldPos++;
+
+            this.pushComponent(basePath.components, newString[newPos], undefined, undefined);
+          }
+          basePath.newPos = newPos;
+          return oldPos;
+        },
+
+        equals: function(left, right) {
+          var reWhitespace = /\S/;
+          if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) {
+            return true;
+          } else {
+            return left === right;
+          }
+        },
+        join: function(left, right) {
+          return left + right;
+        },
+        tokenize: function(value) {
+          return value;
         }
-      },
-
-      pushComponent: function(components, value, added, removed) {
-        var last = components[components.length-1];
-        if (last && last.added === added && last.removed === removed) {
-          // We need to clone here as the component clone operation is just
-          // as shallow array clone
-          components[components.length-1] =
-            {value: this.join(last.value, value), added: added, removed: removed };
-        } else {
-          components.push({value: value, added: added, removed: removed });
-        }
-      },
-      extractCommon: function(basePath, newString, oldString, diagonalPath) {
-        var newLen = newString.length,
-            oldLen = oldString.length,
-            newPos = basePath.newPos,
-            oldPos = newPos - diagonalPath;
-        while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) {
-          newPos++;
-          oldPos++;
-
-          this.pushComponent(basePath.components, newString[newPos], undefined, undefined);
-        }
-        basePath.newPos = newPos;
-        return oldPos;
-      },
-
-      equals: function(left, right) {
-        var reWhitespace = /\S/;
-        if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) {
-          return true;
-        } else {
-          return left === right;
+    };
+
+    var CharDiff = new Diff();
+
+    var WordDiff = new Diff(true);
+    var WordWithSpaceDiff = new Diff();
+    WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) {
+      return removeEmpty(value.split(/(\s+|\b)/));
+    };
+
+    var CssDiff = new Diff(true);
+    CssDiff.tokenize = function(value) {
+      return removeEmpty(value.split(/([{}:;,]|\s+)/));
+    };
+
+    var LineDiff = new Diff();
+    LineDiff.tokenize = function(value) {
+      var retLines = [],
+          lines = value.split(/^/m);
+
+      for(var i = 0; i < lines.length; i++) {
+        var line = lines[i],
+            lastLine = lines[i - 1];
+
+        // Merge lines that may contain windows new lines
+        if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') {
+          retLines[retLines.length - 1] += '\n';
+        } else if (line) {
+          retLines.push(line);
         }
-      },
-      join: function(left, right) {
-        return left + right;
-      },
-      tokenize: function(value) {
-        return value;
-      }
-  };
-
-  var CharDiff = new Diff();
-
-  var WordDiff = new Diff(true);
-  var WordWithSpaceDiff = new Diff();
-  WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) {
-    return removeEmpty(value.split(/(\s+|\b)/));
-  };
-
-  var CssDiff = new Diff(true);
-  CssDiff.tokenize = function(value) {
-    return removeEmpty(value.split(/([{}:;,]|\s+)/));
-  };
-
-  var LineDiff = new Diff();
-  LineDiff.tokenize = function(value) {
-    var retLines = [],
-        lines = value.split(/^/m);
-
-    for(var i = 0; i < lines.length; i++) {
-      var line = lines[i],
-          lastLine = lines[i - 1];
-
-      // Merge lines that may contain windows new lines
-      if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') {
-        retLines[retLines.length - 1] += '\n';
-      } else if (line) {
-        retLines.push(line);
       }
-    }
 
-    return retLines;
-  };
+      return retLines;
+    };
 
-  return {
-    Diff: Diff,
+    return {
+      Diff: Diff,
 
-    diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); },
-    diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); },
-    diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); },
-    diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); },
+      diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); },
+      diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); },
+      diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); },
+      diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); },
 
-    diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); },
+      diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); },
 
-    createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) {
-      var ret = [];
+      createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) {
+        var ret = [];
 
-      ret.push('Index: ' + fileName);
-      ret.push('===================================================================');
-      ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader));
-      ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader));
+        ret.push('Index: ' + fileName);
+        ret.push('===================================================================');
+        ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader));
+        ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader));
 
-      var diff = LineDiff.diff(oldStr, newStr);
-      if (!diff[diff.length-1].value) {
-        diff.pop();   // Remove trailing newline add
-      }
-      diff.push({value: '', lines: []});   // Append an empty value to make cleanup easier
+        var diff = LineDiff.diff(oldStr, newStr);
+        if (!diff[diff.length-1].value) {
+          diff.pop();   // Remove trailing newline add
+        }
+        diff.push({value: '', lines: []});   // Append an empty value to make cleanup easier
 
-      function contextLines(lines) {
-        return lines.map(function(entry) { return ' ' + entry; });
-      }
-      function eofNL(curRange, i, current) {
-        var last = diff[diff.length-2],
-            isLast = i === diff.length-2,
-            isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed);
-
-        // Figure out if this is the last line for the given file and missing NL
-        if (!/\n$/.test(current.value) && (isLast || isLastOfType)) {
-          curRange.push('\\ No newline at end of file');
+        function contextLines(lines) {
+          return lines.map(function(entry) { return ' ' + entry; });
+        }
+        function eofNL(curRange, i, current) {
+          var last = diff[diff.length-2],
+              isLast = i === diff.length-2,
+              isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed);
+
+          // Figure out if this is the last line for the given file and missing NL
+          if (!/\n$/.test(current.value) && (isLast || isLastOfType)) {
+            curRange.push('\\ No newline at end of file');
+          }
         }
-      }
 
-      var oldRangeStart = 0, newRangeStart = 0, curRange = [],
-          oldLine = 1, newLine = 1;
-      for (var i = 0; i < diff.length; i++) {
-        var current = diff[i],
-            lines = current.lines || current.value.replace(/\n$/, '').split('\n');
-        current.lines = lines;
-
-        if (current.added || current.removed) {
-          if (!oldRangeStart) {
-            var prev = diff[i-1];
-            oldRangeStart = oldLine;
-            newRangeStart = newLine;
-
-            if (prev) {
-              curRange = contextLines(prev.lines.slice(-4));
-              oldRangeStart -= curRange.length;
-              newRangeStart -= curRange.length;
+        var oldRangeStart = 0, newRangeStart = 0, curRange = [],
+            oldLine = 1, newLine = 1;
+        for (var i = 0; i < diff.length; i++) {
+          var current = diff[i],
+              lines = current.lines || current.value.replace(/\n$/, '').split('\n');
+          current.lines = lines;
+
+          if (current.added || current.removed) {
+            if (!oldRangeStart) {
+              var prev = diff[i-1];
+              oldRangeStart = oldLine;
+              newRangeStart = newLine;
+
+              if (prev) {
+                curRange = contextLines(prev.lines.slice(-4));
+                oldRangeStart -= curRange.length;
+                newRangeStart -= curRange.length;
+              }
             }
-          }
-          curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; }));
-          eofNL(curRange, i, current);
+            curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; }));
+            eofNL(curRange, i, current);
 
-          if (current.added) {
-            newLine += lines.length;
+            if (current.added) {
+              newLine += lines.length;
+            } else {
+              oldLine += lines.length;
+            }
           } else {
+            if (oldRangeStart) {
+              // Close out any changes that have been output (or join overlapping)
+              if (lines.length <= 8 && i < diff.length-2) {
+                // Overlapping
+                curRange.push.apply(curRange, contextLines(lines));
+              } else {
+                // end the range and output
+                var contextSize = Math.min(lines.length, 4);
+                ret.push(
+                    '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize)
+                    + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize)
+                    + ' @@');
+                ret.push.apply(ret, curRange);
+                ret.push.apply(ret, contextLines(lines.slice(0, contextSize)));
+                if (lines.length <= 4) {
+                  eofNL(ret, i, current);
+                }
+
+                oldRangeStart = 0;  newRangeStart = 0; curRange = [];
+              }
+            }
             oldLine += lines.length;
+            newLine += lines.length;
           }
-        } else {
-          if (oldRangeStart) {
-            // Close out any changes that have been output (or join overlapping)
-            if (lines.length <= 8 && i < diff.length-2) {
-              // Overlapping
-              curRange.push.apply(curRange, contextLines(lines));
-            } else {
-              // end the range and output
-              var contextSize = Math.min(lines.length, 4);
-              ret.push(
-                  '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize)
-                  + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize)
-                  + ' @@');
-              ret.push.apply(ret, curRange);
-              ret.push.apply(ret, contextLines(lines.slice(0, contextSize)));
-              if (lines.length <= 4) {
-                eofNL(ret, i, current);
-              }
+        }
+
+        return ret.join('\n') + '\n';
+      },
 
-              oldRangeStart = 0;  newRangeStart = 0; curRange = [];
+      applyPatch: function(oldStr, uniDiff) {
+        var diffstr = uniDiff.split('\n');
+        var diff = [];
+        var remEOFNL = false,
+            addEOFNL = false;
+
+        for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) {
+          if(diffstr[i][0] === '@') {
+            var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/);
+            diff.unshift({
+              start:meh[3],
+              oldlength:meh[2],
+              oldlines:[],
+              newlength:meh[4],
+              newlines:[]
+            });
+          } else if(diffstr[i][0] === '+') {
+            diff[0].newlines.push(diffstr[i].substr(1));
+          } else if(diffstr[i][0] === '-') {
+            diff[0].oldlines.push(diffstr[i].substr(1));
+          } else if(diffstr[i][0] === ' ') {
+            diff[0].newlines.push(diffstr[i].substr(1));
+            diff[0].oldlines.push(diffstr[i].substr(1));
+          } else if(diffstr[i][0] === '\\') {
+            if (diffstr[i-1][0] === '+') {
+              remEOFNL = true;
+            } else if(diffstr[i-1][0] === '-') {
+              addEOFNL = true;
             }
           }
-          oldLine += lines.length;
-          newLine += lines.length;
         }
-      }
 
-      return ret.join('\n') + '\n';
-    },
-
-    applyPatch: function(oldStr, uniDiff) {
-      var diffstr = uniDiff.split('\n');
-      var diff = [];
-      var remEOFNL = false,
-          addEOFNL = false;
-
-      for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) {
-        if(diffstr[i][0] === '@') {
-          var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/);
-          diff.unshift({
-            start:meh[3],
-            oldlength:meh[2],
-            oldlines:[],
-            newlength:meh[4],
-            newlines:[]
-          });
-        } else if(diffstr[i][0] === '+') {
-          diff[0].newlines.push(diffstr[i].substr(1));
-        } else if(diffstr[i][0] === '-') {
-          diff[0].oldlines.push(diffstr[i].substr(1));
-        } else if(diffstr[i][0] === ' ') {
-          diff[0].newlines.push(diffstr[i].substr(1));
-          diff[0].oldlines.push(diffstr[i].substr(1));
-        } else if(diffstr[i][0] === '\\') {
-          if (diffstr[i-1][0] === '+') {
-            remEOFNL = true;
-          } else if(diffstr[i-1][0] === '-') {
-            addEOFNL = true;
+        var str = oldStr.split('\n');
+        for (var i = diff.length - 1; i >= 0; i--) {
+          var d = diff[i];
+          for (var j = 0; j < d.oldlength; j++) {
+            if(str[d.start-1+j] !== d.oldlines[j]) {
+              return false;
+            }
           }
+          Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines));
         }
-      }
 
-      var str = oldStr.split('\n');
-      for (var i = diff.length - 1; i >= 0; i--) {
-        var d = diff[i];
-        for (var j = 0; j < d.oldlength; j++) {
-          if(str[d.start-1+j] !== d.oldlines[j]) {
-            return false;
+        if (remEOFNL) {
+          while (!str[str.length-1]) {
+            str.pop();
           }
+        } else if (addEOFNL) {
+          str.push('');
         }
-        Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines));
-      }
+        return str.join('\n');
+      },
 
-      if (remEOFNL) {
-        while (!str[str.length-1]) {
-          str.pop();
-        }
-      } else if (addEOFNL) {
-        str.push('');
-      }
-      return str.join('\n');
-    },
+      convertChangesToXML: function(changes){
+        var ret = [];
+        for ( var i = 0; i < changes.length; i++) {
+          var change = changes[i];
+          if (change.added) {
+            ret.push('');
+          } else if (change.removed) {
+            ret.push('');
+          }
 
-    convertChangesToXML: function(changes){
-      var ret = [];
-      for ( var i = 0; i < changes.length; i++) {
-        var change = changes[i];
-        if (change.added) {
-          ret.push('');
-        } else if (change.removed) {
-          ret.push('');
-        }
+          ret.push(escapeHTML(change.value));
 
-        ret.push(escapeHTML(change.value));
+          if (change.added) {
+            ret.push('');
+          } else if (change.removed) {
+            ret.push('');
+          }
+        }
+        return ret.join('');
+      },
 
-        if (change.added) {
-          ret.push('');
-        } else if (change.removed) {
-          ret.push('');
+      // See: http://code.google.com/p/google-diff-match-patch/wiki/API
+      convertChangesToDMP: function(changes){
+        var ret = [], change;
+        for ( var i = 0; i < changes.length; i++) {
+          change = changes[i];
+          ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]);
         }
+        return ret;
       }
-      return ret.join('');
-    },
-
-    // See: http://code.google.com/p/google-diff-match-patch/wiki/API
-    convertChangesToDMP: function(changes){
-      var ret = [], change;
-      for ( var i = 0; i < changes.length; i++) {
-        change = changes[i];
-        ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]);
-      }
-      return ret;
-    }
-  };
-})();
+    };
+  })();
 
-if (typeof module !== 'undefined') {
-    module.exports = JsDiff;
-}
+  if (typeof module !== 'undefined') {
+      module.exports = JsDiff;
+  }
+  else if (typeof define === 'function') {
+    define([], function() { return JsDiff; });
+  }
+  else if (typeof global.JsDiff === 'undefined') {
+    global.JsDiff = JsDiff;
+  }
+})(this);

From a9191cb2d9967c6d45f226359eed26a44b396ec1 Mon Sep 17 00:00:00 2001
From: Andreas Lind 
Date: Thu, 4 Sep 2014 21:40:31 +0200
Subject: [PATCH 049/527] Document the useLongestToken property.

---
 diff.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/diff.js b/diff.js
index 4e412fda..c98738ad 100644
--- a/diff.js
+++ b/diff.js
@@ -187,6 +187,8 @@ var JsDiff = (function() {
   };
 
   JsonDiff = new Diff();
+  // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a
+  // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output:
   JsonDiff.useLongestToken = true;
   JsonDiff.tokenize = LineDiff.tokenize;
   JsonDiff.equals = function(left, right) {

From d786a63e574529c47074fc3c66a3e8102cb256bf Mon Sep 17 00:00:00 2001
From: Andreas Lind 
Date: Thu, 4 Sep 2014 21:50:26 +0200
Subject: [PATCH 050/527] diffJson: Test that a circular reference causes
 JSON.stringify to throw (rather than an infinite loop).

---
 test/diffTest.js | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/test/diffTest.js b/test/diffTest.js
index 18b35971..abd04b37 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -159,6 +159,17 @@ describe('#diffJson', function() {
       {a: 123, b: 456, c: 789});
     diff.convertChangesToXML(diffResult).should.equal('{\n  "a": 123,\n  "b": 456,\n  "c": 789\n}');
   });
+
+  it('should throw an error if one of the objects being diffed has a circular reference', function() {
+    var circular = {foo: 123};
+    circular.bar = circular;
+    (function () {
+      diff.diffJson(
+        circular,
+        {foo: 123, bar: {}}
+      );
+    }).should.throw('Converting circular structure to JSON');
+  });
 });
 
 describe('convertToDMP', function() {

From d12c46d3078f0752f204482a1b0f7b2de4b72156 Mon Sep 17 00:00:00 2001
From: Andreas Lind 
Date: Thu, 4 Sep 2014 22:14:42 +0200
Subject: [PATCH 051/527] canonicalize: Keep track of the replacement objects
 so the correct object can be substituted when a circular reference is
 detected.

---
 diff.js              | 21 +++++++++++++++------
 test/canonicalize.js | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 6 deletions(-)
 create mode 100644 test/canonicalize.js

diff --git a/diff.js b/diff.js
index c98738ad..2e3dc5f9 100644
--- a/diff.js
+++ b/diff.js
@@ -197,14 +197,17 @@ var JsDiff = (function() {
 
   var objectPrototypeToString = Object.prototype.toString;
 
-  function canonicalize(obj, stack) {
+  // This function handles the presence of circular references by bailing out when encountering an
+  // object that is already on the "stack" of items being processed.
+  function canonicalize(obj, stack, replacementStack) {
     stack = stack || [];
+    replacementStack = replacementStack || [];
 
     var i;
 
     for (var i = 0 ; i < stack.length ; i += 1) {
       if (stack[i] === obj) {
-        return obj;
+        return replacementStack[i];
       }
     }
 
@@ -213,13 +216,16 @@ var JsDiff = (function() {
     if ('[object Array]' === objectPrototypeToString.call(obj)) {
       stack.push(obj);
       canonicalizedObj = new Array(obj.length);
+      replacementStack.push(canonicalizedObj);
       for (i = 0 ; i < obj.length ; i += 1) {
-        canonicalizedObj[i] = canonicalize(obj[i], stack);
+        canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack);
       }
       stack.pop();
+      replacementStack.pop();
     } else if (typeof obj === 'object' && obj !== null) {
       stack.push(obj);
       canonicalizedObj = {};
+      replacementStack.push(canonicalizedObj);
       var sortedKeys = [];
       for (var key in obj) {
         sortedKeys.push(key);
@@ -227,14 +233,15 @@ var JsDiff = (function() {
       sortedKeys.sort();
       for (i = 0 ; i < sortedKeys.length ; i += 1) {
         var key = sortedKeys[i];
-        canonicalizedObj[key] = canonicalize(obj[key], stack);
+        canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack);
       }
       stack.pop();
+      replacementStack.pop();
     } else {
       canonicalizedObj = obj;
     }
     return canonicalizedObj;
-  }
+  };
 
   return {
     Diff: Diff,
@@ -420,7 +427,9 @@ var JsDiff = (function() {
         ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]);
       }
       return ret;
-    }
+    },
+
+    canonicalize: canonicalize
   };
 })();
 
diff --git a/test/canonicalize.js b/test/canonicalize.js
new file mode 100644
index 00000000..7df0600c
--- /dev/null
+++ b/test/canonicalize.js
@@ -0,0 +1,37 @@
+const VERBOSE = false;
+
+var diff = require('../diff');
+
+function getKeys(obj) {
+  var keys = [];
+  for (var key in obj) {
+    if (obj.hasOwnProperty(key)) {
+      keys.push(key);
+    }
+  }
+  return keys;
+}
+
+describe('#canonicalize', function() {
+  it('should put the keys in canonical order', function() {
+    getKeys(diff.canonicalize({b: 456, a: 123})).should.eql(['a', 'b']);
+  });
+
+  it('should dive into nested objects', function() {
+    var canonicalObj = diff.canonicalize({b: 456, a: {d: 123, c: 456}});
+    getKeys(canonicalObj.a).should.eql(['c', 'd']);
+  });
+
+  it('should dive into nested arrays', function() {
+    var canonicalObj = diff.canonicalize({b: 456, a: [789, {d: 123, c: 456}]});
+    getKeys(canonicalObj.a[1]).should.eql(['c', 'd']);
+  });
+
+  it('should handle circular references correctly', function() {
+    var obj = {b: 456};
+    obj.a = obj;
+    var canonicalObj = diff.canonicalize(obj);
+    getKeys(canonicalObj).should.eql(['a', 'b']);
+    getKeys(canonicalObj.a).should.eql(['a', 'b']);
+  });
+});

From dcea56fc5a33fa867104598f39521fe0509c2692 Mon Sep 17 00:00:00 2001
From: Andreas Lind 
Date: Fri, 8 Aug 2014 23:13:51 +0200
Subject: [PATCH 052/527] Don't rely on Array.prototype.map

---
 diff.js | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/diff.js b/diff.js
index 2118d0ac..76ef11ee 100644
--- a/diff.js
+++ b/diff.js
@@ -17,6 +17,20 @@
 (function(global, undefined) {
   var JsDiff = (function() {
     /*jshint maxparams: 5*/
+    function map(arr, mapper, that) {
+      if (Array.prototype.map) {
+        return Array.prototype.map.call(arr, mapper, that);
+      }
+
+      var other = new Array(arr.length);
+
+      for (var i = 0, n = arr.length; i < n; i++) {
+        if (i in arr) {
+          other[i] = mapper.call(that, arr[i], i, arr);
+        }
+      }
+      return other;
+    }
     function clonePath(path) {
       return { newPos: path.newPos, components: path.components.slice(0) };
     }
@@ -209,7 +223,7 @@
         diff.push({value: '', lines: []});   // Append an empty value to make cleanup easier
 
         function contextLines(lines) {
-          return lines.map(function(entry) { return ' ' + entry; });
+          return map(lines, function(entry) { return ' ' + entry; });
         }
         function eofNL(curRange, i, current) {
           var last = diff[diff.length-2],
@@ -241,7 +255,7 @@
                 newRangeStart -= curRange.length;
               }
             }
-            curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; }));
+            curRange.push.apply(curRange, map(lines, function(entry) { return (current.added?'+':'-') + entry; }));
             eofNL(curRange, i, current);
 
             if (current.added) {

From 03b6e4cf757f29632a4395b97655548407e84f3e Mon Sep 17 00:00:00 2001
From: Andreas Lind 
Date: Tue, 9 Sep 2014 06:35:32 +0200
Subject: [PATCH 053/527] Map: Removed sparse array support

---
 diff.js | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/diff.js b/diff.js
index 76ef11ee..e151a475 100644
--- a/diff.js
+++ b/diff.js
@@ -25,9 +25,7 @@
       var other = new Array(arr.length);
 
       for (var i = 0, n = arr.length; i < n; i++) {
-        if (i in arr) {
-          other[i] = mapper.call(that, arr[i], i, arr);
-        }
+        other[i] = mapper.call(that, arr[i], i, arr);
       }
       return other;
     }

From dbe6e82f05219b94d53b2baa0b36a2ce7a2295ef Mon Sep 17 00:00:00 2001
From: vmariano 
Date: Tue, 12 Aug 2014 11:26:17 -0300
Subject: [PATCH 054/527] Add support for component

---
 component.json | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 component.json

diff --git a/component.json b/component.json
new file mode 100644
index 00000000..b62cb018
--- /dev/null
+++ b/component.json
@@ -0,0 +1,13 @@
+{
+  "name": "jsdiff",
+  "repo": "kpdecker/jsdiff",
+  "description": "A javascript text diff implementation.",
+  "keywords": [
+    "diff",
+    "text"
+  ],
+  "version": "1.0.8",
+  "scripts": [ "diff.js" ],
+  "main":  "diff.js",
+  "license": "BSD"
+}
\ No newline at end of file

From 9c802f7c2ac98d25d7a8f40fe176c02c34436894 Mon Sep 17 00:00:00 2001
From: vmariano 
Date: Thu, 23 Oct 2014 16:51:18 -0300
Subject: [PATCH 055/527] Add support for sentences.

---
 diff.js          | 10 ++++++++--
 test/diffTest.js | 13 +++++++++++++
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/diff.js b/diff.js
index e151a475..fce6422f 100644
--- a/diff.js
+++ b/diff.js
@@ -196,13 +196,19 @@
       return retLines;
     };
 
-    return {
-      Diff: Diff,
+    var SentenceDiff = new Diff();
+    SentenceDiff.tokenize = function (value) {
+      return removeEmpty(value.split(/(\S.+?[.!?])(?=\s+|$)/));
+    };
+
+  return {
+    Diff: Diff,
 
       diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); },
       diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); },
       diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); },
       diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); },
+      diffSentences: function(oldStr, newStr) { return SentenceDiff.diff(oldStr, newStr); },
 
       diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); },
 
diff --git a/test/diffTest.js b/test/diffTest.js
index e2aa8eab..817376f2 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -69,6 +69,19 @@ describe('#diffWordsWithSpace', function() {
   });
 });
 
+describe('#diffSentences', function() {
+
+  it('Should diff Sentences', function() {
+    var diffResult = diff.diffSentences('New Value.', 'New ValueMoreData.');
+    diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData.New Value.');
+  });
+
+  it('should diff only the last sentence', function() {
+    var diffResult = diff.diffSentences('Here im. Rock you like old man.', 'Here im. Rock you like hurricane.');
+    diff.convertChangesToXML(diffResult).should.equal('Here im. Rock you like hurricane.Rock you like old man.');
+  });
+});
+
 // CSS Diff
 describe('#diffCss', function() {
   it('should diff css', function() {

From 36f5b23a7278795b2447cc95ebbcd7a35a26315d Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Tue, 25 Nov 2014 01:58:29 -0600
Subject: [PATCH 056/527] Update release notes

---
 release-notes.md | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/release-notes.md b/release-notes.md
index 4667f8b8..f1b30e87 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -2,7 +2,14 @@
 
 ## Development
 
-[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.8...master)
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.1.0...master)
+
+## v1.1.0 - November 25th, 2014
+- [#33](https://github.com/kpdecker/jsdiff/pull/33) - AMD and global exports ([@ovcharik](https://api.github.com/users/ovcharik))
+- [#32](https://github.com/kpdecker/jsdiff/pull/32) - Add support for component ([@vmariano](https://api.github.com/users/vmariano))
+- [#31](https://github.com/kpdecker/jsdiff/pull/31) - Don't rely on Array.prototype.map ([@papandreou](https://api.github.com/users/papandreou))
+
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.0.8...v1.1.0)
 
 ## v1.0.8 - December 22nd, 2013
 - [#24](https://github.com/kpdecker/jsdiff/pull/24) - Handle windows newlines on non windows machines. ([@benogle](https://api.github.com/users/benogle))

From 4677fb2fa59dfb8374f312767ad9805b8a57f78b Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Tue, 25 Nov 2014 01:58:31 -0600
Subject: [PATCH 057/527] v1.1.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 7f5318fa..2aa34166 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "diff",
-  "version": "1.0.8",
+  "version": "1.1.0",
   "description": "A javascript text diff implementation.",
   "keywords": [
     "diff",

From 24694a6c3b2b861d6d341476db92d0fcb7dc7771 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 13:43:50 -0600
Subject: [PATCH 058/527] Optimize memory management

There were a number of cases that were wasting memory and causing large diffs to hit environmental memory limits. Greatly reduced this by tracking the value content in an integer counter and then doing a pass of the result set to reconstruct the value parameter after we have found the final solution. This avoids the large memory (and CPU) overhead associated with storing the temporary value solutions, many of which will be thrown out.

On my local dev machine this dropped exec time for #27 from 18min to 2min 5s.

Fixes #27 (or improves as best as the algorithm allows for)
---
 diff.js | 70 ++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 49 insertions(+), 21 deletions(-)

diff --git a/diff.js b/diff.js
index e151a475..a03f6f57 100644
--- a/diff.js
+++ b/diff.js
@@ -51,6 +51,31 @@
       return n;
     }
 
+    function buildValues(components, newString, oldString) {
+      var componentPos = 0,
+          componentLen = components.length,
+          newPos = 0,
+          oldPos = 0;
+
+      for (; componentPos < componentLen; componentPos++) {
+        var component = components[componentPos];
+        if (!component.removed) {
+          component.value = newString.slice(newPos, newPos + component.count).join('');
+          newPos += component.count;
+
+          // Common case
+          if (!component.added) {
+            oldPos += component.count;
+          }
+        } else {
+          component.value = oldString.slice(oldPos, oldPos + component.count).join('');
+          oldPos += component.count;
+        }
+      }
+
+      return components;
+    }
+
     var Diff = function(ignoreWhitespace) {
       this.ignoreWhitespace = ignoreWhitespace;
     };
@@ -74,10 +99,11 @@
           var maxEditLength = newLen + oldLen;
           var bestPath = [{ newPos: -1, components: [] }];
 
-          // Seed editLength = 0
+          // Seed editLength = 0, i.e. the content starts with the same values
           var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
           if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) {
-            return bestPath[0].components;
+            // Identity
+            return [{value: newString}];
           }
 
           for (var editLength = 1; editLength <= maxEditLength; editLength++) {
@@ -94,6 +120,7 @@
               var canAdd = addPath && addPath.newPos+1 < newLen;
               var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
               if (!canAdd && !canRemove) {
+                // If this path is a terminal then prune
                 bestPath[diagonalPath] = undefined;
                 continue;
               }
@@ -103,60 +130,60 @@
               // and does not pass the bounds of the diff graph
               if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
                 basePath = clonePath(removePath);
-                this.pushComponent(basePath.components, oldString[oldPos], undefined, true);
+                this.pushComponent(basePath.components, undefined, true);
               } else {
-                basePath = clonePath(addPath);
+                basePath = addPath;   // No need to clone, we've pulled it from the list
                 basePath.newPos++;
-                this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined);
+                this.pushComponent(basePath.components, true, undefined);
               }
 
               var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath);
 
+              // If we have hit the end of both strings, then we are done
               if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) {
-                return basePath.components;
+                return buildValues(basePath.components, newString, oldString);
               } else {
+                // Otherwise track this path as a potential candidate and continue.
                 bestPath[diagonalPath] = basePath;
               }
             }
           }
         },
 
-        pushComponent: function(components, value, added, removed) {
+        pushComponent: function(components, added, removed) {
           var last = components[components.length-1];
           if (last && last.added === added && last.removed === removed) {
             // We need to clone here as the component clone operation is just
             // as shallow array clone
-            components[components.length-1] =
-              {value: this.join(last.value, value), added: added, removed: removed };
+            components[components.length-1] = {count: last.count + 1, added: added, removed: removed };
           } else {
-            components.push({value: value, added: added, removed: removed });
+            components.push({count: 1, added: added, removed: removed });
           }
         },
         extractCommon: function(basePath, newString, oldString, diagonalPath) {
           var newLen = newString.length,
               oldLen = oldString.length,
               newPos = basePath.newPos,
-              oldPos = newPos - diagonalPath;
+              oldPos = newPos - diagonalPath,
+
+              commonCount = 0;
           while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) {
             newPos++;
             oldPos++;
+            commonCount++;
+          }
 
-            this.pushComponent(basePath.components, newString[newPos], undefined, undefined);
+          if (commonCount) {
+            basePath.components.push({count: commonCount});
           }
+
           basePath.newPos = newPos;
           return oldPos;
         },
 
         equals: function(left, right) {
           var reWhitespace = /\S/;
-          if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) {
-            return true;
-          } else {
-            return left === right;
-          }
-        },
-        join: function(left, right) {
-          return left + right;
+          return left === right || (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right));
         },
         tokenize: function(value) {
           return value;
@@ -186,7 +213,7 @@
             lastLine = lines[i - 1];
 
         // Merge lines that may contain windows new lines
-        if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') {
+        if (line === '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') {
           retLines[retLines.length - 1] += '\n';
         } else if (line) {
           retLines.push(line);
@@ -381,6 +408,7 @@
       module.exports = JsDiff;
   }
   else if (typeof define === 'function') {
+    /*global define */
     define([], function() { return JsDiff; });
   }
   else if (typeof global.JsDiff === 'undefined') {

From 23579b09348ab16cb1e8c1bcba555a75d657031e Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 14:57:16 -0600
Subject: [PATCH 059/527] Drop unused constant

---
 test/diffTest.js | 2 --
 1 file changed, 2 deletions(-)

diff --git a/test/diffTest.js b/test/diffTest.js
index 817376f2..81fbfa9f 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -1,5 +1,3 @@
-const VERBOSE = false;
-
 var diff = require('../diff');
 
 describe('#diffWords', function() {

From 8d28552e2c7a8b4333b9a88cb3114ab41eca3a15 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 16:18:16 -0600
Subject: [PATCH 060/527] Add coverage testing

---
 .gitignore   | 1 +
 diff.js      | 4 +++-
 package.json | 7 ++++---
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/.gitignore b/.gitignore
index 93f13619..df9af16b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
+coverage
 node_modules
 npm-debug.log
diff --git a/diff.js b/diff.js
index 46fb91d8..18d2c294 100644
--- a/diff.js
+++ b/diff.js
@@ -17,6 +17,7 @@
 (function(global, undefined) {
   var JsDiff = (function() {
     /*jshint maxparams: 5*/
+    /*istanbul ignore next*/
     function map(arr, mapper, that) {
       if (Array.prototype.map) {
         return Array.prototype.map.call(arr, mapper, that);
@@ -112,7 +113,7 @@
           // Seed editLength = 0, i.e. the content starts with the same values
           var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
           if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) {
-            // Identity
+            // Identity per the equality and tokenizer
             return [{value: newString}];
           }
 
@@ -485,6 +486,7 @@
     };
   })();
 
+  /*istanbul ignore next */
   if (typeof module !== 'undefined') {
       module.exports = JsDiff;
   }
diff --git a/package.json b/package.json
index 2aa34166..8bedb360 100644
--- a/package.json
+++ b/package.json
@@ -28,13 +28,14 @@
   },
   "main": "./diff",
   "scripts": {
-    "test": "node_modules/.bin/mocha test/*.js"
+    "test": "istanbul cover node_modules/.bin/_mocha test/*.js"
   },
   "dependencies": {},
   "devDependencies": {
+    "colors": "~0.6.2",
+    "istanbul": "^0.3.2",
     "mocha": "~1.6",
-    "should": "~1.2",
-    "colors": "~0.6.2"
+    "should": "~1.2"
   },
   "optionalDependencies": {},
   "files": [

From 0cd21f1386ea8f0ff2d8fb0b39c496430418060a Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 16:19:39 -0600
Subject: [PATCH 061/527] Fix ~identify equality case

---
 diff.js          | 2 +-
 test/diffTest.js | 5 +++++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/diff.js b/diff.js
index 18d2c294..b0e92365 100644
--- a/diff.js
+++ b/diff.js
@@ -114,7 +114,7 @@
           var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
           if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) {
             // Identity per the equality and tokenizer
-            return [{value: newString}];
+            return [{value: newString.join('')}];
           }
 
           for (var editLength = 1; editLength <= maxEditLength; editLength++) {
diff --git a/test/diffTest.js b/test/diffTest.js
index 7a4e1d98..6bada44a 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -53,6 +53,11 @@ describe('#diffWords', function() {
     var diffResult = diff.diffWords('New Value New Value', 'Value Value New New');
     diff.convertChangesToXML(diffResult).should.equal('ValueNew Value New NewValue');
   });
+
+  it('should diff has identical content', function() {
+    var diffResult = diff.diffWords('New Value', 'New  Value');
+    diff.convertChangesToXML(diffResult).should.equal('New  Value');
+  });
 });
 
 describe('#diffWordsWithSpace', function() {

From fa0ffb3f4caadb9ba27acf87155bbc3b1098c74a Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 16:21:43 -0600
Subject: [PATCH 062/527] Remove unused and nonsensical tokenize impl

---
 diff.js | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/diff.js b/diff.js
index b0e92365..3c92e641 100644
--- a/diff.js
+++ b/diff.js
@@ -196,9 +196,7 @@
           var reWhitespace = /\S/;
           return left === right || (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right));
         },
-        tokenize: function(value) {
-          return value;
-        }
+        tokenize: undefined
     };
 
     var CharDiff = new Diff();

From 785b4a2b12bd217c7d8d3ad630410727c099fd8c Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 16:22:36 -0600
Subject: [PATCH 063/527] Drop old node versions from Travis exec

---
 .travis.yml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index c693a939..6e5919de 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,3 @@
 language: node_js
 node_js:
-  - 0.6
-  - 0.8
   - "0.10"

From 641ee8af572ddcc99a14bab1f03bd86d39575e38 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 16:30:21 -0600
Subject: [PATCH 064/527] Fix incorrectly defined tests

---
 test/diffTest.js | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/test/diffTest.js b/test/diffTest.js
index 6bada44a..adb96d50 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -27,24 +27,27 @@ describe('#diffWords', function() {
   });
 
   // Diff without changes
-  exports['Diff without changes'] = function() {
+  it('should handle identity', function() {
     var diffResult = diff.diffWords('New Value', 'New Value');
     diff.convertChangesToXML(diffResult).should.equal('New Value');
-
-    diffResult = diff.diffWords('New Value', 'New  Value');
-    diff.convertChangesToXML(diffResult).should.equal('New  Value');
-
-    diffResult = diff.diffWords('', '');
+  });
+  it('should handle empty', function() {
+    var diffResult = diff.diffWords('', '');
     diff.convertChangesToXML(diffResult).should.equal('');
-  };
+  });
+  it('should diff has identical content', function() {
+    var diffResult = diff.diffWords('New Value', 'New  Value');
+    diff.convertChangesToXML(diffResult).should.equal('New  Value');
+  });
 
   // Empty diffs
-  it('should diff empty content', function() {
+  it('should diff empty new content', function() {
     var diffResult = diff.diffWords('New Value', '');
     diffResult.length.should.equal(1);
     diff.convertChangesToXML(diffResult).should.equal('New Value');
-
-    diffResult = diff.diffWords('', 'New Value');
+  });
+  it('should diff empty old content', function() {
+    var diffResult = diff.diffWords('', 'New Value');
     diff.convertChangesToXML(diffResult).should.equal('New Value');
   });
 
@@ -53,11 +56,6 @@ describe('#diffWords', function() {
     var diffResult = diff.diffWords('New Value New Value', 'Value Value New New');
     diff.convertChangesToXML(diffResult).should.equal('ValueNew Value New NewValue');
   });
-
-  it('should diff has identical content', function() {
-    var diffResult = diff.diffWords('New Value', 'New  Value');
-    diff.convertChangesToXML(diffResult).should.equal('New  Value');
-  });
 });
 
 describe('#diffWordsWithSpace', function() {

From eaa44ed125f3c3b5f797373fb6aef06966e50e1a Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 16:47:03 -0600
Subject: [PATCH 065/527] Fix diffChars implementation

---
 diff.js          | 4 +++-
 test/diffTest.js | 9 ++++++++-
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/diff.js b/diff.js
index 3c92e641..f9e3aea4 100644
--- a/diff.js
+++ b/diff.js
@@ -196,7 +196,9 @@
           var reWhitespace = /\S/;
           return left === right || (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right));
         },
-        tokenize: undefined
+        tokenize: function(value) {
+          return value.split('');
+        }
     };
 
     var CharDiff = new Diff();
diff --git a/test/diffTest.js b/test/diffTest.js
index adb96d50..1315e7b7 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -70,8 +70,15 @@ describe('#diffWordsWithSpace', function() {
   });
 });
 
-describe('#diffSentences', function() {
+describe('#diffChars', function() {
+  it('Should diff chars', function() {
+    var diffResult = diff.diffSentences('New Value.', 'New ValueMoreData.');
+    diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData.');
+  });
+});
+
 
+describe('#diffSentences', function() {
   it('Should diff Sentences', function() {
     var diffResult = diff.diffSentences('New Value.', 'New ValueMoreData.');
     diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData.New Value.');

From 19385b962fe658042f4fc72c344b74569497d9ea Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 17:01:31 -0600
Subject: [PATCH 066/527] Allow for optional async diffing

Allow callers to avoid blocking the event loop for large diffs (at the expense of code clarity :( )

Fixes #34
---
 diff.js          | 78 +++++++++++++++++++++++++++++++++++++-----------
 test/diffTest.js | 69 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 128 insertions(+), 19 deletions(-)

diff --git a/diff.js b/diff.js
index f9e3aea4..86c5dc81 100644
--- a/diff.js
+++ b/diff.js
@@ -91,16 +91,27 @@
       this.ignoreWhitespace = ignoreWhitespace;
     };
     Diff.prototype = {
-        diff: function(oldString, newString) {
+        diff: function(oldString, newString, callback) {
+          var self = this;
+
+          function done(value) {
+            if (callback) {
+              setTimeout(function() { callback(undefined, value); }, 0);
+              return true;
+            } else {
+              return value;
+            }
+          }
+
           // Handle the identity case (this is due to unrolling editLength == 0
           if (newString === oldString) {
-            return [{ value: newString }];
+            return done([{ value: newString }]);
           }
           if (!newString) {
-            return [{ value: oldString, removed: true }];
+            return done([{ value: oldString, removed: true }]);
           }
           if (!oldString) {
-            return [{ value: newString, added: true }];
+            return done([{ value: newString, added: true }]);
           }
 
           newString = this.tokenize(newString);
@@ -114,10 +125,11 @@
           var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
           if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) {
             // Identity per the equality and tokenizer
-            return [{value: newString.join('')}];
+            return done([{value: newString.join('')}]);
           }
 
-          for (var editLength = 1; editLength <= maxEditLength; editLength++) {
+          // Main worker method. checks all permutations of a given edit length for acceptance.
+          function execEditLength() {
             for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) {
               var basePath;
               var addPath = bestPath[diagonalPath-1],
@@ -141,23 +153,52 @@
               // and does not pass the bounds of the diff graph
               if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
                 basePath = clonePath(removePath);
-                this.pushComponent(basePath.components, undefined, true);
+                self.pushComponent(basePath.components, undefined, true);
               } else {
                 basePath = addPath;   // No need to clone, we've pulled it from the list
                 basePath.newPos++;
-                this.pushComponent(basePath.components, true, undefined);
+                self.pushComponent(basePath.components, true, undefined);
               }
 
-              var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath);
+              var oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath);
 
               // If we have hit the end of both strings, then we are done
               if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) {
-                return buildValues(basePath.components, newString, oldString, this.useLongestToken);
+                return done(buildValues(basePath.components, newString, oldString, self.useLongestToken));
               } else {
                 // Otherwise track this path as a potential candidate and continue.
                 bestPath[diagonalPath] = basePath;
               }
             }
+
+            editLength++;
+          }
+
+          // Performs the length of edit iteration. Is a bit fugly as this has to support the 
+          // sync and async mode which is never fun. Loops over execEditLength until a value
+          // is produced.
+          var editLength = 1;
+          if (callback) {
+            (function exec() {
+              setTimeout(function() {
+                // This should not happen, but we want to be safe.
+                /*istanbul ignore next */
+                if (editLength > maxEditLength) {
+                  return callback();
+                }
+
+                if (!execEditLength()) {
+                  exec();
+                }
+              }, 0);
+            })();
+          } else {
+            while(editLength <= maxEditLength) {
+              var ret = execEditLength();
+              if (ret) {
+                return ret;
+              }
+            }
           }
         },
 
@@ -299,17 +340,18 @@
     return {
       Diff: Diff,
 
-      diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); },
-      diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); },
-      diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); },
-      diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); },
-      diffSentences: function(oldStr, newStr) { return SentenceDiff.diff(oldStr, newStr); },
+      diffChars: function(oldStr, newStr, callback) { return CharDiff.diff(oldStr, newStr, callback); },
+      diffWords: function(oldStr, newStr, callback) { return WordDiff.diff(oldStr, newStr, callback); },
+      diffWordsWithSpace: function(oldStr, newStr, callback) { return WordWithSpaceDiff.diff(oldStr, newStr, callback); },
+      diffLines: function(oldStr, newStr, callback) { return LineDiff.diff(oldStr, newStr, callback); },
+      diffSentences: function(oldStr, newStr, callback) { return SentenceDiff.diff(oldStr, newStr, callback); },
 
-      diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); },
-      diffJson: function(oldObj, newObj) {
+      diffCss: function(oldStr, newStr, callback) { return CssDiff.diff(oldStr, newStr, callback); },
+      diffJson: function(oldObj, newObj, callback) {
         return JsonDiff.diff(
           typeof oldObj === 'string' ? oldObj : JSON.stringify(canonicalize(oldObj), undefined, '  '),
-          typeof newObj === 'string' ? newObj : JSON.stringify(canonicalize(newObj), undefined, '  ')
+          typeof newObj === 'string' ? newObj : JSON.stringify(canonicalize(newObj), undefined, '  '),
+          callback
         );
       },
 
diff --git a/test/diffTest.js b/test/diffTest.js
index 1315e7b7..cfc01f37 100644
--- a/test/diffTest.js
+++ b/test/diffTest.js
@@ -58,6 +58,73 @@ describe('#diffWords', function() {
   });
 });
 
+describe('#diffWords - async', function() {
+  it('should diff whitespace', function(done) {
+    diff.diffWords('New Value', 'New  ValueMoreData', function(err, diffResult) {
+      diff.convertChangesToXML(diffResult).should.equal('New  ValueMoreDataValue');
+      done();
+    });
+  });
+
+  it('should diff multiple whitespace values', function(done) {
+    diff.diffWords('New Value  ', 'New  ValueMoreData ', function(err, diffResult) {
+      diff.convertChangesToXML(diffResult).should.equal('New  ValueMoreDataValue ');
+      done();
+    });
+  });
+
+  // Diff on word boundary
+  it('should diff on word boundaries', function(done) {
+    diff.diffWords('New :Value:Test', 'New  ValueMoreData ', function(err, diffResult) {
+      diff.convertChangesToXML(diffResult).should.equal('New  ValueMoreData :Value:Test');
+      done();
+    });
+  });
+
+  // Diff without changes
+  it('should handle identity', function(done) {
+    diff.diffWords('New Value', 'New Value', function(err, diffResult) {
+      diff.convertChangesToXML(diffResult).should.equal('New Value');
+      done();
+    });
+  });
+  it('should handle empty', function(done) {
+    diff.diffWords('', '', function(err, diffResult) {
+      diff.convertChangesToXML(diffResult).should.equal('');
+      done();
+    });
+  });
+  it('should diff has identical content', function(done) {
+    diff.diffWords('New Value', 'New  Value', function(err, diffResult) {
+      diff.convertChangesToXML(diffResult).should.equal('New  Value');
+      done();
+    });
+  });
+
+  // Empty diffs
+  it('should diff empty new content', function(done) {
+    diff.diffWords('New Value', '', function(err, diffResult) {
+      diffResult.length.should.equal(1);
+      diff.convertChangesToXML(diffResult).should.equal('New Value');
+      done();
+    });
+  });
+  it('should diff empty old content', function(done) {
+    diff.diffWords('', 'New Value', function(err, diffResult) {
+      diff.convertChangesToXML(diffResult).should.equal('New Value');
+      done();
+    });
+  });
+
+  // With without anchor (the Heckel algorithm error case)
+  it('should diff when there is no anchor value', function(done) {
+    diff.diffWords('New Value New Value', 'Value Value New New', function(err, diffResult) {
+      diff.convertChangesToXML(diffResult).should.equal('ValueNew Value New NewValue');
+      done();
+    });
+  });
+});
+
 describe('#diffWordsWithSpace', function() {
   it('should diff whitespace', function() {
     var diffResult = diff.diffWordsWithSpace('New Value', 'New  ValueMoreData');
@@ -72,7 +139,7 @@ describe('#diffWordsWithSpace', function() {
 
 describe('#diffChars', function() {
   it('Should diff chars', function() {
-    var diffResult = diff.diffSentences('New Value.', 'New ValueMoreData.');
+    var diffResult = diff.diffChars('New Value.', 'New ValueMoreData.');
     diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData.');
   });
 });

From e2c6f6a4654b4b0323c9a7cf42f4d2c708b28379 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 17:03:28 -0600
Subject: [PATCH 067/527] Update docs for latest changes

---
 README.md | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 1bc32d5a..9566b129 100644
--- a/README.md
+++ b/README.md
@@ -17,19 +17,31 @@ or
 
 ## API
 
-* `JsDiff.diffChars(oldStr, newStr)` - diffs two blocks of text, comparing character by character.
+* `JsDiff.diffChars(oldStr, newStr[, callback])` - diffs two blocks of text, comparing character by character.
 
     Returns a list of change objects (See below).
 
-* `JsDiff.diffWords(oldStr, newStr)` - diffs two blocks of text, comparing word by word.
+* `JsDiff.diffWords(oldStr, newStr[, callback])` - diffs two blocks of text, comparing word by word, ignoring whitespace.
 
     Returns a list of change objects (See below).
 
-* `JsDiff.diffLines(oldStr, newStr)` - diffs two blocks of text, comparing line by line.
+* `JsDiff.diffWordsWithSpace(oldStr, newStr[, callback])` - diffs two blocks of text, comparing word by word, treating whitespace as significant.
 
     Returns a list of change objects (See below).
 
-* `JsDiff.diffCss(oldStr, newStr)` - diffs two blocks of text, comparing CSS tokens.
+* `JsDiff.diffLines(oldStr, newStr[, callback])` - diffs two blocks of text, comparing line by line.
+
+    Returns a list of change objects (See below).
+
+* `JsDiff.diffSentences(oldStr, newStr[, callback])` - diffs two blocks of text, comparing sentence by sentence.
+
+    Returns a list of change objects (See below).
+
+* `JsDiff.diffCss(oldStr, newStr[, callback])` - diffs two blocks of text, comparing CSS tokens.
+
+    Returns a list of change objects (See below).
+
+* `JsDiff.diffJson(oldObj, newObj[, callback])` - diffs two JSON objects, comparing the fields defined on each. The order of fields, etc does not matter in this comparison.
 
     Returns a list of change objects (See below).
 
@@ -48,6 +60,9 @@ or
 
 * `convertChangesToXML(changes)` - converts a list of changes to a serialized XML format
 
+
+All methods above which accept the optional callback method will run in sync mode when that parameter is omitted and in async mode when supplied. This allows for larger diffs without blocking the event loop.
+
 ### Change Objects
 Many of the methods above return change objects. These objects are consist of the following fields:
 

From 181fb302eca550bfa30822e9b7358e9361627261 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 17:05:52 -0600
Subject: [PATCH 068/527] Update release notes

---
 release-notes.md | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/release-notes.md b/release-notes.md
index f1b30e87..71164479 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -2,7 +2,16 @@
 
 ## Development
 
-[Commits](https://github.com/kpdecker/jsdiff/compare/v1.1.0...master)
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.2.0...master)
+
+## v1.2.0 - November 29th, 2014
+- [#37](https://github.com/kpdecker/jsdiff/pull/37) - Add support for sentences. ([@vmariano](https://api.github.com/users/vmariano))
+- [#28](https://github.com/kpdecker/jsdiff/pull/28) - Implemented diffJson ([@papandreou](https://api.github.com/users/papandreou))
+- [#27](https://github.com/kpdecker/jsdiff/issues/27) - Slow to execute over diffs with a large number of changes ([@termi](https://api.github.com/users/termi))
+- Allow for optional async diffing - 19385b9
+- Fix diffChars implementation - eaa44ed
+
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.1.0...v1.2.0)
 
 ## v1.1.0 - November 25th, 2014
 - [#33](https://github.com/kpdecker/jsdiff/pull/33) - AMD and global exports ([@ovcharik](https://api.github.com/users/ovcharik))

From 8527fe43fbdd206c3b9fdeed767478746edaa82e Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Sat, 29 Nov 2014 17:05:54 -0600
Subject: [PATCH 069/527] v1.2.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 8bedb360..6c4d3b6c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "diff",
-  "version": "1.1.0",
+  "version": "1.2.0",
   "description": "A javascript text diff implementation.",
   "keywords": [
     "diff",

From 46bff51542c39815dcfe75ebe5d838bc5c84f1c4 Mon Sep 17 00:00:00 2001
From: ChulHee Lee 
Date: Mon, 22 Dec 2014 13:43:24 +0900
Subject: [PATCH 070/527] change condition of using node export system.

The original condition of using node export system causes error when used with QUnit.
---
 diff.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/diff.js b/diff.js
index 86c5dc81..5b31e234 100644
--- a/diff.js
+++ b/diff.js
@@ -529,8 +529,12 @@
   })();
 
   /*istanbul ignore next */
-  if (typeof module !== 'undefined') {
-      module.exports = JsDiff;
+  if (typeof exports !== 'undefined') {
+    /* node.js export */
+    if (typeof module !== 'undefined' && module.exports) {
+      exports = module.exports = JsDiff;
+    }
+    exports.JsDiff = JsDiff;
   }
   else if (typeof define === 'function') {
     /*global define */

From f1d0d7ce2b36c6d8f2f7fd8a1e115497d7f2d8e4 Mon Sep 17 00:00:00 2001
From: ChulHee Lee 
Date: Fri, 26 Dec 2014 18:13:15 +0900
Subject: [PATCH 071/527] check module.exports existence

---
 diff.js | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/diff.js b/diff.js
index 5b31e234..af82963b 100644
--- a/diff.js
+++ b/diff.js
@@ -529,12 +529,8 @@
   })();
 
   /*istanbul ignore next */
-  if (typeof exports !== 'undefined') {
-    /* node.js export */
-    if (typeof module !== 'undefined' && module.exports) {
-      exports = module.exports = JsDiff;
-    }
-    exports.JsDiff = JsDiff;
+  if (typeof module !== 'undefined' && module.exports) {
+    module.exports = JsDiff;
   }
   else if (typeof define === 'function') {
     /*global define */

From 91796f244f235190984bfb715f4ef8f3a87c1239 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Fri, 26 Dec 2014 13:33:34 -0600
Subject: [PATCH 072/527] Update release notes

---
 release-notes.md | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/release-notes.md b/release-notes.md
index 71164479..0b3af425 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -2,7 +2,12 @@
 
 ## Development
 
-[Commits](https://github.com/kpdecker/jsdiff/compare/v1.2.0...master)
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.2.1...master)
+
+## v1.2.1 - December 26th, 2014
+- [#41](https://github.com/kpdecker/jsdiff/pull/41) - change condition of using node export system. ([@ironhee](https://api.github.com/users/ironhee))
+
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.2.0...v1.2.1)
 
 ## v1.2.0 - November 29th, 2014
 - [#37](https://github.com/kpdecker/jsdiff/pull/37) - Add support for sentences. ([@vmariano](https://api.github.com/users/vmariano))

From d041c27fe4218fee299f27f9276324b1517cf0ee Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Fri, 26 Dec 2014 13:33:41 -0600
Subject: [PATCH 073/527] v1.2.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 6c4d3b6c..89600c6f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "diff",
-  "version": "1.2.0",
+  "version": "1.2.1",
   "description": "A javascript text diff implementation.",
   "keywords": [
     "diff",

From ca673f10ac53d70e04bfd4bcef6187817b29e65b Mon Sep 17 00:00:00 2001
From: Nate Brustein 
Date: Tue, 13 Jan 2015 13:42:33 -0500
Subject: [PATCH 074/527] added a bower file

---
 bower.json | 9 +++++++++
 1 file changed, 9 insertions(+)
 create mode 100644 bower.json

diff --git a/bower.json b/bower.json
new file mode 100644
index 00000000..6bd1a7f5
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,9 @@
+{
+    "name": "jsdiff",
+    "version": "1.2.1",
+    "main": [
+        "diff.js"
+    ],
+    "dependencies": {},
+    "ignore": []
+}
\ No newline at end of file

From 5350601ba52686d724b88fd78fd5a72e433f0bba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Carric=CC=A7o?= 
Date: Fri, 23 Jan 2015 15:31:29 +0000
Subject: [PATCH 075/527] Fix AMD module loading

---
 diff.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/diff.js b/diff.js
index af82963b..84b3fa39 100644
--- a/diff.js
+++ b/diff.js
@@ -532,7 +532,7 @@
   if (typeof module !== 'undefined' && module.exports) {
     module.exports = JsDiff;
   }
-  else if (typeof define === 'function') {
+  else if (typeof define === 'function' && define.amd) {
     /*global define */
     define([], function() { return JsDiff; });
   }

From b3a15bb25d6d98cd3895bf9d06941260052caf46 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Mon, 26 Jan 2015 16:55:18 -0600
Subject: [PATCH 076/527] Update release notes

---
 release-notes.md | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/release-notes.md b/release-notes.md
index 0b3af425..6c8dbb5d 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -2,7 +2,13 @@
 
 ## Development
 
-[Commits](https://github.com/kpdecker/jsdiff/compare/v1.2.1...master)
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.2.2...master)
+
+## v1.2.2 - January 26th, 2015
+- [#45](https://github.com/kpdecker/jsdiff/pull/45) - Fix AMD module loading ([@pedrocarrico](https://api.github.com/users/pedrocarrico))
+- [#43](https://github.com/kpdecker/jsdiff/pull/43) - added a bower file ([@nbrustein](https://api.github.com/users/nbrustein))
+
+[Commits](https://github.com/kpdecker/jsdiff/compare/v1.2.1...v1.2.2)
 
 ## v1.2.1 - December 26th, 2014
 - [#41](https://github.com/kpdecker/jsdiff/pull/41) - change condition of using node export system. ([@ironhee](https://api.github.com/users/ironhee))

From 5199cc4ee4f16b33f5bfddd4d70e2df8338caf60 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Mon, 26 Jan 2015 16:55:22 -0600
Subject: [PATCH 077/527] v1.2.2

---
 bower.json   | 16 ++++++++--------
 package.json |  2 +-
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/bower.json b/bower.json
index 6bd1a7f5..95b56b53 100644
--- a/bower.json
+++ b/bower.json
@@ -1,9 +1,9 @@
 {
-    "name": "jsdiff",
-    "version": "1.2.1",
-    "main": [
-        "diff.js"
-    ],
-    "dependencies": {},
-    "ignore": []
-}
\ No newline at end of file
+  "name": "jsdiff",
+  "version": "1.2.2",
+  "main": [
+    "diff.js"
+  ],
+  "dependencies": {},
+  "ignore": []
+}
diff --git a/package.json b/package.json
index 89600c6f..df3a96f6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "diff",
-  "version": "1.2.1",
+  "version": "1.2.2",
   "description": "A javascript text diff implementation.",
   "keywords": [
     "diff",

From 3ac9cb4c9e5611c9a64627d1f69424a895a12386 Mon Sep 17 00:00:00 2001
From: James Gould 
Date: Sun, 8 Feb 2015 23:00:46 -0700
Subject: [PATCH 078/527] Adding Diff Trimmed Lines

I extended the LineDiff.tokenize function to add the ability to ignore
leading and trailing spaces when using TrimmedLineDiff, and added
diffTrimmedLines to the public-facing functions. My changes should not
affect any previous functionality.
---
 diff.js    | 18 +++++++++++++-----
 index.html |  1 +
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/diff.js b/diff.js
index 84b3fa39..8bb65b88 100644
--- a/diff.js
+++ b/diff.js
@@ -15,6 +15,7 @@
  * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
  */
 (function(global, undefined) {
+
   var JsDiff = (function() {
     /*jshint maxparams: 5*/
     /*istanbul ignore next*/
@@ -114,8 +115,8 @@
             return done([{ value: newString, added: true }]);
           }
 
-          newString = this.tokenize(newString);
-          oldString = this.tokenize(oldString);
+          newString = this.tokenize(newString, !!self.ignoreTrim);
+          oldString = this.tokenize(oldString, !!self.ignoreTrim);
 
           var newLen = newString.length, oldLen = oldString.length;
           var maxEditLength = newLen + oldLen;
@@ -174,7 +175,7 @@
             editLength++;
           }
 
-          // Performs the length of edit iteration. Is a bit fugly as this has to support the 
+          // Performs the length of edit iteration. Is a bit fugly as this has to support the
           // sync and async mode which is never fun. Loops over execEditLength until a value
           // is produced.
           var editLength = 1;
@@ -256,7 +257,11 @@
     };
 
     var LineDiff = new Diff();
-    LineDiff.tokenize = function(value) {
+
+    var TrimmedLineDiff = new Diff();
+    TrimmedLineDiff.ignoreTrim = true;
+
+    LineDiff.tokenize = TrimmedLineDiff.tokenize = function(value, ignoreTrim) {
       var retLines = [],
           lines = value.split(/^/m);
 
@@ -268,13 +273,14 @@
         if (line === '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') {
           retLines[retLines.length - 1] += '\n';
         } else if (line) {
-          retLines.push(line);
+          retLines.push(ignoreTrim ? line.trim() + '\n' : line);
         }
       }
 
       return retLines;
     };
 
+
     var SentenceDiff = new Diff();
     SentenceDiff.tokenize = function (value) {
       return removeEmpty(value.split(/(\S.+?[.!?])(?=\s+|$)/));
@@ -344,6 +350,8 @@
       diffWords: function(oldStr, newStr, callback) { return WordDiff.diff(oldStr, newStr, callback); },
       diffWordsWithSpace: function(oldStr, newStr, callback) { return WordWithSpaceDiff.diff(oldStr, newStr, callback); },
       diffLines: function(oldStr, newStr, callback) { return LineDiff.diff(oldStr, newStr, callback); },
+      diffTrimmedLines: function(oldStr, newStr, callback) { return TrimmedLineDiff.diff(oldStr, newStr, callback); },
+
       diffSentences: function(oldStr, newStr, callback) { return SentenceDiff.diff(oldStr, newStr, callback); },
 
       diffCss: function(oldStr, newStr, callback) { return CssDiff.diff(oldStr, newStr, callback); },
diff --git a/index.html b/index.html
index 33bb403b..a6dff732 100644
--- a/index.html
+++ b/index.html
@@ -11,6 +11,7 @@ 

Diff

+ From 1892d7796392dd269542d3e29760769661d5db55 Mon Sep 17 00:00:00 2001 From: James Gould Date: Tue, 10 Feb 2015 21:07:23 -0700 Subject: [PATCH 079/527] Documentation and tests for TrimmedLineDiff Refactoring to avoid changing tokenize API, adding documentation to README.md, and adding 4 tests for TrimmedLineDiff to diffTest.js. I also found a bug in how TrimmedLineDiff handled windows new lines while adding the test, as well as changed TrimmedLineDiff so it doesn't add a newline character ('\n') if it is the last line. --- README.md | 5 ++++- diff.js | 26 +++++++++++++++++++------- test/diffTest.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9566b129..1eb2d3d0 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ or Returns a list of change objects (See below). +* `JsDiff.TrimmedLineDiff(oldStr, newStr[, callback])` - diffs two blocks of text, comparing line by line, ignoring leading and trailing whitespace. + + Returns a list of change objects (See below). + * `JsDiff.diffSentences(oldStr, newStr[, callback])` - diffs two blocks of text, comparing sentence by sentence. Returns a list of change objects (See below). @@ -166,4 +170,3 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/kpdecker/jsdiff/trend.png)](https://bitdeli.com/free "Bitdeli Badge") - diff --git a/diff.js b/diff.js index 8bb65b88..7c5572dd 100644 --- a/diff.js +++ b/diff.js @@ -115,8 +115,8 @@ return done([{ value: newString, added: true }]); } - newString = this.tokenize(newString, !!self.ignoreTrim); - oldString = this.tokenize(oldString, !!self.ignoreTrim); + newString = this.tokenize(newString); + oldString = this.tokenize(oldString); var newLen = newString.length, oldLen = oldString.length; var maxEditLength = newLen + oldLen; @@ -261,19 +261,31 @@ var TrimmedLineDiff = new Diff(); TrimmedLineDiff.ignoreTrim = true; - LineDiff.tokenize = TrimmedLineDiff.tokenize = function(value, ignoreTrim) { + LineDiff.tokenize = TrimmedLineDiff.tokenize = function(value) { var retLines = [], lines = value.split(/^/m); - for(var i = 0; i < lines.length; i++) { var line = lines[i], lastLine = lines[i - 1]; // Merge lines that may contain windows new lines - if (line === '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') { - retLines[retLines.length - 1] += '\n'; + if (line === '\n' && lastLine && lastLine[lastLine.length - 1]) { + if(this.ignoreTrim){ + retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0,-1) + '\r\n'; + } + else{ + retLines[retLines.length - 1] += '\n'; + } } else if (line) { - retLines.push(ignoreTrim ? line.trim() + '\n' : line); + if(this.ignoreTrim){ + + line = line.trim(); + //add a newline unless this is the last line. + if(!(i + 1 === lines.length)){ + line += '\n'; + } + } + retLines.push(line); } } diff --git a/test/diffTest.js b/test/diffTest.js index cfc01f37..e36244c8 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -200,6 +200,36 @@ describe('#diffLines', function() { }); }); +// Line Diff +describe('#TrimmedLineDiff', function() { + it('should diff lines', function() { + var diffResult = diff.diffTrimmedLines( + 'line\nold value\nline', + 'line\nnew value\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\nnew value\nold value\nline'); + }); + it('should the same lines in diff', function() { + var diffResult = diff.diffTrimmedLines( + 'line\nvalue\nline', + 'line\nvalue\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\nvalue\nline'); + }); + + it('should ignore shorespace', function() { + var diffResult = diff.diffTrimmedLines( + 'line\nvalue \nline', + 'line\nvalue\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\nvalue\nline'); + }); + + it('should handle windows line endings', function() { + var diffResult = diff.diffTrimmedLines( + 'line\r\nold value \r\nline', + 'line\r\nnew value\r\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\r\nnew value\r\nold value\r\nline'); + }); +}); + describe('#diffJson', function() { it('should accept objects', function() { diff.diffJson( From b8cd87f7531818d7d5f3f54941bd95855b25b474 Mon Sep 17 00:00:00 2001 From: James Gould Date: Thu, 12 Feb 2015 10:00:47 -0700 Subject: [PATCH 080/527] Fixing windows new line merge and readme I updated the Readme with the Function rather than the Object name. In the windows new line merge, I added a check for '\n', as well as added a comment. The reason I've extended the windows new line merge functionality is to handle any situations where '\n\n' may come up. We want '\r\n' instead. --- README.md | 2 +- diff.js | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1eb2d3d0..0f62251f 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ or Returns a list of change objects (See below). -* `JsDiff.TrimmedLineDiff(oldStr, newStr[, callback])` - diffs two blocks of text, comparing line by line, ignoring leading and trailing whitespace. +* `JsDiff.diffTrimmedLines(oldStr, newStr[, callback])` - diffs two blocks of text, comparing line by line, ignoring leading and trailing whitespace. Returns a list of change objects (See below). diff --git a/diff.js b/diff.js index 7c5572dd..bc72c692 100644 --- a/diff.js +++ b/diff.js @@ -266,11 +266,14 @@ lines = value.split(/^/m); for(var i = 0; i < lines.length; i++) { var line = lines[i], - lastLine = lines[i - 1]; + lastLine = lines[i - 1], + lastLineLastChar = lastLine ? lastLine[lastLine.length - 1] : ''; // Merge lines that may contain windows new lines - if (line === '\n' && lastLine && lastLine[lastLine.length - 1]) { - if(this.ignoreTrim){ + if (line === '\n' && lastLine && + (lastLineLastChar === '\r' || lastLineLastChar === '\n')) { + if(this.ignoreTrim || lastLineLastChar === '\n'){ + //to avoid merging to \n\n, remove \n and add \r\n. retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0,-1) + '\r\n'; } else{ @@ -278,7 +281,6 @@ } } else if (line) { if(this.ignoreTrim){ - line = line.trim(); //add a newline unless this is the last line. if(!(i + 1 === lines.length)){ From cb0c827ecd69db514970b0f3dcc72119019cef02 Mon Sep 17 00:00:00 2001 From: James Gould Date: Wed, 18 Feb 2015 20:36:40 -0700 Subject: [PATCH 081/527] Style, nitpick, and word choice Fixing everything discussed in the pull request. Also noticed the comment for the Trimmed Line Diff tests just said Line Diff, and fixed if ( style lower in diff.js --- diff.js | 26 ++++++++++++-------------- test/diffTest.js | 6 +++--- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/diff.js b/diff.js index bc72c692..8f9426fa 100644 --- a/diff.js +++ b/diff.js @@ -270,20 +270,18 @@ lastLineLastChar = lastLine ? lastLine[lastLine.length - 1] : ''; // Merge lines that may contain windows new lines - if (line === '\n' && lastLine && - (lastLineLastChar === '\r' || lastLineLastChar === '\n')) { - if(this.ignoreTrim || lastLineLastChar === '\n'){ + if (line === '\n' && (lastLineLastChar === '\r' || lastLineLastChar === '\n')) { + if (this.ignoreTrim || lastLineLastChar === '\n'){ //to avoid merging to \n\n, remove \n and add \r\n. retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0,-1) + '\r\n'; - } - else{ + } else { retLines[retLines.length - 1] += '\n'; } } else if (line) { - if(this.ignoreTrim){ + if (this.ignoreTrim) { line = line.trim(); //add a newline unless this is the last line. - if(!(i + 1 === lines.length)){ + if (i < lines.length - 1) { line += '\n'; } } @@ -469,7 +467,7 @@ addEOFNL = false; for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { - if(diffstr[i][0] === '@') { + if (diffstr[i][0] === '@') { var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); diff.unshift({ start:meh[3], @@ -478,17 +476,17 @@ newlength:meh[4], newlines:[] }); - } else if(diffstr[i][0] === '+') { + } else if (diffstr[i][0] === '+') { diff[0].newlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] === '-') { + } else if (diffstr[i][0] === '-') { diff[0].oldlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] === ' ') { + } else if (diffstr[i][0] === ' ') { diff[0].newlines.push(diffstr[i].substr(1)); diff[0].oldlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] === '\\') { + } else if (diffstr[i][0] === '\\') { if (diffstr[i-1][0] === '+') { remEOFNL = true; - } else if(diffstr[i-1][0] === '-') { + } else if (diffstr[i-1][0] === '-') { addEOFNL = true; } } @@ -498,7 +496,7 @@ for (var i = diff.length - 1; i >= 0; i--) { var d = diff[i]; for (var j = 0; j < d.oldlength; j++) { - if(str[d.start-1+j] !== d.oldlines[j]) { + if (str[d.start-1+j] !== d.oldlines[j]) { return false; } } diff --git a/test/diffTest.js b/test/diffTest.js index e36244c8..f52d2852 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -185,7 +185,7 @@ describe('#diffLines', function() { diff.convertChangesToXML(diffResult).should.equal('line\nvalue\nline'); }); - it('should handle shorespace', function() { + it('should handle leading and trailing whitespace', function() { var diffResult = diff.diffLines( 'line\nvalue \nline', 'line\nvalue\nline'); @@ -200,7 +200,7 @@ describe('#diffLines', function() { }); }); -// Line Diff +// Trimmed Line Diff describe('#TrimmedLineDiff', function() { it('should diff lines', function() { var diffResult = diff.diffTrimmedLines( @@ -215,7 +215,7 @@ describe('#TrimmedLineDiff', function() { diff.convertChangesToXML(diffResult).should.equal('line\nvalue\nline'); }); - it('should ignore shorespace', function() { + it('should ignore leading and trailing whitespace', function() { var diffResult = diff.diffTrimmedLines( 'line\nvalue \nline', 'line\nvalue\nline'); From e83af50d1485904d5baba35a30da1f0ff806f02b Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 2 Mar 2015 10:32:38 -0800 Subject: [PATCH 082/527] Update release notes --- release-notes.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/release-notes.md b/release-notes.md index 6c8dbb5d..23031387 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,12 @@ ## Development -[Commits](https://github.com/kpdecker/jsdiff/compare/v1.2.2...master) +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.3.0...master) + +## v1.3.0 - March 2nd, 2015 +- [#47](https://github.com/kpdecker/jsdiff/pull/47) - Adding Diff Trimmed Lines ([@JamesGould123](https://api.github.com/users/JamesGould123)) + +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.2.2...v1.3.0) ## v1.2.2 - January 26th, 2015 - [#45](https://github.com/kpdecker/jsdiff/pull/45) - Fix AMD module loading ([@pedrocarrico](https://api.github.com/users/pedrocarrico)) From b7f143cbbe959d5ccc5c4ef3c5aef69d61503c68 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 2 Mar 2015 10:32:40 -0800 Subject: [PATCH 083/527] v1.3.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 95b56b53..827cf9bc 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "jsdiff", - "version": "1.2.2", + "version": "1.3.0", "main": [ "diff.js" ], diff --git a/package.json b/package.json index df3a96f6..5977c67c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "1.2.2", + "version": "1.3.0", "description": "A javascript text diff implementation.", "keywords": [ "diff", From 13b33ef3185972a17618f48a5980f8f9ae0f6093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9licien=20FRANCOIS?= Date: Fri, 13 Mar 2015 14:29:45 +0100 Subject: [PATCH 084/527] Fix for #51 Wrong result of JsDiff.diffLines --- diff.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/diff.js b/diff.js index 8f9426fa..2d42039e 100644 --- a/diff.js +++ b/diff.js @@ -270,13 +270,8 @@ lastLineLastChar = lastLine ? lastLine[lastLine.length - 1] : ''; // Merge lines that may contain windows new lines - if (line === '\n' && (lastLineLastChar === '\r' || lastLineLastChar === '\n')) { - if (this.ignoreTrim || lastLineLastChar === '\n'){ - //to avoid merging to \n\n, remove \n and add \r\n. + if (line === '\n' && lastLineLastChar === '\r') { retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0,-1) + '\r\n'; - } else { - retLines[retLines.length - 1] += '\n'; - } } else if (line) { if (this.ignoreTrim) { line = line.trim(); From 824c365ef460b242695e4e06b4587183cec7a3d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9licien=20FRANCOIS?= Date: Fri, 13 Mar 2015 15:11:06 +0100 Subject: [PATCH 085/527] Test case for #51 fix --- test/diffTest.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/diffTest.js b/test/diffTest.js index f52d2852..ba02eacb 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -198,6 +198,13 @@ describe('#diffLines', function() { 'line\r\nnew value\r\nline'); diff.convertChangesToXML(diffResult).should.equal('line\r\nnew value\r\nold value \r\nline'); }); + + it('should handle empty lines', function() { + var diffResult = diff.diffLines( + 'line\n\nold value \n\nline', + 'line\n\nnew value\n\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\n\nnew value\nold value \n\nline'); + }); }); // Trimmed Line Diff From 284df85cd7b8c0dea6de5818300a640481c5f89f Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 13 Mar 2015 10:47:00 -0500 Subject: [PATCH 086/527] Update release notes --- release-notes.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/release-notes.md b/release-notes.md index 23031387..9e5720d2 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,12 @@ ## Development -[Commits](https://github.com/kpdecker/jsdiff/compare/v1.3.0...master) +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.3.1...master) + +## v1.3.1 - March 13th, 2015 +- [#52](https://github.com/kpdecker/jsdiff/pull/52) - Fix for #51 Wrong result of JsDiff.diffLines ([@felicienfrancois](https://api.github.com/users/felicienfrancois)) + +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.3.0...v1.3.1) ## v1.3.0 - March 2nd, 2015 - [#47](https://github.com/kpdecker/jsdiff/pull/47) - Adding Diff Trimmed Lines ([@JamesGould123](https://api.github.com/users/JamesGould123)) From 018333d019ce280be622d08355e76545244ffca1 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 13 Mar 2015 10:47:05 -0500 Subject: [PATCH 087/527] v1.3.1 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 827cf9bc..372a7343 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "jsdiff", - "version": "1.3.0", + "version": "1.3.1", "main": [ "diff.js" ], diff --git a/package.json b/package.json index 5977c67c..01169041 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "1.3.0", + "version": "1.3.1", "description": "A javascript text diff implementation.", "keywords": [ "diff", From 8461626a5d4f28d432cd4424ca3416a92ec2eb95 Mon Sep 17 00:00:00 2001 From: Olivia Briggs Date: Thu, 26 Mar 2015 17:34:58 -0400 Subject: [PATCH 088/527] Updated README.MD with Bower installation --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0f62251f..ffaa0607 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ Based on the algorithm proposed in npm install diff +or + + bower install jsdiff + or git clone git://github.com/kpdecker/jsdiff.git From 6c2844e1785a223d55732d9565c9452f2422a838 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 30 Mar 2015 21:39:26 -0500 Subject: [PATCH 089/527] Ignore header content when applying patch Fixes #49 --- diff.js | 10 ++++++++-- test/applyPatch.js | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/diff.js b/diff.js index 2d42039e..22040858 100644 --- a/diff.js +++ b/diff.js @@ -458,10 +458,16 @@ applyPatch: function(oldStr, uniDiff) { var diffstr = uniDiff.split('\n'); var diff = []; - var remEOFNL = false, + var i = 0, + remEOFNL = false, addEOFNL = false; - for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { + // Skip to the first change chunk + while (i < diffstr.length && !/^@@/.test(diffstr[i])) { + i++; + } + + for (; i < diffstr.length; i++) { if (diffstr[i][0] === '@') { var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); diff.unshift({ diff --git a/test/applyPatch.js b/test/applyPatch.js index b7d0207e..f7cae2a0 100644 --- a/test/applyPatch.js +++ b/test/applyPatch.js @@ -269,4 +269,24 @@ describe('#applyPatch', function() { + '+++ testFileName\tNew Header\n'; diff.applyPatch(oldFile, diffFile).should.equal(oldFile); }); + + it('should apply patches that lack an index header', function() { + diff.applyPatch( + 'line2\n'+ + 'line3\n'+ + 'line5\n', + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,3 +1,4 @@\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + ' line5\n') + .should.equal( + 'line2\n' + + 'line3\n' + + 'line4\n' + + 'line5\n'); + }); }); From fa61759948b2a2a2b7aa261edbba7cf6e3de4896 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 30 Mar 2015 21:42:22 -0500 Subject: [PATCH 090/527] Update release notes --- release-notes.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/release-notes.md b/release-notes.md index 9e5720d2..87c7b600 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,14 @@ ## Development -[Commits](https://github.com/kpdecker/jsdiff/compare/v1.3.1...master) +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.3.2...master) + +## v1.3.2 - March 30th, 2015 +- [#53](https://github.com/kpdecker/jsdiff/pull/53) - Updated README.MD with Bower installation instructions ([@ofbriggs](https://api.github.com/users/ofbriggs)) +- [#49](https://github.com/kpdecker/jsdiff/issues/49) - Cannot read property 'oldlines' of undefined ([@nwtn](https://api.github.com/users/nwtn)) +- [#44](https://github.com/kpdecker/jsdiff/issues/44) - invalid-meta jsdiff is missing "main" entry in bower.json + +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.3.1...v1.3.2) ## v1.3.1 - March 13th, 2015 - [#52](https://github.com/kpdecker/jsdiff/pull/52) - Fix for #51 Wrong result of JsDiff.diffLines ([@felicienfrancois](https://api.github.com/users/felicienfrancois)) From 2b9ec732289f8c9c9a94c167cbd6a2c1591f5d12 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 30 Mar 2015 21:42:25 -0500 Subject: [PATCH 091/527] v1.3.2 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 372a7343..aa6862d1 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "jsdiff", - "version": "1.3.1", + "version": "1.3.2", "main": [ "diff.js" ], diff --git a/package.json b/package.json index 01169041..6b5f1c05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "1.3.1", + "version": "1.3.2", "description": "A javascript text diff implementation.", "keywords": [ "diff", From b75f9fd369b67825e2b27f36c4dae7db9216ef1e Mon Sep 17 00:00:00 2001 From: Raphael Geissert Date: Sun, 3 May 2015 17:21:56 +0200 Subject: [PATCH 092/527] Add createTwoFilesPatch, to be able to use two file names --- diff.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/diff.js b/diff.js index 22040858..881edf0e 100644 --- a/diff.js +++ b/diff.js @@ -370,13 +370,15 @@ ); }, - createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { + createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader) { var ret = []; - ret.push('Index: ' + fileName); + if (oldFileName == newFileName) { + ret.push('Index: ' + oldFileName); + } ret.push('==================================================================='); - ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); - ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); + ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); + ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); var diff = LineDiff.diff(oldStr, newStr); if (!diff[diff.length-1].value) { @@ -455,6 +457,10 @@ return ret.join('\n') + '\n'; }, + createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { + return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader); + }, + applyPatch: function(oldStr, uniDiff) { var diffstr = uniDiff.split('\n'); var diff = []; From beede57817dabd12c735c42222ce65716afe8a08 Mon Sep 17 00:00:00 2001 From: Raphael Geissert Date: Sun, 3 May 2015 17:30:55 +0200 Subject: [PATCH 093/527] Document the new createTwoFilesPatch --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ffaa0607..b867e19a 100644 --- a/README.md +++ b/README.md @@ -53,15 +53,20 @@ or Returns a list of change objects (See below). -* `JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader)` - creates a unified diff patch. +* `JsDiff.createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader)` - creates a unified diff patch. Parameters: - * `fileName` : String to be output in the filename sections of the patch + * `oldFileName` : String to be output in the filename section of the patch for the removals + * `newFileName` : String to be output in the filename section of the patch for the additions * `oldStr` : Original string value * `newStr` : New string value * `oldHeader` : Additional information to include in the old file header * `newHeader` : Additional information to include in thew new file header +* `JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader)` - creates a unified diff patch. + + Just like JsDiff.createTwoFilesPatch, but with oldFileName being equal to newFileName. + * `JsDiff.applyPatch(oldStr, diffStr)` - applies a unified diff patch. Return a string containing new version of provided data. From 86ba741b52fa9d5a4b17344284f96bd3fa44b228 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 4 May 2015 13:54:16 -0500 Subject: [PATCH 094/527] Update dev dependencies to latest --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 6b5f1c05..d748af96 100644 --- a/package.json +++ b/package.json @@ -32,10 +32,10 @@ }, "dependencies": {}, "devDependencies": { - "colors": "~0.6.2", + "colors": "^1.1.0", "istanbul": "^0.3.2", - "mocha": "~1.6", - "should": "~1.2" + "mocha": "^2.2.4", + "should": "^6.0.1" }, "optionalDependencies": {}, "files": [ From a04889dbf5df6ad970d17bce74a6927cbddce903 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 4 May 2015 18:05:01 -0500 Subject: [PATCH 095/527] Simple style cleanup --- diff.js | 131 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 58 deletions(-) diff --git a/diff.js b/diff.js index 881edf0e..a1016e7b 100644 --- a/diff.js +++ b/diff.js @@ -15,6 +15,7 @@ * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 */ (function(global, undefined) { + var objectPrototypeToString = Object.prototype.toString; var JsDiff = (function() { /*jshint maxparams: 5*/ @@ -88,9 +89,9 @@ return components; } - var Diff = function(ignoreWhitespace) { + function Diff(ignoreWhitespace) { this.ignoreWhitespace = ignoreWhitespace; - }; + } Diff.prototype = { diff: function(oldString, newString, callback) { var self = this; @@ -119,6 +120,7 @@ oldString = this.tokenize(oldString); var newLen = newString.length, oldLen = oldString.length; + var editLength = 1; var maxEditLength = newLen + oldLen; var bestPath = [{ newPos: -1, components: [] }]; @@ -131,17 +133,17 @@ // Main worker method. checks all permutations of a given edit length for acceptance. function execEditLength() { - for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) { + for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { var basePath; - var addPath = bestPath[diagonalPath-1], - removePath = bestPath[diagonalPath+1]; - oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + var addPath = bestPath[diagonalPath - 1], + removePath = bestPath[diagonalPath + 1], + oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; if (addPath) { // No one else is going to attempt to use this value, clear it - bestPath[diagonalPath-1] = undefined; + bestPath[diagonalPath - 1] = undefined; } - var canAdd = addPath && addPath.newPos+1 < newLen; + var canAdd = addPath && addPath.newPos + 1 < newLen; var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; if (!canAdd && !canRemove) { // If this path is a terminal then prune @@ -161,10 +163,10 @@ self.pushComponent(basePath.components, true, undefined); } - var oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); + oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); // If we have hit the end of both strings, then we are done - if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) { + if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) { return done(buildValues(basePath.components, newString, oldString, self.useLongestToken)); } else { // Otherwise track this path as a potential candidate and continue. @@ -178,7 +180,6 @@ // Performs the length of edit iteration. Is a bit fugly as this has to support the // sync and async mode which is never fun. Loops over execEditLength until a value // is produced. - var editLength = 1; if (callback) { (function exec() { setTimeout(function() { @@ -192,9 +193,9 @@ exec(); } }, 0); - })(); + }()); } else { - while(editLength <= maxEditLength) { + while (editLength <= maxEditLength) { var ret = execEditLength(); if (ret) { return ret; @@ -204,11 +205,11 @@ }, pushComponent: function(components, added, removed) { - var last = components[components.length-1]; + var last = components[components.length - 1]; if (last && last.added === added && last.removed === removed) { // We need to clone here as the component clone operation is just // as shallow array clone - components[components.length-1] = {count: last.count + 1, added: added, removed: removed }; + components[components.length - 1] = {count: last.count + 1, added: added, removed: removed }; } else { components.push({count: 1, added: added, removed: removed }); } @@ -220,7 +221,7 @@ oldPos = newPos - diagonalPath, commonCount = 0; - while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { newPos++; oldPos++; commonCount++; @@ -264,18 +265,18 @@ LineDiff.tokenize = TrimmedLineDiff.tokenize = function(value) { var retLines = [], lines = value.split(/^/m); - for(var i = 0; i < lines.length; i++) { + for (var i = 0; i < lines.length; i++) { var line = lines[i], lastLine = lines[i - 1], lastLineLastChar = lastLine ? lastLine[lastLine.length - 1] : ''; // Merge lines that may contain windows new lines if (line === '\n' && lastLineLastChar === '\r') { - retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0,-1) + '\r\n'; + retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0, -1) + '\r\n'; } else if (line) { if (this.ignoreTrim) { line = line.trim(); - //add a newline unless this is the last line. + // add a newline unless this is the last line. if (i < lines.length - 1) { line += '\n'; } @@ -289,7 +290,7 @@ var SentenceDiff = new Diff(); - SentenceDiff.tokenize = function (value) { + SentenceDiff.tokenize = function(value) { return removeEmpty(value.split(/(\S.+?[.!?])(?=\s+|$)/)); }; @@ -302,8 +303,6 @@ return LineDiff.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); }; - var objectPrototypeToString = Object.prototype.toString; - // This function handles the presence of circular references by bailing out when encountering an // object that is already on the "stack" of items being processed. function canonicalize(obj, stack, replacementStack) { @@ -375,27 +374,30 @@ if (oldFileName == newFileName) { ret.push('Index: ' + oldFileName); - } + } ret.push('==================================================================='); ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); var diff = LineDiff.diff(oldStr, newStr); - if (!diff[diff.length-1].value) { + if (!diff[diff.length - 1].value) { diff.pop(); // Remove trailing newline add } diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier + // Formats a given set of lines for printing as context lines in a patch function contextLines(lines) { return map(lines, function(entry) { return ' ' + entry; }); } + + // Outputs the no newline at end of file warning if needed function eofNL(curRange, i, current) { - var last = diff[diff.length-2], - isLast = i === diff.length-2, - isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); + var last = diff[diff.length - 2], + isLast = i === diff.length - 2, + isLastOfType = i === diff.length - 3 && (current.added !== last.added || current.removed !== last.removed); // Figure out if this is the last line for the given file and missing NL - if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { + if (!(/\n$/.test(current.value)) && (isLast || isLastOfType)) { curRange.push('\\ No newline at end of file'); } } @@ -409,7 +411,7 @@ if (current.added || current.removed) { if (!oldRangeStart) { - var prev = diff[i-1]; + var prev = diff[i - 1]; oldRangeStart = oldLine; newRangeStart = newLine; @@ -419,7 +421,9 @@ newRangeStart -= curRange.length; } } - curRange.push.apply(curRange, map(lines, function(entry) { return (current.added?'+':'-') + entry; })); + curRange.push.apply(curRange, map(lines, function(entry) { + return (current.added ? '+' : '-') + entry; + })); eofNL(curRange, i, current); if (current.added) { @@ -430,15 +434,15 @@ } else { if (oldRangeStart) { // Close out any changes that have been output (or join overlapping) - if (lines.length <= 8 && i < diff.length-2) { + if (lines.length <= 8 && i < diff.length - 2) { // Overlapping curRange.push.apply(curRange, contextLines(lines)); } else { // end the range and output var contextSize = Math.min(lines.length, 4); ret.push( - '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) - + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) + '@@ -' + oldRangeStart + ',' + (oldLine - oldRangeStart + contextSize) + + ' +' + newRangeStart + ',' + (newLine - newRangeStart + contextSize) + ' @@'); ret.push.apply(ret, curRange); ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); @@ -446,7 +450,9 @@ eofNL(ret, i, current); } - oldRangeStart = 0; newRangeStart = 0; curRange = []; + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; } } oldLine += lines.length; @@ -469,19 +475,19 @@ addEOFNL = false; // Skip to the first change chunk - while (i < diffstr.length && !/^@@/.test(diffstr[i])) { + while (i < diffstr.length && !(/^@@/.test(diffstr[i]))) { i++; } for (; i < diffstr.length; i++) { if (diffstr[i][0] === '@') { - var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); + var chnukHeader = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); diff.unshift({ - start:meh[3], - oldlength:meh[2], - oldlines:[], - newlength:meh[4], - newlines:[] + start: chnukHeader[3], + oldlength: chnukHeader[2], + oldlines: [], + newlength: chnukHeader[4], + newlines: [] }); } else if (diffstr[i][0] === '+') { diff[0].newlines.push(diffstr[i].substr(1)); @@ -491,27 +497,27 @@ diff[0].newlines.push(diffstr[i].substr(1)); diff[0].oldlines.push(diffstr[i].substr(1)); } else if (diffstr[i][0] === '\\') { - if (diffstr[i-1][0] === '+') { + if (diffstr[i - 1][0] === '+') { remEOFNL = true; - } else if (diffstr[i-1][0] === '-') { + } else if (diffstr[i - 1][0] === '-') { addEOFNL = true; } } } var str = oldStr.split('\n'); - for (var i = diff.length - 1; i >= 0; i--) { + for (i = diff.length - 1; i >= 0; i--) { var d = diff[i]; for (var j = 0; j < d.oldlength; j++) { - if (str[d.start-1+j] !== d.oldlines[j]) { + if (str[d.start - 1 + j] !== d.oldlines[j]) { return false; } } - Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); + Array.prototype.splice.apply(str, [d.start - 1, +d.oldlength].concat(d.newlines)); } if (remEOFNL) { - while (!str[str.length-1]) { + while (!str[str.length - 1]) { str.pop(); } } else if (addEOFNL) { @@ -520,9 +526,9 @@ return str.join('\n'); }, - convertChangesToXML: function(changes){ + convertChangesToXML: function(changes) { var ret = []; - for ( var i = 0; i < changes.length; i++) { + for (var i = 0; i < changes.length; i++) { var change = changes[i]; if (change.added) { ret.push(''); @@ -542,11 +548,21 @@ }, // See: http://code.google.com/p/google-diff-match-patch/wiki/API - convertChangesToDMP: function(changes){ - var ret = [], change; - for ( var i = 0; i < changes.length; i++) { + convertChangesToDMP: function(changes) { + var ret = [], + change, + operation; + for (var i = 0; i < changes.length; i++) { change = changes[i]; - ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); + if (change.added) { + operation = 1; + } else if (change.removed) { + operation = -1; + } else { + operation = 0; + } + + ret.push([operation, change.value]); } return ret; }, @@ -556,14 +572,13 @@ })(); /*istanbul ignore next */ + /*global module */ if (typeof module !== 'undefined' && module.exports) { module.exports = JsDiff; - } - else if (typeof define === 'function' && define.amd) { + } else if (typeof define === 'function' && define.amd) { /*global define */ define([], function() { return JsDiff; }); - } - else if (typeof global.JsDiff === 'undefined') { + } else if (typeof global.JsDiff === 'undefined') { global.JsDiff = JsDiff; } -})(this); +}(this)); From 477e0643a46342b2042e64990a464ff5105274ab Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 4 May 2015 18:10:21 -0500 Subject: [PATCH 096/527] Remove unnecessary inner IIFE --- diff.js | 958 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 478 insertions(+), 480 deletions(-) diff --git a/diff.js b/diff.js index a1016e7b..2864489f 100644 --- a/diff.js +++ b/diff.js @@ -17,559 +17,557 @@ (function(global, undefined) { var objectPrototypeToString = Object.prototype.toString; - var JsDiff = (function() { - /*jshint maxparams: 5*/ - /*istanbul ignore next*/ - function map(arr, mapper, that) { - if (Array.prototype.map) { - return Array.prototype.map.call(arr, mapper, that); - } + /*istanbul ignore next*/ + function map(arr, mapper, that) { + if (Array.prototype.map) { + return Array.prototype.map.call(arr, mapper, that); + } - var other = new Array(arr.length); + var other = new Array(arr.length); - for (var i = 0, n = arr.length; i < n; i++) { - other[i] = mapper.call(that, arr[i], i, arr); - } - return other; - } - function clonePath(path) { - return { newPos: path.newPos, components: path.components.slice(0) }; + for (var i = 0, n = arr.length; i < n; i++) { + other[i] = mapper.call(that, arr[i], i, arr); } - function removeEmpty(array) { - var ret = []; - for (var i = 0; i < array.length; i++) { - if (array[i]) { - ret.push(array[i]); - } + return other; + } + function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; + } + function removeEmpty(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); } - return ret; - } - function escapeHTML(s) { - var n = s; - n = n.replace(/&/g, '&'); - n = n.replace(//g, '>'); - n = n.replace(/"/g, '"'); - - return n; } + return ret; + } + function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; + } - function buildValues(components, newString, oldString, useLongestToken) { - var componentPos = 0, - componentLen = components.length, - newPos = 0, - oldPos = 0; - - for (; componentPos < componentLen; componentPos++) { - var component = components[componentPos]; - if (!component.removed) { - if (!component.added && useLongestToken) { - var value = newString.slice(newPos, newPos + component.count); - value = map(value, function(value, i) { - var oldValue = oldString[oldPos + i]; - return oldValue.length > value.length ? oldValue : value; - }); - - component.value = value.join(''); - } else { - component.value = newString.slice(newPos, newPos + component.count).join(''); - } - newPos += component.count; + // This function handles the presence of circular references by bailing out when encountering an + // object that is already on the "stack" of items being processed. + function canonicalize(obj, stack, replacementStack) { + stack = stack || []; + replacementStack = replacementStack || []; - // Common case - if (!component.added) { - oldPos += component.count; - } - } else { - component.value = oldString.slice(oldPos, oldPos + component.count).join(''); - oldPos += component.count; - } - } + var i; - return components; + for (i = 0; i < stack.length; i += 1) { + if (stack[i] === obj) { + return replacementStack[i]; + } } - function Diff(ignoreWhitespace) { - this.ignoreWhitespace = ignoreWhitespace; + var canonicalizedObj; + + if ('[object Array]' === objectPrototypeToString.call(obj)) { + stack.push(obj); + canonicalizedObj = new Array(obj.length); + replacementStack.push(canonicalizedObj); + for (i = 0; i < obj.length; i += 1) { + canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack); + } + stack.pop(); + replacementStack.pop(); + } else if (typeof obj === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + replacementStack.push(canonicalizedObj); + var sortedKeys = [], + key; + for (key in obj) { + sortedKeys.push(key); + } + sortedKeys.sort(); + for (i = 0; i < sortedKeys.length; i += 1) { + key = sortedKeys[i]; + canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack); + } + stack.pop(); + replacementStack.pop(); + } else { + canonicalizedObj = obj; } - Diff.prototype = { - diff: function(oldString, newString, callback) { - var self = this; - - function done(value) { - if (callback) { - setTimeout(function() { callback(undefined, value); }, 0); - return true; - } else { - return value; - } - } + return canonicalizedObj; + } - // Handle the identity case (this is due to unrolling editLength == 0 - if (newString === oldString) { - return done([{ value: newString }]); - } - if (!newString) { - return done([{ value: oldString, removed: true }]); - } - if (!oldString) { - return done([{ value: newString, added: true }]); - } + function buildValues(components, newString, oldString, useLongestToken) { + var componentPos = 0, + componentLen = components.length, + newPos = 0, + oldPos = 0; + + for (; componentPos < componentLen; componentPos++) { + var component = components[componentPos]; + if (!component.removed) { + if (!component.added && useLongestToken) { + var value = newString.slice(newPos, newPos + component.count); + value = map(value, function(value, i) { + var oldValue = oldString[oldPos + i]; + return oldValue.length > value.length ? oldValue : value; + }); + + component.value = value.join(''); + } else { + component.value = newString.slice(newPos, newPos + component.count).join(''); + } + newPos += component.count; - newString = this.tokenize(newString); - oldString = this.tokenize(oldString); + // Common case + if (!component.added) { + oldPos += component.count; + } + } else { + component.value = oldString.slice(oldPos, oldPos + component.count).join(''); + oldPos += component.count; + } + } - var newLen = newString.length, oldLen = oldString.length; - var editLength = 1; - var maxEditLength = newLen + oldLen; - var bestPath = [{ newPos: -1, components: [] }]; + return components; + } - // Seed editLength = 0, i.e. the content starts with the same values - var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); - if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) { - // Identity per the equality and tokenizer - return done([{value: newString.join('')}]); - } + function Diff(ignoreWhitespace) { + this.ignoreWhitespace = ignoreWhitespace; + } + Diff.prototype = { + diff: function(oldString, newString, callback) { + var self = this; + + function done(value) { + if (callback) { + setTimeout(function() { callback(undefined, value); }, 0); + return true; + } else { + return value; + } + } - // Main worker method. checks all permutations of a given edit length for acceptance. - function execEditLength() { - for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { - var basePath; - var addPath = bestPath[diagonalPath - 1], - removePath = bestPath[diagonalPath + 1], - oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; - if (addPath) { - // No one else is going to attempt to use this value, clear it - bestPath[diagonalPath - 1] = undefined; - } + // Handle the identity case (this is due to unrolling editLength == 0 + if (newString === oldString) { + return done([{ value: newString }]); + } + if (!newString) { + return done([{ value: oldString, removed: true }]); + } + if (!oldString) { + return done([{ value: newString, added: true }]); + } - var canAdd = addPath && addPath.newPos + 1 < newLen; - var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; - if (!canAdd && !canRemove) { - // If this path is a terminal then prune - bestPath[diagonalPath] = undefined; - continue; - } + newString = this.tokenize(newString); + oldString = this.tokenize(oldString); - // Select the diagonal that we want to branch from. We select the prior - // path whose position in the new string is the farthest from the origin - // and does not pass the bounds of the diff graph - if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { - basePath = clonePath(removePath); - self.pushComponent(basePath.components, undefined, true); - } else { - basePath = addPath; // No need to clone, we've pulled it from the list - basePath.newPos++; - self.pushComponent(basePath.components, true, undefined); - } + var newLen = newString.length, oldLen = oldString.length; + var editLength = 1; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; - oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); + // Seed editLength = 0, i.e. the content starts with the same values + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + // Identity per the equality and tokenizer + return done([{value: newString.join('')}]); + } - // If we have hit the end of both strings, then we are done - if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) { - return done(buildValues(basePath.components, newString, oldString, self.useLongestToken)); - } else { - // Otherwise track this path as a potential candidate and continue. - bestPath[diagonalPath] = basePath; - } - } + // Main worker method. checks all permutations of a given edit length for acceptance. + function execEditLength() { + for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { + var basePath; + var addPath = bestPath[diagonalPath - 1], + removePath = bestPath[diagonalPath + 1], + oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath - 1] = undefined; + } - editLength++; + var canAdd = addPath && addPath.newPos + 1 < newLen; + var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; + if (!canAdd && !canRemove) { + // If this path is a terminal then prune + bestPath[diagonalPath] = undefined; + continue; } - // Performs the length of edit iteration. Is a bit fugly as this has to support the - // sync and async mode which is never fun. Loops over execEditLength until a value - // is produced. - if (callback) { - (function exec() { - setTimeout(function() { - // This should not happen, but we want to be safe. - /*istanbul ignore next */ - if (editLength > maxEditLength) { - return callback(); - } - - if (!execEditLength()) { - exec(); - } - }, 0); - }()); + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { + basePath = clonePath(removePath); + self.pushComponent(basePath.components, undefined, true); } else { - while (editLength <= maxEditLength) { - var ret = execEditLength(); - if (ret) { - return ret; - } - } + basePath = addPath; // No need to clone, we've pulled it from the list + basePath.newPos++; + self.pushComponent(basePath.components, true, undefined); } - }, - - pushComponent: function(components, added, removed) { - var last = components[components.length - 1]; - if (last && last.added === added && last.removed === removed) { - // We need to clone here as the component clone operation is just - // as shallow array clone - components[components.length - 1] = {count: last.count + 1, added: added, removed: removed }; + + oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); + + // If we have hit the end of both strings, then we are done + if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + return done(buildValues(basePath.components, newString, oldString, self.useLongestToken)); } else { - components.push({count: 1, added: added, removed: removed }); - } - }, - extractCommon: function(basePath, newString, oldString, diagonalPath) { - var newLen = newString.length, - oldLen = oldString.length, - newPos = basePath.newPos, - oldPos = newPos - diagonalPath, - - commonCount = 0; - while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { - newPos++; - oldPos++; - commonCount++; + // Otherwise track this path as a potential candidate and continue. + bestPath[diagonalPath] = basePath; } + } - if (commonCount) { - basePath.components.push({count: commonCount}); - } + editLength++; + } - basePath.newPos = newPos; - return oldPos; - }, + // Performs the length of edit iteration. Is a bit fugly as this has to support the + // sync and async mode which is never fun. Loops over execEditLength until a value + // is produced. + if (callback) { + (function exec() { + setTimeout(function() { + // This should not happen, but we want to be safe. + /*istanbul ignore next */ + if (editLength > maxEditLength) { + return callback(); + } - equals: function(left, right) { - var reWhitespace = /\S/; - return left === right || (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)); - }, - tokenize: function(value) { - return value.split(''); - } - }; - - var CharDiff = new Diff(); - - var WordDiff = new Diff(true); - var WordWithSpaceDiff = new Diff(); - WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { - return removeEmpty(value.split(/(\s+|\b)/)); - }; - - var CssDiff = new Diff(true); - CssDiff.tokenize = function(value) { - return removeEmpty(value.split(/([{}:;,]|\s+)/)); - }; - - var LineDiff = new Diff(); - - var TrimmedLineDiff = new Diff(); - TrimmedLineDiff.ignoreTrim = true; - - LineDiff.tokenize = TrimmedLineDiff.tokenize = function(value) { - var retLines = [], - lines = value.split(/^/m); - for (var i = 0; i < lines.length; i++) { - var line = lines[i], - lastLine = lines[i - 1], - lastLineLastChar = lastLine ? lastLine[lastLine.length - 1] : ''; - - // Merge lines that may contain windows new lines - if (line === '\n' && lastLineLastChar === '\r') { - retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0, -1) + '\r\n'; - } else if (line) { - if (this.ignoreTrim) { - line = line.trim(); - // add a newline unless this is the last line. - if (i < lines.length - 1) { - line += '\n'; + if (!execEditLength()) { + exec(); } + }, 0); + }()); + } else { + while (editLength <= maxEditLength) { + var ret = execEditLength(); + if (ret) { + return ret; } - retLines.push(line); } } + }, + + pushComponent: function(components, added, removed) { + var last = components[components.length - 1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length - 1] = {count: last.count + 1, added: added, removed: removed }; + } else { + components.push({count: 1, added: added, removed: removed }); + } + }, + extractCommon: function(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath, + + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + newPos++; + oldPos++; + commonCount++; + } - return retLines; - }; - - - var SentenceDiff = new Diff(); - SentenceDiff.tokenize = function(value) { - return removeEmpty(value.split(/(\S.+?[.!?])(?=\s+|$)/)); - }; - - var JsonDiff = new Diff(); - // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a - // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: - JsonDiff.useLongestToken = true; - JsonDiff.tokenize = LineDiff.tokenize; - JsonDiff.equals = function(left, right) { - return LineDiff.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); - }; - - // This function handles the presence of circular references by bailing out when encountering an - // object that is already on the "stack" of items being processed. - function canonicalize(obj, stack, replacementStack) { - stack = stack || []; - replacementStack = replacementStack || []; - - var i; - - for (var i = 0 ; i < stack.length ; i += 1) { - if (stack[i] === obj) { - return replacementStack[i]; - } + if (commonCount) { + basePath.components.push({count: commonCount}); } - var canonicalizedObj; + basePath.newPos = newPos; + return oldPos; + }, - if ('[object Array]' === objectPrototypeToString.call(obj)) { - stack.push(obj); - canonicalizedObj = new Array(obj.length); - replacementStack.push(canonicalizedObj); - for (i = 0 ; i < obj.length ; i += 1) { - canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack); - } - stack.pop(); - replacementStack.pop(); - } else if (typeof obj === 'object' && obj !== null) { - stack.push(obj); - canonicalizedObj = {}; - replacementStack.push(canonicalizedObj); - var sortedKeys = []; - for (var key in obj) { - sortedKeys.push(key); - } - sortedKeys.sort(); - for (i = 0 ; i < sortedKeys.length ; i += 1) { - var key = sortedKeys[i]; - canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack); + equals: function(left, right) { + var reWhitespace = /\S/; + return left === right || (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)); + }, + tokenize: function(value) { + return value.split(''); + } + }; + + var CharDiff = new Diff(); + + var WordDiff = new Diff(true); + var WordWithSpaceDiff = new Diff(); + WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { + return removeEmpty(value.split(/(\s+|\b)/)); + }; + + var CssDiff = new Diff(true); + CssDiff.tokenize = function(value) { + return removeEmpty(value.split(/([{}:;,]|\s+)/)); + }; + + var LineDiff = new Diff(); + + var TrimmedLineDiff = new Diff(); + TrimmedLineDiff.ignoreTrim = true; + + LineDiff.tokenize = TrimmedLineDiff.tokenize = function(value) { + var retLines = [], + lines = value.split(/^/m); + for (var i = 0; i < lines.length; i++) { + var line = lines[i], + lastLine = lines[i - 1], + lastLineLastChar = lastLine ? lastLine[lastLine.length - 1] : ''; + + // Merge lines that may contain windows new lines + if (line === '\n' && lastLineLastChar === '\r') { + retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0, -1) + '\r\n'; + } else if (line) { + if (this.ignoreTrim) { + line = line.trim(); + // add a newline unless this is the last line. + if (i < lines.length - 1) { + line += '\n'; + } } - stack.pop(); - replacementStack.pop(); - } else { - canonicalizedObj = obj; + retLines.push(line); } - return canonicalizedObj; } - return { - Diff: Diff, - - diffChars: function(oldStr, newStr, callback) { return CharDiff.diff(oldStr, newStr, callback); }, - diffWords: function(oldStr, newStr, callback) { return WordDiff.diff(oldStr, newStr, callback); }, - diffWordsWithSpace: function(oldStr, newStr, callback) { return WordWithSpaceDiff.diff(oldStr, newStr, callback); }, - diffLines: function(oldStr, newStr, callback) { return LineDiff.diff(oldStr, newStr, callback); }, - diffTrimmedLines: function(oldStr, newStr, callback) { return TrimmedLineDiff.diff(oldStr, newStr, callback); }, - - diffSentences: function(oldStr, newStr, callback) { return SentenceDiff.diff(oldStr, newStr, callback); }, - - diffCss: function(oldStr, newStr, callback) { return CssDiff.diff(oldStr, newStr, callback); }, - diffJson: function(oldObj, newObj, callback) { - return JsonDiff.diff( - typeof oldObj === 'string' ? oldObj : JSON.stringify(canonicalize(oldObj), undefined, ' '), - typeof newObj === 'string' ? newObj : JSON.stringify(canonicalize(newObj), undefined, ' '), - callback - ); - }, - - createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader) { - var ret = []; + return retLines; + }; + + + var SentenceDiff = new Diff(); + SentenceDiff.tokenize = function(value) { + return removeEmpty(value.split(/(\S.+?[.!?])(?=\s+|$)/)); + }; + + var JsonDiff = new Diff(); + // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a + // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: + JsonDiff.useLongestToken = true; + JsonDiff.tokenize = LineDiff.tokenize; + JsonDiff.equals = function(left, right) { + return LineDiff.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); + }; + + var JsDiff = { + Diff: Diff, + + diffChars: function(oldStr, newStr, callback) { return CharDiff.diff(oldStr, newStr, callback); }, + diffWords: function(oldStr, newStr, callback) { return WordDiff.diff(oldStr, newStr, callback); }, + diffWordsWithSpace: function(oldStr, newStr, callback) { return WordWithSpaceDiff.diff(oldStr, newStr, callback); }, + diffLines: function(oldStr, newStr, callback) { return LineDiff.diff(oldStr, newStr, callback); }, + diffTrimmedLines: function(oldStr, newStr, callback) { return TrimmedLineDiff.diff(oldStr, newStr, callback); }, + + diffSentences: function(oldStr, newStr, callback) { return SentenceDiff.diff(oldStr, newStr, callback); }, + + diffCss: function(oldStr, newStr, callback) { return CssDiff.diff(oldStr, newStr, callback); }, + diffJson: function(oldObj, newObj, callback) { + return JsonDiff.diff( + typeof oldObj === 'string' ? oldObj : JSON.stringify(canonicalize(oldObj), undefined, ' '), + typeof newObj === 'string' ? newObj : JSON.stringify(canonicalize(newObj), undefined, ' '), + callback + ); + }, + + createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader) { + var ret = []; - if (oldFileName == newFileName) { - ret.push('Index: ' + oldFileName); - } - ret.push('==================================================================='); - ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); - ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); + if (oldFileName == newFileName) { + ret.push('Index: ' + oldFileName); + } + ret.push('==================================================================='); + ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); + ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); - var diff = LineDiff.diff(oldStr, newStr); - if (!diff[diff.length - 1].value) { - diff.pop(); // Remove trailing newline add - } - diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier + var diff = LineDiff.diff(oldStr, newStr); + if (!diff[diff.length - 1].value) { + diff.pop(); // Remove trailing newline add + } + diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier - // Formats a given set of lines for printing as context lines in a patch - function contextLines(lines) { - return map(lines, function(entry) { return ' ' + entry; }); - } + // Formats a given set of lines for printing as context lines in a patch + function contextLines(lines) { + return map(lines, function(entry) { return ' ' + entry; }); + } - // Outputs the no newline at end of file warning if needed - function eofNL(curRange, i, current) { - var last = diff[diff.length - 2], - isLast = i === diff.length - 2, - isLastOfType = i === diff.length - 3 && (current.added !== last.added || current.removed !== last.removed); + // Outputs the no newline at end of file warning if needed + function eofNL(curRange, i, current) { + var last = diff[diff.length - 2], + isLast = i === diff.length - 2, + isLastOfType = i === diff.length - 3 && (current.added !== last.added || current.removed !== last.removed); - // Figure out if this is the last line for the given file and missing NL - if (!(/\n$/.test(current.value)) && (isLast || isLastOfType)) { - curRange.push('\\ No newline at end of file'); - } + // Figure out if this is the last line for the given file and missing NL + if (!(/\n$/.test(current.value)) && (isLast || isLastOfType)) { + curRange.push('\\ No newline at end of file'); } + } - var oldRangeStart = 0, newRangeStart = 0, curRange = [], - oldLine = 1, newLine = 1; - for (var i = 0; i < diff.length; i++) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - if (!oldRangeStart) { - var prev = diff[i - 1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = contextLines(prev.lines.slice(-4)); - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; - } + var oldRangeStart = 0, newRangeStart = 0, curRange = [], + oldLine = 1, newLine = 1; + for (var i = 0; i < diff.length; i++) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = contextLines(prev.lines.slice(-4)); + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; } - curRange.push.apply(curRange, map(lines, function(entry) { - return (current.added ? '+' : '-') + entry; - })); - eofNL(curRange, i, current); + } + curRange.push.apply(curRange, map(lines, function(entry) { + return (current.added ? '+' : '-') + entry; + })); + eofNL(curRange, i, current); - if (current.added) { - newLine += lines.length; - } else { - oldLine += lines.length; - } + if (current.added) { + newLine += lines.length; } else { - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= 8 && i < diff.length - 2) { - // Overlapping - curRange.push.apply(curRange, contextLines(lines)); - } else { - // end the range and output - var contextSize = Math.min(lines.length, 4); - ret.push( - '@@ -' + oldRangeStart + ',' + (oldLine - oldRangeStart + contextSize) - + ' +' + newRangeStart + ',' + (newLine - newRangeStart + contextSize) - + ' @@'); - ret.push.apply(ret, curRange); - ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); - if (lines.length <= 4) { - eofNL(ret, i, current); - } - - oldRangeStart = 0; - newRangeStart = 0; - curRange = []; + oldLine += lines.length; + } + } else { + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= 8 && i < diff.length - 2) { + // Overlapping + curRange.push.apply(curRange, contextLines(lines)); + } else { + // end the range and output + var contextSize = Math.min(lines.length, 4); + ret.push( + '@@ -' + oldRangeStart + ',' + (oldLine - oldRangeStart + contextSize) + + ' +' + newRangeStart + ',' + (newLine - newRangeStart + contextSize) + + ' @@'); + ret.push.apply(ret, curRange); + ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); + if (lines.length <= 4) { + eofNL(ret, i, current); } + + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; } - oldLine += lines.length; - newLine += lines.length; } + oldLine += lines.length; + newLine += lines.length; } + } - return ret.join('\n') + '\n'; - }, + return ret.join('\n') + '\n'; + }, - createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { - return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader); - }, + createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { + return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader); + }, - applyPatch: function(oldStr, uniDiff) { - var diffstr = uniDiff.split('\n'); - var diff = []; - var i = 0, - remEOFNL = false, - addEOFNL = false; + applyPatch: function(oldStr, uniDiff) { + var diffstr = uniDiff.split('\n'); + var diff = []; + var i = 0, + remEOFNL = false, + addEOFNL = false; - // Skip to the first change chunk - while (i < diffstr.length && !(/^@@/.test(diffstr[i]))) { - i++; - } + // Skip to the first change chunk + while (i < diffstr.length && !(/^@@/.test(diffstr[i]))) { + i++; + } - for (; i < diffstr.length; i++) { - if (diffstr[i][0] === '@') { - var chnukHeader = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); - diff.unshift({ - start: chnukHeader[3], - oldlength: chnukHeader[2], - oldlines: [], - newlength: chnukHeader[4], - newlines: [] - }); - } else if (diffstr[i][0] === '+') { - diff[0].newlines.push(diffstr[i].substr(1)); - } else if (diffstr[i][0] === '-') { - diff[0].oldlines.push(diffstr[i].substr(1)); - } else if (diffstr[i][0] === ' ') { - diff[0].newlines.push(diffstr[i].substr(1)); - diff[0].oldlines.push(diffstr[i].substr(1)); - } else if (diffstr[i][0] === '\\') { - if (diffstr[i - 1][0] === '+') { - remEOFNL = true; - } else if (diffstr[i - 1][0] === '-') { - addEOFNL = true; - } + for (; i < diffstr.length; i++) { + if (diffstr[i][0] === '@') { + var chnukHeader = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); + diff.unshift({ + start: chnukHeader[3], + oldlength: chnukHeader[2], + oldlines: [], + newlength: chnukHeader[4], + newlines: [] + }); + } else if (diffstr[i][0] === '+') { + diff[0].newlines.push(diffstr[i].substr(1)); + } else if (diffstr[i][0] === '-') { + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if (diffstr[i][0] === ' ') { + diff[0].newlines.push(diffstr[i].substr(1)); + diff[0].oldlines.push(diffstr[i].substr(1)); + } else if (diffstr[i][0] === '\\') { + if (diffstr[i - 1][0] === '+') { + remEOFNL = true; + } else if (diffstr[i - 1][0] === '-') { + addEOFNL = true; } } + } - var str = oldStr.split('\n'); - for (i = diff.length - 1; i >= 0; i--) { - var d = diff[i]; - for (var j = 0; j < d.oldlength; j++) { - if (str[d.start - 1 + j] !== d.oldlines[j]) { - return false; - } + var str = oldStr.split('\n'); + for (i = diff.length - 1; i >= 0; i--) { + var d = diff[i]; + for (var j = 0; j < d.oldlength; j++) { + if (str[d.start - 1 + j] !== d.oldlines[j]) { + return false; } - Array.prototype.splice.apply(str, [d.start - 1, +d.oldlength].concat(d.newlines)); } + Array.prototype.splice.apply(str, [d.start - 1, +d.oldlength].concat(d.newlines)); + } - if (remEOFNL) { - while (!str[str.length - 1]) { - str.pop(); - } - } else if (addEOFNL) { - str.push(''); + if (remEOFNL) { + while (!str[str.length - 1]) { + str.pop(); } - return str.join('\n'); - }, - - convertChangesToXML: function(changes) { - var ret = []; - for (var i = 0; i < changes.length; i++) { - var change = changes[i]; - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } - - ret.push(escapeHTML(change.value)); + } else if (addEOFNL) { + str.push(''); + } + return str.join('\n'); + }, - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } + convertChangesToXML: function(changes) { + var ret = []; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); } - return ret.join(''); - }, - - // See: http://code.google.com/p/google-diff-match-patch/wiki/API - convertChangesToDMP: function(changes) { - var ret = [], - change, - operation; - for (var i = 0; i < changes.length; i++) { - change = changes[i]; - if (change.added) { - operation = 1; - } else if (change.removed) { - operation = -1; - } else { - operation = 0; - } - ret.push([operation, change.value]); + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + return ret.join(''); + }, + + // See: http://code.google.com/p/google-diff-match-patch/wiki/API + convertChangesToDMP: function(changes) { + var ret = [], + change, + operation; + for (var i = 0; i < changes.length; i++) { + change = changes[i]; + if (change.added) { + operation = 1; + } else if (change.removed) { + operation = -1; + } else { + operation = 0; } - return ret; - }, - canonicalize: canonicalize - }; - })(); + ret.push([operation, change.value]); + } + return ret; + }, + + canonicalize: canonicalize + }; /*istanbul ignore next */ /*global module */ From 7c0a20e5de291034282b5ee4f29bb8480c4dba40 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 4 May 2015 18:15:00 -0500 Subject: [PATCH 097/527] Style and error handler updates --- test/diffTest.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/test/diffTest.js b/test/diffTest.js index ba02eacb..28b74fb9 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -1,4 +1,5 @@ -var diff = require('../diff'); +var diff = require('../diff'), + should = require('should'); describe('#diffWords', function() { it('should diff whitespace', function() { @@ -61,6 +62,7 @@ describe('#diffWords', function() { describe('#diffWords - async', function() { it('should diff whitespace', function(done) { diff.diffWords('New Value', 'New ValueMoreData', function(err, diffResult) { + should(err).not.exist; diff.convertChangesToXML(diffResult).should.equal('New ValueMoreDataValue'); done(); }); @@ -68,6 +70,7 @@ describe('#diffWords - async', function() { it('should diff multiple whitespace values', function(done) { diff.diffWords('New Value ', 'New ValueMoreData ', function(err, diffResult) { + should(err).not.exist; diff.convertChangesToXML(diffResult).should.equal('New ValueMoreDataValue '); done(); }); @@ -76,6 +79,7 @@ describe('#diffWords - async', function() { // Diff on word boundary it('should diff on word boundaries', function(done) { diff.diffWords('New :Value:Test', 'New ValueMoreData ', function(err, diffResult) { + should(err).not.exist; diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData :Value:Test'); done(); }); @@ -84,18 +88,21 @@ describe('#diffWords - async', function() { // Diff without changes it('should handle identity', function(done) { diff.diffWords('New Value', 'New Value', function(err, diffResult) { + should(err).not.exist; diff.convertChangesToXML(diffResult).should.equal('New Value'); done(); }); }); it('should handle empty', function(done) { diff.diffWords('', '', function(err, diffResult) { + should(err).not.exist; diff.convertChangesToXML(diffResult).should.equal(''); done(); }); }); it('should diff has identical content', function(done) { diff.diffWords('New Value', 'New Value', function(err, diffResult) { + should(err).not.exist; diff.convertChangesToXML(diffResult).should.equal('New Value'); done(); }); @@ -198,7 +205,7 @@ describe('#diffLines', function() { 'line\r\nnew value\r\nline'); diff.convertChangesToXML(diffResult).should.equal('line\r\nnew value\r\nold value \r\nline'); }); - + it('should handle empty lines', function() { var diffResult = diff.diffLines( 'line\n\nold value \n\nline', @@ -298,12 +305,12 @@ describe('#diffJson', function() { it('should throw an error if one of the objects being diffed has a circular reference', function() { var circular = {foo: 123}; circular.bar = circular; - (function () { + (function() { diff.diffJson( circular, {foo: 123, bar: {}} ); - }).should.throw('Converting circular structure to JSON'); + }.should['throw']('Converting circular structure to JSON')); }); }); @@ -312,6 +319,6 @@ describe('convertToDMP', function() { var diffResult = diff.diffWords('New Value ', 'New ValueMoreData '); diff.convertChangesToDMP(diffResult).should.eql( - [[0,'New '],[1,'ValueMoreData'],[-1,'Value'],[0,' ']]); + [[0, 'New '], [1, 'ValueMoreData'], [-1, 'Value'], [0, ' ']]); }); }); From 5e27060d2198198c4fad10e37cd7b70520a8813a Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 6 May 2015 02:28:05 -0500 Subject: [PATCH 098/527] Remove dead code This code had no input on the behavior of this edge case. --- diff.js | 3 --- test/createPatch.js | 10 ++++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/diff.js b/diff.js index 2864489f..908a804b 100644 --- a/diff.js +++ b/diff.js @@ -379,9 +379,6 @@ ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); var diff = LineDiff.diff(oldStr, newStr); - if (!diff[diff.length - 1].value) { - diff.pop(); // Remove trailing newline add - } diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier // Formats a given set of lines for printing as context lines in a patch diff --git a/test/createPatch.js b/test/createPatch.js index 067f6e18..16569307 100644 --- a/test/createPatch.js +++ b/test/createPatch.js @@ -485,4 +485,14 @@ describe('#createPatch', function() { var diffResult = diff.createPatch('testFileName', oldFile, oldFile, 'Old Header', 'New Header'); diffResult.should.equal(expectedResult); }); + + it('should safely handle empty inputs', function() { + var expectedResult = + 'Index: testFileName\n' + + '===================================================================\n' + + '--- testFileName\n' + + '+++ testFileName\n'; + var diffResult = diff.createPatch('testFileName', '', ''); + diffResult.should.equal(expectedResult); + }); }); From 9f6279d5a0a11a233a5f71491df821c8193b72ba Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 6 May 2015 02:28:12 -0500 Subject: [PATCH 099/527] Add additional test coverage --- diff.js | 50 ++++++++++++++++++++++++--------------------- test/applyPatch.js | 28 +++++++++++++++++++------ test/createPatch.js | 19 +++++++++++++++++ test/diffTest.js | 7 +++++++ 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/diff.js b/diff.js index 908a804b..79d99679 100644 --- a/diff.js +++ b/diff.js @@ -318,7 +318,7 @@ // Merge lines that may contain windows new lines if (line === '\n' && lastLineLastChar === '\r') { retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0, -1) + '\r\n'; - } else if (line) { + } else { if (this.ignoreTrim) { line = line.trim(); // add a newline unless this is the last line. @@ -464,34 +464,35 @@ }, applyPatch: function(oldStr, uniDiff) { - var diffstr = uniDiff.split('\n'); - var diff = []; - var i = 0, + var diffstr = uniDiff.split('\n'), + hunks = [], + i = 0, remEOFNL = false, addEOFNL = false; - // Skip to the first change chunk + // Skip to the first change hunk while (i < diffstr.length && !(/^@@/.test(diffstr[i]))) { i++; } + // Parse the unified diff for (; i < diffstr.length; i++) { if (diffstr[i][0] === '@') { var chnukHeader = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); - diff.unshift({ + hunks.unshift({ start: chnukHeader[3], - oldlength: chnukHeader[2], - oldlines: [], + oldlength: +chnukHeader[2], + removed: [], newlength: chnukHeader[4], - newlines: [] + added: [] }); } else if (diffstr[i][0] === '+') { - diff[0].newlines.push(diffstr[i].substr(1)); + hunks[0].added.push(diffstr[i].substr(1)); } else if (diffstr[i][0] === '-') { - diff[0].oldlines.push(diffstr[i].substr(1)); + hunks[0].removed.push(diffstr[i].substr(1)); } else if (diffstr[i][0] === ' ') { - diff[0].newlines.push(diffstr[i].substr(1)); - diff[0].oldlines.push(diffstr[i].substr(1)); + hunks[0].added.push(diffstr[i].substr(1)); + hunks[0].removed.push(diffstr[i].substr(1)); } else if (diffstr[i][0] === '\\') { if (diffstr[i - 1][0] === '+') { remEOFNL = true; @@ -501,25 +502,28 @@ } } - var str = oldStr.split('\n'); - for (i = diff.length - 1; i >= 0; i--) { - var d = diff[i]; - for (var j = 0; j < d.oldlength; j++) { - if (str[d.start - 1 + j] !== d.oldlines[j]) { + // Apply the diff to the input + var lines = oldStr.split('\n'); + for (i = hunks.length - 1; i >= 0; i--) { + var hunk = hunks[i]; + // Sanity check the input string. Bail if we don't match. + for (var j = 0; j < hunk.oldlength; j++) { + if (lines[hunk.start - 1 + j] !== hunk.removed[j]) { return false; } } - Array.prototype.splice.apply(str, [d.start - 1, +d.oldlength].concat(d.newlines)); + Array.prototype.splice.apply(lines, [hunk.start - 1, hunk.oldlength].concat(hunk.added)); } + // Handle EOFNL insertion/removal if (remEOFNL) { - while (!str[str.length - 1]) { - str.pop(); + while (!lines[lines.length - 1]) { + lines.pop(); } } else if (addEOFNL) { - str.push(''); + lines.push(''); } - return str.join('\n'); + return lines.join('\n'); }, convertChangesToXML: function(changes) { diff --git a/test/applyPatch.js b/test/applyPatch.js index f7cae2a0..304ded65 100644 --- a/test/applyPatch.js +++ b/test/applyPatch.js @@ -3,9 +3,9 @@ var diff = require('../diff'); describe('#applyPatch', function() { it('should apply patches that change the last line', function() { diff.applyPatch( - 'line2\n'+ - 'line3\n'+ - 'line5\n', + 'line2\n' + + 'line3\n' + + 'line5\n', 'Index: test\n' + '===================================================================\n' @@ -272,9 +272,9 @@ describe('#applyPatch', function() { it('should apply patches that lack an index header', function() { diff.applyPatch( - 'line2\n'+ - 'line3\n'+ - 'line5\n', + 'line2\n' + + 'line3\n' + + 'line5\n', '--- test\theader1\n' + '+++ test\theader2\n' @@ -289,4 +289,20 @@ describe('#applyPatch', function() { + 'line4\n' + 'line5\n'); }); + + it('should fail on mismatch', function() { + diff.applyPatch( + 'line2\n' + + 'line2\n' + + 'line5\n', + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,3 +1,4 @@\n' + + ' line2\n' + + ' line3\n' + + '+line4\n' + + ' line5\n') + .should.equal(false); + }); }); diff --git a/test/createPatch.js b/test/createPatch.js index 16569307..439e9de0 100644 --- a/test/createPatch.js +++ b/test/createPatch.js @@ -486,6 +486,16 @@ describe('#createPatch', function() { diffResult.should.equal(expectedResult); }); + it('should omit headers if undefined', function() { + var expectedResult = + 'Index: testFileName\n' + + '===================================================================\n' + + '--- testFileName\n' + + '+++ testFileName\n'; + var diffResult = diff.createPatch('testFileName', oldFile, oldFile); + diffResult.should.equal(expectedResult); + }); + it('should safely handle empty inputs', function() { var expectedResult = 'Index: testFileName\n' @@ -495,4 +505,13 @@ describe('#createPatch', function() { var diffResult = diff.createPatch('testFileName', '', ''); diffResult.should.equal(expectedResult); }); + + it('should omit index with multiple file names', function() { + var expectedResult = + '===================================================================\n' + + '--- foo\n' + + '+++ bar\n'; + var diffResult = diff.createTwoFilesPatch('foo', 'bar', '', ''); + diffResult.should.equal(expectedResult); + }); }); diff --git a/test/diffTest.js b/test/diffTest.js index 28b74fb9..42903348 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -212,6 +212,13 @@ describe('#diffLines', function() { 'line\n\nnew value\n\nline'); diff.convertChangesToXML(diffResult).should.equal('line\n\nnew value\nold value \n\nline'); }); + + it('should handle empty input', function() { + var diffResult = diff.diffLines( + 'line\n\nold value \n\nline', + ''); + diff.convertChangesToXML(diffResult).should.equal('line\n\nold value \n\nline'); + }); }); // Trimmed Line Diff From 7c9541f2976f845597109e57a3f84ca19fdfc4d1 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 6 May 2015 02:47:50 -0500 Subject: [PATCH 100/527] Provide better comments in patch generator --- diff.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/diff.js b/diff.js index 79d99679..46cd77a2 100644 --- a/diff.js +++ b/diff.js @@ -406,6 +406,7 @@ current.lines = lines; if (current.added || current.removed) { + // If we have previous context, start with that if (!oldRangeStart) { var prev = diff[i - 1]; oldRangeStart = oldLine; @@ -417,17 +418,21 @@ newRangeStart -= curRange.length; } } + + // Output our changes curRange.push.apply(curRange, map(lines, function(entry) { return (current.added ? '+' : '-') + entry; })); eofNL(curRange, i, current); + // Track the updated file position if (current.added) { newLine += lines.length; } else { oldLine += lines.length; } } else { + // Identical context lines. Track line changes if (oldRangeStart) { // Close out any changes that have been output (or join overlapping) if (lines.length <= 8 && i < diff.length - 2) { From b86c6e7b583c933a2fbc35a772fb282aba84d930 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 6 May 2015 03:21:20 -0500 Subject: [PATCH 101/527] Output removals prior to additions Fixes #14 --- diff.js | 15 ++++++++++++--- test/createPatch.js | 24 +++++++++++------------ test/diffTest.js | 46 ++++++++++++++++++++++----------------------- 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/diff.js b/diff.js index 46cd77a2..482a0a71 100644 --- a/diff.js +++ b/diff.js @@ -128,6 +128,15 @@ } else { component.value = oldString.slice(oldPos, oldPos + component.count).join(''); oldPos += component.count; + + // Reverse add and remove so removes are output first to match common convention + // The diffing algorithm is tied to add then remove output and this is the simplest + // route to get the desired output with minimal overhead. + if (componentPos && components[componentPos - 1].added) { + var tmp = components[componentPos - 1]; + components[componentPos - 1] = components[componentPos]; + components[componentPos] = tmp; + } } } @@ -188,8 +197,8 @@ bestPath[diagonalPath - 1] = undefined; } - var canAdd = addPath && addPath.newPos + 1 < newLen; - var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; + var canAdd = addPath && addPath.newPos + 1 < newLen, + canRemove = removePath && 0 <= oldPos && oldPos < oldLen; if (!canAdd && !canRemove) { // If this path is a terminal then prune bestPath[diagonalPath] = undefined; @@ -390,7 +399,7 @@ function eofNL(curRange, i, current) { var last = diff[diff.length - 2], isLast = i === diff.length - 2, - isLastOfType = i === diff.length - 3 && (current.added !== last.added || current.removed !== last.removed); + isLastOfType = i === diff.length - 3 && current.added !== last.added; // Figure out if this is the last line for the given file and missing NL if (!(/\n$/.test(current.value)) && (isLast || isLastOfType)) { diff --git a/test/createPatch.js b/test/createPatch.js index 439e9de0..62665acf 100644 --- a/test/createPatch.js +++ b/test/createPatch.js @@ -39,8 +39,8 @@ describe('#createPatch', function() { + ' line1\n' + ' line2\n' + ' line3\n' - + '+line44\n' - + '-line4\n'); + + '-line4\n' + + '+line44\n'); diff.createPatch('test', 'line1\nline2\nline3\nline4\n', 'line1\nline2\nline3\nline44\nline5\n', 'header1', 'header2').should.equal( 'Index: test\n' @@ -51,9 +51,9 @@ describe('#createPatch', function() { + ' line1\n' + ' line2\n' + ' line3\n' + + '-line4\n' + '+line44\n' - + '+line5\n' - + '-line4\n'); + + '+line5\n'); }); it('should output no newline at end of file message', function() { @@ -66,9 +66,9 @@ describe('#createPatch', function() { + ' line1\n' + ' line2\n' + ' line3\n' + + '-line4\n' + '+line4\n' - + '\\ No newline at end of file\n' - + '-line4\n'); + + '\\ No newline at end of file\n'); diff.createPatch('test', 'line1\nline2\nline3\nline4', 'line1\nline2\nline3\nline4\n', 'header1', 'header2').should.equal( 'Index: test\n' @@ -79,9 +79,9 @@ describe('#createPatch', function() { + ' line1\n' + ' line2\n' + ' line3\n' - + '+line4\n' + '-line4\n' - + '\\ No newline at end of file\n'); + + '\\ No newline at end of file\n' + + '+line4\n'); diff.createPatch('test', 'line11\nline2\nline3\nline4', 'line1\nline2\nline3\nline4', 'header1', 'header2').should.equal( 'Index: test\n' @@ -89,8 +89,8 @@ describe('#createPatch', function() { + '--- test\theader1\n' + '+++ test\theader2\n' + '@@ -1,4 +1,4 @@\n' - + '+line1\n' + '-line11\n' + + '+line1\n' + ' line2\n' + ' line3\n' + ' line4\n' @@ -102,8 +102,8 @@ describe('#createPatch', function() { + '--- test\theader1\n' + '+++ test\theader2\n' + '@@ -1,5 +1,5 @@\n' - + '+line1\n' + '-line11\n' + + '+line1\n' + ' line2\n' + ' line3\n' + ' line4\n' @@ -429,9 +429,9 @@ describe('#createPatch', function() { + '--- testFileName\tOld Header\n' + '+++ testFileName\tNew Header\n' + '@@ -1,5 +1,6 @@\n' + + '-value\n' + '+new value\n' + '+new value 2\n' - + '-value\n' + ' context\n' + ' context\n' + ' context\n' @@ -465,9 +465,9 @@ describe('#createPatch', function() { + ' context\n' + ' context\n' + ' context\n' + + '-value\n' + '+new value\n' + '+new value 2\n' - + '-value\n' + ' context\n' + ' context\n' + '\\ No newline at end of file\n'; diff --git a/test/diffTest.js b/test/diffTest.js index 42903348..fe034d0e 100644 --- a/test/diffTest.js +++ b/test/diffTest.js @@ -4,24 +4,24 @@ var diff = require('../diff'), describe('#diffWords', function() { it('should diff whitespace', function() { var diffResult = diff.diffWords('New Value', 'New ValueMoreData'); - diff.convertChangesToXML(diffResult).should.equal('New ValueMoreDataValue'); + diff.convertChangesToXML(diffResult).should.equal('New ValueValueMoreData'); }); it('should diff multiple whitespace values', function() { var diffResult = diff.diffWords('New Value ', 'New ValueMoreData '); - diff.convertChangesToXML(diffResult).should.equal('New ValueMoreDataValue '); + diff.convertChangesToXML(diffResult).should.equal('New ValueValueMoreData '); }); // Diff on word boundary it('should diff on word boundaries', function() { var diffResult = diff.diffWords('New :Value:Test', 'New ValueMoreData '); - diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData :Value:Test'); + diff.convertChangesToXML(diffResult).should.equal('New :Value:TestValueMoreData '); diffResult = diff.diffWords('New Value:Test', 'New Value:MoreData '); - diff.convertChangesToXML(diffResult).should.equal('New Value:MoreData Test'); + diff.convertChangesToXML(diffResult).should.equal('New Value:TestMoreData '); diffResult = diff.diffWords('New Value-Test', 'New Value:MoreData '); - diff.convertChangesToXML(diffResult).should.equal('New Value:MoreData -Test'); + diff.convertChangesToXML(diffResult).should.equal('New Value-Test:MoreData '); diffResult = diff.diffWords('New Value', 'New Value:MoreData '); diff.convertChangesToXML(diffResult).should.equal('New Value:MoreData '); @@ -55,7 +55,7 @@ describe('#diffWords', function() { // With without anchor (the Heckel algorithm error case) it('should diff when there is no anchor value', function() { var diffResult = diff.diffWords('New Value New Value', 'Value Value New New'); - diff.convertChangesToXML(diffResult).should.equal('ValueNew Value New NewValue'); + diff.convertChangesToXML(diffResult).should.equal('NewValue Value New ValueNew'); }); }); @@ -63,7 +63,7 @@ describe('#diffWords - async', function() { it('should diff whitespace', function(done) { diff.diffWords('New Value', 'New ValueMoreData', function(err, diffResult) { should(err).not.exist; - diff.convertChangesToXML(diffResult).should.equal('New ValueMoreDataValue'); + diff.convertChangesToXML(diffResult).should.equal('New ValueValueMoreData'); done(); }); }); @@ -71,7 +71,7 @@ describe('#diffWords - async', function() { it('should diff multiple whitespace values', function(done) { diff.diffWords('New Value ', 'New ValueMoreData ', function(err, diffResult) { should(err).not.exist; - diff.convertChangesToXML(diffResult).should.equal('New ValueMoreDataValue '); + diff.convertChangesToXML(diffResult).should.equal('New ValueValueMoreData '); done(); }); }); @@ -80,7 +80,7 @@ describe('#diffWords - async', function() { it('should diff on word boundaries', function(done) { diff.diffWords('New :Value:Test', 'New ValueMoreData ', function(err, diffResult) { should(err).not.exist; - diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData :Value:Test'); + diff.convertChangesToXML(diffResult).should.equal('New :Value:TestValueMoreData '); done(); }); }); @@ -126,7 +126,7 @@ describe('#diffWords - async', function() { // With without anchor (the Heckel algorithm error case) it('should diff when there is no anchor value', function(done) { diff.diffWords('New Value New Value', 'Value Value New New', function(err, diffResult) { - diff.convertChangesToXML(diffResult).should.equal('ValueNew Value New NewValue'); + diff.convertChangesToXML(diffResult).should.equal('NewValue Value New ValueNew'); done(); }); }); @@ -135,7 +135,7 @@ describe('#diffWords - async', function() { describe('#diffWordsWithSpace', function() { it('should diff whitespace', function() { var diffResult = diff.diffWordsWithSpace('New Value', 'New ValueMoreData'); - diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData Value'); + diff.convertChangesToXML(diffResult).should.equal('New Value ValueMoreData'); }); it('should diff multiple whitespace values', function() { @@ -155,12 +155,12 @@ describe('#diffChars', function() { describe('#diffSentences', function() { it('Should diff Sentences', function() { var diffResult = diff.diffSentences('New Value.', 'New ValueMoreData.'); - diff.convertChangesToXML(diffResult).should.equal('New ValueMoreData.New Value.'); + diff.convertChangesToXML(diffResult).should.equal('New Value.New ValueMoreData.'); }); it('should diff only the last sentence', function() { var diffResult = diff.diffSentences('Here im. Rock you like old man.', 'Here im. Rock you like hurricane.'); - diff.convertChangesToXML(diffResult).should.equal('Here im. Rock you like hurricane.Rock you like old man.'); + diff.convertChangesToXML(diffResult).should.equal('Here im. Rock you like old man.Rock you like hurricane.'); }); }); @@ -171,9 +171,9 @@ describe('#diffCss', function() { '.test,#value .test{margin-left:50px;margin-right:-40px}', '.test2, #value2 .test {\nmargin-top:50px;\nmargin-right:-400px;\n}'); diff.convertChangesToXML(diffResult).should.equal( - '.test2.test,#value #value2 .test {\n' - + 'margin-topmargin-left:50px;\n' - + 'margin-right:-400px;\n-40px}'); + '.test.test2,#value #value2 .test {' + + 'margin-left\nmargin-top:50px;\n' + + 'margin-right:-40px-400px;\n}'); }); }); @@ -183,7 +183,7 @@ describe('#diffLines', function() { var diffResult = diff.diffLines( 'line\nold value\nline', 'line\nnew value\nline'); - diff.convertChangesToXML(diffResult).should.equal('line\nnew value\nold value\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\nold value\nnew value\nline'); }); it('should the same lines in diff', function() { var diffResult = diff.diffLines( @@ -196,21 +196,21 @@ describe('#diffLines', function() { var diffResult = diff.diffLines( 'line\nvalue \nline', 'line\nvalue\nline'); - diff.convertChangesToXML(diffResult).should.equal('line\nvalue\nvalue \nline'); + diff.convertChangesToXML(diffResult).should.equal('line\nvalue \nvalue\nline'); }); it('should handle windows line endings', function() { var diffResult = diff.diffLines( 'line\r\nold value \r\nline', 'line\r\nnew value\r\nline'); - diff.convertChangesToXML(diffResult).should.equal('line\r\nnew value\r\nold value \r\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\r\nold value \r\nnew value\r\nline'); }); it('should handle empty lines', function() { var diffResult = diff.diffLines( 'line\n\nold value \n\nline', 'line\n\nnew value\n\nline'); - diff.convertChangesToXML(diffResult).should.equal('line\n\nnew value\nold value \n\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\n\nold value \nnew value\n\nline'); }); it('should handle empty input', function() { @@ -227,7 +227,7 @@ describe('#TrimmedLineDiff', function() { var diffResult = diff.diffTrimmedLines( 'line\nold value\nline', 'line\nnew value\nline'); - diff.convertChangesToXML(diffResult).should.equal('line\nnew value\nold value\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\nold value\nnew value\nline'); }); it('should the same lines in diff', function() { var diffResult = diff.diffTrimmedLines( @@ -247,7 +247,7 @@ describe('#TrimmedLineDiff', function() { var diffResult = diff.diffTrimmedLines( 'line\r\nold value \r\nline', 'line\r\nnew value\r\nline'); - diff.convertChangesToXML(diffResult).should.equal('line\r\nnew value\r\nold value\r\nline'); + diff.convertChangesToXML(diffResult).should.equal('line\r\nold value\r\nnew value\r\nline'); }); }); @@ -326,6 +326,6 @@ describe('convertToDMP', function() { var diffResult = diff.diffWords('New Value ', 'New ValueMoreData '); diff.convertChangesToDMP(diffResult).should.eql( - [[0, 'New '], [1, 'ValueMoreData'], [-1, 'Value'], [0, ' ']]); + [[0, 'New '], [-1, 'Value'], [1, 'ValueMoreData'], [0, ' ']]); }); }); From 98a5bb2503cbafb2431ba46e9ef6b7d4bc7adfae Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 6 May 2015 09:20:53 -0500 Subject: [PATCH 102/527] Only use ASCII new lines for patch tokenization Fixes #57 --- diff.js | 26 ++++++++++++++++++++++++-- test/applyPatch.js | 8 ++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/diff.js b/diff.js index 482a0a71..421854a1 100644 --- a/diff.js +++ b/diff.js @@ -322,7 +322,7 @@ for (var i = 0; i < lines.length; i++) { var line = lines[i], lastLine = lines[i - 1], - lastLineLastChar = lastLine ? lastLine[lastLine.length - 1] : ''; + lastLineLastChar = lastLine && lastLine[lastLine.length - 1]; // Merge lines that may contain windows new lines if (line === '\n' && lastLineLastChar === '\r') { @@ -342,6 +342,28 @@ return retLines; }; + var PatchDiff = new Diff(); + PatchDiff.tokenize = function(value) { + var ret = [], + linesAndNewlines = value.split(/(\n|\r\n)/); + + // Ignore the final empty token that occurs if the string ends with a new line + if (!linesAndNewlines[linesAndNewlines.length - 1]) { + linesAndNewlines.pop(); + } + + // Merge the content and line separators into single tokens + for (var i = 0; i < linesAndNewlines.length; i++) { + var line = linesAndNewlines[i]; + + if (i % 2) { + ret[ret.length - 1] += line; + } else { + ret.push(line); + } + } + return ret; + }; var SentenceDiff = new Diff(); SentenceDiff.tokenize = function(value) { @@ -387,7 +409,7 @@ ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); - var diff = LineDiff.diff(oldStr, newStr); + var diff = PatchDiff.diff(oldStr, newStr); diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier // Formats a given set of lines for printing as context lines in a patch diff --git a/test/applyPatch.js b/test/applyPatch.js index 304ded65..0ca02601 100644 --- a/test/applyPatch.js +++ b/test/applyPatch.js @@ -305,4 +305,12 @@ describe('#applyPatch', function() { + ' line5\n') .should.equal(false); }); + + it('should work with unicode newline characters', function() { + var oldtext = 'AAAAAAAAAAAAAAAA\n\n'; + var newtext = 'AAAAAAAAAAAAAAAA\nBBBBBB' + String.fromCharCode(0x2028) + '\nCCCCCCCCCCCCCCCCCC\n\n'; + + var diffed = diff.createPatch('test', oldtext, newtext); + diff.applyPatch(oldtext, diffed).should.equal(newtext); + }); }); From f8e79fd6d9006c3cefb26ed41e0e416738bba57c Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 6 May 2015 09:21:02 -0500 Subject: [PATCH 103/527] Remove unused var --- test/canonicalize.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/canonicalize.js b/test/canonicalize.js index 7df0600c..568de919 100644 --- a/test/canonicalize.js +++ b/test/canonicalize.js @@ -1,5 +1,3 @@ -const VERBOSE = false; - var diff = require('../diff'); function getKeys(obj) { From 9582b04f891fb8642931771f2939a3bf4acb9a68 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 6 May 2015 09:21:14 -0500 Subject: [PATCH 104/527] Add coverage checking at 100% --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d748af96..903a59c2 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ }, "main": "./diff", "scripts": { - "test": "istanbul cover node_modules/.bin/_mocha test/*.js" + "test": "istanbul cover node_modules/.bin/_mocha test/*.js && istanbul check-coverage --statements 100 --functions 100 --branches 100 --lines 100 coverage/coverage.json" }, "dependencies": {}, "devDependencies": { From 9e81c0fe861ba850dade04404cb0fd55772b0f14 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 6 May 2015 09:31:18 -0500 Subject: [PATCH 105/527] Update release notes --- release-notes.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/release-notes.md b/release-notes.md index 87c7b600..58ef9887 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,14 @@ ## Development -[Commits](https://github.com/kpdecker/jsdiff/compare/v1.3.2...master) +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.4.0...master) + +## v1.4.0 - May 6th, 2015 +- [#57](https://github.com/kpdecker/jsdiff/issues/57) - createPatch -> applyPatch failed. ([@mog422](https://api.github.com/users/mog422)) +- [#56](https://github.com/kpdecker/jsdiff/pull/56) - Two files patch ([@rgeissert](https://api.github.com/users/rgeissert)) +- [#14](https://github.com/kpdecker/jsdiff/issues/14) - Flip added and removed order? ([@jakesandlund](https://api.github.com/users/jakesandlund)) + +[Commits](https://github.com/kpdecker/jsdiff/compare/v1.3.2...v1.4.0) ## v1.3.2 - March 30th, 2015 - [#53](https://github.com/kpdecker/jsdiff/pull/53) - Updated README.MD with Bower installation instructions ([@ofbriggs](https://api.github.com/users/ofbriggs)) From 27a750e9116e6ade6303bc24a9be72f6845e00ed Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 6 May 2015 09:31:20 -0500 Subject: [PATCH 106/527] v1.4.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index aa6862d1..c755b0ac 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "jsdiff", - "version": "1.3.2", + "version": "1.4.0", "main": [ "diff.js" ], diff --git a/package.json b/package.json index 903a59c2..2bd81f89 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diff", - "version": "1.3.2", + "version": "1.4.0", "description": "A javascript text diff implementation.", "keywords": [ "diff", From f5d3bd15620fa96001b225752f6a5bf75f05a5bb Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Fri, 10 Jul 2015 20:07:25 +0800 Subject: [PATCH 107/527] Use svg instead of png to get better image quality --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b867e19a..7ff7fffc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # jsdiff -[![Build Status](https://secure.travis-ci.org/kpdecker/jsdiff.png)](http://travis-ci.org/kpdecker/jsdiff) +[![Build Status](https://secure.travis-ci.org/kpdecker/jsdiff.svg)](http://travis-ci.org/kpdecker/jsdiff) A javascript text differencing implementation. From b257c312567da10726cf92ec3510b206ca831a07 Mon Sep 17 00:00:00 2001 From: Quest Date: Wed, 22 Jul 2015 18:19:12 +0200 Subject: [PATCH 108/527] Allow retreiving the patch data as a list of hunk objects --- diff.js | 101 ++++++++++++++++++++++++---------------- test/createPatch.js | 76 +++++++++++++++++++++++++++++- test/structuredPatch.js | 26 +++++++++++ 3 files changed, 162 insertions(+), 41 deletions(-) create mode 100644 test/structuredPatch.js diff --git a/diff.js b/diff.js index 421854a1..1c9e5c5d 100644 --- a/diff.js +++ b/diff.js @@ -398,39 +398,22 @@ callback ); }, - - createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader) { - var ret = []; - - if (oldFileName == newFileName) { - ret.push('Index: ' + oldFileName); + + structuredPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + if(!options) { + options = { context: 4 }; } - ret.push('==================================================================='); - ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); - ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); - + var diff = PatchDiff.diff(oldStr, newStr); diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier - - // Formats a given set of lines for printing as context lines in a patch + function contextLines(lines) { return map(lines, function(entry) { return ' ' + entry; }); } - // Outputs the no newline at end of file warning if needed - function eofNL(curRange, i, current) { - var last = diff[diff.length - 2], - isLast = i === diff.length - 2, - isLastOfType = i === diff.length - 3 && current.added !== last.added; - - // Figure out if this is the last line for the given file and missing NL - if (!(/\n$/.test(current.value)) && (isLast || isLastOfType)) { - curRange.push('\\ No newline at end of file'); - } - } - + var hunks = []; var oldRangeStart = 0, newRangeStart = 0, curRange = [], - oldLine = 1, newLine = 1; + oldLine = 1, newLine = 1; for (var i = 0; i < diff.length; i++) { var current = diff[i], lines = current.lines || current.value.replace(/\n$/, '').split('\n'); @@ -444,7 +427,7 @@ newRangeStart = newLine; if (prev) { - curRange = contextLines(prev.lines.slice(-4)); + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; oldRangeStart -= curRange.length; newRangeStart -= curRange.length; } @@ -454,7 +437,6 @@ curRange.push.apply(curRange, map(lines, function(entry) { return (current.added ? '+' : '-') + entry; })); - eofNL(curRange, i, current); // Track the updated file position if (current.added) { @@ -466,21 +448,34 @@ // Identical context lines. Track line changes if (oldRangeStart) { // Close out any changes that have been output (or join overlapping) - if (lines.length <= 8 && i < diff.length - 2) { + if (lines.length <= options.context * 2 && i < diff.length - 2) { // Overlapping curRange.push.apply(curRange, contextLines(lines)); } else { // end the range and output - var contextSize = Math.min(lines.length, 4); - ret.push( - '@@ -' + oldRangeStart + ',' + (oldLine - oldRangeStart + contextSize) - + ' +' + newRangeStart + ',' + (newLine - newRangeStart + contextSize) - + ' @@'); - ret.push.apply(ret, curRange); - ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); - if (lines.length <= 4) { - eofNL(ret, i, current); + var contextSize = Math.min(lines.length, options.context); + var hunklines = []; + hunklines.push.apply(hunklines, curRange); + hunklines.push.apply(hunklines, contextLines(lines.slice(0, contextSize))); + + var hunk = { + oldStart: oldRangeStart, + oldLines: (oldLine - oldRangeStart + contextSize), + newStart: newRangeStart, + newLines: (newLine - newRangeStart + contextSize), + lines: hunklines + } + if(i >= diff.length - 2 && lines.length <= options.context) { + // EOF is inside this hunk + var oldEOFNewline = /\n$/.test(oldStr); + var newEOFNewline = /\n$/.test(newStr); + if(lines.length == 0 && !oldEOFNewline) { + hunklines.splice(hunk.oldLines, 0, '\\ No newline at end of file') + } else if (!oldEOFNewline || !newEOFNewline) { + hunklines.push('\\ No newline at end of file') + } } + hunks.push(hunk); oldRangeStart = 0; newRangeStart = 0; @@ -491,12 +486,40 @@ newLine += lines.length; } } + + return { + oldFileName: oldFileName, newFileName: newFileName, + oldHeader: oldHeader, newHeader: newHeader, + hunks: hunks + }; + }, + createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + var diff = JsDiff.structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); + + var ret = []; + if (oldFileName == newFileName) { + ret.push('Index: ' + oldFileName); + } + ret.push('==================================================================='); + ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); + ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); + + for(var i = 0; i < diff.hunks.length; i++) { + var hunk = diff.hunks[i]; + ret.push( + '@@ -' + hunk.oldStart + ',' + hunk.oldLines + + ' +' + hunk.newStart + ',' + hunk.newLines + + ' @@' + ); + ret.push.apply(ret, hunk.lines); + } + return ret.join('\n') + '\n'; }, - createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { - return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader); + createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader, options) { + return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); }, applyPatch: function(oldStr, uniDiff) { diff --git a/test/createPatch.js b/test/createPatch.js index 62665acf..fea4aa83 100644 --- a/test/createPatch.js +++ b/test/createPatch.js @@ -56,7 +56,7 @@ describe('#createPatch', function() { + '+line5\n'); }); - it('should output no newline at end of file message', function() { + it('should output "no newline" at end of file message on new missing nl', function() { diff.createPatch('test', 'line1\nline2\nline3\nline4\n', 'line1\nline2\nline3\nline4', 'header1', 'header2').should.equal( 'Index: test\n' + '===================================================================\n' @@ -69,7 +69,9 @@ describe('#createPatch', function() { + '-line4\n' + '+line4\n' + '\\ No newline at end of file\n'); + }); + it('should output "no newline" at end of file message on old missing nl', function() { diff.createPatch('test', 'line1\nline2\nline3\nline4', 'line1\nline2\nline3\nline4\n', 'header1', 'header2').should.equal( 'Index: test\n' + '===================================================================\n' @@ -82,7 +84,9 @@ describe('#createPatch', function() { + '-line4\n' + '\\ No newline at end of file\n' + '+line4\n'); + }); + it('should output "no newline" at end of file message on context missing nl', function() { diff.createPatch('test', 'line11\nline2\nline3\nline4', 'line1\nline2\nline3\nline4', 'header1', 'header2').should.equal( 'Index: test\n' + '===================================================================\n' @@ -95,7 +99,9 @@ describe('#createPatch', function() { + ' line3\n' + ' line4\n' + '\\ No newline at end of file\n'); + }); + it('should not output no newline at end of file message when eof outside hunk', function() { diff.createPatch('test', 'line11\nline2\nline3\nline4\nline4\nline4\nline4', 'line1\nline2\nline3\nline4\nline4\nline4\nline4', 'header1', 'header2').should.equal( 'Index: test\n' + '===================================================================\n' @@ -422,7 +428,7 @@ describe('#createPatch', function() { + 'context\n' + 'context'; - it('should generate a patch', function() { + it('should generate a patch with default context size', function() { var expectedResult = 'Index: testFileName\n' + '===================================================================\n' @@ -475,6 +481,72 @@ describe('#createPatch', function() { var diffResult = diff.createPatch('testFileName', oldFile, newFile, 'Old Header', 'New Header'); diffResult.should.equal(expectedResult); }); + + it('should generatea a patch with context size 0', function() { + var expectedResult = + 'Index: testFileName\n' + + '===================================================================\n' + + '--- testFileName\tOld Header\n' + + '+++ testFileName\tNew Header\n' + + '@@ -1,1 +1,2 @@\n' + + '-value\n' + + '+new value\n' + + '+new value 2\n' + + '@@ -11,1 +12,0 @@\n' + + '-remove value\n' + + '@@ -21,1 +21,0 @@\n' + + '-remove value\n' + + '@@ -30,0 +29,1 @@\n' + + '+add value\n' + + '@@ -34,1 +34,2 @@\n' + + '-value\n' + + '+new value\n' + + '+new value 2\n'; + var diffResult = diff.createPatch('testFileName', oldFile, newFile, 'Old Header', 'New Header', { context: 0 }); + diffResult.should.equal(expectedResult); + }); + + it('should generate a patch with context size 2', function() { + var expectedResult = + 'Index: testFileName\n' + + '===================================================================\n' + + '--- testFileName\tOld Header\n' + + '+++ testFileName\tNew Header\n' + + '@@ -1,3 +1,4 @@\n' + + '-value\n' + + '+new value\n' + + '+new value 2\n' + + ' context\n' + + ' context\n' + + '@@ -9,5 +10,4 @@\n' + + ' context\n' + + ' context\n' + + '-remove value\n' + + ' context\n' + + ' context\n' + + '@@ -19,5 +19,4 @@\n' + + ' context\n' + + ' context\n' + + '-remove value\n' + + ' context\n' + + ' context\n' + + '@@ -28,9 +27,11 @@\n' + + ' context\n' + + ' context\n' + + '+add value\n' + + ' context\n' + + ' context\n' + + ' context\n' + + ' context\n' + + '-value\n' + + '+new value\n' + + '+new value 2\n' + + ' context\n' + + ' context\n' + + '\\ No newline at end of file\n'; + var diffResult = diff.createPatch('testFileName', oldFile, newFile, 'Old Header', 'New Header', { context: 2 }); + diffResult.should.equal(expectedResult); + }); it('should output headers only for identical files', function() { var expectedResult = diff --git a/test/structuredPatch.js b/test/structuredPatch.js new file mode 100644 index 00000000..08201b81 --- /dev/null +++ b/test/structuredPatch.js @@ -0,0 +1,26 @@ +const VERBOSE = false; + +var diff = require('../diff'); + +function log() { + VERBOSE && console.log.apply(console, arguments); +} + +describe('#structuredPatch', function() { + it('should handle files with the last line changed', function() { + var res = diff.structuredPatch( + 'oldfile', 'newfile', + 'line2\nline3\nline5\n', 'line2\nline3\nline4\nline5\n', + 'header1', 'header2' + ); + res.should.eql({ + oldFileName: 'oldfile', newFileName: 'newfile', + oldHeader: 'header1', newHeader: 'header2', + hunks: [{ + oldStart: 1, oldLines: 3, newStart: 1, newLines: 4, + lines: [' line2', ' line3', '+line4', ' line5'], + oldEOFNewline: true, newEOFNewline: true, + }] + }); + }); +}); From bc11bccc0fcebefc2655081928407fb548804d3b Mon Sep 17 00:00:00 2001 From: Quest Date: Wed, 22 Jul 2015 22:46:55 +0200 Subject: [PATCH 109/527] Test on structured patch method updated --- test/structuredPatch.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/structuredPatch.js b/test/structuredPatch.js index 08201b81..d88077c5 100644 --- a/test/structuredPatch.js +++ b/test/structuredPatch.js @@ -10,16 +10,15 @@ describe('#structuredPatch', function() { it('should handle files with the last line changed', function() { var res = diff.structuredPatch( 'oldfile', 'newfile', - 'line2\nline3\nline5\n', 'line2\nline3\nline4\nline5\n', + 'line2\nline3\nline4\n', 'line2\nline3\nline5', 'header1', 'header2' ); res.should.eql({ oldFileName: 'oldfile', newFileName: 'newfile', oldHeader: 'header1', newHeader: 'header2', hunks: [{ - oldStart: 1, oldLines: 3, newStart: 1, newLines: 4, - lines: [' line2', ' line3', '+line4', ' line5'], - oldEOFNewline: true, newEOFNewline: true, + oldStart: 1, oldLines: 3, newStart: 1, newLines: 3, + lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'], }] }); }); From 8209a6bf8becad36a010a1575b6c0e4df9f27636 Mon Sep 17 00:00:00 2001 From: Quest Date: Fri, 31 Jul 2015 01:32:20 +0200 Subject: [PATCH 110/527] As per @kpdecker comments to PR #62 --- diff.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/diff.js b/diff.js index 1c9e5c5d..66f4a02d 100644 --- a/diff.js +++ b/diff.js @@ -400,7 +400,7 @@ }, structuredPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { - if(!options) { + if (!options) { options = { context: 4 }; } @@ -413,7 +413,7 @@ var hunks = []; var oldRangeStart = 0, newRangeStart = 0, curRange = [], - oldLine = 1, newLine = 1; + oldLine = 1, newLine = 1; for (var i = 0; i < diff.length; i++) { var current = diff[i], lines = current.lines || current.value.replace(/\n$/, '').split('\n'); @@ -454,25 +454,24 @@ } else { // end the range and output var contextSize = Math.min(lines.length, options.context); - var hunklines = []; - hunklines.push.apply(hunklines, curRange); - hunklines.push.apply(hunklines, contextLines(lines.slice(0, contextSize))); + curRange.push.apply(curRange, contextLines(lines.slice(0, contextSize))); var hunk = { oldStart: oldRangeStart, oldLines: (oldLine - oldRangeStart + contextSize), newStart: newRangeStart, newLines: (newLine - newRangeStart + contextSize), - lines: hunklines + lines: curRange } - if(i >= diff.length - 2 && lines.length <= options.context) { + if (i >= diff.length - 2 && lines.length <= options.context) { // EOF is inside this hunk var oldEOFNewline = /\n$/.test(oldStr); var newEOFNewline = /\n$/.test(newStr); - if(lines.length == 0 && !oldEOFNewline) { - hunklines.splice(hunk.oldLines, 0, '\\ No newline at end of file') + if (lines.length == 0 && !oldEOFNewline) { + // special case: old has no eol and no trailing context; no-nl can end up before adds + curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file') } else if (!oldEOFNewline || !newEOFNewline) { - hunklines.push('\\ No newline at end of file') + curRange.push('\\ No newline at end of file') } } hunks.push(hunk); @@ -505,7 +504,7 @@ ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); - for(var i = 0; i < diff.hunks.length; i++) { + for (var i = 0; i < diff.hunks.length; i++) { var hunk = diff.hunks[i]; ret.push( '@@ -' + hunk.oldStart + ',' + hunk.oldLines From cf440256a4548722319838be7b7bee44434e77aa Mon Sep 17 00:00:00 2001 From: Quest Date: Fri, 31 Jul 2015 16:25:00 +0200 Subject: [PATCH 111/527] Documentationn for structuredPatch --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 7ff7fffc..6f415f8c 100644 --- a/README.md +++ b/README.md @@ -62,11 +62,29 @@ or * `newStr` : New string value * `oldHeader` : Additional information to include in the old file header * `newHeader` : Additional information to include in thew new file header + * `options` : An object with options. Currently, only `context` is supported and describes how many lines of context should be included. * `JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader)` - creates a unified diff patch. Just like JsDiff.createTwoFilesPatch, but with oldFileName being equal to newFileName. + + * `JsDiff.structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)` - returns an object with an array of hunk objects. + + This method is similar to createTwoFilesPatch, but returns a data structure + suitable for further processing. Parameters are the same as createTwoFilesPatch. The data structure returned may look like this: + + ```js + { + oldFileName: 'oldfile', newFileName: 'newfile', + oldHeader: 'header1', newHeader: 'header2', + hunks: [{ + oldStart: 1, oldLines: 3, newStart: 1, newLines: 3, + lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'], + }] + } + ``` + * `JsDiff.applyPatch(oldStr, diffStr)` - applies a unified diff patch. Return a string containing new version of provided data. From b49299afd6f59e4b07369d07dd53b7f60a7f3c8f Mon Sep 17 00:00:00 2001 From: Quest Date: Fri, 31 Jul 2015 16:26:54 +0200 Subject: [PATCH 112/527] Documentationn for structuredPatch --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 6f415f8c..8308de6a 100644 --- a/README.md +++ b/README.md @@ -69,20 +69,20 @@ or Just like JsDiff.createTwoFilesPatch, but with oldFileName being equal to newFileName. - * `JsDiff.structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)` - returns an object with an array of hunk objects. - - This method is similar to createTwoFilesPatch, but returns a data structure - suitable for further processing. Parameters are the same as createTwoFilesPatch. The data structure returned may look like this: - - ```js - { - oldFileName: 'oldfile', newFileName: 'newfile', - oldHeader: 'header1', newHeader: 'header2', - hunks: [{ - oldStart: 1, oldLines: 3, newStart: 1, newLines: 3, - lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'], - }] - } +* `JsDiff.structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)` - returns an object with an array of hunk objects. + + This method is similar to createTwoFilesPatch, but returns a data structure + suitable for further processing. Parameters are the same as createTwoFilesPatch. The data structure returned may look like this: + +```js +{ + oldFileName: 'oldfile', newFileName: 'newfile', + oldHeader: 'header1', newHeader: 'header2', + hunks: [{ + oldStart: 1, oldLines: 3, newStart: 1, newLines: 3, + lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'], + }] +} ``` * `JsDiff.applyPatch(oldStr, diffStr)` - applies a unified diff patch. From 30189fbe094fd0eca9e89539cc816bf9e74fa3f0 Mon Sep 17 00:00:00 2001 From: Quest Date: Fri, 31 Jul 2015 16:30:03 +0200 Subject: [PATCH 113/527] Documentationn for structuredPatch --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8308de6a..65ea13f2 100644 --- a/README.md +++ b/README.md @@ -74,15 +74,15 @@ or This method is similar to createTwoFilesPatch, but returns a data structure suitable for further processing. Parameters are the same as createTwoFilesPatch. The data structure returned may look like this: -```js -{ - oldFileName: 'oldfile', newFileName: 'newfile', - oldHeader: 'header1', newHeader: 'header2', - hunks: [{ - oldStart: 1, oldLines: 3, newStart: 1, newLines: 3, - lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'], - }] -} + ```js + { + oldFileName: 'oldfile', newFileName: 'newfile', + oldHeader: 'header1', newHeader: 'header2', + hunks: [{ + oldStart: 1, oldLines: 3, newStart: 1, newLines: 3, + lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'], + }] + } ``` * `JsDiff.applyPatch(oldStr, diffStr)` - applies a unified diff patch. From 091aeef6fc5fc8ac1247758ffb608dde4090f6a1 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Tue, 4 Aug 2015 17:05:30 -0500 Subject: [PATCH 114/527] Add eslint style checking to build --- .eslintrc | 178 ++++++++++++++++++++++++++++++++++++++++ diff.js | 38 ++++----- package.json | 3 +- test/.eslintrc | 14 ++++ test/createPatch.js | 10 +-- test/structuredPatch.js | 16 ++-- 6 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 .eslintrc create mode 100644 test/.eslintrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..15ecb91b --- /dev/null +++ b/.eslintrc @@ -0,0 +1,178 @@ +{ + "env": { + "browser": true + }, + "rules": { + // Possible Errors // + //-----------------// + + "comma-dangle": [2, "never"], + "no-cond-assign": [2, "except-parens"], + + // Allow for debugging + "no-console": 1, + + "no-constant-condition": 2, + "no-control-regex": 2, + + // Allow for debugging + "no-debugger": 1, + + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-empty": 2, + "no-empty-character-class": 2, + "no-ex-assign": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": [2, "functions"], + "no-extra-semi": 2, + "no-func-assign": 2, + + // Stylistic... might consider disallowing in the future + "no-inner-declarations": 0, + + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-negated-in-lhs": 2, + "no-obj-calls": 2, + "no-regex-spaces": 2, + "no-sparse-arrays": 0, + + // Optimizer and coverage will handle/highlight this and can be useful for debugging + "no-unreachable": 1, + + "use-isnan": 2, + "valid-jsdoc": 0, + "valid-typeof": 2, + + + // Best Practices // + //----------------// + "block-scoped-var": 0, + "complexity": 0, + "consistent-return": 0, + "curly": 2, + "default-case": 1, + "dot-notation": [2, {"allowKeywords": false}], + "eqeqeq": 0, + "guard-for-in": 1, + "no-alert": 2, + "no-caller": 2, + "no-div-regex": 1, + "no-else-return": 0, + "no-empty-label": 2, + "no-eq-null": 0, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-iterator": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-loop-func": 0, + "no-multi-spaces": 2, + "no-multi-str": 1, + "no-native-reassign": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-wrappers": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-param-reassign": 0, + "no-process-env": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-return-assign": 2, + "no-script-url": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-throw-literal": 2, + "no-unused-expressions": 2, + "no-void": 0, + "no-warning-comments": 1, + "no-with": 2, + "radix": 2, + "vars-on-top": 0, + "wrap-iife": 2, + "yoda": 0, + + + // Strict // + //--------// + "strict": 0, + + + // Variables // + //-----------// + "no-catch-shadow": 2, + "no-delete-var": 2, + "no-label-var": 2, + "no-shadow": 0, + "no-shadow-restricted-names": 0, + "no-undef": 2, + "no-undef-init": 2, + "no-undefined": 0, + "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], + "no-use-before-define": [2, "nofunc"], + + + // Node.js // + //---------// + // Others left to environment defaults + "no-mixed-requires": 0, + + + // Stylistic // + //-----------// + "indent": 0, + "brace-style": [2, "1tbs", {"allowSingleLine": true}], + "camelcase": 2, + "comma-spacing": [2, {"before": false, "after": true}], + "comma-style": [2, "last"], + "consistent-this": [1, "self"], + "eol-last": 2, + "func-names": 0, + "func-style": [2, "declaration"], + "key-spacing": [2, { + "beforeColon": false, + "afterColon": true + }], + "max-nested-callbacks": 0, + "new-cap": 2, + "new-parens": 2, + "newline-after-var": 0, + "no-array-constructor": 2, + "no-continue": 0, + "no-inline-comments": 0, + "no-lonely-if": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multiple-empty-lines": 0, + "no-nested-ternary": 1, + "no-new-object": 2, + "no-spaced-func": 2, + "no-ternary": 0, + "no-trailing-spaces": 2, + "no-underscore-dangle": 0, + "one-var": 0, + "operator-assignment": 0, + "padded-blocks": 0, + "quote-props": [2, "as-needed", {"keywords": true}], + "quotes": [2, "single", "avoid-escape"], + "semi": 2, + "semi-spacing": [2, {"before": false, "after": true}], + "sort-vars": 0, + "space-after-keywords": [2, "always"], + "space-before-blocks": [2, "always"], + "space-before-function-paren": [2, {"anonymous": "never", "named": "never"}], + "space-in-brackets": 0, + "space-in-parens": [2, "never"], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": 2, + "spaced-comment": [2, "always"], + "wrap-regex": 1 + } +} \ No newline at end of file diff --git a/diff.js b/diff.js index 66f4a02d..dae487df 100644 --- a/diff.js +++ b/diff.js @@ -17,7 +17,7 @@ (function(global, undefined) { var objectPrototypeToString = Object.prototype.toString; - /*istanbul ignore next*/ + /* istanbul ignore next*/ function map(arr, mapper, that) { if (Array.prototype.map) { return Array.prototype.map.call(arr, mapper, that); @@ -238,7 +238,7 @@ (function exec() { setTimeout(function() { // This should not happen, but we want to be safe. - /*istanbul ignore next */ + /* istanbul ignore next */ if (editLength > maxEditLength) { return callback(); } @@ -398,15 +398,15 @@ callback ); }, - + structuredPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { if (!options) { options = { context: 4 }; } - + var diff = PatchDiff.diff(oldStr, newStr); diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier - + function contextLines(lines) { return map(lines, function(entry) { return ' ' + entry; }); } @@ -456,22 +456,22 @@ var contextSize = Math.min(lines.length, options.context); curRange.push.apply(curRange, contextLines(lines.slice(0, contextSize))); - var hunk = { - oldStart: oldRangeStart, - oldLines: (oldLine - oldRangeStart + contextSize), - newStart: newRangeStart, + var hunk = { + oldStart: oldRangeStart, + oldLines: (oldLine - oldRangeStart + contextSize), + newStart: newRangeStart, newLines: (newLine - newRangeStart + contextSize), lines: curRange - } + }; if (i >= diff.length - 2 && lines.length <= options.context) { // EOF is inside this hunk - var oldEOFNewline = /\n$/.test(oldStr); - var newEOFNewline = /\n$/.test(newStr); + var oldEOFNewline = (/\n$/.test(oldStr)); + var newEOFNewline = (/\n$/.test(newStr)); if (lines.length == 0 && !oldEOFNewline) { // special case: old has no eol and no trailing context; no-nl can end up before adds - curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file') + curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); } else if (!oldEOFNewline || !newEOFNewline) { - curRange.push('\\ No newline at end of file') + curRange.push('\\ No newline at end of file'); } } hunks.push(hunk); @@ -485,7 +485,7 @@ newLine += lines.length; } } - + return { oldFileName: oldFileName, newFileName: newFileName, oldHeader: oldHeader, newHeader: newHeader, @@ -513,7 +513,7 @@ ); ret.push.apply(ret, hunk.lines); } - + return ret.join('\n') + '\n'; }, @@ -628,12 +628,12 @@ canonicalize: canonicalize }; - /*istanbul ignore next */ - /*global module */ + /* istanbul ignore next */ + /* global module */ if (typeof module !== 'undefined' && module.exports) { module.exports = JsDiff; } else if (typeof define === 'function' && define.amd) { - /*global define */ + /* global define */ define([], function() { return JsDiff; }); } else if (typeof global.JsDiff === 'undefined') { global.JsDiff = JsDiff; diff --git a/package.json b/package.json index 2bd81f89..70764eed 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,12 @@ }, "main": "./diff", "scripts": { - "test": "istanbul cover node_modules/.bin/_mocha test/*.js && istanbul check-coverage --statements 100 --functions 100 --branches 100 --lines 100 coverage/coverage.json" + "test": "eslint *.js test/ && istanbul cover node_modules/.bin/_mocha test/*.js && istanbul check-coverage --statements 100 --functions 100 --branches 100 --lines 100 coverage/coverage.json" }, "dependencies": {}, "devDependencies": { "colors": "^1.1.0", + "eslint": "^1.0.0", "istanbul": "^0.3.2", "mocha": "^2.2.4", "should": "^6.0.1" diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 00000000..bc43bc2e --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,14 @@ +{ + "env": { + "node": true, + "mocha": true + }, + "rules": { + // Disabling for tests, for now. + "no-unused-expressions": 0, + "no-path-concat": 0, + + "no-var": 0, + "no-console": 0 + } +} \ No newline at end of file diff --git a/test/createPatch.js b/test/createPatch.js index fea4aa83..365e7c2c 100644 --- a/test/createPatch.js +++ b/test/createPatch.js @@ -1,4 +1,4 @@ -const VERBOSE = false; +var VERBOSE = false; var diff = require('../diff'); @@ -314,11 +314,11 @@ describe('#createPatch', function() { var largeNewValue = largeTest, len = largeTest.length, count = nextRandom() % 20, - removeBound = len-(count*100), + removeBound = len - (count * 100), logData = []; for (; count > 0; count--) { var removePos = nextRandom() % removeBound, - removeLength = 1+nextRandom()%100; + removeLength = 1 + nextRandom() % 100; logData.push('(' + removePos + ', ' + removeLength + ')'); largeNewValue = largeNewValue.substring(0, removePos) + largeNewValue.substring(removePos + removeLength); @@ -481,7 +481,7 @@ describe('#createPatch', function() { var diffResult = diff.createPatch('testFileName', oldFile, newFile, 'Old Header', 'New Header'); diffResult.should.equal(expectedResult); }); - + it('should generatea a patch with context size 0', function() { var expectedResult = 'Index: testFileName\n' @@ -505,7 +505,7 @@ describe('#createPatch', function() { var diffResult = diff.createPatch('testFileName', oldFile, newFile, 'Old Header', 'New Header', { context: 0 }); diffResult.should.equal(expectedResult); }); - + it('should generate a patch with context size 2', function() { var expectedResult = 'Index: testFileName\n' diff --git a/test/structuredPatch.js b/test/structuredPatch.js index d88077c5..6d3d4ef9 100644 --- a/test/structuredPatch.js +++ b/test/structuredPatch.js @@ -1,24 +1,18 @@ -const VERBOSE = false; - var diff = require('../diff'); -function log() { - VERBOSE && console.log.apply(console, arguments); -} - describe('#structuredPatch', function() { it('should handle files with the last line changed', function() { var res = diff.structuredPatch( - 'oldfile', 'newfile', - 'line2\nline3\nline4\n', 'line2\nline3\nline5', + 'oldfile', 'newfile', + 'line2\nline3\nline4\n', 'line2\nline3\nline5', 'header1', 'header2' ); res.should.eql({ - oldFileName: 'oldfile', newFileName: 'newfile', + oldFileName: 'oldfile', newFileName: 'newfile', oldHeader: 'header1', newHeader: 'header2', - hunks: [{ + hunks: [{ oldStart: 1, oldLines: 3, newStart: 1, newLines: 3, - lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'], + lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'] }] }); }); From f01f57a1c65183e62fe6cecd6fd7f55951feac8c Mon Sep 17 00:00:00 2001 From: kpdecker Date: Tue, 4 Aug 2015 17:09:43 -0500 Subject: [PATCH 115/527] Run removeEmpty on all tokenize calls This is the most likely behavior that users will want. Should it not be desired for whatever reason, subclasses may override the removeEmpty field. Fixes #64 --- diff.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/diff.js b/diff.js index dae487df..ba1b063b 100644 --- a/diff.js +++ b/diff.js @@ -33,15 +33,6 @@ function clonePath(path) { return { newPos: path.newPos, components: path.components.slice(0) }; } - function removeEmpty(array) { - var ret = []; - for (var i = 0; i < array.length; i++) { - if (array[i]) { - ret.push(array[i]); - } - } - return ret; - } function escapeHTML(s) { var n = s; n = n.replace(/&/g, '&'); @@ -170,8 +161,8 @@ return done([{ value: newString, added: true }]); } - newString = this.tokenize(newString); - oldString = this.tokenize(oldString); + newString = this.removeEmpty(this.tokenize(newString)); + oldString = this.removeEmpty(this.tokenize(oldString)); var newLen = newString.length, oldLen = oldString.length; var editLength = 1; @@ -293,6 +284,15 @@ var reWhitespace = /\S/; return left === right || (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)); }, + removeEmpty: function(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + return ret; + }, tokenize: function(value) { return value.split(''); } @@ -303,12 +303,12 @@ var WordDiff = new Diff(true); var WordWithSpaceDiff = new Diff(); WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { - return removeEmpty(value.split(/(\s+|\b)/)); + return value.split(/(\s+|\b)/); }; var CssDiff = new Diff(true); CssDiff.tokenize = function(value) { - return removeEmpty(value.split(/([{}:;,]|\s+)/)); + return value.split(/([{}:;,]|\s+)/); }; var LineDiff = new Diff(); @@ -367,7 +367,7 @@ var SentenceDiff = new Diff(); SentenceDiff.tokenize = function(value) { - return removeEmpty(value.split(/(\S.+?[.!?])(?=\s+|$)/)); + return value.split(/(\S.+?[.!?])(?=\s+|$)/); }; var JsonDiff = new Diff(); From bcb87ec2f8383013bf530e653b264c23c518bd10 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Wed, 5 Aug 2015 12:58:52 -0500 Subject: [PATCH 116/527] Convert to webpack and babel built library --- .gitignore | 1 + Gruntfile.js | 90 +++++++++++++++++++++++++++++++++++++++ examples/node_example.js | 2 +- examples/web_example.html | 2 +- package.json | 19 +++++++-- diff.js => src/diff.js | 0 test/applyPatch.js | 2 +- test/canonicalize.js | 2 +- test/createPatch.js | 2 +- test/diffTest.js | 2 +- test/structuredPatch.js | 2 +- 11 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 Gruntfile.js rename diff.js => src/diff.js (100%) diff --git a/.gitignore b/.gitignore index df9af16b..b034aafe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ coverage node_modules npm-debug.log +dist \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..814174c4 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,90 @@ +/* esline-env node */ +/* eslint-disable no-process-env, camelcase */ +module.exports = function(grunt) { + + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + + clean: ['dist'], + + eslint: { + options: { + }, + files: [ + 'src/**/*.js', + 'test/**/*.js' + ] + }, + + clean: ['dist'], + + webpack: { + options: { + context: __dirname, + module: { + loaders: [ + // the optional 'runtime' transformer tells babel to require the runtime instead of inlining it. + { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader?optional=runtime&loose=es6.modules&auxiliaryCommentBefore=istanbul%20ignore%20next' } + ] + }, + output: { + path: 'dist/', + library: 'JsDiff', + libraryTarget: 'umd' + } + }, + dist: { + entry: './src/diff.js', + output: { + filename: 'diff.js' + } + } + }, + + mocha_istanbul: { + coverage: { + src: 'test' + } + }, + istanbul_check_coverage: { + 'default': { + options: { + coverageFolder: 'coverage*', // will check both coverage folders and merge the coverage results + check: { + statements: 100, + functions: 100, + branches: 100, + lines: 100 + } + } + } + }, + + watch: { + scripts: { + options: { + atBegin: true + }, + + files: ['src/**/*.js', 'test/**/*.js'], + tasks: ['build', 'test'] + } + } + }); + + // Build a new version of the library + this.registerTask('build', 'Builds a distributable version of the current project', ['eslint', 'webpack']); + this.registerTask('test', ['mocha_istanbul:coverage', 'istanbul_check_coverage']); + + // Load tasks from npm + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-eslint'); + grunt.loadNpmTasks('grunt-mocha-istanbul'); + grunt.loadNpmTasks('grunt-webpack'); + + grunt.registerTask('travis', 'default'); + + grunt.registerTask('dev', ['clean', 'watch']); + grunt.registerTask('default', ['clean', 'build', 'test']); +}; diff --git a/examples/node_example.js b/examples/node_example.js index 0c62ca1b..8e696caf 100644 --- a/examples/node_example.js +++ b/examples/node_example.js @@ -1,5 +1,5 @@ require('colors') -var jsdiff = require('../diff'); +var jsdiff = require('../'); var one = 'beep boop'; var other = 'beep boob blah'; diff --git a/examples/web_example.html b/examples/web_example.html index fc58df7e..7f2cb636 100644 --- a/examples/web_example.html +++ b/examples/web_example.html @@ -1,5 +1,5 @@

-
+
 
\ No newline at end of file
+
+

From d20f367576161b1a43e9c0d6d31542cdb3731fc8 Mon Sep 17 00:00:00 2001
From: Charlie Ozinga 
Date: Tue, 31 May 2016 12:25:47 -0600
Subject: [PATCH 232/527] Add a fix for applying 0-length destination patches

---
 src/patch/apply.js  |  1 +
 test/patch/apply.js | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/src/patch/apply.js b/src/patch/apply.js
index fda26ece..864d52da 100644
--- a/src/patch/apply.js
+++ b/src/patch/apply.js
@@ -81,6 +81,7 @@ export function applyPatch(source, uniDiff, options = {}) {
   for (let i = 0; i < hunks.length; i++) {
     let hunk = hunks[i],
         toPos = hunk.offset + hunk.newStart - 1;
+    if (hunk.newLines == 0) { toPos++; }
 
     for (let j = 0; j < hunk.lines.length; j++) {
       let line = hunk.lines[j],
diff --git a/test/patch/apply.js b/test/patch/apply.js
index dbf24277..3e278f61 100644
--- a/test/patch/apply.js
+++ b/test/patch/apply.js
@@ -416,6 +416,23 @@ describe('patch/apply', function() {
           + 'line5\n');
     });
 
+    it('should erase a file', function() {
+      expect(applyPatch(
+          'line1\n'
+          + 'line2\n'
+          + 'line3\n'
+          + 'line4\n',
+
+          '--- test\theader1\n'
+          + '+++ test\theader2\n'
+          + '@@ -1,4 +0,0 @@\n'
+          + '-line1\n'
+          + '-line2\n'
+          + '-line3\n'
+          + '-line4\n'))
+        .to.equal('');
+    });
+
     it('should allow custom line comparison', function() {
       expect(applyPatch(
           'line2\n'

From 06eaeee2711fcbda31e6c249a14ad75d7ba1cd5f Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Tue, 31 May 2016 15:06:24 -0500
Subject: [PATCH 233/527] Update release notes

---
 release-notes.md | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/release-notes.md b/release-notes.md
index 3172175c..9dfa3c0b 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -2,7 +2,14 @@
 
 ## Development
 
-[Commits](https://github.com/kpdecker/jsdiff/compare/v2.2.2...master)
+[Commits](https://github.com/kpdecker/jsdiff/compare/v2.2.3...master)
+
+## v2.2.3 - May 31st, 2016
+- [#118](https://github.com/kpdecker/jsdiff/pull/118) - Add a fix for applying 0-length destination patches ([@chaaz](https://api.github.com/users/chaaz))
+- [#115](https://github.com/kpdecker/jsdiff/pull/115) - Fixed grammar in README ([@krizalys](https://api.github.com/users/krizalys))
+- [#113](https://github.com/kpdecker/jsdiff/pull/113) - fix typo ([@vmazare](https://api.github.com/users/vmazare))
+
+[Commits](https://github.com/kpdecker/jsdiff/compare/v2.2.2...v2.2.3)
 
 ## v2.2.2 - March 13th, 2016
 - [#102](https://github.com/kpdecker/jsdiff/issues/102) - diffJson with dates, returns empty curly braces  ([@dr-dimitru](https://api.github.com/users/dr-dimitru))

From ec007c364e88c37ccc6b3f94a4cfe163d8389b85 Mon Sep 17 00:00:00 2001
From: kpdecker 
Date: Tue, 31 May 2016 15:06:36 -0500
Subject: [PATCH 234/527] v2.2.3

---
 components/bower.json     | 2 +-
 components/component.json | 2 +-
 package.json              | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/components/bower.json b/components/bower.json
index efdfba2c..7a8404b3 100644
--- a/components/bower.json
+++ b/components/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "jsdiff",
-  "version": "2.2.2",
+  "version": "2.2.3",
   "main": [
     "diff.js"
   ],
diff --git a/components/component.json b/components/component.json
index ce9dd4e0..e90ac11d 100644
--- a/components/component.json
+++ b/components/component.json
@@ -6,7 +6,7 @@
     "diff",
     "text"
   ],
-  "version": "2.2.2",
+  "version": "2.2.3",
   "scripts": [ "diff.js" ],
   "main":  "diff.js",
   "license": "BSD"
diff --git a/package.json b/package.json
index 3194fcd0..f0466532 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "diff",
-  "version": "2.2.2",
+  "version": "2.2.3",
   "description": "A javascript text diff implementation.",
   "keywords": [
     "diff",

From 6c829046e2a41ab363a3178d2c425dcc1a6840c2 Mon Sep 17 00:00:00 2001
From: wifiextender 
Date: Thu, 2 Jun 2016 02:06:52 +0200
Subject: [PATCH 235/527] Do single reflow

---
 README.md                 | 19 ++++++++++++-------
 examples/web_example.html | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index 39842cc2..dab7189d 100644
--- a/README.md
+++ b/README.md
@@ -156,23 +156,28 @@ Basic example in a web page
 

 
 
 ```
 
diff --git a/examples/web_example.html b/examples/web_example.html
index 7a5f52e9..c4c1ed42 100644
--- a/examples/web_example.html
+++ b/examples/web_example.html
@@ -1,22 +1,28 @@