##// END OF EJS Templates
If websocket connection failed, fallback to JS update
neko259 -
r1163:2e21d394 default
parent child Browse files
Show More
@@ -1,383 +1,386 b''
1 1 /*
2 2 @licstart The following is the entire license notice for the
3 3 JavaScript code in this page.
4 4
5 5
6 6 Copyright (C) 2013-2014 neko259
7 7
8 8 The JavaScript code in this page is free software: you can
9 9 redistribute it and/or modify it under the terms of the GNU
10 10 General Public License (GNU GPL) as published by the Free Software
11 11 Foundation, either version 3 of the License, or (at your option)
12 12 any later version. The code is distributed WITHOUT ANY WARRANTY;
13 13 without even the implied warranty of MERCHANTABILITY or FITNESS
14 14 FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
15 15
16 16 As additional permission under GNU GPL version 3 section 7, you
17 17 may distribute non-source (e.g., minimized or compacted) forms of
18 18 that code without the copy of the GNU GPL normally required by
19 19 section 4, provided you include this license notice and a URL
20 20 through which recipients can access the Corresponding Source.
21 21
22 22 @licend The above is the entire license notice
23 23 for the JavaScript code in this page.
24 24 */
25 25
26 26 var CLASS_POST = '.post'
27 27
28 28 var POST_ADDED = 0;
29 29 var POST_UPDATED = 1;
30 30
31 31 var JS_AUTOUPDATE_PERIOD = 20000;
32 32
33 33 var wsUser = '';
34 34
35 35 var unreadPosts = 0;
36 36 var documentOriginalTitle = '';
37 37
38 38 // Thread ID does not change, can be stored one time
39 39 var threadId = $('div.thread').children(CLASS_POST).first().attr('id');
40 40
41 41 /**
42 42 * Connect to websocket server and subscribe to thread updates. On any update we
43 43 * request a thread diff.
44 44 *
45 45 * @returns {boolean} true if connected, false otherwise
46 46 */
47 47 function connectWebsocket() {
48 48 var metapanel = $('.metapanel')[0];
49 49
50 50 var wsHost = metapanel.getAttribute('data-ws-host');
51 51 var wsPort = metapanel.getAttribute('data-ws-port');
52 52
53 53 if (wsHost.length > 0 && wsPort.length > 0) {
54 54 var centrifuge = new Centrifuge({
55 55 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
56 56 "project": metapanel.getAttribute('data-ws-project'),
57 57 "user": wsUser,
58 58 "timestamp": metapanel.getAttribute('data-ws-token-time'),
59 59 "token": metapanel.getAttribute('data-ws-token'),
60 60 "debug": false
61 61 });
62 62
63 63 centrifuge.on('error', function(error_message) {
64 64 console.log("Error connecting to websocket server.");
65 65 console.log(error_message);
66 return false;
66 console.log("Using javascript update instead.");
67
68 // If websockets don't work, enable JS update instead
69 enableJsUpdate()
67 70 });
68 71
69 72 centrifuge.on('connect', function() {
70 73 var channelName = 'thread:' + threadId;
71 74 centrifuge.subscribe(channelName, function(message) {
72 75 getThreadDiff();
73 76 });
74 77
75 78 // For the case we closed the browser and missed some updates
76 79 getThreadDiff();
77 80 $('#autoupdate').hide();
78 81 });
79 82
80 83 centrifuge.connect();
81 84
82 85 return true;
83 86 } else {
84 87 return false;
85 88 }
86 89 }
87 90
88 91 /**
89 92 * Get diff of the posts from the current thread timestamp.
90 93 * This is required if the browser was closed and some post updates were
91 94 * missed.
92 95 */
93 96 function getThreadDiff() {
94 97 var lastUpdateTime = $('.metapanel').attr('data-last-update');
95 98 var lastPostId = $('.post').last().attr('id');
96 99
97 100 var uids = '';
98 101 var posts = $('.post');
99 102 for (var i = 0; i < posts.length; i++) {
100 103 uids += posts[i].getAttribute('data-uid') + ' ';
101 104 }
102 105
103 106 var data = {
104 107 uids: uids
105 108 }
106 109
107 110 var diffUrl = '/api/diff_thread?thread=' + threadId;
108 111
109 112 $.post(diffUrl,
110 113 data,
111 114 function(data) {
112 115 var updatedPosts = data.updated;
113 116 var addedPostCount = 0;
114 117
115 118 for (var i = 0; i < updatedPosts.length; i++) {
116 119 var postText = updatedPosts[i];
117 120 var post = $(postText);
118 121
119 122 if (updatePost(post) == POST_ADDED) {
120 123 addedPostCount++;
121 124 }
122 125 }
123 126
124 127 var hasMetaUpdates = updatedPosts.length > 0;
125 128 if (hasMetaUpdates) {
126 129 updateMetadataPanel();
127 130 }
128 131
129 132 if (addedPostCount > 0) {
130 133 updateBumplimitProgress(addedPostCount);
131 134 }
132 135
133 136 if (updatedPosts.length > 0) {
134 137 showNewPostsTitle(addedPostCount);
135 138 }
136 139
137 140 // TODO Process removed posts if any
138 141 $('.metapanel').attr('data-last-update', data.last_update);
139 142 },
140 143 'json'
141 144 )
142 145 }
143 146
144 147 /**
145 148 * Add or update the post on html page.
146 149 */
147 150 function updatePost(postHtml) {
148 151 // This needs to be set on start because the page is scrolled after posts
149 152 // are added or updated
150 153 var bottom = isPageBottom();
151 154
152 155 var post = $(postHtml);
153 156
154 157 var threadBlock = $('div.thread');
155 158
156 159 var postId = post.attr('id');
157 160
158 161 // If the post already exists, replace it. Otherwise add as a new one.
159 162 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
160 163
161 164 var type;
162 165
163 166 if (existingPosts.size() > 0) {
164 167 existingPosts.replaceWith(post);
165 168
166 169 type = POST_UPDATED;
167 170 } else {
168 171 post.appendTo(threadBlock);
169 172
170 173 if (bottom) {
171 174 scrollToBottom();
172 175 }
173 176
174 177 type = POST_ADDED;
175 178 }
176 179
177 180 processNewPost(post);
178 181
179 182 return type;
180 183 }
181 184
182 185 /**
183 186 * Initiate a blinking animation on a node to show it was updated.
184 187 */
185 188 function blink(node) {
186 189 var blinkCount = 2;
187 190
188 191 var nodeToAnimate = node;
189 192 for (var i = 0; i < blinkCount; i++) {
190 193 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
191 194 }
192 195 }
193 196
194 197 function isPageBottom() {
195 198 var scroll = $(window).scrollTop() / ($(document).height()
196 199 - $(window).height());
197 200
198 201 return scroll == 1
199 202 }
200 203
201 204 function enableJsUpdate() {
202 205 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
203 206 return true;
204 207 }
205 208
206 209 function initAutoupdate() {
207 210 if (location.protocol === 'https:') {
208 211 return enableJsUpdate();
209 212 } else {
210 213 if (connectWebsocket()) {
211 214 return true;
212 215 } else {
213 216 return enableJsUpdate();
214 217 }
215 218 }
216 219 }
217 220
218 221 function getReplyCount() {
219 222 return $('.thread').children(CLASS_POST).length
220 223 }
221 224
222 225 function getImageCount() {
223 226 return $('.thread').find('img').length
224 227 }
225 228
226 229 /**
227 230 * Update post count, images count and last update time in the metadata
228 231 * panel.
229 232 */
230 233 function updateMetadataPanel() {
231 234 var replyCountField = $('#reply-count');
232 235 var imageCountField = $('#image-count');
233 236
234 237 replyCountField.text(getReplyCount());
235 238 imageCountField.text(getImageCount());
236 239
237 240 var lastUpdate = $('.post:last').children('.post-info').first()
238 241 .children('.pub_time').first().html();
239 242 if (lastUpdate !== '') {
240 243 var lastUpdateField = $('#last-update');
241 244 lastUpdateField.html(lastUpdate);
242 245 blink(lastUpdateField);
243 246 }
244 247
245 248 blink(replyCountField);
246 249 blink(imageCountField);
247 250 }
248 251
249 252 /**
250 253 * Update bumplimit progress bar
251 254 */
252 255 function updateBumplimitProgress(postDelta) {
253 256 var progressBar = $('#bumplimit_progress');
254 257 if (progressBar) {
255 258 var postsToLimitElement = $('#left_to_limit');
256 259
257 260 var oldPostsToLimit = parseInt(postsToLimitElement.text());
258 261 var postCount = getReplyCount();
259 262 var bumplimit = postCount - postDelta + oldPostsToLimit;
260 263
261 264 var newPostsToLimit = bumplimit - postCount;
262 265 if (newPostsToLimit <= 0) {
263 266 $('.bar-bg').remove();
264 267 } else {
265 268 postsToLimitElement.text(newPostsToLimit);
266 269 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
267 270 }
268 271 }
269 272 }
270 273
271 274 /**
272 275 * Show 'new posts' text in the title if the document is not visible to a user
273 276 */
274 277 function showNewPostsTitle(newPostCount) {
275 278 if (document.hidden) {
276 279 if (documentOriginalTitle === '') {
277 280 documentOriginalTitle = document.title;
278 281 }
279 282 unreadPosts = unreadPosts + newPostCount;
280 283
281 284 var newTitle = '* ';
282 285 if (unreadPosts > 0) {
283 286 newTitle += '[' + unreadPosts + '] ';
284 287 }
285 288 newTitle += documentOriginalTitle;
286 289
287 290 document.title = newTitle;
288 291
289 292 document.addEventListener('visibilitychange', function() {
290 293 if (documentOriginalTitle !== '') {
291 294 document.title = documentOriginalTitle;
292 295 documentOriginalTitle = '';
293 296 unreadPosts = 0;
294 297 }
295 298
296 299 document.removeEventListener('visibilitychange', null);
297 300 });
298 301 }
299 302 }
300 303
301 304 /**
302 305 * Clear all entered values in the form fields
303 306 */
304 307 function resetForm(form) {
305 308 form.find('input:text, input:password, input:file, select, textarea').val('');
306 309 form.find('input:radio, input:checkbox')
307 310 .removeAttr('checked').removeAttr('selected');
308 311 $('.file_wrap').find('.file-thumb').remove();
309 312 }
310 313
311 314 /**
312 315 * When the form is posted, this method will be run as a callback
313 316 */
314 317 function updateOnPost(response, statusText, xhr, form) {
315 318 var json = $.parseJSON(response);
316 319 var status = json.status;
317 320
318 321 showAsErrors(form, '');
319 322
320 323 if (status === 'ok') {
321 324 resetFormPosition();
322 325 resetForm(form);
323 326 getThreadDiff();
324 327 scrollToBottom();
325 328 } else {
326 329 var errors = json.errors;
327 330 for (var i = 0; i < errors.length; i++) {
328 331 var fieldErrors = errors[i];
329 332
330 333 var error = fieldErrors.errors;
331 334
332 335 showAsErrors(form, error);
333 336 }
334 337 }
335 338 }
336 339
337 340 /**
338 341 * Show text in the errors row of the form.
339 342 * @param form
340 343 * @param text
341 344 */
342 345 function showAsErrors(form, text) {
343 346 form.children('.form-errors').remove();
344 347
345 348 if (text.length > 0) {
346 349 var errorList = $('<div class="form-errors">' + text + '<div>');
347 350 errorList.appendTo(form);
348 351 }
349 352 }
350 353
351 354 /**
352 355 * Run js methods that are usually run on the document, on the new post
353 356 */
354 357 function processNewPost(post) {
355 358 addRefLinkPreview(post[0]);
356 359 highlightCode(post);
357 360 blink(post);
358 361 }
359 362
360 363 $(document).ready(function(){
361 364 if (initAutoupdate()) {
362 365 // Post form data over AJAX
363 366 var threadId = $('div.thread').children('.post').first().attr('id');
364 367
365 368 var form = $('#form');
366 369
367 370 if (form.length > 0) {
368 371 var options = {
369 372 beforeSubmit: function(arr, $form, options) {
370 373 showAsErrors($('form'), gettext('Sending message...'));
371 374 },
372 375 success: updateOnPost,
373 376 url: '/api/add_post/' + threadId + '/'
374 377 };
375 378
376 379 form.ajaxForm(options);
377 380
378 381 resetForm(form);
379 382 }
380 383 }
381 384
382 385 $('#autoupdate').click(getThreadDiff);
383 386 });
General Comments 0
You need to be logged in to leave comments. Login now