##// END OF EJS Templates
Don't update metadata panel on thread opening
neko259 -
r1074:7609c920 default
parent child Browse files
Show More
@@ -1,331 +1,336
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-2014 neko259
6 Copyright (C) 2013-2014 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 wsUser = '';
26 var wsUser = '';
27
27
28 var unreadPosts = 0;
28 var unreadPosts = 0;
29 var documentOriginalTitle = '';
29 var documentOriginalTitle = '';
30
30
31 // Thread ID does not change, can be stored one time
31 // Thread ID does not change, can be stored one time
32 var threadId = $('div.thread').children('.post').first().attr('id');
32 var threadId = $('div.thread').children('.post').first().attr('id');
33
33
34 /**
34 /**
35 * Connect to websocket server and subscribe to thread updates. On any update we
35 * Connect to websocket server and subscribe to thread updates. On any update we
36 * request a thread diff.
36 * request a thread diff.
37 *
37 *
38 * @returns {boolean} true if connected, false otherwise
38 * @returns {boolean} true if connected, false otherwise
39 */
39 */
40 function connectWebsocket() {
40 function connectWebsocket() {
41 var metapanel = $('.metapanel')[0];
41 var metapanel = $('.metapanel')[0];
42
42
43 var wsHost = metapanel.getAttribute('data-ws-host');
43 var wsHost = metapanel.getAttribute('data-ws-host');
44 var wsPort = metapanel.getAttribute('data-ws-port');
44 var wsPort = metapanel.getAttribute('data-ws-port');
45
45
46 if (wsHost.length > 0 && wsPort.length > 0)
46 if (wsHost.length > 0 && wsPort.length > 0)
47 var centrifuge = new Centrifuge({
47 var centrifuge = new Centrifuge({
48 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
48 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
49 "project": metapanel.getAttribute('data-ws-project'),
49 "project": metapanel.getAttribute('data-ws-project'),
50 "user": wsUser,
50 "user": wsUser,
51 "timestamp": metapanel.getAttribute('data-last-update'),
51 "timestamp": metapanel.getAttribute('data-last-update'),
52 "token": metapanel.getAttribute('data-ws-token'),
52 "token": metapanel.getAttribute('data-ws-token'),
53 "debug": false
53 "debug": false
54 });
54 });
55
55
56 centrifuge.on('error', function(error_message) {
56 centrifuge.on('error', function(error_message) {
57 console.log("Error connecting to websocket server.");
57 console.log("Error connecting to websocket server.");
58 return false;
58 return false;
59 });
59 });
60
60
61 centrifuge.on('connect', function() {
61 centrifuge.on('connect', function() {
62 var channelName = 'thread:' + threadId;
62 var channelName = 'thread:' + threadId;
63 centrifuge.subscribe(channelName, function(message) {
63 centrifuge.subscribe(channelName, function(message) {
64 getThreadDiff();
64 getThreadDiff();
65 });
65 });
66
66
67 // For the case we closed the browser and missed some updates
67 // For the case we closed the browser and missed some updates
68 getThreadDiff();
68 getThreadDiff();
69 $('#autoupdate').hide();
69 $('#autoupdate').hide();
70 });
70 });
71
71
72 centrifuge.connect();
72 centrifuge.connect();
73
73
74 return true;
74 return true;
75 }
75 }
76
76
77 /**
77 /**
78 * Get diff of the posts from the current thread timestamp.
78 * Get diff of the posts from the current thread timestamp.
79 * This is required if the browser was closed and some post updates were
79 * This is required if the browser was closed and some post updates were
80 * missed.
80 * missed.
81 */
81 */
82 function getThreadDiff() {
82 function getThreadDiff() {
83 var lastUpdateTime = $('.metapanel').attr('data-last-update');
83 var lastUpdateTime = $('.metapanel').attr('data-last-update');
84
84
85 var diffUrl = '/api/diff_thread/' + threadId + '/' + lastUpdateTime + '/';
85 var diffUrl = '/api/diff_thread/' + threadId + '/' + lastUpdateTime + '/';
86
86
87 $.getJSON(diffUrl)
87 $.getJSON(diffUrl)
88 .success(function(data) {
88 .success(function(data) {
89 var addedPosts = data.added;
89 var addedPosts = data.added;
90 var hasMetaUpdates = false;
90
91
91 for (var i = 0; i < addedPosts.length; i++) {
92 for (var i = 0; i < addedPosts.length; i++) {
92 var postText = addedPosts[i];
93 var postText = addedPosts[i];
93 var post = $(postText);
94 var post = $(postText);
94
95
95 updatePost(post)
96 updatePost(post);
97 hasMetaUpdates = true;
96 }
98 }
97
99
98 var updatedPosts = data.updated;
100 var updatedPosts = data.updated;
99
101
100 for (var i = 0; i < updatedPosts.length; i++) {
102 for (var i = 0; i < updatedPosts.length; i++) {
101 var postText = updatedPosts[i];
103 var postText = updatedPosts[i];
102 var post = $(postText);
104 var post = $(postText);
103
105
104 updatePost(post)
106 updatePost(post);
107 hasMetaUpdates = true;
105 }
108 }
106
109
107 updateMetadataPanel();
110 if (hasMetaUpdates) {
111 updateMetadataPanel();
112 }
108
113
109 // TODO Process removed posts if any
114 // TODO Process removed posts if any
110 $('.metapanel').attr('data-last-update', data.last_update);
115 $('.metapanel').attr('data-last-update', data.last_update);
111 })
116 })
112 }
117 }
113
118
114 /**
119 /**
115 * Add or update the post on html page.
120 * Add or update the post on html page.
116 */
121 */
117 function updatePost(postHtml) {
122 function updatePost(postHtml) {
118 // This needs to be set on start because the page is scrolled after posts
123 // This needs to be set on start because the page is scrolled after posts
119 // are added or updated
124 // are added or updated
120 var bottom = isPageBottom();
125 var bottom = isPageBottom();
121
126
122 var post = $(postHtml);
127 var post = $(postHtml);
123
128
124 var threadBlock = $('div.thread');
129 var threadBlock = $('div.thread');
125
130
126 var postId = post.attr('id');
131 var postId = post.attr('id');
127
132
128 // If the post already exists, replace it. Otherwise add as a new one.
133 // If the post already exists, replace it. Otherwise add as a new one.
129 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
134 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
130
135
131 if (existingPosts.size() > 0) {
136 if (existingPosts.size() > 0) {
132 existingPosts.replaceWith(post);
137 existingPosts.replaceWith(post);
133 } else {
138 } else {
134 var threadPosts = threadBlock.children('.post');
139 var threadPosts = threadBlock.children('.post');
135 var lastPost = threadPosts.last();
140 var lastPost = threadPosts.last();
136
141
137 post.appendTo(lastPost.parent());
142 post.appendTo(lastPost.parent());
138
143
139 updateBumplimitProgress(1);
144 updateBumplimitProgress(1);
140 showNewPostsTitle(1);
145 showNewPostsTitle(1);
141
146
142 if (bottom) {
147 if (bottom) {
143 scrollToBottom();
148 scrollToBottom();
144 }
149 }
145 }
150 }
146
151
147 processNewPost(post);
152 processNewPost(post);
148 }
153 }
149
154
150 /**
155 /**
151 * Initiate a blinking animation on a node to show it was updated.
156 * Initiate a blinking animation on a node to show it was updated.
152 */
157 */
153 function blink(node) {
158 function blink(node) {
154 var blinkCount = 2;
159 var blinkCount = 2;
155
160
156 var nodeToAnimate = node;
161 var nodeToAnimate = node;
157 for (var i = 0; i < blinkCount; i++) {
162 for (var i = 0; i < blinkCount; i++) {
158 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
163 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
159 }
164 }
160 }
165 }
161
166
162 function isPageBottom() {
167 function isPageBottom() {
163 var scroll = $(window).scrollTop() / ($(document).height()
168 var scroll = $(window).scrollTop() / ($(document).height()
164 - $(window).height());
169 - $(window).height());
165
170
166 return scroll == 1
171 return scroll == 1
167 }
172 }
168
173
169 function initAutoupdate() {
174 function initAutoupdate() {
170 return connectWebsocket();
175 return connectWebsocket();
171 }
176 }
172
177
173 function getReplyCount() {
178 function getReplyCount() {
174 return $('.thread').children('.post').length
179 return $('.thread').children('.post').length
175 }
180 }
176
181
177 function getImageCount() {
182 function getImageCount() {
178 return $('.thread').find('img').length
183 return $('.thread').find('img').length
179 }
184 }
180
185
181 /**
186 /**
182 * Update post count, images count and last update time in the metadata
187 * Update post count, images count and last update time in the metadata
183 * panel.
188 * panel.
184 */
189 */
185 function updateMetadataPanel() {
190 function updateMetadataPanel() {
186 var replyCountField = $('#reply-count');
191 var replyCountField = $('#reply-count');
187 var imageCountField = $('#image-count');
192 var imageCountField = $('#image-count');
188
193
189 replyCountField.text(getReplyCount());
194 replyCountField.text(getReplyCount());
190 imageCountField.text(getImageCount());
195 imageCountField.text(getImageCount());
191
196
192 var lastUpdate = $('.post:last').children('.post-info').first()
197 var lastUpdate = $('.post:last').children('.post-info').first()
193 .children('.pub_time').first().html();
198 .children('.pub_time').first().html();
194 if (lastUpdate !== '') {
199 if (lastUpdate !== '') {
195 var lastUpdateField = $('#last-update');
200 var lastUpdateField = $('#last-update');
196 lastUpdateField.html(lastUpdate);
201 lastUpdateField.html(lastUpdate);
197 blink(lastUpdateField);
202 blink(lastUpdateField);
198 }
203 }
199
204
200 blink(replyCountField);
205 blink(replyCountField);
201 blink(imageCountField);
206 blink(imageCountField);
202 }
207 }
203
208
204 /**
209 /**
205 * Update bumplimit progress bar
210 * Update bumplimit progress bar
206 */
211 */
207 function updateBumplimitProgress(postDelta) {
212 function updateBumplimitProgress(postDelta) {
208 var progressBar = $('#bumplimit_progress');
213 var progressBar = $('#bumplimit_progress');
209 if (progressBar) {
214 if (progressBar) {
210 var postsToLimitElement = $('#left_to_limit');
215 var postsToLimitElement = $('#left_to_limit');
211
216
212 var oldPostsToLimit = parseInt(postsToLimitElement.text());
217 var oldPostsToLimit = parseInt(postsToLimitElement.text());
213 var postCount = getReplyCount();
218 var postCount = getReplyCount();
214 var bumplimit = postCount - postDelta + oldPostsToLimit;
219 var bumplimit = postCount - postDelta + oldPostsToLimit;
215
220
216 var newPostsToLimit = bumplimit - postCount;
221 var newPostsToLimit = bumplimit - postCount;
217 if (newPostsToLimit <= 0) {
222 if (newPostsToLimit <= 0) {
218 $('.bar-bg').remove();
223 $('.bar-bg').remove();
219 } else {
224 } else {
220 postsToLimitElement.text(newPostsToLimit);
225 postsToLimitElement.text(newPostsToLimit);
221 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
226 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
222 }
227 }
223 }
228 }
224 }
229 }
225
230
226 /**
231 /**
227 * Show 'new posts' text in the title if the document is not visible to a user
232 * Show 'new posts' text in the title if the document is not visible to a user
228 */
233 */
229 function showNewPostsTitle(newPostCount) {
234 function showNewPostsTitle(newPostCount) {
230 if (document.hidden) {
235 if (document.hidden) {
231 if (documentOriginalTitle === '') {
236 if (documentOriginalTitle === '') {
232 documentOriginalTitle = document.title;
237 documentOriginalTitle = document.title;
233 }
238 }
234 unreadPosts = unreadPosts + newPostCount;
239 unreadPosts = unreadPosts + newPostCount;
235 document.title = '[' + unreadPosts + '] ' + documentOriginalTitle;
240 document.title = '[' + unreadPosts + '] ' + documentOriginalTitle;
236
241
237 document.addEventListener('visibilitychange', function() {
242 document.addEventListener('visibilitychange', function() {
238 if (documentOriginalTitle !== '') {
243 if (documentOriginalTitle !== '') {
239 document.title = documentOriginalTitle;
244 document.title = documentOriginalTitle;
240 documentOriginalTitle = '';
245 documentOriginalTitle = '';
241 unreadPosts = 0;
246 unreadPosts = 0;
242 }
247 }
243
248
244 document.removeEventListener('visibilitychange', null);
249 document.removeEventListener('visibilitychange', null);
245 });
250 });
246 }
251 }
247 }
252 }
248
253
249 /**
254 /**
250 * Clear all entered values in the form fields
255 * Clear all entered values in the form fields
251 */
256 */
252 function resetForm(form) {
257 function resetForm(form) {
253 form.find('input:text, input:password, input:file, select, textarea').val('');
258 form.find('input:text, input:password, input:file, select, textarea').val('');
254 form.find('input:radio, input:checkbox')
259 form.find('input:radio, input:checkbox')
255 .removeAttr('checked').removeAttr('selected');
260 .removeAttr('checked').removeAttr('selected');
256 $('.file_wrap').find('.file-thumb').remove();
261 $('.file_wrap').find('.file-thumb').remove();
257 }
262 }
258
263
259 /**
264 /**
260 * When the form is posted, this method will be run as a callback
265 * When the form is posted, this method will be run as a callback
261 */
266 */
262 function updateOnPost(response, statusText, xhr, form) {
267 function updateOnPost(response, statusText, xhr, form) {
263 var json = $.parseJSON(response);
268 var json = $.parseJSON(response);
264 var status = json.status;
269 var status = json.status;
265
270
266 showAsErrors(form, '');
271 showAsErrors(form, '');
267
272
268 if (status === 'ok') {
273 if (status === 'ok') {
269 resetFormPosition();
274 resetFormPosition();
270 resetForm(form);
275 resetForm(form);
271 getThreadDiff();
276 getThreadDiff();
272 scrollToBottom();
277 scrollToBottom();
273 } else {
278 } else {
274 var errors = json.errors;
279 var errors = json.errors;
275 for (var i = 0; i < errors.length; i++) {
280 for (var i = 0; i < errors.length; i++) {
276 var fieldErrors = errors[i];
281 var fieldErrors = errors[i];
277
282
278 var error = fieldErrors.errors;
283 var error = fieldErrors.errors;
279
284
280 showAsErrors(form, error);
285 showAsErrors(form, error);
281 }
286 }
282 }
287 }
283 }
288 }
284
289
285 /**
290 /**
286 * Show text in the errors row of the form.
291 * Show text in the errors row of the form.
287 * @param form
292 * @param form
288 * @param text
293 * @param text
289 */
294 */
290 function showAsErrors(form, text) {
295 function showAsErrors(form, text) {
291 form.children('.form-errors').remove();
296 form.children('.form-errors').remove();
292
297
293 if (text.length > 0) {
298 if (text.length > 0) {
294 var errorList = $('<div class="form-errors">' + text + '<div>');
299 var errorList = $('<div class="form-errors">' + text + '<div>');
295 errorList.appendTo(form);
300 errorList.appendTo(form);
296 }
301 }
297 }
302 }
298
303
299 /**
304 /**
300 * Run js methods that are usually run on the document, on the new post
305 * Run js methods that are usually run on the document, on the new post
301 */
306 */
302 function processNewPost(post) {
307 function processNewPost(post) {
303 addRefLinkPreview(post[0]);
308 addRefLinkPreview(post[0]);
304 highlightCode(post);
309 highlightCode(post);
305 blink(post);
310 blink(post);
306 }
311 }
307
312
308 $(document).ready(function(){
313 $(document).ready(function(){
309 if (initAutoupdate()) {
314 if (initAutoupdate()) {
310 // Post form data over AJAX
315 // Post form data over AJAX
311 var threadId = $('div.thread').children('.post').first().attr('id');
316 var threadId = $('div.thread').children('.post').first().attr('id');
312
317
313 var form = $('#form');
318 var form = $('#form');
314
319
315 if (form.length > 0) {
320 if (form.length > 0) {
316 var options = {
321 var options = {
317 beforeSubmit: function(arr, $form, options) {
322 beforeSubmit: function(arr, $form, options) {
318 showAsErrors($('form'), gettext('Sending message...'));
323 showAsErrors($('form'), gettext('Sending message...'));
319 },
324 },
320 success: updateOnPost,
325 success: updateOnPost,
321 url: '/api/add_post/' + threadId + '/'
326 url: '/api/add_post/' + threadId + '/'
322 };
327 };
323
328
324 form.ajaxForm(options);
329 form.ajaxForm(options);
325
330
326 resetForm(form);
331 resetForm(form);
327 }
332 }
328 }
333 }
329
334
330 $('#autoupdate').click(getThreadDiff);
335 $('#autoupdate').click(getThreadDiff);
331 });
336 });
General Comments 0
You need to be logged in to leave comments. Login now