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