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