From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

// Copied from: http://en.wikipedia.org/wiki/User:Stevage/EnhanceHistory.user.js



// ==UserScript==



// @name           Enhanced history display



// @namespace      stevage



// @description    Collapses consecutive edits from the same person into one, shows diffs on history page



// @include        *.wikipedia.org/*action=history



// ==/UserScript==

// This page should be found at http://en.wikipedia.org/wiki/User:Stevage/EnhanceHistory.user.js



// Install it from http://en.wikipedia.org/?action=raw&ctype=text/javascript&dontcountme=s&title=User:Stevage/EnhanceHistory.user.js



(

function() {

  GM_log('in blank function');

  function compress() {

    GM_log('in compress function');



    if (!document.getElementById('bodyContent')) {

        return;

    }

    

    this.add_buttons();

    

  }



  compress.prototype.add_buttons = function() {

    GM_log('in add_buttons');



    // Create the compress buttion

    var button1 = document.createElement('input');

    button1.setAttribute('id', 'compress_button1');

    button1.className = 'historysubmit';

    button1.style.marginLeft = '5px';

    button1.setAttribute('type', 'button');

    button1.value = 'Compress history';

    button1.onclick = function() { compress.start(); }



    // Create the ShowDiffs buttion

    var button1 = document.createElement('input');

    button1.setAttribute('id', 'showdiffs1');

    button1.className = 'historysubmit';

    button1.style.marginLeft = '5px';

    button1.setAttribute('type', 'button');

    button1.value = 'Show diffs';

    button1.onclick = function() { compress.showDiffs(); }



    // Add the button to the page

    var history = document.getElementById('pagehistory');

    history.parentNode.insertBefore(button1, history);

  }



/////////////////////////////////////////////////////////



  function getPlainText(s) {

    GM_log(">getPlainText");

    

    if (s==null)

      return "";

    var len = s.length;

    if (len > 20) {

      return "<small>" + s.substr(0,10)+'...'+ s.substr(len-10,10)+ "</small>";

    } else  {

      return "<small>" + s + "</small>";

    }

    GM_log("<getPlainText");

  }

  

  

  function diffString(text1, text2) {

  var d = diff(text1, text2);

  var html = '';

  for (var x=0; x<d.length; x++) {

    var m = dx][0]; // Mode (-1=delete, 0=copy, 1=add)

    var i = dx][1]; // Index of change.

    var t = dx][2]; // Text of change.

    t = t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

    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+"'>" +getPlainText(t) + "</SPAN>";

  }

  return html;

}



// Find the differences between two texts.  Return an array of changes.

function diff(text1, text2) {

  // Check for equality (speedup)

  if (text1 == text2)

    return [[0, 0, text1]];



  var a;

  // Trim off common prefix (speedup)

  a = diff_prefix(text1, text2);

  text1 = a0];

  text2 = a1];

  var commonprefix = a2];

  

  // Trim off common suffix (speedup)

  a = diff_suffix(text1, text2);

  text1 = a0];

  text2 = a1];

  var commonsuffix = a2];



  if (!text1) {  // Just add some text (speedup)

    a = [[1, commonprefix.length, text2]];

  } else if (!text2) { // Just delete some text (speedup)

    a = [[-1, commonprefix.length, text1]];

  } else {



    // Check to see if the problem can be split in two.

    var longtext = text1.length > text2.length ? text1 : text2;

    var shorttext = text1.length > text2.length ? text2 : text1;

    var hm = diff_halfmatch(longtext, shorttext, Math.ceil(longtext.length/4));

    if (!hm)

      hm = diff_halfmatch(longtext, shorttext, Math.ceil(longtext.length/2));

    if (hm) {

      if (text1.length > text2.length) {

        var text1_a = hm0];

        var text1_b = hm1];

        var text2_a = hm2];

        var text2_b = hm3];

      } else {

        var text2_a = hm0];

        var text2_b = hm1];

        var text1_a = hm2];

        var text1_b = hm3];

      }

      var mid_common = hm4];

      var result_a = diff(text1_a, text2_a);

      var result_b = diff(text1_b, text2_b);

      if (commonprefix) // Shift the indicies forwards due to the commonprefix.

        for (var x=0; x<result_a.length; x++)

          result_ax][1 += commonprefix.length;

      result_a.push([0, commonprefix.length+text2_a.length, mid_common]);

      while (result_b.length) {

        result_b0][1 += commonprefix.length+text2_a.length+mid_common.length;

        result_a.push(result_b.shift());

      }

      a = result_a;

    } else {

      var result = diff_map(text1, text2);

      if (result)

        a = diffchar2diffarray(result, commonprefix.length);

      else // No acceptable result.

        a = [[-1, commonprefix.length, text1], 1, commonprefix.length, text2]];

    }

  }



  if (commonprefix)

    a.unshift([0, 0, commonprefix]);

  if (commonsuffix)

    a.push([0, commonprefix.length + text2.length, commonsuffix]);  

  return a;

}



