0
+# from django.conf import settings
0
+from django.conf import settings
0
+from django import template
0
+from django.template.defaultfilters import stringfilter
0
+register = template.Library()
0
+ """Wraps apersands in HTML with ``<span class="amp">`` so they can be
0
+ styled with CSS. Apersands are also normalized to ``&``. Requires
0
+ ampersands to have whitespace or an `` `` on both sides.
0
+ 'One <span class="amp">&</span> two'
0
+ >>> amp('One & two')
0
+ 'One <span class="amp">&</span> two'
0
+ >>> amp('One & two')
0
+ 'One <span class="amp">&</span> two'
0
+ >>> amp('One & two')
0
+ 'One <span class="amp">&</span> two'
0
+ It won't mess up & that are already wrapped, in entities or URLs
0
+ >>> amp('One <span class="amp">&</span> two')
0
+ 'One <span class="amp">&</span> two'
0
+ >>> amp('“this” & <a href="/?that&test">that</a>')
0
+ '“this” <span class="amp">&</span> <a href="/?that&test">that</a>'
0
+ It should ignore standalone amps that are in attributes
0
+ >>> amp('<link href="xyz.html" title="One & Two">xyz</link>')
0
+ '<link href="xyz.html" title="One & Two">xyz</link>'
0
+ # tag_pattern from http://haacked.com/archive/2004/10/25/usingregularexpressionstomatchhtml.aspx
0
+ # it kinda sucks but it fixes the standalone amps in attributes bug
0
+ tag_pattern = '</?\w+((\s+\w+(\s*=\s*(?:".*?"|\'.*?\'|[^\'">\s]+))?)+\s*|\s*)/?>'
0
+ amp_finder = re.compile(r"(\s| )(&|&|&\#38;)(\s| )")
0
+ intra_tag_finder = re.compile(r'(?P<prefix>(%s)?)(?P<text>([^<]*))(?P<suffix>(%s)?)' % (tag_pattern, tag_pattern))
0
+ def _amp_process(groups):
0
+ prefix = groups.group('prefix') or ''
0
+ text = amp_finder.sub(r"""\1<span class="amp">&</span>\3""", groups.group('text'))
0
+ suffix = groups.group('suffix') or ''
0
+ return prefix + text + suffix
0
+ return intra_tag_finder.sub(_amp_process, text)
0
+# amp = stringfilter(amp)
0
+ """Wraps multiple capital letters in ``<span class="caps">``
0
+ so they can be styled with CSS.
0
+ >>> caps("A message from KU")
0
+ 'A message from <span class="caps">KU</span>'
0
+ Uses the smartypants tokenizer to not screw with HTML or with tags it shouldn't.
0
+ >>> caps("<PRE>CAPS</pre> more CAPS")
0
+ '<PRE>CAPS</pre> more <span class="caps">CAPS</span>'
0
+ >>> caps("A message from 2KU2 with digits")
0
+ 'A message from <span class="caps">2KU2</span> with digits'
0
+ >>> caps("Dotted caps followed by spaces should never include them in the wrap D.O.T. like so.")
0
+ 'Dotted caps followed by spaces should never include them in the wrap <span class="caps">D.O.T.</span> like so.'
0
+ >>> caps("<i>D.O.T.</i>HE34T<b>RFID</b>")
0
+ '<i><span class="caps">D.O.T.</span></i><span class="caps">HE34T</span><b><span class="caps">RFID</span></b>'
0
+ raise template.TemplateSyntaxError, "Error in {% caps %} filter: The Python SmartyPants library isn't installed."
0
+ tokens = smartypants._tokenize(text)
0
+ in_skipped_tag = False
0
+ cap_finder = re.compile(r"""(
0
+ (\b[A-Z\d]* # Group 2: Any amount of caps and digits
0
+ [A-Z]\d*[A-Z] # A cap string much at least include two caps (but they can have digits between them)
0
+ [A-Z\d]*\b) # Any amount of caps and digits
0
+ | (\b[A-Z]+\.\s? # OR: Group 3: Some caps, followed by a '.' and an optional space
0
+ (?:[A-Z]+\.\s?)+) # Followed by the same thing at least once more
0
+ def _cap_wrapper(matchobj):
0
+ """This is necessary to keep dotted cap strings to pick up extra spaces"""
0
+ return """<span class="caps">%s</span>""" % matchobj.group(2)
0
+ if matchobj.group(3)[-1] == " ":
0
+ caps = matchobj.group(3)[:-1]
0
+ caps = matchobj.group(3)
0
+ return """<span class="caps">%s</span>%s""" % (caps, tail)
0
+ tags_to_skip_regex = re.compile("<(/)?(?:pre|code|kbd|script|math)[^>]*>", re.IGNORECASE)
0
+ # Don't mess with tags.
0
+ result.append(token[1])
0
+ close_match = tags_to_skip_regex.match(token[1])
0
+ if close_match and close_match.group(1) == None:
0
+ in_skipped_tag = False
0
+ result.append(token[1])
0
+ result.append(cap_finder.sub(_cap_wrapper, token[1]))
0
+ return "".join(result)
0
+# caps = stringfilter(caps)
0
+def initial_quotes(text):
0
+ """Wraps initial quotes in ``class="dquo"`` for double quotes or
0
+ ``class="quo"`` for single quotes. Works in these block tags ``(h1-h6, p, li, dt, dd)``
0
+ and also accounts for potential opening inline elements ``a, em, strong, span, b, i``
0
+ >>> initial_quotes('"With primes"')
0
+ '<span class="dquo">"</span>With primes"'
0
+ >>> initial_quotes("'With single primes'")
0
+ '<span class="quo">\\'</span>With single primes\\''
0
+ >>> initial_quotes('<a href="#">"With primes and a link"</a>')
0
+ '<a href="#"><span class="dquo">"</span>With primes and a link"</a>'
0
+ >>> initial_quotes('“With smartypanted quotes”')
0
+ '<span class="dquo">“</span>With smartypanted quotes”'
0
+ quote_finder = re.compile(r"""((<(p|h[1-6]|li|dt|dd)[^>]*>|^) # start with an opening p, h1-6, li, dd, dt or the start of the string
0
+ \s* # optional white space!
0
+ (<(a|em|span|strong|i|b)[^>]*>\s*)*) # optional opening inline tags, with more optional white space for each.
0
+ (("|“|&\#8220;)|('|‘|&\#8216;)) # Find me a quote! (only need to find the left quotes and the primes)
0
+ # double quotes are in group 7, singles in group 8
0
+ def _quote_wrapper(matchobj):
0
+ quote = matchobj.group(7)
0
+ quote = matchobj.group(8)
0
+ return """%s<span class="%s">%s</span>""" % (matchobj.group(1), classname, quote)
0
+ return quote_finder.sub(_quote_wrapper, text)
0
+# initial_quotes = stringfilter(initial_quotes)
0
+ """Applies smarty pants to curl quotes.
0
+ >>> smartypants('The "Green" man')
0
+ 'The “Green” man'
0
+ raise template.TemplateSyntaxError, "Error in {% smartypants %} filter: The Python smartypants library isn't installed."
0
+ return smartypants.smartyPants(text)
0
+# smartypants = stringfilter(smartypants)
0
+ """The super typography filter
0
+ Applies the following filters: widont, smartypants, caps, amp, initial_quotes
0
+ >>> typogrify('<h2>"Jayhawks" & KU fans act extremely obnoxiously</h2>')
0
+ '<h2><span class="dquo">“</span>Jayhawks” <span class="amp">&</span> <span class="caps">KU</span> fans act extremely obnoxiously</h2>'
0
+ text = smartypants(text)
0
+ text = initial_quotes(text)
0
+# typogrify = stringfilter(typogrify)
0
+ """Replaces the space between the last two words in a string with `` ``
0
+ Works in these block tags ``(h1-h6, p, li, dd, dt)`` and also accounts for
0
+ potential closing inline elements ``a, em, strong, span, b, i``
0
+ >>> widont('A very simple test')
0
+ 'A very simple test'
0
+ Single word items shouldn't be changed
0
+ >>> widont('<ul><li>Test</p></li><ul>')
0
+ '<ul><li>Test</p></li><ul>'
0
+ >>> widont('<ul><li> Test</p></li><ul>')
0
+ '<ul><li> Test</p></li><ul>'
0
+ >>> widont('<p>In a couple of paragraphs</p><p>paragraph two</p>')
0
+ '<p>In a couple of paragraphs</p><p>paragraph two</p>'
0
+ >>> widont('<h1><a href="#">In a link inside a heading</i> </a></h1>')
0
+ '<h1><a href="#">In a link inside a heading</i> </a></h1>'
0
+ >>> widont('<h1><a href="#">In a link</a> followed by other text</h1>')
0
+ '<h1><a href="#">In a link</a> followed by other text</h1>'
0
+ Empty HTMLs shouldn't error
0
+ >>> widont('<h1><a href="#"></a></h1>')
0
+ '<h1><a href="#"></a></h1>'
0
+ >>> widont('<div>Divs get no love!</div>')
0
+ '<div>Divs get no love!</div>'
0
+ >>> widont('<pre>Neither do PREs</pre>')
0
+ '<pre>Neither do PREs</pre>'
0
+ >>> widont('<div><p>But divs with paragraphs do!</p></div>')
0
+ '<div><p>But divs with paragraphs do!</p></div>'
0
+ widont_finder = re.compile(r"""((?:</?(?:a|em|span|strong|i|b)[^>]*>)|[^<>\s]) # must be proceeded by an approved inline opening or closing tag or a nontag/nonspace
0
+ \s+ # the space to replace
0
+ ([^<>\s]+ # must be flollowed by non-tag non-space characters
0
+ \s* # optional white space!
0
+ (</(a|em|span|strong|i|b)>\s*)* # optional closing inline tags with optional white space after each
0
+ ((</(p|h[1-6]|li|dt|dd)>)|$)) # end with a closing p, h1-6, li or the end of the string
0
+ return widont_finder.sub(r'\1 \2', text)
0
+# widont = stringfilter(widont)
0
+register.filter('amp', amp)
0
+register.filter('caps', caps)
0
+register.filter('initial_quotes', initial_quotes)
0
+register.filter('smartypants', smartypants)
0
+register.filter('typogrify', typogrify)
0
+register.filter('widont', widont)
0
+if __name__ == "__main__":
Comments
No one has commented yet.