Code

Ticket #6587: new_template_lib_loader_14.diff

File new_template_lib_loader_14.diff, 135.0 KB (added by oyvind, 6 years ago)

Final patch, i hope :)

Line 
1diff --git a/django/template/__init__.py b/django/template/__init__.py
2index 5c4ab30..58755db 100644
3--- a/django/template/__init__.py
4+++ b/django/template/__init__.py
5@@ -49,6 +49,7 @@ u'<html><h1>Hello</h1></html>'
6 u'<html></html>'
7 """
8 import re
9+import imp
10 from inspect import getargspec
11 from django.conf import settings
12 from django.template.context import Context, RequestContext, ContextPopException
13@@ -59,6 +60,7 @@ from django.utils.encoding import smart_unicode, force_unicode
14 from django.utils.translation import ugettext as _
15 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
16 from django.utils.html import escape
17+from django.templatetags import get_templatetags_modules
18 
19 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
20 
21@@ -913,22 +915,48 @@ class Library(object):
22             return func
23         return dec
24 
25-def get_library(module_name):
26-    lib = libraries.get(module_name, None)
27+def import_library(taglib_module):
28+    components = taglib_module.split('.')
29+    parent_components = components[:-1]
30+    parent_module = '.'.join(parent_components)
31+    try:
32+        mod = __import__(parent_module, {}, {}, [parent_components[-1]])
33+        imp.find_module(components[-1], mod.__path__)
34+    except ImportError:
35+        return None
36+    mod = __import__(taglib_module, {}, {}, [components[-1]])
37+    try:
38+        return mod.register
39+    except AttributeError:
40+        raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % taglib_module)
41+
42+def get_library(library_name):
43+    lib = libraries.get(library_name, None)
44     if not lib:
45-        try:
46-            mod = __import__(module_name, {}, {}, [''])
47-        except ImportError, e:
48-            raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e))
49-        try:
50-            lib = mod.register
51-            libraries[module_name] = lib
52-        except AttributeError:
53-            raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name)
54+
55+        """
56+        If library is not already loaded loop over all templatetags modules to locate it.
57+
58+        {% load somelib %} and {% load someotherlib %} loops twice.
59+
60+        Subsequent loads eg. {% load somelib %} in the same thread will grab the cached
61+        module from libraries.
62+        """
63+        templatetags_modules = get_templatetags_modules()
64+        tried_modules = []
65+        for module in templatetags_modules:
66+            taglib_module = str('%s.%s' % (module, library_name))
67+            tried_modules.append(taglib_module)
68+            lib = import_library(taglib_module)
69+            if lib:
70+                libraries[library_name] = lib
71+                break
72+        if not lib:
73+            raise InvalidTemplateLibrary("Template library %s not found, tried %s" % (library_name, ','.join(tried_modules)))
74     return lib
75 
76-def add_to_builtins(module_name):
77-    builtins.append(get_library(module_name))
78+def add_to_builtins(module):
79+    builtins.append(import_library(module))
80 
81-add_to_builtins('django.template.defaulttags')
82-add_to_builtins('django.template.defaultfilters')
83+add_to_builtins('django.templatetags.defaulttags')
84+add_to_builtins('django.templatetags.defaultfilters')
85diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
86deleted file mode 100644
87index cef3143..0000000
88--- a/django/template/defaultfilters.py
89+++ /dev/null
90@@ -1,851 +0,0 @@
91-"""Default variable filters."""
92-
93-import re
94-import random as random_module
95-try:
96-    from functools import wraps
97-except ImportError:
98-    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
99-
100-from django.template import Variable, Library
101-from django.conf import settings
102-from django.utils.translation import ugettext, ungettext
103-from django.utils.encoding import force_unicode, iri_to_uri
104-from django.utils.safestring import mark_safe, SafeData
105-
106-register = Library()
107-
108-#######################
109-# STRING DECORATOR    #
110-#######################
111-
112-def stringfilter(func):
113-    """
114-    Decorator for filters which should only receive unicode objects. The object
115-    passed as the first positional argument will be converted to a unicode
116-    object.
117-    """
118-    def _dec(*args, **kwargs):
119-        if args:
120-            args = list(args)
121-            args[0] = force_unicode(args[0])
122-            if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False):
123-                return mark_safe(func(*args, **kwargs))
124-        return func(*args, **kwargs)
125-
126-    # Include a reference to the real function (used to check original
127-    # arguments by the template parser).
128-    _dec._decorated_function = getattr(func, '_decorated_function', func)
129-    for attr in ('is_safe', 'needs_autoescape'):
130-        if hasattr(func, attr):
131-            setattr(_dec, attr, getattr(func, attr))
132-    return wraps(func)(_dec)
133-
134-###################
135-# STRINGS         #
136-###################
137-
138-
139-def addslashes(value):
140-    """
141-    Adds slashes before quotes. Useful for escaping strings in CSV, for
142-    example. Less useful for escaping JavaScript; use the ``escapejs``
143-    filter instead.
144-    """
145-    return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
146-addslashes.is_safe = True
147-addslashes = stringfilter(addslashes)
148-
149-def capfirst(value):
150-    """Capitalizes the first character of the value."""
151-    return value and value[0].upper() + value[1:]
152-capfirst.is_safe=True
153-capfirst = stringfilter(capfirst)
154-
155-_js_escapes = (
156-    ('\\', '\\\\'),
157-    ('"', '\\"'),
158-    ("'", "\\'"),
159-    ('\n', '\\n'),
160-    ('\r', '\\r'),
161-    ('\b', '\\b'),
162-    ('\f', '\\f'),
163-    ('\t', '\\t'),
164-    ('\v', '\\v'),
165-    ('</', '<\\/'),
166-)
167-def escapejs(value):
168-    """Backslash-escapes characters for use in JavaScript strings."""
169-    for bad, good in _js_escapes:
170-        value = value.replace(bad, good)
171-    return value
172-escapejs = stringfilter(escapejs)
173-
174-def fix_ampersands(value):
175-    """Replaces ampersands with ``&amp;`` entities."""
176-    from django.utils.html import fix_ampersands
177-    return fix_ampersands(value)
178-fix_ampersands.is_safe=True
179-fix_ampersands = stringfilter(fix_ampersands)
180-
181-def floatformat(text, arg=-1):
182-    """
183-    Displays a float to a specified number of decimal places.
184-
185-    If called without an argument, it displays the floating point number with
186-    one decimal place -- but only if there's a decimal place to be displayed:
187-
188-    * num1 = 34.23234
189-    * num2 = 34.00000
190-    * num3 = 34.26000
191-    * {{ num1|floatformat }} displays "34.2"
192-    * {{ num2|floatformat }} displays "34"
193-    * {{ num3|floatformat }} displays "34.3"
194-
195-    If arg is positive, it will always display exactly arg number of decimal
196-    places:
197-
198-    * {{ num1|floatformat:3 }} displays "34.232"
199-    * {{ num2|floatformat:3 }} displays "34.000"
200-    * {{ num3|floatformat:3 }} displays "34.260"
201-
202-    If arg is negative, it will display arg number of decimal places -- but
203-    only if there are places to be displayed:
204-
205-    * {{ num1|floatformat:"-3" }} displays "34.232"
206-    * {{ num2|floatformat:"-3" }} displays "34"
207-    * {{ num3|floatformat:"-3" }} displays "34.260"
208-    """
209-    try:
210-        f = float(text)
211-    except (ValueError, TypeError):
212-        return u''
213-    try:
214-        d = int(arg)
215-    except ValueError:
216-        return force_unicode(f)
217-    try:
218-        m = f - int(f)
219-    except OverflowError:
220-        return force_unicode(f)
221-    if not m and d < 0:
222-        return mark_safe(u'%d' % int(f))
223-    else:
224-        formatstr = u'%%.%df' % abs(d)
225-        return mark_safe(formatstr % f)
226-floatformat.is_safe = True
227-
228-def iriencode(value):
229-    """Escapes an IRI value for use in a URL."""
230-    return force_unicode(iri_to_uri(value))
231-iriencode.is_safe = True
232-iriencode = stringfilter(iriencode)
233-
234-def linenumbers(value, autoescape=None):
235-    """Displays text with line numbers."""
236-    from django.utils.html import escape
237-    lines = value.split(u'\n')
238-    # Find the maximum width of the line count, for use with zero padding
239-    # string format command
240-    width = unicode(len(unicode(len(lines))))
241-    if not autoescape or isinstance(value, SafeData):
242-        for i, line in enumerate(lines):
243-            lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, line)
244-    else:
245-        for i, line in enumerate(lines):
246-            lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, escape(line))
247-    return mark_safe(u'\n'.join(lines))
248-linenumbers.is_safe = True
249-linenumbers.needs_autoescape = True
250-linenumbers = stringfilter(linenumbers)
251-
252-def lower(value):
253-    """Converts a string into all lowercase."""
254-    return value.lower()
255-lower.is_safe = True
256-lower = stringfilter(lower)
257-
258-def make_list(value):
259-    """
260-    Returns the value turned into a list.
261-
262-    For an integer, it's a list of digits.
263-    For a string, it's a list of characters.
264-    """
265-    return list(value)
266-make_list.is_safe = False
267-make_list = stringfilter(make_list)
268-
269-def slugify(value):
270-    """
271-    Normalizes string, converts to lowercase, removes non-alpha characters,
272-    and converts spaces to hyphens.
273-    """
274-    import unicodedata
275-    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
276-    value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
277-    return mark_safe(re.sub('[-\s]+', '-', value))
278-slugify.is_safe = True
279-slugify = stringfilter(slugify)
280-
281-def stringformat(value, arg):
282-    """
283-    Formats the variable according to the arg, a string formatting specifier.
284-
285-    This specifier uses Python string formating syntax, with the exception that
286-    the leading "%" is dropped.
287-
288-    See http://docs.python.org/lib/typesseq-strings.html for documentation
289-    of Python string formatting
290-    """
291-    try:
292-        return (u"%" + unicode(arg)) % value
293-    except (ValueError, TypeError):
294-        return u""
295-stringformat.is_safe = True
296-
297-def title(value):
298-    """Converts a string into titlecase."""
299-    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
300-title.is_safe = True
301-title = stringfilter(title)
302-
303-def truncatewords(value, arg):
304-    """
305-    Truncates a string after a certain number of words.
306-
307-    Argument: Number of words to truncate after.
308-    """
309-    from django.utils.text import truncate_words
310-    try:
311-        length = int(arg)
312-    except ValueError: # Invalid literal for int().
313-        return value # Fail silently.
314-    return truncate_words(value, length)
315-truncatewords.is_safe = True
316-truncatewords = stringfilter(truncatewords)
317-
318-def truncatewords_html(value, arg):
319-    """
320-    Truncates HTML after a certain number of words.
321-
322-    Argument: Number of words to truncate after.
323-    """
324-    from django.utils.text import truncate_html_words
325-    try:
326-        length = int(arg)
327-    except ValueError: # invalid literal for int()
328-        return value # Fail silently.
329-    return truncate_html_words(value, length)
330-truncatewords_html.is_safe = True
331-truncatewords_html = stringfilter(truncatewords_html)
332-
333-def upper(value):
334-    """Converts a string into all uppercase."""
335-    return value.upper()
336-upper.is_safe = False
337-upper = stringfilter(upper)
338-
339-def urlencode(value):
340-    """Escapes a value for use in a URL."""
341-    from django.utils.http import urlquote
342-    return urlquote(value)
343-urlencode.is_safe = False
344-urlencode = stringfilter(urlencode)
345-
346-def urlize(value, autoescape=None):
347-    """Converts URLs in plain text into clickable links."""
348-    from django.utils.html import urlize
349-    return mark_safe(urlize(value, nofollow=True, autoescape=autoescape))
350-urlize.is_safe=True
351-urlize.needs_autoescape = True
352-urlize = stringfilter(urlize)
353-
354-def urlizetrunc(value, limit, autoescape=None):
355-    """
356-    Converts URLs into clickable links, truncating URLs to the given character
357-    limit, and adding 'rel=nofollow' attribute to discourage spamming.
358-
359-    Argument: Length to truncate URLs to.
360-    """
361-    from django.utils.html import urlize
362-    return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True,
363-                            autoescape=autoescape))
364-urlizetrunc.is_safe = True
365-urlizetrunc.needs_autoescape = True
366-urlizetrunc = stringfilter(urlizetrunc)
367-
368-def wordcount(value):
369-    """Returns the number of words."""
370-    return len(value.split())
371-wordcount.is_safe = False
372-wordcount = stringfilter(wordcount)
373-
374-def wordwrap(value, arg):
375-    """
376-    Wraps words at specified line length.
377-
378-    Argument: number of characters to wrap the text at.
379-    """
380-    from django.utils.text import wrap
381-    return wrap(value, int(arg))
382-wordwrap.is_safe = True
383-wordwrap = stringfilter(wordwrap)
384-
385-def ljust(value, arg):
386-    """
387-    Left-aligns the value in a field of a given width.
388-
389-    Argument: field size.
390-    """
391-    return value.ljust(int(arg))
392-ljust.is_safe = True
393-ljust = stringfilter(ljust)
394-
395-def rjust(value, arg):
396-    """
397-    Right-aligns the value in a field of a given width.
398-
399-    Argument: field size.
400-    """
401-    return value.rjust(int(arg))
402-rjust.is_safe = True
403-rjust = stringfilter(rjust)
404-
405-def center(value, arg):
406-    """Centers the value in a field of a given width."""
407-    return value.center(int(arg))
408-center.is_safe = True
409-center = stringfilter(center)
410-
411-def cut(value, arg):
412-    """
413-    Removes all values of arg from the given string.
414-    """
415-    safe = isinstance(value, SafeData)
416-    value = value.replace(arg, u'')
417-    if safe and arg != ';':
418-        return mark_safe(value)
419-    return value
420-cut = stringfilter(cut)
421-
422-###################
423-# HTML STRINGS    #
424-###################
425-
426-def escape(value):
427-    """
428-    Marks the value as a string that should not be auto-escaped.
429-    """
430-    from django.utils.safestring import mark_for_escaping
431-    return mark_for_escaping(value)
432-escape.is_safe = True
433-escape = stringfilter(escape)
434-
435-def force_escape(value):
436-    """
437-    Escapes a string's HTML. This returns a new string containing the escaped
438-    characters (as opposed to "escape", which marks the content for later
439-    possible escaping).
440-    """
441-    from django.utils.html import escape
442-    return mark_safe(escape(value))
443-force_escape = stringfilter(force_escape)
444-force_escape.is_safe = True
445-
446-def linebreaks(value, autoescape=None):
447-    """
448-    Replaces line breaks in plain text with appropriate HTML; a single
449-    newline becomes an HTML line break (``<br />``) and a new line
450-    followed by a blank line becomes a paragraph break (``</p>``).
451-    """
452-    from django.utils.html import linebreaks
453-    autoescape = autoescape and not isinstance(value, SafeData)
454-    return mark_safe(linebreaks(value, autoescape))
455-linebreaks.is_safe = True
456-linebreaks.needs_autoescape = True
457-linebreaks = stringfilter(linebreaks)
458-
459-def linebreaksbr(value, autoescape=None):
460-    """
461-    Converts all newlines in a piece of plain text to HTML line breaks
462-    (``<br />``).
463-    """
464-    if autoescape and not isinstance(value, SafeData):
465-        from django.utils.html import escape
466-        value = escape(value)
467-    return mark_safe(value.replace('\n', '<br />'))
468-linebreaksbr.is_safe = True
469-linebreaksbr.needs_autoescape = True
470-linebreaksbr = stringfilter(linebreaksbr)
471-
472-def safe(value):
473-    """
474-    Marks the value as a string that should not be auto-escaped.
475-    """
476-    from django.utils.safestring import mark_safe
477-    return mark_safe(value)
478-safe.is_safe = True
479-safe = stringfilter(safe)
480-
481-def removetags(value, tags):
482-    """Removes a space separated list of [X]HTML tags from the output."""
483-    tags = [re.escape(tag) for tag in tags.split()]
484-    tags_re = u'(%s)' % u'|'.join(tags)
485-    starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U)
486-    endtag_re = re.compile(u'</%s>' % tags_re)
487-    value = starttag_re.sub(u'', value)
488-    value = endtag_re.sub(u'', value)
489-    return value
490-removetags.is_safe = True
491-removetags = stringfilter(removetags)
492-
493-def striptags(value):
494-    """Strips all [X]HTML tags."""
495-    from django.utils.html import strip_tags
496-    return strip_tags(value)
497-striptags.is_safe = True
498-striptags = stringfilter(striptags)
499-
500-###################
501-# LISTS           #
502-###################
503-
504-def dictsort(value, arg):
505-    """
506-    Takes a list of dicts, returns that list sorted by the property given in
507-    the argument.
508-    """
509-    var_resolve = Variable(arg).resolve
510-    decorated = [(var_resolve(item), item) for item in value]
511-    decorated.sort()
512-    return [item[1] for item in decorated]
513-dictsort.is_safe = False
514-
515-def dictsortreversed(value, arg):
516-    """
517-    Takes a list of dicts, returns that list sorted in reverse order by the
518-    property given in the argument.
519-    """
520-    var_resolve = Variable(arg).resolve
521-    decorated = [(var_resolve(item), item) for item in value]
522-    decorated.sort()
523-    decorated.reverse()
524-    return [item[1] for item in decorated]
525-dictsortreversed.is_safe = False
526-
527-def first(value):
528-    """Returns the first item in a list."""
529-    try:
530-        return value[0]
531-    except IndexError:
532-        return u''
533-first.is_safe = False
534-
535-def join(value, arg):
536-    """Joins a list with a string, like Python's ``str.join(list)``."""
537-    try:
538-        data = arg.join(map(force_unicode, value))
539-    except AttributeError: # fail silently but nicely
540-        return value
541-    safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData),
542-            value, True)
543-    if safe_args:
544-        return mark_safe(data)
545-    else:
546-        return data
547-join.is_safe = True
548-
549-def last(value):
550-    "Returns the last item in a list"
551-    try:
552-        return value[-1]
553-    except IndexError:
554-        return u''
555-last.is_safe = True
556-
557-def length(value):
558-    """Returns the length of the value - useful for lists."""
559-    return len(value)
560-length.is_safe = True
561-
562-def length_is(value, arg):
563-    """Returns a boolean of whether the value's length is the argument."""
564-    return len(value) == int(arg)
565-length_is.is_safe = True
566-
567-def random(value):
568-    """Returns a random item from the list."""
569-    return random_module.choice(value)
570-random.is_safe = True
571-
572-def slice_(value, arg):
573-    """
574-    Returns a slice of the list.
575-
576-    Uses the same syntax as Python's list slicing; see
577-    http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
578-    for an introduction.
579-    """
580-    try:
581-        bits = []
582-        for x in arg.split(u':'):
583-            if len(x) == 0:
584-                bits.append(None)
585-            else:
586-                bits.append(int(x))
587-        return value[slice(*bits)]
588-
589-    except (ValueError, TypeError):
590-        return value # Fail silently.
591-slice_.is_safe = True
592-
593-def unordered_list(value, autoescape=None):
594-    """
595-    Recursively takes a self-nested list and returns an HTML unordered list --
596-    WITHOUT opening and closing <ul> tags.
597-
598-    The list is assumed to be in the proper format. For example, if ``var``
599-    contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``,
600-    then ``{{ var|unordered_list }}`` would return::
601-
602-        <li>States
603-        <ul>
604-                <li>Kansas
605-                <ul>
606-                        <li>Lawrence</li>
607-                        <li>Topeka</li>
608-                </ul>
609-                </li>
610-                <li>Illinois</li>
611-        </ul>
612-        </li>
613-    """
614-    if autoescape:
615-        from django.utils.html import conditional_escape
616-        escaper = conditional_escape
617-    else:
618-        escaper = lambda x: x
619-    def convert_old_style_list(list_):
620-        """
621-        Converts old style lists to the new easier to understand format.
622-
623-        The old list format looked like:
624-            ['Item 1', [['Item 1.1', []], ['Item 1.2', []]]
625-
626-        And it is converted to:
627-            ['Item 1', ['Item 1.1', 'Item 1.2]]
628-        """
629-        if not isinstance(list_, (tuple, list)) or len(list_) != 2:
630-            return list_, False
631-        first_item, second_item = list_
632-        if second_item == []:
633-            return [first_item], True
634-        old_style_list = True
635-        new_second_item = []
636-        for sublist in second_item:
637-            item, old_style_list = convert_old_style_list(sublist)
638-            if not old_style_list:
639-                break
640-            new_second_item.extend(item)
641-        if old_style_list:
642-            second_item = new_second_item
643-        return [first_item, second_item], old_style_list
644-    def _helper(list_, tabs=1):
645-        indent = u'\t' * tabs
646-        output = []
647-
648-        list_length = len(list_)
649-        i = 0
650-        while i < list_length:
651-            title = list_[i]
652-            sublist = ''
653-            sublist_item = None
654-            if isinstance(title, (list, tuple)):
655-                sublist_item = title
656-                title = ''
657-            elif i < list_length - 1:
658-                next_item = list_[i+1]
659-                if next_item and isinstance(next_item, (list, tuple)):
660-                    # The next item is a sub-list.
661-                    sublist_item = next_item
662-                    # We've processed the next item now too.
663-                    i += 1
664-            if sublist_item:
665-                sublist = _helper(sublist_item, tabs+1)
666-                sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist,
667-                                                         indent, indent)
668-            output.append('%s<li>%s%s</li>' % (indent,
669-                    escaper(force_unicode(title)), sublist))
670-            i += 1
671-        return '\n'.join(output)
672-    value, converted = convert_old_style_list(value)
673-    return mark_safe(_helper(value))
674-unordered_list.is_safe = True
675-unordered_list.needs_autoescape = True
676-
677-###################
678-# INTEGERS        #
679-###################
680-
681-def add(value, arg):
682-    """Adds the arg to the value."""
683-    return int(value) + int(arg)
684-add.is_safe = False
685-
686-def get_digit(value, arg):
687-    """
688-    Given a whole number, returns the requested digit of it, where 1 is the
689-    right-most digit, 2 is the second-right-most digit, etc. Returns the
690-    original value for invalid input (if input or argument is not an integer,
691-    or if argument is less than 1). Otherwise, output is always an integer.
692-    """
693-    try:
694-        arg = int(arg)
695-        value = int(value)
696-    except ValueError:
697-        return value # Fail silently for an invalid argument
698-    if arg < 1:
699-        return value
700-    try:
701-        return int(str(value)[-arg])
702-    except IndexError:
703-        return 0
704-get_digit.is_safe = False
705-
706-###################
707-# DATES           #
708-###################
709-
710-def date(value, arg=None):
711-    """Formats a date according to the given format."""
712-    from django.utils.dateformat import format
713-    if not value:
714-        return u''
715-    if arg is None:
716-        arg = settings.DATE_FORMAT
717-    return format(value, arg)
718-date.is_safe = False
719-
720-def time(value, arg=None):
721-    """Formats a time according to the given format."""
722-    from django.utils.dateformat import time_format
723-    if value in (None, u''):
724-        return u''
725-    if arg is None:
726-        arg = settings.TIME_FORMAT
727-    return time_format(value, arg)
728-time.is_safe = False
729-
730-def timesince(value, arg=None):
731-    """Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
732-    from django.utils.timesince import timesince
733-    if not value:
734-        return u''
735-    if arg:
736-        return timesince(arg, value)
737-    return timesince(value)
738-timesince.is_safe = False
739-
740-def timeuntil(value, arg=None):
741-    """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
742-    from django.utils.timesince import timesince
743-    from datetime import datetime
744-    if not value:
745-        return u''
746-    if arg:
747-        return timesince(arg, value)
748-    return timesince(datetime.now(), value)
749-timeuntil.is_safe = False
750-
751-###################
752-# LOGIC           #
753-###################
754-
755-def default(value, arg):
756-    """If value is unavailable, use given default."""
757-    return value or arg
758-default.is_safe = False
759-
760-def default_if_none(value, arg):
761-    """If value is None, use given default."""
762-    if value is None:
763-        return arg
764-    return value
765-default_if_none.is_safe = False
766-
767-def divisibleby(value, arg):
768-    """Returns True if the value is devisible by the argument."""
769-    return int(value) % int(arg) == 0
770-divisibleby.is_safe = False
771-
772-def yesno(value, arg=None):
773-    """
774-    Given a string mapping values for true, false and (optionally) None,
775-    returns one of those strings accoding to the value:
776-
777-    ==========  ======================  ==================================
778-    Value       Argument                Outputs
779-    ==========  ======================  ==================================
780-    ``True``    ``"yeah,no,maybe"``     ``yeah``
781-    ``False``   ``"yeah,no,maybe"``     ``no``
782-    ``None``    ``"yeah,no,maybe"``     ``maybe``
783-    ``None``    ``"yeah,no"``           ``"no"`` (converts None to False
784-                                        if no mapping for None is given.
785-    ==========  ======================  ==================================
786-    """
787-    if arg is None:
788-        arg = ugettext('yes,no,maybe')
789-    bits = arg.split(u',')
790-    if len(bits) < 2:
791-        return value # Invalid arg.
792-    try:
793-        yes, no, maybe = bits
794-    except ValueError:
795-        # Unpack list of wrong size (no "maybe" value provided).
796-        yes, no, maybe = bits[0], bits[1], bits[1]
797-    if value is None:
798-        return maybe
799-    if value:
800-        return yes
801-    return no
802-yesno.is_safe = False
803-
804-###################
805-# MISC            #
806-###################
807-
808-def filesizeformat(bytes):
809-    """
810-    Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
811-    102 bytes, etc).
812-    """
813-    try:
814-        bytes = float(bytes)
815-    except TypeError:
816-        return u"0 bytes"
817-
818-    if bytes < 1024:
819-        return ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
820-    if bytes < 1024 * 1024:
821-        return ugettext("%.1f KB") % (bytes / 1024)
822-    if bytes < 1024 * 1024 * 1024:
823-        return ugettext("%.1f MB") % (bytes / (1024 * 1024))
824-    return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024))
825-filesizeformat.is_safe = True
826-
827-def pluralize(value, arg=u's'):
828-    """
829-    Returns a plural suffix if the value is not 1. By default, 's' is used as
830-    the suffix:
831-
832-    * If value is 0, vote{{ value|pluralize }} displays "0 votes".
833-    * If value is 1, vote{{ value|pluralize }} displays "1 vote".
834-    * If value is 2, vote{{ value|pluralize }} displays "2 votes".
835-
836-    If an argument is provided, that string is used instead:
837-
838-    * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes".
839-    * If value is 1, class{{ value|pluralize:"es" }} displays "1 class".
840-    * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes".
841-
842-    If the provided argument contains a comma, the text before the comma is
843-    used for the singular case and the text after the comma is used for the
844-    plural case:
845-
846-    * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies".
847-    * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy".
848-    * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies".
849-    """
850-    if not u',' in arg:
851-        arg = u',' + arg
852-    bits = arg.split(u',')
853-    if len(bits) > 2:
854-        return u''
855-    singular_suffix, plural_suffix = bits[:2]
856-
857-    try:
858-        if int(value) != 1:
859-            return plural_suffix
860-    except ValueError: # Invalid string that's not a number.
861-        pass
862-    except TypeError: # Value isn't a string or a number; maybe it's a list?
863-        try:
864-            if len(value) != 1:
865-                return plural_suffix
866-        except TypeError: # len() of unsized object.
867-            pass
868-    return singular_suffix
869-pluralize.is_safe = False
870-
871-def phone2numeric(value):
872-    """Takes a phone number and converts it in to its numerical equivalent."""
873-    from django.utils.text import phone2numeric
874-    return phone2numeric(value)
875-phone2numeric.is_safe = True
876-
877-def pprint(value):
878-    """A wrapper around pprint.pprint -- for debugging, really."""
879-    from pprint import pformat
880-    try:
881-        return pformat(value)
882-    except Exception, e:
883-        return u"Error in formatting: %s" % force_unicode(e, errors="replace")
884-pprint.is_safe = True
885-
886-# Syntax: register.filter(name of filter, callback)
887-register.filter(add)
888-register.filter(addslashes)
889-register.filter(capfirst)
890-register.filter(center)
891-register.filter(cut)
892-register.filter(date)
893-register.filter(default)
894-register.filter(default_if_none)
895-register.filter(dictsort)
896-register.filter(dictsortreversed)
897-register.filter(divisibleby)
898-register.filter(escape)
899-register.filter(escapejs)
900-register.filter(filesizeformat)
901-register.filter(first)
902-register.filter(fix_ampersands)
903-register.filter(floatformat)
904-register.filter(force_escape)
905-register.filter(get_digit)
906-register.filter(iriencode)
907-register.filter(join)
908-register.filter(last)
909-register.filter(length)
910-register.filter(length_is)
911-register.filter(linebreaks)
912-register.filter(linebreaksbr)
913-register.filter(linenumbers)
914-register.filter(ljust)
915-register.filter(lower)
916-register.filter(make_list)
917-register.filter(phone2numeric)
918-register.filter(pluralize)
919-register.filter(pprint)
920-register.filter(removetags)
921-register.filter(random)
922-register.filter(rjust)
923-register.filter(safe)
924-register.filter('slice', slice_)
925-register.filter(slugify)
926-register.filter(stringformat)
927-register.filter(striptags)
928-register.filter(time)
929-register.filter(timesince)
930-register.filter(timeuntil)
931-register.filter(title)
932-register.filter(truncatewords)
933-register.filter(truncatewords_html)
934-register.filter(unordered_list)
935-register.filter(upper)
936-register.filter(urlencode)
937-register.filter(urlize)
938-register.filter(urlizetrunc)
939-register.filter(wordcount)
940-register.filter(wordwrap)
941-register.filter(yesno)
942diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
943deleted file mode 100644
944index 01c43ee..0000000
945--- a/django/template/defaulttags.py
946+++ /dev/null
947@@ -1,1105 +0,0 @@
948-"""Default tags used by the template system, available to all templates."""
949-
950-import sys
951-import re
952-from itertools import cycle as itertools_cycle
953-try:
954-    reversed
955-except NameError:
956-    from django.utils.itercompat import reversed     # Python 2.3 fallback
957-
958-from django.template import Node, NodeList, Template, Context, Variable
959-from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
960-from django.template import get_library, Library, InvalidTemplateLibrary
961-from django.conf import settings
962-from django.utils.encoding import smart_str, smart_unicode
963-from django.utils.itercompat import groupby
964-from django.utils.safestring import mark_safe
965-
966-register = Library()
967-
968-class AutoEscapeControlNode(Node):
969-    """Implements the actions of the autoescape tag."""
970-    def __init__(self, setting, nodelist):
971-        self.setting, self.nodelist = setting, nodelist
972-
973-    def render(self, context):
974-        old_setting = context.autoescape
975-        context.autoescape = self.setting
976-        output = self.nodelist.render(context)
977-        context.autoescape = old_setting
978-        if self.setting:
979-            return mark_safe(output)
980-        else:
981-            return output
982-
983-class CommentNode(Node):
984-    def render(self, context):
985-        return ''
986-
987-class CycleNode(Node):
988-    def __init__(self, cyclevars, variable_name=None):
989-        self.cycle_iter = itertools_cycle([Variable(v) for v in cyclevars])
990-        self.variable_name = variable_name
991-
992-    def render(self, context):
993-        value = self.cycle_iter.next().resolve(context)
994-        if self.variable_name:
995-            context[self.variable_name] = value
996-        return value
997-
998-class DebugNode(Node):
999-    def render(self, context):
1000-        from pprint import pformat
1001-        output = [pformat(val) for val in context]
1002-        output.append('\n\n')
1003-        output.append(pformat(sys.modules))
1004-        return ''.join(output)
1005-
1006-class FilterNode(Node):
1007-    def __init__(self, filter_expr, nodelist):
1008-        self.filter_expr, self.nodelist = filter_expr, nodelist
1009-
1010-    def render(self, context):
1011-        output = self.nodelist.render(context)
1012-        # Apply filters.
1013-        context.update({'var': output})
1014-        filtered = self.filter_expr.resolve(context)
1015-        context.pop()
1016-        return filtered
1017-
1018-class FirstOfNode(Node):
1019-    def __init__(self, vars):
1020-        self.vars = map(Variable, vars)
1021-
1022-    def render(self, context):
1023-        for var in self.vars:
1024-            try:
1025-                value = var.resolve(context)
1026-            except VariableDoesNotExist:
1027-                continue
1028-            if value:
1029-                return smart_unicode(value)
1030-        return u''
1031-
1032-class ForNode(Node):
1033-    def __init__(self, loopvars, sequence, is_reversed, nodelist_loop):
1034-        self.loopvars, self.sequence = loopvars, sequence
1035-        self.is_reversed = is_reversed
1036-        self.nodelist_loop = nodelist_loop
1037-
1038-    def __repr__(self):
1039-        reversed_text = self.is_reversed and ' reversed' or ''
1040-        return "<For Node: for %s in %s, tail_len: %d%s>" % \
1041-            (', '.join(self.loopvars), self.sequence, len(self.nodelist_loop),
1042-             reversed_text)
1043-
1044-    def __iter__(self):
1045-        for node in self.nodelist_loop:
1046-            yield node
1047-
1048-    def get_nodes_by_type(self, nodetype):
1049-        nodes = []
1050-        if isinstance(self, nodetype):
1051-            nodes.append(self)
1052-        nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))
1053-        return nodes
1054-
1055-    def render(self, context):
1056-        nodelist = NodeList()
1057-        if 'forloop' in context:
1058-            parentloop = context['forloop']
1059-        else:
1060-            parentloop = {}
1061-        context.push()
1062-        try:
1063-            values = self.sequence.resolve(context, True)
1064-        except VariableDoesNotExist:
1065-            values = []
1066-        if values is None:
1067-            values = []
1068-        if not hasattr(values, '__len__'):
1069-            values = list(values)
1070-        len_values = len(values)
1071-        if self.is_reversed:
1072-            values = reversed(values)
1073-        unpack = len(self.loopvars) > 1
1074-        # Create a forloop value in the context.  We'll update counters on each
1075-        # iteration just below.
1076-        loop_dict = context['forloop'] = {'parentloop': parentloop}
1077-        for i, item in enumerate(values):
1078-            # Shortcuts for current loop iteration number.
1079-            loop_dict['counter0'] = i
1080-            loop_dict['counter'] = i+1
1081-            # Reverse counter iteration numbers.
1082-            loop_dict['revcounter'] = len_values - i
1083-            loop_dict['revcounter0'] = len_values - i - 1
1084-            # Boolean values designating first and last times through loop.
1085-            loop_dict['first'] = (i == 0)
1086-            loop_dict['last'] = (i == len_values - 1)
1087-
1088-            if unpack:
1089-                # If there are multiple loop variables, unpack the item into
1090-                # them.
1091-                context.update(dict(zip(self.loopvars, item)))
1092-            else:
1093-                context[self.loopvars[0]] = item
1094-            for node in self.nodelist_loop:
1095-                nodelist.append(node.render(context))
1096-            if unpack:
1097-                # The loop variables were pushed on to the context so pop them
1098-                # off again. This is necessary because the tag lets the length
1099-                # of loopvars differ to the length of each set of items and we
1100-                # don't want to leave any vars from the previous loop on the
1101-                # context.
1102-                context.pop()
1103-        context.pop()
1104-        return nodelist.render(context)
1105-
1106-class IfChangedNode(Node):
1107-    def __init__(self, nodelist, *varlist):
1108-        self.nodelist = nodelist
1109-        self._last_seen = None
1110-        self._varlist = map(Variable, varlist)
1111-        self._id = str(id(self))
1112-
1113-    def render(self, context):
1114-        if 'forloop' in context and self._id not in context['forloop']:
1115-            self._last_seen = None
1116-            context['forloop'][self._id] = 1
1117-        try:
1118-            if self._varlist:
1119-                # Consider multiple parameters.  This automatically behaves
1120-                # like an OR evaluation of the multiple variables.
1121-                compare_to = [var.resolve(context) for var in self._varlist]
1122-            else:
1123-                compare_to = self.nodelist.render(context)
1124-        except VariableDoesNotExist:
1125-            compare_to = None
1126-
1127-        if  compare_to != self._last_seen:
1128-            firstloop = (self._last_seen == None)
1129-            self._last_seen = compare_to
1130-            context.push()
1131-            context['ifchanged'] = {'firstloop': firstloop}
1132-            content = self.nodelist.render(context)
1133-            context.pop()
1134-            return content
1135-        else:
1136-            return ''
1137-
1138-class IfEqualNode(Node):
1139-    def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
1140-        self.var1, self.var2 = Variable(var1), Variable(var2)
1141-        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
1142-        self.negate = negate
1143-
1144-    def __repr__(self):
1145-        return "<IfEqualNode>"
1146-
1147-    def render(self, context):
1148-        try:
1149-            val1 = self.var1.resolve(context)
1150-        except VariableDoesNotExist:
1151-            val1 = None
1152-        try:
1153-            val2 = self.var2.resolve(context)
1154-        except VariableDoesNotExist:
1155-            val2 = None
1156-        if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
1157-            return self.nodelist_true.render(context)
1158-        return self.nodelist_false.render(context)
1159-
1160-class IfNode(Node):
1161-    def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type):
1162-        self.bool_exprs = bool_exprs
1163-        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
1164-        self.link_type = link_type
1165-
1166-    def __repr__(self):
1167-        return "<If node>"
1168-
1169-    def __iter__(self):
1170-        for node in self.nodelist_true:
1171-            yield node
1172-        for node in self.nodelist_false:
1173-            yield node
1174-
1175-    def get_nodes_by_type(self, nodetype):
1176-        nodes = []
1177-        if isinstance(self, nodetype):
1178-            nodes.append(self)
1179-        nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
1180-        nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
1181-        return nodes
1182-
1183-    def render(self, context):
1184-        if self.link_type == IfNode.LinkTypes.or_:
1185-            for ifnot, bool_expr in self.bool_exprs:
1186-                try:
1187-                    value = bool_expr.resolve(context, True)
1188-                except VariableDoesNotExist:
1189-                    value = None
1190-                if (value and not ifnot) or (ifnot and not value):
1191-                    return self.nodelist_true.render(context)
1192-            return self.nodelist_false.render(context)
1193-        else:
1194-            for ifnot, bool_expr in self.bool_exprs:
1195-                try:
1196-                    value = bool_expr.resolve(context, True)
1197-                except VariableDoesNotExist:
1198-                    value = None
1199-                if not ((value and not ifnot) or (ifnot and not value)):
1200-                    return self.nodelist_false.render(context)
1201-            return self.nodelist_true.render(context)
1202-
1203-    class LinkTypes:
1204-        and_ = 0,
1205-        or_ = 1
1206-
1207-class RegroupNode(Node):
1208-    def __init__(self, target, expression, var_name):
1209-        self.target, self.expression = target, expression
1210-        self.var_name = var_name
1211-
1212-    def render(self, context):
1213-        obj_list = self.target.resolve(context, True)
1214-        if obj_list == None:
1215-            # target variable wasn't found in context; fail silently.
1216-            context[self.var_name] = []
1217-            return ''
1218-        # List of dictionaries in the format:
1219-        # {'grouper': 'key', 'list': [list of contents]}.
1220-        context[self.var_name] = [
1221-            {'grouper': key, 'list': list(val)}
1222-            for key, val in
1223-            groupby(obj_list, lambda v, f=self.expression.resolve: f(v, True))
1224-        ]
1225-        return ''
1226-
1227-def include_is_allowed(filepath):
1228-    for root in settings.ALLOWED_INCLUDE_ROOTS:
1229-        if filepath.startswith(root):
1230-            return True
1231-    return False
1232-
1233-class SsiNode(Node):
1234-    def __init__(self, filepath, parsed):
1235-        self.filepath, self.parsed = filepath, parsed
1236-
1237-    def render(self, context):
1238-        if not include_is_allowed(self.filepath):
1239-            if settings.DEBUG:
1240-                return "[Didn't have permission to include file]"
1241-            else:
1242-                return '' # Fail silently for invalid includes.
1243-        try:
1244-            fp = open(self.filepath, 'r')
1245-            output = fp.read()
1246-            fp.close()
1247-        except IOError:
1248-            output = ''
1249-        if self.parsed:
1250-            try:
1251-                t = Template(output, name=self.filepath)
1252-                return t.render(context)
1253-            except TemplateSyntaxError, e:
1254-                if settings.DEBUG:
1255-                    return "[Included template had syntax error: %s]" % e
1256-                else:
1257-                    return '' # Fail silently for invalid included templates.
1258-        return output
1259-
1260-class LoadNode(Node):
1261-    def render(self, context):
1262-        return ''
1263-
1264-class NowNode(Node):
1265-    def __init__(self, format_string):
1266-        self.format_string = format_string
1267-
1268-    def render(self, context):
1269-        from datetime import datetime
1270-        from django.utils.dateformat import DateFormat
1271-        df = DateFormat(datetime.now())
1272-        return df.format(self.format_string)
1273-
1274-class SpacelessNode(Node):
1275-    def __init__(self, nodelist):
1276-        self.nodelist = nodelist
1277-
1278-    def render(self, context):
1279-        from django.utils.html import strip_spaces_between_tags
1280-        return strip_spaces_between_tags(self.nodelist.render(context).strip())
1281-
1282-class TemplateTagNode(Node):
1283-    mapping = {'openblock': BLOCK_TAG_START,
1284-               'closeblock': BLOCK_TAG_END,
1285-               'openvariable': VARIABLE_TAG_START,
1286-               'closevariable': VARIABLE_TAG_END,
1287-               'openbrace': SINGLE_BRACE_START,
1288-               'closebrace': SINGLE_BRACE_END,
1289-               'opencomment': COMMENT_TAG_START,
1290-               'closecomment': COMMENT_TAG_END,
1291-               }
1292-
1293-    def __init__(self, tagtype):
1294-        self.tagtype = tagtype
1295-
1296-    def render(self, context):
1297-        return self.mapping.get(self.tagtype, '')
1298-
1299-class URLNode(Node):
1300-    def __init__(self, view_name, args, kwargs):
1301-        self.view_name = view_name
1302-        self.args = args
1303-        self.kwargs = kwargs
1304-
1305-    def render(self, context):
1306-        from django.core.urlresolvers import reverse, NoReverseMatch
1307-        args = [arg.resolve(context) for arg in self.args]
1308-        kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
1309-                       for k, v in self.kwargs.items()])
1310-        try:
1311-            return reverse(self.view_name, args=args, kwargs=kwargs)
1312-        except NoReverseMatch:
1313-            try:
1314-                project_name = settings.SETTINGS_MODULE.split('.')[0]
1315-                return reverse(project_name + '.' + self.view_name,
1316-                               args=args, kwargs=kwargs)
1317-            except NoReverseMatch:
1318-                return ''
1319-
1320-class WidthRatioNode(Node):
1321-    def __init__(self, val_expr, max_expr, max_width):
1322-        self.val_expr = val_expr
1323-        self.max_expr = max_expr
1324-        self.max_width = max_width
1325-
1326-    def render(self, context):
1327-        try:
1328-            value = self.val_expr.resolve(context)
1329-            maxvalue = self.max_expr.resolve(context)
1330-        except VariableDoesNotExist:
1331-            return ''
1332-        try:
1333-            value = float(value)
1334-            maxvalue = float(maxvalue)
1335-            ratio = (value / maxvalue) * int(self.max_width)
1336-        except (ValueError, ZeroDivisionError):
1337-            return ''
1338-        return str(int(round(ratio)))
1339-
1340-class WithNode(Node):
1341-    def __init__(self, var, name, nodelist):
1342-        self.var = var
1343-        self.name = name
1344-        self.nodelist = nodelist
1345-
1346-    def __repr__(self):
1347-        return "<WithNode>"
1348-
1349-    def render(self, context):
1350-        val = self.var.resolve(context)
1351-        context.push()
1352-        context[self.name] = val
1353-        output = self.nodelist.render(context)
1354-        context.pop()
1355-        return output
1356-
1357-#@register.tag
1358-def autoescape(parser, token):
1359-    """
1360-    Force autoescape behaviour for this block.
1361-    """
1362-    args = token.contents.split()
1363-    if len(args) != 2:
1364-        raise TemplateSyntaxError("'Autoescape' tag requires exactly one argument.")
1365-    arg = args[1]
1366-    if arg not in (u'on', u'off'):
1367-        raise TemplateSyntaxError("'Autoescape' argument should be 'on' or 'off'")
1368-    nodelist = parser.parse(('endautoescape',))
1369-    parser.delete_first_token()
1370-    return AutoEscapeControlNode((arg == 'on'), nodelist)
1371-autoescape = register.tag(autoescape)
1372-
1373-#@register.tag
1374-def comment(parser, token):
1375-    """
1376-    Ignores everything between ``{% comment %}`` and ``{% endcomment %}``.
1377-    """
1378-    parser.skip_past('endcomment')
1379-    return CommentNode()
1380-comment = register.tag(comment)
1381-
1382-#@register.tag
1383-def cycle(parser, token):
1384-    """
1385-    Cycles among the given strings each time this tag is encountered.
1386-
1387-    Within a loop, cycles among the given strings each time through
1388-    the loop::
1389-
1390-        {% for o in some_list %}
1391-            <tr class="{% cycle 'row1' 'row2' %}">
1392-                ...
1393-            </tr>
1394-        {% endfor %}
1395-
1396-    Outside of a loop, give the values a unique name the first time you call
1397-    it, then use that name each sucessive time through::
1398-
1399-            <tr class="{% cycle 'row1' 'row2' 'row3' as rowcolors %}">...</tr>
1400-            <tr class="{% cycle rowcolors %}">...</tr>
1401-            <tr class="{% cycle rowcolors %}">...</tr>
1402-
1403-    You can use any number of values, separated by spaces. Commas can also
1404-    be used to separate values; if a comma is used, the cycle values are
1405-    interpreted as literal strings.
1406-    """
1407-
1408-    # Note: This returns the exact same node on each {% cycle name %} call;
1409-    # that is, the node object returned from {% cycle a b c as name %} and the
1410-    # one returned from {% cycle name %} are the exact same object. This
1411-    # shouldn't cause problems (heh), but if it does, now you know.
1412-    #
1413-    # Ugly hack warning: This stuffs the named template dict into parser so
1414-    # that names are only unique within each template (as opposed to using
1415-    # a global variable, which would make cycle names have to be unique across
1416-    # *all* templates.
1417-
1418-    args = token.split_contents()
1419-
1420-    if len(args) < 2:
1421-        raise TemplateSyntaxError("'cycle' tag requires at least two arguments")
1422-
1423-    if ',' in args[1]:
1424-        # Backwards compatibility: {% cycle a,b %} or {% cycle a,b as foo %}
1425-        # case.
1426-        args[1:2] = ['"%s"' % arg for arg in args[1].split(",")]
1427-
1428-    if len(args) == 2:
1429-        # {% cycle foo %} case.
1430-        name = args[1]
1431-        if not hasattr(parser, '_namedCycleNodes'):
1432-            raise TemplateSyntaxError("No named cycles in template. '%s' is not defined" % name)
1433-        if not name in parser._namedCycleNodes:
1434-            raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
1435-        return parser._namedCycleNodes[name]
1436-
1437-    if len(args) > 4 and args[-2] == 'as':
1438-        name = args[-1]
1439-        node = CycleNode(args[1:-2], name)
1440-        if not hasattr(parser, '_namedCycleNodes'):
1441-            parser._namedCycleNodes = {}
1442-        parser._namedCycleNodes[name] = node
1443-    else:
1444-        node = CycleNode(args[1:])
1445-    return node
1446-cycle = register.tag(cycle)
1447-
1448-def debug(parser, token):
1449-    """
1450-    Outputs a whole load of debugging information, including the current
1451-    context and imported modules.
1452-
1453-    Sample usage::
1454-
1455-        <pre>
1456-            {% debug %}
1457-        </pre>
1458-    """
1459-    return DebugNode()
1460-debug = register.tag(debug)
1461-
1462-#@register.tag(name="filter")
1463-def do_filter(parser, token):
1464-    """
1465-    Filters the contents of the block through variable filters.
1466-
1467-    Filters can also be piped through each other, and they can have
1468-    arguments -- just like in variable syntax.
1469-
1470-    Sample usage::
1471-
1472-        {% filter force_escape|lower %}
1473-            This text will be HTML-escaped, and will appear in lowercase.
1474-        {% endfilter %}
1475-    """
1476-    _, rest = token.contents.split(None, 1)
1477-    filter_expr = parser.compile_filter("var|%s" % (rest))
1478-    for func, unused in filter_expr.filters:
1479-        if getattr(func, '_decorated_function', func).__name__ in ('escape', 'safe'):
1480-            raise TemplateSyntaxError('"filter %s" is not permitted.  Use the "autoescape" tag instead.' % func.__name__)
1481-    nodelist = parser.parse(('endfilter',))
1482-    parser.delete_first_token()
1483-    return FilterNode(filter_expr, nodelist)
1484-do_filter = register.tag("filter", do_filter)
1485-
1486-#@register.tag
1487-def firstof(parser, token):
1488-    """
1489-    Outputs the first variable passed that is not False.
1490-
1491-    Outputs nothing if all the passed variables are False.
1492-
1493-    Sample usage::
1494-
1495-        {% firstof var1 var2 var3 %}
1496-
1497-    This is equivalent to::
1498-
1499-        {% if var1 %}
1500-            {{ var1 }}
1501-        {% else %}{% if var2 %}
1502-            {{ var2 }}
1503-        {% else %}{% if var3 %}
1504-            {{ var3 }}
1505-        {% endif %}{% endif %}{% endif %}
1506-
1507-    but obviously much cleaner!
1508-
1509-    You can also use a literal string as a fallback value in case all
1510-    passed variables are False::
1511-
1512-        {% firstof var1 var2 var3 "fallback value" %}
1513-
1514-    """
1515-    bits = token.split_contents()[1:]
1516-    if len(bits) < 1:
1517-        raise TemplateSyntaxError("'firstof' statement requires at least one"
1518-                                  " argument")
1519-    return FirstOfNode(bits)
1520-firstof = register.tag(firstof)
1521-
1522-#@register.tag(name="for")
1523-def do_for(parser, token):
1524-    """
1525-    Loops over each item in an array.
1526-
1527-    For example, to display a list of athletes given ``athlete_list``::
1528-
1529-        <ul>
1530-        {% for athlete in athlete_list %}
1531-            <li>{{ athlete.name }}</li>
1532-        {% endfor %}
1533-        </ul>
1534-
1535-    You can loop over a list in reverse by using
1536-    ``{% for obj in list reversed %}``.
1537-
1538-    You can also unpack multiple values from a two-dimensional array::
1539-
1540-        {% for key,value in dict.items %}
1541-            {{ key }}: {{ value }}
1542-        {% endfor %}
1543-
1544-    The for loop sets a number of variables available within the loop:
1545-
1546-        ==========================  ================================================
1547-        Variable                    Description
1548-        ==========================  ================================================
1549-        ``forloop.counter``         The current iteration of the loop (1-indexed)
1550-        ``forloop.counter0``        The current iteration of the loop (0-indexed)
1551-        ``forloop.revcounter``      The number of iterations from the end of the
1552-                                    loop (1-indexed)
1553-        ``forloop.revcounter0``     The number of iterations from the end of the
1554-                                    loop (0-indexed)
1555-        ``forloop.first``           True if this is the first time through the loop
1556-        ``forloop.last``            True if this is the last time through the loop
1557-        ``forloop.parentloop``      For nested loops, this is the loop "above" the
1558-                                    current one
1559-        ==========================  ================================================
1560-
1561-    """
1562-    bits = token.contents.split()
1563-    if len(bits) < 4:
1564-        raise TemplateSyntaxError("'for' statements should have at least four"
1565-                                  " words: %s" % token.contents)
1566-
1567-    is_reversed = bits[-1] == 'reversed'
1568-    in_index = is_reversed and -3 or -2
1569-    if bits[in_index] != 'in':
1570-        raise TemplateSyntaxError("'for' statements should use the format"
1571-                                  " 'for x in y': %s" % token.contents)
1572-
1573-    loopvars = re.sub(r' *, *', ',', ' '.join(bits[1:in_index])).split(',')
1574-    for var in loopvars:
1575-        if not var or ' ' in var:
1576-            raise TemplateSyntaxError("'for' tag received an invalid argument:"
1577-                                      " %s" % token.contents)
1578-
1579-    sequence = parser.compile_filter(bits[in_index+1])
1580-    nodelist_loop = parser.parse(('endfor',))
1581-    parser.delete_first_token()
1582-    return ForNode(loopvars, sequence, is_reversed, nodelist_loop)
1583-do_for = register.tag("for", do_for)
1584-
1585-def do_ifequal(parser, token, negate):
1586-    bits = list(token.split_contents())
1587-    if len(bits) != 3:
1588-        raise TemplateSyntaxError, "%r takes two arguments" % bits[0]
1589-    end_tag = 'end' + bits[0]
1590-    nodelist_true = parser.parse(('else', end_tag))
1591-    token = parser.next_token()
1592-    if token.contents == 'else':
1593-        nodelist_false = parser.parse((end_tag,))
1594-        parser.delete_first_token()
1595-    else:
1596-        nodelist_false = NodeList()
1597-    return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate)
1598-
1599-#@register.tag
1600-def ifequal(parser, token):
1601-    """
1602-    Outputs the contents of the block if the two arguments equal each other.
1603-
1604-    Examples::
1605-
1606-        {% ifequal user.id comment.user_id %}
1607-            ...
1608-        {% endifequal %}
1609-
1610-        {% ifnotequal user.id comment.user_id %}
1611-            ...
1612-        {% else %}
1613-            ...
1614-        {% endifnotequal %}
1615-    """
1616-    return do_ifequal(parser, token, False)
1617-ifequal = register.tag(ifequal)
1618-
1619-#@register.tag
1620-def ifnotequal(parser, token):
1621-    """
1622-    Outputs the contents of the block if the two arguments are not equal.
1623-    See ifequal.
1624-    """
1625-    return do_ifequal(parser, token, True)
1626-ifnotequal = register.tag(ifnotequal)
1627-
1628-#@register.tag(name="if")
1629-def do_if(parser, token):
1630-    """
1631-    The ``{% if %}`` tag evaluates a variable, and if that variable is "true"
1632-    (i.e., exists, is not empty, and is not a false boolean value), the
1633-    contents of the block are output:
1634-
1635-    ::
1636-
1637-        {% if athlete_list %}
1638-            Number of athletes: {{ athlete_list|count }}
1639-        {% else %}
1640-            No athletes.
1641-        {% endif %}
1642-
1643-    In the above, if ``athlete_list`` is not empty, the number of athletes will
1644-    be displayed by the ``{{ athlete_list|count }}`` variable.
1645-
1646-    As you can see, the ``if`` tag can take an option ``{% else %}`` clause
1647-    that will be displayed if the test fails.
1648-
1649-    ``if`` tags may use ``or``, ``and`` or ``not`` to test a number of
1650-    variables or to negate a given variable::
1651-
1652-        {% if not athlete_list %}
1653-            There are no athletes.
1654-        {% endif %}
1655-
1656-        {% if athlete_list or coach_list %}
1657-            There are some athletes or some coaches.
1658-        {% endif %}
1659-
1660-        {% if athlete_list and coach_list %}
1661-            Both atheletes and coaches are available.
1662-        {% endif %}
1663-
1664-        {% if not athlete_list or coach_list %}
1665-            There are no athletes, or there are some coaches.
1666-        {% endif %}
1667-
1668-        {% if athlete_list and not coach_list %}
1669-            There are some athletes and absolutely no coaches.
1670-        {% endif %}
1671-
1672-    ``if`` tags do not allow ``and`` and ``or`` clauses with the same tag,
1673-    because the order of logic would be ambigous. For example, this is
1674-    invalid::
1675-
1676-        {% if athlete_list and coach_list or cheerleader_list %}
1677-
1678-    If you need to combine ``and`` and ``or`` to do advanced logic, just use
1679-    nested if tags. For example::
1680-
1681-        {% if athlete_list %}
1682-            {% if coach_list or cheerleader_list %}
1683-                We have athletes, and either coaches or cheerleaders!
1684-            {% endif %}
1685-        {% endif %}
1686-    """
1687-    bits = token.contents.split()
1688-    del bits[0]
1689-    if not bits:
1690-        raise TemplateSyntaxError("'if' statement requires at least one argument")
1691-    # Bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d']
1692-    bitstr = ' '.join(bits)
1693-    boolpairs = bitstr.split(' and ')
1694-    boolvars = []
1695-    if len(boolpairs) == 1:
1696-        link_type = IfNode.LinkTypes.or_
1697-        boolpairs = bitstr.split(' or ')
1698-    else:
1699-        link_type = IfNode.LinkTypes.and_
1700-        if ' or ' in bitstr:
1701-            raise TemplateSyntaxError, "'if' tags can't mix 'and' and 'or'"
1702-    for boolpair in boolpairs:
1703-        if ' ' in boolpair:
1704-            try:
1705-                not_, boolvar = boolpair.split()
1706-            except ValueError:
1707-                raise TemplateSyntaxError, "'if' statement improperly formatted"
1708-            if not_ != 'not':
1709-                raise TemplateSyntaxError, "Expected 'not' in if statement"
1710-            boolvars.append((True, parser.compile_filter(boolvar)))
1711-        else:
1712-            boolvars.append((False, parser.compile_filter(boolpair)))
1713-    nodelist_true = parser.parse(('else', 'endif'))
1714-    token = parser.next_token()
1715-    if token.contents == 'else':
1716-        nodelist_false = parser.parse(('endif',))
1717-        parser.delete_first_token()
1718-    else:
1719-        nodelist_false = NodeList()
1720-    return IfNode(boolvars, nodelist_true, nodelist_false, link_type)
1721-do_if = register.tag("if", do_if)
1722-
1723-#@register.tag
1724-def ifchanged(parser, token):
1725-    """
1726-    Checks if a value has changed from the last iteration of a loop.
1727-
1728-    The 'ifchanged' block tag is used within a loop. It has two possible uses.
1729-
1730-    1. Checks its own rendered contents against its previous state and only
1731-       displays the content if it has changed. For example, this displays a
1732-       list of days, only displaying the month if it changes::
1733-
1734-            <h1>Archive for {{ year }}</h1>
1735-
1736-            {% for date in days %}
1737-                {% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}
1738-                <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
1739-            {% endfor %}
1740-
1741-    2. If given a variable, check whether that variable has changed.
1742-       For example, the following shows the date every time it changes, but
1743-       only shows the hour if both the hour and the date have changed::
1744-
1745-            {% for date in days %}
1746-                {% ifchanged date.date %} {{ date.date }} {% endifchanged %}
1747-                {% ifchanged date.hour date.date %}
1748-                    {{ date.hour }}
1749-                {% endifchanged %}
1750-            {% endfor %}
1751-    """
1752-    bits = token.contents.split()
1753-    nodelist = parser.parse(('endifchanged',))
1754-    parser.delete_first_token()
1755-    return IfChangedNode(nodelist, *bits[1:])
1756-ifchanged = register.tag(ifchanged)
1757-
1758-#@register.tag
1759-def ssi(parser, token):
1760-    """
1761-    Outputs the contents of a given file into the page.
1762-
1763-    Like a simple "include" tag, the ``ssi`` tag includes the contents
1764-    of another file -- which must be specified using an absolute path --
1765-    in the current page::
1766-
1767-        {% ssi /home/html/ljworld.com/includes/right_generic.html %}
1768-
1769-    If the optional "parsed" parameter is given, the contents of the included
1770-    file are evaluated as template code, with the current context::
1771-
1772-        {% ssi /home/html/ljworld.com/includes/right_generic.html parsed %}
1773-    """
1774-    bits = token.contents.split()
1775-    parsed = False
1776-    if len(bits) not in (2, 3):
1777-        raise TemplateSyntaxError("'ssi' tag takes one argument: the path to"
1778-                                  " the file to be included")
1779-    if len(bits) == 3:
1780-        if bits[2] == 'parsed':
1781-            parsed = True
1782-        else:
1783-            raise TemplateSyntaxError("Second (optional) argument to %s tag"
1784-                                      " must be 'parsed'" % bits[0])
1785-    return SsiNode(bits[1], parsed)
1786-ssi = register.tag(ssi)
1787-
1788-#@register.tag
1789-def load(parser, token):
1790-    """
1791-    Loads a custom template tag set.
1792-
1793-    For example, to load the template tags in
1794-    ``django/templatetags/news/photos.py``::
1795-
1796-        {% load news.photos %}
1797-    """
1798-    bits = token.contents.split()
1799-    for taglib in bits[1:]:
1800-        # add the library to the parser
1801-        try:
1802-            lib = get_library("django.templatetags.%s" % taglib)
1803-            parser.add_library(lib)
1804-        except InvalidTemplateLibrary, e:
1805-            raise TemplateSyntaxError("'%s' is not a valid tag library: %s" %
1806-                                      (taglib, e))
1807-    return LoadNode()
1808-load = register.tag(load)
1809-
1810-#@register.tag
1811-def now(parser, token):
1812-    """
1813-    Displays the date, formatted according to the given string.
1814-
1815-    Uses the same format as PHP's ``date()`` function; see http://php.net/date
1816-    for all the possible values.
1817-
1818-    Sample usage::
1819-
1820-        It is {% now "jS F Y H:i" %}
1821-    """
1822-    bits = token.contents.split('"')
1823-    if len(bits) != 3:
1824-        raise TemplateSyntaxError, "'now' statement takes one argument"
1825-    format_string = bits[1]
1826-    return NowNode(format_string)
1827-now = register.tag(now)
1828-
1829-#@register.tag
1830-def regroup(parser, token):
1831-    """
1832-    Regroups a list of alike objects by a common attribute.
1833-
1834-    This complex tag is best illustrated by use of an example:  say that
1835-    ``people`` is a list of ``Person`` objects that have ``first_name``,
1836-    ``last_name``, and ``gender`` attributes, and you'd like to display a list
1837-    that looks like:
1838-
1839-        * Male:
1840-            * George Bush
1841-            * Bill Clinton
1842-        * Female:
1843-            * Margaret Thatcher
1844-            * Colendeeza Rice
1845-        * Unknown:
1846-            * Pat Smith
1847-
1848-    The following snippet of template code would accomplish this dubious task::
1849-
1850-        {% regroup people by gender as grouped %}
1851-        <ul>
1852-        {% for group in grouped %}
1853-            <li>{{ group.grouper }}
1854-            <ul>
1855-                {% for item in group.list %}
1856-                <li>{{ item }}</li>
1857-                {% endfor %}
1858-            </ul>
1859-        {% endfor %}
1860-        </ul>
1861-
1862-    As you can see, ``{% regroup %}`` populates a variable with a list of
1863-    objects with ``grouper`` and ``list`` attributes.  ``grouper`` contains the
1864-    item that was grouped by; ``list`` contains the list of objects that share
1865-    that ``grouper``.  In this case, ``grouper`` would be ``Male``, ``Female``
1866-    and ``Unknown``, and ``list`` is the list of people with those genders.
1867-
1868-    Note that `{% regroup %}`` does not work when the list to be grouped is not
1869-    sorted by the key you are grouping by!  This means that if your list of
1870-    people was not sorted by gender, you'd need to make sure it is sorted
1871-    before using it, i.e.::
1872-
1873-        {% regroup people|dictsort:"gender" by gender as grouped %}
1874-
1875-    """
1876-    firstbits = token.contents.split(None, 3)
1877-    if len(firstbits) != 4:
1878-        raise TemplateSyntaxError, "'regroup' tag takes five arguments"
1879-    target = parser.compile_filter(firstbits[1])
1880-    if firstbits[2] != 'by':
1881-        raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'")
1882-    lastbits_reversed = firstbits[3][::-1].split(None, 2)
1883-    if lastbits_reversed[1][::-1] != 'as':
1884-        raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
1885-                                  " be 'as'")
1886-
1887-    expression = parser.compile_filter(lastbits_reversed[2][::-1])
1888-
1889-    var_name = lastbits_reversed[0][::-1]
1890-    return RegroupNode(target, expression, var_name)
1891-regroup = register.tag(regroup)
1892-
1893-def spaceless(parser, token):
1894-    """
1895-    Removes whitespace between HTML tags, including tab and newline characters.
1896-
1897-    Example usage::
1898-
1899-        {% spaceless %}
1900-            <p>
1901-                <a href="foo/">Foo</a>
1902-            </p>
1903-        {% endspaceless %}
1904-
1905-    This example would return this HTML::
1906-
1907-        <p><a href="foo/">Foo</a></p>
1908-
1909-    Only space between *tags* is normalized -- not space between tags and text.
1910-    In this example, the space around ``Hello`` won't be stripped::
1911-
1912-        {% spaceless %}
1913-            <strong>
1914-                Hello
1915-            </strong>
1916-        {% endspaceless %}
1917-    """
1918-    nodelist = parser.parse(('endspaceless',))
1919-    parser.delete_first_token()
1920-    return SpacelessNode(nodelist)
1921-spaceless = register.tag(spaceless)
1922-
1923-#@register.tag
1924-def templatetag(parser, token):
1925-    """
1926-    Outputs one of the bits used to compose template tags.
1927-
1928-    Since the template system has no concept of "escaping", to display one of
1929-    the bits used in template tags, you must use the ``{% templatetag %}`` tag.
1930-
1931-    The argument tells which template bit to output:
1932-
1933-        ==================  =======
1934-        Argument            Outputs
1935-        ==================  =======
1936-        ``openblock``       ``{%``
1937-        ``closeblock``      ``%}``
1938-        ``openvariable``    ``{{``
1939-        ``closevariable``   ``}}``
1940-        ``openbrace``       ``{``
1941-        ``closebrace``      ``}``
1942-        ``opencomment``     ``{#``
1943-        ``closecomment``    ``#}``
1944-        ==================  =======
1945-    """
1946-    bits = token.contents.split()
1947-    if len(bits) != 2:
1948-        raise TemplateSyntaxError, "'templatetag' statement takes one argument"
1949-    tag = bits[1]
1950-    if tag not in TemplateTagNode.mapping:
1951-        raise TemplateSyntaxError("Invalid templatetag argument: '%s'."
1952-                                  " Must be one of: %s" %
1953-                                  (tag, TemplateTagNode.mapping.keys()))
1954-    return TemplateTagNode(tag)
1955-templatetag = register.tag(templatetag)
1956-
1957-def url(parser, token):
1958-    """
1959-    Returns an absolute URL matching given view with its parameters.
1960-
1961-    This is a way to define links that aren't tied to a particular URL
1962-    configuration::
1963-
1964-        {% url path.to.some_view arg1,arg2,name1=value1 %}
1965-
1966-    The first argument is a path to a view. It can be an absolute python path
1967-    or just ``app_name.view_name`` without the project name if the view is
1968-    located inside the project.  Other arguments are comma-separated values
1969-    that will be filled in place of positional and keyword arguments in the
1970-    URL. All arguments for the URL should be present.
1971-
1972-    For example if you have a view ``app_name.client`` taking client's id and
1973-    the corresponding line in a URLconf looks like this::
1974-
1975-        ('^client/(\d+)/$', 'app_name.client')
1976-
1977-    and this app's URLconf is included into the project's URLconf under some
1978-    path::
1979-
1980-        ('^clients/', include('project_name.app_name.urls'))
1981-
1982-    then in a template you can create a link for a certain client like this::
1983-
1984-        {% url app_name.client client.id %}
1985-
1986-    The URL will look like ``/clients/client/123/``.
1987-    """
1988-    bits = token.contents.split(' ', 2)
1989-    if len(bits) < 2:
1990-        raise TemplateSyntaxError("'%s' takes at least one argument"
1991-                                  " (path to a view)" % bits[0])
1992-    args = []
1993-    kwargs = {}
1994-    if len(bits) > 2:
1995-        for arg in bits[2].split(','):
1996-            if '=' in arg:
1997-                k, v = arg.split('=', 1)
1998-                k = k.strip()
1999-                kwargs[k] = parser.compile_filter(v)
2000-            else:
2001-                args.append(parser.compile_filter(arg))
2002-    return URLNode(bits[1], args, kwargs)
2003-url = register.tag(url)
2004-
2005-#@register.tag
2006-def widthratio(parser, token):
2007-    """
2008-    For creating bar charts and such, this tag calculates the ratio of a given
2009-    value to a maximum value, and then applies that ratio to a constant.
2010-
2011-    For example::
2012-
2013-        <img src='bar.gif' height='10' width='{% widthratio this_value max_value 100 %}' />
2014-
2015-    Above, if ``this_value`` is 175 and ``max_value`` is 200, the the image in
2016-    the above example will be 88 pixels wide (because 175/200 = .875;
2017-    .875 * 100 = 87.5 which is rounded up to 88).
2018-    """
2019-    bits = token.contents.split()
2020-    if len(bits) != 4:
2021-        raise TemplateSyntaxError("widthratio takes three arguments")
2022-    tag, this_value_expr, max_value_expr, max_width = bits
2023-    try:
2024-        max_width = int(max_width)
2025-    except ValueError:
2026-        raise TemplateSyntaxError("widthratio final argument must be an integer")
2027-    return WidthRatioNode(parser.compile_filter(this_value_expr),
2028-                          parser.compile_filter(max_value_expr), max_width)
2029-widthratio = register.tag(widthratio)
2030-
2031-#@register.tag
2032-def do_with(parser, token):
2033-    """
2034-    Adds a value to the context (inside of this block) for caching and easy
2035-    access.
2036-
2037-    For example::
2038-
2039-        {% with person.some_sql_method as total %}
2040-            {{ total }} object{{ total|pluralize }}
2041-        {% endwith %}
2042-    """
2043-    bits = list(token.split_contents())
2044-    if len(bits) != 4 or bits[2] != "as":
2045-        raise TemplateSyntaxError("%r expected format is 'value as name'" %
2046-                                  bits[0])
2047-    var = parser.compile_filter(bits[1])
2048-    name = bits[3]
2049-    nodelist = parser.parse(('endwith',))
2050-    parser.delete_first_token()
2051-    return WithNode(var, name, nodelist)
2052-do_with = register.tag('with', do_with)
2053diff --git a/django/templatetags/__init__.py b/django/templatetags/__init__.py
2054index 9204535..9c47230 100644
2055--- a/django/templatetags/__init__.py
2056+++ b/django/templatetags/__init__.py
2057@@ -1,7 +1,18 @@
2058+import imp
2059 from django.conf import settings
2060 
2061-for a in settings.INSTALLED_APPS:
2062-    try:
2063-        __path__.extend(__import__(a + '.templatetags', {}, {}, ['']).__path__)
2064-    except ImportError:
2065-        pass
2066+templatetags_modules= []
2067+
2068+def get_templatetags_modules():
2069+    if not templatetags_modules:
2070+        """ Populate list once per thread. """
2071+        for app_module in ['django'] + list(settings.INSTALLED_APPS):
2072+            try:
2073+                components = app_module.split('.')
2074+                mod = __import__(app_module, {}, {}, [components[-1]])
2075+                imp.find_module('templatetags', mod.__path__)
2076+                templatetag_module = '%s.templatetags' % app_module
2077+                templatetags_modules.append(templatetag_module)
2078+            except ImportError:
2079+                continue
2080+    return templatetags_modules
2081diff --git a/django/templatetags/defaultfilters.py b/django/templatetags/defaultfilters.py
2082new file mode 100644
2083index 0000000..cef3143
2084--- /dev/null
2085+++ b/django/templatetags/defaultfilters.py
2086@@ -0,0 +1,851 @@
2087+"""Default variable filters."""
2088+
2089+import re
2090+import random as random_module
2091+try:
2092+    from functools import wraps
2093+except ImportError:
2094+    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
2095+
2096+from django.template import Variable, Library
2097+from django.conf import settings
2098+from django.utils.translation import ugettext, ungettext
2099+from django.utils.encoding import force_unicode, iri_to_uri
2100+from django.utils.safestring import mark_safe, SafeData
2101+
2102+register = Library()
2103+
2104+#######################
2105+# STRING DECORATOR    #
2106+#######################
2107+
2108+def stringfilter(func):
2109+    """
2110+    Decorator for filters which should only receive unicode objects. The object
2111+    passed as the first positional argument will be converted to a unicode
2112+    object.
2113+    """
2114+    def _dec(*args, **kwargs):
2115+        if args:
2116+            args = list(args)
2117+            args[0] = force_unicode(args[0])
2118+            if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False):
2119+                return mark_safe(func(*args, **kwargs))
2120+        return func(*args, **kwargs)
2121+
2122+    # Include a reference to the real function (used to check original
2123+    # arguments by the template parser).
2124+    _dec._decorated_function = getattr(func, '_decorated_function', func)
2125+    for attr in ('is_safe', 'needs_autoescape'):
2126+        if hasattr(func, attr):
2127+            setattr(_dec, attr, getattr(func, attr))
2128+    return wraps(func)(_dec)
2129+
2130+###################
2131+# STRINGS         #
2132+###################
2133+
2134+
2135+def addslashes(value):
2136+    """
2137+    Adds slashes before quotes. Useful for escaping strings in CSV, for
2138+    example. Less useful for escaping JavaScript; use the ``escapejs``
2139+    filter instead.
2140+    """
2141+    return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
2142+addslashes.is_safe = True
2143+addslashes = stringfilter(addslashes)
2144+
2145+def capfirst(value):
2146+    """Capitalizes the first character of the value."""
2147+    return value and value[0].upper() + value[1:]
2148+capfirst.is_safe=True
2149+capfirst = stringfilter(capfirst)
2150+
2151+_js_escapes = (
2152+    ('\\', '\\\\'),
2153+    ('"', '\\"'),
2154+    ("'", "\\'"),
2155+    ('\n', '\\n'),
2156+    ('\r', '\\r'),
2157+    ('\b', '\\b'),
2158+    ('\f', '\\f'),
2159+    ('\t', '\\t'),
2160+    ('\v', '\\v'),
2161+    ('</', '<\\/'),
2162+)
2163+def escapejs(value):
2164+    """Backslash-escapes characters for use in JavaScript strings."""
2165+    for bad, good in _js_escapes:
2166+        value = value.replace(bad, good)
2167+    return value
2168+escapejs = stringfilter(escapejs)
2169+
2170+def fix_ampersands(value):
2171+    """Replaces ampersands with ``&amp;`` entities."""
2172+    from django.utils.html import fix_ampersands
2173+    return fix_ampersands(value)
2174+fix_ampersands.is_safe=True
2175+fix_ampersands = stringfilter(fix_ampersands)
2176+
2177+def floatformat(text, arg=-1):
2178+    """
2179+    Displays a float to a specified number of decimal places.
2180+
2181+    If called without an argument, it displays the floating point number with
2182+    one decimal place -- but only if there's a decimal place to be displayed:
2183+
2184+    * num1 = 34.23234
2185+    * num2 = 34.00000
2186+    * num3 = 34.26000
2187+    * {{ num1|floatformat }} displays "34.2"
2188+    * {{ num2|floatformat }} displays "34"
2189+    * {{ num3|floatformat }} displays "34.3"
2190+
2191+    If arg is positive, it will always display exactly arg number of decimal
2192+    places:
2193+
2194+    * {{ num1|floatformat:3 }} displays "34.232"
2195+    * {{ num2|floatformat:3 }} displays "34.000"
2196+    * {{ num3|floatformat:3 }} displays "34.260"
2197+
2198+    If arg is negative, it will display arg number of decimal places -- but
2199+    only if there are places to be displayed:
2200+
2201+    * {{ num1|floatformat:"-3" }} displays "34.232"
2202+    * {{ num2|floatformat:"-3" }} displays "34"
2203+    * {{ num3|floatformat:"-3" }} displays "34.260"
2204+    """
2205+    try:
2206+        f = float(text)
2207+    except (ValueError, TypeError):
2208+        return u''
2209+    try:
2210+        d = int(arg)
2211+    except ValueError:
2212+        return force_unicode(f)
2213+    try:
2214+        m = f - int(f)
2215+    except OverflowError:
2216+        return force_unicode(f)
2217+    if not m and d < 0:
2218+        return mark_safe(u'%d' % int(f))
2219+    else:
2220+        formatstr = u'%%.%df' % abs(d)
2221+        return mark_safe(formatstr % f)
2222+floatformat.is_safe = True
2223+
2224+def iriencode(value):
2225+    """Escapes an IRI value for use in a URL."""
2226+    return force_unicode(iri_to_uri(value))
2227+iriencode.is_safe = True
2228+iriencode = stringfilter(iriencode)
2229+
2230+def linenumbers(value, autoescape=None):
2231+    """Displays text with line numbers."""
2232+    from django.utils.html import escape
2233+    lines = value.split(u'\n')
2234+    # Find the maximum width of the line count, for use with zero padding
2235+    # string format command
2236+    width = unicode(len(unicode(len(lines))))
2237+    if not autoescape or isinstance(value, SafeData):
2238+        for i, line in enumerate(lines):
2239+            lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, line)
2240+    else:
2241+        for i, line in enumerate(lines):
2242+            lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, escape(line))
2243+    return mark_safe(u'\n'.join(lines))
2244+linenumbers.is_safe = True
2245+linenumbers.needs_autoescape = True
2246+linenumbers = stringfilter(linenumbers)
2247+
2248+def lower(value):
2249+    """Converts a string into all lowercase."""
2250+    return value.lower()
2251+lower.is_safe = True
2252+lower = stringfilter(lower)
2253+
2254+def make_list(value):
2255+    """
2256+    Returns the value turned into a list.
2257+
2258+    For an integer, it's a list of digits.
2259+    For a string, it's a list of characters.
2260+    """
2261+    return list(value)
2262+make_list.is_safe = False
2263+make_list = stringfilter(make_list)
2264+
2265+def slugify(value):
2266+    """
2267+    Normalizes string, converts to lowercase, removes non-alpha characters,
2268+    and converts spaces to hyphens.
2269+    """
2270+    import unicodedata
2271+    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
2272+    value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
2273+    return mark_safe(re.sub('[-\s]+', '-', value))
2274+slugify.is_safe = True
2275+slugify = stringfilter(slugify)
2276+
2277+def stringformat(value, arg):
2278+    """
2279+    Formats the variable according to the arg, a string formatting specifier.
2280+
2281+    This specifier uses Python string formating syntax, with the exception that
2282+    the leading "%" is dropped.
2283+
2284+    See http://docs.python.org/lib/typesseq-strings.html for documentation
2285+    of Python string formatting
2286+    """
2287+    try:
2288+        return (u"%" + unicode(arg)) % value
2289+    except (ValueError, TypeError):
2290+        return u""
2291+stringformat.is_safe = True
2292+
2293+def title(value):
2294+    """Converts a string into titlecase."""
2295+    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
2296+title.is_safe = True
2297+title = stringfilter(title)
2298+
2299+def truncatewords(value, arg):
2300+    """
2301+    Truncates a string after a certain number of words.
2302+
2303+    Argument: Number of words to truncate after.
2304+    """
2305+    from django.utils.text import truncate_words
2306+    try:
2307+        length = int(arg)
2308+    except ValueError: # Invalid literal for int().
2309+        return value # Fail silently.
2310+    return truncate_words(value, length)
2311+truncatewords.is_safe = True
2312+truncatewords = stringfilter(truncatewords)
2313+
2314+def truncatewords_html(value, arg):
2315+    """
2316+    Truncates HTML after a certain number of words.
2317+
2318+    Argument: Number of words to truncate after.
2319+    """
2320+    from django.utils.text import truncate_html_words
2321+    try:
2322+        length = int(arg)
2323+    except ValueError: # invalid literal for int()
2324+        return value # Fail silently.
2325+    return truncate_html_words(value, length)
2326+truncatewords_html.is_safe = True
2327+truncatewords_html = stringfilter(truncatewords_html)
2328+
2329+def upper(value):
2330+    """Converts a string into all uppercase."""
2331+    return value.upper()
2332+upper.is_safe = False
2333+upper = stringfilter(upper)
2334+
2335+def urlencode(value):
2336+    """Escapes a value for use in a URL."""
2337+    from django.utils.http import urlquote
2338+    return urlquote(value)
2339+urlencode.is_safe = False
2340+urlencode = stringfilter(urlencode)
2341+
2342+def urlize(value, autoescape=None):
2343+    """Converts URLs in plain text into clickable links."""
2344+    from django.utils.html import urlize
2345+    return mark_safe(urlize(value, nofollow=True, autoescape=autoescape))
2346+urlize.is_safe=True
2347+urlize.needs_autoescape = True
2348+urlize = stringfilter(urlize)
2349+
2350+def urlizetrunc(value, limit, autoescape=None):
2351+    """
2352+    Converts URLs into clickable links, truncating URLs to the given character
2353+    limit, and adding 'rel=nofollow' attribute to discourage spamming.
2354+
2355+    Argument: Length to truncate URLs to.
2356+    """
2357+    from django.utils.html import urlize
2358+    return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True,
2359+                            autoescape=autoescape))
2360+urlizetrunc.is_safe = True
2361+urlizetrunc.needs_autoescape = True
2362+urlizetrunc = stringfilter(urlizetrunc)
2363+
2364+def wordcount(value):
2365+    """Returns the number of words."""
2366+    return len(value.split())
2367+wordcount.is_safe = False
2368+wordcount = stringfilter(wordcount)
2369+
2370+def wordwrap(value, arg):
2371+    """
2372+    Wraps words at specified line length.
2373+
2374+    Argument: number of characters to wrap the text at.
2375+    """
2376+    from django.utils.text import wrap
2377+    return wrap(value, int(arg))
2378+wordwrap.is_safe = True
2379+wordwrap = stringfilter(wordwrap)
2380+
2381+def ljust(value, arg):
2382+    """
2383+    Left-aligns the value in a field of a given width.
2384+
2385+    Argument: field size.
2386+    """
2387+    return value.ljust(int(arg))
2388+ljust.is_safe = True
2389+ljust = stringfilter(ljust)
2390+
2391+def rjust(value, arg):
2392+    """
2393+    Right-aligns the value in a field of a given width.
2394+
2395+    Argument: field size.
2396+    """
2397+    return value.rjust(int(arg))
2398+rjust.is_safe = True
2399+rjust = stringfilter(rjust)
2400+
2401+def center(value, arg):
2402+    """Centers the value in a field of a given width."""
2403+    return value.center(int(arg))
2404+center.is_safe = True
2405+center = stringfilter(center)
2406+
2407+def cut(value, arg):
2408+    """
2409+    Removes all values of arg from the given string.
2410+    """
2411+    safe = isinstance(value, SafeData)
2412+    value = value.replace(arg, u'')
2413+    if safe and arg != ';':
2414+        return mark_safe(value)
2415+    return value
2416+cut = stringfilter(cut)
2417+
2418+###################
2419+# HTML STRINGS    #
2420+###################
2421+
2422+def escape(value):
2423+    """
2424+    Marks the value as a string that should not be auto-escaped.
2425+    """
2426+    from django.utils.safestring import mark_for_escaping
2427+    return mark_for_escaping(value)
2428+escape.is_safe = True
2429+escape = stringfilter(escape)
2430+
2431+def force_escape(value):
2432+    """
2433+    Escapes a string's HTML. This returns a new string containing the escaped
2434+    characters (as opposed to "escape", which marks the content for later
2435+    possible escaping).
2436+    """
2437+    from django.utils.html import escape
2438+    return mark_safe(escape(value))
2439+force_escape = stringfilter(force_escape)
2440+force_escape.is_safe = True
2441+
2442+def linebreaks(value, autoescape=None):
2443+    """
2444+    Replaces line breaks in plain text with appropriate HTML; a single
2445+    newline becomes an HTML line break (``<br />``) and a new line
2446+    followed by a blank line becomes a paragraph break (``</p>``).
2447+    """
2448+    from django.utils.html import linebreaks
2449+    autoescape = autoescape and not isinstance(value, SafeData)
2450+    return mark_safe(linebreaks(value, autoescape))
2451+linebreaks.is_safe = True
2452+linebreaks.needs_autoescape = True
2453+linebreaks = stringfilter(linebreaks)
2454+
2455+def linebreaksbr(value, autoescape=None):
2456+    """
2457+    Converts all newlines in a piece of plain text to HTML line breaks
2458+    (``<br />``).
2459+    """
2460+    if autoescape and not isinstance(value, SafeData):
2461+        from django.utils.html import escape
2462+        value = escape(value)
2463+    return mark_safe(value.replace('\n', '<br />'))
2464+linebreaksbr.is_safe = True
2465+linebreaksbr.needs_autoescape = True
2466+linebreaksbr = stringfilter(linebreaksbr)
2467+
2468+def safe(value):
2469+    """
2470+    Marks the value as a string that should not be auto-escaped.
2471+    """
2472+    from django.utils.safestring import mark_safe
2473+    return mark_safe(value)
2474+safe.is_safe = True
2475+safe = stringfilter(safe)
2476+
2477+def removetags(value, tags):
2478+    """Removes a space separated list of [X]HTML tags from the output."""
2479+    tags = [re.escape(tag) for tag in tags.split()]
2480+    tags_re = u'(%s)' % u'|'.join(tags)
2481+    starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U)
2482+    endtag_re = re.compile(u'</%s>' % tags_re)
2483+    value = starttag_re.sub(u'', value)
2484+    value = endtag_re.sub(u'', value)
2485+    return value
2486+removetags.is_safe = True
2487+removetags = stringfilter(removetags)
2488+
2489+def striptags(value):
2490+    """Strips all [X]HTML tags."""
2491+    from django.utils.html import strip_tags
2492+    return strip_tags(value)
2493+striptags.is_safe = True
2494+striptags = stringfilter(striptags)
2495+
2496+###################
2497+# LISTS           #
2498+###################
2499+
2500+def dictsort(value, arg):
2501+    """
2502+    Takes a list of dicts, returns that list sorted by the property given in
2503+    the argument.
2504+    """
2505+    var_resolve = Variable(arg).resolve
2506+    decorated = [(var_resolve(item), item) for item in value]
2507+    decorated.sort()
2508+    return [item[1] for item in decorated]
2509+dictsort.is_safe = False
2510+
2511+def dictsortreversed(value, arg):
2512+    """
2513+    Takes a list of dicts, returns that list sorted in reverse order by the
2514+    property given in the argument.
2515+    """
2516+    var_resolve = Variable(arg).resolve
2517+    decorated = [(var_resolve(item), item) for item in value]
2518+    decorated.sort()
2519+    decorated.reverse()
2520+    return [item[1] for item in decorated]
2521+dictsortreversed.is_safe = False
2522+
2523+def first(value):
2524+    """Returns the first item in a list."""
2525+    try:
2526+        return value[0]
2527+    except IndexError:
2528+        return u''
2529+first.is_safe = False
2530+
2531+def join(value, arg):
2532+    """Joins a list with a string, like Python's ``str.join(list)``."""
2533+    try:
2534+        data = arg.join(map(force_unicode, value))
2535+    except AttributeError: # fail silently but nicely
2536+        return value
2537+    safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData),
2538+            value, True)
2539+    if safe_args:
2540+        return mark_safe(data)
2541+    else:
2542+        return data
2543+join.is_safe = True
2544+
2545+def last(value):
2546+    "Returns the last item in a list"
2547+    try:
2548+        return value[-1]
2549+    except IndexError:
2550+        return u''
2551+last.is_safe = True
2552+
2553+def length(value):
2554+    """Returns the length of the value - useful for lists."""
2555+    return len(value)
2556+length.is_safe = True
2557+
2558+def length_is(value, arg):
2559+    """Returns a boolean of whether the value's length is the argument."""
2560+    return len(value) == int(arg)
2561+length_is.is_safe = True
2562+
2563+def random(value):
2564+    """Returns a random item from the list."""
2565+    return random_module.choice(value)
2566+random.is_safe = True
2567+
2568+def slice_(value, arg):
2569+    """
2570+    Returns a slice of the list.
2571+
2572+    Uses the same syntax as Python's list slicing; see
2573+    http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
2574+    for an introduction.
2575+    """
2576+    try:
2577+        bits = []
2578+        for x in arg.split(u':'):
2579+            if len(x) == 0:
2580+                bits.append(None)
2581+            else:
2582+                bits.append(int(x))
2583+        return value[slice(*bits)]
2584+
2585+    except (ValueError, TypeError):
2586+        return value # Fail silently.
2587+slice_.is_safe = True
2588+
2589+def unordered_list(value, autoescape=None):
2590+    """
2591+    Recursively takes a self-nested list and returns an HTML unordered list --
2592+    WITHOUT opening and closing <ul> tags.
2593+
2594+    The list is assumed to be in the proper format. For example, if ``var``
2595+    contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``,
2596+    then ``{{ var|unordered_list }}`` would return::
2597+
2598+        <li>States
2599+        <ul>
2600+                <li>Kansas
2601+                <ul>
2602+                        <li>Lawrence</li>
2603+                        <li>Topeka</li>
2604+                </ul>
2605+                </li>
2606+                <li>Illinois</li>
2607+        </ul>
2608+        </li>
2609+    """
2610+    if autoescape:
2611+        from django.utils.html import conditional_escape
2612+        escaper = conditional_escape
2613+    else:
2614+        escaper = lambda x: x
2615+    def convert_old_style_list(list_):
2616+        """
2617+        Converts old style lists to the new easier to understand format.
2618+
2619+        The old list format looked like:
2620+            ['Item 1', [['Item 1.1', []], ['Item 1.2', []]]
2621+
2622+        And it is converted to:
2623+            ['Item 1', ['Item 1.1', 'Item 1.2]]
2624+        """
2625+        if not isinstance(list_, (tuple, list)) or len(list_) != 2:
2626+            return list_, False
2627+        first_item, second_item = list_
2628+        if second_item == []:
2629+            return [first_item], True
2630+        old_style_list = True
2631+        new_second_item = []
2632+        for sublist in second_item:
2633+            item, old_style_list = convert_old_style_list(sublist)
2634+            if not old_style_list:
2635+                break
2636+            new_second_item.extend(item)
2637+        if old_style_list:
2638+            second_item = new_second_item
2639+        return [first_item, second_item], old_style_list
2640+    def _helper(list_, tabs=1):
2641+        indent = u'\t' * tabs
2642+        output = []
2643+
2644+        list_length = len(list_)
2645+        i = 0
2646+        while i < list_length:
2647+            title = list_[i]
2648+            sublist = ''
2649+            sublist_item = None
2650+            if isinstance(title, (list, tuple)):
2651+                sublist_item = title
2652+                title = ''
2653+            elif i < list_length - 1:
2654+                next_item = list_[i+1]
2655+                if next_item and isinstance(next_item, (list, tuple)):
2656+                    # The next item is a sub-list.
2657+                    sublist_item = next_item
2658+                    # We've processed the next item now too.
2659+                    i += 1
2660+            if sublist_item:
2661+                sublist = _helper(sublist_item, tabs+1)
2662+                sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist,
2663+                                                         indent, indent)
2664+            output.append('%s<li>%s%s</li>' % (indent,
2665+                    escaper(force_unicode(title)), sublist))
2666+            i += 1
2667+        return '\n'.join(output)
2668+    value, converted = convert_old_style_list(value)
2669+    return mark_safe(_helper(value))
2670+unordered_list.is_safe = True
2671+unordered_list.needs_autoescape = True
2672+
2673+###################
2674+# INTEGERS        #
2675+###################
2676+
2677+def add(value, arg):
2678+    """Adds the arg to the value."""
2679+    return int(value) + int(arg)
2680+add.is_safe = False
2681+
2682+def get_digit(value, arg):
2683+    """
2684+    Given a whole number, returns the requested digit of it, where 1 is the
2685+    right-most digit, 2 is the second-right-most digit, etc. Returns the
2686+    original value for invalid input (if input or argument is not an integer,
2687+    or if argument is less than 1). Otherwise, output is always an integer.
2688+    """
2689+    try:
2690+        arg = int(arg)
2691+        value = int(value)
2692+    except ValueError:
2693+        return value # Fail silently for an invalid argument
2694+    if arg < 1:
2695+        return value
2696+    try:
2697+        return int(str(value)[-arg])
2698+    except IndexError:
2699+        return 0
2700+get_digit.is_safe = False
2701+
2702+###################
2703+# DATES           #
2704+###################
2705+
2706+def date(value, arg=None):
2707+    """Formats a date according to the given format."""
2708+    from django.utils.dateformat import format
2709+    if not value:
2710+        return u''
2711+    if arg is None:
2712+        arg = settings.DATE_FORMAT
2713+    return format(value, arg)
2714+date.is_safe = False
2715+
2716+def time(value, arg=None):
2717+    """Formats a time according to the given format."""
2718+    from django.utils.dateformat import time_format
2719+    if value in (None, u''):
2720+        return u''
2721+    if arg is None:
2722+        arg = settings.TIME_FORMAT
2723+    return time_format(value, arg)
2724+time.is_safe = False
2725+
2726+def timesince(value, arg=None):
2727+    """Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
2728+    from django.utils.timesince import timesince
2729+    if not value:
2730+        return u''
2731+    if arg:
2732+        return timesince(arg, value)
2733+    return timesince(value)
2734+timesince.is_safe = False
2735+
2736+def timeuntil(value, arg=None):
2737+    """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
2738+    from django.utils.timesince import timesince
2739+    from datetime import datetime
2740+    if not value:
2741+        return u''
2742+    if arg:
2743+        return timesince(arg, value)
2744+    return timesince(datetime.now(), value)
2745+timeuntil.is_safe = False
2746+
2747+###################
2748+# LOGIC           #
2749+###################
2750+
2751+def default(value, arg):
2752+    """If value is unavailable, use given default."""
2753+    return value or arg
2754+default.is_safe = False
2755+
2756+def default_if_none(value, arg):
2757+    """If value is None, use given default."""
2758+    if value is None:
2759+        return arg
2760+    return value
2761+default_if_none.is_safe = False
2762+
2763+def divisibleby(value, arg):
2764+    """Returns True if the value is devisible by the argument."""
2765+    return int(value) % int(arg) == 0
2766+divisibleby.is_safe = False
2767+
2768+def yesno(value, arg=None):
2769+    """
2770+    Given a string mapping values for true, false and (optionally) None,
2771+    returns one of those strings accoding to the value:
2772+
2773+    ==========  ======================  ==================================
2774+    Value       Argument                Outputs
2775+    ==========  ======================  ==================================
2776+    ``True``    ``"yeah,no,maybe"``     ``yeah``
2777+    ``False``   ``"yeah,no,maybe"``     ``no``
2778+    ``None``    ``"yeah,no,maybe"``     ``maybe``
2779+    ``None``    ``"yeah,no"``           ``"no"`` (converts None to False
2780+                                        if no mapping for None is given.
2781+    ==========  ======================  ==================================
2782+    """
2783+    if arg is None:
2784+        arg = ugettext('yes,no,maybe')
2785+    bits = arg.split(u',')
2786+    if len(bits) < 2:
2787+        return value # Invalid arg.
2788+    try:
2789+        yes, no, maybe = bits
2790+    except ValueError:
2791+        # Unpack list of wrong size (no "maybe" value provided).
2792+        yes, no, maybe = bits[0], bits[1], bits[1]
2793+    if value is None:
2794+        return maybe
2795+    if value:
2796+        return yes
2797+    return no
2798+yesno.is_safe = False
2799+
2800+###################
2801+# MISC            #
2802+###################
2803+
2804+def filesizeformat(bytes):
2805+    """
2806+    Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
2807+    102 bytes, etc).
2808+    """
2809+    try:
2810+        bytes = float(bytes)
2811+    except TypeError:
2812+        return u"0 bytes"
2813+
2814+    if bytes < 1024:
2815+        return ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
2816+    if bytes < 1024 * 1024:
2817+        return ugettext("%.1f KB") % (bytes / 1024)
2818+    if bytes < 1024 * 1024 * 1024:
2819+        return ugettext("%.1f MB") % (bytes / (1024 * 1024))
2820+    return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024))
2821+filesizeformat.is_safe = True
2822+
2823+def pluralize(value, arg=u's'):
2824+    """
2825+    Returns a plural suffix if the value is not 1. By default, 's' is used as
2826+    the suffix:
2827+
2828+    * If value is 0, vote{{ value|pluralize }} displays "0 votes".
2829+    * If value is 1, vote{{ value|pluralize }} displays "1 vote".
2830+    * If value is 2, vote{{ value|pluralize }} displays "2 votes".
2831+
2832+    If an argument is provided, that string is used instead:
2833+
2834+    * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes".
2835+    * If value is 1, class{{ value|pluralize:"es" }} displays "1 class".
2836+    * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes".
2837+
2838+    If the provided argument contains a comma, the text before the comma is
2839+    used for the singular case and the text after the comma is used for the
2840+    plural case:
2841+
2842+    * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies".
2843+    * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy".
2844+    * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies".
2845+    """
2846+    if not u',' in arg:
2847+        arg = u',' + arg
2848+    bits = arg.split(u',')
2849+    if len(bits) > 2:
2850+        return u''
2851+    singular_suffix, plural_suffix = bits[:2]
2852+
2853+    try:
2854+        if int(value) != 1:
2855+            return plural_suffix
2856+    except ValueError: # Invalid string that's not a number.
2857+        pass
2858+    except TypeError: # Value isn't a string or a number; maybe it's a list?
2859+        try:
2860+            if len(value) != 1:
2861+                return plural_suffix
2862+        except TypeError: # len() of unsized object.
2863+            pass
2864+    return singular_suffix
2865+pluralize.is_safe = False
2866+
2867+def phone2numeric(value):
2868+    """Takes a phone number and converts it in to its numerical equivalent."""
2869+    from django.utils.text import phone2numeric
2870+    return phone2numeric(value)
2871+phone2numeric.is_safe = True
2872+
2873+def pprint(value):
2874+    """A wrapper around pprint.pprint -- for debugging, really."""
2875+    from pprint import pformat
2876+    try:
2877+        return pformat(value)
2878+    except Exception, e:
2879+        return u"Error in formatting: %s" % force_unicode(e, errors="replace")
2880+pprint.is_safe = True
2881+
2882+# Syntax: register.filter(name of filter, callback)
2883+register.filter(add)
2884+register.filter(addslashes)
2885+register.filter(capfirst)
2886+register.filter(center)
2887+register.filter(cut)
2888+register.filter(date)
2889+register.filter(default)
2890+register.filter(default_if_none)
2891+register.filter(dictsort)
2892+register.filter(dictsortreversed)
2893+register.filter(divisibleby)
2894+register.filter(escape)
2895+register.filter(escapejs)
2896+register.filter(filesizeformat)
2897+register.filter(first)
2898+register.filter(fix_ampersands)
2899+register.filter(floatformat)
2900+register.filter(force_escape)
2901+register.filter(get_digit)
2902+register.filter(iriencode)
2903+register.filter(join)
2904+register.filter(last)
2905+register.filter(length)
2906+register.filter(length_is)
2907+register.filter(linebreaks)
2908+register.filter(linebreaksbr)
2909+register.filter(linenumbers)
2910+register.filter(ljust)
2911+register.filter(lower)
2912+register.filter(make_list)
2913+register.filter(phone2numeric)
2914+register.filter(pluralize)
2915+register.filter(pprint)
2916+register.filter(removetags)
2917+register.filter(random)
2918+register.filter(rjust)
2919+register.filter(safe)
2920+register.filter('slice', slice_)
2921+register.filter(slugify)
2922+register.filter(stringformat)
2923+register.filter(striptags)
2924+register.filter(time)
2925+register.filter(timesince)
2926+register.filter(timeuntil)
2927+register.filter(title)
2928+register.filter(truncatewords)
2929+register.filter(truncatewords_html)
2930+register.filter(unordered_list)
2931+register.filter(upper)
2932+register.filter(urlencode)
2933+register.filter(urlize)
2934+register.filter(urlizetrunc)
2935+register.filter(wordcount)
2936+register.filter(wordwrap)
2937+register.filter(yesno)
2938diff --git a/django/templatetags/defaulttags.py b/django/templatetags/defaulttags.py
2939new file mode 100644
2940index 0000000..535457a
2941--- /dev/null
2942+++ b/django/templatetags/defaulttags.py
2943@@ -0,0 +1,1105 @@
2944+"""Default tags used by the template system, available to all templates."""
2945+
2946+import sys
2947+import re
2948+from itertools import cycle as itertools_cycle
2949+try:
2950+    reversed
2951+except NameError:
2952+    from django.utils.itercompat import reversed     # Python 2.3 fallback
2953+
2954+from django.template import Node, NodeList, Template, Context, Variable
2955+from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
2956+from django.template import get_library, Library, InvalidTemplateLibrary
2957+from django.conf import settings
2958+from django.utils.encoding import smart_str, smart_unicode
2959+from django.utils.itercompat import groupby
2960+from django.utils.safestring import mark_safe
2961+
2962+register = Library()
2963+
2964+class AutoEscapeControlNode(Node):
2965+    """Implements the actions of the autoescape tag."""
2966+    def __init__(self, setting, nodelist):
2967+        self.setting, self.nodelist = setting, nodelist
2968+
2969+    def render(self, context):
2970+        old_setting = context.autoescape
2971+        context.autoescape = self.setting
2972+        output = self.nodelist.render(context)
2973+        context.autoescape = old_setting
2974+        if self.setting:
2975+            return mark_safe(output)
2976+        else:
2977+            return output
2978+
2979+class CommentNode(Node):
2980+    def render(self, context):
2981+        return ''
2982+
2983+class CycleNode(Node):
2984+    def __init__(self, cyclevars, variable_name=None):
2985+        self.cycle_iter = itertools_cycle([Variable(v) for v in cyclevars])
2986+        self.variable_name = variable_name
2987+
2988+    def render(self, context):
2989+        value = self.cycle_iter.next().resolve(context)
2990+        if self.variable_name:
2991+            context[self.variable_name] = value
2992+        return value
2993+
2994+class DebugNode(Node):
2995+    def render(self, context):
2996+        from pprint import pformat
2997+        output = [pformat(val) for val in context]
2998+        output.append('\n\n')
2999+        output.append(pformat(sys.modules))
3000+        return ''.join(output)
3001+
3002+class FilterNode(Node):
3003+    def __init__(self, filter_expr, nodelist):
3004+        self.filter_expr, self.nodelist = filter_expr, nodelist
3005+
3006+    def render(self, context):
3007+        output = self.nodelist.render(context)
3008+        # Apply filters.
3009+        context.update({'var': output})
3010+        filtered = self.filter_expr.resolve(context)
3011+        context.pop()
3012+        return filtered
3013+
3014+class FirstOfNode(Node):
3015+    def __init__(self, vars):
3016+        self.vars = map(Variable, vars)
3017+
3018+    def render(self, context):
3019+        for var in self.vars:
3020+            try:
3021+                value = var.resolve(context)
3022+            except VariableDoesNotExist:
3023+                continue
3024+            if value:
3025+                return smart_unicode(value)
3026+        return u''
3027+
3028+class ForNode(Node):
3029+    def __init__(self, loopvars, sequence, is_reversed, nodelist_loop):
3030+        self.loopvars, self.sequence = loopvars, sequence
3031+        self.is_reversed = is_reversed
3032+        self.nodelist_loop = nodelist_loop
3033+
3034+    def __repr__(self):
3035+        reversed_text = self.is_reversed and ' reversed' or ''
3036+        return "<For Node: for %s in %s, tail_len: %d%s>" % \
3037+            (', '.join(self.loopvars), self.sequence, len(self.nodelist_loop),
3038+             reversed_text)
3039+
3040+    def __iter__(self):
3041+        for node in self.nodelist_loop:
3042+            yield node
3043+
3044+    def get_nodes_by_type(self, nodetype):
3045+        nodes = []
3046+        if isinstance(self, nodetype):
3047+            nodes.append(self)
3048+        nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))
3049+        return nodes
3050+
3051+    def render(self, context):
3052+        nodelist = NodeList()
3053+        if 'forloop' in context:
3054+            parentloop = context['forloop']
3055+        else:
3056+            parentloop = {}
3057+        context.push()
3058+        try:
3059+            values = self.sequence.resolve(context, True)
3060+        except VariableDoesNotExist:
3061+            values = []
3062+        if values is None:
3063+            values = []
3064+        if not hasattr(values, '__len__'):
3065+            values = list(values)
3066+        len_values = len(values)
3067+        if self.is_reversed:
3068+            values = reversed(values)
3069+        unpack = len(self.loopvars) > 1
3070+        # Create a forloop value in the context.  We'll update counters on each
3071+        # iteration just below.
3072+        loop_dict = context['forloop'] = {'parentloop': parentloop}
3073+        for i, item in enumerate(values):
3074+            # Shortcuts for current loop iteration number.
3075+            loop_dict['counter0'] = i
3076+            loop_dict['counter'] = i+1
3077+            # Reverse counter iteration numbers.
3078+            loop_dict['revcounter'] = len_values - i
3079+            loop_dict['revcounter0'] = len_values - i - 1
3080+            # Boolean values designating first and last times through loop.
3081+            loop_dict['first'] = (i == 0)
3082+            loop_dict['last'] = (i == len_values - 1)
3083+
3084+            if unpack:
3085+                # If there are multiple loop variables, unpack the item into
3086+                # them.
3087+                context.update(dict(zip(self.loopvars, item)))
3088+            else:
3089+                context[self.loopvars[0]] = item
3090+            for node in self.nodelist_loop:
3091+                nodelist.append(node.render(context))
3092+            if unpack:
3093+                # The loop variables were pushed on to the context so pop them
3094+                # off again. This is necessary because the tag lets the length
3095+                # of loopvars differ to the length of each set of items and we
3096+                # don't want to leave any vars from the previous loop on the
3097+                # context.
3098+                context.pop()
3099+        context.pop()
3100+        return nodelist.render(context)
3101+
3102+class IfChangedNode(Node):
3103+    def __init__(self, nodelist, *varlist):
3104+        self.nodelist = nodelist
3105+        self._last_seen = None
3106+        self._varlist = map(Variable, varlist)
3107+        self._id = str(id(self))
3108+
3109+    def render(self, context):
3110+        if 'forloop' in context and self._id not in context['forloop']:
3111+            self._last_seen = None
3112+            context['forloop'][self._id] = 1
3113+        try:
3114+            if self._varlist:
3115+                # Consider multiple parameters.  This automatically behaves
3116+                # like an OR evaluation of the multiple variables.
3117+                compare_to = [var.resolve(context) for var in self._varlist]
3118+            else:
3119+                compare_to = self.nodelist.render(context)
3120+        except VariableDoesNotExist:
3121+            compare_to = None
3122+
3123+        if  compare_to != self._last_seen:
3124+            firstloop = (self._last_seen == None)
3125+            self._last_seen = compare_to
3126+            context.push()
3127+            context['ifchanged'] = {'firstloop': firstloop}
3128+            content = self.nodelist.render(context)
3129+            context.pop()
3130+            return content
3131+        else:
3132+            return ''
3133+
3134+class IfEqualNode(Node):
3135+    def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
3136+        self.var1, self.var2 = Variable(var1), Variable(var2)
3137+        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
3138+        self.negate = negate
3139+
3140+    def __repr__(self):
3141+        return "<IfEqualNode>"
3142+
3143+    def render(self, context):
3144+        try:
3145+            val1 = self.var1.resolve(context)
3146+        except VariableDoesNotExist:
3147+            val1 = None
3148+        try:
3149+            val2 = self.var2.resolve(context)
3150+        except VariableDoesNotExist:
3151+            val2 = None
3152+        if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
3153+            return self.nodelist_true.render(context)
3154+        return self.nodelist_false.render(context)
3155+
3156+class IfNode(Node):
3157+    def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type):
3158+        self.bool_exprs = bool_exprs
3159+        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
3160+        self.link_type = link_type
3161+
3162+    def __repr__(self):
3163+        return "<If node>"
3164+
3165+    def __iter__(self):
3166+        for node in self.nodelist_true:
3167+            yield node
3168+        for node in self.nodelist_false:
3169+            yield node
3170+
3171+    def get_nodes_by_type(self, nodetype):
3172+        nodes = []
3173+        if isinstance(self, nodetype):
3174+            nodes.append(self)
3175+        nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
3176+        nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
3177+        return nodes
3178+
3179+    def render(self, context):
3180+        if self.link_type == IfNode.LinkTypes.or_:
3181+            for ifnot, bool_expr in self.bool_exprs:
3182+                try:
3183+                    value = bool_expr.resolve(context, True)
3184+                except VariableDoesNotExist:
3185+                    value = None
3186+                if (value and not ifnot) or (ifnot and not value):
3187+                    return self.nodelist_true.render(context)
3188+            return self.nodelist_false.render(context)
3189+        else:
3190+            for ifnot, bool_expr in self.bool_exprs:
3191+                try:
3192+                    value = bool_expr.resolve(context, True)
3193+                except VariableDoesNotExist:
3194+                    value = None
3195+                if not ((value and not ifnot) or (ifnot and not value)):
3196+                    return self.nodelist_false.render(context)
3197+            return self.nodelist_true.render(context)
3198+
3199+    class LinkTypes:
3200+        and_ = 0,
3201+        or_ = 1
3202+
3203+class RegroupNode(Node):
3204+    def __init__(self, target, expression, var_name):
3205+        self.target, self.expression = target, expression
3206+        self.var_name = var_name
3207+
3208+    def render(self, context):
3209+        obj_list = self.target.resolve(context, True)
3210+        if obj_list == None:
3211+            # target variable wasn't found in context; fail silently.
3212+            context[self.var_name] = []
3213+            return ''
3214+        # List of dictionaries in the format:
3215+        # {'grouper': 'key', 'list': [list of contents]}.
3216+        context[self.var_name] = [
3217+            {'grouper': key, 'list': list(val)}
3218+            for key, val in
3219+            groupby(obj_list, lambda v, f=self.expression.resolve: f(v, True))
3220+        ]
3221+        return ''
3222+
3223+def include_is_allowed(filepath):
3224+    for root in settings.ALLOWED_INCLUDE_ROOTS:
3225+        if filepath.startswith(root):
3226+            return True
3227+    return False
3228+
3229+class SsiNode(Node):
3230+    def __init__(self, filepath, parsed):
3231+        self.filepath, self.parsed = filepath, parsed
3232+
3233+    def render(self, context):
3234+        if not include_is_allowed(self.filepath):
3235+            if settings.DEBUG:
3236+                return "[Didn't have permission to include file]"
3237+            else:
3238+                return '' # Fail silently for invalid includes.
3239+        try:
3240+            fp = open(self.filepath, 'r')
3241+            output = fp.read()
3242+            fp.close()
3243+        except IOError:
3244+            output = ''
3245+        if self.parsed:
3246+            try:
3247+                t = Template(output, name=self.filepath)
3248+                return t.render(context)
3249+            except TemplateSyntaxError, e:
3250+                if settings.DEBUG:
3251+                    return "[Included template had syntax error: %s]" % e
3252+                else:
3253+                    return '' # Fail silently for invalid included templates.
3254+        return output
3255+
3256+class LoadNode(Node):
3257+    def render(self, context):
3258+        return ''
3259+
3260+class NowNode(Node):
3261+    def __init__(self, format_string):
3262+        self.format_string = format_string
3263+
3264+    def render(self, context):
3265+        from datetime import datetime
3266+        from django.utils.dateformat import DateFormat
3267+        df = DateFormat(datetime.now())
3268+        return df.format(self.format_string)
3269+
3270+class SpacelessNode(Node):
3271+    def __init__(self, nodelist):
3272+        self.nodelist = nodelist
3273+
3274+    def render(self, context):
3275+        from django.utils.html import strip_spaces_between_tags
3276+        return strip_spaces_between_tags(self.nodelist.render(context).strip())
3277+
3278+class TemplateTagNode(Node):
3279+    mapping = {'openblock': BLOCK_TAG_START,
3280+               'closeblock': BLOCK_TAG_END,
3281+               'openvariable': VARIABLE_TAG_START,
3282+               'closevariable': VARIABLE_TAG_END,
3283+               'openbrace': SINGLE_BRACE_START,
3284+               'closebrace': SINGLE_BRACE_END,
3285+               'opencomment': COMMENT_TAG_START,
3286+               'closecomment': COMMENT_TAG_END,
3287+               }
3288+
3289+    def __init__(self, tagtype):
3290+        self.tagtype = tagtype
3291+
3292+    def render(self, context):
3293+        return self.mapping.get(self.tagtype, '')
3294+
3295+class URLNode(Node):
3296+    def __init__(self, view_name, args, kwargs):
3297+        self.view_name = view_name
3298+        self.args = args
3299+        self.kwargs = kwargs
3300+
3301+    def render(self, context):
3302+        from django.core.urlresolvers import reverse, NoReverseMatch
3303+        args = [arg.resolve(context) for arg in self.args]
3304+        kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
3305+                       for k, v in self.kwargs.items()])
3306+        try:
3307+            return reverse(self.view_name, args=args, kwargs=kwargs)
3308+        except NoReverseMatch:
3309+            try:
3310+                project_name = settings.SETTINGS_MODULE.split('.')[0]
3311+                return reverse(project_name + '.' + self.view_name,
3312+                               args=args, kwargs=kwargs)
3313+            except NoReverseMatch:
3314+                return ''
3315+
3316+class WidthRatioNode(Node):
3317+    def __init__(self, val_expr, max_expr, max_width):
3318+        self.val_expr = val_expr
3319+        self.max_expr = max_expr
3320+        self.max_width = max_width
3321+
3322+    def render(self, context):
3323+        try:
3324+            value = self.val_expr.resolve(context)
3325+            maxvalue = self.max_expr.resolve(context)
3326+        except VariableDoesNotExist:
3327+            return ''
3328+        try:
3329+            value = float(value)
3330+            maxvalue = float(maxvalue)
3331+            ratio = (value / maxvalue) * int(self.max_width)
3332+        except (ValueError, ZeroDivisionError):
3333+            return ''
3334+        return str(int(round(ratio)))
3335+
3336+class WithNode(Node):
3337+    def __init__(self, var, name, nodelist):
3338+        self.var = var
3339+        self.name = name
3340+        self.nodelist = nodelist
3341+
3342+    def __repr__(self):
3343+        return "<WithNode>"
3344+
3345+    def render(self, context):
3346+        val = self.var.resolve(context)
3347+        context.push()
3348+        context[self.name] = val
3349+        output = self.nodelist.render(context)
3350+        context.pop()
3351+        return output
3352+
3353+#@register.tag
3354+def autoescape(parser, token):
3355+    """
3356+    Force autoescape behaviour for this block.
3357+    """
3358+    args = token.contents.split()
3359+    if len(args) != 2:
3360+        raise TemplateSyntaxError("'Autoescape' tag requires exactly one argument.")
3361+    arg = args[1]
3362+    if arg not in (u'on', u'off'):
3363+        raise TemplateSyntaxError("'Autoescape' argument should be 'on' or 'off'")
3364+    nodelist = parser.parse(('endautoescape',))
3365+    parser.delete_first_token()
3366+    return AutoEscapeControlNode((arg == 'on'), nodelist)
3367+autoescape = register.tag(autoescape)
3368+
3369+#@register.tag
3370+def comment(parser, token):
3371+    """
3372+    Ignores everything between ``{% comment %}`` and ``{% endcomment %}``.
3373+    """
3374+    parser.skip_past('endcomment')
3375+    return CommentNode()
3376+comment = register.tag(comment)
3377+
3378+#@register.tag
3379+def cycle(parser, token):
3380+    """
3381+    Cycles among the given strings each time this tag is encountered.
3382+
3383+    Within a loop, cycles among the given strings each time through
3384+    the loop::
3385+
3386+        {% for o in some_list %}
3387+            <tr class="{% cycle 'row1' 'row2' %}">
3388+                ...
3389+            </tr>
3390+        {% endfor %}
3391+
3392+    Outside of a loop, give the values a unique name the first time you call
3393+    it, then use that name each sucessive time through::
3394+
3395+            <tr class="{% cycle 'row1' 'row2' 'row3' as rowcolors %}">...</tr>
3396+            <tr class="{% cycle rowcolors %}">...</tr>
3397+            <tr class="{% cycle rowcolors %}">...</tr>
3398+
3399+    You can use any number of values, separated by spaces. Commas can also
3400+    be used to separate values; if a comma is used, the cycle values are
3401+    interpreted as literal strings.
3402+    """
3403+
3404+    # Note: This returns the exact same node on each {% cycle name %} call;
3405+    # that is, the node object returned from {% cycle a b c as name %} and the
3406+    # one returned from {% cycle name %} are the exact same object. This
3407+    # shouldn't cause problems (heh), but if it does, now you know.
3408+    #
3409+    # Ugly hack warning: This stuffs the named template dict into parser so
3410+    # that names are only unique within each template (as opposed to using
3411+    # a global variable, which would make cycle names have to be unique across
3412+    # *all* templates.
3413+
3414+    args = token.split_contents()
3415+
3416+    if len(args) < 2:
3417+        raise TemplateSyntaxError("'cycle' tag requires at least two arguments")
3418+
3419+    if ',' in args[1]:
3420+        # Backwards compatibility: {% cycle a,b %} or {% cycle a,b as foo %}
3421+        # case.
3422+        args[1:2] = ['"%s"' % arg for arg in args[1].split(",")]
3423+
3424+    if len(args) == 2:
3425+        # {% cycle foo %} case.
3426+        name = args[1]
3427+        if not hasattr(parser, '_namedCycleNodes'):
3428+            raise TemplateSyntaxError("No named cycles in template. '%s' is not defined" % name)
3429+        if not name in parser._namedCycleNodes:
3430+            raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
3431+        return parser._namedCycleNodes[name]
3432+
3433+    if len(args) > 4 and args[-2] == 'as':
3434+        name = args[-1]
3435+        node = CycleNode(args[1:-2], name)
3436+        if not hasattr(parser, '_namedCycleNodes'):
3437+            parser._namedCycleNodes = {}
3438+        parser._namedCycleNodes[name] = node
3439+    else:
3440+        node = CycleNode(args[1:])
3441+    return node
3442+cycle = register.tag(cycle)
3443+
3444+def debug(parser, token):
3445+    """
3446+    Outputs a whole load of debugging information, including the current
3447+    context and imported modules.
3448+
3449+    Sample usage::
3450+
3451+        <pre>
3452+            {% debug %}
3453+        </pre>
3454+    """
3455+    return DebugNode()
3456+debug = register.tag(debug)
3457+
3458+#@register.tag(name="filter")
3459+def do_filter(parser, token):
3460+    """
3461+    Filters the contents of the block through variable filters.
3462+
3463+    Filters can also be piped through each other, and they can have
3464+    arguments -- just like in variable syntax.
3465+
3466+    Sample usage::
3467+
3468+        {% filter force_escape|lower %}
3469+            This text will be HTML-escaped, and will appear in lowercase.
3470+        {% endfilter %}
3471+    """
3472+    _, rest = token.contents.split(None, 1)
3473+    filter_expr = parser.compile_filter("var|%s" % (rest))
3474+    for func, unused in filter_expr.filters:
3475+        if getattr(func, '_decorated_function', func).__name__ in ('escape', 'safe'):
3476+            raise TemplateSyntaxError('"filter %s" is not permitted.  Use the "autoescape" tag instead.' % func.__name__)
3477+    nodelist = parser.parse(('endfilter',))
3478+    parser.delete_first_token()
3479+    return FilterNode(filter_expr, nodelist)
3480+do_filter = register.tag("filter", do_filter)
3481+
3482+#@register.tag
3483+def firstof(parser, token):
3484+    """
3485+    Outputs the first variable passed that is not False.
3486+
3487+    Outputs nothing if all the passed variables are False.
3488+
3489+    Sample usage::
3490+
3491+        {% firstof var1 var2 var3 %}
3492+
3493+    This is equivalent to::
3494+
3495+        {% if var1 %}
3496+            {{ var1 }}
3497+        {% else %}{% if var2 %}
3498+            {{ var2 }}
3499+        {% else %}{% if var3 %}
3500+            {{ var3 }}
3501+        {% endif %}{% endif %}{% endif %}
3502+
3503+    but obviously much cleaner!
3504+
3505+    You can also use a literal string as a fallback value in case all
3506+    passed variables are False::
3507+
3508+        {% firstof var1 var2 var3 "fallback value" %}
3509+
3510+    """
3511+    bits = token.split_contents()[1:]
3512+    if len(bits) < 1:
3513+        raise TemplateSyntaxError("'firstof' statement requires at least one"
3514+                                  " argument")
3515+    return FirstOfNode(bits)
3516+firstof = register.tag(firstof)
3517+
3518+#@register.tag(name="for")
3519+def do_for(parser, token):
3520+    """
3521+    Loops over each item in an array.
3522+
3523+    For example, to display a list of athletes given ``athlete_list``::
3524+
3525+        <ul>
3526+        {% for athlete in athlete_list %}
3527+            <li>{{ athlete.name }}</li>
3528+        {% endfor %}
3529+        </ul>
3530+
3531+    You can loop over a list in reverse by using
3532+    ``{% for obj in list reversed %}``.
3533+
3534+    You can also unpack multiple values from a two-dimensional array::
3535+
3536+        {% for key,value in dict.items %}
3537+            {{ key }}: {{ value }}
3538+        {% endfor %}
3539+
3540+    The for loop sets a number of variables available within the loop:
3541+
3542+        ==========================  ================================================
3543+        Variable                    Description
3544+        ==========================  ================================================
3545+        ``forloop.counter``         The current iteration of the loop (1-indexed)
3546+        ``forloop.counter0``        The current iteration of the loop (0-indexed)
3547+        ``forloop.revcounter``      The number of iterations from the end of the
3548+                                    loop (1-indexed)
3549+        ``forloop.revcounter0``     The number of iterations from the end of the
3550+                                    loop (0-indexed)
3551+        ``forloop.first``           True if this is the first time through the loop
3552+        ``forloop.last``            True if this is the last time through the loop
3553+        ``forloop.parentloop``      For nested loops, this is the loop "above" the
3554+                                    current one
3555+        ==========================  ================================================
3556+
3557+    """
3558+    bits = token.contents.split()
3559+    if len(bits) < 4:
3560+        raise TemplateSyntaxError("'for' statements should have at least four"
3561+                                  " words: %s" % token.contents)
3562+
3563+    is_reversed = bits[-1] == 'reversed'
3564+    in_index = is_reversed and -3 or -2
3565+    if bits[in_index] != 'in':
3566+        raise TemplateSyntaxError("'for' statements should use the format"
3567+                                  " 'for x in y': %s" % token.contents)
3568+
3569+    loopvars = re.sub(r' *, *', ',', ' '.join(bits[1:in_index])).split(',')
3570+    for var in loopvars:
3571+        if not var or ' ' in var:
3572+            raise TemplateSyntaxError("'for' tag received an invalid argument:"
3573+                                      " %s" % token.contents)
3574+
3575+    sequence = parser.compile_filter(bits[in_index+1])
3576+    nodelist_loop = parser.parse(('endfor',))
3577+    parser.delete_first_token()
3578+    return ForNode(loopvars, sequence, is_reversed, nodelist_loop)
3579+do_for = register.tag("for", do_for)
3580+
3581+def do_ifequal(parser, token, negate):
3582+    bits = list(token.split_contents())
3583+    if len(bits) != 3:
3584+        raise TemplateSyntaxError, "%r takes two arguments" % bits[0]
3585+    end_tag = 'end' + bits[0]
3586+    nodelist_true = parser.parse(('else', end_tag))
3587+    token = parser.next_token()
3588+    if token.contents == 'else':
3589+        nodelist_false = parser.parse((end_tag,))
3590+        parser.delete_first_token()
3591+    else:
3592+        nodelist_false = NodeList()
3593+    return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate)
3594+
3595+#@register.tag
3596+def ifequal(parser, token):
3597+    """
3598+    Outputs the contents of the block if the two arguments equal each other.
3599+
3600+    Examples::
3601+
3602+        {% ifequal user.id comment.user_id %}
3603+            ...
3604+        {% endifequal %}
3605+
3606+        {% ifnotequal user.id comment.user_id %}
3607+            ...
3608+        {% else %}
3609+            ...
3610+        {% endifnotequal %}
3611+    """
3612+    return do_ifequal(parser, token, False)
3613+ifequal = register.tag(ifequal)
3614+
3615+#@register.tag
3616+def ifnotequal(parser, token):
3617+    """
3618+    Outputs the contents of the block if the two arguments are not equal.
3619+    See ifequal.
3620+    """
3621+    return do_ifequal(parser, token, True)
3622+ifnotequal = register.tag(ifnotequal)
3623+
3624+#@register.tag(name="if")
3625+def do_if(parser, token):
3626+    """
3627+    The ``{% if %}`` tag evaluates a variable, and if that variable is "true"
3628+    (i.e., exists, is not empty, and is not a false boolean value), the
3629+    contents of the block are output:
3630+
3631+    ::
3632+
3633+        {% if athlete_list %}
3634+            Number of athletes: {{ athlete_list|count }}
3635+        {% else %}
3636+            No athletes.
3637+        {% endif %}
3638+
3639+    In the above, if ``athlete_list`` is not empty, the number of athletes will
3640+    be displayed by the ``{{ athlete_list|count }}`` variable.
3641+
3642+    As you can see, the ``if`` tag can take an option ``{% else %}`` clause
3643+    that will be displayed if the test fails.
3644+
3645+    ``if`` tags may use ``or``, ``and`` or ``not`` to test a number of
3646+    variables or to negate a given variable::
3647+
3648+        {% if not athlete_list %}
3649+            There are no athletes.
3650+        {% endif %}
3651+
3652+        {% if athlete_list or coach_list %}
3653+            There are some athletes or some coaches.
3654+        {% endif %}
3655+
3656+        {% if athlete_list and coach_list %}
3657+            Both atheletes and coaches are available.
3658+        {% endif %}
3659+
3660+        {% if not athlete_list or coach_list %}
3661+            There are no athletes, or there are some coaches.
3662+        {% endif %}
3663+
3664+        {% if athlete_list and not coach_list %}
3665+            There are some athletes and absolutely no coaches.
3666+        {% endif %}
3667+
3668+    ``if`` tags do not allow ``and`` and ``or`` clauses with the same tag,
3669+    because the order of logic would be ambigous. For example, this is
3670+    invalid::
3671+
3672+        {% if athlete_list and coach_list or cheerleader_list %}
3673+
3674+    If you need to combine ``and`` and ``or`` to do advanced logic, just use
3675+    nested if tags. For example::
3676+
3677+        {% if athlete_list %}
3678+            {% if coach_list or cheerleader_list %}
3679+                We have athletes, and either coaches or cheerleaders!
3680+            {% endif %}
3681+        {% endif %}
3682+    """
3683+    bits = token.contents.split()
3684+    del bits[0]
3685+    if not bits:
3686+        raise TemplateSyntaxError("'if' statement requires at least one argument")
3687+    # Bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d']
3688+    bitstr = ' '.join(bits)
3689+    boolpairs = bitstr.split(' and ')
3690+    boolvars = []
3691+    if len(boolpairs) == 1:
3692+        link_type = IfNode.LinkTypes.or_
3693+        boolpairs = bitstr.split(' or ')
3694+    else:
3695+        link_type = IfNode.LinkTypes.and_
3696+        if ' or ' in bitstr:
3697+            raise TemplateSyntaxError, "'if' tags can't mix 'and' and 'or'"
3698+    for boolpair in boolpairs:
3699+        if ' ' in boolpair:
3700+            try:
3701+                not_, boolvar = boolpair.split()
3702+            except ValueError:
3703+                raise TemplateSyntaxError, "'if' statement improperly formatted"
3704+            if not_ != 'not':
3705+                raise TemplateSyntaxError, "Expected 'not' in if statement"
3706+            boolvars.append((True, parser.compile_filter(boolvar)))
3707+        else:
3708+            boolvars.append((False, parser.compile_filter(boolpair)))
3709+    nodelist_true = parser.parse(('else', 'endif'))
3710+    token = parser.next_token()
3711+    if token.contents == 'else':
3712+        nodelist_false = parser.parse(('endif',))
3713+        parser.delete_first_token()
3714+    else:
3715+        nodelist_false = NodeList()
3716+    return IfNode(boolvars, nodelist_true, nodelist_false, link_type)
3717+do_if = register.tag("if", do_if)
3718+
3719+#@register.tag
3720+def ifchanged(parser, token):
3721+    """
3722+    Checks if a value has changed from the last iteration of a loop.
3723+
3724+    The 'ifchanged' block tag is used within a loop. It has two possible uses.
3725+
3726+    1. Checks its own rendered contents against its previous state and only
3727+       displays the content if it has changed. For example, this displays a
3728+       list of days, only displaying the month if it changes::
3729+
3730+            <h1>Archive for {{ year }}</h1>
3731+
3732+            {% for date in days %}
3733+                {% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}
3734+                <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
3735+            {% endfor %}
3736+
3737+    2. If given a variable, check whether that variable has changed.
3738+       For example, the following shows the date every time it changes, but
3739+       only shows the hour if both the hour and the date have changed::
3740+
3741+            {% for date in days %}
3742+                {% ifchanged date.date %} {{ date.date }} {% endifchanged %}
3743+                {% ifchanged date.hour date.date %}
3744+                    {{ date.hour }}
3745+                {% endifchanged %}
3746+            {% endfor %}
3747+    """
3748+    bits = token.contents.split()
3749+    nodelist = parser.parse(('endifchanged',))
3750+    parser.delete_first_token()
3751+    return IfChangedNode(nodelist, *bits[1:])
3752+ifchanged = register.tag(ifchanged)
3753+
3754+#@register.tag
3755+def ssi(parser, token):
3756+    """
3757+    Outputs the contents of a given file into the page.
3758+
3759+    Like a simple "include" tag, the ``ssi`` tag includes the contents
3760+    of another file -- which must be specified using an absolute path --
3761+    in the current page::
3762+
3763+        {% ssi /home/html/ljworld.com/includes/right_generic.html %}
3764+
3765+    If the optional "parsed" parameter is given, the contents of the included
3766+    file are evaluated as template code, with the current context::
3767+
3768+        {% ssi /home/html/ljworld.com/includes/right_generic.html parsed %}
3769+    """
3770+    bits = token.contents.split()
3771+    parsed = False
3772+    if len(bits) not in (2, 3):
3773+        raise TemplateSyntaxError("'ssi' tag takes one argument: the path to"
3774+                                  " the file to be included")
3775+    if len(bits) == 3:
3776+        if bits[2] == 'parsed':
3777+            parsed = True
3778+        else:
3779+            raise TemplateSyntaxError("Second (optional) argument to %s tag"
3780+                                      " must be 'parsed'" % bits[0])
3781+    return SsiNode(bits[1], parsed)
3782+ssi = register.tag(ssi)
3783+
3784+#@register.tag
3785+def load(parser, token):
3786+    """
3787+    Loads a custom template tag set.
3788+
3789+    For example, to load the template tags in
3790+    ``django/templatetags/news/photos.py``::
3791+
3792+        {% load news.photos %}
3793+    """
3794+    bits = token.contents.split()
3795+    for taglib in bits[1:]:
3796+        # add the library to the parser
3797+        try:
3798+            lib = get_library(taglib)
3799+            parser.add_library(lib)
3800+        except InvalidTemplateLibrary, e:
3801+            raise TemplateSyntaxError("'%s' is not a valid tag library: %s" %
3802+                                      (taglib, e))
3803+    return LoadNode()
3804+load = register.tag(load)
3805+
3806+#@register.tag
3807+def now(parser, token):
3808+    """
3809+    Displays the date, formatted according to the given string.
3810+
3811+    Uses the same format as PHP's ``date()`` function; see http://php.net/date
3812+    for all the possible values.
3813+
3814+    Sample usage::
3815+
3816+        It is {% now "jS F Y H:i" %}
3817+    """
3818+    bits = token.contents.split('"')
3819+    if len(bits) != 3:
3820+        raise TemplateSyntaxError, "'now' statement takes one argument"
3821+    format_string = bits[1]
3822+    return NowNode(format_string)
3823+now = register.tag(now)
3824+
3825+#@register.tag
3826+def regroup(parser, token):
3827+    """
3828+    Regroups a list of alike objects by a common attribute.
3829+
3830+    This complex tag is best illustrated by use of an example:  say that
3831+    ``people`` is a list of ``Person`` objects that have ``first_name``,
3832+    ``last_name``, and ``gender`` attributes, and you'd like to display a list
3833+    that looks like:
3834+
3835+        * Male:
3836+            * George Bush
3837+            * Bill Clinton
3838+        * Female:
3839+            * Margaret Thatcher
3840+            * Colendeeza Rice
3841+        * Unknown:
3842+            * Pat Smith
3843+
3844+    The following snippet of template code would accomplish this dubious task::
3845+
3846+        {% regroup people by gender as grouped %}
3847+        <ul>
3848+        {% for group in grouped %}
3849+            <li>{{ group.grouper }}
3850+            <ul>
3851+                {% for item in group.list %}
3852+                <li>{{ item }}</li>
3853+                {% endfor %}
3854+            </ul>
3855+        {% endfor %}
3856+        </ul>
3857+
3858+    As you can see, ``{% regroup %}`` populates a variable with a list of
3859+    objects with ``grouper`` and ``list`` attributes.  ``grouper`` contains the
3860+    item that was grouped by; ``list`` contains the list of objects that share
3861+    that ``grouper``.  In this case, ``grouper`` would be ``Male``, ``Female``
3862+    and ``Unknown``, and ``list`` is the list of people with those genders.
3863+
3864+    Note that `{% regroup %}`` does not work when the list to be grouped is not
3865+    sorted by the key you are grouping by!  This means that if your list of
3866+    people was not sorted by gender, you'd need to make sure it is sorted
3867+    before using it, i.e.::
3868+
3869+        {% regroup people|dictsort:"gender" by gender as grouped %}
3870+
3871+    """
3872+    firstbits = token.contents.split(None, 3)
3873+    if len(firstbits) != 4:
3874+        raise TemplateSyntaxError, "'regroup' tag takes five arguments"
3875+    target = parser.compile_filter(firstbits[1])
3876+    if firstbits[2] != 'by':
3877+        raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'")
3878+    lastbits_reversed = firstbits[3][::-1].split(None, 2)
3879+    if lastbits_reversed[1][::-1] != 'as':
3880+        raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
3881+                                  " be 'as'")
3882+
3883+    expression = parser.compile_filter(lastbits_reversed[2][::-1])
3884+
3885+    var_name = lastbits_reversed[0][::-1]
3886+    return RegroupNode(target, expression, var_name)
3887+regroup = register.tag(regroup)
3888+
3889+def spaceless(parser, token):
3890+    """
3891+    Removes whitespace between HTML tags, including tab and newline characters.
3892+
3893+    Example usage::
3894+
3895+        {% spaceless %}
3896+            <p>
3897+                <a href="foo/">Foo</a>
3898+            </p>
3899+        {% endspaceless %}
3900+
3901+    This example would return this HTML::
3902+
3903+        <p><a href="foo/">Foo</a></p>
3904+
3905+    Only space between *tags* is normalized -- not space between tags and text.
3906+    In this example, the space around ``Hello`` won't be stripped::
3907+
3908+        {% spaceless %}
3909+            <strong>
3910+                Hello
3911+            </strong>
3912+        {% endspaceless %}
3913+    """
3914+    nodelist = parser.parse(('endspaceless',))
3915+    parser.delete_first_token()
3916+    return SpacelessNode(nodelist)
3917+spaceless = register.tag(spaceless)
3918+
3919+#@register.tag
3920+def templatetag(parser, token):
3921+    """
3922+    Outputs one of the bits used to compose template tags.
3923+
3924+    Since the template system has no concept of "escaping", to display one of
3925+    the bits used in template tags, you must use the ``{% templatetag %}`` tag.
3926+
3927+    The argument tells which template bit to output:
3928+
3929+        ==================  =======
3930+        Argument            Outputs
3931+        ==================  =======
3932+        ``openblock``       ``{%``
3933+        ``closeblock``      ``%}``
3934+        ``openvariable``    ``{{``
3935+        ``closevariable``   ``}}``
3936+        ``openbrace``       ``{``
3937+        ``closebrace``      ``}``
3938+        ``opencomment``     ``{#``
3939+        ``closecomment``    ``#}``
3940+        ==================  =======
3941+    """
3942+    bits = token.contents.split()
3943+    if len(bits) != 2:
3944+        raise TemplateSyntaxError, "'templatetag' statement takes one argument"
3945+    tag = bits[1]
3946+    if tag not in TemplateTagNode.mapping:
3947+        raise TemplateSyntaxError("Invalid templatetag argument: '%s'."
3948+                                  " Must be one of: %s" %
3949+                                  (tag, TemplateTagNode.mapping.keys()))
3950+    return TemplateTagNode(tag)
3951+templatetag = register.tag(templatetag)
3952+
3953+def url(parser, token):
3954+    """
3955+    Returns an absolute URL matching given view with its parameters.
3956+
3957+    This is a way to define links that aren't tied to a particular URL
3958+    configuration::
3959+
3960+        {% url path.to.some_view arg1,arg2,name1=value1 %}
3961+
3962+    The first argument is a path to a view. It can be an absolute python path
3963+    or just ``app_name.view_name`` without the project name if the view is
3964+    located inside the project.  Other arguments are comma-separated values
3965+    that will be filled in place of positional and keyword arguments in the
3966+    URL. All arguments for the URL should be present.
3967+
3968+    For example if you have a view ``app_name.client`` taking client's id and
3969+    the corresponding line in a URLconf looks like this::
3970+
3971+        ('^client/(\d+)/$', 'app_name.client')
3972+
3973+    and this app's URLconf is included into the project's URLconf under some
3974+    path::
3975+
3976+        ('^clients/', include('project_name.app_name.urls'))
3977+
3978+    then in a template you can create a link for a certain client like this::
3979+
3980+        {% url app_name.client client.id %}
3981+
3982+    The URL will look like ``/clients/client/123/``.
3983+    """
3984+    bits = token.contents.split(' ', 2)
3985+    if len(bits) < 2:
3986+        raise TemplateSyntaxError("'%s' takes at least one argument"
3987+                                  " (path to a view)" % bits[0])
3988+    args = []
3989+    kwargs = {}
3990+    if len(bits) > 2:
3991+        for arg in bits[2].split(','):
3992+            if '=' in arg:
3993+                k, v = arg.split('=', 1)
3994+                k = k.strip()
3995+                kwargs[k] = parser.compile_filter(v)
3996+            else:
3997+                args.append(parser.compile_filter(arg))
3998+    return URLNode(bits[1], args, kwargs)
3999+url = register.tag(url)
4000+
4001+#@register.tag
4002+def widthratio(parser, token):
4003+    """
4004+    For creating bar charts and such, this tag calculates the ratio of a given
4005+    value to a maximum value, and then applies that ratio to a constant.
4006+
4007+    For example::
4008+
4009+        <img src='bar.gif' height='10' width='{% widthratio this_value max_value 100 %}' />
4010+
4011+    Above, if ``this_value`` is 175 and ``max_value`` is 200, the the image in
4012+    the above example will be 88 pixels wide (because 175/200 = .875;
4013+    .875 * 100 = 87.5 which is rounded up to 88).
4014+    """
4015+    bits = token.contents.split()
4016+    if len(bits) != 4:
4017+        raise TemplateSyntaxError("widthratio takes three arguments")
4018+    tag, this_value_expr, max_value_expr, max_width = bits
4019+    try:
4020+        max_width = int(max_width)
4021+    except ValueError:
4022+        raise TemplateSyntaxError("widthratio final argument must be an integer")
4023+    return WidthRatioNode(parser.compile_filter(this_value_expr),
4024+                          parser.compile_filter(max_value_expr), max_width)
4025+widthratio = register.tag(widthratio)
4026+
4027+#@register.tag
4028+def do_with(parser, token):
4029+    """
4030+    Adds a value to the context (inside of this block) for caching and easy
4031+    access.
4032+
4033+    For example::
4034+
4035+        {% with person.some_sql_method as total %}
4036+            {{ total }} object{{ total|pluralize }}
4037+        {% endwith %}
4038+    """
4039+    bits = list(token.split_contents())
4040+    if len(bits) != 4 or bits[2] != "as":
4041+        raise TemplateSyntaxError("%r expected format is 'value as name'" %
4042+                                  bits[0])
4043+    var = parser.compile_filter(bits[1])
4044+    name = bits[3]
4045+    nodelist = parser.parse(('endwith',))
4046+    parser.delete_first_token()
4047+    return WithNode(var, name, nodelist)
4048+do_with = register.tag('with', do_with)
4049diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
4050index 186b8aa..ac0912d 100644
4051--- a/tests/regressiontests/templates/tests.py
4052+++ b/tests/regressiontests/templates/tests.py
4053@@ -51,7 +51,7 @@ def do_echo(parser, token):
4054 
4055 register.tag("echo", do_echo)
4056 
4057-template.libraries['django.templatetags.testtags'] = register
4058+template.libraries['testtags'] = register
4059 
4060 #####################################
4061 # Helper objects for template tests #