Show More
@@ -63,7 +63,7 b'' | |||||
63 | <div class="message"> |
|
63 | <div class="message"> | |
64 | {% autoescape off %} |
|
64 | {% autoescape off %} | |
65 | {% if truncated %} |
|
65 | {% if truncated %} | |
66 | {{ post.get_text|truncatewords_html:50 }} |
|
66 | {{ post.get_text|truncatewords_html:50|truncate_lines:3 }} | |
67 | {% else %} |
|
67 | {% else %} | |
68 | {{ post.get_text }} |
|
68 | {{ post.get_text }} | |
69 | {% endif %} |
|
69 | {% endif %} |
@@ -1,6 +1,12 b'' | |||||
|
1 | import re | |||
1 | from django.shortcuts import get_object_or_404 |
|
2 | from django.shortcuts import get_object_or_404 | |
2 | from django import template |
|
3 | from django import template | |
3 |
|
4 | |||
|
5 | ELLIPSIZER = '...' | |||
|
6 | ||||
|
7 | REGEX_LINES = re.compile(r'(<div class="br"></div>)', re.U | re.S) | |||
|
8 | REGEX_TAG = re.compile(r'<(/)?([^ ]+?)(?:(\s*/)| .*?)?>', re.S) | |||
|
9 | ||||
4 |
|
10 | |||
5 | register = template.Library() |
|
11 | register = template.Library() | |
6 |
|
12 | |||
@@ -72,3 +78,68 b' def post_view(post, moderator=False, nee' | |||||
72 | 'truncated': truncated, |
|
78 | 'truncated': truncated, | |
73 | 'opening_post_id': opening_post_id, |
|
79 | 'opening_post_id': opening_post_id, | |
74 | } |
|
80 | } | |
|
81 | ||||
|
82 | ||||
|
83 | @register.filter(is_safe=True) | |||
|
84 | def truncate_lines(text, length): | |||
|
85 | if length <= 0: | |||
|
86 | return '' | |||
|
87 | ||||
|
88 | html4_singlets = ( | |||
|
89 | 'br', 'col', 'link', 'base', 'img', | |||
|
90 | 'param', 'area', 'hr', 'input' | |||
|
91 | ) | |||
|
92 | ||||
|
93 | # Count non-HTML chars/words and keep note of open tags | |||
|
94 | pos = 0 | |||
|
95 | end_text_pos = 0 | |||
|
96 | current_len = 0 | |||
|
97 | open_tags = [] | |||
|
98 | ||||
|
99 | while current_len <= length: | |||
|
100 | m = REGEX_LINES.search(text, pos) | |||
|
101 | if not m: | |||
|
102 | # Checked through whole string | |||
|
103 | break | |||
|
104 | pos = m.end(0) | |||
|
105 | if m.group(1): | |||
|
106 | # It's an actual non-HTML word or char | |||
|
107 | current_len += 1 | |||
|
108 | if current_len == length: | |||
|
109 | end_text_pos = pos | |||
|
110 | continue | |||
|
111 | # Check for tag | |||
|
112 | tag = REGEX_TAG.match(m.group(0)) | |||
|
113 | if not tag or current_len >= length: | |||
|
114 | # Don't worry about non tags or tags after our truncate point | |||
|
115 | continue | |||
|
116 | closing_tag, tagname, self_closing = tag.groups() | |||
|
117 | # Element names are always case-insensitive | |||
|
118 | tagname = tagname.lower() | |||
|
119 | if self_closing or tagname in html4_singlets: | |||
|
120 | pass | |||
|
121 | elif closing_tag: | |||
|
122 | # Check for match in open tags list | |||
|
123 | try: | |||
|
124 | i = open_tags.index(tagname) | |||
|
125 | except ValueError: | |||
|
126 | pass | |||
|
127 | else: | |||
|
128 | # SGML: An end tag closes, back to the matching start tag, | |||
|
129 | # all unclosed intervening start tags with omitted end tags | |||
|
130 | open_tags = open_tags[i + 1:] | |||
|
131 | else: | |||
|
132 | # Add it to the start of the open tags list | |||
|
133 | open_tags.insert(0, tagname) | |||
|
134 | ||||
|
135 | if current_len <= length: | |||
|
136 | return text | |||
|
137 | out = text[:end_text_pos] | |||
|
138 | ||||
|
139 | if not out.endswith(ELLIPSIZER): | |||
|
140 | out += ELLIPSIZER | |||
|
141 | # Close any tags still open | |||
|
142 | for tag in open_tags: | |||
|
143 | out += '</%s>' % tag | |||
|
144 | # Return string | |||
|
145 | return out |
General Comments 0
You need to be logged in to leave comments.
Login now