##// END OF EJS Templates
Do not update global fav button instead of thread's one
neko259 -
r1756:fa9529ca default
parent child Browse files
Show More
@@ -1,462 +1,462 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 // TODO This needs to be the same for attachment download time limit.
33 // TODO This needs to be the same for attachment download time limit.
34 var POST_AJAX_TIMEOUT = 30000;
34 var POST_AJAX_TIMEOUT = 30000;
35 var BLINK_SPEED = 500;
35 var BLINK_SPEED = 500;
36
36
37 var ALLOWED_FOR_PARTIAL_UPDATE = [
37 var ALLOWED_FOR_PARTIAL_UPDATE = [
38 'refmap',
38 'refmap',
39 'post-info'
39 'post-info'
40 ];
40 ];
41
41
42 var ATTR_CLASS = 'class';
42 var ATTR_CLASS = 'class';
43 var ATTR_UID = 'data-uid';
43 var ATTR_UID = 'data-uid';
44
44
45 var wsUser = '';
45 var wsUser = '';
46
46
47 var unreadPosts = 0;
47 var unreadPosts = 0;
48 var documentOriginalTitle = '';
48 var documentOriginalTitle = '';
49
49
50 // Thread ID does not change, can be stored one time
50 // Thread ID does not change, can be stored one time
51 var threadId = $('div.thread').children(CLASS_POST).first().attr('id');
51 var threadId = $('div.thread').children(CLASS_POST).first().attr('id');
52 var blinkColor = $('<div class="post-blink"></div>').css('background-color');
52 var blinkColor = $('<div class="post-blink"></div>').css('background-color');
53
53
54 /**
54 /**
55 * Connect to websocket server and subscribe to thread updates. On any update we
55 * Connect to websocket server and subscribe to thread updates. On any update we
56 * request a thread diff.
56 * request a thread diff.
57 *
57 *
58 * @returns {boolean} true if connected, false otherwise
58 * @returns {boolean} true if connected, false otherwise
59 */
59 */
60 function connectWebsocket() {
60 function connectWebsocket() {
61 var metapanel = $('.metapanel')[0];
61 var metapanel = $('.metapanel')[0];
62
62
63 var wsHost = metapanel.getAttribute('data-ws-host');
63 var wsHost = metapanel.getAttribute('data-ws-host');
64 var wsPort = metapanel.getAttribute('data-ws-port');
64 var wsPort = metapanel.getAttribute('data-ws-port');
65
65
66 if (wsHost.length > 0 && wsPort.length > 0) {
66 if (wsHost.length > 0 && wsPort.length > 0) {
67 var centrifuge = new Centrifuge({
67 var centrifuge = new Centrifuge({
68 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
68 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
69 "project": metapanel.getAttribute('data-ws-project'),
69 "project": metapanel.getAttribute('data-ws-project'),
70 "user": wsUser,
70 "user": wsUser,
71 "timestamp": metapanel.getAttribute('data-ws-token-time'),
71 "timestamp": metapanel.getAttribute('data-ws-token-time'),
72 "token": metapanel.getAttribute('data-ws-token'),
72 "token": metapanel.getAttribute('data-ws-token'),
73 "debug": false
73 "debug": false
74 });
74 });
75
75
76 centrifuge.on('error', function(error_message) {
76 centrifuge.on('error', function(error_message) {
77 console.log("Error connecting to websocket server.");
77 console.log("Error connecting to websocket server.");
78 console.log(error_message);
78 console.log(error_message);
79 console.log("Using javascript update instead.");
79 console.log("Using javascript update instead.");
80
80
81 // If websockets don't work, enable JS update instead
81 // If websockets don't work, enable JS update instead
82 enableJsUpdate()
82 enableJsUpdate()
83 });
83 });
84
84
85 centrifuge.on('connect', function() {
85 centrifuge.on('connect', function() {
86 var channelName = 'thread:' + threadId;
86 var channelName = 'thread:' + threadId;
87 centrifuge.subscribe(channelName, function(message) {
87 centrifuge.subscribe(channelName, function(message) {
88 getThreadDiff();
88 getThreadDiff();
89 });
89 });
90
90
91 // For the case we closed the browser and missed some updates
91 // For the case we closed the browser and missed some updates
92 getThreadDiff();
92 getThreadDiff();
93 $('#autoupdate').hide();
93 $('#autoupdate').hide();
94 });
94 });
95
95
96 centrifuge.connect();
96 centrifuge.connect();
97
97
98 return true;
98 return true;
99 } else {
99 } else {
100 return false;
100 return false;
101 }
101 }
102 }
102 }
103
103
104 /**
104 /**
105 * Get diff of the posts from the current thread timestamp.
105 * Get diff of the posts from the current thread timestamp.
106 * This is required if the browser was closed and some post updates were
106 * This is required if the browser was closed and some post updates were
107 * missed.
107 * missed.
108 */
108 */
109 function getThreadDiff() {
109 function getThreadDiff() {
110 var all_posts = $('.post');
110 var all_posts = $('.post');
111
111
112 var uids = '';
112 var uids = '';
113 var posts = all_posts;
113 var posts = all_posts;
114 for (var i = 0; i < posts.length; i++) {
114 for (var i = 0; i < posts.length; i++) {
115 uids += posts[i].getAttribute('data-uid') + ' ';
115 uids += posts[i].getAttribute('data-uid') + ' ';
116 }
116 }
117
117
118 var data = {
118 var data = {
119 uids: uids,
119 uids: uids,
120 thread: threadId
120 thread: threadId
121 };
121 };
122
122
123 var diffUrl = '/api/diff_thread/';
123 var diffUrl = '/api/diff_thread/';
124
124
125 $.post(diffUrl,
125 $.post(diffUrl,
126 data,
126 data,
127 function(data) {
127 function(data) {
128 var updatedPosts = data.updated;
128 var updatedPosts = data.updated;
129 var addedPostCount = 0;
129 var addedPostCount = 0;
130
130
131 for (var i = 0; i < updatedPosts.length; i++) {
131 for (var i = 0; i < updatedPosts.length; i++) {
132 var postText = updatedPosts[i];
132 var postText = updatedPosts[i];
133 var post = $(postText);
133 var post = $(postText);
134
134
135 if (updatePost(post) == POST_ADDED) {
135 if (updatePost(post) == POST_ADDED) {
136 addedPostCount++;
136 addedPostCount++;
137 }
137 }
138 }
138 }
139
139
140 var hasMetaUpdates = updatedPosts.length > 0;
140 var hasMetaUpdates = updatedPosts.length > 0;
141 if (hasMetaUpdates) {
141 if (hasMetaUpdates) {
142 updateMetadataPanel();
142 updateMetadataPanel();
143 }
143 }
144
144
145 if (addedPostCount > 0) {
145 if (addedPostCount > 0) {
146 updateBumplimitProgress(addedPostCount);
146 updateBumplimitProgress(addedPostCount);
147 }
147 }
148
148
149 if (updatedPosts.length > 0) {
149 if (updatedPosts.length > 0) {
150 showNewPostsTitle(addedPostCount);
150 showNewPostsTitle(addedPostCount);
151 }
151 }
152
152
153 // TODO Process removed posts if any
153 // TODO Process removed posts if any
154 $('.metapanel').attr('data-last-update', data.last_update);
154 $('.metapanel').attr('data-last-update', data.last_update);
155
155
156 if (data.subscribed == 'True') {
156 if (data.subscribed == 'True') {
157 var favButton = $('.not_fav');
157 var favButton = $('#thread-fav-button .not_fav');
158
158
159 if (favButton.length > 0) {
159 if (favButton.length > 0) {
160 favButton.attr('value', 'unsubscribe');
160 favButton.attr('value', 'unsubscribe');
161 favButton.removeClass('not_fav');
161 favButton.removeClass('not_fav');
162 favButton.addClass('fav');
162 favButton.addClass('fav');
163 }
163 }
164 }
164 }
165 },
165 },
166 'json'
166 'json'
167 )
167 )
168 }
168 }
169
169
170 /**
170 /**
171 * Add or update the post on html page.
171 * Add or update the post on html page.
172 */
172 */
173 function updatePost(postHtml) {
173 function updatePost(postHtml) {
174 // This needs to be set on start because the page is scrolled after posts
174 // This needs to be set on start because the page is scrolled after posts
175 // are added or updated
175 // are added or updated
176 var bottom = isPageBottom();
176 var bottom = isPageBottom();
177
177
178 var post = $(postHtml);
178 var post = $(postHtml);
179
179
180 var threadBlock = $('div.thread');
180 var threadBlock = $('div.thread');
181
181
182 var postId = post.attr('id');
182 var postId = post.attr('id');
183
183
184 // If the post already exists, replace it. Otherwise add as a new one.
184 // If the post already exists, replace it. Otherwise add as a new one.
185 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
185 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
186
186
187 var type;
187 var type;
188
188
189 if (existingPosts.size() > 0) {
189 if (existingPosts.size() > 0) {
190 replacePartial(existingPosts.first(), post, false);
190 replacePartial(existingPosts.first(), post, false);
191 post = existingPosts.first();
191 post = existingPosts.first();
192
192
193 type = POST_UPDATED;
193 type = POST_UPDATED;
194 } else {
194 } else {
195 post.appendTo(threadBlock);
195 post.appendTo(threadBlock);
196
196
197 if (bottom) {
197 if (bottom) {
198 scrollToBottom();
198 scrollToBottom();
199 }
199 }
200
200
201 type = POST_ADDED;
201 type = POST_ADDED;
202 }
202 }
203
203
204 processNewPost(post);
204 processNewPost(post);
205
205
206 return type;
206 return type;
207 }
207 }
208
208
209 /**
209 /**
210 * Initiate a blinking animation on a node to show it was updated.
210 * Initiate a blinking animation on a node to show it was updated.
211 */
211 */
212 function blink(node) {
212 function blink(node) {
213 node.effect('highlight', { color: blinkColor }, BLINK_SPEED);
213 node.effect('highlight', { color: blinkColor }, BLINK_SPEED);
214 }
214 }
215
215
216 function isPageBottom() {
216 function isPageBottom() {
217 var scroll = $(window).scrollTop() / ($(document).height()
217 var scroll = $(window).scrollTop() / ($(document).height()
218 - $(window).height());
218 - $(window).height());
219
219
220 return scroll == 1
220 return scroll == 1
221 }
221 }
222
222
223 function enableJsUpdate() {
223 function enableJsUpdate() {
224 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
224 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
225 return true;
225 return true;
226 }
226 }
227
227
228 function initAutoupdate() {
228 function initAutoupdate() {
229 if (location.protocol === 'https:') {
229 if (location.protocol === 'https:') {
230 return enableJsUpdate();
230 return enableJsUpdate();
231 } else {
231 } else {
232 if (connectWebsocket()) {
232 if (connectWebsocket()) {
233 return true;
233 return true;
234 } else {
234 } else {
235 return enableJsUpdate();
235 return enableJsUpdate();
236 }
236 }
237 }
237 }
238 }
238 }
239
239
240 function getReplyCount() {
240 function getReplyCount() {
241 return $('.thread').children(CLASS_POST).length
241 return $('.thread').children(CLASS_POST).length
242 }
242 }
243
243
244 function getImageCount() {
244 function getImageCount() {
245 return $('.thread').find('img').length
245 return $('.thread').find('img').length
246 }
246 }
247
247
248 /**
248 /**
249 * Update post count, images count and last update time in the metadata
249 * Update post count, images count and last update time in the metadata
250 * panel.
250 * panel.
251 */
251 */
252 function updateMetadataPanel() {
252 function updateMetadataPanel() {
253 var replyCountField = $('#reply-count');
253 var replyCountField = $('#reply-count');
254 var imageCountField = $('#image-count');
254 var imageCountField = $('#image-count');
255
255
256 var replyCount = getReplyCount();
256 var replyCount = getReplyCount();
257 replyCountField.text(replyCount);
257 replyCountField.text(replyCount);
258 var imageCount = getImageCount();
258 var imageCount = getImageCount();
259 imageCountField.text(imageCount);
259 imageCountField.text(imageCount);
260
260
261 var lastUpdate = $('.post:last').children('.post-info').first()
261 var lastUpdate = $('.post:last').children('.post-info').first()
262 .children('.pub_time').first().html();
262 .children('.pub_time').first().html();
263 if (lastUpdate !== '') {
263 if (lastUpdate !== '') {
264 var lastUpdateField = $('#last-update');
264 var lastUpdateField = $('#last-update');
265 lastUpdateField.html(lastUpdate);
265 lastUpdateField.html(lastUpdate);
266 blink(lastUpdateField);
266 blink(lastUpdateField);
267 }
267 }
268
268
269 blink(replyCountField);
269 blink(replyCountField);
270 blink(imageCountField);
270 blink(imageCountField);
271
271
272 $('#message-count-text').text(ngettext('message', 'messages', replyCount));
272 $('#message-count-text').text(ngettext('message', 'messages', replyCount));
273 $('#image-count-text').text(ngettext('image', 'images', imageCount));
273 $('#image-count-text').text(ngettext('image', 'images', imageCount));
274 }
274 }
275
275
276 /**
276 /**
277 * Update bumplimit progress bar
277 * Update bumplimit progress bar
278 */
278 */
279 function updateBumplimitProgress(postDelta) {
279 function updateBumplimitProgress(postDelta) {
280 var progressBar = $('#bumplimit_progress');
280 var progressBar = $('#bumplimit_progress');
281 if (progressBar) {
281 if (progressBar) {
282 var postsToLimitElement = $('#left_to_limit');
282 var postsToLimitElement = $('#left_to_limit');
283
283
284 var oldPostsToLimit = parseInt(postsToLimitElement.text());
284 var oldPostsToLimit = parseInt(postsToLimitElement.text());
285 var postCount = getReplyCount();
285 var postCount = getReplyCount();
286 var bumplimit = postCount - postDelta + oldPostsToLimit;
286 var bumplimit = postCount - postDelta + oldPostsToLimit;
287
287
288 var newPostsToLimit = bumplimit - postCount;
288 var newPostsToLimit = bumplimit - postCount;
289 if (newPostsToLimit <= 0) {
289 if (newPostsToLimit <= 0) {
290 $('.bar-bg').remove();
290 $('.bar-bg').remove();
291 } else {
291 } else {
292 postsToLimitElement.text(newPostsToLimit);
292 postsToLimitElement.text(newPostsToLimit);
293 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
293 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
294 }
294 }
295 }
295 }
296 }
296 }
297
297
298 /**
298 /**
299 * Show 'new posts' text in the title if the document is not visible to a user
299 * Show 'new posts' text in the title if the document is not visible to a user
300 */
300 */
301 function showNewPostsTitle(newPostCount) {
301 function showNewPostsTitle(newPostCount) {
302 if (document.hidden) {
302 if (document.hidden) {
303 if (documentOriginalTitle === '') {
303 if (documentOriginalTitle === '') {
304 documentOriginalTitle = document.title;
304 documentOriginalTitle = document.title;
305 }
305 }
306 unreadPosts = unreadPosts + newPostCount;
306 unreadPosts = unreadPosts + newPostCount;
307
307
308 var newTitle = null;
308 var newTitle = null;
309 if (unreadPosts > 0) {
309 if (unreadPosts > 0) {
310 newTitle = '[' + unreadPosts + '] ';
310 newTitle = '[' + unreadPosts + '] ';
311 } else {
311 } else {
312 newTitle = '* ';
312 newTitle = '* ';
313 }
313 }
314 newTitle += documentOriginalTitle;
314 newTitle += documentOriginalTitle;
315
315
316 document.title = newTitle;
316 document.title = newTitle;
317
317
318 document.addEventListener('visibilitychange', function() {
318 document.addEventListener('visibilitychange', function() {
319 if (documentOriginalTitle !== '') {
319 if (documentOriginalTitle !== '') {
320 document.title = documentOriginalTitle;
320 document.title = documentOriginalTitle;
321 documentOriginalTitle = '';
321 documentOriginalTitle = '';
322 unreadPosts = 0;
322 unreadPosts = 0;
323 }
323 }
324
324
325 document.removeEventListener('visibilitychange', null);
325 document.removeEventListener('visibilitychange', null);
326 });
326 });
327 }
327 }
328 }
328 }
329
329
330 /**
330 /**
331 * Clear all entered values in the form fields
331 * Clear all entered values in the form fields
332 */
332 */
333 function resetForm(form) {
333 function resetForm(form) {
334 form.find('input:text, input:password, input:file, select, textarea').val('');
334 form.find('input:text, input:password, input:file, select, textarea').val('');
335 form.find('input:radio, input:checkbox')
335 form.find('input:radio, input:checkbox')
336 .removeAttr('checked').removeAttr('selected');
336 .removeAttr('checked').removeAttr('selected');
337 $('.file_wrap').find('.file-thumb').remove();
337 $('.file_wrap').find('.file-thumb').remove();
338 $('#preview-text').hide();
338 $('#preview-text').hide();
339 }
339 }
340
340
341 /**
341 /**
342 * When the form is posted, this method will be run as a callback
342 * When the form is posted, this method will be run as a callback
343 */
343 */
344 function updateOnPost(response, statusText, xhr, form) {
344 function updateOnPost(response, statusText, xhr, form) {
345 var json = $.parseJSON(response);
345 var json = $.parseJSON(response);
346 var status = json.status;
346 var status = json.status;
347
347
348 showAsErrors(form, '');
348 showAsErrors(form, '');
349 $('.post-form-w').unblock();
349 $('.post-form-w').unblock();
350
350
351 if (status === 'ok') {
351 if (status === 'ok') {
352 resetFormPosition();
352 resetFormPosition();
353 resetForm(form);
353 resetForm(form);
354 getThreadDiff();
354 getThreadDiff();
355 scrollToBottom();
355 scrollToBottom();
356 } else {
356 } else {
357 var errors = json.errors;
357 var errors = json.errors;
358 for (var i = 0; i < errors.length; i++) {
358 for (var i = 0; i < errors.length; i++) {
359 var fieldErrors = errors[i];
359 var fieldErrors = errors[i];
360
360
361 var error = fieldErrors.errors;
361 var error = fieldErrors.errors;
362
362
363 showAsErrors(form, error);
363 showAsErrors(form, error);
364 }
364 }
365 }
365 }
366 }
366 }
367
367
368
368
369 /**
369 /**
370 * Run js methods that are usually run on the document, on the new post
370 * Run js methods that are usually run on the document, on the new post
371 */
371 */
372 function processNewPost(post) {
372 function processNewPost(post) {
373 addScriptsToPost(post);
373 addScriptsToPost(post);
374 blink(post);
374 blink(post);
375 }
375 }
376
376
377 function replacePartial(oldNode, newNode, recursive) {
377 function replacePartial(oldNode, newNode, recursive) {
378 if (!equalNodes(oldNode, newNode)) {
378 if (!equalNodes(oldNode, newNode)) {
379 // Update parent node attributes
379 // Update parent node attributes
380 updateNodeAttr(oldNode, newNode, ATTR_CLASS);
380 updateNodeAttr(oldNode, newNode, ATTR_CLASS);
381 updateNodeAttr(oldNode, newNode, ATTR_UID);
381 updateNodeAttr(oldNode, newNode, ATTR_UID);
382
382
383 // Replace children
383 // Replace children
384 var children = oldNode.children();
384 var children = oldNode.children();
385 if (children.length == 0) {
385 if (children.length == 0) {
386 oldNode.replaceWith(newNode);
386 oldNode.replaceWith(newNode);
387 } else {
387 } else {
388 var newChildren = newNode.children();
388 var newChildren = newNode.children();
389 newChildren.each(function(i) {
389 newChildren.each(function(i) {
390 var newChild = newChildren.eq(i);
390 var newChild = newChildren.eq(i);
391 var newChildClass = newChild.attr(ATTR_CLASS);
391 var newChildClass = newChild.attr(ATTR_CLASS);
392
392
393 // Update only certain allowed blocks (e.g. not images)
393 // Update only certain allowed blocks (e.g. not images)
394 if (ALLOWED_FOR_PARTIAL_UPDATE.indexOf(newChildClass) > -1) {
394 if (ALLOWED_FOR_PARTIAL_UPDATE.indexOf(newChildClass) > -1) {
395 var oldChild = oldNode.children('.' + newChildClass);
395 var oldChild = oldNode.children('.' + newChildClass);
396
396
397 if (oldChild.length == 0) {
397 if (oldChild.length == 0) {
398 oldNode.append(newChild);
398 oldNode.append(newChild);
399 } else {
399 } else {
400 if (!equalNodes(oldChild, newChild)) {
400 if (!equalNodes(oldChild, newChild)) {
401 if (recursive) {
401 if (recursive) {
402 replacePartial(oldChild, newChild, false);
402 replacePartial(oldChild, newChild, false);
403 } else {
403 } else {
404 oldChild.replaceWith(newChild);
404 oldChild.replaceWith(newChild);
405 }
405 }
406 }
406 }
407 }
407 }
408 }
408 }
409 });
409 });
410 }
410 }
411 }
411 }
412 }
412 }
413
413
414 /**
414 /**
415 * Compare nodes by content
415 * Compare nodes by content
416 */
416 */
417 function equalNodes(node1, node2) {
417 function equalNodes(node1, node2) {
418 return node1[0].outerHTML == node2[0].outerHTML;
418 return node1[0].outerHTML == node2[0].outerHTML;
419 }
419 }
420
420
421 /**
421 /**
422 * Update attribute of a node if it has changed
422 * Update attribute of a node if it has changed
423 */
423 */
424 function updateNodeAttr(oldNode, newNode, attrName) {
424 function updateNodeAttr(oldNode, newNode, attrName) {
425 var oldAttr = oldNode.attr(attrName);
425 var oldAttr = oldNode.attr(attrName);
426 var newAttr = newNode.attr(attrName);
426 var newAttr = newNode.attr(attrName);
427 if (oldAttr != newAttr) {
427 if (oldAttr != newAttr) {
428 oldNode.attr(attrName, newAttr);
428 oldNode.attr(attrName, newAttr);
429 }
429 }
430 }
430 }
431
431
432 $(document).ready(function() {
432 $(document).ready(function() {
433 if (initAutoupdate()) {
433 if (initAutoupdate()) {
434 // Post form data over AJAX
434 // Post form data over AJAX
435 var threadId = $('div.thread').children('.post').first().attr('id');
435 var threadId = $('div.thread').children('.post').first().attr('id');
436
436
437 var form = $('#form');
437 var form = $('#form');
438
438
439 if (form.length > 0) {
439 if (form.length > 0) {
440 var options = {
440 var options = {
441 beforeSubmit: function(arr, form, options) {
441 beforeSubmit: function(arr, form, options) {
442 $('.post-form-w').block({ message: gettext('Sending message...') });
442 $('.post-form-w').block({ message: gettext('Sending message...') });
443 },
443 },
444 success: updateOnPost,
444 success: updateOnPost,
445 error: function(xhr, textStatus, errorString) {
445 error: function(xhr, textStatus, errorString) {
446 var errorText = gettext('Server error: ') + textStatus;
446 var errorText = gettext('Server error: ') + textStatus;
447 if (errorString) {
447 if (errorString) {
448 errorText += ' / ' + errorString;
448 errorText += ' / ' + errorString;
449 }
449 }
450 showAsErrors(form, errorText);
450 showAsErrors(form, errorText);
451 $('.post-form-w').unblock();
451 $('.post-form-w').unblock();
452 },
452 },
453 url: '/api/add_post/' + threadId + '/',
453 url: '/api/add_post/' + threadId + '/',
454 timeout: POST_AJAX_TIMEOUT
454 timeout: POST_AJAX_TIMEOUT
455 };
455 };
456
456
457 form.ajaxForm(options);
457 form.ajaxForm(options);
458
458
459 resetForm(form);
459 resetForm(form);
460 }
460 }
461 }
461 }
462 });
462 });
@@ -1,80 +1,80 b''
1 {% extends "boards/thread.html" %}
1 {% extends "boards/thread.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load static from staticfiles %}
4 {% load static from staticfiles %}
5 {% load board %}
5 {% load board %}
6 {% load tz %}
6 {% load tz %}
7
7
8 {% block thread_content %}
8 {% block thread_content %}
9 {% get_current_language as LANGUAGE_CODE %}
9 {% get_current_language as LANGUAGE_CODE %}
10 {% get_current_timezone as TIME_ZONE %}
10 {% get_current_timezone as TIME_ZONE %}
11
11
12 <div id="quote-button">{% trans 'Quote' %}</div>
12 <div id="quote-button">{% trans 'Quote' %}</div>
13
13
14 <div class="tag_info">
14 <div class="tag_info">
15 <h2>
15 <h2>
16 <form action="{% url 'thread' opening_post.id %}" method="post" class="post-button-form">
16 <form action="{% url 'thread' opening_post.id %}" method="post" class="post-button-form">
17 {% csrf_token %}
17 {% csrf_token %}
18 {% if is_favorite %}
18 {% if is_favorite %}
19 <button name="method" value="unsubscribe" class="fav">β˜…</button>
19 <button id="thread-fav-button" name="method" value="unsubscribe" class="fav">β˜…</button>
20 {% else %}
20 {% else %}
21 <button name="method" value="subscribe" class="not_fav">β˜…</button>
21 <button id="thread-fav-button" name="method" value="subscribe" class="not_fav">β˜…</button>
22 {% endif %}
22 {% endif %}
23 </form>
23 </form>
24 {{ opening_post.get_title_or_text }}
24 {{ opening_post.get_title_or_text }}
25 </h2>
25 </h2>
26 </div>
26 </div>
27
27
28 {% if bumpable and thread.has_post_limit %}
28 {% if bumpable and thread.has_post_limit %}
29 <div class="bar-bg">
29 <div class="bar-bg">
30 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
30 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
31 </div>
31 </div>
32 <div class="bar-text">
32 <div class="bar-text">
33 <span id="left_to_limit">{{ posts_left }}</span> {% trans 'posts to bumplimit' %}
33 <span id="left_to_limit">{{ posts_left }}</span> {% trans 'posts to bumplimit' %}
34 </div>
34 </div>
35 </div>
35 </div>
36 {% endif %}
36 {% endif %}
37
37
38 <div class="thread">
38 <div class="thread">
39 {% for post in thread.get_viewable_replies %}
39 {% for post in thread.get_viewable_replies %}
40 {% post_view post reply_link=True thread=thread %}
40 {% post_view post reply_link=True thread=thread %}
41 {% endfor %}
41 {% endfor %}
42 </div>
42 </div>
43
43
44 {% if not thread.is_archived %}
44 {% if not thread.is_archived %}
45 <div class="post-form-w">
45 <div class="post-form-w">
46 <script src="{% static 'js/panel.js' %}"></script>
46 <script src="{% static 'js/panel.js' %}"></script>
47 <div class="form-title">{% trans "Reply to thread" %} #{{ opening_post.id }}<span class="reply-to-message"> {% trans "to message " %} #<span id="reply-to-message-id"></span></span></div>
47 <div class="form-title">{% trans "Reply to thread" %} #{{ opening_post.id }}<span class="reply-to-message"> {% trans "to message " %} #<span id="reply-to-message-id"></span></span></div>
48 <div class="post-form" id="compact-form" data-hasher="{% static 'js/3party/sha256.js' %}"
48 <div class="post-form" id="compact-form" data-hasher="{% static 'js/3party/sha256.js' %}"
49 data-pow-script="{% static 'js/proof_of_work.js' %}">
49 data-pow-script="{% static 'js/proof_of_work.js' %}">
50 <div class="swappable-form-full">
50 <div class="swappable-form-full">
51 <form enctype="multipart/form-data" method="post" id="form">{% csrf_token %}
51 <form enctype="multipart/form-data" method="post" id="form">{% csrf_token %}
52 <div class="compact-form-text"></div>
52 <div class="compact-form-text"></div>
53 {{ form.as_div }}
53 {{ form.as_div }}
54 <div class="form-submit">
54 <div class="form-submit">
55 <input type="submit" value="{% trans "Post" %}"/>
55 <input type="submit" value="{% trans "Post" %}"/>
56 <button id="preview-button" type="button" onclick="return false;">{% trans 'Preview' %}</button>
56 <button id="preview-button" type="button" onclick="return false;">{% trans 'Preview' %}</button>
57 </div>
57 </div>
58 </form>
58 </form>
59 </div>
59 </div>
60 <div id="preview-text"></div>
60 <div id="preview-text"></div>
61 <div>
61 <div>
62 {% with size=max_file_size|filesizeformat %}
62 {% with size=max_file_size|filesizeformat %}
63 {% blocktrans %}Max file size is {{ size }}.{% endblocktrans %}
63 {% blocktrans %}Max file size is {{ size }}.{% endblocktrans %}
64 {% endwith %}
64 {% endwith %}
65 </div>
65 </div>
66 <div><a href="{% url "staticpage" name="help" %}">
66 <div><a href="{% url "staticpage" name="help" %}">
67 {% trans 'Text syntax' %}</a></div>
67 {% trans 'Text syntax' %}</a></div>
68 <div><a id="form-close-button" href="#" onClick="resetFormPosition(); return false;">{% trans 'Close form' %}</a></div>
68 <div><a id="form-close-button" href="#" onClick="resetFormPosition(); return false;">{% trans 'Close form' %}</a></div>
69 </div>
69 </div>
70 </div>
70 </div>
71
71
72 <script src="{% static 'js/form.js' %}"></script>
72 <script src="{% static 'js/form.js' %}"></script>
73 <script src="{% static 'js/jquery.form.min.js' %}"></script>
73 <script src="{% static 'js/jquery.form.min.js' %}"></script>
74 <script src="{% static 'js/3party/jquery.blockUI.js' %}"></script>
74 <script src="{% static 'js/3party/jquery.blockUI.js' %}"></script>
75 <script src="{% static 'js/thread.js' %}"></script>
75 <script src="{% static 'js/thread.js' %}"></script>
76 <script src="{% static 'js/thread_update.js' %}"></script>
76 <script src="{% static 'js/thread_update.js' %}"></script>
77 {% endif %}
77 {% endif %}
78
78
79 <script src="{% static 'js/3party/centrifuge.js' %}"></script>
79 <script src="{% static 'js/3party/centrifuge.js' %}"></script>
80 {% endblock %}
80 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now