##// END OF EJS Templates
Small design changes. Count board speed since yesterday, not today
neko259 -
r411:debeb613 default
parent child Browse files
Show More
@@ -1,330 +1,330 b''
1 from datetime import datetime, timedelta
1 from datetime import datetime, timedelta
2 from datetime import time as dtime
2 from datetime import time as dtime
3 import os
3 import os
4 from random import random
4 from random import random
5 import time
5 import time
6 import math
6 import math
7 import re
7 import re
8 from django.core.cache import cache
8 from django.core.cache import cache
9
9
10 from django.db import models
10 from django.db import models
11 from django.http import Http404
11 from django.http import Http404
12 from django.utils import timezone
12 from django.utils import timezone
13 from markupfield.fields import MarkupField
13 from markupfield.fields import MarkupField
14
14
15 from neboard import settings
15 from neboard import settings
16 from boards import thumbs
16 from boards import thumbs
17
17
18 APP_LABEL_BOARDS = 'boards'
18 APP_LABEL_BOARDS = 'boards'
19
19
20 CACHE_KEY_PPD = 'ppd'
20 CACHE_KEY_PPD = 'ppd'
21
21
22 POSTS_PER_DAY_RANGE = range(7)
22 POSTS_PER_DAY_RANGE = range(7)
23
23
24 BAN_REASON_AUTO = 'Auto'
24 BAN_REASON_AUTO = 'Auto'
25
25
26 IMAGE_THUMB_SIZE = (200, 150)
26 IMAGE_THUMB_SIZE = (200, 150)
27
27
28 TITLE_MAX_LENGTH = 50
28 TITLE_MAX_LENGTH = 50
29
29
30 DEFAULT_MARKUP_TYPE = 'markdown'
30 DEFAULT_MARKUP_TYPE = 'markdown'
31
31
32 NO_PARENT = -1
32 NO_PARENT = -1
33 NO_IP = '0.0.0.0'
33 NO_IP = '0.0.0.0'
34 UNKNOWN_UA = ''
34 UNKNOWN_UA = ''
35 ALL_PAGES = -1
35 ALL_PAGES = -1
36 IMAGES_DIRECTORY = 'images/'
36 IMAGES_DIRECTORY = 'images/'
37 FILE_EXTENSION_DELIMITER = '.'
37 FILE_EXTENSION_DELIMITER = '.'
38
38
39 SETTING_MODERATE = "moderate"
39 SETTING_MODERATE = "moderate"
40
40
41 REGEX_REPLY = re.compile('>>(\d+)')
41 REGEX_REPLY = re.compile('>>(\d+)')
42
42
43
43
44 class PostManager(models.Manager):
44 class PostManager(models.Manager):
45
45
46 def create_post(self, title, text, image=None, thread=None,
46 def create_post(self, title, text, image=None, thread=None,
47 ip=NO_IP, tags=None, user=None):
47 ip=NO_IP, tags=None, user=None):
48 cache.delete(CACHE_KEY_PPD)
48 cache.delete(CACHE_KEY_PPD)
49
49
50 posting_time = timezone.now()
50 posting_time = timezone.now()
51 if not thread:
51 if not thread:
52 thread = Thread.objects.create(bump_time=posting_time,
52 thread = Thread.objects.create(bump_time=posting_time,
53 last_edit_time=posting_time)
53 last_edit_time=posting_time)
54 else:
54 else:
55 thread.bump()
55 thread.bump()
56 thread.last_edit_time = posting_time
56 thread.last_edit_time = posting_time
57 thread.save()
57 thread.save()
58
58
59 post = self.create(title=title,
59 post = self.create(title=title,
60 text=text,
60 text=text,
61 pub_time=posting_time,
61 pub_time=posting_time,
62 thread_new=thread,
62 thread_new=thread,
63 image=image,
63 image=image,
64 poster_ip=ip,
64 poster_ip=ip,
65 poster_user_agent=UNKNOWN_UA,
65 poster_user_agent=UNKNOWN_UA,
66 last_edit_time=posting_time,
66 last_edit_time=posting_time,
67 user=user)
67 user=user)
68
68
69 thread.replies.add(post)
69 thread.replies.add(post)
70 if tags:
70 if tags:
71 linked_tags = []
71 linked_tags = []
72 for tag in tags:
72 for tag in tags:
73 tag_linked_tags = tag.get_linked_tags()
73 tag_linked_tags = tag.get_linked_tags()
74 if len(tag_linked_tags) > 0:
74 if len(tag_linked_tags) > 0:
75 linked_tags.extend(tag_linked_tags)
75 linked_tags.extend(tag_linked_tags)
76
76
77 tags.extend(linked_tags)
77 tags.extend(linked_tags)
78 map(thread.add_tag, tags)
78 map(thread.add_tag, tags)
79
79
80 self._delete_old_threads()
80 self._delete_old_threads()
81 self.connect_replies(post)
81 self.connect_replies(post)
82
82
83 return post
83 return post
84
84
85 def delete_post(self, post):
85 def delete_post(self, post):
86 thread = post.thread_new
86 thread = post.thread_new
87 thread.last_edit_time = timezone.now()
87 thread.last_edit_time = timezone.now()
88 thread.save()
88 thread.save()
89
89
90 post.delete()
90 post.delete()
91
91
92 def delete_posts_by_ip(self, ip):
92 def delete_posts_by_ip(self, ip):
93 posts = self.filter(poster_ip=ip)
93 posts = self.filter(poster_ip=ip)
94 map(self.delete_post, posts)
94 map(self.delete_post, posts)
95
95
96 # TODO Move this method to thread manager
96 # TODO Move this method to thread manager
97 def get_threads(self, tag=None, page=ALL_PAGES,
97 def get_threads(self, tag=None, page=ALL_PAGES,
98 order_by='-bump_time'):
98 order_by='-bump_time'):
99 if tag:
99 if tag:
100 threads = tag.threads
100 threads = tag.threads
101
101
102 if not threads.exists():
102 if not threads.exists():
103 raise Http404
103 raise Http404
104 else:
104 else:
105 threads = Thread.objects.all()
105 threads = Thread.objects.all()
106
106
107 threads = threads.order_by(order_by)
107 threads = threads.order_by(order_by)
108
108
109 if page != ALL_PAGES:
109 if page != ALL_PAGES:
110 thread_count = threads.count()
110 thread_count = threads.count()
111
111
112 if page < self._get_page_count(thread_count):
112 if page < self._get_page_count(thread_count):
113 start_thread = page * settings.THREADS_PER_PAGE
113 start_thread = page * settings.THREADS_PER_PAGE
114 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
114 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
115 thread_count)
115 thread_count)
116 threads = threads[start_thread:end_thread]
116 threads = threads[start_thread:end_thread]
117
117
118 return threads
118 return threads
119
119
120 # TODO Move this method to thread manager
120 # TODO Move this method to thread manager
121 def get_thread_page_count(self, tag=None):
121 def get_thread_page_count(self, tag=None):
122 if tag:
122 if tag:
123 threads = Thread.objects.filter(tags=tag)
123 threads = Thread.objects.filter(tags=tag)
124 else:
124 else:
125 threads = Thread.objects.all()
125 threads = Thread.objects.all()
126
126
127 return self._get_page_count(threads.count())
127 return self._get_page_count(threads.count())
128
128
129 # TODO Move this method to thread manager
129 # TODO Move this method to thread manager
130 def _delete_old_threads(self):
130 def _delete_old_threads(self):
131 """
131 """
132 Preserves maximum thread count. If there are too many threads,
132 Preserves maximum thread count. If there are too many threads,
133 delete the old ones.
133 delete the old ones.
134 """
134 """
135
135
136 # TODO Move old threads to the archive instead of deleting them.
136 # TODO Move old threads to the archive instead of deleting them.
137 # Maybe make some 'old' field in the model to indicate the thread
137 # Maybe make some 'old' field in the model to indicate the thread
138 # must not be shown and be able for replying.
138 # must not be shown and be able for replying.
139
139
140 threads = Thread.objects.all()
140 threads = Thread.objects.all()
141 thread_count = threads.count()
141 thread_count = threads.count()
142
142
143 if thread_count > settings.MAX_THREAD_COUNT:
143 if thread_count > settings.MAX_THREAD_COUNT:
144 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
144 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
145 old_threads = threads[thread_count - num_threads_to_delete:]
145 old_threads = threads[thread_count - num_threads_to_delete:]
146
146
147 map(Thread.delete_with_posts, old_threads)
147 map(Thread.delete_with_posts, old_threads)
148
148
149 def connect_replies(self, post):
149 def connect_replies(self, post):
150 """Connect replies to a post to show them as a refmap"""
150 """Connect replies to a post to show them as a refmap"""
151
151
152 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
152 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
153 post_id = reply_number.group(1)
153 post_id = reply_number.group(1)
154 ref_post = self.filter(id=post_id)
154 ref_post = self.filter(id=post_id)
155 if ref_post.count() > 0:
155 if ref_post.count() > 0:
156 referenced_post = ref_post[0]
156 referenced_post = ref_post[0]
157 referenced_post.referenced_posts.add(post)
157 referenced_post.referenced_posts.add(post)
158 referenced_post.last_edit_time = post.pub_time
158 referenced_post.last_edit_time = post.pub_time
159 referenced_post.save()
159 referenced_post.save()
160
160
161 def _get_page_count(self, thread_count):
161 def _get_page_count(self, thread_count):
162 return int(math.ceil(thread_count / float(settings.THREADS_PER_PAGE)))
162 return int(math.ceil(thread_count / float(settings.THREADS_PER_PAGE)))
163
163
164 def get_posts_per_day(self):
164 def get_posts_per_day(self):
165 """Get count of posts for the current day"""
165 """Get count of posts for the current day"""
166
166
167 ppd = cache.get(CACHE_KEY_PPD)
167 ppd = cache.get(CACHE_KEY_PPD)
168 if ppd:
168 if ppd:
169 return ppd
169 return ppd
170
170
171 today = datetime.now().date()
171 today = datetime.now().date()
172
172
173 posts_per_days = []
173 posts_per_days = []
174 for i in POSTS_PER_DAY_RANGE:
174 for i in POSTS_PER_DAY_RANGE:
175 day_end = today + timedelta(i)
175 day_end = today + timedelta(i - 1)
176 day_start = today + timedelta(i - 1)
176 day_start = today + timedelta(i - 2)
177 day_time_start = datetime.combine(day_start, dtime())
177 day_time_start = datetime.combine(day_start, dtime())
178 day_time_end = datetime.combine(day_end, dtime())
178 day_time_end = datetime.combine(day_end, dtime())
179
179
180 posts_per_days.append(float(self.filter(
180 posts_per_days.append(float(self.filter(
181 pub_time__lte=day_time_end,
181 pub_time__lte=day_time_end,
182 pub_time__gte=day_time_start).count()))
182 pub_time__gte=day_time_start).count()))
183
183
184 ppd = (sum(posts_per_day for posts_per_day in posts_per_days) /
184 ppd = (sum(posts_per_day for posts_per_day in posts_per_days) /
185 len(posts_per_days))
185 len(posts_per_days))
186 cache.set(CACHE_KEY_PPD, ppd)
186 cache.set(CACHE_KEY_PPD, ppd)
187 return ppd
187 return ppd
188
188
189
189
190 class Post(models.Model):
190 class Post(models.Model):
191 """A post is a message."""
191 """A post is a message."""
192
192
193 objects = PostManager()
193 objects = PostManager()
194
194
195 class Meta:
195 class Meta:
196 app_label = APP_LABEL_BOARDS
196 app_label = APP_LABEL_BOARDS
197
197
198 def _update_image_filename(self, filename):
198 def _update_image_filename(self, filename):
199 """Get unique image filename"""
199 """Get unique image filename"""
200
200
201 path = IMAGES_DIRECTORY
201 path = IMAGES_DIRECTORY
202 new_name = str(int(time.mktime(time.gmtime())))
202 new_name = str(int(time.mktime(time.gmtime())))
203 new_name += str(int(random() * 1000))
203 new_name += str(int(random() * 1000))
204 new_name += FILE_EXTENSION_DELIMITER
204 new_name += FILE_EXTENSION_DELIMITER
205 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
205 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
206
206
207 return os.path.join(path, new_name)
207 return os.path.join(path, new_name)
208
208
209 title = models.CharField(max_length=TITLE_MAX_LENGTH)
209 title = models.CharField(max_length=TITLE_MAX_LENGTH)
210 pub_time = models.DateTimeField()
210 pub_time = models.DateTimeField()
211 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
211 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
212 escape_html=False)
212 escape_html=False)
213
213
214 image_width = models.IntegerField(default=0)
214 image_width = models.IntegerField(default=0)
215 image_height = models.IntegerField(default=0)
215 image_height = models.IntegerField(default=0)
216
216
217 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
217 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
218 blank=True, sizes=(IMAGE_THUMB_SIZE,),
218 blank=True, sizes=(IMAGE_THUMB_SIZE,),
219 width_field='image_width',
219 width_field='image_width',
220 height_field='image_height')
220 height_field='image_height')
221
221
222 poster_ip = models.GenericIPAddressField()
222 poster_ip = models.GenericIPAddressField()
223 poster_user_agent = models.TextField()
223 poster_user_agent = models.TextField()
224
224
225 thread = models.ForeignKey('Post', null=True, default=None)
225 thread = models.ForeignKey('Post', null=True, default=None)
226 thread_new = models.ForeignKey('Thread', null=True, default=None)
226 thread_new = models.ForeignKey('Thread', null=True, default=None)
227 last_edit_time = models.DateTimeField()
227 last_edit_time = models.DateTimeField()
228 user = models.ForeignKey('User', null=True, default=None)
228 user = models.ForeignKey('User', null=True, default=None)
229
229
230 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
230 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
231 null=True,
231 null=True,
232 blank=True, related_name='rfp+')
232 blank=True, related_name='rfp+')
233
233
234 def __unicode__(self):
234 def __unicode__(self):
235 return '#' + str(self.id) + ' ' + self.title + ' (' + \
235 return '#' + str(self.id) + ' ' + self.title + ' (' + \
236 self.text.raw[:50] + ')'
236 self.text.raw[:50] + ')'
237
237
238 def get_title(self):
238 def get_title(self):
239 title = self.title
239 title = self.title
240 if len(title) == 0:
240 if len(title) == 0:
241 title = self.text.raw[:20]
241 title = self.text.raw[:20]
242
242
243 return title
243 return title
244
244
245 def get_sorted_referenced_posts(self):
245 def get_sorted_referenced_posts(self):
246 return self.referenced_posts.order_by('id')
246 return self.referenced_posts.order_by('id')
247
247
248 def is_referenced(self):
248 def is_referenced(self):
249 return self.referenced_posts.all().exists()
249 return self.referenced_posts.all().exists()
250
250
251 def is_opening(self):
251 def is_opening(self):
252 return self.thread_new.get_replies()[0] == self
252 return self.thread_new.get_replies()[0] == self
253
253
254
254
255 class Thread(models.Model):
255 class Thread(models.Model):
256
256
257 class Meta:
257 class Meta:
258 app_label = APP_LABEL_BOARDS
258 app_label = APP_LABEL_BOARDS
259
259
260 tags = models.ManyToManyField('Tag')
260 tags = models.ManyToManyField('Tag')
261 bump_time = models.DateTimeField()
261 bump_time = models.DateTimeField()
262 last_edit_time = models.DateTimeField()
262 last_edit_time = models.DateTimeField()
263 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
263 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
264 blank=True, related_name='tre+')
264 blank=True, related_name='tre+')
265
265
266 def get_tags(self):
266 def get_tags(self):
267 """Get a sorted tag list"""
267 """Get a sorted tag list"""
268
268
269 return self.tags.order_by('name')
269 return self.tags.order_by('name')
270
270
271 def bump(self):
271 def bump(self):
272 """Bump (move to up) thread"""
272 """Bump (move to up) thread"""
273
273
274 if self.can_bump():
274 if self.can_bump():
275 self.bump_time = timezone.now()
275 self.bump_time = timezone.now()
276
276
277 def get_reply_count(self):
277 def get_reply_count(self):
278 return self.replies.count()
278 return self.replies.count()
279
279
280 def get_images_count(self):
280 def get_images_count(self):
281 return self.replies.filter(image_width__gt=0).count()
281 return self.replies.filter(image_width__gt=0).count()
282
282
283 def can_bump(self):
283 def can_bump(self):
284 """Check if the thread can be bumped by replying"""
284 """Check if the thread can be bumped by replying"""
285
285
286 post_count = self.get_reply_count()
286 post_count = self.get_reply_count()
287
287
288 return post_count <= settings.MAX_POSTS_PER_THREAD
288 return post_count <= settings.MAX_POSTS_PER_THREAD
289
289
290 def delete_with_posts(self):
290 def delete_with_posts(self):
291 """Completely delete thread"""
291 """Completely delete thread"""
292
292
293 if self.replies.count() > 0:
293 if self.replies.count() > 0:
294 map(Post.objects.delete_post, self.replies.all())
294 map(Post.objects.delete_post, self.replies.all())
295
295
296 self.delete()
296 self.delete()
297
297
298 def get_last_replies(self):
298 def get_last_replies(self):
299 """Get last replies, not including opening post"""
299 """Get last replies, not including opening post"""
300
300
301 if settings.LAST_REPLIES_COUNT > 0:
301 if settings.LAST_REPLIES_COUNT > 0:
302 reply_count = self.get_reply_count()
302 reply_count = self.get_reply_count()
303
303
304 if reply_count > 0:
304 if reply_count > 0:
305 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
305 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
306 reply_count - 1)
306 reply_count - 1)
307 last_replies = self.replies.all().order_by('pub_time')[
307 last_replies = self.replies.all().order_by('pub_time')[
308 reply_count - reply_count_to_show:]
308 reply_count - reply_count_to_show:]
309
309
310 return last_replies
310 return last_replies
311
311
312 def get_replies(self):
312 def get_replies(self):
313 """Get sorted thread posts"""
313 """Get sorted thread posts"""
314
314
315 return self.replies.all().order_by('pub_time')
315 return self.replies.all().order_by('pub_time')
316
316
317 def add_tag(self, tag):
317 def add_tag(self, tag):
318 """Connect thread to a tag and tag to a thread"""
318 """Connect thread to a tag and tag to a thread"""
319
319
320 self.tags.add(tag)
320 self.tags.add(tag)
321 tag.threads.add(self)
321 tag.threads.add(self)
322
322
323 def get_opening_post(self):
323 def get_opening_post(self):
324 return self.get_replies()[0]
324 return self.get_replies()[0]
325
325
326 def __unicode__(self):
326 def __unicode__(self):
327 return str(self.get_replies()[0].id)
327 return str(self.get_replies()[0].id)
328
328
329 def get_pub_time(self):
329 def get_pub_time(self):
330 return self.get_opening_post().pub_time No newline at end of file
330 return self.get_opening_post().pub_time
@@ -1,354 +1,355 b''
1 html {
1 html {
2 background: #555;
2 background: #555;
3 color: #ffffff;
3 color: #ffffff;
4 }
4 }
5
5
6 #admin_panel {
6 #admin_panel {
7 background: #FF0000;
7 background: #FF0000;
8 color: #00FF00
8 color: #00FF00
9 }
9 }
10
10
11 .input_field {
11 .input_field {
12
12
13 }
13 }
14
14
15 .input_field_name {
15 .input_field_name {
16
16
17 }
17 }
18
18
19 .input_field_error {
19 .input_field_error {
20 color: #FF0000;
20 color: #FF0000;
21 }
21 }
22
22
23
23
24 .title {
24 .title {
25 font-weight: bold;
25 font-weight: bold;
26 color: #ffcc00;
26 color: #ffcc00;
27 font-size: 2ex;
27 }
28 }
28
29
29 .link, a {
30 .link, a {
30 color: #afdcec;
31 color: #afdcec;
31 }
32 }
32
33
33 .block {
34 .block {
34 display: inline-block;
35 display: inline-block;
35 vertical-align: top;
36 vertical-align: top;
36 }
37 }
37
38
38 .tag {
39 .tag {
39 color: #b4cfec;
40 color: #b4cfec;
40 }
41 }
41
42
42 .post_id {
43 .post_id {
43 color: #fff380;
44 color: #fff380;
44 }
45 }
45
46
46 .post, .dead_post, #posts-table {
47 .post, .dead_post, #posts-table {
47 background: #333;
48 background: #333;
48 margin: 5px;
49 margin: 5px;
49 padding: 10px;
50 padding: 10px;
50 border: solid 1px #888;
51 border: solid 1px #888;
51 clear: left;
52 clear: left;
52 word-wrap: break-word;
53 word-wrap: break-word;
53 }
54 }
54
55
55 .metadata {
56 .metadata {
56 padding-top: 5px;
57 padding-top: 5px;
57 margin-top: 10px;
58 margin-top: 10px;
58 border-top: solid 1px #666;
59 border-top: solid 1px #666;
59 font-size: 0.9em;
60 font-size: 0.9em;
60 color: #ddd;
61 color: #ddd;
61 }
62 }
62
63
63 .navigation_panel, .tag_info {
64 .navigation_panel, .tag_info {
64 background: #444;
65 background: #444;
65 margin: 5px;
66 margin: 5px;
66 padding: 10px;
67 padding: 10px;
67 border: solid 1px #888;
68 border: solid 1px #888;
68 color: #eee;
69 color: #eee;
69 }
70 }
70
71
71 .navigation_panel .link {
72 .navigation_panel .link {
72 border-right: 1px solid #fff;
73 border-right: 1px solid #fff;
73 font-weight: bold;
74 font-weight: bold;
74 margin-right: 1ex;
75 margin-right: 1ex;
75 padding-right: 1ex;
76 padding-right: 1ex;
76 }
77 }
77 .navigation_panel .link:last-child {
78 .navigation_panel .link:last-child {
78 border-left: 1px solid #fff;
79 border-left: 1px solid #fff;
79 border-right: none;
80 border-right: none;
80 float: right;
81 float: right;
81 margin-left: 1ex;
82 margin-left: 1ex;
82 margin-right: 0;
83 margin-right: 0;
83 padding-left: 1ex;
84 padding-left: 1ex;
84 padding-right: 0;
85 padding-right: 0;
85 }
86 }
86
87
87 .navigation_panel::after, .post::after {
88 .navigation_panel::after, .post::after {
88 clear: both;
89 clear: both;
89 content: ".";
90 content: ".";
90 display: block;
91 display: block;
91 height: 0;
92 height: 0;
92 line-height: 0;
93 line-height: 0;
93 visibility: hidden;
94 visibility: hidden;
94 }
95 }
95
96
96 p {
97 p {
97 margin-top: .5em;
98 margin-top: .5em;
98 margin-bottom: .5em;
99 margin-bottom: .5em;
99 }
100 }
100
101
101 .post-form-w {
102 .post-form-w {
102 display: table;
103 display: table;
103 background: #333344;
104 background: #333344;
104 border: solid 1px #888;
105 border: solid 1px #888;
105 color: #fff;
106 color: #fff;
106 padding: 10px;
107 padding: 10px;
107 margin: 5px;
108 margin: 5px;
108 }
109 }
109
110
110 .form-row {
111 .form-row {
111 display: table-row;
112 display: table-row;
112 }
113 }
113
114
114 .form-label, .form-input, .form-errors {
115 .form-label, .form-input, .form-errors {
115 display: table-cell;
116 display: table-cell;
116 }
117 }
117
118
118 .form-label {
119 .form-label {
119 padding: .25em 1ex .25em 0;
120 padding: .25em 1ex .25em 0;
120 vertical-align: top;
121 vertical-align: top;
121 }
122 }
122
123
123 .form-input {
124 .form-input {
124 padding: .25em 0;
125 padding: .25em 0;
125 }
126 }
126
127
127 .form-errors {
128 .form-errors {
128 padding-left: 1ex;
129 padding-left: 1ex;
129 font-weight: bold;
130 font-weight: bold;
130 vertical-align: middle;
131 vertical-align: middle;
131 }
132 }
132
133
133 .post-form input, .post-form textarea {
134 .post-form input, .post-form textarea {
134 background: #333;
135 background: #333;
135 color: #fff;
136 color: #fff;
136 border: solid 1px;
137 border: solid 1px;
137 padding: 0;
138 padding: 0;
138 width: 100%;
139 width: 100%;
139 font: medium sans;
140 font: medium sans;
140 }
141 }
141
142
142 .form-submit {
143 .form-submit {
143 border-bottom: 2px solid #ddd;
144 border-bottom: 2px solid #ddd;
144 margin-bottom: .5em;
145 margin-bottom: .5em;
145 padding-bottom: .5em;
146 padding-bottom: .5em;
146 }
147 }
147
148
148 .form-title {
149 .form-title {
149 font-weight: bold;
150 font-weight: bold;
150 font-size: 2.5ex;
151 font-size: 2.5ex;
151 text-decoration: underline;
152 text-decoration: underline;
152 }
153 }
153
154
154 input[type="submit"] {
155 input[type="submit"] {
155 background: #222;
156 background: #222;
156 border: solid 1px #fff;
157 border: solid 1px #fff;
157 color: #fff;
158 color: #fff;
158 }
159 }
159
160
160 blockquote {
161 blockquote {
161 border-left: solid 2px;
162 border-left: solid 2px;
162 padding-left: 5px;
163 padding-left: 5px;
163 color: #B1FB17;
164 color: #B1FB17;
164 margin: 0;
165 margin: 0;
165 }
166 }
166
167
167 .post > .image {
168 .post > .image {
168 float: left;
169 float: left;
169 margin: 0 1ex .5ex 0;
170 margin: 0 1ex .5ex 0;
170 min-width: 1px;
171 min-width: 1px;
171 text-align: center;
172 text-align: center;
172 display: table-row;
173 display: table-row;
173
174
174 height: 150px;
175 height: 150px;
175 }
176 }
176
177
177 .post > .metadata {
178 .post > .metadata {
178 clear: left;
179 clear: left;
179 }
180 }
180
181
181 .get {
182 .get {
182 font-weight: bold;
183 font-weight: bold;
183 color: #d55;
184 color: #d55;
184 }
185 }
185
186
186 * {
187 * {
187 text-decoration: none;
188 text-decoration: none;
188 }
189 }
189
190
190 .dead_post {
191 .dead_post {
191 background-color: #442222;
192 background-color: #442222;
192 }
193 }
193
194
194 .mark_btn {
195 .mark_btn {
195 padding: 2px 4px;
196 padding: 2px 4px;
196 border: 1px solid;
197 border: 1px solid;
197 }
198 }
198
199
199 .mark_btn:hover {
200 .mark_btn:hover {
200 background: #555;
201 background: #555;
201 }
202 }
202
203
203 .quote {
204 .quote {
204 color: #92cf38;
205 color: #92cf38;
205 font-style: italic;
206 font-style: italic;
206 }
207 }
207
208
208 .spoiler {
209 .spoiler {
209 background: white;
210 background: white;
210 color: white;
211 color: white;
211 }
212 }
212
213
213 .spoiler:hover {
214 .spoiler:hover {
214 color: black;
215 color: black;
215 }
216 }
216
217
217 .comment {
218 .comment {
218 color: #eb2;
219 color: #eb2;
219 font-style: italic;
220 font-style: italic;
220 }
221 }
221
222
222 a:hover {
223 a:hover {
223 text-decoration: underline;
224 text-decoration: underline;
224 }
225 }
225
226
226 .last-replies {
227 .last-replies {
227 margin-left: 3ex;
228 margin-left: 3ex;
228 }
229 }
229
230
230 .thread {
231 .thread {
231 margin-bottom: 3ex;
232 margin-bottom: 3ex;
232 }
233 }
233
234
234 .post:target {
235 .post:target {
235 border: solid 2px white;
236 border: solid 2px white;
236 }
237 }
237
238
238 pre{
239 pre{
239 white-space:pre-wrap
240 white-space:pre-wrap
240 }
241 }
241
242
242 li {
243 li {
243 list-style-position: inside;
244 list-style-position: inside;
244 }
245 }
245
246
246 .fancybox-skin {
247 .fancybox-skin {
247 position: relative;
248 position: relative;
248 background-color: #fff;
249 background-color: #fff;
249 color: #ddd;
250 color: #ddd;
250 text-shadow: none;
251 text-shadow: none;
251 }
252 }
252
253
253 .fancybox-image {
254 .fancybox-image {
254 border: 1px solid black;
255 border: 1px solid black;
255 }
256 }
256
257
257 .image-mode-tab {
258 .image-mode-tab {
258 background: #444;
259 background: #444;
259 color: #eee;
260 color: #eee;
260 display: table;
261 display: table;
261 margin: 5px;
262 margin: 5px;
262 padding: 5px;
263 padding: 5px;
263 border: 1px solid #888;
264 border: 1px solid #888;
264 }
265 }
265
266
266 .image-mode-tab > label {
267 .image-mode-tab > label {
267 margin: 0 1ex;
268 margin: 0 1ex;
268 }
269 }
269
270
270 .image-mode-tab > label > input {
271 .image-mode-tab > label > input {
271 margin-right: .5ex;
272 margin-right: .5ex;
272 }
273 }
273
274
274 #posts-table {
275 #posts-table {
275 margin: 5px;
276 margin: 5px;
276 }
277 }
277
278
278 .tag_info {
279 .tag_info {
279 display: table;
280 display: table;
280 }
281 }
281
282
282 .tag_info > h2 {
283 .tag_info > h2 {
283 margin: 0;
284 margin: 0;
284 }
285 }
285
286
286 .post-info {
287 .post-info {
287 color: #ddd;
288 color: #ddd;
288 }
289 }
289
290
290 .moderator_info {
291 .moderator_info {
291 color: #e99d41;
292 color: #e99d41;
292 border: dashed 1px;
293 border: dashed 1px;
293 padding: 3px;
294 padding: 3px;
294 }
295 }
295
296
296 .refmap {
297 .refmap {
297 font-size: 0.9em;
298 font-size: 0.9em;
298 color: #ccc;
299 color: #ccc;
299 margin-top: 1em;
300 margin-top: 1em;
300 }
301 }
301
302
302 input[type="submit"]:hover {
303 input[type="submit"]:hover {
303 background: #555;
304 background: #555;
304 }
305 }
305
306
306 .fav {
307 .fav {
307 color: yellow;
308 color: yellow;
308 }
309 }
309
310
310 .not_fav {
311 .not_fav {
311 color: #ccc;
312 color: #ccc;
312 }
313 }
313
314
314 .role {
315 .role {
315 text-decoration: underline;
316 text-decoration: underline;
316 }
317 }
317
318
318 .form-email {
319 .form-email {
319 display: none;
320 display: none;
320 }
321 }
321
322
322 .footer {
323 .footer {
323 margin: 5px;
324 margin: 5px;
324 }
325 }
325
326
326 .bar-value {
327 .bar-value {
327 background: rgba(50, 55, 164, 0.45);
328 background: rgba(50, 55, 164, 0.45);
328 font-size: 0.9em;
329 font-size: 0.9em;
329 height: 1.5em;
330 height: 1.5em;
330 }
331 }
331
332
332 .bar-bg {
333 .bar-bg {
333 position: relative;
334 position: relative;
334 border: solid 1px #888;
335 border: solid 1px #888;
335 margin: 5px;
336 margin: 5px;
336 overflow: hidden;
337 overflow: hidden;
337 }
338 }
338
339
339 .bar-text {
340 .bar-text {
340 padding: 2px;
341 padding: 2px;
341 position: absolute;
342 position: absolute;
342 left: 0;
343 left: 0;
343 top: 0;
344 top: 0;
344 }
345 }
345
346
346 .page_link {
347 .page_link {
347 display: table;
348 display: table;
348 background: #444;
349 background: #444;
349 margin: 5px;
350 margin: 5px;
350 border: solid 1px #888;
351 border: solid 1px #888;
351 padding: 5px;
352 padding: 5px;
352 font-weight: bolder;
353 font-weight: bolder;
353 color: #eee;
354 color: #eee;
354 }
355 }
General Comments 0
You need to be logged in to leave comments. Login now