##// END OF EJS Templates
Fixed thread update
neko259 -
r710:3be7d3c8 default
parent child Browse files
Show More
@@ -1,272 +1,272 b''
1 /*
1 /*
2 @licstart The following is the entire license notice for the
2 @licstart The following is the entire license notice for the
3 JavaScript code in this page.
3 JavaScript code in this page.
4
4
5
5
6 Copyright (C) 2013 neko259
6 Copyright (C) 2013 neko259
7
7
8 The JavaScript code in this page is free software: you can
8 The JavaScript code in this page is free software: you can
9 redistribute it and/or modify it under the terms of the GNU
9 redistribute it and/or modify it under the terms of the GNU
10 General Public License (GNU GPL) as published by the Free Software
10 General Public License (GNU GPL) as published by the Free Software
11 Foundation, either version 3 of the License, or (at your option)
11 Foundation, either version 3 of the License, or (at your option)
12 any later version. The code is distributed WITHOUT ANY WARRANTY;
12 any later version. The code is distributed WITHOUT ANY WARRANTY;
13 without even the implied warranty of MERCHANTABILITY or FITNESS
13 without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
14 FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
15
15
16 As additional permission under GNU GPL version 3 section 7, you
16 As additional permission under GNU GPL version 3 section 7, you
17 may distribute non-source (e.g., minimized or compacted) forms of
17 may distribute non-source (e.g., minimized or compacted) forms of
18 that code without the copy of the GNU GPL normally required by
18 that code without the copy of the GNU GPL normally required by
19 section 4, provided you include this license notice and a URL
19 section 4, provided you include this license notice and a URL
20 through which recipients can access the Corresponding Source.
20 through which recipients can access the Corresponding Source.
21
21
22 @licend The above is the entire license notice
22 @licend The above is the entire license notice
23 for the JavaScript code in this page.
23 for the JavaScript code in this page.
24 */
24 */
25
25
26 var THREAD_UPDATE_DELAY = 10000;
26 var THREAD_UPDATE_DELAY = 10000;
27
27
28 var loading = false;
28 var loading = false;
29 var lastUpdateTime = null;
29 var lastUpdateTime = null;
30 var unreadPosts = 0
30 var unreadPosts = 0
31
31
32 function blink(node) {
32 function blink(node) {
33 var blinkCount = 2;
33 var blinkCount = 2;
34
34
35 var nodeToAnimate = node;
35 var nodeToAnimate = node;
36 for (var i = 0; i < blinkCount; i++) {
36 for (var i = 0; i < blinkCount; i++) {
37 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
37 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
38 }
38 }
39 }
39 }
40
40
41 function updateThread() {
41 function updateThread() {
42 if (loading) {
42 if (loading) {
43 return;
43 return;
44 }
44 }
45
45
46 loading = true;
46 loading = true;
47
47
48 var threadPosts = $('div.thread').children('.post');
48 var threadPosts = $('div.thread').children('.post');
49
49
50 var lastPost = threadPosts.last();
50 var lastPost = threadPosts.last();
51 var threadId = threadPosts.first().attr('id');
51 var threadId = threadPosts.first().attr('id');
52
52
53 var diffUrl = '/api/diff_thread/' + threadId + '/' + lastUpdateTime + '/';
53 var diffUrl = '/api/diff_thread/' + threadId + '/' + lastUpdateTime + '/';
54 $.getJSON(diffUrl)
54 $.getJSON(diffUrl)
55 .success(function(data) {
55 .success(function(data) {
56 var bottom = isPageBottom();
56 var bottom = isPageBottom();
57
57
58 var lastUpdate = '';
58 var lastUpdate = '';
59
59
60 var addedPosts = data.added;
60 var addedPosts = data.added;
61 for (var i = 0; i < addedPosts.length; i++) {
61 for (var i = 0; i < addedPosts.length; i++) {
62 var postText = addedPosts[i];
62 var postText = addedPosts[i];
63
63
64 var post = $(postText);
64 var post = $(postText);
65
65
66 if (lastUpdate === '') {
66 if (lastUpdate === '') {
67 lastUpdate = post.find('.pub_time').text();
67 lastUpdate = post.find('.pub_time').text();
68 }
68 }
69
69
70 post.appendTo(lastPost.parent());
70 post.appendTo(lastPost.parent());
71 processNewPost(post[0]);
71 processNewPost(post);
72
72
73 lastPost = post;
73 lastPost = post;
74 blink(post);
74 blink(post);
75 }
75 }
76
76
77 var updatedPosts = data.updated;
77 var updatedPosts = data.updated;
78 for (var i = 0; i < updatedPosts.length; i++) {
78 for (var i = 0; i < updatedPosts.length; i++) {
79 var postText = updatedPosts[i];
79 var postText = updatedPosts[i];
80
80
81 var post = $(postText);
81 var post = $(postText);
82
82
83 if (lastUpdate === '') {
83 if (lastUpdate === '') {
84 lastUpdate = post.find('.pub_time').text();
84 lastUpdate = post.find('.pub_time').text();
85 }
85 }
86
86
87 var postId = post.attr('id');
87 var postId = post.attr('id');
88
88
89 var oldPost = $('div.thread').children('.post[id=' + postId + ']');
89 var oldPost = $('div.thread').children('.post[id=' + postId + ']');
90
90
91 oldPost.replaceWith(post);
91 oldPost.replaceWith(post);
92 processNewPost(post[0]);
92 processNewPost(post);
93
93
94 blink(post);
94 blink(post);
95 }
95 }
96
96
97 // TODO Process deleted posts
97 // TODO Process deleted posts
98
98
99 lastUpdateTime = data.last_update;
99 lastUpdateTime = data.last_update;
100 loading = false;
100 loading = false;
101
101
102 if (bottom) {
102 if (bottom) {
103 scrollToBottom();
103 scrollToBottom();
104 }
104 }
105
105
106 var hasPostChanges = (updatedPosts.length > 0)
106 var hasPostChanges = (updatedPosts.length > 0)
107 || (addedPosts.length > 0);
107 || (addedPosts.length > 0);
108 if (hasPostChanges) {
108 if (hasPostChanges) {
109 updateMetadataPanel(lastUpdate);
109 updateMetadataPanel(lastUpdate);
110 }
110 }
111
111
112 updateBumplimitProgress(data.added.length);
112 updateBumplimitProgress(data.added.length);
113
113
114 if (data.added.length + data.updated.length > 0) {
114 if (data.added.length + data.updated.length > 0) {
115 showNewPostsTitle(data.added.length);
115 showNewPostsTitle(data.added.length);
116 }
116 }
117 })
117 })
118 .error(function(data) {
118 .error(function(data) {
119 // TODO Show error message that server is unavailable?
119 // TODO Show error message that server is unavailable?
120
120
121 loading = false;
121 loading = false;
122 });
122 });
123 }
123 }
124
124
125 function isPageBottom() {
125 function isPageBottom() {
126 var scroll = $(window).scrollTop() / ($(document).height()
126 var scroll = $(window).scrollTop() / ($(document).height()
127 - $(window).height())
127 - $(window).height())
128
128
129 return scroll == 1
129 return scroll == 1
130 }
130 }
131
131
132 function initAutoupdate() {
132 function initAutoupdate() {
133 loading = false;
133 loading = false;
134
134
135 lastUpdateTime = $('.metapanel').attr('data-last-update');
135 lastUpdateTime = $('.metapanel').attr('data-last-update');
136
136
137 setInterval(updateThread, THREAD_UPDATE_DELAY);
137 setInterval(updateThread, THREAD_UPDATE_DELAY);
138 }
138 }
139
139
140 function getReplyCount() {
140 function getReplyCount() {
141 return $('.thread').children('.post').length
141 return $('.thread').children('.post').length
142 }
142 }
143
143
144 function getImageCount() {
144 function getImageCount() {
145 return $('.thread').find('img').length
145 return $('.thread').find('img').length
146 }
146 }
147
147
148 function updateMetadataPanel(lastUpdate) {
148 function updateMetadataPanel(lastUpdate) {
149 var replyCountField = $('#reply-count');
149 var replyCountField = $('#reply-count');
150 var imageCountField = $('#image-count');
150 var imageCountField = $('#image-count');
151
151
152 replyCountField.text(getReplyCount());
152 replyCountField.text(getReplyCount());
153 imageCountField.text(getImageCount());
153 imageCountField.text(getImageCount());
154
154
155 if (lastUpdate !== '') {
155 if (lastUpdate !== '') {
156 var lastUpdateField = $('#last-update');
156 var lastUpdateField = $('#last-update');
157 lastUpdateField.text(lastUpdate);
157 lastUpdateField.text(lastUpdate);
158 blink(lastUpdateField);
158 blink(lastUpdateField);
159 }
159 }
160
160
161 blink(replyCountField);
161 blink(replyCountField);
162 blink(imageCountField);
162 blink(imageCountField);
163 }
163 }
164
164
165 /**
165 /**
166 * Update bumplimit progress bar
166 * Update bumplimit progress bar
167 */
167 */
168 function updateBumplimitProgress(postDelta) {
168 function updateBumplimitProgress(postDelta) {
169 var progressBar = $('#bumplimit_progress');
169 var progressBar = $('#bumplimit_progress');
170 if (progressBar) {
170 if (progressBar) {
171 var postsToLimitElement = $('#left_to_limit');
171 var postsToLimitElement = $('#left_to_limit');
172
172
173 var oldPostsToLimit = parseInt(postsToLimitElement.text());
173 var oldPostsToLimit = parseInt(postsToLimitElement.text());
174 var postCount = getReplyCount();
174 var postCount = getReplyCount();
175 var bumplimit = postCount - postDelta + oldPostsToLimit;
175 var bumplimit = postCount - postDelta + oldPostsToLimit;
176
176
177 var newPostsToLimit = bumplimit - postCount;
177 var newPostsToLimit = bumplimit - postCount;
178 if (newPostsToLimit <= 0) {
178 if (newPostsToLimit <= 0) {
179 $('.bar-bg').remove();
179 $('.bar-bg').remove();
180 $('.thread').children('.post').addClass('dead_post');
180 $('.thread').children('.post').addClass('dead_post');
181 } else {
181 } else {
182 postsToLimitElement.text(newPostsToLimit);
182 postsToLimitElement.text(newPostsToLimit);
183 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
183 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
184 }
184 }
185 }
185 }
186 }
186 }
187
187
188 var documentOriginalTitle = '';
188 var documentOriginalTitle = '';
189 /**
189 /**
190 * Show 'new posts' text in the title if the document is not visible to a user
190 * Show 'new posts' text in the title if the document is not visible to a user
191 */
191 */
192 function showNewPostsTitle(newPostCount) {
192 function showNewPostsTitle(newPostCount) {
193 if (document.hidden) {
193 if (document.hidden) {
194 if (documentOriginalTitle === '') {
194 if (documentOriginalTitle === '') {
195 documentOriginalTitle = document.title;
195 documentOriginalTitle = document.title;
196 }
196 }
197 unreadPosts = unreadPosts + newPostCount;
197 unreadPosts = unreadPosts + newPostCount;
198 document.title = '[' + unreadPosts + '] ' + documentOriginalTitle;
198 document.title = '[' + unreadPosts + '] ' + documentOriginalTitle;
199
199
200 document.addEventListener('visibilitychange', function() {
200 document.addEventListener('visibilitychange', function() {
201 if (documentOriginalTitle !== '') {
201 if (documentOriginalTitle !== '') {
202 document.title = documentOriginalTitle;
202 document.title = documentOriginalTitle;
203 documentOriginalTitle = '';
203 documentOriginalTitle = '';
204 unreadPosts = 0;
204 unreadPosts = 0;
205 }
205 }
206
206
207 document.removeEventListener('visibilitychange', null);
207 document.removeEventListener('visibilitychange', null);
208 });
208 });
209 }
209 }
210 }
210 }
211
211
212 /**
212 /**
213 * Clear all entered values in the form fields
213 * Clear all entered values in the form fields
214 */
214 */
215 function resetForm(form) {
215 function resetForm(form) {
216 form.find('input:text, input:password, input:file, select, textarea').val('');
216 form.find('input:text, input:password, input:file, select, textarea').val('');
217 form.find('input:radio, input:checkbox')
217 form.find('input:radio, input:checkbox')
218 .removeAttr('checked').removeAttr('selected');
218 .removeAttr('checked').removeAttr('selected');
219 $('.file_wrap').find('.file-thumb').remove();
219 $('.file_wrap').find('.file-thumb').remove();
220 }
220 }
221
221
222 /**
222 /**
223 * When the form is posted, this method will be run as a callback
223 * When the form is posted, this method will be run as a callback
224 */
224 */
225 function updateOnPost(response, statusText, xhr, form) {
225 function updateOnPost(response, statusText, xhr, form) {
226 var json = $.parseJSON(response);
226 var json = $.parseJSON(response);
227 var status = json.status;
227 var status = json.status;
228
228
229 form.children('.form-errors').remove();
229 form.children('.form-errors').remove();
230
230
231 if (status === 'ok') {
231 if (status === 'ok') {
232 resetForm(form);
232 resetForm(form);
233 updateThread();
233 updateThread();
234 } else {
234 } else {
235 var errors = json.errors;
235 var errors = json.errors;
236 for (var i = 0; i < errors.length; i++) {
236 for (var i = 0; i < errors.length; i++) {
237 var fieldErrors = errors[i];
237 var fieldErrors = errors[i];
238
238
239 var error = fieldErrors.errors;
239 var error = fieldErrors.errors;
240
240
241 var errorList = $('<div class="form-errors">' + error
241 var errorList = $('<div class="form-errors">' + error
242 + '<div>');
242 + '<div>');
243 errorList.appendTo(form);
243 errorList.appendTo(form);
244 }
244 }
245 }
245 }
246 }
246 }
247
247
248 /**
248 /**
249 * Run js methods that are usually run on the document, on the new post
249 * Run js methods that are usually run on the document, on the new post
250 */
250 */
251 function processNewPost(post) {
251 function processNewPost(post) {
252 addRefLinkPreview(post);
252 addRefLinkPreview(post[0]);
253 highlightCode(post);
253 highlightCode(post);
254 }
254 }
255
255
256 $(document).ready(function(){
256 $(document).ready(function(){
257 initAutoupdate();
257 initAutoupdate();
258
258
259 // Post form data over AJAX
259 // Post form data over AJAX
260 var threadId = $('div.thread').children('.post').first().attr('id');;
260 var threadId = $('div.thread').children('.post').first().attr('id');;
261
261
262 var form = $('#form');
262 var form = $('#form');
263
263
264 var options = {
264 var options = {
265 success: updateOnPost,
265 success: updateOnPost,
266 url: '/api/add_post/' + threadId + '/'
266 url: '/api/add_post/' + threadId + '/'
267 };
267 };
268
268
269 form.ajaxForm(options);
269 form.ajaxForm(options);
270
270
271 resetForm(form);
271 resetForm(form);
272 });
272 });
General Comments 0
You need to be logged in to leave comments. Login now