function diff_map(text1, text2) {

  // Explore the intersection points between the two texts.

  var now = new Date();

  var ms_end = now.getTime() + 1000; // Don't run for more than one second.

  var max = text1.length + text2.length;

  var v_map = new Array();

  var v = new Array();

  v1 = 0;

  var x, y;

  for (var d=0; d<=max; d++) {

    now = new Date();

    if (now.getTime() > ms_end) // JavaScript timeout reached

      return null;

    v_mapd = new Object;

    for (var k=-d; k<=d; k+=2) {

      if (k == -d || k != d && vk-1 < vk+1])

        x = vk+1];

      else

        x = vk-1+1;

      y = x - k;

      while (x < text1.length && y < text2.length && text1.charAt(x) == text2.charAt(y)) {

        x++; y++;

      }

      vk = x;

      v_mapd][k = x;

      if (x >= text1.length && y >= text2.length) {

        var str = diff_path(v_map, text1, text2);

        return str;

      }

    }

  }

  alert("No result.  Can't happen. (diff_map)");

  return null;

}



function diff_path(v_map, text1, text2) {

  // Work from the end back to the start to determine the path.

  var path = '';

  var x = text1.length;

  var y = text2.length;

  for (var d=v_map.length-2; d>=0; d--) {

    while(1) {

      if (diff_match(v_mapd], x-1, y)) {

        x--;

        path = "-"+text1.substring(x, x+1) + path;

        break;

      } else if (diff_match(v_mapd], x, y-1)) {

        y--;

        path = "+"+text2.substring(y, y+1) + path;

        break;

      } else {

        x--;

        y--;

        //if (text1.substring(x, x+1) != text2.substring(y, y+1))

        //  return alert("No diagonal.  Can't happen. (diff_path)");

        path = "="+text1.substring(x, x+1) + path;

      }

    }

  }

  return path;

}



function diff_match(v, x, y) {

  // Does the vector list contain an x/y coordinate?

  for (var k in v)

    if (vk == x && x-k == y)

      return true;

  return false;

}



function diff_prefix(text1, text2) {

  // Trim off common prefix

  var pointermin = 0;

  var pointermax = Math.min(text1.length, text2.length);

  var pointermid = pointermax;

  while(pointermin < pointermid) {

    if (text1.substring(0, pointermid) == text2.substring(0, pointermid))

      pointermin = pointermid;

    else

      pointermax = pointermid;

    pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);

  }

  var commonprefix = text1.substring(0, pointermid);

  text1 = text1.substring(pointermid);

  text2 = text2.substring(pointermid);

  return text1, text2, commonprefix];

}



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(longtext, shorttext, i) {

  // Do the two texts share a substring which is at least half the length of the longer text?

  // Start with a 1/4 length substring at position i as a seed.

  if (longtext.length < 10 || shorttext.length < 1)

    return null; // Pointless.

  var seed = longtext.substring(i, i+Math.floor(longtext.length/4));

  var j=0;

  var j_index;

  var best_common = '';

  while ((j_index = shorttext.substring(j).indexOf(seed)) != -1) {

    j += j_index;

    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_suffix2 + my_prefix2]).length) {

      best_common = my_suffix2 + my_prefix2];

      best_longtext_a = my_suffix0];

      best_longtext_b = my_prefix0];

      best_shorttext_a = my_suffix1];

      best_shorttext_b = my_prefix1];

    }

    j++;

  }

  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;

}



