thread_update.js
270 lines
| 7.4 KiB
| application/javascript
|
JavascriptLexer
neko259
|
r365 | /* | ||
@licstart The following is the entire license notice for the | ||||
JavaScript code in this page. | ||||
Copyright (C) 2013 neko259 | ||||
The JavaScript code in this page is free software: you can | ||||
redistribute it and/or modify it under the terms of the GNU | ||||
General Public License (GNU GPL) as published by the Free Software | ||||
Foundation, either version 3 of the License, or (at your option) | ||||
any later version. The code is distributed WITHOUT ANY WARRANTY; | ||||
without even the implied warranty of MERCHANTABILITY or FITNESS | ||||
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. | ||||
As additional permission under GNU GPL version 3 section 7, you | ||||
may distribute non-source (e.g., minimized or compacted) forms of | ||||
that code without the copy of the GNU GPL normally required by | ||||
section 4, provided you include this license notice and a URL | ||||
through which recipients can access the Corresponding Source. | ||||
@licend The above is the entire license notice | ||||
for the JavaScript code in this page. | ||||
*/ | ||||
neko259
|
r361 | var THREAD_UPDATE_DELAY = 10000; | ||
var loading = false; | ||||
neko259
|
r363 | var lastUpdateTime = null; | ||
neko259
|
r468 | var unreadPosts = 0 | ||
neko259
|
r363 | |||
function blink(node) { | ||||
var blinkCount = 2; | ||||
var nodeToAnimate = node; | ||||
for (var i = 0; i < blinkCount; i++) { | ||||
neko259
|
r481 | nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0); | ||
neko259
|
r363 | } | ||
} | ||||
neko259
|
r361 | |||
function updateThread() { | ||||
if (loading) { | ||||
return; | ||||
} | ||||
loading = true; | ||||
var threadPosts = $('div.thread').children('.post'); | ||||
var lastPost = threadPosts.last(); | ||||
var threadId = threadPosts.first().attr('id'); | ||||
neko259
|
r363 | var diffUrl = '/api/diff_thread/' + threadId + '/' + lastUpdateTime + '/'; | ||
neko259
|
r361 | $.getJSON(diffUrl) | ||
.success(function(data) { | ||||
neko259
|
r373 | var bottom = isPageBottom(); | ||
neko259
|
r536 | var lastUpdate = ''; | ||
neko259
|
r361 | var addedPosts = data.added; | ||
for (var i = 0; i < addedPosts.length; i++) { | ||||
var postText = addedPosts[i]; | ||||
neko259
|
r363 | var post = $(postText); | ||
neko259
|
r536 | |||
if (lastUpdate === '') { | ||||
lastUpdate = post.find('.pub_time').text(); | ||||
} | ||||
neko259
|
r363 | post.appendTo(lastPost.parent()); | ||
neko259
|
r361 | addRefLinkPreview(post[0]); | ||
lastPost = post; | ||||
neko259
|
r363 | blink(post); | ||
neko259
|
r361 | } | ||
neko259
|
r364 | var updatedPosts = data.updated; | ||
for (var i = 0; i < updatedPosts.length; i++) { | ||||
var postText = updatedPosts[i]; | ||||
var post = $(postText); | ||||
neko259
|
r536 | |||
if (lastUpdate === '') { | ||||
lastUpdate = post.find('.pub_time').text(); | ||||
} | ||||
neko259
|
r364 | var postId = post.attr('id'); | ||
var oldPost = $('div.thread').children('.post[id=' + postId + ']'); | ||||
oldPost.replaceWith(post); | ||||
addRefLinkPreview(post[0]); | ||||
blink(post); | ||||
} | ||||
neko259
|
r536 | |||
neko259
|
r373 | // TODO Process deleted posts | ||
neko259
|
r363 | |||
lastUpdateTime = data.last_update; | ||||
loading = false; | ||||
neko259
|
r373 | |||
if (bottom) { | ||||
var $target = $('html,body'); | ||||
$target.animate({scrollTop: $target.height()}, 1000); | ||||
} | ||||
neko259
|
r391 | |||
neko259
|
r536 | var hasPostChanges = (updatedPosts.length > 0) | ||
|| (addedPosts.length > 0); | ||||
if (hasPostChanges) { | ||||
updateMetadataPanel(lastUpdate); | ||||
} | ||||
neko259
|
r420 | |||
neko259
|
r427 | updateBumplimitProgress(data.added.length); | ||
neko259
|
r429 | updatePostBumpableStatus(); | ||
neko259
|
r451 | |||
if (data.added.length + data.updated.length > 0) { | ||||
neko259
|
r468 | showNewPostsTitle(data.added.length); | ||
neko259
|
r451 | } | ||
neko259
|
r363 | }) | ||
.error(function(data) { | ||||
// TODO Show error message that server is unavailable? | ||||
neko259
|
r361 | loading = false; | ||
}); | ||||
} | ||||
neko259
|
r373 | function isPageBottom() { | ||
var scroll = $(window).scrollTop() / ($(document).height() | ||||
- $(window).height()) | ||||
return scroll == 1 | ||||
} | ||||
neko259
|
r363 | function initAutoupdate() { | ||
loading = false; | ||||
lastUpdateTime = $('.metapanel').attr('data-last-update'); | ||||
setInterval(updateThread, THREAD_UPDATE_DELAY); | ||||
} | ||||
neko259
|
r391 | |||
function getReplyCount() { | ||||
neko259
|
r404 | return $('.thread').children('.post').length | ||
neko259
|
r391 | } | ||
function getImageCount() { | ||||
return $('.thread').find('img').length | ||||
} | ||||
neko259
|
r420 | |||
neko259
|
r536 | function updateMetadataPanel(lastUpdate) { | ||
var replyCountField = $('#reply-count'); | ||||
var imageCountField = $('#image-count'); | ||||
replyCountField.text(getReplyCount()); | ||||
imageCountField.text(getImageCount()); | ||||
if (lastUpdate !== '') { | ||||
var lastUpdateField = $('#last-update'); | ||||
lastUpdateField.text(lastUpdate); | ||||
blink(lastUpdateField); | ||||
} | ||||
blink(replyCountField); | ||||
blink(imageCountField); | ||||
} | ||||
neko259
|
r429 | /** | ||
* Update bumplimit progress bar | ||||
*/ | ||||
neko259
|
r420 | function updateBumplimitProgress(postDelta) { | ||
var progressBar = $('#bumplimit_progress'); | ||||
if (progressBar) { | ||||
var postsToLimitElement = $('#left_to_limit'); | ||||
var oldPostsToLimit = parseInt(postsToLimitElement.text()); | ||||
var postCount = getReplyCount(); | ||||
var bumplimit = postCount - postDelta + oldPostsToLimit; | ||||
var newPostsToLimit = bumplimit - postCount; | ||||
neko259
|
r429 | if (newPostsToLimit <= 0) { | ||
$('.bar-bg').remove(); | ||||
neko259
|
r420 | } else { | ||
postsToLimitElement.text(newPostsToLimit); | ||||
progressBar.width((100 - postCount / bumplimit * 100.0) + '%'); | ||||
} | ||||
} | ||||
neko259
|
r427 | } | ||
neko259
|
r429 | |||
/** | ||||
* If the bumplimit is reached, add dead_post class to all posts | ||||
*/ | ||||
function updatePostBumpableStatus() { | ||||
var postsToLimitElement = $('#left_to_limit'); | ||||
neko259
|
r536 | if (postsToLimitElement === null) { | ||
$('.thread').children('.post').addClass('dead_post'); | ||||
neko259
|
r429 | } | ||
} | ||||
neko259
|
r451 | |||
var documentOriginalTitle = ''; | ||||
/** | ||||
* Show 'new posts' text in the title if the document is not visible to a user | ||||
*/ | ||||
neko259
|
r468 | function showNewPostsTitle(newPostCount) { | ||
neko259
|
r451 | if (document.hidden) { | ||
neko259
|
r468 | if (documentOriginalTitle === '') { | ||
documentOriginalTitle = document.title; | ||||
} | ||||
unreadPosts = unreadPosts + newPostCount; | ||||
document.title = '[' + unreadPosts + '] ' + documentOriginalTitle; | ||||
neko259
|
r451 | |||
document.addEventListener('visibilitychange', function() { | ||||
if (documentOriginalTitle !== '') { | ||||
document.title = documentOriginalTitle; | ||||
documentOriginalTitle = ''; | ||||
neko259
|
r468 | unreadPosts = 0; | ||
neko259
|
r451 | } | ||
document.removeEventListener('visibilitychange', null); | ||||
}); | ||||
} | ||||
neko259
|
r458 | } | ||
neko259
|
r533 | /** | ||
* Clear all entered values in the form fields | ||||
*/ | ||||
function resetForm(form) { | ||||
form.find('input:text, input:password, input:file, select, textarea').val(''); | ||||
form.find('input:radio, input:checkbox') | ||||
.removeAttr('checked').removeAttr('selected'); | ||||
} | ||||
neko259
|
r458 | $(document).ready(function(){ | ||
initAutoupdate(); | ||||
neko259
|
r534 | |||
// Post form data over AJAX | ||||
var threadId = $('div.thread').children('.post').first().attr('id');; | ||||
var form = $('#form'); | ||||
var options = { | ||||
success: updateOnPost, | ||||
url: '/api/add_post/' + threadId + '/', | ||||
}; | ||||
form.ajaxForm(options); | ||||
function updateOnPost(response, statusText, xhr, $form) { | ||||
var json = $.parseJSON(response); | ||||
var status = json.status; | ||||
form.children('.form-errors').remove(); | ||||
if (status === 'ok') { | ||||
resetForm(form); | ||||
updateThread(); | ||||
} else { | ||||
var errors = json.errors; | ||||
for (var i = 0; i < errors.length; i++) { | ||||
neko259
|
r536 | var fieldErrors = errors[i]; | ||
neko259
|
r534 | |||
neko259
|
r536 | var error = fieldErrors.errors; | ||
neko259
|
r534 | |||
var errorList = $('<div class="form-errors">' + error | ||||
+ '<div>'); | ||||
errorList.appendTo(form); | ||||
} | ||||
} | ||||
} | ||||
neko259
|
r458 | }); | ||