##// END OF EJS Templates
hgweb: add HTML elements to control whitespace settings for annotate...
Gregory Szorc -
r34392:6797f1fb default
parent child Browse files
Show More
@@ -1,1394 +1,1398 b''
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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 copy
11 import copy
12 import mimetypes
12 import mimetypes
13 import os
13 import os
14 import re
14 import re
15
15
16 from ..i18n import _
16 from ..i18n import _
17 from ..node import hex, short
17 from ..node import hex, short
18
18
19 from .common import (
19 from .common import (
20 ErrorResponse,
20 ErrorResponse,
21 HTTP_FORBIDDEN,
21 HTTP_FORBIDDEN,
22 HTTP_NOT_FOUND,
22 HTTP_NOT_FOUND,
23 HTTP_OK,
23 HTTP_OK,
24 get_contact,
24 get_contact,
25 paritygen,
25 paritygen,
26 staticfile,
26 staticfile,
27 )
27 )
28
28
29 from .. import (
29 from .. import (
30 archival,
30 archival,
31 dagop,
31 dagop,
32 encoding,
32 encoding,
33 error,
33 error,
34 graphmod,
34 graphmod,
35 revset,
35 revset,
36 revsetlang,
36 revsetlang,
37 scmutil,
37 scmutil,
38 smartset,
38 smartset,
39 templatefilters,
39 templatefilters,
40 templater,
40 templater,
41 util,
41 util,
42 )
42 )
43
43
44 from . import (
44 from . import (
45 webutil,
45 webutil,
46 )
46 )
47
47
48 __all__ = []
48 __all__ = []
49 commands = {}
49 commands = {}
50
50
51 class webcommand(object):
51 class webcommand(object):
52 """Decorator used to register a web command handler.
52 """Decorator used to register a web command handler.
53
53
54 The decorator takes as its positional arguments the name/path the
54 The decorator takes as its positional arguments the name/path the
55 command should be accessible under.
55 command should be accessible under.
56
56
57 Usage:
57 Usage:
58
58
59 @webcommand('mycommand')
59 @webcommand('mycommand')
60 def mycommand(web, req, tmpl):
60 def mycommand(web, req, tmpl):
61 pass
61 pass
62 """
62 """
63
63
64 def __init__(self, name):
64 def __init__(self, name):
65 self.name = name
65 self.name = name
66
66
67 def __call__(self, func):
67 def __call__(self, func):
68 __all__.append(self.name)
68 __all__.append(self.name)
69 commands[self.name] = func
69 commands[self.name] = func
70 return func
70 return func
71
71
72 @webcommand('log')
72 @webcommand('log')
73 def log(web, req, tmpl):
73 def log(web, req, tmpl):
74 """
74 """
75 /log[/{revision}[/{path}]]
75 /log[/{revision}[/{path}]]
76 --------------------------
76 --------------------------
77
77
78 Show repository or file history.
78 Show repository or file history.
79
79
80 For URLs of the form ``/log/{revision}``, a list of changesets starting at
80 For URLs of the form ``/log/{revision}``, a list of changesets starting at
81 the specified changeset identifier is shown. If ``{revision}`` is not
81 the specified changeset identifier is shown. If ``{revision}`` is not
82 defined, the default is ``tip``. This form is equivalent to the
82 defined, the default is ``tip``. This form is equivalent to the
83 ``changelog`` handler.
83 ``changelog`` handler.
84
84
85 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
85 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
86 file will be shown. This form is equivalent to the ``filelog`` handler.
86 file will be shown. This form is equivalent to the ``filelog`` handler.
87 """
87 """
88
88
89 if 'file' in req.form and req.form['file'][0]:
89 if 'file' in req.form and req.form['file'][0]:
90 return filelog(web, req, tmpl)
90 return filelog(web, req, tmpl)
91 else:
91 else:
92 return changelog(web, req, tmpl)
92 return changelog(web, req, tmpl)
93
93
94 @webcommand('rawfile')
94 @webcommand('rawfile')
95 def rawfile(web, req, tmpl):
95 def rawfile(web, req, tmpl):
96 guessmime = web.configbool('web', 'guessmime', False)
96 guessmime = web.configbool('web', 'guessmime', False)
97
97
98 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
98 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
99 if not path:
99 if not path:
100 content = manifest(web, req, tmpl)
100 content = manifest(web, req, tmpl)
101 req.respond(HTTP_OK, web.ctype)
101 req.respond(HTTP_OK, web.ctype)
102 return content
102 return content
103
103
104 try:
104 try:
105 fctx = webutil.filectx(web.repo, req)
105 fctx = webutil.filectx(web.repo, req)
106 except error.LookupError as inst:
106 except error.LookupError as inst:
107 try:
107 try:
108 content = manifest(web, req, tmpl)
108 content = manifest(web, req, tmpl)
109 req.respond(HTTP_OK, web.ctype)
109 req.respond(HTTP_OK, web.ctype)
110 return content
110 return content
111 except ErrorResponse:
111 except ErrorResponse:
112 raise inst
112 raise inst
113
113
114 path = fctx.path()
114 path = fctx.path()
115 text = fctx.data()
115 text = fctx.data()
116 mt = 'application/binary'
116 mt = 'application/binary'
117 if guessmime:
117 if guessmime:
118 mt = mimetypes.guess_type(path)[0]
118 mt = mimetypes.guess_type(path)[0]
119 if mt is None:
119 if mt is None:
120 if util.binary(text):
120 if util.binary(text):
121 mt = 'application/binary'
121 mt = 'application/binary'
122 else:
122 else:
123 mt = 'text/plain'
123 mt = 'text/plain'
124 if mt.startswith('text/'):
124 if mt.startswith('text/'):
125 mt += '; charset="%s"' % encoding.encoding
125 mt += '; charset="%s"' % encoding.encoding
126
126
127 req.respond(HTTP_OK, mt, path, body=text)
127 req.respond(HTTP_OK, mt, path, body=text)
128 return []
128 return []
129
129
130 def _filerevision(web, req, tmpl, fctx):
130 def _filerevision(web, req, tmpl, fctx):
131 f = fctx.path()
131 f = fctx.path()
132 text = fctx.data()
132 text = fctx.data()
133 parity = paritygen(web.stripecount)
133 parity = paritygen(web.stripecount)
134 ishead = fctx.filerev() in fctx.filelog().headrevs()
134 ishead = fctx.filerev() in fctx.filelog().headrevs()
135
135
136 if util.binary(text):
136 if util.binary(text):
137 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
137 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
138 text = '(binary:%s)' % mt
138 text = '(binary:%s)' % mt
139
139
140 def lines():
140 def lines():
141 for lineno, t in enumerate(text.splitlines(True)):
141 for lineno, t in enumerate(text.splitlines(True)):
142 yield {"line": t,
142 yield {"line": t,
143 "lineid": "l%d" % (lineno + 1),
143 "lineid": "l%d" % (lineno + 1),
144 "linenumber": "% 6d" % (lineno + 1),
144 "linenumber": "% 6d" % (lineno + 1),
145 "parity": next(parity)}
145 "parity": next(parity)}
146
146
147 return tmpl("filerevision",
147 return tmpl("filerevision",
148 file=f,
148 file=f,
149 path=webutil.up(f),
149 path=webutil.up(f),
150 text=lines(),
150 text=lines(),
151 symrev=webutil.symrevorshortnode(req, fctx),
151 symrev=webutil.symrevorshortnode(req, fctx),
152 rename=webutil.renamelink(fctx),
152 rename=webutil.renamelink(fctx),
153 permissions=fctx.manifest().flags(f),
153 permissions=fctx.manifest().flags(f),
154 ishead=int(ishead),
154 ishead=int(ishead),
155 **webutil.commonentry(web.repo, fctx))
155 **webutil.commonentry(web.repo, fctx))
156
156
157 @webcommand('file')
157 @webcommand('file')
158 def file(web, req, tmpl):
158 def file(web, req, tmpl):
159 """
159 """
160 /file/{revision}[/{path}]
160 /file/{revision}[/{path}]
161 -------------------------
161 -------------------------
162
162
163 Show information about a directory or file in the repository.
163 Show information about a directory or file in the repository.
164
164
165 Info about the ``path`` given as a URL parameter will be rendered.
165 Info about the ``path`` given as a URL parameter will be rendered.
166
166
167 If ``path`` is a directory, information about the entries in that
167 If ``path`` is a directory, information about the entries in that
168 directory will be rendered. This form is equivalent to the ``manifest``
168 directory will be rendered. This form is equivalent to the ``manifest``
169 handler.
169 handler.
170
170
171 If ``path`` is a file, information about that file will be shown via
171 If ``path`` is a file, information about that file will be shown via
172 the ``filerevision`` template.
172 the ``filerevision`` template.
173
173
174 If ``path`` is not defined, information about the root directory will
174 If ``path`` is not defined, information about the root directory will
175 be rendered.
175 be rendered.
176 """
176 """
177 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
177 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
178 if not path:
178 if not path:
179 return manifest(web, req, tmpl)
179 return manifest(web, req, tmpl)
180 try:
180 try:
181 return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
181 return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
182 except error.LookupError as inst:
182 except error.LookupError as inst:
183 try:
183 try:
184 return manifest(web, req, tmpl)
184 return manifest(web, req, tmpl)
185 except ErrorResponse:
185 except ErrorResponse:
186 raise inst
186 raise inst
187
187
188 def _search(web, req, tmpl):
188 def _search(web, req, tmpl):
189 MODE_REVISION = 'rev'
189 MODE_REVISION = 'rev'
190 MODE_KEYWORD = 'keyword'
190 MODE_KEYWORD = 'keyword'
191 MODE_REVSET = 'revset'
191 MODE_REVSET = 'revset'
192
192
193 def revsearch(ctx):
193 def revsearch(ctx):
194 yield ctx
194 yield ctx
195
195
196 def keywordsearch(query):
196 def keywordsearch(query):
197 lower = encoding.lower
197 lower = encoding.lower
198 qw = lower(query).split()
198 qw = lower(query).split()
199
199
200 def revgen():
200 def revgen():
201 cl = web.repo.changelog
201 cl = web.repo.changelog
202 for i in xrange(len(web.repo) - 1, 0, -100):
202 for i in xrange(len(web.repo) - 1, 0, -100):
203 l = []
203 l = []
204 for j in cl.revs(max(0, i - 99), i):
204 for j in cl.revs(max(0, i - 99), i):
205 ctx = web.repo[j]
205 ctx = web.repo[j]
206 l.append(ctx)
206 l.append(ctx)
207 l.reverse()
207 l.reverse()
208 for e in l:
208 for e in l:
209 yield e
209 yield e
210
210
211 for ctx in revgen():
211 for ctx in revgen():
212 miss = 0
212 miss = 0
213 for q in qw:
213 for q in qw:
214 if not (q in lower(ctx.user()) or
214 if not (q in lower(ctx.user()) or
215 q in lower(ctx.description()) or
215 q in lower(ctx.description()) or
216 q in lower(" ".join(ctx.files()))):
216 q in lower(" ".join(ctx.files()))):
217 miss = 1
217 miss = 1
218 break
218 break
219 if miss:
219 if miss:
220 continue
220 continue
221
221
222 yield ctx
222 yield ctx
223
223
224 def revsetsearch(revs):
224 def revsetsearch(revs):
225 for r in revs:
225 for r in revs:
226 yield web.repo[r]
226 yield web.repo[r]
227
227
228 searchfuncs = {
228 searchfuncs = {
229 MODE_REVISION: (revsearch, 'exact revision search'),
229 MODE_REVISION: (revsearch, 'exact revision search'),
230 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
230 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
231 MODE_REVSET: (revsetsearch, 'revset expression search'),
231 MODE_REVSET: (revsetsearch, 'revset expression search'),
232 }
232 }
233
233
234 def getsearchmode(query):
234 def getsearchmode(query):
235 try:
235 try:
236 ctx = web.repo[query]
236 ctx = web.repo[query]
237 except (error.RepoError, error.LookupError):
237 except (error.RepoError, error.LookupError):
238 # query is not an exact revision pointer, need to
238 # query is not an exact revision pointer, need to
239 # decide if it's a revset expression or keywords
239 # decide if it's a revset expression or keywords
240 pass
240 pass
241 else:
241 else:
242 return MODE_REVISION, ctx
242 return MODE_REVISION, ctx
243
243
244 revdef = 'reverse(%s)' % query
244 revdef = 'reverse(%s)' % query
245 try:
245 try:
246 tree = revsetlang.parse(revdef)
246 tree = revsetlang.parse(revdef)
247 except error.ParseError:
247 except error.ParseError:
248 # can't parse to a revset tree
248 # can't parse to a revset tree
249 return MODE_KEYWORD, query
249 return MODE_KEYWORD, query
250
250
251 if revsetlang.depth(tree) <= 2:
251 if revsetlang.depth(tree) <= 2:
252 # no revset syntax used
252 # no revset syntax used
253 return MODE_KEYWORD, query
253 return MODE_KEYWORD, query
254
254
255 if any((token, (value or '')[:3]) == ('string', 're:')
255 if any((token, (value or '')[:3]) == ('string', 're:')
256 for token, value, pos in revsetlang.tokenize(revdef)):
256 for token, value, pos in revsetlang.tokenize(revdef)):
257 return MODE_KEYWORD, query
257 return MODE_KEYWORD, query
258
258
259 funcsused = revsetlang.funcsused(tree)
259 funcsused = revsetlang.funcsused(tree)
260 if not funcsused.issubset(revset.safesymbols):
260 if not funcsused.issubset(revset.safesymbols):
261 return MODE_KEYWORD, query
261 return MODE_KEYWORD, query
262
262
263 mfunc = revset.match(web.repo.ui, revdef, repo=web.repo)
263 mfunc = revset.match(web.repo.ui, revdef, repo=web.repo)
264 try:
264 try:
265 revs = mfunc(web.repo)
265 revs = mfunc(web.repo)
266 return MODE_REVSET, revs
266 return MODE_REVSET, revs
267 # ParseError: wrongly placed tokens, wrongs arguments, etc
267 # ParseError: wrongly placed tokens, wrongs arguments, etc
268 # RepoLookupError: no such revision, e.g. in 'revision:'
268 # RepoLookupError: no such revision, e.g. in 'revision:'
269 # Abort: bookmark/tag not exists
269 # Abort: bookmark/tag not exists
270 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
270 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
271 except (error.ParseError, error.RepoLookupError, error.Abort,
271 except (error.ParseError, error.RepoLookupError, error.Abort,
272 LookupError):
272 LookupError):
273 return MODE_KEYWORD, query
273 return MODE_KEYWORD, query
274
274
275 def changelist(**map):
275 def changelist(**map):
276 count = 0
276 count = 0
277
277
278 for ctx in searchfunc[0](funcarg):
278 for ctx in searchfunc[0](funcarg):
279 count += 1
279 count += 1
280 n = ctx.node()
280 n = ctx.node()
281 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
281 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
282 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
282 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
283
283
284 yield tmpl('searchentry',
284 yield tmpl('searchentry',
285 parity=next(parity),
285 parity=next(parity),
286 changelogtag=showtags,
286 changelogtag=showtags,
287 files=files,
287 files=files,
288 **webutil.commonentry(web.repo, ctx))
288 **webutil.commonentry(web.repo, ctx))
289
289
290 if count >= revcount:
290 if count >= revcount:
291 break
291 break
292
292
293 query = req.form['rev'][0]
293 query = req.form['rev'][0]
294 revcount = web.maxchanges
294 revcount = web.maxchanges
295 if 'revcount' in req.form:
295 if 'revcount' in req.form:
296 try:
296 try:
297 revcount = int(req.form.get('revcount', [revcount])[0])
297 revcount = int(req.form.get('revcount', [revcount])[0])
298 revcount = max(revcount, 1)
298 revcount = max(revcount, 1)
299 tmpl.defaults['sessionvars']['revcount'] = revcount
299 tmpl.defaults['sessionvars']['revcount'] = revcount
300 except ValueError:
300 except ValueError:
301 pass
301 pass
302
302
303 lessvars = copy.copy(tmpl.defaults['sessionvars'])
303 lessvars = copy.copy(tmpl.defaults['sessionvars'])
304 lessvars['revcount'] = max(revcount / 2, 1)
304 lessvars['revcount'] = max(revcount / 2, 1)
305 lessvars['rev'] = query
305 lessvars['rev'] = query
306 morevars = copy.copy(tmpl.defaults['sessionvars'])
306 morevars = copy.copy(tmpl.defaults['sessionvars'])
307 morevars['revcount'] = revcount * 2
307 morevars['revcount'] = revcount * 2
308 morevars['rev'] = query
308 morevars['rev'] = query
309
309
310 mode, funcarg = getsearchmode(query)
310 mode, funcarg = getsearchmode(query)
311
311
312 if 'forcekw' in req.form:
312 if 'forcekw' in req.form:
313 showforcekw = ''
313 showforcekw = ''
314 showunforcekw = searchfuncs[mode][1]
314 showunforcekw = searchfuncs[mode][1]
315 mode = MODE_KEYWORD
315 mode = MODE_KEYWORD
316 funcarg = query
316 funcarg = query
317 else:
317 else:
318 if mode != MODE_KEYWORD:
318 if mode != MODE_KEYWORD:
319 showforcekw = searchfuncs[MODE_KEYWORD][1]
319 showforcekw = searchfuncs[MODE_KEYWORD][1]
320 else:
320 else:
321 showforcekw = ''
321 showforcekw = ''
322 showunforcekw = ''
322 showunforcekw = ''
323
323
324 searchfunc = searchfuncs[mode]
324 searchfunc = searchfuncs[mode]
325
325
326 tip = web.repo['tip']
326 tip = web.repo['tip']
327 parity = paritygen(web.stripecount)
327 parity = paritygen(web.stripecount)
328
328
329 return tmpl('search', query=query, node=tip.hex(), symrev='tip',
329 return tmpl('search', query=query, node=tip.hex(), symrev='tip',
330 entries=changelist, archives=web.archivelist("tip"),
330 entries=changelist, archives=web.archivelist("tip"),
331 morevars=morevars, lessvars=lessvars,
331 morevars=morevars, lessvars=lessvars,
332 modedesc=searchfunc[1],
332 modedesc=searchfunc[1],
333 showforcekw=showforcekw, showunforcekw=showunforcekw)
333 showforcekw=showforcekw, showunforcekw=showunforcekw)
334
334
335 @webcommand('changelog')
335 @webcommand('changelog')
336 def changelog(web, req, tmpl, shortlog=False):
336 def changelog(web, req, tmpl, shortlog=False):
337 """
337 """
338 /changelog[/{revision}]
338 /changelog[/{revision}]
339 -----------------------
339 -----------------------
340
340
341 Show information about multiple changesets.
341 Show information about multiple changesets.
342
342
343 If the optional ``revision`` URL argument is absent, information about
343 If the optional ``revision`` URL argument is absent, information about
344 all changesets starting at ``tip`` will be rendered. If the ``revision``
344 all changesets starting at ``tip`` will be rendered. If the ``revision``
345 argument is present, changesets will be shown starting from the specified
345 argument is present, changesets will be shown starting from the specified
346 revision.
346 revision.
347
347
348 If ``revision`` is absent, the ``rev`` query string argument may be
348 If ``revision`` is absent, the ``rev`` query string argument may be
349 defined. This will perform a search for changesets.
349 defined. This will perform a search for changesets.
350
350
351 The argument for ``rev`` can be a single revision, a revision set,
351 The argument for ``rev`` can be a single revision, a revision set,
352 or a literal keyword to search for in changeset data (equivalent to
352 or a literal keyword to search for in changeset data (equivalent to
353 :hg:`log -k`).
353 :hg:`log -k`).
354
354
355 The ``revcount`` query string argument defines the maximum numbers of
355 The ``revcount`` query string argument defines the maximum numbers of
356 changesets to render.
356 changesets to render.
357
357
358 For non-searches, the ``changelog`` template will be rendered.
358 For non-searches, the ``changelog`` template will be rendered.
359 """
359 """
360
360
361 query = ''
361 query = ''
362 if 'node' in req.form:
362 if 'node' in req.form:
363 ctx = webutil.changectx(web.repo, req)
363 ctx = webutil.changectx(web.repo, req)
364 symrev = webutil.symrevorshortnode(req, ctx)
364 symrev = webutil.symrevorshortnode(req, ctx)
365 elif 'rev' in req.form:
365 elif 'rev' in req.form:
366 return _search(web, req, tmpl)
366 return _search(web, req, tmpl)
367 else:
367 else:
368 ctx = web.repo['tip']
368 ctx = web.repo['tip']
369 symrev = 'tip'
369 symrev = 'tip'
370
370
371 def changelist():
371 def changelist():
372 revs = []
372 revs = []
373 if pos != -1:
373 if pos != -1:
374 revs = web.repo.changelog.revs(pos, 0)
374 revs = web.repo.changelog.revs(pos, 0)
375 curcount = 0
375 curcount = 0
376 for rev in revs:
376 for rev in revs:
377 curcount += 1
377 curcount += 1
378 if curcount > revcount + 1:
378 if curcount > revcount + 1:
379 break
379 break
380
380
381 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
381 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
382 entry['parity'] = next(parity)
382 entry['parity'] = next(parity)
383 yield entry
383 yield entry
384
384
385 if shortlog:
385 if shortlog:
386 revcount = web.maxshortchanges
386 revcount = web.maxshortchanges
387 else:
387 else:
388 revcount = web.maxchanges
388 revcount = web.maxchanges
389
389
390 if 'revcount' in req.form:
390 if 'revcount' in req.form:
391 try:
391 try:
392 revcount = int(req.form.get('revcount', [revcount])[0])
392 revcount = int(req.form.get('revcount', [revcount])[0])
393 revcount = max(revcount, 1)
393 revcount = max(revcount, 1)
394 tmpl.defaults['sessionvars']['revcount'] = revcount
394 tmpl.defaults['sessionvars']['revcount'] = revcount
395 except ValueError:
395 except ValueError:
396 pass
396 pass
397
397
398 lessvars = copy.copy(tmpl.defaults['sessionvars'])
398 lessvars = copy.copy(tmpl.defaults['sessionvars'])
399 lessvars['revcount'] = max(revcount / 2, 1)
399 lessvars['revcount'] = max(revcount / 2, 1)
400 morevars = copy.copy(tmpl.defaults['sessionvars'])
400 morevars = copy.copy(tmpl.defaults['sessionvars'])
401 morevars['revcount'] = revcount * 2
401 morevars['revcount'] = revcount * 2
402
402
403 count = len(web.repo)
403 count = len(web.repo)
404 pos = ctx.rev()
404 pos = ctx.rev()
405 parity = paritygen(web.stripecount)
405 parity = paritygen(web.stripecount)
406
406
407 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
407 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
408
408
409 entries = list(changelist())
409 entries = list(changelist())
410 latestentry = entries[:1]
410 latestentry = entries[:1]
411 if len(entries) > revcount:
411 if len(entries) > revcount:
412 nextentry = entries[-1:]
412 nextentry = entries[-1:]
413 entries = entries[:-1]
413 entries = entries[:-1]
414 else:
414 else:
415 nextentry = []
415 nextentry = []
416
416
417 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
417 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
418 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
418 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
419 entries=entries,
419 entries=entries,
420 latestentry=latestentry, nextentry=nextentry,
420 latestentry=latestentry, nextentry=nextentry,
421 archives=web.archivelist("tip"), revcount=revcount,
421 archives=web.archivelist("tip"), revcount=revcount,
422 morevars=morevars, lessvars=lessvars, query=query)
422 morevars=morevars, lessvars=lessvars, query=query)
423
423
424 @webcommand('shortlog')
424 @webcommand('shortlog')
425 def shortlog(web, req, tmpl):
425 def shortlog(web, req, tmpl):
426 """
426 """
427 /shortlog
427 /shortlog
428 ---------
428 ---------
429
429
430 Show basic information about a set of changesets.
430 Show basic information about a set of changesets.
431
431
432 This accepts the same parameters as the ``changelog`` handler. The only
432 This accepts the same parameters as the ``changelog`` handler. The only
433 difference is the ``shortlog`` template will be rendered instead of the
433 difference is the ``shortlog`` template will be rendered instead of the
434 ``changelog`` template.
434 ``changelog`` template.
435 """
435 """
436 return changelog(web, req, tmpl, shortlog=True)
436 return changelog(web, req, tmpl, shortlog=True)
437
437
438 @webcommand('changeset')
438 @webcommand('changeset')
439 def changeset(web, req, tmpl):
439 def changeset(web, req, tmpl):
440 """
440 """
441 /changeset[/{revision}]
441 /changeset[/{revision}]
442 -----------------------
442 -----------------------
443
443
444 Show information about a single changeset.
444 Show information about a single changeset.
445
445
446 A URL path argument is the changeset identifier to show. See ``hg help
446 A URL path argument is the changeset identifier to show. See ``hg help
447 revisions`` for possible values. If not defined, the ``tip`` changeset
447 revisions`` for possible values. If not defined, the ``tip`` changeset
448 will be shown.
448 will be shown.
449
449
450 The ``changeset`` template is rendered. Contents of the ``changesettag``,
450 The ``changeset`` template is rendered. Contents of the ``changesettag``,
451 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
451 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
452 templates related to diffs may all be used to produce the output.
452 templates related to diffs may all be used to produce the output.
453 """
453 """
454 ctx = webutil.changectx(web.repo, req)
454 ctx = webutil.changectx(web.repo, req)
455
455
456 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
456 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
457
457
458 rev = webcommand('rev')(changeset)
458 rev = webcommand('rev')(changeset)
459
459
460 def decodepath(path):
460 def decodepath(path):
461 """Hook for mapping a path in the repository to a path in the
461 """Hook for mapping a path in the repository to a path in the
462 working copy.
462 working copy.
463
463
464 Extensions (e.g., largefiles) can override this to remap files in
464 Extensions (e.g., largefiles) can override this to remap files in
465 the virtual file system presented by the manifest command below."""
465 the virtual file system presented by the manifest command below."""
466 return path
466 return path
467
467
468 @webcommand('manifest')
468 @webcommand('manifest')
469 def manifest(web, req, tmpl):
469 def manifest(web, req, tmpl):
470 """
470 """
471 /manifest[/{revision}[/{path}]]
471 /manifest[/{revision}[/{path}]]
472 -------------------------------
472 -------------------------------
473
473
474 Show information about a directory.
474 Show information about a directory.
475
475
476 If the URL path arguments are omitted, information about the root
476 If the URL path arguments are omitted, information about the root
477 directory for the ``tip`` changeset will be shown.
477 directory for the ``tip`` changeset will be shown.
478
478
479 Because this handler can only show information for directories, it
479 Because this handler can only show information for directories, it
480 is recommended to use the ``file`` handler instead, as it can handle both
480 is recommended to use the ``file`` handler instead, as it can handle both
481 directories and files.
481 directories and files.
482
482
483 The ``manifest`` template will be rendered for this handler.
483 The ``manifest`` template will be rendered for this handler.
484 """
484 """
485 if 'node' in req.form:
485 if 'node' in req.form:
486 ctx = webutil.changectx(web.repo, req)
486 ctx = webutil.changectx(web.repo, req)
487 symrev = webutil.symrevorshortnode(req, ctx)
487 symrev = webutil.symrevorshortnode(req, ctx)
488 else:
488 else:
489 ctx = web.repo['tip']
489 ctx = web.repo['tip']
490 symrev = 'tip'
490 symrev = 'tip'
491 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
491 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
492 mf = ctx.manifest()
492 mf = ctx.manifest()
493 node = ctx.node()
493 node = ctx.node()
494
494
495 files = {}
495 files = {}
496 dirs = {}
496 dirs = {}
497 parity = paritygen(web.stripecount)
497 parity = paritygen(web.stripecount)
498
498
499 if path and path[-1] != "/":
499 if path and path[-1] != "/":
500 path += "/"
500 path += "/"
501 l = len(path)
501 l = len(path)
502 abspath = "/" + path
502 abspath = "/" + path
503
503
504 for full, n in mf.iteritems():
504 for full, n in mf.iteritems():
505 # the virtual path (working copy path) used for the full
505 # the virtual path (working copy path) used for the full
506 # (repository) path
506 # (repository) path
507 f = decodepath(full)
507 f = decodepath(full)
508
508
509 if f[:l] != path:
509 if f[:l] != path:
510 continue
510 continue
511 remain = f[l:]
511 remain = f[l:]
512 elements = remain.split('/')
512 elements = remain.split('/')
513 if len(elements) == 1:
513 if len(elements) == 1:
514 files[remain] = full
514 files[remain] = full
515 else:
515 else:
516 h = dirs # need to retain ref to dirs (root)
516 h = dirs # need to retain ref to dirs (root)
517 for elem in elements[0:-1]:
517 for elem in elements[0:-1]:
518 if elem not in h:
518 if elem not in h:
519 h[elem] = {}
519 h[elem] = {}
520 h = h[elem]
520 h = h[elem]
521 if len(h) > 1:
521 if len(h) > 1:
522 break
522 break
523 h[None] = None # denotes files present
523 h[None] = None # denotes files present
524
524
525 if mf and not files and not dirs:
525 if mf and not files and not dirs:
526 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
526 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
527
527
528 def filelist(**map):
528 def filelist(**map):
529 for f in sorted(files):
529 for f in sorted(files):
530 full = files[f]
530 full = files[f]
531
531
532 fctx = ctx.filectx(full)
532 fctx = ctx.filectx(full)
533 yield {"file": full,
533 yield {"file": full,
534 "parity": next(parity),
534 "parity": next(parity),
535 "basename": f,
535 "basename": f,
536 "date": fctx.date(),
536 "date": fctx.date(),
537 "size": fctx.size(),
537 "size": fctx.size(),
538 "permissions": mf.flags(full)}
538 "permissions": mf.flags(full)}
539
539
540 def dirlist(**map):
540 def dirlist(**map):
541 for d in sorted(dirs):
541 for d in sorted(dirs):
542
542
543 emptydirs = []
543 emptydirs = []
544 h = dirs[d]
544 h = dirs[d]
545 while isinstance(h, dict) and len(h) == 1:
545 while isinstance(h, dict) and len(h) == 1:
546 k, v = h.items()[0]
546 k, v = h.items()[0]
547 if v:
547 if v:
548 emptydirs.append(k)
548 emptydirs.append(k)
549 h = v
549 h = v
550
550
551 path = "%s%s" % (abspath, d)
551 path = "%s%s" % (abspath, d)
552 yield {"parity": next(parity),
552 yield {"parity": next(parity),
553 "path": path,
553 "path": path,
554 "emptydirs": "/".join(emptydirs),
554 "emptydirs": "/".join(emptydirs),
555 "basename": d}
555 "basename": d}
556
556
557 return tmpl("manifest",
557 return tmpl("manifest",
558 symrev=symrev,
558 symrev=symrev,
559 path=abspath,
559 path=abspath,
560 up=webutil.up(abspath),
560 up=webutil.up(abspath),
561 upparity=next(parity),
561 upparity=next(parity),
562 fentries=filelist,
562 fentries=filelist,
563 dentries=dirlist,
563 dentries=dirlist,
564 archives=web.archivelist(hex(node)),
564 archives=web.archivelist(hex(node)),
565 **webutil.commonentry(web.repo, ctx))
565 **webutil.commonentry(web.repo, ctx))
566
566
567 @webcommand('tags')
567 @webcommand('tags')
568 def tags(web, req, tmpl):
568 def tags(web, req, tmpl):
569 """
569 """
570 /tags
570 /tags
571 -----
571 -----
572
572
573 Show information about tags.
573 Show information about tags.
574
574
575 No arguments are accepted.
575 No arguments are accepted.
576
576
577 The ``tags`` template is rendered.
577 The ``tags`` template is rendered.
578 """
578 """
579 i = list(reversed(web.repo.tagslist()))
579 i = list(reversed(web.repo.tagslist()))
580 parity = paritygen(web.stripecount)
580 parity = paritygen(web.stripecount)
581
581
582 def entries(notip, latestonly, **map):
582 def entries(notip, latestonly, **map):
583 t = i
583 t = i
584 if notip:
584 if notip:
585 t = [(k, n) for k, n in i if k != "tip"]
585 t = [(k, n) for k, n in i if k != "tip"]
586 if latestonly:
586 if latestonly:
587 t = t[:1]
587 t = t[:1]
588 for k, n in t:
588 for k, n in t:
589 yield {"parity": next(parity),
589 yield {"parity": next(parity),
590 "tag": k,
590 "tag": k,
591 "date": web.repo[n].date(),
591 "date": web.repo[n].date(),
592 "node": hex(n)}
592 "node": hex(n)}
593
593
594 return tmpl("tags",
594 return tmpl("tags",
595 node=hex(web.repo.changelog.tip()),
595 node=hex(web.repo.changelog.tip()),
596 entries=lambda **x: entries(False, False, **x),
596 entries=lambda **x: entries(False, False, **x),
597 entriesnotip=lambda **x: entries(True, False, **x),
597 entriesnotip=lambda **x: entries(True, False, **x),
598 latestentry=lambda **x: entries(True, True, **x))
598 latestentry=lambda **x: entries(True, True, **x))
599
599
600 @webcommand('bookmarks')
600 @webcommand('bookmarks')
601 def bookmarks(web, req, tmpl):
601 def bookmarks(web, req, tmpl):
602 """
602 """
603 /bookmarks
603 /bookmarks
604 ----------
604 ----------
605
605
606 Show information about bookmarks.
606 Show information about bookmarks.
607
607
608 No arguments are accepted.
608 No arguments are accepted.
609
609
610 The ``bookmarks`` template is rendered.
610 The ``bookmarks`` template is rendered.
611 """
611 """
612 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
612 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
613 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
613 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
614 i = sorted(i, key=sortkey, reverse=True)
614 i = sorted(i, key=sortkey, reverse=True)
615 parity = paritygen(web.stripecount)
615 parity = paritygen(web.stripecount)
616
616
617 def entries(latestonly, **map):
617 def entries(latestonly, **map):
618 t = i
618 t = i
619 if latestonly:
619 if latestonly:
620 t = i[:1]
620 t = i[:1]
621 for k, n in t:
621 for k, n in t:
622 yield {"parity": next(parity),
622 yield {"parity": next(parity),
623 "bookmark": k,
623 "bookmark": k,
624 "date": web.repo[n].date(),
624 "date": web.repo[n].date(),
625 "node": hex(n)}
625 "node": hex(n)}
626
626
627 if i:
627 if i:
628 latestrev = i[0][1]
628 latestrev = i[0][1]
629 else:
629 else:
630 latestrev = -1
630 latestrev = -1
631
631
632 return tmpl("bookmarks",
632 return tmpl("bookmarks",
633 node=hex(web.repo.changelog.tip()),
633 node=hex(web.repo.changelog.tip()),
634 lastchange=[{"date": web.repo[latestrev].date()}],
634 lastchange=[{"date": web.repo[latestrev].date()}],
635 entries=lambda **x: entries(latestonly=False, **x),
635 entries=lambda **x: entries(latestonly=False, **x),
636 latestentry=lambda **x: entries(latestonly=True, **x))
636 latestentry=lambda **x: entries(latestonly=True, **x))
637
637
638 @webcommand('branches')
638 @webcommand('branches')
639 def branches(web, req, tmpl):
639 def branches(web, req, tmpl):
640 """
640 """
641 /branches
641 /branches
642 ---------
642 ---------
643
643
644 Show information about branches.
644 Show information about branches.
645
645
646 All known branches are contained in the output, even closed branches.
646 All known branches are contained in the output, even closed branches.
647
647
648 No arguments are accepted.
648 No arguments are accepted.
649
649
650 The ``branches`` template is rendered.
650 The ``branches`` template is rendered.
651 """
651 """
652 entries = webutil.branchentries(web.repo, web.stripecount)
652 entries = webutil.branchentries(web.repo, web.stripecount)
653 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
653 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
654 return tmpl('branches', node=hex(web.repo.changelog.tip()),
654 return tmpl('branches', node=hex(web.repo.changelog.tip()),
655 entries=entries, latestentry=latestentry)
655 entries=entries, latestentry=latestentry)
656
656
657 @webcommand('summary')
657 @webcommand('summary')
658 def summary(web, req, tmpl):
658 def summary(web, req, tmpl):
659 """
659 """
660 /summary
660 /summary
661 --------
661 --------
662
662
663 Show a summary of repository state.
663 Show a summary of repository state.
664
664
665 Information about the latest changesets, bookmarks, tags, and branches
665 Information about the latest changesets, bookmarks, tags, and branches
666 is captured by this handler.
666 is captured by this handler.
667
667
668 The ``summary`` template is rendered.
668 The ``summary`` template is rendered.
669 """
669 """
670 i = reversed(web.repo.tagslist())
670 i = reversed(web.repo.tagslist())
671
671
672 def tagentries(**map):
672 def tagentries(**map):
673 parity = paritygen(web.stripecount)
673 parity = paritygen(web.stripecount)
674 count = 0
674 count = 0
675 for k, n in i:
675 for k, n in i:
676 if k == "tip": # skip tip
676 if k == "tip": # skip tip
677 continue
677 continue
678
678
679 count += 1
679 count += 1
680 if count > 10: # limit to 10 tags
680 if count > 10: # limit to 10 tags
681 break
681 break
682
682
683 yield tmpl("tagentry",
683 yield tmpl("tagentry",
684 parity=next(parity),
684 parity=next(parity),
685 tag=k,
685 tag=k,
686 node=hex(n),
686 node=hex(n),
687 date=web.repo[n].date())
687 date=web.repo[n].date())
688
688
689 def bookmarks(**map):
689 def bookmarks(**map):
690 parity = paritygen(web.stripecount)
690 parity = paritygen(web.stripecount)
691 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
691 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
692 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
692 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
693 marks = sorted(marks, key=sortkey, reverse=True)
693 marks = sorted(marks, key=sortkey, reverse=True)
694 for k, n in marks[:10]: # limit to 10 bookmarks
694 for k, n in marks[:10]: # limit to 10 bookmarks
695 yield {'parity': next(parity),
695 yield {'parity': next(parity),
696 'bookmark': k,
696 'bookmark': k,
697 'date': web.repo[n].date(),
697 'date': web.repo[n].date(),
698 'node': hex(n)}
698 'node': hex(n)}
699
699
700 def changelist(**map):
700 def changelist(**map):
701 parity = paritygen(web.stripecount, offset=start - end)
701 parity = paritygen(web.stripecount, offset=start - end)
702 l = [] # build a list in forward order for efficiency
702 l = [] # build a list in forward order for efficiency
703 revs = []
703 revs = []
704 if start < end:
704 if start < end:
705 revs = web.repo.changelog.revs(start, end - 1)
705 revs = web.repo.changelog.revs(start, end - 1)
706 for i in revs:
706 for i in revs:
707 ctx = web.repo[i]
707 ctx = web.repo[i]
708
708
709 l.append(tmpl(
709 l.append(tmpl(
710 'shortlogentry',
710 'shortlogentry',
711 parity=next(parity),
711 parity=next(parity),
712 **webutil.commonentry(web.repo, ctx)))
712 **webutil.commonentry(web.repo, ctx)))
713
713
714 for entry in reversed(l):
714 for entry in reversed(l):
715 yield entry
715 yield entry
716
716
717 tip = web.repo['tip']
717 tip = web.repo['tip']
718 count = len(web.repo)
718 count = len(web.repo)
719 start = max(0, count - web.maxchanges)
719 start = max(0, count - web.maxchanges)
720 end = min(count, start + web.maxchanges)
720 end = min(count, start + web.maxchanges)
721
721
722 desc = web.config("web", "description")
722 desc = web.config("web", "description")
723 if not desc:
723 if not desc:
724 desc = 'unknown'
724 desc = 'unknown'
725 return tmpl("summary",
725 return tmpl("summary",
726 desc=desc,
726 desc=desc,
727 owner=get_contact(web.config) or "unknown",
727 owner=get_contact(web.config) or "unknown",
728 lastchange=tip.date(),
728 lastchange=tip.date(),
729 tags=tagentries,
729 tags=tagentries,
730 bookmarks=bookmarks,
730 bookmarks=bookmarks,
731 branches=webutil.branchentries(web.repo, web.stripecount, 10),
731 branches=webutil.branchentries(web.repo, web.stripecount, 10),
732 shortlog=changelist,
732 shortlog=changelist,
733 node=tip.hex(),
733 node=tip.hex(),
734 symrev='tip',
734 symrev='tip',
735 archives=web.archivelist("tip"),
735 archives=web.archivelist("tip"),
736 labels=web.configlist('web', 'labels'))
736 labels=web.configlist('web', 'labels'))
737
737
738 @webcommand('filediff')
738 @webcommand('filediff')
739 def filediff(web, req, tmpl):
739 def filediff(web, req, tmpl):
740 """
740 """
741 /diff/{revision}/{path}
741 /diff/{revision}/{path}
742 -----------------------
742 -----------------------
743
743
744 Show how a file changed in a particular commit.
744 Show how a file changed in a particular commit.
745
745
746 The ``filediff`` template is rendered.
746 The ``filediff`` template is rendered.
747
747
748 This handler is registered under both the ``/diff`` and ``/filediff``
748 This handler is registered under both the ``/diff`` and ``/filediff``
749 paths. ``/diff`` is used in modern code.
749 paths. ``/diff`` is used in modern code.
750 """
750 """
751 fctx, ctx = None, None
751 fctx, ctx = None, None
752 try:
752 try:
753 fctx = webutil.filectx(web.repo, req)
753 fctx = webutil.filectx(web.repo, req)
754 except LookupError:
754 except LookupError:
755 ctx = webutil.changectx(web.repo, req)
755 ctx = webutil.changectx(web.repo, req)
756 path = webutil.cleanpath(web.repo, req.form['file'][0])
756 path = webutil.cleanpath(web.repo, req.form['file'][0])
757 if path not in ctx.files():
757 if path not in ctx.files():
758 raise
758 raise
759
759
760 if fctx is not None:
760 if fctx is not None:
761 path = fctx.path()
761 path = fctx.path()
762 ctx = fctx.changectx()
762 ctx = fctx.changectx()
763 basectx = ctx.p1()
763 basectx = ctx.p1()
764
764
765 style = web.config('web', 'style')
765 style = web.config('web', 'style')
766 if 'style' in req.form:
766 if 'style' in req.form:
767 style = req.form['style'][0]
767 style = req.form['style'][0]
768
768
769 diffs = webutil.diffs(web, tmpl, ctx, basectx, [path], style)
769 diffs = webutil.diffs(web, tmpl, ctx, basectx, [path], style)
770 if fctx is not None:
770 if fctx is not None:
771 rename = webutil.renamelink(fctx)
771 rename = webutil.renamelink(fctx)
772 ctx = fctx
772 ctx = fctx
773 else:
773 else:
774 rename = []
774 rename = []
775 ctx = ctx
775 ctx = ctx
776 return tmpl("filediff",
776 return tmpl("filediff",
777 file=path,
777 file=path,
778 symrev=webutil.symrevorshortnode(req, ctx),
778 symrev=webutil.symrevorshortnode(req, ctx),
779 rename=rename,
779 rename=rename,
780 diff=diffs,
780 diff=diffs,
781 **webutil.commonentry(web.repo, ctx))
781 **webutil.commonentry(web.repo, ctx))
782
782
783 diff = webcommand('diff')(filediff)
783 diff = webcommand('diff')(filediff)
784
784
785 @webcommand('comparison')
785 @webcommand('comparison')
786 def comparison(web, req, tmpl):
786 def comparison(web, req, tmpl):
787 """
787 """
788 /comparison/{revision}/{path}
788 /comparison/{revision}/{path}
789 -----------------------------
789 -----------------------------
790
790
791 Show a comparison between the old and new versions of a file from changes
791 Show a comparison between the old and new versions of a file from changes
792 made on a particular revision.
792 made on a particular revision.
793
793
794 This is similar to the ``diff`` handler. However, this form features
794 This is similar to the ``diff`` handler. However, this form features
795 a split or side-by-side diff rather than a unified diff.
795 a split or side-by-side diff rather than a unified diff.
796
796
797 The ``context`` query string argument can be used to control the lines of
797 The ``context`` query string argument can be used to control the lines of
798 context in the diff.
798 context in the diff.
799
799
800 The ``filecomparison`` template is rendered.
800 The ``filecomparison`` template is rendered.
801 """
801 """
802 ctx = webutil.changectx(web.repo, req)
802 ctx = webutil.changectx(web.repo, req)
803 if 'file' not in req.form:
803 if 'file' not in req.form:
804 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
804 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
805 path = webutil.cleanpath(web.repo, req.form['file'][0])
805 path = webutil.cleanpath(web.repo, req.form['file'][0])
806
806
807 parsecontext = lambda v: v == 'full' and -1 or int(v)
807 parsecontext = lambda v: v == 'full' and -1 or int(v)
808 if 'context' in req.form:
808 if 'context' in req.form:
809 context = parsecontext(req.form['context'][0])
809 context = parsecontext(req.form['context'][0])
810 else:
810 else:
811 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
811 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
812
812
813 def filelines(f):
813 def filelines(f):
814 if f.isbinary():
814 if f.isbinary():
815 mt = mimetypes.guess_type(f.path())[0]
815 mt = mimetypes.guess_type(f.path())[0]
816 if not mt:
816 if not mt:
817 mt = 'application/octet-stream'
817 mt = 'application/octet-stream'
818 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
818 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
819 return f.data().splitlines()
819 return f.data().splitlines()
820
820
821 fctx = None
821 fctx = None
822 parent = ctx.p1()
822 parent = ctx.p1()
823 leftrev = parent.rev()
823 leftrev = parent.rev()
824 leftnode = parent.node()
824 leftnode = parent.node()
825 rightrev = ctx.rev()
825 rightrev = ctx.rev()
826 rightnode = ctx.node()
826 rightnode = ctx.node()
827 if path in ctx:
827 if path in ctx:
828 fctx = ctx[path]
828 fctx = ctx[path]
829 rightlines = filelines(fctx)
829 rightlines = filelines(fctx)
830 if path not in parent:
830 if path not in parent:
831 leftlines = ()
831 leftlines = ()
832 else:
832 else:
833 pfctx = parent[path]
833 pfctx = parent[path]
834 leftlines = filelines(pfctx)
834 leftlines = filelines(pfctx)
835 else:
835 else:
836 rightlines = ()
836 rightlines = ()
837 pfctx = ctx.parents()[0][path]
837 pfctx = ctx.parents()[0][path]
838 leftlines = filelines(pfctx)
838 leftlines = filelines(pfctx)
839
839
840 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
840 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
841 if fctx is not None:
841 if fctx is not None:
842 rename = webutil.renamelink(fctx)
842 rename = webutil.renamelink(fctx)
843 ctx = fctx
843 ctx = fctx
844 else:
844 else:
845 rename = []
845 rename = []
846 ctx = ctx
846 ctx = ctx
847 return tmpl('filecomparison',
847 return tmpl('filecomparison',
848 file=path,
848 file=path,
849 symrev=webutil.symrevorshortnode(req, ctx),
849 symrev=webutil.symrevorshortnode(req, ctx),
850 rename=rename,
850 rename=rename,
851 leftrev=leftrev,
851 leftrev=leftrev,
852 leftnode=hex(leftnode),
852 leftnode=hex(leftnode),
853 rightrev=rightrev,
853 rightrev=rightrev,
854 rightnode=hex(rightnode),
854 rightnode=hex(rightnode),
855 comparison=comparison,
855 comparison=comparison,
856 **webutil.commonentry(web.repo, ctx))
856 **webutil.commonentry(web.repo, ctx))
857
857
858 @webcommand('annotate')
858 @webcommand('annotate')
859 def annotate(web, req, tmpl):
859 def annotate(web, req, tmpl):
860 """
860 """
861 /annotate/{revision}/{path}
861 /annotate/{revision}/{path}
862 ---------------------------
862 ---------------------------
863
863
864 Show changeset information for each line in a file.
864 Show changeset information for each line in a file.
865
865
866 The ``ignorews``, ``ignorewsamount``, ``ignorewseol``, and
866 The ``ignorews``, ``ignorewsamount``, ``ignorewseol``, and
867 ``ignoreblanklines`` query string arguments have the same meaning as
867 ``ignoreblanklines`` query string arguments have the same meaning as
868 their ``[annotate]`` config equivalents. A value of ``0`` sets the
868 their ``[annotate]`` config equivalents. A value of ``0`` sets the
869 whitespace option to false. All other values are true. If not defined,
869 whitespace option to false. All other values are true. If not defined,
870 the server default settings are used.
870 the server default settings are used.
871
871
872 The ``fileannotate`` template is rendered.
872 The ``fileannotate`` template is rendered.
873 """
873 """
874 fctx = webutil.filectx(web.repo, req)
874 fctx = webutil.filectx(web.repo, req)
875 f = fctx.path()
875 f = fctx.path()
876 parity = paritygen(web.stripecount)
876 parity = paritygen(web.stripecount)
877 ishead = fctx.filerev() in fctx.filelog().headrevs()
877 ishead = fctx.filerev() in fctx.filelog().headrevs()
878
878
879 # parents() is called once per line and several lines likely belong to
879 # parents() is called once per line and several lines likely belong to
880 # same revision. So it is worth caching.
880 # same revision. So it is worth caching.
881 # TODO there are still redundant operations within basefilectx.parents()
881 # TODO there are still redundant operations within basefilectx.parents()
882 # and from the fctx.annotate() call itself that could be cached.
882 # and from the fctx.annotate() call itself that could be cached.
883 parentscache = {}
883 parentscache = {}
884 def parents(f):
884 def parents(f):
885 rev = f.rev()
885 rev = f.rev()
886 if rev not in parentscache:
886 if rev not in parentscache:
887 parentscache[rev] = []
887 parentscache[rev] = []
888 for p in f.parents():
888 for p in f.parents():
889 entry = {
889 entry = {
890 'node': p.hex(),
890 'node': p.hex(),
891 'rev': p.rev(),
891 'rev': p.rev(),
892 }
892 }
893 parentscache[rev].append(entry)
893 parentscache[rev].append(entry)
894
894
895 for p in parentscache[rev]:
895 for p in parentscache[rev]:
896 yield p
896 yield p
897
897
898 def annotate(**map):
898 def annotate(**map):
899 if fctx.isbinary():
899 if fctx.isbinary():
900 mt = (mimetypes.guess_type(fctx.path())[0]
900 mt = (mimetypes.guess_type(fctx.path())[0]
901 or 'application/octet-stream')
901 or 'application/octet-stream')
902 lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
902 lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
903 else:
903 else:
904 lines = webutil.annotate(req, fctx, web.repo.ui)
904 lines = webutil.annotate(req, fctx, web.repo.ui)
905
905
906 previousrev = None
906 previousrev = None
907 blockparitygen = paritygen(1)
907 blockparitygen = paritygen(1)
908 for lineno, ((f, targetline), l) in enumerate(lines):
908 for lineno, ((f, targetline), l) in enumerate(lines):
909 rev = f.rev()
909 rev = f.rev()
910 if rev != previousrev:
910 if rev != previousrev:
911 blockhead = True
911 blockhead = True
912 blockparity = next(blockparitygen)
912 blockparity = next(blockparitygen)
913 else:
913 else:
914 blockhead = None
914 blockhead = None
915 previousrev = rev
915 previousrev = rev
916 yield {"parity": next(parity),
916 yield {"parity": next(parity),
917 "node": f.hex(),
917 "node": f.hex(),
918 "rev": rev,
918 "rev": rev,
919 "author": f.user(),
919 "author": f.user(),
920 "parents": parents(f),
920 "parents": parents(f),
921 "desc": f.description(),
921 "desc": f.description(),
922 "extra": f.extra(),
922 "extra": f.extra(),
923 "file": f.path(),
923 "file": f.path(),
924 "blockhead": blockhead,
924 "blockhead": blockhead,
925 "blockparity": blockparity,
925 "blockparity": blockparity,
926 "targetline": targetline,
926 "targetline": targetline,
927 "line": l,
927 "line": l,
928 "lineno": lineno + 1,
928 "lineno": lineno + 1,
929 "lineid": "l%d" % (lineno + 1),
929 "lineid": "l%d" % (lineno + 1),
930 "linenumber": "% 6d" % (lineno + 1),
930 "linenumber": "% 6d" % (lineno + 1),
931 "revdate": f.date()}
931 "revdate": f.date()}
932
932
933 diffopts = webutil.difffeatureopts(req, web.repo.ui, 'annotate')
934 diffopts = {k: getattr(diffopts, k) for k in diffopts.defaults}
935
933 return tmpl("fileannotate",
936 return tmpl("fileannotate",
934 file=f,
937 file=f,
935 annotate=annotate,
938 annotate=annotate,
936 path=webutil.up(f),
939 path=webutil.up(f),
937 symrev=webutil.symrevorshortnode(req, fctx),
940 symrev=webutil.symrevorshortnode(req, fctx),
938 rename=webutil.renamelink(fctx),
941 rename=webutil.renamelink(fctx),
939 permissions=fctx.manifest().flags(f),
942 permissions=fctx.manifest().flags(f),
940 ishead=int(ishead),
943 ishead=int(ishead),
944 diffopts=diffopts,
941 **webutil.commonentry(web.repo, fctx))
945 **webutil.commonentry(web.repo, fctx))
942
946
943 @webcommand('filelog')
947 @webcommand('filelog')
944 def filelog(web, req, tmpl):
948 def filelog(web, req, tmpl):
945 """
949 """
946 /filelog/{revision}/{path}
950 /filelog/{revision}/{path}
947 --------------------------
951 --------------------------
948
952
949 Show information about the history of a file in the repository.
953 Show information about the history of a file in the repository.
950
954
951 The ``revcount`` query string argument can be defined to control the
955 The ``revcount`` query string argument can be defined to control the
952 maximum number of entries to show.
956 maximum number of entries to show.
953
957
954 The ``filelog`` template will be rendered.
958 The ``filelog`` template will be rendered.
955 """
959 """
956
960
957 try:
961 try:
958 fctx = webutil.filectx(web.repo, req)
962 fctx = webutil.filectx(web.repo, req)
959 f = fctx.path()
963 f = fctx.path()
960 fl = fctx.filelog()
964 fl = fctx.filelog()
961 except error.LookupError:
965 except error.LookupError:
962 f = webutil.cleanpath(web.repo, req.form['file'][0])
966 f = webutil.cleanpath(web.repo, req.form['file'][0])
963 fl = web.repo.file(f)
967 fl = web.repo.file(f)
964 numrevs = len(fl)
968 numrevs = len(fl)
965 if not numrevs: # file doesn't exist at all
969 if not numrevs: # file doesn't exist at all
966 raise
970 raise
967 rev = webutil.changectx(web.repo, req).rev()
971 rev = webutil.changectx(web.repo, req).rev()
968 first = fl.linkrev(0)
972 first = fl.linkrev(0)
969 if rev < first: # current rev is from before file existed
973 if rev < first: # current rev is from before file existed
970 raise
974 raise
971 frev = numrevs - 1
975 frev = numrevs - 1
972 while fl.linkrev(frev) > rev:
976 while fl.linkrev(frev) > rev:
973 frev -= 1
977 frev -= 1
974 fctx = web.repo.filectx(f, fl.linkrev(frev))
978 fctx = web.repo.filectx(f, fl.linkrev(frev))
975
979
976 revcount = web.maxshortchanges
980 revcount = web.maxshortchanges
977 if 'revcount' in req.form:
981 if 'revcount' in req.form:
978 try:
982 try:
979 revcount = int(req.form.get('revcount', [revcount])[0])
983 revcount = int(req.form.get('revcount', [revcount])[0])
980 revcount = max(revcount, 1)
984 revcount = max(revcount, 1)
981 tmpl.defaults['sessionvars']['revcount'] = revcount
985 tmpl.defaults['sessionvars']['revcount'] = revcount
982 except ValueError:
986 except ValueError:
983 pass
987 pass
984
988
985 lrange = webutil.linerange(req)
989 lrange = webutil.linerange(req)
986
990
987 lessvars = copy.copy(tmpl.defaults['sessionvars'])
991 lessvars = copy.copy(tmpl.defaults['sessionvars'])
988 lessvars['revcount'] = max(revcount / 2, 1)
992 lessvars['revcount'] = max(revcount / 2, 1)
989 morevars = copy.copy(tmpl.defaults['sessionvars'])
993 morevars = copy.copy(tmpl.defaults['sessionvars'])
990 morevars['revcount'] = revcount * 2
994 morevars['revcount'] = revcount * 2
991
995
992 patch = 'patch' in req.form
996 patch = 'patch' in req.form
993 if patch:
997 if patch:
994 lessvars['patch'] = morevars['patch'] = req.form['patch'][0]
998 lessvars['patch'] = morevars['patch'] = req.form['patch'][0]
995 descend = 'descend' in req.form
999 descend = 'descend' in req.form
996 if descend:
1000 if descend:
997 lessvars['descend'] = morevars['descend'] = req.form['descend'][0]
1001 lessvars['descend'] = morevars['descend'] = req.form['descend'][0]
998
1002
999 count = fctx.filerev() + 1
1003 count = fctx.filerev() + 1
1000 start = max(0, count - revcount) # first rev on this page
1004 start = max(0, count - revcount) # first rev on this page
1001 end = min(count, start + revcount) # last rev on this page
1005 end = min(count, start + revcount) # last rev on this page
1002 parity = paritygen(web.stripecount, offset=start - end)
1006 parity = paritygen(web.stripecount, offset=start - end)
1003
1007
1004 repo = web.repo
1008 repo = web.repo
1005 revs = fctx.filelog().revs(start, end - 1)
1009 revs = fctx.filelog().revs(start, end - 1)
1006 entries = []
1010 entries = []
1007
1011
1008 diffstyle = web.config('web', 'style')
1012 diffstyle = web.config('web', 'style')
1009 if 'style' in req.form:
1013 if 'style' in req.form:
1010 diffstyle = req.form['style'][0]
1014 diffstyle = req.form['style'][0]
1011
1015
1012 def diff(fctx, linerange=None):
1016 def diff(fctx, linerange=None):
1013 ctx = fctx.changectx()
1017 ctx = fctx.changectx()
1014 basectx = ctx.p1()
1018 basectx = ctx.p1()
1015 path = fctx.path()
1019 path = fctx.path()
1016 return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle,
1020 return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle,
1017 linerange=linerange,
1021 linerange=linerange,
1018 lineidprefix='%s-' % ctx.hex()[:12])
1022 lineidprefix='%s-' % ctx.hex()[:12])
1019
1023
1020 linerange = None
1024 linerange = None
1021 if lrange is not None:
1025 if lrange is not None:
1022 linerange = webutil.formatlinerange(*lrange)
1026 linerange = webutil.formatlinerange(*lrange)
1023 # deactivate numeric nav links when linerange is specified as this
1027 # deactivate numeric nav links when linerange is specified as this
1024 # would required a dedicated "revnav" class
1028 # would required a dedicated "revnav" class
1025 nav = None
1029 nav = None
1026 if descend:
1030 if descend:
1027 it = dagop.blockdescendants(fctx, *lrange)
1031 it = dagop.blockdescendants(fctx, *lrange)
1028 else:
1032 else:
1029 it = dagop.blockancestors(fctx, *lrange)
1033 it = dagop.blockancestors(fctx, *lrange)
1030 for i, (c, lr) in enumerate(it, 1):
1034 for i, (c, lr) in enumerate(it, 1):
1031 diffs = None
1035 diffs = None
1032 if patch:
1036 if patch:
1033 diffs = diff(c, linerange=lr)
1037 diffs = diff(c, linerange=lr)
1034 # follow renames accross filtered (not in range) revisions
1038 # follow renames accross filtered (not in range) revisions
1035 path = c.path()
1039 path = c.path()
1036 entries.append(dict(
1040 entries.append(dict(
1037 parity=next(parity),
1041 parity=next(parity),
1038 filerev=c.rev(),
1042 filerev=c.rev(),
1039 file=path,
1043 file=path,
1040 diff=diffs,
1044 diff=diffs,
1041 linerange=webutil.formatlinerange(*lr),
1045 linerange=webutil.formatlinerange(*lr),
1042 **webutil.commonentry(repo, c)))
1046 **webutil.commonentry(repo, c)))
1043 if i == revcount:
1047 if i == revcount:
1044 break
1048 break
1045 lessvars['linerange'] = webutil.formatlinerange(*lrange)
1049 lessvars['linerange'] = webutil.formatlinerange(*lrange)
1046 morevars['linerange'] = lessvars['linerange']
1050 morevars['linerange'] = lessvars['linerange']
1047 else:
1051 else:
1048 for i in revs:
1052 for i in revs:
1049 iterfctx = fctx.filectx(i)
1053 iterfctx = fctx.filectx(i)
1050 diffs = None
1054 diffs = None
1051 if patch:
1055 if patch:
1052 diffs = diff(iterfctx)
1056 diffs = diff(iterfctx)
1053 entries.append(dict(
1057 entries.append(dict(
1054 parity=next(parity),
1058 parity=next(parity),
1055 filerev=i,
1059 filerev=i,
1056 file=f,
1060 file=f,
1057 diff=diffs,
1061 diff=diffs,
1058 rename=webutil.renamelink(iterfctx),
1062 rename=webutil.renamelink(iterfctx),
1059 **webutil.commonentry(repo, iterfctx)))
1063 **webutil.commonentry(repo, iterfctx)))
1060 entries.reverse()
1064 entries.reverse()
1061 revnav = webutil.filerevnav(web.repo, fctx.path())
1065 revnav = webutil.filerevnav(web.repo, fctx.path())
1062 nav = revnav.gen(end - 1, revcount, count)
1066 nav = revnav.gen(end - 1, revcount, count)
1063
1067
1064 latestentry = entries[:1]
1068 latestentry = entries[:1]
1065
1069
1066 return tmpl("filelog",
1070 return tmpl("filelog",
1067 file=f,
1071 file=f,
1068 nav=nav,
1072 nav=nav,
1069 symrev=webutil.symrevorshortnode(req, fctx),
1073 symrev=webutil.symrevorshortnode(req, fctx),
1070 entries=entries,
1074 entries=entries,
1071 descend=descend,
1075 descend=descend,
1072 patch=patch,
1076 patch=patch,
1073 latestentry=latestentry,
1077 latestentry=latestentry,
1074 linerange=linerange,
1078 linerange=linerange,
1075 revcount=revcount,
1079 revcount=revcount,
1076 morevars=morevars,
1080 morevars=morevars,
1077 lessvars=lessvars,
1081 lessvars=lessvars,
1078 **webutil.commonentry(web.repo, fctx))
1082 **webutil.commonentry(web.repo, fctx))
1079
1083
1080 @webcommand('archive')
1084 @webcommand('archive')
1081 def archive(web, req, tmpl):
1085 def archive(web, req, tmpl):
1082 """
1086 """
1083 /archive/{revision}.{format}[/{path}]
1087 /archive/{revision}.{format}[/{path}]
1084 -------------------------------------
1088 -------------------------------------
1085
1089
1086 Obtain an archive of repository content.
1090 Obtain an archive of repository content.
1087
1091
1088 The content and type of the archive is defined by a URL path parameter.
1092 The content and type of the archive is defined by a URL path parameter.
1089 ``format`` is the file extension of the archive type to be generated. e.g.
1093 ``format`` is the file extension of the archive type to be generated. e.g.
1090 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1094 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1091 server configuration.
1095 server configuration.
1092
1096
1093 The optional ``path`` URL parameter controls content to include in the
1097 The optional ``path`` URL parameter controls content to include in the
1094 archive. If omitted, every file in the specified revision is present in the
1098 archive. If omitted, every file in the specified revision is present in the
1095 archive. If included, only the specified file or contents of the specified
1099 archive. If included, only the specified file or contents of the specified
1096 directory will be included in the archive.
1100 directory will be included in the archive.
1097
1101
1098 No template is used for this handler. Raw, binary content is generated.
1102 No template is used for this handler. Raw, binary content is generated.
1099 """
1103 """
1100
1104
1101 type_ = req.form.get('type', [None])[0]
1105 type_ = req.form.get('type', [None])[0]
1102 allowed = web.configlist("web", "allow_archive")
1106 allowed = web.configlist("web", "allow_archive")
1103 key = req.form['node'][0]
1107 key = req.form['node'][0]
1104
1108
1105 if type_ not in web.archivespecs:
1109 if type_ not in web.archivespecs:
1106 msg = 'Unsupported archive type: %s' % type_
1110 msg = 'Unsupported archive type: %s' % type_
1107 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1111 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1108
1112
1109 if not ((type_ in allowed or
1113 if not ((type_ in allowed or
1110 web.configbool("web", "allow" + type_, False))):
1114 web.configbool("web", "allow" + type_, False))):
1111 msg = 'Archive type not allowed: %s' % type_
1115 msg = 'Archive type not allowed: %s' % type_
1112 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1116 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1113
1117
1114 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1118 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1115 cnode = web.repo.lookup(key)
1119 cnode = web.repo.lookup(key)
1116 arch_version = key
1120 arch_version = key
1117 if cnode == key or key == 'tip':
1121 if cnode == key or key == 'tip':
1118 arch_version = short(cnode)
1122 arch_version = short(cnode)
1119 name = "%s-%s" % (reponame, arch_version)
1123 name = "%s-%s" % (reponame, arch_version)
1120
1124
1121 ctx = webutil.changectx(web.repo, req)
1125 ctx = webutil.changectx(web.repo, req)
1122 pats = []
1126 pats = []
1123 match = scmutil.match(ctx, [])
1127 match = scmutil.match(ctx, [])
1124 file = req.form.get('file', None)
1128 file = req.form.get('file', None)
1125 if file:
1129 if file:
1126 pats = ['path:' + file[0]]
1130 pats = ['path:' + file[0]]
1127 match = scmutil.match(ctx, pats, default='path')
1131 match = scmutil.match(ctx, pats, default='path')
1128 if pats:
1132 if pats:
1129 files = [f for f in ctx.manifest().keys() if match(f)]
1133 files = [f for f in ctx.manifest().keys() if match(f)]
1130 if not files:
1134 if not files:
1131 raise ErrorResponse(HTTP_NOT_FOUND,
1135 raise ErrorResponse(HTTP_NOT_FOUND,
1132 'file(s) not found: %s' % file[0])
1136 'file(s) not found: %s' % file[0])
1133
1137
1134 mimetype, artype, extension, encoding = web.archivespecs[type_]
1138 mimetype, artype, extension, encoding = web.archivespecs[type_]
1135 headers = [
1139 headers = [
1136 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1140 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1137 ]
1141 ]
1138 if encoding:
1142 if encoding:
1139 headers.append(('Content-Encoding', encoding))
1143 headers.append(('Content-Encoding', encoding))
1140 req.headers.extend(headers)
1144 req.headers.extend(headers)
1141 req.respond(HTTP_OK, mimetype)
1145 req.respond(HTTP_OK, mimetype)
1142
1146
1143 archival.archive(web.repo, req, cnode, artype, prefix=name,
1147 archival.archive(web.repo, req, cnode, artype, prefix=name,
1144 matchfn=match,
1148 matchfn=match,
1145 subrepos=web.configbool("web", "archivesubrepos"))
1149 subrepos=web.configbool("web", "archivesubrepos"))
1146 return []
1150 return []
1147
1151
1148
1152
1149 @webcommand('static')
1153 @webcommand('static')
1150 def static(web, req, tmpl):
1154 def static(web, req, tmpl):
1151 fname = req.form['file'][0]
1155 fname = req.form['file'][0]
1152 # a repo owner may set web.static in .hg/hgrc to get any file
1156 # a repo owner may set web.static in .hg/hgrc to get any file
1153 # readable by the user running the CGI script
1157 # readable by the user running the CGI script
1154 static = web.config("web", "static", None, untrusted=False)
1158 static = web.config("web", "static", None, untrusted=False)
1155 if not static:
1159 if not static:
1156 tp = web.templatepath or templater.templatepaths()
1160 tp = web.templatepath or templater.templatepaths()
1157 if isinstance(tp, str):
1161 if isinstance(tp, str):
1158 tp = [tp]
1162 tp = [tp]
1159 static = [os.path.join(p, 'static') for p in tp]
1163 static = [os.path.join(p, 'static') for p in tp]
1160 staticfile(static, fname, req)
1164 staticfile(static, fname, req)
1161 return []
1165 return []
1162
1166
1163 @webcommand('graph')
1167 @webcommand('graph')
1164 def graph(web, req, tmpl):
1168 def graph(web, req, tmpl):
1165 """
1169 """
1166 /graph[/{revision}]
1170 /graph[/{revision}]
1167 -------------------
1171 -------------------
1168
1172
1169 Show information about the graphical topology of the repository.
1173 Show information about the graphical topology of the repository.
1170
1174
1171 Information rendered by this handler can be used to create visual
1175 Information rendered by this handler can be used to create visual
1172 representations of repository topology.
1176 representations of repository topology.
1173
1177
1174 The ``revision`` URL parameter controls the starting changeset.
1178 The ``revision`` URL parameter controls the starting changeset.
1175
1179
1176 The ``revcount`` query string argument can define the number of changesets
1180 The ``revcount`` query string argument can define the number of changesets
1177 to show information for.
1181 to show information for.
1178
1182
1179 This handler will render the ``graph`` template.
1183 This handler will render the ``graph`` template.
1180 """
1184 """
1181
1185
1182 if 'node' in req.form:
1186 if 'node' in req.form:
1183 ctx = webutil.changectx(web.repo, req)
1187 ctx = webutil.changectx(web.repo, req)
1184 symrev = webutil.symrevorshortnode(req, ctx)
1188 symrev = webutil.symrevorshortnode(req, ctx)
1185 else:
1189 else:
1186 ctx = web.repo['tip']
1190 ctx = web.repo['tip']
1187 symrev = 'tip'
1191 symrev = 'tip'
1188 rev = ctx.rev()
1192 rev = ctx.rev()
1189
1193
1190 bg_height = 39
1194 bg_height = 39
1191 revcount = web.maxshortchanges
1195 revcount = web.maxshortchanges
1192 if 'revcount' in req.form:
1196 if 'revcount' in req.form:
1193 try:
1197 try:
1194 revcount = int(req.form.get('revcount', [revcount])[0])
1198 revcount = int(req.form.get('revcount', [revcount])[0])
1195 revcount = max(revcount, 1)
1199 revcount = max(revcount, 1)
1196 tmpl.defaults['sessionvars']['revcount'] = revcount
1200 tmpl.defaults['sessionvars']['revcount'] = revcount
1197 except ValueError:
1201 except ValueError:
1198 pass
1202 pass
1199
1203
1200 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1204 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1201 lessvars['revcount'] = max(revcount / 2, 1)
1205 lessvars['revcount'] = max(revcount / 2, 1)
1202 morevars = copy.copy(tmpl.defaults['sessionvars'])
1206 morevars = copy.copy(tmpl.defaults['sessionvars'])
1203 morevars['revcount'] = revcount * 2
1207 morevars['revcount'] = revcount * 2
1204
1208
1205 count = len(web.repo)
1209 count = len(web.repo)
1206 pos = rev
1210 pos = rev
1207
1211
1208 uprev = min(max(0, count - 1), rev + revcount)
1212 uprev = min(max(0, count - 1), rev + revcount)
1209 downrev = max(0, rev - revcount)
1213 downrev = max(0, rev - revcount)
1210 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1214 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1211
1215
1212 tree = []
1216 tree = []
1213 if pos != -1:
1217 if pos != -1:
1214 allrevs = web.repo.changelog.revs(pos, 0)
1218 allrevs = web.repo.changelog.revs(pos, 0)
1215 revs = []
1219 revs = []
1216 for i in allrevs:
1220 for i in allrevs:
1217 revs.append(i)
1221 revs.append(i)
1218 if len(revs) >= revcount:
1222 if len(revs) >= revcount:
1219 break
1223 break
1220
1224
1221 # We have to feed a baseset to dagwalker as it is expecting smartset
1225 # We have to feed a baseset to dagwalker as it is expecting smartset
1222 # object. This does not have a big impact on hgweb performance itself
1226 # object. This does not have a big impact on hgweb performance itself
1223 # since hgweb graphing code is not itself lazy yet.
1227 # since hgweb graphing code is not itself lazy yet.
1224 dag = graphmod.dagwalker(web.repo, smartset.baseset(revs))
1228 dag = graphmod.dagwalker(web.repo, smartset.baseset(revs))
1225 # As we said one line above... not lazy.
1229 # As we said one line above... not lazy.
1226 tree = list(graphmod.colored(dag, web.repo))
1230 tree = list(graphmod.colored(dag, web.repo))
1227
1231
1228 def getcolumns(tree):
1232 def getcolumns(tree):
1229 cols = 0
1233 cols = 0
1230 for (id, type, ctx, vtx, edges) in tree:
1234 for (id, type, ctx, vtx, edges) in tree:
1231 if type != graphmod.CHANGESET:
1235 if type != graphmod.CHANGESET:
1232 continue
1236 continue
1233 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1237 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1234 max([edge[1] for edge in edges] or [0]))
1238 max([edge[1] for edge in edges] or [0]))
1235 return cols
1239 return cols
1236
1240
1237 def graphdata(usetuples, encodestr):
1241 def graphdata(usetuples, encodestr):
1238 data = []
1242 data = []
1239
1243
1240 row = 0
1244 row = 0
1241 for (id, type, ctx, vtx, edges) in tree:
1245 for (id, type, ctx, vtx, edges) in tree:
1242 if type != graphmod.CHANGESET:
1246 if type != graphmod.CHANGESET:
1243 continue
1247 continue
1244 node = str(ctx)
1248 node = str(ctx)
1245 age = encodestr(templatefilters.age(ctx.date()))
1249 age = encodestr(templatefilters.age(ctx.date()))
1246 desc = templatefilters.firstline(encodestr(ctx.description()))
1250 desc = templatefilters.firstline(encodestr(ctx.description()))
1247 desc = cgi.escape(templatefilters.nonempty(desc))
1251 desc = cgi.escape(templatefilters.nonempty(desc))
1248 user = cgi.escape(templatefilters.person(encodestr(ctx.user())))
1252 user = cgi.escape(templatefilters.person(encodestr(ctx.user())))
1249 branch = cgi.escape(encodestr(ctx.branch()))
1253 branch = cgi.escape(encodestr(ctx.branch()))
1250 try:
1254 try:
1251 branchnode = web.repo.branchtip(branch)
1255 branchnode = web.repo.branchtip(branch)
1252 except error.RepoLookupError:
1256 except error.RepoLookupError:
1253 branchnode = None
1257 branchnode = None
1254 branch = branch, branchnode == ctx.node()
1258 branch = branch, branchnode == ctx.node()
1255
1259
1256 if usetuples:
1260 if usetuples:
1257 data.append((node, vtx, edges, desc, user, age, branch,
1261 data.append((node, vtx, edges, desc, user, age, branch,
1258 [cgi.escape(encodestr(x)) for x in ctx.tags()],
1262 [cgi.escape(encodestr(x)) for x in ctx.tags()],
1259 [cgi.escape(encodestr(x))
1263 [cgi.escape(encodestr(x))
1260 for x in ctx.bookmarks()]))
1264 for x in ctx.bookmarks()]))
1261 else:
1265 else:
1262 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1266 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1263 'color': (edge[2] - 1) % 6 + 1,
1267 'color': (edge[2] - 1) % 6 + 1,
1264 'width': edge[3], 'bcolor': edge[4]}
1268 'width': edge[3], 'bcolor': edge[4]}
1265 for edge in edges]
1269 for edge in edges]
1266
1270
1267 data.append(
1271 data.append(
1268 {'node': node,
1272 {'node': node,
1269 'col': vtx[0],
1273 'col': vtx[0],
1270 'color': (vtx[1] - 1) % 6 + 1,
1274 'color': (vtx[1] - 1) % 6 + 1,
1271 'edges': edgedata,
1275 'edges': edgedata,
1272 'row': row,
1276 'row': row,
1273 'nextrow': row + 1,
1277 'nextrow': row + 1,
1274 'desc': desc,
1278 'desc': desc,
1275 'user': user,
1279 'user': user,
1276 'age': age,
1280 'age': age,
1277 'bookmarks': webutil.nodebookmarksdict(
1281 'bookmarks': webutil.nodebookmarksdict(
1278 web.repo, ctx.node()),
1282 web.repo, ctx.node()),
1279 'branches': webutil.nodebranchdict(web.repo, ctx),
1283 'branches': webutil.nodebranchdict(web.repo, ctx),
1280 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1284 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1281 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1285 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1282
1286
1283 row += 1
1287 row += 1
1284
1288
1285 return data
1289 return data
1286
1290
1287 cols = getcolumns(tree)
1291 cols = getcolumns(tree)
1288 rows = len(tree)
1292 rows = len(tree)
1289 canvasheight = (rows + 1) * bg_height - 27
1293 canvasheight = (rows + 1) * bg_height - 27
1290
1294
1291 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount,
1295 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount,
1292 uprev=uprev,
1296 uprev=uprev,
1293 lessvars=lessvars, morevars=morevars, downrev=downrev,
1297 lessvars=lessvars, morevars=morevars, downrev=downrev,
1294 cols=cols, rows=rows,
1298 cols=cols, rows=rows,
1295 canvaswidth=(cols + 1) * bg_height,
1299 canvaswidth=(cols + 1) * bg_height,
1296 truecanvasheight=rows * bg_height,
1300 truecanvasheight=rows * bg_height,
1297 canvasheight=canvasheight, bg_height=bg_height,
1301 canvasheight=canvasheight, bg_height=bg_height,
1298 # {jsdata} will be passed to |json, so it must be in utf-8
1302 # {jsdata} will be passed to |json, so it must be in utf-8
1299 jsdata=lambda **x: graphdata(True, encoding.fromlocal),
1303 jsdata=lambda **x: graphdata(True, encoding.fromlocal),
1300 nodes=lambda **x: graphdata(False, str),
1304 nodes=lambda **x: graphdata(False, str),
1301 node=ctx.hex(), changenav=changenav)
1305 node=ctx.hex(), changenav=changenav)
1302
1306
1303 def _getdoc(e):
1307 def _getdoc(e):
1304 doc = e[0].__doc__
1308 doc = e[0].__doc__
1305 if doc:
1309 if doc:
1306 doc = _(doc).partition('\n')[0]
1310 doc = _(doc).partition('\n')[0]
1307 else:
1311 else:
1308 doc = _('(no help text available)')
1312 doc = _('(no help text available)')
1309 return doc
1313 return doc
1310
1314
1311 @webcommand('help')
1315 @webcommand('help')
1312 def help(web, req, tmpl):
1316 def help(web, req, tmpl):
1313 """
1317 """
1314 /help[/{topic}]
1318 /help[/{topic}]
1315 ---------------
1319 ---------------
1316
1320
1317 Render help documentation.
1321 Render help documentation.
1318
1322
1319 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1323 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1320 is defined, that help topic will be rendered. If not, an index of
1324 is defined, that help topic will be rendered. If not, an index of
1321 available help topics will be rendered.
1325 available help topics will be rendered.
1322
1326
1323 The ``help`` template will be rendered when requesting help for a topic.
1327 The ``help`` template will be rendered when requesting help for a topic.
1324 ``helptopics`` will be rendered for the index of help topics.
1328 ``helptopics`` will be rendered for the index of help topics.
1325 """
1329 """
1326 from .. import commands, help as helpmod # avoid cycle
1330 from .. import commands, help as helpmod # avoid cycle
1327
1331
1328 topicname = req.form.get('node', [None])[0]
1332 topicname = req.form.get('node', [None])[0]
1329 if not topicname:
1333 if not topicname:
1330 def topics(**map):
1334 def topics(**map):
1331 for entries, summary, _doc in helpmod.helptable:
1335 for entries, summary, _doc in helpmod.helptable:
1332 yield {'topic': entries[0], 'summary': summary}
1336 yield {'topic': entries[0], 'summary': summary}
1333
1337
1334 early, other = [], []
1338 early, other = [], []
1335 primary = lambda s: s.partition('|')[0]
1339 primary = lambda s: s.partition('|')[0]
1336 for c, e in commands.table.iteritems():
1340 for c, e in commands.table.iteritems():
1337 doc = _getdoc(e)
1341 doc = _getdoc(e)
1338 if 'DEPRECATED' in doc or c.startswith('debug'):
1342 if 'DEPRECATED' in doc or c.startswith('debug'):
1339 continue
1343 continue
1340 cmd = primary(c)
1344 cmd = primary(c)
1341 if cmd.startswith('^'):
1345 if cmd.startswith('^'):
1342 early.append((cmd[1:], doc))
1346 early.append((cmd[1:], doc))
1343 else:
1347 else:
1344 other.append((cmd, doc))
1348 other.append((cmd, doc))
1345
1349
1346 early.sort()
1350 early.sort()
1347 other.sort()
1351 other.sort()
1348
1352
1349 def earlycommands(**map):
1353 def earlycommands(**map):
1350 for c, doc in early:
1354 for c, doc in early:
1351 yield {'topic': c, 'summary': doc}
1355 yield {'topic': c, 'summary': doc}
1352
1356
1353 def othercommands(**map):
1357 def othercommands(**map):
1354 for c, doc in other:
1358 for c, doc in other:
1355 yield {'topic': c, 'summary': doc}
1359 yield {'topic': c, 'summary': doc}
1356
1360
1357 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1361 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1358 othercommands=othercommands, title='Index')
1362 othercommands=othercommands, title='Index')
1359
1363
1360 # Render an index of sub-topics.
1364 # Render an index of sub-topics.
1361 if topicname in helpmod.subtopics:
1365 if topicname in helpmod.subtopics:
1362 topics = []
1366 topics = []
1363 for entries, summary, _doc in helpmod.subtopics[topicname]:
1367 for entries, summary, _doc in helpmod.subtopics[topicname]:
1364 topics.append({
1368 topics.append({
1365 'topic': '%s.%s' % (topicname, entries[0]),
1369 'topic': '%s.%s' % (topicname, entries[0]),
1366 'basename': entries[0],
1370 'basename': entries[0],
1367 'summary': summary,
1371 'summary': summary,
1368 })
1372 })
1369
1373
1370 return tmpl('helptopics', topics=topics, title=topicname,
1374 return tmpl('helptopics', topics=topics, title=topicname,
1371 subindex=True)
1375 subindex=True)
1372
1376
1373 u = webutil.wsgiui.load()
1377 u = webutil.wsgiui.load()
1374 u.verbose = True
1378 u.verbose = True
1375
1379
1376 # Render a page from a sub-topic.
1380 # Render a page from a sub-topic.
1377 if '.' in topicname:
1381 if '.' in topicname:
1378 # TODO implement support for rendering sections, like
1382 # TODO implement support for rendering sections, like
1379 # `hg help` works.
1383 # `hg help` works.
1380 topic, subtopic = topicname.split('.', 1)
1384 topic, subtopic = topicname.split('.', 1)
1381 if topic not in helpmod.subtopics:
1385 if topic not in helpmod.subtopics:
1382 raise ErrorResponse(HTTP_NOT_FOUND)
1386 raise ErrorResponse(HTTP_NOT_FOUND)
1383 else:
1387 else:
1384 topic = topicname
1388 topic = topicname
1385 subtopic = None
1389 subtopic = None
1386
1390
1387 try:
1391 try:
1388 doc = helpmod.help_(u, commands, topic, subtopic=subtopic)
1392 doc = helpmod.help_(u, commands, topic, subtopic=subtopic)
1389 except error.UnknownCommand:
1393 except error.UnknownCommand:
1390 raise ErrorResponse(HTTP_NOT_FOUND)
1394 raise ErrorResponse(HTTP_NOT_FOUND)
1391 return tmpl('help', topic=topicname, doc=doc)
1395 return tmpl('help', topic=topicname, doc=doc)
1392
1396
1393 # tell hggettext to extract docstrings from these functions:
1397 # tell hggettext to extract docstrings from these functions:
1394 i18nfunctions = commands.values()
1398 i18nfunctions = commands.values()
@@ -1,78 +1,85 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
2 <title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
3 <link rel="alternate" type="application/atom+xml"
3 <link rel="alternate" type="application/atom+xml"
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
4 href="{url|urlescape}atom-log" title="Atom feed for {repo|escape}"/>
5 <link rel="alternate" type="application/rss+xml"
5 <link rel="alternate" type="application/rss+xml"
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
6 href="{url|urlescape}rss-log" title="RSS feed for {repo|escape}"/>
7 </head>
7 </head>
8 <body>
8 <body>
9
9
10 <div class="page_header">
10 <div class="page_header">
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
11 <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate
12 <a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate
13 </div>
13 </div>
14
14
15 <div class="page_nav">
15 <div class="page_nav">
16 <div>
16 <div>
17 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
17 <a href="{url|urlescape}summary{sessionvars%urlparameter}">summary</a> |
18 <a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a> |
18 <a href="{url|urlescape}shortlog{sessionvars%urlparameter}">shortlog</a> |
19 <a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a> |
19 <a href="{url|urlescape}log{sessionvars%urlparameter}">changelog</a> |
20 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
20 <a href="{url|urlescape}graph{sessionvars%urlparameter}">graph</a> |
21 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
21 <a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a> |
22 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
22 <a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
23 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
23 <a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a> |
24 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">files</a> |
24 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">files</a> |
25 <a href="{url|urlescape}rev/{symrev}{sessionvars%urlparameter}">changeset</a> |
25 <a href="{url|urlescape}rev/{symrev}{sessionvars%urlparameter}">changeset</a> |
26 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
26 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
27 <a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
27 <a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
28 <a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
28 <a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
29 annotate |
29 annotate |
30 <a href="{url|urlescape}diff/{symrev}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
30 <a href="{url|urlescape}diff/{symrev}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
31 <a href="{url|urlescape}comparison/{symrev}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
31 <a href="{url|urlescape}comparison/{symrev}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
32 <a href="{url|urlescape}raw-file/{symrev}/{file|urlescape}">raw</a> |
32 <a href="{url|urlescape}raw-file/{symrev}/{file|urlescape}">raw</a> |
33 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
33 <a href="{url|urlescape}help{sessionvars%urlparameter}">help</a>
34 </div>
34 </div>
35 {searchform}
35 {searchform}
36 </div>
36 </div>
37
37
38 <div class="title">{file|escape}</div>
38 <div class="title">{file|escape}</div>
39
39
40 <div class="title_text">
40 <div class="title_text">
41 <table cellspacing="0">
41 <table cellspacing="0">
42 <tr>
42 <tr>
43 <td>author</td>
43 <td>author</td>
44 <td>{author|obfuscate}</td>
44 <td>{author|obfuscate}</td>
45 </tr>
45 </tr>
46 <tr>
46 <tr>
47 <td></td>
47 <td></td>
48 <td class="date age">{date|rfc822date}</td>
48 <td class="date age">{date|rfc822date}</td>
49 </tr>
49 </tr>
50 {branch%filerevbranch}
50 {branch%filerevbranch}
51 <tr>
51 <tr>
52 <td>changeset {rev}</td>
52 <td>changeset {rev}</td>
53 <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
53 <td style="font-family:monospace"><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
54 </tr>
54 </tr>
55 {parent%fileannotateparent}
55 {parent%fileannotateparent}
56 {child%fileannotatechild}
56 {child%fileannotatechild}
57 <tr>
57 <tr>
58 <td>permissions</td>
58 <td>permissions</td>
59 <td style="font-family:monospace">{permissions|permissions}</td>
59 <td style="font-family:monospace">{permissions|permissions}</td>
60 </tr>
60 </tr>
61 </table>
61 </table>
62 </div>
62 </div>
63
63
64 <div class="page_path description">{desc|strip|escape|websub|nonempty}</div>
64 <div class="page_path description">{desc|strip|escape|websub|nonempty}</div>
65
66 {diffoptsform}
67
68 <script type="text/javascript"{if(nonce, ' nonce="{nonce}"')}>
69 renderDiffOptsForm();
70 </script>
71
65 <div class="page_body">
72 <div class="page_body">
66 <table>
73 <table>
67 <tbody class="sourcelines"
74 <tbody class="sourcelines"
68 data-logurl="{url|urlescape}log/{symrev}/{file|urlescape}"
75 data-logurl="{url|urlescape}log/{symrev}/{file|urlescape}"
69 data-selectabletag="TR"
76 data-selectabletag="TR"
70 data-ishead="{ishead}">
77 data-ishead="{ishead}">
71 {annotate%annotateline}
78 {annotate%annotateline}
72 </tbody>
79 </tbody>
73 </table>
80 </table>
74 </div>
81 </div>
75
82
76 <script type="text/javascript" src="{staticurl|urlescape}followlines.js"></script>
83 <script type="text/javascript" src="{staticurl|urlescape}followlines.js"></script>
77
84
78 {footer}
85 {footer}
@@ -1,336 +1,352 b''
1 default = 'summary'
1 default = 'summary'
2 mimetype = 'text/html; charset={encoding}'
2 mimetype = 'text/html; charset={encoding}'
3 header = header.tmpl
3 header = header.tmpl
4 footer = footer.tmpl
4 footer = footer.tmpl
5 search = search.tmpl
5 search = search.tmpl
6 changelog = changelog.tmpl
6 changelog = changelog.tmpl
7 summary = summary.tmpl
7 summary = summary.tmpl
8 error = error.tmpl
8 error = error.tmpl
9 notfound = notfound.tmpl
9 notfound = notfound.tmpl
10
10
11 help = help.tmpl
11 help = help.tmpl
12 helptopics = helptopics.tmpl
12 helptopics = helptopics.tmpl
13
13
14 helpentry = '
14 helpentry = '
15 <tr><td>
15 <tr><td>
16 <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">
16 <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">
17 {if(basename, '{basename|escape}', '{topic|escape}')}
17 {if(basename, '{basename|escape}', '{topic|escape}')}
18 </a>
18 </a>
19 </td><td>
19 </td><td>
20 {summary|escape}
20 {summary|escape}
21 </td></tr>'
21 </td></tr>'
22
22
23 naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
23 naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
24 navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
24 navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
25 navgraphentry = '<a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
25 navgraphentry = '<a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
26 filenaventry = '<a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
26 filenaventry = '<a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
27 filedifflink = '<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
27 filedifflink = '<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
28 filenodelink = '
28 filenodelink = '
29 <tr class="parity{parity}">
29 <tr class="parity{parity}">
30 <td><a class="list" href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
30 <td><a class="list" href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
31 <td></td>
31 <td></td>
32 <td class="link">
32 <td class="link">
33 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
33 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
34 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
34 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
35 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
35 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
36 <a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
36 <a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
37 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
37 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
38 </td>
38 </td>
39 </tr>'
39 </tr>'
40 filenolink = '
40 filenolink = '
41 <tr class="parity{parity}">
41 <tr class="parity{parity}">
42 <td><a class="list" href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
42 <td><a class="list" href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
43 <td></td>
43 <td></td>
44 <td class="link">
44 <td class="link">
45 file |
45 file |
46 annotate |
46 annotate |
47 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
47 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
48 <a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
48 <a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
49 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
49 <a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
50 </td>
50 </td>
51 </tr>'
51 </tr>'
52
52
53 nav = '{before%naventry} {after%naventry}'
53 nav = '{before%naventry} {after%naventry}'
54 navshort = '{before%navshortentry}{after%navshortentry}'
54 navshort = '{before%navshortentry}{after%navshortentry}'
55 navgraph = '{before%navgraphentry}{after%navgraphentry}'
55 navgraph = '{before%navgraphentry}{after%navgraphentry}'
56 filenav = '{before%filenaventry}{after%filenaventry}'
56 filenav = '{before%filenaventry}{after%filenaventry}'
57
57
58 fileellipses = '...'
58 fileellipses = '...'
59 changelogentry = changelogentry.tmpl
59 changelogentry = changelogentry.tmpl
60 searchentry = changelogentry.tmpl
60 searchentry = changelogentry.tmpl
61 changeset = changeset.tmpl
61 changeset = changeset.tmpl
62 manifest = manifest.tmpl
62 manifest = manifest.tmpl
63 direntry = '
63 direntry = '
64 <tr class="parity{parity}">
64 <tr class="parity{parity}">
65 <td style="font-family:monospace">drwxr-xr-x</td>
65 <td style="font-family:monospace">drwxr-xr-x</td>
66 <td style="font-family:monospace"></td>
66 <td style="font-family:monospace"></td>
67 <td style="font-family:monospace"></td>
67 <td style="font-family:monospace"></td>
68 <td>
68 <td>
69 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
69 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
70 <a href="{url|urlescape}file/{symrev}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">{emptydirs|escape}</a>
70 <a href="{url|urlescape}file/{symrev}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">{emptydirs|escape}</a>
71 </td>
71 </td>
72 <td class="link">
72 <td class="link">
73 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">files</a>
73 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">files</a>
74 </td>
74 </td>
75 </tr>'
75 </tr>'
76 fileentry = '
76 fileentry = '
77 <tr class="parity{parity}">
77 <tr class="parity{parity}">
78 <td style="font-family:monospace">{permissions|permissions}</td>
78 <td style="font-family:monospace">{permissions|permissions}</td>
79 <td style="font-family:monospace" align=right>{date|isodate}</td>
79 <td style="font-family:monospace" align=right>{date|isodate}</td>
80 <td style="font-family:monospace" align=right>{size}</td>
80 <td style="font-family:monospace" align=right>{size}</td>
81 <td class="list">
81 <td class="list">
82 <a class="list" href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
82 <a class="list" href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
83 </td>
83 </td>
84 <td class="link">
84 <td class="link">
85 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
85 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
86 <a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
86 <a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
87 <a href="{url|urlescape}annotate/{symrev}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
87 <a href="{url|urlescape}annotate/{symrev}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
88 </td>
88 </td>
89 </tr>'
89 </tr>'
90 filerevision = filerevision.tmpl
90 filerevision = filerevision.tmpl
91 fileannotate = fileannotate.tmpl
91 fileannotate = fileannotate.tmpl
92 filediff = filediff.tmpl
92 filediff = filediff.tmpl
93 filecomparison = filecomparison.tmpl
93 filecomparison = filecomparison.tmpl
94 filelog = filelog.tmpl
94 filelog = filelog.tmpl
95 fileline = '
95 fileline = '
96 <a href="#{lineid}"></a><span id="{lineid}">{strip(line|escape, '\r\n')}</span>'
96 <a href="#{lineid}"></a><span id="{lineid}">{strip(line|escape, '\r\n')}</span>'
97 annotateline = '
97 annotateline = '
98 <tr id="{lineid}" style="font-family:monospace" class="parity{parity}{ifeq(node, originalnode, ' thisrev')}">
98 <tr id="{lineid}" style="font-family:monospace" class="parity{parity}{ifeq(node, originalnode, ' thisrev')}">
99 <td class="annotate linenr parity{blockparity}" style="text-align: right;">
99 <td class="annotate linenr parity{blockparity}" style="text-align: right;">
100 {if(blockhead,
100 {if(blockhead,
101 '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
101 '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
102 {rev}
102 {rev}
103 </a>')}
103 </a>')}
104 <div class="annotate-info">
104 <div class="annotate-info">
105 <div>
105 <div>
106 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
106 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
107 {node|short}</a>
107 {node|short}</a>
108 {desc|escape|firstline}
108 {desc|escape|firstline}
109 </div>
109 </div>
110 <div><em>{author|obfuscate}</em></div>
110 <div><em>{author|obfuscate}</em></div>
111 <div>parents: {parents%annotateparent}</div>
111 <div>parents: {parents%annotateparent}</div>
112 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>
112 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>
113 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
113 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
114 </div>
114 </div>
115 </td>
115 </td>
116 <td class="followlines-btn-parent"><pre><a class="linenr" href="#{lineid}">{linenumber}</a></pre></td>
116 <td class="followlines-btn-parent"><pre><a class="linenr" href="#{lineid}">{linenumber}</a></pre></td>
117 <td><pre>{line|escape}</pre></td>
117 <td><pre>{line|escape}</pre></td>
118 </tr>'
118 </tr>'
119 annotateparent = '
119 annotateparent = '
120 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rev}</a>'
120 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rev}</a>'
121 difflineplus = '
121 difflineplus = '
122 <a href="#{lineid}"></a><span id="{lineid}" class="difflineplus">{strip(line|escape, '\r\n')}</span>'
122 <a href="#{lineid}"></a><span id="{lineid}" class="difflineplus">{strip(line|escape, '\r\n')}</span>'
123 difflineminus = '
123 difflineminus = '
124 <a href="#{lineid}"></a><span id="{lineid}" class="difflineminus">{strip(line|escape, '\r\n')}</span>'
124 <a href="#{lineid}"></a><span id="{lineid}" class="difflineminus">{strip(line|escape, '\r\n')}</span>'
125 difflineat = '
125 difflineat = '
126 <a href="#{lineid}"></a><span id="{lineid}" class="difflineat">{strip(line|escape, '\r\n')}</span>'
126 <a href="#{lineid}"></a><span id="{lineid}" class="difflineat">{strip(line|escape, '\r\n')}</span>'
127 diffline = '
127 diffline = '
128 <a href="#{lineid}"></a><span id="{lineid}">{strip(line|escape, '\r\n')}</span>'
128 <a href="#{lineid}"></a><span id="{lineid}">{strip(line|escape, '\r\n')}</span>'
129
129
130 comparisonblock ='
130 comparisonblock ='
131 <tbody class="block">
131 <tbody class="block">
132 {lines}
132 {lines}
133 </tbody>'
133 </tbody>'
134 comparisonline = '
134 comparisonline = '
135 <tr id="{lineid}" style="font-family:monospace">
135 <tr id="{lineid}" style="font-family:monospace">
136 <td class="{type}"><pre><a class="linenr" href="#{lineid}">{leftlinenumber}</a> {leftline|escape}</pre></td>
136 <td class="{type}"><pre><a class="linenr" href="#{lineid}">{leftlinenumber}</a> {leftline|escape}</pre></td>
137 <td class="{type}"><pre><a class="linenr" href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</pre></td>
137 <td class="{type}"><pre><a class="linenr" href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</pre></td>
138 </tr>'
138 </tr>'
139
139
140 changesetlink = '<a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>'
140 changesetlink = '<a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>'
141 changesetbranch = '<tr><td>branch</td><td>{name|escape}</td></tr>'
141 changesetbranch = '<tr><td>branch</td><td>{name|escape}</td></tr>'
142 changesetparent = '
142 changesetparent = '
143 <tr>
143 <tr>
144 <td>parent {rev}</td>
144 <td>parent {rev}</td>
145 <td style="font-family:monospace">
145 <td style="font-family:monospace">
146 {changesetlink}
146 {changesetlink}
147 </td>
147 </td>
148 </tr>'
148 </tr>'
149 changesetparentdiff = '
149 changesetparentdiff = '
150 <tr>
150 <tr>
151 <td>parent {rev}</td>
151 <td>parent {rev}</td>
152 <td style="font-family:monospace">
152 <td style="font-family:monospace">
153 {changesetlink} {ifeq(node, basenode, '(current diff)', '({difffrom})')}
153 {changesetlink} {ifeq(node, basenode, '(current diff)', '({difffrom})')}
154 </td>
154 </td>
155 </tr>'
155 </tr>'
156 difffrom = '<a href="{url|urlescape}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">diff</a>'
156 difffrom = '<a href="{url|urlescape}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">diff</a>'
157 filerevbranch = '<tr><td>branch</td><td>{name|escape}</td></tr>'
157 filerevbranch = '<tr><td>branch</td><td>{name|escape}</td></tr>'
158 filerevparent = '
158 filerevparent = '
159 <tr>
159 <tr>
160 <td>parent {rev}</td>
160 <td>parent {rev}</td>
161 <td style="font-family:monospace">
161 <td style="font-family:monospace">
162 <a class="list" href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
162 <a class="list" href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
163 {rename%filerename}{node|short}
163 {rename%filerename}{node|short}
164 </a>
164 </a>
165 </td>
165 </td>
166 </tr>'
166 </tr>'
167 filerename = '{file|escape}@'
167 filerename = '{file|escape}@'
168 filelogrename = '| <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">base</a>'
168 filelogrename = '| <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">base</a>'
169 fileannotateparent = '
169 fileannotateparent = '
170 <tr>
170 <tr>
171 <td>parent {rev}</td>
171 <td>parent {rev}</td>
172 <td style="font-family:monospace">
172 <td style="font-family:monospace">
173 <a class="list" href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
173 <a class="list" href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
174 {rename%filerename}{node|short}
174 {rename%filerename}{node|short}
175 </a>
175 </a>
176 </td>
176 </td>
177 </tr>'
177 </tr>'
178 changesetchild = '
178 changesetchild = '
179 <tr>
179 <tr>
180 <td>child {rev}</td>
180 <td>child {rev}</td>
181 <td style="font-family:monospace">
181 <td style="font-family:monospace">
182 <a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
182 <a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
183 </td>
183 </td>
184 </tr>'
184 </tr>'
185 filerevchild = '
185 filerevchild = '
186 <tr>
186 <tr>
187 <td>child {rev}</td>
187 <td>child {rev}</td>
188 <td style="font-family:monospace">
188 <td style="font-family:monospace">
189 <a class="list" href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
189 <a class="list" href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
190 </tr>'
190 </tr>'
191 fileannotatechild = '
191 fileannotatechild = '
192 <tr>
192 <tr>
193 <td>child {rev}</td>
193 <td>child {rev}</td>
194 <td style="font-family:monospace">
194 <td style="font-family:monospace">
195 <a class="list" href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
195 <a class="list" href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
196 </tr>'
196 </tr>'
197 tags = tags.tmpl
197 tags = tags.tmpl
198 tagentry = '
198 tagentry = '
199 <tr class="parity{parity}">
199 <tr class="parity{parity}">
200 <td class="age"><i class="age">{date|rfc822date}</i></td>
200 <td class="age"><i class="age">{date|rfc822date}</i></td>
201 <td><a class="list" href="{url|urlescape}rev/{tag|revescape}{sessionvars%urlparameter}"><b>{tag|escape}</b></a></td>
201 <td><a class="list" href="{url|urlescape}rev/{tag|revescape}{sessionvars%urlparameter}"><b>{tag|escape}</b></a></td>
202 <td class="link">
202 <td class="link">
203 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
203 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
204 <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
204 <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
205 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
205 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
206 </td>
206 </td>
207 </tr>'
207 </tr>'
208 bookmarks = bookmarks.tmpl
208 bookmarks = bookmarks.tmpl
209 bookmarkentry = '
209 bookmarkentry = '
210 <tr class="parity{parity}">
210 <tr class="parity{parity}">
211 <td class="age"><i class="age">{date|rfc822date}</i></td>
211 <td class="age"><i class="age">{date|rfc822date}</i></td>
212 <td><a class="list" href="{url|urlescape}rev/{bookmark|revescape}{sessionvars%urlparameter}"><b>{bookmark|escape}</b></a></td>
212 <td><a class="list" href="{url|urlescape}rev/{bookmark|revescape}{sessionvars%urlparameter}"><b>{bookmark|escape}</b></a></td>
213 <td class="link">
213 <td class="link">
214 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
214 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
215 <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
215 <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
216 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
216 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
217 </td>
217 </td>
218 </tr>'
218 </tr>'
219 branches = branches.tmpl
219 branches = branches.tmpl
220 branchentry = '
220 branchentry = '
221 <tr class="parity{parity}">
221 <tr class="parity{parity}">
222 <td class="age"><i class="age">{date|rfc822date}</i></td>
222 <td class="age"><i class="age">{date|rfc822date}</i></td>
223 <td class="{status}"><a class="list" href="{url|urlescape}shortlog/{branch|revescape}{sessionvars%urlparameter}"><b>{branch|escape}</b></a></td>
223 <td class="{status}"><a class="list" href="{url|urlescape}shortlog/{branch|revescape}{sessionvars%urlparameter}"><b>{branch|escape}</b></a></td>
224 <td class="link">
224 <td class="link">
225 <a href="{url|urlescape}changeset/{node|short}{sessionvars%urlparameter}">changeset</a> |
225 <a href="{url|urlescape}changeset/{node|short}{sessionvars%urlparameter}">changeset</a> |
226 <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
226 <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
227 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
227 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
228 </td>
228 </td>
229 </tr>'
229 </tr>'
230 diffblock = '<div class="diffblock"><pre class="sourcelines">{lines}</pre></div>'
230 diffblock = '<div class="diffblock"><pre class="sourcelines">{lines}</pre></div>'
231 filediffparent = '
231 filediffparent = '
232 <tr>
232 <tr>
233 <td>parent {rev}</td>
233 <td>parent {rev}</td>
234 <td style="font-family:monospace">
234 <td style="font-family:monospace">
235 <a class="list" href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
235 <a class="list" href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
236 {node|short}
236 {node|short}
237 </a>
237 </a>
238 </td>
238 </td>
239 </tr>'
239 </tr>'
240 filecompparent = '
240 filecompparent = '
241 <tr>
241 <tr>
242 <td>parent {rev}</td>
242 <td>parent {rev}</td>
243 <td style="font-family:monospace">
243 <td style="font-family:monospace">
244 <a class="list" href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
244 <a class="list" href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
245 {node|short}
245 {node|short}
246 </a>
246 </a>
247 </td>
247 </td>
248 </tr>'
248 </tr>'
249 filediffchild = '
249 filediffchild = '
250 <tr>
250 <tr>
251 <td>child {rev}</td>
251 <td>child {rev}</td>
252 <td style="font-family:monospace">
252 <td style="font-family:monospace">
253 <a class="list" href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
253 <a class="list" href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
254 </td>
254 </td>
255 </tr>'
255 </tr>'
256 filecompchild = '
256 filecompchild = '
257 <tr>
257 <tr>
258 <td>child {rev}</td>
258 <td>child {rev}</td>
259 <td style="font-family:monospace">
259 <td style="font-family:monospace">
260 <a class="list" href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
260 <a class="list" href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
261 </td>
261 </td>
262 </tr>'
262 </tr>'
263 shortlog = shortlog.tmpl
263 shortlog = shortlog.tmpl
264 graph = graph.tmpl
264 graph = graph.tmpl
265 tagtag = '<span class="tagtag" title="{name|escape}">{name|escape}</span> '
265 tagtag = '<span class="tagtag" title="{name|escape}">{name|escape}</span> '
266 branchtag = '<span class="branchtag" title="{name|escape}">{name|escape}</span> '
266 branchtag = '<span class="branchtag" title="{name|escape}">{name|escape}</span> '
267 inbranchtag = '<span class="inbranchtag" title="{name|escape}">{name|escape}</span> '
267 inbranchtag = '<span class="inbranchtag" title="{name|escape}">{name|escape}</span> '
268 bookmarktag = '<span class="bookmarktag" title="{name|escape}">{name|escape}</span> '
268 bookmarktag = '<span class="bookmarktag" title="{name|escape}">{name|escape}</span> '
269 shortlogentry = '
269 shortlogentry = '
270 <tr class="parity{parity}">
270 <tr class="parity{parity}">
271 <td class="age"><i class="age">{date|rfc822date}</i></td>
271 <td class="age"><i class="age">{date|rfc822date}</i></td>
272 <td><i>{author|person}</i></td>
272 <td><i>{author|person}</i></td>
273 <td>
273 <td>
274 <a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
274 <a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
275 <b>{desc|strip|firstline|escape|nonempty}</b>
275 <b>{desc|strip|firstline|escape|nonempty}</b>
276 <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
276 <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
277 </a>
277 </a>
278 </td>
278 </td>
279 <td class="link" nowrap>
279 <td class="link" nowrap>
280 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
280 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
281 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
281 <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a>
282 </td>
282 </td>
283 </tr>'
283 </tr>'
284 filelogentry = '
284 filelogentry = '
285 <tr class="parity{if(patch, '1', '{parity}')}">
285 <tr class="parity{if(patch, '1', '{parity}')}">
286 <td class="age"><i class="age">{date|rfc822date}</i></td>
286 <td class="age"><i class="age">{date|rfc822date}</i></td>
287 <td><i>{author|person}</i></td>
287 <td><i>{author|person}</i></td>
288 <td>
288 <td>
289 <a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
289 <a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
290 <b>{desc|strip|firstline|escape|nonempty}</b>
290 <b>{desc|strip|firstline|escape|nonempty}</b>
291 <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
291 <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
292 </a>
292 </a>
293 </td>
293 </td>
294 <td class="link">
294 <td class="link">
295 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
295 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
296 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
296 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
297 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
297 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
298 {rename%filelogrename}
298 {rename%filelogrename}
299 </td>
299 </td>
300 </tr>
300 </tr>
301 {if(patch, '<tr><td colspan="4">{diff}</td></tr>')}'
301 {if(patch, '<tr><td colspan="4">{diff}</td></tr>')}'
302 archiveentry = ' | <a href="{url|urlescape}archive/{symrev}{extension}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a> '
302 archiveentry = ' | <a href="{url|urlescape}archive/{symrev}{extension}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a> '
303 indexentry = '
303 indexentry = '
304 <tr class="parity{parity}">
304 <tr class="parity{parity}">
305 <td>
305 <td>
306 <a class="list" href="{url|urlescape}{sessionvars%urlparameter}">
306 <a class="list" href="{url|urlescape}{sessionvars%urlparameter}">
307 <b>{name|escape}</b>
307 <b>{name|escape}</b>
308 </a>
308 </a>
309 </td>
309 </td>
310 <td>{description}</td>
310 <td>{description}</td>
311 <td>{contact|obfuscate}</td>
311 <td>{contact|obfuscate}</td>
312 <td class="age">{lastchange|rfc822date}</td>
312 <td class="age">{lastchange|rfc822date}</td>
313 <td class="indexlinks">{archives%indexarchiveentry}</td>
313 <td class="indexlinks">{archives%indexarchiveentry}</td>
314 <td>{if(isdirectory, '',
314 <td>{if(isdirectory, '',
315 '<div class="rss_logo">
315 '<div class="rss_logo">
316 <a href="{url|urlescape}rss-log">RSS</a> <a href="{url|urlescape}atom-log">Atom</a>
316 <a href="{url|urlescape}rss-log">RSS</a> <a href="{url|urlescape}atom-log">Atom</a>
317 </div>'
317 </div>'
318 )}
318 )}
319 </td>
319 </td>
320 </tr>\n'
320 </tr>\n'
321 indexarchiveentry = ' <a href="{url|urlescape}archive/{node|short}{extension}">{type|escape}</a> '
321 indexarchiveentry = ' <a href="{url|urlescape}archive/{node|short}{extension}">{type|escape}</a> '
322 index = index.tmpl
322 index = index.tmpl
323 urlparameter = '{separator}{name}={value|urlescape}'
323 urlparameter = '{separator}{name}={value|urlescape}'
324 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
324 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
325 breadcrumb = '&gt; <a href="{url|urlescape}">{name|escape}</a> '
325 breadcrumb = '&gt; <a href="{url|urlescape}">{name|escape}</a> '
326
326
327 searchform = '
327 searchform = '
328 <div class="search">
328 <div class="search">
329 <form id="searchform" action="{url|urlescape}log">
329 <form id="searchform" action="{url|urlescape}log">
330 {sessionvars%hiddenformentry}
330 {sessionvars%hiddenformentry}
331 <input name="rev" type="text" value="{query|escape}" size="40" />
331 <input name="rev" type="text" value="{query|escape}" size="40" />
332 <div id="hint">{searchhint}</div>
332 <div id="hint">{searchhint}</div>
333 </form>
333 </form>
334 </div>'
334 </div>'
335 searchhint = 'Find changesets by keywords (author, files, the commit message), revision
335 searchhint = 'Find changesets by keywords (author, files, the commit message), revision
336 number or hash, or <a href="{url|urlescape}help/revsets">revset expression</a>.'
336 number or hash, or <a href="{url|urlescape}help/revsets">revset expression</a>.'
337
338 diffoptsform = '
339 <form id="diffopts-form"
340 data-ignorews="{if(get(diffopts, 'ignorews'), '1', '0')}"
341 data-ignorewsamount="{if(get(diffopts, 'ignorewsamount'), '1', '0')}"
342 data-ignorewseol="{if(get(diffopts, 'ignorewseol'), '1', '0')}"
343 data-ignoreblanklines="{if(get(diffopts, 'ignoreblanklines'), '1', '0')}">
344 <span>Ignore whitespace changes - </span>
345 <span>Everywhere:</span>
346 <input id="ignorews-checkbox" type="checkbox" />
347 <span>Within whitespace:</span>
348 <input id="ignorewsamount-checkbox" type="checkbox" />
349 <span>At end of lines:</span>
350 <input id="ignorewseol-checkbox" type="checkbox" />
351 </form>
352 </div>'
@@ -1,89 +1,95 b''
1 {header}
1 {header}
2 <title>{repo|escape}: {file|escape} annotate</title>
2 <title>{repo|escape}: {file|escape} annotate</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
6 <div class="container">
6 <div class="container">
7 <div class="menu">
7 <div class="menu">
8 <div class="logo">
8 <div class="logo">
9 <a href="{logourl}">
9 <a href="{logourl}">
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
10 <img src="{staticurl|urlescape}{logoimg}" alt="mercurial" /></a>
11 </div>
11 </div>
12 <ul>
12 <ul>
13 <li><a href="{url|urlescape}shortlog/{symrev}{sessionvars%urlparameter}">log</a></li>
13 <li><a href="{url|urlescape}shortlog/{symrev}{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url|urlescape}graph/{symrev}{sessionvars%urlparameter}">graph</a></li>
14 <li><a href="{url|urlescape}graph/{symrev}{sessionvars%urlparameter}">graph</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
15 <li><a href="{url|urlescape}tags{sessionvars%urlparameter}">tags</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
16 <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
17 <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
18 </ul>
18 </ul>
19
19
20 <ul>
20 <ul>
21 <li><a href="{url|urlescape}rev/{symrev}{sessionvars%urlparameter}">changeset</a></li>
21 <li><a href="{url|urlescape}rev/{symrev}{sessionvars%urlparameter}">changeset</a></li>
22 <li><a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
22 <li><a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
23 </ul>
23 </ul>
24 <ul>
24 <ul>
25 <li><a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
25 <li><a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
26 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
26 <li><a href="{url|urlescape}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
27 <li><a href="{url|urlescape}diff/{symrev}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
27 <li><a href="{url|urlescape}diff/{symrev}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
28 <li><a href="{url|urlescape}comparison/{symrev}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
28 <li><a href="{url|urlescape}comparison/{symrev}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
29 <li class="active">annotate</li>
29 <li class="active">annotate</li>
30 <li><a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
30 <li><a href="{url|urlescape}log/{symrev}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
31 <li><a href="{url|urlescape}raw-file/{symrev}/{file|urlescape}">raw</a></li>
31 <li><a href="{url|urlescape}raw-file/{symrev}/{file|urlescape}">raw</a></li>
32 </ul>
32 </ul>
33 <ul>
33 <ul>
34 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
34 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
35 </ul>
35 </ul>
36 </div>
36 </div>
37
37
38 <div class="main">
38 <div class="main">
39 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
39 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
40 <h3>
40 <h3>
41 annotate {file|escape} @ {rev}:<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
41 annotate {file|escape} @ {rev}:<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
42 {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}
42 {branch%changelogbranchname}{tags%changelogtag}{bookmarks%changelogtag}
43 </h3>
43 </h3>
44
44
45 {searchform}
45 {searchform}
46
46
47 <div class="description">{desc|strip|escape|websub|nonempty}</div>
47 <div class="description">{desc|strip|escape|websub|nonempty}</div>
48
48
49 <table id="changesetEntry">
49 <table id="changesetEntry">
50 <tr>
50 <tr>
51 <th class="author">author</th>
51 <th class="author">author</th>
52 <td class="author">{author|obfuscate}</td>
52 <td class="author">{author|obfuscate}</td>
53 </tr>
53 </tr>
54 <tr>
54 <tr>
55 <th class="date">date</th>
55 <th class="date">date</th>
56 <td class="date age">{date|rfc822date}</td>
56 <td class="date age">{date|rfc822date}</td>
57 </tr>
57 </tr>
58 <tr>
58 <tr>
59 <th class="author">parents</th>
59 <th class="author">parents</th>
60 <td class="author">{parent%filerevparent}</td>
60 <td class="author">{parent%filerevparent}</td>
61 </tr>
61 </tr>
62 <tr>
62 <tr>
63 <th class="author">children</th>
63 <th class="author">children</th>
64 <td class="author">{child%filerevchild}</td>
64 <td class="author">{child%filerevchild}</td>
65 </tr>
65 </tr>
66 </table>
66 </table>
67
67
68 {diffoptsform}
69
70 <script type="text/javascript"{if(nonce, ' nonce="{nonce}"')}>
71 renderDiffOptsForm();
72 </script>
73
68 <div class="overflow">
74 <div class="overflow">
69 <table class="bigtable">
75 <table class="bigtable">
70 <thead>
76 <thead>
71 <tr>
77 <tr>
72 <th class="annotate">rev</th>
78 <th class="annotate">rev</th>
73 <th class="line">&nbsp;&nbsp;line source</th>
79 <th class="line">&nbsp;&nbsp;line source</th>
74 </tr>
80 </tr>
75 </thead>
81 </thead>
76 <tbody class="stripes2 sourcelines"
82 <tbody class="stripes2 sourcelines"
77 data-logurl="{url|urlescape}log/{symrev}/{file|urlescape}"
83 data-logurl="{url|urlescape}log/{symrev}/{file|urlescape}"
78 data-selectabletag="TR"
84 data-selectabletag="TR"
79 data-ishead="{ishead}">
85 data-ishead="{ishead}">
80 {annotate%annotateline}
86 {annotate%annotateline}
81 </tbody>
87 </tbody>
82 </table>
88 </table>
83 </div>
89 </div>
84 </div>
90 </div>
85 </div>
91 </div>
86
92
87 <script type="text/javascript" src="{staticurl|urlescape}followlines.js"></script>
93 <script type="text/javascript" src="{staticurl|urlescape}followlines.js"></script>
88
94
89 {footer}
95 {footer}
@@ -1,253 +1,268 b''
1 default = 'shortlog'
1 default = 'shortlog'
2
2
3 mimetype = 'text/html; charset={encoding}'
3 mimetype = 'text/html; charset={encoding}'
4 header = header.tmpl
4 header = header.tmpl
5 footer = footer.tmpl
5 footer = footer.tmpl
6 search = search.tmpl
6 search = search.tmpl
7
7
8 changelog = shortlog.tmpl
8 changelog = shortlog.tmpl
9 shortlog = shortlog.tmpl
9 shortlog = shortlog.tmpl
10 shortlogentry = shortlogentry.tmpl
10 shortlogentry = shortlogentry.tmpl
11 graph = graph.tmpl
11 graph = graph.tmpl
12 help = help.tmpl
12 help = help.tmpl
13 helptopics = helptopics.tmpl
13 helptopics = helptopics.tmpl
14
14
15 helpentry = '
15 helpentry = '
16 <tr><td>
16 <tr><td>
17 <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">
17 <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">
18 {if(basename, '{basename|escape}', '{topic|escape}')}
18 {if(basename, '{basename|escape}', '{topic|escape}')}
19 </a>
19 </a>
20 </td><td>
20 </td><td>
21 {summary|escape}
21 {summary|escape}
22 </td></tr>'
22 </td></tr>'
23
23
24 naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
24 naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
25 navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
25 navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
26 navgraphentry = '<a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
26 navgraphentry = '<a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
27 filenaventry = '<a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
27 filenaventry = '<a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
28 filedifflink = '<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
28 filedifflink = '<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
29 filenodelink = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
29 filenodelink = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
30 filenolink = '{file|escape} '
30 filenolink = '{file|escape} '
31 fileellipses = '...'
31 fileellipses = '...'
32 diffstatlink = diffstat.tmpl
32 diffstatlink = diffstat.tmpl
33 diffstatnolink = diffstat.tmpl
33 diffstatnolink = diffstat.tmpl
34 changelogentry = shortlogentry.tmpl
34 changelogentry = shortlogentry.tmpl
35 searchentry = shortlogentry.tmpl
35 searchentry = shortlogentry.tmpl
36 changeset = changeset.tmpl
36 changeset = changeset.tmpl
37 manifest = manifest.tmpl
37 manifest = manifest.tmpl
38
38
39 nav = '{before%naventry} {after%naventry}'
39 nav = '{before%naventry} {after%naventry}'
40 navshort = '{before%navshortentry}{after%navshortentry}'
40 navshort = '{before%navshortentry}{after%navshortentry}'
41 navgraph = '{before%navgraphentry}{after%navgraphentry}'
41 navgraph = '{before%navgraphentry}{after%navgraphentry}'
42 filenav = '{before%filenaventry}{after%filenaventry}'
42 filenav = '{before%filenaventry}{after%filenaventry}'
43
43
44 direntry = '
44 direntry = '
45 <tr class="fileline">
45 <tr class="fileline">
46 <td class="name">
46 <td class="name">
47 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">
47 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">
48 <img src="{staticurl|urlescape}coal-folder.png" alt="dir."/> {basename|escape}/
48 <img src="{staticurl|urlescape}coal-folder.png" alt="dir."/> {basename|escape}/
49 </a>
49 </a>
50 <a href="{url|urlescape}file/{symrev}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">
50 <a href="{url|urlescape}file/{symrev}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">
51 {emptydirs|escape}
51 {emptydirs|escape}
52 </a>
52 </a>
53 </td>
53 </td>
54 <td class="size"></td>
54 <td class="size"></td>
55 <td class="permissions">drwxr-xr-x</td>
55 <td class="permissions">drwxr-xr-x</td>
56 </tr>'
56 </tr>'
57
57
58 fileentry = '
58 fileentry = '
59 <tr class="fileline">
59 <tr class="fileline">
60 <td class="filename">
60 <td class="filename">
61 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">
61 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">
62 <img src="{staticurl|urlescape}coal-file.png" alt="file"/> {basename|escape}
62 <img src="{staticurl|urlescape}coal-file.png" alt="file"/> {basename|escape}
63 </a>
63 </a>
64 </td>
64 </td>
65 <td class="size">{size}</td>
65 <td class="size">{size}</td>
66 <td class="permissions">{permissions|permissions}</td>
66 <td class="permissions">{permissions|permissions}</td>
67 </tr>'
67 </tr>'
68
68
69 filerevision = filerevision.tmpl
69 filerevision = filerevision.tmpl
70 fileannotate = fileannotate.tmpl
70 fileannotate = fileannotate.tmpl
71 filediff = filediff.tmpl
71 filediff = filediff.tmpl
72 filecomparison = filecomparison.tmpl
72 filecomparison = filecomparison.tmpl
73 filelog = filelog.tmpl
73 filelog = filelog.tmpl
74 fileline = '
74 fileline = '
75 <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
75 <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
76 filelogentry = filelogentry.tmpl
76 filelogentry = filelogentry.tmpl
77
77
78 annotateline = '
78 annotateline = '
79 <tr id="{lineid}"{ifeq(node, originalnode, ' class="thisrev"')}>
79 <tr id="{lineid}"{ifeq(node, originalnode, ' class="thisrev"')}>
80 <td class="annotate parity{blockparity}">
80 <td class="annotate parity{blockparity}">
81 {if(blockhead,
81 {if(blockhead,
82 '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
82 '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
83 {rev}
83 {rev}
84 </a>')}
84 </a>')}
85 <div class="annotate-info">
85 <div class="annotate-info">
86 <div>
86 <div>
87 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
87 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
88 {node|short}</a>
88 {node|short}</a>
89 {desc|escape|firstline}
89 {desc|escape|firstline}
90 </div>
90 </div>
91 <div><em>{author|obfuscate}</em></div>
91 <div><em>{author|obfuscate}</em></div>
92 <div>parents: {parents%annotateparent}</div>
92 <div>parents: {parents%annotateparent}</div>
93 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>
93 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>
94 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
94 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
95 </div>
95 </div>
96 </td>
96 </td>
97 <td class="source followlines-btn-parent"><a href="#{lineid}">{linenumber}</a> {line|escape}</td>
97 <td class="source followlines-btn-parent"><a href="#{lineid}">{linenumber}</a> {line|escape}</td>
98 </tr>'
98 </tr>'
99 annotateparent = '
99 annotateparent = '
100 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rev}</a>'
100 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rev}</a>'
101 diffblock = '<div class="bottomline inc-lineno"><pre class="sourcelines wrap">{lines}</pre></div>'
101 diffblock = '<div class="bottomline inc-lineno"><pre class="sourcelines wrap">{lines}</pre></div>'
102 difflineplus = '
102 difflineplus = '
103 <span id="{lineid}" class="plusline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
103 <span id="{lineid}" class="plusline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
104 difflineminus = '
104 difflineminus = '
105 <span id="{lineid}" class="minusline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
105 <span id="{lineid}" class="minusline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
106 difflineat = '
106 difflineat = '
107 <span id="{lineid}" class="atline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
107 <span id="{lineid}" class="atline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
108 diffline = '
108 diffline = '
109 <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
109 <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
110
110
111 comparisonblock ='
111 comparisonblock ='
112 <tbody class="block">
112 <tbody class="block">
113 {lines}
113 {lines}
114 </tbody>'
114 </tbody>'
115 comparisonline = '
115 comparisonline = '
116 <tr id="{lineid}">
116 <tr id="{lineid}">
117 <td class="source {type}"><a href="#{lineid}">{leftlinenumber}</a> {leftline|escape}</td>
117 <td class="source {type}"><a href="#{lineid}">{leftlinenumber}</a> {leftline|escape}</td>
118 <td class="source {type}"><a href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</td>
118 <td class="source {type}"><a href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</td>
119 </tr>'
119 </tr>'
120
120
121 changesetparent = '<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> '
121 changesetparent = '<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> '
122
122
123 changesetparentdiff = '
123 changesetparentdiff = '
124 {changesetparent}
124 {changesetparent}
125 {ifeq(node, basenode, '(current diff)', '({difffrom})')}'
125 {ifeq(node, basenode, '(current diff)', '({difffrom})')}'
126
126
127 difffrom = '<a href="{url|urlescape}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">diff</a>'
127 difffrom = '<a href="{url|urlescape}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">diff</a>'
128
128
129 filerevparent = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> '
129 filerevparent = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> '
130 filerevchild = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> '
130 filerevchild = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> '
131
131
132 filerename = '{file|escape}@'
132 filerename = '{file|escape}@'
133 filelogrename = '
133 filelogrename = '
134 <span class="base">
134 <span class="base">
135 base
135 base
136 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
136 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
137 {file|escape}@{node|short}
137 {file|escape}@{node|short}
138 </a>
138 </a>
139 </span>'
139 </span>'
140 fileannotateparent = '
140 fileannotateparent = '
141 <tr>
141 <tr>
142 <td class="metatag">parent:</td>
142 <td class="metatag">parent:</td>
143 <td>
143 <td>
144 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
144 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
145 {rename%filerename}{node|short}
145 {rename%filerename}{node|short}
146 </a>
146 </a>
147 </td>
147 </td>
148 </tr>'
148 </tr>'
149 changesetchild = ' <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>'
149 changesetchild = ' <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>'
150 fileannotatechild = '
150 fileannotatechild = '
151 <tr>
151 <tr>
152 <td class="metatag">child:</td>
152 <td class="metatag">child:</td>
153 <td>
153 <td>
154 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
154 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
155 {node|short}
155 {node|short}
156 </a>
156 </a>
157 </td>
157 </td>
158 </tr>'
158 </tr>'
159 tags = tags.tmpl
159 tags = tags.tmpl
160 tagentry = '
160 tagentry = '
161 <tr class="tagEntry">
161 <tr class="tagEntry">
162 <td>
162 <td>
163 <a href="{url|urlescape}rev/{tag|revescape}{sessionvars%urlparameter}">
163 <a href="{url|urlescape}rev/{tag|revescape}{sessionvars%urlparameter}">
164 {tag|escape}
164 {tag|escape}
165 </a>
165 </a>
166 </td>
166 </td>
167 <td class="node">
167 <td class="node">
168 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
168 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
169 {node|short}
169 {node|short}
170 </a>
170 </a>
171 </td>
171 </td>
172 </tr>'
172 </tr>'
173 bookmarks = bookmarks.tmpl
173 bookmarks = bookmarks.tmpl
174 bookmarkentry = '
174 bookmarkentry = '
175 <tr class="tagEntry">
175 <tr class="tagEntry">
176 <td>
176 <td>
177 <a href="{url|urlescape}rev/{bookmark|revescape}{sessionvars%urlparameter}">
177 <a href="{url|urlescape}rev/{bookmark|revescape}{sessionvars%urlparameter}">
178 {bookmark|escape}
178 {bookmark|escape}
179 </a>
179 </a>
180 </td>
180 </td>
181 <td class="node">
181 <td class="node">
182 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
182 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
183 {node|short}
183 {node|short}
184 </a>
184 </a>
185 </td>
185 </td>
186 </tr>'
186 </tr>'
187 branches = branches.tmpl
187 branches = branches.tmpl
188 branchentry = '
188 branchentry = '
189 <tr class="tagEntry">
189 <tr class="tagEntry">
190 <td>
190 <td>
191 <a href="{url|urlescape}shortlog/{branch|revescape}{sessionvars%urlparameter}" class="{status}">
191 <a href="{url|urlescape}shortlog/{branch|revescape}{sessionvars%urlparameter}" class="{status}">
192 {branch|escape}
192 {branch|escape}
193 </a>
193 </a>
194 </td>
194 </td>
195 <td class="node">
195 <td class="node">
196 <a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}" class="{status}">
196 <a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}" class="{status}">
197 {node|short}
197 {node|short}
198 </a>
198 </a>
199 </td>
199 </td>
200 </tr>'
200 </tr>'
201 changelogtag = '<span class="tag">{name|escape}</span> '
201 changelogtag = '<span class="tag">{name|escape}</span> '
202 changesettag = '<span class="tag">{tag|escape}</span> '
202 changesettag = '<span class="tag">{tag|escape}</span> '
203 changesetbookmark = '<span class="tag">{bookmark|escape}</span> '
203 changesetbookmark = '<span class="tag">{bookmark|escape}</span> '
204 changelogbranchhead = '<span class="branchhead">{name|escape}</span> '
204 changelogbranchhead = '<span class="branchhead">{name|escape}</span> '
205 changelogbranchname = '<span class="branchname">{name|escape}</span> '
205 changelogbranchname = '<span class="branchname">{name|escape}</span> '
206
206
207 filediffparent = '
207 filediffparent = '
208 <tr>
208 <tr>
209 <th class="parent">parent {rev}:</th>
209 <th class="parent">parent {rev}:</th>
210 <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
210 <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
211 </tr>'
211 </tr>'
212 filediffchild = '
212 filediffchild = '
213 <tr>
213 <tr>
214 <th class="child">child {rev}:</th>
214 <th class="child">child {rev}:</th>
215 <td class="child"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
215 <td class="child"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
216 </td>
216 </td>
217 </tr>'
217 </tr>'
218
218
219 indexentry = '
219 indexentry = '
220 <tr>
220 <tr>
221 <td><a href="{url|urlescape}{sessionvars%urlparameter}">{name|escape}</a></td>
221 <td><a href="{url|urlescape}{sessionvars%urlparameter}">{name|escape}</a></td>
222 <td>{description}</td>
222 <td>{description}</td>
223 <td>{contact|obfuscate}</td>
223 <td>{contact|obfuscate}</td>
224 <td class="age">{lastchange|rfc822date}</td>
224 <td class="age">{lastchange|rfc822date}</td>
225 <td class="indexlinks">{archives%indexarchiveentry}</td>
225 <td class="indexlinks">{archives%indexarchiveentry}</td>
226 <td>
226 <td>
227 {if(isdirectory, '',
227 {if(isdirectory, '',
228 '<a href="{url|urlescape}atom-log" title="subscribe to repository atom feed">
228 '<a href="{url|urlescape}atom-log" title="subscribe to repository atom feed">
229 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="subscribe to repository atom feed">
229 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="subscribe to repository atom feed">
230 </a>'
230 </a>'
231 )}
231 )}
232 </td>
232 </td>
233 </tr>\n'
233 </tr>\n'
234 indexarchiveentry = '<a href="{url|urlescape}archive/{node|short}{extension|urlescape}">&nbsp;&darr;{type|escape}</a>'
234 indexarchiveentry = '<a href="{url|urlescape}archive/{node|short}{extension|urlescape}">&nbsp;&darr;{type|escape}</a>'
235 index = index.tmpl
235 index = index.tmpl
236 archiveentry = '
236 archiveentry = '
237 <li>
237 <li>
238 <a href="{url|urlescape}archive/{symrev}{extension|urlescape}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a>
238 <a href="{url|urlescape}archive/{symrev}{extension|urlescape}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a>
239 </li>'
239 </li>'
240 notfound = notfound.tmpl
240 notfound = notfound.tmpl
241 error = error.tmpl
241 error = error.tmpl
242 urlparameter = '{separator}{name}={value|urlescape}'
242 urlparameter = '{separator}{name}={value|urlescape}'
243 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
243 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
244 breadcrumb = '&gt; <a href="{url|urlescape}">{name|escape}</a> '
244 breadcrumb = '&gt; <a href="{url|urlescape}">{name|escape}</a> '
245
245
246 searchform = '
246 searchform = '
247 <form class="search" action="{url|urlescape}log">
247 <form class="search" action="{url|urlescape}log">
248 {sessionvars%hiddenformentry}
248 {sessionvars%hiddenformentry}
249 <p><input name="rev" id="search1" type="text" size="30" value="{query|escape}" /></p>
249 <p><input name="rev" id="search1" type="text" size="30" value="{query|escape}" /></p>
250 <div id="hint">{searchhint}</div>
250 <div id="hint">{searchhint}</div>
251 </form>'
251 </form>'
252 searchhint = 'Find changesets by keywords (author, files, the commit message), revision
252 searchhint = 'Find changesets by keywords (author, files, the commit message), revision
253 number or hash, or <a href="{url|urlescape}help/revsets">revset expression</a>.'
253 number or hash, or <a href="{url|urlescape}help/revsets">revset expression</a>.'
254
255 diffoptsform = '
256 <form id="diffopts-form"
257 data-ignorews="{if(get(diffopts, 'ignorews'), '1', '0')}"
258 data-ignorewsamount="{if(get(diffopts, 'ignorewsamount'), '1', '0')}"
259 data-ignorewseol="{if(get(diffopts, 'ignorewseol'), '1', '0')}"
260 data-ignoreblanklines="{if(get(diffopts, 'ignoreblanklines'), '1', '0')}">
261 <span>Ignore whitespace changes - </span>
262 <span>Everywhere:</span>
263 <input id="ignorews-checkbox" type="checkbox" />
264 <span>Within whitespace:</span>
265 <input id="ignorewsamount-checkbox" type="checkbox" />
266 <span>At end of lines:</span>
267 <input id="ignorewseol-checkbox" type="checkbox" />
268 </form>'
@@ -1,439 +1,489 b''
1 // mercurial.js - JavaScript utility functions
1 // mercurial.js - JavaScript utility functions
2 //
2 //
3 // Rendering of branch DAGs on the client side
3 // Rendering of branch DAGs on the client side
4 // Display of elapsed time
4 // Display of elapsed time
5 // Show or hide diffstat
5 // Show or hide diffstat
6 //
6 //
7 // Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl>
7 // Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl>
8 // Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
8 // Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
9 //
9 //
10 // derived from code written by Scott James Remnant <scott@ubuntu.com>
10 // derived from code written by Scott James Remnant <scott@ubuntu.com>
11 // Copyright 2005 Canonical Ltd.
11 // Copyright 2005 Canonical Ltd.
12 //
12 //
13 // This software may be used and distributed according to the terms
13 // This software may be used and distributed according to the terms
14 // of the GNU General Public License, incorporated herein by reference.
14 // of the GNU General Public License, incorporated herein by reference.
15
15
16 var colors = [
16 var colors = [
17 [ 1.0, 0.0, 0.0 ],
17 [ 1.0, 0.0, 0.0 ],
18 [ 1.0, 1.0, 0.0 ],
18 [ 1.0, 1.0, 0.0 ],
19 [ 0.0, 1.0, 0.0 ],
19 [ 0.0, 1.0, 0.0 ],
20 [ 0.0, 1.0, 1.0 ],
20 [ 0.0, 1.0, 1.0 ],
21 [ 0.0, 0.0, 1.0 ],
21 [ 0.0, 0.0, 1.0 ],
22 [ 1.0, 0.0, 1.0 ]
22 [ 1.0, 0.0, 1.0 ]
23 ];
23 ];
24
24
25 function Graph() {
25 function Graph() {
26
26
27 this.canvas = document.getElementById('graph');
27 this.canvas = document.getElementById('graph');
28 if (window.G_vmlCanvasManager) this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);
28 if (window.G_vmlCanvasManager) this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);
29 this.ctx = this.canvas.getContext('2d');
29 this.ctx = this.canvas.getContext('2d');
30 this.ctx.strokeStyle = 'rgb(0, 0, 0)';
30 this.ctx.strokeStyle = 'rgb(0, 0, 0)';
31 this.ctx.fillStyle = 'rgb(0, 0, 0)';
31 this.ctx.fillStyle = 'rgb(0, 0, 0)';
32 this.cur = [0, 0];
32 this.cur = [0, 0];
33 this.line_width = 3;
33 this.line_width = 3;
34 this.bg = [0, 4];
34 this.bg = [0, 4];
35 this.cell = [2, 0];
35 this.cell = [2, 0];
36 this.columns = 0;
36 this.columns = 0;
37 this.revlink = '';
37 this.revlink = '';
38
38
39 this.reset = function() {
39 this.reset = function() {
40 this.bg = [0, 4];
40 this.bg = [0, 4];
41 this.cell = [2, 0];
41 this.cell = [2, 0];
42 this.columns = 0;
42 this.columns = 0;
43 document.getElementById('nodebgs').innerHTML = '';
43 document.getElementById('nodebgs').innerHTML = '';
44 document.getElementById('graphnodes').innerHTML = '';
44 document.getElementById('graphnodes').innerHTML = '';
45 }
45 }
46
46
47 this.scale = function(height) {
47 this.scale = function(height) {
48 this.bg_height = height;
48 this.bg_height = height;
49 this.box_size = Math.floor(this.bg_height / 1.2);
49 this.box_size = Math.floor(this.bg_height / 1.2);
50 this.cell_height = this.box_size;
50 this.cell_height = this.box_size;
51 }
51 }
52
52
53 this.setColor = function(color, bg, fg) {
53 this.setColor = function(color, bg, fg) {
54
54
55 // Set the colour.
55 // Set the colour.
56 //
56 //
57 // If color is a string, expect an hexadecimal RGB
57 // If color is a string, expect an hexadecimal RGB
58 // value and apply it unchanged. If color is a number,
58 // value and apply it unchanged. If color is a number,
59 // pick a distinct colour based on an internal wheel;
59 // pick a distinct colour based on an internal wheel;
60 // the bg parameter provides the value that should be
60 // the bg parameter provides the value that should be
61 // assigned to the 'zero' colours and the fg parameter
61 // assigned to the 'zero' colours and the fg parameter
62 // provides the multiplier that should be applied to
62 // provides the multiplier that should be applied to
63 // the foreground colours.
63 // the foreground colours.
64 var s;
64 var s;
65 if(typeof color == "string") {
65 if(typeof color == "string") {
66 s = "#" + color;
66 s = "#" + color;
67 } else { //typeof color == "number"
67 } else { //typeof color == "number"
68 color %= colors.length;
68 color %= colors.length;
69 var red = (colors[color][0] * fg) || bg;
69 var red = (colors[color][0] * fg) || bg;
70 var green = (colors[color][1] * fg) || bg;
70 var green = (colors[color][1] * fg) || bg;
71 var blue = (colors[color][2] * fg) || bg;
71 var blue = (colors[color][2] * fg) || bg;
72 red = Math.round(red * 255);
72 red = Math.round(red * 255);
73 green = Math.round(green * 255);
73 green = Math.round(green * 255);
74 blue = Math.round(blue * 255);
74 blue = Math.round(blue * 255);
75 s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
75 s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
76 }
76 }
77 this.ctx.strokeStyle = s;
77 this.ctx.strokeStyle = s;
78 this.ctx.fillStyle = s;
78 this.ctx.fillStyle = s;
79 return s;
79 return s;
80
80
81 }
81 }
82
82
83 this.edge = function(x0, y0, x1, y1, color, width) {
83 this.edge = function(x0, y0, x1, y1, color, width) {
84
84
85 this.setColor(color, 0.0, 0.65);
85 this.setColor(color, 0.0, 0.65);
86 if(width >= 0)
86 if(width >= 0)
87 this.ctx.lineWidth = width;
87 this.ctx.lineWidth = width;
88 this.ctx.beginPath();
88 this.ctx.beginPath();
89 this.ctx.moveTo(x0, y0);
89 this.ctx.moveTo(x0, y0);
90 this.ctx.lineTo(x1, y1);
90 this.ctx.lineTo(x1, y1);
91 this.ctx.stroke();
91 this.ctx.stroke();
92
92
93 }
93 }
94
94
95 this.render = function(data) {
95 this.render = function(data) {
96
96
97 var backgrounds = '';
97 var backgrounds = '';
98 var nodedata = '';
98 var nodedata = '';
99
99
100 for (var i in data) {
100 for (var i in data) {
101
101
102 var parity = i % 2;
102 var parity = i % 2;
103 this.cell[1] += this.bg_height;
103 this.cell[1] += this.bg_height;
104 this.bg[1] += this.bg_height;
104 this.bg[1] += this.bg_height;
105
105
106 var cur = data[i];
106 var cur = data[i];
107 var node = cur[1];
107 var node = cur[1];
108 var edges = cur[2];
108 var edges = cur[2];
109 var fold = false;
109 var fold = false;
110
110
111 var prevWidth = this.ctx.lineWidth;
111 var prevWidth = this.ctx.lineWidth;
112 for (var j in edges) {
112 for (var j in edges) {
113
113
114 line = edges[j];
114 line = edges[j];
115 start = line[0];
115 start = line[0];
116 end = line[1];
116 end = line[1];
117 color = line[2];
117 color = line[2];
118 var width = line[3];
118 var width = line[3];
119 if(width < 0)
119 if(width < 0)
120 width = prevWidth;
120 width = prevWidth;
121 var branchcolor = line[4];
121 var branchcolor = line[4];
122 if(branchcolor)
122 if(branchcolor)
123 color = branchcolor;
123 color = branchcolor;
124
124
125 if (end > this.columns || start > this.columns) {
125 if (end > this.columns || start > this.columns) {
126 this.columns += 1;
126 this.columns += 1;
127 }
127 }
128
128
129 if (start == this.columns && start > end) {
129 if (start == this.columns && start > end) {
130 var fold = true;
130 var fold = true;
131 }
131 }
132
132
133 x0 = this.cell[0] + this.box_size * start + this.box_size / 2;
133 x0 = this.cell[0] + this.box_size * start + this.box_size / 2;
134 y0 = this.bg[1] - this.bg_height / 2;
134 y0 = this.bg[1] - this.bg_height / 2;
135 x1 = this.cell[0] + this.box_size * end + this.box_size / 2;
135 x1 = this.cell[0] + this.box_size * end + this.box_size / 2;
136 y1 = this.bg[1] + this.bg_height / 2;
136 y1 = this.bg[1] + this.bg_height / 2;
137
137
138 this.edge(x0, y0, x1, y1, color, width);
138 this.edge(x0, y0, x1, y1, color, width);
139
139
140 }
140 }
141 this.ctx.lineWidth = prevWidth;
141 this.ctx.lineWidth = prevWidth;
142
142
143 // Draw the revision node in the right column
143 // Draw the revision node in the right column
144
144
145 column = node[0]
145 column = node[0]
146 color = node[1]
146 color = node[1]
147
147
148 radius = this.box_size / 8;
148 radius = this.box_size / 8;
149 x = this.cell[0] + this.box_size * column + this.box_size / 2;
149 x = this.cell[0] + this.box_size * column + this.box_size / 2;
150 y = this.bg[1] - this.bg_height / 2;
150 y = this.bg[1] - this.bg_height / 2;
151 var add = this.vertex(x, y, color, parity, cur);
151 var add = this.vertex(x, y, color, parity, cur);
152 backgrounds += add[0];
152 backgrounds += add[0];
153 nodedata += add[1];
153 nodedata += add[1];
154
154
155 if (fold) this.columns -= 1;
155 if (fold) this.columns -= 1;
156
156
157 }
157 }
158
158
159 document.getElementById('nodebgs').innerHTML += backgrounds;
159 document.getElementById('nodebgs').innerHTML += backgrounds;
160 document.getElementById('graphnodes').innerHTML += nodedata;
160 document.getElementById('graphnodes').innerHTML += nodedata;
161
161
162 }
162 }
163
163
164 }
164 }
165
165
166
166
167 function process_dates(parentSelector){
167 function process_dates(parentSelector){
168
168
169 // derived from code from mercurial/templatefilter.py
169 // derived from code from mercurial/templatefilter.py
170
170
171 var scales = {
171 var scales = {
172 'year': 365 * 24 * 60 * 60,
172 'year': 365 * 24 * 60 * 60,
173 'month': 30 * 24 * 60 * 60,
173 'month': 30 * 24 * 60 * 60,
174 'week': 7 * 24 * 60 * 60,
174 'week': 7 * 24 * 60 * 60,
175 'day': 24 * 60 * 60,
175 'day': 24 * 60 * 60,
176 'hour': 60 * 60,
176 'hour': 60 * 60,
177 'minute': 60,
177 'minute': 60,
178 'second': 1
178 'second': 1
179 };
179 };
180
180
181 function format(count, string){
181 function format(count, string){
182 var ret = count + ' ' + string;
182 var ret = count + ' ' + string;
183 if (count > 1){
183 if (count > 1){
184 ret = ret + 's';
184 ret = ret + 's';
185 }
185 }
186 return ret;
186 return ret;
187 }
187 }
188
188
189 function shortdate(date){
189 function shortdate(date){
190 var ret = date.getFullYear() + '-';
190 var ret = date.getFullYear() + '-';
191 // getMonth() gives a 0-11 result
191 // getMonth() gives a 0-11 result
192 var month = date.getMonth() + 1;
192 var month = date.getMonth() + 1;
193 if (month <= 9){
193 if (month <= 9){
194 ret += '0' + month;
194 ret += '0' + month;
195 } else {
195 } else {
196 ret += month;
196 ret += month;
197 }
197 }
198 ret += '-';
198 ret += '-';
199 var day = date.getDate();
199 var day = date.getDate();
200 if (day <= 9){
200 if (day <= 9){
201 ret += '0' + day;
201 ret += '0' + day;
202 } else {
202 } else {
203 ret += day;
203 ret += day;
204 }
204 }
205 return ret;
205 return ret;
206 }
206 }
207
207
208 function age(datestr){
208 function age(datestr){
209 var now = new Date();
209 var now = new Date();
210 var once = new Date(datestr);
210 var once = new Date(datestr);
211 if (isNaN(once.getTime())){
211 if (isNaN(once.getTime())){
212 // parsing error
212 // parsing error
213 return datestr;
213 return datestr;
214 }
214 }
215
215
216 var delta = Math.floor((now.getTime() - once.getTime()) / 1000);
216 var delta = Math.floor((now.getTime() - once.getTime()) / 1000);
217
217
218 var future = false;
218 var future = false;
219 if (delta < 0){
219 if (delta < 0){
220 future = true;
220 future = true;
221 delta = -delta;
221 delta = -delta;
222 if (delta > (30 * scales.year)){
222 if (delta > (30 * scales.year)){
223 return "in the distant future";
223 return "in the distant future";
224 }
224 }
225 }
225 }
226
226
227 if (delta > (2 * scales.year)){
227 if (delta > (2 * scales.year)){
228 return shortdate(once);
228 return shortdate(once);
229 }
229 }
230
230
231 for (unit in scales){
231 for (unit in scales){
232 var s = scales[unit];
232 var s = scales[unit];
233 var n = Math.floor(delta / s);
233 var n = Math.floor(delta / s);
234 if ((n >= 2) || (s == 1)){
234 if ((n >= 2) || (s == 1)){
235 if (future){
235 if (future){
236 return format(n, unit) + ' from now';
236 return format(n, unit) + ' from now';
237 } else {
237 } else {
238 return format(n, unit) + ' ago';
238 return format(n, unit) + ' ago';
239 }
239 }
240 }
240 }
241 }
241 }
242 }
242 }
243
243
244 var nodes = document.querySelectorAll((parentSelector || '') + ' .age');
244 var nodes = document.querySelectorAll((parentSelector || '') + ' .age');
245 var dateclass = new RegExp('\\bdate\\b');
245 var dateclass = new RegExp('\\bdate\\b');
246 for (var i=0; i<nodes.length; ++i){
246 for (var i=0; i<nodes.length; ++i){
247 var node = nodes[i];
247 var node = nodes[i];
248 var classes = node.className;
248 var classes = node.className;
249 var agevalue = age(node.textContent);
249 var agevalue = age(node.textContent);
250 if (dateclass.test(classes)){
250 if (dateclass.test(classes)){
251 // We want both: date + (age)
251 // We want both: date + (age)
252 node.textContent += ' ('+agevalue+')';
252 node.textContent += ' ('+agevalue+')';
253 } else {
253 } else {
254 node.title = node.textContent;
254 node.title = node.textContent;
255 node.textContent = agevalue;
255 node.textContent = agevalue;
256 }
256 }
257 }
257 }
258 }
258 }
259
259
260 function toggleDiffstat() {
260 function toggleDiffstat() {
261 var curdetails = document.getElementById('diffstatdetails').style.display;
261 var curdetails = document.getElementById('diffstatdetails').style.display;
262 var curexpand = curdetails == 'none' ? 'inline' : 'none';
262 var curexpand = curdetails == 'none' ? 'inline' : 'none';
263 document.getElementById('diffstatdetails').style.display = curexpand;
263 document.getElementById('diffstatdetails').style.display = curexpand;
264 document.getElementById('diffstatexpand').style.display = curdetails;
264 document.getElementById('diffstatexpand').style.display = curdetails;
265 }
265 }
266
266
267 function toggleLinewrap() {
267 function toggleLinewrap() {
268 function getLinewrap() {
268 function getLinewrap() {
269 var nodes = document.getElementsByClassName('sourcelines');
269 var nodes = document.getElementsByClassName('sourcelines');
270 // if there are no such nodes, error is thrown here
270 // if there are no such nodes, error is thrown here
271 return nodes[0].classList.contains('wrap');
271 return nodes[0].classList.contains('wrap');
272 }
272 }
273
273
274 function setLinewrap(enable) {
274 function setLinewrap(enable) {
275 var nodes = document.getElementsByClassName('sourcelines');
275 var nodes = document.getElementsByClassName('sourcelines');
276 for (var i = 0; i < nodes.length; i++) {
276 for (var i = 0; i < nodes.length; i++) {
277 if (enable) {
277 if (enable) {
278 nodes[i].classList.add('wrap');
278 nodes[i].classList.add('wrap');
279 } else {
279 } else {
280 nodes[i].classList.remove('wrap');
280 nodes[i].classList.remove('wrap');
281 }
281 }
282 }
282 }
283
283
284 var links = document.getElementsByClassName('linewraplink');
284 var links = document.getElementsByClassName('linewraplink');
285 for (var i = 0; i < links.length; i++) {
285 for (var i = 0; i < links.length; i++) {
286 links[i].innerHTML = enable ? 'on' : 'off';
286 links[i].innerHTML = enable ? 'on' : 'off';
287 }
287 }
288 }
288 }
289
289
290 setLinewrap(!getLinewrap());
290 setLinewrap(!getLinewrap());
291 }
291 }
292
292
293 function format(str, replacements) {
293 function format(str, replacements) {
294 return str.replace(/%(\w+)%/g, function(match, p1) {
294 return str.replace(/%(\w+)%/g, function(match, p1) {
295 return String(replacements[p1]);
295 return String(replacements[p1]);
296 });
296 });
297 }
297 }
298
298
299 function makeRequest(url, method, onstart, onsuccess, onerror, oncomplete) {
299 function makeRequest(url, method, onstart, onsuccess, onerror, oncomplete) {
300 xfr = new XMLHttpRequest();
300 xfr = new XMLHttpRequest();
301 xfr.onreadystatechange = function() {
301 xfr.onreadystatechange = function() {
302 if (xfr.readyState === 4) {
302 if (xfr.readyState === 4) {
303 try {
303 try {
304 if (xfr.status === 200) {
304 if (xfr.status === 200) {
305 onsuccess(xfr.responseText);
305 onsuccess(xfr.responseText);
306 } else {
306 } else {
307 throw 'server error';
307 throw 'server error';
308 }
308 }
309 } catch (e) {
309 } catch (e) {
310 onerror(e);
310 onerror(e);
311 } finally {
311 } finally {
312 oncomplete();
312 oncomplete();
313 }
313 }
314 }
314 }
315 };
315 };
316
316
317 xfr.open(method, url);
317 xfr.open(method, url);
318 xfr.overrideMimeType("text/xhtml; charset=" + document.characterSet.toLowerCase());
318 xfr.overrideMimeType("text/xhtml; charset=" + document.characterSet.toLowerCase());
319 xfr.send();
319 xfr.send();
320 onstart();
320 onstart();
321 return xfr;
321 return xfr;
322 }
322 }
323
323
324 function removeByClassName(className) {
324 function removeByClassName(className) {
325 var nodes = document.getElementsByClassName(className);
325 var nodes = document.getElementsByClassName(className);
326 while (nodes.length) {
326 while (nodes.length) {
327 nodes[0].parentNode.removeChild(nodes[0]);
327 nodes[0].parentNode.removeChild(nodes[0]);
328 }
328 }
329 }
329 }
330
330
331 function docFromHTML(html) {
331 function docFromHTML(html) {
332 var doc = document.implementation.createHTMLDocument('');
332 var doc = document.implementation.createHTMLDocument('');
333 doc.documentElement.innerHTML = html;
333 doc.documentElement.innerHTML = html;
334 return doc;
334 return doc;
335 }
335 }
336
336
337 function appendFormatHTML(element, formatStr, replacements) {
337 function appendFormatHTML(element, formatStr, replacements) {
338 element.insertAdjacentHTML('beforeend', format(formatStr, replacements));
338 element.insertAdjacentHTML('beforeend', format(formatStr, replacements));
339 }
339 }
340
340
341 function ajaxScrollInit(urlFormat,
341 function ajaxScrollInit(urlFormat,
342 nextPageVar,
342 nextPageVar,
343 nextPageVarGet,
343 nextPageVarGet,
344 containerSelector,
344 containerSelector,
345 messageFormat,
345 messageFormat,
346 mode) {
346 mode) {
347 updateInitiated = false;
347 updateInitiated = false;
348 container = document.querySelector(containerSelector);
348 container = document.querySelector(containerSelector);
349
349
350 function scrollHandler() {
350 function scrollHandler() {
351 if (updateInitiated) {
351 if (updateInitiated) {
352 return;
352 return;
353 }
353 }
354
354
355 var scrollHeight = document.documentElement.scrollHeight;
355 var scrollHeight = document.documentElement.scrollHeight;
356 var clientHeight = document.documentElement.clientHeight;
356 var clientHeight = document.documentElement.clientHeight;
357 var scrollTop = document.body.scrollTop
357 var scrollTop = document.body.scrollTop
358 || document.documentElement.scrollTop;
358 || document.documentElement.scrollTop;
359
359
360 if (scrollHeight - (scrollTop + clientHeight) < 50) {
360 if (scrollHeight - (scrollTop + clientHeight) < 50) {
361 updateInitiated = true;
361 updateInitiated = true;
362 removeByClassName('scroll-loading-error');
362 removeByClassName('scroll-loading-error');
363 container.lastElementChild.classList.add('scroll-separator');
363 container.lastElementChild.classList.add('scroll-separator');
364
364
365 if (!nextPageVar) {
365 if (!nextPageVar) {
366 var message = {
366 var message = {
367 'class': 'scroll-loading-info',
367 'class': 'scroll-loading-info',
368 text: 'No more entries'
368 text: 'No more entries'
369 };
369 };
370 appendFormatHTML(container, messageFormat, message);
370 appendFormatHTML(container, messageFormat, message);
371 return;
371 return;
372 }
372 }
373
373
374 makeRequest(
374 makeRequest(
375 format(urlFormat, {next: nextPageVar}),
375 format(urlFormat, {next: nextPageVar}),
376 'GET',
376 'GET',
377 function onstart() {
377 function onstart() {
378 var message = {
378 var message = {
379 'class': 'scroll-loading',
379 'class': 'scroll-loading',
380 text: 'Loading...'
380 text: 'Loading...'
381 };
381 };
382 appendFormatHTML(container, messageFormat, message);
382 appendFormatHTML(container, messageFormat, message);
383 },
383 },
384 function onsuccess(htmlText) {
384 function onsuccess(htmlText) {
385 if (mode == 'graph') {
385 if (mode == 'graph') {
386 var sizes = htmlText.match(/^\s*<canvas id="graph" width="(\d+)" height="(\d+)"><\/canvas>$/m);
386 var sizes = htmlText.match(/^\s*<canvas id="graph" width="(\d+)" height="(\d+)"><\/canvas>$/m);
387 var addWidth = sizes[1];
387 var addWidth = sizes[1];
388 var addHeight = sizes[2];
388 var addHeight = sizes[2];
389 addWidth = parseInt(addWidth);
389 addWidth = parseInt(addWidth);
390 addHeight = parseInt(addHeight);
390 addHeight = parseInt(addHeight);
391 graph.canvas.width = addWidth;
391 graph.canvas.width = addWidth;
392 graph.canvas.height = addHeight;
392 graph.canvas.height = addHeight;
393
393
394 var dataStr = htmlText.match(/^\s*var data = (.*);$/m)[1];
394 var dataStr = htmlText.match(/^\s*var data = (.*);$/m)[1];
395 var data = JSON.parse(dataStr);
395 var data = JSON.parse(dataStr);
396 if (data.length < nextPageVar) {
396 if (data.length < nextPageVar) {
397 nextPageVar = undefined;
397 nextPageVar = undefined;
398 }
398 }
399 graph.reset();
399 graph.reset();
400 graph.render(data);
400 graph.render(data);
401 } else {
401 } else {
402 var doc = docFromHTML(htmlText);
402 var doc = docFromHTML(htmlText);
403 var nodes = doc.querySelector(containerSelector).children;
403 var nodes = doc.querySelector(containerSelector).children;
404 var curClass = 'c' + Date.now();
404 var curClass = 'c' + Date.now();
405 while (nodes.length) {
405 while (nodes.length) {
406 var node = nodes[0];
406 var node = nodes[0];
407 node = document.adoptNode(node);
407 node = document.adoptNode(node);
408 node.classList.add(curClass);
408 node.classList.add(curClass);
409 container.appendChild(node);
409 container.appendChild(node);
410 }
410 }
411 process_dates('.' + curClass);
411 process_dates('.' + curClass);
412 }
412 }
413
413
414 nextPageVar = nextPageVarGet(htmlText, nextPageVar);
414 nextPageVar = nextPageVarGet(htmlText, nextPageVar);
415 },
415 },
416 function onerror(errorText) {
416 function onerror(errorText) {
417 var message = {
417 var message = {
418 'class': 'scroll-loading-error',
418 'class': 'scroll-loading-error',
419 text: 'Error: ' + errorText
419 text: 'Error: ' + errorText
420 };
420 };
421 appendFormatHTML(container, messageFormat, message);
421 appendFormatHTML(container, messageFormat, message);
422 },
422 },
423 function oncomplete() {
423 function oncomplete() {
424 removeByClassName('scroll-loading');
424 removeByClassName('scroll-loading');
425 updateInitiated = false;
425 updateInitiated = false;
426 scrollHandler();
426 scrollHandler();
427 }
427 }
428 );
428 );
429 }
429 }
430 }
430 }
431
431
432 window.addEventListener('scroll', scrollHandler);
432 window.addEventListener('scroll', scrollHandler);
433 window.addEventListener('resize', scrollHandler);
433 window.addEventListener('resize', scrollHandler);
434 scrollHandler();
434 scrollHandler();
435 }
435 }
436
436
437 function renderDiffOptsForm() {
438 // We use URLSearchParams for query string manipulation. Old browsers don't
439 // support this API.
440 if (!("URLSearchParams" in window)) {
441 return;
442 }
443
444 var form = document.getElementById("diffopts-form");
445
446 var KEYS = [
447 "ignorews",
448 "ignorewsamount",
449 "ignorewseol",
450 "ignoreblanklines",
451 ];
452
453 var urlParams = new URLSearchParams(window.location.search);
454
455 function updateAndRefresh(e) {
456 var checkbox = e.target;
457 var name = checkbox.id.substr(0, checkbox.id.indexOf("-"));
458 urlParams.set(name, checkbox.checked ? "1" : "0");
459 window.location.search = urlParams.toString();
460 }
461
462 var allChecked = form.getAttribute("data-ignorews") == "1";
463
464 for (var i = 0; i < KEYS.length; i++) {
465 var key = KEYS[i];
466
467 var checkbox = document.getElementById(key + "-checkbox");
468 if (!checkbox) {
469 continue;
470 }
471
472 currentValue = form.getAttribute("data-" + key);
473 checkbox.checked = currentValue != "0";
474
475 // ignorews implies ignorewsamount and ignorewseol.
476 if (allChecked && (key == "ignorewsamount" || key == "ignorewseol")) {
477 checkbox.checked = true;
478 checkbox.disabled = true;
479 }
480
481 checkbox.addEventListener("change", updateAndRefresh, false);
482 }
483
484 form.style.display = 'block';
485 }
486
437 document.addEventListener('DOMContentLoaded', function() {
487 document.addEventListener('DOMContentLoaded', function() {
438 process_dates();
488 process_dates();
439 }, false);
489 }, false);
@@ -1,384 +1,390 b''
1 body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; }
1 body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; }
2 a { color:#0000cc; }
2 a { color:#0000cc; }
3 a:hover, a:visited, a:active { color:#880000; }
3 a:hover, a:visited, a:active { color:#880000; }
4 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
4 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
5 div.page_header a:visited { color:#0000cc; }
5 div.page_header a:visited { color:#0000cc; }
6 div.page_header a:hover { color:#880000; }
6 div.page_header a:hover { color:#880000; }
7 div.page_nav {
7 div.page_nav {
8 padding:8px;
8 padding:8px;
9 display: flex;
9 display: flex;
10 justify-content: space-between;
10 justify-content: space-between;
11 align-items: center;
11 align-items: center;
12 }
12 }
13 div.page_nav a:visited { color:#0000cc; }
13 div.page_nav a:visited { color:#0000cc; }
14 div.extra_nav {
14 div.extra_nav {
15 padding: 8px;
15 padding: 8px;
16 }
16 }
17 div.extra_nav a:visited {
17 div.extra_nav a:visited {
18 color: #0000cc;
18 color: #0000cc;
19 }
19 }
20 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
20 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
21 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
21 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
22 div.page_footer_text { float:left; color:#555555; font-style:italic; }
22 div.page_footer_text { float:left; color:#555555; font-style:italic; }
23 div.page_body { padding:8px; }
23 div.page_body { padding:8px; }
24 div.title, a.title {
24 div.title, a.title {
25 display:block; padding:6px 8px;
25 display:block; padding:6px 8px;
26 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
26 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
27 }
27 }
28 a.title:hover { background-color: #d9d8d1; }
28 a.title:hover { background-color: #d9d8d1; }
29 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
29 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
30 div.log_body { padding:8px 8px 8px 150px; }
30 div.log_body { padding:8px 8px 8px 150px; }
31 .age { white-space:nowrap; }
31 .age { white-space:nowrap; }
32 span.age { position:relative; float:left; width:142px; font-style:italic; }
32 span.age { position:relative; float:left; width:142px; font-style:italic; }
33 div.log_link {
33 div.log_link {
34 padding:0px 8px;
34 padding:0px 8px;
35 font-size:10px; font-family:sans-serif; font-style:normal;
35 font-size:10px; font-family:sans-serif; font-style:normal;
36 position:relative; float:left; width:136px;
36 position:relative; float:left; width:136px;
37 }
37 }
38 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
38 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
39 a.list { text-decoration:none; color:#000000; }
39 a.list { text-decoration:none; color:#000000; }
40 a.list:hover { text-decoration:underline; color:#880000; }
40 a.list:hover { text-decoration:underline; color:#880000; }
41 table { padding:8px 4px; }
41 table { padding:8px 4px; }
42 th { padding:2px 5px; font-size:12px; text-align:left; }
42 th { padding:2px 5px; font-size:12px; text-align:left; }
43 .parity0 { background-color:#ffffff; }
43 .parity0 { background-color:#ffffff; }
44 tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+4) { background-color:#f6f6f0; }
44 tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+4) { background-color:#f6f6f0; }
45 tr.light:hover, .parity0:hover, tr.dark:hover, .parity1:hover,
45 tr.light:hover, .parity0:hover, tr.dark:hover, .parity1:hover,
46 pre.sourcelines.stripes > :nth-child(4n+2):hover,
46 pre.sourcelines.stripes > :nth-child(4n+2):hover,
47 pre.sourcelines.stripes > :nth-child(4n+4):hover,
47 pre.sourcelines.stripes > :nth-child(4n+4):hover,
48 pre.sourcelines.stripes > :nth-child(4n+1):hover + :nth-child(4n+2),
48 pre.sourcelines.stripes > :nth-child(4n+1):hover + :nth-child(4n+2),
49 pre.sourcelines.stripes > :nth-child(4n+3):hover + :nth-child(4n+4) { background-color:#edece6; }
49 pre.sourcelines.stripes > :nth-child(4n+3):hover + :nth-child(4n+4) { background-color:#edece6; }
50 td { padding:2px 5px; font-size:12px; vertical-align:top; }
50 td { padding:2px 5px; font-size:12px; vertical-align:top; }
51 td.closed { background-color: #99f; }
51 td.closed { background-color: #99f; }
52 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
52 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
53 td.indexlinks { white-space: nowrap; }
53 td.indexlinks { white-space: nowrap; }
54 td.indexlinks a {
54 td.indexlinks a {
55 padding: 2px 5px; line-height: 10px;
55 padding: 2px 5px; line-height: 10px;
56 border: 1px solid;
56 border: 1px solid;
57 color: #ffffff; background-color: #7777bb;
57 color: #ffffff; background-color: #7777bb;
58 border-color: #aaaadd #333366 #333366 #aaaadd;
58 border-color: #aaaadd #333366 #333366 #aaaadd;
59 font-weight: bold; text-align: center; text-decoration: none;
59 font-weight: bold; text-align: center; text-decoration: none;
60 font-size: 10px;
60 font-size: 10px;
61 }
61 }
62 td.indexlinks a:hover { background-color: #6666aa; }
62 td.indexlinks a:hover { background-color: #6666aa; }
63 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
63 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
64 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
64 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
65 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
65 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
66
66
67 .search {
67 .search {
68 margin-right: 8px;
68 margin-right: 8px;
69 }
69 }
70
70
71 div#hint {
71 div#hint {
72 position: absolute;
72 position: absolute;
73 display: none;
73 display: none;
74 width: 250px;
74 width: 250px;
75 padding: 5px;
75 padding: 5px;
76 background: #ffc;
76 background: #ffc;
77 border: 1px solid yellow;
77 border: 1px solid yellow;
78 border-radius: 5px;
78 border-radius: 5px;
79 }
79 }
80
80
81 #searchform:hover div#hint { display: block; }
81 #searchform:hover div#hint { display: block; }
82
82
83 tr.thisrev a { color:#999999; text-decoration: none; }
83 tr.thisrev a { color:#999999; text-decoration: none; }
84 tr.thisrev pre { color:#009900; }
84 tr.thisrev pre { color:#009900; }
85 td.annotate {
85 td.annotate {
86 white-space: nowrap;
86 white-space: nowrap;
87 }
87 }
88 div.annotate-info {
88 div.annotate-info {
89 z-index: 5;
89 z-index: 5;
90 display: none;
90 display: none;
91 position: absolute;
91 position: absolute;
92 background-color: #FFFFFF;
92 background-color: #FFFFFF;
93 border: 1px solid #d9d8d1;
93 border: 1px solid #d9d8d1;
94 text-align: left;
94 text-align: left;
95 color: #000000;
95 color: #000000;
96 padding: 5px;
96 padding: 5px;
97 }
97 }
98 div.annotate-info a { color: #0000FF; text-decoration: underline; }
98 div.annotate-info a { color: #0000FF; text-decoration: underline; }
99 td.annotate:hover div.annotate-info { display: inline; }
99 td.annotate:hover div.annotate-info { display: inline; }
100
101 #diffopts-form {
102 padding-left: 8px;
103 display: none;
104 }
105
100 .linenr { color:#999999; text-decoration:none }
106 .linenr { color:#999999; text-decoration:none }
101 div.rss_logo { float: right; white-space: nowrap; }
107 div.rss_logo { float: right; white-space: nowrap; }
102 div.rss_logo a {
108 div.rss_logo a {
103 padding:3px 6px; line-height:10px;
109 padding:3px 6px; line-height:10px;
104 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
110 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
105 color:#ffffff; background-color:#ff6600;
111 color:#ffffff; background-color:#ff6600;
106 font-weight:bold; font-family:sans-serif; font-size:10px;
112 font-weight:bold; font-family:sans-serif; font-size:10px;
107 text-align:center; text-decoration:none;
113 text-align:center; text-decoration:none;
108 }
114 }
109 div.rss_logo a:hover { background-color:#ee5500; }
115 div.rss_logo a:hover { background-color:#ee5500; }
110 pre { margin: 0; }
116 pre { margin: 0; }
111 span.logtags span {
117 span.logtags span {
112 padding: 0px 4px;
118 padding: 0px 4px;
113 font-size: 10px;
119 font-size: 10px;
114 font-weight: normal;
120 font-weight: normal;
115 border: 1px solid;
121 border: 1px solid;
116 background-color: #ffaaff;
122 background-color: #ffaaff;
117 border-color: #ffccff #ff00ee #ff00ee #ffccff;
123 border-color: #ffccff #ff00ee #ff00ee #ffccff;
118 }
124 }
119 span.logtags span.tagtag {
125 span.logtags span.tagtag {
120 background-color: #ffffaa;
126 background-color: #ffffaa;
121 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
127 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
122 }
128 }
123 span.logtags span.branchtag {
129 span.logtags span.branchtag {
124 background-color: #aaffaa;
130 background-color: #aaffaa;
125 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
131 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
126 }
132 }
127 span.logtags span.inbranchtag {
133 span.logtags span.inbranchtag {
128 background-color: #d5dde6;
134 background-color: #d5dde6;
129 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
135 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
130 }
136 }
131 span.logtags span.bookmarktag {
137 span.logtags span.bookmarktag {
132 background-color: #afdffa;
138 background-color: #afdffa;
133 border-color: #ccecff #46ace6 #46ace6 #ccecff;
139 border-color: #ccecff #46ace6 #46ace6 #ccecff;
134 }
140 }
135 span.difflineplus { color:#008800; }
141 span.difflineplus { color:#008800; }
136 span.difflineminus { color:#cc0000; }
142 span.difflineminus { color:#cc0000; }
137 span.difflineat { color:#990099; }
143 span.difflineat { color:#990099; }
138 div.diffblocks { counter-reset: lineno; }
144 div.diffblocks { counter-reset: lineno; }
139 div.diffblock { counter-increment: lineno; }
145 div.diffblock { counter-increment: lineno; }
140 pre.sourcelines { position: relative; counter-reset: lineno; }
146 pre.sourcelines { position: relative; counter-reset: lineno; }
141 pre.sourcelines > span {
147 pre.sourcelines > span {
142 display: inline-block;
148 display: inline-block;
143 box-sizing: border-box;
149 box-sizing: border-box;
144 width: 100%;
150 width: 100%;
145 padding: 0 0 0 5em;
151 padding: 0 0 0 5em;
146 counter-increment: lineno;
152 counter-increment: lineno;
147 vertical-align: top;
153 vertical-align: top;
148 }
154 }
149 pre.sourcelines > span:before {
155 pre.sourcelines > span:before {
150 -moz-user-select: -moz-none;
156 -moz-user-select: -moz-none;
151 -khtml-user-select: none;
157 -khtml-user-select: none;
152 -webkit-user-select: none;
158 -webkit-user-select: none;
153 -ms-user-select: none;
159 -ms-user-select: none;
154 user-select: none;
160 user-select: none;
155 display: inline-block;
161 display: inline-block;
156 margin-left: -6em;
162 margin-left: -6em;
157 width: 4em;
163 width: 4em;
158 color: #999;
164 color: #999;
159 text-align: right;
165 text-align: right;
160 content: counters(lineno,".");
166 content: counters(lineno,".");
161 float: left;
167 float: left;
162 }
168 }
163 pre.sourcelines > a {
169 pre.sourcelines > a {
164 display: inline-block;
170 display: inline-block;
165 position: absolute;
171 position: absolute;
166 left: 0px;
172 left: 0px;
167 width: 4em;
173 width: 4em;
168 height: 1em;
174 height: 1em;
169 }
175 }
170 tr:target td,
176 tr:target td,
171 pre.sourcelines > span:target,
177 pre.sourcelines > span:target,
172 pre.sourcelines.stripes > span:target {
178 pre.sourcelines.stripes > span:target {
173 background-color: #bfdfff;
179 background-color: #bfdfff;
174 }
180 }
175
181
176 .description {
182 .description {
177 font-family: monospace;
183 font-family: monospace;
178 white-space: pre;
184 white-space: pre;
179 }
185 }
180
186
181 /* Followlines */
187 /* Followlines */
182 tbody.sourcelines > tr.followlines-selected,
188 tbody.sourcelines > tr.followlines-selected,
183 pre.sourcelines > span.followlines-selected {
189 pre.sourcelines > span.followlines-selected {
184 background-color: #99C7E9 !important;
190 background-color: #99C7E9 !important;
185 }
191 }
186
192
187 div#followlines {
193 div#followlines {
188 background-color: #B7B7B7;
194 background-color: #B7B7B7;
189 border: 1px solid #CCC;
195 border: 1px solid #CCC;
190 border-radius: 5px;
196 border-radius: 5px;
191 padding: 4px;
197 padding: 4px;
192 position: fixed;
198 position: fixed;
193 }
199 }
194
200
195 div.followlines-cancel {
201 div.followlines-cancel {
196 text-align: right;
202 text-align: right;
197 }
203 }
198
204
199 div.followlines-cancel > button {
205 div.followlines-cancel > button {
200 line-height: 80%;
206 line-height: 80%;
201 padding: 0;
207 padding: 0;
202 border: 0;
208 border: 0;
203 border-radius: 2px;
209 border-radius: 2px;
204 background-color: inherit;
210 background-color: inherit;
205 font-weight: bold;
211 font-weight: bold;
206 }
212 }
207
213
208 div.followlines-cancel > button:hover {
214 div.followlines-cancel > button:hover {
209 color: #FFFFFF;
215 color: #FFFFFF;
210 background-color: #CF1F1F;
216 background-color: #CF1F1F;
211 }
217 }
212
218
213 div.followlines-link {
219 div.followlines-link {
214 margin: 2px;
220 margin: 2px;
215 margin-top: 4px;
221 margin-top: 4px;
216 font-family: sans-serif;
222 font-family: sans-serif;
217 }
223 }
218
224
219 .btn-followlines {
225 .btn-followlines {
220 display: none;
226 display: none;
221 cursor: pointer;
227 cursor: pointer;
222 box-sizing: content-box;
228 box-sizing: content-box;
223 font-size: 11px;
229 font-size: 11px;
224 width: 13px;
230 width: 13px;
225 height: 13px;
231 height: 13px;
226 border-radius: 3px;
232 border-radius: 3px;
227 margin: 0px;
233 margin: 0px;
228 margin-top: -2px;
234 margin-top: -2px;
229 padding: 0px;
235 padding: 0px;
230 background-color: #E5FDE5;
236 background-color: #E5FDE5;
231 border: 1px solid #9BC19B;
237 border: 1px solid #9BC19B;
232 font-family: monospace;
238 font-family: monospace;
233 text-align: center;
239 text-align: center;
234 line-height: 5px;
240 line-height: 5px;
235 }
241 }
236
242
237 tr .btn-followlines {
243 tr .btn-followlines {
238 position: absolute;
244 position: absolute;
239 }
245 }
240
246
241 span .btn-followlines {
247 span .btn-followlines {
242 float: left;
248 float: left;
243 }
249 }
244
250
245 span.followlines-select .btn-followlines {
251 span.followlines-select .btn-followlines {
246 margin-left: -1.6em;
252 margin-left: -1.6em;
247 }
253 }
248
254
249 .btn-followlines:hover {
255 .btn-followlines:hover {
250 transform: scale(1.1, 1.1);
256 transform: scale(1.1, 1.1);
251 }
257 }
252
258
253 .btn-followlines .followlines-plus {
259 .btn-followlines .followlines-plus {
254 color: green;
260 color: green;
255 }
261 }
256
262
257 .btn-followlines .followlines-minus {
263 .btn-followlines .followlines-minus {
258 color: red;
264 color: red;
259 }
265 }
260
266
261 .btn-followlines-end {
267 .btn-followlines-end {
262 background-color: #ffdcdc;
268 background-color: #ffdcdc;
263 }
269 }
264
270
265 .sourcelines tr:hover .btn-followlines,
271 .sourcelines tr:hover .btn-followlines,
266 .sourcelines span.followlines-select:hover > .btn-followlines {
272 .sourcelines span.followlines-select:hover > .btn-followlines {
267 display: inline;
273 display: inline;
268 }
274 }
269
275
270 .btn-followlines-hidden,
276 .btn-followlines-hidden,
271 .sourcelines tr:hover .btn-followlines-hidden {
277 .sourcelines tr:hover .btn-followlines-hidden {
272 display: none;
278 display: none;
273 }
279 }
274
280
275 /* Graph */
281 /* Graph */
276 div#wrapper {
282 div#wrapper {
277 position: relative;
283 position: relative;
278 margin: 0;
284 margin: 0;
279 padding: 0;
285 padding: 0;
280 margin-top: 3px;
286 margin-top: 3px;
281 }
287 }
282
288
283 canvas {
289 canvas {
284 position: absolute;
290 position: absolute;
285 z-index: 5;
291 z-index: 5;
286 top: -0.9em;
292 top: -0.9em;
287 margin: 0;
293 margin: 0;
288 }
294 }
289
295
290 ul#nodebgs {
296 ul#nodebgs {
291 list-style: none inside none;
297 list-style: none inside none;
292 padding: 0;
298 padding: 0;
293 margin: 0;
299 margin: 0;
294 top: -0.7em;
300 top: -0.7em;
295 }
301 }
296
302
297 ul#graphnodes li, ul#nodebgs li {
303 ul#graphnodes li, ul#nodebgs li {
298 height: 39px;
304 height: 39px;
299 }
305 }
300
306
301 ul#graphnodes {
307 ul#graphnodes {
302 position: absolute;
308 position: absolute;
303 z-index: 10;
309 z-index: 10;
304 top: -0.8em;
310 top: -0.8em;
305 list-style: none inside none;
311 list-style: none inside none;
306 padding: 0;
312 padding: 0;
307 }
313 }
308
314
309 ul#graphnodes li .info {
315 ul#graphnodes li .info {
310 display: block;
316 display: block;
311 font-size: 100%;
317 font-size: 100%;
312 position: relative;
318 position: relative;
313 top: -3px;
319 top: -3px;
314 font-style: italic;
320 font-style: italic;
315 }
321 }
316
322
317 /* Comparison */
323 /* Comparison */
318 .legend {
324 .legend {
319 padding: 1.5% 0 1.5% 0;
325 padding: 1.5% 0 1.5% 0;
320 }
326 }
321
327
322 .legendinfo {
328 .legendinfo {
323 border: 1px solid #d9d8d1;
329 border: 1px solid #d9d8d1;
324 font-size: 80%;
330 font-size: 80%;
325 text-align: center;
331 text-align: center;
326 padding: 0.5%;
332 padding: 0.5%;
327 }
333 }
328
334
329 .equal {
335 .equal {
330 background-color: #ffffff;
336 background-color: #ffffff;
331 }
337 }
332
338
333 .delete {
339 .delete {
334 background-color: #faa;
340 background-color: #faa;
335 color: #333;
341 color: #333;
336 }
342 }
337
343
338 .insert {
344 .insert {
339 background-color: #ffa;
345 background-color: #ffa;
340 }
346 }
341
347
342 .replace {
348 .replace {
343 background-color: #e8e8e8;
349 background-color: #e8e8e8;
344 }
350 }
345
351
346 .comparison {
352 .comparison {
347 overflow-x: auto;
353 overflow-x: auto;
348 }
354 }
349
355
350 .header th {
356 .header th {
351 text-align: center;
357 text-align: center;
352 }
358 }
353
359
354 .block {
360 .block {
355 border-top: 1px solid #d9d8d1;
361 border-top: 1px solid #d9d8d1;
356 }
362 }
357
363
358 .scroll-loading {
364 .scroll-loading {
359 -webkit-animation: change_color 1s linear 0s infinite alternate;
365 -webkit-animation: change_color 1s linear 0s infinite alternate;
360 -moz-animation: change_color 1s linear 0s infinite alternate;
366 -moz-animation: change_color 1s linear 0s infinite alternate;
361 -o-animation: change_color 1s linear 0s infinite alternate;
367 -o-animation: change_color 1s linear 0s infinite alternate;
362 animation: change_color 1s linear 0s infinite alternate;
368 animation: change_color 1s linear 0s infinite alternate;
363 }
369 }
364
370
365 @-webkit-keyframes change_color {
371 @-webkit-keyframes change_color {
366 from { background-color: #A0CEFF; } to { }
372 from { background-color: #A0CEFF; } to { }
367 }
373 }
368 @-moz-keyframes change_color {
374 @-moz-keyframes change_color {
369 from { background-color: #A0CEFF; } to { }
375 from { background-color: #A0CEFF; } to { }
370 }
376 }
371 @-o-keyframes change_color {
377 @-o-keyframes change_color {
372 from { background-color: #A0CEFF; } to { }
378 from { background-color: #A0CEFF; } to { }
373 }
379 }
374 @keyframes change_color {
380 @keyframes change_color {
375 from { background-color: #A0CEFF; } to { }
381 from { background-color: #A0CEFF; } to { }
376 }
382 }
377
383
378 .scroll-loading-error {
384 .scroll-loading-error {
379 background-color: #FFCCCC !important;
385 background-color: #FFCCCC !important;
380 }
386 }
381
387
382 #doc {
388 #doc {
383 margin: 0 8px;
389 margin: 0 8px;
384 }
390 }
@@ -1,526 +1,533 b''
1 body {
1 body {
2 margin: 0;
2 margin: 0;
3 padding: 0;
3 padding: 0;
4 background: white;
4 background: white;
5 color: black;
5 color: black;
6 font-family: sans-serif;
6 font-family: sans-serif;
7 }
7 }
8
8
9 .container {
9 .container {
10 padding-left: 115px;
10 padding-left: 115px;
11 }
11 }
12
12
13 .main {
13 .main {
14 position: relative;
14 position: relative;
15 background: white;
15 background: white;
16 padding: 2em 2em 2em 0;
16 padding: 2em 2em 2em 0;
17 }
17 }
18
18
19 #.main {
19 #.main {
20 width: 98%;
20 width: 98%;
21 }
21 }
22
22
23 .overflow {
23 .overflow {
24 width: 100%;
24 width: 100%;
25 overflow: auto;
25 overflow: auto;
26 }
26 }
27
27
28 .menu {
28 .menu {
29 width: 90px;
29 width: 90px;
30 margin: 0;
30 margin: 0;
31 font-size: 80%;
31 font-size: 80%;
32 text-align: left;
32 text-align: left;
33 position: absolute;
33 position: absolute;
34 top: 20px;
34 top: 20px;
35 left: 20px;
35 left: 20px;
36 right: auto;
36 right: auto;
37 }
37 }
38
38
39 .menu ul {
39 .menu ul {
40 list-style: none;
40 list-style: none;
41 padding: 0;
41 padding: 0;
42 margin: 10px 0 0 0;
42 margin: 10px 0 0 0;
43 border-left: 2px solid #999;
43 border-left: 2px solid #999;
44 }
44 }
45
45
46 .menu li {
46 .menu li {
47 margin-bottom: 3px;
47 margin-bottom: 3px;
48 padding: 2px 4px;
48 padding: 2px 4px;
49 background: white;
49 background: white;
50 color: black;
50 color: black;
51 font-weight: normal;
51 font-weight: normal;
52 }
52 }
53
53
54 .menu li.active {
54 .menu li.active {
55 font-weight: bold;
55 font-weight: bold;
56 }
56 }
57
57
58 .menu img {
58 .menu img {
59 width: 75px;
59 width: 75px;
60 height: 90px;
60 height: 90px;
61 border: 0;
61 border: 0;
62 }
62 }
63
63
64 div.atom-logo {
64 div.atom-logo {
65 margin-top: 10px;
65 margin-top: 10px;
66 }
66 }
67
67
68 .atom-logo img{
68 .atom-logo img{
69 width: 14px;
69 width: 14px;
70 height: 14px;
70 height: 14px;
71 border: 0;
71 border: 0;
72 }
72 }
73
73
74 .menu a { color: black; display: block; }
74 .menu a { color: black; display: block; }
75
75
76 .search {
76 .search {
77 position: absolute;
77 position: absolute;
78 top: .7em;
78 top: .7em;
79 right: 2em;
79 right: 2em;
80 }
80 }
81
81
82 form.search div#hint {
82 form.search div#hint {
83 display: none;
83 display: none;
84 position: absolute;
84 position: absolute;
85 top: 40px;
85 top: 40px;
86 right: 0px;
86 right: 0px;
87 width: 190px;
87 width: 190px;
88 padding: 5px;
88 padding: 5px;
89 background: #ffc;
89 background: #ffc;
90 font-size: 70%;
90 font-size: 70%;
91 border: 1px solid yellow;
91 border: 1px solid yellow;
92 border-radius: 5px;
92 border-radius: 5px;
93 }
93 }
94
94
95 form.search:hover div#hint { display: block; }
95 form.search:hover div#hint { display: block; }
96
96
97 a { text-decoration:none; }
97 a { text-decoration:none; }
98 .age { white-space:nowrap; }
98 .age { white-space:nowrap; }
99 .date { white-space:nowrap; }
99 .date { white-space:nowrap; }
100 .indexlinks { white-space:nowrap; }
100 .indexlinks { white-space:nowrap; }
101 .parity0,
101 .parity0,
102 .stripes4 > :nth-child(4n+1),
102 .stripes4 > :nth-child(4n+1),
103 .stripes2 > :nth-child(2n+1) { background-color: #f0f0f0; }
103 .stripes2 > :nth-child(2n+1) { background-color: #f0f0f0; }
104 .parity1,
104 .parity1,
105 .stripes4 > :nth-child(4n+3),
105 .stripes4 > :nth-child(4n+3),
106 .stripes2 > :nth-child(2n+2) { background-color: white; }
106 .stripes2 > :nth-child(2n+2) { background-color: white; }
107 .plusline { color: green; }
107 .plusline { color: green; }
108 .minusline { color: #dc143c; } /* crimson */
108 .minusline { color: #dc143c; } /* crimson */
109 .atline { color: purple; }
109 .atline { color: purple; }
110
110
111 .diffstat-table {
111 .diffstat-table {
112 margin-top: 1em;
112 margin-top: 1em;
113 }
113 }
114 .diffstat-file {
114 .diffstat-file {
115 white-space: nowrap;
115 white-space: nowrap;
116 font-size: 90%;
116 font-size: 90%;
117 }
117 }
118 .diffstat-total {
118 .diffstat-total {
119 white-space: nowrap;
119 white-space: nowrap;
120 font-size: 90%;
120 font-size: 90%;
121 }
121 }
122 .diffstat-graph {
122 .diffstat-graph {
123 width: 100%;
123 width: 100%;
124 }
124 }
125 .diffstat-add {
125 .diffstat-add {
126 background-color: green;
126 background-color: green;
127 float: left;
127 float: left;
128 }
128 }
129 .diffstat-remove {
129 .diffstat-remove {
130 background-color: red;
130 background-color: red;
131 float: left;
131 float: left;
132 }
132 }
133
133
134 .navigate {
134 .navigate {
135 text-align: right;
135 text-align: right;
136 font-size: 60%;
136 font-size: 60%;
137 margin: 1em 0;
137 margin: 1em 0;
138 }
138 }
139
139
140 .tag {
140 .tag {
141 color: #999;
141 color: #999;
142 font-size: 70%;
142 font-size: 70%;
143 font-weight: normal;
143 font-weight: normal;
144 margin-left: .5em;
144 margin-left: .5em;
145 vertical-align: baseline;
145 vertical-align: baseline;
146 }
146 }
147
147
148 .branchhead {
148 .branchhead {
149 color: #000;
149 color: #000;
150 font-size: 80%;
150 font-size: 80%;
151 font-weight: normal;
151 font-weight: normal;
152 margin-left: .5em;
152 margin-left: .5em;
153 vertical-align: baseline;
153 vertical-align: baseline;
154 }
154 }
155
155
156 ul#graphnodes .branchhead {
156 ul#graphnodes .branchhead {
157 font-size: 75%;
157 font-size: 75%;
158 }
158 }
159
159
160 .branchname {
160 .branchname {
161 color: #000;
161 color: #000;
162 font-size: 60%;
162 font-size: 60%;
163 font-weight: normal;
163 font-weight: normal;
164 margin-left: .5em;
164 margin-left: .5em;
165 vertical-align: baseline;
165 vertical-align: baseline;
166 }
166 }
167
167
168 h3 .branchname {
168 h3 .branchname {
169 font-size: 80%;
169 font-size: 80%;
170 }
170 }
171
171
172 /* Common */
172 /* Common */
173 pre { margin: 0; }
173 pre { margin: 0; }
174
174
175 h2 { font-size: 120%; border-bottom: 1px solid #999; }
175 h2 { font-size: 120%; border-bottom: 1px solid #999; }
176 h2 a { color: #000; }
176 h2 a { color: #000; }
177 h3 {
177 h3 {
178 margin-top: +.7em;
178 margin-top: +.7em;
179 font-size: 100%;
179 font-size: 100%;
180 }
180 }
181
181
182 /* log and tags tables */
182 /* log and tags tables */
183 .bigtable {
183 .bigtable {
184 border-bottom: 1px solid #999;
184 border-bottom: 1px solid #999;
185 border-collapse: collapse;
185 border-collapse: collapse;
186 font-size: 90%;
186 font-size: 90%;
187 width: 100%;
187 width: 100%;
188 font-weight: normal;
188 font-weight: normal;
189 text-align: left;
189 text-align: left;
190 }
190 }
191
191
192 .bigtable td {
192 .bigtable td {
193 vertical-align: top;
193 vertical-align: top;
194 }
194 }
195
195
196 .bigtable th {
196 .bigtable th {
197 padding: 1px 4px;
197 padding: 1px 4px;
198 border-bottom: 1px solid #999;
198 border-bottom: 1px solid #999;
199 }
199 }
200 .bigtable tr { border: none; }
200 .bigtable tr { border: none; }
201 .bigtable .age { width: 7em; }
201 .bigtable .age { width: 7em; }
202 .bigtable .author { width: 15em; }
202 .bigtable .author { width: 15em; }
203 .bigtable .description { }
203 .bigtable .description { }
204 .bigtable .description .base { font-size: 70%; float: right; line-height: 1.66; }
204 .bigtable .description .base { font-size: 70%; float: right; line-height: 1.66; }
205 .bigtable .node { width: 5em; font-family: monospace;}
205 .bigtable .node { width: 5em; font-family: monospace;}
206 .bigtable .permissions { width: 8em; text-align: left;}
206 .bigtable .permissions { width: 8em; text-align: left;}
207 .bigtable .size { width: 5em; text-align: right; }
207 .bigtable .size { width: 5em; text-align: right; }
208 .bigtable .annotate { text-align: right; }
208 .bigtable .annotate { text-align: right; }
209 .bigtable td.annotate { font-size: smaller; }
209 .bigtable td.annotate { font-size: smaller; }
210 .bigtable td.source { font-size: inherit; }
210 .bigtable td.source { font-size: inherit; }
211 tr.thisrev a { color:#999999; text-decoration: none; }
211 tr.thisrev a { color:#999999; text-decoration: none; }
212 tr.thisrev td.source { color:#009900; }
212 tr.thisrev td.source { color:#009900; }
213 td.annotate {
213 td.annotate {
214 white-space: nowrap;
214 white-space: nowrap;
215 }
215 }
216 div.annotate-info {
216 div.annotate-info {
217 z-index: 5;
217 z-index: 5;
218 display: none;
218 display: none;
219 position: absolute;
219 position: absolute;
220 background-color: #FFFFFF;
220 background-color: #FFFFFF;
221 border: 1px solid #999;
221 border: 1px solid #999;
222 text-align: left;
222 text-align: left;
223 color: #000000;
223 color: #000000;
224 padding: 5px;
224 padding: 5px;
225 }
225 }
226 div.annotate-info a { color: #0000FF; }
226 div.annotate-info a { color: #0000FF; }
227 td.annotate:hover div.annotate-info { display: inline; }
227 td.annotate:hover div.annotate-info { display: inline; }
228
228
229 #diffopts-form {
230 font-size: smaller;
231 color: #424242;
232 padding-bottom: 10px;
233 display: none;
234 }
235
229 .source, .sourcefirst {
236 .source, .sourcefirst {
230 font-family: monospace;
237 font-family: monospace;
231 white-space: pre;
238 white-space: pre;
232 padding: 1px 4px;
239 padding: 1px 4px;
233 font-size: 90%;
240 font-size: 90%;
234 }
241 }
235 .sourcefirst { border-bottom: 1px solid #999; font-weight: bold; }
242 .sourcefirst { border-bottom: 1px solid #999; font-weight: bold; }
236 .source a { color: #999; font-size: smaller; font-family: monospace;}
243 .source a { color: #999; font-size: smaller; font-family: monospace;}
237 .bottomline { border-bottom: 1px solid #999; }
244 .bottomline { border-bottom: 1px solid #999; }
238
245
239 .sourcelines {
246 .sourcelines {
240 font-size: 90%;
247 font-size: 90%;
241 position: relative;
248 position: relative;
242 counter-reset: lineno;
249 counter-reset: lineno;
243 }
250 }
244
251
245 .wrap > span {
252 .wrap > span {
246 white-space: pre-wrap;
253 white-space: pre-wrap;
247 }
254 }
248
255
249 .linewraptoggle {
256 .linewraptoggle {
250 float: right;
257 float: right;
251 }
258 }
252
259
253 .diffblocks { counter-reset: lineno; }
260 .diffblocks { counter-reset: lineno; }
254 .diffblocks > div { counter-increment: lineno; }
261 .diffblocks > div { counter-increment: lineno; }
255
262
256 .sourcelines > span {
263 .sourcelines > span {
257 display: inline-block;
264 display: inline-block;
258 box-sizing: border-box;
265 box-sizing: border-box;
259 width: 100%;
266 width: 100%;
260 padding: 1px 0px 1px 5em;
267 padding: 1px 0px 1px 5em;
261 counter-increment: lineno;
268 counter-increment: lineno;
262 }
269 }
263
270
264 .sourcelines > span:before {
271 .sourcelines > span:before {
265 -moz-user-select: -moz-none;
272 -moz-user-select: -moz-none;
266 -khtml-user-select: none;
273 -khtml-user-select: none;
267 -webkit-user-select: none;
274 -webkit-user-select: none;
268 -ms-user-select: none;
275 -ms-user-select: none;
269 user-select: none;
276 user-select: none;
270 display: inline-block;
277 display: inline-block;
271 margin-left: -6em;
278 margin-left: -6em;
272 width: 4em;
279 width: 4em;
273 font-size: smaller;
280 font-size: smaller;
274 color: #999;
281 color: #999;
275 text-align: right;
282 text-align: right;
276 content: counters(lineno, ".");
283 content: counters(lineno, ".");
277 float: left;
284 float: left;
278 }
285 }
279
286
280 .sourcelines > span:target, tr:target td {
287 .sourcelines > span:target, tr:target td {
281 background-color: #bfdfff;
288 background-color: #bfdfff;
282 }
289 }
283
290
284 /* Followlines */
291 /* Followlines */
285 tbody.sourcelines > tr.followlines-selected,
292 tbody.sourcelines > tr.followlines-selected,
286 pre.sourcelines > span.followlines-selected {
293 pre.sourcelines > span.followlines-selected {
287 background-color: #99C7E9;
294 background-color: #99C7E9;
288 }
295 }
289
296
290 div#followlines {
297 div#followlines {
291 background-color: #B7B7B7;
298 background-color: #B7B7B7;
292 border: 1px solid #CCC;
299 border: 1px solid #CCC;
293 border-radius: 5px;
300 border-radius: 5px;
294 padding: 4px;
301 padding: 4px;
295 position: fixed;
302 position: fixed;
296 }
303 }
297
304
298 div.followlines-cancel {
305 div.followlines-cancel {
299 text-align: right;
306 text-align: right;
300 }
307 }
301
308
302 div.followlines-cancel > button {
309 div.followlines-cancel > button {
303 line-height: 80%;
310 line-height: 80%;
304 padding: 0;
311 padding: 0;
305 border: 0;
312 border: 0;
306 border-radius: 2px;
313 border-radius: 2px;
307 background-color: inherit;
314 background-color: inherit;
308 font-weight: bold;
315 font-weight: bold;
309 }
316 }
310
317
311 div.followlines-cancel > button:hover {
318 div.followlines-cancel > button:hover {
312 color: #FFFFFF;
319 color: #FFFFFF;
313 background-color: #CF1F1F;
320 background-color: #CF1F1F;
314 }
321 }
315
322
316 div.followlines-link {
323 div.followlines-link {
317 margin: 2px;
324 margin: 2px;
318 margin-top: 4px;
325 margin-top: 4px;
319 font-family: sans-serif;
326 font-family: sans-serif;
320 }
327 }
321
328
322 .btn-followlines {
329 .btn-followlines {
323 display: none;
330 display: none;
324 cursor: pointer;
331 cursor: pointer;
325 box-sizing: content-box;
332 box-sizing: content-box;
326 font-size: 12px;
333 font-size: 12px;
327 width: 13px;
334 width: 13px;
328 height: 13px;
335 height: 13px;
329 border-radius: 3px;
336 border-radius: 3px;
330 margin: 0px;
337 margin: 0px;
331 margin-top: -2px;
338 margin-top: -2px;
332 padding: 0px;
339 padding: 0px;
333 background-color: #E5FDE5;
340 background-color: #E5FDE5;
334 border: 1px solid #9BC19B;
341 border: 1px solid #9BC19B;
335 font-family: monospace;
342 font-family: monospace;
336 text-align: center;
343 text-align: center;
337 line-height: 5px;
344 line-height: 5px;
338 }
345 }
339
346
340 tr .btn-followlines {
347 tr .btn-followlines {
341 position: absolute;
348 position: absolute;
342 }
349 }
343
350
344 span .btn-followlines {
351 span .btn-followlines {
345 float: left;
352 float: left;
346 }
353 }
347
354
348 span.followlines-select .btn-followlines {
355 span.followlines-select .btn-followlines {
349 margin-left: -1.5em;
356 margin-left: -1.5em;
350 }
357 }
351
358
352 .btn-followlines:hover {
359 .btn-followlines:hover {
353 transform: scale(1.2, 1.2);
360 transform: scale(1.2, 1.2);
354 }
361 }
355
362
356 .btn-followlines .followlines-plus {
363 .btn-followlines .followlines-plus {
357 color: green;
364 color: green;
358 }
365 }
359
366
360 .btn-followlines .followlines-minus {
367 .btn-followlines .followlines-minus {
361 color: red;
368 color: red;
362 }
369 }
363
370
364 .btn-followlines-end {
371 .btn-followlines-end {
365 background-color: #ffdcdc;
372 background-color: #ffdcdc;
366 }
373 }
367
374
368 .sourcelines tr:hover .btn-followlines,
375 .sourcelines tr:hover .btn-followlines,
369 .sourcelines span.followlines-select:hover > .btn-followlines {
376 .sourcelines span.followlines-select:hover > .btn-followlines {
370 display: inline;
377 display: inline;
371 }
378 }
372
379
373 .btn-followlines-hidden,
380 .btn-followlines-hidden,
374 .sourcelines tr:hover .btn-followlines-hidden {
381 .sourcelines tr:hover .btn-followlines-hidden {
375 display: none;
382 display: none;
376 }
383 }
377
384
378 .sourcelines > a {
385 .sourcelines > a {
379 display: inline-block;
386 display: inline-block;
380 position: absolute;
387 position: absolute;
381 left: 0px;
388 left: 0px;
382 width: 4em;
389 width: 4em;
383 height: 1em;
390 height: 1em;
384 }
391 }
385
392
386 .fileline { font-family: monospace; }
393 .fileline { font-family: monospace; }
387 .fileline img { border: 0; }
394 .fileline img { border: 0; }
388
395
389 .tagEntry .closed { color: #99f; }
396 .tagEntry .closed { color: #99f; }
390
397
391 /* Changeset entry */
398 /* Changeset entry */
392 #changesetEntry {
399 #changesetEntry {
393 border-collapse: collapse;
400 border-collapse: collapse;
394 font-size: 90%;
401 font-size: 90%;
395 width: 100%;
402 width: 100%;
396 margin-bottom: 1em;
403 margin-bottom: 1em;
397 }
404 }
398
405
399 #changesetEntry th {
406 #changesetEntry th {
400 padding: 1px 4px;
407 padding: 1px 4px;
401 width: 4em;
408 width: 4em;
402 text-align: right;
409 text-align: right;
403 font-weight: normal;
410 font-weight: normal;
404 color: #999;
411 color: #999;
405 margin-right: .5em;
412 margin-right: .5em;
406 vertical-align: top;
413 vertical-align: top;
407 }
414 }
408
415
409 div.description {
416 div.description {
410 border-left: 2px solid #999;
417 border-left: 2px solid #999;
411 margin: 1em 0 1em 0;
418 margin: 1em 0 1em 0;
412 padding: .3em;
419 padding: .3em;
413 white-space: pre;
420 white-space: pre;
414 font-family: monospace;
421 font-family: monospace;
415 }
422 }
416
423
417 /* Graph */
424 /* Graph */
418 div#wrapper {
425 div#wrapper {
419 position: relative;
426 position: relative;
420 border-top: 1px solid black;
427 border-top: 1px solid black;
421 border-bottom: 1px solid black;
428 border-bottom: 1px solid black;
422 margin: 0;
429 margin: 0;
423 padding: 0;
430 padding: 0;
424 }
431 }
425
432
426 canvas {
433 canvas {
427 position: absolute;
434 position: absolute;
428 z-index: 5;
435 z-index: 5;
429 top: -0.7em;
436 top: -0.7em;
430 margin: 0;
437 margin: 0;
431 }
438 }
432
439
433 ul#graphnodes {
440 ul#graphnodes {
434 position: absolute;
441 position: absolute;
435 z-index: 10;
442 z-index: 10;
436 top: -1.0em;
443 top: -1.0em;
437 list-style: none inside none;
444 list-style: none inside none;
438 padding: 0;
445 padding: 0;
439 }
446 }
440
447
441 ul#nodebgs {
448 ul#nodebgs {
442 list-style: none inside none;
449 list-style: none inside none;
443 padding: 0;
450 padding: 0;
444 margin: 0;
451 margin: 0;
445 top: -0.7em;
452 top: -0.7em;
446 }
453 }
447
454
448 ul#graphnodes li, ul#nodebgs li {
455 ul#graphnodes li, ul#nodebgs li {
449 height: 39px;
456 height: 39px;
450 }
457 }
451
458
452 ul#graphnodes li .info {
459 ul#graphnodes li .info {
453 display: block;
460 display: block;
454 font-size: 70%;
461 font-size: 70%;
455 position: relative;
462 position: relative;
456 top: -3px;
463 top: -3px;
457 }
464 }
458
465
459 /* Comparison */
466 /* Comparison */
460 .legend {
467 .legend {
461 padding: 1.5% 0 1.5% 0;
468 padding: 1.5% 0 1.5% 0;
462 }
469 }
463
470
464 .legendinfo {
471 .legendinfo {
465 border: 1px solid #999;
472 border: 1px solid #999;
466 font-size: 80%;
473 font-size: 80%;
467 text-align: center;
474 text-align: center;
468 padding: 0.5%;
475 padding: 0.5%;
469 }
476 }
470
477
471 .equal {
478 .equal {
472 background-color: #ffffff;
479 background-color: #ffffff;
473 }
480 }
474
481
475 .delete {
482 .delete {
476 background-color: #faa;
483 background-color: #faa;
477 color: #333;
484 color: #333;
478 }
485 }
479
486
480 .insert {
487 .insert {
481 background-color: #ffa;
488 background-color: #ffa;
482 }
489 }
483
490
484 .replace {
491 .replace {
485 background-color: #e8e8e8;
492 background-color: #e8e8e8;
486 }
493 }
487
494
488 .header {
495 .header {
489 text-align: center;
496 text-align: center;
490 }
497 }
491
498
492 .block {
499 .block {
493 border-top: 1px solid #999;
500 border-top: 1px solid #999;
494 }
501 }
495
502
496 .breadcrumb {
503 .breadcrumb {
497 color: gray;
504 color: gray;
498 }
505 }
499
506
500 .breadcrumb a {
507 .breadcrumb a {
501 color: blue;
508 color: blue;
502 }
509 }
503
510
504 .scroll-loading {
511 .scroll-loading {
505 -webkit-animation: change_color 1s linear 0s infinite alternate;
512 -webkit-animation: change_color 1s linear 0s infinite alternate;
506 -moz-animation: change_color 1s linear 0s infinite alternate;
513 -moz-animation: change_color 1s linear 0s infinite alternate;
507 -o-animation: change_color 1s linear 0s infinite alternate;
514 -o-animation: change_color 1s linear 0s infinite alternate;
508 animation: change_color 1s linear 0s infinite alternate;
515 animation: change_color 1s linear 0s infinite alternate;
509 }
516 }
510
517
511 @-webkit-keyframes change_color {
518 @-webkit-keyframes change_color {
512 from { background-color: #A0CEFF; } to { }
519 from { background-color: #A0CEFF; } to { }
513 }
520 }
514 @-moz-keyframes change_color {
521 @-moz-keyframes change_color {
515 from { background-color: #A0CEFF; } to { }
522 from { background-color: #A0CEFF; } to { }
516 }
523 }
517 @-o-keyframes change_color {
524 @-o-keyframes change_color {
518 from { background-color: #A0CEFF; } to { }
525 from { background-color: #A0CEFF; } to { }
519 }
526 }
520 @keyframes change_color {
527 @keyframes change_color {
521 from { background-color: #A0CEFF; } to { }
528 from { background-color: #A0CEFF; } to { }
522 }
529 }
523
530
524 .scroll-loading-error {
531 .scroll-loading-error {
525 background-color: #FFCCCC !important;
532 background-color: #FFCCCC !important;
526 }
533 }
@@ -1,874 +1,880 b''
1 #require serve
1 #require serve
2
2
3 Some tests for hgweb. Tests static files, plain files and different 404's.
3 Some tests for hgweb. Tests static files, plain files and different 404's.
4
4
5 $ hg init test
5 $ hg init test
6 $ cd test
6 $ cd test
7 $ mkdir da
7 $ mkdir da
8 $ echo foo > da/foo
8 $ echo foo > da/foo
9 $ echo foo > foo
9 $ echo foo > foo
10 $ hg ci -Ambase
10 $ hg ci -Ambase
11 adding da/foo
11 adding da/foo
12 adding foo
12 adding foo
13 $ hg bookmark -r0 '@'
13 $ hg bookmark -r0 '@'
14 $ hg bookmark -r0 'a b c'
14 $ hg bookmark -r0 'a b c'
15 $ hg bookmark -r0 'd/e/f'
15 $ hg bookmark -r0 'd/e/f'
16 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
16 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
17 $ cat hg.pid >> $DAEMON_PIDS
17 $ cat hg.pid >> $DAEMON_PIDS
18
18
19 manifest
19 manifest
20
20
21 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=raw')
21 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=raw')
22 200 Script output follows
22 200 Script output follows
23
23
24
24
25 drwxr-xr-x da
25 drwxr-xr-x da
26 -rw-r--r-- 4 foo
26 -rw-r--r-- 4 foo
27
27
28
28
29 $ (get-with-headers.py localhost:$HGPORT 'file/tip/da?style=raw')
29 $ (get-with-headers.py localhost:$HGPORT 'file/tip/da?style=raw')
30 200 Script output follows
30 200 Script output follows
31
31
32
32
33 -rw-r--r-- 4 foo
33 -rw-r--r-- 4 foo
34
34
35
35
36
36
37 plain file
37 plain file
38
38
39 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?style=raw'
39 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?style=raw'
40 200 Script output follows
40 200 Script output follows
41
41
42 foo
42 foo
43
43
44 should give a 404 - static file that does not exist
44 should give a 404 - static file that does not exist
45
45
46 $ get-with-headers.py localhost:$HGPORT 'static/bogus'
46 $ get-with-headers.py localhost:$HGPORT 'static/bogus'
47 404 Not Found
47 404 Not Found
48
48
49 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
49 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
50 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
50 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
51 <head>
51 <head>
52 <link rel="icon" href="/static/hgicon.png" type="image/png" />
52 <link rel="icon" href="/static/hgicon.png" type="image/png" />
53 <meta name="robots" content="index, nofollow" />
53 <meta name="robots" content="index, nofollow" />
54 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
54 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
55 <script type="text/javascript" src="/static/mercurial.js"></script>
55 <script type="text/javascript" src="/static/mercurial.js"></script>
56
56
57 <title>test: error</title>
57 <title>test: error</title>
58 </head>
58 </head>
59 <body>
59 <body>
60
60
61 <div class="container">
61 <div class="container">
62 <div class="menu">
62 <div class="menu">
63 <div class="logo">
63 <div class="logo">
64 <a href="https://mercurial-scm.org/">
64 <a href="https://mercurial-scm.org/">
65 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
65 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
66 </div>
66 </div>
67 <ul>
67 <ul>
68 <li><a href="/shortlog">log</a></li>
68 <li><a href="/shortlog">log</a></li>
69 <li><a href="/graph">graph</a></li>
69 <li><a href="/graph">graph</a></li>
70 <li><a href="/tags">tags</a></li>
70 <li><a href="/tags">tags</a></li>
71 <li><a href="/bookmarks">bookmarks</a></li>
71 <li><a href="/bookmarks">bookmarks</a></li>
72 <li><a href="/branches">branches</a></li>
72 <li><a href="/branches">branches</a></li>
73 </ul>
73 </ul>
74 <ul>
74 <ul>
75 <li><a href="/help">help</a></li>
75 <li><a href="/help">help</a></li>
76 </ul>
76 </ul>
77 </div>
77 </div>
78
78
79 <div class="main">
79 <div class="main">
80
80
81 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
81 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
82 <h3>error</h3>
82 <h3>error</h3>
83
83
84
84
85 <form class="search" action="/log">
85 <form class="search" action="/log">
86
86
87 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
87 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
88 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
88 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
89 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
89 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
90 </form>
90 </form>
91
91
92 <div class="description">
92 <div class="description">
93 <p>
93 <p>
94 An error occurred while processing your request:
94 An error occurred while processing your request:
95 </p>
95 </p>
96 <p>
96 <p>
97 Not Found
97 Not Found
98 </p>
98 </p>
99 </div>
99 </div>
100 </div>
100 </div>
101 </div>
101 </div>
102
102
103
103
104
104
105 </body>
105 </body>
106 </html>
106 </html>
107
107
108 [1]
108 [1]
109
109
110 should give a 404 - bad revision
110 should give a 404 - bad revision
111
111
112 $ get-with-headers.py localhost:$HGPORT 'file/spam/foo?style=raw'
112 $ get-with-headers.py localhost:$HGPORT 'file/spam/foo?style=raw'
113 404 Not Found
113 404 Not Found
114
114
115
115
116 error: revision not found: spam
116 error: revision not found: spam
117 [1]
117 [1]
118
118
119 should give a 400 - bad command
119 should give a 400 - bad command
120
120
121 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?cmd=spam&style=raw'
121 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?cmd=spam&style=raw'
122 400* (glob)
122 400* (glob)
123
123
124
124
125 error: no such method: spam
125 error: no such method: spam
126 [1]
126 [1]
127
127
128 $ get-with-headers.py --headeronly localhost:$HGPORT '?cmd=spam'
128 $ get-with-headers.py --headeronly localhost:$HGPORT '?cmd=spam'
129 400 no such method: spam
129 400 no such method: spam
130 [1]
130 [1]
131
131
132 should give a 400 - bad command as a part of url path (issue4071)
132 should give a 400 - bad command as a part of url path (issue4071)
133
133
134 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam'
134 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam'
135 400 no such method: spam
135 400 no such method: spam
136 [1]
136 [1]
137
137
138 $ get-with-headers.py --headeronly localhost:$HGPORT 'raw-spam'
138 $ get-with-headers.py --headeronly localhost:$HGPORT 'raw-spam'
139 400 no such method: spam
139 400 no such method: spam
140 [1]
140 [1]
141
141
142 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam/tip/foo'
142 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam/tip/foo'
143 400 no such method: spam
143 400 no such method: spam
144 [1]
144 [1]
145
145
146 should give a 404 - file does not exist
146 should give a 404 - file does not exist
147
147
148 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork?style=raw'
148 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork?style=raw'
149 404 Not Found
149 404 Not Found
150
150
151
151
152 error: bork@2ef0ac749a14: not found in manifest
152 error: bork@2ef0ac749a14: not found in manifest
153 [1]
153 [1]
154 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork'
154 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork'
155 404 Not Found
155 404 Not Found
156
156
157 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
157 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
158 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
158 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
159 <head>
159 <head>
160 <link rel="icon" href="/static/hgicon.png" type="image/png" />
160 <link rel="icon" href="/static/hgicon.png" type="image/png" />
161 <meta name="robots" content="index, nofollow" />
161 <meta name="robots" content="index, nofollow" />
162 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
162 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
163 <script type="text/javascript" src="/static/mercurial.js"></script>
163 <script type="text/javascript" src="/static/mercurial.js"></script>
164
164
165 <title>test: error</title>
165 <title>test: error</title>
166 </head>
166 </head>
167 <body>
167 <body>
168
168
169 <div class="container">
169 <div class="container">
170 <div class="menu">
170 <div class="menu">
171 <div class="logo">
171 <div class="logo">
172 <a href="https://mercurial-scm.org/">
172 <a href="https://mercurial-scm.org/">
173 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
173 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
174 </div>
174 </div>
175 <ul>
175 <ul>
176 <li><a href="/shortlog">log</a></li>
176 <li><a href="/shortlog">log</a></li>
177 <li><a href="/graph">graph</a></li>
177 <li><a href="/graph">graph</a></li>
178 <li><a href="/tags">tags</a></li>
178 <li><a href="/tags">tags</a></li>
179 <li><a href="/bookmarks">bookmarks</a></li>
179 <li><a href="/bookmarks">bookmarks</a></li>
180 <li><a href="/branches">branches</a></li>
180 <li><a href="/branches">branches</a></li>
181 </ul>
181 </ul>
182 <ul>
182 <ul>
183 <li><a href="/help">help</a></li>
183 <li><a href="/help">help</a></li>
184 </ul>
184 </ul>
185 </div>
185 </div>
186
186
187 <div class="main">
187 <div class="main">
188
188
189 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
189 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
190 <h3>error</h3>
190 <h3>error</h3>
191
191
192
192
193 <form class="search" action="/log">
193 <form class="search" action="/log">
194
194
195 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
195 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
196 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
196 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
197 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
197 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
198 </form>
198 </form>
199
199
200 <div class="description">
200 <div class="description">
201 <p>
201 <p>
202 An error occurred while processing your request:
202 An error occurred while processing your request:
203 </p>
203 </p>
204 <p>
204 <p>
205 bork@2ef0ac749a14: not found in manifest
205 bork@2ef0ac749a14: not found in manifest
206 </p>
206 </p>
207 </div>
207 </div>
208 </div>
208 </div>
209 </div>
209 </div>
210
210
211
211
212
212
213 </body>
213 </body>
214 </html>
214 </html>
215
215
216 [1]
216 [1]
217 $ get-with-headers.py localhost:$HGPORT 'diff/tip/bork?style=raw'
217 $ get-with-headers.py localhost:$HGPORT 'diff/tip/bork?style=raw'
218 404 Not Found
218 404 Not Found
219
219
220
220
221 error: bork@2ef0ac749a14: not found in manifest
221 error: bork@2ef0ac749a14: not found in manifest
222 [1]
222 [1]
223
223
224 try bad style
224 try bad style
225
225
226 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=foobar')
226 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=foobar')
227 200 Script output follows
227 200 Script output follows
228
228
229 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
229 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
230 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
230 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
231 <head>
231 <head>
232 <link rel="icon" href="/static/hgicon.png" type="image/png" />
232 <link rel="icon" href="/static/hgicon.png" type="image/png" />
233 <meta name="robots" content="index, nofollow" />
233 <meta name="robots" content="index, nofollow" />
234 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
234 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
235 <script type="text/javascript" src="/static/mercurial.js"></script>
235 <script type="text/javascript" src="/static/mercurial.js"></script>
236
236
237 <title>test: 2ef0ac749a14 /</title>
237 <title>test: 2ef0ac749a14 /</title>
238 </head>
238 </head>
239 <body>
239 <body>
240
240
241 <div class="container">
241 <div class="container">
242 <div class="menu">
242 <div class="menu">
243 <div class="logo">
243 <div class="logo">
244 <a href="https://mercurial-scm.org/">
244 <a href="https://mercurial-scm.org/">
245 <img src="/static/hglogo.png" alt="mercurial" /></a>
245 <img src="/static/hglogo.png" alt="mercurial" /></a>
246 </div>
246 </div>
247 <ul>
247 <ul>
248 <li><a href="/shortlog/tip">log</a></li>
248 <li><a href="/shortlog/tip">log</a></li>
249 <li><a href="/graph/tip">graph</a></li>
249 <li><a href="/graph/tip">graph</a></li>
250 <li><a href="/tags">tags</a></li>
250 <li><a href="/tags">tags</a></li>
251 <li><a href="/bookmarks">bookmarks</a></li>
251 <li><a href="/bookmarks">bookmarks</a></li>
252 <li><a href="/branches">branches</a></li>
252 <li><a href="/branches">branches</a></li>
253 </ul>
253 </ul>
254 <ul>
254 <ul>
255 <li><a href="/rev/tip">changeset</a></li>
255 <li><a href="/rev/tip">changeset</a></li>
256 <li class="active">browse</li>
256 <li class="active">browse</li>
257 </ul>
257 </ul>
258 <ul>
258 <ul>
259
259
260 </ul>
260 </ul>
261 <ul>
261 <ul>
262 <li><a href="/help">help</a></li>
262 <li><a href="/help">help</a></li>
263 </ul>
263 </ul>
264 </div>
264 </div>
265
265
266 <div class="main">
266 <div class="main">
267 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
267 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
268 <h3>
268 <h3>
269 directory / @ 0:<a href="/rev/2ef0ac749a14">2ef0ac749a14</a>
269 directory / @ 0:<a href="/rev/2ef0ac749a14">2ef0ac749a14</a>
270 <span class="tag">tip</span> <span class="tag">@</span> <span class="tag">a b c</span> <span class="tag">d/e/f</span>
270 <span class="tag">tip</span> <span class="tag">@</span> <span class="tag">a b c</span> <span class="tag">d/e/f</span>
271 </h3>
271 </h3>
272
272
273
273
274 <form class="search" action="/log">
274 <form class="search" action="/log">
275
275
276 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
276 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
277 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
277 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
278 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
278 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
279 </form>
279 </form>
280
280
281 <table class="bigtable">
281 <table class="bigtable">
282 <thead>
282 <thead>
283 <tr>
283 <tr>
284 <th class="name">name</th>
284 <th class="name">name</th>
285 <th class="size">size</th>
285 <th class="size">size</th>
286 <th class="permissions">permissions</th>
286 <th class="permissions">permissions</th>
287 </tr>
287 </tr>
288 </thead>
288 </thead>
289 <tbody class="stripes2">
289 <tbody class="stripes2">
290 <tr class="fileline">
290 <tr class="fileline">
291 <td class="name"><a href="/file/tip/">[up]</a></td>
291 <td class="name"><a href="/file/tip/">[up]</a></td>
292 <td class="size"></td>
292 <td class="size"></td>
293 <td class="permissions">drwxr-xr-x</td>
293 <td class="permissions">drwxr-xr-x</td>
294 </tr>
294 </tr>
295
295
296 <tr class="fileline">
296 <tr class="fileline">
297 <td class="name">
297 <td class="name">
298 <a href="/file/tip/da">
298 <a href="/file/tip/da">
299 <img src="/static/coal-folder.png" alt="dir."/> da/
299 <img src="/static/coal-folder.png" alt="dir."/> da/
300 </a>
300 </a>
301 <a href="/file/tip/da/">
301 <a href="/file/tip/da/">
302
302
303 </a>
303 </a>
304 </td>
304 </td>
305 <td class="size"></td>
305 <td class="size"></td>
306 <td class="permissions">drwxr-xr-x</td>
306 <td class="permissions">drwxr-xr-x</td>
307 </tr>
307 </tr>
308
308
309 <tr class="fileline">
309 <tr class="fileline">
310 <td class="filename">
310 <td class="filename">
311 <a href="/file/tip/foo">
311 <a href="/file/tip/foo">
312 <img src="/static/coal-file.png" alt="file"/> foo
312 <img src="/static/coal-file.png" alt="file"/> foo
313 </a>
313 </a>
314 </td>
314 </td>
315 <td class="size">4</td>
315 <td class="size">4</td>
316 <td class="permissions">-rw-r--r--</td>
316 <td class="permissions">-rw-r--r--</td>
317 </tr>
317 </tr>
318 </tbody>
318 </tbody>
319 </table>
319 </table>
320 </div>
320 </div>
321 </div>
321 </div>
322
322
323
323
324 </body>
324 </body>
325 </html>
325 </html>
326
326
327
327
328 stop and restart
328 stop and restart
329
329
330 $ killdaemons.py
330 $ killdaemons.py
331 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
331 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
332 $ cat hg.pid >> $DAEMON_PIDS
332 $ cat hg.pid >> $DAEMON_PIDS
333
333
334 Test the access/error files are opened in append mode
334 Test the access/error files are opened in append mode
335
335
336 $ $PYTHON -c "print len(file('access.log').readlines()), 'log lines written'"
336 $ $PYTHON -c "print len(file('access.log').readlines()), 'log lines written'"
337 14 log lines written
337 14 log lines written
338
338
339 static file
339 static file
340
340
341 $ get-with-headers.py --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server
341 $ get-with-headers.py --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server
342 200 Script output follows
342 200 Script output follows
343 content-length: 9007
343 content-length: 9066
344 content-type: text/css
344 content-type: text/css
345
345
346 body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; }
346 body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; }
347 a { color:#0000cc; }
347 a { color:#0000cc; }
348 a:hover, a:visited, a:active { color:#880000; }
348 a:hover, a:visited, a:active { color:#880000; }
349 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
349 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
350 div.page_header a:visited { color:#0000cc; }
350 div.page_header a:visited { color:#0000cc; }
351 div.page_header a:hover { color:#880000; }
351 div.page_header a:hover { color:#880000; }
352 div.page_nav {
352 div.page_nav {
353 padding:8px;
353 padding:8px;
354 display: flex;
354 display: flex;
355 justify-content: space-between;
355 justify-content: space-between;
356 align-items: center;
356 align-items: center;
357 }
357 }
358 div.page_nav a:visited { color:#0000cc; }
358 div.page_nav a:visited { color:#0000cc; }
359 div.extra_nav {
359 div.extra_nav {
360 padding: 8px;
360 padding: 8px;
361 }
361 }
362 div.extra_nav a:visited {
362 div.extra_nav a:visited {
363 color: #0000cc;
363 color: #0000cc;
364 }
364 }
365 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
365 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
366 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
366 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
367 div.page_footer_text { float:left; color:#555555; font-style:italic; }
367 div.page_footer_text { float:left; color:#555555; font-style:italic; }
368 div.page_body { padding:8px; }
368 div.page_body { padding:8px; }
369 div.title, a.title {
369 div.title, a.title {
370 display:block; padding:6px 8px;
370 display:block; padding:6px 8px;
371 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
371 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
372 }
372 }
373 a.title:hover { background-color: #d9d8d1; }
373 a.title:hover { background-color: #d9d8d1; }
374 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
374 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
375 div.log_body { padding:8px 8px 8px 150px; }
375 div.log_body { padding:8px 8px 8px 150px; }
376 .age { white-space:nowrap; }
376 .age { white-space:nowrap; }
377 span.age { position:relative; float:left; width:142px; font-style:italic; }
377 span.age { position:relative; float:left; width:142px; font-style:italic; }
378 div.log_link {
378 div.log_link {
379 padding:0px 8px;
379 padding:0px 8px;
380 font-size:10px; font-family:sans-serif; font-style:normal;
380 font-size:10px; font-family:sans-serif; font-style:normal;
381 position:relative; float:left; width:136px;
381 position:relative; float:left; width:136px;
382 }
382 }
383 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
383 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
384 a.list { text-decoration:none; color:#000000; }
384 a.list { text-decoration:none; color:#000000; }
385 a.list:hover { text-decoration:underline; color:#880000; }
385 a.list:hover { text-decoration:underline; color:#880000; }
386 table { padding:8px 4px; }
386 table { padding:8px 4px; }
387 th { padding:2px 5px; font-size:12px; text-align:left; }
387 th { padding:2px 5px; font-size:12px; text-align:left; }
388 .parity0 { background-color:#ffffff; }
388 .parity0 { background-color:#ffffff; }
389 tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+4) { background-color:#f6f6f0; }
389 tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+4) { background-color:#f6f6f0; }
390 tr.light:hover, .parity0:hover, tr.dark:hover, .parity1:hover,
390 tr.light:hover, .parity0:hover, tr.dark:hover, .parity1:hover,
391 pre.sourcelines.stripes > :nth-child(4n+2):hover,
391 pre.sourcelines.stripes > :nth-child(4n+2):hover,
392 pre.sourcelines.stripes > :nth-child(4n+4):hover,
392 pre.sourcelines.stripes > :nth-child(4n+4):hover,
393 pre.sourcelines.stripes > :nth-child(4n+1):hover + :nth-child(4n+2),
393 pre.sourcelines.stripes > :nth-child(4n+1):hover + :nth-child(4n+2),
394 pre.sourcelines.stripes > :nth-child(4n+3):hover + :nth-child(4n+4) { background-color:#edece6; }
394 pre.sourcelines.stripes > :nth-child(4n+3):hover + :nth-child(4n+4) { background-color:#edece6; }
395 td { padding:2px 5px; font-size:12px; vertical-align:top; }
395 td { padding:2px 5px; font-size:12px; vertical-align:top; }
396 td.closed { background-color: #99f; }
396 td.closed { background-color: #99f; }
397 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
397 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
398 td.indexlinks { white-space: nowrap; }
398 td.indexlinks { white-space: nowrap; }
399 td.indexlinks a {
399 td.indexlinks a {
400 padding: 2px 5px; line-height: 10px;
400 padding: 2px 5px; line-height: 10px;
401 border: 1px solid;
401 border: 1px solid;
402 color: #ffffff; background-color: #7777bb;
402 color: #ffffff; background-color: #7777bb;
403 border-color: #aaaadd #333366 #333366 #aaaadd;
403 border-color: #aaaadd #333366 #333366 #aaaadd;
404 font-weight: bold; text-align: center; text-decoration: none;
404 font-weight: bold; text-align: center; text-decoration: none;
405 font-size: 10px;
405 font-size: 10px;
406 }
406 }
407 td.indexlinks a:hover { background-color: #6666aa; }
407 td.indexlinks a:hover { background-color: #6666aa; }
408 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
408 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
409 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
409 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
410 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
410 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
411
411
412 .search {
412 .search {
413 margin-right: 8px;
413 margin-right: 8px;
414 }
414 }
415
415
416 div#hint {
416 div#hint {
417 position: absolute;
417 position: absolute;
418 display: none;
418 display: none;
419 width: 250px;
419 width: 250px;
420 padding: 5px;
420 padding: 5px;
421 background: #ffc;
421 background: #ffc;
422 border: 1px solid yellow;
422 border: 1px solid yellow;
423 border-radius: 5px;
423 border-radius: 5px;
424 }
424 }
425
425
426 #searchform:hover div#hint { display: block; }
426 #searchform:hover div#hint { display: block; }
427
427
428 tr.thisrev a { color:#999999; text-decoration: none; }
428 tr.thisrev a { color:#999999; text-decoration: none; }
429 tr.thisrev pre { color:#009900; }
429 tr.thisrev pre { color:#009900; }
430 td.annotate {
430 td.annotate {
431 white-space: nowrap;
431 white-space: nowrap;
432 }
432 }
433 div.annotate-info {
433 div.annotate-info {
434 z-index: 5;
434 z-index: 5;
435 display: none;
435 display: none;
436 position: absolute;
436 position: absolute;
437 background-color: #FFFFFF;
437 background-color: #FFFFFF;
438 border: 1px solid #d9d8d1;
438 border: 1px solid #d9d8d1;
439 text-align: left;
439 text-align: left;
440 color: #000000;
440 color: #000000;
441 padding: 5px;
441 padding: 5px;
442 }
442 }
443 div.annotate-info a { color: #0000FF; text-decoration: underline; }
443 div.annotate-info a { color: #0000FF; text-decoration: underline; }
444 td.annotate:hover div.annotate-info { display: inline; }
444 td.annotate:hover div.annotate-info { display: inline; }
445
446 #diffopts-form {
447 padding-left: 8px;
448 display: none;
449 }
450
445 .linenr { color:#999999; text-decoration:none }
451 .linenr { color:#999999; text-decoration:none }
446 div.rss_logo { float: right; white-space: nowrap; }
452 div.rss_logo { float: right; white-space: nowrap; }
447 div.rss_logo a {
453 div.rss_logo a {
448 padding:3px 6px; line-height:10px;
454 padding:3px 6px; line-height:10px;
449 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
455 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
450 color:#ffffff; background-color:#ff6600;
456 color:#ffffff; background-color:#ff6600;
451 font-weight:bold; font-family:sans-serif; font-size:10px;
457 font-weight:bold; font-family:sans-serif; font-size:10px;
452 text-align:center; text-decoration:none;
458 text-align:center; text-decoration:none;
453 }
459 }
454 div.rss_logo a:hover { background-color:#ee5500; }
460 div.rss_logo a:hover { background-color:#ee5500; }
455 pre { margin: 0; }
461 pre { margin: 0; }
456 span.logtags span {
462 span.logtags span {
457 padding: 0px 4px;
463 padding: 0px 4px;
458 font-size: 10px;
464 font-size: 10px;
459 font-weight: normal;
465 font-weight: normal;
460 border: 1px solid;
466 border: 1px solid;
461 background-color: #ffaaff;
467 background-color: #ffaaff;
462 border-color: #ffccff #ff00ee #ff00ee #ffccff;
468 border-color: #ffccff #ff00ee #ff00ee #ffccff;
463 }
469 }
464 span.logtags span.tagtag {
470 span.logtags span.tagtag {
465 background-color: #ffffaa;
471 background-color: #ffffaa;
466 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
472 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
467 }
473 }
468 span.logtags span.branchtag {
474 span.logtags span.branchtag {
469 background-color: #aaffaa;
475 background-color: #aaffaa;
470 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
476 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
471 }
477 }
472 span.logtags span.inbranchtag {
478 span.logtags span.inbranchtag {
473 background-color: #d5dde6;
479 background-color: #d5dde6;
474 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
480 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
475 }
481 }
476 span.logtags span.bookmarktag {
482 span.logtags span.bookmarktag {
477 background-color: #afdffa;
483 background-color: #afdffa;
478 border-color: #ccecff #46ace6 #46ace6 #ccecff;
484 border-color: #ccecff #46ace6 #46ace6 #ccecff;
479 }
485 }
480 span.difflineplus { color:#008800; }
486 span.difflineplus { color:#008800; }
481 span.difflineminus { color:#cc0000; }
487 span.difflineminus { color:#cc0000; }
482 span.difflineat { color:#990099; }
488 span.difflineat { color:#990099; }
483 div.diffblocks { counter-reset: lineno; }
489 div.diffblocks { counter-reset: lineno; }
484 div.diffblock { counter-increment: lineno; }
490 div.diffblock { counter-increment: lineno; }
485 pre.sourcelines { position: relative; counter-reset: lineno; }
491 pre.sourcelines { position: relative; counter-reset: lineno; }
486 pre.sourcelines > span {
492 pre.sourcelines > span {
487 display: inline-block;
493 display: inline-block;
488 box-sizing: border-box;
494 box-sizing: border-box;
489 width: 100%;
495 width: 100%;
490 padding: 0 0 0 5em;
496 padding: 0 0 0 5em;
491 counter-increment: lineno;
497 counter-increment: lineno;
492 vertical-align: top;
498 vertical-align: top;
493 }
499 }
494 pre.sourcelines > span:before {
500 pre.sourcelines > span:before {
495 -moz-user-select: -moz-none;
501 -moz-user-select: -moz-none;
496 -khtml-user-select: none;
502 -khtml-user-select: none;
497 -webkit-user-select: none;
503 -webkit-user-select: none;
498 -ms-user-select: none;
504 -ms-user-select: none;
499 user-select: none;
505 user-select: none;
500 display: inline-block;
506 display: inline-block;
501 margin-left: -6em;
507 margin-left: -6em;
502 width: 4em;
508 width: 4em;
503 color: #999;
509 color: #999;
504 text-align: right;
510 text-align: right;
505 content: counters(lineno,".");
511 content: counters(lineno,".");
506 float: left;
512 float: left;
507 }
513 }
508 pre.sourcelines > a {
514 pre.sourcelines > a {
509 display: inline-block;
515 display: inline-block;
510 position: absolute;
516 position: absolute;
511 left: 0px;
517 left: 0px;
512 width: 4em;
518 width: 4em;
513 height: 1em;
519 height: 1em;
514 }
520 }
515 tr:target td,
521 tr:target td,
516 pre.sourcelines > span:target,
522 pre.sourcelines > span:target,
517 pre.sourcelines.stripes > span:target {
523 pre.sourcelines.stripes > span:target {
518 background-color: #bfdfff;
524 background-color: #bfdfff;
519 }
525 }
520
526
521 .description {
527 .description {
522 font-family: monospace;
528 font-family: monospace;
523 white-space: pre;
529 white-space: pre;
524 }
530 }
525
531
526 /* Followlines */
532 /* Followlines */
527 tbody.sourcelines > tr.followlines-selected,
533 tbody.sourcelines > tr.followlines-selected,
528 pre.sourcelines > span.followlines-selected {
534 pre.sourcelines > span.followlines-selected {
529 background-color: #99C7E9 !important;
535 background-color: #99C7E9 !important;
530 }
536 }
531
537
532 div#followlines {
538 div#followlines {
533 background-color: #B7B7B7;
539 background-color: #B7B7B7;
534 border: 1px solid #CCC;
540 border: 1px solid #CCC;
535 border-radius: 5px;
541 border-radius: 5px;
536 padding: 4px;
542 padding: 4px;
537 position: fixed;
543 position: fixed;
538 }
544 }
539
545
540 div.followlines-cancel {
546 div.followlines-cancel {
541 text-align: right;
547 text-align: right;
542 }
548 }
543
549
544 div.followlines-cancel > button {
550 div.followlines-cancel > button {
545 line-height: 80%;
551 line-height: 80%;
546 padding: 0;
552 padding: 0;
547 border: 0;
553 border: 0;
548 border-radius: 2px;
554 border-radius: 2px;
549 background-color: inherit;
555 background-color: inherit;
550 font-weight: bold;
556 font-weight: bold;
551 }
557 }
552
558
553 div.followlines-cancel > button:hover {
559 div.followlines-cancel > button:hover {
554 color: #FFFFFF;
560 color: #FFFFFF;
555 background-color: #CF1F1F;
561 background-color: #CF1F1F;
556 }
562 }
557
563
558 div.followlines-link {
564 div.followlines-link {
559 margin: 2px;
565 margin: 2px;
560 margin-top: 4px;
566 margin-top: 4px;
561 font-family: sans-serif;
567 font-family: sans-serif;
562 }
568 }
563
569
564 .btn-followlines {
570 .btn-followlines {
565 display: none;
571 display: none;
566 cursor: pointer;
572 cursor: pointer;
567 box-sizing: content-box;
573 box-sizing: content-box;
568 font-size: 11px;
574 font-size: 11px;
569 width: 13px;
575 width: 13px;
570 height: 13px;
576 height: 13px;
571 border-radius: 3px;
577 border-radius: 3px;
572 margin: 0px;
578 margin: 0px;
573 margin-top: -2px;
579 margin-top: -2px;
574 padding: 0px;
580 padding: 0px;
575 background-color: #E5FDE5;
581 background-color: #E5FDE5;
576 border: 1px solid #9BC19B;
582 border: 1px solid #9BC19B;
577 font-family: monospace;
583 font-family: monospace;
578 text-align: center;
584 text-align: center;
579 line-height: 5px;
585 line-height: 5px;
580 }
586 }
581
587
582 tr .btn-followlines {
588 tr .btn-followlines {
583 position: absolute;
589 position: absolute;
584 }
590 }
585
591
586 span .btn-followlines {
592 span .btn-followlines {
587 float: left;
593 float: left;
588 }
594 }
589
595
590 span.followlines-select .btn-followlines {
596 span.followlines-select .btn-followlines {
591 margin-left: -1.6em;
597 margin-left: -1.6em;
592 }
598 }
593
599
594 .btn-followlines:hover {
600 .btn-followlines:hover {
595 transform: scale(1.1, 1.1);
601 transform: scale(1.1, 1.1);
596 }
602 }
597
603
598 .btn-followlines .followlines-plus {
604 .btn-followlines .followlines-plus {
599 color: green;
605 color: green;
600 }
606 }
601
607
602 .btn-followlines .followlines-minus {
608 .btn-followlines .followlines-minus {
603 color: red;
609 color: red;
604 }
610 }
605
611
606 .btn-followlines-end {
612 .btn-followlines-end {
607 background-color: #ffdcdc;
613 background-color: #ffdcdc;
608 }
614 }
609
615
610 .sourcelines tr:hover .btn-followlines,
616 .sourcelines tr:hover .btn-followlines,
611 .sourcelines span.followlines-select:hover > .btn-followlines {
617 .sourcelines span.followlines-select:hover > .btn-followlines {
612 display: inline;
618 display: inline;
613 }
619 }
614
620
615 .btn-followlines-hidden,
621 .btn-followlines-hidden,
616 .sourcelines tr:hover .btn-followlines-hidden {
622 .sourcelines tr:hover .btn-followlines-hidden {
617 display: none;
623 display: none;
618 }
624 }
619
625
620 /* Graph */
626 /* Graph */
621 div#wrapper {
627 div#wrapper {
622 position: relative;
628 position: relative;
623 margin: 0;
629 margin: 0;
624 padding: 0;
630 padding: 0;
625 margin-top: 3px;
631 margin-top: 3px;
626 }
632 }
627
633
628 canvas {
634 canvas {
629 position: absolute;
635 position: absolute;
630 z-index: 5;
636 z-index: 5;
631 top: -0.9em;
637 top: -0.9em;
632 margin: 0;
638 margin: 0;
633 }
639 }
634
640
635 ul#nodebgs {
641 ul#nodebgs {
636 list-style: none inside none;
642 list-style: none inside none;
637 padding: 0;
643 padding: 0;
638 margin: 0;
644 margin: 0;
639 top: -0.7em;
645 top: -0.7em;
640 }
646 }
641
647
642 ul#graphnodes li, ul#nodebgs li {
648 ul#graphnodes li, ul#nodebgs li {
643 height: 39px;
649 height: 39px;
644 }
650 }
645
651
646 ul#graphnodes {
652 ul#graphnodes {
647 position: absolute;
653 position: absolute;
648 z-index: 10;
654 z-index: 10;
649 top: -0.8em;
655 top: -0.8em;
650 list-style: none inside none;
656 list-style: none inside none;
651 padding: 0;
657 padding: 0;
652 }
658 }
653
659
654 ul#graphnodes li .info {
660 ul#graphnodes li .info {
655 display: block;
661 display: block;
656 font-size: 100%;
662 font-size: 100%;
657 position: relative;
663 position: relative;
658 top: -3px;
664 top: -3px;
659 font-style: italic;
665 font-style: italic;
660 }
666 }
661
667
662 /* Comparison */
668 /* Comparison */
663 .legend {
669 .legend {
664 padding: 1.5% 0 1.5% 0;
670 padding: 1.5% 0 1.5% 0;
665 }
671 }
666
672
667 .legendinfo {
673 .legendinfo {
668 border: 1px solid #d9d8d1;
674 border: 1px solid #d9d8d1;
669 font-size: 80%;
675 font-size: 80%;
670 text-align: center;
676 text-align: center;
671 padding: 0.5%;
677 padding: 0.5%;
672 }
678 }
673
679
674 .equal {
680 .equal {
675 background-color: #ffffff;
681 background-color: #ffffff;
676 }
682 }
677
683
678 .delete {
684 .delete {
679 background-color: #faa;
685 background-color: #faa;
680 color: #333;
686 color: #333;
681 }
687 }
682
688
683 .insert {
689 .insert {
684 background-color: #ffa;
690 background-color: #ffa;
685 }
691 }
686
692
687 .replace {
693 .replace {
688 background-color: #e8e8e8;
694 background-color: #e8e8e8;
689 }
695 }
690
696
691 .comparison {
697 .comparison {
692 overflow-x: auto;
698 overflow-x: auto;
693 }
699 }
694
700
695 .header th {
701 .header th {
696 text-align: center;
702 text-align: center;
697 }
703 }
698
704
699 .block {
705 .block {
700 border-top: 1px solid #d9d8d1;
706 border-top: 1px solid #d9d8d1;
701 }
707 }
702
708
703 .scroll-loading {
709 .scroll-loading {
704 -webkit-animation: change_color 1s linear 0s infinite alternate;
710 -webkit-animation: change_color 1s linear 0s infinite alternate;
705 -moz-animation: change_color 1s linear 0s infinite alternate;
711 -moz-animation: change_color 1s linear 0s infinite alternate;
706 -o-animation: change_color 1s linear 0s infinite alternate;
712 -o-animation: change_color 1s linear 0s infinite alternate;
707 animation: change_color 1s linear 0s infinite alternate;
713 animation: change_color 1s linear 0s infinite alternate;
708 }
714 }
709
715
710 @-webkit-keyframes change_color {
716 @-webkit-keyframes change_color {
711 from { background-color: #A0CEFF; } to { }
717 from { background-color: #A0CEFF; } to { }
712 }
718 }
713 @-moz-keyframes change_color {
719 @-moz-keyframes change_color {
714 from { background-color: #A0CEFF; } to { }
720 from { background-color: #A0CEFF; } to { }
715 }
721 }
716 @-o-keyframes change_color {
722 @-o-keyframes change_color {
717 from { background-color: #A0CEFF; } to { }
723 from { background-color: #A0CEFF; } to { }
718 }
724 }
719 @keyframes change_color {
725 @keyframes change_color {
720 from { background-color: #A0CEFF; } to { }
726 from { background-color: #A0CEFF; } to { }
721 }
727 }
722
728
723 .scroll-loading-error {
729 .scroll-loading-error {
724 background-color: #FFCCCC !important;
730 background-color: #FFCCCC !important;
725 }
731 }
726
732
727 #doc {
733 #doc {
728 margin: 0 8px;
734 margin: 0 8px;
729 }
735 }
730 304 Not Modified
736 304 Not Modified
731
737
732
738
733 phase changes are refreshed (issue4061)
739 phase changes are refreshed (issue4061)
734
740
735 $ echo bar >> foo
741 $ echo bar >> foo
736 $ hg ci -msecret --secret
742 $ hg ci -msecret --secret
737 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
743 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
738 200 Script output follows
744 200 Script output follows
739
745
740
746
741 # HG changelog
747 # HG changelog
742 # Node ID 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
748 # Node ID 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
743
749
744 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
750 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
745 revision: 0
751 revision: 0
746 user: test
752 user: test
747 date: Thu, 01 Jan 1970 00:00:00 +0000
753 date: Thu, 01 Jan 1970 00:00:00 +0000
748 summary: base
754 summary: base
749 branch: default
755 branch: default
750 tag: tip
756 tag: tip
751 bookmark: @
757 bookmark: @
752 bookmark: a b c
758 bookmark: a b c
753 bookmark: d/e/f
759 bookmark: d/e/f
754
760
755
761
756 $ hg phase --draft tip
762 $ hg phase --draft tip
757 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
763 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
758 200 Script output follows
764 200 Script output follows
759
765
760
766
761 # HG changelog
767 # HG changelog
762 # Node ID a084749e708a9c4c0a5b652a2a446322ce290e04
768 # Node ID a084749e708a9c4c0a5b652a2a446322ce290e04
763
769
764 changeset: a084749e708a9c4c0a5b652a2a446322ce290e04
770 changeset: a084749e708a9c4c0a5b652a2a446322ce290e04
765 revision: 1
771 revision: 1
766 user: test
772 user: test
767 date: Thu, 01 Jan 1970 00:00:00 +0000
773 date: Thu, 01 Jan 1970 00:00:00 +0000
768 summary: secret
774 summary: secret
769 branch: default
775 branch: default
770 tag: tip
776 tag: tip
771
777
772 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
778 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
773 revision: 0
779 revision: 0
774 user: test
780 user: test
775 date: Thu, 01 Jan 1970 00:00:00 +0000
781 date: Thu, 01 Jan 1970 00:00:00 +0000
776 summary: base
782 summary: base
777 bookmark: @
783 bookmark: @
778 bookmark: a b c
784 bookmark: a b c
779 bookmark: d/e/f
785 bookmark: d/e/f
780
786
781
787
782
788
783 access bookmarks
789 access bookmarks
784
790
785 $ get-with-headers.py localhost:$HGPORT 'rev/@?style=paper' | egrep '^200|changeset 0:'
791 $ get-with-headers.py localhost:$HGPORT 'rev/@?style=paper' | egrep '^200|changeset 0:'
786 200 Script output follows
792 200 Script output follows
787 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
793 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
788
794
789 $ get-with-headers.py localhost:$HGPORT 'rev/%40?style=paper' | egrep '^200|changeset 0:'
795 $ get-with-headers.py localhost:$HGPORT 'rev/%40?style=paper' | egrep '^200|changeset 0:'
790 200 Script output follows
796 200 Script output follows
791 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
797 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
792
798
793 $ get-with-headers.py localhost:$HGPORT 'rev/a%20b%20c?style=paper' | egrep '^200|changeset 0:'
799 $ get-with-headers.py localhost:$HGPORT 'rev/a%20b%20c?style=paper' | egrep '^200|changeset 0:'
794 200 Script output follows
800 200 Script output follows
795 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
801 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
796
802
797 $ get-with-headers.py localhost:$HGPORT 'rev/d%252Fe%252Ff?style=paper' | egrep '^200|changeset 0:'
803 $ get-with-headers.py localhost:$HGPORT 'rev/d%252Fe%252Ff?style=paper' | egrep '^200|changeset 0:'
798 200 Script output follows
804 200 Script output follows
799 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
805 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
800
806
801 no style can be loaded from directories other than the specified paths
807 no style can be loaded from directories other than the specified paths
802
808
803 $ mkdir -p x/templates/fallback
809 $ mkdir -p x/templates/fallback
804 $ cat <<EOF > x/templates/fallback/map
810 $ cat <<EOF > x/templates/fallback/map
805 > default = 'shortlog'
811 > default = 'shortlog'
806 > shortlog = 'fall back to default\n'
812 > shortlog = 'fall back to default\n'
807 > mimetype = 'text/plain'
813 > mimetype = 'text/plain'
808 > EOF
814 > EOF
809 $ cat <<EOF > x/map
815 $ cat <<EOF > x/map
810 > default = 'shortlog'
816 > default = 'shortlog'
811 > shortlog = 'access to outside of templates directory\n'
817 > shortlog = 'access to outside of templates directory\n'
812 > mimetype = 'text/plain'
818 > mimetype = 'text/plain'
813 > EOF
819 > EOF
814
820
815 $ killdaemons.py
821 $ killdaemons.py
816 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log \
822 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log \
817 > --config web.style=fallback --config web.templates=x/templates
823 > --config web.style=fallback --config web.templates=x/templates
818 $ cat hg.pid >> $DAEMON_PIDS
824 $ cat hg.pid >> $DAEMON_PIDS
819
825
820 $ get-with-headers.py localhost:$HGPORT "?style=`pwd`/x"
826 $ get-with-headers.py localhost:$HGPORT "?style=`pwd`/x"
821 200 Script output follows
827 200 Script output follows
822
828
823 fall back to default
829 fall back to default
824
830
825 $ get-with-headers.py localhost:$HGPORT '?style=..'
831 $ get-with-headers.py localhost:$HGPORT '?style=..'
826 200 Script output follows
832 200 Script output follows
827
833
828 fall back to default
834 fall back to default
829
835
830 $ get-with-headers.py localhost:$HGPORT '?style=./..'
836 $ get-with-headers.py localhost:$HGPORT '?style=./..'
831 200 Script output follows
837 200 Script output follows
832
838
833 fall back to default
839 fall back to default
834
840
835 $ get-with-headers.py localhost:$HGPORT '?style=.../.../'
841 $ get-with-headers.py localhost:$HGPORT '?style=.../.../'
836 200 Script output follows
842 200 Script output follows
837
843
838 fall back to default
844 fall back to default
839
845
840 errors
846 errors
841
847
842 $ cat errors.log
848 $ cat errors.log
843
849
844 Uncaught exceptions result in a logged error and canned HTTP response
850 Uncaught exceptions result in a logged error and canned HTTP response
845
851
846 $ killdaemons.py
852 $ killdaemons.py
847 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
853 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
848 $ cat hg.pid >> $DAEMON_PIDS
854 $ cat hg.pid >> $DAEMON_PIDS
849
855
850 $ get-with-headers.py localhost:$HGPORT 'raiseerror' transfer-encoding content-type
856 $ get-with-headers.py localhost:$HGPORT 'raiseerror' transfer-encoding content-type
851 500 Internal Server Error
857 500 Internal Server Error
852 transfer-encoding: chunked
858 transfer-encoding: chunked
853
859
854 Internal Server Error (no-eol)
860 Internal Server Error (no-eol)
855 [1]
861 [1]
856
862
857 $ killdaemons.py
863 $ killdaemons.py
858 $ head -1 errors.log
864 $ head -1 errors.log
859 .* Exception happened during processing request '/raiseerror': (re)
865 .* Exception happened during processing request '/raiseerror': (re)
860
866
861 Uncaught exception after partial content sent
867 Uncaught exception after partial content sent
862
868
863 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
869 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
864 $ cat hg.pid >> $DAEMON_PIDS
870 $ cat hg.pid >> $DAEMON_PIDS
865 $ get-with-headers.py localhost:$HGPORT 'raiseerror?partialresponse=1' transfer-encoding content-type
871 $ get-with-headers.py localhost:$HGPORT 'raiseerror?partialresponse=1' transfer-encoding content-type
866 200 Script output follows
872 200 Script output follows
867 transfer-encoding: chunked
873 transfer-encoding: chunked
868 content-type: text/plain
874 content-type: text/plain
869
875
870 partial content
876 partial content
871 Internal Server Error (no-eol)
877 Internal Server Error (no-eol)
872
878
873 $ killdaemons.py
879 $ killdaemons.py
874 $ cd ..
880 $ cd ..
@@ -1,998 +1,1017 b''
1 #require pygments serve
1 #require pygments serve
2
2
3 $ cat <<EOF >> $HGRCPATH
3 $ cat <<EOF >> $HGRCPATH
4 > [extensions]
4 > [extensions]
5 > highlight =
5 > highlight =
6 > [web]
6 > [web]
7 > pygments_style = friendly
7 > pygments_style = friendly
8 > highlightfiles = **.py and size('<100KB')
8 > highlightfiles = **.py and size('<100KB')
9 > EOF
9 > EOF
10 $ hg init test
10 $ hg init test
11 $ cd test
11 $ cd test
12
12
13 $ filterhtml () {
13 $ filterhtml () {
14 > sed -e "s/class=\"k\"/class=\"kn\"/g" \
14 > sed -e "s/class=\"k\"/class=\"kn\"/g" \
15 > -e "s/class=\"mf\"/class=\"mi\"/g" \
15 > -e "s/class=\"mf\"/class=\"mi\"/g" \
16 > -e "s/class=\"vm\"/class=\"n\"/g" \
16 > -e "s/class=\"vm\"/class=\"n\"/g" \
17 > -e "s/class=\"\([cs]\)[h12]\"/class=\"\1\"/g"
17 > -e "s/class=\"\([cs]\)[h12]\"/class=\"\1\"/g"
18 > }
18 > }
19
19
20 create random Python file to exercise Pygments
20 create random Python file to exercise Pygments
21
21
22 $ cat <<EOF > primes.py
22 $ cat <<EOF > primes.py
23 > """Fun with generators. Corresponding Haskell implementation:
23 > """Fun with generators. Corresponding Haskell implementation:
24 >
24 >
25 > primes = 2 : sieve [3, 5..]
25 > primes = 2 : sieve [3, 5..]
26 > where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]
26 > where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]
27 > """
27 > """
28 >
28 >
29 > from itertools import dropwhile, ifilter, islice, count, chain
29 > from itertools import dropwhile, ifilter, islice, count, chain
30 >
30 >
31 > def primes():
31 > def primes():
32 > """Generate all primes."""
32 > """Generate all primes."""
33 > def sieve(ns):
33 > def sieve(ns):
34 > p = ns.next()
34 > p = ns.next()
35 > # It is important to yield *here* in order to stop the
35 > # It is important to yield *here* in order to stop the
36 > # infinite recursion.
36 > # infinite recursion.
37 > yield p
37 > yield p
38 > ns = ifilter(lambda n: n % p != 0, ns)
38 > ns = ifilter(lambda n: n % p != 0, ns)
39 > for n in sieve(ns):
39 > for n in sieve(ns):
40 > yield n
40 > yield n
41 >
41 >
42 > odds = ifilter(lambda i: i % 2 == 1, count())
42 > odds = ifilter(lambda i: i % 2 == 1, count())
43 > return chain([2], sieve(dropwhile(lambda n: n < 3, odds)))
43 > return chain([2], sieve(dropwhile(lambda n: n < 3, odds)))
44 >
44 >
45 > if __name__ == "__main__":
45 > if __name__ == "__main__":
46 > import sys
46 > import sys
47 > try:
47 > try:
48 > n = int(sys.argv[1])
48 > n = int(sys.argv[1])
49 > except (ValueError, IndexError):
49 > except (ValueError, IndexError):
50 > n = 10
50 > n = 10
51 > p = primes()
51 > p = primes()
52 > print("The first %d primes: %s" % (n, list(islice(p, n))))
52 > print("The first %d primes: %s" % (n, list(islice(p, n))))
53 > EOF
53 > EOF
54 $ echo >> primes.py # to test html markup with an empty line just before EOF
54 $ echo >> primes.py # to test html markup with an empty line just before EOF
55 $ hg ci -Ama
55 $ hg ci -Ama
56 adding primes.py
56 adding primes.py
57
57
58 hg serve
58 hg serve
59
59
60 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
60 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
61 $ cat hg.pid >> $DAEMON_PIDS
61 $ cat hg.pid >> $DAEMON_PIDS
62
62
63 hgweb filerevision, html
63 hgweb filerevision, html
64
64
65 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py') | filterhtml
65 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py') | filterhtml
66 200 Script output follows
66 200 Script output follows
67
67
68 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
68 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
69 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
69 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
70 <head>
70 <head>
71 <link rel="icon" href="/static/hgicon.png" type="image/png" />
71 <link rel="icon" href="/static/hgicon.png" type="image/png" />
72 <meta name="robots" content="index, nofollow" />
72 <meta name="robots" content="index, nofollow" />
73 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
73 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
74 <script type="text/javascript" src="/static/mercurial.js"></script>
74 <script type="text/javascript" src="/static/mercurial.js"></script>
75
75
76 <link rel="stylesheet" href="/highlightcss" type="text/css" />
76 <link rel="stylesheet" href="/highlightcss" type="text/css" />
77 <title>test: f4fca47b67e6 primes.py</title>
77 <title>test: f4fca47b67e6 primes.py</title>
78 </head>
78 </head>
79 <body>
79 <body>
80
80
81 <div class="container">
81 <div class="container">
82 <div class="menu">
82 <div class="menu">
83 <div class="logo">
83 <div class="logo">
84 <a href="https://mercurial-scm.org/">
84 <a href="https://mercurial-scm.org/">
85 <img src="/static/hglogo.png" alt="mercurial" /></a>
85 <img src="/static/hglogo.png" alt="mercurial" /></a>
86 </div>
86 </div>
87 <ul>
87 <ul>
88 <li><a href="/shortlog/tip">log</a></li>
88 <li><a href="/shortlog/tip">log</a></li>
89 <li><a href="/graph/tip">graph</a></li>
89 <li><a href="/graph/tip">graph</a></li>
90 <li><a href="/tags">tags</a></li>
90 <li><a href="/tags">tags</a></li>
91 <li><a href="/bookmarks">bookmarks</a></li>
91 <li><a href="/bookmarks">bookmarks</a></li>
92 <li><a href="/branches">branches</a></li>
92 <li><a href="/branches">branches</a></li>
93 </ul>
93 </ul>
94 <ul>
94 <ul>
95 <li><a href="/rev/tip">changeset</a></li>
95 <li><a href="/rev/tip">changeset</a></li>
96 <li><a href="/file/tip/">browse</a></li>
96 <li><a href="/file/tip/">browse</a></li>
97 </ul>
97 </ul>
98 <ul>
98 <ul>
99 <li class="active">file</li>
99 <li class="active">file</li>
100 <li><a href="/file/tip/primes.py">latest</a></li>
100 <li><a href="/file/tip/primes.py">latest</a></li>
101 <li><a href="/diff/tip/primes.py">diff</a></li>
101 <li><a href="/diff/tip/primes.py">diff</a></li>
102 <li><a href="/comparison/tip/primes.py">comparison</a></li>
102 <li><a href="/comparison/tip/primes.py">comparison</a></li>
103 <li><a href="/annotate/tip/primes.py">annotate</a></li>
103 <li><a href="/annotate/tip/primes.py">annotate</a></li>
104 <li><a href="/log/tip/primes.py">file log</a></li>
104 <li><a href="/log/tip/primes.py">file log</a></li>
105 <li><a href="/raw-file/tip/primes.py">raw</a></li>
105 <li><a href="/raw-file/tip/primes.py">raw</a></li>
106 </ul>
106 </ul>
107 <ul>
107 <ul>
108 <li><a href="/help">help</a></li>
108 <li><a href="/help">help</a></li>
109 </ul>
109 </ul>
110 </div>
110 </div>
111
111
112 <div class="main">
112 <div class="main">
113 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
113 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
114 <h3>
114 <h3>
115 view primes.py @ 0:<a href="/rev/f4fca47b67e6">f4fca47b67e6</a>
115 view primes.py @ 0:<a href="/rev/f4fca47b67e6">f4fca47b67e6</a>
116 <span class="tag">tip</span>
116 <span class="tag">tip</span>
117 </h3>
117 </h3>
118
118
119
119
120 <form class="search" action="/log">
120 <form class="search" action="/log">
121
121
122 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
122 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
123 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
123 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
124 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
124 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
125 </form>
125 </form>
126
126
127 <div class="description">a</div>
127 <div class="description">a</div>
128
128
129 <table id="changesetEntry">
129 <table id="changesetEntry">
130 <tr>
130 <tr>
131 <th class="author">author</th>
131 <th class="author">author</th>
132 <td class="author">&#116;&#101;&#115;&#116;</td>
132 <td class="author">&#116;&#101;&#115;&#116;</td>
133 </tr>
133 </tr>
134 <tr>
134 <tr>
135 <th class="date">date</th>
135 <th class="date">date</th>
136 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
136 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
137 </tr>
137 </tr>
138 <tr>
138 <tr>
139 <th class="author">parents</th>
139 <th class="author">parents</th>
140 <td class="author"></td>
140 <td class="author"></td>
141 </tr>
141 </tr>
142 <tr>
142 <tr>
143 <th class="author">children</th>
143 <th class="author">children</th>
144 <td class="author"></td>
144 <td class="author"></td>
145 </tr>
145 </tr>
146 </table>
146 </table>
147
147
148 <div class="overflow">
148 <div class="overflow">
149 <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div>
149 <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div>
150 <div class="sourcefirst"> line source</div>
150 <div class="sourcefirst"> line source</div>
151 <pre class="sourcelines stripes4 wrap bottomline"
151 <pre class="sourcelines stripes4 wrap bottomline"
152 data-logurl="/log/tip/primes.py"
152 data-logurl="/log/tip/primes.py"
153 data-selectabletag="SPAN"
153 data-selectabletag="SPAN"
154 data-ishead="1">
154 data-ishead="1">
155
155
156 <span id="l1"><span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></span><a href="#l1"></a>
156 <span id="l1"><span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></span><a href="#l1"></a>
157 <span id="l2"></span><a href="#l2"></a>
157 <span id="l2"></span><a href="#l2"></a>
158 <span id="l3"><span class="sd">primes = 2 : sieve [3, 5..]</span></span><a href="#l3"></a>
158 <span id="l3"><span class="sd">primes = 2 : sieve [3, 5..]</span></span><a href="#l3"></a>
159 <span id="l4"><span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></span><a href="#l4"></a>
159 <span id="l4"><span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></span><a href="#l4"></a>
160 <span id="l5"><span class="sd">&quot;&quot;&quot;</span></span><a href="#l5"></a>
160 <span id="l5"><span class="sd">&quot;&quot;&quot;</span></span><a href="#l5"></a>
161 <span id="l6"></span><a href="#l6"></a>
161 <span id="l6"></span><a href="#l6"></a>
162 <span id="l7"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></span><a href="#l7"></a>
162 <span id="l7"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></span><a href="#l7"></a>
163 <span id="l8"></span><a href="#l8"></a>
163 <span id="l8"></span><a href="#l8"></a>
164 <span id="l9"><span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></span><a href="#l9"></a>
164 <span id="l9"><span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></span><a href="#l9"></a>
165 <span id="l10"> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></span><a href="#l10"></a>
165 <span id="l10"> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></span><a href="#l10"></a>
166 <span id="l11"> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l11"></a>
166 <span id="l11"> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l11"></a>
167 <span id="l12"> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></span><a href="#l12"></a>
167 <span id="l12"> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></span><a href="#l12"></a>
168 <span id="l13"> <span class="c"># It is important to yield *here* in order to stop the</span></span><a href="#l13"></a>
168 <span id="l13"> <span class="c"># It is important to yield *here* in order to stop the</span></span><a href="#l13"></a>
169 <span id="l14"> <span class="c"># infinite recursion.</span></span><a href="#l14"></a>
169 <span id="l14"> <span class="c"># infinite recursion.</span></span><a href="#l14"></a>
170 <span id="l15"> <span class="kn">yield</span> <span class="n">p</span></span><a href="#l15"></a>
170 <span id="l15"> <span class="kn">yield</span> <span class="n">p</span></span><a href="#l15"></a>
171 <span id="l16"> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></span><a href="#l16"></a>
171 <span id="l16"> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></span><a href="#l16"></a>
172 <span id="l17"> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l17"></a>
172 <span id="l17"> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l17"></a>
173 <span id="l18"> <span class="kn">yield</span> <span class="n">n</span></span><a href="#l18"></a>
173 <span id="l18"> <span class="kn">yield</span> <span class="n">n</span></span><a href="#l18"></a>
174 <span id="l19"></span><a href="#l19"></a>
174 <span id="l19"></span><a href="#l19"></a>
175 <span id="l20"> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></span><a href="#l20"></a>
175 <span id="l20"> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></span><a href="#l20"></a>
176 <span id="l21"> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></span><a href="#l21"></a>
176 <span id="l21"> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></span><a href="#l21"></a>
177 <span id="l22"></span><a href="#l22"></a>
177 <span id="l22"></span><a href="#l22"></a>
178 <span id="l23"><span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></span><a href="#l23"></a>
178 <span id="l23"><span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></span><a href="#l23"></a>
179 <span id="l24"> <span class="kn">import</span> <span class="nn">sys</span></span><a href="#l24"></a>
179 <span id="l24"> <span class="kn">import</span> <span class="nn">sys</span></span><a href="#l24"></a>
180 <span id="l25"> <span class="kn">try</span><span class="p">:</span></span><a href="#l25"></a>
180 <span id="l25"> <span class="kn">try</span><span class="p">:</span></span><a href="#l25"></a>
181 <span id="l26"> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></span><a href="#l26"></a>
181 <span id="l26"> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></span><a href="#l26"></a>
182 <span id="l27"> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></span><a href="#l27"></a>
182 <span id="l27"> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></span><a href="#l27"></a>
183 <span id="l28"> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></span><a href="#l28"></a>
183 <span id="l28"> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></span><a href="#l28"></a>
184 <span id="l29"> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></span><a href="#l29"></a>
184 <span id="l29"> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></span><a href="#l29"></a>
185 <span id="l30"> <span class="kn">print</span><span class="p">(</span><span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">))))</span></span><a href="#l30"></a>
185 <span id="l30"> <span class="kn">print</span><span class="p">(</span><span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">))))</span></span><a href="#l30"></a>
186 <span id="l31"></span><a href="#l31"></a>
186 <span id="l31"></span><a href="#l31"></a>
187 </pre>
187 </pre>
188 </div>
188 </div>
189
189
190 <script type="text/javascript" src="/static/followlines.js"></script>
190 <script type="text/javascript" src="/static/followlines.js"></script>
191
191
192 </div>
192 </div>
193 </div>
193 </div>
194
194
195
195
196
196
197 </body>
197 </body>
198 </html>
198 </html>
199
199
200
200
201 hgweb fileannotate, html
201 hgweb fileannotate, html
202
202
203 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py') | filterhtml
203 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py') | filterhtml
204 200 Script output follows
204 200 Script output follows
205
205
206 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
206 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
207 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
207 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
208 <head>
208 <head>
209 <link rel="icon" href="/static/hgicon.png" type="image/png" />
209 <link rel="icon" href="/static/hgicon.png" type="image/png" />
210 <meta name="robots" content="index, nofollow" />
210 <meta name="robots" content="index, nofollow" />
211 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
211 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
212 <script type="text/javascript" src="/static/mercurial.js"></script>
212 <script type="text/javascript" src="/static/mercurial.js"></script>
213
213
214 <link rel="stylesheet" href="/highlightcss" type="text/css" />
214 <link rel="stylesheet" href="/highlightcss" type="text/css" />
215 <title>test: primes.py annotate</title>
215 <title>test: primes.py annotate</title>
216 </head>
216 </head>
217 <body>
217 <body>
218
218
219 <div class="container">
219 <div class="container">
220 <div class="menu">
220 <div class="menu">
221 <div class="logo">
221 <div class="logo">
222 <a href="https://mercurial-scm.org/">
222 <a href="https://mercurial-scm.org/">
223 <img src="/static/hglogo.png" alt="mercurial" /></a>
223 <img src="/static/hglogo.png" alt="mercurial" /></a>
224 </div>
224 </div>
225 <ul>
225 <ul>
226 <li><a href="/shortlog/tip">log</a></li>
226 <li><a href="/shortlog/tip">log</a></li>
227 <li><a href="/graph/tip">graph</a></li>
227 <li><a href="/graph/tip">graph</a></li>
228 <li><a href="/tags">tags</a></li>
228 <li><a href="/tags">tags</a></li>
229 <li><a href="/bookmarks">bookmarks</a></li>
229 <li><a href="/bookmarks">bookmarks</a></li>
230 <li><a href="/branches">branches</a></li>
230 <li><a href="/branches">branches</a></li>
231 </ul>
231 </ul>
232
232
233 <ul>
233 <ul>
234 <li><a href="/rev/tip">changeset</a></li>
234 <li><a href="/rev/tip">changeset</a></li>
235 <li><a href="/file/tip/">browse</a></li>
235 <li><a href="/file/tip/">browse</a></li>
236 </ul>
236 </ul>
237 <ul>
237 <ul>
238 <li><a href="/file/tip/primes.py">file</a></li>
238 <li><a href="/file/tip/primes.py">file</a></li>
239 <li><a href="/file/tip/primes.py">latest</a></li>
239 <li><a href="/file/tip/primes.py">latest</a></li>
240 <li><a href="/diff/tip/primes.py">diff</a></li>
240 <li><a href="/diff/tip/primes.py">diff</a></li>
241 <li><a href="/comparison/tip/primes.py">comparison</a></li>
241 <li><a href="/comparison/tip/primes.py">comparison</a></li>
242 <li class="active">annotate</li>
242 <li class="active">annotate</li>
243 <li><a href="/log/tip/primes.py">file log</a></li>
243 <li><a href="/log/tip/primes.py">file log</a></li>
244 <li><a href="/raw-file/tip/primes.py">raw</a></li>
244 <li><a href="/raw-file/tip/primes.py">raw</a></li>
245 </ul>
245 </ul>
246 <ul>
246 <ul>
247 <li><a href="/help">help</a></li>
247 <li><a href="/help">help</a></li>
248 </ul>
248 </ul>
249 </div>
249 </div>
250
250
251 <div class="main">
251 <div class="main">
252 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
252 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
253 <h3>
253 <h3>
254 annotate primes.py @ 0:<a href="/rev/f4fca47b67e6">f4fca47b67e6</a>
254 annotate primes.py @ 0:<a href="/rev/f4fca47b67e6">f4fca47b67e6</a>
255 <span class="tag">tip</span>
255 <span class="tag">tip</span>
256 </h3>
256 </h3>
257
257
258
258
259 <form class="search" action="/log">
259 <form class="search" action="/log">
260
260
261 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
261 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
262 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
262 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
263 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
263 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
264 </form>
264 </form>
265
265
266 <div class="description">a</div>
266 <div class="description">a</div>
267
267
268 <table id="changesetEntry">
268 <table id="changesetEntry">
269 <tr>
269 <tr>
270 <th class="author">author</th>
270 <th class="author">author</th>
271 <td class="author">&#116;&#101;&#115;&#116;</td>
271 <td class="author">&#116;&#101;&#115;&#116;</td>
272 </tr>
272 </tr>
273 <tr>
273 <tr>
274 <th class="date">date</th>
274 <th class="date">date</th>
275 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
275 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
276 </tr>
276 </tr>
277 <tr>
277 <tr>
278 <th class="author">parents</th>
278 <th class="author">parents</th>
279 <td class="author"></td>
279 <td class="author"></td>
280 </tr>
280 </tr>
281 <tr>
281 <tr>
282 <th class="author">children</th>
282 <th class="author">children</th>
283 <td class="author"></td>
283 <td class="author"></td>
284 </tr>
284 </tr>
285 </table>
285 </table>
286
286
287
288 <form id="diffopts-form"
289 data-ignorews="0"
290 data-ignorewsamount="0"
291 data-ignorewseol="0"
292 data-ignoreblanklines="0">
293 <span>Ignore whitespace changes - </span>
294 <span>Everywhere:</span>
295 <input id="ignorews-checkbox" type="checkbox" />
296 <span>Within whitespace:</span>
297 <input id="ignorewsamount-checkbox" type="checkbox" />
298 <span>At end of lines:</span>
299 <input id="ignorewseol-checkbox" type="checkbox" />
300 </form>
301
302 <script type="text/javascript">
303 renderDiffOptsForm();
304 </script>
305
287 <div class="overflow">
306 <div class="overflow">
288 <table class="bigtable">
307 <table class="bigtable">
289 <thead>
308 <thead>
290 <tr>
309 <tr>
291 <th class="annotate">rev</th>
310 <th class="annotate">rev</th>
292 <th class="line">&nbsp;&nbsp;line source</th>
311 <th class="line">&nbsp;&nbsp;line source</th>
293 </tr>
312 </tr>
294 </thead>
313 </thead>
295 <tbody class="stripes2 sourcelines"
314 <tbody class="stripes2 sourcelines"
296 data-logurl="/log/tip/primes.py"
315 data-logurl="/log/tip/primes.py"
297 data-selectabletag="TR"
316 data-selectabletag="TR"
298 data-ishead="1">
317 data-ishead="1">
299
318
300 <tr id="l1" class="thisrev">
319 <tr id="l1" class="thisrev">
301 <td class="annotate parity0">
320 <td class="annotate parity0">
302 <a href="/annotate/f4fca47b67e6/primes.py#l1">
321 <a href="/annotate/f4fca47b67e6/primes.py#l1">
303 0
322 0
304 </a>
323 </a>
305 <div class="annotate-info">
324 <div class="annotate-info">
306 <div>
325 <div>
307 <a href="/annotate/f4fca47b67e6/primes.py#l1">
326 <a href="/annotate/f4fca47b67e6/primes.py#l1">
308 f4fca47b67e6</a>
327 f4fca47b67e6</a>
309 a
328 a
310 </div>
329 </div>
311 <div><em>&#116;&#101;&#115;&#116;</em></div>
330 <div><em>&#116;&#101;&#115;&#116;</em></div>
312 <div>parents: </div>
331 <div>parents: </div>
313 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
332 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
314 <a href="/rev/f4fca47b67e6">changeset</a>
333 <a href="/rev/f4fca47b67e6">changeset</a>
315 </div>
334 </div>
316 </td>
335 </td>
317 <td class="source followlines-btn-parent"><a href="#l1"> 1</a> <span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></td>
336 <td class="source followlines-btn-parent"><a href="#l1"> 1</a> <span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></td>
318 </tr>
337 </tr>
319 <tr id="l2" class="thisrev">
338 <tr id="l2" class="thisrev">
320 <td class="annotate parity0">
339 <td class="annotate parity0">
321
340
322 <div class="annotate-info">
341 <div class="annotate-info">
323 <div>
342 <div>
324 <a href="/annotate/f4fca47b67e6/primes.py#l2">
343 <a href="/annotate/f4fca47b67e6/primes.py#l2">
325 f4fca47b67e6</a>
344 f4fca47b67e6</a>
326 a
345 a
327 </div>
346 </div>
328 <div><em>&#116;&#101;&#115;&#116;</em></div>
347 <div><em>&#116;&#101;&#115;&#116;</em></div>
329 <div>parents: </div>
348 <div>parents: </div>
330 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
349 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
331 <a href="/rev/f4fca47b67e6">changeset</a>
350 <a href="/rev/f4fca47b67e6">changeset</a>
332 </div>
351 </div>
333 </td>
352 </td>
334 <td class="source followlines-btn-parent"><a href="#l2"> 2</a> </td>
353 <td class="source followlines-btn-parent"><a href="#l2"> 2</a> </td>
335 </tr>
354 </tr>
336 <tr id="l3" class="thisrev">
355 <tr id="l3" class="thisrev">
337 <td class="annotate parity0">
356 <td class="annotate parity0">
338
357
339 <div class="annotate-info">
358 <div class="annotate-info">
340 <div>
359 <div>
341 <a href="/annotate/f4fca47b67e6/primes.py#l3">
360 <a href="/annotate/f4fca47b67e6/primes.py#l3">
342 f4fca47b67e6</a>
361 f4fca47b67e6</a>
343 a
362 a
344 </div>
363 </div>
345 <div><em>&#116;&#101;&#115;&#116;</em></div>
364 <div><em>&#116;&#101;&#115;&#116;</em></div>
346 <div>parents: </div>
365 <div>parents: </div>
347 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
366 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
348 <a href="/rev/f4fca47b67e6">changeset</a>
367 <a href="/rev/f4fca47b67e6">changeset</a>
349 </div>
368 </div>
350 </td>
369 </td>
351 <td class="source followlines-btn-parent"><a href="#l3"> 3</a> <span class="sd">primes = 2 : sieve [3, 5..]</span></td>
370 <td class="source followlines-btn-parent"><a href="#l3"> 3</a> <span class="sd">primes = 2 : sieve [3, 5..]</span></td>
352 </tr>
371 </tr>
353 <tr id="l4" class="thisrev">
372 <tr id="l4" class="thisrev">
354 <td class="annotate parity0">
373 <td class="annotate parity0">
355
374
356 <div class="annotate-info">
375 <div class="annotate-info">
357 <div>
376 <div>
358 <a href="/annotate/f4fca47b67e6/primes.py#l4">
377 <a href="/annotate/f4fca47b67e6/primes.py#l4">
359 f4fca47b67e6</a>
378 f4fca47b67e6</a>
360 a
379 a
361 </div>
380 </div>
362 <div><em>&#116;&#101;&#115;&#116;</em></div>
381 <div><em>&#116;&#101;&#115;&#116;</em></div>
363 <div>parents: </div>
382 <div>parents: </div>
364 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
383 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
365 <a href="/rev/f4fca47b67e6">changeset</a>
384 <a href="/rev/f4fca47b67e6">changeset</a>
366 </div>
385 </div>
367 </td>
386 </td>
368 <td class="source followlines-btn-parent"><a href="#l4"> 4</a> <span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></td>
387 <td class="source followlines-btn-parent"><a href="#l4"> 4</a> <span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></td>
369 </tr>
388 </tr>
370 <tr id="l5" class="thisrev">
389 <tr id="l5" class="thisrev">
371 <td class="annotate parity0">
390 <td class="annotate parity0">
372
391
373 <div class="annotate-info">
392 <div class="annotate-info">
374 <div>
393 <div>
375 <a href="/annotate/f4fca47b67e6/primes.py#l5">
394 <a href="/annotate/f4fca47b67e6/primes.py#l5">
376 f4fca47b67e6</a>
395 f4fca47b67e6</a>
377 a
396 a
378 </div>
397 </div>
379 <div><em>&#116;&#101;&#115;&#116;</em></div>
398 <div><em>&#116;&#101;&#115;&#116;</em></div>
380 <div>parents: </div>
399 <div>parents: </div>
381 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
400 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
382 <a href="/rev/f4fca47b67e6">changeset</a>
401 <a href="/rev/f4fca47b67e6">changeset</a>
383 </div>
402 </div>
384 </td>
403 </td>
385 <td class="source followlines-btn-parent"><a href="#l5"> 5</a> <span class="sd">&quot;&quot;&quot;</span></td>
404 <td class="source followlines-btn-parent"><a href="#l5"> 5</a> <span class="sd">&quot;&quot;&quot;</span></td>
386 </tr>
405 </tr>
387 <tr id="l6" class="thisrev">
406 <tr id="l6" class="thisrev">
388 <td class="annotate parity0">
407 <td class="annotate parity0">
389
408
390 <div class="annotate-info">
409 <div class="annotate-info">
391 <div>
410 <div>
392 <a href="/annotate/f4fca47b67e6/primes.py#l6">
411 <a href="/annotate/f4fca47b67e6/primes.py#l6">
393 f4fca47b67e6</a>
412 f4fca47b67e6</a>
394 a
413 a
395 </div>
414 </div>
396 <div><em>&#116;&#101;&#115;&#116;</em></div>
415 <div><em>&#116;&#101;&#115;&#116;</em></div>
397 <div>parents: </div>
416 <div>parents: </div>
398 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
417 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
399 <a href="/rev/f4fca47b67e6">changeset</a>
418 <a href="/rev/f4fca47b67e6">changeset</a>
400 </div>
419 </div>
401 </td>
420 </td>
402 <td class="source followlines-btn-parent"><a href="#l6"> 6</a> </td>
421 <td class="source followlines-btn-parent"><a href="#l6"> 6</a> </td>
403 </tr>
422 </tr>
404 <tr id="l7" class="thisrev">
423 <tr id="l7" class="thisrev">
405 <td class="annotate parity0">
424 <td class="annotate parity0">
406
425
407 <div class="annotate-info">
426 <div class="annotate-info">
408 <div>
427 <div>
409 <a href="/annotate/f4fca47b67e6/primes.py#l7">
428 <a href="/annotate/f4fca47b67e6/primes.py#l7">
410 f4fca47b67e6</a>
429 f4fca47b67e6</a>
411 a
430 a
412 </div>
431 </div>
413 <div><em>&#116;&#101;&#115;&#116;</em></div>
432 <div><em>&#116;&#101;&#115;&#116;</em></div>
414 <div>parents: </div>
433 <div>parents: </div>
415 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
434 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
416 <a href="/rev/f4fca47b67e6">changeset</a>
435 <a href="/rev/f4fca47b67e6">changeset</a>
417 </div>
436 </div>
418 </td>
437 </td>
419 <td class="source followlines-btn-parent"><a href="#l7"> 7</a> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></td>
438 <td class="source followlines-btn-parent"><a href="#l7"> 7</a> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></td>
420 </tr>
439 </tr>
421 <tr id="l8" class="thisrev">
440 <tr id="l8" class="thisrev">
422 <td class="annotate parity0">
441 <td class="annotate parity0">
423
442
424 <div class="annotate-info">
443 <div class="annotate-info">
425 <div>
444 <div>
426 <a href="/annotate/f4fca47b67e6/primes.py#l8">
445 <a href="/annotate/f4fca47b67e6/primes.py#l8">
427 f4fca47b67e6</a>
446 f4fca47b67e6</a>
428 a
447 a
429 </div>
448 </div>
430 <div><em>&#116;&#101;&#115;&#116;</em></div>
449 <div><em>&#116;&#101;&#115;&#116;</em></div>
431 <div>parents: </div>
450 <div>parents: </div>
432 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
451 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
433 <a href="/rev/f4fca47b67e6">changeset</a>
452 <a href="/rev/f4fca47b67e6">changeset</a>
434 </div>
453 </div>
435 </td>
454 </td>
436 <td class="source followlines-btn-parent"><a href="#l8"> 8</a> </td>
455 <td class="source followlines-btn-parent"><a href="#l8"> 8</a> </td>
437 </tr>
456 </tr>
438 <tr id="l9" class="thisrev">
457 <tr id="l9" class="thisrev">
439 <td class="annotate parity0">
458 <td class="annotate parity0">
440
459
441 <div class="annotate-info">
460 <div class="annotate-info">
442 <div>
461 <div>
443 <a href="/annotate/f4fca47b67e6/primes.py#l9">
462 <a href="/annotate/f4fca47b67e6/primes.py#l9">
444 f4fca47b67e6</a>
463 f4fca47b67e6</a>
445 a
464 a
446 </div>
465 </div>
447 <div><em>&#116;&#101;&#115;&#116;</em></div>
466 <div><em>&#116;&#101;&#115;&#116;</em></div>
448 <div>parents: </div>
467 <div>parents: </div>
449 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
468 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
450 <a href="/rev/f4fca47b67e6">changeset</a>
469 <a href="/rev/f4fca47b67e6">changeset</a>
451 </div>
470 </div>
452 </td>
471 </td>
453 <td class="source followlines-btn-parent"><a href="#l9"> 9</a> <span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></td>
472 <td class="source followlines-btn-parent"><a href="#l9"> 9</a> <span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></td>
454 </tr>
473 </tr>
455 <tr id="l10" class="thisrev">
474 <tr id="l10" class="thisrev">
456 <td class="annotate parity0">
475 <td class="annotate parity0">
457
476
458 <div class="annotate-info">
477 <div class="annotate-info">
459 <div>
478 <div>
460 <a href="/annotate/f4fca47b67e6/primes.py#l10">
479 <a href="/annotate/f4fca47b67e6/primes.py#l10">
461 f4fca47b67e6</a>
480 f4fca47b67e6</a>
462 a
481 a
463 </div>
482 </div>
464 <div><em>&#116;&#101;&#115;&#116;</em></div>
483 <div><em>&#116;&#101;&#115;&#116;</em></div>
465 <div>parents: </div>
484 <div>parents: </div>
466 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
485 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
467 <a href="/rev/f4fca47b67e6">changeset</a>
486 <a href="/rev/f4fca47b67e6">changeset</a>
468 </div>
487 </div>
469 </td>
488 </td>
470 <td class="source followlines-btn-parent"><a href="#l10"> 10</a> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></td>
489 <td class="source followlines-btn-parent"><a href="#l10"> 10</a> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></td>
471 </tr>
490 </tr>
472 <tr id="l11" class="thisrev">
491 <tr id="l11" class="thisrev">
473 <td class="annotate parity0">
492 <td class="annotate parity0">
474
493
475 <div class="annotate-info">
494 <div class="annotate-info">
476 <div>
495 <div>
477 <a href="/annotate/f4fca47b67e6/primes.py#l11">
496 <a href="/annotate/f4fca47b67e6/primes.py#l11">
478 f4fca47b67e6</a>
497 f4fca47b67e6</a>
479 a
498 a
480 </div>
499 </div>
481 <div><em>&#116;&#101;&#115;&#116;</em></div>
500 <div><em>&#116;&#101;&#115;&#116;</em></div>
482 <div>parents: </div>
501 <div>parents: </div>
483 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
502 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
484 <a href="/rev/f4fca47b67e6">changeset</a>
503 <a href="/rev/f4fca47b67e6">changeset</a>
485 </div>
504 </div>
486 </td>
505 </td>
487 <td class="source followlines-btn-parent"><a href="#l11"> 11</a> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
506 <td class="source followlines-btn-parent"><a href="#l11"> 11</a> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
488 </tr>
507 </tr>
489 <tr id="l12" class="thisrev">
508 <tr id="l12" class="thisrev">
490 <td class="annotate parity0">
509 <td class="annotate parity0">
491
510
492 <div class="annotate-info">
511 <div class="annotate-info">
493 <div>
512 <div>
494 <a href="/annotate/f4fca47b67e6/primes.py#l12">
513 <a href="/annotate/f4fca47b67e6/primes.py#l12">
495 f4fca47b67e6</a>
514 f4fca47b67e6</a>
496 a
515 a
497 </div>
516 </div>
498 <div><em>&#116;&#101;&#115;&#116;</em></div>
517 <div><em>&#116;&#101;&#115;&#116;</em></div>
499 <div>parents: </div>
518 <div>parents: </div>
500 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
519 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
501 <a href="/rev/f4fca47b67e6">changeset</a>
520 <a href="/rev/f4fca47b67e6">changeset</a>
502 </div>
521 </div>
503 </td>
522 </td>
504 <td class="source followlines-btn-parent"><a href="#l12"> 12</a> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></td>
523 <td class="source followlines-btn-parent"><a href="#l12"> 12</a> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></td>
505 </tr>
524 </tr>
506 <tr id="l13" class="thisrev">
525 <tr id="l13" class="thisrev">
507 <td class="annotate parity0">
526 <td class="annotate parity0">
508
527
509 <div class="annotate-info">
528 <div class="annotate-info">
510 <div>
529 <div>
511 <a href="/annotate/f4fca47b67e6/primes.py#l13">
530 <a href="/annotate/f4fca47b67e6/primes.py#l13">
512 f4fca47b67e6</a>
531 f4fca47b67e6</a>
513 a
532 a
514 </div>
533 </div>
515 <div><em>&#116;&#101;&#115;&#116;</em></div>
534 <div><em>&#116;&#101;&#115;&#116;</em></div>
516 <div>parents: </div>
535 <div>parents: </div>
517 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
536 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
518 <a href="/rev/f4fca47b67e6">changeset</a>
537 <a href="/rev/f4fca47b67e6">changeset</a>
519 </div>
538 </div>
520 </td>
539 </td>
521 <td class="source followlines-btn-parent"><a href="#l13"> 13</a> <span class="c"># It is important to yield *here* in order to stop the</span></td>
540 <td class="source followlines-btn-parent"><a href="#l13"> 13</a> <span class="c"># It is important to yield *here* in order to stop the</span></td>
522 </tr>
541 </tr>
523 <tr id="l14" class="thisrev">
542 <tr id="l14" class="thisrev">
524 <td class="annotate parity0">
543 <td class="annotate parity0">
525
544
526 <div class="annotate-info">
545 <div class="annotate-info">
527 <div>
546 <div>
528 <a href="/annotate/f4fca47b67e6/primes.py#l14">
547 <a href="/annotate/f4fca47b67e6/primes.py#l14">
529 f4fca47b67e6</a>
548 f4fca47b67e6</a>
530 a
549 a
531 </div>
550 </div>
532 <div><em>&#116;&#101;&#115;&#116;</em></div>
551 <div><em>&#116;&#101;&#115;&#116;</em></div>
533 <div>parents: </div>
552 <div>parents: </div>
534 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
553 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
535 <a href="/rev/f4fca47b67e6">changeset</a>
554 <a href="/rev/f4fca47b67e6">changeset</a>
536 </div>
555 </div>
537 </td>
556 </td>
538 <td class="source followlines-btn-parent"><a href="#l14"> 14</a> <span class="c"># infinite recursion.</span></td>
557 <td class="source followlines-btn-parent"><a href="#l14"> 14</a> <span class="c"># infinite recursion.</span></td>
539 </tr>
558 </tr>
540 <tr id="l15" class="thisrev">
559 <tr id="l15" class="thisrev">
541 <td class="annotate parity0">
560 <td class="annotate parity0">
542
561
543 <div class="annotate-info">
562 <div class="annotate-info">
544 <div>
563 <div>
545 <a href="/annotate/f4fca47b67e6/primes.py#l15">
564 <a href="/annotate/f4fca47b67e6/primes.py#l15">
546 f4fca47b67e6</a>
565 f4fca47b67e6</a>
547 a
566 a
548 </div>
567 </div>
549 <div><em>&#116;&#101;&#115;&#116;</em></div>
568 <div><em>&#116;&#101;&#115;&#116;</em></div>
550 <div>parents: </div>
569 <div>parents: </div>
551 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
570 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
552 <a href="/rev/f4fca47b67e6">changeset</a>
571 <a href="/rev/f4fca47b67e6">changeset</a>
553 </div>
572 </div>
554 </td>
573 </td>
555 <td class="source followlines-btn-parent"><a href="#l15"> 15</a> <span class="kn">yield</span> <span class="n">p</span></td>
574 <td class="source followlines-btn-parent"><a href="#l15"> 15</a> <span class="kn">yield</span> <span class="n">p</span></td>
556 </tr>
575 </tr>
557 <tr id="l16" class="thisrev">
576 <tr id="l16" class="thisrev">
558 <td class="annotate parity0">
577 <td class="annotate parity0">
559
578
560 <div class="annotate-info">
579 <div class="annotate-info">
561 <div>
580 <div>
562 <a href="/annotate/f4fca47b67e6/primes.py#l16">
581 <a href="/annotate/f4fca47b67e6/primes.py#l16">
563 f4fca47b67e6</a>
582 f4fca47b67e6</a>
564 a
583 a
565 </div>
584 </div>
566 <div><em>&#116;&#101;&#115;&#116;</em></div>
585 <div><em>&#116;&#101;&#115;&#116;</em></div>
567 <div>parents: </div>
586 <div>parents: </div>
568 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
587 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
569 <a href="/rev/f4fca47b67e6">changeset</a>
588 <a href="/rev/f4fca47b67e6">changeset</a>
570 </div>
589 </div>
571 </td>
590 </td>
572 <td class="source followlines-btn-parent"><a href="#l16"> 16</a> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></td>
591 <td class="source followlines-btn-parent"><a href="#l16"> 16</a> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></td>
573 </tr>
592 </tr>
574 <tr id="l17" class="thisrev">
593 <tr id="l17" class="thisrev">
575 <td class="annotate parity0">
594 <td class="annotate parity0">
576
595
577 <div class="annotate-info">
596 <div class="annotate-info">
578 <div>
597 <div>
579 <a href="/annotate/f4fca47b67e6/primes.py#l17">
598 <a href="/annotate/f4fca47b67e6/primes.py#l17">
580 f4fca47b67e6</a>
599 f4fca47b67e6</a>
581 a
600 a
582 </div>
601 </div>
583 <div><em>&#116;&#101;&#115;&#116;</em></div>
602 <div><em>&#116;&#101;&#115;&#116;</em></div>
584 <div>parents: </div>
603 <div>parents: </div>
585 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
604 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
586 <a href="/rev/f4fca47b67e6">changeset</a>
605 <a href="/rev/f4fca47b67e6">changeset</a>
587 </div>
606 </div>
588 </td>
607 </td>
589 <td class="source followlines-btn-parent"><a href="#l17"> 17</a> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
608 <td class="source followlines-btn-parent"><a href="#l17"> 17</a> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
590 </tr>
609 </tr>
591 <tr id="l18" class="thisrev">
610 <tr id="l18" class="thisrev">
592 <td class="annotate parity0">
611 <td class="annotate parity0">
593
612
594 <div class="annotate-info">
613 <div class="annotate-info">
595 <div>
614 <div>
596 <a href="/annotate/f4fca47b67e6/primes.py#l18">
615 <a href="/annotate/f4fca47b67e6/primes.py#l18">
597 f4fca47b67e6</a>
616 f4fca47b67e6</a>
598 a
617 a
599 </div>
618 </div>
600 <div><em>&#116;&#101;&#115;&#116;</em></div>
619 <div><em>&#116;&#101;&#115;&#116;</em></div>
601 <div>parents: </div>
620 <div>parents: </div>
602 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
621 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
603 <a href="/rev/f4fca47b67e6">changeset</a>
622 <a href="/rev/f4fca47b67e6">changeset</a>
604 </div>
623 </div>
605 </td>
624 </td>
606 <td class="source followlines-btn-parent"><a href="#l18"> 18</a> <span class="kn">yield</span> <span class="n">n</span></td>
625 <td class="source followlines-btn-parent"><a href="#l18"> 18</a> <span class="kn">yield</span> <span class="n">n</span></td>
607 </tr>
626 </tr>
608 <tr id="l19" class="thisrev">
627 <tr id="l19" class="thisrev">
609 <td class="annotate parity0">
628 <td class="annotate parity0">
610
629
611 <div class="annotate-info">
630 <div class="annotate-info">
612 <div>
631 <div>
613 <a href="/annotate/f4fca47b67e6/primes.py#l19">
632 <a href="/annotate/f4fca47b67e6/primes.py#l19">
614 f4fca47b67e6</a>
633 f4fca47b67e6</a>
615 a
634 a
616 </div>
635 </div>
617 <div><em>&#116;&#101;&#115;&#116;</em></div>
636 <div><em>&#116;&#101;&#115;&#116;</em></div>
618 <div>parents: </div>
637 <div>parents: </div>
619 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
638 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
620 <a href="/rev/f4fca47b67e6">changeset</a>
639 <a href="/rev/f4fca47b67e6">changeset</a>
621 </div>
640 </div>
622 </td>
641 </td>
623 <td class="source followlines-btn-parent"><a href="#l19"> 19</a> </td>
642 <td class="source followlines-btn-parent"><a href="#l19"> 19</a> </td>
624 </tr>
643 </tr>
625 <tr id="l20" class="thisrev">
644 <tr id="l20" class="thisrev">
626 <td class="annotate parity0">
645 <td class="annotate parity0">
627
646
628 <div class="annotate-info">
647 <div class="annotate-info">
629 <div>
648 <div>
630 <a href="/annotate/f4fca47b67e6/primes.py#l20">
649 <a href="/annotate/f4fca47b67e6/primes.py#l20">
631 f4fca47b67e6</a>
650 f4fca47b67e6</a>
632 a
651 a
633 </div>
652 </div>
634 <div><em>&#116;&#101;&#115;&#116;</em></div>
653 <div><em>&#116;&#101;&#115;&#116;</em></div>
635 <div>parents: </div>
654 <div>parents: </div>
636 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
655 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
637 <a href="/rev/f4fca47b67e6">changeset</a>
656 <a href="/rev/f4fca47b67e6">changeset</a>
638 </div>
657 </div>
639 </td>
658 </td>
640 <td class="source followlines-btn-parent"><a href="#l20"> 20</a> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></td>
659 <td class="source followlines-btn-parent"><a href="#l20"> 20</a> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></td>
641 </tr>
660 </tr>
642 <tr id="l21" class="thisrev">
661 <tr id="l21" class="thisrev">
643 <td class="annotate parity0">
662 <td class="annotate parity0">
644
663
645 <div class="annotate-info">
664 <div class="annotate-info">
646 <div>
665 <div>
647 <a href="/annotate/f4fca47b67e6/primes.py#l21">
666 <a href="/annotate/f4fca47b67e6/primes.py#l21">
648 f4fca47b67e6</a>
667 f4fca47b67e6</a>
649 a
668 a
650 </div>
669 </div>
651 <div><em>&#116;&#101;&#115;&#116;</em></div>
670 <div><em>&#116;&#101;&#115;&#116;</em></div>
652 <div>parents: </div>
671 <div>parents: </div>
653 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
672 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
654 <a href="/rev/f4fca47b67e6">changeset</a>
673 <a href="/rev/f4fca47b67e6">changeset</a>
655 </div>
674 </div>
656 </td>
675 </td>
657 <td class="source followlines-btn-parent"><a href="#l21"> 21</a> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></td>
676 <td class="source followlines-btn-parent"><a href="#l21"> 21</a> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></td>
658 </tr>
677 </tr>
659 <tr id="l22" class="thisrev">
678 <tr id="l22" class="thisrev">
660 <td class="annotate parity0">
679 <td class="annotate parity0">
661
680
662 <div class="annotate-info">
681 <div class="annotate-info">
663 <div>
682 <div>
664 <a href="/annotate/f4fca47b67e6/primes.py#l22">
683 <a href="/annotate/f4fca47b67e6/primes.py#l22">
665 f4fca47b67e6</a>
684 f4fca47b67e6</a>
666 a
685 a
667 </div>
686 </div>
668 <div><em>&#116;&#101;&#115;&#116;</em></div>
687 <div><em>&#116;&#101;&#115;&#116;</em></div>
669 <div>parents: </div>
688 <div>parents: </div>
670 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
689 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
671 <a href="/rev/f4fca47b67e6">changeset</a>
690 <a href="/rev/f4fca47b67e6">changeset</a>
672 </div>
691 </div>
673 </td>
692 </td>
674 <td class="source followlines-btn-parent"><a href="#l22"> 22</a> </td>
693 <td class="source followlines-btn-parent"><a href="#l22"> 22</a> </td>
675 </tr>
694 </tr>
676 <tr id="l23" class="thisrev">
695 <tr id="l23" class="thisrev">
677 <td class="annotate parity0">
696 <td class="annotate parity0">
678
697
679 <div class="annotate-info">
698 <div class="annotate-info">
680 <div>
699 <div>
681 <a href="/annotate/f4fca47b67e6/primes.py#l23">
700 <a href="/annotate/f4fca47b67e6/primes.py#l23">
682 f4fca47b67e6</a>
701 f4fca47b67e6</a>
683 a
702 a
684 </div>
703 </div>
685 <div><em>&#116;&#101;&#115;&#116;</em></div>
704 <div><em>&#116;&#101;&#115;&#116;</em></div>
686 <div>parents: </div>
705 <div>parents: </div>
687 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
706 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
688 <a href="/rev/f4fca47b67e6">changeset</a>
707 <a href="/rev/f4fca47b67e6">changeset</a>
689 </div>
708 </div>
690 </td>
709 </td>
691 <td class="source followlines-btn-parent"><a href="#l23"> 23</a> <span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></td>
710 <td class="source followlines-btn-parent"><a href="#l23"> 23</a> <span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></td>
692 </tr>
711 </tr>
693 <tr id="l24" class="thisrev">
712 <tr id="l24" class="thisrev">
694 <td class="annotate parity0">
713 <td class="annotate parity0">
695
714
696 <div class="annotate-info">
715 <div class="annotate-info">
697 <div>
716 <div>
698 <a href="/annotate/f4fca47b67e6/primes.py#l24">
717 <a href="/annotate/f4fca47b67e6/primes.py#l24">
699 f4fca47b67e6</a>
718 f4fca47b67e6</a>
700 a
719 a
701 </div>
720 </div>
702 <div><em>&#116;&#101;&#115;&#116;</em></div>
721 <div><em>&#116;&#101;&#115;&#116;</em></div>
703 <div>parents: </div>
722 <div>parents: </div>
704 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
723 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
705 <a href="/rev/f4fca47b67e6">changeset</a>
724 <a href="/rev/f4fca47b67e6">changeset</a>
706 </div>
725 </div>
707 </td>
726 </td>
708 <td class="source followlines-btn-parent"><a href="#l24"> 24</a> <span class="kn">import</span> <span class="nn">sys</span></td>
727 <td class="source followlines-btn-parent"><a href="#l24"> 24</a> <span class="kn">import</span> <span class="nn">sys</span></td>
709 </tr>
728 </tr>
710 <tr id="l25" class="thisrev">
729 <tr id="l25" class="thisrev">
711 <td class="annotate parity0">
730 <td class="annotate parity0">
712
731
713 <div class="annotate-info">
732 <div class="annotate-info">
714 <div>
733 <div>
715 <a href="/annotate/f4fca47b67e6/primes.py#l25">
734 <a href="/annotate/f4fca47b67e6/primes.py#l25">
716 f4fca47b67e6</a>
735 f4fca47b67e6</a>
717 a
736 a
718 </div>
737 </div>
719 <div><em>&#116;&#101;&#115;&#116;</em></div>
738 <div><em>&#116;&#101;&#115;&#116;</em></div>
720 <div>parents: </div>
739 <div>parents: </div>
721 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
740 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
722 <a href="/rev/f4fca47b67e6">changeset</a>
741 <a href="/rev/f4fca47b67e6">changeset</a>
723 </div>
742 </div>
724 </td>
743 </td>
725 <td class="source followlines-btn-parent"><a href="#l25"> 25</a> <span class="kn">try</span><span class="p">:</span></td>
744 <td class="source followlines-btn-parent"><a href="#l25"> 25</a> <span class="kn">try</span><span class="p">:</span></td>
726 </tr>
745 </tr>
727 <tr id="l26" class="thisrev">
746 <tr id="l26" class="thisrev">
728 <td class="annotate parity0">
747 <td class="annotate parity0">
729
748
730 <div class="annotate-info">
749 <div class="annotate-info">
731 <div>
750 <div>
732 <a href="/annotate/f4fca47b67e6/primes.py#l26">
751 <a href="/annotate/f4fca47b67e6/primes.py#l26">
733 f4fca47b67e6</a>
752 f4fca47b67e6</a>
734 a
753 a
735 </div>
754 </div>
736 <div><em>&#116;&#101;&#115;&#116;</em></div>
755 <div><em>&#116;&#101;&#115;&#116;</em></div>
737 <div>parents: </div>
756 <div>parents: </div>
738 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
757 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
739 <a href="/rev/f4fca47b67e6">changeset</a>
758 <a href="/rev/f4fca47b67e6">changeset</a>
740 </div>
759 </div>
741 </td>
760 </td>
742 <td class="source followlines-btn-parent"><a href="#l26"> 26</a> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></td>
761 <td class="source followlines-btn-parent"><a href="#l26"> 26</a> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></td>
743 </tr>
762 </tr>
744 <tr id="l27" class="thisrev">
763 <tr id="l27" class="thisrev">
745 <td class="annotate parity0">
764 <td class="annotate parity0">
746
765
747 <div class="annotate-info">
766 <div class="annotate-info">
748 <div>
767 <div>
749 <a href="/annotate/f4fca47b67e6/primes.py#l27">
768 <a href="/annotate/f4fca47b67e6/primes.py#l27">
750 f4fca47b67e6</a>
769 f4fca47b67e6</a>
751 a
770 a
752 </div>
771 </div>
753 <div><em>&#116;&#101;&#115;&#116;</em></div>
772 <div><em>&#116;&#101;&#115;&#116;</em></div>
754 <div>parents: </div>
773 <div>parents: </div>
755 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
774 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
756 <a href="/rev/f4fca47b67e6">changeset</a>
775 <a href="/rev/f4fca47b67e6">changeset</a>
757 </div>
776 </div>
758 </td>
777 </td>
759 <td class="source followlines-btn-parent"><a href="#l27"> 27</a> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></td>
778 <td class="source followlines-btn-parent"><a href="#l27"> 27</a> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></td>
760 </tr>
779 </tr>
761 <tr id="l28" class="thisrev">
780 <tr id="l28" class="thisrev">
762 <td class="annotate parity0">
781 <td class="annotate parity0">
763
782
764 <div class="annotate-info">
783 <div class="annotate-info">
765 <div>
784 <div>
766 <a href="/annotate/f4fca47b67e6/primes.py#l28">
785 <a href="/annotate/f4fca47b67e6/primes.py#l28">
767 f4fca47b67e6</a>
786 f4fca47b67e6</a>
768 a
787 a
769 </div>
788 </div>
770 <div><em>&#116;&#101;&#115;&#116;</em></div>
789 <div><em>&#116;&#101;&#115;&#116;</em></div>
771 <div>parents: </div>
790 <div>parents: </div>
772 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
791 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
773 <a href="/rev/f4fca47b67e6">changeset</a>
792 <a href="/rev/f4fca47b67e6">changeset</a>
774 </div>
793 </div>
775 </td>
794 </td>
776 <td class="source followlines-btn-parent"><a href="#l28"> 28</a> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></td>
795 <td class="source followlines-btn-parent"><a href="#l28"> 28</a> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></td>
777 </tr>
796 </tr>
778 <tr id="l29" class="thisrev">
797 <tr id="l29" class="thisrev">
779 <td class="annotate parity0">
798 <td class="annotate parity0">
780
799
781 <div class="annotate-info">
800 <div class="annotate-info">
782 <div>
801 <div>
783 <a href="/annotate/f4fca47b67e6/primes.py#l29">
802 <a href="/annotate/f4fca47b67e6/primes.py#l29">
784 f4fca47b67e6</a>
803 f4fca47b67e6</a>
785 a
804 a
786 </div>
805 </div>
787 <div><em>&#116;&#101;&#115;&#116;</em></div>
806 <div><em>&#116;&#101;&#115;&#116;</em></div>
788 <div>parents: </div>
807 <div>parents: </div>
789 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
808 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
790 <a href="/rev/f4fca47b67e6">changeset</a>
809 <a href="/rev/f4fca47b67e6">changeset</a>
791 </div>
810 </div>
792 </td>
811 </td>
793 <td class="source followlines-btn-parent"><a href="#l29"> 29</a> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></td>
812 <td class="source followlines-btn-parent"><a href="#l29"> 29</a> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></td>
794 </tr>
813 </tr>
795 <tr id="l30" class="thisrev">
814 <tr id="l30" class="thisrev">
796 <td class="annotate parity0">
815 <td class="annotate parity0">
797
816
798 <div class="annotate-info">
817 <div class="annotate-info">
799 <div>
818 <div>
800 <a href="/annotate/f4fca47b67e6/primes.py#l30">
819 <a href="/annotate/f4fca47b67e6/primes.py#l30">
801 f4fca47b67e6</a>
820 f4fca47b67e6</a>
802 a
821 a
803 </div>
822 </div>
804 <div><em>&#116;&#101;&#115;&#116;</em></div>
823 <div><em>&#116;&#101;&#115;&#116;</em></div>
805 <div>parents: </div>
824 <div>parents: </div>
806 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
825 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
807 <a href="/rev/f4fca47b67e6">changeset</a>
826 <a href="/rev/f4fca47b67e6">changeset</a>
808 </div>
827 </div>
809 </td>
828 </td>
810 <td class="source followlines-btn-parent"><a href="#l30"> 30</a> <span class="kn">print</span><span class="p">(</span><span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">))))</span></td>
829 <td class="source followlines-btn-parent"><a href="#l30"> 30</a> <span class="kn">print</span><span class="p">(</span><span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">))))</span></td>
811 </tr>
830 </tr>
812 <tr id="l31" class="thisrev">
831 <tr id="l31" class="thisrev">
813 <td class="annotate parity0">
832 <td class="annotate parity0">
814
833
815 <div class="annotate-info">
834 <div class="annotate-info">
816 <div>
835 <div>
817 <a href="/annotate/f4fca47b67e6/primes.py#l31">
836 <a href="/annotate/f4fca47b67e6/primes.py#l31">
818 f4fca47b67e6</a>
837 f4fca47b67e6</a>
819 a
838 a
820 </div>
839 </div>
821 <div><em>&#116;&#101;&#115;&#116;</em></div>
840 <div><em>&#116;&#101;&#115;&#116;</em></div>
822 <div>parents: </div>
841 <div>parents: </div>
823 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
842 <a href="/diff/f4fca47b67e6/primes.py">diff</a>
824 <a href="/rev/f4fca47b67e6">changeset</a>
843 <a href="/rev/f4fca47b67e6">changeset</a>
825 </div>
844 </div>
826 </td>
845 </td>
827 <td class="source followlines-btn-parent"><a href="#l31"> 31</a> </td>
846 <td class="source followlines-btn-parent"><a href="#l31"> 31</a> </td>
828 </tr>
847 </tr>
829 </tbody>
848 </tbody>
830 </table>
849 </table>
831 </div>
850 </div>
832 </div>
851 </div>
833 </div>
852 </div>
834
853
835 <script type="text/javascript" src="/static/followlines.js"></script>
854 <script type="text/javascript" src="/static/followlines.js"></script>
836
855
837
856
838
857
839 </body>
858 </body>
840 </html>
859 </html>
841
860
842
861
843 hgweb fileannotate, raw
862 hgweb fileannotate, raw
844
863
845 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py?style=raw') \
864 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py?style=raw') \
846 > | sed "s/test@//" > a
865 > | sed "s/test@//" > a
847 $ echo "200 Script output follows" > b
866 $ echo "200 Script output follows" > b
848 $ echo "" >> b
867 $ echo "" >> b
849 $ echo "" >> b
868 $ echo "" >> b
850 $ hg annotate "primes.py" >> b
869 $ hg annotate "primes.py" >> b
851 $ echo "" >> b
870 $ echo "" >> b
852 $ echo "" >> b
871 $ echo "" >> b
853 $ echo "" >> b
872 $ echo "" >> b
854 $ echo "" >> b
873 $ echo "" >> b
855 $ cmp b a || diff -u b a
874 $ cmp b a || diff -u b a
856
875
857 hgweb filerevision, raw
876 hgweb filerevision, raw
858
877
859 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py?style=raw') \
878 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py?style=raw') \
860 > > a
879 > > a
861 $ echo "200 Script output follows" > b
880 $ echo "200 Script output follows" > b
862 $ echo "" >> b
881 $ echo "" >> b
863 $ hg cat primes.py >> b
882 $ hg cat primes.py >> b
864 $ cmp b a || diff -u b a
883 $ cmp b a || diff -u b a
865
884
866 hgweb highlightcss friendly
885 hgweb highlightcss friendly
867
886
868 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
887 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
869 $ head -n 4 out
888 $ head -n 4 out
870 200 Script output follows
889 200 Script output follows
871
890
872 /* pygments_style = friendly */
891 /* pygments_style = friendly */
873
892
874 $ rm out
893 $ rm out
875
894
876 errors encountered
895 errors encountered
877
896
878 $ cat errors.log
897 $ cat errors.log
879 $ killdaemons.py
898 $ killdaemons.py
880
899
881 Change the pygments style
900 Change the pygments style
882
901
883 $ cat > .hg/hgrc <<EOF
902 $ cat > .hg/hgrc <<EOF
884 > [web]
903 > [web]
885 > pygments_style = fruity
904 > pygments_style = fruity
886 > EOF
905 > EOF
887
906
888 hg serve again
907 hg serve again
889
908
890 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
909 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
891 $ cat hg.pid >> $DAEMON_PIDS
910 $ cat hg.pid >> $DAEMON_PIDS
892
911
893 hgweb highlightcss fruity
912 hgweb highlightcss fruity
894
913
895 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
914 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
896 $ head -n 4 out
915 $ head -n 4 out
897 200 Script output follows
916 200 Script output follows
898
917
899 /* pygments_style = fruity */
918 /* pygments_style = fruity */
900
919
901 $ rm out
920 $ rm out
902
921
903 errors encountered
922 errors encountered
904
923
905 $ cat errors.log
924 $ cat errors.log
906 $ killdaemons.py
925 $ killdaemons.py
907
926
908 only highlight C source files
927 only highlight C source files
909
928
910 $ cat > .hg/hgrc <<EOF
929 $ cat > .hg/hgrc <<EOF
911 > [web]
930 > [web]
912 > highlightfiles = **.c
931 > highlightfiles = **.c
913 > EOF
932 > EOF
914
933
915 hg serve again
934 hg serve again
916
935
917 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
936 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
918 $ cat hg.pid >> $DAEMON_PIDS
937 $ cat hg.pid >> $DAEMON_PIDS
919
938
920 test that fileset in highlightfiles works and primes.py is not highlighted
939 test that fileset in highlightfiles works and primes.py is not highlighted
921
940
922 $ get-with-headers.py localhost:$HGPORT 'file/tip/primes.py' | grep 'id="l11"'
941 $ get-with-headers.py localhost:$HGPORT 'file/tip/primes.py' | grep 'id="l11"'
923 <span id="l11"> def sieve(ns):</span><a href="#l11"></a>
942 <span id="l11"> def sieve(ns):</span><a href="#l11"></a>
924
943
925 errors encountered
944 errors encountered
926
945
927 $ cat errors.log
946 $ cat errors.log
928 $ cd ..
947 $ cd ..
929 $ hg init eucjp
948 $ hg init eucjp
930 $ cd eucjp
949 $ cd eucjp
931 $ $PYTHON -c 'print("\265\376")' >> eucjp.txt # Japanese kanji "Kyo"
950 $ $PYTHON -c 'print("\265\376")' >> eucjp.txt # Japanese kanji "Kyo"
932 $ hg ci -Ama
951 $ hg ci -Ama
933 adding eucjp.txt
952 adding eucjp.txt
934 $ hgserveget () {
953 $ hgserveget () {
935 > killdaemons.py
954 > killdaemons.py
936 > echo % HGENCODING="$1" hg serve
955 > echo % HGENCODING="$1" hg serve
937 > HGENCODING="$1" hg serve -p $HGPORT -d -n test --pid-file=hg.pid -E errors.log
956 > HGENCODING="$1" hg serve -p $HGPORT -d -n test --pid-file=hg.pid -E errors.log
938 > cat hg.pid >> $DAEMON_PIDS
957 > cat hg.pid >> $DAEMON_PIDS
939 >
958 >
940 > echo % hgweb filerevision, html
959 > echo % hgweb filerevision, html
941 > get-with-headers.py localhost:$HGPORT "file/tip/$2" \
960 > get-with-headers.py localhost:$HGPORT "file/tip/$2" \
942 > | grep '<div class="parity0 source">'
961 > | grep '<div class="parity0 source">'
943 > echo % errors encountered
962 > echo % errors encountered
944 > cat errors.log
963 > cat errors.log
945 > }
964 > }
946 $ hgserveget euc-jp eucjp.txt
965 $ hgserveget euc-jp eucjp.txt
947 % HGENCODING=euc-jp hg serve
966 % HGENCODING=euc-jp hg serve
948 % hgweb filerevision, html
967 % hgweb filerevision, html
949 % errors encountered
968 % errors encountered
950 $ hgserveget utf-8 eucjp.txt
969 $ hgserveget utf-8 eucjp.txt
951 % HGENCODING=utf-8 hg serve
970 % HGENCODING=utf-8 hg serve
952 % hgweb filerevision, html
971 % hgweb filerevision, html
953 % errors encountered
972 % errors encountered
954 $ hgserveget us-ascii eucjp.txt
973 $ hgserveget us-ascii eucjp.txt
955 % HGENCODING=us-ascii hg serve
974 % HGENCODING=us-ascii hg serve
956 % hgweb filerevision, html
975 % hgweb filerevision, html
957 % errors encountered
976 % errors encountered
958
977
959 We attempt to highlight unknown files by default
978 We attempt to highlight unknown files by default
960
979
961 $ killdaemons.py
980 $ killdaemons.py
962
981
963 $ cat > .hg/hgrc << EOF
982 $ cat > .hg/hgrc << EOF
964 > [web]
983 > [web]
965 > highlightfiles = **
984 > highlightfiles = **
966 > EOF
985 > EOF
967
986
968 $ cat > unknownfile << EOF
987 $ cat > unknownfile << EOF
969 > #!$PYTHON
988 > #!$PYTHON
970 > def foo():
989 > def foo():
971 > pass
990 > pass
972 > EOF
991 > EOF
973
992
974 $ hg add unknownfile
993 $ hg add unknownfile
975 $ hg commit -m unknown unknownfile
994 $ hg commit -m unknown unknownfile
976
995
977 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
996 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
978 $ cat hg.pid >> $DAEMON_PIDS
997 $ cat hg.pid >> $DAEMON_PIDS
979
998
980 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
999 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
981 <span id="l2"><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span></span><a href="#l2"></a>
1000 <span id="l2"><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span></span><a href="#l2"></a>
982
1001
983 We can prevent Pygments from falling back to a non filename-based
1002 We can prevent Pygments from falling back to a non filename-based
984 detection mode
1003 detection mode
985
1004
986 $ cat > .hg/hgrc << EOF
1005 $ cat > .hg/hgrc << EOF
987 > [web]
1006 > [web]
988 > highlightfiles = **
1007 > highlightfiles = **
989 > highlightonlymatchfilename = true
1008 > highlightonlymatchfilename = true
990 > EOF
1009 > EOF
991
1010
992 $ killdaemons.py
1011 $ killdaemons.py
993 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
1012 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
994 $ cat hg.pid >> $DAEMON_PIDS
1013 $ cat hg.pid >> $DAEMON_PIDS
995 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
1014 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
996 <span id="l2">def foo():</span><a href="#l2"></a>
1015 <span id="l2">def foo():</span><a href="#l2"></a>
997
1016
998 $ cd ..
1017 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now