changeset 19:8ec70abb260d

Allow different local changeset revisions to be diffed.
author Atul Varma <varmaa@toolness.com>
date Thu, 12 Feb 2009 17:32:32 -0800
parents 530941426679
children 7497ac9a62b6
files jsdiff.js wiki.css wiki.html wiki.js
diffstat 4 files changed, 215 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jsdiff.js	Thu Feb 12 17:32:32 2009 -0800
@@ -0,0 +1,159 @@
+/*
+ * Javascript Diff Algorithm
+ *  By John Resig (http://ejohn.org/)
+ *  Modified by Chu Alan "sprite"
+ *
+ * More Info:
+ *  http://ejohn.org/projects/javascript-diff-algorithm/
+ */
+
+function escape(s) {
+    var n = s;
+    n = n.replace(/&/g, "&amp;");
+    n = n.replace(/</g, "&lt;");
+    n = n.replace(/>/g, "&gt;");
+    n = n.replace(/"/g, "&quot;");
+
+    return n;
+}
+
+function diffString( o, n ) {
+  o = o.replace(/\s+$/, '');
+  n = n.replace(/\s+$/, '');
+
+  var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );
+  var str = "";
+
+  var oSpace = o.match(/\s+/g);
+  if (oSpace == null) {
+    oSpace = ["\n"];
+  } else {
+    oSpace.push("\n");
+  }
+  var nSpace = n.match(/\s+/g);
+  if (nSpace == null) {
+    nSpace = ["\n"];
+  } else {
+    nSpace.push("\n");
+  }
+
+  if (out.n.length == 0) {
+      for (var i = 0; i < out.o.length; i++) {
+        str += '<del>' + escape(out.o[i]) + oSpace[i] + "</del>";
+      }
+  } else {
+    if (out.n[0].text == null) {
+      for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
+        str += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>";
+      }
+    }
+
+    for ( var i = 0; i < out.n.length; i++ ) {
+      if (out.n[i].text == null) {
+        str += '<ins>' + escape(out.n[i]) + nSpace[i] + "</ins>";
+      } else {
+        var pre = "";
+
+        for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
+          pre += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>";
+        }
+        str += " " + out.n[i].text + nSpace[i] + pre;
+      }
+    }
+  }
+
+  return str;
+}
+
+function randomColor() {
+    return "rgb(" + (Math.random() * 100) + "%, " +
+                    (Math.random() * 100) + "%, " +
+                    (Math.random() * 100) + "%)";
+}
+function diffString2( o, n ) {
+  o = o.replace(/\s+$/, '');
+  n = n.replace(/\s+$/, '');
+
+  var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );
+
+  var oSpace = o.match(/\s+/g);
+  if (oSpace == null) {
+    oSpace = ["\n"];
+  } else {
+    oSpace.push("\n");
+  }
+  var nSpace = n.match(/\s+/g);
+  if (nSpace == null) {
+    nSpace = ["\n"];
+  } else {
+    nSpace.push("\n");
+  }
+
+  var os = "";
+  var colors = new Array();
+  for (var i = 0; i < out.o.length; i++) {
+      colors[i] = randomColor();
+
+      if (out.o[i].text != null) {
+          os += '<span style="background-color: ' +colors[i]+ '">' +
+                escape(out.o[i].text) + oSpace[i] + "</span>";
+      } else {
+          os += "<del>" + escape(out.o[i]) + oSpace[i] + "</del>";
+      }
+  }
+
+  var ns = "";
+  for (var i = 0; i < out.n.length; i++) {
+      if (out.n[i].text != null) {
+          ns += '<span style="background-color: ' +colors[out.n[i].row]+ '">' +
+                escape(out.n[i].text) + nSpace[i] + "</span>";
+      } else {
+          ns += "<ins>" + escape(out.n[i]) + nSpace[i] + "</ins>";
+      }
+  }
+
+  return { o : os , n : ns };
+}
+
+function diff( o, n ) {
+  var ns = new Object();
+  var os = new Object();
+
+  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 && n[i].row + 1 < o.length && 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 && n[i].row > 0 && 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 };
+}
+
--- a/wiki.css	Thu Feb 12 13:09:28 2009 -0800
+++ b/wiki.css	Thu Feb 12 17:32:32 2009 -0800
@@ -4,6 +4,21 @@
     text-align: center;
 }
 
+.diff {
+    font-family: monaco, andale mono, lucidatypewriter, courier,
+                 courier new, monospace;
+    font-size: 10pt;
+    white-space: pre;
+}
+
+.diff ins {
+    background-color: green;
+}
+
+.diff del {
+    background-color: red;
+}
+
 #sidebar {
     opacity: 0.75;
     width: 160px;
@@ -44,6 +59,21 @@
     padding-right: 5px;
 }
 
+.changeset {
+    padding-left: 5px;
+    padding-right: 5px;
+    color: #444444;
+}
+
+.changeset.selected {
+    color: #000000;
+}
+
+.changeset.diffed {
+    background: red;
+    color: #000000;
+}
+
 #content {
     width: 640px;
     text-align: left;
--- a/wiki.html	Thu Feb 12 13:09:28 2009 -0800
+++ b/wiki.html	Thu Feb 12 17:32:32 2009 -0800
@@ -27,6 +27,7 @@
 </body>
 <script src="jquery.js"></script>
 <script src="jquery.timeago.js"></script>
+<script src="jsdiff.js"></script>
 <script src="wikicreole.js"></script>
 <script src="wiki.js"></script>
 </html>
--- a/wiki.js	Thu Feb 12 13:09:28 2009 -0800
+++ b/wiki.js	Thu Feb 12 17:32:32 2009 -0800
@@ -65,18 +65,38 @@
   var changeset = {date: new Date(),
                    content: markup};
   App.localChanges.push(changeset);
-  var changesetElem = $('<div class="changeset"></div>');
+  var changesetElem = $('<div class="changeset selected"></div>');
   function setText() {
     changesetElem.text(jQuery.timeago(changeset.date));
   }
   window.setInterval(setText, 60000);
   setText();
-  changesetElem.click(
-    function() {
-      $("#content").empty();
-      $("#content").append(App.createParts(changeset.content));
+  changesetElem.mousedown(
+    function(aEvt) {
+
+      aEvt.preventDefault();
+      if (aEvt.shiftKey) {
+        $('#local-changes').find('.changeset').removeClass('diffed');
+        var newerVersion = App.getMarkup($("#content"));
+        var diff = $('<div class="diff"></div>');
+        var markupDiv = $('<div class="creole-markup"></div>');
+        markupDiv.text(newerVersion);
+        diff.html(diffString(changeset.content, newerVersion));
+        $("#content").empty();
+        $("#content").append(diff);
+        $("#content").append(markupDiv);
+        $(this).addClass('diffed');
+      } else {
+        $('#local-changes').find('.changeset').removeClass('selected');
+        $('#local-changes').find('.changeset').removeClass('diffed');
+        $(this).addClass('selected');
+        $("#content").empty();
+        $("#content").append(App.createParts(changeset.content));
+      }
     }
   );
+  $('#local-changes').find('.changeset').removeClass('selected');
+  $('#local-changes').find('.changeset').removeClass('diffed');
   $('#local-changes').find('h1').after(changesetElem);
   if (doFadeIn)
     $('#local-changes').fadeIn();