Merge branch 'mark-as-read' into next
[Sone.git] / src / main / resources / static / javascript / sone.js
index f7a686d..a0bbf8a 100644 (file)
@@ -47,6 +47,7 @@ function registerInputTextareaSwap(inputElement, defaultText, inputFieldName, op
                                textarea.show();
                        }
                        $(inputField.get(0).form).submit(function() {
+                               inputField.attr("disabled", "disabled");
                                if (!optional && (textarea.val() == "")) {
                                        return false;
                                }
@@ -64,10 +65,11 @@ function registerInputTextareaSwap(inputElement, defaultText, inputFieldName, op
  *            The element to add a “comment” link to
  */
 function addCommentLink(postId, element, insertAfterThisElement) {
-       if ($(element).find(".show-reply-form").length > 0) {
+       if (($(element).find(".show-reply-form").length > 0) || (getPostElement(element).find(".create-reply").length == 0)) {
                return;
        }
        commentElement = (function(postId) {
+               separator = $("<span> · </span>").addClass("separator");
                var commentElement = $("<div><span>Comment</span></div>").addClass("show-reply-form").click(function() {
                        markPostAsKnown(getPostElement(this));
                        replyElement = $("#sone .post#" + postId + " .create-reply");
@@ -87,6 +89,7 @@ function addCommentLink(postId, element, insertAfterThisElement) {
                return commentElement;
        })(postId);
        $(insertAfterThisElement).after(commentElement.clone(true));
+       $(insertAfterThisElement).after(separator);
 }
 
 var translations = {};
@@ -150,7 +153,13 @@ function updateSoneStatus(soneId, name, status, modified, locked, lastUpdated) {
                toggleClass("modified", modified);
        $("#sone .sone." + filterSoneId(soneId) + " .lock").toggleClass("hidden", locked);
        $("#sone .sone." + filterSoneId(soneId) + " .unlock").toggleClass("hidden", !locked);
-       $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(lastUpdated);
+       if (lastUpdated != null) {
+               $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(lastUpdated);
+       } else {
+               getTranslation("View.Sone.Text.UnknownDate", function(unknown) {
+                       $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(unknown);
+               });
+       }
        $("#sone .sone." + filterSoneId(soneId) + " .profile-link a").text(name);
 }
 
@@ -301,6 +310,17 @@ function getPostTime(element) {
        return getPostElement(element).find(".post-time").text();
 }
 
+/**
+ * Returns the author of the post the given element belongs to.
+ *
+ * @param element
+ *            The element whose post to get the author for
+ * @returns The ID of the authoring Sone
+ */
+function getPostAuthor(element) {
+       return getPostElement(element).find(".post-author").text();
+}
+
 function getReplyElement(element) {
        return $(element).closest(".reply");
 }
@@ -313,6 +333,17 @@ function getReplyTime(element) {
        return getReplyElement(element).find(".reply-time").text();
 }
 
+/**
+ * Returns the author of the reply the given element belongs to.
+ *
+ * @param element
+ *            The element whose reply to get the author for
+ * @returns The ID of the authoring Sone
+ */
+function getReplyAuthor(element) {
+       return getReplyElement(element).find(".reply-author").text();
+}
+
 function likePost(postId) {
        $.getJSON("like.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data, textStatus) {
                if ((data == null) || !data.success) {
@@ -377,6 +408,74 @@ function unlikeReply(replyId) {
        });
 }
 
+/**
+ * Trusts the Sone with the given ID.
+ *
+ * @param soneId
+ *            The ID of the Sone to trust
+ */
+function trustSone(soneId) {
+       $.getJSON("trustSone.ajax", { "formPassword" : getFormPassword(), "sone" : soneId }, function(data, textStatus) {
+               if ((data != null) && data.success) {
+                       updateTrustControls(soneId, data.trustValue);
+               }
+       });
+}
+
+/**
+ * Distrusts the Sone with the given ID, i.e. assigns a negative trust value.
+ *
+ * @param soneId
+ *            The ID of the Sone to distrust
+ */
+function distrustSone(soneId) {
+       $.getJSON("distrustSone.ajax", { "formPassword" : getFormPassword(), "sone" : soneId }, function(data, textStatus) {
+               if ((data != null) && data.success) {
+                       updateTrustControls(soneId, data.trustValue);
+               }
+       });
+}
+
+/**
+ * Untrusts the Sone with the given ID, i.e. removes any trust assignment.
+ *
+ * @param soneId
+ *            The ID of the Sone to untrust
+ */
+function untrustSone(soneId) {
+       $.getJSON("untrustSone.ajax", { "formPassword" : getFormPassword(), "sone" : soneId }, function(data, textStatus) {
+               if ((data != null) && data.success) {
+                       updateTrustControls(soneId, data.trustValue);
+               }
+       });
+}
+
+/**
+ * Updates the trust controls for all posts and replies of the given Sone,
+ * according to the given trust value.
+ *
+ * @param soneId
+ *            The ID of the Sone to update all trust controls for
+ * @param trustValue
+ *            The trust value for the Sone
+ */
+function updateTrustControls(soneId, trustValue) {
+       $("#sone .post").each(function() {
+               if (getPostAuthor(this) == soneId) {
+                       getPostElement(this).find(".post-trust").toggleClass("hidden", trustValue != null);
+                       getPostElement(this).find(".post-distrust").toggleClass("hidden", trustValue != null);
+                       getPostElement(this).find(".post-untrust").toggleClass("hidden", trustValue == null);
+               }
+       });
+       $("#sone .reply").each(function() {
+               if (getReplyAuthor(this) == soneId) {
+                       getReplyElement(this).find(".reply-trust").toggleClass("hidden", trustValue != null);
+                       getReplyElement(this).find(".reply-distrust").toggleClass("hidden", trustValue != null);
+                       getReplyElement(this).find(".reply-untrust").toggleClass("hidden", trustValue == null);
+               }
+       });
+}
+
 function updateReplyLikes(replyId) {
        $.getJSON("getLikes.ajax", { "type": "reply", "reply": replyId }, function(data, textStatus) {
                if ((data != null) && data.success) {
@@ -392,6 +491,8 @@ function updateReplyLikes(replyId) {
 /**
  * Posts a reply and calls the given callback when the request finishes.
  *
+ * @param sender
+ *            The ID of the sender
  * @param postId
  *            The ID of the post the reply refers to
  * @param text
@@ -400,14 +501,14 @@ function updateReplyLikes(replyId) {
  *            The callback function to call when the request finishes (takes 3
  *            parameters: success, error, replyId)
  */
-function postReply(postId, text, callbackFunction) {
-       $.getJSON("createReply.ajax", { "formPassword" : getFormPassword(), "post" : postId, "text": text }, function(data, textStatus) {
+function postReply(sender, postId, text, callbackFunction) {
+       $.getJSON("createReply.ajax", { "formPassword" : getFormPassword(), "sender": sender, "post" : postId, "text": text }, function(data, textStatus) {
                if (data == null) {
                        /* TODO - show error */
                        return;
                }
                if (data.success) {
-                       callbackFunction(true, null, data.reply);
+                       callbackFunction(true, null, data.reply, data.sone);
                } else {
                        callbackFunction(false, data.error);
                }
@@ -446,21 +547,25 @@ function ajaxifyPost(postElement) {
                return false;
        });
        $(postElement).find(".create-reply button:submit").click(function() {
-               inputField = $(this.form).find(":input:enabled").get(0);
+               sender = $(this.form).find(":input[name=sender]").val();
+               inputField = $(this.form).find(":input[name=text]:enabled").get(0);
                postId = getPostId(this);
                text = $(inputField).val();
-               (function(postId, text, inputField) {
-                       postReply(postId, text, function(success, error, replyId) {
+               (function(sender, postId, text, inputField) {
+                       postReply(sender, postId, text, function(success, error, replyId, soneId) {
                                if (success) {
                                        $(inputField).val("");
-                                       loadNewReply(replyId);
+                                       loadNewReply(replyId, soneId, postId);
                                        markPostAsKnown(getPostElement(inputField));
                                        $("#sone .post#" + postId + " .create-reply").addClass("hidden");
+                                       $("#sone .post#" + postId + " .create-reply .sender").hide();
+                                       $("#sone .post#" + postId + " .create-reply .select-sender").show();
+                                       $("#sone .post#" + postId + " .create-reply :input[name=sender]").val(getCurrentSoneId());
                                } else {
                                        alert(error);
                                }
                        });
-               })(postId, text, inputField);
+               })(sender, postId, text, inputField);
                return false;
        });
 
@@ -484,6 +589,20 @@ function ajaxifyPost(postElement) {
                return false;
        });
 
+       /* convert trust control buttons to javascript functions. */
+       $(postElement).find(".post-trust").submit(function() {
+               trustSone(getPostAuthor(this));
+               return false;
+       });
+       $(postElement).find(".post-distrust").submit(function() {
+               distrustSone(getPostAuthor(this));
+               return false;
+       });
+       $(postElement).find(".post-untrust").submit(function() {
+               untrustSone(getPostAuthor(this));
+               return false;
+       });
+
        /* add “comment” link. */
        addCommentLink(getPostId(postElement), postElement, $(postElement).find(".post-status-line .time"));
 
@@ -499,8 +618,20 @@ function ajaxifyPost(postElement) {
                });
        });
 
+       /* process sender selection. */
+       $(".select-sender", postElement).css("display", "inline");
+       $(".sender", postElement).hide();
+       $(".select-sender button", postElement).click(function() {
+               $(".sender", postElement).show();
+               $(".select-sender", postElement).hide();
+               return false;
+       });
+
        /* mark everything as known on click. */
-       $(postElement).click(function() {
+       $(postElement).click(function(event) {
+               if ($(event.target).hasClass("click-to-show")) {
+                       return false;
+               }
                markPostAsKnown(this);
        });
 
@@ -534,6 +665,20 @@ function ajaxifyReply(replyElement) {
        })(replyElement);
        addCommentLink(getPostId(replyElement), replyElement, $(replyElement).find(".reply-status-line .time"));
 
+       /* convert trust control buttons to javascript functions. */
+       $(replyElement).find(".reply-trust").submit(function() {
+               trustSone(getReplyAuthor(this));
+               return false;
+       });
+       $(replyElement).find(".reply-distrust").submit(function() {
+               distrustSone(getReplyAuthor(this));
+               return false;
+       });
+       $(replyElement).find(".reply-untrust").submit(function() {
+               untrustSone(getReplyAuthor(this));
+               return false;
+       });
+
        /* mark post and all replies as known on click. */
        $(replyElement).click(function() {
                markPostAsKnown(getPostElement(this));
@@ -547,9 +692,17 @@ function ajaxifyReply(replyElement) {
  *            jQuery object representing the notification.
  */
 function ajaxifyNotification(notification) {
-       notification.find("form.dismiss").submit(function() {
+       notification.find("form").submit(function() {
                return false;
        });
+       notification.find("input[name=returnPage]").val($.url.attr("relative"));
+       if (notification.find(".short-text").length > 0) {
+               notification.find(".short-text").removeClass("hidden");
+               notification.find(".text").addClass("hidden");
+       }
+       notification.find("form.mark-as-read button").click(function() {
+               $.getJSON("markAsKnown.ajax", {"formPassword": getFormPassword(), "type": $(":input[name=type]", this.form).val(), "id": $(":input[name=id]", this.form).val()});
+       });
        notification.find("form.dismiss button").click(function() {
                $.getJSON("dismissNotification.ajax", { "formPassword" : getFormPassword(), "notification" : notification.attr("id") }, function(data, textStatus) {
                        /* dismiss in case of error, too. */
@@ -566,7 +719,7 @@ function getStatus() {
                if ((data != null) && data.success) {
                        /* process Sone information. */
                        $.each(data.sones, function(index, value) {
-                               updateSoneStatus(value.id, value.name, value.status, value.modified, value.locked, value.lastUpdated);
+                               updateSoneStatus(value.id, value.name, value.status, value.modified, value.locked, value.lastUpdatedUnknown ? null : value.lastUpdated);
                        });
                        /* process notifications. */
                        $.each(data.notifications, function(index, value) {
@@ -585,11 +738,11 @@ function getStatus() {
                        });
                        /* process new posts. */
                        $.each(data.newPosts, function(index, value) {
-                               loadNewPost(value);
+                               loadNewPost(value.id, value.sone, value.recipient, value.time);
                        });
                        /* process new replies. */
                        $.each(data.newReplies, function(index, value) {
-                               loadNewReply(value);
+                               loadNewReply(value.id, value.sone, value.post, value.postSone);
                        });
                        /* do it again in 5 seconds. */
                        setTimeout(getStatus, 5000);
@@ -706,11 +859,18 @@ function hasReply(replyId) {
        return $("#sone .reply#" + replyId).length > 0;
 }
 
-function loadNewPost(postId) {
+function loadNewPost(postId, soneId, recipientId, time) {
        if (hasPost(postId)) {
                return;
        }
-       if (!isIndexPage() && (!isViewPostPage() || (getShownPostId() != postId))) {
+       if (!isIndexPage()) {
+               if (!isViewPostPage() || (getShownPostId() != postId)) {
+                       if (!isViewSonePage() || ((getShownSoneId() != soneId) && (getShownSoneId() != recipientId))) {
+                               return;
+                       }
+               }
+       }
+       if (getPostTime($("#sone .post").last()) > time) {
                return;
        }
        $.getJSON("getPost.ajax", { "post" : postId }, function(data, textStatus) {
@@ -731,8 +891,6 @@ function loadNewPost(postId) {
                        newPost = $(data.post.html).addClass("hidden");
                        if (firstOlderPost != null) {
                                newPost.insertBefore(firstOlderPost);
-                       } else {
-                               $("#sone #posts").append(newPost);
                        }
                        ajaxifyPost(newPost);
                        newPost.slideDown();
@@ -741,10 +899,13 @@ function loadNewPost(postId) {
        });
 }
 
-function loadNewReply(replyId) {
+function loadNewReply(replyId, soneId, postId, postSoneId) {
        if (hasReply(replyId)) {
                return;
        }
+       if (!hasPost(postId)) {
+               return;
+       }
        $.getJSON("getReply.ajax", { "reply": replyId }, function(data, textStatus) {
                /* find post. */
                if ((data != null) && data.success) {
@@ -783,8 +944,9 @@ function markPostAsKnown(postElements) {
                postElement = this;
                if ($(postElement).hasClass("new")) {
                        (function(postElement) {
-                               $.getJSON("markPostAsKnown.ajax", {"formPassword": getFormPassword(), "post": getPostId(postElement)}, function(data, textStatus) {
+                               $.getJSON("markAsKnown.ajax", {"formPassword": getFormPassword(), "type": "post", "id": getPostId(postElement)}, function(data, textStatus) {
                                        $(postElement).removeClass("new");
+                                       $(".click-to-show", postElement).removeClass("new");
                                });
                        })(postElement);
                }
@@ -797,7 +959,7 @@ function markReplyAsKnown(replyElements) {
                replyElement = this;
                if ($(replyElement).hasClass("new")) {
                        (function(replyElement) {
-                               $.getJSON("markReplyAsKnown.ajax", {"formPassword": getFormPassword(), "reply": getReplyId(replyElement)}, function(data, textStatus) {
+                               $.getJSON("markAsKnown.ajax", {"formPassword": getFormPassword(), "type": "reply", "id": getReplyId(replyElement)}, function(data, textStatus) {
                                        $(replyElement).removeClass("new");
                                });
                        })(replyElement);
@@ -854,6 +1016,80 @@ function showNotificationDetails(notificationId) {
        $("#sone .notification#" + notificationId + " .short-text").hide();
 }
 
+/**
+ * Deletes the field with the given ID from the profile.
+ *
+ * @param fieldId
+ *            The ID of the field to delete
+ */
+function deleteProfileField(fieldId) {
+       $.getJSON("deleteProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId}, function(data, textStatus) {
+               if (data && data.success) {
+                       $("#sone .profile-field#" + data.field.id).slideUp();
+               }
+       });
+}
+
+/**
+ * Renames a profile field.
+ *
+ * @param fieldId
+ *            The ID of the field to rename
+ * @param newName
+ *            The new name of the field
+ * @param successFunction
+ *            Called when the renaming was successful
+ */
+function editProfileField(fieldId, newName, successFunction) {
+       $.getJSON("editProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId, "name": newName}, function(data, textStatus) {
+               if (data && data.success) {
+                       successFunction();
+               }
+       });
+}
+
+/**
+ * Moves the profile field with the given ID one slot in the given direction.
+ *
+ * @param fieldId
+ *            The ID of the field to move
+ * @param direction
+ *            The direction to move in (“up” or “down”)
+ * @param successFunction
+ *            Function to call on success
+ */
+function moveProfileField(fieldId, direction, successFunction) {
+       $.getJSON("moveProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId, "direction": direction}, function(data, textStatus) {
+               if (data && data.success) {
+                       successFunction();
+               }
+       });
+}
+
+/**
+ * Moves the profile field with the given ID up one slot.
+ *
+ * @param fieldId
+ *            The ID of the field to move
+ * @param successFunction
+ *            Function to call on success
+ */
+function moveProfileFieldUp(fieldId, successFunction) {
+       moveProfileField(fieldId, "up", successFunction);
+}
+
+/**
+ * Moves the profile field with the given ID down one slot.
+ *
+ * @param fieldId
+ *            The ID of the field to move
+ * @param successFunction
+ *            Function to call on success
+ */
+function moveProfileFieldDown(fieldId, successFunction) {
+       moveProfileField(fieldId, "down", successFunction);
+}
+
 //
 // EVERYTHING BELOW HERE IS EXECUTED AFTER LOADING THE PAGE
 //
@@ -865,17 +1101,28 @@ $(document).ready(function() {
        /* this initializes the status update input field. */
        getTranslation("WebInterface.DefaultText.StatusUpdate", function(defaultText) {
                registerInputTextareaSwap("#sone #update-status .status-input", defaultText, "text", false, false);
+               $("#sone #update-status .select-sender").css("display", "inline");
+               $("#sone #update-status .sender").hide();
+               $("#sone #update-status .select-sender button").click(function() {
+                       $("#sone #update-status .sender").show();
+                       $("#sone #update-status .select-sender").hide();
+                       return false;
+               });
                $("#sone #update-status").submit(function() {
                        if ($(this).find(":input.default:enabled").length > 0) {
                                return false;
                        }
-                       text = $(this).find(":input:enabled").val();
-                       $.getJSON("createPost.ajax", { "formPassword": getFormPassword(), "text": text }, function(data, textStatus) {
+                       sender = $(this).find(":input[name=sender]").val();
+                       text = $(this).find(":input[name=text]:enabled").val();
+                       $.getJSON("createPost.ajax", { "formPassword": getFormPassword(), "sender": sender, "text": text }, function(data, textStatus) {
                                if ((data != null) && data.success) {
-                                       loadNewPost(data.postId);
+                                       loadNewPost(data.postId, data.sone, data.recipient);
                                }
                        });
-                       $(this).find(":input:enabled").val("").blur();
+                       $(this).find(":input[name=sender]").val(getCurrentSoneId());
+                       $(this).find(":input[name=text]:enabled").val("").blur();
+                       $(this).find(".sender").hide();
+                       $(this).find(".select-sender").show();
                        return false;
                });
        });
@@ -887,7 +1134,7 @@ $(document).ready(function() {
                        text = $(this).find(":input:enabled").val();
                        $.getJSON("createPost.ajax", { "formPassword": getFormPassword(), "recipient": getShownSoneId(), "text": text }, function(data, textStatus) {
                                if ((data != null) && data.success) {
-                                       loadNewPost(data.postId);
+                                       loadNewPost(data.postId, getCurrentSoneId());
                                }
                        });
                        $(this).find(":input:enabled").val("").blur();