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