##// END OF EJS Templates
Changed parser to show quotes and reflinks as they are posted, not with borders and '#' character.
neko259 -
r90:8f1d0214 default
parent child Browse files
Show More
@@ -1,83 +1,85 b''
1 import markdown
1 import markdown
2 from markdown.inlinepatterns import Pattern
2 from markdown.inlinepatterns import Pattern
3 from markdown.util import etree
3 from markdown.util import etree
4
4
5 __author__ = 'neko259'
5 __author__ = 'neko259'
6
6
7
7
8 class AutolinkPattern(Pattern):
8 class AutolinkPattern(Pattern):
9 def handleMatch(self, m):
9 def handleMatch(self, m):
10 link_element = etree.Element('a')
10 link_element = etree.Element('a')
11 href = m.group(2)
11 href = m.group(2)
12 link_element.set('href', href)
12 link_element.set('href', href)
13 link_element.text = href
13 link_element.text = href
14
14
15 return link_element
15 return link_element
16
16
17
17
18 class QuotePattern(Pattern):
18 class QuotePattern(Pattern):
19 def handleMatch(self, m):
19 def handleMatch(self, m):
20 quote_element = etree.Element('span')
20 quote_element = etree.Element('span')
21 quote_element.set('class', 'quote')
21 quote_element.set('class', 'quote')
22 quote_element.text = m.group(3)
22 quote_element.text = m.group(2)
23
23
24 return quote_element
24 return quote_element
25
25
26
26
27 class ReflinkPattern(Pattern):
27 class ReflinkPattern(Pattern):
28 def handleMatch(self, m):
28 def handleMatch(self, m):
29 ref_element = etree.Element('a')
29 ref_element = etree.Element('a')
30 post_id = m.group(4)
30 post_id = m.group(4)
31 ref_element.set('href', '#' + str(post_id))
31 ref_element.set('href', '#' + str(post_id))
32 ref_element.text = '#' + post_id
32 ref_element.text = m.group(2)
33
33
34 return ref_element
34 return ref_element
35
35
36
36
37 class SpoilerPattern(Pattern):
37 class SpoilerPattern(Pattern):
38 def handleMatch(self, m):
38 def handleMatch(self, m):
39 quote_element = etree.Element('span')
39 quote_element = etree.Element('span')
40 quote_element.set('class', 'spoiler')
40 quote_element.set('class', 'spoiler')
41 quote_element.text = m.group(2)
41 quote_element.text = m.group(2)
42
42
43 return quote_element
43 return quote_element
44
44
45
45
46 class CommentPattern(Pattern):
46 class CommentPattern(Pattern):
47 def handleMatch(self, m):
47 def handleMatch(self, m):
48 quote_element = etree.Element('span')
48 quote_element = etree.Element('span')
49 quote_element.set('class', 'comment')
49 quote_element.set('class', 'comment')
50 quote_element.text = '//' + m.group(3)
50 quote_element.text = '//' + m.group(3)
51
51
52 return quote_element
52 return quote_element
53
53
54
54
55 class NeboardMarkdown(markdown.Extension):
55 class NeboardMarkdown(markdown.Extension):
56 AUTOLINK_PATTERN = r'(https?://\S+)'
56 AUTOLINK_PATTERN = r'(https?://\S+)'
57 QUOTE_PATTERN = r'^(>){1}(.+)'
57 QUOTE_PATTERN = r'^(?<!>)(>[^>]+)$'
58 REFLINK_PATTERN = r'((&gt;){2}(\d+))'
58 REFLINK_PATTERN = r'((>>)(\d+))'
59 SPOILER_PATTERN = r'%%(.+)%%'
59 SPOILER_PATTERN = r'%%(.+)%%'
60 COMMENT_PATTERN = r'^(//(.+))'
60 COMMENT_PATTERN = r'^(//(.+))'
61
61
62 def extendMarkdown(self, md, md_globals):
62 def extendMarkdown(self, md, md_globals):
63 autolink = AutolinkPattern(self.AUTOLINK_PATTERN, md)
63 autolink = AutolinkPattern(self.AUTOLINK_PATTERN, md)
64 quote = QuotePattern(self.QUOTE_PATTERN, md)
64 quote = QuotePattern(self.QUOTE_PATTERN, md)
65 reflink = ReflinkPattern(self.REFLINK_PATTERN, md)
65 reflink = ReflinkPattern(self.REFLINK_PATTERN, md)
66 spoiler = SpoilerPattern(self.SPOILER_PATTERN, md)
66 spoiler = SpoilerPattern(self.SPOILER_PATTERN, md)
67 comment = CommentPattern(self.COMMENT_PATTERN, md)
67 comment = CommentPattern(self.COMMENT_PATTERN, md)
68
68
69 md.inlinePatterns[u'autolink_ext'] = autolink
69 md.inlinePatterns[u'autolink_ext'] = autolink
70 md.inlinePatterns.add(u'reflink', reflink, '<entity')
71 md.inlinePatterns.add(u'quote', quote, '<entity')
72 md.inlinePatterns[u'spoiler'] = spoiler
70 md.inlinePatterns[u'spoiler'] = spoiler
73 md.inlinePatterns[u'comment'] = comment
71 md.inlinePatterns[u'comment'] = comment
72 md.inlinePatterns[u'reflink'] = reflink
73 md.inlinePatterns[u'quote'] = quote
74
75 del md.parser.blockprocessors['quote']
74
76
75
77
76 def makeExtension(configs=None):
78 def makeExtension(configs=None):
77 return NeboardMarkdown(configs=configs)
79 return NeboardMarkdown(configs=configs)
78
80
79 neboard_extension = makeExtension()
81 neboard_extension = makeExtension()
80
82
81
83
82 def markdown_extended(markup):
84 def markdown_extended(markup):
83 return markdown.markdown(markup, [neboard_extension])
85 return markdown.markdown(markup, [neboard_extension], safe_mode=True)
@@ -1,288 +1,288 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import re
3 import re
4 import time
4 import time
5 import math
5 import math
6
6
7 from django.db import models
7 from django.db import models
8 from django.http import Http404
8 from django.http import Http404
9 from django.utils import timezone
9 from django.utils import timezone
10 from markupfield.fields import MarkupField
10 from markupfield.fields import MarkupField
11
11
12 from neboard import settings
12 from neboard import settings
13 import thumbs
13 import thumbs
14
14
15 IMAGE_THUMB_SIZE = (200, 150)
15 IMAGE_THUMB_SIZE = (200, 150)
16
16
17 TITLE_MAX_LENGTH = 50
17 TITLE_MAX_LENGTH = 50
18
18
19 DEFAULT_MARKUP_TYPE = 'markdown'
19 DEFAULT_MARKUP_TYPE = 'markdown'
20
20
21 NO_PARENT = -1
21 NO_PARENT = -1
22 NO_IP = '0.0.0.0'
22 NO_IP = '0.0.0.0'
23 UNKNOWN_UA = ''
23 UNKNOWN_UA = ''
24 ALL_PAGES = -1
24 ALL_PAGES = -1
25 OPENING_POST_POPULARITY_WEIGHT = 2
25 OPENING_POST_POPULARITY_WEIGHT = 2
26 IMAGES_DIRECTORY = 'images/'
26 IMAGES_DIRECTORY = 'images/'
27 FILE_EXTENSION_DELIMITER = '.'
27 FILE_EXTENSION_DELIMITER = '.'
28
28
29 REGEX_PRETTY = re.compile(r'^\d(0)+$')
29 REGEX_PRETTY = re.compile(r'^\d(0)+$')
30 REGEX_SAME = re.compile(r'^(.)\1+$')
30 REGEX_SAME = re.compile(r'^(.)\1+$')
31
31
32
32
33 class PostManager(models.Manager):
33 class PostManager(models.Manager):
34 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
34 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
35 ip=NO_IP, tags=None):
35 ip=NO_IP, tags=None):
36 post = self.create(title=title,
36 post = self.create(title=title,
37 text=text,
37 text=text,
38 pub_time=timezone.now(),
38 pub_time=timezone.now(),
39 parent=parent_id,
39 parent=parent_id,
40 image=image,
40 image=image,
41 poster_ip=ip,
41 poster_ip=ip,
42 poster_user_agent=UNKNOWN_UA,
42 poster_user_agent=UNKNOWN_UA,
43 last_edit_time=timezone.now())
43 last_edit_time=timezone.now())
44
44
45 if tags:
45 if tags:
46 map(post.tags.add, tags)
46 map(post.tags.add, tags)
47
47
48 if parent_id != NO_PARENT:
48 if parent_id != NO_PARENT:
49 self._bump_thread(parent_id)
49 self._bump_thread(parent_id)
50 else:
50 else:
51 self._delete_old_threads()
51 self._delete_old_threads()
52
52
53 return post
53 return post
54
54
55 def delete_post(self, post):
55 def delete_post(self, post):
56 children = self.filter(parent=post.id)
56 children = self.filter(parent=post.id)
57 for child in children:
57 for child in children:
58 self.delete_post(child)
58 self.delete_post(child)
59 post.delete()
59 post.delete()
60
60
61 def delete_posts_by_ip(self, ip):
61 def delete_posts_by_ip(self, ip):
62 posts = self.filter(poster_ip=ip)
62 posts = self.filter(poster_ip=ip)
63 for post in posts:
63 for post in posts:
64 self.delete_post(post)
64 self.delete_post(post)
65
65
66 def get_threads(self, tag=None, page=ALL_PAGES,
66 def get_threads(self, tag=None, page=ALL_PAGES,
67 order_by='-last_edit_time'):
67 order_by='-last_edit_time'):
68 if tag:
68 if tag:
69 threads = self.filter(parent=NO_PARENT, tags=tag)
69 threads = self.filter(parent=NO_PARENT, tags=tag)
70
70
71 # TODO Throw error 404 if no threads for tag found?
71 # TODO Throw error 404 if no threads for tag found?
72 else:
72 else:
73 threads = self.filter(parent=NO_PARENT)
73 threads = self.filter(parent=NO_PARENT)
74
74
75 threads = threads.order_by(order_by)
75 threads = threads.order_by(order_by)
76
76
77 if page != ALL_PAGES:
77 if page != ALL_PAGES:
78 thread_count = len(threads)
78 thread_count = len(threads)
79
79
80 if page < self.get_thread_page_count(tag=tag):
80 if page < self.get_thread_page_count(tag=tag):
81 start_thread = page * settings.THREADS_PER_PAGE
81 start_thread = page * settings.THREADS_PER_PAGE
82 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
82 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
83 thread_count)
83 thread_count)
84 threads = threads[start_thread:end_thread]
84 threads = threads[start_thread:end_thread]
85
85
86 return threads
86 return threads
87
87
88 def get_thread(self, opening_post_id):
88 def get_thread(self, opening_post_id):
89 try:
89 try:
90 opening_post = self.get(id=opening_post_id, parent=NO_PARENT)
90 opening_post = self.get(id=opening_post_id, parent=NO_PARENT)
91 except Post.DoesNotExist:
91 except Post.DoesNotExist:
92 raise Http404
92 raise Http404
93
93
94 if opening_post.parent == NO_PARENT:
94 if opening_post.parent == NO_PARENT:
95 replies = self.filter(parent=opening_post_id)
95 replies = self.filter(parent=opening_post_id)
96
96
97 thread = [opening_post]
97 thread = [opening_post]
98 thread.extend(replies)
98 thread.extend(replies)
99
99
100 return thread
100 return thread
101
101
102 def exists(self, post_id):
102 def exists(self, post_id):
103 posts = self.filter(id=post_id)
103 posts = self.filter(id=post_id)
104
104
105 return posts.count() > 0
105 return posts.count() > 0
106
106
107 def get_thread_page_count(self, tag=None):
107 def get_thread_page_count(self, tag=None):
108 if tag:
108 if tag:
109 threads = self.filter(parent=NO_PARENT, tags=tag)
109 threads = self.filter(parent=NO_PARENT, tags=tag)
110 else:
110 else:
111 threads = self.filter(parent=NO_PARENT)
111 threads = self.filter(parent=NO_PARENT)
112
112
113 return int(math.ceil(threads.count() / float(
113 return int(math.ceil(threads.count() / float(
114 settings.THREADS_PER_PAGE)))
114 settings.THREADS_PER_PAGE)))
115
115
116 def _delete_old_threads(self):
116 def _delete_old_threads(self):
117 """
117 """
118 Preserves maximum thread count. If there are too many threads,
118 Preserves maximum thread count. If there are too many threads,
119 delete the old ones.
119 delete the old ones.
120 """
120 """
121
121
122 # TODO Move old threads to the archive instead of deleting them.
122 # TODO Move old threads to the archive instead of deleting them.
123 # Maybe make some 'old' field in the model to indicate the thread
123 # Maybe make some 'old' field in the model to indicate the thread
124 # must not be shown and be able for replying.
124 # must not be shown and be able for replying.
125
125
126 threads = self.get_threads()
126 threads = self.get_threads()
127 thread_count = len(threads)
127 thread_count = len(threads)
128
128
129 if thread_count > settings.MAX_THREAD_COUNT:
129 if thread_count > settings.MAX_THREAD_COUNT:
130 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
130 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
131 old_threads = threads[thread_count - num_threads_to_delete:]
131 old_threads = threads[thread_count - num_threads_to_delete:]
132
132
133 for thread in old_threads:
133 for thread in old_threads:
134 self.delete_post(thread)
134 self.delete_post(thread)
135
135
136 def _bump_thread(self, thread_id):
136 def _bump_thread(self, thread_id):
137 thread = self.get(id=thread_id)
137 thread = self.get(id=thread_id)
138
138
139 if thread.can_bump():
139 if thread.can_bump():
140 thread.last_edit_time = timezone.now()
140 thread.last_edit_time = timezone.now()
141 thread.save()
141 thread.save()
142
142
143
143
144 class TagManager(models.Manager):
144 class TagManager(models.Manager):
145 def get_not_empty_tags(self):
145 def get_not_empty_tags(self):
146 all_tags = self.all().order_by('name')
146 all_tags = self.all().order_by('name')
147 tags = []
147 tags = []
148 for tag in all_tags:
148 for tag in all_tags:
149 if not tag.is_empty():
149 if not tag.is_empty():
150 tags.append(tag)
150 tags.append(tag)
151
151
152 return tags
152 return tags
153
153
154 def get_popular_tags(self):
154 def get_popular_tags(self):
155 all_tags = self.get_not_empty_tags()
155 all_tags = self.get_not_empty_tags()
156
156
157 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
157 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
158 reverse=True)
158 reverse=True)
159
159
160 return sorted_tags[:settings.POPULAR_TAGS]
160 return sorted_tags[:settings.POPULAR_TAGS]
161
161
162
162
163 class Tag(models.Model):
163 class Tag(models.Model):
164 """
164 """
165 A tag is a text node assigned to the post. The tag serves as a board
165 A tag is a text node assigned to the post. The tag serves as a board
166 section. There can be multiple tags for each message
166 section. There can be multiple tags for each message
167 """
167 """
168
168
169 objects = TagManager()
169 objects = TagManager()
170
170
171 name = models.CharField(max_length=100)
171 name = models.CharField(max_length=100)
172 # TODO Connect the tag to its posts to check the number of threads for
172 # TODO Connect the tag to its posts to check the number of threads for
173 # the tag.
173 # the tag.
174
174
175 def __unicode__(self):
175 def __unicode__(self):
176 return self.name
176 return self.name
177
177
178 def is_empty(self):
178 def is_empty(self):
179 return self.get_post_count() == 0
179 return self.get_post_count() == 0
180
180
181 def get_post_count(self):
181 def get_post_count(self):
182 posts_with_tag = Post.objects.get_threads(tag=self)
182 posts_with_tag = Post.objects.get_threads(tag=self)
183 return posts_with_tag.count()
183 return posts_with_tag.count()
184
184
185 def get_popularity(self):
185 def get_popularity(self):
186 posts_with_tag = Post.objects.get_threads(tag=self)
186 posts_with_tag = Post.objects.get_threads(tag=self)
187 reply_count = 0
187 reply_count = 0
188 for post in posts_with_tag:
188 for post in posts_with_tag:
189 reply_count += post.get_reply_count()
189 reply_count += post.get_reply_count()
190 reply_count += OPENING_POST_POPULARITY_WEIGHT
190 reply_count += OPENING_POST_POPULARITY_WEIGHT
191
191
192 return reply_count
192 return reply_count
193
193
194
194
195 class Post(models.Model):
195 class Post(models.Model):
196 """A post is a message."""
196 """A post is a message."""
197
197
198 objects = PostManager()
198 objects = PostManager()
199
199
200 def _update_image_filename(self, filename):
200 def _update_image_filename(self, filename):
201 """Get unique image filename"""
201 """Get unique image filename"""
202
202
203 path = IMAGES_DIRECTORY
203 path = IMAGES_DIRECTORY
204 new_name = str(int(time.mktime(time.gmtime())))
204 new_name = str(int(time.mktime(time.gmtime())))
205 new_name += str(int(random() * 1000))
205 new_name += str(int(random() * 1000))
206 new_name += FILE_EXTENSION_DELIMITER
206 new_name += FILE_EXTENSION_DELIMITER
207 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
207 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
208
208
209 return os.path.join(path, new_name)
209 return os.path.join(path, new_name)
210
210
211 title = models.CharField(max_length=TITLE_MAX_LENGTH)
211 title = models.CharField(max_length=TITLE_MAX_LENGTH)
212 pub_time = models.DateTimeField()
212 pub_time = models.DateTimeField()
213 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
213 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
214 escape_html=True)
214 escape_html=False)
215 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
215 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
216 blank=True, sizes=(IMAGE_THUMB_SIZE,))
216 blank=True, sizes=(IMAGE_THUMB_SIZE,))
217 poster_ip = models.IPAddressField()
217 poster_ip = models.IPAddressField()
218 poster_user_agent = models.TextField()
218 poster_user_agent = models.TextField()
219 parent = models.BigIntegerField()
219 parent = models.BigIntegerField()
220 tags = models.ManyToManyField(Tag)
220 tags = models.ManyToManyField(Tag)
221 last_edit_time = models.DateTimeField()
221 last_edit_time = models.DateTimeField()
222
222
223 def __unicode__(self):
223 def __unicode__(self):
224 return '#' + str(self.id) + ' ' + self.title + ' (' + self.text.raw + \
224 return '#' + str(self.id) + ' ' + self.title + ' (' + self.text.raw + \
225 ')'
225 ')'
226
226
227 def _get_replies(self):
227 def _get_replies(self):
228 return Post.objects.filter(parent=self.id)
228 return Post.objects.filter(parent=self.id)
229
229
230 def get_reply_count(self):
230 def get_reply_count(self):
231 return self._get_replies().count()
231 return self._get_replies().count()
232
232
233 def get_images_count(self):
233 def get_images_count(self):
234 images_count = 1 if self.image else 0
234 images_count = 1 if self.image else 0
235 for reply in self._get_replies():
235 for reply in self._get_replies():
236 if reply.image:
236 if reply.image:
237 images_count += 1
237 images_count += 1
238
238
239 return images_count
239 return images_count
240
240
241 def get_gets_count(self):
241 def get_gets_count(self):
242 gets_count = 1 if self.is_get() else 0
242 gets_count = 1 if self.is_get() else 0
243 for reply in self._get_replies():
243 for reply in self._get_replies():
244 if reply.is_get():
244 if reply.is_get():
245 gets_count += 1
245 gets_count += 1
246
246
247 return gets_count
247 return gets_count
248
248
249 def is_get(self):
249 def is_get(self):
250 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
250 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
251
251
252 first = self.id == 1
252 first = self.id == 1
253
253
254 id_str = str(self.id)
254 id_str = str(self.id)
255 pretty = REGEX_PRETTY.match(id_str)
255 pretty = REGEX_PRETTY.match(id_str)
256 same_digits = REGEX_SAME.match(id_str)
256 same_digits = REGEX_SAME.match(id_str)
257
257
258 return first or pretty or same_digits
258 return first or pretty or same_digits
259
259
260 def can_bump(self):
260 def can_bump(self):
261 """Check if the thread can be bumped by replying"""
261 """Check if the thread can be bumped by replying"""
262
262
263 replies_count = len(Post.objects.get_thread(self.id))
263 replies_count = len(Post.objects.get_thread(self.id))
264
264
265 return replies_count <= settings.MAX_POSTS_PER_THREAD
265 return replies_count <= settings.MAX_POSTS_PER_THREAD
266
266
267 def get_last_replies(self):
267 def get_last_replies(self):
268 if settings.LAST_REPLIES_COUNT > 0:
268 if settings.LAST_REPLIES_COUNT > 0:
269 reply_count = self.get_reply_count()
269 reply_count = self.get_reply_count()
270
270
271 if reply_count > 0:
271 if reply_count > 0:
272 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
272 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
273 reply_count)
273 reply_count)
274 last_replies = self._get_replies()[reply_count
274 last_replies = self._get_replies()[reply_count
275 - reply_count_to_show:]
275 - reply_count_to_show:]
276
276
277 return last_replies
277 return last_replies
278
278
279
279
280 class Admin(models.Model):
280 class Admin(models.Model):
281 """
281 """
282 Model for admin users
282 Model for admin users
283 """
283 """
284 name = models.CharField(max_length=100)
284 name = models.CharField(max_length=100)
285 password = models.CharField(max_length=100)
285 password = models.CharField(max_length=100)
286
286
287 def __unicode__(self):
287 def __unicode__(self):
288 return self.name + '/' + '*' * len(self.password)
288 return self.name + '/' + '*' * len(self.password)
@@ -1,272 +1,271 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 }
27 }
28
28
29 .link, a {
29 .link, a {
30 color: #afdcec;
30 color: #afdcec;
31 }
31 }
32
32
33 .block {
33 .block {
34 display: inline-block;
34 display: inline-block;
35 vertical-align: top;
35 vertical-align: top;
36 }
36 }
37
37
38 .tag {
38 .tag {
39 color: #b4cfec;
39 color: #b4cfec;
40 }
40 }
41
41
42 .post_id {
42 .post_id {
43 color: #fff380;
43 color: #fff380;
44 }
44 }
45
45
46 .post, .dead_post {
46 .post, .dead_post {
47 background: #333;
47 background: #333;
48 margin: 5px;
48 margin: 5px;
49 padding: 10px;
49 padding: 10px;
50 border-radius: 5px;
50 border-radius: 5px;
51 clear: left;
51 clear: left;
52 word-wrap: break-word;
52 word-wrap: break-word;
53 }
53 }
54
54
55 .metadata {
55 .metadata {
56 padding: 5px;
56 padding: 5px;
57 margin-top: 10px;
57 margin-top: 10px;
58 border: solid 1px #666;
58 border: solid 1px #666;
59 font-size: 0.9em;
59 font-size: 0.9em;
60 color: #ddd;
60 color: #ddd;
61 display: table;
61 display: table;
62 }
62 }
63
63
64 .navigation_panel, .tag_info {
64 .navigation_panel, .tag_info {
65 background: #444;
65 background: #444;
66 margin: 5px;
66 margin: 5px;
67 padding: 10px;
67 padding: 10px;
68 border-radius: 5px;
68 border-radius: 5px;
69 color: #eee;
69 color: #eee;
70 }
70 }
71
71
72 .navigation_panel .link {
72 .navigation_panel .link {
73 border-right: 1px solid #fff;
73 border-right: 1px solid #fff;
74 font-weight: bold;
74 font-weight: bold;
75 margin-right: 1ex;
75 margin-right: 1ex;
76 padding-right: 1ex;
76 padding-right: 1ex;
77 }
77 }
78 .navigation_panel .link:last-child {
78 .navigation_panel .link:last-child {
79 border-left: 1px solid #fff;
79 border-left: 1px solid #fff;
80 border-right: none;
80 border-right: none;
81 float: right;
81 float: right;
82 margin-left: 1ex;
82 margin-left: 1ex;
83 margin-right: 0;
83 margin-right: 0;
84 padding-left: 1ex;
84 padding-left: 1ex;
85 padding-right: 0;
85 padding-right: 0;
86 }
86 }
87
87
88 .navigation_panel::after, .post::after {
88 .navigation_panel::after, .post::after {
89 clear: both;
89 clear: both;
90 content: ".";
90 content: ".";
91 display: block;
91 display: block;
92 height: 0;
92 height: 0;
93 line-height: 0;
93 line-height: 0;
94 visibility: hidden;
94 visibility: hidden;
95 }
95 }
96
96
97 p {
97 p {
98 margin-top: .5em;
98 margin-top: .5em;
99 margin-bottom: .5em;
99 margin-bottom: .5em;
100 }
100 }
101
101
102 .post-form-w {
102 .post-form-w {
103 display: table;
103 display: table;
104 background: #333344;
104 background: #333344;
105 border-radius: 5px;
105 border-radius: 5px;
106 color: #fff;
106 color: #fff;
107 padding: 10px;
107 padding: 10px;
108 margin: 5px
108 margin: 5px
109 }
109 }
110
110
111 .form-row {
111 .form-row {
112 display: table-row;
112 display: table-row;
113 }
113 }
114
114
115 .form-label, .form-input, .form-errors {
115 .form-label, .form-input, .form-errors {
116 display: table-cell;
116 display: table-cell;
117 }
117 }
118
118
119 .form-label {
119 .form-label {
120 padding: .25em 1ex .25em 0;
120 padding: .25em 1ex .25em 0;
121 vertical-align: top;
121 vertical-align: top;
122 }
122 }
123
123
124 .form-input {
124 .form-input {
125 padding: .25em 0;
125 padding: .25em 0;
126 }
126 }
127
127
128 .form-errors {
128 .form-errors {
129 padding-left: 1ex;
129 padding-left: 1ex;
130 font-weight: bold;
130 font-weight: bold;
131 vertical-align: top;
131 vertical-align: top;
132 }
132 }
133
133
134 .post-form input, .post-form textarea {
134 .post-form input, .post-form textarea {
135 background: #333;
135 background: #333;
136 color: #fff;
136 color: #fff;
137 border: solid 1px;
137 border: solid 1px;
138 padding: 0;
138 padding: 0;
139 width: 100%;
139 width: 100%;
140 }
140 }
141
141
142 .form-submit {
142 .form-submit {
143 border-bottom: 2px solid #ddd;
143 border-bottom: 2px solid #ddd;
144 margin-bottom: .5em;
144 margin-bottom: .5em;
145 padding-bottom: .5em;
145 padding-bottom: .5em;
146 }
146 }
147
147
148 .form-title {
148 .form-title {
149 font-weight: bold;
149 font-weight: bold;
150 }
150 }
151
151
152 input[type="submit"] {
152 input[type="submit"] {
153 background: #222;
153 background: #222;
154 border: solid 1px #fff;
154 border: solid 1px #fff;
155 color: #fff;
155 color: #fff;
156 }
156 }
157
157
158 blockquote {
158 blockquote {
159 border-left: solid 2px;
159 border-left: solid 2px;
160 padding-left: 5px;
160 padding-left: 5px;
161 color: #B1FB17;
161 color: #B1FB17;
162 margin: 0;
162 margin: 0;
163 }
163 }
164
164
165 .post > .image {
165 .post > .image {
166 float: left;
166 float: left;
167 margin: 0 1ex .5ex 0;
167 margin: 0 1ex .5ex 0;
168 min-width: 1px;
168 min-width: 1px;
169 height: 150px;
169 height: 150px;
170 text-align: center;
170 text-align: center;
171 display: table-row;
171 display: table-row;
172 }
172 }
173
173
174 .post > .metadata {
174 .post > .metadata {
175 clear: left;
175 clear: left;
176 }
176 }
177
177
178 .get {
178 .get {
179 font-weight: bold;
179 font-weight: bold;
180 color: #d55;
180 color: #d55;
181 }
181 }
182
182
183 * {
183 * {
184 text-decoration: none;
184 text-decoration: none;
185 }
185 }
186
186
187 .dead_post {
187 .dead_post {
188 background-color: #442222;
188 background-color: #442222;
189 }
189 }
190
190
191 .quote {
191 .quote {
192 color: greenyellow;
192 color: #92cf38;
193 padding-left: 5px;
193 padding-left: 5px;
194 border-left: solid 2px greenyellow;
195 }
194 }
196
195
197 .spoiler {
196 .spoiler {
198 background: white;
197 background: white;
199 color: white;
198 color: white;
200 }
199 }
201
200
202 .spoiler:hover {
201 .spoiler:hover {
203 background: black;
202 background: black;
204 }
203 }
205
204
206 .comment {
205 .comment {
207 color: darkseagreen;
206 color: #5dbcb7;
208 }
207 }
209
208
210 a:hover {
209 a:hover {
211 text-decoration: underline;
210 text-decoration: underline;
212 }
211 }
213
212
214 .last-replies {
213 .last-replies {
215 margin-left: 3ex;
214 margin-left: 3ex;
216 }
215 }
217
216
218 .thread {
217 .thread {
219 margin-bottom: 3ex;
218 margin-bottom: 3ex;
220 }
219 }
221
220
222 .post:target {
221 .post:target {
223 border: solid 2px white;
222 border: solid 2px white;
224 }
223 }
225
224
226 pre{
225 pre{
227 white-space:pre-wrap
226 white-space:pre-wrap
228 }
227 }
229
228
230 li {
229 li {
231 list-style-position: inside;
230 list-style-position: inside;
232 }
231 }
233
232
234 .fancybox-skin {
233 .fancybox-skin {
235 position: relative;
234 position: relative;
236 background-color: #fff;
235 background-color: #fff;
237 color: #ddd;
236 color: #ddd;
238 text-shadow: none;
237 text-shadow: none;
239 }
238 }
240
239
241 .fancybox-image {
240 .fancybox-image {
242 border: 1px solid black;
241 border: 1px solid black;
243 }
242 }
244
243
245 .image-mode-tab {
244 .image-mode-tab {
246 background: #444;
245 background: #444;
247 color: #eee;
246 color: #eee;
248 display: table;
247 display: table;
249 margin: 5px;
248 margin: 5px;
250 padding: 5px;
249 padding: 5px;
251 border-radius: 5px;
250 border-radius: 5px;
252 }
251 }
253
252
254 .image-mode-tab > label {
253 .image-mode-tab > label {
255 margin: 0 1ex;
254 margin: 0 1ex;
256 }
255 }
257
256
258 .image-mode-tab > label > input {
257 .image-mode-tab > label > input {
259 margin-right: .5ex;
258 margin-right: .5ex;
260 }
259 }
261
260
262 #posts-table {
261 #posts-table {
263 margin: 5px;
262 margin: 5px;
264 }
263 }
265
264
266 .tag_info {
265 .tag_info {
267 display: table;
266 display: table;
268 }
267 }
269
268
270 .tag_info > h2 {
269 .tag_info > h2 {
271 margin: 0;
270 margin: 0;
272 } No newline at end of file
271 }
General Comments 0
You need to be logged in to leave comments. Login now