Difference detection

by Arnaud Buchholz

HTML

<h1>LIMIT IS <span id="limit"></span>...</h1>

<div class="test" >
  <div class="string from">abc</div>
  <div class="string to">abdc</div>  
</div>


<div class="test">
  <div class="string from">Arnaud a une idee</div>
  <div class="string to">Arnaud a une idee</div>  
</div>

<div class="test">
  <div class="string from">Arnaud a une idee</div>
  <div class="string to">Arnaud a une bonne idee</div>  
</div>

<div class="test">
  <div class="string from">Arnaud a une idee</div>
  <div class="string to">Arnaud a vraiment une bonne idee</div>  
</div>

<div class="test">
  <div class="string from">Arnaud a vraiment une bonne idee</div>  
  <div class="string to">Arnaud a une idee</div>
</div>

<div class="test">
  <div class="string from">Arnaud a une idee</div>
  <div class="string to">Azzedine a vraiment une bonne idee</div>  
</div>

<div class="test">
  <div class="string from">Arnaud a une idee</div>
  <div class="string to">Jonathan a vraiment une bonne idee</div>  
</div>

<div class="test">
  <div class="string from">Arnaud est perdu</div>
  <div class="string to">Azzedine a vraiment une bonne idee</div>  
</div>

<div class="test">
  <div class="string from">Arnaud est perdu</div>
  <div class="string to">Jonathan a vraiment une bonne idee</div>  
</div>

<div class="test">
  <div class="string from">Jonathan a vraiment une bonne idee</div>  
  <div class="string to">Arnaud est perdu</div>
</div>

<div class="test">
  <div class="string from">Arnaud dit que Jonathan a une bonne idée</div>  
  <div class="string to">Arnaud dit que Isabelle a vraiment une bonne idée</div>
</div>

CSS

div.test {
  padding-bottom: 1rem;
}

div.string {
  font-family: monospace;
  white-space: pre;
}

div.string span:nth-child(1) {
  background-color: lightgreen;
  border-right: solid 1px green;
}

div.string span:nth-child(2) {
  background-color: orange;
}

div.string span:nth-child(3) {
  background-color: lightgreen;
  border-left: solid 1px green;
}

JavaScript

// Visually show the matching start
function tag(name, content) {
  var element = document.createElement(name);
  element.innerHTML = content;
  return element;
}

function show (div, text, start, end) {
	div.innerHTML = "";
	div.appendChild(tag("span", text.substr(0, start)));
	div.appendChild(tag("span", text.substr(start, end - start)));
	div.appendChild(tag("span", text.substr(end)));
}

var LIMIT = 5,
		BEFORE = 10,
		ELLIPSIS = "\u2026";
    
document.getElementById("limit").innerHTML = LIMIT;

[].slice.call(document.querySelectorAll("div.test")).forEach(function (test) {

  var div_from = test.querySelector(".from"),
      div_to = test.querySelector(".to"); 

  var ls_from = div_from.innerHTML,
      ls_to = div_to.innerHTML;

  var li_start = 0,
      li_from_end = ls_from.length - 1,
      li_to_end = ls_to.length - 1;

  while (li_start <= li_from_end  && li_start <= li_to_end &&
  			 ls_from.charAt(li_start) === ls_to.charAt(li_start)) {
    ++li_start;
  }

  while (li_from_end > li_start  && li_to_end > li_start &&
         ls_from.charAt(li_from_end) === ls_to.charAt(li_to_end)) {
    --li_from_end;
    --li_to_end;
  }
  if (ls_from.charAt(li_from_end) !== ls_to.charAt(li_to_end)) {
    ++li_from_end;
    ++li_to_end;
  }

  show(div_from, ls_from, li_start, li_from_end);
  show(div_to, ls_to, li_start, li_to_end);
  
  
  
  var diffFrom = ls_from.substr(li_start, li_from_end - li_start),
  		diffTo = ls_to.substr(li_start, li_to_end - li_start);
      
	function getVisualDiff (ls_value, ls_diff, li_position) {
  	if (ls_diff.length <= LIMIT) {
    	if (li_position === 0) {
      	return ls_value.substr(0, LIMIT - 1) + ELLIPSIS;
      }
      if (li_position > LIMIT) {
      	return ELLIPSIS + ls_value.substr(li_position, LIMIT - 1);
      }
    
      // diff is smaller than the limit, include some of the original string to give context
    	return ls_diff;
    }
    if (ls_diff.length > LIMIT) {
    	return ls_diff.substr(0, LIMIT - 1) +...