##// END OF EJS Templates
moved tooltip function from the for loop and from templates to helpers
marcink -
r990:7a1df013 beta
parent child Browse files
Show More
@@ -1,595 +1,605
1 1 """Helper functions
2 2
3 3 Consists of functions to typically be used within templates, but also
4 4 available to Controllers. This module is available to both as 'h'.
5 5 """
6 6 import random
7 7 import hashlib
8 8 import StringIO
9 9 from pygments.formatters import HtmlFormatter
10 10 from pygments import highlight as code_highlight
11 11 from pylons import url, app_globals as g
12 12 from pylons.i18n.translation import _, ungettext
13 13 from vcs.utils.annotate import annotate_highlight
14 14 from webhelpers.html import literal, HTML, escape
15 15 from webhelpers.html.tools import *
16 16 from webhelpers.html.builder import make_tag
17 17 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
18 18 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
19 19 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
20 20 password, textarea, title, ul, xml_declaration, radio
21 21 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
22 22 mail_to, strip_links, strip_tags, tag_re
23 23 from webhelpers.number import format_byte_size, format_bit_size
24 24 from webhelpers.pylonslib import Flash as _Flash
25 25 from webhelpers.pylonslib.secure_form import secure_form
26 26 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
27 27 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
28 28 replace_whitespace, urlify, truncate, wrap_paragraphs
29 29 from webhelpers.date import time_ago_in_words
30 30
31 31 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
32 32 convert_boolean_attrs, NotGiven
33 33
34 34 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
35 35 """Reset button
36 36 """
37 37 _set_input_attrs(attrs, type, name, value)
38 38 _set_id_attr(attrs, id, name)
39 39 convert_boolean_attrs(attrs, ["disabled"])
40 40 return HTML.input(**attrs)
41 41
42 42 reset = _reset
43 43
44 44
45 45 def get_token():
46 46 """Return the current authentication token, creating one if one doesn't
47 47 already exist.
48 48 """
49 49 token_key = "_authentication_token"
50 50 from pylons import session
51 51 if not token_key in session:
52 52 try:
53 53 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
54 54 except AttributeError: # Python < 2.4
55 55 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
56 56 session[token_key] = token
57 57 if hasattr(session, 'save'):
58 58 session.save()
59 59 return session[token_key]
60 60
61 61 class _GetError(object):
62 62 """Get error from form_errors, and represent it as span wrapped error
63 63 message
64 64
65 65 :param field_name: field to fetch errors for
66 66 :param form_errors: form errors dict
67 67 """
68 68
69 69 def __call__(self, field_name, form_errors):
70 70 tmpl = """<span class="error_msg">%s</span>"""
71 71 if form_errors and form_errors.has_key(field_name):
72 72 return literal(tmpl % form_errors.get(field_name))
73 73
74 74 get_error = _GetError()
75 75
76 76 def recursive_replace(str, replace=' '):
77 77 """Recursive replace of given sign to just one instance
78 78
79 79 :param str: given string
80 80 :param replace: char to find and replace multiple instances
81 81
82 82 Examples::
83 83 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
84 84 'Mighty-Mighty-Bo-sstones'
85 85 """
86 86
87 87 if str.find(replace * 2) == -1:
88 88 return str
89 89 else:
90 90 str = str.replace(replace * 2, replace)
91 91 return recursive_replace(str, replace)
92 92
93 93 class _ToolTip(object):
94 94
95 95 def __call__(self, tooltip_title, trim_at=50):
96 96 """Special function just to wrap our text into nice formatted
97 97 autowrapped text
98 98
99 99 :param tooltip_title:
100 100 """
101 101
102 102 return wrap_paragraphs(escape(tooltip_title), trim_at)\
103 103 .replace('\n', '<br/>')
104 104
105 105 def activate(self):
106 106 """Adds tooltip mechanism to the given Html all tooltips have to have
107 107 set class `tooltip` and set attribute `tooltip_title`.
108 108 Then a tooltip will be generated based on that. All with yui js tooltip
109 109 """
110 110
111 111 js = '''
112 112 YAHOO.util.Event.onDOMReady(function(){
113 113 function toolTipsId(){
114 114 var ids = [];
115 115 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
116 116
117 117 for (var i = 0; i < tts.length; i++) {
118 118 //if element doesn't not have and id autogenerate one for tooltip
119 119
120 120 if (!tts[i].id){
121 121 tts[i].id='tt'+i*100;
122 122 }
123 123 ids.push(tts[i].id);
124 124 }
125 125 return ids
126 126 };
127 127 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
128 128 context: toolTipsId(),
129 129 monitorresize:false,
130 130 xyoffset :[0,0],
131 131 autodismissdelay:300000,
132 132 hidedelay:5,
133 133 showdelay:20,
134 134 });
135 135
136 136 // Set the text for the tooltip just before we display it. Lazy method
137 137 myToolTips.contextTriggerEvent.subscribe(
138 138 function(type, args) {
139 139
140 140 var context = args[0];
141 141
142 142 //positioning of tooltip
143 143 var tt_w = this.element.clientWidth;//tooltip width
144 144 var tt_h = this.element.clientHeight;//tooltip height
145 145
146 146 var context_w = context.offsetWidth;
147 147 var context_h = context.offsetHeight;
148 148
149 149 var pos_x = YAHOO.util.Dom.getX(context);
150 150 var pos_y = YAHOO.util.Dom.getY(context);
151 151
152 152 var display_strategy = 'right';
153 153 var xy_pos = [0,0];
154 154 switch (display_strategy){
155 155
156 156 case 'top':
157 157 var cur_x = (pos_x+context_w/2)-(tt_w/2);
158 158 var cur_y = (pos_y-tt_h-4);
159 159 xy_pos = [cur_x,cur_y];
160 160 break;
161 161 case 'bottom':
162 162 var cur_x = (pos_x+context_w/2)-(tt_w/2);
163 163 var cur_y = pos_y+context_h+4;
164 164 xy_pos = [cur_x,cur_y];
165 165 break;
166 166 case 'left':
167 167 var cur_x = (pos_x-tt_w-4);
168 168 var cur_y = pos_y-((tt_h/2)-context_h/2);
169 169 xy_pos = [cur_x,cur_y];
170 170 break;
171 171 case 'right':
172 172 var cur_x = (pos_x+context_w+4);
173 173 var cur_y = pos_y-((tt_h/2)-context_h/2);
174 174 xy_pos = [cur_x,cur_y];
175 175 break;
176 176 default:
177 177 var cur_x = (pos_x+context_w/2)-(tt_w/2);
178 178 var cur_y = pos_y-tt_h-4;
179 179 xy_pos = [cur_x,cur_y];
180 180 break;
181 181
182 182 }
183 183
184 184 this.cfg.setProperty("xy",xy_pos);
185 185
186 186 });
187 187
188 188 //Mouse out
189 189 myToolTips.contextMouseOutEvent.subscribe(
190 190 function(type, args) {
191 191 var context = args[0];
192 192
193 193 });
194 194 });
195 195 '''
196 196 return literal(js)
197 197
198 198 tooltip = _ToolTip()
199 199
200 200 class _FilesBreadCrumbs(object):
201 201
202 202 def __call__(self, repo_name, rev, paths):
203 203 if isinstance(paths, str):
204 204 paths = paths.decode('utf-8')
205 205 url_l = [link_to(repo_name, url('files_home',
206 206 repo_name=repo_name,
207 207 revision=rev, f_path=''))]
208 208 paths_l = paths.split('/')
209 209 for cnt, p in enumerate(paths_l):
210 210 if p != '':
211 211 url_l.append(link_to(p, url('files_home',
212 212 repo_name=repo_name,
213 213 revision=rev,
214 214 f_path='/'.join(paths_l[:cnt + 1]))))
215 215
216 216 return literal('/'.join(url_l))
217 217
218 218 files_breadcrumbs = _FilesBreadCrumbs()
219 219
220 220 class CodeHtmlFormatter(HtmlFormatter):
221 221 """My code Html Formatter for source codes
222 222 """
223 223
224 224 def wrap(self, source, outfile):
225 225 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
226 226
227 227 def _wrap_code(self, source):
228 228 for cnt, it in enumerate(source):
229 229 i, t = it
230 230 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
231 231 yield i, t
232 232
233 233 def _wrap_tablelinenos(self, inner):
234 234 dummyoutfile = StringIO.StringIO()
235 235 lncount = 0
236 236 for t, line in inner:
237 237 if t:
238 238 lncount += 1
239 239 dummyoutfile.write(line)
240 240
241 241 fl = self.linenostart
242 242 mw = len(str(lncount + fl - 1))
243 243 sp = self.linenospecial
244 244 st = self.linenostep
245 245 la = self.lineanchors
246 246 aln = self.anchorlinenos
247 247 nocls = self.noclasses
248 248 if sp:
249 249 lines = []
250 250
251 251 for i in range(fl, fl + lncount):
252 252 if i % st == 0:
253 253 if i % sp == 0:
254 254 if aln:
255 255 lines.append('<a href="#%s%d" class="special">%*d</a>' %
256 256 (la, i, mw, i))
257 257 else:
258 258 lines.append('<span class="special">%*d</span>' % (mw, i))
259 259 else:
260 260 if aln:
261 261 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
262 262 else:
263 263 lines.append('%*d' % (mw, i))
264 264 else:
265 265 lines.append('')
266 266 ls = '\n'.join(lines)
267 267 else:
268 268 lines = []
269 269 for i in range(fl, fl + lncount):
270 270 if i % st == 0:
271 271 if aln:
272 272 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
273 273 else:
274 274 lines.append('%*d' % (mw, i))
275 275 else:
276 276 lines.append('')
277 277 ls = '\n'.join(lines)
278 278
279 279 # in case you wonder about the seemingly redundant <div> here: since the
280 280 # content in the other cell also is wrapped in a div, some browsers in
281 281 # some configurations seem to mess up the formatting...
282 282 if nocls:
283 283 yield 0, ('<table class="%stable">' % self.cssclass +
284 284 '<tr><td><div class="linenodiv" '
285 285 'style="background-color: #f0f0f0; padding-right: 10px">'
286 286 '<pre style="line-height: 125%">' +
287 287 ls + '</pre></div></td><td class="code">')
288 288 else:
289 289 yield 0, ('<table class="%stable">' % self.cssclass +
290 290 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
291 291 ls + '</pre></div></td><td class="code">')
292 292 yield 0, dummyoutfile.getvalue()
293 293 yield 0, '</td></tr></table>'
294 294
295 295
296 296 def pygmentize(filenode, **kwargs):
297 297 """pygmentize function using pygments
298 298
299 299 :param filenode:
300 300 """
301 301
302 302 return literal(code_highlight(filenode.content,
303 303 filenode.lexer, CodeHtmlFormatter(**kwargs)))
304 304
305 305 def pygmentize_annotation(filenode, **kwargs):
306 306 """pygmentize function for annotation
307 307
308 308 :param filenode:
309 309 """
310 310
311 311 color_dict = {}
312 312 def gen_color(n=10000):
313 313 """generator for getting n of evenly distributed colors using
314 314 hsv color and golden ratio. It always return same order of colors
315 315
316 316 :returns: RGB tuple
317 317 """
318 318 import colorsys
319 319 golden_ratio = 0.618033988749895
320 320 h = 0.22717784590367374
321 321
322 322 for c in xrange(n):
323 323 h += golden_ratio
324 324 h %= 1
325 325 HSV_tuple = [h, 0.95, 0.95]
326 326 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
327 327 yield map(lambda x:str(int(x * 256)), RGB_tuple)
328 328
329 329 cgenerator = gen_color()
330 330
331 331 def get_color_string(cs):
332 332 if color_dict.has_key(cs):
333 333 col = color_dict[cs]
334 334 else:
335 335 col = color_dict[cs] = cgenerator.next()
336 336 return "color: rgb(%s)! important;" % (', '.join(col))
337 337
338 338 def url_func(changeset):
339 339 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
340 340 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
341 341
342 342 tooltip_html = tooltip_html % (changeset.author,
343 343 changeset.date,
344 344 tooltip(changeset.message))
345 345 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
346 346 short_id(changeset.raw_id))
347 347 uri = link_to(
348 348 lnk_format,
349 349 url('changeset_home', repo_name=changeset.repository.name,
350 350 revision=changeset.raw_id),
351 351 style=get_color_string(changeset.raw_id),
352 352 class_='tooltip',
353 353 title=tooltip_html
354 354 )
355 355
356 356 uri += '\n'
357 357 return uri
358 358 return literal(annotate_highlight(filenode, url_func, **kwargs))
359 359
360 360 def repo_name_slug(value):
361 361 """Return slug of name of repository
362 362 This function is called on each creation/modification
363 363 of repository to prevent bad names in repo
364 364 """
365 365
366 366 slug = remove_formatting(value)
367 367 slug = strip_tags(slug)
368 368
369 369 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
370 370 slug = slug.replace(c, '-')
371 371 slug = recursive_replace(slug, '-')
372 372 slug = collapse(slug, '-')
373 373 return slug
374 374
375 375 def get_changeset_safe(repo, rev):
376 376 from vcs.backends.base import BaseRepository
377 377 from vcs.exceptions import RepositoryError
378 378 if not isinstance(repo, BaseRepository):
379 379 raise Exception('You must pass an Repository '
380 380 'object as first argument got %s', type(repo))
381 381
382 382 try:
383 383 cs = repo.get_changeset(rev)
384 384 except RepositoryError:
385 385 from rhodecode.lib.utils import EmptyChangeset
386 386 cs = EmptyChangeset()
387 387 return cs
388 388
389 389
390 390 flash = _Flash()
391 391
392 392
393 393 #==============================================================================
394 394 # MERCURIAL FILTERS available via h.
395 395 #==============================================================================
396 396 from mercurial import util
397 397 from mercurial.templatefilters import person as _person
398 398
399 399 def _age(curdate):
400 400 """turns a datetime into an age string."""
401 401
402 402 if not curdate:
403 403 return ''
404 404
405 405 from datetime import timedelta, datetime
406 406
407 407 agescales = [("year", 3600 * 24 * 365),
408 408 ("month", 3600 * 24 * 30),
409 409 ("day", 3600 * 24),
410 410 ("hour", 3600),
411 411 ("minute", 60),
412 412 ("second", 1), ]
413 413
414 414 age = datetime.now() - curdate
415 415 age_seconds = (age.days * agescales[2][1]) + age.seconds
416 416 pos = 1
417 417 for scale in agescales:
418 418 if scale[1] <= age_seconds:
419 419 if pos == 6:pos = 5
420 420 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
421 421 pos += 1
422 422
423 423 return _('just now')
424 424
425 425 age = lambda x:_age(x)
426 426 capitalize = lambda x: x.capitalize()
427 427 email = util.email
428 428 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
429 429 person = lambda x: _person(x)
430 430 short_id = lambda x: x[:12]
431 431
432 432
433 433 def bool2icon(value):
434 434 """Returns True/False values represented as small html image of true/false
435 435 icons
436 436
437 437 :param value: bool value
438 438 """
439 439
440 440 if value is True:
441 441 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
442 442
443 443 if value is False:
444 444 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
445 445
446 446 return value
447 447
448 448
449 449 def action_parser(user_log):
450 450 """This helper will map the specified string action into translated
451 451 fancy names with icons and links
452 452
453 453 :param user_log: user log instance
454 454 """
455 455
456 456 action = user_log.action
457 457 action_params = ' '
458 458
459 459 x = action.split(':')
460 460
461 461 if len(x) > 1:
462 462 action, action_params = x
463 463
464 464 def get_cs_links():
465 465 revs_limit = 5 #display this amount always
466 466 revs_top_limit = 50 #show upto this amount of changesets hidden
467 467 revs = action_params.split(',')
468 468 repo_name = user_log.repository.repo_name
469 469 from rhodecode.model.scm import ScmModel
470 470
471 471 message = lambda rev: get_changeset_safe(ScmModel().get(repo_name),
472 472 rev).message
473 473
474 474 cs_links = " " + ', '.join ([link_to(rev,
475 475 url('changeset_home',
476 476 repo_name=repo_name,
477 477 revision=rev), title=tooltip(message(rev)),
478 478 class_='tooltip') for rev in revs[:revs_limit] ])
479 479 if len(revs) > revs_limit:
480 480 uniq_id = revs[0]
481 481 html_tmpl = ('<span> %s '
482 482 '<a class="show_more" id="_%s" href="#">%s</a> '
483 483 '%s</span>')
484 484 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
485 485 % (len(revs) - revs_limit),
486 486 _('revisions'))
487 487
488 488 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
489 489 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
490 490 url('changeset_home',
491 491 repo_name=repo_name, revision=rev),
492 492 title=message(rev), class_='tooltip')
493 493 for rev in revs[revs_limit:revs_top_limit]]))
494 494
495 495 return cs_links
496 496
497 497 def get_fork_name():
498 498 from rhodecode.model.scm import ScmModel
499 499 repo_name = action_params
500 500 repo = ScmModel().get(repo_name)
501 501 if repo is None:
502 502 return repo_name
503 503 return link_to(action_params, url('summary_home',
504 504 repo_name=repo.name,),
505 505 title=repo.dbrepo.description)
506 506
507 507 map = {'user_deleted_repo':(_('User [deleted] repository'), None),
508 508 'user_created_repo':(_('User [created] repository'), None),
509 509 'user_forked_repo':(_('User [forked] repository as:'), get_fork_name),
510 510 'user_updated_repo':(_('User [updated] repository'), None),
511 511 'admin_deleted_repo':(_('Admin [delete] repository'), None),
512 512 'admin_created_repo':(_('Admin [created] repository'), None),
513 513 'admin_forked_repo':(_('Admin [forked] repository'), None),
514 514 'admin_updated_repo':(_('Admin [updated] repository'), None),
515 515 'push':(_('[Pushed]'), get_cs_links),
516 516 'pull':(_('[Pulled]'), None),
517 517 'started_following_repo':(_('User [started following] repository'), None),
518 518 'stopped_following_repo':(_('User [stopped following] repository'), None),
519 519 }
520 520
521 521 action_str = map.get(action, action)
522 522 action = action_str[0].replace('[', '<span class="journal_highlight">')\
523 523 .replace(']', '</span>')
524 524 if action_str[1] is not None:
525 525 action = action + " " + action_str[1]()
526 526
527 527 return literal(action)
528 528
529 529 def action_parser_icon(user_log):
530 530 action = user_log.action
531 531 action_params = None
532 532 x = action.split(':')
533 533
534 534 if len(x) > 1:
535 535 action, action_params = x
536 536
537 537 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
538 538 map = {'user_deleted_repo':'database_delete.png',
539 539 'user_created_repo':'database_add.png',
540 540 'user_forked_repo':'arrow_divide.png',
541 541 'user_updated_repo':'database_edit.png',
542 542 'admin_deleted_repo':'database_delete.png',
543 543 'admin_created_repo':'database_add.png',
544 544 'admin_forked_repo':'arrow_divide.png',
545 545 'admin_updated_repo':'database_edit.png',
546 546 'push':'script_add.png',
547 547 'pull':'down_16.png',
548 548 'started_following_repo':'heart_add.png',
549 549 'stopped_following_repo':'heart_delete.png',
550 550 }
551 551 return literal(tmpl % (map.get(action, action), action))
552 552
553 553
554 554 #==============================================================================
555 555 # PERMS
556 556 #==============================================================================
557 557 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
558 558 HasRepoPermissionAny, HasRepoPermissionAll
559 559
560 560 #==============================================================================
561 561 # GRAVATAR URL
562 562 #==============================================================================
563 563 import hashlib
564 564 import urllib
565 565 from pylons import request
566 566
567 567 def gravatar_url(email_address, size=30):
568 568 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
569 569 default = 'identicon'
570 570 baseurl_nossl = "http://www.gravatar.com/avatar/"
571 571 baseurl_ssl = "https://secure.gravatar.com/avatar/"
572 572 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
573 573
574 574
575 575 # construct the url
576 576 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
577 577 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
578 578
579 579 return gravatar_url
580 580
581 581 def safe_unicode(str):
582 582 """safe unicode function. In case of UnicodeDecode error we try to return
583 583 unicode with errors replace, if this failes we return unicode with
584 584 string_escape decoding """
585 585
586 586 try:
587 587 u_str = unicode(str)
588 588 except UnicodeDecodeError:
589 589 try:
590 590 u_str = unicode(str, 'utf-8', 'replace')
591 591 except UnicodeDecodeError:
592 592 #incase we have a decode error just represent as byte string
593 593 u_str = unicode(str(str).encode('string_escape'))
594 594
595 595 return u_str
596
597 def changed_tooltip(nodes):
598 if nodes:
599 pref = ': <br/> '
600 suf = ''
601 if len(nodes) > 30:
602 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
603 return literal(pref + '<br/> '.join([x.path for x in nodes[:30]]) + suf)
604 else:
605 return ': ' + _('No Files')
@@ -1,174 +1,161
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%inherit file="/base/base.html"/>
4 4
5 5 <%def name="title()">
6 6 ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
7 7 </%def>
8 8
9 9 <%def name="breadcrumbs_links()">
10 10 ${h.link_to(u'Home',h.url('/'))}
11 11 &raquo;
12 12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 13 &raquo;
14 14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
15 15 </%def>
16 16
17 17 <%def name="page_nav()">
18 18 ${self.menu('changelog')}
19 19 </%def>
20 20
21 21 <%def name="main()">
22 22 <div class="box">
23 23 <!-- box / title -->
24 24 <div class="title">
25 25 ${self.breadcrumbs()}
26 26 </div>
27 27 <div class="table">
28 28 % if c.pagination:
29 29 <div id="graph">
30 30 <div id="graph_nodes">
31 31 <canvas id="graph_canvas"></canvas>
32 32 </div>
33 33 <div id="graph_content">
34 34 <div class="container_header">
35 35 ${h.form(h.url.current(),method='get')}
36 36 <div class="info_box">
37 37 <span>${_('Show')}:</span>
38 38 ${h.text('size',size=1,value=c.size)}
39 39 <span>${_('revisions')}</span>
40 40 ${h.submit('set',_('set'))}
41 41
42 42 </div>
43 43 ${h.end_form()}
44 44 <div id="rev_range_container" style="display:none"></div>
45 45 </div>
46 46
47 47 %for cnt,cs in enumerate(c.pagination):
48 48 <div id="chg_${cnt+1}" class="container">
49 49 <div class="left">
50 50 <div class="date">
51 51 ${h.checkbox(cs.short_id,class_="changeset_range")}
52 52 <span>${_('commit')} ${cs.revision}: ${h.short_id(cs.raw_id)}@${cs.date}</span>
53 53 </div>
54 54 <div class="author">
55 55 <div class="gravatar">
56 56 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
57 57 </div>
58 58 <span>${h.person(cs.author)}</span><br/>
59 59 <span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
60 60 </div>
61 61 <div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
62 62 </div>
63 63 <div class="right">
64 <div class="changes">
65
66 <%
67 def changed_tooltip(cs):
68 if cs:
69 pref = ': '
70 suf = ''
71 if len(cs) > 30:
72 suf='<br/>'+_(' and %s more') % (len(cs) - 30)
73 return pref+'<br/> '.join([x.path for x in cs[:30]]) + suf
74 else:
75 return ': '+_('No Files')
76 %>
77
78 <span class="removed tooltip" title="${_('removed')}${h.literal(changed_tooltip(cs.removed))}">${len(cs.removed)}</span>
79 <span class="changed tooltip" title="${_('changed')}${h.literal(changed_tooltip(cs.changed))}">${len(cs.changed)}</span>
80 <span class="added tooltip" title="${_('added')}${h.literal(changed_tooltip(cs.added))}">${len(cs.added)}</span>
64 <div class="changes">
65 <span class="removed tooltip" title="<b>${_('removed')}</b>${h.changed_tooltip(cs.removed)}">${len(cs.removed)}</span>
66 <span class="changed tooltip" title="<b>${_('changed')}</b>${h.changed_tooltip(cs.changed)}">${len(cs.changed)}</span>
67 <span class="added tooltip" title="<b>${_('added')}</b>${h.changed_tooltip(cs.added)}">${len(cs.added)}</span>
81 68 </div>
82 69 %if len(cs.parents)>1:
83 70 <div class="merge">
84 71 ${_('merge')}<img alt="merge" src="/images/icons/arrow_join.png"/>
85 72 </div>
86 73 %endif
87 74 %if cs.parents:
88 75 %for p_cs in reversed(cs.parents):
89 76 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
90 77 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
91 78 </div>
92 79 %endfor
93 80 %else:
94 81 <div class="parent">${_('No parents')}</div>
95 82 %endif
96 83
97 84 <span class="logtags">
98 85 %if cs.branch:
99 86 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
100 87 ${h.link_to(cs.branch,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
101 88 %endif
102 89 %for tag in cs.tags:
103 90 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
104 91 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
105 92 %endfor
106 93 </span>
107 94 </div>
108 95 </div>
109 96
110 97 %endfor
111 98 <div class="pagination-wh pagination-left">
112 99 ${c.pagination.pager('$link_previous ~2~ $link_next')}
113 100 </div>
114 101 </div>
115 102 </div>
116 103
117 104 <script type="text/javascript" src="/js/graph.js"></script>
118 105 <script type="text/javascript">
119 106 YAHOO.util.Event.onDOMReady(function(){
120 107
121 108 //Monitor range checkboxes and build a link to changesets
122 109 //ranges
123 110 var checkboxes = YUD.getElementsByClassName('changeset_range');
124 111 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
125 112 YUE.on(checkboxes,'click',function(e){
126 113 var checked_checkboxes = [];
127 114 for (pos in checkboxes){
128 115 if(checkboxes[pos].checked){
129 116 checked_checkboxes.push(checkboxes[pos]);
130 117 }
131 118 }
132 119 if(checked_checkboxes.length>1){
133 120 var rev_end = checked_checkboxes[0].name;
134 121 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
135 122
136 123 var url = url_tmpl.replace('__REVRANGE__',
137 124 rev_start+'...'+rev_end);
138 125
139 126 var link = "<a href="+url+">${_('Show selected changes __S -> __E')}</a>"
140 127 link = link.replace('__S',rev_start);
141 128 link = link.replace('__E',rev_end);
142 129 YUD.get('rev_range_container').innerHTML = link;
143 130 YUD.setStyle('rev_range_container','display','');
144 131 }
145 132 else{
146 133 YUD.setStyle('rev_range_container','display','none');
147 134
148 135 }
149 136 });
150 137
151 138 function set_canvas() {
152 139 var c = document.getElementById('graph_nodes');
153 140 var t = document.getElementById('graph_content');
154 141 canvas = document.getElementById('graph_canvas');
155 142 var div_h = t.clientHeight;
156 143 c.style.height=div_h+'px';
157 144 canvas.setAttribute('height',div_h);
158 145 canvas.setAttribute('width',160);
159 146 };
160 147 set_canvas();
161 148 var jsdata = ${c.jsdata|n};
162 149 var r = new BranchRenderer();
163 150 r.render(jsdata);
164 151
165 152
166 153
167 154 });
168 155 </script>
169 156 %else:
170 157 ${_('There are no changes yet')}
171 158 %endif
172 159 </div>
173 160 </div>
174 161 </%def> No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now