blob: 38743ad60f485d14948e08175484ae2ecc54e6be [file] [log] [blame]
$.Class("ChronoShare", {}, {
init: function(username, foldername) {
$("#folder-name").text(foldername);
$("#user-name").text(username);
this.username = new Name(username);
this.files = new Name("/localhost")
.add(this.username)
.add("chronoshare")
.add(foldername)
.add("info")
.add("files")
.add("folder");
this.actions = new Name("/localhost")
.add(this.username)
.add("chronoshare")
.add(foldername)
.add("info")
.add("actions");
this.restore = new Name("/localhost")
.add(this.username)
.add("chronoshare")
.add(foldername)
.add("cmd")
.add("restore")
.add("file");
this.ndn = new NDN({host: "127.0.0.1"});
this.ndn.verify = false; // disable content verification, works WAAAAY faster
},
run: function() {
console.log("RUN page: " + PAGE);
$("#loader").fadeIn(500);
$("#error").addClass("hidden");
cmd = {};
if (PAGE == "fileList") {
cmd = this.info_files(PARAMS.item);
}
else if (PAGE == "folderHistory") {
cmd = this.info_actions("folder", PARAMS.item);
}
else if (PAGE == "fileHistory") {
cmd = this.info_actions("file", PARAMS.item);
}
if (cmd.request && cmd.callback) {
console.log(cmd.request.to_uri());
this.ndn.expressInterest(cmd.request, cmd.callback);
}
else {
$("#loader").fadeOut(500); // ("hidden");
$("#content").empty();
if (cmd.error) {
$("#error").html(cmd.error);
}
else {
$("#error").html("Unknown error with " + PAGE);
}
$("#error").removeClass("hidden");
}
},
info_files: function(folder) {
request = new Name()
.add(this.files)
./*add (folder_in_question).*/ addSegment(
PARAMS.offset ? PARAMS.offset : 0);
return {request: request, callback: new FilesClosure(this)};
},
info_actions: function(type /*"file" or "folder"*/,
fileOrFolder /*file or folder name*/) {
if (type == "file" && !fileOrFolder) {
return {error: "info_actions: fileOrFolder parameter is missing"};
}
request = new Name().add(this.actions).add(type);
if (fileOrFolder) {
request.add(fileOrFolder);
}
request.addSegment(PARAMS.offset ? PARAMS.offset : 0);
return {request: request, callback: new HistoryClosure(this)};
},
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));
},
get_file:
function(modifiedBy, hash, segments,
callback /*function (bool <- data received, data <- returned data)*/) {
baseName = new Name(modifiedBy).add("chronoshare").add("file").add(hash);
new FileGetter(this.ndn, baseName, segments, callback).start();
}
});
$.Class("CmdRestoreFileClosure", {}, {
init: function(chronoshare, callback) {
this.chronoshare = chronoshare;
this.callback = callback;
},
upcall: function(kind, upcallInfo) {
if (kind == Closure.UPCALL_CONTENT ||
kind == Closure.UPCALL_CONTENT_UNVERIFIED) { // disable content verification
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("FileGetter", {}, {
init: function(ndn, baseName, segments, callback) {
this.ndn = ndn;
this.callback = callback;
this.baseName = baseName;
this.segments = segments;
this.lastSegmentRequested = -1;
this.data = "";
},
start: function() {
this.lastSegmentRequested++;
request =
new Name().add(this.baseName).addSegment(this.lastSegmentRequested);
console.log(request.to_uri());
this.ndn.expressInterest(request, this);
},
upcall: function(kind, upcallInfo) {
if (kind == Closure.UPCALL_CONTENT ||
kind == Closure.UPCALL_CONTENT_UNVERIFIED) { // disable content verification
convertedData = DataUtils.toString(upcallInfo.contentObject.content);
this.data += convertedData;
if (this.lastSegmentRequested + 1 == this.segments) {
this.callback(true, this.data);
}
else {
this.start();
}
}
else {
this.callback(false, "Interest timed out");
}
}
});
$.Class("RestPipelineClosure", {}, {
init: function(collectionName, moreName) {
this.collectionName = collectionName;
this.moreName = moreName;
$("#json").empty();
this.collection = [];
this.counter = 0;
},
upcall: function(kind, upcallInfo) {
if (kind == Closure.UPCALL_CONTENT ||
kind == Closure.UPCALL_CONTENT_UNVERIFIED) { // disable content verification
convertedData = DataUtils.toString(upcallInfo.contentObject.content);
if (PARAMS.debug) {
$("#json").append($(document.createTextNode(convertedData)));
$("#json").removeClass("hidden");
}
data = JSON.parse(convertedData);
this.collection = this.collection.concat(data[this.collectionName]);
if (data[this.moreName] !== undefined) {
nextSegment =
upcallInfo.interest.name.cut(1).addSegment(data[this.moreName]);
this.counter++;
if (this.counter < 5) {
console.log("MORE: " + nextSegment.to_uri());
CHRONOSHARE.ndn.expressInterest(nextSegment, this);
}
else {
$("#loader").fadeOut(500); // ("hidden");
this.onData(this.collection, data[this.moreName]);
}
}
else {
$("#loader").fadeOut(500); // ("hidden");
this.onData(this.collection, undefined);
}
}
else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
$("#loader").fadeOut(500); // ("hidden");
this.onTimeout(upcallInfo.interest);
}
else {
$("#loader").fadeOut(500); // ("hidden");
this.onUnknownError(upcallInfo.interest);
}
return Closure.RESULT_OK; // make sure we never re-express the interest
},
onData: function(data, more) {},
onTimeout: function() {
$("#error").html("Interest timed out");
$("#error").removeClass("hidden");
},
onUnknownError: function() {
$("#error").html("Unknown error happened");
$("#error").removeClass("hidden");
}
});
// $.Class ("FilesClosure", {}, {
RestPipelineClosure("FilesClosure", {}, {
init: function(chronoshare) {
this._super("files", "more");
this.chronoshare = chronoshare;
},
onData: function(data, more) {
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)),
$("<table />", {"class": "item-list"})
.append(
$("<thead />")
.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": "5",
"class": "border-right border-left"
})))));
newcontent.hide();
for (var i = 0; i < data.length; i++) {
file = data[i];
row = $("<tr />", {"class": "with-context-menu"});
if (i % 2) {
row.addClass("odd");
}
row.bind('mouseenter mouseleave', function() {
$(this).toggleClass('highlighted');
});
row.attr("filename",
file.filename); // encodeURIComponent(encodeURIComponent(file.filename)));
row.bind('click', function(e) {
openHistoryForItem($(this).attr("filename"))
});
row.append(
$("<td />", {"class": "filename border-left"})
.text(file.filename)
.prepend(
$("<img />", {"src": imgFullPath(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 + "+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)));
tbody = tbody.append(row);
}
displayContent(newcontent, more, this.base_url());
$.contextMenu('destroy', ".with-context-menu"); // cleanup
$.contextMenu({
selector: ".with-context-menu",
items: {
"info": {name: "x", type: "html", html: "<b>File operations</b>"},
"sep1": "---------",
history: {
name: "View file history",
icon: "quit", // need a better icon
callback: function(key, opt) {
openHistoryForItem(opt.$trigger.attr("filename"));
}
},
}
});
},
base_url: function() {
url = "#fileList" +
"&user=" + encodeURIComponent(encodeURIComponent(PARAMS.user)) +
"&folder=" + encodeURIComponent(encodeURIComponent(PARAMS.folder));
if (PARAMS.item !== undefined) {
url += "&item=" + encodeURIComponent(encodeURIComponent(PARAMS.item));
}
return url;
}
});
RestPipelineClosure("HistoryClosure", {}, {
init: function(chronoshare) {
this._super("actions", "more");
this.chronoshare = chronoshare;
},
previewFile: function(file) {
if (fileExtension(file.attr("filename")) == "txt") {
CHRONOSHARE.get_file(file.attr("file_modified_by"),
DataUtils.toNumbers(file.attr("file_hash")),
file.attr("file_seg_num"), function(status, data) {
$("<div />", {
"title": "Preview of " + file.attr("filename") +
" version " + file.attr("file_version")
})
.append($("<pre />").text(data))
.dialog({
resizable: true,
width: $(window).width() * 0.8,
maxHeight: $(window).height() * 0.8,
show: "blind",
hide: "fold",
modal: true,
});
});
}
else {
custom_alert("Preview is not support for this type of file");
}
},
onData: function(data, more) {
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"})
.append(
$("<thead />")
.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": "5",
"class": "border-right border-left"
})))));
for (var i = 0; i < data.length; i++) {
action = data[i];
row = $("<tr />");
if (i % 2) {
row.addClass("odd");
}
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_seg_num", action.update.segNum);
row.attr("file_modified_by", action.id.userName);
}
row.attr("filename", action.filename);
self = this;
if (PARAMS.item != action.filename) {
row.bind('click', function(e) {
openHistoryForItem($(this).attr("filename"))
});
}
else {
row.bind('click', function(e) {
self.previewFile($(this));
});
}
row.bind('mouseenter mouseleave', function() {
$(this).toggleClass('highlighted');
});
row.append(
$("<td />", {"class": "filename border-left"})
.text(action.filename)
.prepend($("<img />",
{"src": imgFullPath(fileExtension(action.filename))})));
row.append($("<td />", {"class": "version"}).text(action.version));
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)));
tbody = tbody.append(row);
}
displayContent(newcontent, more, this.base_url(PAGE));
self = this;
$.contextMenu('destroy', ".with-context-menu"); // cleanup
$.contextMenu({
selector: ".with-context-menu",
items: {
"sep1": "---------",
preview: {
name: "Preview revision",
icon: "edit", // ned a better icon
callback: function(key, opt) {
self.previewFile(opt.$trigger);
}
},
"sep3": "---------",
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");
$("<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,
show: "blind",
hide: "fold",
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");
$.timer(function() {
CHRONOSHARE.run();
})
.once(1000);
});
},
Cancel: function() {
$(this).dialog("close");
}
}
});
// openHistoryForItem (opt.$trigger.attr ("filename"));
}
},
"sep2": "---------",
}
});
},
base_no_item_url: function(page) {
url = "#" + page + "&user=" +
encodeURIComponent(encodeURIComponent(PARAMS.user)) + "&folder=" +
encodeURIComponent(encodeURIComponent(PARAMS.folder));
return url;
},
base_url: function(page) {
url = "#" + page + "&user=" +
encodeURIComponent(encodeURIComponent(PARAMS.user)) + "&folder=" +
encodeURIComponent(encodeURIComponent(PARAMS.folder));
if (PARAMS.item !== undefined) {
url += "&item=" + encodeURIComponent(encodeURIComponent(PARAMS.item));
}
return url;
}
});