##// END OF EJS Templates
Added ability to rollover thread
neko259 -
r2108:ed3f943b default
parent child Browse files
Show More
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,93 +1,97 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: 2015-09-04 18:47+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/3party/jquery-ui.min.js:8
22 #: static/js/3party/jquery-ui.min.js:8
23 msgid "'"
23 msgid "'"
24 msgstr ""
24 msgstr ""
25
25
26 #: static/js/refpopup.js:72
26 #: static/js/refpopup.js:72
27 msgid "Loading..."
27 msgid "Loading..."
28 msgstr "Загрузка..."
28 msgstr "Загрузка..."
29
29
30 #: static/js/refpopup.js:91
30 #: static/js/refpopup.js:91
31 msgid "Post not found"
31 msgid "Post not found"
32 msgstr "Сообщение не найдено"
32 msgstr "Сообщение не найдено"
33
33
34 #: static/js/thread_update.js:261
34 #: static/js/thread_update.js:261
35 msgid "message"
35 msgid "message"
36 msgid_plural "messages"
36 msgid_plural "messages"
37 msgstr[0] "сообщение"
37 msgstr[0] "сообщение"
38 msgstr[1] "сообщения"
38 msgstr[1] "сообщения"
39 msgstr[2] "сообщений"
39 msgstr[2] "сообщений"
40
40
41 #: static/js/thread_update.js:262
41 #: static/js/thread_update.js:262
42 msgid "image"
42 msgid "image"
43 msgid_plural "images"
43 msgid_plural "images"
44 msgstr[0] "изображение"
44 msgstr[0] "изображение"
45 msgstr[1] "изображения"
45 msgstr[1] "изображения"
46 msgstr[2] "изображений"
46 msgstr[2] "изображений"
47
47
48 #: static/js/thread_update.js:445
48 #: static/js/thread_update.js:445
49 msgid "Sending message..."
49 msgid "Sending message..."
50 msgstr "Отправка сообщения..."
50 msgstr "Отправка сообщения..."
51
51
52 #: static/js/thread_update.js:449
52 #: static/js/thread_update.js:449
53 msgid "Server error!"
53 msgid "Server error!"
54 msgstr "Ошибка сервера!"
54 msgstr "Ошибка сервера!"
55
55
56 msgid "Computing PoW..."
56 msgid "Computing PoW..."
57 msgstr "Расчёт PoW..."
57 msgstr "Расчёт PoW..."
58
58
59 msgid "Duplicates search"
59 msgid "Duplicates search"
60 msgstr "Поиск дубликатов"
60 msgstr "Поиск дубликатов"
61
61
62 msgid "Add local sticker"
62 msgid "Add local sticker"
63 msgstr "Добавить локальный стикер"
63 msgstr "Добавить локальный стикер"
64
64
65 msgid "Input sticker name"
65 msgid "Input sticker name"
66 msgstr "Введите название стикера"
66 msgstr "Введите название стикера"
67
67
68 msgid "Images posted: "
68 msgid "Images posted: "
69 msgstr "Вставленных изображений: "
69 msgstr "Вставленных изображений: "
70
70
71 msgid "Ban and delete"
71 msgid "Ban and delete"
72 msgstr "Забанить и удалить"
72 msgstr "Забанить и удалить"
73
73
74 msgid "Are you sure?"
74 msgid "Are you sure?"
75 msgstr "Вы уверены?"
75 msgstr "Вы уверены?"
76
76
77 msgid "Ban"
77 msgid "Ban"
78 msgstr "Забанить"
78 msgstr "Забанить"
79
79
80 msgid "Delete post"
80 msgid "Delete post"
81 msgstr "Удалить пост"
81 msgstr "Удалить пост"
82
82
83 msgid "Delete thread"
83 msgid "Delete thread"
84 msgstr "Удалить тему"
84 msgstr "Удалить тему"
85
85
86 msgid "Edit"
86 msgid "Edit"
87 msgstr "Изменить"
87 msgstr "Изменить"
88
88
89 msgid "Edit thread"
89 msgid "Edit thread"
90 msgstr "Изменить тему"
90 msgstr "Изменить тему"
91
91
92 msgid "Hide/show"
92 msgid "Hide/show"
93 msgstr "Скрыть/показать"
93 msgstr "Скрыть/показать"
94
95 msgid "Rollover thread"
96 msgstr "Перекатить тред"
97
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,93 +1,97 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: 2015-09-04 18:47+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/3party/jquery-ui.min.js:8
22 #: static/js/3party/jquery-ui.min.js:8
23 msgid "'"
23 msgid "'"
24 msgstr ""
24 msgstr ""
25
25
26 #: static/js/refpopup.js:72
26 #: static/js/refpopup.js:72
27 msgid "Loading..."
27 msgid "Loading..."
28 msgstr "Завантаження..."
28 msgstr "Завантаження..."
29
29
30 #: static/js/refpopup.js:91
30 #: static/js/refpopup.js:91
31 msgid "Post not found"
31 msgid "Post not found"
32 msgstr "Повідомлення не знайдене"
32 msgstr "Повідомлення не знайдене"
33
33
34 #: static/js/thread_update.js:261
34 #: static/js/thread_update.js:261
35 msgid "message"
35 msgid "message"
36 msgid_plural "messages"
36 msgid_plural "messages"
37 msgstr[0] "повідомлення"
37 msgstr[0] "повідомлення"
38 msgstr[1] "повідомлення"
38 msgstr[1] "повідомлення"
39 msgstr[2] "повідомлень"
39 msgstr[2] "повідомлень"
40
40
41 #: static/js/thread_update.js:262
41 #: static/js/thread_update.js:262
42 msgid "image"
42 msgid "image"
43 msgid_plural "images"
43 msgid_plural "images"
44 msgstr[0] "зображення"
44 msgstr[0] "зображення"
45 msgstr[1] "зображення"
45 msgstr[1] "зображення"
46 msgstr[2] "зображень"
46 msgstr[2] "зображень"
47
47
48 #: static/js/thread_update.js:445
48 #: static/js/thread_update.js:445
49 msgid "Sending message..."
49 msgid "Sending message..."
50 msgstr "Повідомлення надсилається..."
50 msgstr "Повідомлення надсилається..."
51
51
52 #: static/js/thread_update.js:449
52 #: static/js/thread_update.js:449
53 msgid "Server error!"
53 msgid "Server error!"
54 msgstr "Сервер нездужає! Заходьте пізніше!"
54 msgstr "Сервер нездужає! Заходьте пізніше!"
55
55
56 msgid "Computing PoW..."
56 msgid "Computing PoW..."
57 msgstr "Розраховується PoW..."
57 msgstr "Розраховується PoW..."
58
58
59 msgid "Duplicates search"
59 msgid "Duplicates search"
60 msgstr "Пошук дублікатів"
60 msgstr "Пошук дублікатів"
61
61
62 msgid "Add local sticker"
62 msgid "Add local sticker"
63 msgstr "Додати локальний стікер"
63 msgstr "Додати локальний стікер"
64
64
65 msgid "Input sticker name"
65 msgid "Input sticker name"
66 msgstr "Введіть назву стікера"
66 msgstr "Введіть назву стікера"
67
67
68 msgid "Images posted: "
68 msgid "Images posted: "
69 msgstr "Вставлених зображень: "
69 msgstr "Вставлених зображень: "
70
70
71 msgid "Ban and delete"
71 msgid "Ban and delete"
72 msgstr "Заблокувати й видалити"
72 msgstr "Заблокувати й видалити"
73
73
74 msgid "Are you sure?"
74 msgid "Are you sure?"
75 msgstr "Чи ви певні?"
75 msgstr "Чи ви певні?"
76
76
77 msgid "Ban"
77 msgid "Ban"
78 msgstr "Заблокувати"
78 msgstr "Заблокувати"
79
79
80 msgid "Delete post"
80 msgid "Delete post"
81 msgstr "Видалити повідомлення"
81 msgstr "Видалити повідомлення"
82
82
83 msgid "Delete thread"
83 msgid "Delete thread"
84 msgstr "Вирвати нитку"
84 msgstr "Вирвати нитку"
85
85
86 msgid "Edit"
86 msgid "Edit"
87 msgstr "Змінити"
87 msgstr "Змінити"
88
88
89 msgid "Edit thread"
89 msgid "Edit thread"
90 msgstr "Змінити нитку"
90 msgstr "Змінити нитку"
91
91
92 msgid "Hide/show"
92 msgid "Hide/show"
93 msgstr "Приховати/відобразити"
93 msgstr "Приховати/відобразити"
94
95 msgid "Rollover thread"
96 msgstr "Перекотити нитку"
97
@@ -1,362 +1,371 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 neko259
6 Copyright (C) 2013 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 ITEM_VOLUME_LEVEL = 'volumeLevel';
26 var ITEM_VOLUME_LEVEL = 'volumeLevel';
27 var ITEM_HIDDEN_POSTS = 'hiddenPosts';
27 var ITEM_HIDDEN_POSTS = 'hiddenPosts';
28
28
29 var IMAGE_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/gif'];
29 var IMAGE_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/gif'];
30
30
31 /**
31 /**
32 * An email is a hidden file to prevent spam bots from posting. It has to be
32 * An email is a hidden file to prevent spam bots from posting. It has to be
33 * hidden.
33 * hidden.
34 */
34 */
35 function hideEmailFromForm() {
35 function hideEmailFromForm() {
36 $('.form-email').parent().parent().hide();
36 $('.form-email').parent().parent().hide();
37 }
37 }
38
38
39 /**
39 /**
40 * Highlight code blocks with code highlighter
40 * Highlight code blocks with code highlighter
41 */
41 */
42 function highlightCode(node) {
42 function highlightCode(node) {
43 node.find('pre code').each(function(i, e) {
43 node.find('pre code').each(function(i, e) {
44 hljs.highlightBlock(e);
44 hljs.highlightBlock(e);
45 });
45 });
46 }
46 }
47
47
48 function updateFavPosts(data) {
48 function updateFavPosts(data) {
49 var includePostBody = $('#fav-panel').is(":visible");
49 var includePostBody = $('#fav-panel').is(":visible");
50
50
51 var allNewPostCount = 0;
51 var allNewPostCount = 0;
52
52
53 if (includePostBody) {
53 if (includePostBody) {
54 var favoriteThreadPanel = $('#fav-panel');
54 var favoriteThreadPanel = $('#fav-panel');
55 favoriteThreadPanel.empty();
55 favoriteThreadPanel.empty();
56 }
56 }
57
57
58 $.each($.parseJSON(data), function (_, dict) {
58 $.each($.parseJSON(data), function (_, dict) {
59 var newPostCount = dict.new_post_count;
59 var newPostCount = dict.new_post_count;
60 allNewPostCount += newPostCount;
60 allNewPostCount += newPostCount;
61
61
62 if (includePostBody) {
62 if (includePostBody) {
63 var favThreadNode = $('<div class="post"></div>');
63 var favThreadNode = $('<div class="post"></div>');
64 favThreadNode.append($(dict.post_url));
64 favThreadNode.append($(dict.post_url));
65 favThreadNode.append(' ');
65 favThreadNode.append(' ');
66 favThreadNode.append($('<span class="title">' + dict.title + '</span>'));
66 favThreadNode.append($('<span class="title">' + dict.title + '</span>'));
67
67
68 if (newPostCount > 0) {
68 if (newPostCount > 0) {
69 favThreadNode.append(' (<a href="' + dict.newest_post_link + '">+' + newPostCount + "</a>)");
69 favThreadNode.append(' (<a href="' + dict.newest_post_link + '">+' + newPostCount + "</a>)");
70 }
70 }
71
71
72 favoriteThreadPanel.append(favThreadNode);
72 favoriteThreadPanel.append(favThreadNode);
73
73
74 addRefLinkPreview(favThreadNode[0]);
74 addRefLinkPreview(favThreadNode[0]);
75 }
75 }
76 });
76 });
77
77
78 var newPostCountNode = $('#new-fav-post-count');
78 var newPostCountNode = $('#new-fav-post-count');
79 if (allNewPostCount > 0) {
79 if (allNewPostCount > 0) {
80 newPostCountNode.text('(+' + allNewPostCount + ')');
80 newPostCountNode.text('(+' + allNewPostCount + ')');
81 newPostCountNode.show();
81 newPostCountNode.show();
82 } else {
82 } else {
83 newPostCountNode.hide();
83 newPostCountNode.hide();
84 }
84 }
85 }
85 }
86
86
87 function initFavPanel() {
87 function initFavPanel() {
88 var favPanelButton = $('#fav-panel-btn');
88 var favPanelButton = $('#fav-panel-btn');
89 if (favPanelButton.length > 0 && typeof SharedWorker != 'undefined') {
89 if (favPanelButton.length > 0 && typeof SharedWorker != 'undefined') {
90 var worker = new SharedWorker($('body').attr('data-update-script'));
90 var worker = new SharedWorker($('body').attr('data-update-script'));
91 worker.port.onmessage = function(e) {
91 worker.port.onmessage = function(e) {
92 updateFavPosts(e.data);
92 updateFavPosts(e.data);
93 };
93 };
94 worker.onerror = function(event){
94 worker.onerror = function(event){
95 throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")");
95 throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")");
96 };
96 };
97 worker.port.start();
97 worker.port.start();
98
98
99 $(favPanelButton).click(function() {
99 $(favPanelButton).click(function() {
100 var favPanel = $('#fav-panel');
100 var favPanel = $('#fav-panel');
101 favPanel.toggle();
101 favPanel.toggle();
102
102
103 worker.port.postMessage({ includePostBody: favPanel.is(':visible')});
103 worker.port.postMessage({ includePostBody: favPanel.is(':visible')});
104
104
105 return false;
105 return false;
106 });
106 });
107
107
108 $(document).on('keyup.removepic', function(e) {
108 $(document).on('keyup.removepic', function(e) {
109 if(e.which === 27) {
109 if(e.which === 27) {
110 $('#fav-panel').hide();
110 $('#fav-panel').hide();
111 }
111 }
112 });
112 });
113 }
113 }
114 }
114 }
115
115
116 function setVolumeLevel(level) {
116 function setVolumeLevel(level) {
117 localStorage.setItem(ITEM_VOLUME_LEVEL, level);
117 localStorage.setItem(ITEM_VOLUME_LEVEL, level);
118 }
118 }
119
119
120 function getVolumeLevel() {
120 function getVolumeLevel() {
121 var level = localStorage.getItem(ITEM_VOLUME_LEVEL);
121 var level = localStorage.getItem(ITEM_VOLUME_LEVEL);
122 if (level == null) {
122 if (level == null) {
123 level = 1.0;
123 level = 1.0;
124 }
124 }
125 return level
125 return level
126 }
126 }
127
127
128 function processVolumeUser(node) {
128 function processVolumeUser(node) {
129 if (!window.localStorage) return;
129 if (!window.localStorage) return;
130 node.prop("volume", getVolumeLevel());
130 node.prop("volume", getVolumeLevel());
131 node.on('volumechange', function(event) {
131 node.on('volumechange', function(event) {
132 setVolumeLevel(event.target.volume);
132 setVolumeLevel(event.target.volume);
133 $("video,audio").prop("volume", getVolumeLevel());
133 $("video,audio").prop("volume", getVolumeLevel());
134 });
134 });
135 }
135 }
136
136
137 function getHiddenPosts() {
137 function getHiddenPosts() {
138 var arr = Array();
138 var arr = Array();
139 var hiddenPosts = localStorage.getItem(ITEM_HIDDEN_POSTS);
139 var hiddenPosts = localStorage.getItem(ITEM_HIDDEN_POSTS);
140 if (hiddenPosts) {
140 if (hiddenPosts) {
141 arr = JSON.parse(hiddenPosts);
141 arr = JSON.parse(hiddenPosts);
142 }
142 }
143 return arr;
143 return arr;
144 }
144 }
145
145
146 function processPostHiding(posts) {
146 function processPostHiding(posts) {
147 var hiddenPosts = getHiddenPosts();
147 var hiddenPosts = getHiddenPosts();
148
148
149 $.each(posts, function(index) {
149 $.each(posts, function(index) {
150 var post = $(this);
150 var post = $(this);
151 if (hiddenPosts.indexOf(post.attr("id")) > -1) {
151 if (hiddenPosts.indexOf(post.attr("id")) > -1) {
152 post.toggleClass("hidden_post");
152 post.toggleClass("hidden_post");
153 }
153 }
154 });
154 });
155 }
155 }
156
156
157 /**
157 /**
158 * Add all scripts than need to work on post, when the post is added to the
158 * Add all scripts than need to work on post, when the post is added to the
159 * document.
159 * document.
160 */
160 */
161 function addScriptsToPost(post) {
161 function addScriptsToPost(post) {
162 addRefLinkPreview(post[0]);
162 addRefLinkPreview(post[0]);
163 highlightCode(post);
163 highlightCode(post);
164 processVolumeUser(post.find("video,audio"));
164 processVolumeUser(post.find("video,audio"));
165 processPostHiding([post]);
165 processPostHiding([post]);
166 }
166 }
167
167
168 /**
168 /**
169 * Fix compatibility issues with some rare browsers
169 * Fix compatibility issues with some rare browsers
170 */
170 */
171 function compatibilityCrutches() {
171 function compatibilityCrutches() {
172 if (window.operamini) {
172 if (window.operamini) {
173 $('#form textarea').each(function() { this.placeholder = ''; });
173 $('#form textarea').each(function() { this.placeholder = ''; });
174 }
174 }
175 }
175 }
176
176
177 function togglePostHidden(postId) {
177 function togglePostHidden(postId) {
178 var hiddenPosts = getHiddenPosts();
178 var hiddenPosts = getHiddenPosts();
179
179
180 var elIndex = hiddenPosts.indexOf(postId);
180 var elIndex = hiddenPosts.indexOf(postId);
181 if (elIndex > -1) {
181 if (elIndex > -1) {
182 hiddenPosts.splice(elIndex, 1);
182 hiddenPosts.splice(elIndex, 1);
183 } else {
183 } else {
184 hiddenPosts.push(postId);
184 hiddenPosts.push(postId);
185 }
185 }
186 localStorage.setItem(ITEM_HIDDEN_POSTS, JSON.stringify(hiddenPosts));
186 localStorage.setItem(ITEM_HIDDEN_POSTS, JSON.stringify(hiddenPosts));
187
187
188 $('#' + postId).toggleClass("hidden_post");
188 $('#' + postId).toggleClass("hidden_post");
189 }
189 }
190
190
191 function addContextMenu() {
191 function addContextMenu() {
192 $.contextMenu({
192 $.contextMenu({
193 selector: '.file-menu',
193 selector: '.file-menu',
194 trigger: 'left',
194 trigger: 'left',
195
195
196 build: function($trigger, e) {
196 build: function($trigger, e) {
197 var fileSearchUrl = $trigger.data('search-url');
197 var fileSearchUrl = $trigger.data('search-url');
198 var isImage = IMAGE_TYPES.indexOf($trigger.data('type')) > -1;
198 var isImage = IMAGE_TYPES.indexOf($trigger.data('type')) > -1;
199 var hasUrl = fileSearchUrl.length > 0;
199 var hasUrl = fileSearchUrl.length > 0;
200 var id = $trigger.data('id');
200 var id = $trigger.data('id');
201 return {
201 return {
202 items: {
202 items: {
203 duplicates: {
203 duplicates: {
204 name: gettext('Duplicates search'),
204 name: gettext('Duplicates search'),
205 callback: function(key, opts) {
205 callback: function(key, opts) {
206 window.location = '/feed/?image=' + $trigger.data('filename');
206 window.location = '/feed/?image=' + $trigger.data('filename');
207 }
207 }
208 },
208 },
209 google: {
209 google: {
210 name: 'Google',
210 name: 'Google',
211 visible: isImage && hasUrl,
211 visible: isImage && hasUrl,
212 callback: function(key, opts) {
212 callback: function(key, opts) {
213 window.location = 'https://www.google.com/searchbyimage?image_url=' + fileSearchUrl;
213 window.location = 'https://www.google.com/searchbyimage?image_url=' + fileSearchUrl;
214 }
214 }
215 },
215 },
216 iqdb: {
216 iqdb: {
217 name: 'IQDB',
217 name: 'IQDB',
218 visible: isImage && hasUrl,
218 visible: isImage && hasUrl,
219 callback: function(key, opts) {
219 callback: function(key, opts) {
220 window.location = 'http://iqdb.org/?url=' + fileSearchUrl;
220 window.location = 'http://iqdb.org/?url=' + fileSearchUrl;
221 }
221 }
222 },
222 },
223 tineye: {
223 tineye: {
224 name: 'TinEye',
224 name: 'TinEye',
225 visible: isImage && hasUrl,
225 visible: isImage && hasUrl,
226 callback: function(key, opts) {
226 callback: function(key, opts) {
227 window.location = 'http://tineye.com/search?url=' + fileSearchUrl;
227 window.location = 'http://tineye.com/search?url=' + fileSearchUrl;
228 }
228 }
229 },
229 },
230 addAlias: {
230 addAlias: {
231 name: gettext('Add local sticker'),
231 name: gettext('Add local sticker'),
232 callback: function(key, opts) {
232 callback: function(key, opts) {
233 var alias = prompt(gettext('Input sticker name'));
233 var alias = prompt(gettext('Input sticker name'));
234 if (alias) {
234 if (alias) {
235 window.location = '/stickers/?action=add&name=' + alias + '&id=' + id;
235 window.location = '/stickers/?action=add&name=' + alias + '&id=' + id;
236 }
236 }
237 }
237 }
238 }
238 }
239 }
239 }
240 };
240 };
241 }
241 }
242 });
242 });
243
243
244 $.contextMenu({
244 $.contextMenu({
245 selector: '.post .post-menu',
245 selector: '.post .post-menu',
246 trigger: 'left',
246 trigger: 'left',
247 build: function($trigger, e) {
247 build: function($trigger, e) {
248 var canEditPost = PERMS['change_post'];
248 var canEditPost = PERMS['change_post'];
249 var canDeletePost = PERMS['delete_post'];
249 var canDeletePost = PERMS['delete_post'];
250 var canEditThread = PERMS['change_thread'];
250 var canEditThread = PERMS['change_thread'];
251 var canDeleteThread = PERMS['delete_thread'];
251 var canDeleteThread = PERMS['delete_thread'];
252
252
253 var post = $trigger.parents('.post');
253 var post = $trigger.parents('.post');
254
254
255 var isOpening = post.data('opening') === 'True';
255 var isOpening = post.data('opening') === 'True';
256 var threadId = post.data('thread-id');
256 var threadId = post.data('thread-id');
257 var hasGlobalId = post.data('has-global-id') === 'True';
257 var hasGlobalId = post.data('has-global-id') === 'True';
258
258
259 var posterIp = $trigger.siblings('.pub_time').attr('title');
259 var posterIp = $trigger.siblings('.pub_time').attr('title');
260 var hasIp = posterIp != null;
260 var hasIp = posterIp != null;
261
261
262 var postId = post.attr('id');
262 var postId = post.attr('id');
263
263
264 return {
264 return {
265 items: {
265 items: {
266 hide: {
266 hide: {
267 name: gettext('Hide/show'),
267 name: gettext('Hide/show'),
268 callback: function(key, opt) {
268 callback: function(key, opt) {
269 togglePostHidden(postId);
269 togglePostHidden(postId);
270 }
270 }
271 },
271 },
272 edit: {
272 edit: {
273 name: gettext('Edit'),
273 name: gettext('Edit'),
274 callback: function(key, opt) {
274 callback: function(key, opt) {
275 window.location = '/admin/boards/post/' + postId + '/change/';
275 window.location = '/admin/boards/post/' + postId + '/change/';
276 },
276 },
277 visible: canEditPost
277 visible: canEditPost
278 },
278 },
279 deletePost: {
279 deletePost: {
280 name: gettext('Delete post'),
280 name: gettext('Delete post'),
281 callback: function(key, opt) {
281 callback: function(key, opt) {
282 window.location = '/admin/boards/post/' + postId + '/delete/';
282 window.location = '/admin/boards/post/' + postId + '/delete/';
283 },
283 },
284 visible: !isOpening && canDeletePost
284 visible: !isOpening && canDeletePost
285 },
285 },
286 editThread: {
286 editThread: {
287 name: gettext('Edit thread'),
287 name: gettext('Edit thread'),
288 callback: function(key, opt) {
288 callback: function(key, opt) {
289 window.location = '/admin/boards/thread/' + threadId + '/change/';
289 window.location = '/admin/boards/thread/' + threadId + '/change/';
290 },
290 },
291 visible: isOpening && canEditThread
291 visible: isOpening && canEditThread
292 },
292 },
293 deleteThread: {
293 deleteThread: {
294 name: gettext('Delete thread'),
294 name: gettext('Delete thread'),
295 callback: function(key, opt) {
295 callback: function(key, opt) {
296 window.location = '/admin/boards/thread/' + threadId + '/delete/';
296 window.location = '/admin/boards/thread/' + threadId + '/delete/';
297 },
297 },
298 visible: isOpening && canDeleteThread
298 visible: isOpening && canDeleteThread
299 },
299 },
300 findByIp: {
300 findByIp: {
301 name: 'IP = ' + posterIp,
301 name: 'IP = ' + posterIp,
302 callback: function(key, opt) {
302 callback: function(key, opt) {
303 window.location = '/feed/?ip=' + posterIp;
303 window.location = '/feed/?ip=' + posterIp;
304 },
304 },
305 visible: canEditPost && hasIp
305 visible: canEditPost && hasIp
306 },
306 },
307 raw: {
307 raw: {
308 name: 'RAW',
308 name: 'RAW',
309 callback: function(key, opt) {
309 callback: function(key, opt) {
310 window.location = '/post_xml/' + postId;
310 window.location = '/post_xml/' + postId;
311 },
311 },
312 visible: canEditPost && hasGlobalId
312 visible: canEditPost && hasGlobalId
313 },
313 },
314 ban: {
314 ban: {
315 name: gettext('Ban'),
315 name: gettext('Ban'),
316 callback: function(key, opt) {
316 callback: function(key, opt) {
317 if (confirm(gettext('Are you sure?'))) {
317 if (confirm(gettext('Are you sure?'))) {
318 window.location = '/utils?method=ban&post_id=' + postId;
318 window.location = '/utils?method=ban&post_id=' + postId;
319 }
319 }
320 },
320 },
321 visible: canEditPost && hasIp
321 visible: canEditPost && hasIp
322 },
322 },
323 banAndDelete: {
323 banAndDelete: {
324 name: gettext('Ban and delete'),
324 name: gettext('Ban and delete'),
325 callback: function(key, opt) {
325 callback: function(key, opt) {
326 if (confirm(gettext('Are you sure?'))) {
326 if (confirm(gettext('Are you sure?'))) {
327 window.location = '/utils?method=ban_and_delete&post_id=' + postId;
327 window.location = '/utils?method=ban_and_delete&post_id=' + postId;
328 }
328 }
329 },
329 },
330 visible: hasIp && canDeletePost
330 visible: hasIp && canDeletePost
331 },
332 rollover: {
333 name: gettext('Rollover thread'),
334 callback: function(key, opt) {
335 if (confirm(gettext('Are you sure?'))) {
336 window.location = '/all/?t_from_id=' + postId + '#form';
337 }
338 },
339 visible: isOpening
331 }
340 }
332 }
341 }
333 };
342 };
334 }
343 }
335 });
344 });
336 }
345 }
337
346
338 $( document ).ready(function() {
347 $( document ).ready(function() {
339 hideEmailFromForm();
348 hideEmailFromForm();
340
349
341 $("a[href='#top']").click(function() {
350 $("a[href='#top']").click(function() {
342 $("html, body").animate({ scrollTop: 0 }, "slow");
351 $("html, body").animate({ scrollTop: 0 }, "slow");
343 return false;
352 return false;
344 });
353 });
345
354
346 addImgPreview();
355 addImgPreview();
347
356
348 addRefLinkPreview();
357 addRefLinkPreview();
349
358
350 highlightCode($(document));
359 highlightCode($(document));
351
360
352 initFavPanel();
361 initFavPanel();
353
362
354 var volumeUsers = $("video,audio");
363 var volumeUsers = $("video,audio");
355 processVolumeUser(volumeUsers);
364 processVolumeUser(volumeUsers);
356
365
357 addContextMenu();
366 addContextMenu();
358
367
359 compatibilityCrutches();
368 compatibilityCrutches();
360
369
361 processPostHiding($('.post'));
370 processPostHiding($('.post'));
362 });
371 });
@@ -1,136 +1,151 b''
1 from django.core.paginator import EmptyPage
1 from django.core.paginator import EmptyPage
2 from django.http import Http404
2 from django.http import Http404
3 from django.shortcuts import render, redirect
3 from django.shortcuts import render, redirect
4 from django.urls import reverse
4 from django.urls import reverse
5 from django.utils.decorators import method_decorator
5 from django.utils.decorators import method_decorator
6 from django.views.decorators.csrf import csrf_protect
6 from django.views.decorators.csrf import csrf_protect
7
7
8 from boards import settings
8 from boards import settings
9 from boards.abstracts.constants import PARAM_PAGE
9 from boards.abstracts.constants import PARAM_PAGE
10 from boards.abstracts.paginator import get_paginator
10 from boards.abstracts.paginator import get_paginator
11 from boards.abstracts.settingsmanager import get_settings_manager, \
11 from boards.abstracts.settingsmanager import get_settings_manager, \
12 SETTING_ONLY_FAVORITES, SETTING_SUBSCRIBE_BY_DEFAULT
12 SETTING_ONLY_FAVORITES, SETTING_SUBSCRIBE_BY_DEFAULT
13 from boards.forms import ThreadForm, PlainErrorList
13 from boards.forms import ThreadForm, PlainErrorList
14 from boards.models import Post, Thread
14 from boards.models import Post, Thread
15 from boards.settings import SECTION_VIEW
15 from boards.settings import SECTION_VIEW
16 from boards.views.base import BaseBoardView, CONTEXT_FORM
16 from boards.views.base import BaseBoardView, CONTEXT_FORM
17 from boards.views.mixins import PaginatedMixin, \
17 from boards.views.mixins import PaginatedMixin, \
18 DispatcherMixin, PARAMETER_METHOD
18 DispatcherMixin, PARAMETER_METHOD
19
19
20 ORDER_BUMP = 'bump'
20 ORDER_BUMP = 'bump'
21
21
22 PARAM_ORDER = 'order'
22 PARAM_ORDER = 'order'
23
23
24 FORM_TAGS = 'tags'
24 FORM_TAGS = 'tags'
25 FORM_TEXT = 'text'
25 FORM_TEXT = 'text'
26 FORM_TITLE = 'title'
26 FORM_TITLE = 'title'
27 FORM_IMAGE = 'image'
27 FORM_IMAGE = 'image'
28 FORM_THREADS = 'threads'
28 FORM_THREADS = 'threads'
29
29
30 TAG_DELIMITER = ' '
30 TAG_DELIMITER = ' '
31
31
32 PARAMETER_CURRENT_PAGE = 'current_page'
32 PARAMETER_CURRENT_PAGE = 'current_page'
33 PARAMETER_PAGINATOR = 'paginator'
33 PARAMETER_PAGINATOR = 'paginator'
34 PARAMETER_THREADS = 'threads'
34 PARAMETER_THREADS = 'threads'
35 PARAMETER_ADDITIONAL = 'additional_params'
35 PARAMETER_ADDITIONAL = 'additional_params'
36 PARAMETER_RSS_URL = 'rss_url'
36 PARAMETER_RSS_URL = 'rss_url'
37
37
38 TEMPLATE = 'boards/all_threads.html'
38 TEMPLATE = 'boards/all_threads.html'
39 DEFAULT_PAGE = 1
39 DEFAULT_PAGE = 1
40
40
41
41
42 class AllThreadsView(BaseBoardView, PaginatedMixin, DispatcherMixin):
42 class AllThreadsView(BaseBoardView, PaginatedMixin, DispatcherMixin):
43
43
44 tag_name = ''
44 tag_name = ''
45
45
46 def __init__(self):
46 def __init__(self):
47 self.settings_manager = None
47 self.settings_manager = None
48 super(AllThreadsView, self).__init__()
48 super(AllThreadsView, self).__init__()
49
49
50 @method_decorator(csrf_protect)
50 @method_decorator(csrf_protect)
51 def get(self, request, form: ThreadForm=None):
51 def get(self, request, form: ThreadForm=None):
52 page = request.GET.get(PARAM_PAGE, DEFAULT_PAGE)
52 page = request.GET.get(PARAM_PAGE, DEFAULT_PAGE)
53
53
54 params = self.get_context_data(request=request)
54 params = self.get_context_data(request=request)
55
55
56 subscribe_by_default = get_settings_manager(request).get_setting(
56 subscribe_by_default = get_settings_manager(request).get_setting(
57 SETTING_SUBSCRIBE_BY_DEFAULT, False)
57 SETTING_SUBSCRIBE_BY_DEFAULT, False)
58
58
59 if not form:
59 if not form:
60 form = ThreadForm(error_class=PlainErrorList,
60 t_from_id = request.GET.get('t_from_id')
61 initial={FORM_TAGS: self.tag_name,
61 if t_from_id:
62 'subscribe': subscribe_by_default})
62 source_op = Post.objects.get(id=int(t_from_id))
63 tags_str = ' '.join([tag.get_name() for tag in source_op.get_thread().get_tags()])
64 post_link = '[post]{}[/post]'.format(source_op.id) #FIXME To constants
65 new_title = source_op.get_title() + ' NEW' # TODO More intelligent name change
66
67 form = ThreadForm(error_class=PlainErrorList,
68 initial={
69 FORM_TAGS: tags_str,
70 'subscribe': subscribe_by_default,
71 FORM_TEXT: post_link,
72 FORM_TITLE: new_title,
73 })
74 else:
75 form = ThreadForm(error_class=PlainErrorList,
76 initial={FORM_TAGS: self.tag_name,
77 'subscribe': subscribe_by_default})
63
78
64 self.settings_manager = get_settings_manager(request)
79 self.settings_manager = get_settings_manager(request)
65
80
66 threads = self.get_threads()
81 threads = self.get_threads()
67
82
68 order = request.GET.get(PARAM_ORDER, ORDER_BUMP)
83 order = request.GET.get(PARAM_ORDER, ORDER_BUMP)
69 if order == ORDER_BUMP:
84 if order == ORDER_BUMP:
70 threads = threads.order_by('-bump_time')
85 threads = threads.order_by('-bump_time')
71 else:
86 else:
72 threads = threads.filter(replies__opening=True)\
87 threads = threads.filter(replies__opening=True)\
73 .order_by('-replies__pub_time')
88 .order_by('-replies__pub_time')
74 threads = threads.distinct()
89 threads = threads.distinct()
75
90
76 paginator = get_paginator(threads, settings.get_int(
91 paginator = get_paginator(threads, settings.get_int(
77 SECTION_VIEW, 'ThreadsPerPage'),
92 SECTION_VIEW, 'ThreadsPerPage'),
78 link=self.get_reverse_url(),
93 link=self.get_reverse_url(),
79 params=request.GET.dict())
94 params=request.GET.dict())
80 paginator.current_page = int(page)
95 paginator.current_page = int(page)
81
96
82 try:
97 try:
83 threads = paginator.page(page).object_list
98 threads = paginator.page(page).object_list
84 except EmptyPage:
99 except EmptyPage:
85 raise Http404()
100 raise Http404()
86
101
87 params[PARAMETER_THREADS] = threads
102 params[PARAMETER_THREADS] = threads
88 params[CONTEXT_FORM] = form
103 params[CONTEXT_FORM] = form
89 params[PARAMETER_RSS_URL] = self.get_rss_url()
104 params[PARAMETER_RSS_URL] = self.get_rss_url()
90
105
91 params.update(self.get_page_context(paginator, page))
106 params.update(self.get_page_context(paginator, page))
92
107
93 return render(request, TEMPLATE, params)
108 return render(request, TEMPLATE, params)
94
109
95 @method_decorator(csrf_protect)
110 @method_decorator(csrf_protect)
96 def post(self, request):
111 def post(self, request):
97 if PARAMETER_METHOD in request.POST:
112 if PARAMETER_METHOD in request.POST:
98 self.dispatch_method(request)
113 self.dispatch_method(request)
99
114
100 return redirect(self.get_reverse_url())
115 return redirect(self.get_reverse_url())
101
116
102 form = ThreadForm(request.POST, request.FILES,
117 form = ThreadForm(request.POST, request.FILES,
103 error_class=PlainErrorList, session=request.session)
118 error_class=PlainErrorList, session=request.session)
104
119
105 if form.is_valid():
120 if form.is_valid():
106 return Post.objects.create_from_form(request, form, None)
121 return Post.objects.create_from_form(request, form, None)
107 if form.need_to_ban:
122 if form.need_to_ban:
108 # Ban user because he is suspected to be a bot
123 # Ban user because he is suspected to be a bot
109 self._ban_current_user(request)
124 self._ban_current_user(request)
110
125
111 return self.get(request, form)
126 return self.get(request, form)
112
127
113 def get_reverse_url(self):
128 def get_reverse_url(self):
114 return reverse('index')
129 return reverse('index')
115
130
116 def get_threads(self):
131 def get_threads(self):
117 """
132 """
118 Gets list of threads that will be shown on a page.
133 Gets list of threads that will be shown on a page.
119 """
134 """
120
135
121 threads = Thread.objects\
136 threads = Thread.objects\
122 .exclude(tags__in=self.settings_manager.get_hidden_tags())
137 .exclude(tags__in=self.settings_manager.get_hidden_tags())
123 if self.settings_manager.get_setting(SETTING_ONLY_FAVORITES):
138 if self.settings_manager.get_setting(SETTING_ONLY_FAVORITES):
124 fav_tags = self.settings_manager.get_fav_tags()
139 fav_tags = self.settings_manager.get_fav_tags()
125 if len(fav_tags) > 0:
140 if len(fav_tags) > 0:
126 threads = threads.filter(tags__in=fav_tags)
141 threads = threads.filter(tags__in=fav_tags)
127
142
128 return threads
143 return threads
129
144
130 def get_rss_url(self):
145 def get_rss_url(self):
131 return self.get_reverse_url() + 'rss/'
146 return self.get_reverse_url() + 'rss/'
132
147
133 def toggle_fav(self, request):
148 def toggle_fav(self, request):
134 settings_manager = get_settings_manager(request)
149 settings_manager = get_settings_manager(request)
135 settings_manager.set_setting(SETTING_ONLY_FAVORITES,
150 settings_manager.set_setting(SETTING_ONLY_FAVORITES,
136 not settings_manager.get_setting(SETTING_ONLY_FAVORITES, False))
151 not settings_manager.get_setting(SETTING_ONLY_FAVORITES, False))
General Comments 0
You need to be logged in to leave comments. Login now