##// END OF EJS Templates
Updated "preview" button style
neko259 -
r1313:309c5363 default
parent child Browse files
Show More
@@ -1,551 +1,549 b''
1 1 * {
2 2 text-decoration: none;
3 3 font-weight: inherit;
4 4 }
5 5
6 6 b, strong {
7 7 font-weight: bold;
8 8 }
9 9
10 10 html {
11 11 background: #555;
12 12 color: #ffffff;
13 13 }
14 14
15 15 body {
16 16 margin: 0;
17 17 }
18 18
19 19 #admin_panel {
20 20 background: #FF0000;
21 21 color: #00FF00
22 22 }
23 23
24 24 .input_field_error {
25 25 color: #FF0000;
26 26 }
27 27
28 28 .title {
29 29 font-weight: bold;
30 30 color: #ffcc00;
31 31 }
32 32
33 33 .link, a {
34 34 color: #afdcec;
35 35 }
36 36
37 37 .block {
38 38 display: inline-block;
39 39 vertical-align: top;
40 40 }
41 41
42 42 .tag {
43 43 color: #FFD37D;
44 44 }
45 45
46 46 .post_id {
47 47 color: #fff380;
48 48 }
49 49
50 50 .post, .dead_post, .archive_post, #posts-table {
51 51 background: #333;
52 52 padding: 10px;
53 53 clear: left;
54 54 word-wrap: break-word;
55 55 border-top: 1px solid #777;
56 56 border-bottom: 1px solid #777;
57 57 }
58 58
59 59 .post + .post {
60 60 border-top: none;
61 61 }
62 62
63 63 .dead_post + .dead_post {
64 64 border-top: none;
65 65 }
66 66
67 67 .archive_post + .archive_post {
68 68 border-top: none;
69 69 }
70 70
71 71 .metadata {
72 72 padding-top: 5px;
73 73 margin-top: 10px;
74 74 border-top: solid 1px #666;
75 75 color: #ddd;
76 76 }
77 77
78 78 .navigation_panel, .tag_info {
79 79 background: #222;
80 80 margin-bottom: 5px;
81 81 margin-top: 5px;
82 82 padding: 10px;
83 83 border-bottom: solid 1px #888;
84 84 border-top: solid 1px #888;
85 85 color: #eee;
86 86 }
87 87
88 88 .navigation_panel .link:first-child {
89 89 border-right: 1px solid #fff;
90 90 font-weight: bold;
91 91 margin-right: 1ex;
92 92 padding-right: 1ex;
93 93 }
94 94
95 95 .navigation_panel .right-link {
96 96 border-left: 1px solid #fff;
97 97 border-right: none;
98 98 float: right;
99 99 margin-left: 1ex;
100 100 margin-right: 0;
101 101 padding-left: 1ex;
102 102 padding-right: 0;
103 103 }
104 104
105 105 .navigation_panel .link {
106 106 font-weight: bold;
107 107 }
108 108
109 109 .navigation_panel::after, .post::after {
110 110 clear: both;
111 111 content: ".";
112 112 display: block;
113 113 height: 0;
114 114 line-height: 0;
115 115 visibility: hidden;
116 116 }
117 117
118 118 .header {
119 119 border-bottom: solid 2px #ccc;
120 120 margin-bottom: 5px;
121 121 border-top: none;
122 122 margin-top: 0;
123 123 }
124 124
125 125 .footer {
126 126 border-top: solid 2px #ccc;
127 127 margin-top: 5px;
128 128 border-bottom: none;
129 129 margin-bottom: 0;
130 130 }
131 131
132 132 p, .br {
133 133 margin-top: .5em;
134 134 margin-bottom: .5em;
135 135 }
136 136
137 137 .post-form-w {
138 138 background: #333344;
139 139 border-top: solid 1px #888;
140 140 border-bottom: solid 1px #888;
141 141 color: #fff;
142 142 padding: 10px;
143 143 margin-bottom: 5px;
144 144 margin-top: 5px;
145 145 }
146 146
147 147 .form-row {
148 148 width: 100%;
149 149 display: table-row;
150 150 }
151 151
152 152 .form-label {
153 153 padding: .25em 1ex .25em 0;
154 154 vertical-align: top;
155 155 display: table-cell;
156 156 }
157 157
158 158 .form-input {
159 159 padding: .25em 0;
160 160 width: 100%;
161 161 display: table-cell;
162 162 }
163 163
164 164 .form-errors {
165 165 font-weight: bolder;
166 166 vertical-align: middle;
167 167 display: table-cell;
168 168 }
169 169
170 170 .post-form input:not([name="image"]):not([type="checkbox"]):not([type="submit"]), .post-form textarea, .post-form select {
171 171 background: #333;
172 172 color: #fff;
173 173 border: solid 1px;
174 174 padding: 0;
175 175 font: medium sans-serif;
176 176 width: 100%;
177 177 }
178 178
179 179 .post-form textarea {
180 180 resize: vertical;
181 181 }
182 182
183 183 .form-submit {
184 184 display: table;
185 185 margin-bottom: 1ex;
186 186 margin-top: 1ex;
187 187 }
188 188
189 189 .form-title {
190 190 font-weight: bold;
191 191 font-size: 2ex;
192 192 margin-bottom: 0.5ex;
193 193 }
194 194
195 input[type="submit"] {
195 input[type="submit"], button {
196 196 background: #222;
197 197 border: solid 2px #fff;
198 198 color: #fff;
199 199 padding: 0.5ex;
200 200 }
201 201
202 202 input[type="submit"]:hover {
203 203 background: #060;
204 204 }
205 205
206 button:hover {
207 background: #006;
208 }
209
206 210 blockquote {
207 211 border-left: solid 2px;
208 212 padding-left: 5px;
209 213 color: #B1FB17;
210 214 margin: 0;
211 215 }
212 216
213 217 .post > .image {
214 218 float: left;
215 219 margin: 0 1ex .5ex 0;
216 220 min-width: 1px;
217 221 text-align: center;
218 222 display: table-row;
219 223 }
220 224
221 225 .post > .metadata {
222 226 clear: left;
223 227 }
224 228
225 229 .get {
226 230 font-weight: bold;
227 231 color: #d55;
228 232 }
229 233
230 234 * {
231 235 text-decoration: none;
232 236 }
233 237
234 238 .dead_post > .post-info {
235 239 font-style: italic;
236 240 }
237 241
238 242 .archive_post > .post-info {
239 243 text-decoration: line-through;
240 244 }
241 245
242 246 .mark_btn {
243 247 border: 1px solid;
244 248 padding: 2px 2ex;
245 249 display: inline-block;
246 250 margin: 0 5px 4px 0;
247 251 }
248 252
249 253 .mark_btn:hover {
250 254 background: #555;
251 255 }
252 256
253 257 .quote {
254 258 color: #92cf38;
255 259 font-style: italic;
256 260 }
257 261
258 262 .multiquote {
259 263 padding: 3px;
260 264 display: inline-block;
261 265 background: #222;
262 266 border-style: solid;
263 267 border-width: 1px 1px 1px 4px;
264 268 font-size: 0.9em;
265 269 }
266 270
267 271 .spoiler {
268 272 background: black;
269 273 color: black;
270 274 padding: 0 1ex 0 1ex;
271 275 }
272 276
273 277 .spoiler:hover {
274 278 color: #ddd;
275 279 }
276 280
277 281 .comment {
278 282 color: #eb2;
279 283 }
280 284
281 285 a:hover {
282 286 text-decoration: underline;
283 287 }
284 288
285 289 .last-replies {
286 290 margin-left: 3ex;
287 291 margin-right: 3ex;
288 292 border-left: solid 1px #777;
289 293 border-right: solid 1px #777;
290 294 }
291 295
292 296 .last-replies > .post:first-child {
293 297 border-top: none;
294 298 }
295 299
296 300 .thread {
297 301 margin-bottom: 3ex;
298 302 margin-top: 1ex;
299 303 }
300 304
301 305 .post:target {
302 306 border: solid 2px white;
303 307 }
304 308
305 309 pre{
306 310 white-space:pre-wrap
307 311 }
308 312
309 313 li {
310 314 list-style-position: inside;
311 315 }
312 316
313 317 .fancybox-skin {
314 318 position: relative;
315 319 background-color: #fff;
316 320 color: #ddd;
317 321 text-shadow: none;
318 322 }
319 323
320 324 .fancybox-image {
321 325 border: 1px solid black;
322 326 }
323 327
324 328 .image-mode-tab {
325 329 background: #444;
326 330 color: #eee;
327 331 margin-top: 5px;
328 332 padding: 5px;
329 333 border-top: 1px solid #888;
330 334 border-bottom: 1px solid #888;
331 335 }
332 336
333 337 .image-mode-tab > label {
334 338 margin: 0 1ex;
335 339 }
336 340
337 341 .image-mode-tab > label > input {
338 342 margin-right: .5ex;
339 343 }
340 344
341 345 #posts-table {
342 346 margin-top: 5px;
343 347 margin-bottom: 5px;
344 348 }
345 349
346 350 .tag_info > h2 {
347 351 margin: 0;
348 352 }
349 353
350 354 .post-info {
351 355 color: #ddd;
352 356 margin-bottom: 1ex;
353 357 }
354 358
355 359 .moderator_info {
356 360 color: #e99d41;
357 361 opacity: 0.4;
358 362 }
359 363
360 364 .moderator_info:hover {
361 365 opacity: 1;
362 366 }
363 367
364 368 .refmap {
365 369 font-size: 0.9em;
366 370 color: #ccc;
367 371 margin-top: 1em;
368 372 }
369 373
370 374 .fav {
371 375 color: yellow;
372 376 }
373 377
374 378 .not_fav {
375 379 color: #ccc;
376 380 }
377 381
378 382 .role {
379 383 text-decoration: underline;
380 384 }
381 385
382 386 .form-email {
383 387 display: none;
384 388 }
385 389
386 390 .bar-value {
387 391 background: rgba(50, 55, 164, 0.45);
388 392 font-size: 0.9em;
389 393 height: 1.5em;
390 394 }
391 395
392 396 .bar-bg {
393 397 position: relative;
394 398 border-top: solid 1px #888;
395 399 border-bottom: solid 1px #888;
396 400 margin-top: 5px;
397 401 overflow: hidden;
398 402 }
399 403
400 404 .bar-text {
401 405 padding: 2px;
402 406 position: absolute;
403 407 left: 0;
404 408 top: 0;
405 409 }
406 410
407 411 .page_link {
408 412 background: #444;
409 413 border-top: solid 1px #888;
410 414 border-bottom: solid 1px #888;
411 415 padding: 5px;
412 416 color: #eee;
413 417 font-size: 2ex;
414 418 }
415 419
416 420 .skipped_replies {
417 421 padding: 5px;
418 422 margin-left: 3ex;
419 423 margin-right: 3ex;
420 424 border-left: solid 1px #888;
421 425 border-right: solid 1px #888;
422 426 border-bottom: solid 1px #888;
423 427 background: #000;
424 428 }
425 429
426 430 .current_page {
427 431 padding: 2px;
428 432 background-color: #afdcec;
429 433 color: #000;
430 434 }
431 435
432 436 .current_mode {
433 437 font-weight: bold;
434 438 }
435 439
436 440 .gallery_image {
437 441 border: solid 1px;
438 442 padding: 0.5ex;
439 443 margin: 0.5ex;
440 444 text-align: center;
441 445 }
442 446
443 447 code {
444 448 border: dashed 1px #ccc;
445 449 background: #111;
446 450 padding: 2px;
447 451 font-size: 1.2em;
448 452 display: inline-block;
449 453 }
450 454
451 455 pre {
452 456 overflow: auto;
453 457 }
454 458
455 459 .img-full {
456 460 background: #222;
457 461 border: solid 1px white;
458 462 }
459 463
460 464 .tag_item {
461 465 display: inline-block;
462 466 }
463 467
464 468 #id_models li {
465 469 list-style: none;
466 470 }
467 471
468 472 #id_q {
469 473 margin-left: 1ex;
470 474 }
471 475
472 476 ul {
473 477 padding-left: 0px;
474 478 }
475 479
476 480 .quote-header {
477 481 border-bottom: 2px solid #ddd;
478 482 margin-bottom: 1ex;
479 483 padding-bottom: .5ex;
480 484 color: #ddd;
481 485 font-size: 1.2em;
482 486 }
483 487
484 488 /* Reflink preview */
485 489 .post_preview {
486 490 border-left: 1px solid #777;
487 491 border-right: 1px solid #777;
488 492 max-width: 600px;
489 493 }
490 494
491 495 /* Code highlighter */
492 496 .hljs {
493 497 color: #fff;
494 498 background: #000;
495 499 display: inline-block;
496 500 }
497 501
498 502 .hljs, .hljs-subst, .hljs-tag .hljs-title, .lisp .hljs-title, .clojure .hljs-built_in, .nginx .hljs-title {
499 503 color: #fff;
500 504 }
501 505
502 506 #up {
503 507 position: fixed;
504 508 bottom: 5px;
505 509 right: 5px;
506 510 border: 1px solid #777;
507 511 background: #000;
508 512 padding: 4px;
509 513 }
510 514
511 515 .user-cast {
512 516 border: solid #ffffff 1px;
513 517 padding: .2ex;
514 518 background: #152154;
515 519 color: #fff;
516 520 }
517 521
518 522 .highlight {
519 523 background: #222;
520 524 }
521 525
522 526 .post-button-form > button:hover {
523 527 text-decoration: underline;
524 528 }
525 529
526 530 .tree_reply > .post {
527 531 margin-top: 1ex;
528 532 border-left: solid 1px #777;
529 533 padding-right: 0;
530 534 }
531 535
532 536 #preview-text {
533 537 border: solid 1px white;
534 538 margin: 1ex 0 1ex 0;
535 539 padding: 1ex;
536 540 }
537 541
538 button {
539 border: 1px solid white;
540 margin-bottom: .5ex;
541 margin-top: .5ex;
542 }
543
544 542 .image-metadata {
545 543 font-style: italic;
546 544 font-size: 0.9em;
547 545 }
548 546
549 547 .tripcode {
550 548 color: white;
551 549 }
@@ -1,389 +1,411 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 JS_AUTOUPDATE_PERIOD = 20000;
32 32
33 33 var wsUser = '';
34 34
35 35 var unreadPosts = 0;
36 36 var documentOriginalTitle = '';
37 37
38 38 // Thread ID does not change, can be stored one time
39 39 var threadId = $('div.thread').children(CLASS_POST).first().attr('id');
40 40
41 41 /**
42 42 * Connect to websocket server and subscribe to thread updates. On any update we
43 43 * request a thread diff.
44 44 *
45 45 * @returns {boolean} true if connected, false otherwise
46 46 */
47 47 function connectWebsocket() {
48 48 var metapanel = $('.metapanel')[0];
49 49
50 50 var wsHost = metapanel.getAttribute('data-ws-host');
51 51 var wsPort = metapanel.getAttribute('data-ws-port');
52 52
53 53 if (wsHost.length > 0 && wsPort.length > 0) {
54 54 var centrifuge = new Centrifuge({
55 55 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
56 56 "project": metapanel.getAttribute('data-ws-project'),
57 57 "user": wsUser,
58 58 "timestamp": metapanel.getAttribute('data-ws-token-time'),
59 59 "token": metapanel.getAttribute('data-ws-token'),
60 60 "debug": false
61 61 });
62 62
63 63 centrifuge.on('error', function(error_message) {
64 64 console.log("Error connecting to websocket server.");
65 65 console.log(error_message);
66 66 console.log("Using javascript update instead.");
67 67
68 68 // If websockets don't work, enable JS update instead
69 69 enableJsUpdate()
70 70 });
71 71
72 72 centrifuge.on('connect', function() {
73 73 var channelName = 'thread:' + threadId;
74 74 centrifuge.subscribe(channelName, function(message) {
75 75 getThreadDiff();
76 76 });
77 77
78 78 // For the case we closed the browser and missed some updates
79 79 getThreadDiff();
80 80 $('#autoupdate').hide();
81 81 });
82 82
83 83 centrifuge.connect();
84 84
85 85 return true;
86 86 } else {
87 87 return false;
88 88 }
89 89 }
90 90
91 91 /**
92 92 * Get diff of the posts from the current thread timestamp.
93 93 * This is required if the browser was closed and some post updates were
94 94 * missed.
95 95 */
96 96 function getThreadDiff() {
97 97 var lastUpdateTime = $('.metapanel').attr('data-last-update');
98 98 var lastPostId = $('.post').last().attr('id');
99 99
100 100 var uids = '';
101 101 var posts = $('.post');
102 102 for (var i = 0; i < posts.length; i++) {
103 103 uids += posts[i].getAttribute('data-uid') + ' ';
104 104 }
105 105
106 106 var data = {
107 107 uids: uids,
108 108 thread: threadId
109 109 }
110 110
111 111 var diffUrl = '/api/diff_thread/';
112 112
113 113 $.post(diffUrl,
114 114 data,
115 115 function(data) {
116 116 var updatedPosts = data.updated;
117 117 var addedPostCount = 0;
118 118
119 119 for (var i = 0; i < updatedPosts.length; i++) {
120 120 var postText = updatedPosts[i];
121 121 var post = $(postText);
122 122
123 123 if (updatePost(post) == POST_ADDED) {
124 124 addedPostCount++;
125 125 }
126 126 }
127 127
128 128 var hasMetaUpdates = updatedPosts.length > 0;
129 129 if (hasMetaUpdates) {
130 130 updateMetadataPanel();
131 131 }
132 132
133 133 if (addedPostCount > 0) {
134 134 updateBumplimitProgress(addedPostCount);
135 135 }
136 136
137 137 if (updatedPosts.length > 0) {
138 138 showNewPostsTitle(addedPostCount);
139 139 }
140 140
141 141 // TODO Process removed posts if any
142 142 $('.metapanel').attr('data-last-update', data.last_update);
143 143 },
144 144 'json'
145 145 )
146 146 }
147 147
148 148 /**
149 149 * Add or update the post on html page.
150 150 */
151 151 function updatePost(postHtml) {
152 152 // This needs to be set on start because the page is scrolled after posts
153 153 // are added or updated
154 154 var bottom = isPageBottom();
155 155
156 156 var post = $(postHtml);
157 157
158 158 var threadBlock = $('div.thread');
159 159
160 160 var postId = post.attr('id');
161 161
162 162 // If the post already exists, replace it. Otherwise add as a new one.
163 163 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
164 164
165 165 var type;
166 166
167 167 if (existingPosts.size() > 0) {
168 existingPosts.replaceWith(post);
168 replacePartial(existingPosts.first(), post);
169 169
170 170 type = POST_UPDATED;
171 171 } else {
172 172 post.appendTo(threadBlock);
173 173
174 174 if (bottom) {
175 175 scrollToBottom();
176 176 }
177 177
178 178 type = POST_ADDED;
179 179 }
180 180
181 181 processNewPost(post);
182 182
183 183 return type;
184 184 }
185 185
186 186 /**
187 187 * Initiate a blinking animation on a node to show it was updated.
188 188 */
189 189 function blink(node) {
190 190 var blinkCount = 2;
191 191
192 192 var nodeToAnimate = node;
193 193 for (var i = 0; i < blinkCount; i++) {
194 194 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
195 195 }
196 196 }
197 197
198 198 function isPageBottom() {
199 199 var scroll = $(window).scrollTop() / ($(document).height()
200 200 - $(window).height());
201 201
202 202 return scroll == 1
203 203 }
204 204
205 205 function enableJsUpdate() {
206 206 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
207 207 return true;
208 208 }
209 209
210 210 function initAutoupdate() {
211 211 if (location.protocol === 'https:') {
212 212 return enableJsUpdate();
213 213 } else {
214 214 if (connectWebsocket()) {
215 215 return true;
216 216 } else {
217 217 return enableJsUpdate();
218 218 }
219 219 }
220 220 }
221 221
222 222 function getReplyCount() {
223 223 return $('.thread').children(CLASS_POST).length
224 224 }
225 225
226 226 function getImageCount() {
227 227 return $('.thread').find('img').length
228 228 }
229 229
230 230 /**
231 231 * Update post count, images count and last update time in the metadata
232 232 * panel.
233 233 */
234 234 function updateMetadataPanel() {
235 235 var replyCountField = $('#reply-count');
236 236 var imageCountField = $('#image-count');
237 237
238 238 replyCountField.text(getReplyCount());
239 239 imageCountField.text(getImageCount());
240 240
241 241 var lastUpdate = $('.post:last').children('.post-info').first()
242 242 .children('.pub_time').first().html();
243 243 if (lastUpdate !== '') {
244 244 var lastUpdateField = $('#last-update');
245 245 lastUpdateField.html(lastUpdate);
246 246 blink(lastUpdateField);
247 247 }
248 248
249 249 blink(replyCountField);
250 250 blink(imageCountField);
251 251 }
252 252
253 253 /**
254 254 * Update bumplimit progress bar
255 255 */
256 256 function updateBumplimitProgress(postDelta) {
257 257 var progressBar = $('#bumplimit_progress');
258 258 if (progressBar) {
259 259 var postsToLimitElement = $('#left_to_limit');
260 260
261 261 var oldPostsToLimit = parseInt(postsToLimitElement.text());
262 262 var postCount = getReplyCount();
263 263 var bumplimit = postCount - postDelta + oldPostsToLimit;
264 264
265 265 var newPostsToLimit = bumplimit - postCount;
266 266 if (newPostsToLimit <= 0) {
267 267 $('.bar-bg').remove();
268 268 } else {
269 269 postsToLimitElement.text(newPostsToLimit);
270 270 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
271 271 }
272 272 }
273 273 }
274 274
275 275 /**
276 276 * Show 'new posts' text in the title if the document is not visible to a user
277 277 */
278 278 function showNewPostsTitle(newPostCount) {
279 279 if (document.hidden) {
280 280 if (documentOriginalTitle === '') {
281 281 documentOriginalTitle = document.title;
282 282 }
283 283 unreadPosts = unreadPosts + newPostCount;
284 284
285 285 var newTitle = '* ';
286 286 if (unreadPosts > 0) {
287 287 newTitle += '[' + unreadPosts + '] ';
288 288 }
289 289 newTitle += documentOriginalTitle;
290 290
291 291 document.title = newTitle;
292 292
293 293 document.addEventListener('visibilitychange', function() {
294 294 if (documentOriginalTitle !== '') {
295 295 document.title = documentOriginalTitle;
296 296 documentOriginalTitle = '';
297 297 unreadPosts = 0;
298 298 }
299 299
300 300 document.removeEventListener('visibilitychange', null);
301 301 });
302 302 }
303 303 }
304 304
305 305 /**
306 306 * Clear all entered values in the form fields
307 307 */
308 308 function resetForm(form) {
309 309 form.find('input:text, input:password, input:file, select, textarea').val('');
310 310 form.find('input:radio, input:checkbox')
311 311 .removeAttr('checked').removeAttr('selected');
312 312 $('.file_wrap').find('.file-thumb').remove();
313 313 $('#preview-text').hide();
314 314 }
315 315
316 316 /**
317 317 * When the form is posted, this method will be run as a callback
318 318 */
319 319 function updateOnPost(response, statusText, xhr, form) {
320 320 var json = $.parseJSON(response);
321 321 var status = json.status;
322 322
323 323 showAsErrors(form, '');
324 324
325 325 if (status === 'ok') {
326 326 resetFormPosition();
327 327 resetForm(form);
328 328 getThreadDiff();
329 329 scrollToBottom();
330 330 } else {
331 331 var errors = json.errors;
332 332 for (var i = 0; i < errors.length; i++) {
333 333 var fieldErrors = errors[i];
334 334
335 335 var error = fieldErrors.errors;
336 336
337 337 showAsErrors(form, error);
338 338 }
339 339 }
340 340 }
341 341
342 342 /**
343 343 * Show text in the errors row of the form.
344 344 * @param form
345 345 * @param text
346 346 */
347 347 function showAsErrors(form, text) {
348 348 form.children('.form-errors').remove();
349 349
350 350 if (text.length > 0) {
351 351 var errorList = $('<div class="form-errors">' + text + '<div>');
352 352 errorList.appendTo(form);
353 353 }
354 354 }
355 355
356 356 /**
357 357 * Run js methods that are usually run on the document, on the new post
358 358 */
359 359 function processNewPost(post) {
360 360 addRefLinkPreview(post[0]);
361 361 highlightCode(post);
362 362 blink(post);
363 363 }
364 364
365 function replacePartial(oldNode, newNode) {
366 var oldContent = oldNode[0].outerHTML;
367 var newContent = newNode[0].outerHTML;
368
369 // TODO Handle different children sizes
370
371 if (oldContent != newContent) {
372 var children = oldNode.children();
373 if (children.length == 0) {
374 console.log(oldContent);
375 console.log(newContent)
376
377 oldNode.replaceWith(newNode);
378 } else {
379 var newChildren = newNode.children();
380 children.each(function(i) {
381 replacePartial(children.eq(i), newChildren.eq(i));
382 });
383 }
384 }
385 }
386
365 387 $(document).ready(function(){
366 388 if (initAutoupdate()) {
367 389 // Post form data over AJAX
368 390 var threadId = $('div.thread').children('.post').first().attr('id');
369 391
370 392 var form = $('#form');
371 393
372 394 if (form.length > 0) {
373 395 var options = {
374 396 beforeSubmit: function(arr, $form, options) {
375 397 showAsErrors($('form'), gettext('Sending message...'));
376 398 },
377 399 success: updateOnPost,
378 400 error: function() {
379 401 showAsErrors($('form'), gettext('Server error!'));
380 402 },
381 403 url: '/api/add_post/' + threadId + '/'
382 404 };
383 405
384 406 form.ajaxForm(options);
385 407
386 408 resetForm(form);
387 409 }
388 410 }
389 411 });
@@ -1,186 +1,186 b''
1 1 {% extends "boards/base.html" %}
2 2
3 3 {% load i18n %}
4 4 {% load board %}
5 5 {% load static %}
6 6 {% load tz %}
7 7
8 8 {% block head %}
9 9 <meta name="robots" content="noindex">
10 10
11 11 {% if tag %}
12 12 <title>{{ tag.name }} - {{ site_name }}</title>
13 13 {% else %}
14 14 <title>{{ site_name }}</title>
15 15 {% endif %}
16 16
17 17 {% if prev_page_link %}
18 18 <link rel="prev" href="{{ prev_page_link }}" />
19 19 {% endif %}
20 20 {% if next_page_link %}
21 21 <link rel="next" href="{{ next_page_link }}" />
22 22 {% endif %}
23 23
24 24 {% endblock %}
25 25
26 26 {% block content %}
27 27
28 28 {% get_current_language as LANGUAGE_CODE %}
29 29 {% get_current_timezone as TIME_ZONE %}
30 30
31 31 {% for banner in banners %}
32 32 <div class="post">
33 33 <div class="title">{{ banner.title }}</div>
34 34 <div>{{ banner.text }}</div>
35 35 <div>{% trans 'Related message' %}: <a href="{{ banner.post.get_absolute_url }}">>>{{ banner.post.id }}</a></div>
36 36 </div>
37 37 {% endfor %}
38 38
39 39 {% if tag %}
40 40 <div class="tag_info">
41 41 {% if random_image_post %}
42 42 <div class="tag-image">
43 43 {% with image=random_image_post.images.first %}
44 44 <a href="{{ random_image_post.get_absolute_url }}"><img
45 45 src="{{ image.image.url_200x150 }}"
46 46 width="{{ image.pre_width }}"
47 47 height="{{ image.pre_height }}"/></a>
48 48 {% endwith %}
49 49 </div>
50 50 {% endif %}
51 51 <div class="tag-text-data">
52 52 <h2>
53 53 <form action="{% url 'tag' tag.name %}" method="post" class="post-button-form">
54 54 {% if is_favorite %}
55 55 <button name="method" value="unsubscribe" class="fav">β˜…</button>
56 56 {% else %}
57 57 <button name="method" value="subscribe" class="not_fav">β˜…</button>
58 58 {% endif %}
59 59 </form>
60 60 <form action="{% url 'tag' tag.name %}" method="post" class="post-button-form">
61 61 {% if is_hidden %}
62 62 <button name="method" value="unhide" class="fav">H</button>
63 63 {% else %}
64 64 <button name="method" value="hide" class="not_fav">H</button>
65 65 {% endif %}
66 66 </form>
67 67 {{ tag.get_view|safe }}
68 68 {% if moderator %}
69 69 <span class="moderator_info">| <a href="{% url 'admin:boards_tag_change' tag.id %}">{% trans 'Edit tag' %}</a></span>
70 70 {% endif %}
71 71 </h2>
72 72 {% if tag.get_description %}
73 73 <p>{{ tag.get_description|safe }}</p>
74 74 {% endif %}
75 75 <p>{% blocktrans with active_thread_count=tag.get_active_thread_count thread_count=tag.get_thread_count post_count=tag.get_post_count %}This tag has {{ thread_count }} threads ({{ active_thread_count}} active) and {{ post_count }} posts.{% endblocktrans %}</p>
76 76 {% if related_tags %}
77 77 <p>{% trans 'Related tags:' %}
78 78 {% for rel_tag in related_tags %}
79 79 {{ rel_tag.get_view|safe }}{% if not forloop.last %}, {% else %}.{% endif %}
80 80 {% endfor %}
81 81 </p>
82 82 {% endif %}
83 83 </div>
84 84 </div>
85 85 {% endif %}
86 86
87 87 {% if threads %}
88 88 {% if prev_page_link %}
89 89 <div class="page_link">
90 90 <a href="{{ prev_page_link }}">{% trans "Previous page" %}</a>
91 91 </div>
92 92 {% endif %}
93 93
94 94 {% for thread in threads %}
95 95 <div class="thread">
96 96 {% post_view thread.get_opening_post moderator=moderator is_opening=True thread=thread truncated=True need_open_link=True %}
97 97 {% if not thread.archived %}
98 98 {% with last_replies=thread.get_last_replies %}
99 99 {% if last_replies %}
100 100 {% with skipped_replies_count=thread.get_skipped_replies_count %}
101 101 {% if skipped_replies_count %}
102 102 <div class="skipped_replies">
103 103 <a href="{% url 'thread' thread.get_opening_post_id %}">
104 104 {% blocktrans with count=skipped_replies_count %}Skipped {{ count }} replies. Open thread to see all replies.{% endblocktrans %}
105 105 </a>
106 106 </div>
107 107 {% endif %}
108 108 {% endwith %}
109 109 <div class="last-replies">
110 110 {% for post in last_replies %}
111 111 {% post_view post is_opening=False moderator=moderator truncated=True %}
112 112 {% endfor %}
113 113 </div>
114 114 {% endif %}
115 115 {% endwith %}
116 116 {% endif %}
117 117 </div>
118 118 {% endfor %}
119 119
120 120 {% if next_page_link %}
121 121 <div class="page_link">
122 122 <a href="{{ next_page_link }}">{% trans "Next page" %}</a>
123 123 </div>
124 124 {% endif %}
125 125 {% else %}
126 126 <div class="post">
127 127 {% trans 'No threads exist. Create the first one!' %}</div>
128 128 {% endif %}
129 129
130 130 <div class="post-form-w">
131 131 <script src="{% static 'js/panel.js' %}"></script>
132 132 <div class="post-form">
133 133 <div class="form-title">{% trans "Create new thread" %}</div>
134 134 <div class="swappable-form-full">
135 135 <form enctype="multipart/form-data" method="post" id="form">{% csrf_token %}
136 136 {{ form.as_div }}
137 137 <div class="form-submit">
138 138 <input type="submit" value="{% trans "Post" %}"/>
139 <button id="preview-button" onclick="return false;">{% trans 'Preview' %}</button>
139 140 </div>
140 141 </form>
141 142 </div>
142 143 <div>
143 144 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
144 145 </div>
145 <div><button id="preview-button">{% trans 'Preview' %}</button></div>
146 146 <div id="preview-text"></div>
147 147 <div><a href="{% url "staticpage" name="help" %}">{% trans 'Text syntax' %}</a></div>
148 148 <div><a href="{% url "tags" "required" %}">{% trans 'Tags' %}</a></div>
149 149 </div>
150 150 </div>
151 151
152 152 <script src="{% static 'js/form.js' %}"></script>
153 153 <script src="{% static 'js/thread_create.js' %}"></script>
154 154
155 155 {% endblock %}
156 156
157 157 {% block metapanel %}
158 158
159 159 <span class="metapanel">
160 160 <b><a href="{% url "authors" %}">{{ site_name }}</a> {{ version }}</b>
161 161 {% trans "Pages:" %}
162 162 [
163 163 {% with dividers=paginator.get_dividers %}
164 164 {% for page in paginator.get_divided_range %}
165 165 {% if page in dividers %}
166 166 …,
167 167 {% endif %}
168 168 <a
169 169 {% ifequal page current_page.number %}
170 170 class="current_page"
171 171 {% endifequal %}
172 172 href="
173 173 {% if tag %}
174 174 {% url "tag" tag_name=tag.name %}?page={{ page }}
175 175 {% else %}
176 176 {% url "index" %}?page={{ page }}
177 177 {% endif %}
178 178 ">{{ page }}</a>
179 179 {% if not forloop.last %},{% endif %}
180 180 {% endfor %}
181 181 {% endwith %}
182 182 ]
183 183 [<a href="rss/">RSS</a>]
184 184 </span>
185 185
186 186 {% endblock %}
@@ -1,57 +1,57 b''
1 1 {% extends "boards/thread.html" %}
2 2
3 3 {% load i18n %}
4 4 {% load static from staticfiles %}
5 5 {% load board %}
6 6 {% load tz %}
7 7
8 8 {% block thread_content %}
9 9 {% get_current_language as LANGUAGE_CODE %}
10 10 {% get_current_timezone as TIME_ZONE %}
11 11
12 12 {% if bumpable and thread.has_post_limit %}
13 13 <div class="bar-bg">
14 14 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
15 15 </div>
16 16 <div class="bar-text">
17 17 <span id="left_to_limit">{{ posts_left }}</span> {% trans 'posts to bumplimit' %}
18 18 </div>
19 19 </div>
20 20 {% endif %}
21 21
22 22 <div class="thread">
23 23 {% for post in thread.get_replies %}
24 24 {% post_view post moderator=moderator reply_link=True %}
25 25 {% endfor %}
26 26 </div>
27 27
28 28 {% if not thread.archived %}
29 29 <div class="post-form-w">
30 30 <script src="{% static 'js/panel.js' %}"></script>
31 31 <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>
32 32 <div class="post-form" id="compact-form">
33 33 <div class="swappable-form-full">
34 34 <form enctype="multipart/form-data" method="post" id="form">{% csrf_token %}
35 35 <div class="compact-form-text"></div>
36 36 {{ form.as_div }}
37 37 <div class="form-submit">
38 38 <input type="submit" value="{% trans "Post" %}"/>
39 <button id="preview-button" onclick="return false;">{% trans 'Preview' %}</button>
39 40 </div>
40 41 </form>
41 42 </div>
42 <div><button id="preview-button">{% trans 'Preview' %}</button></div>
43 43 <div id="preview-text"></div>
44 44 <div><a href="{% url "staticpage" name="help" %}">
45 45 {% trans 'Text syntax' %}</a></div>
46 46 <div><a id="form-close-button" href="#" onClick="resetFormPosition(); return false;">{% trans 'Close form' %}</a></div>
47 47 </div>
48 48 </div>
49 49
50 50 <script src="{% static 'js/jquery.form.min.js' %}"></script>
51 51 {% endif %}
52 52
53 53 <script src="{% static 'js/form.js' %}"></script>
54 54 <script src="{% static 'js/thread.js' %}"></script>
55 55 <script src="{% static 'js/thread_update.js' %}"></script>
56 56 <script src="{% static 'js/3party/centrifuge.js' %}"></script>
57 57 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now