Add context menues when hovering over avatar images.
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 22 Jun 2011 19:36:38 +0000 (21:36 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 22 Jun 2011 19:36:38 +0000 (21:36 +0200)
This fixes #191.

1  2 
src/main/resources/i18n/sone.en.properties
src/main/resources/static/css/sone.css
src/main/resources/static/javascript/sone.js
src/main/resources/templates/include/viewPost.html
src/main/resources/templates/include/viewReply.html

@@@ -38,7 -38,6 +38,7 @@@ Page.Options.Option.AutoFollow.Descript
  Page.Options.Section.RuntimeOptions.Title=Runtime Behaviour
  Page.Options.Option.InsertionDelay.Description=The number of seconds the Sone inserter waits after a modification of a Sone before it is being inserted.
  Page.Options.Option.PostsPerPage.Description=The number of posts to display on a page before pagination controls are being shown.
 +Page.Options.Option.CharactersPerPost.Description=The number of characters to display from a post before cutting it off and showing a link to expand it (-1 to disable).
  Page.Options.Option.RequireFullAccess.Description=Whether to deny access to Sone to any host that has not been granted full access.
  Page.Options.Section.TrustOptions.Title=Trust Settings
  Page.Options.Option.PositiveTrust.Description=The amount of positive trust you want to assign to other Sones by clicking the checkmark below a post or reply.
@@@ -145,7 -144,7 +145,7 @@@ Page.ViewSone.PostList.Title=Posts by {
  Page.ViewSone.PostList.Text.NoPostYet=This Sone has not yet posted anything.
  Page.ViewSone.Profile.Title=Profile
  Page.ViewSone.Profile.Label.Name=Name
- Page.ViewSone.Profile.Name.WoTLink=Web of trust profile
+ Page.ViewSone.Profile.Name.WoTLink=web of trust profile
  Page.ViewSone.Replies.Title=Posts {sone} has replied to
  
  Page.ViewPost.Title=View Post - Sone
@@@ -236,6 -235,7 +236,7 @@@ View.Sone.Status.Downloading=This Sone 
  View.Sone.Status.Inserting=This Sone is currently being inserted.
  
  View.Post.UnknownAuthor=(unknown)
+ View.Post.WebOfTrustLink=web of trust profile
  View.Post.Permalink=link post
  View.Post.PermalinkAuthor=link author
  View.Post.Bookmarks.PostIsBookmarked=Post is bookmarked, click to remove from bookmarks
@@@ -247,8 -247,6 +248,8 @@@ View.Post.LikeLink=Lik
  View.Post.UnlikeLink=Unlike
  View.Post.ShowSource=Toggle Parser
  View.Post.NotDownloaded=This post has not yet been downloaded, or it has been deleted.
 +View.Post.ShowMore=show more
 +View.Post.ShowLess=show less
  
  View.UpdateStatus.Text.ChooseSenderIdentity=Choose the sender identity
  
@@@ -285,7 -283,6 +286,7 @@@ WebInterface.DefaultText.BirthYear=Yea
  WebInterface.DefaultText.FieldName=Field name
  WebInterface.DefaultText.Option.InsertionDelay=Time to wait after a Sone is modified before insert (in seconds)
  WebInterface.DefaultText.Option.PostsPerPage=Number of posts to show on a page
 +WebInterface.DefaultText.Option.CharactersPerPost=Number of characters per post after which to cut the post off
  WebInterface.DefaultText.Option.PositiveTrust=The positive trust to assign
  WebInterface.DefaultText.Option.NegativeTrust=The negative trust to assign
  WebInterface.DefaultText.Option.TrustComment=The comment to set in the web of trust
@@@ -145,7 -145,7 +145,7 @@@ textarea 
  
  #sone #notification-area #local-post-notification, #sone #notification-area #local-reply-notification {
        display: none;
 -} 
 +}
  
  #sone #plugin-warning {
        border: solid 0.5em red;
        padding: 1ex 0px;
        border-bottom: solid 1px #ccc;
        clear: both;
+       position: relative;
  }
  
  #sone .post.new {
        border-bottom: none;
  }
  
+ #sone .post .sone-menu {
+       position: absolute;
+       top: 0;
+       left: -1ex;
+       padding: 1ex 1ex;
+       margin: -1px -1px;
+       display: none;
+       background-color: rgb(255, 255, 224);
+       border: solid 1px rgb(0, 0, 0);
+       z-index: 1;
+ }
+ #sone .post .sone-menu .avatar {
+       position: absolute;
+       margin-right: 1ex;
+ }
+ #sone .post .sone-menu .inner-menu {
+       margin-left: 64px;
+       padding-left: 1ex;
+       min-height: 64px;
+ }
+ #sone .sone-menu .follow, #sone .sone-menu .unfollow {
+       cursor: pointer;
+ }
  #sone .post > .avatar {
        position: absolute;
  }
        font-weight: bold;
  }
  
 -#sone .post .text, #sone .post .raw-text {
