gui/html: Finally implementing restore operation

+ modal jQueryUI-based dialogs for restore operation
+ fixing a small bug with NDN/REST API

Unrelated note: unless we do something with JSON compression, we can't request more
than 10 items at a time (I tried 20, and they already didn't fit).

Change-Id: I9206fa9ac6b02062915a6f6980ad9e69eaa8da3c
diff --git a/gui/html/chronoshare.js b/gui/html/chronoshare.js
index b4c3d73..91fe02e 100644
--- a/gui/html/chronoshare.js
+++ b/gui/html/chronoshare.js
@@ -65,15 +65,35 @@
          return { request: request, callback: new HistoryClosure (this) };
      },
 
-     cmd_restore_file: function (filename, version, hash) {
+     cmd_restore_file: function (filename, version, hash, callback/*function (bool <- data received, status <- returned status)*/) {
          request = new Name ().add (this.restore)
              .add (filename)
              .addSegment (version)
              .add (hash);
          console.log (request.to_uri ());
+	 this.ndn.expressInterest (request, new CmdRestoreFileClosure (this, callback));
      }
  });
 
+$.Class ("CmdRestoreFileClosure", {}, {
+    init: function (chronoshare, callback) {
+        this.chronoshare = chronoshare;
+	this.callback = callback;
+    },
+    upcall: function(kind, upcallInfo) {
+        if (kind == Closure.UPCALL_CONTENT) {
+            convertedData = DataUtils.toString (upcallInfo.contentObject.content);
+	    this.callback (true, convertedData);
+	}
+        else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
+	    this.callback (false, "Interest timed out");
+        }
+        else {
+	    this.callback (false, "Unknown error happened");
+        }
+    }
+});
+
 $.Class ("FilesClosure", {}, {
     init: function (chronoshare) {
         this.chronoshare = chronoshare;
@@ -91,6 +111,9 @@
 
             tbody = $("<tbody />", { "id": "file-list-files" });
 
+            /// @todo Eventually set title for other pages
+            $("title").text ("ChronoShare - List of files" + (PARAMS.item?" - "+PARAMS.item:""));
+
             // error handling?
             newcontent = $("<div />", { "id": "content" }).append (
 		$("<h2 />").append ($(document.createTextNode("List of files ")), $("<green />").text (PARAMS.item)),
@@ -126,7 +149,7 @@
 			    .prepend ($("<img />", { "src": fileExtension(file.filename) })));
 		row.append ($("<td />", { "class": "version" }).text (file.version));
 		row.append ($("<td />", { "class": "size" }).text (SegNumToFileSize (file.segNum)));
-		row.append ($("<td />", { "class": "modified" }).text (new Date (file.timestamp)));
+		row.append ($("<td />", { "class": "modified" }).text (new Date (file.timestamp+"+00:00"))); // convert from UTC
 		row.append ($("<td />", { "class": "modified-by border-right"})
 			    .append ($("<userName />").text (file.owner.userName))
 			    .append ($("<seqNo> /").text (file.owner.seqNo)));
@@ -134,60 +157,11 @@
 		tbody = tbody.append (row);
             }
 
-            // if (!PARAMS.offset || PARAMS.offset==0)
-            // {
-            $("#content").fadeOut ("fast", function () {
-                $(this).replaceWith (newcontent);
-                $("#content").fadeIn ("fast");
-            });
+	    displayContent (newcontent, data.more, this.base_url ());
 
-            self = this; // small "cheat"
-            $("#content-nav").fadeOut ("fast", function () {
-                $("#content-nav a").hide ();
-
-                if (PARAMS.offset !== undefined || data.more !== undefined) {
-                    $("#content-nav").fadeIn ("fast");
-
-                    if (data.more !== undefined) {
-                        $("#get-more").show ();
-
-                        $("#get-more").unbind ('click').click (function () {
-                            url = self.base_url ();
-                            url += "&offset="+data.more;
-
-                            document.location = url;
-                        });
-                    }
-                    if (PARAMS.offset > 0) {
-                        $("#get-less").show ();
-
-                        $("#get-less").unbind ('click').click (function () {
-                            url = self.base_url ();
-                            if (PARAMS.offset > 1) {
-                                url += "&offset="+(PARAMS.offset - 1);
-                            }
-
-                            document.location = url;
-                        });
-                    }
-                }
-            });
-            // }
-            // else {
-            //     tbody.children ().each (function (index, row) {
-            //         $("#history-list-actions").append (row);
-            //     });
-            // }
-
+	    $.contextMenu( 'destroy',  ".with-context-menu" ); // cleanup
 	    $.contextMenu({
 		selector: ".with-context-menu",
-		events: {
-		    show: function (opt) {
-			console.log(opt.$trigger.attr ("filename"));
-			opt.items.info.name = opt.$trigger.attr ("filename");
-			console.log (opt.items.info.name);
-		    },
-		},
 		items: {
 		    "info": {name: "x", type: "html", html: "<b>File operations</b>"},
 		    "sep1": "---------",
@@ -196,9 +170,7 @@
 			      callback: function(key, opt) {
 				  openHistoryForItem (opt.$trigger.attr ("filename"));
 			      }},
-		    // bar: {name: "Bar", callback: function(key, opt){ alert("Bar!") }}
 		}
-		// there's more, have a look at the demos and docs...
 	    });
         }
         else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
