eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/templatefilters.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # template-filters.py - common template expansion filters
       
     2 #
       
     3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 import cgi, re, os, time, urllib
       
     9 import encoding, node, util
       
    10 
       
    11 def stringify(thing):
       
    12     '''turn nested template iterator into string.'''
       
    13     if hasattr(thing, '__iter__') and not isinstance(thing, str):
       
    14         return "".join([stringify(t) for t in thing if t is not None])
       
    15     return str(thing)
       
    16 
       
    17 agescales = [("year", 3600 * 24 * 365),
       
    18              ("month", 3600 * 24 * 30),
       
    19              ("week", 3600 * 24 * 7),
       
    20              ("day", 3600 * 24),
       
    21              ("hour", 3600),
       
    22              ("minute", 60),
       
    23              ("second", 1)]
       
    24 
       
    25 def age(date):
       
    26     '''turn a (timestamp, tzoff) tuple into an age string.'''
       
    27 
       
    28     def plural(t, c):
       
    29         if c == 1:
       
    30             return t
       
    31         return t + "s"
       
    32     def fmt(t, c):
       
    33         return "%d %s" % (c, plural(t, c))
       
    34 
       
    35     now = time.time()
       
    36     then = date[0]
       
    37     if then > now:
       
    38         return 'in the future'
       
    39 
       
    40     delta = max(1, int(now - then))
       
    41     if delta > agescales[0][1] * 2:
       
    42         return util.shortdate(date)
       
    43 
       
    44     for t, s in agescales:
       
    45         n = delta // s
       
    46         if n >= 2 or s == 1:
       
    47             return '%s ago' % fmt(t, n)
       
    48 
       
    49 para_re = None
       
    50 space_re = None
       
    51 
       
    52 def fill(text, width):
       
    53     '''fill many paragraphs.'''
       
    54     global para_re, space_re
       
    55     if para_re is None:
       
    56         para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
       
    57         space_re = re.compile(r'  +')
       
    58 
       
    59     def findparas():
       
    60         start = 0
       
    61         while True:
       
    62             m = para_re.search(text, start)
       
    63             if not m:
       
    64                 uctext = unicode(text[start:], encoding.encoding)
       
    65                 w = len(uctext)
       
    66                 while 0 < w and uctext[w - 1].isspace():
       
    67                     w -= 1
       
    68                 yield (uctext[:w].encode(encoding.encoding),
       
    69                        uctext[w:].encode(encoding.encoding))
       
    70                 break
       
    71             yield text[start:m.start(0)], m.group(1)
       
    72             start = m.end(1)
       
    73 
       
    74     return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
       
    75                     for para, rest in findparas()])
       
    76 
       
    77 def firstline(text):
       
    78     '''return the first line of text'''
       
    79     try:
       
    80         return text.splitlines(True)[0].rstrip('\r\n')
       
    81     except IndexError:
       
    82         return ''
       
    83 
       
    84 def nl2br(text):
       
    85     '''replace raw newlines with xhtml line breaks.'''
       
    86     return text.replace('\n', '<br/>\n')
       
    87 
       
    88 def obfuscate(text):
       
    89     text = unicode(text, encoding.encoding, 'replace')
       
    90     return ''.join(['&#%d;' % ord(c) for c in text])
       
    91 
       
    92 def domain(author):
       
    93     '''get domain of author, or empty string if none.'''
       
    94     f = author.find('@')
       
    95     if f == -1:
       
    96         return ''
       
    97     author = author[f + 1:]
       
    98     f = author.find('>')
       
    99     if f >= 0:
       
   100         author = author[:f]
       
   101     return author
       
   102 
       
   103 def person(author):
       
   104     '''get name of author, or else username.'''
       
   105     if not '@' in author:
       
   106         return author
       
   107     f = author.find('<')
       
   108     if f == -1:
       
   109         return util.shortuser(author)
       
   110     return author[:f].rstrip()
       
   111 
       
   112 def indent(text, prefix):
       
   113     '''indent each non-empty line of text after first with prefix.'''
       
   114     lines = text.splitlines()
       
   115     num_lines = len(lines)
       
   116     endswithnewline = text[-1:] == '\n'
       
   117     def indenter():
       
   118         for i in xrange(num_lines):
       
   119             l = lines[i]
       
   120             if i and l.strip():
       
   121                 yield prefix
       
   122             yield l
       
   123             if i < num_lines - 1 or endswithnewline:
       
   124                 yield '\n'
       
   125     return "".join(indenter())
       
   126 
       
   127 def permissions(flags):
       
   128     if "l" in flags:
       
   129         return "lrwxrwxrwx"
       
   130     if "x" in flags:
       
   131         return "-rwxr-xr-x"
       
   132     return "-rw-r--r--"
       
   133 
       
   134 def xmlescape(text):
       
   135     text = (text
       
   136             .replace('&', '&amp;')
       
   137             .replace('<', '&lt;')
       
   138             .replace('>', '&gt;')
       
   139             .replace('"', '&quot;')
       
   140             .replace("'", '&#39;')) # &apos; invalid in HTML
       
   141     return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
       
   142 
       
   143 def uescape(c):
       
   144     if ord(c) < 0x80:
       
   145         return c
       
   146     else:
       
   147         return '\\u%04x' % ord(c)
       
   148 
       
   149 _escapes = [
       
   150     ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
       
   151     ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
       
   152 ]
       
   153 
       
   154 def jsonescape(s):
       
   155     for k, v in _escapes:
       
   156         s = s.replace(k, v)
       
   157     return ''.join(uescape(c) for c in s)
       
   158 
       
   159 def json(obj):
       
   160     if obj is None or obj is False or obj is True:
       
   161         return {None: 'null', False: 'false', True: 'true'}[obj]
       
   162     elif isinstance(obj, int) or isinstance(obj, float):
       
   163         return str(obj)
       
   164     elif isinstance(obj, str):
       
   165         u = unicode(obj, encoding.encoding, 'replace')
       
   166         return '"%s"' % jsonescape(u)
       
   167     elif isinstance(obj, unicode):
       
   168         return '"%s"' % jsonescape(obj)
       
   169     elif hasattr(obj, 'keys'):
       
   170         out = []
       
   171         for k, v in obj.iteritems():
       
   172             s = '%s: %s' % (json(k), json(v))
       
   173             out.append(s)
       
   174         return '{' + ', '.join(out) + '}'
       
   175     elif hasattr(obj, '__iter__'):
       
   176         out = []
       
   177         for i in obj:
       
   178             out.append(json(i))
       
   179         return '[' + ', '.join(out) + ']'
       
   180     else:
       
   181         raise TypeError('cannot encode type %s' % obj.__class__.__name__)
       
   182 
       
   183 def stripdir(text):
       
   184     '''Treat the text as path and strip a directory level, if possible.'''
       
   185     dir = os.path.dirname(text)
       
   186     if dir == "":
       
   187         return os.path.basename(text)
       
   188     else:
       
   189         return dir
       
   190 
       
   191 def nonempty(str):
       
   192     return str or "(none)"
       
   193 
       
   194 filters = {
       
   195     "addbreaks": nl2br,
       
   196     "basename": os.path.basename,
       
   197     "stripdir": stripdir,
       
   198     "age": age,
       
   199     "date": lambda x: util.datestr(x),
       
   200     "domain": domain,
       
   201     "email": util.email,
       
   202     "escape": lambda x: cgi.escape(x, True),
       
   203     "fill68": lambda x: fill(x, width=68),
       
   204     "fill76": lambda x: fill(x, width=76),
       
   205     "firstline": firstline,
       
   206     "tabindent": lambda x: indent(x, '\t'),
       
   207     "hgdate": lambda x: "%d %d" % x,
       
   208     "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
       
   209     "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
       
   210     "json": json,
       
   211     "jsonescape": jsonescape,
       
   212     "localdate": lambda x: (x[0], util.makedate()[1]),
       
   213     "nonempty": nonempty,
       
   214     "obfuscate": obfuscate,
       
   215     "permissions": permissions,
       
   216     "person": person,
       
   217     "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"),
       
   218     "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"),
       
   219     "hex": node.hex,
       
   220     "short": lambda x: x[:12],
       
   221     "shortdate": util.shortdate,
       
   222     "stringify": stringify,
       
   223     "strip": lambda x: x.strip(),
       
   224     "urlescape": lambda x: urllib.quote(x),
       
   225     "user": lambda x: util.shortuser(x),
       
   226     "stringescape": lambda x: x.encode('string_escape'),
       
   227     "xmlescape": xmlescape,
       
   228 }