##// END OF EJS Templates
Don't show an asterist in the page title after updating posts when number of...
neko259 -
r1467:02e3df39 default
parent child Browse files
Show More
@@ -1,444 +1,446 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 'json'
156 'json'
157 )
157 )
158 }
158 }
159
159
160 /**
160 /**
161 * Add or update the post on html page.
161 * Add or update the post on html page.
162 */
162 */
163 function updatePost(postHtml) {
163 function updatePost(postHtml) {
164 // This needs to be set on start because the page is scrolled after posts
164 // This needs to be set on start because the page is scrolled after posts
165 // are added or updated
165 // are added or updated
166 var bottom = isPageBottom();
166 var bottom = isPageBottom();
167
167
168 var post = $(postHtml);
168 var post = $(postHtml);
169
169
170 var threadBlock = $('div.thread');
170 var threadBlock = $('div.thread');
171
171
172 var postId = post.attr('id');
172 var postId = post.attr('id');
173
173
174 // If the post already exists, replace it. Otherwise add as a new one.
174 // If the post already exists, replace it. Otherwise add as a new one.
175 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
175 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
176
176
177 var type;
177 var type;
178
178
179 if (existingPosts.size() > 0) {
179 if (existingPosts.size() > 0) {
180 replacePartial(existingPosts.first(), post, false);
180 replacePartial(existingPosts.first(), post, false);
181 post = existingPosts.first();
181 post = existingPosts.first();
182
182
183 type = POST_UPDATED;
183 type = POST_UPDATED;
184 } else {
184 } else {
185 post.appendTo(threadBlock);
185 post.appendTo(threadBlock);
186
186
187 if (bottom) {
187 if (bottom) {
188 scrollToBottom();
188 scrollToBottom();
189 }
189 }
190
190
191 type = POST_ADDED;
191 type = POST_ADDED;
192 }
192 }
193
193
194 processNewPost(post);
194 processNewPost(post);
195
195
196 return type;
196 return type;
197 }
197 }
198
198
199 /**
199 /**
200 * Initiate a blinking animation on a node to show it was updated.
200 * Initiate a blinking animation on a node to show it was updated.
201 */
201 */
202 function blink(node) {
202 function blink(node) {
203 node.effect('highlight', { color: blinkColor }, BLINK_SPEED);
203 node.effect('highlight', { color: blinkColor }, BLINK_SPEED);
204 }
204 }
205
205
206 function isPageBottom() {
206 function isPageBottom() {
207 var scroll = $(window).scrollTop() / ($(document).height()
207 var scroll = $(window).scrollTop() / ($(document).height()
208 - $(window).height());
208 - $(window).height());
209
209
210 return scroll == 1
210 return scroll == 1
211 }
211 }
212
212
213 function enableJsUpdate() {
213 function enableJsUpdate() {
214 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
214 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
215 return true;
215 return true;
216 }
216 }
217
217
218 function initAutoupdate() {
218 function initAutoupdate() {
219 if (location.protocol === 'https:') {
219 if (location.protocol === 'https:') {
220 return enableJsUpdate();
220 return enableJsUpdate();
221 } else {
221 } else {
222 if (connectWebsocket()) {
222 if (connectWebsocket()) {
223 return true;
223 return true;
224 } else {
224 } else {
225 return enableJsUpdate();
225 return enableJsUpdate();
226 }
226 }
227 }
227 }
228 }
228 }
229
229
230 function getReplyCount() {
230 function getReplyCount() {
231 return $('.thread').children(CLASS_POST).length
231 return $('.thread').children(CLASS_POST).length
232 }
232 }
233
233
234 function getImageCount() {
234 function getImageCount() {
235 return $('.thread').find('img').length
235 return $('.thread').find('img').length
236 }
236 }
237
237
238 /**
238 /**
239 * Update post count, images count and last update time in the metadata
239 * Update post count, images count and last update time in the metadata
240 * panel.
240 * panel.
241 */
241 */
242 function updateMetadataPanel() {
242 function updateMetadataPanel() {
243 var replyCountField = $('#reply-count');
243 var replyCountField = $('#reply-count');
244 var imageCountField = $('#image-count');
244 var imageCountField = $('#image-count');
245
245
246 var replyCount = getReplyCount();
246 var replyCount = getReplyCount();
247 replyCountField.text(replyCount);
247 replyCountField.text(replyCount);
248 var imageCount = getImageCount();
248 var imageCount = getImageCount();
249 imageCountField.text(imageCount);
249 imageCountField.text(imageCount);
250
250
251 var lastUpdate = $('.post:last').children('.post-info').first()
251 var lastUpdate = $('.post:last').children('.post-info').first()
252 .children('.pub_time').first().html();
252 .children('.pub_time').first().html();
253 if (lastUpdate !== '') {
253 if (lastUpdate !== '') {
254 var lastUpdateField = $('#last-update');
254 var lastUpdateField = $('#last-update');
255 lastUpdateField.html(lastUpdate);
255 lastUpdateField.html(lastUpdate);
256 blink(lastUpdateField);
256 blink(lastUpdateField);
257 }
257 }
258
258
259 blink(replyCountField);
259 blink(replyCountField);
260 blink(imageCountField);
260 blink(imageCountField);
261
261
262 $('#message-count-text').text(ngettext('message', 'messages', replyCount));
262 $('#message-count-text').text(ngettext('message', 'messages', replyCount));
263 $('#image-count-text').text(ngettext('image', 'images', imageCount));
263 $('#image-count-text').text(ngettext('image', 'images', imageCount));
264 }
264 }
265
265
266 /**
266 /**
267 * Update bumplimit progress bar
267 * Update bumplimit progress bar
268 */
268 */
269 function updateBumplimitProgress(postDelta) {
269 function updateBumplimitProgress(postDelta) {
270 var progressBar = $('#bumplimit_progress');
270 var progressBar = $('#bumplimit_progress');
271 if (progressBar) {
271 if (progressBar) {
272 var postsToLimitElement = $('#left_to_limit');
272 var postsToLimitElement = $('#left_to_limit');
273
273
274 var oldPostsToLimit = parseInt(postsToLimitElement.text());
274 var oldPostsToLimit = parseInt(postsToLimitElement.text());
275 var postCount = getReplyCount();
275 var postCount = getReplyCount();
276 var bumplimit = postCount - postDelta + oldPostsToLimit;
276 var bumplimit = postCount - postDelta + oldPostsToLimit;
277
277
278 var newPostsToLimit = bumplimit - postCount;
278 var newPostsToLimit = bumplimit - postCount;
279 if (newPostsToLimit <= 0) {
279 if (newPostsToLimit <= 0) {
280 $('.bar-bg').remove();
280 $('.bar-bg').remove();
281 } else {
281 } else {
282 postsToLimitElement.text(newPostsToLimit);
282 postsToLimitElement.text(newPostsToLimit);
283 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
283 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
284 }
284 }
285 }
285 }
286 }
286 }
287
287
288 /**
288 /**
289 * Show 'new posts' text in the title if the document is not visible to a user
289 * Show 'new posts' text in the title if the document is not visible to a user
290 */
290 */
291 function showNewPostsTitle(newPostCount) {
291 function showNewPostsTitle(newPostCount) {
292 if (document.hidden) {
292 if (document.hidden) {
293 if (documentOriginalTitle === '') {
293 if (documentOriginalTitle === '') {
294 documentOriginalTitle = document.title;
294 documentOriginalTitle = document.title;
295 }
295 }
296 unreadPosts = unreadPosts + newPostCount;
296 unreadPosts = unreadPosts + newPostCount;
297
297
298 var newTitle = '* ';
298 var newTitle = null;
299 if (unreadPosts > 0) {
299 if (unreadPosts > 0) {
300 newTitle += '[' + unreadPosts + '] ';
300 newTitle = '[' + unreadPosts + '] ';
301 } else {
302 newTitle = '* ';
301 }
303 }
302 newTitle += documentOriginalTitle;
304 newTitle += documentOriginalTitle;
303
305
304 document.title = newTitle;
306 document.title = newTitle;
305
307
306 document.addEventListener('visibilitychange', function() {
308 document.addEventListener('visibilitychange', function() {
307 if (documentOriginalTitle !== '') {
309 if (documentOriginalTitle !== '') {
308 document.title = documentOriginalTitle;
310 document.title = documentOriginalTitle;
309 documentOriginalTitle = '';
311 documentOriginalTitle = '';
310 unreadPosts = 0;
312 unreadPosts = 0;
311 }
313 }
312
314
313 document.removeEventListener('visibilitychange', null);
315 document.removeEventListener('visibilitychange', null);
314 });
316 });
315 }
317 }
316 }
318 }
317
319
318 /**
320 /**
319 * Clear all entered values in the form fields
321 * Clear all entered values in the form fields
320 */
322 */
321 function resetForm(form) {
323 function resetForm(form) {
322 form.find('input:text, input:password, input:file, select, textarea').val('');
324 form.find('input:text, input:password, input:file, select, textarea').val('');
323 form.find('input:radio, input:checkbox')
325 form.find('input:radio, input:checkbox')
324 .removeAttr('checked').removeAttr('selected');
326 .removeAttr('checked').removeAttr('selected');
325 $('.file_wrap').find('.file-thumb').remove();
327 $('.file_wrap').find('.file-thumb').remove();
326 $('#preview-text').hide();
328 $('#preview-text').hide();
327 }
329 }
328
330
329 /**
331 /**
330 * When the form is posted, this method will be run as a callback
332 * When the form is posted, this method will be run as a callback
331 */
333 */
332 function updateOnPost(response, statusText, xhr, form) {
334 function updateOnPost(response, statusText, xhr, form) {
333 var json = $.parseJSON(response);
335 var json = $.parseJSON(response);
334 var status = json.status;
336 var status = json.status;
335
337
336 showAsErrors(form, '');
338 showAsErrors(form, '');
337
339
338 if (status === 'ok') {
340 if (status === 'ok') {
339 resetFormPosition();
341 resetFormPosition();
340 resetForm(form);
342 resetForm(form);
341 getThreadDiff();
343 getThreadDiff();
342 scrollToBottom();
344 scrollToBottom();
343 } else {
345 } else {
344 var errors = json.errors;
346 var errors = json.errors;
345 for (var i = 0; i < errors.length; i++) {
347 for (var i = 0; i < errors.length; i++) {
346 var fieldErrors = errors[i];
348 var fieldErrors = errors[i];
347
349
348 var error = fieldErrors.errors;
350 var error = fieldErrors.errors;
349
351
350 showAsErrors(form, error);
352 showAsErrors(form, error);
351 }
353 }
352 }
354 }
353 }
355 }
354
356
355
357
356 /**
358 /**
357 * Run js methods that are usually run on the document, on the new post
359 * Run js methods that are usually run on the document, on the new post
358 */
360 */
359 function processNewPost(post) {
361 function processNewPost(post) {
360 addScriptsToPost(post);
362 addScriptsToPost(post);
361 blink(post);
363 blink(post);
362 }
364 }
363
365
364 function replacePartial(oldNode, newNode, recursive) {
366 function replacePartial(oldNode, newNode, recursive) {
365 if (!equalNodes(oldNode, newNode)) {
367 if (!equalNodes(oldNode, newNode)) {
366 // Update parent node attributes
368 // Update parent node attributes
367 updateNodeAttr(oldNode, newNode, ATTR_CLASS);
369 updateNodeAttr(oldNode, newNode, ATTR_CLASS);
368 updateNodeAttr(oldNode, newNode, ATTR_UID);
370 updateNodeAttr(oldNode, newNode, ATTR_UID);
369
371
370 // Replace children
372 // Replace children
371 var children = oldNode.children();
373 var children = oldNode.children();
372 if (children.length == 0) {
374 if (children.length == 0) {
373 oldNode.replaceWith(newNode);
375 oldNode.replaceWith(newNode);
374 } else {
376 } else {
375 var newChildren = newNode.children();
377 var newChildren = newNode.children();
376 newChildren.each(function(i) {
378 newChildren.each(function(i) {
377 var newChild = newChildren.eq(i);
379 var newChild = newChildren.eq(i);
378 var newChildClass = newChild.attr(ATTR_CLASS);
380 var newChildClass = newChild.attr(ATTR_CLASS);
379
381
380 // Update only certain allowed blocks (e.g. not images)
382 // Update only certain allowed blocks (e.g. not images)
381 if (ALLOWED_FOR_PARTIAL_UPDATE.indexOf(newChildClass) > -1) {
383 if (ALLOWED_FOR_PARTIAL_UPDATE.indexOf(newChildClass) > -1) {
382 var oldChild = oldNode.children('.' + newChildClass);
384 var oldChild = oldNode.children('.' + newChildClass);
383
385
384 if (oldChild.length == 0) {
386 if (oldChild.length == 0) {
385 oldNode.append(newChild);
387 oldNode.append(newChild);
386 } else {
388 } else {
387 if (!equalNodes(oldChild, newChild)) {
389 if (!equalNodes(oldChild, newChild)) {
388 if (recursive) {
390 if (recursive) {
389 replacePartial(oldChild, newChild, false);
391 replacePartial(oldChild, newChild, false);
390 } else {
392 } else {
391 oldChild.replaceWith(newChild);
393 oldChild.replaceWith(newChild);
392 }
394 }
393 }
395 }
394 }
396 }
395 }
397 }
396 });
398 });
397 }
399 }
398 }
400 }
399 }
401 }
400
402
401 /**
403 /**
402 * Compare nodes by content
404 * Compare nodes by content
403 */
405 */
404 function equalNodes(node1, node2) {
406 function equalNodes(node1, node2) {
405 return node1[0].outerHTML == node2[0].outerHTML;
407 return node1[0].outerHTML == node2[0].outerHTML;
406 }
408 }
407
409
408 /**
410 /**
409 * Update attribute of a node if it has changed
411 * Update attribute of a node if it has changed
410 */
412 */
411 function updateNodeAttr(oldNode, newNode, attrName) {
413 function updateNodeAttr(oldNode, newNode, attrName) {
412 var oldAttr = oldNode.attr(attrName);
414 var oldAttr = oldNode.attr(attrName);
413 var newAttr = newNode.attr(attrName);
415 var newAttr = newNode.attr(attrName);
414 if (oldAttr != newAttr) {
416 if (oldAttr != newAttr) {
415 oldNode.attr(attrName, newAttr);
417 oldNode.attr(attrName, newAttr);
416 }
418 }
417 }
419 }
418
420
419 $(document).ready(function() {
421 $(document).ready(function() {
420 if (initAutoupdate()) {
422 if (initAutoupdate()) {
421 // Post form data over AJAX
423 // Post form data over AJAX
422 var threadId = $('div.thread').children('.post').first().attr('id');
424 var threadId = $('div.thread').children('.post').first().attr('id');
423
425
424 var form = $('#form');
426 var form = $('#form');
425
427
426 if (form.length > 0) {
428 if (form.length > 0) {
427 var options = {
429 var options = {
428 beforeSubmit: function(arr, form, options) {
430 beforeSubmit: function(arr, form, options) {
429 showAsErrors(form, gettext('Sending message...'));
431 showAsErrors(form, gettext('Sending message...'));
430 },
432 },
431 success: updateOnPost,
433 success: updateOnPost,
432 error: function() {
434 error: function() {
433 showAsErrors(form, gettext('Server error!'));
435 showAsErrors(form, gettext('Server error!'));
434 },
436 },
435 url: '/api/add_post/' + threadId + '/',
437 url: '/api/add_post/' + threadId + '/',
436 timeout: POST_AJAX_TIMEOUT
438 timeout: POST_AJAX_TIMEOUT
437 };
439 };
438
440
439 form.ajaxForm(options);
441 form.ajaxForm(options);
440
442
441 resetForm(form);
443 resetForm(form);
442 }
444 }
443 }
445 }
444 });
446 });
General Comments 0
You need to be logged in to leave comments. Login now