##// END OF EJS Templates
fixes issue #271...
marcink -
r1890:a3efaaa6 beta
parent child Browse files
Show More
@@ -1,235 +1,233 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.summary
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Summary controller for Rhodecode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import traceback
27 27 import calendar
28 28 import logging
29 29 from time import mktime
30 30 from datetime import timedelta, date
31 31 from itertools import product
32 32 from urlparse import urlparse
33 33
34 34 from vcs.exceptions import ChangesetError, EmptyRepositoryError, \
35 35 NodeDoesNotExistError
36 36
37 37 from pylons import tmpl_context as c, request, url, config
38 38 from pylons.i18n.translation import _
39 39
40 40 from beaker.cache import cache_region, region_invalidate
41 41
42 42 from rhodecode.model.db import Statistics, CacheInvalidation
43 43 from rhodecode.lib import ALL_READMES, ALL_EXTS
44 44 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
45 45 from rhodecode.lib.base import BaseRepoController, render
46 46 from rhodecode.lib.utils import EmptyChangeset
47 47 from rhodecode.lib.markup_renderer import MarkupRenderer
48 48 from rhodecode.lib.celerylib import run_task
49 49 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
50 50 LANGUAGES_EXTENSIONS_MAP
51 51 from rhodecode.lib.helpers import RepoPage
52 52 from rhodecode.lib.compat import json, OrderedDict
53 53
54 54 log = logging.getLogger(__name__)
55 55
56 56 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
57 57 sorted(list(product(ALL_READMES, ALL_EXTS)),
58 58 key=lambda y:y[0][1] + y[1][1])]
59 59
60 60
61 61 class SummaryController(BaseRepoController):
62 62
63 63 @LoginRequired()
64 64 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
65 65 'repository.admin')
66 66 def __before__(self):
67 67 super(SummaryController, self).__before__()
68 68
69 69 def index(self, repo_name):
70 70 c.dbrepo = dbrepo = c.rhodecode_db_repo
71 71 c.following = self.scm_model.is_following_repo(repo_name,
72 72 self.rhodecode_user.user_id)
73 73
74 74 def url_generator(**kw):
75 75 return url('shortlog_home', repo_name=repo_name, size=10, **kw)
76 76
77 77 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
78 78 items_per_page=10, url=url_generator)
79 79
80 80 if self.rhodecode_user.username == 'default':
81 81 # for default(anonymous) user we don't need to pass credentials
82 82 username = ''
83 83 password = ''
84 84 else:
85 85 username = str(self.rhodecode_user.username)
86 86 password = '@'
87 87
88 88 parsed_url = urlparse(url.current(qualified=True))
89 89
90 90 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
91 91
92 92 uri_tmpl = config.get('clone_uri', default_clone_uri)
93 93 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
94 94
95 95 uri_dict = {
96 96 'user': username,
97 97 'pass': password,
98 98 'scheme': parsed_url.scheme,
99 99 'netloc': parsed_url.netloc,
100 100 'path': parsed_url.path
101 101 }
102 102 uri = uri_tmpl % uri_dict
103 103 # generate another clone url by id
104 104 uri_dict.update({'path': '/_%s' % c.dbrepo.repo_id})
105 105 uri_id = uri_tmpl % uri_dict
106 106
107 107 c.clone_repo_url = uri
108 108 c.clone_repo_url_id = uri_id
109 109 c.repo_tags = OrderedDict()
110 110 for name, hash in c.rhodecode_repo.tags.items()[:10]:
111 111 try:
112 112 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
113 113 except ChangesetError:
114 114 c.repo_tags[name] = EmptyChangeset(hash)
115 115
116 116 c.repo_branches = OrderedDict()
117 117 for name, hash in c.rhodecode_repo.branches.items()[:10]:
118 118 try:
119 119 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
120 120 except ChangesetError:
121 121 c.repo_branches[name] = EmptyChangeset(hash)
122 122
123 123 td = date.today() + timedelta(days=1)
124 124 td_1m = td - timedelta(days=calendar.mdays[td.month])
125 125 td_1y = td - timedelta(days=365)
126 126
127 127 ts_min_m = mktime(td_1m.timetuple())
128 128 ts_min_y = mktime(td_1y.timetuple())
129 129 ts_max_y = mktime(td.timetuple())
130 130
131 131 if dbrepo.enable_statistics:
132 132 c.show_stats = True
133 133 c.no_data_msg = _('No data loaded yet')
134 134 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
135 135 else:
136 136 c.show_stats = False
137 137 c.no_data_msg = _('Statistics are disabled for this repository')
138 138 c.ts_min = ts_min_m
139 139 c.ts_max = ts_max_y
140 140
141 141 stats = self.sa.query(Statistics)\
142 142 .filter(Statistics.repository == dbrepo)\
143 143 .scalar()
144 144
145 145 c.stats_percentage = 0
146 146
147 147 if stats and stats.languages:
148 148 c.no_data = False is dbrepo.enable_statistics
149 149 lang_stats_d = json.loads(stats.languages)
150 150 c.commit_data = stats.commit_activity
151 151 c.overview_data = stats.commit_activity_combined
152 152
153 153 lang_stats = ((x, {"count": y,
154 154 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
155 155 for x, y in lang_stats_d.items())
156 156
157 c.trending_languages = json.dumps(OrderedDict(
158 sorted(lang_stats, reverse=True,
159 key=lambda k: k[1])[:10]
160 )
161 )
157 c.trending_languages = json.dumps(
158 sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
159 )
162 160 last_rev = stats.stat_on_revision + 1
163 161 c.repo_last_rev = c.rhodecode_repo.count()\
164 162 if c.rhodecode_repo.revisions else 0
165 163 if last_rev == 0 or c.repo_last_rev == 0:
166 164 pass
167 165 else:
168 166 c.stats_percentage = '%.2f' % ((float((last_rev)) /
169 167 c.repo_last_rev) * 100)
170 168 else:
171 169 c.commit_data = json.dumps({})
172 170 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
173 171 c.trending_languages = json.dumps({})
174 172 c.no_data = True
175 173
176 174 c.enable_downloads = dbrepo.enable_downloads
177 175 if c.enable_downloads:
178 176 c.download_options = self._get_download_links(c.rhodecode_repo)
179 177
180 178 c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_repo)
181 179 return render('summary/summary.html')
182 180
183 181 def __get_readme_data(self, repo):
184 182
185 183 @cache_region('long_term')
186 184 def _get_readme_from_cache(key):
187 185 readme_data = None
188 186 readme_file = None
189 187 log.debug('Fetching readme file')
190 188 try:
191 189 cs = repo.get_changeset('tip')
192 190 renderer = MarkupRenderer()
193 191 for f in README_FILES:
194 192 try:
195 193 readme = cs.get_node(f)
196 194 readme_file = f
197 195 readme_data = renderer.render(readme.content, f)
198 196 log.debug('Found readme %s' % readme_file)
199 197 break
200 198 except NodeDoesNotExistError:
201 199 continue
202 200 except ChangesetError:
203 201 pass
204 202 except EmptyRepositoryError:
205 203 pass
206 204 except Exception:
207 205 log.error(traceback.format_exc())
208 206
209 207 return readme_data, readme_file
210 208
211 209 key = repo.name + '_README'
212 210 inv = CacheInvalidation.invalidate(key)
213 211 if inv is not None:
214 212 region_invalidate(_get_readme_from_cache, None, key)
215 213 CacheInvalidation.set_valid(inv.cache_key)
216 214 return _get_readme_from_cache(key)
217 215
218 216 def _get_download_links(self, repo):
219 217
220 218 download_l = []
221 219
222 220 branches_group = ([], _("Branches"))
223 221 tags_group = ([], _("Tags"))
224 222
225 223 for name, chs in c.rhodecode_repo.branches.items():
226 224 #chs = chs.split(':')[-1]
227 225 branches_group[0].append((chs, name),)
228 226 download_l.append(branches_group)
229 227
230 228 for name, chs in c.rhodecode_repo.tags.items():
231 229 #chs = chs.split(':')[-1]
232 230 tags_group[0].append((chs, name),)
233 231 download_l.append(tags_group)
234 232
235 233 return download_l
@@ -1,379 +1,381 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.compat
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Python backward compatibility functions and common libs
7 7
8 8
9 9 :created_on: Oct 7, 2011
10 10 :author: marcink
11 11 :copyright: (C) 2010-2010 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 from rhodecode import __platform__, PLATFORM_WIN
29 29
30 30 #==============================================================================
31 31 # json
32 32 #==============================================================================
33 33 try:
34 34 import json
35 35 except ImportError:
36 36 import simplejson as json
37 37
38 38
39 39 #==============================================================================
40 40 # izip_longest
41 41 #==============================================================================
42 42 try:
43 43 from itertools import izip_longest
44 44 except ImportError:
45 45 import itertools
46 46
47 47 def izip_longest(*args, **kwds): # noqa
48 48 fillvalue = kwds.get("fillvalue")
49 49
50 50 def sentinel(counter=([fillvalue] * (len(args) - 1)).pop):
51 51 yield counter() # yields the fillvalue, or raises IndexError
52 52
53 53 fillers = itertools.repeat(fillvalue)
54 54 iters = [itertools.chain(it, sentinel(), fillers)
55 55 for it in args]
56 56 try:
57 57 for tup in itertools.izip(*iters):
58 58 yield tup
59 59 except IndexError:
60 60 pass
61 61
62 62
63 63 #==============================================================================
64 64 # OrderedDict
65 65 #==============================================================================
66 66
67 67 # Python Software Foundation License
68 68
69 69 # XXX: it feels like using the class with "is" and "is not" instead of "==" and
70 70 # "!=" should be faster.
71 71 class _Nil(object):
72 72
73 73 def __repr__(self):
74 74 return "nil"
75 75
76 76 def __eq__(self, other):
77 77 if (isinstance(other, _Nil)):
78 78 return True
79 79 else:
80 80 return NotImplemented
81 81
82 82 def __ne__(self, other):
83 83 if (isinstance(other, _Nil)):
84 84 return False
85 85 else:
86 86 return NotImplemented
87 87
88 88 _nil = _Nil()
89 89
90
90 91 class _odict(object):
91 92 """Ordered dict data structure, with O(1) complexity for dict operations
92 93 that modify one element.
93 94
94 95 Overwriting values doesn't change their original sequential order.
95 96 """
96 97
97 98 def _dict_impl(self):
98 99 return None
99 100
100 101 def __init__(self, data=(), **kwds):
101 102 """This doesn't accept keyword initialization as normal dicts to avoid
102 103 a trap - inside a function or method the keyword args are accessible
103 104 only as a dict, without a defined order, so their original order is
104 105 lost.
105 106 """
106 107 if kwds:
107 108 raise TypeError("__init__() of ordered dict takes no keyword "
108 109 "arguments to avoid an ordering trap.")
109 110 self._dict_impl().__init__(self)
110 111 # If you give a normal dict, then the order of elements is undefined
111 112 if hasattr(data, "iteritems"):
112 113 for key, val in data.iteritems():
113 114 self[key] = val
114 115 else:
115 116 for key, val in data:
116 117 self[key] = val
117 118
118 119 # Double-linked list header
119 120 def _get_lh(self):
120 121 dict_impl = self._dict_impl()
121 122 if not hasattr(self, '_lh'):
122 123 dict_impl.__setattr__(self, '_lh', _nil)
123 124 return dict_impl.__getattribute__(self, '_lh')
124 125
125 126 def _set_lh(self, val):
126 127 self._dict_impl().__setattr__(self, '_lh', val)
127 128
128 129 lh = property(_get_lh, _set_lh)
129 130
130 131 # Double-linked list tail
131 132 def _get_lt(self):
132 133 dict_impl = self._dict_impl()
133 134 if not hasattr(self, '_lt'):
134 135 dict_impl.__setattr__(self, '_lt', _nil)
135 136 return dict_impl.__getattribute__(self, '_lt')
136 137
137 138 def _set_lt(self, val):
138 139 self._dict_impl().__setattr__(self, '_lt', val)
139 140
140 141 lt = property(_get_lt, _set_lt)
141 142
142 143 def __getitem__(self, key):
143 144 return self._dict_impl().__getitem__(self, key)[1]
144 145
145 146 def __setitem__(self, key, val):
146 147 dict_impl = self._dict_impl()
147 148 try:
148 149 dict_impl.__getitem__(self, key)[1] = val
149 except KeyError, e:
150 except KeyError:
150 151 new = [dict_impl.__getattribute__(self, 'lt'), val, _nil]
151 152 dict_impl.__setitem__(self, key, new)
152 153 if dict_impl.__getattribute__(self, 'lt') == _nil:
153 154 dict_impl.__setattr__(self, 'lh', key)
154 155 else:
155 156 dict_impl.__getitem__(
156 157 self, dict_impl.__getattribute__(self, 'lt'))[2] = key
157 158 dict_impl.__setattr__(self, 'lt', key)
158 159
159 160 def __delitem__(self, key):
160 161 dict_impl = self._dict_impl()
161 pred, _ , succ = self._dict_impl().__getitem__(self, key)
162 pred, _, succ = self._dict_impl().__getitem__(self, key)
162 163 if pred == _nil:
163 164 dict_impl.__setattr__(self, 'lh', succ)
164 165 else:
165 166 dict_impl.__getitem__(self, pred)[2] = succ
166 167 if succ == _nil:
167 168 dict_impl.__setattr__(self, 'lt', pred)
168 169 else:
169 170 dict_impl.__getitem__(self, succ)[0] = pred
170 171 dict_impl.__delitem__(self, key)
171 172
172 173 def __contains__(self, key):
173 174 return key in self.keys()
174 175
175 176 def __len__(self):
176 177 return len(self.keys())
177 178
178 179 def __str__(self):
179 180 pairs = ("%r: %r" % (k, v) for k, v in self.iteritems())
180 181 return "{%s}" % ", ".join(pairs)
181 182
182 183 def __repr__(self):
183 184 if self:
184 185 pairs = ("(%r, %r)" % (k, v) for k, v in self.iteritems())
185 186 return "odict([%s])" % ", ".join(pairs)
186 187 else:
187 188 return "odict()"
188 189
189 190 def get(self, k, x=None):
190 191 if k in self:
191 192 return self._dict_impl().__getitem__(self, k)[1]
192 193 else:
193 194 return x
194 195
195 196 def __iter__(self):
196 197 dict_impl = self._dict_impl()
197 198 curr_key = dict_impl.__getattribute__(self, 'lh')
198 199 while curr_key != _nil:
199 200 yield curr_key
200 201 curr_key = dict_impl.__getitem__(self, curr_key)[2]
201 202
202 203 iterkeys = __iter__
203 204
204 205 def keys(self):
205 206 return list(self.iterkeys())
206 207
207 208 def itervalues(self):
208 209 dict_impl = self._dict_impl()
209 210 curr_key = dict_impl.__getattribute__(self, 'lh')
210 211 while curr_key != _nil:
211 212 _, val, curr_key = dict_impl.__getitem__(self, curr_key)
212 213 yield val
213 214
214 215 def values(self):
215 216 return list(self.itervalues())
216 217
217 218 def iteritems(self):
218 219 dict_impl = self._dict_impl()
219 220 curr_key = dict_impl.__getattribute__(self, 'lh')
220 221 while curr_key != _nil:
221 222 _, val, next_key = dict_impl.__getitem__(self, curr_key)
222 223 yield curr_key, val
223 224 curr_key = next_key
224 225
225 226 def items(self):
226 227 return list(self.iteritems())
227 228
228 229 def sort(self, cmp=None, key=None, reverse=False):
229 230 items = [(k, v) for k, v in self.items()]
230 231 if cmp is not None:
231 232 items = sorted(items, cmp=cmp)
232 233 elif key is not None:
233 234 items = sorted(items, key=key)
234 235 else:
235 236 items = sorted(items, key=lambda x: x[1])
236 237 if reverse:
237 238 items.reverse()
238 239 self.clear()
239 240 self.__init__(items)
240 241
241 242 def clear(self):
242 243 dict_impl = self._dict_impl()
243 244 dict_impl.clear(self)
244 245 dict_impl.__setattr__(self, 'lh', _nil)
245 246 dict_impl.__setattr__(self, 'lt', _nil)
246 247
247 248 def copy(self):
248 249 return self.__class__(self)
249 250
250 251 def update(self, data=(), **kwds):
251 252 if kwds:
252 253 raise TypeError("update() of ordered dict takes no keyword "
253 254 "arguments to avoid an ordering trap.")
254 255 if hasattr(data, "iteritems"):
255 256 data = data.iteritems()
256 257 for key, val in data:
257 258 self[key] = val
258 259
259 260 def setdefault(self, k, x=None):
260 261 try:
261 262 return self[k]
262 263 except KeyError:
263 264 self[k] = x
264 265 return x
265 266
266 267 def pop(self, k, x=_nil):
267 268 try:
268 269 val = self[k]
269 270 del self[k]
270 271 return val
271 272 except KeyError:
272 273 if x == _nil:
273 274 raise
274 275 return x
275 276
276 277 def popitem(self):
277 278 try:
278 279 dict_impl = self._dict_impl()
279 280 key = dict_impl.__getattribute__(self, 'lt')
280 281 return key, self.pop(key)
281 282 except KeyError:
282 283 raise KeyError("'popitem(): ordered dictionary is empty'")
283 284
284 285 def riterkeys(self):
285 286 """To iterate on keys in reversed order.
286 287 """
287 288 dict_impl = self._dict_impl()
288 289 curr_key = dict_impl.__getattribute__(self, 'lt')
289 290 while curr_key != _nil:
290 291 yield curr_key
291 292 curr_key = dict_impl.__getitem__(self, curr_key)[0]
292 293
293 294 __reversed__ = riterkeys
294 295
295 296 def rkeys(self):
296 297 """List of the keys in reversed order.
297 298 """
298 299 return list(self.riterkeys())
299 300
300 301 def ritervalues(self):
301 302 """To iterate on values in reversed order.
302 303 """
303 304 dict_impl = self._dict_impl()
304 305 curr_key = dict_impl.__getattribute__(self, 'lt')
305 306 while curr_key != _nil:
306 307 curr_key, val, _ = dict_impl.__getitem__(self, curr_key)
307 308 yield val
308 309
309 310 def rvalues(self):
310 311 """List of the values in reversed order.
311 312 """
312 313 return list(self.ritervalues())
313 314
314 315 def riteritems(self):
315 316 """To iterate on (key, value) in reversed order.
316 317 """
317 318 dict_impl = self._dict_impl()
318 319 curr_key = dict_impl.__getattribute__(self, 'lt')
319 320 while curr_key != _nil:
320 321 pred_key, val, _ = dict_impl.__getitem__(self, curr_key)
321 322 yield curr_key, val
322 323 curr_key = pred_key
323 324
324 325 def ritems(self):
325 326 """List of the (key, value) in reversed order.
326 327 """
327 328 return list(self.riteritems())
328 329
329 330 def firstkey(self):
330 331 if self:
331 332 return self._dict_impl().__getattribute__(self, 'lh')
332 333 else:
333 334 raise KeyError("'firstkey(): ordered dictionary is empty'")
334 335
335 336 def lastkey(self):
336 337 if self:
337 338 return self._dict_impl().__getattribute__(self, 'lt')
338 339 else:
339 340 raise KeyError("'lastkey(): ordered dictionary is empty'")
340 341
341 342 def as_dict(self):
342 343 return self._dict_impl()(self.items())
343 344
344 345 def _repr(self):
345 346 """_repr(): low level repr of the whole data contained in the odict.
346 347 Useful for debugging.
347 348 """
348 349 dict_impl = self._dict_impl()
349 350 form = "odict low level repr lh,lt,data: %r, %r, %s"
350 351 return form % (dict_impl.__getattribute__(self, 'lh'),
351 352 dict_impl.__getattribute__(self, 'lt'),
352 353 dict_impl.__repr__(self))
353 354
355
354 356 class OrderedDict(_odict, dict):
355 357
356 358 def _dict_impl(self):
357 359 return dict
358 360
359 361
360 362 #==============================================================================
361 363 # OrderedSet
362 364 #==============================================================================
363 365 from sqlalchemy.util import OrderedSet
364 366
365 367
366 368 #==============================================================================
367 369 # kill FUNCTIONS
368 370 #==============================================================================
369 371 if __platform__ in PLATFORM_WIN:
370 372 import ctypes
371 373
372 374 def kill(pid, sig):
373 375 """kill function for Win32"""
374 376 kernel32 = ctypes.windll.kernel32
375 377 handle = kernel32.OpenProcess(1, 0, pid)
376 378 return (0 != kernel32.TerminateProcess(handle, 0))
377 379
378 380 else:
379 381 kill = os.kill
@@ -1,695 +1,697 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(u'Home',h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('summary')}
13 13 </%def>
14 14
15 15 <%def name="page_nav()">
16 16 ${self.menu('summary')}
17 17 </%def>
18 18
19 19 <%def name="main()">
20 20 <%
21 21 summary = lambda n:{False:'summary-short'}.get(n)
22 22 %>
23 23 %if c.show_stats:
24 24 <div class="box box-left">
25 25 %else:
26 26 <div class="box">
27 27 %endif
28 28 <!-- box / title -->
29 29 <div class="title">
30 30 ${self.breadcrumbs()}
31 31 </div>
32 32 <!-- end box / title -->
33 33 <div class="form">
34 34 <div id="summary" class="fields">
35 35
36 36 <div class="field">
37 37 <div class="label-summary">
38 38 <label>${_('Name')}:</label>
39 39 </div>
40 40 <div class="input ${summary(c.show_stats)}">
41 41 <div style="float:right;padding:5px 0px 0px 5px">
42 42 %if c.rhodecode_user.username != 'default':
43 43 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
44 44 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
45 45 %else:
46 46 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
47 47 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
48 48 %endif
49 49 </div>
50 50 %if c.rhodecode_user.username != 'default':
51 51 %if c.following:
52 52 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
53 53 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
54 54 </span>
55 55 %else:
56 56 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
57 57 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
58 58 </span>
59 59 %endif
60 60 %endif:
61 61 ##REPO TYPE
62 62 %if c.dbrepo.repo_type =='hg':
63 63 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
64 64 %endif
65 65 %if c.dbrepo.repo_type =='git':
66 66 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
67 67 %endif
68 68
69 69 ##PUBLIC/PRIVATE
70 70 %if c.dbrepo.private:
71 71 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
72 72 %else:
73 73 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
74 74 %endif
75 75
76 76 ##REPO NAME
77 77 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
78 78
79 79 ##FORK
80 80 %if c.dbrepo.fork:
81 81 <div style="margin-top:5px;clear:both"">
82 82 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
83 83 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
84 84 </a>
85 85 </div>
86 86 %endif
87 87 ##REMOTE
88 88 %if c.dbrepo.clone_uri:
89 89 <div style="margin-top:5px;clear:both">
90 90 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
91 91 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
92 92 </a>
93 93 </div>
94 94 %endif
95 95 </div>
96 96 </div>
97 97
98 98 <div class="field">
99 99 <div class="label-summary">
100 100 <label>${_('Description')}:</label>
101 101 </div>
102 102 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
103 103 </div>
104 104
105 105 <div class="field">
106 106 <div class="label-summary">
107 107 <label>${_('Contact')}:</label>
108 108 </div>
109 109 <div class="input ${summary(c.show_stats)}">
110 110 <div class="gravatar">
111 111 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
112 112 </div>
113 113 ${_('Username')}: ${c.dbrepo.user.username}<br/>
114 114 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
115 115 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
116 116 </div>
117 117 </div>
118 118
119 119 <div class="field">
120 120 <div class="label-summary">
121 121 <label>${_('Clone url')}:</label>
122 122 </div>
123 123 <div class="input ${summary(c.show_stats)}">
124 124 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
125 125 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
126 126 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
127 127 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
128 128 </div>
129 129 </div>
130 130
131 131 <div class="field">
132 132 <div class="label-summary">
133 133 <label>${_('Trending files')}:</label>
134 134 </div>
135 135 <div class="input ${summary(c.show_stats)}">
136 136 %if c.show_stats:
137 137 <div id="lang_stats"></div>
138 138 %else:
139 139 ${_('Statistics are disabled for this repository')}
140 140 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
141 141 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
142 142 %endif
143 143 %endif
144 144 </div>
145 145 </div>
146 146
147 147 <div class="field">
148 148 <div class="label-summary">
149 149 <label>${_('Download')}:</label>
150 150 </div>
151 151 <div class="input ${summary(c.show_stats)}">
152 152 %if len(c.rhodecode_repo.revisions) == 0:
153 153 ${_('There are no downloads yet')}
154 154 %elif c.enable_downloads is False:
155 155 ${_('Downloads are disabled for this repository')}
156 156 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
157 157 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
158 158 %endif
159 159 %else:
160 160 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
161 161 <span id="${'zip_link'}">${h.link_to('Download as zip',h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
162 162 <span style="vertical-align: bottom">
163 163 <input id="archive_subrepos" type="checkbox" name="subrepos"/> <span class="tooltip" title="${_('Check this to download archive with subrepos')}" >${_('with subrepos')}</span>
164 164 </span>
165 165 %endif
166 166 </div>
167 167 </div>
168 168 </div>
169 169 </div>
170 170 </div>
171 171
172 172 %if c.show_stats:
173 173 <div class="box box-right" style="min-height:455px">
174 174 <!-- box / title -->
175 175 <div class="title">
176 176 <h5>${_('Commit activity by day / author')}</h5>
177 177 </div>
178 178
179 179 <div class="graph">
180 180 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">
181 181 %if c.no_data:
182 182 ${c.no_data_msg}
183 183 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
184 184 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
185 185 %endif
186 186 %else:
187 187 ${_('Loaded in')} ${c.stats_percentage} %
188 188 %endif
189 189 </div>
190 190 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
191 191 <div style="clear: both;height: 10px"></div>
192 192 <div id="overview" style="width:450px;height:100px;float:left"></div>
193 193
194 194 <div id="legend_data" style="clear:both;margin-top:10px;">
195 195 <div id="legend_container"></div>
196 196 <div id="legend_choices">
197 197 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
198 198 </div>
199 199 </div>
200 200 </div>
201 201 </div>
202 202 %endif
203 203
204 204 <div class="box">
205 205 <div class="title">
206 206 <div class="breadcrumbs">
207 207 %if c.repo_changesets:
208 208 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
209 209 %else:
210 210 ${_('Quick start')}
211 211 %endif
212 212 </div>
213 213 </div>
214 214 <div class="table">
215 215 <div id="shortlog_data">
216 216 <%include file='../shortlog/shortlog_data.html'/>
217 217 </div>
218 218 </div>
219 219 </div>
220 220
221 221 %if c.readme_data:
222 222 <div class="box" style="background-color: #FAFAFA">
223 223 <div class="title">
224 224 <div class="breadcrumbs"><a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a></div>
225 225 </div>
226 226 <div class="readme">
227 227 <div class="readme_box">
228 228 ${c.readme_data|n}
229 229 </div>
230 230 </div>
231 231 </div>
232 232 %endif
233 233
234 234 <script type="text/javascript">
235 235 var clone_url = 'clone_url';
236 236 YUE.on(clone_url,'click',function(e){
237 237 if(YUD.hasClass(clone_url,'selected')){
238 238 return
239 239 }
240 240 else{
241 241 YUD.addClass(clone_url,'selected');
242 242 YUD.get(clone_url).select();
243 243 }
244 244 })
245 245
246 246 YUE.on('clone_by_name','click',function(e){
247 247 // show url by name and hide name button
248 248 YUD.setStyle('clone_url','display','');
249 249 YUD.setStyle('clone_by_name','display','none');
250 250
251 251 // hide url by id and show name button
252 252 YUD.setStyle('clone_by_id','display','');
253 253 YUD.setStyle('clone_url_id','display','none');
254 254
255 255 })
256 256 YUE.on('clone_by_id','click',function(e){
257 257
258 258 // show url by id and hide id button
259 259 YUD.setStyle('clone_by_id','display','none');
260 260 YUD.setStyle('clone_url_id','display','');
261 261
262 262 // hide url by name and show id button
263 263 YUD.setStyle('clone_by_name','display','');
264 264 YUD.setStyle('clone_url','display','none');
265 265 })
266 266
267 267
268 268 var tmpl_links = {};
269 269 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
270 270 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
271 271 %endfor
272 272
273 273 YUE.on(['download_options','archive_subrepos'],'change',function(e){
274 274 var sm = YUD.get('download_options');
275 275 var new_cs = sm.options[sm.selectedIndex];
276 276
277 277 for(k in tmpl_links){
278 278 var s = YUD.get(k+'_link');
279 279 if(s){
280 280 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
281 281 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
282 282 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
283 283
284 284 var url = tmpl_links[k].replace('__CS__',new_cs.value);
285 285 var subrepos = YUD.get('archive_subrepos').checked;
286 286 url = url.replace('__SUB__',subrepos);
287 287 url = url.replace('__NAME__',title_tmpl);
288 288 s.innerHTML = url
289 289 }
290 290 }
291 291 });
292 292 </script>
293 293 %if c.show_stats:
294 294 <script type="text/javascript">
295 295 var data = ${c.trending_languages|n};
296 296 var total = 0;
297 297 var no_data = true;
298 for (k in data){
299 total += data[k].count;
300 no_data = false;
301 }
302 298 var tbl = document.createElement('table');
303 299 tbl.setAttribute('class','trending_language_tbl');
304 300 var cnt = 0;
305 for (k in data){
301
302 for (var i=0;i<data.length;i++){
303 total += data[i][1].count;
306 304 cnt += 1;
305 no_data = false;
306
307 307 var hide = cnt>2;
308 308 var tr = document.createElement('tr');
309 309 if (hide){
310 310 tr.setAttribute('style','display:none');
311 311 tr.setAttribute('class','stats_hidden');
312 312 }
313 var percentage = Math.round((data[k].count/total*100),2);
314 var value = data[k].count;
313 var k = data[i][0];
314 var obj = data[i][1];
315 var percentage = Math.round((obj.count/total*100),2);
316
315 317 var td1 = document.createElement('td');
316 318 td1.width = 150;
317 319 var trending_language_label = document.createElement('div');
318 trending_language_label.innerHTML = data[k].desc+" ("+k+")";
320 trending_language_label.innerHTML = obj.desc+" ("+k+")";
319 321 td1.appendChild(trending_language_label);
320 322
321 323 var td2 = document.createElement('td');
322 324 td2.setAttribute('style','padding-right:14px !important');
323 325 var trending_language = document.createElement('div');
324 var nr_files = value+" ${_('files')}";
326 var nr_files = obj.count+" ${_('files')}";
325 327
326 328 trending_language.title = k+" "+nr_files;
327 329
328 330 if (percentage>22){
329 331 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
330 332 }
331 333 else{
332 334 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
333 335 }
334 336
335 337 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
336 338 trending_language.style.width=percentage+"%";
337 339 td2.appendChild(trending_language);
338 340
339 341 tr.appendChild(td1);
340 342 tr.appendChild(td2);
341 343 tbl.appendChild(tr);
342 344 if(cnt == 3){
343 345 var show_more = document.createElement('tr');
344 346 var td = document.createElement('td');
345 347 lnk = document.createElement('a');
346 348
347 349 lnk.href='#';
348 350 lnk.innerHTML = "${_('show more')}";
349 351 lnk.id='code_stats_show_more';
350 352 td.appendChild(lnk);
351 353
352 354 show_more.appendChild(td);
353 355 show_more.appendChild(document.createElement('td'));
354 356 tbl.appendChild(show_more);
355 357 }
356 358
357 359 }
358 360
359 361 YUD.get('lang_stats').appendChild(tbl);
360 362 YUE.on('code_stats_show_more','click',function(){
361 363 l = YUD.getElementsByClassName('stats_hidden')
362 364 for (e in l){
363 365 YUD.setStyle(l[e],'display','');
364 366 };
365 367 YUD.setStyle(YUD.get('code_stats_show_more'),
366 368 'display','none');
367 369 });
368 370 </script>
369 371 <script type="text/javascript">
370 372 /**
371 373 * Plots summary graph
372 374 *
373 375 * @class SummaryPlot
374 376 * @param {from} initial from for detailed graph
375 377 * @param {to} initial to for detailed graph
376 378 * @param {dataset}
377 379 * @param {overview_dataset}
378 380 */
379 381 function SummaryPlot(from,to,dataset,overview_dataset) {
380 382 var initial_ranges = {
381 383 "xaxis":{
382 384 "from":from,
383 385 "to":to,
384 386 },
385 387 };
386 388 var dataset = dataset;
387 389 var overview_dataset = [overview_dataset];
388 390 var choiceContainer = YUD.get("legend_choices");
389 391 var choiceContainerTable = YUD.get("legend_choices_tables");
390 392 var plotContainer = YUD.get('commit_history');
391 393 var overviewContainer = YUD.get('overview');
392 394
393 395 var plot_options = {
394 396 bars: {show:true,align:'center',lineWidth:4},
395 397 legend: {show:true, container:"legend_container"},
396 398 points: {show:true,radius:0,fill:false},
397 399 yaxis: {tickDecimals:0,},
398 400 xaxis: {
399 401 mode: "time",
400 402 timeformat: "%d/%m",
401 403 min:from,
402 404 max:to,
403 405 },
404 406 grid: {
405 407 hoverable: true,
406 408 clickable: true,
407 409 autoHighlight:true,
408 410 color: "#999"
409 411 },
410 412 //selection: {mode: "x"}
411 413 };
412 414 var overview_options = {
413 415 legend:{show:false},
414 416 bars: {show:true,barWidth: 2,},
415 417 shadowSize: 0,
416 418 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
417 419 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
418 420 grid: {color: "#999",},
419 421 selection: {mode: "x"}
420 422 };
421 423
422 424 /**
423 425 *get dummy data needed in few places
424 426 */
425 427 function getDummyData(label){
426 428 return {"label":label,
427 429 "data":[{"time":0,
428 430 "commits":0,
429 431 "added":0,
430 432 "changed":0,
431 433 "removed":0,
432 434 }],
433 435 "schema":["commits"],
434 436 "color":'#ffffff',
435 437 }
436 438 }
437 439
438 440 /**
439 441 * generate checkboxes accordindly to data
440 442 * @param keys
441 443 * @returns
442 444 */
443 445 function generateCheckboxes(data) {
444 446 //append checkboxes
445 447 var i = 0;
446 448 choiceContainerTable.innerHTML = '';
447 449 for(var pos in data) {
448 450
449 451 data[pos].color = i;
450 452 i++;
451 453 if(data[pos].label != ''){
452 454 choiceContainerTable.innerHTML += '<tr><td>'+
453 455 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
454 456 +data[pos].label+
455 457 '</td></tr>';
456 458 }
457 459 }
458 460 }
459 461
460 462 /**
461 463 * ToolTip show
462 464 */
463 465 function showTooltip(x, y, contents) {
464 466 var div=document.getElementById('tooltip');
465 467 if(!div) {
466 468 div = document.createElement('div');
467 469 div.id="tooltip";
468 470 div.style.position="absolute";
469 471 div.style.border='1px solid #fdd';
470 472 div.style.padding='2px';
471 473 div.style.backgroundColor='#fee';
472 474 document.body.appendChild(div);
473 475 }
474 476 YUD.setStyle(div, 'opacity', 0);
475 477 div.innerHTML = contents;
476 478 div.style.top=(y + 5) + "px";
477 479 div.style.left=(x + 5) + "px";
478 480
479 481 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
480 482 anim.animate();
481 483 }
482 484
483 485 /**
484 486 * This function will detect if selected period has some changesets
485 487 for this user if it does this data is then pushed for displaying
486 488 Additionally it will only display users that are selected by the checkbox
487 489 */
488 490 function getDataAccordingToRanges(ranges) {
489 491
490 492 var data = [];
491 493 var new_dataset = {};
492 494 var keys = [];
493 495 var max_commits = 0;
494 496 for(var key in dataset){
495 497
496 498 for(var ds in dataset[key].data){
497 499 commit_data = dataset[key].data[ds];
498 500 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
499 501
500 502 if(new_dataset[key] === undefined){
501 503 new_dataset[key] = {data:[],schema:["commits"],label:key};
502 504 }
503 505 new_dataset[key].data.push(commit_data);
504 506 }
505 507 }
506 508 if (new_dataset[key] !== undefined){
507 509 data.push(new_dataset[key]);
508 510 }
509 511 }
510 512
511 513 if (data.length > 0){
512 514 return data;
513 515 }
514 516 else{
515 517 //just return dummy data for graph to plot itself
516 518 return [getDummyData('')];
517 519 }
518 520 }
519 521
520 522 /**
521 523 * redraw using new checkbox data
522 524 */
523 525 function plotchoiced(e,args){
524 526 var cur_data = args[0];
525 527 var cur_ranges = args[1];
526 528
527 529 var new_data = [];
528 530 var inputs = choiceContainer.getElementsByTagName("input");
529 531
530 532 //show only checked labels
531 533 for(var i=0; i<inputs.length; i++) {
532 534 var checkbox_key = inputs[i].name;
533 535
534 536 if(inputs[i].checked){
535 537 for(var d in cur_data){
536 538 if(cur_data[d].label == checkbox_key){
537 539 new_data.push(cur_data[d]);
538 540 }
539 541 }
540 542 }
541 543 else{
542 544 //push dummy data to not hide the label
543 545 new_data.push(getDummyData(checkbox_key));
544 546 }
545 547 }
546 548
547 549 var new_options = YAHOO.lang.merge(plot_options, {
548 550 xaxis: {
549 551 min: cur_ranges.xaxis.from,
550 552 max: cur_ranges.xaxis.to,
551 553 mode:"time",
552 554 timeformat: "%d/%m",
553 555 },
554 556 });
555 557 if (!new_data){
556 558 new_data = [[0,1]];
557 559 }
558 560 // do the zooming
559 561 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
560 562
561 563 plot.subscribe("plotselected", plotselected);
562 564
563 565 //resubscribe plothover
564 566 plot.subscribe("plothover", plothover);
565 567
566 568 // don't fire event on the overview to prevent eternal loop
567 569 overview.setSelection(cur_ranges, true);
568 570
569 571 }
570 572
571 573 /**
572 574 * plot only selected items from overview
573 575 * @param ranges
574 576 * @returns
575 577 */
576 578 function plotselected(ranges,cur_data) {
577 579 //updates the data for new plot
578 580 var data = getDataAccordingToRanges(ranges);
579 581 generateCheckboxes(data);
580 582
581 583 var new_options = YAHOO.lang.merge(plot_options, {
582 584 xaxis: {
583 585 min: ranges.xaxis.from,
584 586 max: ranges.xaxis.to,
585 587 mode:"time",
586 588 timeformat: "%d/%m",
587 589 },
588 590 });
589 591 // do the zooming
590 592 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
591 593
592 594 plot.subscribe("plotselected", plotselected);
593 595
594 596 //resubscribe plothover
595 597 plot.subscribe("plothover", plothover);
596 598
597 599 // don't fire event on the overview to prevent eternal loop
598 600 overview.setSelection(ranges, true);
599 601
600 602 //resubscribe choiced
601 603 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
602 604 }
603 605
604 606 var previousPoint = null;
605 607
606 608 function plothover(o) {
607 609 var pos = o.pos;
608 610 var item = o.item;
609 611
610 612 //YUD.get("x").innerHTML = pos.x.toFixed(2);
611 613 //YUD.get("y").innerHTML = pos.y.toFixed(2);
612 614 if (item) {
613 615 if (previousPoint != item.datapoint) {
614 616 previousPoint = item.datapoint;
615 617
616 618 var tooltip = YUD.get("tooltip");
617 619 if(tooltip) {
618 620 tooltip.parentNode.removeChild(tooltip);
619 621 }
620 622 var x = item.datapoint.x.toFixed(2);
621 623 var y = item.datapoint.y.toFixed(2);
622 624
623 625 if (!item.series.label){
624 626 item.series.label = 'commits';
625 627 }
626 628 var d = new Date(x*1000);
627 629 var fd = d.toDateString()
628 630 var nr_commits = parseInt(y);
629 631
630 632 var cur_data = dataset[item.series.label].data[item.dataIndex];
631 633 var added = cur_data.added;
632 634 var changed = cur_data.changed;
633 635 var removed = cur_data.removed;
634 636
635 637 var nr_commits_suffix = " ${_('commits')} ";
636 638 var added_suffix = " ${_('files added')} ";
637 639 var changed_suffix = " ${_('files changed')} ";
638 640 var removed_suffix = " ${_('files removed')} ";
639 641
640 642
641 643 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
642 644 if(added==1){added_suffix=" ${_('file added')} ";}
643 645 if(changed==1){changed_suffix=" ${_('file changed')} ";}
644 646 if(removed==1){removed_suffix=" ${_('file removed')} ";}
645 647
646 648 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
647 649 +'<br/>'+
648 650 nr_commits + nr_commits_suffix+'<br/>'+
649 651 added + added_suffix +'<br/>'+
650 652 changed + changed_suffix + '<br/>'+
651 653 removed + removed_suffix + '<br/>');
652 654 }
653 655 }
654 656 else {
655 657 var tooltip = YUD.get("tooltip");
656 658
657 659 if(tooltip) {
658 660 tooltip.parentNode.removeChild(tooltip);
659 661 }
660 662 previousPoint = null;
661 663 }
662 664 }
663 665
664 666 /**
665 667 * MAIN EXECUTION
666 668 */
667 669
668 670 var data = getDataAccordingToRanges(initial_ranges);
669 671 generateCheckboxes(data);
670 672
671 673 //main plot
672 674 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
673 675
674 676 //overview
675 677 var overview = YAHOO.widget.Flot(overviewContainer,
676 678 overview_dataset, overview_options);
677 679
678 680 //show initial selection on overview
679 681 overview.setSelection(initial_ranges);
680 682
681 683 plot.subscribe("plotselected", plotselected);
682 684 plot.subscribe("plothover", plothover)
683 685
684 686 overview.subscribe("plotselected", function (ranges) {
685 687 plot.setSelection(ranges);
686 688 });
687 689
688 690 // user choices on overview
689 691 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
690 692 }
691 693 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
692 694 </script>
693 695 %endif
694 696
695 697 </%def>
General Comments 0
You need to be logged in to leave comments. Login now