##// END OF EJS Templates
Blink with fade out
neko259 -
r2134:d8cfb857 default
parent child Browse files
Show More
@@ -1,358 +1,357 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 // TODO These need to be syncronized with board settings.
32 32 var JS_AUTOUPDATE_PERIOD = 20000;
33 33 var BLINK_SPEED = 500;
34 34
35 35 var ALLOWED_FOR_PARTIAL_UPDATE = [
36 36 'refmap',
37 37 'post-info'
38 38 ];
39 39
40 40 var ATTR_CLASS = 'class';
41 41 var ATTR_UID = 'data-uid';
42 42
43 43 var unreadPosts = 0;
44 44 var documentOriginalTitle = '';
45 45
46 46 // Thread ID does not change, can be stored one time
47 47 var threadId = $('div.thread').children(CLASS_POST).first().attr('id');
48 var blinkColor = $('<div class="post-blink"></div>').css('background-color');
49 48
50 49 /**
51 50 * Get diff of the posts from the current thread timestamp.
52 51 * This is required if the browser was closed and some post updates were
53 52 * missed.
54 53 */
55 54 function getThreadDiff() {
56 55 var all_posts = $('.post');
57 56
58 57 var uids = '';
59 58 var posts = all_posts;
60 59 for (var i = 0; i < posts.length; i++) {
61 60 uids += posts[i].getAttribute('data-uid') + ' ';
62 61 }
63 62
64 63 var data = {
65 64 uids: uids,
66 65 thread: threadId
67 66 };
68 67
69 68 var diffUrl = '/api/diff_thread/';
70 69
71 70 $.post(diffUrl,
72 71 data,
73 72 function(data) {
74 73 var updatedPosts = data.updated;
75 74 var addedPostCount = 0;
76 75
77 76 for (var i = 0; i < updatedPosts.length; i++) {
78 77 var postText = updatedPosts[i];
79 78 var post = $(postText);
80 79
81 80 if (updatePost(post) == POST_ADDED) {
82 81 addedPostCount++;
83 82 }
84 83 }
85 84
86 85 var hasMetaUpdates = updatedPosts.length > 0;
87 86 if (hasMetaUpdates) {
88 87 updateMetadataPanel();
89 88 }
90 89
91 90 if (addedPostCount > 0) {
92 91 updateBumplimitProgress(addedPostCount);
93 92 }
94 93
95 94 if (updatedPosts.length > 0) {
96 95 showNewPostsTitle(addedPostCount);
97 96 }
98 97
99 98 // TODO Process removed posts if any
100 99 $('.metapanel').attr('data-last-update', data.last_update);
101 100
102 101 if (data.subscribed == 'True') {
103 102 var favButton = $('#thread-fav-button .not_fav');
104 103
105 104 if (favButton.length > 0) {
106 105 favButton.attr('value', 'unsubscribe');
107 106 favButton.removeClass('not_fav');
108 107 favButton.addClass('fav');
109 108 }
110 109 }
111 110 },
112 111 'json'
113 112 )
114 113 }
115 114
116 115 /**
117 116 * Add or update the post on html page.
118 117 */
119 118 function updatePost(postHtml) {
120 119 // This needs to be set on start because the page is scrolled after posts
121 120 // are added or updated
122 121 var bottom = isPageBottom();
123 122
124 123 var post = $(postHtml);
125 124
126 125 var threadBlock = $('div.thread');
127 126
128 127 var postId = post.attr('id');
129 128
130 129 // If the post already exists, replace it. Otherwise add as a new one.
131 130 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
132 131
133 132 var type;
134 133
135 134 if (existingPosts.size() > 0) {
136 135 replacePartial(existingPosts.first(), post, false);
137 136 post = existingPosts.first();
138 137
139 138 type = POST_UPDATED;
140 139 } else {
141 140 var postPubTime = new Date(post.find('time').attr('datetime'));
142 141 var allPosts = $('.post');
143 142 var previousPost = null;
144 143
145 144 allPosts.each(function(i) {
146 145 var curPost = $(this);
147 146 var curPostTime = new Date(curPost.find('time').attr('datetime'));
148 147
149 148 if (i + 0 && curPostTime > postPubTime) {
150 149 previousPost = allPosts.get(i - 1);
151 150 return false;
152 151 };
153 152 });
154 153
155 154 if (previousPost == null) {
156 155 previousPost = allPosts.last();
157 156 }
158 157
159 158 post.insertAfter(previousPost);
160 159
161 160 if (bottom) {
162 161 scrollToBottom();
163 162 }
164 163
165 164 type = POST_ADDED;
166 165 }
167 166
168 167 processNewPost(post);
169 168
170 169 return type;
171 170 }
172 171
173 172 /**
174 173 * Initiate a blinking animation on a node to show it was updated.
175 174 */
176 175 function blink(node) {
177 node.effect('highlight', { color: blinkColor }, BLINK_SPEED);
176 node.fadeOut(BLINK_SPEED).fadeIn(BLINK_SPEED);
178 177 }
179 178
180 179 function isPageBottom() {
181 180 var scroll = $(window).scrollTop() / ($(document).height()
182 181 - $(window).height());
183 182
184 183 return scroll == 1
185 184 }
186 185
187 186 function enableJsUpdate() {
188 187 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
189 188 return true;
190 189 }
191 190
192 191 function initAutoupdate() {
193 192 return enableJsUpdate();
194 193 }
195 194
196 195 function getReplyCount() {
197 196 return $('.thread').children(CLASS_POST).length
198 197 }
199 198
200 199 function getImageCount() {
201 200 return $('.thread').find('.image').length
202 201 }
203 202
204 203 /**
205 204 * Update post count, images count and last update time in the metadata
206 205 * panel.
207 206 */
208 207 function updateMetadataPanel() {
209 208 var replyCountField = $('#reply-count');
210 209 var imageCountField = $('#image-count');
211 210
212 211 var replyCount = getReplyCount();
213 212 replyCountField.text(replyCount);
214 213 var imageCount = getImageCount();
215 214 imageCountField.text(imageCount);
216 215
217 216 var lastUpdate = $('.post:last').children('.post-info').first()
218 217 .children('.pub_time').first().html();
219 218 if (lastUpdate !== '') {
220 219 var lastUpdateField = $('#last-update');
221 220 lastUpdateField.html(lastUpdate);
222 221 blink(lastUpdateField);
223 222 }
224 223
225 224 blink(replyCountField);
226 225 blink(imageCountField);
227 226 }
228 227
229 228 /**
230 229 * Update bumplimit progress bar
231 230 */
232 231 function updateBumplimitProgress(postDelta) {
233 232 var progressBar = $('#bumplimit_progress');
234 233 if (progressBar) {
235 234 var postsToLimitElement = $('#left_to_limit');
236 235
237 236 var oldPostsToLimit = parseInt(postsToLimitElement.text());
238 237 var postCount = getReplyCount();
239 238 var bumplimit = postCount - postDelta + oldPostsToLimit;
240 239
241 240 var newPostsToLimit = bumplimit - postCount;
242 241 if (newPostsToLimit <= 0) {
243 242 $('.bar-bg').remove();
244 243 } else {
245 244 postsToLimitElement.text(newPostsToLimit);
246 245 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
247 246 }
248 247 }
249 248 }
250 249
251 250 /**
252 251 * Show 'new posts' text in the title if the document is not visible to a user
253 252 */
254 253 function showNewPostsTitle(newPostCount) {
255 254 if (document.hidden) {
256 255 if (documentOriginalTitle === '') {
257 256 documentOriginalTitle = document.title;
258 257 }
259 258 unreadPosts = unreadPosts + newPostCount;
260 259
261 260 var newTitle = null;
262 261 if (unreadPosts > 0) {
263 262 newTitle = '[' + unreadPosts + '] ';
264 263 } else {
265 264 newTitle = '* ';
266 265 }
267 266 newTitle += documentOriginalTitle;
268 267
269 268 document.title = newTitle;
270 269
271 270 document.addEventListener('visibilitychange', function() {
272 271 if (documentOriginalTitle !== '') {
273 272 document.title = documentOriginalTitle;
274 273 documentOriginalTitle = '';
275 274 unreadPosts = 0;
276 275 }
277 276
278 277 document.removeEventListener('visibilitychange', null);
279 278 });
280 279 }
281 280 }
282 281
283 282 /**
284 283 * Run js methods that are usually run on the document, on the new post
285 284 */
286 285 function processNewPost(post) {
287 286 addScriptsToPost(post);
288 287 blink(post);
289 288 }
290 289
291 290 function replacePartial(oldNode, newNode, recursive) {
292 291 if (!equalNodes(oldNode, newNode)) {
293 292 // Update parent node attributes
294 293 updateNodeAttr(oldNode, newNode, ATTR_CLASS);
295 294 updateNodeAttr(oldNode, newNode, ATTR_UID);
296 295
297 296 // Replace children
298 297 var children = oldNode.children();
299 298 if (children.length == 0) {
300 299 oldNode.replaceWith(newNode);
301 300 } else {
302 301 var newChildren = newNode.children();
303 302 newChildren.each(function(i) {
304 303 var newChild = newChildren.eq(i);
305 304 var newChildClass = newChild.attr(ATTR_CLASS);
306 305
307 306 // Update only certain allowed blocks (e.g. not images)
308 307 if (ALLOWED_FOR_PARTIAL_UPDATE.indexOf(newChildClass) > -1) {
309 308 var oldChild = oldNode.children('.' + newChildClass);
310 309
311 310 if (oldChild.length == 0) {
312 311 oldNode.append(newChild);
313 312 } else {
314 313 if (!equalNodes(oldChild, newChild)) {
315 314 if (recursive) {
316 315 replacePartial(oldChild, newChild, false);
317 316 } else {
318 317 oldChild.replaceWith(newChild);
319 318 }
320 319 }
321 320 }
322 321 }
323 322 });
324 323 }
325 324 }
326 325 }
327 326
328 327 /**
329 328 * Compare nodes by content
330 329 */
331 330 function equalNodes(node1, node2) {
332 331 return node1[0].outerHTML == node2[0].outerHTML;
333 332 }
334 333
335 334 /**
336 335 * Update attribute of a node if it has changed
337 336 */
338 337 function updateNodeAttr(oldNode, newNode, attrName) {
339 338 var oldAttr = oldNode.attr(attrName);
340 339 var newAttr = newNode.attr(attrName);
341 340 if (oldAttr != newAttr) {
342 341 oldNode.attr(attrName, newAttr);
343 342 }
344 343 }
345 344
346 345 $(document).ready(function() {
347 346 if (initAutoupdate()) {
348 347 // Post form data over AJAX
349 348 var threadId = $('div.thread').children('.post').first().attr('id');
350 349
351 350 var form = $('#form');
352 351
353 352 initAjaxForm(threadId);
354 353 if (form.length > 0) {
355 354 resetForm();
356 355 }
357 356 }
358 357 });
General Comments 0
You need to be logged in to leave comments. Login now