##// END OF EJS Templates
Pluralize thread info inside a thread
neko259 -
r1332:255bbeb2 default
parent child Browse files
Show More
1 NO CONTENT: modified file, binary diff hidden
@@ -1,469 +1,472 b''
1 1 # SOME DESCRIPTIVE TITLE.
2 2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 3 # This file is distributed under the same license as the PACKAGE package.
4 4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5 5 #
6 6 msgid ""
7 7 msgstr ""
8 8 "Project-Id-Version: PACKAGE VERSION\n"
9 9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2015-09-04 16:30+0300\n"
10 "POT-Creation-Date: 2015-09-04 19:00+0300\n"
11 11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 13 "Language-Team: LANGUAGE <LL@li.org>\n"
14 14 "Language: ru\n"
15 15 "MIME-Version: 1.0\n"
16 16 "Content-Type: text/plain; charset=UTF-8\n"
17 17 "Content-Transfer-Encoding: 8bit\n"
18 18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20 20
21 21 #: admin.py:22
22 22 msgid "{} posters were banned"
23 23 msgstr ""
24 24
25 25 #: authors.py:9
26 26 msgid "author"
27 27 msgstr "автор"
28 28
29 29 #: authors.py:10
30 30 msgid "developer"
31 31 msgstr "разработчик"
32 32
33 33 #: authors.py:11
34 34 msgid "javascript developer"
35 35 msgstr "разработчик javascript"
36 36
37 37 #: authors.py:12
38 38 msgid "designer"
39 39 msgstr "дизайнер"
40 40
41 41 #: forms.py:30
42 42 msgid "Type message here. Use formatting panel for more advanced usage."
43 43 msgstr ""
44 44 "Вводите сообщение сюда. Используйте панель для более сложного форматирования."
45 45
46 46 #: forms.py:31
47 47 msgid "music images i_dont_like_tags"
48 48 msgstr "музыка картинки теги_не_нужны"
49 49
50 50 #: forms.py:33
51 51 msgid "Title"
52 52 msgstr "Заголовок"
53 53
54 54 #: forms.py:34
55 55 msgid "Text"
56 56 msgstr "Текст"
57 57
58 58 #: forms.py:35
59 59 msgid "Tag"
60 60 msgstr "Метка"
61 61
62 62 #: forms.py:36 templates/boards/base.html:40 templates/search/search.html:7
63 63 msgid "Search"
64 64 msgstr "Поиск"
65 65
66 66 #: forms.py:38
67 67 #, python-format
68 68 msgid "Please wait %s seconds before sending message"
69 69 msgstr "Пожалуйста подождите %s секунд перед отправкой сообщения"
70 70
71 71 #: forms.py:138
72 72 msgid "File"
73 73 msgstr "Файл"
74 74
75 75 #: forms.py:141
76 76 msgid "File URL"
77 77 msgstr "URL файла"
78 78
79 79 #: forms.py:147
80 80 msgid "e-mail"
81 81 msgstr ""
82 82
83 83 #: forms.py:150
84 84 msgid "Additional threads"
85 85 msgstr "Дополнительные темы"
86 86
87 87 #: forms.py:161
88 88 #, python-format
89 89 msgid "Title must have less than %s characters"
90 90 msgstr "Заголовок должен иметь меньше %s символов"
91 91
92 92 #: forms.py:171
93 93 #, python-format
94 94 msgid "Text must have less than %s characters"
95 95 msgstr "Текст должен быть короче %s символов"
96 96
97 97 #: forms.py:191
98 98 msgid "Invalid URL"
99 99 msgstr "Неверный URL"
100 100
101 101 #: forms.py:212
102 102 msgid "Invalid additional thread list"
103 103 msgstr "Неверный список дополнительных тем"
104 104
105 105 #: forms.py:257
106 106 msgid "Either text or file must be entered."
107 107 msgstr "Текст или файл должны быть введены."
108 108
109 109 #: forms.py:316 templates/boards/all_threads.html:148
110 110 #: templates/boards/rss/post.html:10 templates/boards/tags.html:6
111 111 msgid "Tags"
112 112 msgstr "Метки"
113 113
114 114 #: forms.py:323
115 115 msgid "Inappropriate characters in tags."
116 116 msgstr "Недопустимые символы в метках."
117 117
118 118 #: forms.py:337
119 119 msgid "Need at least one section."
120 120 msgstr "Нужен хотя бы один раздел."
121 121
122 122 #: forms.py:349
123 123 msgid "Theme"
124 124 msgstr "Тема"
125 125
126 126 #: forms.py:350
127 127 msgid "Image view mode"
128 128 msgstr "Режим просмотра изображений"
129 129
130 130 #: forms.py:351
131 131 msgid "User name"
132 132 msgstr "Имя пользователя"
133 133
134 134 #: forms.py:352
135 135 msgid "Time zone"
136 136 msgstr "Часовой пояс"
137 137
138 138 #: forms.py:358
139 139 msgid "Inappropriate characters."
140 140 msgstr "Недопустимые символы."
141 141
142 142 #: templates/boards/404.html:6
143 143 msgid "Not found"
144 144 msgstr "Не найдено"
145 145
146 146 #: templates/boards/404.html:12
147 147 msgid "This page does not exist"
148 148 msgstr "Этой страницы не существует"
149 149
150 150 #: templates/boards/all_threads.html:35
151 151 msgid "Related message"
152 152 msgstr "Связанное сообщение"
153 153
154 154 #: templates/boards/all_threads.html:69
155 155 msgid "Edit tag"
156 156 msgstr "Изменить метку"
157 157
158 158 #: templates/boards/all_threads.html:75
159 159 #, python-format
160 160 msgid ""
161 161 "This tag has %(thread_count)s threads (%(active_thread_count)s active) and "
162 162 "%(post_count)s posts."
163 163 msgstr ""
164 164 "С этой меткой есть %(thread_count)s тем (%(active_thread_count)s активных) и "
165 165 "%(post_count)s сообщений."
166 166
167 167 #: templates/boards/all_threads.html:77
168 168 msgid "Related tags:"
169 169 msgstr "Похожие метки:"
170 170
171 171 #: templates/boards/all_threads.html:90 templates/boards/feed.html:30
172 172 #: templates/boards/notifications.html:17 templates/search/search.html:26
173 173 msgid "Previous page"
174 174 msgstr "Предыдущая страница"
175 175
176 176 #: templates/boards/all_threads.html:104
177 177 #, python-format
178 178 msgid "Skipped %(count)s replies. Open thread to see all replies."
179 179 msgstr "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы."
180 180
181 181 #: templates/boards/all_threads.html:122 templates/boards/feed.html:40
182 182 #: templates/boards/notifications.html:27 templates/search/search.html:37
183 183 msgid "Next page"
184 184 msgstr "Следующая страница"
185 185
186 186 #: templates/boards/all_threads.html:127
187 187 msgid "No threads exist. Create the first one!"
188 188 msgstr "Нет тем. Создайте первую!"
189 189
190 190 #: templates/boards/all_threads.html:133
191 191 msgid "Create new thread"
192 192 msgstr "Создать новую тему"
193 193
194 194 #: templates/boards/all_threads.html:138 templates/boards/preview.html:16
195 195 #: templates/boards/thread_normal.html:51
196 196 msgid "Post"
197 197 msgstr "Отправить"
198 198
199 199 #: templates/boards/all_threads.html:139 templates/boards/preview.html:6
200 200 #: templates/boards/staticpages/help.html:21
201 201 #: templates/boards/thread_normal.html:52
202 202 msgid "Preview"
203 203 msgstr "Предпросмотр"
204 204
205 205 #: templates/boards/all_threads.html:144
206 206 msgid "Tags must be delimited by spaces. Text or image is required."
207 207 msgstr ""
208 208 "Метки должны быть разделены пробелами. Текст или изображение обязательны."
209 209
210 210 #: templates/boards/all_threads.html:147 templates/boards/thread_normal.html:58
211 211 msgid "Text syntax"
212 212 msgstr "Синтаксис текста"
213 213
214 214 #: templates/boards/all_threads.html:161 templates/boards/feed.html:53
215 215 msgid "Pages:"
216 216 msgstr "Страницы: "
217 217
218 218 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
219 219 msgid "Authors"
220 220 msgstr "Авторы"
221 221
222 222 #: templates/boards/authors.html:26
223 223 msgid "Distributed under the"
224 224 msgstr "Распространяется под"
225 225
226 226 #: templates/boards/authors.html:28
227 227 msgid "license"
228 228 msgstr "лицензией"
229 229
230 230 #: templates/boards/authors.html:30
231 231 msgid "Repository"
232 232 msgstr "Репозиторий"
233 233
234 234 #: templates/boards/base.html:14 templates/boards/base.html.py:41
235 235 msgid "Feed"
236 236 msgstr "Лента"
237 237
238 238 #: templates/boards/base.html:31
239 239 msgid "All threads"
240 240 msgstr "Все темы"
241 241
242 242 #: templates/boards/base.html:37
243 243 msgid "Add tags"
244 244 msgstr "Добавить метки"
245 245
246 246 #: templates/boards/base.html:39
247 247 msgid "Tag management"
248 248 msgstr "Управление метками"
249 249
250 250 #: templates/boards/base.html:39
251 251 msgid "tags"
252 252 msgstr "метки"
253 253
254 254 #: templates/boards/base.html:40
255 255 msgid "search"
256 256 msgstr "поиск"
257 257
258 258 #: templates/boards/base.html:41 templates/boards/feed.html:11
259 259 msgid "feed"
260 260 msgstr "лента"
261 261
262 262 #: templates/boards/base.html:42 templates/boards/random.html:6
263 263 msgid "Random images"
264 264 msgstr "Случайные изображения"
265 265
266 266 #: templates/boards/base.html:42
267 267 msgid "random"
268 268 msgstr "случайные"
269 269
270 270 #: templates/boards/base.html:45 templates/boards/base.html.py:46
271 271 #: templates/boards/notifications.html:8
272 272 msgid "Notifications"
273 273 msgstr "Уведомления"
274 274
275 275 #: templates/boards/base.html:53 templates/boards/settings.html:8
276 276 msgid "Settings"
277 277 msgstr "Настройки"
278 278
279 279 #: templates/boards/base.html:79
280 280 msgid "Admin"
281 281 msgstr "Администрирование"
282 282
283 283 #: templates/boards/base.html:81
284 284 #, python-format
285 285 msgid "Speed: %(ppd)s posts per day"
286 286 msgstr "Скорость: %(ppd)s сообщений в день"
287 287
288 288 #: templates/boards/base.html:83
289 289 msgid "Up"
290 290 msgstr "Вверх"
291 291
292 292 #: templates/boards/feed.html:45
293 293 msgid "No posts exist. Create the first one!"
294 294 msgstr "Нет сообщений. Создайте первое!"
295 295
296 296 #: templates/boards/post.html:32
297 297 msgid "Open"
298 298 msgstr "Открыть"
299 299
300 300 #: templates/boards/post.html:34 templates/boards/post.html.py:45
301 301 msgid "Reply"
302 302 msgstr "Ответить"
303 303
304 304 #: templates/boards/post.html:40
305 305 msgid " in "
306 306 msgstr " в "
307 307
308 308 #: templates/boards/post.html:50
309 309 msgid "Edit"
310 310 msgstr "Изменить"
311 311
312 312 #: templates/boards/post.html:52
313 313 msgid "Edit thread"
314 314 msgstr "Изменить тему"
315 315
316 316 #: templates/boards/post.html:94
317 317 msgid "Replies"
318 318 msgstr "Ответы"
319 319
320 320 #: templates/boards/post.html:105
321 321 #, python-format
322 #| msgid "%(count)s message,"
323 #| msgid_plural "%(count)s messages,"
324 msgid "%(count)s message,"
322 msgid "%(count)s message"
325 323 msgid_plural "%(count)s messages"
326 324 msgstr[0] "%(count)s сообщение"
327 325 msgstr[1] "%(count)s сообщения"
328 326 msgstr[2] "%(count)s сообщений"
329 327
330 328 #: templates/boards/post.html:106
331 329 #, python-format
332 #| msgid "%(count)s message,"
333 #| msgid_plural "%(count)s messages,"
334 msgid "%(count)s image,"
330 msgid "%(count)s image"
335 331 msgid_plural "%(count)s images"
336 332 msgstr[0] "%(count)s изображение"
337 333 msgstr[1] "%(count)s изображения"
338 334 msgstr[2] "%(count)s изображений"
339 335
340 336 #: templates/boards/rss/post.html:5
341 337 msgid "Post image"
342 338 msgstr "Изображение сообщения"
343 339
344 340 #: templates/boards/settings.html:15
345 341 msgid "You are moderator."
346 342 msgstr "Вы модератор."
347 343
348 344 #: templates/boards/settings.html:19
349 345 msgid "Hidden tags:"
350 346 msgstr "Скрытые метки:"
351 347
352 348 #: templates/boards/settings.html:25
353 349 msgid "No hidden tags."
354 350 msgstr "Нет скрытых меток."
355 351
356 352 #: templates/boards/settings.html:34
357 353 msgid "Save"
358 354 msgstr "Сохранить"
359 355
360 356 #: templates/boards/staticpages/banned.html:6
361 357 msgid "Banned"
362 358 msgstr "Заблокирован"
363 359
364 360 #: templates/boards/staticpages/banned.html:11
365 361 msgid "Your IP address has been banned. Contact the administrator"
366 362 msgstr "Ваш IP адрес был заблокирован. Свяжитесь с администратором"
367 363
368 364 #: templates/boards/staticpages/help.html:6
369 365 #: templates/boards/staticpages/help.html:10
370 366 msgid "Syntax"
371 367 msgstr "Синтаксис"
372 368
373 369 #: templates/boards/staticpages/help.html:11
374 370 msgid "Italic text"
375 371 msgstr "Курсивный текст"
376 372
377 373 #: templates/boards/staticpages/help.html:12
378 374 msgid "Bold text"
379 375 msgstr "Полужирный текст"
380 376
381 377 #: templates/boards/staticpages/help.html:13
382 378 msgid "Spoiler"
383 379 msgstr "Спойлер"
384 380
385 381 #: templates/boards/staticpages/help.html:14
386 382 msgid "Link to a post"
387 383 msgstr "Ссылка на сообщение"
388 384
389 385 #: templates/boards/staticpages/help.html:15
390 386 msgid "Strikethrough text"
391 387 msgstr "Зачеркнутый текст"
392 388
393 389 #: templates/boards/staticpages/help.html:16
394 390 msgid "Comment"
395 391 msgstr "Комментарий"
396 392
397 393 #: templates/boards/staticpages/help.html:17
398 394 #: templates/boards/staticpages/help.html:18
399 395 msgid "Quote"
400 396 msgstr "Цитата"
401 397
402 398 #: templates/boards/staticpages/help.html:21
403 399 msgid "You can try pasting the text and previewing the result here:"
404 400 msgstr "Вы можете попробовать вставить текст и проверить результат здесь:"
405 401
406 402 #: templates/boards/tags.html:17
407 403 msgid "Sections:"
408 404 msgstr "Разделы:"
409 405
410 406 #: templates/boards/tags.html:30
411 407 msgid "Other tags:"
412 408 msgstr "Другие метки:"
413 409
414 410 #: templates/boards/tags.html:43
415 411 msgid "All tags..."
416 412 msgstr "Все метки..."
417 413
418 414 #: templates/boards/thread.html:15
419 415 msgid "Normal"
420 416 msgstr "Нормальный"
421 417
422 418 #: templates/boards/thread.html:16
423 419 msgid "Gallery"
424 420 msgstr "Галерея"
425 421
426 422 #: templates/boards/thread.html:17
427 423 msgid "Tree"
428 424 msgstr "Дерево"
429 425
430 #: templates/boards/thread.html:34
431 msgid "messages"
432 msgstr "сообщений"
426 #: templates/boards/thread.html:36
427 #| msgid "messages"
428 msgid "message"
429 msgid_plural "messages"
430 msgstr[0] "сообщение"
431 msgstr[1] "сообщения"
432 msgstr[2] "сообщений"
433 433
434 #: templates/boards/thread.html:35
435 msgid "images"
436 msgstr "изображений"
434 #: templates/boards/thread.html:39
435 msgid "image"
436 msgid_plural "images"
437 msgstr[0] "изображение"
438 msgstr[1] "изображения"
439 msgstr[2] "изображений"
437 440
438 #: templates/boards/thread.html:36
441 #: templates/boards/thread.html:41
439 442 msgid "Last update: "
440 443 msgstr "Последнее обновление: "
441 444
442 445 #: templates/boards/thread_gallery.html:36
443 446 msgid "No images."
444 447 msgstr "Нет изображений."
445 448
446 449 #: templates/boards/thread_normal.html:30
447 450 msgid "posts to bumplimit"
448 451 msgstr "сообщений до бамплимита"
449 452
450 453 #: templates/boards/thread_normal.html:44
451 454 msgid "Reply to thread"
452 455 msgstr "Ответить в тему"
453 456
454 457 #: templates/boards/thread_normal.html:44
455 458 msgid "to message "
456 459 msgstr "на сообщение"
457 460
458 461 #: templates/boards/thread_normal.html:59
459 462 msgid "Close form"
460 463 msgstr "Закрыть форму"
461 464
462 465 #: templates/search/search.html:17
463 466 msgid "Ok"
464 467 msgstr "Ок"
465 468
466 469 #: utils.py:102
467 470 #, python-format
468 471 msgid "File must be less than %s bytes"
469 472 msgstr "Файл должен быть менее %s байт"
1 NO CONTENT: modified file, binary diff hidden
@@ -1,32 +1,55 b''
1 1 # SOME DESCRIPTIVE TITLE.
2 2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 3 # This file is distributed under the same license as the PACKAGE package.
4 4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5 5 #
6 6 #, fuzzy
7 7 msgid ""
8 8 msgstr ""
9 9 "Project-Id-Version: PACKAGE VERSION\n"
10 10 "Report-Msgid-Bugs-To: \n"
11 "POT-Creation-Date: 2014-07-02 13:26+0300\n"
11 "POT-Creation-Date: 2015-09-04 18:47+0300\n"
12 12 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 14 "Language-Team: LANGUAGE <LL@li.org>\n"
15 15 "Language: \n"
16 16 "MIME-Version: 1.0\n"
17 17 "Content-Type: text/plain; charset=UTF-8\n"
18 18 "Content-Transfer-Encoding: 8bit\n"
19 19 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
20 20 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
21 21
22 #: static/js/refpopup.js:58
22 #: static/js/3party/jquery-ui.min.js:8
23 msgid "'"
24 msgstr ""
25
26 #: static/js/refpopup.js:72
23 27 msgid "Loading..."
24 28 msgstr "Загрузка..."
25 29
26 #: static/js/refpopup.js:77
30 #: static/js/refpopup.js:91
27 31 msgid "Post not found"
28 32 msgstr "Сообщение не найдено"
29 33
30 #: static/js/thread_update.js:279
34 #: static/js/thread_update.js:261
35 msgid "message"
36 msgid_plural "messages"
37 msgstr[0] "сообщение"
38 msgstr[1] "сообщения"
39 msgstr[2] "сообщений"
40
41 #: static/js/thread_update.js:262
42 msgid "image"
43 msgid_plural "images"
44 msgstr[0] "изображение"
45 msgstr[1] "изображения"
46 msgstr[2] "изображений"
47
48 #: static/js/thread_update.js:445
31 49 msgid "Sending message..."
32 msgstr "Отправка сообщения..." No newline at end of file
50 msgstr "Отправка сообщения..."
51
52 #: static/js/thread_update.js:449
53 msgid "Server error!"
54 msgstr "Ошибка сервера!"
55
@@ -1,456 +1,457 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 ALLOWED_FOR_PARTIAL_UPDATE = [
34 34 'refmap',
35 35 'post-info'
36 36 ];
37 37
38 38 var ATTR_CLASS = 'class';
39 39 var ATTR_UID = 'data-uid';
40 40
41 41 var wsUser = '';
42 42
43 43 var unreadPosts = 0;
44 44 var documentOriginalTitle = '';
45 45
46 46 // Thread ID does not change, can be stored one time
47 47 var threadId = $('div.thread').children(CLASS_POST).first().attr('id');
48 48
49 49 /**
50 50 * Connect to websocket server and subscribe to thread updates. On any update we
51 51 * request a thread diff.
52 52 *
53 53 * @returns {boolean} true if connected, false otherwise
54 54 */
55 55 function connectWebsocket() {
56 56 var metapanel = $('.metapanel')[0];
57 57
58 58 var wsHost = metapanel.getAttribute('data-ws-host');
59 59 var wsPort = metapanel.getAttribute('data-ws-port');
60 60
61 61 if (wsHost.length > 0 && wsPort.length > 0) {
62 62 var centrifuge = new Centrifuge({
63 63 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
64 64 "project": metapanel.getAttribute('data-ws-project'),
65 65 "user": wsUser,
66 66 "timestamp": metapanel.getAttribute('data-ws-token-time'),
67 67 "token": metapanel.getAttribute('data-ws-token'),
68 68 "debug": false
69 69 });
70 70
71 71 centrifuge.on('error', function(error_message) {
72 72 console.log("Error connecting to websocket server.");
73 73 console.log(error_message);
74 74 console.log("Using javascript update instead.");
75 75
76 76 // If websockets don't work, enable JS update instead
77 77 enableJsUpdate()
78 78 });
79 79
80 80 centrifuge.on('connect', function() {
81 81 var channelName = 'thread:' + threadId;
82 82 centrifuge.subscribe(channelName, function(message) {
83 83 getThreadDiff();
84 84 });
85 85
86 86 // For the case we closed the browser and missed some updates
87 87 getThreadDiff();
88 88 $('#autoupdate').hide();
89 89 });
90 90
91 91 centrifuge.connect();
92 92
93 93 return true;
94 94 } else {
95 95 return false;
96 96 }
97 97 }
98 98
99 99 /**
100 100 * Get diff of the posts from the current thread timestamp.
101 101 * This is required if the browser was closed and some post updates were
102 102 * missed.
103 103 */
104 104 function getThreadDiff() {
105 var lastUpdateTime = $('.metapanel').attr('data-last-update');
106 var lastPostId = $('.post').last().attr('id');
105 var all_posts = $('.post');
107 106
108 107 var uids = '';
109 var posts = $('.post');
108 var posts = all_posts;
110 109 for (var i = 0; i < posts.length; i++) {
111 110 uids += posts[i].getAttribute('data-uid') + ' ';
112 111 }
113 112
114 113 var data = {
115 114 uids: uids,
116 115 thread: threadId
117 116 }
118 117
119 118 var diffUrl = '/api/diff_thread/';
120 119
121 120 $.post(diffUrl,
122 121 data,
123 122 function(data) {
124 123 var updatedPosts = data.updated;
125 124 var addedPostCount = 0;
126 125
127 126 for (var i = 0; i < updatedPosts.length; i++) {
128 127 var postText = updatedPosts[i];
129 128 var post = $(postText);
130 129
131 130 if (updatePost(post) == POST_ADDED) {
132 131 addedPostCount++;
133 132 }
134 133 }
135 134
136 135 var hasMetaUpdates = updatedPosts.length > 0;
137 136 if (hasMetaUpdates) {
138 137 updateMetadataPanel();
139 138 }
140 139
141 140 if (addedPostCount > 0) {
142 141 updateBumplimitProgress(addedPostCount);
143 142 }
144 143
145 144 if (updatedPosts.length > 0) {
146 145 showNewPostsTitle(addedPostCount);
147 146 }
148 147
149 148 // TODO Process removed posts if any
150 149 $('.metapanel').attr('data-last-update', data.last_update);
151 150 },
152 151 'json'
153 152 )
154 153 }
155 154
156 155 /**
157 156 * Add or update the post on html page.
158 157 */
159 158 function updatePost(postHtml) {
160 159 // This needs to be set on start because the page is scrolled after posts
161 160 // are added or updated
162 161 var bottom = isPageBottom();
163 162
164 163 var post = $(postHtml);
165 164
166 165 var threadBlock = $('div.thread');
167 166
168 167 var postId = post.attr('id');
169 168
170 169 // If the post already exists, replace it. Otherwise add as a new one.
171 170 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
172 171
173 172 var type;
174 173
175 174 if (existingPosts.size() > 0) {
176 175 replacePartial(existingPosts.first(), post, false);
177 176 post = existingPosts.first();
178 177
179 178 type = POST_UPDATED;
180 179 } else {
181 180 post.appendTo(threadBlock);
182 181
183 182 if (bottom) {
184 183 scrollToBottom();
185 184 }
186 185
187 186 type = POST_ADDED;
188 187 }
189 188
190 189 processNewPost(post);
191 190
192 191 return type;
193 192 }
194 193
195 194 /**
196 195 * Initiate a blinking animation on a node to show it was updated.
197 196 */
198 197 function blink(node) {
199 198 var blinkCount = 2;
200 199
201 200 var nodeToAnimate = node;
202 201 for (var i = 0; i < blinkCount; i++) {
203 202 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
204 203 }
205 204 }
206 205
207 206 function isPageBottom() {
208 207 var scroll = $(window).scrollTop() / ($(document).height()
209 208 - $(window).height());
210 209
211 210 return scroll == 1
212 211 }
213 212
214 213 function enableJsUpdate() {
215 214 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
216 215 return true;
217 216 }
218 217
219 218 function initAutoupdate() {
220 219 if (location.protocol === 'https:') {
221 220 return enableJsUpdate();
222 221 } else {
223 222 if (connectWebsocket()) {
224 223 return true;
225 224 } else {
226 225 return enableJsUpdate();
227 226 }
228 227 }
229 228 }
230 229
231 230 function getReplyCount() {
232 231 return $('.thread').children(CLASS_POST).length
233 232 }
234 233
235 234 function getImageCount() {
236 235 return $('.thread').find('img').length
237 236 }
238 237
239 238 /**
240 239 * Update post count, images count and last update time in the metadata
241 240 * panel.
242 241 */
243 242 function updateMetadataPanel() {
244 243 var replyCountField = $('#reply-count');
245 244 var imageCountField = $('#image-count');
246 245
247 replyCountField.text(getReplyCount());
248 imageCountField.text(getImageCount());
246 var replyCount = getReplyCount();
247 replyCountField.text(replyCount);
248 var imageCount = getImageCount();
249 imageCountField.text(imageCount);
249 250
250 251 var lastUpdate = $('.post:last').children('.post-info').first()
251 252 .children('.pub_time').first().html();
252 253 if (lastUpdate !== '') {
253 254 var lastUpdateField = $('#last-update');
254 255 lastUpdateField.html(lastUpdate);
255 256 blink(lastUpdateField);
256 257 }
257 258
258 259 blink(replyCountField);
259 260 blink(imageCountField);
261
262 $('#message-count-text').text(ngettext('message', 'messages', replyCount));
263 $('#image-count-text').text(ngettext('image', 'images', imageCount));
260 264 }
261 265
262 266 /**
263 267 * Update bumplimit progress bar
264 268 */
265 269 function updateBumplimitProgress(postDelta) {
266 270 var progressBar = $('#bumplimit_progress');
267 271 if (progressBar) {
268 272 var postsToLimitElement = $('#left_to_limit');
269 273
270 274 var oldPostsToLimit = parseInt(postsToLimitElement.text());
271 275 var postCount = getReplyCount();
272 276 var bumplimit = postCount - postDelta + oldPostsToLimit;
273 277
274 278 var newPostsToLimit = bumplimit - postCount;
275 279 if (newPostsToLimit <= 0) {
276 280 $('.bar-bg').remove();
277 281 } else {
278 282 postsToLimitElement.text(newPostsToLimit);
279 283 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
280 284 }
281 285 }
282 286 }
283 287
284 288 /**
285 289 * Show 'new posts' text in the title if the document is not visible to a user
286 290 */
287 291 function showNewPostsTitle(newPostCount) {
288 292 if (document.hidden) {
289 293 if (documentOriginalTitle === '') {
290 294 documentOriginalTitle = document.title;
291 295 }
292 296 unreadPosts = unreadPosts + newPostCount;
293 297
294 298 var newTitle = '* ';
295 299 if (unreadPosts > 0) {
296 300 newTitle += '[' + unreadPosts + '] ';
297 301 }
298 302 newTitle += documentOriginalTitle;
299 303
300 304 document.title = newTitle;
301 305
302 306 document.addEventListener('visibilitychange', function() {
303 307 if (documentOriginalTitle !== '') {
304 308 document.title = documentOriginalTitle;
305 309 documentOriginalTitle = '';
306 310 unreadPosts = 0;
307 311 }
308 312
309 313 document.removeEventListener('visibilitychange', null);
310 314 });
311 315 }
312 316 }
313 317
314 318 /**
315 319 * Clear all entered values in the form fields
316 320 */
317 321 function resetForm(form) {
318 322 form.find('input:text, input:password, input:file, select, textarea').val('');
319 323 form.find('input:radio, input:checkbox')
320 324 .removeAttr('checked').removeAttr('selected');
321 325 $('.file_wrap').find('.file-thumb').remove();
322 326 $('#preview-text').hide();
323 327 }
324 328
325 329 /**
326 330 * When the form is posted, this method will be run as a callback
327 331 */
328 332 function updateOnPost(response, statusText, xhr, form) {
329 333 var json = $.parseJSON(response);
330 334 var status = json.status;
331 335
332 336 showAsErrors(form, '');
333 337
334 338 if (status === 'ok') {
335 339 resetFormPosition();
336 340 resetForm(form);
337 341 getThreadDiff();
338 342 scrollToBottom();
339 343 } else {
340 344 var errors = json.errors;
341 345 for (var i = 0; i < errors.length; i++) {
342 346 var fieldErrors = errors[i];
343 347
344 348 var error = fieldErrors.errors;
345 349
346 350 showAsErrors(form, error);
347 351 }
348 352 }
349 353 }
350 354
351 355 /**
352 356 * Show text in the errors row of the form.
353 357 * @param form
354 358 * @param text
355 359 */
356 360 function showAsErrors(form, text) {
357 361 form.children('.form-errors').remove();
358 362
359 363 if (text.length > 0) {
360 364 var errorList = $('<div class="form-errors">' + text + '<div>');
361 365 errorList.appendTo(form);
362 366 }
363 367 }
364 368
365 369 /**
366 370 * Run js methods that are usually run on the document, on the new post
367 371 */
368 372 function processNewPost(post) {
369 373 addRefLinkPreview(post[0]);
370 374 highlightCode(post);
371 375 blink(post);
372 376 }
373 377
374 378 function replacePartial(oldNode, newNode, recursive) {
375 379 if (!equalNodes(oldNode, newNode)) {
376 380 // Update parent node attributes
377 381 updateNodeAttr(oldNode, newNode, ATTR_CLASS);
378 382 updateNodeAttr(oldNode, newNode, ATTR_UID);
379 383
380 384 // Replace children
381 385 var children = oldNode.children();
382 386 if (children.length == 0) {
383 console.log(oldContent);
384 console.log(newContent)
385
386 387 oldNode.replaceWith(newNode);
387 388 } else {
388 389 var newChildren = newNode.children();
389 390 newChildren.each(function(i) {
390 391 var newChild = newChildren.eq(i);
391 392 var newChildClass = newChild.attr(ATTR_CLASS);
392 393
393 394 // Update only certain allowed blocks (e.g. not images)
394 395 if (ALLOWED_FOR_PARTIAL_UPDATE.indexOf(newChildClass) > -1) {
395 396 var oldChild = oldNode.children('.' + newChildClass);
396 397
397 398 if (oldChild.length == 0) {
398 399 oldNode.append(newChild);
399 400 } else {
400 401 if (!equalNodes(oldChild, newChild)) {
401 402 if (recursive) {
402 403 replacePartial(oldChild, newChild, false);
403 404 } else {
404 405 oldChild.replaceWith(newChild);
405 406 }
406 407 }
407 408 }
408 409 }
409 410 });
410 411 }
411 412 }
412 413 }
413 414
414 415 /**
415 416 * Compare nodes by content
416 417 */
417 418 function equalNodes(node1, node2) {
418 419 return node1[0].outerHTML == node2[0].outerHTML;
419 420 }
420 421
421 422 /**
422 423 * Update attribute of a node if it has changed
423 424 */
424 425 function updateNodeAttr(oldNode, newNode, attrName) {
425 426 var oldAttr = oldNode.attr(attrName);
426 427 var newAttr = newNode.attr(attrName);
427 428 if (oldAttr != newAttr) {
428 429 oldNode.attr(attrName, newAttr);
429 };
430 }
430 431 }
431 432
432 433 $(document).ready(function(){
433 434 if (initAutoupdate()) {
434 435 // Post form data over AJAX
435 436 var threadId = $('div.thread').children('.post').first().attr('id');
436 437
437 438 var form = $('#form');
438 439
439 440 if (form.length > 0) {
440 441 var options = {
441 442 beforeSubmit: function(arr, $form, options) {
442 443 showAsErrors($('#form'), gettext('Sending message...'));
443 444 },
444 445 success: updateOnPost,
445 446 error: function() {
446 447 showAsErrors($('#form'), gettext('Server error!'));
447 448 },
448 449 url: '/api/add_post/' + threadId + '/'
449 450 };
450 451
451 452 form.ajaxForm(options);
452 453
453 454 resetForm(form);
454 455 }
455 456 }
456 457 });
@@ -1,109 +1,109 b''
1 1 {% load i18n %}
2 2 {% load board %}
3 3
4 4 {% get_current_language as LANGUAGE_CODE %}
5 5
6 6 <div class="{{ css_class }}" id="{{ post.id }}" data-uid="{{ post.uid }}">
7 7 <div class="post-info">
8 8 <a class="post_id" href="{{ post.get_absolute_url }}">#{{ post.get_absolute_id }}</a>
9 9 <span class="title">{{ post.title }}</span>
10 10 <span class="pub_time"><time datetime="{{ post.pub_time|date:'c' }}">{{ post.pub_time }}</time></span>
11 11 {% if post.tripcode %}
12 12 {% with tripcode=post.get_tripcode %}
13 13 <a href="{% url 'feed' %}?tripcode={{ tripcode.get_full_text }}"
14 14 class="tripcode" title="{{ tripcode.get_full_text }}"
15 15 style="border: solid 2px #{{ tripcode.get_color }}; border-left: solid 1ex #{{ tripcode.get_color }};">{{ tripcode.get_short_text }}</a>
16 16 {% endwith %}
17 17 {% endif %}
18 18 {% comment %}
19 19 Thread death time needs to be shown only if the thread is alredy archived
20 20 and this is an opening post (thread death time) or a post for popup
21 21 (we don't see OP here so we show the death time in the post itself).
22 22 {% endcomment %}
23 23 {% if thread.archived %}
24 24 {% if is_opening %}
25 25 <time datetime="{{ thread.bump_time|date:'c' }}">{{ thread.bump_time }}</time>
26 26 {% endif %}
27 27 {% endif %}
28 28 {% if is_opening %}
29 29 {% if need_open_link %}
30 30 {% if thread.archived %}
31 31 <a class="link" href="{% url 'thread' post.id %}">{% trans "Open" %}</a>
32 32 {% else %}
33 33 <a class="link" href="{% url 'thread' post.id %}#form">{% trans "Reply" %}</a>
34 34 {% endif %}
35 35 {% endif %}
36 36 {% else %}
37 37 {% if need_op_data %}
38 38 {% with thread.get_opening_post as op %}
39 39 {% trans " in " %}{{ op.get_link_view|safe }} <span class="title">{{ op.get_title|striptags|truncatewords:5 }}</span>
40 40 {% endwith %}
41 41 {% endif %}
42 42 {% endif %}
43 43 {% if reply_link and not thread.archived %}
44 44 <a href="#form" onclick="addQuickReply('{{ post.id }}'); return false;">{% trans 'Reply' %}</a>
45 45 {% endif %}
46 46
47 47 {% if moderator %}
48 48 <span class="moderator_info">
49 49 | <a href="{% url 'admin:boards_post_change' post.id %}">{% trans 'Edit' %}</a>
50 50 {% if is_opening %}
51 51 | <a href="{% url 'admin:boards_thread_change' thread.id %}">{% trans 'Edit thread' %}</a>
52 52 {% endif %}
53 53 </span>
54 54 {% endif %}
55 55 </div>
56 56 {% comment %}
57 57 Post images. Currently only 1 image can be posted and shown, but post model
58 58 supports multiple.
59 59 {% endcomment %}
60 60 {% if post.images.exists %}
61 61 {% with post.images.first as image %}
62 62 {{ image.get_view|safe }}
63 63 {% endwith %}
64 64 {% endif %}
65 65 {% if post.attachments.exists %}
66 66 {% with post.attachments.first as file %}
67 67 {{ file.get_view|safe }}
68 68 {% endwith %}
69 69 {% endif %}
70 70 {% comment %}
71 71 Post message (text)
72 72 {% endcomment %}
73 73 <div class="message">
74 74 {% autoescape off %}
75 75 {% if truncated %}
76 76 {{ post.get_text|truncatewords_html:50 }}
77 77 {% else %}
78 78 {{ post.get_text }}
79 79 {% endif %}
80 80 {% endautoescape %}
81 81 </div>
82 82 {% if post.is_referenced %}
83 83 {% if mode_tree %}
84 84 <div class="tree_reply">
85 85 {% for refpost in post.get_referenced_posts %}
86 86 {% post_view refpost mode_tree=True %}
87 87 {% endfor %}
88 88 </div>
89 89 {% else %}
90 90 <div class="refmap">
91 91 {% trans "Replies" %}: {{ post.refmap|safe }}
92 92 </div>
93 93 {% endif %}
94 94 {% endif %}
95 95 {% comment %}
96 96 Thread metadata: counters, tags etc
97 97 {% endcomment %}
98 98 {% if is_opening %}
99 99 <div class="metadata">
100 100 {% if is_opening and need_open_link %}
101 {% blocktrans count count=thread.get_reply_count %}{{ count }} message,{% plural %}{{ count }} messages{% endblocktrans %},
102 {% blocktrans count count=thread.get_images_count %}{{ count }} image,{% plural %}{{ count }} images{% endblocktrans %}.
101 {% blocktrans count count=thread.get_reply_count %}{{ count }} message{% plural %}{{ count }} messages{% endblocktrans %},
102 {% blocktrans count count=thread.get_images_count %}{{ count }} image{% plural %}{{ count }} images{% endblocktrans %}.
103 103 {% endif %}
104 104 <span class="tags">
105 105 {{ thread.get_tag_url_list|safe }}
106 106 </span>
107 107 </div>
108 108 {% endif %}
109 109 </div>
@@ -1,40 +1,45 b''
1 1 {% extends "boards/base.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 head %}
9 9 <title>{{ opening_post.get_title|striptags|truncatewords:10 }}
10 10 - {{ site_name }}</title>
11 11 {% endblock %}
12 12
13 13 {% block content %}
14 14 <div class="image-mode-tab">
15 15 <a {% ifequal mode 'normal' %}class="current_mode"{% endifequal %} href="{% url 'thread' opening_post.id %}">{% trans 'Normal' %}</a>,
16 16 <a {% ifequal mode 'gallery' %}class="current_mode"{% endifequal %} href="{% url 'thread_gallery' opening_post.id %}">{% trans 'Gallery' %}</a>,
17 17 <a {% ifequal mode 'tree' %}class="current_mode"{% endifequal %} href="{% url 'thread_tree' opening_post.id %}">{% trans 'Tree' %}</a>
18 18 </div>
19 19
20 20 {% block thread_content %}
21 21 {% endblock %}
22 22 {% endblock %}
23 23
24 24 {% block metapanel %}
25 25
26 26 <span class="metapanel"
27 27 data-last-update="{{ last_update }}"
28 28 data-ws-token-time="{{ ws_token_time }}"
29 29 data-ws-token="{{ ws_token }}"
30 30 data-ws-project="{{ ws_project }}"
31 31 data-ws-host="{{ ws_host }}"
32 32 data-ws-port="{{ ws_port }}">
33 33
34 <span id="reply-count">{{ thread.get_reply_count }}</span>{% if thread.has_post_limit %}/{{ thread.max_posts }}{% endif %} {% trans 'messages' %},
35 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
34 {% with replies_count=thread.get_reply_count%}
35 <span id="reply-count">{{ thread.get_reply_count }}</span>{% if thread.has_post_limit %}/{{ thread.max_posts }}{% endif %}
36 <span id="message-count-text">{% blocktrans count repliess_count=replies_count %}message{% plural %}messages{% endblocktrans %}</span>,
37 {% endwith %}
38 {% with images_count=thread.get_images_count%}
39 <span id="image-count">{{ images_count }}</span> <span id="image-count-text">{% blocktrans count count=images_count %}image{% plural %}images{% endblocktrans %}</span>.
40 {% endwith %}
36 41 {% trans 'Last update: ' %}<span id="last-update"><time datetime="{{ thread.last_edit_time|date:'c' }}">{{ thread.last_edit_time }}</time></span>
37 42 [<a href="rss/">RSS</a>]
38 43 </span>
39 44
40 45 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now