You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							738 lines
						
					
					
						
							25 KiB
						
					
					
				
			
		
		
	
	
							738 lines
						
					
					
						
							25 KiB
						
					
					
				/** | 
						|
 * This library modifies the diff-patch-match library by Neil Fraser | 
						|
 * by removing the patch and match functionality and certain advanced | 
						|
 * options in the diff function. The original license is as follows: | 
						|
 * | 
						|
 * === | 
						|
 * | 
						|
 * Diff Match and Patch | 
						|
 * | 
						|
 * Copyright 2006 Google Inc. | 
						|
 * http://code.google.com/p/google-diff-match-patch/ | 
						|
 * | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License"); | 
						|
 * you may not use this file except in compliance with the License. | 
						|
 * You may obtain a copy of the License at | 
						|
 * | 
						|
 *   http://www.apache.org/licenses/LICENSE-2.0 | 
						|
 * | 
						|
 * Unless required by applicable law or agreed to in writing, software | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS, | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
						|
 * See the License for the specific language governing permissions and | 
						|
 * limitations under the License. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
/** | 
						|
 * The data structure representing a diff is an array of tuples: | 
						|
 * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] | 
						|
 * which means: delete 'Hello', add 'Goodbye' and keep ' world.' | 
						|
 */ | 
						|
var DIFF_DELETE = -1; | 
						|
var DIFF_INSERT = 1; | 
						|
var DIFF_EQUAL = 0; | 
						|
 | 
						|
 | 
						|
/** | 
						|
 * Find the differences between two texts.  Simplifies the problem by stripping | 
						|
 * any common prefix or suffix off the texts before diffing. | 
						|
 * @param {string} text1 Old string to be diffed. | 
						|
 * @param {string} text2 New string to be diffed. | 
						|
 * @param {Int} cursor_pos Expected edit position in text1 (optional) | 
						|
 * @return {Array} Array of diff tuples. | 
						|
 */ | 
						|
function diff_main(text1, text2, cursor_pos) { | 
						|
  // Check for equality (speedup). | 
						|
  if (text1 == text2) { | 
						|
    if (text1) { | 
						|
      return [[DIFF_EQUAL, text1]]; | 
						|
    } | 
						|
    return []; | 
						|
  } | 
						|
 | 
						|
  // Check cursor_pos within bounds | 
						|
  if (cursor_pos < 0 || text1.length < cursor_pos) { | 
						|
    cursor_pos = null; | 
						|
  } | 
						|
 | 
						|
  // Trim off common prefix (speedup). | 
						|
  var commonlength = diff_commonPrefix(text1, text2); | 
						|
  var commonprefix = text1.substring(0, commonlength); | 
						|
  text1 = text1.substring(commonlength); | 
						|
  text2 = text2.substring(commonlength); | 
						|
 | 
						|
  // Trim off common suffix (speedup). | 
						|
  commonlength = diff_commonSuffix(text1, text2); | 
						|
  var commonsuffix = text1.substring(text1.length - commonlength); | 
						|
  text1 = text1.substring(0, text1.length - commonlength); | 
						|
  text2 = text2.substring(0, text2.length - commonlength); | 
						|
 | 
						|
  // Compute the diff on the middle block. | 
						|
  var diffs = diff_compute_(text1, text2); | 
						|
 | 
						|
  // Restore the prefix and suffix. | 
						|
  if (commonprefix) { | 
						|
    diffs.unshift([DIFF_EQUAL, commonprefix]); | 
						|
  } | 
						|
  if (commonsuffix) { | 
						|
    diffs.push([DIFF_EQUAL, commonsuffix]); | 
						|
  } | 
						|
  diff_cleanupMerge(diffs); | 
						|
  if (cursor_pos != null) { | 
						|
    diffs = fix_cursor(diffs, cursor_pos); | 
						|
  } | 
						|
  diffs = fix_emoji(diffs); | 
						|
  return diffs; | 
						|
}; | 
						|
 | 
						|
 | 
						|
/** | 
						|
 * Find the differences between two texts.  Assumes that the texts do not | 
						|
 * have any common prefix or suffix. | 
						|
 * @param {string} text1 Old string to be diffed. | 
						|
 * @param {string} text2 New string to be diffed. | 
						|
 * @return {Array} Array of diff tuples. | 
						|
 */ | 
						|
