📄 diff.js
字号:
}function diff_suffix(text1, text2) { // Trim off common suffix var pointermin = 0; var pointermax = Math.min(text1.length, text2.length); var pointermid = pointermax; while(pointermin < pointermid) { if (text1.substring(text1.length-pointermid) == text2.substring(text2.length-pointermid)) pointermin = pointermid; else pointermax = pointermid; pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); } var commonsuffix = text1.substring(text1.length-pointermid); text1 = text1.substring(0, text1.length-pointermid); text2 = text2.substring(0, text2.length-pointermid); return [text1, text2, commonsuffix];}function diff_halfmatch(text1, text2) { // Do the two texts share a substring which is at least half the length of the longer text? var longtext = text1.length > text2.length ? text1 : text2; var shorttext = text1.length > text2.length ? text2 : text1; if (longtext.length < 10 || shorttext.length < 1) return null; // Pointless. function diff_halfmatch_i(longtext, shorttext, i) { // Start with a 1/4 length substring at position i as a seed. var seed = longtext.substring(i, i+Math.floor(longtext.length/4)); var j = -1; var best_common = ''; var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; while ((j = shorttext.indexOf(seed, j+1)) != -1) { var my_prefix = diff_prefix(longtext.substring(i), shorttext.substring(j)); var my_suffix = diff_suffix(longtext.substring(0, i), shorttext.substring(0, j)); if (best_common.length < (my_suffix[2] + my_prefix[2]).length) { best_common = my_suffix[2] + my_prefix[2]; best_longtext_a = my_suffix[0]; best_longtext_b = my_prefix[0]; best_shorttext_a = my_suffix[1]; best_shorttext_b = my_prefix[1]; } } if (best_common.length >= longtext.length/2) return [best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b, best_common]; else return null; } // First check if the second quarter is the seed for a half-match. var hm1 = diff_halfmatch_i(longtext, shorttext, Math.ceil(longtext.length/4)); // Check again based on the third quarter. var hm2 = diff_halfmatch_i(longtext, shorttext, Math.ceil(longtext.length/2)); var hm; if (!hm1 && !hm2) return null; else if (!hm2) hm = hm1; else if (!hm1) hm = hm2; else // Both matched. Select the longest. hm = hm1[4].length > hm2[4].length ? hm1 : hm2; // A half-match was found, sort out the return data. if (text1.length > text2.length) { var text1_a = hm[0]; var text1_b = hm[1]; var text2_a = hm[2]; var text2_b = hm[3]; } else { var text2_a = hm[0]; var text2_b = hm[1]; var text1_a = hm[2]; var text1_b = hm[3]; } var mid_common = hm[4]; return [text1_a, text1_b, text2_a, text2_b, mid_common];}function diff_cleanup_semantic(diff) { // Reduce the number of edits by eliminating semantically trivial equalities. var changes = false; var equalities = []; // Stack of indices where equalities are found. var lastequality = null; // Always equal to equalities[equalities.length-1][1] var pointer = 0; // Index of current position. var length_changes1 = 0; // Number of characters that changed prior to the equality. var length_changes2 = 0; // Number of characters that changed after the equality. while (pointer < diff.length) { if (diff[pointer][0] == 0) { // equality found equalities.push(pointer); length_changes1 = length_changes2; length_changes2 = 0; lastequality = diff[pointer][1]; } else { // an insertion or deletion length_changes2 += diff[pointer][1].length; if (lastequality != null && (lastequality.length <= length_changes1) && (lastequality.length <= length_changes2)) { //alert("Splitting: '"+lastequality+"'"); diff.splice(equalities[equalities.length-1], 0, [-1, lastequality]); // Duplicate record diff[equalities[equalities.length-1]+1][0] = 1; // Change second copy to insert. equalities.pop(); // Throw away the equality we just deleted; equalities.pop(); // Throw away the previous equality; pointer = equalities.length ? equalities[equalities.length-1] : -1; length_changes1 = 0; // Reset the counters. length_changes2 = 0; lastequality = null; changes = true; } } pointer++; } if (changes) diff_cleanup_merge(diff);}function diff_cleanup_efficiency(diff) { // Reduce the number of edits by eliminating operationally trivial equalities. var changes = false; var equalities = []; // Stack of indices where equalities are found. var lastequality = ''; // Always equal to equalities[equalities.length-1][1] var pointer = 0; // Index of current position. var pre_ins = false; // Is there an insertion operation before the last equality. var pre_del = false; // Is there an deletion operation before the last equality. var post_ins = false; // Is there an insertion operation after the last equality. var post_del = false; // Is there an deletion operation after the last equality. while (pointer < diff.length) { if (diff[pointer][0] == 0) { // equality found if (diff[pointer][1].length < DIFF_EDIT_COST && (post_ins || post_del)) { // Candidate found. equalities.push(pointer); pre_ins = post_ins; pre_del = post_del; lastequality = diff[pointer][1]; } else { // Not a candidate, and can never become one. equalities = []; lastequality = ''; } post_ins = post_del = false; } else { // an insertion or deletion if (diff[pointer][0] == -1) post_del = true; else post_ins = true; // Five types to be split: // <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del> // <ins>A</ins>X<ins>C</ins><del>D</del> // <ins>A</ins><del>B</del>X<ins>C</ins> // <ins>A</del>X<ins>C</ins><del>D</del> // <ins>A</ins><del>B</del>X<del>C</del> 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))) { //alert("Splitting: '"+lastequality+"'"); diff.splice(equalities[equalities.length-1], 0, [-1, lastequality]); // Duplicate record diff[equalities[equalities.length-1]+1][0] = 1; // Change second copy to insert. equalities.pop(); // Throw away the equality we just deleted; lastequality = ''; if (pre_ins && pre_del) { // No changes made which could affect previous entry, keep going. post_ins = post_del = true; equalities = []; } else { equalities.pop(); // Throw away the previous equality; pointer = equalities.length ? equalities[equalities.length-1] : -1; post_ins = post_del = false; } changes = true; } } pointer++; } if (changes) diff_cleanup_merge(diff);}function diff_cleanup_merge(diff) { // Reorder and merge like edit sections. Merge equalities. // Any edit section can move as long as it doesn't cross an equality. diff.push([0, '']); // Add a dummy entry at the end. var pointer = 0; var count_delete = 0; var count_insert = 0; var text_delete = ''; var text_insert = ''; var record_insert, record_delete; var my_xfix; while(pointer < diff.length) { if (diff[pointer][0] == 1) { count_insert++; text_insert += diff[pointer][1]; pointer++; } else if (diff[pointer][0] == -1) { count_delete++; text_delete += diff[pointer][1]; pointer++; } else { // Upon reaching an equality, check for prior redundancies. if (count_delete > 1 || count_insert > 1) { if (count_delete > 1 && count_insert > 1) { // Factor out any common prefixies. my_xfix = diff_prefix(text_insert, text_delete); if (my_xfix[2] != '') { if ((pointer - count_delete - count_insert) > 0 && diff[pointer - count_delete - count_insert - 1][0] == 0) { text_insert = my_xfix[0]; text_delete = my_xfix[1]; diff[pointer - count_delete - count_insert - 1][1] += my_xfix[2]; } } // Factor out any common suffixies. my_xfix = diff_suffix(text_insert, text_delete); if (my_xfix[2] != '') { text_insert = my_xfix[0]; text_delete = my_xfix[1]; diff[pointer][1] = my_xfix[2] + diff[pointer][1]; } } // Delete the offending records and add the merged ones. if (count_delete == 0) diff.splice(pointer - count_delete - count_insert, count_delete + count_insert, [1, text_insert]); else if (count_insert == 0) diff.splice(pointer - count_delete - count_insert, count_delete + count_insert, [-1, text_delete]); else diff.splice(pointer - count_delete - count_insert, count_delete + count_insert, [-1, text_delete], [1, text_insert]); pointer = pointer - count_delete - count_insert + (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; } else if (pointer != 0 && diff[pointer-1][0] == 0) { // Merge this equality with the previous one. diff[pointer-1][1] += diff[pointer][1]; diff.splice(pointer, 1); } else { pointer++; } count_insert = 0; count_delete = 0; text_delete = ''; text_insert = ''; } } if (diff[diff.length-1][1] == '') diff.pop(); // Remove the dummy entry at the end.}function diff_addindex(diff) { // Add an index to each tuple, represents where the tuple is located in text2. // e.g. [[-1, 'h', 0], [1, 'c', 0], [0, 'at', 1]] var i = 0; for (var x=0; x<diff.length; x++) { diff[x].push(i); if (diff[x][0] != -1) i += diff[x][1].length; }}function diff_xindex(diff, loc) { // loc is a location in text1, compute and return the equivalent location in text2. // e.g. "The cat" vs "The big cat", 1->1, 5->8 var chars1 = 0; var chars2 = 0; var last_chars1 = 0; var last_chars2 = 0; for (var x=0; x<diff.length; x++) { if (diff[x][0] != 1) // Equality or deletion. chars1 += diff[x][1].length; if (diff[x][0] != -1) // Equality or insertion. chars2 += diff[x][1].length; if (chars1 > loc) // Overshot the location. break; last_chars1 = chars1; last_chars2 = chars2; } if (diff.length != x && diff[x][0] == -1) // The location was deleted. return last_chars2; // Add the remaining character length. return last_chars2 + (loc - last_chars1);}function diff_prettyhtml(diff) { // Convert a diff array into a pretty HTML report. diff_addindex(diff); var html = ''; for (var x=0; x<diff.length; x++) { var m = diff[x][0]; // Mode (-1=delete, 0=copy, 1=add) var t = diff[x][1]; // Text of change. var i = diff[x][2]; // Index of change. t = t.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); t = t.replace(/\n/g, "¶<BR>"); if (m == -1) html += "<DEL STYLE='background:#FFE6E6;' TITLE='i="+i+"'>"+t+"</DEL>"; else if (m == 1) html += "<INS STYLE='background:#E6FFE6;' TITLE='i="+i+"'>"+t+"</INS>"; else html += "<SPAN TITLE='i="+i+"'>"+t+"</SPAN>"; } return html;} ////////////////////////////////////////////////////////////////////// // Match ////////////////////////////////////////////////////////////////////////function match_getmaxbits() { // Compute the number of bits in an int. // The normal answer for JavaScript is 32. var maxbits = 0; var oldi = 1; var newi = 2; while (oldi != newi) { maxbits++; oldi = newi; newi = newi << 1; } return maxbits;}var MATCH_MAXBITS = match_getmaxbits();function match_main(text, pattern, loc) { // Locate the best instance of 'pattern' in 'text' near 'loc'. loc = Math.max(0, Math.min(loc, text.length-pattern.length)); if (text == pattern) { // Shortcut (potentially not guaranteed by the algorithm) return 0; } else if (text.length == 0) { // Nothing to match. return null; } else if (text.substring(loc, loc + pattern.length) == pattern) { // Perfect match at the perfect spot! (Includes case of null pattern) return loc; } else { // Do a fuzzy compare. var match = match_bitap(text, pattern, loc); return match; }}function match_bitap(text, pattern, loc) { // Locate the best instance of 'pattern' in 'text' near 'loc' using the Bitap algorithm. if (pattern.length > MATCH_MAXBITS) return alert("Pattern too long for this browser."); // Initialise the alphabet. var s = match_alphabet(pattern); var score_text_length = text.length; // Coerce the text length between reasonable maximums and minimums. score_text_length = Math.max(score_text_length, MATCH_MINLENGTH); score_text_length = Math.min(score_text_length, MATCH_MAXLENGTH); function match_bitap_score (e, x) { // Compute and return the score for a match with e errors and x location. var d = Math.abs(loc-x); return (e / pattern.length / MATCH_BALANCE) + (d / score_text_length / (1.0 - MATCH_BALANCE)); } // Highest score beyond which we give up. var score_threshold = MATCH_THRESHOLD; // Is there a nearby exact match? (speedup) var best_loc = text.indexOf(pattern, loc); if (best_loc != -1) score_threshold = Math.min(match_bitap_score(0, best_loc), score_threshold); // What about in the other direction? (speedup) best_loc = text.lastIndexOf(pattern, loc+pattern.length); if (best_loc != -1) score_threshold = Math.min(match_bitap_score(0, best_loc), score_threshold); // Initialise the bit arrays. var r = Array(); var d = -1; var matchmask = Math.pow(2, pattern.length-1); best_loc = null; var bin_min, bin_mid; var bin_max = Math.max(loc+loc, text.length); var last_rd; for (var d=0; d<pattern.length; d++) { // Scan for the best match; each iteration allows for one more error. var rd = Array(text.length); // Run a binary search to determine how far from 'loc' we can stray at this error level. bin_min = loc; bin_mid = bin_max; while(bin_min < bin_mid) { if (match_bitap_score(d, bin_mid) < score_threshold) bin_min = bin_mid; else bin_max = bin_mid; bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -