diff -urN -x '*pyc' kid-0.9.3/kid/djangohtml.py /usr/lib/python2.4/site-packages/kid-0.9.3-py2.4.egg/kid/djangohtml.py
--- kid-0.9.3/kid/djangohtml.py 1970-01-01 02:00:00.000000000 +0200
+++ /usr/lib/python2.4/site-packages/kid-0.9.3-py2.4.egg/kid/djangohtml.py 2006-12-04 16:15:42.000000000 +0200
@@ -0,0 +1,115 @@
+"HTML utilities suitable for global use."
+
+import re, string
+
+# Configuration for urlize() function
+LEADING_PUNCTUATION = ['(', '<', '<']
+TRAILING_PUNCTUATION = ['.', ',', ')', '>', '\n', '>']
+
+# list of possible strings used for bullets in bulleted lists
+DOTS = ['·', '*', '\xe2\x80\xa2', '', '•', '•']
+
+unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)')
+word_split_re = re.compile(r'(\s+)')
+punctuation_re = re.compile('^(?P (?:%s).*?[a-zA-Z].*? (?: |\s| and %s
|<\/i>|<\/b>|<\/em>|<\/strong>|<\/?smallcaps>|<\/?uppercase>)', re.IGNORECASE)
+hard_coded_bullets_re = re.compile(r'((?:
)*?
s"
+ value = re.sub(r'\r\n|\r|\n', '\n', value) # normalize newlines
+ paras = re.split('\n{2,}', value)
+ paras = ['
') for p in paras]
+ return '\n\n'.join(paras)
+
+def strip_tags(value):
+ "Returns the given HTML with all tags stripped"
+ return re.sub(r'<[^>]*?>', '', value)
+
+def strip_spaces_between_tags(value):
+ "Returns the given HTML with spaces between tags normalized to a single space"
+ return re.sub(r'>\s+<', '> <', value)
+
+def strip_entities(value):
+ "Returns the given HTML with all entities (&something;) stripped"
+ return re.sub(r'&(?:\w+|#\d);', '', value)
+
+def fix_ampersands(value):
+ "Returns the given HTML with all unencoded ampersands encoded correctly"
+ return unencoded_ampersands_re.sub('&', value)
+
+def urlize(text, trim_url_limit=None, nofollow=False):
+ """
+ Converts any URLs in text into clickable links. Works on http://, https:// and
+ www. links. Links can have trailing punctuation (periods, commas, close-parens)
+ and leading punctuation (opening parens) and it'll still do the right thing.
+
+ If trim_url_limit is not None, the URLs in link text will be limited to
+ trim_url_limit characters.
+
+ If nofollow is True, the URLs in link text will get a rel="nofollow" attribute.
+ """
+ trim_url = lambda x, limit=trim_url_limit: limit is not None and (x[:limit] + (len(x) >=limit and '...' or '')) or x
+ words = word_split_re.split(text)
+ nofollow_attr = nofollow and ' rel="nofollow"' or ''
+ for i, word in enumerate(words):
+ match = punctuation_re.match(word)
+ if match:
+ lead, middle, trail = match.groups()
+ if middle.startswith('www.') or ('@' not in middle and not middle.startswith('http://') and \
+ len(middle) > 0 and middle[0] in string.letters + string.digits and \
+ (middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))):
+ middle = '%s' % (middle, nofollow_attr, trim_url(anchor))
+ if middle.startswith('http://') or middle.startswith('https://'):
+ middle = '%s' % (middle, nofollow_attr, trim_url(middle))
+ if '@' in middle and not middle.startswith('www.') and not ':' in middle \
+ and simple_email_re.match(middle):
+ middle = '%s' % (middle, middle)
+ if lead + middle + trail != word:
+ words[i] = lead + middle + trail
+ return ''.join(words)
+
+def clean_html(text):
+ """
+ Cleans the given HTML. Specifically, it does the following:
+ * Converts and to and .
+ * Encodes all ampersands correctly.
+ * Removes all "target" attributes from tags.
+ * Removes extraneous HTML, such as presentational tags that open and
+ immediately close and
.
+ * Converts hard-coded bullets into HTML unordered lists.
+ * Removes stuff like "
.
+ text = html_gunk_re.sub('', text)
+ # Convert hard-coded bullets into HTML unordered lists.
+ def replace_p_tags(match):
+ s = match.group().replace('
%s' % d, '
", but only if it's at the bottom of the text. + text = trailing_empty_content_re.sub('', text) + return text + diff -urN -x '*pyc' kid-0.9.3/kid/djangotext.py /usr/lib/python2.4/site-packages/kid-0.9.3-py2.4.egg/kid/djangotext.py --- kid-0.9.3/kid/djangotext.py 1970-01-01 02:00:00.000000000 +0200 +++ /usr/lib/python2.4/site-packages/kid-0.9.3-py2.4.egg/kid/djangotext.py 2006-12-04 13:42:51.000000000 +0200 @@ -0,0 +1,111 @@ +import re + +# Capitalizes the first letter of a string. +capfirst = lambda x: x and x[0].upper() + x[1:] + +def wrap(text, width): + """ + A word-wrap function that preserves existing line breaks and most spaces in + the text. Expects that existing line breaks are posix newlines (\n). + See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061 + """ + return reduce(lambda line, word, width=width: '%s%s%s' % + (line, + ' \n'[(len(line[line.rfind('\n')+1:]) + + len(word.split('\n',1)[0] + ) >= width)], + word), + text.split(' ') + ) + +def truncate_words(s, num): + "Truncates a string after a certain number of words." + length = int(num) + words = s.split() + if len(words) > length: + words = words[:length] + if not words[-1].endswith('...'): + words.append('...') + return ' '.join(words) + +def get_valid_filename(s): + """ + Returns the given string converted to a string that can be used for a clean + filename. Specifically, leading and trailing spaces are removed; other + spaces are converted to underscores; and all non-filename-safe characters + are removed. + >>> get_valid_filename("john's portrait in 2004.jpg") + 'johns_portrait_in_2004.jpg' + """ + s = s.strip().replace(' ', '_') + return re.sub(r'[^-A-Za-z0-9_.]', '', s) + +def get_text_list(list_, last_word='or'): + """ + >>> get_text_list(['a', 'b', 'c', 'd']) + 'a, b, c or d' + >>> get_text_list(['a', 'b', 'c'], 'and') + 'a, b and c' + >>> get_text_list(['a', 'b'], 'and') + 'a and b' + >>> get_text_list(['a']) + 'a' + >>> get_text_list([]) + '' + """ + if len(list_) == 0: return '' + if len(list_) == 1: return list_[0] + return '%s %s %s' % (', '.join([str(i) for i in list_][:-1]), last_word, list_[-1]) + +def normalize_newlines(text): + return re.sub(r'\r\n|\r|\n', '\n', text) + +def recapitalize(text): + "Recapitalizes text, placing caps after end-of-sentence punctuation." +# capwords = () + text = text.lower() + capsRE = re.compile(r'(?:^|(?<=[\.\?\!] ))([a-z])') + text = capsRE.sub(lambda x: x.group(1).upper(), text) +# for capword in capwords: +# capwordRE = re.compile(r'\b%s\b' % capword, re.I) +# text = capwordRE.sub(capword, text) + return text + +def phone2numeric(phone): + "Converts a phone number with letters into its numeric equivalent." + letters = re.compile(r'[A-PR-Y]', re.I) + char2number = lambda m: {'a': '2', 'c': '2', 'b': '2', 'e': '3', + 'd': '3', 'g': '4', 'f': '3', 'i': '4', 'h': '4', 'k': '5', + 'j': '5', 'm': '6', 'l': '5', 'o': '6', 'n': '6', 'p': '7', + 's': '7', 'r': '7', 'u': '8', 't': '8', 'w': '9', 'v': '8', + 'y': '9', 'x': '9'}.get(m.group(0).lower()) + return letters.sub(char2number, phone) + +# From http://www.xhaus.com/alan/python/httpcomp.html#gzip +# Used with permission. +def compress_string(s): + import cStringIO, gzip + zbuf = cStringIO.StringIO() + zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuf) + zfile.write(s) + zfile.close() + return zbuf.getvalue() + +smart_split_re = re.compile('("(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|[^\\s]+)') +def smart_split(text): + """ + Generator that splits a string by spaces, leaving quoted phrases together. + Supports both single and double quotes, and supports escaping quotes with + backslashes. In the output, strings will keep their initial and trailing + quote marks. + >>> list(smart_split('This is "a person\'s" test.')) + ['This', 'is', '"a person\'s"', 'test.'] + """ + for bit in smart_split_re.finditer(text): + bit = bit.group(0) + if bit[0] == '"': + yield '"' + bit[1:-1].replace('\\"', '"').replace('\\\\', '\\') + '"' + elif bit[0] == "'": + yield "'" + bit[1:-1].replace("\\'", "'").replace("\\\\", "\\") + "'" + else: + yield bit diff -urN -x '*pyc' kid-0.9.3/kid/filterslib.py /usr/lib/python2.4/site-packages/kid-0.9.3-py2.4.egg/kid/filterslib.py --- kid-0.9.3/kid/filterslib.py 1970-01-01 02:00:00.000000000 +0200 +++ /usr/lib/python2.4/site-packages/kid-0.9.3-py2.4.egg/kid/filterslib.py 2006-12-04 16:51:15.000000000 +0200 @@ -0,0 +1,454 @@ +import re +import random as random_module +from kid.pull import XML + +def register_filter(filter_name, filter_func): + globals()[filter_name]=filter_func + +################### +# STRINGS # +################### + +def addslashes(value): + "Adds slashes - useful for passing strings to JavaScript, for example." + return value.replace('"', '\\"').replace("'", "\\'") + +def capfirst(value): + "Capitalizes the first character of the value" + value = str(value) + return value and value[0].upper() + value[1:] + +def addslashes(value): + "Adds slashes - useful for passing strings to JavaScript, for example." + return value.replace('"', '\\"').replace("'", "\\'") + +def capfirst(value): + "Capitalizes the first character of the value" + value = str(value) + return value and value[0].upper() + value[1:] + +def fix_ampersands(value): + "Replaces ampersands with ``&`` entities" + from djangohtml import fix_ampersands + return fix_ampersands(value) + +def floatformat(text): + """ + Displays a floating point number as 34.2 (with one decimal place) -- but + only if there's a point to be displayed + """ + try: + f = float(text) + except ValueError: + return '' + m = f - int(f) + if m: + return '%.1f' % f + else: + return '%d' % int(f) + +def linenumbers(value): + "Displays text with line numbers" + from djangohtml import escape + lines = value.split('\n') + # Find the maximum width of the line count, for use with zero padding string format command + width = str(len(str(len(lines)))) + for i, line in enumerate(lines): + lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line)) + return '\n'.join(lines) + +def lower(value): + "Converts a string into all lowercase" + return value.lower() + +def make_list(value): + """ + Returns the value turned into a list. For an integer, it's a list of + digits. For a string, it's a list of characters. + """ + return list(str(value)) + +def slugify(value): + "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" + value = re.sub('[^\w\s-]', '', value).strip().lower() + return re.sub('[-\s]+', '-', value) + +def stringformat(value, arg): + """ + Formats the variable according to the argument, a string formatting specifier. + This specifier uses Python string formating syntax, with the exception that + the leading "%" is dropped. + + See http://docs.python.org/lib/typesseq-strings.html for documentation + of Python string formatting + """ + try: + return ("%" + arg) % value + except (ValueError, TypeError): + return "" + +def title(value): + "Converts a string into titlecase" + return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) + +def truncatewords(value, arg): + """ + Truncates a string after a certain number of words + + Argument: Number of words to truncate after + """ + from djangotext import truncate_words + try: + length = int(arg) + except ValueError: # invalid literal for int() + return value # Fail silently. + if not isinstance(value, basestring): + value = str(value) + return truncate_words(value, length) + +def upper(value): + "Converts a string into all uppercase" + return value.upper() + +def urlencode(value): + "Escapes a value for use in a URL" + import urllib + return urllib.quote(value) + +def urlize(value): + "Converts URLs in plain text into clickable links" + from djangohtml import urlize, fix_ampersands + return XML(fix_ampersands(urlize(value, nofollow=True))) + +def urlizetrunc(value, limit): + """ + Converts URLs into clickable links, truncating URLs to the given character limit, + and adding 'rel=nofollow' attribute to discourage spamming. + + Argument: Length to truncate URLs to. + """ + from djangohtml import urlize, fix_ampersands + return XML(fix_ampersands(urlize(value, trim_url_limit=int(limit), nofollow=True))) + +def wordcount(value): + "Returns the number of words" + return len(value.split()) + +def wordwrap(value, arg): + """ + Wraps words at specified line length + + Argument: number of characters to wrap the text at. + """ + from djangotext import wrap + return wrap(str(value), int(arg)) + +def ljust(value, arg): + """ + Left-aligns the value in a field of a given width + + Argument: field size + """ + return str(value).ljust(int(arg)) + +def rjust(value, arg): + """ + Right-aligns the value in a field of a given width + + Argument: field size + """ + return str(value).rjust(int(arg)) + +def center(value, arg): + "Centers the value in a field of a given width" + return str(value).center(int(arg)) + +def cut(value, arg): + "Removes all values of arg from the given string" + return value.replace(arg, '') + +################### +# HTML STRINGS # +################### + +def escape(value): + "Escapes a string's HTML" + from djangohtml import escape + return escape(value) + +def linebreaks(value): + "Converts newlines into
and
s"
+ from djangohtml import linebreaks
+ return linebreaks(value)
+
+def linebreaksbr(value):
+ "Converts newlines into
s"
+ return value.replace('\n', '
')
+
+def removetags(value, tags):
+ "Removes a space separated list of [X]HTML tags from the output"
+ tags = [re.escape(tag) for tag in tags.split()]
+ tags_re = '(%s)' % '|'.join(tags)
+ starttag_re = re.compile(r'<%s(/?>|(\s+[^>]*>))' % tags_re)
+ endtag_re = re.compile('%s>' % tags_re)
+ value = starttag_re.sub('', value)
+ value = endtag_re.sub('', value)
+ return value
+
+def striptags(value):
+ "Strips all [X]HTML tags"
+ from djangohtml import strip_tags
+ if not isinstance(value, basestring):
+ value = str(value)
+ return strip_tags(value)
+
+###################
+# LISTS #
+###################
+
+def dictsort(value, arg):
+ """
+ Takes a list of dicts, returns that list sorted by the property given in
+ the argument.
+ """
+ raise NotImplementedError
+
+def dictsortreversed(value, arg):
+ """
+ Takes a list of dicts, returns that list sorted in reverse order by the
+ property given in the argument.
+ """
+ decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
+ decorated.sort()
+ decorated.reverse()
+ return [item[1] for item in decorated]
+
+def first(value):
+ "Returns the first item in a list"
+ try:
+ return value[0]
+ except IndexError:
+ return ''
+
+def join(value, arg):
+ "Joins a list with a string, like Python's ``str.join(list)``"
+ try:
+ return arg.join(map(str, value))
+ except AttributeError: # fail silently but nicely
+ return value
+
+def length(value):
+ "Returns the length of the value - useful for lists"
+ return len(value)
+
+def length_is(value, arg):
+ "Returns a boolean of whether the value's length is the argument"
+ return len(value) == int(arg)
+
+def random(value):
+ "Returns a random item from the list"
+ return random_module.choice(value)
+
+def slice_(value, arg):
+ """
+ Returns a slice of the list.
+
+ Uses the same syntax as Python's list slicing; see
+ http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
+ for an introduction.
+ """
+ try:
+ bits = []
+ for x in arg.split(':'):
+ if len(x) == 0:
+ bits.append(None)
+ else:
+ bits.append(int(x))
+ return value[slice(*bits)]
+
+ except (ValueError, TypeError):
+ return value # Fail silently.
+
+def unordered_list(value):
+ """
+ Recursively takes a self-nested list and returns an HTML unordered list --
+ WITHOUT opening and closing