function diff_compute_(text1, text2) { | 
						|
  var diffs; | 
						|
 | 
						|
  if (!text1) { | 
						|
    // Just add some text (speedup). | 
						|
    return [[DIFF_INSERT, text2]]; | 
						|
  } | 
						|
 | 
						|
  if (!text2) { | 
						|
    // Just delete some text (speedup). | 
						|
    return [[DIFF_DELETE, text1]]; | 
						|
  } | 
						|
 | 
						|
  var longtext = text1.length > text2.length ? text1 : text2; | 
						|
  var shorttext = text1.length > text2.length ? text2 : text1; | 
						|
  var i = longtext.indexOf(shorttext); | 
						|
  if (i != -1) { | 
						|
    // Shorter text is inside the longer text (speedup). | 
						|
    diffs = [[DIFF_INSERT, longtext.substring(0, i)], | 
						|
             [DIFF_EQUAL, shorttext], | 
						|
             [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; | 
						|
    // Swap insertions for deletions if diff is reversed. | 
						|
    if (text1.length > text2.length) { | 
						|
      diffs[0][0] = diffs[2][0] = DIFF_DELETE; | 
						|
    } | 
						|
    return diffs; | 
						|
  } | 
						|
 | 
						|
  if (shorttext.length == 1) { | 
						|
    // Single character string. | 
						|
    // After the previous speedup, the character can't be an equality. | 
						|
    return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; | 
						|
  } | 
						|
 | 
						|
  // Check to see if the problem can be split in two. | 
						|
  var hm = diff_halfMatch_(text1, text2); | 
						|
  if (hm) { | 
						|
    // A half-match was found, sort out the return data. | 
						|
    var text1_a = hm[0]; | 
						|
    var text1_b = hm[1]; | 
						|
    var text2_a = hm[2]; | 
						|
    var text2_b = hm[3]; | 
						|
    var mid_common = hm[4]; | 
						|
    // Send both pairs off for separate processing. | 
						|
    var diffs_a = diff_main(text1_a, text2_a); | 
						|
    var diffs_b = diff_main(text1_b, text2_b); | 
						|
    // Merge the results. | 
						|
    return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b); | 
						|
  } | 
						|
 | 
						|
  return diff_bisect_(text1, text2); | 
						|
}; | 
						|
 | 
						|
 | 
						|
/** | 
						|
 * Find the 'middle snake' of a diff, split the problem in two | 
						|
 * and return the recursively constructed diff. | 
						|
 * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. | 
						|
 * @param {string} text1 Old string to be diffed. | 
						|
 * @param {string} text2 New string to be diffed. | 
						|
 * @return {Array} Array of diff tuples. | 
						|
 * @private | 
						|
 */ | 
						|
function diff_bisect_(text1, text2) { | 
						|
  // Cache the text lengths to prevent multiple calls. | 
						|
  var text1_length = text1.length; | 
						|
  var text2_length = text2.length; | 
						|
  var max_d = Math.ceil((text1_length + text2_length) / 2); | 
						|
  var v_offset = max_d; | 
						|
  var v_length = 2 * max_d; | 
						|
  var v1 = new Array(v_length); | 
						|
  var v2 = new Array(v_length); | 
						|
  // Setting all elements to -1 is faster in Chrome & Firefox than mixing | 
						|
  // integers and undefined. | 
						|
  for (var x = 0; x < v_length; x++) { | 
						|
    v1[x] = -1; | 
						|
    v2[x] = -1; | 
						|
  } | 
						|
  v1[v_offset + 1] = 0; | 
						|
  v2[v_offset + 1] = 0; | 
						|
  var delta = text1_length - text2_length; | 
						|
  // If the total number of characters is odd, then the front path will collide | 
						|
  // with the reverse path. | 
						|
  var front = (delta % 2 != 0); | 
						|
  // Offsets for start and end of k loop. | 
						|
  // Prevents mapping of space beyond the grid. | 
						|
  var k1start = 0; | 
						|
  var k1end = 0; | 
						|
  var k2start = 0; | 
						|
  var k2end = 0; | 
						|
  for (var d = 0; d < max_d; d++) { | 
						|
    // Walk the front path one step. | 
						|
    for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { | 
						|
      var k1_offset = v_offset + k1; | 
						|
      var x1; | 
						|
      if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { | 
						|
        x1 = v1[k1_offset + 1]; | 
						|
      } else { | 
						|
        x1 = v1[k1_offset - 1] + 1; | 
						|
      } | 
						|
      var y1 = x1 - k1; | 
						|
      while (x1 < text1_length && y1 < text2_length && | 
						|
             text1.charAt(x1) == text2.charAt(y1)) { | 
						|
        x1++; | 
						|
        y1++; | 
						|
      } | 
						|
      v1[k1_offset] = x1; | 
						|
      if (x1 > text1_length) { | 
						|
        // Ran off the right of the graph. | 
						|
        k1end += 2; | 
						|
      } else if (y1 > text2_length) { | 
						|
        // Ran off the bottom of the graph. | 
						|
        k1start += 2; | 
						|
      } else if (front) { | 
						|
        var k2_offset = v_offset + delta - k1; | 
						|
        if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { | 
						|
          // Mirror x2 onto top-left coordinate system. | 
						|
          var x2 = text1_length - v2[k2_offset]; | 
						|
          if (x1 >= x2) { | 
						|
            // Overlap detected. | 
						|
            return diff_bisectSplit_(text1, text2, x1, y1); | 
						|
          } | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    // Walk the reverse path one step. | 
						|
    for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { | 
						|
      var k2_offset = v_offset + k2; | 
						|
      var x2; | 
						|
      if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { | 
						|
        x2 = v2[k2_offset + 1]; | 
						|
      } else { | 
						|
        x2 = v2[k2_offset - 1] + 1; | 
						|
      } | 
						|
      var y2 = x2 - k2; | 
						|
      while (x2 < text1_length && y2 < text2_length && | 
						|
             text1.charAt(text1_length - x2 - 1) == | 
						|
             text2.charAt(text2_length - y2 - 1)) { | 
						|
        x2++; | 
						|
        y2++; | 
						|
      } | 
						|
      v2[k2_offset] = x2; | 
						|
      if (x2 > text1_length) { | 
						|
        // Ran off the left of the graph. | 
						|
        k2end += 2; | 
						|
      } else if (y2 > text2_length) { | 
						|
        // Ran off the top of the graph. | 
						|
        k2start += 2; | 
						|
      } else if (!front) { | 
						|
        var k1_offset = v_offset + delta - k2; | 
						|
        if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { | 
						|
          var x1 = v1[k1_offset]; | 
						|
          var y1 = v_offset + x1 - k1_offset; | 
						|
          // Mirror x2 onto top-left coordinate system. | 
						|
          x2 = text1_length - x2; | 
						|
          if (x1 >= x2) { | 
						|
            // Overlap detected. | 
						|
            return diff_bisectSplit_(text1, text2, x1, y1); | 
						|
          } | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
  // Diff took too long and hit the deadline or | 
						|
  // number of diffs equals number of characters, no commonality at all. | 
						|
  return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; | 
						|
}; | 
						|
 | 
						|
 | 
						|
/** | 
						|
 * Given the location of the 'middle snake', split the diff in two parts | 
						|
 * and recurse. | 
						|
 * @param {string} text1 Old string to be diffed. | 
						|
 * @param {string} text2 New string to be diffed. | 
						|
 * @param {number} x Index of split point in text1. | 
						|
 * @param {number} y Index of split point in text2. | 
						|
 * @return {Array} Array of diff tuples. | 
						|
 */ | 
						|
function diff_bisectSplit_(text1, text2, x, y) { | 
						|
  var text1a = text1.substring(0, x); | 
						|
  var text2a = text2.substring(0, y); | 
						|
  var text1b = text1.substring(x); | 
						|
  var text2b = text2.substring(y); | 
						|
 | 
						|
  // Compute both diffs serially. | 
						|
  var diffs = diff_main(text1a, text2a); | 
						|
  var diffsb = diff_main(text1b, text2b); | 
						|
 | 
						|
  return diffs.concat(diffsb); | 
						|
}; | 
						|
 | 
						|
 | 
						|
/** | 
						|
 * Determine the common prefix of two strings. | 
						|
 * @param {string} text1 First string. | 
						|
 * @param {string} text2 Second string. | 
						|
 * @return {number} The number of characters common to the start of each | 
						|
 *     string. | 
						|
 */ | 
						|
function diff_commonPrefix(text1, text2) { | 
						|
  // Quick check for common null cases. | 
						|
  if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) { | 
						|
    return 0; | 
						|
  } | 
						|
  // Binary search. | 
						|
  // Performance analysis: http://neil.fraser.name/news/2007/10/09/ | 
						|
  var pointermin = 0; | 
						|
  var pointermax = Math.min(text1.length, text2.length); | 
						|
  var pointermid = pointermax; | 
						|
  var pointerstart = 0; | 
						|
  while (pointermin < pointermid) { | 
						|
    if (text1.substring(pointerstart, pointermid) == | 
						|
        text2.substring(pointerstart, pointermid)) { | 
						|
      pointermin = pointermid; | 
						|
      pointerstart = pointermin; | 
						|
    } else { | 
						|
      pointermax = pointermid; | 
						|
    } | 
						|
    pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); | 
						|
  } | 
						|
  return pointermid; | 
						|
}; | 
						|
 | 
						|
 | 
						|
/** | 
						|
 * Determine the common suffix of two strings. | 
						|
 * @param {string} text1 First string. | 
						|
 * @param {string} text2 Second string. | 
						|
 * @return {number} The number of characters common to the end of each string. | 
						|
 */ | 
						|
function diff_commonSuffix(text1, text2) { | 
						|
  // Quick check for common null cases. | 
						|
  if (!text1 || !text2 || | 
						|
      text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) { | 
						|
    return 0; | 
						|
  } | 
						|
  // Binary search. | 
						|
  // Performance analysis: http://neil.fraser.name/news/2007/10/09/ | 
						|
  var pointermin = 0; | 
						|
  var pointermax = Math.min(text1.length, text2.length); | 
						|
  var pointermid = pointermax; | 
						|
  var pointerend = 0; | 
						|
  while (pointermin < pointermid) { | 
						|
    if (text1.substring(text1.length - pointermid, text1.length - pointerend) == | 
						|
        text2.substring(text2.length - pointermid, text2.length - pointerend)) { | 
						|
      pointermin = pointermid; | 
						|
      pointerend = pointermin; | 
						|
    } else { | 
						|
      pointermax = pointermid; | 
						|
    } | 
						|
    pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); | 
						|
  } | 
						|
  return pointermid; | 
						|
}; | 
						|
 | 
						|
 | 
						|
/** | 
						|
 * Do the two texts share a substring which is at least half the length of the | 
						|
 * longer text? | 
						|
 * This speedup can produce non-minimal diffs. | 
						|
 * @param {string} text1 First string. | 
						|
 * @param {string} text2 Second string. | 
						|
 * @return {Array.<string>} Five element Array, containing the prefix of | 
						|
 *     text1, the suffix of text1, the prefix of text2, the suffix of | 
						|
 *     text2 and the common middle.  Or null if there was no match. | 
						|
 */ | 
						|
function diff_halfMatch_(text1, text2) { | 
						|
  var longtext = text1.length > text2.length ? text1 : text2; | 
						|
  var shorttext = text1.length > text2.length ? text2 : text1; | 
						|
  if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { | 
						|
    return null;  // Pointless. | 
						|
  } | 
						|
 | 
						|
  /** | 
						|
   * Does a substring of shorttext exist within longtext such that the substring | 
						|
   * is at least half the length of longtext? | 
						|
   * Closure, but does not reference any external variables. | 
						|
   * @param {string} longtext Longer string. | 
						|
   * @param {string} shorttext Shorter string. | 
						|
   * @param {number} i Start index of quarter length substring within longtext. | 
						|
   * @return {Array.<string>} Five element Array, containing the prefix of | 
						|
   *     longtext, the suffix of longtext, the prefix of shorttext, the suffix | 
						|
   *     of shorttext and the common middle.  Or null if there was no match. | 
						|
   * @private | 
						|
   */ | 
						|
  function diff_halfMatchI_(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 prefixLength = diff_commonPrefix(longtext.substring(i), | 
						|
                                           shorttext.substring(j)); | 
						|
      var suffixLength = diff_commonSuffix(longtext.substring(0, i), | 
						|
                                           shorttext.substring(0, j)); | 
						|
      if (best_common.length < suffixLength + prefixLength) { | 
						|
        best_common = shorttext.substring(j - suffixLength, j) + | 
						|
            shorttext.substring(j, j + prefixLength); | 
						|
        best_longtext_a = longtext.substring(0, i - suffixLength); | 
						|
        best_longtext_b = longtext.substring(i + prefixLength); | 
						|
        best_shorttext_a = shorttext.substring(0, j - suffixLength); | 
						|
        best_shorttext_b = shorttext.substring(j + prefixLength); | 
						|
      } | 
						|
    } | 
						|
    if (best_common.length * 2 >= longtext.length) { | 
						|
      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_halfMatchI_(longtext, shorttext, | 
						|
                             Math.ceil(longtext.length / 4)); | 
						|
  // Check again based on the third quarter. | 
						|
  var hm2 = diff_halfMatchI_(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. | 
						|
  var text1_a, text1_b, text2_a, text2_b; | 
						|
  if (text1.length > text2.length) { | 
						|
    text1_a = hm[0]; | 
						|
    text1_b = hm[1]; | 
						|
    text2_a = hm[2]; | 
						|
    text2_b = hm[3]; | 
						|
  } else { | 
						|
    text2_a = hm[0]; | 
						|
    text2_b = hm[1]; | 
						|
    text1_a = hm[2]; | 
						|
    text1_b = hm[3]; | 
						|
  } | 
						|
  var mid_common = hm[4]; | 
						|
  return [text1_a, text1_b, text2_a, text2_b, mid_common]; | 
						|
}; | 
						|
 | 
						|
 | 
						|
/** | 
						|
 * Reorder and merge like edit sections.  Merge equalities. | 
						|
 * Any edit section can move as long as it doesn't cross an equality. | 
						|
 * @param {Array} diffs Array of diff tuples. | 
						|
 */ | 
						|
function diff_cleanupMerge(diffs) { | 
						|
  diffs.push([DIFF_EQUAL, '']);  // 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 commonlength; | 
						|
  while (pointer < diffs.length) { | 
						|
    switch (diffs[pointer][0]) { | 
						|
      case DIFF_INSERT: | 
						|
        count_insert++; | 
						|
        text_insert += diffs[pointer][1]; | 
						|
        pointer++; | 
						|
        break; | 
						|
      case DIFF_DELETE: | 
						|
        count_delete++; | 
						|
        text_delete += diffs[pointer][1]; | 
						|
        pointer++; | 
						|
        break; | 
						|
      case DIFF_EQUAL: | 
						|
        // Upon reaching an equality, check for prior redundancies. | 
						|
        if (count_delete + count_insert > 1) { | 
						|
          if (count_delete !== 0 && count_insert !== 0) { | 
						|
            // Factor out any common prefixies. | 
						|
            commonlength = diff_commonPrefix(text_insert, text_delete); | 
						|
            if (commonlength !== 0) { | 
						|
              if ((pointer - count_delete - count_insert) > 0 && | 
						|
                  diffs[pointer - count_delete - count_insert - 1][0] == | 
						|
                  DIFF_EQUAL) { | 
						|
                diffs[pointer - count_delete - count_insert - 1][1] += | 
						|
                    text_insert.substring(0, commonlength); | 
						|
              } else { | 
						|
                diffs.splice(0, 0, [DIFF_EQUAL, | 
						|
                                    text_insert.substring(0, commonlength)]); | 
						|
                pointer++; | 
						|
              } | 
						|
              text_insert = text_insert.substring(commonlength); | 
						|
              text_delete = text_delete.substring(commonlength); | 
						|
            } | 
						|
            // Factor out any common suffixies. | 
						|
            commonlength = diff_commonSuffix(text_insert, text_delete); | 
						|
            if (commonlength !== 0) { | 
						|
              diffs[pointer][1] = text_insert.substring(text_insert.length - | 
						|
                  commonlength) + diffs[pointer][1]; | 
						|
              text_insert = text_insert.substring(0, text_insert.length - | 
						|
                  commonlength); | 
						|
              text_delete = text_delete.substring(0, text_delete.length - | 
						|
                  commonlength); | 
						|
            } | 
						|
          } | 
						|
          // Delete the offending records and add the merged ones. | 
						|
          if (count_delete === 0) { | 
						|
            diffs.splice(pointer - count_insert, | 
						|
                count_delete + count_insert, [DIFF_INSERT, text_insert]); | 
						|
          } else if (count_insert === 0) { | 
						|
            diffs.splice(pointer - count_delete, | 
						|
                count_delete + count_insert, [DIFF_DELETE, text_delete]); | 
						|
          } else { | 
						|
            diffs.splice(pointer - count_delete - count_insert, | 
						|
                count_delete + count_insert, [DIFF_DELETE, text_delete], | 
						|
                [DIFF_INSERT, text_insert]); | 
						|
          } | 
						|
          pointer = pointer - count_delete - count_insert + | 
						|
                    (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; | 
						|
        } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) { | 
						|
          // Merge this equality with the previous one. | 
						|
          diffs[pointer - 1][1] += diffs[pointer][1]; | 
						|
          diffs.splice(pointer, 1); | 
						|
        } else { | 
						|
          pointer++; | 
						|
        } | 
						|
        count_insert = 0; | 
						|
        count_delete = 0; | 
						|
        text_delete = ''; | 
						|
        text_insert = ''; | 
						|
        break; | 
						|
    } | 
						|
  } | 
						|
  if (diffs[diffs.length - 1][1] === '') { | 
						|
    diffs.pop();  // Remove the dummy entry at the end. | 
						|
  } | 
						|
 | 
						|
  // Second pass: look for single edits surrounded on both sides by equalities | 
						|
  // which can be shifted sideways to eliminate an equality. | 
						|
  // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC | 
						|
  var changes = false; | 
						|
  pointer = 1; | 
						|
  // Intentionally ignore the first and last element (don't need checking). | 
						|
  while (pointer < diffs.length - 1) { | 
						|
    if (diffs[pointer - 1][0] == DIFF_EQUAL && | 
						|
        diffs[pointer + 1][0] == DIFF_EQUAL) { | 
						|
      // This is a single edit surrounded by equalities. | 
						|
      if (diffs[pointer][1].substring(diffs[pointer][1].length - | 
						|
          diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) { | 
						|
        // Shift the edit over the previous equality. | 
						|
        diffs[pointer][1] = diffs[pointer - 1][1] + | 
						|
            diffs[pointer][1].substring(0, diffs[pointer][1].length - | 
						|
                                        diffs[pointer - 1][1].length); | 
						|
        diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; | 
						|
        diffs.splice(pointer - 1, 1); | 
						|
        changes = true; | 
						|
      } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == | 
						|
          diffs[pointer + 1][1]) { | 
						|
        // Shift the edit over the next equality. | 
						|
        diffs[pointer - 1][1] += diffs[pointer + 1][1]; | 
						|
        diffs[pointer][1] = | 
						|
            diffs[pointer][1].substring(diffs[pointer + 1][1].length) + | 
						|
            diffs[pointer + 1][1]; | 
						|
        diffs.splice(pointer + 1, 1); | 
						|
        changes = true; | 
						|
      } | 
						|
    } | 
						|
    pointer++; | 
						|
  } | 
						|
  // If shifts were made, the diff needs reordering and another shift sweep. | 
						|
  if (changes) { | 
						|
    diff_cleanupMerge(diffs); | 
						|
  } | 
						|
}; | 
						|
 | 
						|
 | 
						|
var diff = diff_main; | 
						|
diff.INSERT = DIFF_INSERT; | 
						|
diff.DELETE = DIFF_DELETE; | 
						|
diff.EQUAL = DIFF_EQUAL; | 
						|
 | 
						|
module.exports = diff; | 
						|
 | 
						|
/* | 
						|
 * Modify a diff such that the cursor position points to the start of a change: | 
						|
 * E.g. | 
						|
 *   cursor_normalize_diff([[DIFF_EQUAL, 'abc']], 1) | 
						|
 *     => [1, [[DIFF_EQUAL, 'a'], [DIFF_EQUAL, 'bc']]] | 
						|
 *   cursor_normalize_diff([[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xyz']], 2) | 
						|
 *     => [2, [[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xy'], [DIFF_DELETE, 'z']]] | 
						|
 * | 
						|
 * @param {Array} diffs Array of diff tuples | 
						|
 * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds! | 
						|
 * @return {Array} A tuple [cursor location in the modified diff, modified diff] | 
						|
 */ | 
						|
function cursor_normalize_diff (diffs, cursor_pos) { | 
						|
  if (cursor_pos === 0) { | 
						|
    return [DIFF_EQUAL, diffs]; | 
						|
  } | 
						|
  for (var current_pos = 0, i = 0; i < diffs.length; i++) { | 
						|
    var d = diffs[i]; | 
						|
    if (d[0] === DIFF_DELETE || d[0] === DIFF_EQUAL) { | 
						|
      var next_pos = current_pos + d[1].length; | 
						|
      if (cursor_pos === next_pos) { | 
						|
        return [i + 1, diffs]; | 
						|
      } else if (cursor_pos < next_pos) { | 
						|
        // copy to prevent side effects | 
						|
        diffs = diffs.slice(); | 
						|
        // split d into two diff changes | 
						|
        var split_pos = cursor_pos - current_pos; | 
						|
        var d_left = [d[0], d[1].slice(0, split_pos)]; | 
						|
        var d_right = [d[0], d[1].slice(split_pos)]; | 
						|
        diffs.splice(i, 1, d_left, d_right); | 
						|
        return [i + 1, diffs]; | 
						|
      } else { | 
						|
        current_pos = next_pos; | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
  throw new Error('cursor_pos is out of bounds!') | 
						|
} | 
						|
 | 
						|
/* | 
						|
 * Modify a diff such that the edit position is "shifted" to the proposed edit location (cursor_position). | 
						|
 * | 
						|
 * Case 1) | 
						|
 *   Check if a naive shift is possible: | 
						|
 *     [0, X], [ 1, Y] -> [ 1, Y], [0, X]    (if X + Y === Y + X) | 
						|
 *     [0, X], [-1, Y] -> [-1, Y], [0, X]    (if X + Y === Y + X) - holds same result | 
						|
 * Case 2) | 
						|
 *   Check if the following shifts are possible: | 
						|
 *     [0, 'pre'], [ 1, 'prefix'] -> [ 1, 'pre'], [0, 'pre'], [ 1, 'fix'] | 
						|
 *     [0, 'pre'], [-1, 'prefix'] -> [-1, 'pre'], [0, 'pre'], [-1, 'fix'] | 
						|
 *         ^            ^ | 
						|
 *         d          d_next | 
						|
 * | 
						|
 * @param {Array} diffs Array of diff tuples | 
						|
 * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds! | 
						|
 * @return {Array} Array of diff tuples | 
						|
 */ | 
						|
function fix_cursor (diffs, cursor_pos) { | 
						|
  var norm = cursor_normalize_diff(diffs, cursor_pos); | 
						|
  var ndiffs = norm[1]; | 
						|
  var cursor_pointer = norm[0]; | 
						|
  var d = ndiffs[cursor_pointer]; | 
						|
  var d_next = ndiffs[cursor_pointer + 1]; | 
						|
 | 
						|
  if (d == null) { | 
						|
    // Text was deleted from end of original string, | 
						|
    // cursor is now out of bounds in new string | 
						|
    return diffs; | 
						|
  } else if (d[0] !== DIFF_EQUAL) { | 
						|
    // A modification happened at the cursor location. | 
						|
    // This is the expected outcome, so we can return the original diff. | 
						|
    return diffs; | 
						|
  } else { | 
						|
    if (d_next != null && d[1] + d_next[1] === d_next[1] + d[1]) { | 
						|
      // Case 1) | 
						|
      // It is possible to perform a naive shift | 
						|
      ndiffs.splice(cursor_pointer, 2, d_next, d) | 
						|
      return merge_tuples(ndiffs, cursor_pointer, 2) | 
						|
    } else if (d_next != null && d_next[1].indexOf(d[1]) === 0) { | 
						|
      // Case 2) | 
						|
      // d[1] is a prefix of d_next[1] | 
						|
      // We can assume that d_next[0] !== 0, since d[0] === 0 | 
						|
      // Shift edit locations.. | 
						|
      ndiffs.splice(cursor_pointer, 2, [d_next[0], d[1]], [0, d[1]]); | 
						|
      var suffix = d_next[1].slice(d[1].length); | 
						|
      if (suffix.length > 0) { | 
						|
        ndiffs.splice(cursor_pointer + 2, 0, [d_next[0], suffix]); | 
						|
      } | 
						|
      return merge_tuples(ndiffs, cursor_pointer, 3) | 
						|
    } else { | 
						|
      // Not possible to perform any modification | 
						|
      return diffs; | 
						|
    } | 
						|
  } | 
						|
} | 
						|
 | 
						|
/* | 
						|
 * Check diff did not split surrogate pairs. | 
						|
 * Ex. [0, '\uD83D'], [-1, '\uDC36'], [1, '\uDC2F'] -> [-1, '\uD83D\uDC36'], [1, '\uD83D\uDC2F'] | 
						|
 *     '\uD83D\uDC36' === '🐶', '\uD83D\uDC2F' === '🐯' | 
						|
 * | 
						|
 * @param {Array} diffs Array of diff tuples | 
						|
 * @return {Array} Array of diff tuples | 
						|
 */ | 
						|
function fix_emoji (diffs) { | 
						|
  var compact = false; | 
						|
  var starts_with_pair_end = function(str) { | 
						|
    return str.charCodeAt(0) >= 0xDC00 && str.charCodeAt(0) <= 0xDFFF; | 
						|
  } | 
						|
  var ends_with_pair_start = function(str) { | 
						|
    return str.charCodeAt(str.length-1) >= 0xD800 && str.charCodeAt(str.length-1) <= 0xDBFF; | 
						|
  } | 
						|
  for (var i = 2; i < diffs.length; i += 1) { | 
						|
    if (diffs[i-2][0] === DIFF_EQUAL && ends_with_pair_start(diffs[i-2][1]) && | 
						|
        diffs[i-1][0] === DIFF_DELETE && starts_with_pair_end(diffs[i-1][1]) && | 
						|
        diffs[i][0] === DIFF_INSERT && starts_with_pair_end(diffs[i][1])) { | 
						|
      compact = true; | 
						|
 | 
						|
      diffs[i-1][1] = diffs[i-2][1].slice(-1) + diffs[i-1][1]; | 
						|
      diffs[i][1] = diffs[i-2][1].slice(-1) + diffs[i][1]; | 
						|
 | 
						|
      diffs[i-2][1] = diffs[i-2][1].slice(0, -1); | 
						|
    } | 
						|
  } | 
						|
  if (!compact) { | 
						|
    return diffs; | 
						|
  } | 
						|
  var fixed_diffs = []; | 
						|
  for (var i = 0; i < diffs.length; i += 1) { | 
						|
    if (diffs[i][1].length > 0) { | 
						|
      fixed_diffs.push(diffs[i]); | 
						|
    } | 
						|
  } | 
						|
  return fixed_diffs; | 
						|
} | 
						|
 | 
						|
/* | 
						|
 * Try to merge tuples with their neigbors in a given range. | 
						|
 * E.g. [0, 'a'], [0, 'b'] -> [0, 'ab'] | 
						|
 * | 
						|
 * @param {Array} diffs Array of diff tuples. | 
						|
 * @param {Int} start Position of the first element to merge (diffs[start] is also merged with diffs[start - 1]). | 
						|
 * @param {Int} length Number of consecutive elements to check. | 
						|
 * @return {Array} Array of merged diff tuples. | 
						|
 */ | 
						|
function merge_tuples (diffs, start, length) { | 
						|
  // Check from (start-1) to (start+length). | 
						|
  for (var i = start + length - 1; i >= 0 && i >= start - 1; i--) { | 
						|
    if (i + 1 < diffs.length) { | 
						|
      var left_d = diffs[i]; | 
						|
      var right_d = diffs[i+1]; | 
						|
      if (left_d[0] === right_d[1]) { | 
						|
        diffs.splice(i, 2, [left_d[0], left_d[1] + right_d[1]]); | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
  return diffs; | 
						|
}
 | 
						|
 |