##// END OF EJS Templates
Disable websocket by default. Use JS thread update when websockets are...
neko259 -
r1147:24687cf5 default
parent child Browse files
Show More
@@ -1,23 +1,23 b''
1 1 VERSION = '2.6.1 Cooper'
2 2 SITE_NAME = 'Neboard'
3 3
4 4 CACHE_TIMEOUT = 600 # Timeout for caching, if cache is used
5 5 LOGIN_TIMEOUT = 3600 # Timeout between login tries
6 6 MAX_TEXT_LENGTH = 30000 # Max post length in characters
7 7 MAX_IMAGE_SIZE = 8 * 1024 * 1024 # Max image size
8 8
9 9 # Thread bumplimit
10 10 MAX_POSTS_PER_THREAD = 10
11 11 # Old posts will be archived or deleted if this value is reached
12 12 MAX_THREAD_COUNT = 5
13 13 THREADS_PER_PAGE = 3
14 14 DEFAULT_THEME = 'md'
15 15 DEFAULT_IMAGE_VIEWER = 'simple'
16 16 LAST_REPLIES_COUNT = 3
17 17
18 18 # Enable archiving threads instead of deletion when the thread limit is reached
19 19 ARCHIVE_THREADS = True
20 20 # Limit posting speed
21 21 LIMIT_POSTING_SPEED = False
22 22 # Thread update
23 WEBSOCKETS_ENABLED = True
23 WEBSOCKETS_ENABLED = False
@@ -1,376 +1,383 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 if (wsHost.length > 0 && wsPort.length > 0)
54 var centrifuge = new Centrifuge({
55 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
56 "project": metapanel.getAttribute('data-ws-project'),
57 "user": wsUser,
58 "timestamp": metapanel.getAttribute('data-ws-token-time'),
59 "token": metapanel.getAttribute('data-ws-token'),
60 "debug": false
61 });
53 if (wsHost.length > 0 && wsPort.length > 0) {
54 var centrifuge = new Centrifuge({
55 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
56 "project": metapanel.getAttribute('data-ws-project'),
57 "user": wsUser,
58 "timestamp": metapanel.getAttribute('data-ws-token-time'),
59 "token": metapanel.getAttribute('data-ws-token'),
60 "debug": false
61 });
62 62
63 centrifuge.on('error', function(error_message) {
64 console.log("Error connecting to websocket server.");
65 console.log(error_message);
66 return false;
67 });
68
69 centrifuge.on('connect', function() {
70 var channelName = 'thread:' + threadId;
71 centrifuge.subscribe(channelName, function(message) {
72 getThreadDiff();
63 centrifuge.on('error', function(error_message) {
64 console.log("Error connecting to websocket server.");
65 console.log(error_message);
66 return false;
73 67 });
74 68
75 // For the case we closed the browser and missed some updates
76 getThreadDiff();
77 $('#autoupdate').hide();
78 });
69 centrifuge.on('connect', function() {
70 var channelName = 'thread:' + threadId;
71 centrifuge.subscribe(channelName, function(message) {
72 getThreadDiff();
73 });
79 74
80 centrifuge.connect();
75 // For the case we closed the browser and missed some updates
76 getThreadDiff();
77 $('#autoupdate').hide();
78 });
81 79
82 return true;
80 centrifuge.connect();
81
82 return true;
83 } else {
84 return false;
85 }
83 86 }
84 87
85 88 /**
86 89 * Get diff of the posts from the current thread timestamp.
87 90 * This is required if the browser was closed and some post updates were
88 91 * missed.
89 92 */
90 93 function getThreadDiff() {
91 94 var lastUpdateTime = $('.metapanel').attr('data-last-update');
92 95 var lastPostId = $('.post').last().attr('id');
93 96
94 97 var uids = '';
95 98 var posts = $('.post');
96 99 for (var i = 0; i < posts.length; i++) {
97 100 uids += posts[i].getAttribute('data-uid') + ' ';
98 101 }
99 102
100 103 var data = {
101 104 uids: uids
102 105 }
103 106
104 107 var diffUrl = '/api/diff_thread?thread=' + threadId;
105 108
106 109 $.post(diffUrl,
107 110 data,
108 111 function(data) {
109 112 var updatedPosts = data.updated;
110 113 var addedPostCount = 0;
111 114
112 115 for (var i = 0; i < updatedPosts.length; i++) {
113 116 var postText = updatedPosts[i];
114 117 var post = $(postText);
115 118
116 119 if (updatePost(post) == POST_ADDED) {
117 120 addedPostCount++;
118 121 }
119 122 }
120 123
121 124 var hasMetaUpdates = updatedPosts.length > 0;
122 125 if (hasMetaUpdates) {
123 126 updateMetadataPanel();
124 127 }
125 128
126 129 if (addedPostCount > 0) {
127 130 updateBumplimitProgress(addedPostCount);
128 131 }
129 132
130 133 if (updatedPosts.length > 0) {
131 134 showNewPostsTitle(addedPostCount);
132 135 }
133 136
134 137 // TODO Process removed posts if any
135 138 $('.metapanel').attr('data-last-update', data.last_update);
136 139 },
137 140 'json'
138 141 )
139 142 }
140 143
141 144 /**
142 145 * Add or update the post on html page.
143 146 */
144 147 function updatePost(postHtml) {
145 148 // This needs to be set on start because the page is scrolled after posts
146 149 // are added or updated
147 150 var bottom = isPageBottom();
148 151
149 152 var post = $(postHtml);
150 153
151 154 var threadBlock = $('div.thread');
152 155
153 156 var postId = post.attr('id');
154 157
155 158 // If the post already exists, replace it. Otherwise add as a new one.
156 159 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
157 160
158 161 var type;
159 162
160 163 if (existingPosts.size() > 0) {
161 164 existingPosts.replaceWith(post);
162 165
163 166 type = POST_UPDATED;
164 167 } else {
165 168 post.appendTo(threadBlock);
166 169
167 170 if (bottom) {
168 171 scrollToBottom();
169 172 }
170 173
171 174 type = POST_ADDED;
172 175 }
173 176
174 177 processNewPost(post);
175 178
176 179 return type;
177 180 }
178 181
179 182 /**
180 183 * Initiate a blinking animation on a node to show it was updated.
181 184 */
182 185 function blink(node) {
183 186 var blinkCount = 2;
184 187
185 188 var nodeToAnimate = node;
186 189 for (var i = 0; i < blinkCount; i++) {
187 190 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
188 191 }
189 192 }
190 193
191 194 function isPageBottom() {
192 195 var scroll = $(window).scrollTop() / ($(document).height()
193 196 - $(window).height());
194 197
195 198 return scroll == 1
196 199 }
197 200
198 201 function enableJsUpdate() {
199 202 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
200 203 return true;
201 204 }
202 205
203 206 function initAutoupdate() {
204 207 if (location.protocol === 'https:') {
205 208 return enableJsUpdate();
206 209 } else {
207 return connectWebsocket();
210 if (connectWebsocket()) {
211 return true;
212 } else {
213 return enableJsUpdate();
214 }
208 215 }
209 216 }
210 217
211 218 function getReplyCount() {
212 219 return $('.thread').children(CLASS_POST).length
213 220 }
214 221
215 222 function getImageCount() {
216 223 return $('.thread').find('img').length
217 224 }
218 225
219 226 /**
220 227 * Update post count, images count and last update time in the metadata
221 228 * panel.
222 229 */
223 230 function updateMetadataPanel() {
224 231 var replyCountField = $('#reply-count');
225 232 var imageCountField = $('#image-count');
226 233
227 234 replyCountField.text(getReplyCount());
228 235 imageCountField.text(getImageCount());
229 236
230 237 var lastUpdate = $('.post:last').children('.post-info').first()
231 238 .children('.pub_time').first().html();
232 239 if (lastUpdate !== '') {
233 240 var lastUpdateField = $('#last-update');
234 241 lastUpdateField.html(lastUpdate);
235 242 blink(lastUpdateField);
236 243 }
237 244
238 245 blink(replyCountField);
239 246 blink(imageCountField);
240 247 }
241 248
242 249 /**
243 250 * Update bumplimit progress bar
244 251 */
245 252 function updateBumplimitProgress(postDelta) {
246 253 var progressBar = $('#bumplimit_progress');
247 254 if (progressBar) {
248 255 var postsToLimitElement = $('#left_to_limit');
249 256
250 257 var oldPostsToLimit = parseInt(postsToLimitElement.text());
251 258 var postCount = getReplyCount();
252 259 var bumplimit = postCount - postDelta + oldPostsToLimit;
253 260
254 261 var newPostsToLimit = bumplimit - postCount;
255 262 if (newPostsToLimit <= 0) {
256 263 $('.bar-bg').remove();
257 264 } else {
258 265 postsToLimitElement.text(newPostsToLimit);
259 266 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
260 267 }
261 268 }
262 269 }
263 270
264 271 /**
265 272 * Show 'new posts' text in the title if the document is not visible to a user
266 273 */
267 274 function showNewPostsTitle(newPostCount) {
268 275 if (document.hidden) {
269 276 if (documentOriginalTitle === '') {
270 277 documentOriginalTitle = document.title;
271 278 }
272 279 unreadPosts = unreadPosts + newPostCount;
273 280
274 281 var newTitle = '* ';
275 282 if (unreadPosts > 0) {
276 283 newTitle += '[' + unreadPosts + '] ';
277 284 }
278 285 newTitle += documentOriginalTitle;
279 286
280 287 document.title = newTitle;
281 288
282 289 document.addEventListener('visibilitychange', function() {
283 290 if (documentOriginalTitle !== '') {
284 291 document.title = documentOriginalTitle;
285 292 documentOriginalTitle = '';
286 293 unreadPosts = 0;
287 294 }
288 295
289 296 document.removeEventListener('visibilitychange', null);
290 297 });
291 298 }
292 299 }
293 300
294 301 /**
295 302 * Clear all entered values in the form fields
296 303 */
297 304 function resetForm(form) {
298 305 form.find('input:text, input:password, input:file, select, textarea').val('');
299 306 form.find('input:radio, input:checkbox')
300 307 .removeAttr('checked').removeAttr('selected');
301 308 $('.file_wrap').find('.file-thumb').remove();
302 309 }
303 310
304 311 /**
305 312 * When the form is posted, this method will be run as a callback
306 313 */
307 314 function updateOnPost(response, statusText, xhr, form) {
308 315 var json = $.parseJSON(response);
309 316 var status = json.status;
310 317
311 318 showAsErrors(form, '');
312 319
313 320 if (status === 'ok') {
314 321 resetFormPosition();
315 322 resetForm(form);
316 323 getThreadDiff();
317 324 scrollToBottom();
318 325 } else {
319 326 var errors = json.errors;
320 327 for (var i = 0; i < errors.length; i++) {
321 328 var fieldErrors = errors[i];
322 329
323 330 var error = fieldErrors.errors;
324 331
325 332 showAsErrors(form, error);
326 333 }
327 334 }
328 335 }
329 336
330 337 /**
331 338 * Show text in the errors row of the form.
332 339 * @param form
333 340 * @param text
334 341 */
335 342 function showAsErrors(form, text) {
336 343 form.children('.form-errors').remove();
337 344
338 345 if (text.length > 0) {
339 346 var errorList = $('<div class="form-errors">' + text + '<div>');
340 347 errorList.appendTo(form);
341 348 }
342 349 }
343 350
344 351 /**
345 352 * Run js methods that are usually run on the document, on the new post
346 353 */
347 354 function processNewPost(post) {
348 355 addRefLinkPreview(post[0]);
349 356 highlightCode(post);
350 357 blink(post);
351 358 }
352 359
353 360 $(document).ready(function(){
354 361 if (initAutoupdate()) {
355 362 // Post form data over AJAX
356 363 var threadId = $('div.thread').children('.post').first().attr('id');
357 364
358 365 var form = $('#form');
359 366
360 367 if (form.length > 0) {
361 368 var options = {
362 369 beforeSubmit: function(arr, $form, options) {
363 370 showAsErrors($('form'), gettext('Sending message...'));
364 371 },
365 372 success: updateOnPost,
366 373 url: '/api/add_post/' + threadId + '/'
367 374 };
368 375
369 376 form.ajaxForm(options);
370 377
371 378 resetForm(form);
372 379 }
373 380 }
374 381
375 382 $('#autoupdate').click(getThreadDiff);
376 383 });
General Comments 0
You need to be logged in to leave comments. Login now