1 /* Sone JavaScript functions. */
3 /* jQuery overrides. */
4 oldGetJson = jQuery.prototype.getJSON;
5 jQuery.prototype.getJSON = function(url, data, successCallback, errorCallback) {
6 if (typeof errorCallback == "undefined") {
7 return oldGetJson(url, data, successCallback);
9 if (jQuery.isFunction(data)) {
10 errorCallback = successCallback;
11 successCallback = data;
17 success: successCallback,
23 return $("#sone").hasClass("online");
26 function registerInputTextareaSwap(inputSelector, defaultText, inputFieldName, optional, dontUseTextarea) {
27 $(inputSelector).each(function() {
28 textarea = $(dontUseTextarea ? "<input type=\"text\" name=\"" + inputFieldName + "\">" : "<textarea name=\"" + inputFieldName + "\"></textarea>").blur(function() {
29 if ($(this).val() == "") {
31 inputField = $(this).data("inputField");
32 inputField.show().removeAttr("disabled").addClass("default");
33 inputField.val(defaultText);
35 }).hide().data("inputField", $(this)).val($(this).val());
36 $(this).after(textarea);
37 (function(inputField, textarea) {
38 inputField.focus(function() {
39 $(this).hide().attr("disabled", "disabled");
40 textarea.show().focus();
42 if (inputField.val() == "") {
43 inputField.addClass("default");
44 inputField.val(defaultText);
46 inputField.hide().attr("disabled", "disabled");
49 $(inputField.get(0).form).submit(function() {
50 if (!optional && (textarea.val() == "")) {
54 })($(this), textarea);
58 /* hide all the “create reply” forms until a link is clicked. */
59 function addCommentLinks() {
63 $("#sone .post").each(function() {
64 postId = $(this).attr("id");
65 addCommentLink(postId, $(this));
70 * Adds a “comment” link to all status lines contained in the given element.
75 * The element to add a “comment” link to
77 function addCommentLink(postId, element) {
78 commentElement = (function(postId) {
79 var commentElement = $("<div><span>Comment</span></div>").addClass("show-reply-form").click(function() {
80 replyElement = $("#sone .post#" + postId + " .create-reply");
81 replyElement.removeClass("hidden");
82 replyElement.removeClass("light");
83 (function(replyElement) {
84 replyElement.find("input.reply-input").blur(function() {
85 if ($(this).hasClass("default")) {
86 replyElement.addClass("light");
89 replyElement.removeClass("light");
92 replyElement.find("input.reply-input").focus();
94 return commentElement;
96 element.find(".create-reply").addClass("hidden");
97 element.find(".status-line .time").each(function() {
98 $(this).after(commentElement.clone(true));
103 * Retrieves the translation for the given key and calls the callback function.
104 * The callback function takes a single parameter, the translated string.
107 * The key of the translation string
109 * The callback function
111 function getTranslation(key, callback) {
112 $.getJSON("ajax/getTranslation.ajax", {"key": key}, function(data, textStatus) {
113 if ((data != null) && data.success) {
114 callback(data.value);
116 }, function(xmlHttpRequest, textStatus, error) {
122 * Fires off an AJAX request to retrieve the current status of a Sone.
127 * <code>true</code> if the Sone is local, <code>false</code>
130 function getSoneStatus(soneId, local) {
131 $.getJSON("ajax/getSoneStatus.ajax", {"sone": soneId}, function(data, textStatus) {
132 if ((data != null) && data.success) {
133 updateSoneStatus(soneId, data.name, data.status, data.modified, data.lastUpdated);
137 if (local || (data!= null) && (data.modified || (data.status == "downloading") || (data.status == "inserting"))) {
140 setTimeout(function() {
141 getSoneStatus(soneId, local);
142 }, updateInterval * 1000);
143 }, function(xmlHttpRequest, textStatus, error) {
149 * Filters the given Sone ID, replacing all “~” characters by an underscore.
152 * The Sone ID to filter
153 * @returns The filtered Sone ID
155 function filterSoneId(soneId) {
156 return soneId.replace(/[^a-zA-Z0-9-]/g, "_");
160 * Updates the status of the given Sone.
163 * The ID of the Sone to update
165 * The status of the Sone (“idle”, “unknown”, “inserting”,
168 * Whether the Sone is modified
170 * The date and time of the last update (formatted for display)
172 function updateSoneStatus(soneId, name, status, modified, lastUpdated) {
173 $("#sone .sone." + filterSoneId(soneId)).
174 toggleClass("unknown", status == "unknown").
175 toggleClass("idle", status == "idle").
176 toggleClass("inserting", status == "inserting").
177 toggleClass("downloading", status == "downloading").
178 toggleClass("modified", modified);
179 $("#sone .sone." + filterSoneId(soneId) + " .last-update span.time").text(lastUpdated);
180 $("#sone .sone." + filterSoneId(soneId) + " .profile-link a").text(name);
183 var watchedSones = {};
186 * Watches this Sone for updates to its status.
189 * The ID of the Sone to watch
191 * <code>true</code> if the Sone is local, <code>false</code>
194 function watchSone(soneId, local) {
195 if (watchedSones[soneId]) {
198 watchedSones[soneId] = true;
200 setTimeout(function() {
201 getSoneStatus(soneId, local);
207 * Enhances a “delete” button so that the confirmation is done on the same page.
210 * The selector of the button
212 * The text to show on the button
213 * @param deleteCallback
214 * The callback that actually deletes something
216 function enhanceDeleteButton(buttonId, text, deleteCallback) {
217 button = $(buttonId);
219 newButton = $("<button></button>").addClass("confirm").hide().text(text).click(function() {
220 $(this).fadeOut("slow");
223 }).insertAfter(button);
224 (function(button, newButton) {
225 button.click(function() {
226 button.fadeOut("slow", function() {
227 newButton.fadeIn("slow");
228 $(document).one("click", function() {
229 if (this != newButton.get(0)) {
230 newButton.fadeOut(function() {
238 })(button, newButton);
243 * Enhances a post’s “delete” button.
246 * The selector of the button
248 * The ID of the post to delete
250 * The text to replace the button with
252 function enhanceDeletePostButton(buttonId, postId, text) {
253 enhanceDeleteButton(buttonId, text, function() {
254 $.getJSON("ajax/deletePost.ajax", { "post": postId, "formPassword": $("#sone #formPassword").text() }, function(data, textStatus) {
259 $("#sone .post#" + postId).slideUp();
260 } else if (data.error == "invalid-post-id") {
261 alert("Invalid post ID given!");
262 } else if (data.error == "auth-required") {
263 alert("You need to be logged in.");
264 } else if (data.error == "not-authorized") {
265 alert("You are not allowed to delete this post.");
267 }, function(xmlHttpRequest, textStatus, error) {
274 * Enhances a reply’s “delete” button.
277 * The selector of the button
279 * The ID of the reply to delete
281 * The text to replace the button with
283 function enhanceDeleteReplyButton(buttonId, replyId, text) {
284 enhanceDeleteButton(buttonId, text, function() {
285 $.getJSON("ajax/deleteReply.ajax", { "reply": replyId, "formPassword": $("#sone #formPassword").text() }, function(data, textStatus) {
290 $("#sone .reply#" + replyId).slideUp();
291 } else if (data.error == "invalid-reply-id") {
292 alert("Invalid reply ID given!");
293 } else if (data.error == "auth-required") {
294 alert("You need to be logged in.");
295 } else if (data.error == "not-authorized") {
296 alert("You are not allowed to delete this reply.");
298 }, function(xmlHttpRequest, textStatus, error) {
304 function getFormPassword() {
305 return $("#sone #formPassword").text();
308 function getSoneElement(element) {
309 return $(element).parents(".sone");
313 * Generates a list of Sones by concatening the names of the given sones with a
314 * new line character (“\n”).
317 * The sones to format
318 * @returns {String} The created string
320 function generateSoneList(sones) {
322 $.each(sones, function() {
323 if (soneList != "") {
326 soneList += this.name;
332 * Returns the ID of the Sone that this element belongs to.
335 * The element to locate the matching Sone ID for
336 * @returns The ID of the Sone, or undefined
338 function getSoneId(element) {
339 return getSoneElement(element).find(".id").text();
342 function getPostElement(element) {
343 return $(element).parents(".post");
346 function getPostId(element) {
347 return getPostElement(element).attr("id");
350 function getReplyElement(element) {
351 return $(element).parents(".reply");
354 function getReplyId(element) {
355 return getReplyElement(element).attr("id");
358 function likePost(postId) {
359 $.getJSON("ajax/like.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data, textStatus) {
360 if ((data == null) || !data.success) {
363 $("#sone .post#" + postId + " > .inner-part > .status-line .like").addClass("hidden");
364 $("#sone .post#" + postId + " > .inner-part > .status-line .unlike").removeClass("hidden");
365 updatePostLikes(postId);
366 }, function(xmlHttpRequest, textStatus, error) {
371 function unlikePost(postId) {
372 $.getJSON("ajax/unlike.ajax", { "type": "post", "post" : postId, "formPassword": getFormPassword() }, function(data, textStatus) {
373 if ((data == null) || !data.success) {
376 $("#sone .post#" + postId + " > .inner-part > .status-line .unlike").addClass("hidden");
377 $("#sone .post#" + postId + " > .inner-part > .status-line .like").removeClass("hidden");
378 updatePostLikes(postId);
379 }, function(xmlHttpRequest, textStatus, error) {
384 function updatePostLikes(postId) {
385 $.getJSON("ajax/getLikes.ajax", { "type": "post", "post": postId }, function(data, textStatus) {
386 if ((data != null) && data.success) {
387 $("#sone .post#" + postId + " > .inner-part > .status-line .likes").toggleClass("hidden", data.likes == 0)
388 $("#sone .post#" + postId + " > .inner-part > .status-line .likes span.like-count").text(data.likes);
389 $("#sone .post#" + postId + " > .inner-part > .status-line .likes > span").attr("title", generateSoneList(data.sones));
391 }, function(xmlHttpRequest, textStatus, error) {
396 function likeReply(replyId) {
397 $.getJSON("ajax/like.ajax", { "type": "reply", "reply" : replyId, "formPassword": getFormPassword() }, function(data, textStatus) {
398 if ((data == null) || !data.success) {
401 $("#sone .reply#" + replyId + " .status-line .like").addClass("hidden");
402 $("#sone .reply#" + replyId + " .status-line .unlike").removeClass("hidden");
403 updateReplyLikes(replyId);
404 }, function(xmlHttpRequest, textStatus, error) {
409 function unlikeReply(replyId) {
410 $.getJSON("ajax/unlike.ajax", { "type": "reply", "reply" : replyId, "formPassword": getFormPassword() }, function(data, textStatus) {
411 if ((data == null) || !data.success) {
414 $("#sone .reply#" + replyId + " .status-line .unlike").addClass("hidden");
415 $("#sone .reply#" + replyId + " .status-line .like").removeClass("hidden");
416 updateReplyLikes(replyId);
417 }, function(xmlHttpRequest, textStatus, error) {
422 function updateReplyLikes(replyId) {
423 $.getJSON("ajax/getLikes.ajax", { "type": "reply", "reply": replyId }, function(data, textStatus) {
424 if ((data != null) && data.success) {
425 $("#sone .reply#" + replyId + " .status-line .likes").toggleClass("hidden", data.likes == 0)
426 $("#sone .reply#" + replyId + " .status-line .likes span.like-count").text(data.likes);
427 $("#sone .reply#" + replyId + " .status-line .likes > span").attr("title", generateSoneList(data.sones));
429 }, function(xmlHttpRequest, textStatus, error) {
435 * Posts a reply and calls the given callback when the request finishes.
438 * The ID of the post the reply refers to
441 * @param callbackFunction
442 * The callback function to call when the request finishes (takes 3
443 * parameters: success, error, replyId)
445 function postReply(postId, text, callbackFunction) {
446 $.getJSON("ajax/createReply.ajax", { "formPassword" : getFormPassword(), "post" : postId, "text": text }, function(data, textStatus) {
448 /* TODO - show error */
452 callbackFunction(true, null, data.reply);
454 callbackFunction(false, data.error);
456 }, function(xmlHttpRequest, textStatus, error) {
462 * Requests information about the reply with the given ID.
465 * The ID of the reply
466 * @param callbackFunction
467 * A callback function (parameters soneId, soneName, replyTime,
468 * replyDisplayTime, text, html)
470 function getReply(replyId, callbackFunction) {
471 $.getJSON("ajax/getReply.ajax", { "reply" : replyId }, function(data, textStatus) {
472 if ((data != null) && data.success) {
473 callbackFunction(data.soneId, data.soneName, data.time, data.displayTime, data.text, data.html);
475 }, function(xmlHttpRequest, textStatus, error) {
481 * Ajaxifies the given notification by replacing the form with AJAX.
483 * @param notification
484 * jQuery object representing the notification.
486 function ajaxifyNotification(notification) {
487 notification.find("form.dismiss").submit(function() {
490 notification.find("form.dismiss button").click(function() {
491 $.getJSON("ajax/dismissNotification.ajax", { "formPassword" : getFormPassword(), "notification" : notification.attr("id") }, function(data, textStatus) {
492 /* dismiss in case of error, too. */
493 notification.slideUp();
494 }, function(xmlHttpRequest, textStatus, error) {
502 * Retrieves all changed notifications.
504 function getNotifications() {
505 $.getJSON("ajax/getNotifications.ajax", {}, function(data, textStatus) {
506 if ((data != null) && data.success) {
507 $.each(data.notifications, function(index, value) {
508 oldNotification = $("#sone #notification-area .notification#" + value.id);
509 notification = ajaxifyNotification(createNotification(value.id, value.text, value.dismissable)).hide();
510 if (oldNotification.length != 0) {
511 oldNotification.replaceWith(notification.show());
513 $("#sone #notification-area").append(notification);
514 notification.slideDown();
517 $.each(data.removedNotifications, function(index, value) {
518 $("#sone #notification-area .notification#" + value.id).slideUp();
520 setTimeout(getNotifications, 5000);
522 setTimeout(getNotifications, 30000);
524 }, function(xmlHttpRequest, textStatus, error) {
530 * Creates a new notification.
533 * The ID of the notificaiton
535 * The text of the notification
537 * <code>true</code> if the notification can be dismissed by the
540 function createNotification(id, text, dismissable) {
541 notification = $("<div></div>").addClass("notification").attr("id", id);
543 dismissForm = $("#sone #notification-area #notification-dismiss-template").clone().removeClass("hidden").removeAttr("id")
544 dismissForm.find("input[name=notification]").val(id);
545 notification.append(dismissForm);
547 notification.append(text);