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.

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/soneMenu.html [new file with mode: 0644]
src/main/resources/templates/include/viewPost.html
src/main/resources/templates/include/viewReply.html

index 96b168a..4c36d4e 100644 (file)
@@ -145,7 +145,7 @@ Page.ViewSone.PostList.Title=Posts by {sone}
 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 +236,7 @@ View.Sone.Status.Downloading=This Sone is currently being downloaded.
 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
index 1ca51ae..13aa26e 100644 (file)
@@ -238,6 +238,7 @@ textarea {
        padding: 1ex 0px;
        border-bottom: solid 1px #ccc;
        clear: both;
+       position: relative;
 }
 
 #sone .post.new {
@@ -250,6 +251,33 @@ textarea {
        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;
 }
@@ -265,6 +293,10 @@ textarea {
        font-weight: bold;
 }
 
+#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;
@@ -401,13 +433,17 @@ textarea {
 }
 
 #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;
 }
index 9ecea90..4f8ff2b 100644 (file)
@@ -283,6 +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”).
  *
@@ -834,6 +846,45 @@ function ajaxifyPost(postElement) {
 
        /* 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);
 }
 
 /**
@@ -914,6 +965,45 @@ function ajaxifyReply(replyElement) {
                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);
 }
 
 /**
diff --git a/src/main/resources/templates/include/soneMenu.html b/src/main/resources/templates/include/soneMenu.html
new file mode 100644 (file)
index 0000000..60f1245
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="sone-menu <% class|css|html>">
+       <div class="sone-id hidden"><%sone.id|html></div>
+       <img class="avatar" src="/WebOfTrust/GetIdenticon?identity=<%sone.id|html>&amp;width=64&height=64" width="64" height="64" alt="Avatar Image" />
+       <div class="inner-menu">
+               <div>
+                       <a class="author" href="viewSone.html?sone=<%sone.id|html>"><%sone.niceName|html></a>
+                       <span class="author-wot-link">(<a href="/WebOfTrust/ShowIdentity?id=<%sone.id|html>"><% =View.Post.WebOfTrustLink|l10n|html></a>)</span>
+               </div>
+               <div>
+                       <a class="follow<%if sone.friend> hidden<%/if>"><%= View.Sone.Button.FollowSone|l10n|html></a>
+                       <a class="unfollow<%if !sone.friend> hidden<%/if>"><%= View.Sone.Button.UnfollowSone|l10n|html></a>
+               </div>
+       </div>
+</div>
index 43e9ea4..ec1c299 100644 (file)
@@ -3,7 +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>
index 63957d5..abf3463 100644 (file)
@@ -3,7 +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">