1 /* Sone JavaScript functions. */
4 return $("#sone").hasClass("online");
7 function registerInputTextareaSwap(inputSelector, defaultText, inputFieldName, optional, dontUseTextarea) {
8 $(inputSelector).each(function() {
9 textarea = $(dontUseTextarea ? "<input type=\"text\" name=\"" + inputFieldName + "\">" : "<textarea name=\"" + inputFieldName + "\"></textarea>").blur(function() {
10 if ($(this).val() == "") {
12 inputField = $(this).data("inputField");
13 inputField.show().removeAttr("disabled").addClass("default");
14 inputField.val(defaultText);
16 }).hide().data("inputField", $(this)).val($(this).val());
17 $(this).after(textarea);
18 (function(inputField, textarea) {
19 inputField.focus(function() {
20 $(this).hide().attr("disabled", "disabled");
21 textarea.show().focus();
23 if (inputField.val() == "") {
24 inputField.addClass("default");
25 inputField.val(defaultText);
27 inputField.hide().attr("disabled", "disabled");
30 $(inputField.get(0).form).submit(function() {
31 if (!optional && (textarea.val() == "")) {
35 })($(this), textarea);
39 /* hide all the “create reply” forms until a link is clicked. */
40 function addCommentLinks() {
44 $("#sone .post").each(function() {
45 postId = $(this).attr("id");
46 addCommentLink(postId, $(this));
51 * Adds a “comment” link to all status lines contained in the given element.
56 * The element to add a “comment” link to
58 function addCommentLink(postId, element) {
59 commentElement = (function(postId) {
60 var commentElement = $("<div><span>Comment</span></div>").addClass("show-reply-form").click(function() {
61 replyElement = $("#sone .post#" + postId + " .create-reply");
62 replyElement.removeClass("hidden");
63 replyElement.removeClass("light");
64 (function(replyElement) {
65 replyElement.find("input.reply-input").blur(function() {
66 if ($(this).hasClass("default")) {
67 replyElement.addClass("light");
70 replyElement.removeClass("light");
73 replyElement.find("input.reply-input").focus();
75 return commentElement;
77 element.find(".create-reply").addClass("hidden");
78 element.find(".status-line .time").each(function() {
79 $(this).after(commentElement.clone(true));
84 * Retrieves the translation for the given key and calls the callback function.
85 * The callback function takes a single parameter, the translated string.
88 * The key of the translation string
90 * The callback function
92 function getTranslation(key, callback) {
93 $.getJSON("ajax/getTranslation.ajax", {"key": key}, function(data, textStatus) {
101 * Fires off an AJAX request to retrieve the current status of a Sone.
106 * <code>true</code> if the Sone is local, <code>false</code>
109 function getSoneStatus(soneId, local) {
110 $.getJSON("ajax/getSoneStatus.ajax", {"sone": soneId}, function(data, textStatus) {
111 if ((data != null) && data.success) {
112 updateSoneStatus(soneId, data.name, data.status, data.modified, data.lastUpdated);
116 if (local || (data!= null) && (data.modified || (data.status == "downloading") || (data.status == "inserting"))) {
119 setTimeout(function() {
120 getSoneStatus(soneId, local);
121 }, updateInterval * 1000);
126 * Filters the given Sone ID, replacing all “~” characters by an underscore.
129 * The Sone ID to filter
130 * @returns The filtered Sone ID
132 function filterSoneId(soneId) {
133 return soneId.replace(/[^a-zA-Z0-9-]/g, "_");
137 * Updates the status of the given Sone.
140 * The ID of the Sone to update
142 * The status of the Sone (“idle”, “unknown”, “inserting”,
145 * Whether the Sone is modified
147 * The date and time of the last update (formatted for display)
149 function updateSoneStatus(soneId, name, status, modified, lastUpdated) {
150 $("#sone .sone." + filterSoneId(soneId)).
151 toggleClass("unknown", status == "unknown").
152 toggleClass("idle", status == "idle").
153 toggleClass("inserting", status == "inserting").
154 toggleClass("downloading", status == "downloading").
155 toggleClass("modified", modified);
156 $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(lastUpdated);
157 $("#sone .sone." + filterSoneId(soneId) + " .profile-link a").text(name);
160 var watchedSones = {};
163 * Watches this Sone for updates to its status.
166 * The ID of the Sone to watch
168 * <code>true</code> if the Sone is local, <code>false</code>
171 function watchSone(soneId, local) {
172 if (watchedSones[soneId]) {
175 watchedSones[soneId] = true;
177 setTimeout(function() {
178 getSoneStatus(soneId, local);
184 * Enhances a “delete” button so that the confirmation is done on the same page.
187 * The selector of the button
189 * The text to show on the button
190 * @param deleteCallback
191 * The callback that actually deletes something
193 function enhanceDeleteButton(buttonId, text, deleteCallback) {
194 button = $(buttonId);
196 newButton = $("<button></button>").addClass("confirm").hide().text(text).click(function() {
197 $(this).fadeOut("slow");
200 }).insertAfter(button);
201 (function(button, newButton) {
202 button.click(function() {
203 button.fadeOut("slow", function() {
204 newButton.fadeIn("slow");
205 $(document).one("click", function() {
206 if (this != newButton.get(0)) {
207 newButton.fadeOut(function() {
215 })(button, newButton);
220 * Enhances a post’s “delete” button.
223 * The selector of the button
225 * The ID of the post to delete
227 * The text to replace the button with
229 function enhanceDeletePostButton(buttonId, postId, text) {
230 enhanceDeleteButton(buttonId, text, function() {
231 $.getJSON("ajax/deletePost.ajax", { "post": postId, "formPassword": $("#sone #formPassword").text() }, function(data, textStatus) {
236 $("#sone .post#" + postId).slideUp();
237 } else if (data.error == "invalid-post-id") {
238 alert("Invalid post ID given!");
239 } else if (data.error == "auth-required") {
240 alert("You need to be logged in.");
241 } else if (data.error == "not-authorized") {
242 alert("You are not allowed to delete this post.");
249 * Enhances a reply’s “delete” button.
252 * The selector of the button
254 * The ID of the reply to delete
256 * The text to replace the button with
258 function enhanceDeleteReplyButton(buttonId, replyId, text) {
259 enhanceDeleteButton(buttonId, text, function() {
260 $.getJSON("ajax/deleteReply.ajax", { "reply": replyId, "formPassword": $("#sone #formPassword").text() }, function(data, textStatus) {
265 $("#sone .reply#" + replyId).slideUp();
266 } else if (data.error == "invalid-reply-id") {
267 alert("Invalid reply ID given!");
268 } else if (data.error == "auth-required") {
269 alert("You need to be logged in.");
270 } else if (data.error == "not-authorized") {
271 alert("You are not allowed to delete this reply.");
277 function getFormPassword() {
278 return $("#sone #formPassword").text();
281 function getSoneElement(element) {
282 return $(element).parents(".sone");
286 * Generates a list of Sones by concatening the names of the given sones with a
287 * new line character (“\n”).
290 * The sones to format
291 * @returns {String} The created string
293 function generateSoneList(sones) {
295 $.each(sones, function() {
296 if (soneList != "") {
299 soneList += this.name;
305 * Returns the ID of the Sone that this element belongs to.
308 * The element to locate the matching Sone ID for
309 * @returns The ID of the Sone, or undefined
311 function getSoneId(element) {
312 return getSoneElement(element).find(".id").text();
315 function getPostElement(element) {
316 return $(element).parents(".post");
319 function getPostId(element) {
320 return getPostElement(element).attr("id");
323 function getReplyElement(element) {
324 return $(element).parents(".reply");
327 function getReplyId(element) {
328 return getReplyElement(element).attr("id");
331 function likePost(postId) {
332 $.getJSON("ajax/like.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data, textStatus) {
333 if ((data == null) || !data.success) {
336 $("#sone .post#" + postId + " > .inner-part > .status-line .like").addClass("hidden");
337 $("#sone .post#" + postId + " > .inner-part > .status-line .unlike").removeClass("hidden");
338 updatePostLikes(postId);
342 function unlikePost(postId) {
343 $.getJSON("ajax/unlike.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data, textStatus) {
344 if ((data == null) || !data.success) {
347 $("#sone .post#" + postId + " > .inner-part > .status-line .unlike").addClass("hidden");
348 $("#sone .post#" + postId + " > .inner-part > .status-line .like").removeClass("hidden");
349 updatePostLikes(postId);
353 function updatePostLikes(postId) {
354 $.getJSON("ajax/getLikes.ajax", { "type": "post", "post": postId }, function(data, textStatus) {
355 if ((data != null) && data.success) {
356 $("#sone .post#" + postId + " > .inner-part > .status-line .likes").toggleClass("hidden", data.likes == 0)
357 $("#sone .post#" + postId + " > .inner-part > .status-line .likes span.like-count").text(data.likes);
358 $("#sone .post#" + postId + " > .inner-part > .status-line .likes > span").attr("title", generateSoneList(data.sones));
363 function likeReply(replyId) {
364 $.getJSON("ajax/like.ajax", { "type": "reply", "reply" : replyId, "formPassword": getFormPassword() }, function(data, textStatus) {
365 if ((data == null) || !data.success) {
368 $("#sone .reply#" + replyId + " .status-line .like").addClass("hidden");
369 $("#sone .reply#" + replyId + " .status-line .unlike").removeClass("hidden");
370 updateReplyLikes(replyId);
374 function unlikeReply(replyId) {
375 $.getJSON("ajax/unlike.ajax", { "type": "reply", "reply" : replyId, "formPassword": getFormPassword() }, function(data, textStatus) {
376 if ((data == null) || !data.success) {
379 $("#sone .reply#" + replyId + " .status-line .unlike").addClass("hidden");
380 $("#sone .reply#" + replyId + " .status-line .like").removeClass("hidden");
381 updateReplyLikes(replyId);
385 function updateReplyLikes(replyId) {
386 $.getJSON("ajax/getLikes.ajax", { "type": "reply", "reply": replyId }, function(data, textStatus) {
387 if ((data != null) && data.success) {
388 $("#sone .reply#" + replyId + " .status-line .likes").toggleClass("hidden", data.likes == 0)
389 $("#sone .reply#" + replyId + " .status-line .likes span.like-count").text(data.likes);
390 $("#sone .reply#" + replyId + " .status-line .likes > span").attr("title", generateSoneList(data.sones));
396 * Posts a reply and calls the given callback when the request finishes.
399 * The ID of the post the reply refers to
402 * @param callbackFunction
403 * The callback function to call when the request finishes (takes 3
404 * parameters: success, error, replyId)
406 function postReply(postId, text, callbackFunction) {
407 $.getJSON("ajax/createReply.ajax", { "formPassword" : getFormPassword(), "post" : postId, "text": text }, function(data, textStatus) {
409 /* TODO - show error */
413 callbackFunction(true, null, data.reply);
415 callbackFunction(false, data.error);
421 * Requests information about the reply with the given ID.
424 * The ID of the reply
425 * @param callbackFunction
426 * A callback function (parameters soneId, soneName, replyTime,
427 * replyDisplayTime, text, html)
429 function getReply(replyId, callbackFunction) {
430 $.getJSON("ajax/getReply.ajax", { "reply" : replyId }, function(data, textStatus) {
431 if ((data != null) && data.success) {
432 callbackFunction(data.soneId, data.soneName, data.time, data.displayTime, data.text, data.html);
438 * Ajaxifies the given notification by replacing the form with AJAX.
440 * @param notification
441 * jQuery object representing the notification.
443 function ajaxifyNotification(notification) {
444 notification.find("form.dismiss").submit(function() {
447 notification.find("form.dismiss button").click(function() {
448 $.getJSON("ajax/dismissNotification.ajax", { "formPassword" : getFormPassword(), "notification" : notification.attr("id") }, function(data, textStatus) {
449 /* dismiss in case of error, too. */
450 notification.slideUp();
457 * Retrieves all changed notifications.
459 function getNotifications() {
460 $.getJSON("ajax/getNotifications.ajax", {}, function(data, textStatus) {
461 if ((data != null) && data.success) {
462 $.each(data.notifications, function(index, value) {
463 oldNotification = $("#sone #notification-area .notification#" + value.id);
464 notification = ajaxifyNotification(createNotification(value.id, value.text, value.dismissable)).hide();
465 if (oldNotification.length != 0) {
466 oldNotification.slideUp();
467 notification.insertBefore(oldNotification);
469 $("#sone #notification-area").append(notification);
471 notification.slideDown();
473 $.each(data.removedNotifications, function(index, value) {
474 $("#sone #notification-area .notification#" + value.id).slideUp();
476 setTimeout(getNotifications, 5000);
478 setTimeout(getNotifications, 30000);
484 * Creates a new notification.
487 * The ID of the notificaiton
489 * The text of the notification
491 * <code>true</code> if the notification can be dismissed by the
494 function createNotification(id, text, dismissable) {
495 notification = $("<div></div>").addClass("notification").attr("id", id);
497 dismissForm = $("#sone #notification-area #notification-dismiss-template").clone().removeClass("hidden").removeAttr("id")
498 dismissForm.find("input[name=notification]").val(id);
499 notification.append(dismissForm);
501 notification.append(text);