+ #sone .post .author-wot-link {
+       font-size: 90%;
+ }
 +#sone .post .text, #sone .post .raw-text, #sone .post .short-text {
        display: inline;
        white-space: pre-wrap;
 +      word-wrap: break-word;
  }
  
 -#sone .post .text.hidden, #sone .post .raw-text.hidden {
 +#sone .post .text.hidden, #sone .post .raw-text.hidden, #sone .post .short-text.hidden {
        display: none;
  }
  
 +#sone .post .expand-post-text:before, #sone .post .expand-reply-text:before {
 +      content: "» ";
 +}
 +
 +#sone .post .shrink-post-text:before, #sone .post .shrink-reply-text:before {
 +      content: "« ";
 +}
 +
 +#sone .post .shrink-post-text {
 +      cursor: pointer;
 +}
 +
  #sone .post .status-line {
        margin-top: 0.5ex;
        font-size: 85%;
  }
  
  #sone .post .reply {
+       position: relative;
        clear: both;
        background-color: #f0f0ff;
-       font-size: 85%;
        margin: 1ex 0px;
        padding: 1ex;
  }
  
+ #sone .post .reply .inner-part {
+       font-size: 85%;
+ }
  #sone .post .reply.new {
        background-color: #ffffa0;
  }
        color: red;
        font-style: italic;
  }
 +
 +#sone #sort-options {
 +      margin-bottom: 1em;
 +}
@@@ -283,6 -283,18 +283,18 @@@ function getSoneElement(element) 
  }
  
  /**
+  * Returns the ID of the sone of the context menu that contains the given
+  * element.
+  *
+  * @param element
+  *            The element within a context menu to get the Sone ID for
+  * @return The Sone ID
+  */
+ function getMenuSone(element) {
+       return $(element).closest(".sone-menu").find(".sone-id").text();
+ }
+ /**
   * Generates a list of Sones by concatening the names of the given sones with a
   * new line character (“\n”).
   *
@@@ -759,43 -771,11 +771,43 @@@ 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);
 +                      rawPostText.toggleClass("hidden");
 +                      if (rawPostText.hasClass("hidden")) {
 +                              $(".post-text.short-text", post).removeClass("hidden");
 +                              $(".post-text.text", post).addClass("hidden");
 +                              $(".expand-post-text", post).removeClass("hidden");
 +                              $(".shrink-post-text", post).addClass("hidden");
 +                      } else {
 +                              $(".post-text.short-text", post).addClass("hidden");
 +                              $(".post-text.text", post).addClass("hidden");
 +                              $(".expand-post-text", post).addClass("hidden");
 +                              $(".shrink-post-text", post).addClass("hidden");
 +                      }
 +                      return false;
 +              });
 +      });
 +
 +      /* convert “show more” link into javascript function. */
 +      $(postElement).find(".expand-post-text").each(function() {
 +              $(this).click(function() {
                        $(".post-text.text", getPostElement(this)).toggleClass("hidden");
 -                      $(".post-text.raw-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(".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;
 +              })
 +      });
  
        /* add “comment” link. */
        addCommentLink(getPostId(postElement), getPostAuthor(postElement), postElement, $(postElement).find(".post-status-line .permalink-author"));
  
        /* hide reply input field. */
        $(postElement).find(".create-reply").addClass("hidden");
+       /* show Sone menu when hovering over the avatar. */
+       $(postElement).find(".post-avatar").mouseover(function() {
+               $(".sone-post-menu", postElement).mouseleave(function() {
+                       $(this).fadeOut();
+               }).fadeIn();
+               return false;
+       });
+       (function(postElement) {
+               var soneId = $(".sone-id", 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;
+               });
+       })(postElement);
  }
  
  /**
@@@ -863,40 -882,8 +914,40 @@@ 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);
 +                      rawReplyText.toggleClass("hidden");
 +                      if (rawReplyText.hasClass("hidden")) {
 +                              $(".reply-text.short-text", reply).removeClass("hidden");
 +                              $(".reply-text.text", reply).addClass("hidden");
 +                              $(".expand-reply-text", reply).removeClass("hidden");
 +                              $(".shrink-reply-text", reply).addClass("hidden");
 +                      } else {
 +                              $(".reply-text.short-text", reply).addClass("hidden");
 +                              $(".reply-text.text", reply).addClass("hidden");
 +                              $(".expand-reply-text", reply).addClass("hidden");
 +                              $(".shrink-reply-text", reply).addClass("hidden");
 +                      }
 +                      return false;
 +              });
 +      });
 +
 +      /* convert “show more” link into javascript function. */
 +      $(replyElement).find(".expand-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;
 +              });
 +      });
 +      $(replyElement).find(".shrink-reply-text").each(function() {
 +              $(this).click(function() {
                        $(".reply-text.text", getReplyElement(this)).toggleClass("hidden");
 -                      $(".reply-text.raw-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;
                });
        });
                untrustSone(getReplyAuthor(this));
                return false;
        });
