##// END OF EJS Templates
json: avoid extra string manipulation of dict keys...
Yuya Nishihara -
r32742:08d0892c default
parent child Browse files
Show More
@@ -1,436 +1,436 b''
1 # templatefilters.py - common template expansion filters
1 # templatefilters.py - common template expansion filters
2 #
2 #
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
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.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import cgi
10 import cgi
11 import os
11 import os
12 import re
12 import re
13 import time
13 import time
14
14
15 from . import (
15 from . import (
16 encoding,
16 encoding,
17 hbisect,
17 hbisect,
18 node,
18 node,
19 pycompat,
19 pycompat,
20 registrar,
20 registrar,
21 templatekw,
21 templatekw,
22 util,
22 util,
23 )
23 )
24
24
25 urlerr = util.urlerr
25 urlerr = util.urlerr
26 urlreq = util.urlreq
26 urlreq = util.urlreq
27
27
28 if pycompat.ispy3:
28 if pycompat.ispy3:
29 long = int
29 long = int
30
30
31 # filters are callables like:
31 # filters are callables like:
32 # fn(obj)
32 # fn(obj)
33 # with:
33 # with:
34 # obj - object to be filtered (text, date, list and so on)
34 # obj - object to be filtered (text, date, list and so on)
35 filters = {}
35 filters = {}
36
36
37 templatefilter = registrar.templatefilter(filters)
37 templatefilter = registrar.templatefilter(filters)
38
38
39 @templatefilter('addbreaks')
39 @templatefilter('addbreaks')
40 def addbreaks(text):
40 def addbreaks(text):
41 """Any text. Add an XHTML "<br />" tag before the end of
41 """Any text. Add an XHTML "<br />" tag before the end of
42 every line except the last.
42 every line except the last.
43 """
43 """
44 return text.replace('\n', '<br/>\n')
44 return text.replace('\n', '<br/>\n')
45
45
46 agescales = [("year", 3600 * 24 * 365, 'Y'),
46 agescales = [("year", 3600 * 24 * 365, 'Y'),
47 ("month", 3600 * 24 * 30, 'M'),
47 ("month", 3600 * 24 * 30, 'M'),
48 ("week", 3600 * 24 * 7, 'W'),
48 ("week", 3600 * 24 * 7, 'W'),
49 ("day", 3600 * 24, 'd'),
49 ("day", 3600 * 24, 'd'),
50 ("hour", 3600, 'h'),
50 ("hour", 3600, 'h'),
51 ("minute", 60, 'm'),
51 ("minute", 60, 'm'),
52 ("second", 1, 's')]
52 ("second", 1, 's')]
53
53
54 @templatefilter('age')
54 @templatefilter('age')
55 def age(date, abbrev=False):
55 def age(date, abbrev=False):
56 """Date. Returns a human-readable date/time difference between the
56 """Date. Returns a human-readable date/time difference between the
57 given date/time and the current date/time.
57 given date/time and the current date/time.
58 """
58 """
59
59
60 def plural(t, c):
60 def plural(t, c):
61 if c == 1:
61 if c == 1:
62 return t
62 return t
63 return t + "s"
63 return t + "s"
64 def fmt(t, c, a):
64 def fmt(t, c, a):
65 if abbrev:
65 if abbrev:
66 return "%d%s" % (c, a)
66 return "%d%s" % (c, a)
67 return "%d %s" % (c, plural(t, c))
67 return "%d %s" % (c, plural(t, c))
68
68
69 now = time.time()
69 now = time.time()
70 then = date[0]
70 then = date[0]
71 future = False
71 future = False
72 if then > now:
72 if then > now:
73 future = True
73 future = True
74 delta = max(1, int(then - now))
74 delta = max(1, int(then - now))
75 if delta > agescales[0][1] * 30:
75 if delta > agescales[0][1] * 30:
76 return 'in the distant future'
76 return 'in the distant future'
77 else:
77 else:
78 delta = max(1, int(now - then))
78 delta = max(1, int(now - then))
79 if delta > agescales[0][1] * 2:
79 if delta > agescales[0][1] * 2:
80 return util.shortdate(date)
80 return util.shortdate(date)
81
81
82 for t, s, a in agescales:
82 for t, s, a in agescales:
83 n = delta // s
83 n = delta // s
84 if n >= 2 or s == 1:
84 if n >= 2 or s == 1:
85 if future:
85 if future:
86 return '%s from now' % fmt(t, n, a)
86 return '%s from now' % fmt(t, n, a)
87 return '%s ago' % fmt(t, n, a)
87 return '%s ago' % fmt(t, n, a)
88
88
89 @templatefilter('basename')
89 @templatefilter('basename')
90 def basename(path):
90 def basename(path):
91 """Any text. Treats the text as a path, and returns the last
91 """Any text. Treats the text as a path, and returns the last
92 component of the path after splitting by the path separator
92 component of the path after splitting by the path separator
93 (ignoring trailing separators). For example, "foo/bar/baz" becomes
93 (ignoring trailing separators). For example, "foo/bar/baz" becomes
94 "baz" and "foo/bar//" becomes "bar".
94 "baz" and "foo/bar//" becomes "bar".
95 """
95 """
96 return os.path.basename(path)
96 return os.path.basename(path)
97
97
98 @templatefilter('count')
98 @templatefilter('count')
99 def count(i):
99 def count(i):
100 """List or text. Returns the length as an integer."""
100 """List or text. Returns the length as an integer."""
101 return len(i)
101 return len(i)
102
102
103 @templatefilter('domain')
103 @templatefilter('domain')
104 def domain(author):
104 def domain(author):
105 """Any text. Finds the first string that looks like an email
105 """Any text. Finds the first string that looks like an email
106 address, and extracts just the domain component. Example: ``User
106 address, and extracts just the domain component. Example: ``User
107 <user@example.com>`` becomes ``example.com``.
107 <user@example.com>`` becomes ``example.com``.
108 """
108 """
109 f = author.find('@')
109 f = author.find('@')
110 if f == -1:
110 if f == -1:
111 return ''
111 return ''
112 author = author[f + 1:]
112 author = author[f + 1:]
113 f = author.find('>')
113 f = author.find('>')
114 if f >= 0:
114 if f >= 0:
115 author = author[:f]
115 author = author[:f]
116 return author
116 return author
117
117
118 @templatefilter('email')
118 @templatefilter('email')
119 def email(text):
119 def email(text):
120 """Any text. Extracts the first string that looks like an email
120 """Any text. Extracts the first string that looks like an email
121 address. Example: ``User <user@example.com>`` becomes
121 address. Example: ``User <user@example.com>`` becomes
122 ``user@example.com``.
122 ``user@example.com``.
123 """
123 """
124 return util.email(text)
124 return util.email(text)
125
125
126 @templatefilter('escape')
126 @templatefilter('escape')
127 def escape(text):
127 def escape(text):
128 """Any text. Replaces the special XML/XHTML characters "&", "<"
128 """Any text. Replaces the special XML/XHTML characters "&", "<"
129 and ">" with XML entities, and filters out NUL characters.
129 and ">" with XML entities, and filters out NUL characters.
130 """
130 """
131 return cgi.escape(text.replace('\0', ''), True)
131 return cgi.escape(text.replace('\0', ''), True)
132
132
133 para_re = None
133 para_re = None
134 space_re = None
134 space_re = None
135
135
136 def fill(text, width, initindent='', hangindent=''):
136 def fill(text, width, initindent='', hangindent=''):
137 '''fill many paragraphs with optional indentation.'''
137 '''fill many paragraphs with optional indentation.'''
138 global para_re, space_re
138 global para_re, space_re
139 if para_re is None:
139 if para_re is None:
140 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
140 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
141 space_re = re.compile(r' +')
141 space_re = re.compile(r' +')
142
142
143 def findparas():
143 def findparas():
144 start = 0
144 start = 0
145 while True:
145 while True:
146 m = para_re.search(text, start)
146 m = para_re.search(text, start)
147 if not m:
147 if not m:
148 uctext = unicode(text[start:], encoding.encoding)
148 uctext = unicode(text[start:], encoding.encoding)
149 w = len(uctext)
149 w = len(uctext)
150 while 0 < w and uctext[w - 1].isspace():
150 while 0 < w and uctext[w - 1].isspace():
151 w -= 1
151 w -= 1
152 yield (uctext[:w].encode(encoding.encoding),
152 yield (uctext[:w].encode(encoding.encoding),
153 uctext[w:].encode(encoding.encoding))
153 uctext[w:].encode(encoding.encoding))
154 break
154 break
155 yield text[start:m.start(0)], m.group(1)
155 yield text[start:m.start(0)], m.group(1)
156 start = m.end(1)
156 start = m.end(1)
157
157
158 return "".join([util.wrap(space_re.sub(' ', util.wrap(para, width)),
158 return "".join([util.wrap(space_re.sub(' ', util.wrap(para, width)),
159 width, initindent, hangindent) + rest
159 width, initindent, hangindent) + rest
160 for para, rest in findparas()])
160 for para, rest in findparas()])
161
161
162 @templatefilter('fill68')
162 @templatefilter('fill68')
163 def fill68(text):
163 def fill68(text):
164 """Any text. Wraps the text to fit in 68 columns."""
164 """Any text. Wraps the text to fit in 68 columns."""
165 return fill(text, 68)
165 return fill(text, 68)
166
166
167 @templatefilter('fill76')
167 @templatefilter('fill76')
168 def fill76(text):
168 def fill76(text):
169 """Any text. Wraps the text to fit in 76 columns."""
169 """Any text. Wraps the text to fit in 76 columns."""
170 return fill(text, 76)
170 return fill(text, 76)
171
171
172 @templatefilter('firstline')
172 @templatefilter('firstline')
173 def firstline(text):
173 def firstline(text):
174 """Any text. Returns the first line of text."""
174 """Any text. Returns the first line of text."""
175 try:
175 try:
176 return text.splitlines(True)[0].rstrip('\r\n')
176 return text.splitlines(True)[0].rstrip('\r\n')
177 except IndexError:
177 except IndexError:
178 return ''
178 return ''
179
179
180 @templatefilter('hex')
180 @templatefilter('hex')
181 def hexfilter(text):
181 def hexfilter(text):
182 """Any text. Convert a binary Mercurial node identifier into
182 """Any text. Convert a binary Mercurial node identifier into
183 its long hexadecimal representation.
183 its long hexadecimal representation.
184 """
184 """
185 return node.hex(text)
185 return node.hex(text)
186
186
187 @templatefilter('hgdate')
187 @templatefilter('hgdate')
188 def hgdate(text):
188 def hgdate(text):
189 """Date. Returns the date as a pair of numbers: "1157407993
189 """Date. Returns the date as a pair of numbers: "1157407993
190 25200" (Unix timestamp, timezone offset).
190 25200" (Unix timestamp, timezone offset).
191 """
191 """
192 return "%d %d" % text
192 return "%d %d" % text
193
193
194 @templatefilter('isodate')
194 @templatefilter('isodate')
195 def isodate(text):
195 def isodate(text):
196 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
196 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
197 +0200".
197 +0200".
198 """
198 """
199 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
199 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
200
200
201 @templatefilter('isodatesec')
201 @templatefilter('isodatesec')
202 def isodatesec(text):
202 def isodatesec(text):
203 """Date. Returns the date in ISO 8601 format, including
203 """Date. Returns the date in ISO 8601 format, including
204 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
204 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
205 filter.
205 filter.
206 """
206 """
207 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
207 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
208
208
209 def indent(text, prefix):
209 def indent(text, prefix):
210 '''indent each non-empty line of text after first with prefix.'''
210 '''indent each non-empty line of text after first with prefix.'''
211 lines = text.splitlines()
211 lines = text.splitlines()
212 num_lines = len(lines)
212 num_lines = len(lines)
213 endswithnewline = text[-1:] == '\n'
213 endswithnewline = text[-1:] == '\n'
214 def indenter():
214 def indenter():
215 for i in xrange(num_lines):
215 for i in xrange(num_lines):
216 l = lines[i]
216 l = lines[i]
217 if i and l.strip():
217 if i and l.strip():
218 yield prefix
218 yield prefix
219 yield l
219 yield l
220 if i < num_lines - 1 or endswithnewline:
220 if i < num_lines - 1 or endswithnewline:
221 yield '\n'
221 yield '\n'
222 return "".join(indenter())
222 return "".join(indenter())
223
223
224 @templatefilter('json')
224 @templatefilter('json')
225 def json(obj, paranoid=True):
225 def json(obj, paranoid=True):
226 if obj is None:
226 if obj is None:
227 return 'null'
227 return 'null'
228 elif obj is False:
228 elif obj is False:
229 return 'false'
229 return 'false'
230 elif obj is True:
230 elif obj is True:
231 return 'true'
231 return 'true'
232 elif isinstance(obj, (int, long, float)):
232 elif isinstance(obj, (int, long, float)):
233 return pycompat.bytestr(obj)
233 return pycompat.bytestr(obj)
234 elif isinstance(obj, bytes):
234 elif isinstance(obj, bytes):
235 return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
235 return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
236 elif util.safehasattr(obj, 'keys'):
236 elif util.safehasattr(obj, 'keys'):
237 out = ['%s: %s' % (json(k), json(v))
237 out = ['"%s": %s' % (encoding.jsonescape(k, paranoid=paranoid), json(v))
238 for k, v in sorted(obj.iteritems())]
238 for k, v in sorted(obj.iteritems())]
239 return '{' + ', '.join(out) + '}'
239 return '{' + ', '.join(out) + '}'
240 elif util.safehasattr(obj, '__iter__'):
240 elif util.safehasattr(obj, '__iter__'):
241 out = [json(i) for i in obj]
241 out = [json(i) for i in obj]
242 return '[' + ', '.join(out) + ']'
242 return '[' + ', '.join(out) + ']'
243 else:
243 else:
244 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
244 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
245
245
246 @templatefilter('lower')
246 @templatefilter('lower')
247 def lower(text):
247 def lower(text):
248 """Any text. Converts the text to lowercase."""
248 """Any text. Converts the text to lowercase."""
249 return encoding.lower(text)
249 return encoding.lower(text)
250
250
251 @templatefilter('nonempty')
251 @templatefilter('nonempty')
252 def nonempty(str):
252 def nonempty(str):
253 """Any text. Returns '(none)' if the string is empty."""
253 """Any text. Returns '(none)' if the string is empty."""
254 return str or "(none)"
254 return str or "(none)"
255
255
256 @templatefilter('obfuscate')
256 @templatefilter('obfuscate')
257 def obfuscate(text):
257 def obfuscate(text):
258 """Any text. Returns the input text rendered as a sequence of
258 """Any text. Returns the input text rendered as a sequence of
259 XML entities.
259 XML entities.
260 """
260 """
261 text = unicode(text, encoding.encoding, 'replace')
261 text = unicode(text, encoding.encoding, 'replace')
262 return ''.join(['&#%d;' % ord(c) for c in text])
262 return ''.join(['&#%d;' % ord(c) for c in text])
263
263
264 @templatefilter('permissions')
264 @templatefilter('permissions')
265 def permissions(flags):
265 def permissions(flags):
266 if "l" in flags:
266 if "l" in flags:
267 return "lrwxrwxrwx"
267 return "lrwxrwxrwx"
268 if "x" in flags:
268 if "x" in flags:
269 return "-rwxr-xr-x"
269 return "-rwxr-xr-x"
270 return "-rw-r--r--"
270 return "-rw-r--r--"
271
271
272 @templatefilter('person')
272 @templatefilter('person')
273 def person(author):
273 def person(author):
274 """Any text. Returns the name before an email address,
274 """Any text. Returns the name before an email address,
275 interpreting it as per RFC 5322.
275 interpreting it as per RFC 5322.
276
276
277 >>> person('foo@bar')
277 >>> person('foo@bar')
278 'foo'
278 'foo'
279 >>> person('Foo Bar <foo@bar>')
279 >>> person('Foo Bar <foo@bar>')
280 'Foo Bar'
280 'Foo Bar'
281 >>> person('"Foo Bar" <foo@bar>')
281 >>> person('"Foo Bar" <foo@bar>')
282 'Foo Bar'
282 'Foo Bar'
283 >>> person('"Foo \"buz\" Bar" <foo@bar>')
283 >>> person('"Foo \"buz\" Bar" <foo@bar>')
284 'Foo "buz" Bar'
284 'Foo "buz" Bar'
285 >>> # The following are invalid, but do exist in real-life
285 >>> # The following are invalid, but do exist in real-life
286 ...
286 ...
287 >>> person('Foo "buz" Bar <foo@bar>')
287 >>> person('Foo "buz" Bar <foo@bar>')
288 'Foo "buz" Bar'
288 'Foo "buz" Bar'
289 >>> person('"Foo Bar <foo@bar>')
289 >>> person('"Foo Bar <foo@bar>')
290 'Foo Bar'
290 'Foo Bar'
291 """
291 """
292 if '@' not in author:
292 if '@' not in author:
293 return author
293 return author
294 f = author.find('<')
294 f = author.find('<')
295 if f != -1:
295 if f != -1:
296 return author[:f].strip(' "').replace('\\"', '"')
296 return author[:f].strip(' "').replace('\\"', '"')
297 f = author.find('@')
297 f = author.find('@')
298 return author[:f].replace('.', ' ')
298 return author[:f].replace('.', ' ')
299
299
300 @templatefilter('revescape')
300 @templatefilter('revescape')
301 def revescape(text):
301 def revescape(text):
302 """Any text. Escapes all "special" characters, except @.
302 """Any text. Escapes all "special" characters, except @.
303 Forward slashes are escaped twice to prevent web servers from prematurely
303 Forward slashes are escaped twice to prevent web servers from prematurely
304 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
304 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
305 """
305 """
306 return urlreq.quote(text, safe='/@').replace('/', '%252F')
306 return urlreq.quote(text, safe='/@').replace('/', '%252F')
307
307
308 @templatefilter('rfc3339date')
308 @templatefilter('rfc3339date')
309 def rfc3339date(text):
309 def rfc3339date(text):
310 """Date. Returns a date using the Internet date format
310 """Date. Returns a date using the Internet date format
311 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
311 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
312 """
312 """
313 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
313 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
314
314
315 @templatefilter('rfc822date')
315 @templatefilter('rfc822date')
316 def rfc822date(text):
316 def rfc822date(text):
317 """Date. Returns a date using the same format used in email
317 """Date. Returns a date using the same format used in email
318 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
318 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
319 """
319 """
320 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
320 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
321
321
322 @templatefilter('short')
322 @templatefilter('short')
323 def short(text):
323 def short(text):
324 """Changeset hash. Returns the short form of a changeset hash,
324 """Changeset hash. Returns the short form of a changeset hash,
325 i.e. a 12 hexadecimal digit string.
325 i.e. a 12 hexadecimal digit string.
326 """
326 """
327 return text[:12]
327 return text[:12]
328
328
329 @templatefilter('shortbisect')
329 @templatefilter('shortbisect')
330 def shortbisect(text):
330 def shortbisect(text):
331 """Any text. Treats `text` as a bisection status, and
331 """Any text. Treats `text` as a bisection status, and
332 returns a single-character representing the status (G: good, B: bad,
332 returns a single-character representing the status (G: good, B: bad,
333 S: skipped, U: untested, I: ignored). Returns single space if `text`
333 S: skipped, U: untested, I: ignored). Returns single space if `text`
334 is not a valid bisection status.
334 is not a valid bisection status.
335 """
335 """
336 return hbisect.shortlabel(text) or ' '
336 return hbisect.shortlabel(text) or ' '
337
337
338 @templatefilter('shortdate')
338 @templatefilter('shortdate')
339 def shortdate(text):
339 def shortdate(text):
340 """Date. Returns a date like "2006-09-18"."""
340 """Date. Returns a date like "2006-09-18"."""
341 return util.shortdate(text)
341 return util.shortdate(text)
342
342
343 @templatefilter('splitlines')
343 @templatefilter('splitlines')
344 def splitlines(text):
344 def splitlines(text):
345 """Any text. Split text into a list of lines."""
345 """Any text. Split text into a list of lines."""
346 return templatekw.hybridlist(text.splitlines(), name='line')
346 return templatekw.hybridlist(text.splitlines(), name='line')
347
347
348 @templatefilter('stringescape')
348 @templatefilter('stringescape')
349 def stringescape(text):
349 def stringescape(text):
350 return util.escapestr(text)
350 return util.escapestr(text)
351
351
352 @templatefilter('stringify')
352 @templatefilter('stringify')
353 def stringify(thing):
353 def stringify(thing):
354 """Any type. Turns the value into text by converting values into
354 """Any type. Turns the value into text by converting values into
355 text and concatenating them.
355 text and concatenating them.
356 """
356 """
357 thing = templatekw.unwraphybrid(thing)
357 thing = templatekw.unwraphybrid(thing)
358 if util.safehasattr(thing, '__iter__') and not isinstance(thing, bytes):
358 if util.safehasattr(thing, '__iter__') and not isinstance(thing, bytes):
359 return "".join([stringify(t) for t in thing if t is not None])
359 return "".join([stringify(t) for t in thing if t is not None])
360 if thing is None:
360 if thing is None:
361 return ""
361 return ""
362 return pycompat.bytestr(thing)
362 return pycompat.bytestr(thing)
363
363
364 @templatefilter('stripdir')
364 @templatefilter('stripdir')
365 def stripdir(text):
365 def stripdir(text):
366 """Treat the text as path and strip a directory level, if
366 """Treat the text as path and strip a directory level, if
367 possible. For example, "foo" and "foo/bar" becomes "foo".
367 possible. For example, "foo" and "foo/bar" becomes "foo".
368 """
368 """
369 dir = os.path.dirname(text)
369 dir = os.path.dirname(text)
370 if dir == "":
370 if dir == "":
371 return os.path.basename(text)
371 return os.path.basename(text)
372 else:
372 else:
373 return dir
373 return dir
374
374
375 @templatefilter('tabindent')
375 @templatefilter('tabindent')
376 def tabindent(text):
376 def tabindent(text):
377 """Any text. Returns the text, with every non-empty line
377 """Any text. Returns the text, with every non-empty line
378 except the first starting with a tab character.
378 except the first starting with a tab character.
379 """
379 """
380 return indent(text, '\t')
380 return indent(text, '\t')
381
381
382 @templatefilter('upper')
382 @templatefilter('upper')
383 def upper(text):
383 def upper(text):
384 """Any text. Converts the text to uppercase."""
384 """Any text. Converts the text to uppercase."""
385 return encoding.upper(text)
385 return encoding.upper(text)
386
386
387 @templatefilter('urlescape')
387 @templatefilter('urlescape')
388 def urlescape(text):
388 def urlescape(text):
389 """Any text. Escapes all "special" characters. For example,
389 """Any text. Escapes all "special" characters. For example,
390 "foo bar" becomes "foo%20bar".
390 "foo bar" becomes "foo%20bar".
391 """
391 """
392 return urlreq.quote(text)
392 return urlreq.quote(text)
393
393
394 @templatefilter('user')
394 @templatefilter('user')
395 def userfilter(text):
395 def userfilter(text):
396 """Any text. Returns a short representation of a user name or email
396 """Any text. Returns a short representation of a user name or email
397 address."""
397 address."""
398 return util.shortuser(text)
398 return util.shortuser(text)
399
399
400 @templatefilter('emailuser')
400 @templatefilter('emailuser')
401 def emailuser(text):
401 def emailuser(text):
402 """Any text. Returns the user portion of an email address."""
402 """Any text. Returns the user portion of an email address."""
403 return util.emailuser(text)
403 return util.emailuser(text)
404
404
405 @templatefilter('utf8')
405 @templatefilter('utf8')
406 def utf8(text):
406 def utf8(text):
407 """Any text. Converts from the local character encoding to UTF-8."""
407 """Any text. Converts from the local character encoding to UTF-8."""
408 return encoding.fromlocal(text)
408 return encoding.fromlocal(text)
409
409
410 @templatefilter('xmlescape')
410 @templatefilter('xmlescape')
411 def xmlescape(text):
411 def xmlescape(text):
412 text = (text
412 text = (text
413 .replace('&', '&amp;')
413 .replace('&', '&amp;')
414 .replace('<', '&lt;')
414 .replace('<', '&lt;')
415 .replace('>', '&gt;')
415 .replace('>', '&gt;')
416 .replace('"', '&quot;')
416 .replace('"', '&quot;')
417 .replace("'", '&#39;')) # &apos; invalid in HTML
417 .replace("'", '&#39;')) # &apos; invalid in HTML
418 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
418 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
419
419
420 def websub(text, websubtable):
420 def websub(text, websubtable):
421 """:websub: Any text. Only applies to hgweb. Applies the regular
421 """:websub: Any text. Only applies to hgweb. Applies the regular
422 expression replacements defined in the websub section.
422 expression replacements defined in the websub section.
423 """
423 """
424 if websubtable:
424 if websubtable:
425 for regexp, format in websubtable:
425 for regexp, format in websubtable:
426 text = regexp.sub(format, text)
426 text = regexp.sub(format, text)
427 return text
427 return text
428
428
429 def loadfilter(ui, extname, registrarobj):
429 def loadfilter(ui, extname, registrarobj):
430 """Load template filter from specified registrarobj
430 """Load template filter from specified registrarobj
431 """
431 """
432 for name, func in registrarobj._table.iteritems():
432 for name, func in registrarobj._table.iteritems():
433 filters[name] = func
433 filters[name] = func
434
434
435 # tell hggettext to extract docstrings from these functions:
435 # tell hggettext to extract docstrings from these functions:
436 i18nfunctions = filters.values()
436 i18nfunctions = filters.values()
General Comments 0
You need to be logged in to leave comments. Login now