@@ -240,6 +212,9 @@
 
             tbody = $("<tbody />", { "id": "history-list-actions" });
 
+            /// @todo Eventually set title for other pages
+            $("title").text ("ChronoShare - Recent actions" + (PARAMS.item?" - "+PARAMS.item:""));
+
             newcontent = $("<div />", { "id": "content" }).append (
 		$("<h2 />").append ($(document.createTextNode("Recent actions ")), $("<green />").text (PARAMS.item)),
                 $("<table />", { "class": "item-list" })
@@ -247,44 +222,42 @@
                              .append ($("<tr />")
                                       .append ($("<th />", { "class": "filename border-left", "scope": "col" }).text ("Filename"))
                                       .append ($("<th />", { "class": "version", "scope": "col" }).text ("Version"))
+                                      .append ($("<th />", { "class": "size", "scope": "col" }).text ("Size"))
                                       .append ($("<th />", { "class": "modified", "scope": "col" }).text ("Modified"))
                                       .append ($("<th />", { "class": "modified-by border-right", "scope": "col" }).text ("Modified By"))))
                     .append (tbody)
                     .append ($("<tfoot />")
                              .append ($("<tr />")
-                                      .append ($("<td />", { "colspan": "4", "class": "border-right border-left" })))));
+                                      .append ($("<td />", { "colspan": "5", "class": "border-right border-left" })))));
 
             for (var i = 0; i < data.actions.length; i++) {
                 action = data.actions[i];
 
-	        row = $("<tr />");
+		row = $("<tr />");
 	        if (i%2) { row.addClass ("odd"); }
-                if (action.action=="DELETE") { row.addClass ("delete"); }
+                if (action.action=="DELETE") {
+		    row.addClass ("delete");
+		}
+		else {
+		    row.addClass ("with-context-menu");
+                    row.attr ("file_version", action.version);
+		    row.attr ("file_hash", action.update.hash);
+		    row.attr ("file_modified_by", action.id.userName);
+		}
+
+                row.attr ("filename", action.filename);
+                row.bind('click', function (e) { openHistoryForItem ($(this).attr ("filename")) });
 
                 row.bind('mouseenter mouseleave', function() {
                     $(this).toggleClass('highlighted');
                 });
 
-                row.attr ("filename", action.filename);
-                row.attr ("file_version", action.version);
-		row.attr ("file_hash", action.hash);
-
-                // row.bind('click', function (e) {
-                //     url = "#fileHistory";
-                //     url += "&item=" + $(this).attr ("filename");
-                //     pos = URIPARAMS.indexOf ("&");
-                //     if (pos >= 0) {
-                //         url += URIPARAMS.substring (pos)
-                //     }
-
-                //     document.location = url;
-                // });
-
 		row.append ($("<td />", { "class": "filename border-left" })
 			    .text (action.filename)
 			    .prepend ($("<img />", { "src": fileExtension(action.filename) })));
 	        row.append ($("<td />", { "class": "version" }).text (action.version));
-	        row.append ($("<td />", { "class": "timestamp" }).text (new Date (action.timestamp)));
+		row.append ($("<td />", { "class": "size" }).text (action.update?SegNumToFileSize (action.update.segNum):""));
+	        row.append ($("<td />", { "class": "timestamp" }).text (new Date (action.timestamp+"+00:00"))); // conversion from UTC timezone (we store action time in UTC)
 	        row.append ($("<td />", { "class": "modified-by border-right" })
 	        	    .append ($("<userName />").text (action.id.userName))
 	        	    .append ($("<seqNo> /").text (action.id.seqNo)));
@@ -292,50 +265,58 @@
 	        tbody = tbody.append (row);
             }
 