+       /* show Sone menu when hovering over the avatar. */
+       $(replyElement).find(".reply-avatar").mouseover(function() {
+               $(".sone-reply-menu", replyElement).mouseleave(function() {
+                       $(this).fadeOut();
+               }).fadeIn();
+               return false;
+       });
+       (function(replyElement) {
+               var soneId = $(".sone-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;
+               });
+       })(replyElement);
  }
  
  /**
@@@ -3,7 -3,8 +3,8 @@@
        <div class="post-time hidden"><% post.time|html></div>
        <div class="post-author hidden"><% post.sone.id|html></div>
        <div class="post-author-local hidden"><% post.sone.local></div>
-       <div class="avatar">
+       <%include include/soneMenu.html class=="sone-post-menu" sone=post.sone>
+       <div class="avatar post-avatar" >
                <%if post.loaded>
                        <img src="/WebOfTrust/GetIdenticon?identity=<% post.sone.id|html>&amp;width=48&height=48" width="48" height="48" alt="Avatar Image" />
                <%else>
                        <%/if>
                        <% post.text|html|store key=originalText text=true>
                        <% post.text|parse sone=post.sone|store key=parsedText text=true>
 +                      <% post.text|parse sone=post.sone length=core.preferences.charactersPerPost|store key=shortText text=true>
                        <div class="post-text raw-text<%if !raw> hidden<%/if>"><% originalText></div>
 -                      <div class="post-text text<%if raw> hidden<%/if>"><% parsedText></div>
 +                      <div class="post-text text<%if raw> hidden<%/if><%if !shortText|match key=parsedText> hidden<%/if>"><% parsedText></div>
 +                      <div class="post-text short-text<%if raw> hidden<%/if><%if shortText|match key=parsedText> hidden<%/if>"><% shortText></div>
 +                      <%if !shortText|match key=parsedText><%if !raw><a class="expand-post-text" href="viewPost.html?post=<% post.id|html>&amp;raw=true"><%= View.Post.ShowMore|l10n|html></a><%/if><%/if>
 +                      <%if !shortText|match key=parsedText><%if !raw><a class="shrink-post-text hidden"><%= View.Post.ShowLess|l10n|html></a><%/if><%/if>
                </div>
                <div class="post-status-line status-line<%if !post.loaded> hidden<%/if>">
                        <div class="bookmarks">
@@@ -3,7 -3,8 +3,8 @@@
        <div class="reply-time hidden"><% reply.time|html></div>
        <div class="reply-author hidden"><% reply.sone.id|html></div>
        <div class="reply-author-local hidden"><% reply.sone.local></div>
-       <div class="avatar">
+       <%include include/soneMenu.html class=="sone-reply-menu" sone=reply.sone>
+       <div class="avatar reply-avatar">
                <img src="/WebOfTrust/GetIdenticon?identity=<% reply.sone.id|html>&amp;width=36&height=36" width="36" height="36" alt="Avatar Image" />
        </div>
        <div class="inner-part">
                        <div class="author profile-link"><a href="viewSone.html?sone=<% reply.sone.id|html>"><% reply.sone.niceName|html></a></div>
                        <% reply.text|html|store key=originalText text=true>
                        <% reply.text|parse sone=reply.sone|store key=parsedText text=true>
 +                      <% reply.text|parse sone=reply.sone length=core.preferences.charactersPerPost|store key=shortText text=true>
                        <div class="reply-text raw-text<%if !raw> hidden<%/if>"><% originalText></div>
 -                      <div class="reply-text text<%if raw> hidden<%/if>"><% parsedText></div>
 +                      <div class="reply-text text<%if raw> hidden<%/if><%if !shortText|match key=parsedText> hidden<%/if>"><% parsedText></div>
 +                      <div class="reply-text short-text<%if raw> hidden<%/if><%if shortText|match key=parsedText> hidden<%/if>"><% shortText></div>
 +                      <%if !shortText|match key=parsedText><%if !raw><a class="expand-reply-text" href="viewPost.html?post=<% reply.post.id|html>&amp;raw=true"><%= View.Post.ShowMore|l10n|html></a><%/if><%/if>
 +                      <%if !shortText|match key=parsedText><%if !raw><a class="shrink-reply-text hidden"><%= View.Post.ShowLess|l10n|html></a><%/if><%/if>
                </div>
                <div class="reply-status-line status-line">
                        <div class="time"><% reply.time|date format="MMM d, yyyy, HH:mm:ss"></div>