🔀 Merge branch 'release/v82'
[Sone.git] / src / main / resources / static / javascript / sone.js
index 6077049..333d9d1 100644 (file)
@@ -2,13 +2,13 @@
 
 function ajaxGet(url, data, successCallback, errorCallback) {
        (function(url, data, successCallback, errorCallback) {
-               $.ajax({"type": "GET", "url": url, "data": data, "dataType": "json", "success": function(data, textStatus, xmlHttpRequest) {
+               $.ajax({"cache": false, "type": "GET", "url": url, "data": data, "dataType": "json", "success": function(data, textStatus) {
                        ajaxSuccess();
                        if (typeof successCallback != "undefined") {
                                successCallback(data, textStatus);
                        }
-               }, "error": function(xmlHttpRequest, textStatus, errorThrown) {
-                       if (xmlHttpRequest.status == 403) {
+               }, "error": function(xmlHttpRequest) {
+                       if (xmlHttpRequest.status === 403) {
                                notLoggedIn = true;
                        }
                        if (typeof errorCallback != "undefined") {
@@ -22,10 +22,10 @@ function ajaxGet(url, data, successCallback, errorCallback) {
 
 function registerInputTextareaSwap(inputElement, defaultText, inputFieldName, optional, dontUseTextarea) {
        $(inputElement).each(function() {
-               textarea = $(dontUseTextarea ? "<input type=\"text\" name=\"" + inputFieldName + "\">" : "<textarea name=\"" + inputFieldName + "\"></textarea>").blur(function() {
-                       if ($(this).val() == "") {
+               const textarea = $(dontUseTextarea ? "<input type=\"text\" name=\"" + inputFieldName + "\">" : "<textarea name=\"" + inputFieldName + "\"></textarea>").blur(function() {
+                       if ($(this).val() === "") {
                                $(this).hide();
-                               inputField = $(this).data("inputField");
+                               const inputField = $(this).data("inputField");
                                inputField.show().removeAttr("disabled").addClass("default");
                                inputField.val(defaultText);
                        }
@@ -33,20 +33,20 @@ function registerInputTextareaSwap(inputElement, defaultText, inputFieldName, op
                $(this).data("textarea", textarea).after(textarea);
                (function(inputField, textarea) {
                        inputField.focus(function() {
-                               $(this).hide().attr("disabled", "disabled");
+                               $(this).hide().prop("disabled", "disabled");
                                /* no, show(), â€śdisplay: block” is not what I need. */
-                               textarea.attr("style", "display: inline").focus();
+                               textarea.prop("style", "display: inline").focus();
                        });
-                       if (inputField.val() == "") {
+                       if (inputField.val() === "") {
                                inputField.addClass("default");
                                inputField.val(defaultText);
                        } else {
-                               inputField.hide().attr("disabled", "disabled");
+                               inputField.hide().prop("disabled", "disabled");
                                textarea.show();
                        }
                        $(inputField.get(0).form).submit(function() {
-                               inputField.attr("disabled", "disabled");
-                               if (!optional && (textarea.val() == "")) {
+                               inputField.prop("disabled", "disabled");
+                               if (!optional && (textarea.val() === "")) {
                                        inputField.removeAttr("disabled").focus();
                                        return false;
                                }
@@ -64,18 +64,18 @@ function registerInputTextareaSwap(inputElement, defaultText, inputFieldName, op
  *            The element to add a â€ścomment” link to
  */
 function addCommentLink(postId, author, element, insertAfterThisElement) {
-       if (($(element).find(".show-reply-form").length > 0) || (getPostElement(element).find(".create-reply").length == 0)) {
+       if (($(element).find(".show-reply-form").length > 0) || (getPostElement(element).find(".create-reply").length === 0)) {
                return;
        }
        (function(postId, author, insertAfterThisElement) {
-               separator = $("<span> Â· </span>").addClass("separator");
+               const separator = $("<span> Â· </span>").addClass("separator");
                getTranslation("WebInterface.Button.Comment", function(text) {
-                       commentElement = $("<div><span>" + text + "</span></div>").addClass("show-reply-form").click(function() {
-                               replyElement = $("#sone .post#post-" + postId + " .create-reply");
+                       const commentElement = $("<div><span>" + text + "</span></div>").addClass("show-reply-form").click(function() {
+                               const replyElement = sone.find(".post#post-" + postId + " .create-reply");
                                replyElement.removeClass("hidden");
                                replyElement.removeClass("light");
                                (function(replyElement) {
-                                       replyElement.find("input.reply-input").blur(function() {
+                                       replyElement.find(":input.reply-input").blur(function() {
                                                if ($(this).hasClass("default")) {
                                                        replyElement.addClass("light");
                                                }
@@ -83,8 +83,8 @@ function addCommentLink(postId, author, element, insertAfterThisElement) {
                                                replyElement.removeClass("light");
                                        });
                                })(replyElement);
-                               textArea = replyElement.find("input.reply-input").focus().data("textarea");
-                               if (author != getCurrentSoneId()) {
+                               const textArea = replyElement.find(":input.reply-input").focus().data("textarea");
+                               if (author !== getCurrentSoneId()) {
                                        textArea.val(textArea.val() + "@sone://" + author + " ");
                                }
                        });
@@ -94,7 +94,7 @@ function addCommentLink(postId, author, element, insertAfterThisElement) {
        })(postId, author, insertAfterThisElement);
 }
 
-var translations = {};
+const translations = {};
 
 /**
  * Retrieves the translation for the given key and calls the callback function.
@@ -110,7 +110,7 @@ function getTranslation(key, callback) {
                callback(translations[key]);
                return;
        }
-       ajaxGet("getTranslation.ajax", {"key": key}, function(data, textStatus) {
+       ajaxGet("getTranslation.ajax", {"key": key}, function(data) {
                if ((data != null) && data.success) {
                        translations[key] = data.value;
                        callback(data.value);
@@ -145,22 +145,22 @@ function filterSoneId(soneId) {
  *            The date and time of the last update (formatted for display)
  */
 function updateSoneStatus(soneId, name, status, modified, locked, lastUpdated, lastUpdatedText) {
-       $("#sone .sone." + filterSoneId(soneId)).
-               toggleClass("unknown", status == "unknown").
-               toggleClass("idle", status == "idle").
-               toggleClass("inserting", status == "inserting").
-               toggleClass("downloading", status == "downloading").
+       const updateSone = sone.find(".sone." + filterSoneId(soneId));
+       updateSone.toggleClass("unknown", status === "unknown").
+               toggleClass("idle", status === "idle").
+               toggleClass("inserting", status === "inserting").
+               toggleClass("downloading", status === "downloading").
                toggleClass("modified", modified);
-       $("#sone .sone." + filterSoneId(soneId) + " .lock").toggleClass("hidden", locked);
-       $("#sone .sone." + filterSoneId(soneId) + " .unlock").toggleClass("hidden", !locked);
+       updateSone.find(".lock").toggleClass("hidden", locked);
+       updateSone.find(".unlock").toggleClass("hidden", !locked);
        if (lastUpdated != null) {
-               $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").attr("title", lastUpdated).text(lastUpdatedText);
+               updateSone.find(".last-update span.time").prop("title", lastUpdated).text(lastUpdatedText);
        } else {
                getTranslation("View.Sone.Text.UnknownDate", function(unknown) {
-                       $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(unknown);
+                       updateSone.find(".last-update span.time").text(unknown);
                });
        }
-       $("#sone .sone." + filterSoneId(soneId) + " .profile-link a").text(name);
+       updateSone.find(".profile-link a").text(name);
 }
 
 /**
@@ -175,7 +175,7 @@ function updateSoneStatus(soneId, name, status, modified, locked, lastUpdated, l
  */
 function enhanceDeleteButton(button, text, deleteCallback) {
        (function(button) {
-               newButton = $("<button></button>").addClass("confirm").hide().text(text).click(function() {
+               const newButton = $("<button></button>").addClass("confirm").hide().text(text).click(function() {
                        $(this).fadeOut("slow");
                        deleteCallback();
                        return false;
@@ -185,7 +185,7 @@ function enhanceDeleteButton(button, text, deleteCallback) {
                                button.fadeOut("slow", function() {
                                        newButton.fadeIn("slow");
                                        $(document).one("click", function() {
-                                               if (this != newButton.get(0)) {
+                                               if (this !== newButton.get(0)) {
                                                        newButton.fadeOut(function() {
                                                                button.fadeIn();
                                                        });
@@ -210,21 +210,21 @@ function enhanceDeleteButton(button, text, deleteCallback) {
  */
 function enhanceDeletePostButton(button, postId, text) {
        enhanceDeleteButton(button, text, function() {
-               ajaxGet("deletePost.ajax", { "post": postId, "formPassword": getFormPassword() }, function(data, textStatus) {
+               ajaxGet("deletePost.ajax", { "post": postId, "formPassword": getFormPassword() }, function(data) {
                        if (data == null) {
                                return;
                        }
                        if (data.success) {
-                               $("#sone .post#post-" + postId).slideUp();
-                       } else if (data.error == "invalid-post-id") {
+                               sone.find(".post#post-" + postId).slideUp();
+                       } else if (data.error === "invalid-post-id") {
                                /* pretend the post is already gone. */
                                getPost(postId).slideUp();
-                       } else if (data.error == "auth-required") {
+                       } else if (data.error === "auth-required") {
                                alert("You need to be logged in.");
-                       } else if (data.error == "not-authorized") {
+                       } else if (data.error === "not-authorized") {
                                alert("You are not allowed to delete this post.");
                        }
-               }, function(xmlHttpRequest, textStatus, error) {
+               }, function() {
                        /* ignore error. */
                });
        });
@@ -242,28 +242,28 @@ function enhanceDeletePostButton(button, postId, text) {
  */
 function enhanceDeleteReplyButton(button, replyId, text) {
        enhanceDeleteButton(button, text, function() {
-               ajaxGet("deleteReply.ajax", { "reply": replyId, "formPassword": $("#sone #formPassword").text() }, function(data, textStatus) {
+               ajaxGet("deleteReply.ajax", { "reply": replyId, "formPassword": sone.find("#formPassword").text() }, function(data) {
                        if (data == null) {
                                return;
                        }
                        if (data.success) {
-                               $("#sone .reply#reply-" + replyId).slideUp();
-                       } else if (data.error == "invalid-reply-id") {
+                               sone.find(".reply#reply-" + replyId).slideUp();
+                       } else if (data.error === "invalid-reply-id") {
                                /* pretend the reply is already gone. */
                                getReply(replyId).slideUp();
-                       } else if (data.error == "auth-required") {
+                       } else if (data.error === "auth-required") {
                                alert("You need to be logged in.");
-                       } else if (data.error == "not-authorized") {
+                       } else if (data.error === "not-authorized") {
                                alert("You are not allowed to delete this reply.");
                        }
-               }, function(xmlHttpRequest, textStatus, error) {
+               }, function() {
                        /* ignore error. */
                });
        });
 }
 
 function getFormPassword() {
-       return $("#sone #formPassword").text();
+       return sone.find("#formPassword").text();
 }
 
 /**
@@ -274,8 +274,8 @@ function getFormPassword() {
  * @returns All Sone elements with the given ID
  */
 function getSone(soneId) {
-       return $("#sone .sone").filter(function(index) {
-               return $(".id", this).text() == soneId;
+       return sone.find(".sone").filter(function() {
+               return $(".id", this).text() === soneId;
        });
 }
 
@@ -297,21 +297,14 @@ function getMenuSone(element) {
 
 /**
  * Generates a list of Sones by concatening the names of the given sones with a
- * new line character (“\n”).
+ * comma.
  *
  * @param sones
  *            The sones to format
  * @returns {String} The created string
  */
 function generateSoneList(sones) {
-       var soneList = "";
-       $.each(sones, function() {
-               if (soneList != "") {
-                       soneList += ", ";
-               }
-               soneList += this.name;
-       });
-       return soneList;
+       return sones.map(sone => sone.name).join(", ")
 }
 
 /**
@@ -333,7 +326,7 @@ function getSoneId(element) {
  * @returns The element of the post
  */
 function getPost(postId) {
-       return $("#sone .post#post-" + postId);
+       return sone.find(".post#post-" + postId);
 }
 
 function getPostElement(element) {
@@ -341,7 +334,7 @@ function getPostElement(element) {
 }
 
 function getPostId(element) {
-       return getPostElement(element).attr("id").substr(5);
+       return getPostElement(element).prop("id").substr(5);
 }
 
 function getPostTime(element) {
@@ -367,7 +360,7 @@ function getPostAuthor(element) {
  * @returns The element of the reply
  */
 function getReply(replyId) {
-       return $("#sone .reply#reply-" + replyId);
+       return sone.find(".reply#reply-" + replyId);
 }
 
 function getReplyElement(element) {
@@ -375,7 +368,7 @@ function getReplyElement(element) {
 }
 
 function getReplyId(element) {
-       return getReplyElement(element).attr("id").substr(6);
+       return getReplyElement(element).prop("id").substr(6);
 }
 
 function getReplyTime(element) {
@@ -401,7 +394,7 @@ function getReplyAuthor(element) {
  * @returns The notification element
  */
 function getNotification(notificationId) {
-       return $("#sone #notification-area .notification#" + notificationId);
+       return sone.find("#notification-area .notification#" + notificationId);
 }
 
 /**
@@ -423,7 +416,7 @@ function getNotificationElement(element) {
  * @returns The ID of the notification
  */
 function getNotificationId(notificationElement) {
-       return $(notificationElement).attr("id");
+       return $(notificationElement).prop("id");
 }
 
 /**
@@ -434,142 +427,74 @@ function getNotificationId(notificationElement) {
  * @returns The last update time of the notification
  */
 function getNotificationLastUpdatedTime(notificationElement) {
-       return $(notificationElement).attr("lastUpdatedTime");
+       return $(notificationElement).prop("lastUpdatedTime");
 }
 
 function likePost(postId) {
-       ajaxGet("like.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data, textStatus) {
+       ajaxGet("like.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data) {
                if ((data == null) || !data.success) {
                        return;
                }
-               $("#sone .post#post-" + postId + " > .inner-part > .status-line .like").addClass("hidden");
-               $("#sone .post#post-" + postId + " > .inner-part > .status-line .unlike").removeClass("hidden");
+               sone.find(".post#post-" + postId + " > .inner-part > .status-line .like").addClass("hidden");
+               sone.find(".post#post-" + postId + " > .inner-part > .status-line .unlike").removeClass("hidden");
                updatePostLikes(postId);
-       }, function(xmlHttpRequest, textStatus, error) {
+       }, function() {
                /* ignore error. */
        });
 }
 
 function unlikePost(postId) {
-       ajaxGet("unlike.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data, textStatus) {
+       ajaxGet("unlike.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data) {
                if ((data == null) || !data.success) {
                        return;
                }
-               $("#sone .post#post-" + postId + " > .inner-part > .status-line .unlike").addClass("hidden");
-               $("#sone .post#post-" + postId + " > .inner-part > .status-line .like").removeClass("hidden");
+               sone.find(".post#post-" + postId + " > .inner-part > .status-line .unlike").addClass("hidden");
+               sone.find(".post#post-" + postId + " > .inner-part > .status-line .like").removeClass("hidden");
                updatePostLikes(postId);
-       }, function(xmlHttpRequest, textStatus, error) {
+       }, function() {
                /* ignore error. */
        });
 }
 
 function updatePostLikes(postId) {
-       ajaxGet("getLikes.ajax", { "type": "post", "post": postId }, function(data, textStatus) {
+       ajaxGet("getLikes.ajax", { "type": "post", "post": postId }, function(data) {
                if ((data != null) && data.success) {
-                       $("#sone .post#post-" + postId + " > .inner-part > .status-line .likes").toggleClass("hidden", data.likes == 0);
-                       $("#sone .post#post-" + postId + " > .inner-part > .status-line .likes span.like-count").text(data.likes);
-                       $("#sone .post#post-" + postId + " > .inner-part > .status-line .likes > span").attr("title", generateSoneList(data.sones));
+                       sone.find(".post#post-" + postId + " > .inner-part > .status-line .likes").toggleClass("hidden", data.likes === 0);
+                       sone.find(".post#post-" + postId + " > .inner-part > .status-line .likes span.like-count").text(data.likes);
+                       sone.find(".post#post-" + postId + " > .inner-part > .status-line .likes > span").prop("title", generateSoneList(data.sones));
                }
-       }, function(xmlHttpRequest, textStatus, error) {
+       }, function() {
                /* ignore error. */
        });
 }
 
 function likeReply(replyId) {
-       ajaxGet("like.ajax", { "type": "reply", "reply" : replyId, "formPassword": getFormPassword() }, function(data, textStatus) {
+       ajaxGet("like.ajax", { "type": "reply", "reply" : replyId, "formPassword": getFormPassword() }, function(data) {
                if ((data == null) || !data.success) {
                        return;
                }
-               $("#sone .reply#reply-" + replyId + " .status-line .like").addClass("hidden");
-               $("#sone .reply#reply-" + replyId + " .status-line .unlike").removeClass("hidden");
+               sone.find(".reply#reply-" + replyId + " .status-line .like").addClass("hidden");
+               sone.find(".reply#reply-" + replyId + " .status-line .unlike").removeClass("hidden");
                updateReplyLikes(replyId);
-       }, function(xmlHttpRequest, textStatus, error) {
+       }, function() {
                /* ignore error. */
        });
 }
 
 function unlikeReply(replyId) {
-       ajaxGet("unlike.ajax", { "type": "reply", "reply" : replyId, "formPassword": getFormPassword() }, function(data, textStatus) {
+       ajaxGet("unlike.ajax", { "type": "reply", "reply" : replyId, "formPassword": getFormPassword() }, function(data) {
                if ((data == null) || !data.success) {
                        return;
                }
-               $("#sone .reply#reply-" + replyId + " .status-line .unlike").addClass("hidden");
-               $("#sone .reply#reply-" + replyId + " .status-line .like").removeClass("hidden");
+               sone.find(".reply#reply-" + replyId + " .status-line .unlike").addClass("hidden");
+               sone.find(".reply#reply-" + replyId + " .status-line .like").removeClass("hidden");
                updateReplyLikes(replyId);
-       }, function(xmlHttpRequest, textStatus, error) {
+       }, function() {
                /* ignore error. */
        });
 }
 
 /**
- * Trusts the Sone with the given ID.
- *
- * @param soneId
- *            The ID of the Sone to trust
- */
-function trustSone(soneId) {
-       ajaxGet("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) {
-       ajaxGet("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) {
-       ajaxGet("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);
-               }
-       });
-}
-
-/**
  * Bookmarks the post with the given ID.
  *
  * @param postId
@@ -577,7 +502,7 @@ function updateTrustControls(soneId, trustValue) {
  */
 function bookmarkPost(postId) {
        (function(postId) {
-               ajaxGet("bookmark.ajax", {"formPassword": getFormPassword(), "type": "post", "post": postId}, function(data, textStatus) {
+               ajaxGet("bookmark.ajax", {"formPassword": getFormPassword(), "type": "post", "post": postId}, function(data) {
                        if ((data != null) && data.success) {
                                getPost(postId).find(".bookmark").toggleClass("hidden", true);
                                getPost(postId).find(".unbookmark").toggleClass("hidden", false);
@@ -593,7 +518,7 @@ function bookmarkPost(postId) {
  *            The ID of the post to unbookmark
  */
 function unbookmarkPost(postId) {
-       ajaxGet("unbookmark.ajax", {"formPassword": getFormPassword(), "type": "post", "post": postId}, function(data, textStatus) {
+       ajaxGet("unbookmark.ajax", {"formPassword": getFormPassword(), "type": "post", "post": postId}, function(data) {
                if ((data != null) && data.success) {
                        getPost(postId).find(".bookmark").toggleClass("hidden", false);
                        getPost(postId).find(".unbookmark").toggleClass("hidden", true);
@@ -602,13 +527,13 @@ function unbookmarkPost(postId) {
 }
 
 function updateReplyLikes(replyId) {
-       ajaxGet("getLikes.ajax", { "type": "reply", "reply": replyId }, function(data, textStatus) {
+       ajaxGet("getLikes.ajax", { "type": "reply", "reply": replyId }, function(data) {
                if ((data != null) && data.success) {
-                       $("#sone .reply#reply-" + replyId + " .status-line .likes").toggleClass("hidden", data.likes == 0);
-                       $("#sone .reply#reply-" + replyId + " .status-line .likes span.like-count").text(data.likes);
-                       $("#sone .reply#reply-" + replyId + " .status-line .likes > span").attr("title", generateSoneList(data.sones));
+                       sone.find(".reply#reply-" + replyId + " .status-line .likes").toggleClass("hidden", data.likes === 0);
+                       sone.find(".reply#reply-" + replyId + " .status-line .likes span.like-count").text(data.likes);
+                       sone.find(".reply#reply-" + replyId + " .status-line .likes > span").prop("title", generateSoneList(data.sones));
                }
-       }, function(xmlHttpRequest, textStatus, error) {
+       }, function() {
                /* ignore error. */
        });
 }
@@ -627,7 +552,7 @@ function updateReplyLikes(replyId) {
  *            parameters: success, error, replyId)
  */
 function postReply(sender, postId, text, callbackFunction) {
-       ajaxGet("createReply.ajax", { "formPassword" : getFormPassword(), "sender": sender, "post" : postId, "text": text }, function(data, textStatus) {
+       ajaxGet("createReply.ajax", { "formPassword" : getFormPassword(), "sender": sender, "post" : postId, "text": text }, function(data) {
                if (data == null) {
                        /* TODO - show error */
                        return;
@@ -637,7 +562,7 @@ function postReply(sender, postId, text, callbackFunction) {
                } else {
                        callbackFunction(false, data.error);
                }
-       }, function(xmlHttpRequest, textStatus, error) {
+       }, function() {
                /* ignore error. */
        });
 }
@@ -654,7 +579,7 @@ function ajaxifySone(soneElement) {
         * nicer.
         */
        $(".follow", soneElement).submit(function() {
-               var followElement = this;
+               const followElement = this;
                ajaxGet("followSone.ajax", { "sone": getSoneId(this), "formPassword": getFormPassword() }, function() {
                        $(followElement).addClass("hidden");
                        $(followElement).parent().find(".unfollow").removeClass("hidden");
@@ -662,7 +587,7 @@ function ajaxifySone(soneElement) {
                return false;
        });
        $(".unfollow", soneElement).submit(function() {
-               var unfollowElement = this;
+               const unfollowElement = this;
                ajaxGet("unfollowSone.ajax", { "sone": getSoneId(this), "formPassword": getFormPassword() }, function() {
                        $(unfollowElement).addClass("hidden");
                        $(unfollowElement).parent().find(".follow").removeClass("hidden");
@@ -670,7 +595,7 @@ function ajaxifySone(soneElement) {
                return false;
        });
        $(".lock", soneElement).submit(function() {
-               var lockElement = this;
+               const lockElement = this;
                ajaxGet("lockSone.ajax", { "sone" : getSoneId(this), "formPassword" : getFormPassword() }, function() {
                        $(lockElement).addClass("hidden");
                        $(lockElement).parent().find(".unlock").removeClass("hidden");
@@ -678,7 +603,7 @@ function ajaxifySone(soneElement) {
                return false;
        });
        $(".unlock", soneElement).submit(function() {
-               var unlockElement = this;
+               const unlockElement = this;
                ajaxGet("unlockSone.ajax", { "sone" : getSoneId(this), "formPassword" : getFormPassword() }, function() {
                        $(unlockElement).addClass("hidden");
                        $(unlockElement).parent().find(".lock").removeClass("hidden");
@@ -692,6 +617,40 @@ function ajaxifySone(soneElement) {
        });
 }
 
+function followSone(soneId) {
+       return function() {
+               const followElement = this;
+               ajaxGet("followSone.ajax", {"sone": soneId, "formPassword": getFormPassword()}, function () {
+                       $(followElement).addClass("hidden");
+                       $(followElement).parent().find(".unfollow").removeClass("hidden");
+                       sone.find(".sone-menu").each(function () {
+                               if (getMenuSone(this) === soneId) {
+                                       $(".follow", this).toggleClass("hidden", true);
+                                       $(".unfollow", this).toggleClass("hidden", false);
+                               }
+                       });
+               });
+               return false;
+       }
+}
+
+function unfollowSone(soneId) {
+       return function() {
+               const unfollowElement = this;
+               ajaxGet("unfollowSone.ajax", {"sone": soneId, "formPassword": getFormPassword()}, function () {
+                       $(unfollowElement).addClass("hidden");
+                       $(unfollowElement).parent().find(".follow").removeClass("hidden");
+                       sone.find(".sone-menu").each(function () {
+                               if (getMenuSone(this) === soneId) {
+                                       $(".follow", this).toggleClass("hidden", false);
+                                       $(".unfollow", this).toggleClass("hidden", true);
+                               }
+                       });
+               });
+               return false;
+       }
+};
+
 /**
  * Ajaxifies the given post by enhancing all eligible elements with AJAX.
  *
@@ -703,21 +662,22 @@ function ajaxifyPost(postElement) {
                return false;
        });
        $(postElement).find(".create-reply button:submit").click(function() {
-               button = $(this);
-               button.attr("disabled", "disabled");
-               sender = $(this.form).find(":input[name=sender]").val();
-               inputField = $(this.form).find(":input[name=text]:enabled").get(0);
-               postId = getPostId(this);
-               text = $(inputField).val();
+               const button = $(this);
+               button.prop("disabled", "disabled");
+               const sender = $(this.form).find(":input[name=sender]").val();
+               const inputField = $(this.form).find(":input[name=text]:enabled").get(0);
+               const postId = getPostId(this);
+               const text = $(inputField).val();
                (function(sender, postId, text, inputField) {
                        postReply(sender, postId, text, function(success, error, replyId, soneId) {
                                if (success) {
                                        $(inputField).val("");
                                        loadNewReply(replyId, soneId, postId);
-                                       $("#sone .post#post-" + postId + " .create-reply").addClass("hidden");
-                                       $("#sone .post#post-" + postId + " .create-reply .sender").hide();
-                                       $("#sone .post#post-" + postId + " .create-reply .select-sender").show();
-                                       $("#sone .post#post-" + postId + " .create-reply :input[name=sender]").val(getCurrentSoneId());
+                                       sone.find(".post#post-" + postId + " .create-reply").addClass("hidden");
+                                       sone.find(".post#post-" + postId + " .create-reply .sender").hide();
+                                       sone.find(".post#post-" + postId + " .create-reply .select-sender").show();
+                                       sone.find(".post#post-" + postId + " .create-reply :input[name=sender]").val(getCurrentSoneId());
+                                       updateReplyTimes(replyId);
                                } else {
                                        alert(error);
                                }
@@ -730,7 +690,7 @@ function ajaxifyPost(postElement) {
        /* replace all â€śdelete” buttons with javascript. */
        (function(postElement) {
                getTranslation("WebInterface.Confirmation.DeletePostButton", function(deletePostText) {
-                       postId = getPostId(postElement);
+                       const postId = getPostId(postElement);
                        enhanceDeletePostButton($(postElement).find(".delete-post button"), postId, deletePostText);
                });
        })(postElement);
@@ -745,20 +705,6 @@ 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;
-       });
-
        /* convert bookmark/unbookmark buttons to javascript functions. */
        $(postElement).find(".bookmark").submit(function() {
                bookmarkPost(getPostId(this));
@@ -772,8 +718,8 @@ function ajaxifyPost(postElement) {
        /* convert â€śshow source” link into javascript function. */
        $(postElement).find(".show-source").each(function() {
                $("a", this).click(function() {
-                       post = getPostElement(this);
-                       rawPostText = $(".post-text.raw-text", post);
+                       const post = getPostElement(this);
+                       const rawPostText = $(".post-text.raw-text", post);
                        rawPostText.toggleClass("hidden");
                        if (rawPostText.hasClass("hidden")) {
                                $(".post-text.short-text", post).removeClass("hidden");
@@ -791,7 +737,7 @@ function ajaxifyPost(postElement) {
        });
 
        /* convert â€śshow more” link into javascript function. */
-       $(postElement).find(".expand-post-text").each(function() {
+       const toggleShowMore = function() {
                $(this).click(function() {
                        $(".post-text.text", getPostElement(this)).toggleClass("hidden");
                        $(".post-text.short-text", getPostElement(this)).toggleClass("hidden");
@@ -799,22 +745,15 @@ function ajaxifyPost(postElement) {
                        $(".shrink-post-text", getPostElement(this)).toggleClass("hidden");
                        return false;
                });
-       });
-       $(postElement).find(".shrink-post-text").each(function() {
-               $(this).click(function() {
-                       $(".post-text.text", getPostElement(this)).toggleClass("hidden");
-                       $(".post-text.short-text", getPostElement(this)).toggleClass("hidden");
-                       $(".expand-post-text", getPostElement(this)).toggleClass("hidden");
-                       $(".shrink-post-text", getPostElement(this)).toggleClass("hidden");
-                       return false;
-               });
-       });
+       };
+       $(postElement).find(".expand-post-text").each(toggleShowMore);
+       $(postElement).find(".shrink-post-text").each(toggleShowMore);
 
        /* ajaxify author/post links */
        $(".post-status-line .permalink a", postElement).click(function() {
                if (!$(".create-reply", postElement).hasClass("hidden")) {
-                       textArea = $("input.reply-input", postElement).focus().data("textarea");
-                       $(textArea).replaceSelection($(this).attr("href"));
+                       const textArea = $(":input.reply-input", postElement).focus().data("textarea");
+                       $(textArea).replaceSelection($(this).prop("href"));
                }
                return false;
        });
@@ -823,7 +762,7 @@ function ajaxifyPost(postElement) {
        addCommentLink(getPostId(postElement), getPostAuthor(postElement), postElement, $(postElement).find(".post-status-line .permalink-author"));
 
        /* process all replies. */
-       replyIds = [];
+       const replyIds = [];
        $(postElement).find(".reply").each(function() {
                replyIds.push(getReplyId(this));
                ajaxifyReply(this);
@@ -832,7 +771,7 @@ function ajaxifyPost(postElement) {
 
        /* process reply input fields. */
        getTranslation("WebInterface.DefaultText.Reply", function(text) {
-               $(postElement).find("input.reply-input").each(function() {
+               $(postElement).find(":input.reply-input").each(function() {
                        registerInputTextareaSwap(this, text, "text", false, false);
                });
        });
@@ -861,7 +800,7 @@ function ajaxifyPost(postElement) {
 
        /* show Sone menu when hovering over the avatar. */
        $(postElement).find(".post-avatar").mouseover(function() {
-               if (typeof currentSoneMenuTimeoutHandler != undefined) {
+               if (typeof currentSoneMenuTimeoutHandler !== undefined) {
                        clearTimeout(currentSoneMenuTimeoutHandler);
                }
                currentSoneMenuId = getPostId(this);
@@ -872,40 +811,14 @@ function ajaxifyPost(postElement) {
                        }).fadeIn();
                }, 1000);
        }).mouseleave(function() {
-               if (currentSoneMenuId = getPostId(this)) {
+               if (currentSoneMenuId === getPostId(this)) {
                        clearTimeout(currentSoneMenuTimeoutHandler);
                }
        });
        (function(postElement) {
-               var soneId = $(".sone-menu-id:first", postElement).text();
-               $(".sone-post-menu .follow", postElement).click(function() {
-                       var followElement = this;
-                       ajaxGet("followSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
-                               $(followElement).addClass("hidden");
-                               $(followElement).parent().find(".unfollow").removeClass("hidden");
-                               $("#sone .sone-menu").each(function() {
-                                       if (getMenuSone(this) == soneId) {
-                                               $(".follow", this).toggleClass("hidden", true);
-                                               $(".unfollow", this).toggleClass("hidden", false);
-                                       }
-                               });
-                       });
-                       return false;
-               });
-               $(".sone-post-menu .unfollow", postElement).click(function() {
-                       var unfollowElement = this;
-                       ajaxGet("unfollowSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
-                               $(unfollowElement).addClass("hidden");
-                               $(unfollowElement).parent().find(".follow").removeClass("hidden");
-                               $("#sone .sone-menu").each(function() {
-                                       if (getMenuSone(this) == soneId) {
-                                               $(".follow", this).toggleClass("hidden", false);
-                                               $(".unfollow", this).toggleClass("hidden", true);
-                                       }
-                               });
-                       });
-                       return false;
-               });
+               const soneId = $(".sone-menu-id:first", postElement).text();
+               $(".sone-post-menu .follow", postElement).click(followSone(soneId));
+               $(".sone-post-menu .unfollow", postElement).click(unfollowSone(soneId));
        })(postElement);
 }
 
@@ -935,8 +848,8 @@ function ajaxifyReply(replyElement) {
        /* ajaxify author links */
        $(".reply-status-line .permalink a", replyElement).click(function() {
                if (!$(".create-reply", getPostElement(replyElement)).hasClass("hidden")) {
-                       textArea = $("input.reply-input", getPostElement(replyElement)).focus().data("textarea");
-                       $(textArea).replaceSelection($(this).attr("href"));
+                       const textArea = $(":input.reply-input", getPostElement(replyElement)).focus().data("textarea");
+                       $(textArea).replaceSelection($(this).prop("href"));
                }
                return false;
        });
@@ -946,8 +859,8 @@ function ajaxifyReply(replyElement) {
        /* convert â€śshow source” link into javascript function. */
        $(replyElement).find(".show-reply-source").each(function() {
                $("a", this).click(function() {
-                       reply = getReplyElement(this);
-                       rawReplyText = $(".reply-text.raw-text", reply);
+                       const reply = getReplyElement(this);
+                       const rawReplyText = $(".reply-text.raw-text", reply);
                        rawReplyText.toggleClass("hidden");
                        if (rawReplyText.hasClass("hidden")) {
                                $(".reply-text.short-text", reply).removeClass("hidden");
@@ -965,7 +878,7 @@ function ajaxifyReply(replyElement) {
        });
 
        /* convert â€śshow more” link into javascript function. */
-       $(replyElement).find(".expand-reply-text").each(function() {
+       const toggleShowMore = function() {
                $(this).click(function() {
                        $(".reply-text.text", getReplyElement(this)).toggleClass("hidden");
                        $(".reply-text.short-text", getReplyElement(this)).toggleClass("hidden");
@@ -973,34 +886,13 @@ function ajaxifyReply(replyElement) {
                        $(".shrink-reply-text", getReplyElement(this)).toggleClass("hidden");
                        return false;
                });
-       });
-       $(replyElement).find(".shrink-reply-text").each(function() {
-               $(this).click(function() {
-                       $(".reply-text.text", getReplyElement(this)).toggleClass("hidden");
-                       $(".reply-text.short-text", getReplyElement(this)).toggleClass("hidden");
-                       $(".expand-reply-text", getReplyElement(this)).toggleClass("hidden");
-                       $(".shrink-reply-text", getReplyElement(this)).toggleClass("hidden");
-                       return false;
-               });
-       });
-
-       /* 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;
-       });
+       };
+       $(replyElement).find(".expand-reply-text").each(toggleShowMore);
+       $(replyElement).find(".shrink-reply-text").each(toggleShowMore);
 
        /* show Sone menu when hovering over the avatar. */
        $(replyElement).find(".reply-avatar").mouseover(function() {
-               if (typeof currentSoneMenuTimeoutHandler != undefined) {
+               if (typeof currentSoneMenuTimeoutHandler !== undefined) {
                        clearTimeout(currentSoneMenuTimeoutHandler);
                }
                currentSoneMenuId = getPostId(this) + "-" + getReplyId(this);
@@ -1011,40 +903,14 @@ function ajaxifyReply(replyElement) {
                        }).fadeIn();
                }, 1000);
        }).mouseleave(function() {
-               if (currentSoneMenuId = getPostId(this) + "-" + getReplyId(this)) {
+               if (currentSoneMenuId === getPostId(this) + "-" + getReplyId(this)) {
                        clearTimeout(currentSoneMenuTimeoutHandler);
                }
        });
        (function(replyElement) {
-               var soneId = $(".sone-menu-id", replyElement).text();
-               $(".sone-menu .follow", replyElement).click(function() {
-                       var followElement = this;
-                       ajaxGet("followSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
-                               $(followElement).addClass("hidden");
-                               $(followElement).parent().find(".unfollow").removeClass("hidden");
-                               $("#sone .sone-menu").each(function() {
-                                       if (getMenuSone(this) == soneId) {
-                                               $(".follow", this).toggleClass("hidden", true);
-                                               $(".unfollow", this).toggleClass("hidden", false);
-                                       }
-                               });
-                       });
-                       return false;
-               });
-               $(".sone-menu .unfollow", replyElement).click(function() {
-                       var unfollowElement = this;
-                       ajaxGet("unfollowSone.ajax", { "sone": soneId, "formPassword": getFormPassword() }, function() {
-                               $(unfollowElement).addClass("hidden");
-                               $(unfollowElement).parent().find(".follow").removeClass("hidden");
-                               $("#sone .sone-menu").each(function() {
-                                       if (getMenuSone(this) == soneId) {
-                                               $(".follow", this).toggleClass("hidden", false);
-                                               $(".unfollow", this).toggleClass("hidden", true);
-                                       }
-                               });
-                       });
-                       return false;
-               });
+               const soneId = $(".sone-menu-id", replyElement).text();
+               $(".sone-menu .follow", replyElement).click(followSone(soneId));
+               $(".sone-menu .unfollow", replyElement).click(unfollowSone(soneId));
        })(replyElement);
 }
 
@@ -1064,26 +930,26 @@ function ajaxifyNotification(notification) {
                notification.find(".text").addClass("hidden");
        }
        notification.find("form.mark-as-read button").click(function() {
-               allIds = $(":input[name=id]", this.form).val().split(" ");
-               for (var index = 0; index < allIds.length; index += 16) {
-                       ids = allIds.slice(index, index + 16).join(" ");
+               const allIds = $(":input[name=id]", this.form).val().split(" ");
+               for (let index = 0; index < allIds.length; index += 16) {
+                       const ids = allIds.slice(index, index + 16).join(" ");
                        ajaxGet("markAsKnown.ajax", {"formPassword": getFormPassword(), "type": $(":input[name=type]", this.form).val(), "id": ids});
                }
        });
        notification.find("a[class^='link-']").each(function() {
-               linkElement = $(this);
+               const linkElement = $(this);
                if (linkElement.is("[href^='viewPost']")) {
-                       id = linkElement.attr("class").substr(5);
+                       const id = linkElement.prop("class").substr(5);
                        if (hasPost(id)) {
-                               linkElement.attr("href", "#post-" + id).addClass("in-page-link");
+                               linkElement.prop("href", "#post-" + id).addClass("in-page-link");
                        }
                }
        });
        notification.find("form.dismiss button").click(function() {
-               ajaxGet("dismissNotification.ajax", { "formPassword" : getFormPassword(), "notification" : notification.attr("id") }, function(data, textStatus) {
+               ajaxGet("dismissNotification.ajax", { "formPassword" : getFormPassword(), "notification" : notification.prop("id") }, function() {
                        /* dismiss in case of error, too. */
                        notification.slideUp();
-               }, function(xmlHttpRequest, textStatus, error) {
+               }, function() {
                        /* ignore error. */
                });
        });
@@ -1095,7 +961,7 @@ function ajaxifyNotification(notification) {
  * determine whether the notifications changed and need to be reloaded.
  */
 function getNotificationHash() {
-       return $("#sone #notification-area #notification-hash").text();
+       return sone.find("#notification-area #notification-hash").text();
 }
 
 /**
@@ -1105,7 +971,7 @@ function getNotificationHash() {
  *            The new notification hash
  */
 function setNotificationHash(notificationHash) {
-       $("#sone #notification-area #notification-hash").text(notificationHash);
+       sone.find("#notification-area #notification-hash").text(notificationHash);
 }
 
 /**
@@ -1118,7 +984,7 @@ function setNotificationHash(notificationHash) {
  * @returns All extracted IDs
  */
 function getElementIds(notification, selector) {
-       elementIds = [];
+       const elementIds = [];
        $(selector, notification).each(function() {
                elementIds.push($(this).text());
        });
@@ -1135,13 +1001,13 @@ function getElementIds(notification, selector) {
  *            The new notification element
  */
 function checkForRemovedSones(oldNotification, newNotification) {
-       if (getNotificationId(oldNotification) != "new-sone-notification") {
+       if (getNotificationId(oldNotification) !== "new-sone-notification") {
                return;
        }
-       oldIds = getElementIds(oldNotification, ".new-sone-id");
-       newIds = getElementIds(newNotification, ".new-sone-id");
+       const oldIds = getElementIds(oldNotification, ".new-sone-id");
+       const newIds = getElementIds(newNotification, ".new-sone-id");
        $.each(oldIds, function(index, value) {
-               if ($.inArray(value, newIds) == -1) {
+               if ($.inArray(value, newIds) === -1) {
                        markSoneAsKnown(getSone(value), true);
                }
        });
@@ -1157,13 +1023,13 @@ function checkForRemovedSones(oldNotification, newNotification) {
  *            The new notification element
  */
 function checkForRemovedPosts(oldNotification, newNotification) {
-       if (getNotificationId(oldNotification) != "new-post-notification") {
+       if (getNotificationId(oldNotification) !== "new-post-notification") {
                return;
        }
-       oldIds = getElementIds(oldNotification, ".post-id");
-       newIds = getElementIds(newNotification, ".post-id");
+       const oldIds = getElementIds(oldNotification, ".post-id");
+       const newIds = getElementIds(newNotification, ".post-id");
        $.each(oldIds, function(index, value) {
-               if ($.inArray(value, newIds) == -1) {
+               if ($.inArray(value, newIds) === -1) {
                        markPostAsKnown(getPost(value), true);
                }
        });
@@ -1180,20 +1046,26 @@ function checkForRemovedPosts(oldNotification, newNotification) {
  *            The new notification element
  */
 function checkForRemovedReplies(oldNotification, newNotification) {
-       if (getNotificationId(oldNotification) != "new-reply-notification") {
+       if (getNotificationId(oldNotification) !== "new-reply-notification") {
                return;
        }
-       oldIds = getElementIds(oldNotification, ".reply-id");
-       newIds = getElementIds(newNotification, ".reply-id");
+       const oldIds = getElementIds(oldNotification, ".reply-id");
+       const newIds = getElementIds(newNotification, ".reply-id");
        $.each(oldIds, function(index, value) {
-               if ($.inArray(value, newIds) == -1) {
+               if ($.inArray(value, newIds) === -1) {
                        markReplyAsKnown(getReply(value), true);
                }
        });
 }
 
 function getStatus() {
-       ajaxGet("getStatus.ajax", isViewSonePage() ? {"soneIds": getShownSoneId() } : {"loadAllSones": isKnownSonesPage()}, function(data, textStatus) {
+       const parameters = isViewSonePage() ? {"soneIds": getShownSoneId()} : isKnownSonesPage() ? {"soneIds": getShownSoneIds()} : {};
+       $.extend(parameters, {
+               "elements": JSON.stringify($(".linked-element.not-loaded").map(function () {
+                       return $(this).prop("title");
+               }).toArray())
+       });
+       ajaxGet("getStatus.ajax", parameters, function(data) {
                if ((data != null) && data.success) {
                        /* process Sone information. */
                        $.each(data.sones, function(index, value) {
@@ -1203,7 +1075,7 @@ function getStatus() {
                        if (!notLoggedIn) {
                                showOfflineMarker(!online);
                        }
-                       if (data.notificationHash != getNotificationHash()) {
+                       if (data.notificationHash !== getNotificationHash()) {
                                console.log("Old hash: ", getNotificationHash(), ", new hash: ", data.notificationHash);
                                requestNotifications();
                                /* process new posts. */
@@ -1212,9 +1084,12 @@ function getStatus() {
                                });
                                /* process new replies. */
                                $.each(data.newReplies, function(index, value) {
-                                       loadNewReply(value.id, value.sone, value.post, value.postSone);
+                                       loadNewReply(value.id, value.sone, value.post);
                                });
                        }
+                       if (data.linkedElements) {
+                               loadLinkedElements(data.linkedElements)
+                       }
                        /* do it again in 5 seconds. */
                        setTimeout(getStatus, 5000);
                } else {
@@ -1228,39 +1103,39 @@ function getStatus() {
 }
 
 function requestNotifications() {
-       ajaxGet("getNotifications.ajax", {}, function(data, textStatus) {
+       ajaxGet("getNotifications.ajax", {}, function(data) {
                if (data && data.success) {
                        /* search for removed notifications. */
-                       $("#sone #notification-area .notification").each(function() {
-                               notificationId = $(this).attr("id");
-                               foundNotification = false;
+                       sone.find("#notification-area .notification").each(function() {
+                               const notificationId = $(this).prop("id");
+                               let foundNotification = false;
                                $.each(data.notifications, function(index, value) {
-                                       if (value.id == notificationId) {
+                                       if (value.id === notificationId) {
                                                foundNotification = true;
                                                return false;
                                        }
                                });
                                if (!foundNotification) {
-                                       if (notificationId == "new-sone-notification" && (data.options["ShowNotification/NewSones"] == true)) {
-                                               $(".new-sone-id", this).each(function(index, element) {
-                                                       soneId = $(this).text();
+                                       if (notificationId === "new-sone-notification" && (data.options["ShowNotification/NewSones"] === true)) {
+                                               $(".new-sone-id", this).each(function() {
+                                                       const soneId = $(this).text();
                                                        markSoneAsKnown(getSone(soneId), true);
                                                });
-                                       } else if (notificationId == "new-post-notification" && (data.options["ShowNotification/NewPosts"] == true)) {
-                                               $(".post-id", this).each(function(index, element) {
-                                                       postId = $(this).text();
+                                       } else if (notificationId === "new-post-notification" && (data.options["ShowNotification/NewPosts"] === true)) {
+                                               $(".post-id", this).each(function() {
+                                                       const postId = $(this).text();
                                                        markPostAsKnown(getPost(postId), true);
                                                });
-                                       } else if (notificationId == "new-reply-notification" && (data.options["ShowNotification/NewReplies"] == true)) {
-                                               $(".reply-id", this).each(function(index, element) {
-                                                       replyId = $(this).text();
+                                       } else if (notificationId === "new-reply-notification" && (data.options["ShowNotification/NewReplies"] === true)) {
+                                               $(".reply-id", this).each(function() {
+                                                       const replyId = $(this).text();
                                                        markReplyAsKnown(getReply(replyId), true);
                                                });
                                        }
                                        $(this).slideUp("normal", function() {
                                                $(this).remove();
                                                /* remove activity when no notifications are visible. */
-                                               if ($("#sone #notification-area .notification").length == 0) {
+                                               if (sone.find("#notification-area .notification").length === 0) {
                                                        resetActivity();
                                                }
                                        });
@@ -1268,11 +1143,11 @@ function requestNotifications() {
                        });
                        /* process notifications. */
                        $.each(data.notifications, function(index, value) {
-                               oldNotification = getNotification(value.id);
-                               notification = ajaxifyNotification(createNotification(value.id, value.lastUpdatedTime, value.text, value.dismissable)).hide();
-                               if (oldNotification.length != 0) {
+                               const oldNotification = getNotification(value.id);
+                               const notification = ajaxifyNotification(createNotification(value.id, value.lastUpdatedTime, value.text, value.dismissable)).hide();
+                               if (oldNotification.length !== 0) {
                                        if ((oldNotification.find(".short-text").length > 0) && (notification.find(".short-text").length > 0)) {
-                                               opened = oldNotification.is(":visible") && oldNotification.find(".short-text").hasClass("hidden");
+                                               const opened = oldNotification.is(":visible") && oldNotification.find(".short-text").hasClass("hidden");
                                                notification.find(".short-text").toggleClass("hidden", opened);
                                                notification.find(".text").toggleClass("hidden", !opened);
                                        }
@@ -1281,8 +1156,8 @@ function requestNotifications() {
                                        checkForRemovedReplies(oldNotification, notification);
                                        oldNotification.replaceWith(notification.show());
                                } else {
-                                       $("#sone #notification-area").append(notification);
-                                       if (value.id.substring(0, 5) != "local") {
+                                       sone.find("#notification-area").append(notification);
+                                       if (value.id.substring(0, 5) !== "local") {
                                                notification.slideDown();
                                                setActivity();
                                        }
@@ -1306,10 +1181,10 @@ function getCurrentSoneId() {
 /**
  * Returns the content of the page-id attribute.
  *
- * @returns The page ID
+ * @returns String The page ID
  */
 function getPageId() {
-       return $("#sone .page-id").text();
+       return sone.find(".page-id").text();
 }
 
 /**
@@ -1319,7 +1194,7 @@ function getPageId() {
  *          <code>false</code> otherwise
  */
 function isIndexPage() {
-       return getPageId() == "index";
+       return getPageId() === "index";
 }
 
 /**
@@ -1331,7 +1206,7 @@ function isIndexPage() {
  * @returns The current page of the pagination
  */
 function getPage(paginationSelector) {
-       pagination = $(paginationSelector);
+       const pagination = $(paginationSelector);
        if (pagination.length > 0) {
                return $(".current-page", paginationSelector).text();
        }
@@ -1345,7 +1220,7 @@ function getPage(paginationSelector) {
  *          page, <code>false</code> otherwise
  */
 function isViewSonePage() {
-       return getPageId() == "view-sone";
+       return getPageId() === "view-sone";
 }
 
 /**
@@ -1355,7 +1230,21 @@ function isViewSonePage() {
  * @returns The ID of the currently shown Sone
  */
 function getShownSoneId() {
-       return $("#sone .sone-id").first().text();
+       return sone.find(".sone-id").first().text();
+}
+
+/**
+ * Returns the ID of all currently visible Sones. This is mainly used on the
+ * â€śKnown Sones” page.
+ *
+ * @returns The ID of the currently shown Sones
+ */
+function getShownSoneIds() {
+       const soneIds = [];
+       sone.find("#known-sones .sone .id").each(function() {
+               soneIds.push($(this).text());
+       });
+       return soneIds.join(",");
 }
 
 /**
@@ -1365,7 +1254,7 @@ function getShownSoneId() {
  *          page, <code>false</code> otherwise
  */
 function isViewPostPage() {
-       return getPageId() == "view-post";
+       return getPageId() === "view-post";
 }
 
 /**
@@ -1375,7 +1264,7 @@ function isViewPostPage() {
  * @returns The ID of the currently shown post
  */
 function getShownPostId() {
-       return $("#sone .post-id").text();
+       return sone.find(".post-id").text();
 }
 
 /**
@@ -1385,7 +1274,7 @@ function getShownPostId() {
  *          Sones” page, <code>false</code> otherwise
  */
 function isKnownSonesPage() {
-       return getPageId() == "known-sones";
+       return getPageId() === "known-sones";
 }
 
 /**
@@ -1409,7 +1298,7 @@ function hasPost(postId) {
  *          exists on the page, <code>false</code> otherwise
  */
 function hasReply(replyId) {
-       return $("#sone .reply#reply-" + replyId).length > 0;
+       return sone.find(".reply#reply-" + replyId).length > 0;
 }
 
 function loadNewPost(postId, soneId, recipientId, time) {
@@ -1417,32 +1306,32 @@ function loadNewPost(postId, soneId, recipientId, time) {
                return;
        }
        if (!isIndexPage() || (getPage(".pagination-index") > 1)) {
-               if (!isViewPostPage() || (getShownPostId() != postId)) {
-                       if (!isViewSonePage() || ((getShownSoneId() != soneId) && (getShownSoneId() != recipientId)) || (getPage(".post-navigation") > 1)) {
+               if (!isViewPostPage() || (getShownPostId() !== postId)) {
+                       if (!isViewSonePage() || ((getShownSoneId() !== soneId) && (getShownSoneId() !== recipientId)) || (getPage(".post-navigation") > 1)) {
                                return;
                        }
                }
        }
-       if (getPostTime($("#sone .post").last()) > time) {
+       if (getPostTime(sone.find(".post").last()) > time) {
                return;
        }
-       ajaxGet("getPost.ajax", { "post" : postId }, function(data, textStatus) {
+       ajaxGet("getPost.ajax", { "post" : postId }, function(data) {
                if ((data != null) && data.success) {
                        if (hasPost(data.post.id)) {
                                return;
                        }
-                       if ((!isIndexPage() || (getPage(".pagination-index") > 1)) && !(isViewSonePage() && ((getShownSoneId() == data.post.sone) || (getShownSoneId() == data.post.recipient) || (getPage(".post-navigation") > 1)))) {
+                       if ((!isIndexPage() || (getPage(".pagination-index") > 1)) && !(isViewSonePage() && ((getShownSoneId() === data.post.sone) || (getShownSoneId() === data.post.recipient) || (getPage(".post-navigation") > 1)))) {
                                return;
                        }
-                       var firstOlderPost = null;
-                       $("#sone .post").each(function() {
+                       let firstOlderPost = null;
+                       sone.find(".post").each(function() {
                                if (getPostTime(this) < data.post.time) {
                                        firstOlderPost = $(this);
                                        return false;
                                }
                        });
-                       newPost = $(data.post.html).addClass("hidden");
-                       if ($(".post-author-local", newPost).text() == "true") {
+                       const newPost = $(data.post.html).addClass("hidden");
+                       if ($(".post-author-local", newPost).text() === "true") {
                                newPost.removeClass("new");
                        }
                        if (firstOlderPost != null) {
@@ -1456,29 +1345,29 @@ function loadNewPost(postId, soneId, recipientId, time) {
        });
 }
 
-function loadNewReply(replyId, soneId, postId, postSoneId) {
+function loadNewReply(replyId, soneId, postId) {
        if (hasReply(replyId)) {
                return;
        }
        if (!hasPost(postId)) {
                return;
        }
-       ajaxGet("getReply.ajax", { "reply": replyId }, function(data, textStatus) {
+       ajaxGet("getReply.ajax", { "reply": replyId }, function(data) {
                /* find post. */
                if ((data != null) && data.success) {
                        if (hasReply(data.reply.id)) {
                                return;
                        }
-                       $("#sone .post#post-" + data.reply.postId).each(function() {
-                               var firstNewerReply = null;
+                       sone.find(".post#post-" + data.reply.postId).each(function() {
+                               let firstNewerReply = null;
                                $(this).find(".replies .reply").each(function() {
                                        if (getReplyTime(this) > data.reply.time) {
                                                firstNewerReply = $(this);
                                                return false;
                                        }
                                });
-                               newReply = $(data.reply.html).addClass("hidden");
-                               if ($(".reply-author-local", newReply).text() == "true") {
+                               const newReply = $(data.reply.html).addClass("hidden");
+                               if ($(".reply-author-local", newReply).text() === "true") {
                                        newReply.removeClass("new");
                                        (function(newReply) {
                                                setTimeout(function() {
@@ -1505,6 +1394,41 @@ function loadNewReply(replyId, soneId, postId, postSoneId) {
        });
 }
 
+function loadLinkedElements(links) {
+       const failedElements = links.filter(function(element) {
+               return element.failed;
+       });
+       if (failedElements.length > 0) {
+               failedElements.forEach(function(element) {
+                       getLinkedElements(element.link).each(function() {
+                               $(this).remove()
+                       });
+               });
+       }
+       const loadedElements = links.filter(function(element) {
+               return !element.loading && !element.failed;
+       });
+       if (loadedElements.length > 0) {
+               ajaxGet("getLinkedElement.ajax", {
+                       "elements": JSON.stringify(loadedElements.map(function(element) {
+                               return element.link;
+                       }))
+               }, function (data) {
+                       if ((data != null) && (data.success)) {
+                               data.linkedElements.forEach(function (linkedElement) {
+                                       getLinkedElements(linkedElement.link).each(function() {
+                                               $(this).replaceWith(linkedElement.html);
+                                       });
+                               });
+                       }
+               });
+       }
+}
+
+function getLinkedElements(link) {
+       return $(".linked-element[title='" + link + "']")
+}
+
 /**
  * Marks the given Sone as known if it is still new.
  *
@@ -1515,7 +1439,7 @@ function loadNewReply(replyId, soneId, postId, postSoneId) {
  *            request
  */
 function markSoneAsKnown(soneElement, skipRequest) {
-       if ($(soneElement).is(".new")) {
+       if ($(soneElement).hasClass("new")) {
                $(soneElement).removeClass("new");
                if ((typeof skipRequest == "undefined") || !skipRequest) {
                        ajaxGet("markAsKnown.ajax", {"formPassword": getFormPassword(), "type": "sone", "id": getSoneId(soneElement)});
@@ -1526,7 +1450,7 @@ function markSoneAsKnown(soneElement, skipRequest) {
 
 function markPostAsKnown(postElements, skipRequest) {
        $(postElements).each(function() {
-               postElement = this;
+               const postElement = this;
                if ($(postElement).hasClass("new") || ((typeof skipRequest != "undefined"))) {
                        (function(postElement) {
                                $(postElement).removeClass("new");
@@ -1543,7 +1467,7 @@ function markPostAsKnown(postElements, skipRequest) {
 
 function markReplyAsKnown(replyElements, skipRequest) {
        $(replyElements).each(function() {
-               replyElement = this;
+               const replyElement = this;
                if ($(replyElement).hasClass("new") || ((typeof skipRequest != "undefined"))) {
                        (function(replyElement) {
                                $(replyElement).removeClass("new");
@@ -1572,7 +1496,7 @@ function updatePostTime(postId, timeText, refreshTime, tooltip) {
        if (!getPost(postId).is(":visible")) {
                return;
        }
-       getPost(postId).find(".post-status-line > .time a").html(timeText).attr("title", tooltip);
+       getPost(postId).find(".post-status-line > .time a").html(timeText).prop("title", tooltip);
        (function(postId, refreshTime) {
                setTimeout(function() {
                        updatePostTimes(postId);
@@ -1587,19 +1511,21 @@ function updatePostTime(postId, timeText, refreshTime, tooltip) {
  *            Comma-separated post IDs
  */
 function updatePostTimes(postIds) {
-       ajaxGet("getTimes.ajax", { "posts" : postIds }, function(data, textStatus) {
-               if ((data != null) && data.success) {
-                       $.each(data.postTimes, function(index, value) {
-                               updatePostTime(index, value.timeText, value.refreshTime, value.tooltip);
-                       });
-               }
-       });
+       if (postIds !== "") {
+        ajaxGet("getTimes.ajax", {"posts": postIds}, function (data) {
+            if ((data != null) && data.success) {
+                $.each(data.postTimes, function (index, value) {
+                    updatePostTime(index, value.timeText, value.refreshTime, value.tooltip);
+                });
+            }
+        });
+    }
 }
 
 /**
  * Updates the time of the reply with the given ID.
  *
- * @param postId
+ * @param replyId
  *            The ID of the reply to update
  * @param timeText
  *            The text of the time to show
@@ -1609,7 +1535,7 @@ function updatePostTimes(postIds) {
  *            The tooltip to show
  */
 function updateReplyTime(replyId, timeText, refreshTime, tooltip) {
-       getReply(replyId).find(".reply-status-line > .time").html(timeText).attr("title", tooltip);
+       getReply(replyId).find(".reply-status-line > .time").html(timeText).prop("title", tooltip);
        (function(replyId, refreshTime) {
                setTimeout(function() {
                        updateReplyTimes(replyId);
@@ -1620,22 +1546,24 @@ function updateReplyTime(replyId, timeText, refreshTime, tooltip) {
 /**
  * Requests new rendered times for the posts with the given IDs.
  *
- * @param postIds
+ * @param replyIds
  *            Comma-separated post IDs
  */
 function updateReplyTimes(replyIds) {
-       ajaxGet("getTimes.ajax", { "replies" : replyIds }, function(data, textStatus) {
-               if ((data != null) && data.success) {
-                       $.each(data.replyTimes, function(index, value) {
-                               updateReplyTime(index, value.timeText, value.refreshTime, value.tooltip);
-                       });
-               }
-       });
+       if (replyIds !== "") {
+        ajaxGet("getTimes.ajax", {"replies": replyIds}, function (data) {
+            if ((data != null) && data.success) {
+                $.each(data.replyTimes, function (index, value) {
+                    updateReplyTime(index, value.timeText, value.refreshTime, value.tooltip);
+                });
+            }
+        });
+    }
 }
 
 function resetActivity() {
-       title = document.title;
-       if (title.indexOf('(') == 0) {
+       const title = document.title;
+       if (title.indexOf('(') === 0) {
                setTitle(title.substr(title.indexOf(' ') + 1));
        }
        iconBlinking = false;
@@ -1643,8 +1571,8 @@ function resetActivity() {
 
 function setActivity() {
        if (!focus) {
-               title = document.title;
-               if (title.indexOf('(') != 0) {
+               const title = document.title;
+               if (title.indexOf('(') !== 0) {
                        setTitle("(!) " + title);
                }
                if (!iconBlinking) {
@@ -1667,10 +1595,10 @@ function setTitle(title) {
 }
 
 /** Whether the icon is currently showing activity. */
-var iconActive = false;
+let iconActive = false;
 
 /** Whether the icon is currently supposed to blink. */
-var iconBlinking = false;
+let iconBlinking = false;
 
 /**
  * Toggles the icon. If the window has gained focus and the icon is still
@@ -1698,7 +1626,7 @@ function toggleIcon() {
  */
 function changeIcon(iconUrl) {
        $("link[rel=icon]").remove();
-       $("head").append($("<link>").attr("rel", "icon").attr("type", "image/png").attr("href", iconUrl));
+       $("head").append($("<link>").prop("rel", "icon").prop("type", "image/png").prop("href", iconUrl));
        $("iframe[id=icon-update]")[0].src += "";
 }
 
@@ -1714,9 +1642,9 @@ function changeIcon(iconUrl) {
  *            user
  */
 function createNotification(id, lastUpdatedTime, text, dismissable) {
-       notification = $("<div></div>").addClass("notification").attr("id", id).attr("lastUpdatedTime", lastUpdatedTime);
+       const notification = $("<div></div>").addClass("notification").prop("id", id).prop("lastUpdatedTime", lastUpdatedTime);
        if (dismissable) {
-               dismissForm = $("#sone #notification-area #notification-dismiss-template").clone().removeClass("hidden").removeAttr("id");
+               const dismissForm = sone.find("#notification-area #notification-dismiss-template").clone().removeClass("hidden").removeAttr("id");
                dismissForm.find("input[name=notification]").val(id);
                notification.append(dismissForm);
        }
@@ -1731,8 +1659,8 @@ function createNotification(id, lastUpdatedTime, text, dismissable) {
  *            The ID of the notification
  */
 function showNotificationDetails(notificationId) {
-       $("#sone .notification#" + notificationId + " .text").removeClass("hidden");
-       $("#sone .notification#" + notificationId + " .short-text").addClass("hidden");
+       sone.find(".notification#" + notificationId + " .text").removeClass("hidden");
+       sone.find(".notification#" + notificationId + " .short-text").addClass("hidden");
 }
 
 /**
@@ -1742,9 +1670,9 @@ function showNotificationDetails(notificationId) {
  *            The ID of the field to delete
  */
 function deleteProfileField(fieldId) {
-       ajaxGet("deleteProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId}, function(data, textStatus) {
+       ajaxGet("deleteProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId}, function(data) {
                if (data && data.success) {
-                       $("#sone .profile-field#" + data.field.id).slideUp();
+                       sone.find(".profile-field#" + data.field.id).slideUp();
                }
        });
 }
@@ -1760,7 +1688,7 @@ function deleteProfileField(fieldId) {
  *            Called when the renaming was successful
  */
 function editProfileField(fieldId, newName, successFunction) {
-       ajaxGet("editProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId, "name": newName}, function(data, textStatus) {
+       ajaxGet("editProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId, "name": newName}, function(data) {
                if (data && data.success) {
                        successFunction();
                }
@@ -1778,7 +1706,7 @@ function editProfileField(fieldId, newName, successFunction) {
  *            Function to call on success
  */
 function moveProfileField(fieldId, direction, successFunction) {
-       ajaxGet("moveProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId, "direction": direction}, function(data, textStatus) {
+       ajaxGet("moveProfileField.ajax", {"formPassword": getFormPassword(), "field": fieldId, "direction": direction}, function(data) {
                if (data && data.success) {
                        successFunction();
                }
@@ -1809,7 +1737,7 @@ function moveProfileFieldDown(fieldId, successFunction) {
        moveProfileField(fieldId, "down", successFunction);
 }
 
-var statusRequestQueued = true;
+let statusRequestQueued = true;
 
 /**
  * Sets the status of the web interface as offline.
@@ -1840,11 +1768,11 @@ function ajaxSuccess() {
  */
 function showOfflineMarker(visible) {
        /* jQuery documentation says toggle() works the other way around?! */
-       $("#sone #offline-marker").toggle(visible);
+       sone.find("#offline-marker").toggle(visible);
        if (visible) {
-               $("#sone #main").addClass("offline");
+               sone.find("#main").addClass("offline");
        } else {
-               $("#sone #main").removeClass("offline");
+               sone.find("#main").removeClass("offline");
        }
 }
 
@@ -1852,38 +1780,47 @@ function showOfflineMarker(visible) {
 // EVERYTHING BELOW HERE IS EXECUTED AFTER LOADING THE PAGE
 //
 
-var focus = true;
-var online = true;
-var initiallyLoggedIn = $("#sone #loggedIn").text() == "true";
-var notLoggedIn = !initiallyLoggedIn;
+const sone = $("#sone");
+let focus = true;
+let online = true;
+const initiallyLoggedIn = sone.find("#loggedIn").text() === "true";
+let notLoggedIn = !initiallyLoggedIn;
 
 /** ID of the next-to-show Sone context menu. */
-var currentSoneMenuId;
+let currentSoneMenuId;
 
 /** Timeout handler for the next-to-show Sone context menu. */
-var currentSoneMenuTimeoutHandler;
+let currentSoneMenuTimeoutHandler;
 
 $(document).ready(function() {
 
+       /* rip out the status update textarea. */
+       sone.find(".rip-out").each(function() {
+               const oldElement = $(this);
+               const newElement = $("<input type='text'/>");
+               newElement.prop("class", oldElement.prop("class")).prop("name", oldElement.prop("name"));
+               oldElement.before(newElement).remove();
+       });
+
        /* 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();
+               sone.find("#update-status .select-sender").css("display", "inline");
+               sone.find("#update-status .sender").hide();
+               sone.find("#update-status .select-sender button").click(function() {
+                       sone.find("#update-status .sender").show();
+                       sone.find("#update-status .select-sender").hide();
                        return false;
                });
-               $("#sone #update-status").submit(function() {
-                       button = $("button:submit", this);
-                       button.attr("disabled", "disabled");
+               sone.find("#update-status").submit(function() {
+                       const button = $("button:submit", this);
+                       button.prop("disabled", "disabled");
                        if ($(this).find(":input.default:enabled").length > 0) {
                                return false;
                        }
-                       sender = $(this).find(":input[name=sender]").val();
-                       text = $(this).find(":input[name=text]:enabled").val();
-                       ajaxGet("createPost.ajax", { "formPassword": getFormPassword(), "sender": sender, "text": text }, function(data, textStatus) {
+                       const sender = $(this).find(":input[name=sender]").val();
+                       const text = $(this).find(":input[name=text]:enabled").val();
+                       ajaxGet("createPost.ajax", { "formPassword": getFormPassword(), "sender": sender, "text": text }, function() {
                                button.removeAttr("disabled");
                        });
                        $(this).find(":input[name=sender]").val(getCurrentSoneId());
@@ -1902,16 +1839,16 @@ $(document).ready(function() {
        /* ajaxify input field on â€śview Sone” page. */
        getTranslation("WebInterface.DefaultText.Message", function(defaultText) {
                registerInputTextareaSwap("#sone #post-message input[name=text]", defaultText, "text", false, false);
-               $("#sone #post-message .select-sender").css("display", "inline");
-               $("#sone #post-message .sender").hide();
-               $("#sone #post-message .select-sender button").click(function() {
-                       $("#sone #post-message .sender").show();
-                       $("#sone #post-message .select-sender").hide();
+               sone.find("#post-message .select-sender").css("display", "inline");
+               sone.find("#post-message .sender").hide();
+               sone.find("#post-message .select-sender button").click(function() {
+                       sone.find("#post-message .sender").show();
+                       sone.find("#post-message .select-sender").hide();
                        return false;
                });
-               $("#sone #post-message").submit(function() {
-                       sender = $(this).find(":input[name=sender]").val();
-                       text = $(this).find(":input[name=text]:enabled").val();
+               sone.find("#post-message").submit(function() {
+                       const sender = $(this).find(":input[name=sender]").val();
+                       const text = $(this).find(":input[name=text]:enabled").val();
                        ajaxGet("createPost.ajax", { "formPassword": getFormPassword(), "recipient": getShownSoneId(), "sender": sender, "text": text });
                        $(this).find(":input[name=sender]").val(getCurrentSoneId());
                        $(this).find(":input[name=text]:enabled").val("").blur();
@@ -1923,11 +1860,11 @@ $(document).ready(function() {
 
        /* Ajaxifies all posts. */
        /* calling getTranslation here will cache the necessary values. */
-       getTranslation("WebInterface.Confirmation.DeletePostButton", function(text) {
-               getTranslation("WebInterface.Confirmation.DeleteReplyButton", function(text) {
-                       getTranslation("WebInterface.DefaultText.Reply", function(text) {
-                               getTranslation("WebInterface.Button.Comment", function(text) {
-                                       $("#sone .post").each(function() {
+       getTranslation("WebInterface.Confirmation.DeletePostButton", function() {
+               getTranslation("WebInterface.Confirmation.DeleteReplyButton", function() {
+                       getTranslation("WebInterface.DefaultText.Reply", function() {
+                getTranslation("WebInterface.Button.Comment", function () {
+                    sone.find(".post").each(function() {
                                                ajaxifyPost(this);
                                        });
                                });
@@ -1936,8 +1873,8 @@ $(document).ready(function() {
        });
 
        /* update post times. */
-       postIds = [];
-       $("#sone .post").each(function() {
+       const postIds = [];
+       sone.find(".post").each(function() {
                postIds.push(getPostId(this));
        });
        updatePostTimes(postIds.join(","));
@@ -1945,15 +1882,15 @@ $(document).ready(function() {
        /* hides all replies but the latest two. */
        if (!isViewPostPage()) {
                getTranslation("WebInterface.ClickToShow.Replies", function(text) {
-                       $("#sone .post .replies").each(function() {
-                               allReplies = $(this).find(".reply");
+                       sone.find(".post .replies").each(function() {
+                               const allReplies = $(this).find(".reply");
                                if (allReplies.length > 2) {
-                                       newHidden = false;
-                                       for (replyIndex = 0; replyIndex < (allReplies.length - 2); ++replyIndex) {
+                                       let newHidden = false;
+                                       for (let replyIndex = 0; replyIndex < (allReplies.length - 2); ++replyIndex) {
                                                $(allReplies[replyIndex]).addClass("hidden");
                                                newHidden |= $(allReplies[replyIndex]).hasClass("new");
                                        }
-                                       clickToShowElement = $("<div></div>").addClass("click-to-show");
+                                       const clickToShowElement = $("<div></div>").addClass("click-to-show");
                                        if (newHidden) {
                                                clickToShowElement.addClass("new");
                                        }
@@ -1970,12 +1907,12 @@ $(document).ready(function() {
                });
        }
 
-       $("#sone .sone").each(function() {
+       sone.find(".sone").each(function() {
                ajaxifySone($(this));
        });
 
        /* process all existing notifications, ajaxify dismiss buttons. */
-       $("#sone #notification-area .notification").each(function() {
+       sone.find("#notification-area .notification").each(function() {
                ajaxifyNotification($(this));
        });