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