A tumblelog CMS built on AJAX, PHP and MySQL.

diff.js 42KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. // Diff_Match_Patch v1.3
  2. // Computes the difference between two texts to create a patch.
  3. // Applies the patch onto another text, allowing for errors.
  4. // Copyright (C) 2006 Neil Fraser
  5. // http://neil.fraser.name/software/diff_match_patch/
  6. // This program is free software; you can redistribute it and/or
  7. // modify it under the terms of the GNU General Public License
  8. // as published by the Free Software Foundation.
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License (www.gnu.org) for more details.
  13. // Constants.
  14. // Redefine these in your program to override the defaults.
  15. // Number of seconds to map a diff before giving up. (0 for infinity)
  16. var DIFF_TIMEOUT = 1.0;
  17. // Cost of an empty edit operation in terms of edit characters.
  18. var DIFF_EDIT_COST = 4;
  19. // Tweak the relative importance (0.0 = accuracy, 1.0 = proximity)
  20. var MATCH_BALANCE = 0.5;
  21. // At what point is no match declared (0.0 = perfection, 1.0 = very loose)
  22. var MATCH_THRESHOLD = 0.5;
  23. // The min and max cutoffs used when computing text lengths.
  24. var MATCH_MINLENGTH = 100;
  25. var MATCH_MAXLENGTH = 1000;
  26. // Chunk size for context length.
  27. var PATCH_MARGIN = 4;
  28. //////////////////////////////////////////////////////////////////////
  29. // Diff //
  30. //////////////////////////////////////////////////////////////////////
  31. // The data structure representing a diff is an array of tuples:
  32. // [[-1, "Hello"], [1, "Goodbye"], [0, " world."]]
  33. // which means: delete "Hello", add "Goodbye" and keep " world."
  34. function diff_main(text1, text2, checklines) {
  35. // Find the differences between two texts. Return an array of changes.
  36. // If checklines is present and false, then don't run a line-level diff first to identify the changed areas.
  37. // Check for equality (speedup)
  38. if (text1 == text2)
  39. return [[0, text1]];
  40. if (typeof checklines == 'undefined')
  41. checklines = true;
  42. var a;
  43. // Trim off common prefix (speedup)
  44. a = diff_prefix(text1, text2);
  45. text1 = a[0];
  46. text2 = a[1];
  47. var commonprefix = a[2];
  48. // Trim off common suffix (speedup)
  49. a = diff_suffix(text1, text2);
  50. text1 = a[0];
  51. text2 = a[1];
  52. var commonsuffix = a[2];
  53. var diff, i;
  54. var longtext = text1.length > text2.length ? text1 : text2;
  55. var shorttext = text1.length > text2.length ? text2 : text1;
  56. if (!text1) { // Just add some text (speedup)
  57. diff = [[1, text2]];
  58. } else if (!text2) { // Just delete some text (speedup)
  59. diff = [[-1, text1]];
  60. } else if ((i = longtext.indexOf(shorttext)) != -1) {
  61. // Shorter text is inside the longer text (speedup)
  62. diff = [[1, longtext.substring(0, i)], [0, shorttext], [1, longtext.substring(i+shorttext.length)]];
  63. // Swap insertions for deletions if diff is reversed.
  64. if (text1.length > text2.length)
  65. diff[0][0] = diff[2][0] = -1;
  66. } else {
  67. longtext = shorttext = null; // Garbage collect
  68. // Check to see if the problem can be split in two.
  69. var hm = diff_halfmatch(text1, text2);
  70. if (hm) {
  71. // A half-match was found, sort out the return data.
  72. var text1_a = hm[0];
  73. var text1_b = hm[1];
  74. var text2_a = hm[2];
  75. var text2_b = hm[3];
  76. var mid_common = hm[4];
  77. // Send both pairs off for separate processing.
  78. var diff_a = diff_main(text1_a, text2_a, checklines);
  79. var diff_b = diff_main(text1_b, text2_b, checklines);
  80. // Merge the results.
  81. diff = diff_a.concat([[0, mid_common]], diff_b);
  82. } else {
  83. // Perform a real diff.
  84. if (checklines && text1.length + text2.length < 250)
  85. checklines = false; // Too trivial for the overhead.
  86. if (checklines) {
  87. // Scan the text on a line-by-line basis first.
  88. a = diff_lines2chars(text1, text2);
  89. text1 = a[0];
  90. text2 = a[1];
  91. var linearray = a[2];
  92. }
  93. diff = diff_map(text1, text2);
  94. if (!diff) // No acceptable result.
  95. diff = [[-1, text1], [1, text2]];
  96. if (checklines) {
  97. diff_chars2lines(diff, linearray); // Convert the diff back to original text.
  98. diff_cleanup_semantic(diff); // Eliminate freak matches (e.g. blank lines)
  99. // Rediff any replacement blocks, this time on character-by-character basis.
  100. diff.push([0, '']); // Add a dummy entry at the end.
  101. var pointer = 0;
  102. var count_delete = 0;
  103. var count_insert = 0;
  104. var text_delete = '';
  105. var text_insert = '';
  106. while(pointer < diff.length) {
  107. if (diff[pointer][0] == 1) {
  108. count_insert++;
  109. text_insert += diff[pointer][1];
  110. } else if (diff[pointer][0] == -1) {
  111. count_delete++;
  112. text_delete += diff[pointer][1];
  113. } else { // Upon reaching an equality, check for prior redundancies.
  114. if (count_delete >= 1 && count_insert >= 1) {
  115. // Delete the offending records and add the merged ones.
  116. a = diff_main(text_delete, text_insert, false);
  117. diff.splice(pointer - count_delete - count_insert, count_delete + count_insert);
  118. pointer = pointer - count_delete - count_insert;
  119. for (i=a.length-1; i>=0; i--)
  120. diff.splice(pointer, 0, a[i]);
  121. pointer = pointer + a.length;
  122. }
  123. count_insert = 0;
  124. count_delete = 0;
  125. text_delete = '';
  126. text_insert = '';
  127. }
  128. pointer++;
  129. }
  130. diff.pop(); // Remove the dummy entry at the end.
  131. }
  132. }
  133. }
  134. if (commonprefix)
  135. diff.unshift([0, commonprefix]);
  136. if (commonsuffix)
  137. diff.push([0, commonsuffix]);
  138. diff_cleanup_merge(diff);
  139. return diff;
  140. }
  141. function diff_lines2chars(text1, text2) {
  142. // Split text into an array of strings.
  143. // Reduce the texts to a string of hashes where each character represents one line.
  144. var linearray = new Array(); // linearray[4] == "Hello\n"
  145. var linehash = new Object(); // linehash["Hello\n"] == 4
  146. // "\x00" is a valid JavaScript character, but the Venkman debugger doesn't like it (bug 335098)
  147. // So we'll insert a junk entry to avoid generating a null character.
  148. linearray.push('');
  149. function diff_lines2chars_munge(text) {
  150. // My first ever closure!
  151. var i, line;
  152. var chars = '';
  153. while (text) {
  154. i = text.indexOf('\n');
  155. if (i == -1)
  156. i = text.length;
  157. line = text.substring(0, i+1);
  158. text = text.substring(i+1);
  159. if (linehash.hasOwnProperty ? linehash.hasOwnProperty(line) : (linehash[line] !== undefined)) {
  160. chars += String.fromCharCode(linehash[line]);
  161. } else {
  162. linearray.push(line);
  163. linehash[line] = linearray.length - 1;
  164. chars += String.fromCharCode(linearray.length - 1);
  165. }
  166. }
  167. return chars;
  168. }
  169. var chars1 = diff_lines2chars_munge(text1);
  170. var chars2 = diff_lines2chars_munge(text2);
  171. return [chars1, chars2, linearray];
  172. }
  173. function diff_chars2lines(diff, linearray) {
  174. // Rehydrate the text in a diff from a string of line hashes to real lines of text.
  175. var chars, text;
  176. for (var x=0; x<diff.length; x++) {
  177. chars = diff[x][1];
  178. text = '';
  179. for (var y=0; y<chars.length; y++)
  180. text += linearray[chars.charCodeAt(y)];
  181. diff[x][1] = text;
  182. }
  183. }
  184. function diff_map(text1, text2) {
  185. // Explore the intersection points between the two texts.
  186. var now = new Date();
  187. var ms_end = now.getTime() + DIFF_TIMEOUT * 1000; // Don't run for too long.
  188. var max = (text1.length + text2.length) / 2;
  189. var v_map1 = new Array();
  190. var v_map2 = new Array();
  191. var v1 = new Object();
  192. var v2 = new Object();
  193. v1[1] = 0;
  194. v2[1] = 0;
  195. var x, y;
  196. var footstep; // Used to track overlapping paths.
  197. var footsteps = new Object();
  198. var done = false;
  199. var hasOwnProperty = !!(footsteps.hasOwnProperty);
  200. // If the total number of characters is odd, then the front path will collide with the reverse path.
  201. var front = (text1.length + text2.length) % 2;
  202. for (var d=0; d<max; d++) {
  203. now = new Date();
  204. if (DIFF_TIMEOUT > 0 && now.getTime() > ms_end) // Timeout reached
  205. return null;
  206. // Walk the front path one step.
  207. v_map1[d] = new Object();
  208. for (var k=-d; k<=d; k+=2) {
  209. if (k == -d || k != d && v1[k-1] < v1[k+1])
  210. x = v1[k+1];
  211. else
  212. x = v1[k-1]+1;
  213. y = x - k;
  214. footstep = x+","+y;
  215. if (front && (hasOwnProperty ? footsteps.hasOwnProperty(footstep) : (footsteps[footstep] !== undefined)))
  216. done = true;
  217. if (!front)
  218. footsteps[footstep] = d;
  219. while (!done && x < text1.length && y < text2.length && text1.charAt(x) == text2.charAt(y)) {
  220. x++; y++;
  221. footstep = x+","+y;
  222. if (front && (hasOwnProperty ? footsteps.hasOwnProperty(footstep) : (footsteps[footstep] !== undefined)))
  223. done = true;
  224. if (!front)
  225. footsteps[footstep] = d;
  226. }
  227. v1[k] = x;
  228. v_map1[d][x+","+y] = true;
  229. if (done) {
  230. // Front path ran over reverse path.
  231. v_map2 = v_map2.slice(0, footsteps[footstep]+1);
  232. var a = diff_path1(v_map1, text1.substring(0, x), text2.substring(0, y));
  233. return a.concat(diff_path2(v_map2, text1.substring(x), text2.substring(y)));
  234. }
  235. }
  236. // Walk the reverse path one step.
  237. v_map2[d] = new Object();
  238. for (var k=-d; k<=d; k+=2) {
  239. if (k == -d || k != d && v2[k-1] < v2[k+1])
  240. x = v2[k+1];
  241. else
  242. x = v2[k-1]+1;
  243. y = x - k;
  244. footstep = (text1.length-x)+","+(text2.length-y);
  245. if (!front && (hasOwnProperty ? footsteps.hasOwnProperty(footstep) : (footsteps[footstep] !== undefined)))
  246. done = true;
  247. if (front)
  248. footsteps[footstep] = d;
  249. while (!done && x < text1.length && y < text2.length && text1.charAt(text1.length-x-1) == text2.charAt(text2.length-y-1)) {
  250. x++; y++;
  251. footstep = (text1.length-x)+","+(text2.length-y);
  252. if (!front && (hasOwnProperty ? footsteps.hasOwnProperty(footstep) : (footsteps[footstep] !== undefined)))
  253. done = true;
  254. if (front)
  255. footsteps[footstep] = d;
  256. }
  257. v2[k] = x;
  258. v_map2[d][x+","+y] = true;
  259. if (done) {
  260. // Reverse path ran over front path.
  261. v_map1 = v_map1.slice(0, footsteps[footstep]+1);
  262. var a = diff_path1(v_map1, text1.substring(0, text1.length-x), text2.substring(0, text2.length-y));
  263. return a.concat(diff_path2(v_map2, text1.substring(text1.length-x), text2.substring(text2.length-y)));
  264. }
  265. }
  266. }
  267. // Number of diffs equals number of characters, no commonality at all.
  268. return null;
  269. }
  270. function diff_path1(v_map, text1, text2) {
  271. // Work from the middle back to the start to determine the path.
  272. var path = [];
  273. var x = text1.length;
  274. var y = text2.length;
  275. var last_op = null;
  276. for (var d=v_map.length-2; d>=0; d--) {
  277. while(1) {
  278. if (v_map[d].hasOwnProperty ? v_map[d].hasOwnProperty((x-1)+","+y) : (v_map[d][(x-1)+","+y] !== undefined)) {
  279. x--;
  280. if (last_op === -1)
  281. path[0][1] = text1.charAt(x) + path[0][1];
  282. else
  283. path.unshift([-1, text1.charAt(x)]);
  284. last_op = -1;
  285. break;
  286. } else if (v_map[d].hasOwnProperty ? v_map[d].hasOwnProperty(x+","+(y-1)) : (v_map[d][x+","+(y-1)] !== undefined)) {
  287. y--;
  288. if (last_op === 1)
  289. path[0][1] = text2.charAt(y) + path[0][1];
  290. else
  291. path.unshift([1, text2.charAt(y)]);
  292. last_op = 1;
  293. break;
  294. } else {
  295. x--;
  296. y--;
  297. //if (text1.charAt(x) != text2.charAt(y))
  298. // return alert("No diagonal. Can't happen. (diff_path1)");
  299. if (last_op === 0)
  300. path[0][1] = text1.charAt(x) + path[0][1];
  301. else
  302. path.unshift([0, text1.charAt(x)]);
  303. last_op = 0;
  304. }
  305. }
  306. }
  307. return path;
  308. }
  309. function diff_path2(v_map, text1, text2) {
  310. // Work from the middle back to the end to determine the path.
  311. var path = [];
  312. var x = text1.length;
  313. var y = text2.length;
  314. var last_op = null;
  315. for (var d=v_map.length-2; d>=0; d--) {
  316. while(1) {
  317. if (v_map[d].hasOwnProperty ? v_map[d].hasOwnProperty((x-1)+","+y) : (v_map[d][(x-1)+","+y] !== undefined)) {
  318. x--;
  319. if (last_op === -1)
  320. path[path.length-1][1] += text1.charAt(text1.length-x-1);
  321. else
  322. path.push([-1, text1.charAt(text1.length-x-1)]);
  323. last_op = -1;
  324. break;
  325. } else if (v_map[d].hasOwnProperty ? v_map[d].hasOwnProperty(x+","+(y-1)) : (v_map[d][x+","+(y-1)] !== undefined)) {
  326. y--;
  327. if (last_op === 1)
  328. path[path.length-1][1] += text2.charAt(text2.length-y-1);
  329. else
  330. path.push([1, text2.charAt(text2.length-y-1)]);
  331. last_op = 1;
  332. break;
  333. } else {
  334. x--;
  335. y--;
  336. //if (text1.charAt(text1.length-x-1) != text2.charAt(text2.length-y-1))
  337. // return alert("No diagonal. Can't happen. (diff_path2)");
  338. if (last_op === 0)
  339. path[path.length-1][1] += text1.charAt(text1.length-x-1);
  340. else
  341. path.push([0, text1.charAt(text1.length-x-1)]);
  342. last_op = 0;
  343. }
  344. }
  345. }
  346. return path;
  347. }
  348. function diff_prefix(text1, text2) {
  349. // Trim off common prefix
  350. var pointermin = 0;
  351. var pointermax = Math.min(text1.length, text2.length);
  352. var pointermid = pointermax;
  353. while(pointermin < pointermid) {
  354. if (text1.substring(0, pointermid) == text2.substring(0, pointermid))
  355. pointermin = pointermid;
  356. else
  357. pointermax = pointermid;
  358. pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  359. }
  360. var commonprefix = text1.substring(0, pointermid);
  361. text1 = text1.substring(pointermid);
  362. text2 = text2.substring(pointermid);
  363. return [text1, text2, commonprefix];
  364. }
  365. function diff_suffix(text1, text2) {
  366. // Trim off common suffix
  367. var pointermin = 0;
  368. var pointermax = Math.min(text1.length, text2.length);
  369. var pointermid = pointermax;
  370. while(pointermin < pointermid) {
  371. if (text1.substring(text1.length-pointermid) == text2.substring(text2.length-pointermid))
  372. pointermin = pointermid;
  373. else
  374. pointermax = pointermid;
  375. pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  376. }
  377. var commonsuffix = text1.substring(text1.length-pointermid);
  378. text1 = text1.substring(0, text1.length-pointermid);
  379. text2 = text2.substring(0, text2.length-pointermid);
  380. return [text1, text2, commonsuffix];
  381. }
  382. function diff_halfmatch(text1, text2) {
  383. // Do the two texts share a substring which is at least half the length of the longer text?
  384. var longtext = text1.length > text2.length ? text1 : text2;
  385. var shorttext = text1.length > text2.length ? text2 : text1;
  386. if (longtext.length < 10 || shorttext.length < 1)
  387. return null; // Pointless.
  388. function diff_halfmatch_i(longtext, shorttext, i) {
  389. // Start with a 1/4 length substring at position i as a seed.
  390. var seed = longtext.substring(i, i+Math.floor(longtext.length/4));
  391. var j = -1;
  392. var best_common = '';
  393. var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b;
  394. while ((j = shorttext.indexOf(seed, j+1)) != -1) {
  395. var my_prefix = diff_prefix(longtext.substring(i), shorttext.substring(j));
  396. var my_suffix = diff_suffix(longtext.substring(0, i), shorttext.substring(0, j));
  397. if (best_common.length < (my_suffix[2] + my_prefix[2]).length) {
  398. best_common = my_suffix[2] + my_prefix[2];
  399. best_longtext_a = my_suffix[0];
  400. best_longtext_b = my_prefix[0];
  401. best_shorttext_a = my_suffix[1];
  402. best_shorttext_b = my_prefix[1];
  403. }
  404. }
  405. if (best_common.length >= longtext.length/2)
  406. return [best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b, best_common];
  407. else
  408. return null;
  409. }
  410. // First check if the second quarter is the seed for a half-match.
  411. var hm1 = diff_halfmatch_i(longtext, shorttext, Math.ceil(longtext.length/4));
  412. // Check again based on the third quarter.
  413. var hm2 = diff_halfmatch_i(longtext, shorttext, Math.ceil(longtext.length/2));
  414. var hm;
  415. if (!hm1 && !hm2)
  416. return null;
  417. else if (!hm2)
  418. hm = hm1;
  419. else if (!hm1)
  420. hm = hm2;
  421. else // Both matched. Select the longest.
  422. hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
  423. // A half-match was found, sort out the return data.
  424. if (text1.length > text2.length) {
  425. var text1_a = hm[0];
  426. var text1_b = hm[1];
  427. var text2_a = hm[2];
  428. var text2_b = hm[3];
  429. } else {
  430. var text2_a = hm[0];
  431. var text2_b = hm[1];
  432. var text1_a = hm[2];
  433. var text1_b = hm[3];
  434. }
  435. var mid_common = hm[4];
  436. return [text1_a, text1_b, text2_a, text2_b, mid_common];
  437. }
  438. function diff_cleanup_semantic(diff) {
  439. // Reduce the number of edits by eliminating semantically trivial equalities.
  440. var changes = false;
  441. var equalities = []; // Stack of indices where equalities are found.
  442. var lastequality = null; // Always equal to equalities[equalities.length-1][1]
  443. var pointer = 0; // Index of current position.
  444. var length_changes1 = 0; // Number of characters that changed prior to the equality.
  445. var length_changes2 = 0; // Number of characters that changed after the equality.
  446. while (pointer < diff.length) {
  447. if (diff[pointer][0] == 0) { // equality found
  448. equalities.push(pointer);
  449. length_changes1 = length_changes2;
  450. length_changes2 = 0;
  451. lastequality = diff[pointer][1];
  452. } else { // an insertion or deletion
  453. length_changes2 += diff[pointer][1].length;
  454. if (lastequality != null && (lastequality.length <= length_changes1) && (lastequality.length <= length_changes2)) {
  455. //alert("Splitting: '"+lastequality+"'");
  456. diff.splice(equalities[equalities.length-1], 0, [-1, lastequality]); // Duplicate record
  457. diff[equalities[equalities.length-1]+1][0] = 1; // Change second copy to insert.
  458. equalities.pop(); // Throw away the equality we just deleted;
  459. equalities.pop(); // Throw away the previous equality;
  460. pointer = equalities.length ? equalities[equalities.length-1] : -1;
  461. length_changes1 = 0; // Reset the counters.
  462. length_changes2 = 0;
  463. lastequality = null;
  464. changes = true;
  465. }
  466. }
  467. pointer++;
  468. }
  469. if (changes)
  470. diff_cleanup_merge(diff);
  471. }
  472. function diff_cleanup_efficiency(diff) {
  473. // Reduce the number of edits by eliminating operationally trivial equalities.
  474. var changes = false;
  475. var equalities = []; // Stack of indices where equalities are found.
  476. var lastequality = ''; // Always equal to equalities[equalities.length-1][1]
  477. var pointer = 0; // Index of current position.
  478. var pre_ins = false; // Is there an insertion operation before the last equality.
  479. var pre_del = false; // Is there an deletion operation before the last equality.
  480. var post_ins = false; // Is there an insertion operation after the last equality.
  481. var post_del = false; // Is there an deletion operation after the last equality.
  482. while (pointer < diff.length) {
  483. if (diff[pointer][0] == 0) { // equality found
  484. if (diff[pointer][1].length < DIFF_EDIT_COST && (post_ins || post_del)) {
  485. // Candidate found.
  486. equalities.push(pointer);
  487. pre_ins = post_ins;
  488. pre_del = post_del;
  489. lastequality = diff[pointer][1];
  490. } else {
  491. // Not a candidate, and can never become one.
  492. equalities = [];
  493. lastequality = '';
  494. }
  495. post_ins = post_del = false;
  496. } else { // an insertion or deletion
  497. if (diff[pointer][0] == -1)
  498. post_del = true;
  499. else
  500. post_ins = true;
  501. // Five types to be split:
  502. // <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
  503. // <ins>A</ins>X<ins>C</ins><del>D</del>
  504. // <ins>A</ins><del>B</del>X<ins>C</ins>
  505. // <ins>A</del>X<ins>C</ins><del>D</del>
  506. // <ins>A</ins><del>B</del>X<del>C</del>
  507. if (lastequality && ((pre_ins && pre_del && post_ins && post_del) || ((lastequality.length < DIFF_EDIT_COST/2) && (pre_ins + pre_del + post_ins + post_del) == 3))) {
  508. //alert("Splitting: '"+lastequality+"'");
  509. diff.splice(equalities[equalities.length-1], 0, [-1, lastequality]); // Duplicate record
  510. diff[equalities[equalities.length-1]+1][0] = 1; // Change second copy to insert.
  511. equalities.pop(); // Throw away the equality we just deleted;
  512. lastequality = '';
  513. if (pre_ins && pre_del) {
  514. // No changes made which could affect previous entry, keep going.
  515. post_ins = post_del = true;
  516. equalities = [];
  517. } else {
  518. equalities.pop(); // Throw away the previous equality;
  519. pointer = equalities.length ? equalities[equalities.length-1] : -1;
  520. post_ins = post_del = false;
  521. }
  522. changes = true;
  523. }
  524. }
  525. pointer++;
  526. }
  527. if (changes)
  528. diff_cleanup_merge(diff);
  529. }
  530. function diff_cleanup_merge(diff) {
  531. // Reorder and merge like edit sections. Merge equalities.
  532. // Any edit section can move as long as it doesn't cross an equality.
  533. diff.push([0, '']); // Add a dummy entry at the end.
  534. var pointer = 0;
  535. var count_delete = 0;
  536. var count_insert = 0;
  537. var text_delete = '';
  538. var text_insert = '';
  539. var record_insert, record_delete;
  540. var my_xfix;
  541. while(pointer < diff.length) {
  542. if (diff[pointer][0] == 1) {
  543. count_insert++;
  544. text_insert += diff[pointer][1];
  545. pointer++;
  546. } else if (diff[pointer][0] == -1) {
  547. count_delete++;
  548. text_delete += diff[pointer][1];
  549. pointer++;
  550. } else { // Upon reaching an equality, check for prior redundancies.
  551. if (count_delete > 1 || count_insert > 1) {
  552. if (count_delete > 1 && count_insert > 1) {
  553. // Factor out any common prefixies.
  554. my_xfix = diff_prefix(text_insert, text_delete);
  555. if (my_xfix[2] != '') {
  556. if ((pointer - count_delete - count_insert) > 0 && diff[pointer - count_delete - count_insert - 1][0] == 0) {
  557. text_insert = my_xfix[0];
  558. text_delete = my_xfix[1];
  559. diff[pointer - count_delete - count_insert - 1][1] += my_xfix[2];
  560. }
  561. }
  562. // Factor out any common suffixies.
  563. my_xfix = diff_suffix(text_insert, text_delete);
  564. if (my_xfix[2] != '') {
  565. text_insert = my_xfix[0];
  566. text_delete = my_xfix[1];
  567. diff[pointer][1] = my_xfix[2] + diff[pointer][1];
  568. }
  569. }
  570. // Delete the offending records and add the merged ones.
  571. if (count_delete == 0)
  572. diff.splice(pointer - count_delete - count_insert, count_delete + count_insert, [1, text_insert]);
  573. else if (count_insert == 0)
  574. diff.splice(pointer - count_delete - count_insert, count_delete + count_insert, [-1, text_delete]);
  575. else
  576. diff.splice(pointer - count_delete - count_insert, count_delete + count_insert, [-1, text_delete], [1, text_insert]);
  577. pointer = pointer - count_delete - count_insert + (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1;
  578. } else if (pointer != 0 && diff[pointer-1][0] == 0) {
  579. // Merge this equality with the previous one.
  580. diff[pointer-1][1] += diff[pointer][1];
  581. diff.splice(pointer, 1);
  582. } else {
  583. pointer++;
  584. }
  585. count_insert = 0;
  586. count_delete = 0;
  587. text_delete = '';
  588. text_insert = '';
  589. }
  590. }
  591. if (diff[diff.length-1][1] == '')
  592. diff.pop(); // Remove the dummy entry at the end.
  593. }
  594. function diff_addindex(diff) {
  595. // Add an index to each tuple, represents where the tuple is located in text2.
  596. // e.g. [[-1, 'h', 0], [1, 'c', 0], [0, 'at', 1]]
  597. var i = 0;
  598. for (var x=0; x<diff.length; x++) {
  599. diff[x].push(i);
  600. if (diff[x][0] != -1)
  601. i += diff[x][1].length;
  602. }
  603. }
  604. function diff_xindex(diff, loc) {
  605. // loc is a location in text1, compute and return the equivalent location in text2.
  606. // e.g. "The cat" vs "The big cat", 1->1, 5->8
  607. var chars1 = 0;
  608. var chars2 = 0;
  609. var last_chars1 = 0;
  610. var last_chars2 = 0;
  611. for (var x=0; x<diff.length; x++) {
  612. if (diff[x][0] != 1) // Equality or deletion.
  613. chars1 += diff[x][1].length;
  614. if (diff[x][0] != -1) // Equality or insertion.
  615. chars2 += diff[x][1].length;
  616. if (chars1 > loc) // Overshot the location.
  617. break;
  618. last_chars1 = chars1;
  619. last_chars2 = chars2;
  620. }
  621. if (diff.length != x && diff[x][0] == -1) // The location was deleted.
  622. return last_chars2;
  623. // Add the remaining character length.
  624. return last_chars2 + (loc - last_chars1);
  625. }
  626. function diff_prettyhtml(diff) {
  627. // Convert a diff array into a pretty HTML report.
  628. diff_addindex(diff);
  629. var html = '';
  630. for (var x=0; x<diff.length; x++) {
  631. var m = diff[x][0]; // Mode (-1=delete, 0=copy, 1=add)
  632. var t = diff[x][1]; // Text of change.
  633. var i = diff[x][2]; // Index of change.
  634. t = t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  635. t = t.replace(/\n/g, "&para;<BR>");
  636. if (m == -1)
  637. html += "<DEL STYLE='background:#FFE6E6;' TITLE='i="+i+"'>"+t+"</DEL>";
  638. else if (m == 1)
  639. html += "<INS STYLE='background:#E6FFE6;' TITLE='i="+i+"'>"+t+"</INS>";
  640. else
  641. html += "<SPAN TITLE='i="+i+"'>"+t+"</SPAN>";
  642. }
  643. return html;
  644. }
  645. //////////////////////////////////////////////////////////////////////
  646. // Match //
  647. //////////////////////////////////////////////////////////////////////
  648. function match_getmaxbits() {
  649. // Compute the number of bits in an int.
  650. // The normal answer for JavaScript is 32.
  651. var maxbits = 0;
  652. var oldi = 1;
  653. var newi = 2;
  654. while (oldi != newi) {
  655. maxbits++;
  656. oldi = newi;
  657. newi = newi << 1;
  658. }
  659. return maxbits;
  660. }
  661. var MATCH_MAXBITS = match_getmaxbits();
  662. function match_main(text, pattern, loc) {
  663. // Locate the best instance of 'pattern' in 'text' near 'loc'.
  664. loc = Math.max(0, Math.min(loc, text.length-pattern.length));
  665. if (text == pattern) {
  666. // Shortcut (potentially not guaranteed by the algorithm)
  667. return 0;
  668. } else if (text.length == 0) {
  669. // Nothing to match.
  670. return null;
  671. } else if (text.substring(loc, loc + pattern.length) == pattern) {
  672. // Perfect match at the perfect spot! (Includes case of null pattern)
  673. return loc;
  674. } else {
  675. // Do a fuzzy compare.
  676. var match = match_bitap(text, pattern, loc);
  677. return match;
  678. }
  679. }
  680. function match_bitap(text, pattern, loc) {
  681. // Locate the best instance of 'pattern' in 'text' near 'loc' using the Bitap algorithm.
  682. if (pattern.length > MATCH_MAXBITS)
  683. return alert("Pattern too long for this browser.");
  684. // Initialise the alphabet.
  685. var s = match_alphabet(pattern);
  686. var score_text_length = text.length;
  687. // Coerce the text length between reasonable maximums and minimums.
  688. score_text_length = Math.max(score_text_length, MATCH_MINLENGTH);
  689. score_text_length = Math.min(score_text_length, MATCH_MAXLENGTH);
  690. function match_bitap_score (e, x) {
  691. // Compute and return the score for a match with e errors and x location.
  692. var d = Math.abs(loc-x);
  693. return (e / pattern.length / MATCH_BALANCE) + (d / score_text_length / (1.0 - MATCH_BALANCE));
  694. }
  695. // Highest score beyond which we give up.
  696. var score_threshold = MATCH_THRESHOLD;
  697. // Is there a nearby exact match? (speedup)
  698. var best_loc = text.indexOf(pattern, loc);
  699. if (best_loc != -1)
  700. score_threshold = Math.min(match_bitap_score(0, best_loc), score_threshold);
  701. // What about in the other direction? (speedup)
  702. best_loc = text.lastIndexOf(pattern, loc+pattern.length);
  703. if (best_loc != -1)
  704. score_threshold = Math.min(match_bitap_score(0, best_loc), score_threshold);
  705. // Initialise the bit arrays.
  706. var r = Array();
  707. var d = -1;
  708. var matchmask = Math.pow(2, pattern.length-1);
  709. best_loc = null;
  710. var bin_min, bin_mid;
  711. var bin_max = Math.max(loc+loc, text.length);
  712. var last_rd;
  713. for (var d=0; d<pattern.length; d++) {
  714. // Scan for the best match; each iteration allows for one more error.
  715. var rd = Array(text.length);
  716. // Run a binary search to determine how far from 'loc' we can stray at this error level.
  717. bin_min = loc;
  718. bin_mid = bin_max;
  719. while(bin_min < bin_mid) {
  720. if (match_bitap_score(d, bin_mid) < score_threshold)
  721. bin_min = bin_mid;
  722. else
  723. bin_max = bin_mid;
  724. bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min);
  725. }
  726. bin_max = bin_mid; // Use the result from this iteration as the maximum for the next.
  727. var start = Math.max(0, loc - (bin_mid - loc) - 1);
  728. var finish = Math.min(text.length-1, pattern.length + bin_mid);
  729. if (text.charAt(finish) == pattern.charAt(pattern.length-1))
  730. rd[finish] = Math.pow(2, d+1)-1;
  731. else
  732. rd[finish] = Math.pow(2, d)-1;
  733. for (var j=finish-1; j>=start; j--) {
  734. // The alphabet (s) is a sparse hash, so the following lines generate warnings.
  735. if (d == 0) // First pass: exact match.
  736. rd[j] = ((rd[j+1] << 1) | 1) & s[text.charAt(j)];
  737. else // Subsequent passes: fuzzy match.
  738. rd[j] = ((rd[j+1] << 1) | 1) & s[text.charAt(j)] | ((last_rd[j+1] << 1) | 1) | ((last_rd[j] << 1) | 1) | last_rd[j+1];
  739. if (rd[j] & matchmask) {
  740. var score = match_bitap_score(d, j);
  741. // This match will almost certainly be better than any existing match. But check anyway.
  742. if (score <= score_threshold) {
  743. // Told you so.
  744. score_threshold = score;
  745. best_loc = j;
  746. if (j > loc) {
  747. // When passing loc, don't exceed our current distance from loc.
  748. start = Math.max(0, loc - (j - loc));
  749. } else {
  750. // Already passed loc, downhill from here on in.
  751. break;
  752. }
  753. }
  754. }
  755. }
  756. if (match_bitap_score(d+1, loc) > score_threshold) // No hope for a (better) match at greater error levels.
  757. break;
  758. last_rd = rd;
  759. }
  760. return best_loc;
  761. }
  762. function match_alphabet(pattern) {
  763. // Initialise the alphabet for the Bitap algorithm.
  764. var s = Object();
  765. for (var i=0; i<pattern.length; i++)
  766. s[pattern.charAt(i)] = 0;
  767. for (var i=0; i<pattern.length; i++)
  768. s[pattern.charAt(i)] |= Math.pow(2, pattern.length-i-1);
  769. return s;
  770. }
  771. //////////////////////////////////////////////////////////////////////
  772. // Patch //
  773. //////////////////////////////////////////////////////////////////////
  774. function patch_obj() {
  775. // Constructor for a patch object.
  776. this.diffs = [];
  777. this.start1 = null;
  778. this.start2 = null;
  779. this.length1 = 0;
  780. this.length2 = 0;
  781. this.toString = function() {
  782. // Emmulate GNU diff's format.
  783. // Header: @@ -382,8 +481,9 @@
  784. // Indicies are printed as 1-based, not 0-based.
  785. var coords1, coords2;
  786. if (this.length1 == 0)
  787. coords1 = this.start1+",0";
  788. else if (this.length1 == 1)
  789. coords1 = this.start1+1;
  790. else
  791. coords1 = (this.start1+1)+","+this.length1;
  792. if (this.length2 == 0)
  793. coords2 = this.start2+",0";
  794. else if (this.length2 == 1)
  795. coords2 = this.start2+1;
  796. else
  797. coords2 = (this.start2+1)+","+this.length2;
  798. var txt = "@@ -"+coords1+" +"+coords2+" @@\n";
  799. // Escape the body of the patch with %xx notation.
  800. for (var x=0; x<this.diffs.length; x++)
  801. txt += ("- +".charAt(this.diffs[x][0]+1)) + encodeURI(this.diffs[x][1]) + "\n";
  802. return txt.replace(/%20/g, ' ');
  803. }
  804. this.text1 = function() {
  805. // Compute and return the source text (all equalities and deletions).
  806. var txt = '';
  807. for (var x=0; x<this.diffs.length; x++)
  808. if (this.diffs[x][0] == 0 || this.diffs[x][0] == -1)
  809. txt += this.diffs[x][1];
  810. return txt;
  811. }
  812. this.text2 = function() {
  813. // Compute and return the destination text (all equalities and insertions).
  814. var txt = '';
  815. for (var x=0; x<this.diffs.length; x++)
  816. if (this.diffs[x][0] == 0 || this.diffs[x][0] == 1)
  817. txt += this.diffs[x][1];
  818. return txt;
  819. }
  820. }
  821. function patch_addcontext(patch, text) {
  822. var pattern = text.substring(patch.start2, patch.start2+patch.length1);
  823. var padding = 0;
  824. // Increase the context until we're unique (but don't let the pattern expand beyond MATCH_MAXBITS).
  825. while (text.indexOf(pattern) != text.lastIndexOf(pattern) && pattern.length < MATCH_MAXBITS-PATCH_MARGIN-PATCH_MARGIN) {
  826. padding += PATCH_MARGIN;
  827. pattern = text.substring(patch.start2 - padding, patch.start2+patch.length1 + padding);
  828. }
  829. // Add one chunk for good luck.
  830. padding += PATCH_MARGIN;
  831. // Add the prefix.
  832. var prefix = text.substring(patch.start2 - padding, patch.start2);
  833. if (prefix != '')
  834. patch.diffs.unshift([0, prefix]);
  835. // Add the suffix
  836. var suffix = text.substring(patch.start2+patch.length1, patch.start2+patch.length1 + padding);
  837. if (suffix != '')
  838. patch.diffs.push([0, suffix]);
  839. // Roll back the start points.
  840. patch.start1 -= prefix.length;
  841. patch.start2 -= prefix.length;
  842. // Extend the lengths.
  843. patch.length1 += prefix.length + suffix.length;
  844. patch.length2 += prefix.length + suffix.length;
  845. }
  846. function patch_make(text1, text2, diff) {
  847. // Compute a list of patches to turn text1 into text2.
  848. // Use diff if provided, otherwise compute it ourselves.
  849. if (typeof diff == 'undefined') {
  850. diff = diff_main(text1, text2, true);
  851. if (diff.length > 2) {
  852. diff_cleanup_semantic(diff);
  853. diff_cleanup_efficiency(diff);
  854. }
  855. }
  856. if (diff.length == 0)
  857. return []; // Get rid of the null case.
  858. var patches = [];
  859. var patch = new patch_obj();
  860. var char_count1 = 0; // Number of characters into the text1 string.
  861. var char_count2 = 0; // Number of characters into the text2 string.
  862. var last_type = null;
  863. var prepatch_text = text1; // Recreate the patches to determine context info.
  864. var postpatch_text = text1;
  865. for (var x=0; x<diff.length; x++) {
  866. var diff_type = diff[x][0];
  867. var diff_text = diff[x][1];
  868. if (patch.diffs.length == 0 && diff_type != 0) {
  869. // A new patch starts here.
  870. patch.start1 = char_count1;
  871. patch.start2 = char_count2;
  872. }
  873. if (diff_type == 1) {
  874. // Insertion
  875. patch.diffs.push(diff[x]);
  876. patch.length2 += diff_text.length;
  877. postpatch_text = postpatch_text.substring(0, char_count2) + diff_text + postpatch_text.substring(char_count2);
  878. } else if (diff_type == -1) {
  879. // Deletion.
  880. patch.length1 += diff_text.length;
  881. patch.diffs.push(diff[x]);
  882. postpatch_text = postpatch_text.substring(0, char_count2) + postpatch_text.substring(char_count2 + diff_text.length);
  883. } else if (diff_type == 0 && diff_text.length <= 2*PATCH_MARGIN && patch.diffs.length != 0 && diff.length != x+1) {
  884. // Small equality inside a patch.
  885. patch.diffs.push(diff[x]);
  886. patch.length1 += diff_text.length;
  887. patch.length2 += diff_text.length;
  888. }
  889. last_type = diff_type;
  890. if (diff_type == 0 && diff_text.length >= 2*PATCH_MARGIN) {
  891. // Time for a new patch.
  892. if (patch.diffs.length != 0) {
  893. patch_addcontext(patch, prepatch_text);
  894. patches.push(patch);
  895. var patch = new patch_obj();
  896. last_type = null;
  897. prepatch_text = postpatch_text;
  898. }
  899. }
  900. // Update the current character count.
  901. if (diff_type != 1)
  902. char_count1 += diff_text.length;
  903. if (diff_type != -1)
  904. char_count2 += diff_text.length;
  905. }
  906. // Pick up the leftover patch if not empty.
  907. if (patch.diffs.length != 0) {
  908. patch_addcontext(patch, prepatch_text);
  909. patches.push(patch);
  910. }
  911. return patches;
  912. }
  913. function patch_apply(patches, text) {
  914. // Merge a set of patches onto the text.
  915. // Return a patched text, as well as a list of true/false values indicating which patches were applied.
  916. patch_splitmax(patches);
  917. var results = [];
  918. var delta = 0;
  919. var expected_loc, start_loc;
  920. var text1, text2;
  921. var diff, mod, index1, index2;
  922. for (var x=0; x<patches.length; x++) {
  923. expected_loc = patches[x].start2 + delta;
  924. text1 = patches[x].text1();
  925. start_loc = match_main(text, text1, expected_loc);
  926. if (start_loc == null) {
  927. // No match found. :(
  928. results.push(false);
  929. } else {
  930. // Found a match. :)
  931. results.push(true);
  932. delta = start_loc - expected_loc;
  933. text2 = text.substring(start_loc, start_loc + text1.length);
  934. if (text1 == text2) {
  935. // Perfect match, just shove the replacement text in.
  936. text = text.substring(0, start_loc) + patches[x].text2() + text.substring(start_loc + text1.length);
  937. } else {
  938. // Imperfect match. Run a diff to get a framework of equivalent indicies.
  939. diff = diff_main(text1, text2, false);
  940. index1 = 0;
  941. for (var y=0; y<patches[x].diffs.length; y++) {
  942. mod = patches[x].diffs[y];
  943. if (mod[0] != 0)
  944. index2 = diff_xindex(diff, index1);
  945. if (mod[0] == 1) // Insertion
  946. text = text.substring(0, start_loc + index2) + mod[1] + text.substring(start_loc + index2);
  947. else if (mod[0] == -1) // Deletion
  948. text = text.substring(0, start_loc + index2) + text.substring(start_loc + diff_xindex(diff, index1 + mod[1].length));
  949. if (mod[0] != -1)
  950. index1 += mod[1].length;
  951. }
  952. }
  953. }
  954. }
  955. return [text, results];
  956. }
  957. function patch_splitmax(patches) {
  958. // Look through the patches and break up any which are longer than the maximum limit of the match algorithm.
  959. var bigpatch, patch, patch_size, start1, start2, diff_type, diff_text, precontext, postcontext, empty;
  960. for (var x=0; x<patches.length; x++) {
  961. if (patches[x].length1 > MATCH_MAXBITS) {
  962. bigpatch = patches[x];
  963. // Remove the big old patch.
  964. patches.splice(x, 1);
  965. patch_size = MATCH_MAXBITS;
  966. start1 = bigpatch.start1;
  967. start2 = bigpatch.start2;
  968. precontext = '';
  969. while (bigpatch.diffs.length != 0) {
  970. // Create one of several smaller patches.
  971. patch = new patch_obj();
  972. empty = true;
  973. patch.start1 = start1 - precontext.length;
  974. patch.start2 = start2 - precontext.length;
  975. if (precontext != '') {
  976. patch.length1 = patch.length2 = precontext.length;
  977. patch.diffs.push([0, precontext]);
  978. }
  979. while (bigpatch.diffs.length != 0 && patch.length1 < patch_size - PATCH_MARGIN) {
  980. diff_type = bigpatch.diffs[0][0];
  981. diff_text = bigpatch.diffs[0][1];
  982. if (diff_type == 1) {
  983. // Insertions are harmless.
  984. patch.length2 += diff_text.length;
  985. start2 += diff_text.length;
  986. patch.diffs.push(bigpatch.diffs.shift());
  987. empty = false;
  988. } else {
  989. // Deletion or equality. Only take as much as we can stomach.
  990. diff_text = diff_text.substring(0, patch_size - patch.length1 - PATCH_MARGIN);
  991. patch.length1 += diff_text.length;
  992. start1 += diff_text.length;
  993. if (diff_type == 0) {
  994. patch.length2 += diff_text.length;
  995. start2 += diff_text.length;
  996. } else {
  997. empty = false;
  998. }
  999. patch.diffs.push([diff_type, diff_text]);
  1000. if (diff_text == bigpatch.diffs[0][1])
  1001. bigpatch.diffs.shift();
  1002. else
  1003. bigpatch.diffs[0][1] = bigpatch.diffs[0][1].substring(diff_text.length);
  1004. }
  1005. }
  1006. // Compute the head context for the next patch.
  1007. precontext = patch.text2();
  1008. precontext = precontext.substring(precontext.length - PATCH_MARGIN);
  1009. // Append the end context for this patch.
  1010. postcontext = bigpatch.text1().substring(0, PATCH_MARGIN);
  1011. if (postcontext != '') {
  1012. patch.length1 += postcontext.length;
  1013. patch.length2 += postcontext.length;
  1014. if (patch.diffs.length > 0 && patch.diffs[patch.diffs.length-1][0] == 0)
  1015. patch.diffs[patch.diffs.length-1][1] += postcontext;
  1016. else
  1017. patch.diffs.push([0, postcontext]);
  1018. }
  1019. if (!empty)
  1020. patches.splice(x++, 0, patch);
  1021. }
  1022. }
  1023. }
  1024. }
  1025. function patch_totext(patches) {
  1026. // Take a list of patches and return a textual representation.
  1027. var text = '';
  1028. for (var x=0; x<patches.length; x++)
  1029. text += patches[x];
  1030. return text;
  1031. }
  1032. function patch_fromtext(text) {
  1033. // Take a textual representation of patches and return a list of patch objects.
  1034. var patches = [];
  1035. text = text.split('\n');
  1036. var patch, m, chars1, chars2, sign, line;
  1037. while (text.length != 0) {
  1038. m = text[0].match(/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/);
  1039. if (!m)
  1040. return alert("Invalid patch string:\n"+text[0]);
  1041. patch = new patch_obj();
  1042. patches.push(patch);
  1043. patch.start1 = parseInt(m[1]);
  1044. if (m[2] == '') {
  1045. patch.start1--;
  1046. patch.length1 = 1;
  1047. } else if (m[2] == '0') {
  1048. patch.length1 = 0;
  1049. } else {
  1050. patch.start1--;
  1051. patch.length1 = parseInt(m[2]);
  1052. }
  1053. patch.start2 = parseInt(m[3]);
  1054. if (m[4] == '') {
  1055. patch.start2--;
  1056. patch.length2 = 1;
  1057. } else if (m[4] == '0') {
  1058. patch.length2 = 0;
  1059. } else {
  1060. patch.start2--;
  1061. patch.length2 = parseInt(m[4]);
  1062. }
  1063. text.shift();
  1064. while (text.length != 0) {
  1065. sign = text[0].charAt(0);
  1066. line = decodeURIComponent(text[0].substring(1));
  1067. if (sign == '-') {
  1068. // Deletion.
  1069. patch.diffs.push([-1, line]);
  1070. } else if (sign == '+') {
  1071. // Insertion.
  1072. patch.diffs.push([1, line]);
  1073. } else if (sign == ' ') {
  1074. // Minor equality.
  1075. patch.diffs.push([0, line]);
  1076. } else if (sign == '@') {
  1077. // Start of next patch.
  1078. break;
  1079. } else if (sign == '') {
  1080. // Blank line? Whatever.
  1081. } else {
  1082. // WTF?
  1083. return alert("Invalid patch mode: '"+sign+"'\n"+line);
  1084. }
  1085. text.shift();
  1086. }
  1087. }
  1088. return patches;
  1089. }
  1090. // EOF