function diffchar2diffarray(text, offset) {

  // Convert '-h+c=a=t' into [[-1, 0, 'h'], [1, 0, 'c'], [0, 1, 'at']]

  // Old format: - remove char, = keep char, + add char

  // New format: array of [m, i, t]

  // Where m: -1 remove char, 0 keep char, 1 add char

  // Where i: index of change in first text

  // Where t: text to be added/kept/removed

  var i = 0;

  if (offset) i += offset;

  var a = new Array();

  var m;

  var last_m = null;

  for (var x=0; x<text.length; x+=2) {

    m = "-=+".indexOf(text.substring(x, x+1)) - 1;

    if (m == -2) return alert("Error: '"+text.substring(x, x+1)+"' is not one of '+=-'");

    if (last_m === m) {

      aa.length-1][2 += text.substring(x+1, x+2);

    } else {

      aa.length = new Array(m, i, text.substring(x+1, x+2));

    }

    last_m = m;

    if (m != -1) i++;

  }

  return a;

}



/*

  // JavaScript diff code thanks to John Resig (http://ejohn.org)

  // http://ejohn.org/files/jsdiff.js

  function diffString( o, n ) {

    GM_log(">diffstring " + o.length + "/" + n.length);

	  var out = diff( o.split(/\s+/), n.split(/\s+/) );

    GM_log("1diffstring");

	  var str = "";

    GM_log("2diffstring");

	  var plaintext = "";

    GM_log("3diffstring");

	  for ( var i = 0; i < out.n.length - 1; i++ ) {

		  if ( out.n[i].text == null ) {

			  if ( out.n[i].indexOf('"') == -1 && out.n[i].indexOf('<') == -1 && out.n[i].indexOf('=') == -1 ) {

  		    str += getPlainText(plaintext) + " " + "<b style='background:#E6FFE6;' class='diff'> " + out.n[i] +"</b>";

			    plaintext = "";

			  } else

				  plaintext += " " + out.n[i];

		  } else {

			  var pre = "";

			  if ( out.n[i].text.indexOf('"') == -1 && out.n[i].text.indexOf('<') == -1 && out.n[i].text.indexOf('=') == -1 ) {

  				

				  var n = out.n[i].row + 1;

				  while ( n < out.o.length && out.o[n].text == null ) {

					  if ( out.o[n].indexOf('"') == -1 && out.o[n].indexOf('<') == -1 && out.o[n].indexOf(':') == -1 && out.o[n].indexOf(';') == -1 && out.o[n].indexOf('=') == -1 )

						  pre += " <s style='background:#FFE6E6;' class='diff'>" + out.o[n] +" </s>";

					  n++;

				  }

			  }

			  plaintext = plaintext + " " + out.n[i].text;

			  if (pre!="") {

				  str += getPlainText(plaintext) + " " + pre;

				  plaintext = "";

			  }

		  } // if

	  } // for

    GM_log("<diffstring");

	  	

	  return str +" " +getPlainText(plaintext);

	}

	

		

	function diff( o, n ) {

		var ns = new Array();

		var os = new Array();

		

		for ( var i = 0; i < n.length; i++ ) {

			if ( ns[ n[i] ] == null )

				ns[ n[i] ] = { rows: new Array(), o: null };

			ns[ n[i] ].rows.push( i );

		}

		

		for ( var i = 0; i < o.length; i++ ) {

			if ( os[ o[i] ] == null )

				os[ o[i] ] = { rows: new Array(), n: null };

			os[ o[i] ].rows.push( i );

		}

		

		for ( var i in ns ) {

			if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) {

				n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] };

				o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] };

			}

		}

		

		for ( var i = 0; i < n.length - 1; i++ ) {

			if ( n[i].text != null && n[i+1].text == null && o[ n[i].row + 1 ].text == null && 

					 n[i+1] == o[ n[i].row + 1 ] ) {

				n[i+1] = { text: n[i+1], row: n[i].row + 1 };

				o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 };

			}

		}

		

		for ( var i = n.length - 1; i > 0; i-- ) {

			if ( n[i].text != null && n[i-1].text == null && o[ n[i].row - 1 ].text == null && 

					 n[i-1] == o[ n[i].row - 1 ] ) {

				n[i-1] = { text: n[i-1], row: n[i].row - 1 };

				o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 };

			}

		}

		

		return { o: o, n: n };

	}



*/

  function stripHTML(oldString) {

    var newString = "";

    var inTag = false;

    for(var i = 0; i < oldString.length; i++) {

      if(oldString.charAt(i) == '<') 

        inTag = true;

      if(oldString.charAt(i) == '>') {

        inTag = false;

        i++;

      }

      if(!inTag) 

        newString += oldString.charAt(i);

    }

    return newString;



  }





  compress.prototype.mediawiki_content = function(text) {

    GM_log(">mw_content:");

    if (text == "") {

      return text;

    } else {

      text = '' + text;

      var start = text.indexOf('<textarea');

      start += text.substr(start, 1000).indexOf('>') + 1;

      var end = text.indexOf('</textarea>');

      GM_log("<mw_content");

      text = text.substr(start, end - start);

      s = text.replace(/</g, "&lt;");

      s = s.replace(/>/g, "&gt;");

      GM_log ("Stripped: " + s.substr(0,50));

      return s;

    }

  }





  compress.prototype.start = function() {

    var hist = document.getElementById('pagehistory');

    if (hist) {

      var diffs;

      diffs = document.evaluate(

        "LI",

        hist,

        null,

        XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,

        null

      );

      var last='*x!', prevdiffcomment;



      for (var i = 0; i < diffs.snapshotLength; i++) {



        var diff = diffs.snapshotItem(i);

        var comment = document.evaluate(

          'SPAN[@class="comment"]',

          diff,

          null,

          XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,

          null

        ).snapshotItem(0);

        //GM_log(comment.innerHTML);

        var a = document.evaluate(

          "SPAN/A",

          diff,

          null,

          XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,

          null

        );

        eacha = a.snapshotItem(0);

        if (eacha.title==last) {

          if (comment) {

            prevdiffcomment.innerHTML = prevdiffcomment.innerHTML + '//' + comment.innerHTML;

          } else {

            prevdiffcomment.innerHTML = prevdiffcomment.innerHTML + '//---';

          }

          diff.parentNode.removeChild(diff);

        } else {

          last = eacha.title;

          if (!comment) {

            comment = document.createElement('SPAN');

            comment.className='comment';

            comment.innerHTML=' ---';

            diff.insertBefore(comment, null);

          }

          prevdiffcomment = comment;



        } //if

      }//for

    } //if hist

  } // function 'start'



  compress.prototype.loadDiff = function(urlno) {

    GM_log("in loadDiff");

    this.urlno = urlno;

    this.hostname = "en.wikipedia.org";

    var url = this.urlsurlno + '&action=edit';

    if (this.urlsurlno == null) {

      var details = new String("");

      details.responseText = ""; // force comparison with blank text;

      compress.loadedDiff(details);

      return;

    }

      

    GM_log(">loading!" + url);

    GM_xmlhttpRequest({

  	  method:'GET',

  	  url:url,

      headers:{

        'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',

        'Accept': 'application/xml',

        },

      onload:function(details) {

        //alert("hello " + details.status + '/' + details.statusText + '/' + details.responseHeaders);

        compress.loadedDiff(details);

      }

    });

    GM_log("<loading!" + url);

  

  }

  compress.prototype.loadedDiff = function(details) {

    GM_log(">loadedDiff "+this.urlno);

    this.pagesthis.urlno = this.mediawiki_content(details.responseText);

    GM_log("-loadedDiff "+this.urlno);

    if (this.urlno > 0) {

      s = diffString(this.pagesthis.urlno], this.pagesthis.urlno-1]);

      GM_log("done diff");

      wh = document.getElementById(this.infothis.urlno -1]);

      span = document.createElement('span');

      span.innerHTML = s;

      wh.insertBefore(span, null);

    }

    if (details.responseText != "") {

      compress.loadDiff(this.urlno+1); // if blank text, stop.

    }      

    GM_log("<loadedDiff");

    

  }



  compress.prototype.showDiffs = function() {

    var hist = document.getElementById('pagehistory');



    if (hist) {

      var diffs;

      diffs = document.evaluate(

        'LI/A[text() != "cur" and text() != "last"][1]',

        hist,

        null,

        XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,

        null

      );



      this.urls = new Array(diffs.snapshotLength);

      this.info = new Array(diffs.snapshotLength);

      this.pages = new Array(diffs.snapshotLength);

      

      GM_log("Number of A's: " + diffs.snapshotLength);

      

      for (var i = 0; i < diffs.snapshotLength; i++) {



        var diff = diffs.snapshotItem(i);

        

        diff.id = "difflink" + i;

        diff.parentNode.id = "diffli" + i;

        this.urlsi = diff.href;

        this.infoi = "diffli" + i;

        

        if (i==0) {

          this.loadDiff(0);

        }

      }//for

    } //if hist

  } // function 'start'



  

  var compress = new compress();

  document.compress = compress;



} // unnamed function



) ();