-            // if (!PARAMS.offset || PARAMS.offset==0)
-            // {
-            $("#content").fadeOut ("fast", function () {
-                $(this).replaceWith (newcontent);
-                $("#content").fadeIn ("fast");
-            });
+	    displayContent (newcontent, data.more, this.base_url (PAGE));
 
-            self = this; // small "cheat"
-            $("#content-nav").fadeOut ("fast", function () {
-                $("#content-nav a").hide ();
+	    $.contextMenu( 'destroy',  ".with-context-menu" ); // cleanup
+	    $.contextMenu({
+		selector: ".with-context-menu",
+		items: {
+		    "sep1": "---------",
+		    restore: {name: "Restore this revision",
+			      icon: "cut", // need a better icon
+			      callback: function(key, opt) {
+				  filename = opt.$trigger.attr ("filename");
+				  version = opt.$trigger.attr ("file_version");
+				  hash = DataUtils.toNumbers (opt.$trigger.attr ("file_hash"));
+				  console.log (hash);
+				  modified_by = opt.$trigger.attr ("file_modified_by");
 
-                if (PARAMS.offset !== undefined || data.more !== undefined) {
-                    $("#content-nav").fadeIn ("fast");
+				  $("<div />", { "title": "Restore version " + version + "?" })
+				      .append ($("<p />")
+					       .append ($("<span />", { "class": "ui-icon ui-icon-alert",
+								        "style": "float: left; margin: 0 7px 50px 0;" }),
+							$(document.createTextNode ("Are you sure you want restore version ")),
+							$("<green/>").text (version),
+							$(document.createTextNode (" by ")),
+							$("<green/>").text (modified_by)))
+				      .dialog ({
+					  resizable: true,
+					  height:200,
+					  width:300,
+					  modal: true,
+					  buttons: {
+					      "Restore": function() {
+						  self = $(this);
+						  CHRONOSHARE.cmd_restore_file (filename, version, hash, function(didGetData, response) {
+						      if (!didGetData || response != "OK") {
+							  custom_alert (response);
+						      }
+						      console.log (response);
+						      self.dialog ("close");
 
-                    if (data.more !== undefined) {
-                        $("#get-more").show ();
-
-                        $("#get-more").unbind ('click').click (function () {
-                            url = self.base_url (PAGE);
-                            url += "&offset="+data.more;
-
-                            document.location = url;
-                        });
-                    }
-                    if (PARAMS.offset > 0) {
-                        $("#get-less").show ();
-
-                        $("#get-less").unbind ('click').click (function () {
-                            url = self.base_url (PAGE);
-                            if (PARAMS.offset > 1) {
-                                url += "&offset="+(PARAMS.offset - 1);
-                            }
-
-                            document.location = url;
-                        });
-                    }
-                }
-            });
-            // }
-            // else {
-            //     tbody.children ().each (function (index, row) {
-            //         $("#history-list-actions").append (row);
-            //     });
-            // }
+						      $.timer (function() {CHRONOSHARE.run ();}).once (1000);
+						  });
+					      },
+					      Cancel: function() {
+						  $(this).dialog ("close");
+					      }
+					  }
+				      });
+				  // openHistoryForItem (opt.$trigger.attr ("filename"));
+			      }},
+		    "sep2": "---------",
+		}
+	    });
         }
         else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
             $("#error").html ("Interest timed out");