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