##// END OF EJS Templates
diff parser: redefined operations stats for changes...
marcink -
r3821:ce4b7023 beta
parent child Browse files
Show More
@@ -0,0 +1,18 b''
1 diff --git a/gravatar.png b/gravatar.png
2 old mode 100644
3 new mode 100755
4 index 54d2d129e3372c45a4c68a742e269864222e99ae..0c1d65410bd51fdff935443e01931c9fc3ad7def
5 GIT binary patch
6 literal 740
7 zc%17D@N?(olHy`uVBq!ia0vp^dx3Z#2NRHduQ>B0kYX$ja(7}_cTVOdki%Kv5n0T@
8 zz%2~Ij105pNB{-dOFVsD*>AH+2@7*F&3eGWz`&H|>EaktaqI0JMZd!V5(hqpU(%ki
9 zz<{N_Dc~^g1GeA{8S#b#jBU<`Ij0FINT{SO2`=WI*Kku%Kte?~QvUtk`~FuatbIH`
10 zU*0#<bY2fr$(Coz4(2b|?k<qNz`M&4#A?WcvPzmkEJg29#pjiUe<s|iw%c!Ml>foD
11 zHLr#RtfPb(q61ZL^D3dLy27sS9LG1ks-ApS8e$A1NIi%J(b=2_we?fTsn6Zh<#UVP
12 z|9tiF%$|eaMRt7@#I6)<9go)1-`(@)=Q(p!@3vNKDKmRt(o@-7dHLx1lNY~Ad}g~_
13 za{b)1``zCC$!DbBm~WE-irmOC$k!;|zwzb_^B2daEtWo!yMN=;86Y!nbQtDu5Wn34
14 zbmZh?b(0@$%sb%=43(hD^~>9*yZ+6yNtdtp|J$sq{^jrc-CO$Jn%~HOWByJ0bK9TY
15 zg@4_kydS*R^JDe{4Z08FKRNbQ`g%Ud?r+k+d3UblwmiT6sL83<_jKRAJ-Pr-V6<tp
16 z?epIfY2R5iFZ#Ccl<T?EX78;p`&F5H??E-!-5(q%k>Y5MY!uto=R2=&d?)y?yZLYL
17 zJ^SCEe$Bh{bKS3dDW9<fAX0p_*<5X%lMalj*{B8|p0JPMtzFP9%Uvy?WXj;_>gTe~
18 RDWNH`I3+QqIFSoR0|1y<SJMCh No newline at end of file
@@ -0,0 +1,10 b''
1 diff --git a/README b/README.rst
2 rename from README
3 rename to README.rst
4 --- a/README
5 +++ b/README.rst
6 @@ -1,1 +1,4 @@
7 readme2
8 +line 1
9 + line2
10 + No newline at end of file
@@ -0,0 +1,15 b''
1 diff --git a/README.rst b/README
2 old mode 100755
3 new mode 100644
4 rename from README.rst
5 rename to README
6 --- a/README.rst
7 +++ b/README
8 @@ -1,4 +1,7 @@
9 readme2
10 line 1
11 line2
12
13 +line 1
14 + line2
15 + No newline at end of file
@@ -0,0 +1,5 b''
1 diff --git a/README.rst b/README
2 old mode 100644
3 new mode 100755
4 rename from README.rst
5 rename to README No newline at end of file
@@ -1,440 +1,439 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.changeset
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 changeset controller for pylons showoing changes beetween
7 7 revisions
8 8
9 9 :created_on: Apr 25, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 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 import logging
27 27 import traceback
28 28 from collections import defaultdict
29 29 from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPNotFound
30 30
31 31 from pylons import tmpl_context as c, url, request, response
32 32 from pylons.i18n.translation import _
33 33 from pylons.controllers.util import redirect
34 34 from rhodecode.lib.utils import jsonify
35 35
36 36 from rhodecode.lib.vcs.exceptions import RepositoryError, \
37 37 ChangesetDoesNotExistError
38 38
39 39 import rhodecode.lib.helpers as h
40 40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
41 41 NotAnonymous
42 42 from rhodecode.lib.base import BaseRepoController, render
43 43 from rhodecode.lib.utils import action_logger
44 44 from rhodecode.lib.compat import OrderedDict
45 45 from rhodecode.lib import diffs
46 46 from rhodecode.model.db import ChangesetComment, ChangesetStatus
47 47 from rhodecode.model.comment import ChangesetCommentsModel
48 48 from rhodecode.model.changeset_status import ChangesetStatusModel
49 49 from rhodecode.model.meta import Session
50 50 from rhodecode.model.repo import RepoModel
51 51 from rhodecode.lib.diffs import LimitedDiffContainer
52 52 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
53 53 from rhodecode.lib.vcs.backends.base import EmptyChangeset
54 54 from rhodecode.lib.utils2 import safe_unicode
55 55
56 56 log = logging.getLogger(__name__)
57 57
58 58
59 59 def _update_with_GET(params, GET):
60 60 for k in ['diff1', 'diff2', 'diff']:
61 61 params[k] += GET.getall(k)
62 62
63 63
64 64 def anchor_url(revision, path, GET):
65 65 fid = h.FID(revision, path)
66 66 return h.url.current(anchor=fid, **dict(GET))
67 67
68 68
69 69 def get_ignore_ws(fid, GET):
70 70 ig_ws_global = GET.get('ignorews')
71 71 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
72 72 if ig_ws:
73 73 try:
74 74 return int(ig_ws[0].split(':')[-1])
75 75 except Exception:
76 76 pass
77 77 return ig_ws_global
78 78
79 79
80 80 def _ignorews_url(GET, fileid=None):
81 81 fileid = str(fileid) if fileid else None
82 82 params = defaultdict(list)
83 83 _update_with_GET(params, GET)
84 84 lbl = _('Show white space')
85 85 ig_ws = get_ignore_ws(fileid, GET)
86 86 ln_ctx = get_line_ctx(fileid, GET)
87 87 # global option
88 88 if fileid is None:
89 89 if ig_ws is None:
90 90 params['ignorews'] += [1]
91 91 lbl = _('Ignore white space')
92 92 ctx_key = 'context'
93 93 ctx_val = ln_ctx
94 94 # per file options
95 95 else:
96 96 if ig_ws is None:
97 97 params[fileid] += ['WS:1']
98 98 lbl = _('Ignore white space')
99 99
100 100 ctx_key = fileid
101 101 ctx_val = 'C:%s' % ln_ctx
102 102 # if we have passed in ln_ctx pass it along to our params
103 103 if ln_ctx:
104 104 params[ctx_key] += [ctx_val]
105 105
106 106 params['anchor'] = fileid
107 107 img = h.image(h.url('/images/icons/text_strikethrough.png'), lbl, class_='icon')
108 108 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
109 109
110 110
111 111 def get_line_ctx(fid, GET):
112 112 ln_ctx_global = GET.get('context')
113 113 if fid:
114 114 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
115 115 else:
116 116 _ln_ctx = filter(lambda k: k.startswith('C'), GET)
117 117 ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
118 118 if ln_ctx:
119 119 ln_ctx = [ln_ctx]
120 120
121 121 if ln_ctx:
122 122 retval = ln_ctx[0].split(':')[-1]
123 123 else:
124 124 retval = ln_ctx_global
125 125
126 126 try:
127 127 return int(retval)
128 128 except Exception:
129 129 return 3
130 130
131 131
132 132 def _context_url(GET, fileid=None):
133 133 """
134 134 Generates url for context lines
135 135
136 136 :param fileid:
137 137 """
138 138
139 139 fileid = str(fileid) if fileid else None
140 140 ig_ws = get_ignore_ws(fileid, GET)
141 141 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
142 142
143 143 params = defaultdict(list)
144 144 _update_with_GET(params, GET)
145 145
146 146 # global option
147 147 if fileid is None:
148 148 if ln_ctx > 0:
149 149 params['context'] += [ln_ctx]
150 150
151 151 if ig_ws:
152 152 ig_ws_key = 'ignorews'
153 153 ig_ws_val = 1
154 154
155 155 # per file option
156 156 else:
157 157 params[fileid] += ['C:%s' % ln_ctx]
158 158 ig_ws_key = fileid
159 159 ig_ws_val = 'WS:%s' % 1
160 160
161 161 if ig_ws:
162 162 params[ig_ws_key] += [ig_ws_val]
163 163
164 164 lbl = _('%s line context') % ln_ctx
165 165
166 166 params['anchor'] = fileid
167 167 img = h.image(h.url('/images/icons/table_add.png'), lbl, class_='icon')
168 168 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
169 169
170 170
171 171 class ChangesetController(BaseRepoController):
172 172
173 173 def __before__(self):
174 174 super(ChangesetController, self).__before__()
175 175 c.affected_files_cut_off = 60
176 176 repo_model = RepoModel()
177 177 c.users_array = repo_model.get_users_js()
178 178 c.users_groups_array = repo_model.get_users_groups_js()
179 179
180 180 def _index(self, revision, method):
181 181 c.anchor_url = anchor_url
182 182 c.ignorews_url = _ignorews_url
183 183 c.context_url = _context_url
184 184 c.fulldiff = fulldiff = request.GET.get('fulldiff')
185 185 #get ranges of revisions if preset
186 186 rev_range = revision.split('...')[:2]
187 187 enable_comments = True
188 188 try:
189 189 if len(rev_range) == 2:
190 190 enable_comments = False
191 191 rev_start = rev_range[0]
192 192 rev_end = rev_range[1]
193 193 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
194 194 end=rev_end)
195 195 else:
196 196 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
197 197
198 198 c.cs_ranges = list(rev_ranges)
199 199 if not c.cs_ranges:
200 200 raise RepositoryError('Changeset range returned empty result')
201 201
202 202 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
203 203 log.error(traceback.format_exc())
204 204 h.flash(str(e), category='error')
205 205 raise HTTPNotFound()
206 206
207 207 c.changes = OrderedDict()
208 208
209 209 c.lines_added = 0 # count of lines added
210 210 c.lines_deleted = 0 # count of lines removes
211 211
212 212 c.changeset_statuses = ChangesetStatus.STATUSES
213 213 c.comments = []
214 214 c.statuses = []
215 215 c.inline_comments = []
216 216 c.inline_cnt = 0
217 217
218 218 # Iterate over ranges (default changeset view is always one changeset)
219 219 for changeset in c.cs_ranges:
220 220 inlines = []
221 221 if method == 'show':
222 222 c.statuses.extend([ChangesetStatusModel().get_status(
223 223 c.rhodecode_db_repo.repo_id, changeset.raw_id)])
224 224
225 225 c.comments.extend(ChangesetCommentsModel()\
226 226 .get_comments(c.rhodecode_db_repo.repo_id,
227 227 revision=changeset.raw_id))
228 228
229 229 #comments from PR
230 230 st = ChangesetStatusModel().get_statuses(
231 231 c.rhodecode_db_repo.repo_id, changeset.raw_id,
232 232 with_revisions=True)
233 233 # from associated statuses, check the pull requests, and
234 234 # show comments from them
235 235
236 236 prs = set([x.pull_request for x in
237 237 filter(lambda x: x.pull_request != None, st)])
238 238
239 239 for pr in prs:
240 240 c.comments.extend(pr.comments)
241 241 inlines = ChangesetCommentsModel()\
242 242 .get_inline_comments(c.rhodecode_db_repo.repo_id,
243 243 revision=changeset.raw_id)
244 244 c.inline_comments.extend(inlines)
245 245
246 246 c.changes[changeset.raw_id] = []
247 247
248 248 cs2 = changeset.raw_id
249 249 cs1 = changeset.parents[0].raw_id if changeset.parents else EmptyChangeset()
250 250 context_lcl = get_line_ctx('', request.GET)
251 251 ign_whitespace_lcl = ign_whitespace_lcl = get_ignore_ws('', request.GET)
252 252
253 253 _diff = c.rhodecode_repo.get_diff(cs1, cs2,
254 254 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
255 255 diff_limit = self.cut_off_limit if not fulldiff else None
256 256 diff_processor = diffs.DiffProcessor(_diff,
257 257 vcs=c.rhodecode_repo.alias,
258 258 format='gitdiff',
259 259 diff_limit=diff_limit)
260 260 cs_changes = OrderedDict()
261 261 if method == 'show':
262 262 _parsed = diff_processor.prepare()
263 263 c.limited_diff = False
264 264 if isinstance(_parsed, LimitedDiffContainer):
265 265 c.limited_diff = True
266 266 for f in _parsed:
267 267 st = f['stats']
268 if st[0] != 'b':
269 c.lines_added += st[0]
270 c.lines_deleted += st[1]
268 c.lines_added += st['added']
269 c.lines_deleted += st['deleted']
271 270 fid = h.FID(changeset.raw_id, f['filename'])
272 271 diff = diff_processor.as_html(enable_comments=enable_comments,
273 272 parsed_lines=[f])
274 273 cs_changes[fid] = [cs1, cs2, f['operation'], f['filename'],
275 274 diff, st]
276 275 else:
277 276 # downloads/raw we only need RAW diff nothing else
278 277 diff = diff_processor.as_raw()
279 278 cs_changes[''] = [None, None, None, None, diff, None]
280 279 c.changes[changeset.raw_id] = cs_changes
281 280
282 281 #sort comments by how they were generated
283 282 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
284 283
285 284 # count inline comments
286 285 for __, lines in c.inline_comments:
287 286 for comments in lines.values():
288 287 c.inline_cnt += len(comments)
289 288
290 289 if len(c.cs_ranges) == 1:
291 290 c.changeset = c.cs_ranges[0]
292 291 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
293 292 for x in c.changeset.parents])
294 293 if method == 'download':
295 294 response.content_type = 'text/plain'
296 295 response.content_disposition = 'attachment; filename=%s.diff' \
297 296 % revision[:12]
298 297 return diff
299 298 elif method == 'patch':
300 299 response.content_type = 'text/plain'
301 300 c.diff = safe_unicode(diff)
302 301 return render('changeset/patch_changeset.html')
303 302 elif method == 'raw':
304 303 response.content_type = 'text/plain'
305 304 return diff
306 305 elif method == 'show':
307 306 if len(c.cs_ranges) == 1:
308 307 return render('changeset/changeset.html')
309 308 else:
310 309 return render('changeset/changeset_range.html')
311 310
312 311 @LoginRequired()
313 312 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
314 313 'repository.admin')
315 314 def index(self, revision, method='show'):
316 315 return self._index(revision, method=method)
317 316
318 317 @LoginRequired()
319 318 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
320 319 'repository.admin')
321 320 def changeset_raw(self, revision):
322 321 return self._index(revision, method='raw')
323 322
324 323 @LoginRequired()
325 324 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
326 325 'repository.admin')
327 326 def changeset_patch(self, revision):
328 327 return self._index(revision, method='patch')
329 328
330 329 @LoginRequired()
331 330 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
332 331 'repository.admin')
333 332 def changeset_download(self, revision):
334 333 return self._index(revision, method='download')
335 334
336 335 @LoginRequired()
337 336 @NotAnonymous()
338 337 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
339 338 'repository.admin')
340 339 @jsonify
341 340 def comment(self, repo_name, revision):
342 341 status = request.POST.get('changeset_status')
343 342 change_status = request.POST.get('change_changeset_status')
344 343 text = request.POST.get('text')
345 344 if status and change_status:
346 345 text = text or (_('Status change -> %s')
347 346 % ChangesetStatus.get_status_lbl(status))
348 347
349 348 c.co = comm = ChangesetCommentsModel().create(
350 349 text=text,
351 350 repo=c.rhodecode_db_repo.repo_id,
352 351 user=c.rhodecode_user.user_id,
353 352 revision=revision,
354 353 f_path=request.POST.get('f_path'),
355 354 line_no=request.POST.get('line'),
356 355 status_change=(ChangesetStatus.get_status_lbl(status)
357 356 if status and change_status else None)
358 357 )
359 358
360 359 # get status if set !
361 360 if status and change_status:
362 361 # if latest status was from pull request and it's closed
363 362 # disallow changing status !
364 363 # dont_allow_on_closed_pull_request = True !
365 364
366 365 try:
367 366 ChangesetStatusModel().set_status(
368 367 c.rhodecode_db_repo.repo_id,
369 368 status,
370 369 c.rhodecode_user.user_id,
371 370 comm,
372 371 revision=revision,
373 372 dont_allow_on_closed_pull_request=True
374 373 )
375 374 except StatusChangeOnClosedPullRequestError:
376 375 log.error(traceback.format_exc())
377 376 msg = _('Changing status on a changeset associated with '
378 377 'a closed pull request is not allowed')
379 378 h.flash(msg, category='warning')
380 379 return redirect(h.url('changeset_home', repo_name=repo_name,
381 380 revision=revision))
382 381 action_logger(self.rhodecode_user,
383 382 'user_commented_revision:%s' % revision,
384 383 c.rhodecode_db_repo, self.ip_addr, self.sa)
385 384
386 385 Session().commit()
387 386
388 387 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
389 388 return redirect(h.url('changeset_home', repo_name=repo_name,
390 389 revision=revision))
391 390 #only ajax below
392 391 data = {
393 392 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
394 393 }
395 394 if comm:
396 395 data.update(comm.get_dict())
397 396 data.update({'rendered_text':
398 397 render('changeset/changeset_comment_block.html')})
399 398
400 399 return data
401 400
402 401 @LoginRequired()
403 402 @NotAnonymous()
404 403 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
405 404 'repository.admin')
406 405 def preview_comment(self):
407 406 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
408 407 raise HTTPBadRequest()
409 408 text = request.POST.get('text')
410 409 if text:
411 410 return h.rst_w_mentions(text)
412 411 return ''
413 412
414 413 @LoginRequired()
415 414 @NotAnonymous()
416 415 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
417 416 'repository.admin')
418 417 @jsonify
419 418 def delete_comment(self, repo_name, comment_id):
420 419 co = ChangesetComment.get(comment_id)
421 420 owner = co.author.user_id == c.rhodecode_user.user_id
422 421 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
423 422 ChangesetCommentsModel().delete(comment=co)
424 423 Session().commit()
425 424 return True
426 425 else:
427 426 raise HTTPForbidden()
428 427
429 428 @LoginRequired()
430 429 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
431 430 'repository.admin')
432 431 @jsonify
433 432 def changeset_info(self, repo_name, revision):
434 433 if request.is_xhr:
435 434 try:
436 435 return c.rhodecode_repo.get_changeset(revision)
437 436 except ChangesetDoesNotExistError, e:
438 437 return EmptyChangeset(message=str(e))
439 438 else:
440 439 raise HTTPBadRequest()
@@ -1,269 +1,269 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.compare
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 compare controller for pylons showing differences between two
7 7 repos, branches, bookmarks or tips
8 8
9 9 :created_on: May 6, 2012
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 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 logging
28 28 import traceback
29 29 import re
30 30
31 31 from webob.exc import HTTPNotFound
32 32 from pylons import request, response, session, tmpl_context as c, url
33 33 from pylons.controllers.util import abort, redirect
34 34 from pylons.i18n.translation import _
35 35
36 36 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError
37 37 from rhodecode.lib.vcs.utils import safe_str
38 38 from rhodecode.lib.vcs.utils.hgcompat import scmutil
39 39 from rhodecode.lib import helpers as h
40 40 from rhodecode.lib.base import BaseRepoController, render
41 41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 42 from rhodecode.lib import diffs, unionrepo
43 43
44 44 from rhodecode.model.db import Repository
45 45 from webob.exc import HTTPBadRequest
46 46 from rhodecode.lib.diffs import LimitedDiffContainer
47 47
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 class CompareController(BaseRepoController):
53 53
54 54 def __before__(self):
55 55 super(CompareController, self).__before__()
56 56
57 57 def __get_cs_or_redirect(self, rev, repo, redirect_after=True,
58 58 partial=False):
59 59 """
60 60 Safe way to get changeset if error occur it redirects to changeset with
61 61 proper message. If partial is set then don't do redirect raise Exception
62 62 instead
63 63
64 64 :param rev: revision to fetch
65 65 :param repo: repo instance
66 66 """
67 67
68 68 try:
69 69 type_, rev = rev
70 70 return repo.scm_instance.get_changeset(rev)
71 71 except EmptyRepositoryError, e:
72 72 if not redirect_after:
73 73 return None
74 74 h.flash(h.literal(_('There are no changesets yet')),
75 75 category='warning')
76 76 redirect(url('summary_home', repo_name=repo.repo_name))
77 77
78 78 except RepositoryError, e:
79 79 log.error(traceback.format_exc())
80 80 h.flash(str(e), category='warning')
81 81 if not partial:
82 82 redirect(h.url('summary_home', repo_name=repo.repo_name))
83 83 raise HTTPBadRequest()
84 84
85 85 def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, merge):
86 86 """
87 87 Returns a list of changesets that can be merged from org_repo@org_ref
88 88 to other_repo@other_ref ... and the ancestor that would be used for merge
89 89
90 90 :param org_repo:
91 91 :param org_ref:
92 92 :param other_repo:
93 93 :param other_ref:
94 94 :param tmp:
95 95 """
96 96
97 97 ancestor = None
98 98
99 99 if alias == 'hg':
100 100 # lookup up the exact node id
101 101 _revset_predicates = {
102 102 'branch': 'branch',
103 103 'book': 'bookmark',
104 104 'tag': 'tag',
105 105 'rev': 'id',
106 106 }
107 107
108 108 org_rev_spec = "max(%s(%%s))" % _revset_predicates[org_ref[0]]
109 109 org_revs = org_repo._repo.revs(org_rev_spec, safe_str(org_ref[1]))
110 110 org_rev = org_repo._repo[org_revs[-1] if org_revs else -1].hex()
111 111
112 112 other_revs_spec = "max(%s(%%s))" % _revset_predicates[other_ref[0]]
113 113 other_revs = other_repo._repo.revs(other_revs_spec, safe_str(other_ref[1]))
114 114 other_rev = other_repo._repo[other_revs[-1] if other_revs else -1].hex()
115 115
116 116 #case two independent repos
117 117 if org_repo != other_repo:
118 118 hgrepo = unionrepo.unionrepository(other_repo.baseui,
119 119 other_repo.path,
120 120 org_repo.path)
121 121 # all the changesets we are looking for will be in other_repo,
122 122 # so rev numbers from hgrepo can be used in other_repo
123 123
124 124 #no remote compare do it on the same repository
125 125 else:
126 126 hgrepo = other_repo._repo
127 127
128 128 if merge:
129 129 revs = hgrepo.revs("ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)",
130 130 other_rev, org_rev, org_rev)
131 131
132 132 ancestors = hgrepo.revs("ancestor(id(%s), id(%s))", org_rev, other_rev)
133 133 if ancestors:
134 134 # pick arbitrary ancestor - but there is usually only one
135 135 ancestor = hgrepo[ancestors[0]].hex()
136 136 else:
137 137 # TODO: have both + and - changesets
138 138 revs = hgrepo.revs("id(%s) :: id(%s) - id(%s)",
139 139 org_rev, other_rev, org_rev)
140 140
141 141 changesets = [other_repo.get_changeset(rev) for rev in revs]
142 142
143 143 elif alias == 'git':
144 144 if org_repo != other_repo:
145 145 raise Exception('Comparing of different GIT repositories is not'
146 146 'allowed. Got %s != %s' % (org_repo, other_repo))
147 147
148 148 so, se = org_repo.run_git_command(
149 149 'log --reverse --pretty="format: %%H" -s -p %s..%s'
150 150 % (org_ref[1], other_ref[1])
151 151 )
152 152 changesets = [org_repo.get_changeset(cs)
153 153 for cs in re.findall(r'[0-9a-fA-F]{40}', so)]
154 154
155 155 return changesets, ancestor
156 156
157 157 @LoginRequired()
158 158 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
159 159 'repository.admin')
160 160 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
161 161 # org_ref will be evaluated in org_repo
162 162 org_repo = c.rhodecode_db_repo.repo_name
163 163 org_ref = (org_ref_type, org_ref)
164 164 # other_ref will be evaluated in other_repo
165 165 other_ref = (other_ref_type, other_ref)
166 166 other_repo = request.GET.get('other_repo', org_repo)
167 167 # If merge is True:
168 168 # Show what org would get if merged with other:
169 169 # List changesets that are ancestors of other but not of org.
170 170 # New changesets in org is thus ignored.
171 171 # Diff will be from common ancestor, and merges of org to other will thus be ignored.
172 172 # If merge is False:
173 173 # Make a raw diff from org to other, no matter if related or not.
174 174 # Changesets in one and not in the other will be ignored
175 175 merge = bool(request.GET.get('merge'))
176 176 # fulldiff disables cut_off_limit
177 177 c.fulldiff = request.GET.get('fulldiff')
178 178 # partial uses compare_cs.html template directly
179 179 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
180 180 # as_form puts hidden input field with changeset revisions
181 181 c.as_form = partial and request.GET.get('as_form')
182 182 # swap url for compare_diff page - never partial and never as_form
183 183 c.swap_url = h.url('compare_url',
184 184 repo_name=other_repo,
185 185 org_ref_type=other_ref[0], org_ref=other_ref[1],
186 186 other_repo=org_repo,
187 187 other_ref_type=org_ref[0], other_ref=org_ref[1],
188 188 merge=merge or '')
189 189
190 190 org_repo = Repository.get_by_repo_name(org_repo)
191 191 other_repo = Repository.get_by_repo_name(other_repo)
192 192
193 193 if org_repo is None:
194 194 log.error('Could not find org repo %s' % org_repo)
195 195 raise HTTPNotFound
196 196 if other_repo is None:
197 197 log.error('Could not find other repo %s' % other_repo)
198 198 raise HTTPNotFound
199 199
200 200 if org_repo != other_repo and h.is_git(org_repo):
201 201 log.error('compare of two remote repos not available for GIT REPOS')
202 202 raise HTTPNotFound
203 203
204 204 if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
205 205 log.error('compare of two different kind of remote repos not available')
206 206 raise HTTPNotFound
207 207
208 208 self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
209 209 self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial)
210 210
211 211 c.org_repo = org_repo
212 212 c.other_repo = other_repo
213 213 c.org_ref = org_ref[1]
214 214 c.other_ref = other_ref[1]
215 215 c.org_ref_type = org_ref[0]
216 216 c.other_ref_type = other_ref[0]
217 217
218 218 c.cs_ranges, c.ancestor = self._get_changesets(org_repo.scm_instance.alias,
219 219 org_repo.scm_instance, org_ref,
220 220 other_repo.scm_instance, other_ref,
221 221 merge)
222 222
223 223 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
224 224 c.cs_ranges])
225 225 if not c.ancestor:
226 226 log.warning('Unable to find ancestor revision')
227 227
228 228 if partial:
229 229 return render('compare/compare_cs.html')
230 230
231 231 if c.ancestor:
232 232 assert merge
233 233 # case we want a simple diff without incoming changesets,
234 234 # previewing what will be merged.
235 235 # Make the diff on the other repo (which is known to have other_ref)
236 236 log.debug('Using ancestor %s as org_ref instead of %s'
237 237 % (c.ancestor, org_ref))
238 238 org_ref = ('rev', c.ancestor)
239 239 org_repo = other_repo
240 240
241 241 diff_limit = self.cut_off_limit if not c.fulldiff else None
242 242
243 243 log.debug('running diff between %s and %s in %s'
244 244 % (org_ref, other_ref, org_repo.scm_instance.path))
245 245 txtdiff = org_repo.scm_instance.get_diff(rev1=safe_str(org_ref[1]), rev2=safe_str(other_ref[1]))
246 246
247 247 diff_processor = diffs.DiffProcessor(txtdiff or '', format='gitdiff',
248 248 diff_limit=diff_limit)
249 249 _parsed = diff_processor.prepare()
250 250
251 251 c.limited_diff = False
252 252 if isinstance(_parsed, LimitedDiffContainer):
253 253 c.limited_diff = True
254 254
255 255 c.files = []
256 256 c.changes = {}
257 257 c.lines_added = 0
258 258 c.lines_deleted = 0
259 259 for f in _parsed:
260 260 st = f['stats']
261 if st[0] != 'b':
262 c.lines_added += st[0]
263 c.lines_deleted += st[1]
261 if not st['binary']:
262 c.lines_added += st['added']
263 c.lines_deleted += st['deleted']
264 264 fid = h.FID('', f['filename'])
265 265 c.files.append([fid, f['operation'], f['filename'], f['stats']])
266 266 htmldiff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
267 267 c.changes[fid] = [f['operation'], f['filename'], htmldiff]
268 268
269 269 return render('compare/compare_diff.html')
@@ -1,179 +1,179 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.feed
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Feed controller for rhodecode
7 7
8 8 :created_on: Apr 23, 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 logging
27 27
28 28 from pylons import url, response, tmpl_context as c
29 29 from pylons.i18n.translation import _
30 30
31 31 from beaker.cache import cache_region, region_invalidate
32 32 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
33 33
34 34 from rhodecode.lib import helpers as h
35 35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 36 from rhodecode.lib.base import BaseRepoController
37 37 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
38 38 from rhodecode.model.db import CacheInvalidation
39 39 from rhodecode.lib.utils2 import safe_int, str2bool, safe_unicode
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43
44 44 class FeedController(BaseRepoController):
45 45
46 46 @LoginRequired(api_access=True)
47 47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
48 48 'repository.admin')
49 49 def __before__(self):
50 50 super(FeedController, self).__before__()
51 51 #common values for feeds
52 52 self.description = _('Changes on %s repository')
53 53 self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
54 54 self.language = 'en-us'
55 55 self.ttl = "5"
56 56 import rhodecode
57 57 CONF = rhodecode.CONFIG
58 58 self.include_diff = str2bool(CONF.get('rss_include_diff', False))
59 59 self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
60 60 # we need to protect from parsing huge diffs here other way
61 61 # we can kill the server
62 62 self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit', 32 * 1024))
63 63
64 64 def _get_title(self, cs):
65 65 return "%s" % (
66 66 h.shorter(cs.message, 160)
67 67 )
68 68
69 69 def __changes(self, cs):
70 70 changes = []
71 71 diff_processor = DiffProcessor(cs.diff(),
72 72 diff_limit=self.feed_diff_limit)
73 73 _parsed = diff_processor.prepare(inline_diff=False)
74 74 limited_diff = False
75 75 if isinstance(_parsed, LimitedDiffContainer):
76 76 limited_diff = True
77 77
78 78 for st in _parsed:
79 st.update({'added': st['stats'][0],
80 'removed': st['stats'][1]})
79 st.update({'added': st['stats']['added'],
80 'removed': st['stats']['deleted']})
81 81 changes.append('\n %(operation)s %(filename)s '
82 82 '(%(added)s lines added, %(removed)s lines removed)'
83 83 % st)
84 84 if limited_diff:
85 85 changes = changes + ['\n ' +
86 86 _('Changeset was too big and was cut off...')]
87 87 return diff_processor, changes
88 88
89 89 def __get_desc(self, cs):
90 90 desc_msg = []
91 91 desc_msg.append((_('%s committed on %s')
92 92 % (h.person(cs.author), h.fmt_date(cs.date))) + '<br/>')
93 93 #branches, tags, bookmarks
94 94 if cs.branch:
95 95 desc_msg.append('branch: %s<br/>' % cs.branch)
96 96 if h.is_hg(c.rhodecode_repo):
97 97 for book in cs.bookmarks:
98 98 desc_msg.append('bookmark: %s<br/>' % book)
99 99 for tag in cs.tags:
100 100 desc_msg.append('tag: %s<br/>' % tag)
101 101 diff_processor, changes = self.__changes(cs)
102 102 # rev link
103 103 _url = url('changeset_home', repo_name=cs.repository.name,
104 104 revision=cs.raw_id, qualified=True)
105 105 desc_msg.append('changeset: <a href="%s">%s</a>' % (_url, cs.raw_id[:8]))
106 106
107 107 desc_msg.append('<pre>')
108 108 desc_msg.append(cs.message)
109 109 desc_msg.append('\n')
110 110 desc_msg.extend(changes)
111 111 if self.include_diff:
112 112 desc_msg.append('\n\n')
113 113 desc_msg.append(diff_processor.as_raw())
114 114 desc_msg.append('</pre>')
115 115 return map(safe_unicode, desc_msg)
116 116
117 117 def atom(self, repo_name):
118 118 """Produce an atom-1.0 feed via feedgenerator module"""
119 119
120 120 @cache_region('long_term')
121 121 def _get_feed_from_cache(key, kind):
122 122 feed = Atom1Feed(
123 123 title=self.title % repo_name,
124 124 link=url('summary_home', repo_name=repo_name,
125 125 qualified=True),
126 126 description=self.description % repo_name,
127 127 language=self.language,
128 128 ttl=self.ttl
129 129 )
130 130
131 131 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
132 132 feed.add_item(title=self._get_title(cs),
133 133 link=url('changeset_home', repo_name=repo_name,
134 134 revision=cs.raw_id, qualified=True),
135 135 author_name=cs.author,
136 136 description=''.join(self.__get_desc(cs)),
137 137 pubdate=cs.date,
138 138 )
139 139
140 140 response.content_type = feed.mime_type
141 141 return feed.writeString('utf-8')
142 142
143 143 kind = 'ATOM'
144 144 valid = CacheInvalidation.test_and_set_valid(repo_name, kind)
145 145 if not valid:
146 146 region_invalidate(_get_feed_from_cache, None, repo_name, kind)
147 147 return _get_feed_from_cache(repo_name, kind)
148 148
149 149 def rss(self, repo_name):
150 150 """Produce an rss2 feed via feedgenerator module"""
151 151
152 152 @cache_region('long_term')
153 153 def _get_feed_from_cache(key, kind):
154 154 feed = Rss201rev2Feed(
155 155 title=self.title % repo_name,
156 156 link=url('summary_home', repo_name=repo_name,
157 157 qualified=True),
158 158 description=self.description % repo_name,
159 159 language=self.language,
160 160 ttl=self.ttl
161 161 )
162 162
163 163 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
164 164 feed.add_item(title=self._get_title(cs),
165 165 link=url('changeset_home', repo_name=repo_name,
166 166 revision=cs.raw_id, qualified=True),
167 167 author_name=cs.author,
168 168 description=''.join(self.__get_desc(cs)),
169 169 pubdate=cs.date,
170 170 )
171 171
172 172 response.content_type = feed.mime_type
173 173 return feed.writeString('utf-8')
174 174
175 175 kind = 'RSS'
176 176 valid = CacheInvalidation.test_and_set_valid(repo_name, kind)
177 177 if not valid:
178 178 region_invalidate(_get_feed_from_cache, None, repo_name, kind)
179 179 return _get_feed_from_cache(repo_name, kind)
@@ -1,560 +1,557 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.pullrequests
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull requests controller for rhodecode for initializing pull requests
7 7
8 8 :created_on: May 7, 2012
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 import logging
26 26 import traceback
27 27 import formencode
28 28
29 29 from webob.exc import HTTPNotFound, HTTPForbidden
30 30 from collections import defaultdict
31 31 from itertools import groupby
32 32
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 37 from rhodecode.lib.compat import json
38 38 from rhodecode.lib.base import BaseRepoController, render
39 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 40 NotAnonymous
41 41 from rhodecode.lib.helpers import Page
42 42 from rhodecode.lib import helpers as h
43 43 from rhodecode.lib import diffs
44 44 from rhodecode.lib.utils import action_logger, jsonify
45 45 from rhodecode.lib.vcs.utils import safe_str
46 46 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
47 47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48 48 from rhodecode.lib.diffs import LimitedDiffContainer
49 49 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
50 50 ChangesetComment
51 51 from rhodecode.model.pull_request import PullRequestModel
52 52 from rhodecode.model.meta import Session
53 53 from rhodecode.model.repo import RepoModel
54 54 from rhodecode.model.comment import ChangesetCommentsModel
55 55 from rhodecode.model.changeset_status import ChangesetStatusModel
56 56 from rhodecode.model.forms import PullRequestForm
57 57 from mercurial import scmutil
58 58 from rhodecode.lib.utils2 import safe_int
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62
63 63 class PullrequestsController(BaseRepoController):
64 64
65 65 def __before__(self):
66 66 super(PullrequestsController, self).__before__()
67 67 repo_model = RepoModel()
68 68 c.users_array = repo_model.get_users_js()
69 69 c.users_groups_array = repo_model.get_users_groups_js()
70 70
71 71 def _get_repo_refs(self, repo, rev=None, branch=None, branch_rev=None):
72 72 """return a structure with repo's interesting changesets, suitable for
73 73 the selectors in pullrequest.html
74 74
75 75 rev: a revision that must be in the list somehow and selected by default
76 76 branch: a branch that must be in the list and selected by default - even if closed
77 77 branch_rev: a revision of which peers should be preferred and available."""
78 78 # list named branches that has been merged to this named branch - it should probably merge back
79 79 peers = []
80 80
81 81 if rev:
82 82 rev = safe_str(rev)
83 83
84 84 if branch:
85 85 branch = safe_str(branch)
86 86
87 87 if branch_rev:
88 88 branch_rev = safe_str(branch_rev)
89 89 # not restricting to merge() would also get branch point and be better
90 90 # (especially because it would get the branch point) ... but is currently too expensive
91 91 otherbranches = {}
92 92 for i in repo._repo.revs(
93 93 "sort(parents(branch(id(%s)) and merge()) - branch(id(%s)))",
94 94 branch_rev, branch_rev):
95 95 cs = repo.get_changeset(i)
96 96 otherbranches[cs.branch] = cs.raw_id
97 97 for abranch, node in otherbranches.iteritems():
98 98 selected = 'branch:%s:%s' % (abranch, node)
99 99 peers.append((selected, abranch))
100 100
101 101 selected = None
102 102
103 103 branches = []
104 104 for abranch, branchrev in repo.branches.iteritems():
105 105 n = 'branch:%s:%s' % (abranch, branchrev)
106 106 branches.append((n, abranch))
107 107 if rev == branchrev:
108 108 selected = n
109 109 if branch == abranch:
110 110 selected = n
111 111 branch = None
112 112 if branch: # branch not in list - it is probably closed
113 113 revs = repo._repo.revs('max(branch(%s))', branch)
114 114 if revs:
115 115 cs = repo.get_changeset(revs[0])
116 116 selected = 'branch:%s:%s' % (branch, cs.raw_id)
117 117 branches.append((selected, branch))
118 118
119 119 bookmarks = []
120 120 for bookmark, bookmarkrev in repo.bookmarks.iteritems():
121 121 n = 'book:%s:%s' % (bookmark, bookmarkrev)
122 122 bookmarks.append((n, bookmark))
123 123 if rev == bookmarkrev:
124 124 selected = n
125 125
126 126 tags = []
127 127 for tag, tagrev in repo.tags.iteritems():
128 128 n = 'tag:%s:%s' % (tag, tagrev)
129 129 tags.append((n, tag))
130 130 if rev == tagrev and tag != 'tip': # tip is not a real tag - and its branch is better
131 131 selected = n
132 132
133 133 # prio 1: rev was selected as existing entry above
134 134
135 135 # prio 2: create special entry for rev; rev _must_ be used
136 136 specials = []
137 137 if rev and selected is None:
138 138 selected = 'rev:%s:%s' % (rev, rev)
139 139 specials = [(selected, '%s: %s' % (_("Changeset"), rev[:12]))]
140 140
141 141 # prio 3: most recent peer branch
142 142 if peers and not selected:
143 143 selected = peers[0][0][0]
144 144
145 145 # prio 4: tip revision
146 146 if not selected:
147 147 selected = 'tag:tip:%s' % repo.tags['tip']
148 148
149 149 groups = [(specials, _("Special")),
150 150 (peers, _("Peer branches")),
151 151 (bookmarks, _("Bookmarks")),
152 152 (branches, _("Branches")),
153 153 (tags, _("Tags")),
154 154 ]
155 155 return [g for g in groups if g[0]], selected
156 156
157 157 def _get_is_allowed_change_status(self, pull_request):
158 158 owner = self.rhodecode_user.user_id == pull_request.user_id
159 159 reviewer = self.rhodecode_user.user_id in [x.user_id for x in
160 160 pull_request.reviewers]
161 161 return (self.rhodecode_user.admin or owner or reviewer)
162 162
163 163 def _load_compare_data(self, pull_request, enable_comments=True):
164 164 """
165 165 Load context data needed for generating compare diff
166 166
167 167 :param pull_request:
168 168 :type pull_request:
169 169 """
170 170 org_repo = pull_request.org_repo
171 171 (org_ref_type,
172 172 org_ref_name,
173 173 org_ref_rev) = pull_request.org_ref.split(':')
174 174
175 175 other_repo = org_repo
176 176 (other_ref_type,
177 177 other_ref_name,
178 178 other_ref_rev) = pull_request.other_ref.split(':')
179 179
180 180 # despite opening revisions for bookmarks/branches/tags, we always
181 181 # convert this to rev to prevent changes after bookmark or branch change
182 182 org_ref = ('rev', org_ref_rev)
183 183 other_ref = ('rev', other_ref_rev)
184 184
185 185 c.org_repo = org_repo
186 186 c.other_repo = other_repo
187 187
188 188 c.fulldiff = fulldiff = request.GET.get('fulldiff')
189 189
190 190 c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions]
191 191
192 192 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
193 193
194 194 c.org_ref = org_ref[1]
195 195 c.org_ref_type = org_ref[0]
196 196 c.other_ref = other_ref[1]
197 197 c.other_ref_type = other_ref[0]
198 198
199 199 diff_limit = self.cut_off_limit if not fulldiff else None
200 200
201 201 # we swap org/other ref since we run a simple diff on one repo
202 202 log.debug('running diff between %s and %s in %s'
203 203 % (other_ref, org_ref, org_repo.scm_instance.path))
204 204 txtdiff = org_repo.scm_instance.get_diff(rev1=safe_str(other_ref[1]), rev2=safe_str(org_ref[1]))
205 205
206 206 diff_processor = diffs.DiffProcessor(txtdiff or '', format='gitdiff',
207 207 diff_limit=diff_limit)
208 208 _parsed = diff_processor.prepare()
209 209
210 210 c.limited_diff = False
211 211 if isinstance(_parsed, LimitedDiffContainer):
212 212 c.limited_diff = True
213 213
214 214 c.files = []
215 215 c.changes = {}
216 c.lines_added = 0
217 c.lines_deleted = 0
218 216 for f in _parsed:
219 217 st = f['stats']
220 if st[0] != 'b':
221 c.lines_added += st[0]
222 c.lines_deleted += st[1]
218 c.lines_added += st['added']
219 c.lines_deleted += st['deleted']
223 220 fid = h.FID('', f['filename'])
224 221 c.files.append([fid, f['operation'], f['filename'], f['stats']])
225 222 htmldiff = diff_processor.as_html(enable_comments=enable_comments,
226 223 parsed_lines=[f])
227 224 c.changes[fid] = [f['operation'], f['filename'], htmldiff]
228 225
229 226 @LoginRequired()
230 227 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
231 228 'repository.admin')
232 229 def show_all(self, repo_name):
233 230 c.pull_requests = PullRequestModel().get_all(repo_name)
234 231 c.repo_name = repo_name
235 232 p = safe_int(request.GET.get('page', 1), 1)
236 233
237 234 c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=10)
238 235
239 236 c.pullrequest_data = render('/pullrequests/pullrequest_data.html')
240 237
241 238 if request.environ.get('HTTP_X_PARTIAL_XHR'):
242 239 return c.pullrequest_data
243 240
244 241 return render('/pullrequests/pullrequest_show_all.html')
245 242
246 243 @LoginRequired()
247 244 @NotAnonymous()
248 245 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
249 246 'repository.admin')
250 247 def index(self):
251 248 org_repo = c.rhodecode_db_repo
252 249
253 250 if org_repo.scm_instance.alias != 'hg':
254 251 log.error('Review not available for GIT REPOS')
255 252 raise HTTPNotFound
256 253
257 254 try:
258 255 org_repo.scm_instance.get_changeset()
259 256 except EmptyRepositoryError, e:
260 257 h.flash(h.literal(_('There are no changesets yet')),
261 258 category='warning')
262 259 redirect(url('summary_home', repo_name=org_repo.repo_name))
263 260
264 261 org_rev = request.GET.get('rev_end')
265 262 # rev_start is not directly useful - its parent could however be used
266 263 # as default for other and thus give a simple compare view
267 264 #other_rev = request.POST.get('rev_start')
268 265 branch = request.GET.get('branch')
269 266
270 267 c.org_repos = []
271 268 c.org_repos.append((org_repo.repo_name, org_repo.repo_name))
272 269 c.default_org_repo = org_repo.repo_name
273 270 c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance, rev=org_rev, branch=branch)
274 271
275 272 c.other_repos = []
276 273 other_repos_info = {}
277 274
278 275 def add_other_repo(repo, branch_rev=None):
279 276 if repo.repo_name in other_repos_info: # shouldn't happen
280 277 return
281 278 c.other_repos.append((repo.repo_name, repo.repo_name))
282 279 other_refs, selected_other_ref = self._get_repo_refs(repo.scm_instance, branch_rev=branch_rev)
283 280 other_repos_info[repo.repo_name] = {
284 281 'user': dict(user_id=repo.user.user_id,
285 282 username=repo.user.username,
286 283 firstname=repo.user.firstname,
287 284 lastname=repo.user.lastname,
288 285 gravatar_link=h.gravatar_url(repo.user.email, 14)),
289 286 'description': repo.description.split('\n', 1)[0],
290 287 'revs': h.select('other_ref', selected_other_ref, other_refs, class_='refs')
291 288 }
292 289
293 290 # add org repo to other so we can open pull request against peer branches on itself
294 291 add_other_repo(org_repo, branch_rev=org_rev)
295 292 c.default_other_repo = org_repo.repo_name
296 293
297 294 # gather forks and add to this list ... even though it is rare to
298 295 # request forks to pull from their parent
299 296 for fork in org_repo.forks:
300 297 add_other_repo(fork)
301 298
302 299 # add parents of this fork also, but only if it's not empty
303 300 if org_repo.parent and org_repo.parent.scm_instance.revisions:
304 301 add_other_repo(org_repo.parent)
305 302 c.default_other_repo = org_repo.parent.repo_name
306 303
307 304 c.default_other_repo_info = other_repos_info[c.default_other_repo]
308 305 c.other_repos_info = json.dumps(other_repos_info)
309 306
310 307 return render('/pullrequests/pullrequest.html')
311 308
312 309 @LoginRequired()
313 310 @NotAnonymous()
314 311 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
315 312 'repository.admin')
316 313 def create(self, repo_name):
317 314 repo = RepoModel()._get_repo(repo_name)
318 315 try:
319 316 _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
320 317 except formencode.Invalid, errors:
321 318 log.error(traceback.format_exc())
322 319 if errors.error_dict.get('revisions'):
323 320 msg = 'Revisions: %s' % errors.error_dict['revisions']
324 321 elif errors.error_dict.get('pullrequest_title'):
325 322 msg = _('Pull request requires a title with min. 3 chars')
326 323 else:
327 324 msg = _('Error creating pull request')
328 325
329 326 h.flash(msg, 'error')
330 327 return redirect(url('pullrequest_home', repo_name=repo_name))
331 328
332 329 org_repo = _form['org_repo']
333 330 org_ref = 'rev:merge:%s' % _form['merge_rev']
334 331 other_repo = _form['other_repo']
335 332 other_ref = 'rev:ancestor:%s' % _form['ancestor_rev']
336 333 revisions = [x for x in reversed(_form['revisions'])]
337 334 reviewers = _form['review_members']
338 335
339 336 title = _form['pullrequest_title']
340 337 description = _form['pullrequest_desc']
341 338 try:
342 339 pull_request = PullRequestModel().create(
343 340 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
344 341 other_ref, revisions, reviewers, title, description
345 342 )
346 343 Session().commit()
347 344 h.flash(_('Successfully opened new pull request'),
348 345 category='success')
349 346 except Exception:
350 347 h.flash(_('Error occurred during sending pull request'),
351 348 category='error')
352 349 log.error(traceback.format_exc())
353 350 return redirect(url('pullrequest_home', repo_name=repo_name))
354 351
355 352 return redirect(url('pullrequest_show', repo_name=other_repo,
356 353 pull_request_id=pull_request.pull_request_id))
357 354
358 355 @LoginRequired()
359 356 @NotAnonymous()
360 357 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
361 358 'repository.admin')
362 359 @jsonify
363 360 def update(self, repo_name, pull_request_id):
364 361 pull_request = PullRequest.get_or_404(pull_request_id)
365 362 if pull_request.is_closed():
366 363 raise HTTPForbidden()
367 364 #only owner or admin can update it
368 365 owner = pull_request.author.user_id == c.rhodecode_user.user_id
369 366 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
370 367 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
371 368 request.POST.get('reviewers_ids', '').split(',')))
372 369
373 370 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
374 371 Session().commit()
375 372 return True
376 373 raise HTTPForbidden()
377 374
378 375 @LoginRequired()
379 376 @NotAnonymous()
380 377 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
381 378 'repository.admin')
382 379 @jsonify
383 380 def delete(self, repo_name, pull_request_id):
384 381 pull_request = PullRequest.get_or_404(pull_request_id)
385 382 #only owner can delete it !
386 383 if pull_request.author.user_id == c.rhodecode_user.user_id:
387 384 PullRequestModel().delete(pull_request)
388 385 Session().commit()
389 386 h.flash(_('Successfully deleted pull request'),
390 387 category='success')
391 388 return redirect(url('admin_settings_my_account', anchor='pullrequests'))
392 389 raise HTTPForbidden()
393 390
394 391 @LoginRequired()
395 392 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
396 393 'repository.admin')
397 394 def show(self, repo_name, pull_request_id):
398 395 repo_model = RepoModel()
399 396 c.users_array = repo_model.get_users_js()
400 397 c.users_groups_array = repo_model.get_users_groups_js()
401 398 c.pull_request = PullRequest.get_or_404(pull_request_id)
402 399 c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
403 400 cc_model = ChangesetCommentsModel()
404 401 cs_model = ChangesetStatusModel()
405 402 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
406 403 pull_request=c.pull_request,
407 404 with_revisions=True)
408 405
409 406 cs_statuses = defaultdict(list)
410 407 for st in _cs_statuses:
411 408 cs_statuses[st.author.username] += [st]
412 409
413 410 c.pull_request_reviewers = []
414 411 c.pull_request_pending_reviewers = []
415 412 for o in c.pull_request.reviewers:
416 413 st = cs_statuses.get(o.user.username, None)
417 414 if st:
418 415 sorter = lambda k: k.version
419 416 st = [(x, list(y)[0])
420 417 for x, y in (groupby(sorted(st, key=sorter), sorter))]
421 418 else:
422 419 c.pull_request_pending_reviewers.append(o.user)
423 420 c.pull_request_reviewers.append([o.user, st])
424 421
425 422 # pull_requests repo_name we opened it against
426 423 # ie. other_repo must match
427 424 if repo_name != c.pull_request.other_repo.repo_name:
428 425 raise HTTPNotFound
429 426
430 427 # load compare data into template context
431 428 enable_comments = not c.pull_request.is_closed()
432 429 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
433 430
434 431 # inline comments
435 432 c.inline_cnt = 0
436 433 c.inline_comments = cc_model.get_inline_comments(
437 434 c.rhodecode_db_repo.repo_id,
438 435 pull_request=pull_request_id)
439 436 # count inline comments
440 437 for __, lines in c.inline_comments:
441 438 for comments in lines.values():
442 439 c.inline_cnt += len(comments)
443 440 # comments
444 441 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
445 442 pull_request=pull_request_id)
446 443
447 444 try:
448 445 cur_status = c.statuses[c.pull_request.revisions[0]][0]
449 446 except Exception:
450 447 log.error(traceback.format_exc())
451 448 cur_status = 'undefined'
452 449 if c.pull_request.is_closed() and 0:
453 450 c.current_changeset_status = cur_status
454 451 else:
455 452 # changeset(pull-request) status calulation based on reviewers
456 453 c.current_changeset_status = cs_model.calculate_status(
457 454 c.pull_request_reviewers,
458 455 )
459 456 c.changeset_statuses = ChangesetStatus.STATUSES
460 457
461 458 c.as_form = False
462 459 c.ancestor = None # there is one - but right here we don't know which
463 460 return render('/pullrequests/pullrequest_show.html')
464 461
465 462 @LoginRequired()
466 463 @NotAnonymous()
467 464 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
468 465 'repository.admin')
469 466 @jsonify
470 467 def comment(self, repo_name, pull_request_id):
471 468 pull_request = PullRequest.get_or_404(pull_request_id)
472 469 if pull_request.is_closed():
473 470 raise HTTPForbidden()
474 471
475 472 status = request.POST.get('changeset_status')
476 473 change_status = request.POST.get('change_changeset_status')
477 474 text = request.POST.get('text')
478 475 close_pr = request.POST.get('save_close')
479 476
480 477 allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
481 478 if status and change_status and allowed_to_change_status:
482 479 _def = (_('Status change -> %s')
483 480 % ChangesetStatus.get_status_lbl(status))
484 481 if close_pr:
485 482 _def = _('Closing with') + ' ' + _def
486 483 text = text or _def
487 484 comm = ChangesetCommentsModel().create(
488 485 text=text,
489 486 repo=c.rhodecode_db_repo.repo_id,
490 487 user=c.rhodecode_user.user_id,
491 488 pull_request=pull_request_id,
492 489 f_path=request.POST.get('f_path'),
493 490 line_no=request.POST.get('line'),
494 491 status_change=(ChangesetStatus.get_status_lbl(status)
495 492 if status and change_status
496 493 and allowed_to_change_status else None),
497 494 closing_pr=close_pr
498 495 )
499 496
500 497 action_logger(self.rhodecode_user,
501 498 'user_commented_pull_request:%s' % pull_request_id,
502 499 c.rhodecode_db_repo, self.ip_addr, self.sa)
503 500
504 501 if allowed_to_change_status:
505 502 # get status if set !
506 503 if status and change_status:
507 504 ChangesetStatusModel().set_status(
508 505 c.rhodecode_db_repo.repo_id,
509 506 status,
510 507 c.rhodecode_user.user_id,
511 508 comm,
512 509 pull_request=pull_request_id
513 510 )
514 511
515 512 if close_pr:
516 513 if status in ['rejected', 'approved']:
517 514 PullRequestModel().close_pull_request(pull_request_id)
518 515 action_logger(self.rhodecode_user,
519 516 'user_closed_pull_request:%s' % pull_request_id,
520 517 c.rhodecode_db_repo, self.ip_addr, self.sa)
521 518 else:
522 519 h.flash(_('Closing pull request on other statuses than '
523 520 'rejected or approved forbidden'),
524 521 category='warning')
525 522
526 523 Session().commit()
527 524
528 525 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
529 526 return redirect(h.url('pullrequest_show', repo_name=repo_name,
530 527 pull_request_id=pull_request_id))
531 528
532 529 data = {
533 530 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
534 531 }
535 532 if comm:
536 533 c.co = comm
537 534 data.update(comm.get_dict())
538 535 data.update({'rendered_text':
539 536 render('changeset/changeset_comment_block.html')})
540 537
541 538 return data
542 539
543 540 @LoginRequired()
544 541 @NotAnonymous()
545 542 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
546 543 'repository.admin')
547 544 @jsonify
548 545 def delete_comment(self, repo_name, comment_id):
549 546 co = ChangesetComment.get(comment_id)
550 547 if co.pull_request.is_closed():
551 548 #don't allow deleting comments on closed pull request
552 549 raise HTTPForbidden()
553 550
554 551 owner = co.author.user_id == c.rhodecode_user.user_id
555 552 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
556 553 ChangesetCommentsModel().delete(comment=co)
557 554 Session().commit()
558 555 return True
559 556 else:
560 557 raise HTTPForbidden()
@@ -1,688 +1,713 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.diffs
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Set of diffing helpers, previously part of vcs
7 7
8 8
9 9 :created_on: Dec 4, 2011
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :original copyright: 2007-2008 by Armin Ronacher
13 13 :license: GPLv3, see COPYING for more details.
14 14 """
15 15 # This program is free software: you can redistribute it and/or modify
16 16 # it under the terms of the GNU General Public License as published by
17 17 # the Free Software Foundation, either version 3 of the License, or
18 18 # (at your option) any later version.
19 19 #
20 20 # This program is distributed in the hope that it will be useful,
21 21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 23 # GNU General Public License for more details.
24 24 #
25 25 # You should have received a copy of the GNU General Public License
26 26 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 27
28 28 import re
29 29 import difflib
30 30 import logging
31 31
32 32 from itertools import tee, imap
33 33
34 34 from pylons.i18n.translation import _
35 35
36 36 from rhodecode.lib.vcs.exceptions import VCSError
37 37 from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode
38 38 from rhodecode.lib.vcs.backends.base import EmptyChangeset
39 39 from rhodecode.lib.helpers import escape
40 40 from rhodecode.lib.utils2 import safe_unicode, safe_str
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 def wrap_to_table(str_):
46 46 return '''<table class="code-difftable">
47 47 <tr class="line no-comment">
48 48 <td class="lineno new"></td>
49 49 <td class="code no-comment"><pre>%s</pre></td>
50 50 </tr>
51 51 </table>''' % str_
52 52
53 53
54 54 def wrapped_diff(filenode_old, filenode_new, cut_off_limit=None,
55 55 ignore_whitespace=True, line_context=3,
56 56 enable_comments=False):
57 57 """
58 58 returns a wrapped diff into a table, checks for cut_off_limit and presents
59 59 proper message
60 60 """
61 61
62 62 if filenode_old is None:
63 63 filenode_old = FileNode(filenode_new.path, '', EmptyChangeset())
64 64
65 65 if filenode_old.is_binary or filenode_new.is_binary:
66 66 diff = wrap_to_table(_('Binary file'))
67 67 stats = (0, 0)
68 68 size = 0
69 69
70 70 elif cut_off_limit != -1 and (cut_off_limit is None or
71 71 (filenode_old.size < cut_off_limit and filenode_new.size < cut_off_limit)):
72 72
73 73 f_gitdiff = get_gitdiff(filenode_old, filenode_new,
74 74 ignore_whitespace=ignore_whitespace,
75 75 context=line_context)
76 76 diff_processor = DiffProcessor(f_gitdiff, format='gitdiff')
77 77
78 78 diff = diff_processor.as_html(enable_comments=enable_comments)
79 79 stats = diff_processor.stat()
80 80 size = len(diff or '')
81 81 else:
82 82 diff = wrap_to_table(_('Changeset was too big and was cut off, use '
83 83 'diff menu to display this diff'))
84 84 stats = (0, 0)
85 85 size = 0
86 86 if not diff:
87 87 submodules = filter(lambda o: isinstance(o, SubModuleNode),
88 88 [filenode_new, filenode_old])
89 89 if submodules:
90 90 diff = wrap_to_table(escape('Submodule %r' % submodules[0]))
91 91 else:
92 92 diff = wrap_to_table(_('No changes detected'))
93 93
94 94 cs1 = filenode_old.changeset.raw_id
95 95 cs2 = filenode_new.changeset.raw_id
96 96
97 97 return size, cs1, cs2, diff, stats
98 98
99 99
100 100 def get_gitdiff(filenode_old, filenode_new, ignore_whitespace=True, context=3):
101 101 """
102 102 Returns git style diff between given ``filenode_old`` and ``filenode_new``.
103 103
104 104 :param ignore_whitespace: ignore whitespaces in diff
105 105 """
106 106 # make sure we pass in default context
107 107 context = context or 3
108 108 submodules = filter(lambda o: isinstance(o, SubModuleNode),
109 109 [filenode_new, filenode_old])
110 110 if submodules:
111 111 return ''
112 112
113 113 for filenode in (filenode_old, filenode_new):
114 114 if not isinstance(filenode, FileNode):
115 115 raise VCSError("Given object should be FileNode object, not %s"
116 116 % filenode.__class__)
117 117
118 118 repo = filenode_new.changeset.repository
119 119 old_raw_id = getattr(filenode_old.changeset, 'raw_id', repo.EMPTY_CHANGESET)
120 120 new_raw_id = getattr(filenode_new.changeset, 'raw_id', repo.EMPTY_CHANGESET)
121 121
122 122 vcs_gitdiff = repo.get_diff(old_raw_id, new_raw_id, filenode_new.path,
123 123 ignore_whitespace, context)
124 124 return vcs_gitdiff
125 125
126 126 NEW_FILENODE = 1
127 127 DEL_FILENODE = 2
128 128 MOD_FILENODE = 3
129 129 RENAMED_FILENODE = 4
130 130 CHMOD_FILENODE = 5
131 BIN_FILENODE = 6
131 132
132 133
133 134 class DiffLimitExceeded(Exception):
134 135 pass
135 136
136 137
137 138 class LimitedDiffContainer(object):
138 139
139 140 def __init__(self, diff_limit, cur_diff_size, diff):
140 141 self.diff = diff
141 142 self.diff_limit = diff_limit
142 143 self.cur_diff_size = cur_diff_size
143 144
144 145 def __iter__(self):
145 146 for l in self.diff:
146 147 yield l
147 148
148 149
149 150 class DiffProcessor(object):
150 151 """
151 152 Give it a unified or git diff and it returns a list of the files that were
152 153 mentioned in the diff together with a dict of meta information that
153 154 can be used to render it in a HTML template.
154 155 """
155 156 _chunk_re = re.compile(r'^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)')
156 157 _newline_marker = re.compile(r'^\\ No newline at end of file')
157 158 _git_header_re = re.compile(r"""
158 159 #^diff[ ]--git
159 160 [ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
160 161 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
161 162 ^rename[ ]from[ ](?P<rename_from>\S+)\n
162 163 ^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
163 164 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
164 165 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
165 166 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
166 167 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
167 168 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
168 169 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
170 (?:^(?P<bin_patch>GIT[ ]binary[ ]patch)(?:\n|$))?
169 171 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
170 172 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
171 173 """, re.VERBOSE | re.MULTILINE)
172 174 _hg_header_re = re.compile(r"""
173 175 #^diff[ ]--git
174 176 [ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
175 177 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
176 178 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
177 179 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%(?:\n|$))?
178 180 (?:^rename[ ]from[ ](?P<rename_from>\S+)\n
179 181 ^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
180 182 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
181 183 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
182 184 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
183 185 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
186 (?:^(?P<bin_patch>GIT[ ]binary[ ]patch)(?:\n|$))?
184 187 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
185 188 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
186 189 """, re.VERBOSE | re.MULTILINE)
187 190
188 191 #used for inline highlighter word split
189 192 _token_re = re.compile(r'()(&gt;|&lt;|&amp;|\W+?)')
190 193
191 194 def __init__(self, diff, vcs='hg', format='gitdiff', diff_limit=None):
192 195 """
193 196 :param diff: a text in diff format
194 197 :param vcs: type of version controll hg or git
195 198 :param format: format of diff passed, `udiff` or `gitdiff`
196 199 :param diff_limit: define the size of diff that is considered "big"
197 200 based on that parameter cut off will be triggered, set to None
198 201 to show full diff
199 202 """
200 203 if not isinstance(diff, basestring):
201 204 raise Exception('Diff must be a basestring got %s instead' % type(diff))
202 205
203 206 self._diff = diff
204 207 self._format = format
205 208 self.adds = 0
206 209 self.removes = 0
207 210 # calculate diff size
208 211 self.diff_size = len(diff)
209 212 self.diff_limit = diff_limit
210 213 self.cur_diff_size = 0
211 214 self.parsed = False
212 215 self.parsed_diff = []
213 216 self.vcs = vcs
214 217
215 218 if format == 'gitdiff':
216 219 self.differ = self._highlight_line_difflib
217 220 self._parser = self._parse_gitdiff
218 221 else:
219 222 self.differ = self._highlight_line_udiff
220 223 self._parser = self._parse_udiff
221 224
222 225 def _copy_iterator(self):
223 226 """
224 227 make a fresh copy of generator, we should not iterate thru
225 228 an original as it's needed for repeating operations on
226 229 this instance of DiffProcessor
227 230 """
228 231 self.__udiff, iterator_copy = tee(self.__udiff)
229 232 return iterator_copy
230 233
231 234 def _escaper(self, string):
232 235 """
233 236 Escaper for diff escapes special chars and checks the diff limit
234 237
235 238 :param string:
236 239 :type string:
237 240 """
238 241
239 242 self.cur_diff_size += len(string)
240 243
241 244 # escaper get's iterated on each .next() call and it checks if each
242 245 # parsed line doesn't exceed the diff limit
243 246 if self.diff_limit is not None and self.cur_diff_size > self.diff_limit:
244 247 raise DiffLimitExceeded('Diff Limit Exceeded')
245 248
246 249 return safe_unicode(string).replace('&', '&amp;')\
247 250 .replace('<', '&lt;')\
248 251 .replace('>', '&gt;')
249 252
250 253 def _line_counter(self, l):
251 254 """
252 255 Checks each line and bumps total adds/removes for this diff
253 256
254 257 :param l:
255 258 """
256 259 if l.startswith('+') and not l.startswith('+++'):
257 260 self.adds += 1
258 261 elif l.startswith('-') and not l.startswith('---'):
259 262 self.removes += 1
260 263 return safe_unicode(l)
261 264
262 265 def _highlight_line_difflib(self, line, next_):
263 266 """
264 267 Highlight inline changes in both lines.
265 268 """
266 269
267 270 if line['action'] == 'del':
268 271 old, new = line, next_
269 272 else:
270 273 old, new = next_, line
271 274
272 275 oldwords = self._token_re.split(old['line'])
273 276 newwords = self._token_re.split(new['line'])
274 277 sequence = difflib.SequenceMatcher(None, oldwords, newwords)
275 278
276 279 oldfragments, newfragments = [], []
277 280 for tag, i1, i2, j1, j2 in sequence.get_opcodes():
278 281 oldfrag = ''.join(oldwords[i1:i2])
279 282 newfrag = ''.join(newwords[j1:j2])
280 283 if tag != 'equal':
281 284 if oldfrag:
282 285 oldfrag = '<del>%s</del>' % oldfrag
283 286 if newfrag:
284 287 newfrag = '<ins>%s</ins>' % newfrag
285 288 oldfragments.append(oldfrag)
286 289 newfragments.append(newfrag)
287 290
288 291 old['line'] = "".join(oldfragments)
289 292 new['line'] = "".join(newfragments)
290 293
291 294 def _highlight_line_udiff(self, line, next_):
292 295 """
293 296 Highlight inline changes in both lines.
294 297 """
295 298 start = 0
296 299 limit = min(len(line['line']), len(next_['line']))
297 300 while start < limit and line['line'][start] == next_['line'][start]:
298 301 start += 1
299 302 end = -1
300 303 limit -= start
301 304 while -end <= limit and line['line'][end] == next_['line'][end]:
302 305 end -= 1
303 306 end += 1
304 307 if start or end:
305 308 def do(l):
306 309 last = end + len(l['line'])
307 310 if l['action'] == 'add':
308 311 tag = 'ins'
309 312 else:
310 313 tag = 'del'
311 314 l['line'] = '%s<%s>%s</%s>%s' % (
312 315 l['line'][:start],
313 316 tag,
314 317 l['line'][start:last],
315 318 tag,
316 319 l['line'][last:]
317 320 )
318 321 do(line)
319 322 do(next_)
320 323
321 324 def _get_header(self, diff_chunk):
322 325 """
323 326 parses the diff header, and returns parts, and leftover diff
324 327 parts consists of 14 elements::
325 328
326 329 a_path, b_path, similarity_index, rename_from, rename_to,
327 330 old_mode, new_mode, new_file_mode, deleted_file_mode,
328 331 a_blob_id, b_blob_id, b_mode, a_file, b_file
329 332
330 333 :param diff_chunk:
331 334 :type diff_chunk:
332 335 """
333 336
334 337 if self.vcs == 'git':
335 338 match = self._git_header_re.match(diff_chunk)
336 339 diff = diff_chunk[match.end():]
337 340 return match.groupdict(), imap(self._escaper, diff.splitlines(1))
338 341 elif self.vcs == 'hg':
339 342 match = self._hg_header_re.match(diff_chunk)
340 343 diff = diff_chunk[match.end():]
341 344 return match.groupdict(), imap(self._escaper, diff.splitlines(1))
342 345 else:
343 346 raise Exception('VCS type %s is not supported' % self.vcs)
344 347
345 348 def _clean_line(self, line, command):
346 349 if command in ['+', '-', ' ']:
347 350 #only modify the line if it's actually a diff thing
348 351 line = line[1:]
349 352 return line
350 353
351 354 def _parse_gitdiff(self, inline_diff=True):
352 355 _files = []
353 356 diff_container = lambda arg: arg
354 357
355 358 ##split the diff in chunks of separate --git a/file b/file chunks
356 359 for raw_diff in ('\n' + self._diff).split('\ndiff --git')[1:]:
357 360 head, diff = self._get_header(raw_diff)
358 361
359 362 op = None
360 stats = None
361 msgs = []
363 stats = {
364 'added': 0,
365 'deleted': 0,
366 'binary': False,
367 'ops': {},
368 }
362 369
363 370 if head['deleted_file_mode']:
364 371 op = 'D'
365 stats = ['b', DEL_FILENODE]
366 msgs.append('deleted file')
372 stats['binary'] = True
373 stats['ops'][DEL_FILENODE] = 'deleted file'
374
367 375 elif head['new_file_mode']:
368 376 op = 'A'
369 stats = ['b', NEW_FILENODE]
370 msgs.append('new file %s' % head['new_file_mode'])
371 else:
377 stats['binary'] = True
378 stats['ops'][NEW_FILENODE] = 'new file %s' % head['new_file_mode']
379 else: # modify operation, can be cp, rename, chmod
380 # CHMOD
372 381 if head['new_mode'] and head['old_mode']:
373 382 op = 'M'
374 stats = ['b', CHMOD_FILENODE]
375 msgs.append('modified file chmod %s => %s'
383 stats['binary'] = True
384 stats['ops'][CHMOD_FILENODE] = ('modified file chmod %s => %s'
376 385 % (head['old_mode'], head['new_mode']))
386 # RENAME
377 387 if (head['rename_from'] and head['rename_to']
378 388 and head['rename_from'] != head['rename_to']):
379 389 op = 'M'
380 stats = ['b', RENAMED_FILENODE] # might overwrite CHMOD_FILENODE
381 msgs.append('file renamed from %s to %s'
390 stats['binary'] = True
391 stats['ops'][RENAMED_FILENODE] = ('file renamed from %s to %s'
382 392 % (head['rename_from'], head['rename_to']))
383 if op is None: # fall back: detect missed old style add or remove
393
394 # FALL BACK: detect missed old style add or remove
395 if op is None:
384 396 if not head['a_file'] and head['b_file']:
385 397 op = 'A'
386 stats = ['b', NEW_FILENODE]
387 msgs.append('new file')
398 stats['binary'] = True
399 stats['ops'][NEW_FILENODE] = 'new file'
400
388 401 elif head['a_file'] and not head['b_file']:
389 402 op = 'D'
390 stats = ['b', DEL_FILENODE]
391 msgs.append('deleted file')
403 stats['binary'] = True
404 stats['ops'][DEL_FILENODE] = 'deleted file'
405
406 # it's not ADD not DELETE
392 407 if op is None:
393 408 op = 'M'
394 stats = ['b', MOD_FILENODE]
409 stats['binary'] = True
410 stats['ops'][MOD_FILENODE] = 'modified file'
395 411
396 if head['a_file'] or head['b_file']: # a real diff
412 # a real non-binary diff
413 if head['a_file'] or head['b_file']:
397 414 try:
398 chunks, stats = self._parse_lines(diff)
415 chunks, _stats = self._parse_lines(diff)
416 stats['binary'] = False
417 stats['added'] = _stats[0]
418 stats['deleted'] = _stats[1]
419 # explicit mark that it's a modified file
420 if op == 'M':
421 stats['ops'][MOD_FILENODE] = 'modified file'
422
399 423 except DiffLimitExceeded:
400 diff_container = lambda _diff: LimitedDiffContainer(
401 self.diff_limit,
402 self.cur_diff_size,
403 _diff)
424 diff_container = lambda _diff: \
425 LimitedDiffContainer(self.diff_limit,
426 self.cur_diff_size, _diff)
404 427 break
405 428 else: # GIT binary patch (or empty diff)
429 # GIT Binary patch
430 if head['bin_patch']:
431 stats['ops'][BIN_FILENODE] = 'binary diff not shown'
406 432 chunks = []
407 msgs.append('binary diff not shown') # or no diff because it was a rename or chmod or add/remove of empty file
408 433
409 if msgs:
410 434 chunks.insert(0, [{
411 435 'old_lineno': '',
412 436 'new_lineno': '',
413 'action': 'binary',
437 'action': 'context',
414 438 'line': msg,
415 } for msg in msgs])
439 } for _op, msg in stats['ops'].iteritems()
440 if _op not in [MOD_FILENODE]])
416 441
417 442 _files.append({
418 443 'filename': head['b_path'],
419 444 'old_revision': head['a_blob_id'],
420 445 'new_revision': head['b_blob_id'],
421 446 'chunks': chunks,
422 447 'operation': op,
423 448 'stats': stats,
424 449 })
425 450
426 451 sorter = lambda info: {'A': 0, 'M': 1, 'D': 2}.get(info['operation'])
427 452
428 453 if not inline_diff:
429 454 return diff_container(sorted(_files, key=sorter))
430 455
431 456 # highlight inline changes
432 457 for diff_data in _files:
433 458 for chunk in diff_data['chunks']:
434 459 lineiter = iter(chunk)
435 460 try:
436 461 while 1:
437 462 line = lineiter.next()
438 463 if line['action'] not in ['unmod', 'context']:
439 464 nextline = lineiter.next()
440 465 if nextline['action'] in ['unmod', 'context'] or \
441 466 nextline['action'] == line['action']:
442 467 continue
443 468 self.differ(line, nextline)
444 469 except StopIteration:
445 470 pass
446 471
447 472 return diff_container(sorted(_files, key=sorter))
448 473
449 474 def _parse_udiff(self, inline_diff=True):
450 475 raise NotImplementedError()
451 476
452 477 def _parse_lines(self, diff):
453 478 """
454 479 Parse the diff an return data for the template.
455 480 """
456 481
457 482 lineiter = iter(diff)
458 483 stats = [0, 0]
459 484
460 485 try:
461 486 chunks = []
462 487 line = lineiter.next()
463 488
464 489 while line:
465 490 lines = []
466 491 chunks.append(lines)
467 492
468 493 match = self._chunk_re.match(line)
469 494
470 495 if not match:
471 496 break
472 497
473 498 gr = match.groups()
474 499 (old_line, old_end,
475 500 new_line, new_end) = [int(x or 1) for x in gr[:-1]]
476 501 old_line -= 1
477 502 new_line -= 1
478 503
479 504 context = len(gr) == 5
480 505 old_end += old_line
481 506 new_end += new_line
482 507
483 508 if context:
484 509 # skip context only if it's first line
485 510 if int(gr[0]) > 1:
486 511 lines.append({
487 512 'old_lineno': '...',
488 513 'new_lineno': '...',
489 514 'action': 'context',
490 515 'line': line,
491 516 })
492 517
493 518 line = lineiter.next()
494 519
495 520 while old_line < old_end or new_line < new_end:
496 521 command = ' '
497 522 if line:
498 523 command = line[0]
499 524
500 525 affects_old = affects_new = False
501 526
502 527 # ignore those if we don't expect them
503 528 if command in '#@':
504 529 continue
505 530 elif command == '+':
506 531 affects_new = True
507 532 action = 'add'
508 533 stats[0] += 1
509 534 elif command == '-':
510 535 affects_old = True
511 536 action = 'del'
512 537 stats[1] += 1
513 538 else:
514 539 affects_old = affects_new = True
515 540 action = 'unmod'
516 541
517 542 if not self._newline_marker.match(line):
518 543 old_line += affects_old
519 544 new_line += affects_new
520 545 lines.append({
521 546 'old_lineno': affects_old and old_line or '',
522 547 'new_lineno': affects_new and new_line or '',
523 548 'action': action,
524 549 'line': self._clean_line(line, command)
525 550 })
526 551
527 552 line = lineiter.next()
528 553
529 554 if self._newline_marker.match(line):
530 555 # we need to append to lines, since this is not
531 556 # counted in the line specs of diff
532 557 lines.append({
533 558 'old_lineno': '...',
534 559 'new_lineno': '...',
535 560 'action': 'context',
536 561 'line': self._clean_line(line, command)
537 562 })
538 563
539 564 except StopIteration:
540 565 pass
541 566 return chunks, stats
542 567
543 568 def _safe_id(self, idstring):
544 569 """Make a string safe for including in an id attribute.
545 570
546 571 The HTML spec says that id attributes 'must begin with
547 572 a letter ([A-Za-z]) and may be followed by any number
548 573 of letters, digits ([0-9]), hyphens ("-"), underscores
549 574 ("_"), colons (":"), and periods (".")'. These regexps
550 575 are slightly over-zealous, in that they remove colons
551 576 and periods unnecessarily.
552 577
553 578 Whitespace is transformed into underscores, and then
554 579 anything which is not a hyphen or a character that
555 580 matches \w (alphanumerics and underscore) is removed.
556 581
557 582 """
558 583 # Transform all whitespace to underscore
559 584 idstring = re.sub(r'\s', "_", '%s' % idstring)
560 585 # Remove everything that is not a hyphen or a member of \w
561 586 idstring = re.sub(r'(?!-)\W', "", idstring).lower()
562 587 return idstring
563 588
564 589 def prepare(self, inline_diff=True):
565 590 """
566 591 Prepare the passed udiff for HTML rendering. It'l return a list
567 592 of dicts with diff information
568 593 """
569 594 parsed = self._parser(inline_diff=inline_diff)
570 595 self.parsed = True
571 596 self.parsed_diff = parsed
572 597 return parsed
573 598
574 599 def as_raw(self, diff_lines=None):
575 600 """
576 601 Returns raw string diff
577 602 """
578 603 return self._diff
579 604 #return u''.join(imap(self._line_counter, self._diff.splitlines(1)))
580 605
581 606 def as_html(self, table_class='code-difftable', line_class='line',
582 607 old_lineno_class='lineno old', new_lineno_class='lineno new',
583 608 code_class='code', enable_comments=False, parsed_lines=None):
584 609 """
585 610 Return given diff as html table with customized css classes
586 611 """
587 612 def _link_to_if(condition, label, url):
588 613 """
589 614 Generates a link if condition is meet or just the label if not.
590 615 """
591 616
592 617 if condition:
593 618 return '''<a href="%(url)s">%(label)s</a>''' % {
594 619 'url': url,
595 620 'label': label
596 621 }
597 622 else:
598 623 return label
599 624 if not self.parsed:
600 625 self.prepare()
601 626
602 627 diff_lines = self.parsed_diff
603 628 if parsed_lines:
604 629 diff_lines = parsed_lines
605 630
606 631 _html_empty = True
607 632 _html = []
608 633 _html.append('''<table class="%(table_class)s">\n''' % {
609 634 'table_class': table_class
610 635 })
611 636
612 637 for diff in diff_lines:
613 638 for line in diff['chunks']:
614 639 _html_empty = False
615 640 for change in line:
616 641 _html.append('''<tr class="%(lc)s %(action)s">\n''' % {
617 642 'lc': line_class,
618 643 'action': change['action']
619 644 })
620 645 anchor_old_id = ''
621 646 anchor_new_id = ''
622 647 anchor_old = "%(filename)s_o%(oldline_no)s" % {
623 648 'filename': self._safe_id(diff['filename']),
624 649 'oldline_no': change['old_lineno']
625 650 }
626 651 anchor_new = "%(filename)s_n%(oldline_no)s" % {
627 652 'filename': self._safe_id(diff['filename']),
628 653 'oldline_no': change['new_lineno']
629 654 }
630 655 cond_old = (change['old_lineno'] != '...' and
631 656 change['old_lineno'])
632 657 cond_new = (change['new_lineno'] != '...' and
633 658 change['new_lineno'])
634 659 if cond_old:
635 660 anchor_old_id = 'id="%s"' % anchor_old
636 661 if cond_new:
637 662 anchor_new_id = 'id="%s"' % anchor_new
638 663 ###########################################################
639 664 # OLD LINE NUMBER
640 665 ###########################################################
641 666 _html.append('''\t<td %(a_id)s class="%(olc)s">''' % {
642 667 'a_id': anchor_old_id,
643 668 'olc': old_lineno_class
644 669 })
645 670
646 671 _html.append('''%(link)s''' % {
647 672 'link': _link_to_if(True, change['old_lineno'],
648 673 '#%s' % anchor_old)
649 674 })
650 675 _html.append('''</td>\n''')
651 676 ###########################################################
652 677 # NEW LINE NUMBER
653 678 ###########################################################
654 679
655 680 _html.append('''\t<td %(a_id)s class="%(nlc)s">''' % {
656 681 'a_id': anchor_new_id,
657 682 'nlc': new_lineno_class
658 683 })
659 684
660 685 _html.append('''%(link)s''' % {
661 686 'link': _link_to_if(True, change['new_lineno'],
662 687 '#%s' % anchor_new)
663 688 })
664 689 _html.append('''</td>\n''')
665 690 ###########################################################
666 691 # CODE
667 692 ###########################################################
668 693 comments = '' if enable_comments else 'no-comment'
669 694 _html.append('''\t<td class="%(cc)s %(inc)s">''' % {
670 695 'cc': code_class,
671 696 'inc': comments
672 697 })
673 698 _html.append('''\n\t\t<pre>%(code)s</pre>\n''' % {
674 699 'code': change['line']
675 700 })
676 701
677 702 _html.append('''\t</td>''')
678 703 _html.append('''\n</tr>\n''')
679 704 _html.append('''</table>''')
680 705 if _html_empty:
681 706 return None
682 707 return ''.join(_html)
683 708
684 709 def stat(self):
685 710 """
686 711 Returns tuple of added, and removed lines for this instance
687 712 """
688 713 return self.adds, self.removes
@@ -1,1343 +1,1371 b''
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 import urllib
10 10 import math
11 11 import logging
12 12 import re
13 13 import urlparse
14 14 import textwrap
15 15
16 16 from datetime import datetime
17 17 from pygments.formatters.html import HtmlFormatter
18 18 from pygments import highlight as code_highlight
19 19 from pylons import url, request, config
20 20 from pylons.i18n.translation import _, ungettext
21 21 from hashlib import md5
22 22
23 23 from webhelpers.html import literal, HTML, escape
24 24 from webhelpers.html.tools import *
25 25 from webhelpers.html.builder import make_tag
26 26 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
27 27 end_form, file, form, hidden, image, javascript_link, link_to, \
28 28 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
29 29 submit, text, password, textarea, title, ul, xml_declaration, radio
30 30 from webhelpers.html.tools import auto_link, button_to, highlight, \
31 31 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
32 32 from webhelpers.number import format_byte_size, format_bit_size
33 33 from webhelpers.pylonslib import Flash as _Flash
34 34 from webhelpers.pylonslib.secure_form import secure_form
35 35 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
36 36 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
37 37 replace_whitespace, urlify, truncate, wrap_paragraphs
38 38 from webhelpers.date import time_ago_in_words
39 39 from webhelpers.paginate import Page as _Page
40 40 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
41 41 convert_boolean_attrs, NotGiven, _make_safe_id_component
42 42
43 43 from rhodecode.lib.annotate import annotate_highlight
44 44 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
45 45 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
46 46 get_changeset_safe, datetime_to_time, time_to_datetime, AttributeDict,\
47 47 safe_int
48 48 from rhodecode.lib.markup_renderer import MarkupRenderer
49 49 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
50 50 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyChangeset
51 51 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
52 52 from rhodecode.model.changeset_status import ChangesetStatusModel
53 53 from rhodecode.model.db import URL_SEP, Permission
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 html_escape_table = {
59 59 "&": "&amp;",
60 60 '"': "&quot;",
61 61 "'": "&apos;",
62 62 ">": "&gt;",
63 63 "<": "&lt;",
64 64 }
65 65
66 66
67 67 def html_escape(text):
68 68 """Produce entities within text."""
69 69 return "".join(html_escape_table.get(c, c) for c in text)
70 70
71 71
72 72 def shorter(text, size=20):
73 73 postfix = '...'
74 74 if len(text) > size:
75 75 return text[:size - len(postfix)] + postfix
76 76 return text
77 77
78 78
79 79 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
80 80 """
81 81 Reset button
82 82 """
83 83 _set_input_attrs(attrs, type, name, value)
84 84 _set_id_attr(attrs, id, name)
85 85 convert_boolean_attrs(attrs, ["disabled"])
86 86 return HTML.input(**attrs)
87 87
88 88 reset = _reset
89 89 safeid = _make_safe_id_component
90 90
91 91
92 92 def FID(raw_id, path):
93 93 """
94 94 Creates a uniqe ID for filenode based on it's hash of path and revision
95 95 it's safe to use in urls
96 96
97 97 :param raw_id:
98 98 :param path:
99 99 """
100 100
101 101 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
102 102
103 103
104 104 def get_token():
105 105 """Return the current authentication token, creating one if one doesn't
106 106 already exist.
107 107 """
108 108 token_key = "_authentication_token"
109 109 from pylons import session
110 110 if not token_key in session:
111 111 try:
112 112 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
113 113 except AttributeError: # Python < 2.4
114 114 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
115 115 session[token_key] = token
116 116 if hasattr(session, 'save'):
117 117 session.save()
118 118 return session[token_key]
119 119
120 120
121 121 class _GetError(object):
122 122 """Get error from form_errors, and represent it as span wrapped error
123 123 message
124 124
125 125 :param field_name: field to fetch errors for
126 126 :param form_errors: form errors dict
127 127 """
128 128
129 129 def __call__(self, field_name, form_errors):
130 130 tmpl = """<span class="error_msg">%s</span>"""
131 131 if form_errors and field_name in form_errors:
132 132 return literal(tmpl % form_errors.get(field_name))
133 133
134 134 get_error = _GetError()
135 135
136 136
137 137 class _ToolTip(object):
138 138
139 139 def __call__(self, tooltip_title, trim_at=50):
140 140 """
141 141 Special function just to wrap our text into nice formatted
142 142 autowrapped text
143 143
144 144 :param tooltip_title:
145 145 """
146 146 tooltip_title = escape(tooltip_title)
147 147 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
148 148 return tooltip_title
149 149 tooltip = _ToolTip()
150 150
151 151
152 152 class _FilesBreadCrumbs(object):
153 153
154 154 def __call__(self, repo_name, rev, paths):
155 155 if isinstance(paths, str):
156 156 paths = safe_unicode(paths)
157 157 url_l = [link_to(repo_name, url('files_home',
158 158 repo_name=repo_name,
159 159 revision=rev, f_path=''),
160 160 class_='ypjax-link')]
161 161 paths_l = paths.split('/')
162 162 for cnt, p in enumerate(paths_l):
163 163 if p != '':
164 164 url_l.append(link_to(p,
165 165 url('files_home',
166 166 repo_name=repo_name,
167 167 revision=rev,
168 168 f_path='/'.join(paths_l[:cnt + 1])
169 169 ),
170 170 class_='ypjax-link'
171 171 )
172 172 )
173 173
174 174 return literal('/'.join(url_l))
175 175
176 176 files_breadcrumbs = _FilesBreadCrumbs()
177 177
178 178
179 179 class CodeHtmlFormatter(HtmlFormatter):
180 180 """
181 181 My code Html Formatter for source codes
182 182 """
183 183
184 184 def wrap(self, source, outfile):
185 185 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
186 186
187 187 def _wrap_code(self, source):
188 188 for cnt, it in enumerate(source):
189 189 i, t = it
190 190 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
191 191 yield i, t
192 192
193 193 def _wrap_tablelinenos(self, inner):
194 194 dummyoutfile = StringIO.StringIO()
195 195 lncount = 0
196 196 for t, line in inner:
197 197 if t:
198 198 lncount += 1
199 199 dummyoutfile.write(line)
200 200
201 201 fl = self.linenostart
202 202 mw = len(str(lncount + fl - 1))
203 203 sp = self.linenospecial
204 204 st = self.linenostep
205 205 la = self.lineanchors
206 206 aln = self.anchorlinenos
207 207 nocls = self.noclasses
208 208 if sp:
209 209 lines = []
210 210
211 211 for i in range(fl, fl + lncount):
212 212 if i % st == 0:
213 213 if i % sp == 0:
214 214 if aln:
215 215 lines.append('<a href="#%s%d" class="special">%*d</a>' %
216 216 (la, i, mw, i))
217 217 else:
218 218 lines.append('<span class="special">%*d</span>' % (mw, i))
219 219 else:
220 220 if aln:
221 221 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
222 222 else:
223 223 lines.append('%*d' % (mw, i))
224 224 else:
225 225 lines.append('')
226 226 ls = '\n'.join(lines)
227 227 else:
228 228 lines = []
229 229 for i in range(fl, fl + lncount):
230 230 if i % st == 0:
231 231 if aln:
232 232 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
233 233 else:
234 234 lines.append('%*d' % (mw, i))
235 235 else:
236 236 lines.append('')
237 237 ls = '\n'.join(lines)
238 238
239 239 # in case you wonder about the seemingly redundant <div> here: since the
240 240 # content in the other cell also is wrapped in a div, some browsers in
241 241 # some configurations seem to mess up the formatting...
242 242 if nocls:
243 243 yield 0, ('<table class="%stable">' % self.cssclass +
244 244 '<tr><td><div class="linenodiv" '
245 245 'style="background-color: #f0f0f0; padding-right: 10px">'
246 246 '<pre style="line-height: 125%">' +
247 247 ls + '</pre></div></td><td id="hlcode" class="code">')
248 248 else:
249 249 yield 0, ('<table class="%stable">' % self.cssclass +
250 250 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
251 251 ls + '</pre></div></td><td id="hlcode" class="code">')
252 252 yield 0, dummyoutfile.getvalue()
253 253 yield 0, '</td></tr></table>'
254 254
255 255
256 256 def pygmentize(filenode, **kwargs):
257 257 """
258 258 pygmentize function using pygments
259 259
260 260 :param filenode:
261 261 """
262 262 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
263 263 return literal(code_highlight(filenode.content, lexer,
264 264 CodeHtmlFormatter(**kwargs)))
265 265
266 266
267 267 def pygmentize_annotation(repo_name, filenode, **kwargs):
268 268 """
269 269 pygmentize function for annotation
270 270
271 271 :param filenode:
272 272 """
273 273
274 274 color_dict = {}
275 275
276 276 def gen_color(n=10000):
277 277 """generator for getting n of evenly distributed colors using
278 278 hsv color and golden ratio. It always return same order of colors
279 279
280 280 :returns: RGB tuple
281 281 """
282 282
283 283 def hsv_to_rgb(h, s, v):
284 284 if s == 0.0:
285 285 return v, v, v
286 286 i = int(h * 6.0) # XXX assume int() truncates!
287 287 f = (h * 6.0) - i
288 288 p = v * (1.0 - s)
289 289 q = v * (1.0 - s * f)
290 290 t = v * (1.0 - s * (1.0 - f))
291 291 i = i % 6
292 292 if i == 0:
293 293 return v, t, p
294 294 if i == 1:
295 295 return q, v, p
296 296 if i == 2:
297 297 return p, v, t
298 298 if i == 3:
299 299 return p, q, v
300 300 if i == 4:
301 301 return t, p, v
302 302 if i == 5:
303 303 return v, p, q
304 304
305 305 golden_ratio = 0.618033988749895
306 306 h = 0.22717784590367374
307 307
308 308 for _ in xrange(n):
309 309 h += golden_ratio
310 310 h %= 1
311 311 HSV_tuple = [h, 0.95, 0.95]
312 312 RGB_tuple = hsv_to_rgb(*HSV_tuple)
313 313 yield map(lambda x: str(int(x * 256)), RGB_tuple)
314 314
315 315 cgenerator = gen_color()
316 316
317 317 def get_color_string(cs):
318 318 if cs in color_dict:
319 319 col = color_dict[cs]
320 320 else:
321 321 col = color_dict[cs] = cgenerator.next()
322 322 return "color: rgb(%s)! important;" % (', '.join(col))
323 323
324 324 def url_func(repo_name):
325 325
326 326 def _url_func(changeset):
327 327 author = changeset.author
328 328 date = changeset.date
329 329 message = tooltip(changeset.message)
330 330
331 331 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
332 332 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
333 333 "</b> %s<br/></div>")
334 334
335 335 tooltip_html = tooltip_html % (author, date, message)
336 336 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
337 337 short_id(changeset.raw_id))
338 338 uri = link_to(
339 339 lnk_format,
340 340 url('changeset_home', repo_name=repo_name,
341 341 revision=changeset.raw_id),
342 342 style=get_color_string(changeset.raw_id),
343 343 class_='tooltip',
344 344 title=tooltip_html
345 345 )
346 346
347 347 uri += '\n'
348 348 return uri
349 349 return _url_func
350 350
351 351 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
352 352
353 353
354 354 def is_following_repo(repo_name, user_id):
355 355 from rhodecode.model.scm import ScmModel
356 356 return ScmModel().is_following_repo(repo_name, user_id)
357 357
358 358 flash = _Flash()
359 359
360 360 #==============================================================================
361 361 # SCM FILTERS available via h.
362 362 #==============================================================================
363 363 from rhodecode.lib.vcs.utils import author_name, author_email
364 364 from rhodecode.lib.utils2 import credentials_filter, age as _age
365 365 from rhodecode.model.db import User, ChangesetStatus
366 366
367 367 age = lambda x, y=False: _age(x, y)
368 368 capitalize = lambda x: x.capitalize()
369 369 email = author_email
370 370 short_id = lambda x: x[:12]
371 371 hide_credentials = lambda x: ''.join(credentials_filter(x))
372 372
373 373
374 374 def show_id(cs):
375 375 """
376 376 Configurable function that shows ID
377 377 by default it's r123:fffeeefffeee
378 378
379 379 :param cs: changeset instance
380 380 """
381 381 from rhodecode import CONFIG
382 382 def_len = safe_int(CONFIG.get('show_sha_length', 12))
383 383 show_rev = str2bool(CONFIG.get('show_revision_number', True))
384 384
385 385 raw_id = cs.raw_id[:def_len]
386 386 if show_rev:
387 387 return 'r%s:%s' % (cs.revision, raw_id)
388 388 else:
389 389 return '%s' % (raw_id)
390 390
391 391
392 392 def fmt_date(date):
393 393 if date:
394 394 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
395 395 return date.strftime(_fmt).decode('utf8')
396 396
397 397 return ""
398 398
399 399
400 400 def is_git(repository):
401 401 if hasattr(repository, 'alias'):
402 402 _type = repository.alias
403 403 elif hasattr(repository, 'repo_type'):
404 404 _type = repository.repo_type
405 405 else:
406 406 _type = repository
407 407 return _type == 'git'
408 408
409 409
410 410 def is_hg(repository):
411 411 if hasattr(repository, 'alias'):
412 412 _type = repository.alias
413 413 elif hasattr(repository, 'repo_type'):
414 414 _type = repository.repo_type
415 415 else:
416 416 _type = repository
417 417 return _type == 'hg'
418 418
419 419
420 420 def email_or_none(author):
421 421 # extract email from the commit string
422 422 _email = email(author)
423 423 if _email != '':
424 424 # check it against RhodeCode database, and use the MAIN email for this
425 425 # user
426 426 user = User.get_by_email(_email, case_insensitive=True, cache=True)
427 427 if user is not None:
428 428 return user.email
429 429 return _email
430 430
431 431 # See if it contains a username we can get an email from
432 432 user = User.get_by_username(author_name(author), case_insensitive=True,
433 433 cache=True)
434 434 if user is not None:
435 435 return user.email
436 436
437 437 # No valid email, not a valid user in the system, none!
438 438 return None
439 439
440 440
441 441 def person(author, show_attr="username_and_name"):
442 442 # attr to return from fetched user
443 443 person_getter = lambda usr: getattr(usr, show_attr)
444 444
445 445 # Valid email in the attribute passed, see if they're in the system
446 446 _email = email(author)
447 447 if _email != '':
448 448 user = User.get_by_email(_email, case_insensitive=True, cache=True)
449 449 if user is not None:
450 450 return person_getter(user)
451 451
452 452 # Maybe it's a username?
453 453 _author = author_name(author)
454 454 user = User.get_by_username(_author, case_insensitive=True,
455 455 cache=True)
456 456 if user is not None:
457 457 return person_getter(user)
458 458
459 459 # Still nothing? Just pass back the author name if any, else the email
460 460 return _author or _email
461 461
462 462
463 463 def person_by_id(id_, show_attr="username_and_name"):
464 464 # attr to return from fetched user
465 465 person_getter = lambda usr: getattr(usr, show_attr)
466 466
467 467 #maybe it's an ID ?
468 468 if str(id_).isdigit() or isinstance(id_, int):
469 469 id_ = int(id_)
470 470 user = User.get(id_)
471 471 if user is not None:
472 472 return person_getter(user)
473 473 return id_
474 474
475 475
476 476 def desc_stylize(value):
477 477 """
478 478 converts tags from value into html equivalent
479 479
480 480 :param value:
481 481 """
482 482 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
483 483 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
484 484 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
485 485 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
486 486 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
487 487 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
488 488 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
489 489 '<div class="metatag" tag="lang">\\2</div>', value)
490 490 value = re.sub(r'\[([a-z]+)\]',
491 491 '<div class="metatag" tag="\\1">\\1</div>', value)
492 492
493 493 return value
494 494
495 495
496 496 def boolicon(value):
497 497 """Returns boolean value of a value, represented as small html image of true/false
498 498 icons
499 499
500 500 :param value: value
501 501 """
502 502
503 503 if value:
504 504 return HTML.tag('img', src=url("/images/icons/accept.png"),
505 505 alt=_('True'))
506 506 else:
507 507 return HTML.tag('img', src=url("/images/icons/cancel.png"),
508 508 alt=_('False'))
509 509
510 510
511 511 def action_parser(user_log, feed=False, parse_cs=False):
512 512 """
513 513 This helper will action_map the specified string action into translated
514 514 fancy names with icons and links
515 515
516 516 :param user_log: user log instance
517 517 :param feed: use output for feeds (no html and fancy icons)
518 518 :param parse_cs: parse Changesets into VCS instances
519 519 """
520 520
521 521 action = user_log.action
522 522 action_params = ' '
523 523
524 524 x = action.split(':')
525 525
526 526 if len(x) > 1:
527 527 action, action_params = x
528 528
529 529 def get_cs_links():
530 530 revs_limit = 3 # display this amount always
531 531 revs_top_limit = 50 # show upto this amount of changesets hidden
532 532 revs_ids = action_params.split(',')
533 533 deleted = user_log.repository is None
534 534 if deleted:
535 535 return ','.join(revs_ids)
536 536
537 537 repo_name = user_log.repository.repo_name
538 538
539 539 def lnk(rev, repo_name):
540 540 if isinstance(rev, BaseChangeset) or isinstance(rev, AttributeDict):
541 541 lazy_cs = True
542 542 if getattr(rev, 'op', None) and getattr(rev, 'ref_name', None):
543 543 lazy_cs = False
544 544 lbl = '?'
545 545 if rev.op == 'delete_branch':
546 546 lbl = '%s' % _('Deleted branch: %s') % rev.ref_name
547 547 title = ''
548 548 elif rev.op == 'tag':
549 549 lbl = '%s' % _('Created tag: %s') % rev.ref_name
550 550 title = ''
551 551 _url = '#'
552 552
553 553 else:
554 554 lbl = '%s' % (rev.short_id[:8])
555 555 _url = url('changeset_home', repo_name=repo_name,
556 556 revision=rev.raw_id)
557 557 title = tooltip(rev.message)
558 558 else:
559 559 ## changeset cannot be found/striped/removed etc.
560 560 lbl = ('%s' % rev)[:12]
561 561 _url = '#'
562 562 title = _('Changeset not found')
563 563 if parse_cs:
564 564 return link_to(lbl, _url, title=title, class_='tooltip')
565 565 return link_to(lbl, _url, raw_id=rev.raw_id, repo_name=repo_name,
566 566 class_='lazy-cs' if lazy_cs else '')
567 567
568 568 def _get_op(rev_txt):
569 569 _op = None
570 570 _name = rev_txt
571 571 if len(rev_txt.split('=>')) == 2:
572 572 _op, _name = rev_txt.split('=>')
573 573 return _op, _name
574 574
575 575 revs = []
576 576 if len(filter(lambda v: v != '', revs_ids)) > 0:
577 577 repo = None
578 578 for rev in revs_ids[:revs_top_limit]:
579 579 _op, _name = _get_op(rev)
580 580
581 581 # we want parsed changesets, or new log store format is bad
582 582 if parse_cs:
583 583 try:
584 584 if repo is None:
585 585 repo = user_log.repository.scm_instance
586 586 _rev = repo.get_changeset(rev)
587 587 revs.append(_rev)
588 588 except ChangesetDoesNotExistError:
589 589 log.error('cannot find revision %s in this repo' % rev)
590 590 revs.append(rev)
591 591 continue
592 592 else:
593 593 _rev = AttributeDict({
594 594 'short_id': rev[:12],
595 595 'raw_id': rev,
596 596 'message': '',
597 597 'op': _op,
598 598 'ref_name': _name
599 599 })
600 600 revs.append(_rev)
601 601 cs_links = []
602 602 cs_links.append(" " + ', '.join(
603 603 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
604 604 )
605 605 )
606 606 _op1, _name1 = _get_op(revs_ids[0])
607 607 _op2, _name2 = _get_op(revs_ids[-1])
608 608
609 609 _rev = '%s...%s' % (_name1, _name2)
610 610
611 611 compare_view = (
612 612 ' <div class="compare_view tooltip" title="%s">'
613 613 '<a href="%s">%s</a> </div>' % (
614 614 _('Show all combined changesets %s->%s') % (
615 615 revs_ids[0][:12], revs_ids[-1][:12]
616 616 ),
617 617 url('changeset_home', repo_name=repo_name,
618 618 revision=_rev
619 619 ),
620 620 _('compare view')
621 621 )
622 622 )
623 623
624 624 # if we have exactly one more than normally displayed
625 625 # just display it, takes less space than displaying
626 626 # "and 1 more revisions"
627 627 if len(revs_ids) == revs_limit + 1:
628 628 rev = revs[revs_limit]
629 629 cs_links.append(", " + lnk(rev, repo_name))
630 630
631 631 # hidden-by-default ones
632 632 if len(revs_ids) > revs_limit + 1:
633 633 uniq_id = revs_ids[0]
634 634 html_tmpl = (
635 635 '<span> %s <a class="show_more" id="_%s" '
636 636 'href="#more">%s</a> %s</span>'
637 637 )
638 638 if not feed:
639 639 cs_links.append(html_tmpl % (
640 640 _('and'),
641 641 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
642 642 _('revisions')
643 643 )
644 644 )
645 645
646 646 if not feed:
647 647 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
648 648 else:
649 649 html_tmpl = '<span id="%s"> %s </span>'
650 650
651 651 morelinks = ', '.join(
652 652 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
653 653 )
654 654
655 655 if len(revs_ids) > revs_top_limit:
656 656 morelinks += ', ...'
657 657
658 658 cs_links.append(html_tmpl % (uniq_id, morelinks))
659 659 if len(revs) > 1:
660 660 cs_links.append(compare_view)
661 661 return ''.join(cs_links)
662 662
663 663 def get_fork_name():
664 664 repo_name = action_params
665 665 _url = url('summary_home', repo_name=repo_name)
666 666 return _('fork name %s') % link_to(action_params, _url)
667 667
668 668 def get_user_name():
669 669 user_name = action_params
670 670 return user_name
671 671
672 672 def get_users_group():
673 673 group_name = action_params
674 674 return group_name
675 675
676 676 def get_pull_request():
677 677 pull_request_id = action_params
678 678 deleted = user_log.repository is None
679 679 if deleted:
680 680 repo_name = user_log.repository_name
681 681 else:
682 682 repo_name = user_log.repository.repo_name
683 683 return link_to(_('Pull request #%s') % pull_request_id,
684 684 url('pullrequest_show', repo_name=repo_name,
685 685 pull_request_id=pull_request_id))
686 686
687 687 # action : translated str, callback(extractor), icon
688 688 action_map = {
689 689 'user_deleted_repo': (_('[deleted] repository'),
690 690 None, 'database_delete.png'),
691 691 'user_created_repo': (_('[created] repository'),
692 692 None, 'database_add.png'),
693 693 'user_created_fork': (_('[created] repository as fork'),
694 694 None, 'arrow_divide.png'),
695 695 'user_forked_repo': (_('[forked] repository'),
696 696 get_fork_name, 'arrow_divide.png'),
697 697 'user_updated_repo': (_('[updated] repository'),
698 698 None, 'database_edit.png'),
699 699 'admin_deleted_repo': (_('[delete] repository'),
700 700 None, 'database_delete.png'),
701 701 'admin_created_repo': (_('[created] repository'),
702 702 None, 'database_add.png'),
703 703 'admin_forked_repo': (_('[forked] repository'),
704 704 None, 'arrow_divide.png'),
705 705 'admin_updated_repo': (_('[updated] repository'),
706 706 None, 'database_edit.png'),
707 707 'admin_created_user': (_('[created] user'),
708 708 get_user_name, 'user_add.png'),
709 709 'admin_updated_user': (_('[updated] user'),
710 710 get_user_name, 'user_edit.png'),
711 711 'admin_created_users_group': (_('[created] user group'),
712 712 get_users_group, 'group_add.png'),
713 713 'admin_updated_users_group': (_('[updated] user group'),
714 714 get_users_group, 'group_edit.png'),
715 715 'user_commented_revision': (_('[commented] on revision in repository'),
716 716 get_cs_links, 'comment_add.png'),
717 717 'user_commented_pull_request': (_('[commented] on pull request for'),
718 718 get_pull_request, 'comment_add.png'),
719 719 'user_closed_pull_request': (_('[closed] pull request for'),
720 720 get_pull_request, 'tick.png'),
721 721 'push': (_('[pushed] into'),
722 722 get_cs_links, 'script_add.png'),
723 723 'push_local': (_('[committed via RhodeCode] into repository'),
724 724 get_cs_links, 'script_edit.png'),
725 725 'push_remote': (_('[pulled from remote] into repository'),
726 726 get_cs_links, 'connect.png'),
727 727 'pull': (_('[pulled] from'),
728 728 None, 'down_16.png'),
729 729 'started_following_repo': (_('[started following] repository'),
730 730 None, 'heart_add.png'),
731 731 'stopped_following_repo': (_('[stopped following] repository'),
732 732 None, 'heart_delete.png'),
733 733 }
734 734
735 735 action_str = action_map.get(action, action)
736 736 if feed:
737 737 action = action_str[0].replace('[', '').replace(']', '')
738 738 else:
739 739 action = action_str[0]\
740 740 .replace('[', '<span class="journal_highlight">')\
741 741 .replace(']', '</span>')
742 742
743 743 action_params_func = lambda: ""
744 744
745 745 if callable(action_str[1]):
746 746 action_params_func = action_str[1]
747 747
748 748 def action_parser_icon():
749 749 action = user_log.action
750 750 action_params = None
751 751 x = action.split(':')
752 752
753 753 if len(x) > 1:
754 754 action, action_params = x
755 755
756 756 tmpl = """<img src="%s%s" alt="%s"/>"""
757 757 ico = action_map.get(action, ['', '', ''])[2]
758 758 return literal(tmpl % ((url('/images/icons/')), ico, action))
759 759
760 760 # returned callbacks we need to call to get
761 761 return [lambda: literal(action), action_params_func, action_parser_icon]
762 762
763 763
764 764
765 765 #==============================================================================
766 766 # PERMS
767 767 #==============================================================================
768 768 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
769 769 HasRepoPermissionAny, HasRepoPermissionAll, HasReposGroupPermissionAll, \
770 770 HasReposGroupPermissionAny
771 771
772 772
773 773 #==============================================================================
774 774 # GRAVATAR URL
775 775 #==============================================================================
776 776
777 777 def gravatar_url(email_address, size=30):
778 778 from pylons import url # doh, we need to re-import url to mock it later
779 779 _def = 'anonymous@rhodecode.org'
780 780 use_gravatar = str2bool(config['app_conf'].get('use_gravatar'))
781 781 email_address = email_address or _def
782 782 if (not use_gravatar or not email_address or email_address == _def):
783 783 f = lambda a, l: min(l, key=lambda x: abs(x - a))
784 784 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
785 785
786 786 if use_gravatar and config['app_conf'].get('alternative_gravatar_url'):
787 787 tmpl = config['app_conf'].get('alternative_gravatar_url', '')
788 788 parsed_url = urlparse.urlparse(url.current(qualified=True))
789 789 tmpl = tmpl.replace('{email}', email_address)\
790 790 .replace('{md5email}', hashlib.md5(email_address.lower()).hexdigest()) \
791 791 .replace('{netloc}', parsed_url.netloc)\
792 792 .replace('{scheme}', parsed_url.scheme)\
793 793 .replace('{size}', str(size))
794 794 return tmpl
795 795
796 796 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
797 797 default = 'identicon'
798 798 baseurl_nossl = "http://www.gravatar.com/avatar/"
799 799 baseurl_ssl = "https://secure.gravatar.com/avatar/"
800 800 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
801 801
802 802 if isinstance(email_address, unicode):
803 803 #hashlib crashes on unicode items
804 804 email_address = safe_str(email_address)
805 805 # construct the url
806 806 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
807 807 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
808 808
809 809 return gravatar_url
810 810
811 811
812 812 class Page(_Page):
813 813 """
814 814 Custom pager to match rendering style with YUI paginator
815 815 """
816 816
817 817 def _get_pos(self, cur_page, max_page, items):
818 818 edge = (items / 2) + 1
819 819 if (cur_page <= edge):
820 820 radius = max(items / 2, items - cur_page)
821 821 elif (max_page - cur_page) < edge:
822 822 radius = (items - 1) - (max_page - cur_page)
823 823 else:
824 824 radius = items / 2
825 825
826 826 left = max(1, (cur_page - (radius)))
827 827 right = min(max_page, cur_page + (radius))
828 828 return left, cur_page, right
829 829
830 830 def _range(self, regexp_match):
831 831 """
832 832 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
833 833
834 834 Arguments:
835 835
836 836 regexp_match
837 837 A "re" (regular expressions) match object containing the
838 838 radius of linked pages around the current page in
839 839 regexp_match.group(1) as a string
840 840
841 841 This function is supposed to be called as a callable in
842 842 re.sub.
843 843
844 844 """
845 845 radius = int(regexp_match.group(1))
846 846
847 847 # Compute the first and last page number within the radius
848 848 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
849 849 # -> leftmost_page = 5
850 850 # -> rightmost_page = 9
851 851 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
852 852 self.last_page,
853 853 (radius * 2) + 1)
854 854 nav_items = []
855 855
856 856 # Create a link to the first page (unless we are on the first page
857 857 # or there would be no need to insert '..' spacers)
858 858 if self.page != self.first_page and self.first_page < leftmost_page:
859 859 nav_items.append(self._pagerlink(self.first_page, self.first_page))
860 860
861 861 # Insert dots if there are pages between the first page
862 862 # and the currently displayed page range
863 863 if leftmost_page - self.first_page > 1:
864 864 # Wrap in a SPAN tag if nolink_attr is set
865 865 text = '..'
866 866 if self.dotdot_attr:
867 867 text = HTML.span(c=text, **self.dotdot_attr)
868 868 nav_items.append(text)
869 869
870 870 for thispage in xrange(leftmost_page, rightmost_page + 1):
871 871 # Hilight the current page number and do not use a link
872 872 if thispage == self.page:
873 873 text = '%s' % (thispage,)
874 874 # Wrap in a SPAN tag if nolink_attr is set
875 875 if self.curpage_attr:
876 876 text = HTML.span(c=text, **self.curpage_attr)
877 877 nav_items.append(text)
878 878 # Otherwise create just a link to that page
879 879 else:
880 880 text = '%s' % (thispage,)
881 881 nav_items.append(self._pagerlink(thispage, text))
882 882
883 883 # Insert dots if there are pages between the displayed
884 884 # page numbers and the end of the page range
885 885 if self.last_page - rightmost_page > 1:
886 886 text = '..'
887 887 # Wrap in a SPAN tag if nolink_attr is set
888 888 if self.dotdot_attr:
889 889 text = HTML.span(c=text, **self.dotdot_attr)
890 890 nav_items.append(text)
891 891
892 892 # Create a link to the very last page (unless we are on the last
893 893 # page or there would be no need to insert '..' spacers)
894 894 if self.page != self.last_page and rightmost_page < self.last_page:
895 895 nav_items.append(self._pagerlink(self.last_page, self.last_page))
896 896
897 897 return self.separator.join(nav_items)
898 898
899 899 def pager(self, format='~2~', page_param='page', partial_param='partial',
900 900 show_if_single_page=False, separator=' ', onclick=None,
901 901 symbol_first='<<', symbol_last='>>',
902 902 symbol_previous='<', symbol_next='>',
903 903 link_attr={'class': 'pager_link'},
904 904 curpage_attr={'class': 'pager_curpage'},
905 905 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
906 906
907 907 self.curpage_attr = curpage_attr
908 908 self.separator = separator
909 909 self.pager_kwargs = kwargs
910 910 self.page_param = page_param
911 911 self.partial_param = partial_param
912 912 self.onclick = onclick
913 913 self.link_attr = link_attr
914 914 self.dotdot_attr = dotdot_attr
915 915
916 916 # Don't show navigator if there is no more than one page
917 917 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
918 918 return ''
919 919
920 920 from string import Template
921 921 # Replace ~...~ in token format by range of pages
922 922 result = re.sub(r'~(\d+)~', self._range, format)
923 923
924 924 # Interpolate '%' variables
925 925 result = Template(result).safe_substitute({
926 926 'first_page': self.first_page,
927 927 'last_page': self.last_page,
928 928 'page': self.page,
929 929 'page_count': self.page_count,
930 930 'items_per_page': self.items_per_page,
931 931 'first_item': self.first_item,
932 932 'last_item': self.last_item,
933 933 'item_count': self.item_count,
934 934 'link_first': self.page > self.first_page and \
935 935 self._pagerlink(self.first_page, symbol_first) or '',
936 936 'link_last': self.page < self.last_page and \
937 937 self._pagerlink(self.last_page, symbol_last) or '',
938 938 'link_previous': self.previous_page and \
939 939 self._pagerlink(self.previous_page, symbol_previous) \
940 940 or HTML.span(symbol_previous, class_="yui-pg-previous"),
941 941 'link_next': self.next_page and \
942 942 self._pagerlink(self.next_page, symbol_next) \
943 943 or HTML.span(symbol_next, class_="yui-pg-next")
944 944 })
945 945
946 946 return literal(result)
947 947
948 948
949 949 #==============================================================================
950 950 # REPO PAGER, PAGER FOR REPOSITORY
951 951 #==============================================================================
952 952 class RepoPage(Page):
953 953
954 954 def __init__(self, collection, page=1, items_per_page=20,
955 955 item_count=None, url=None, **kwargs):
956 956
957 957 """Create a "RepoPage" instance. special pager for paging
958 958 repository
959 959 """
960 960 self._url_generator = url
961 961
962 962 # Safe the kwargs class-wide so they can be used in the pager() method
963 963 self.kwargs = kwargs
964 964
965 965 # Save a reference to the collection
966 966 self.original_collection = collection
967 967
968 968 self.collection = collection
969 969
970 970 # The self.page is the number of the current page.
971 971 # The first page has the number 1!
972 972 try:
973 973 self.page = int(page) # make it int() if we get it as a string
974 974 except (ValueError, TypeError):
975 975 self.page = 1
976 976
977 977 self.items_per_page = items_per_page
978 978
979 979 # Unless the user tells us how many items the collections has
980 980 # we calculate that ourselves.
981 981 if item_count is not None:
982 982 self.item_count = item_count
983 983 else:
984 984 self.item_count = len(self.collection)
985 985
986 986 # Compute the number of the first and last available page
987 987 if self.item_count > 0:
988 988 self.first_page = 1
989 989 self.page_count = int(math.ceil(float(self.item_count) /
990 990 self.items_per_page))
991 991 self.last_page = self.first_page + self.page_count - 1
992 992
993 993 # Make sure that the requested page number is the range of
994 994 # valid pages
995 995 if self.page > self.last_page:
996 996 self.page = self.last_page
997 997 elif self.page < self.first_page:
998 998 self.page = self.first_page
999 999
1000 1000 # Note: the number of items on this page can be less than
1001 1001 # items_per_page if the last page is not full
1002 1002 self.first_item = max(0, (self.item_count) - (self.page *
1003 1003 items_per_page))
1004 1004 self.last_item = ((self.item_count - 1) - items_per_page *
1005 1005 (self.page - 1))
1006 1006
1007 1007 self.items = list(self.collection[self.first_item:self.last_item + 1])
1008 1008
1009 1009 # Links to previous and next page
1010 1010 if self.page > self.first_page:
1011 1011 self.previous_page = self.page - 1
1012 1012 else:
1013 1013 self.previous_page = None
1014 1014
1015 1015 if self.page < self.last_page:
1016 1016 self.next_page = self.page + 1
1017 1017 else:
1018 1018 self.next_page = None
1019 1019
1020 1020 # No items available
1021 1021 else:
1022 1022 self.first_page = None
1023 1023 self.page_count = 0
1024 1024 self.last_page = None
1025 1025 self.first_item = None
1026 1026 self.last_item = None
1027 1027 self.previous_page = None
1028 1028 self.next_page = None
1029 1029 self.items = []
1030 1030
1031 1031 # This is a subclass of the 'list' type. Initialise the list now.
1032 1032 list.__init__(self, reversed(self.items))
1033 1033
1034 1034
1035 1035 def changed_tooltip(nodes):
1036 1036 """
1037 1037 Generates a html string for changed nodes in changeset page.
1038 1038 It limits the output to 30 entries
1039 1039
1040 1040 :param nodes: LazyNodesGenerator
1041 1041 """
1042 1042 if nodes:
1043 1043 pref = ': <br/> '
1044 1044 suf = ''
1045 1045 if len(nodes) > 30:
1046 1046 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
1047 1047 return literal(pref + '<br/> '.join([safe_unicode(x.path)
1048 1048 for x in nodes[:30]]) + suf)
1049 1049 else:
1050 1050 return ': ' + _('No Files')
1051 1051
1052 1052
1053 1053 def repo_link(groups_and_repos):
1054 1054 """
1055 1055 Makes a breadcrumbs link to repo within a group
1056 1056 joins &raquo; on each group to create a fancy link
1057 1057
1058 1058 ex::
1059 1059 group >> subgroup >> repo
1060 1060
1061 1061 :param groups_and_repos:
1062 1062 :param last_url:
1063 1063 """
1064 1064 groups, just_name, repo_name = groups_and_repos
1065 1065 last_url = url('summary_home', repo_name=repo_name)
1066 1066 last_link = link_to(just_name, last_url)
1067 1067
1068 1068 def make_link(group):
1069 1069 return link_to(group.name,
1070 1070 url('repos_group_home', group_name=group.group_name))
1071 1071 return literal(' &raquo; '.join(map(make_link, groups) + ['<span>%s</span>' % last_link]))
1072 1072
1073 1073
1074 1074 def fancy_file_stats(stats):
1075 1075 """
1076 1076 Displays a fancy two colored bar for number of added/deleted
1077 1077 lines of code on file
1078 1078
1079 1079 :param stats: two element list of added/deleted lines of code
1080 1080 """
1081 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
1082 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
1083
1081 1084 def cgen(l_type, a_v, d_v):
1082 1085 mapping = {'tr': 'top-right-rounded-corner-mid',
1083 1086 'tl': 'top-left-rounded-corner-mid',
1084 1087 'br': 'bottom-right-rounded-corner-mid',
1085 1088 'bl': 'bottom-left-rounded-corner-mid'}
1086 1089 map_getter = lambda x: mapping[x]
1087 1090
1088 1091 if l_type == 'a' and d_v:
1089 1092 #case when added and deleted are present
1090 1093 return ' '.join(map(map_getter, ['tl', 'bl']))
1091 1094
1092 1095 if l_type == 'a' and not d_v:
1093 1096 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1094 1097
1095 1098 if l_type == 'd' and a_v:
1096 1099 return ' '.join(map(map_getter, ['tr', 'br']))
1097 1100
1098 1101 if l_type == 'd' and not a_v:
1099 1102 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1100 1103
1101 a, d = stats[0], stats[1]
1104 a, d = stats['added'], stats['deleted']
1102 1105 width = 100
1103 1106
1104 if a == 'b':
1107 if stats['binary']:
1105 1108 #binary mode
1106 b_d = '<div class="bin%s %s" style="width:100%%">%s</div>' % (d, cgen('a', a_v='', d_v=0), 'bin')
1107 b_a = '<div class="bin1" style="width:0%%">%s</div>' % ('bin')
1109 lbl = ''
1110 bin_op = 1
1111
1112 if BIN_FILENODE in stats['ops']:
1113 lbl = 'bin+'
1114
1115 if NEW_FILENODE in stats['ops']:
1116 lbl += _('new file')
1117 bin_op = NEW_FILENODE
1118 elif MOD_FILENODE in stats['ops']:
1119 lbl += _('mod')
1120 bin_op = MOD_FILENODE
1121 elif DEL_FILENODE in stats['ops']:
1122 lbl += _('del')
1123 bin_op = DEL_FILENODE
1124 elif RENAMED_FILENODE in stats['ops']:
1125 lbl += _('rename')
1126 bin_op = RENAMED_FILENODE
1127
1128 #chmod can go with other operations
1129 if CHMOD_FILENODE in stats['ops']:
1130 _org_lbl = _('chmod')
1131 lbl += _org_lbl if lbl.endswith('+') else '+%s' % _org_lbl
1132
1133 #import ipdb;ipdb.set_trace()
1134 b_d = '<div class="bin bin%s %s" style="width:100%%">%s</div>' % (bin_op, cgen('a', a_v='', d_v=0), lbl)
1135 b_a = '<div class="bin bin1" style="width:0%%"></div>'
1108 1136 return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d))
1109 1137
1110 t = stats[0] + stats[1]
1138 t = stats['added'] + stats['deleted']
1111 1139 unit = float(width) / (t or 1)
1112 1140
1113 1141 # needs > 9% of width to be visible or 0 to be hidden
1114 1142 a_p = max(9, unit * a) if a > 0 else 0
1115 1143 d_p = max(9, unit * d) if d > 0 else 0
1116 1144 p_sum = a_p + d_p
1117 1145
1118 1146 if p_sum > width:
1119 1147 #adjust the percentage to be == 100% since we adjusted to 9
1120 1148 if a_p > d_p:
1121 1149 a_p = a_p - (p_sum - width)
1122 1150 else:
1123 1151 d_p = d_p - (p_sum - width)
1124 1152
1125 1153 a_v = a if a > 0 else ''
1126 1154 d_v = d if d > 0 else ''
1127 1155
1128 1156 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
1129 1157 cgen('a', a_v, d_v), a_p, a_v
1130 1158 )
1131 1159 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
1132 1160 cgen('d', a_v, d_v), d_p, d_v
1133 1161 )
1134 1162 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
1135 1163
1136 1164
1137 1165 def urlify_text(text_, safe=True):
1138 1166 """
1139 1167 Extrac urls from text and make html links out of them
1140 1168
1141 1169 :param text_:
1142 1170 """
1143 1171
1144 1172 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
1145 1173 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1146 1174
1147 1175 def url_func(match_obj):
1148 1176 url_full = match_obj.groups()[0]
1149 1177 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1150 1178 _newtext = url_pat.sub(url_func, text_)
1151 1179 if safe:
1152 1180 return literal(_newtext)
1153 1181 return _newtext
1154 1182
1155 1183
1156 1184 def urlify_changesets(text_, repository):
1157 1185 """
1158 1186 Extract revision ids from changeset and make link from them
1159 1187
1160 1188 :param text_:
1161 1189 :param repository: repo name to build the URL with
1162 1190 """
1163 1191 from pylons import url # doh, we need to re-import url to mock it later
1164 1192 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1165 1193
1166 1194 def url_func(match_obj):
1167 1195 rev = match_obj.groups()[1]
1168 1196 pref = match_obj.groups()[0]
1169 1197 suf = match_obj.groups()[2]
1170 1198
1171 1199 tmpl = (
1172 1200 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1173 1201 '%(rev)s</a>%(suf)s'
1174 1202 )
1175 1203 return tmpl % {
1176 1204 'pref': pref,
1177 1205 'cls': 'revision-link',
1178 1206 'url': url('changeset_home', repo_name=repository, revision=rev),
1179 1207 'rev': rev,
1180 1208 'suf': suf
1181 1209 }
1182 1210
1183 1211 newtext = URL_PAT.sub(url_func, text_)
1184 1212
1185 1213 return newtext
1186 1214
1187 1215
1188 1216 def urlify_commit(text_, repository=None, link_=None):
1189 1217 """
1190 1218 Parses given text message and makes proper links.
1191 1219 issues are linked to given issue-server, and rest is a changeset link
1192 1220 if link_ is given, in other case it's a plain text
1193 1221
1194 1222 :param text_:
1195 1223 :param repository:
1196 1224 :param link_: changeset link
1197 1225 """
1198 1226 import traceback
1199 1227 from pylons import url # doh, we need to re-import url to mock it later
1200 1228
1201 1229 def escaper(string):
1202 1230 return string.replace('<', '&lt;').replace('>', '&gt;')
1203 1231
1204 1232 def linkify_others(t, l):
1205 1233 urls = re.compile(r'(\<a.*?\<\/a\>)',)
1206 1234 links = []
1207 1235 for e in urls.split(t):
1208 1236 if not urls.match(e):
1209 1237 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
1210 1238 else:
1211 1239 links.append(e)
1212 1240
1213 1241 return ''.join(links)
1214 1242
1215 1243 # urlify changesets - extrac revisions and make link out of them
1216 1244 newtext = urlify_changesets(escaper(text_), repository)
1217 1245
1218 1246 # extract http/https links and make them real urls
1219 1247 newtext = urlify_text(newtext, safe=False)
1220 1248
1221 1249 try:
1222 1250 from rhodecode import CONFIG
1223 1251 conf = CONFIG
1224 1252
1225 1253 # allow multiple issue servers to be used
1226 1254 valid_indices = [
1227 1255 x.group(1)
1228 1256 for x in map(lambda x: re.match(r'issue_pat(.*)', x), conf.keys())
1229 1257 if x and 'issue_server_link%s' % x.group(1) in conf
1230 1258 and 'issue_prefix%s' % x.group(1) in conf
1231 1259 ]
1232 1260
1233 1261 log.debug('found issue server suffixes `%s` during valuation of: %s'
1234 1262 % (','.join(valid_indices), newtext))
1235 1263
1236 1264 for pattern_index in valid_indices:
1237 1265 ISSUE_PATTERN = conf.get('issue_pat%s' % pattern_index)
1238 1266 ISSUE_SERVER_LNK = conf.get('issue_server_link%s' % pattern_index)
1239 1267 ISSUE_PREFIX = conf.get('issue_prefix%s' % pattern_index)
1240 1268
1241 1269 log.debug('pattern suffix `%s` PAT:%s SERVER_LINK:%s PREFIX:%s'
1242 1270 % (pattern_index, ISSUE_PATTERN, ISSUE_SERVER_LNK,
1243 1271 ISSUE_PREFIX))
1244 1272
1245 1273 URL_PAT = re.compile(r'%s' % ISSUE_PATTERN)
1246 1274
1247 1275 def url_func(match_obj):
1248 1276 pref = ''
1249 1277 if match_obj.group().startswith(' '):
1250 1278 pref = ' '
1251 1279
1252 1280 issue_id = ''.join(match_obj.groups())
1253 1281 tmpl = (
1254 1282 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1255 1283 '%(issue-prefix)s%(id-repr)s'
1256 1284 '</a>'
1257 1285 )
1258 1286 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
1259 1287 if repository:
1260 1288 url = url.replace('{repo}', repository)
1261 1289 repo_name = repository.split(URL_SEP)[-1]
1262 1290 url = url.replace('{repo_name}', repo_name)
1263 1291
1264 1292 return tmpl % {
1265 1293 'pref': pref,
1266 1294 'cls': 'issue-tracker-link',
1267 1295 'url': url,
1268 1296 'id-repr': issue_id,
1269 1297 'issue-prefix': ISSUE_PREFIX,
1270 1298 'serv': ISSUE_SERVER_LNK,
1271 1299 }
1272 1300 newtext = URL_PAT.sub(url_func, newtext)
1273 1301 log.debug('processed prefix:`%s` => %s' % (pattern_index, newtext))
1274 1302
1275 1303 # if we actually did something above
1276 1304 if link_:
1277 1305 # wrap not links into final link => link_
1278 1306 newtext = linkify_others(newtext, link_)
1279 1307 except Exception:
1280 1308 log.error(traceback.format_exc())
1281 1309 pass
1282 1310
1283 1311 return literal(newtext)
1284 1312
1285 1313
1286 1314 def rst(source):
1287 1315 return literal('<div class="rst-block">%s</div>' %
1288 1316 MarkupRenderer.rst(source))
1289 1317
1290 1318
1291 1319 def rst_w_mentions(source):
1292 1320 """
1293 1321 Wrapped rst renderer with @mention highlighting
1294 1322
1295 1323 :param source:
1296 1324 """
1297 1325 return literal('<div class="rst-block">%s</div>' %
1298 1326 MarkupRenderer.rst_with_mentions(source))
1299 1327
1300 1328
1301 1329 def changeset_status(repo, revision):
1302 1330 return ChangesetStatusModel().get_status(repo, revision)
1303 1331
1304 1332
1305 1333 def changeset_status_lbl(changeset_status):
1306 1334 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1307 1335
1308 1336
1309 1337 def get_permission_name(key):
1310 1338 return dict(Permission.PERMS).get(key)
1311 1339
1312 1340
1313 1341 def journal_filter_help():
1314 1342 return _(textwrap.dedent('''
1315 1343 Example filter terms:
1316 1344 repository:vcs
1317 1345 username:marcin
1318 1346 action:*push*
1319 1347 ip:127.0.0.1
1320 1348 date:20120101
1321 1349 date:[20120101100000 TO 20120102]
1322 1350
1323 1351 Generate wildcards using '*' character:
1324 1352 "repositroy:vcs*" - search everything starting with 'vcs'
1325 1353 "repository:*vcs*" - search for repository containing 'vcs'
1326 1354
1327 1355 Optional AND / OR operators in queries
1328 1356 "repository:vcs OR repository:test"
1329 1357 "username:test AND repository:test*"
1330 1358 '''))
1331 1359
1332 1360
1333 1361 def not_mapped_error(repo_name):
1334 1362 flash(_('%s repository is not mapped to db perhaps'
1335 1363 ' it was created or renamed from the filesystem'
1336 1364 ' please run the application again'
1337 1365 ' in order to rescan repositories') % repo_name, category='error')
1338 1366
1339 1367
1340 1368 def ip_range(ip_addr):
1341 1369 from rhodecode.model.db import UserIpMap
1342 1370 s, e = UserIpMap._get_ip_range(ip_addr)
1343 1371 return '%s - %s' % (s, e)
@@ -1,4907 +1,4914 b''
1 1 html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {
2 2 border: 0;
3 3 outline: 0;
4 4 font-size: 100%;
5 5 vertical-align: baseline;
6 6 background: transparent;
7 7 margin: 0;
8 8 padding: 0;
9 9 }
10 10
11 11 body {
12 12 line-height: 1;
13 13 height: 100%;
14 14 background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
15 15 font-family: Lucida Grande, Verdana, Lucida Sans Regular,
16 16 Lucida Sans Unicode, Arial, sans-serif; font-size : 12px;
17 17 color: #000;
18 18 margin: 0;
19 19 padding: 0;
20 20 font-size: 12px;
21 21 }
22 22
23 23 ol, ul {
24 24 list-style: none;
25 25 }
26 26
27 27 blockquote, q {
28 28 quotes: none;
29 29 }
30 30
31 31 blockquote:before, blockquote:after, q:before, q:after {
32 32 content: none;
33 33 }
34 34
35 35 :focus {
36 36 outline: 0;
37 37 }
38 38
39 39 del {
40 40 text-decoration: line-through;
41 41 }
42 42
43 43 table {
44 44 border-collapse: collapse;
45 45 border-spacing: 0;
46 46 }
47 47
48 48 html {
49 49 height: 100%;
50 50 }
51 51
52 52 a {
53 53 color: #003367;
54 54 text-decoration: none;
55 55 cursor: pointer;
56 56 }
57 57
58 58 a:hover {
59 59 color: #316293;
60 60 text-decoration: underline;
61 61 }
62 62
63 63 h1, h2, h3, h4, h5, h6,
64 64 div.h1, div.h2, div.h3, div.h4, div.h5, div.h6 {
65 65 color: #292929;
66 66 font-weight: 700;
67 67 }
68 68
69 69 h1, div.h1 {
70 70 font-size: 22px;
71 71 }
72 72
73 73 h2, div.h2 {
74 74 font-size: 20px;
75 75 }
76 76
77 77 h3, div.h3 {
78 78 font-size: 18px;
79 79 }
80 80
81 81 h4, div.h4 {
82 82 font-size: 16px;
83 83 }
84 84
85 85 h5, div.h5 {
86 86 font-size: 14px;
87 87 }
88 88
89 89 h6, div.h6 {
90 90 font-size: 11px;
91 91 }
92 92
93 93 ul.circle {
94 94 list-style-type: circle;
95 95 }
96 96
97 97 ul.disc {
98 98 list-style-type: disc;
99 99 }
100 100
101 101 ul.square {
102 102 list-style-type: square;
103 103 }
104 104
105 105 ol.lower-roman {
106 106 list-style-type: lower-roman;
107 107 }
108 108
109 109 ol.upper-roman {
110 110 list-style-type: upper-roman;
111 111 }
112 112
113 113 ol.lower-alpha {
114 114 list-style-type: lower-alpha;
115 115 }
116 116
117 117 ol.upper-alpha {
118 118 list-style-type: upper-alpha;
119 119 }
120 120
121 121 ol.decimal {
122 122 list-style-type: decimal;
123 123 }
124 124
125 125 div.color {
126 126 clear: both;
127 127 overflow: hidden;
128 128 position: absolute;
129 129 background: #FFF;
130 130 margin: 7px 0 0 60px;
131 131 padding: 1px 1px 1px 0;
132 132 }
133 133
134 134 div.color a {
135 135 width: 15px;
136 136 height: 15px;
137 137 display: block;
138 138 float: left;
139 139 margin: 0 0 0 1px;
140 140 padding: 0;
141 141 }
142 142
143 143 div.options {
144 144 clear: both;
145 145 overflow: hidden;
146 146 position: absolute;
147 147 background: #FFF;
148 148 margin: 7px 0 0 162px;
149 149 padding: 0;
150 150 }
151 151
152 152 div.options a {
153 153 height: 1%;
154 154 display: block;
155 155 text-decoration: none;
156 156 margin: 0;
157 157 padding: 3px 8px;
158 158 }
159 159
160 160 .top-left-rounded-corner {
161 161 -webkit-border-top-left-radius: 8px;
162 162 -khtml-border-radius-topleft: 8px;
163 163 border-top-left-radius: 8px;
164 164 }
165 165
166 166 .top-right-rounded-corner {
167 167 -webkit-border-top-right-radius: 8px;
168 168 -khtml-border-radius-topright: 8px;
169 169 border-top-right-radius: 8px;
170 170 }
171 171
172 172 .bottom-left-rounded-corner {
173 173 -webkit-border-bottom-left-radius: 8px;
174 174 -khtml-border-radius-bottomleft: 8px;
175 175 border-bottom-left-radius: 8px;
176 176 }
177 177
178 178 .bottom-right-rounded-corner {
179 179 -webkit-border-bottom-right-radius: 8px;
180 180 -khtml-border-radius-bottomright: 8px;
181 181 border-bottom-right-radius: 8px;
182 182 }
183 183
184 184 .top-left-rounded-corner-mid {
185 185 -webkit-border-top-left-radius: 4px;
186 186 -khtml-border-radius-topleft: 4px;
187 187 border-top-left-radius: 4px;
188 188 }
189 189
190 190 .top-right-rounded-corner-mid {
191 191 -webkit-border-top-right-radius: 4px;
192 192 -khtml-border-radius-topright: 4px;
193 193 border-top-right-radius: 4px;
194 194 }
195 195
196 196 .bottom-left-rounded-corner-mid {
197 197 -webkit-border-bottom-left-radius: 4px;
198 198 -khtml-border-radius-bottomleft: 4px;
199 199 border-bottom-left-radius: 4px;
200 200 }
201 201
202 202 .bottom-right-rounded-corner-mid {
203 203 -webkit-border-bottom-right-radius: 4px;
204 204 -khtml-border-radius-bottomright: 4px;
205 205 border-bottom-right-radius: 4px;
206 206 }
207 207
208 208 .help-block {
209 209 color: #999999;
210 210 display: block;
211 211 margin-bottom: 0;
212 212 margin-top: 5px;
213 213 }
214 214
215 215 .empty_data {
216 216 color: #B9B9B9;
217 217 }
218 218
219 219 a.permalink {
220 220 visibility: hidden;
221 221 position: absolute;
222 222 margin: 3px 4px;
223 223 }
224 224
225 225 a.permalink:hover {
226 226 text-decoration: none;
227 227 }
228 228
229 229 h1:hover > a.permalink,
230 230 h2:hover > a.permalink,
231 231 h3:hover > a.permalink,
232 232 h4:hover > a.permalink,
233 233 h5:hover > a.permalink,
234 234 h6:hover > a.permalink,
235 235 div:hover > a.permalink {
236 236 visibility: visible;
237 237 }
238 238
239 239 #header {
240 240 }
241 241 #header ul#logged-user {
242 242 margin-bottom: 5px !important;
243 243 -webkit-border-radius: 0px 0px 8px 8px;
244 244 -khtml-border-radius: 0px 0px 8px 8px;
245 245 border-radius: 0px 0px 8px 8px;
246 246 height: 37px;
247 247 background-color: #003B76;
248 248 background-repeat: repeat-x;
249 249 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
250 250 background-image: -moz-linear-gradient(top, #003b76, #00376e);
251 251 background-image: -ms-linear-gradient(top, #003b76, #00376e);
252 252 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
253 253 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
254 254 background-image: -o-linear-gradient(top, #003b76, #00376e);
255 255 background-image: linear-gradient(to bottom, #003b76, #00376e);
256 256 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
257 257 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
258 258 }
259 259
260 260 #header ul#logged-user li {
261 261 list-style: none;
262 262 float: left;
263 263 margin: 8px 0 0;
264 264 padding: 4px 12px;
265 265 border-left: 1px solid #316293;
266 266 }
267 267
268 268 #header ul#logged-user li.first {
269 269 border-left: none;
270 270 margin: 4px;
271 271 }
272 272
273 273 #header ul#logged-user li.first div.gravatar {
274 274 margin-top: -2px;
275 275 }
276 276
277 277 #header ul#logged-user li.first div.account {
278 278 padding-top: 4px;
279 279 float: left;
280 280 }
281 281
282 282 #header ul#logged-user li.last {
283 283 border-right: none;
284 284 }
285 285
286 286 #header ul#logged-user li a {
287 287 color: #fff;
288 288 font-weight: 700;
289 289 text-decoration: none;
290 290 }
291 291
292 292 #header ul#logged-user li a:hover {
293 293 text-decoration: underline;
294 294 }
295 295
296 296 #header ul#logged-user li.highlight a {
297 297 color: #fff;
298 298 }
299 299
300 300 #header ul#logged-user li.highlight a:hover {
301 301 color: #FFF;
302 302 }
303 303 #header-dd {
304 304 clear: both;
305 305 position: fixed !important;
306 306 background-color: #003B76;
307 307 opacity: 0.01;
308 308 cursor: pointer;
309 309 min-height: 10px;
310 310 width: 100% !important;
311 311 -webkit-border-radius: 0px 0px 4px 4px;
312 312 -khtml-border-radius: 0px 0px 4px 4px;
313 313 border-radius: 0px 0px 4px 4px;
314 314 }
315 315
316 316 #header-dd:hover {
317 317 opacity: 0.2;
318 318 -webkit-transition: opacity 0.5s ease-in-out;
319 319 -moz-transition: opacity 0.5s ease-in-out;
320 320 transition: opacity 0.5s ease-in-out;
321 321 }
322 322
323 323 #header #header-inner {
324 324 min-height: 44px;
325 325 clear: both;
326 326 position: relative;
327 327 background-color: #003B76;
328 328 background-repeat: repeat-x;
329 329 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
330 330 background-image: -moz-linear-gradient(top, #003b76, #00376e);
331 331 background-image: -ms-linear-gradient(top, #003b76, #00376e);
332 332 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),color-stop(100%, #00376e) );
333 333 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
334 334 background-image: -o-linear-gradient(top, #003b76, #00376e);
335 335 background-image: linear-gradient(to bottom, #003b76, #00376e);
336 336 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
337 337 margin: 0;
338 338 padding: 0;
339 339 display: block;
340 340 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
341 341 -webkit-border-radius: 0px 0px 4px 4px;
342 342 -khtml-border-radius: 0px 0px 4px 4px;
343 343 border-radius: 0px 0px 4px 4px;
344 344 }
345 345 #header #header-inner.hover {
346 346 width: 100% !important;
347 347 -webkit-border-radius: 0px 0px 0px 0px;
348 348 -khtml-border-radius: 0px 0px 0px 0px;
349 349 border-radius: 0px 0px 0px 0px;
350 350 position: fixed !important;
351 351 z-index: 10000;
352 352 }
353 353
354 354 .ie7 #header #header-inner.hover,
355 355 .ie8 #header #header-inner.hover,
356 356 .ie9 #header #header-inner.hover
357 357 {
358 358 z-index: auto !important;
359 359 }
360 360
361 361 .header-pos-fix, .anchor {
362 362 margin-top: -46px;
363 363 padding-top: 46px;
364 364 }
365 365
366 366 #header #header-inner #home a {
367 367 height: 40px;
368 368 width: 46px;
369 369 display: block;
370 370 background: url("../images/button_home.png");
371 371 background-position: 0 0;
372 372 margin: 0;
373 373 padding: 0;
374 374 }
375 375
376 376 #header #header-inner #home a:hover {
377 377 background-position: 0 -40px;
378 378 }
379 379
380 380 #header #header-inner #logo {
381 381 float: left;
382 382 position: absolute;
383 383 }
384 384
385 385 #header #header-inner #logo h1 {
386 386 color: #FFF;
387 387 font-size: 20px;
388 388 margin: 12px 0 0 13px;
389 389 padding: 0;
390 390 }
391 391
392 392 #header #header-inner #logo a {
393 393 color: #fff;
394 394 text-decoration: none;
395 395 }
396 396
397 397 #header #header-inner #logo a:hover {
398 398 color: #bfe3ff;
399 399 }
400 400
401 401 #header #header-inner #quick {
402 402 position: relative;
403 403 float: right;
404 404 list-style-type: none;
405 405 list-style-position: outside;
406 406 margin: 4px 8px 0 0;
407 407 padding: 0;
408 408 border-radius: 4px;
409 409 }
410 410
411 411 #header #header-inner #quick li span.short {
412 412 padding: 9px 6px 8px 6px;
413 413 }
414 414
415 415 #header #header-inner #quick li span {
416 416 display: inline;
417 417 margin: 0;
418 418 }
419 419
420 420 #header #header-inner #quick li span.normal {
421 421 border: none;
422 422 padding: 10px 12px 8px;
423 423 }
424 424
425 425 #header #header-inner #quick li span.icon {
426 426 border-left: none;
427 427 padding-left: 10px;
428 428 }
429 429
430 430 #header #header-inner #quick li span.icon_short {
431 431 top: 0;
432 432 left: 0;
433 433 border-left: none;
434 434 border-right: 1px solid #2e5c89;
435 435 padding: 8px 6px 4px;
436 436 }
437 437
438 438 #header #header-inner #quick li span.icon img, #header #header-inner #quick li span.icon_short img {
439 439 vertical-align: middle;
440 440 margin-bottom: 2px;
441 441 }
442 442
443 443 #header #header-inner #quick ul.repo_switcher {
444 444 max-height: 275px;
445 445 overflow-x: hidden;
446 446 overflow-y: auto;
447 447 }
448 448
449 449 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
450 450 padding: 2px 3px;
451 451 padding-right: 17px;
452 452 }
453 453
454 454 #header #header-inner #quick ul.repo_switcher li.qfilter_rs input {
455 455 width: 100%;
456 456 border-radius: 10px;
457 457 padding: 2px 7px;
458 458 }
459 459
460 460 #header #header-inner #quick .repo_switcher_type {
461 461 position: absolute;
462 462 left: 0;
463 463 top: 9px;
464 464 margin: 0px 2px 0px 2px;
465 465 }
466 466
467 467 #header #header-inner #quick li ul li a.journal, #header #header-inner #quick li ul li a.journal:hover {
468 468 background-image: url("../images/icons/book.png");
469 469 }
470 470
471 471 #header #header-inner #quick li ul li a.private_repo, #header #header-inner #quick li ul li a.private_repo:hover {
472 472 background-image: url("../images/icons/lock.png")
473 473 }
474 474
475 475 #header #header-inner #quick li ul li a.public_repo, #header #header-inner #quick li ul li a.public_repo:hover {
476 476 background-image: url("../images/icons/lock_open.png");
477 477 }
478 478
479 479 #header #header-inner #quick li ul li a.hg, #header #header-inner #quick li ul li a.hg:hover {
480 480 background-image: url("../images/icons/hgicon.png");
481 481 padding-left: 42px;
482 482 background-position: 20px 9px;
483 483 }
484 484
485 485 #header #header-inner #quick li ul li a.git, #header #header-inner #quick li ul li a.git:hover {
486 486 background-image: url("../images/icons/giticon.png");
487 487 padding-left: 42px;
488 488 background-position: 20px 9px;
489 489 }
490 490
491 491 #header #header-inner #quick li ul li a.repos, #header #header-inner #quick li ul li a.repos:hover {
492 492 background-image: url("../images/icons/database_edit.png");
493 493 }
494 494
495 495 #header #header-inner #quick li ul li a.repos_groups, #header #header-inner #quick li ul li a.repos_groups:hover {
496 496 background-image: url("../images/icons/database_link.png");
497 497 }
498 498
499 499 #header #header-inner #quick li ul li a.users, #header #header-inner #quick li ul li a.users:hover {
500 500 background-image: url("../images/icons/user_edit.png");
501 501 }
502 502
503 503 #header #header-inner #quick li ul li a.groups, #header #header-inner #quick li ul li a.groups:hover {
504 504 background-image: url("../images/icons/group_edit.png");
505 505 }
506 506
507 507 #header #header-inner #quick li ul li a.defaults, #header #header-inner #quick li ul li a.defaults:hover {
508 508 background-image: url("../images/icons/wrench.png");
509 509 }
510 510
511 511 #header #header-inner #quick li ul li a.settings, #header #header-inner #quick li ul li a.settings:hover {
512 512 background-image: url("../images/icons/cog.png");
513 513 }
514 514
515 515 #header #header-inner #quick li ul li a.permissions, #header #header-inner #quick li ul li a.permissions:hover {
516 516 background-image: url("../images/icons/key.png");
517 517 }
518 518
519 519 #header #header-inner #quick li ul li a.ldap, #header #header-inner #quick li ul li a.ldap:hover {
520 520 background-image: url("../images/icons/server_key.png");
521 521 }
522 522
523 523 #header #header-inner #quick li ul li a.fork, #header #header-inner #quick li ul li a.fork:hover {
524 524 background-image: url("../images/icons/arrow_divide.png");
525 525 }
526 526
527 527 #header #header-inner #quick li ul li a.locking_add, #header #header-inner #quick li ul li a.locking_add:hover {
528 528 background-image: url("../images/icons/lock_add.png");
529 529 }
530 530
531 531 #header #header-inner #quick li ul li a.locking_del, #header #header-inner #quick li ul li a.locking_del:hover {
532 532 background-image: url("../images/icons/lock_delete.png");
533 533 }
534 534
535 535 #header #header-inner #quick li ul li a.pull_request, #header #header-inner #quick li ul li a.pull_request:hover {
536 536 background-image: url("../images/icons/arrow_join.png") ;
537 537 }
538 538
539 539 #header #header-inner #quick li ul li a.compare_request, #header #header-inner #quick li ul li a.compare_request:hover {
540 540 background-image: url("../images/icons/arrow_inout.png");
541 541 }
542 542
543 543 #header #header-inner #quick li ul li a.search, #header #header-inner #quick li ul li a.search:hover {
544 544 background-image: url("../images/icons/search_16.png");
545 545 }
546 546
547 547 #header #header-inner #quick li ul li a.delete, #header #header-inner #quick li ul li a.delete:hover {
548 548 background-image: url("../images/icons/delete.png");
549 549 }
550 550
551 551 #header #header-inner #quick li ul li a.branches, #header #header-inner #quick li ul li a.branches:hover {
552 552 background-image: url("../images/icons/arrow_branch.png");
553 553 }
554 554
555 555 #header #header-inner #quick li ul li a.tags,
556 556 #header #header-inner #quick li ul li a.tags:hover {
557 557 background: #FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
558 558 width: 167px;
559 559 margin: 0;
560 560 padding: 12px 9px 7px 24px;
561 561 }
562 562
563 563 #header #header-inner #quick li ul li a.bookmarks,
564 564 #header #header-inner #quick li ul li a.bookmarks:hover {
565 565 background: #FFF url("../images/icons/tag_green.png") no-repeat 4px 9px;
566 566 width: 167px;
567 567 margin: 0;
568 568 padding: 12px 9px 7px 24px;
569 569 }
570 570
571 571 #header #header-inner #quick li ul li a.admin,
572 572 #header #header-inner #quick li ul li a.admin:hover {
573 573 background: #FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
574 574 width: 167px;
575 575 margin: 0;
576 576 padding: 12px 9px 7px 24px;
577 577 }
578 578
579 579 .groups_breadcrumbs a {
580 580 color: #fff;
581 581 }
582 582
583 583 .groups_breadcrumbs a:hover {
584 584 color: #bfe3ff;
585 585 text-decoration: none;
586 586 }
587 587
588 588 td.quick_repo_menu {
589 589 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
590 590 cursor: pointer;
591 591 width: 8px;
592 592 border: 1px solid transparent;
593 593 }
594 594
595 595 td.quick_repo_menu.active {
596 596 background: url("../images/dt-arrow-dn.png") no-repeat scroll 5px 50% #FFFFFF !important;
597 597 border: 1px solid #003367;
598 598 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
599 599 cursor: pointer;
600 600 }
601 601
602 602 td.quick_repo_menu .menu_items {
603 603 margin-top: 10px;
604 604 margin-left: -6px;
605 605 width: 150px;
606 606 position: absolute;
607 607 background-color: #FFF;
608 608 background: none repeat scroll 0 0 #FFFFFF;
609 609 border-color: #003367 #666666 #666666;
610 610 border-right: 1px solid #666666;
611 611 border-style: solid;
612 612 border-width: 1px;
613 613 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
614 614 border-top-style: none;
615 615 }
616 616
617 617 td.quick_repo_menu .menu_items li {
618 618 padding: 0 !important;
619 619 }
620 620
621 621 td.quick_repo_menu .menu_items a {
622 622 display: block;
623 623 padding: 4px 12px 4px 8px;
624 624 }
625 625
626 626 td.quick_repo_menu .menu_items a:hover {
627 627 background-color: #EEE;
628 628 text-decoration: none;
629 629 }
630 630
631 631 td.quick_repo_menu .menu_items .icon img {
632 632 margin-bottom: -2px;
633 633 }
634 634
635 635 td.quick_repo_menu .menu_items.hidden {
636 636 display: none;
637 637 }
638 638
639 639 .yui-dt-first th {
640 640 text-align: left;
641 641 }
642 642
643 643 /*
644 644 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
645 645 Code licensed under the BSD License:
646 646 http://developer.yahoo.com/yui/license.html
647 647 version: 2.9.0
648 648 */
649 649 .yui-skin-sam .yui-dt-mask {
650 650 position: absolute;
651 651 z-index: 9500;
652 652 }
653 653 .yui-dt-tmp {
654 654 position: absolute;
655 655 left: -9000px;
656 656 }
657 657 .yui-dt-scrollable .yui-dt-bd { overflow: auto }
658 658 .yui-dt-scrollable .yui-dt-hd {
659 659 overflow: hidden;
660 660 position: relative;
661 661 }
662 662 .yui-dt-scrollable .yui-dt-bd thead tr,
663 663 .yui-dt-scrollable .yui-dt-bd thead th {
664 664 position: absolute;
665 665 left: -1500px;
666 666 }
667 667 .yui-dt-scrollable tbody { -moz-outline: 0 }
668 668 .yui-skin-sam thead .yui-dt-sortable { cursor: pointer }
669 669 .yui-skin-sam thead .yui-dt-draggable { cursor: move }
670 670 .yui-dt-coltarget {
671 671 position: absolute;
672 672 z-index: 999;
673 673 }
674 674 .yui-dt-hd { zoom: 1 }
675 675 th.yui-dt-resizeable .yui-dt-resizerliner { position: relative }
676 676 .yui-dt-resizer {
677 677 position: absolute;
678 678 right: 0;
679 679 bottom: 0;
680 680 height: 100%;
681 681 cursor: e-resize;
682 682 cursor: col-resize;
683 683 background-color: #CCC;
684 684 opacity: 0;
685 685 filter: alpha(opacity=0);
686 686 }
687 687 .yui-dt-resizerproxy {
688 688 visibility: hidden;
689 689 position: absolute;
690 690 z-index: 9000;
691 691 background-color: #CCC;
692 692 opacity: 0;
693 693 filter: alpha(opacity=0);
694 694 }
695 695 th.yui-dt-hidden .yui-dt-liner,
696 696 td.yui-dt-hidden .yui-dt-liner,
697 697 th.yui-dt-hidden .yui-dt-resizer { display: none }
698 698 .yui-dt-editor,
699 699 .yui-dt-editor-shim {
700 700 position: absolute;
701 701 z-index: 9000;
702 702 }
703 703 .yui-skin-sam .yui-dt table {
704 704 margin: 0;
705 705 padding: 0;
706 706 font-family: arial;
707 707 font-size: inherit;
708 708 border-collapse: separate;
709 709 *border-collapse: collapse;
710 710 border-spacing: 0;
711 711 border: 1px solid #7f7f7f;
712 712 }
713 713 .yui-skin-sam .yui-dt thead { border-spacing: 0 }
714 714 .yui-skin-sam .yui-dt caption {
715 715 color: #000;
716 716 font-size: 85%;
717 717 font-weight: normal;
718 718 font-style: italic;
719 719 line-height: 1;
720 720 padding: 1em 0;
721 721 text-align: center;
722 722 }
723 723 .yui-skin-sam .yui-dt th { background: #d8d8da url(../images/sprite.png) repeat-x 0 0 }
724 724 .yui-skin-sam .yui-dt th,
725 725 .yui-skin-sam .yui-dt th a {
726 726 font-weight: normal;
727 727 text-decoration: none;
728 728 color: #000;
729 729 vertical-align: bottom;
730 730 }
731 731 .yui-skin-sam .yui-dt th {
732 732 margin: 0;
733 733 padding: 0;
734 734 border: 0;
735 735 border-right: 1px solid #cbcbcb;
736 736 }
737 737 .yui-skin-sam .yui-dt tr.yui-dt-first td { border-top: 1px solid #7f7f7f }
738 738 .yui-skin-sam .yui-dt th .yui-dt-liner { white-space: nowrap }
739 739 .yui-skin-sam .yui-dt-liner {
740 740 margin: 0;
741 741 padding: 0;
742 742 }
743 743 .yui-skin-sam .yui-dt-coltarget {
744 744 width: 5px;
745 745 background-color: red;
746 746 }
747 747 .yui-skin-sam .yui-dt td {
748 748 margin: 0;
749 749 padding: 0;
750 750 border: 0;
751 751 border-right: 1px solid #cbcbcb;
752 752 text-align: left;
753 753 }
754 754 .yui-skin-sam .yui-dt-list td { border-right: 0 }
755 755 .yui-skin-sam .yui-dt-resizer { width: 6px }
756 756 .yui-skin-sam .yui-dt-mask {
757 757 background-color: #000;
758 758 opacity: .25;
759 759 filter: alpha(opacity=25);
760 760 }
761 761 .yui-skin-sam .yui-dt-message { background-color: #FFF }
762 762 .yui-skin-sam .yui-dt-scrollable table { border: 0 }
763 763 .yui-skin-sam .yui-dt-scrollable .yui-dt-hd {
764 764 border-left: 1px solid #7f7f7f;
765 765 border-top: 1px solid #7f7f7f;
766 766 border-right: 1px solid #7f7f7f;
767 767 }
768 768 .yui-skin-sam .yui-dt-scrollable .yui-dt-bd {
769 769 border-left: 1px solid #7f7f7f;
770 770 border-bottom: 1px solid #7f7f7f;
771 771 border-right: 1px solid #7f7f7f;
772 772 background-color: #FFF;
773 773 }
774 774 .yui-skin-sam .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td { border-bottom: 1px solid #7f7f7f }
775 775 .yui-skin-sam th.yui-dt-asc,
776 776 .yui-skin-sam th.yui-dt-desc { background: url(../images/sprite.png) repeat-x 0 -100px }
777 777 .yui-skin-sam th.yui-dt-sortable .yui-dt-label { margin-right: 10px }
778 778 .yui-skin-sam th.yui-dt-asc .yui-dt-liner { background: url(../images/dt-arrow-up.png) no-repeat right }
779 779 .yui-skin-sam th.yui-dt-desc .yui-dt-liner { background: url(../images/dt-arrow-dn.png) no-repeat right }
780 780 tbody .yui-dt-editable { cursor: pointer }
781 781 .yui-dt-editor {
782 782 text-align: left;
783 783 background-color: #f2f2f2;
784 784 border: 1px solid #808080;
785 785 padding: 6px;
786 786 }
787 787 .yui-dt-editor label {
788 788 padding-left: 4px;
789 789 padding-right: 6px;
790 790 }
791 791 .yui-dt-editor .yui-dt-button {
792 792 padding-top: 6px;
793 793 text-align: right;
794 794 }
795 795 .yui-dt-editor .yui-dt-button button {
796 796 background: url(../images/sprite.png) repeat-x 0 0;
797 797 border: 1px solid #999;
798 798 width: 4em;
799 799 height: 1.8em;
800 800 margin-left: 6px;
801 801 }
802 802 .yui-dt-editor .yui-dt-button button.yui-dt-default {
803 803 background: url(../images/sprite.png) repeat-x 0 -1400px;
804 804 background-color: #5584e0;
805 805 border: 1px solid #304369;
806 806 color: #FFF;
807 807 }
808 808 .yui-dt-editor .yui-dt-button button:hover {
809 809 background: url(../images/sprite.png) repeat-x 0 -1300px;
810 810 color: #000;
811 811 }
812 812 .yui-dt-editor .yui-dt-button button:active {
813 813 background: url(../images/sprite.png) repeat-x 0 -1700px;
814 814 color: #000;
815 815 }
816 816 .yui-skin-sam tr.yui-dt-even { background-color: #FFF }
817 817 .yui-skin-sam tr.yui-dt-odd { background-color: #edf5ff }
818 818 .yui-skin-sam tr.yui-dt-even td.yui-dt-asc,
819 819 .yui-skin-sam tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
820 820 .yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,
821 821 .yui-skin-sam tr.yui-dt-odd td.yui-dt-desc { background-color: #dbeaff }
822 822 .yui-skin-sam .yui-dt-list tr.yui-dt-even { background-color: #FFF }
823 823 .yui-skin-sam .yui-dt-list tr.yui-dt-odd { background-color: #FFF }
824 824 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-asc,
825 825 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
826 826 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-asc,
827 827 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-desc { background-color: #edf5ff }
828 828 .yui-skin-sam th.yui-dt-highlighted,
829 829 .yui-skin-sam th.yui-dt-highlighted a { background-color: #b2d2ff }
830 830 .yui-skin-sam tr.yui-dt-highlighted,
831 831 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-asc,
832 832 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-desc,
833 833 .yui-skin-sam tr.yui-dt-even td.yui-dt-highlighted,
834 834 .yui-skin-sam tr.yui-dt-odd td.yui-dt-highlighted {
835 835 cursor: pointer;
836 836 background-color: #b2d2ff;
837 837 }
838 838 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted,
839 839 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted a { background-color: #b2d2ff }
840 840 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted,
841 841 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-asc,
842 842 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc,
843 843 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted,
844 844 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted {
845 845 cursor: pointer;
846 846 background-color: #b2d2ff;
847 847 }
848 848 .yui-skin-sam th.yui-dt-selected,
849 849 .yui-skin-sam th.yui-dt-selected a { background-color: #446cd7 }
850 850 .yui-skin-sam tr.yui-dt-selected td,
851 851 .yui-skin-sam tr.yui-dt-selected td.yui-dt-asc,
852 852 .yui-skin-sam tr.yui-dt-selected td.yui-dt-desc {
853 853 background-color: #426fd9;
854 854 color: #FFF;
855 855 }
856 856 .yui-skin-sam tr.yui-dt-even td.yui-dt-selected,
857 857 .yui-skin-sam tr.yui-dt-odd td.yui-dt-selected {
858 858 background-color: #446cd7;
859 859 color: #FFF;
860 860 }
861 861 .yui-skin-sam .yui-dt-list th.yui-dt-selected,
862 862 .yui-skin-sam .yui-dt-list th.yui-dt-selected a { background-color: #446cd7 }
863 863 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td,
864 864 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-asc,
865 865 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-desc {
866 866 background-color: #426fd9;
867 867 color: #FFF;
868 868 }
869 869 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-selected,
870 870 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-selected {
871 871 background-color: #446cd7;
872 872 color: #FFF;
873 873 }
874 874 .yui-skin-sam .yui-dt-paginator {
875 875 display: block;
876 876 margin: 6px 0;
877 877 white-space: nowrap;
878 878 }
879 879 .yui-skin-sam .yui-dt-paginator .yui-dt-first,
880 880 .yui-skin-sam .yui-dt-paginator .yui-dt-last,
881 881 .yui-skin-sam .yui-dt-paginator .yui-dt-selected { padding: 2px 6px }
882 882 .yui-skin-sam .yui-dt-paginator a.yui-dt-first,
883 883 .yui-skin-sam .yui-dt-paginator a.yui-dt-last { text-decoration: none }
884 884 .yui-skin-sam .yui-dt-paginator .yui-dt-previous,
885 885 .yui-skin-sam .yui-dt-paginator .yui-dt-next { display: none }
886 886 .yui-skin-sam a.yui-dt-page {
887 887 border: 1px solid #cbcbcb;
888 888 padding: 2px 6px;
889 889 text-decoration: none;
890 890 background-color: #fff;
891 891 }
892 892 .yui-skin-sam .yui-dt-selected {
893 893 border: 1px solid #fff;
894 894 background-color: #fff;
895 895 }
896 896
897 897 #content #left {
898 898 left: 0;
899 899 width: 280px;
900 900 position: absolute;
901 901 }
902 902
903 903 #content #right {
904 904 margin: 0 60px 10px 290px;
905 905 }
906 906
907 907 #content div.box {
908 908 clear: both;
909 909 background: #fff;
910 910 margin: 0 0 10px;
911 911 padding: 0 0 10px;
912 912 -webkit-border-radius: 4px 4px 4px 4px;
913 913 -khtml-border-radius: 4px 4px 4px 4px;
914 914 border-radius: 4px 4px 4px 4px;
915 915 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
916 916 }
917 917
918 918 #content div.box-left {
919 919 width: 49%;
920 920 clear: none;
921 921 float: left;
922 922 margin: 0 0 10px;
923 923 }
924 924
925 925 #content div.box-right {
926 926 width: 49%;
927 927 clear: none;
928 928 float: right;
929 929 margin: 0 0 10px;
930 930 }
931 931
932 932 #content div.box div.title {
933 933 clear: both;
934 934 overflow: hidden;
935 935 background-color: #003B76;
936 936 background-repeat: repeat-x;
937 937 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
938 938 background-image: -moz-linear-gradient(top, #003b76, #00376e);
939 939 background-image: -ms-linear-gradient(top, #003b76, #00376e);
940 940 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
941 941 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
942 942 background-image: -o-linear-gradient(top, #003b76, #00376e);
943 943 background-image: linear-gradient(to bottom, #003b76, #00376e);
944 944 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
945 945 margin: 0 0 20px;
946 946 padding: 0;
947 947 border-radius: 4px 4px 0 0;
948 948 }
949 949
950 950 #content div.box div.title h5 {
951 951 float: left;
952 952 border: none;
953 953 color: #fff;
954 954 margin: 0;
955 955 padding: 11px 0 11px 10px;
956 956 }
957 957
958 958 #content div.box div.title .link-white {
959 959 color: #FFFFFF;
960 960 }
961 961
962 962 #content div.box div.title .link-white.current {
963 963 color: #BFE3FF;
964 964 }
965 965
966 966 #content div.box div.title ul.links li {
967 967 list-style: none;
968 968 float: left;
969 969 margin: 0;
970 970 padding: 0;
971 971 }
972 972
973 973 #content div.box div.title ul.links li a {
974 974 border-left: 1px solid #316293;
975 975 color: #FFFFFF;
976 976 display: block;
977 977 float: left;
978 978 font-size: 13px;
979 979 font-weight: 700;
980 980 height: 1%;
981 981 margin: 0;
982 982 padding: 11px 22px 12px;
983 983 text-decoration: none;
984 984 }
985 985
986 986 #content div.box h1, #content div.box h2, #content div.box h3, #content div.box h4, #content div.box h5, #content div.box h6,
987 987 #content div.box div.h1, #content div.box div.h2, #content div.box div.h3, #content div.box div.h4, #content div.box div.h5, #content div.box div.h6 {
988 988 clear: both;
989 989 overflow: hidden;
990 990 border-bottom: 1px solid #DDD;
991 991 margin: 10px 20px;
992 992 padding: 0 0 15px;
993 993 }
994 994
995 995 #content div.box p {
996 996 color: #5f5f5f;
997 997 font-size: 12px;
998 998 line-height: 150%;
999 999 margin: 0 24px 10px;
1000 1000 padding: 0;
1001 1001 }
1002 1002
1003 1003 #content div.box blockquote {
1004 1004 border-left: 4px solid #DDD;
1005 1005 color: #5f5f5f;
1006 1006 font-size: 11px;
1007 1007 line-height: 150%;
1008 1008 margin: 0 34px;
1009 1009 padding: 0 0 0 14px;
1010 1010 }
1011 1011
1012 1012 #content div.box blockquote p {
1013 1013 margin: 10px 0;
1014 1014 padding: 0;
1015 1015 }
1016 1016
1017 1017 #content div.box dl {
1018 1018 margin: 10px 0px;
1019 1019 }
1020 1020
1021 1021 #content div.box dt {
1022 1022 font-size: 12px;
1023 1023 margin: 0;
1024 1024 }
1025 1025
1026 1026 #content div.box dd {
1027 1027 font-size: 12px;
1028 1028 margin: 0;
1029 1029 padding: 8px 0 8px 15px;
1030 1030 }
1031 1031
1032 1032 #content div.box li {
1033 1033 font-size: 12px;
1034 1034 padding: 4px 0;
1035 1035 }
1036 1036
1037 1037 #content div.box ul.disc, #content div.box ul.circle {
1038 1038 margin: 10px 24px 10px 38px;
1039 1039 }
1040 1040
1041 1041 #content div.box ul.square {
1042 1042 margin: 10px 24px 10px 40px;
1043 1043 }
1044 1044
1045 1045 #content div.box img.left {
1046 1046 border: none;
1047 1047 float: left;
1048 1048 margin: 10px 10px 10px 0;
1049 1049 }
1050 1050
1051 1051 #content div.box img.right {
1052 1052 border: none;
1053 1053 float: right;
1054 1054 margin: 10px 0 10px 10px;
1055 1055 }
1056 1056
1057 1057 #content div.box div.messages {
1058 1058 clear: both;
1059 1059 overflow: hidden;
1060 1060 margin: 0 20px;
1061 1061 padding: 0;
1062 1062 }
1063 1063
1064 1064 #content div.box div.message {
1065 1065 clear: both;
1066 1066 overflow: hidden;
1067 1067 margin: 0;
1068 1068 padding: 5px 0;
1069 1069 white-space: pre-wrap;
1070 1070 }
1071 1071 #content div.box div.expand {
1072 1072 width: 110%;
1073 1073 height: 14px;
1074 1074 font-size: 10px;
1075 1075 text-align: center;
1076 1076 cursor: pointer;
1077 1077 color: #666;
1078 1078
1079 1079 background: -webkit-gradient(linear,0% 50%,100% 50%,color-stop(0%,rgba(255,255,255,0)),color-stop(100%,rgba(64,96,128,0.1)));
1080 1080 background: -webkit-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1081 1081 background: -moz-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1082 1082 background: -o-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1083 1083 background: -ms-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1084 1084 background: linear-gradient(to bottom,rgba(255,255,255,0),rgba(64,96,128,0.1));
1085 1085
1086 1086 display: none;
1087 1087 overflow: hidden;
1088 1088 }
1089 1089 #content div.box div.expand .expandtext {
1090 1090 background-color: #ffffff;
1091 1091 padding: 2px;
1092 1092 border-radius: 2px;
1093 1093 }
1094 1094
1095 1095 #content div.box div.message a {
1096 1096 font-weight: 400 !important;
1097 1097 }
1098 1098
1099 1099 #content div.box div.message div.image {
1100 1100 float: left;
1101 1101 margin: 9px 0 0 5px;
1102 1102 padding: 6px;
1103 1103 }
1104 1104
1105 1105 #content div.box div.message div.image img {
1106 1106 vertical-align: middle;
1107 1107 margin: 0;
1108 1108 }
1109 1109
1110 1110 #content div.box div.message div.text {
1111 1111 float: left;
1112 1112 margin: 0;
1113 1113 padding: 9px 6px;
1114 1114 }
1115 1115
1116 1116 #content div.box div.message div.dismiss a {
1117 1117 height: 16px;
1118 1118 width: 16px;
1119 1119 display: block;
1120 1120 background: url("../images/icons/cross.png") no-repeat;
1121 1121 margin: 15px 14px 0 0;
1122 1122 padding: 0;
1123 1123 }
1124 1124
1125 1125 #content div.box div.message div.text h1, #content div.box div.message div.text h2, #content div.box div.message div.text h3, #content div.box div.message div.text h4, #content div.box div.message div.text h5, #content div.box div.message div.text h6 {
1126 1126 border: none;
1127 1127 margin: 0;
1128 1128 padding: 0;
1129 1129 }
1130 1130
1131 1131 #content div.box div.message div.text span {
1132 1132 height: 1%;
1133 1133 display: block;
1134 1134 margin: 0;
1135 1135 padding: 5px 0 0;
1136 1136 }
1137 1137
1138 1138 #content div.box div.message-error {
1139 1139 height: 1%;
1140 1140 clear: both;
1141 1141 overflow: hidden;
1142 1142 background: #FBE3E4;
1143 1143 border: 1px solid #FBC2C4;
1144 1144 color: #860006;
1145 1145 }
1146 1146
1147 1147 #content div.box div.message-error h6 {
1148 1148 color: #860006;
1149 1149 }
1150 1150
1151 1151 #content div.box div.message-warning {
1152 1152 height: 1%;
1153 1153 clear: both;
1154 1154 overflow: hidden;
1155 1155 background: #FFF6BF;
1156 1156 border: 1px solid #FFD324;
1157 1157 color: #5f5200;
1158 1158 }
1159 1159
1160 1160 #content div.box div.message-warning h6 {
1161 1161 color: #5f5200;
1162 1162 }
1163 1163
1164 1164 #content div.box div.message-notice {
1165 1165 height: 1%;
1166 1166 clear: both;
1167 1167 overflow: hidden;
1168 1168 background: #8FBDE0;
1169 1169 border: 1px solid #6BACDE;
1170 1170 color: #003863;
1171 1171 }
1172 1172
1173 1173 #content div.box div.message-notice h6 {
1174 1174 color: #003863;
1175 1175 }
1176 1176
1177 1177 #content div.box div.message-success {
1178 1178 height: 1%;
1179 1179 clear: both;
1180 1180 overflow: hidden;
1181 1181 background: #E6EFC2;
1182 1182 border: 1px solid #C6D880;
1183 1183 color: #4e6100;
1184 1184 }
1185 1185
1186 1186 #content div.box div.message-success h6 {
1187 1187 color: #4e6100;
1188 1188 }
1189 1189
1190 1190 #content div.box div.form div.fields div.field {
1191 1191 height: 1%;
1192 1192 min-height: 12px;
1193 1193 border-bottom: 1px solid #DDD;
1194 1194 clear: both;
1195 1195 margin: 0;
1196 1196 padding: 10px 0;
1197 1197 }
1198 1198
1199 1199 #content div.box div.form div.fields div.field-first {
1200 1200 padding: 0 0 10px;
1201 1201 }
1202 1202
1203 1203 #content div.box div.form div.fields div.field-noborder {
1204 1204 border-bottom: 0 !important;
1205 1205 }
1206 1206
1207 1207 #content div.box div.form div.fields div.field span.error-message {
1208 1208 height: 1%;
1209 1209 display: inline-block;
1210 1210 color: red;
1211 1211 margin: 8px 0 0 4px;
1212 1212 padding: 0;
1213 1213 }
1214 1214
1215 1215 #content div.box div.form div.fields div.field span.success {
1216 1216 height: 1%;
1217 1217 display: block;
1218 1218 color: #316309;
1219 1219 margin: 8px 0 0;
1220 1220 padding: 0;
1221 1221 }
1222 1222
1223 1223 #content div.box div.form div.fields div.field div.label {
1224 1224 left: 70px;
1225 1225 width: 155px;
1226 1226 position: absolute;
1227 1227 margin: 0;
1228 1228 padding: 5px 0 0 0px;
1229 1229 }
1230 1230
1231 1231 #content div.box div.form div.fields div.field div.label-summary {
1232 1232 left: 30px;
1233 1233 width: 155px;
1234 1234 position: absolute;
1235 1235 margin: 0;
1236 1236 padding: 0px 0 0 0px;
1237 1237 }
1238 1238
1239 1239 #content div.box-left div.form div.fields div.field div.label,
1240 1240 #content div.box-right div.form div.fields div.field div.label,
1241 1241 #content div.box-left div.form div.fields div.field div.label,
1242 1242 #content div.box-left div.form div.fields div.field div.label-summary,
1243 1243 #content div.box-right div.form div.fields div.field div.label-summary,
1244 1244 #content div.box-left div.form div.fields div.field div.label-summary {
1245 1245 clear: both;
1246 1246 overflow: hidden;
1247 1247 left: 0;
1248 1248 width: auto;
1249 1249 position: relative;
1250 1250 margin: 0;
1251 1251 padding: 0 0 8px;
1252 1252 }
1253 1253
1254 1254 #content div.box div.form div.fields div.field div.label-select {
1255 1255 padding: 5px 0 0 5px;
1256 1256 }
1257 1257
1258 1258 #content div.box-left div.form div.fields div.field div.label-select,
1259 1259 #content div.box-right div.form div.fields div.field div.label-select {
1260 1260 padding: 0 0 8px;
1261 1261 }
1262 1262
1263 1263 #content div.box-left div.form div.fields div.field div.label-textarea,
1264 1264 #content div.box-right div.form div.fields div.field div.label-textarea {
1265 1265 padding: 0 0 8px !important;
1266 1266 }
1267 1267
1268 1268 #content div.box div.form div.fields div.field div.label label, div.label label {
1269 1269 color: #393939;
1270 1270 font-weight: 700;
1271 1271 }
1272 1272 #content div.box div.form div.fields div.field div.label label, div.label-summary label {
1273 1273 color: #393939;
1274 1274 font-weight: 700;
1275 1275 }
1276 1276 #content div.box div.form div.fields div.field div.input {
1277 1277 margin: 0 0 0 200px;
1278 1278 }
1279 1279
1280 1280 #content div.box div.form div.fields div.field div.input.summary {
1281 1281 margin: 0 0 0 110px;
1282 1282 }
1283 1283 #content div.box div.form div.fields div.field div.input.summary-short {
1284 1284 margin: 0 0 0 110px;
1285 1285 }
1286 1286 #content div.box div.form div.fields div.field div.file {
1287 1287 margin: 0 0 0 200px;
1288 1288 }
1289 1289
1290 1290 #content div.box-left div.form div.fields div.field div.input, #content div.box-right div.form div.fields div.field div.input {
1291 1291 margin: 0 0 0 0px;
1292 1292 }
1293 1293
1294 1294 #content div.box div.form div.fields div.field div.input input,
1295 1295 .reviewer_ac input {
1296 1296 background: #FFF;
1297 1297 border-top: 1px solid #b3b3b3;
1298 1298 border-left: 1px solid #b3b3b3;
1299 1299 border-right: 1px solid #eaeaea;
1300 1300 border-bottom: 1px solid #eaeaea;
1301 1301 color: #000;
1302 1302 font-size: 11px;
1303 1303 margin: 0;
1304 1304 padding: 7px 7px 6px;
1305 1305 }
1306 1306
1307 1307 #content div.box div.form div.fields div.field div.input input#clone_url,
1308 1308 #content div.box div.form div.fields div.field div.input input#clone_url_id
1309 1309 {
1310 1310 font-size: 16px;
1311 1311 padding: 2px;
1312 1312 }
1313 1313
1314 1314 #content div.box div.form div.fields div.field div.file input {
1315 1315 background: none repeat scroll 0 0 #FFFFFF;
1316 1316 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
1317 1317 border-style: solid;
1318 1318 border-width: 1px;
1319 1319 color: #000000;
1320 1320 font-size: 11px;
1321 1321 margin: 0;
1322 1322 padding: 7px 7px 6px;
1323 1323 }
1324 1324
1325 1325 input.disabled {
1326 1326 background-color: #F5F5F5 !important;
1327 1327 }
1328 1328 #content div.box div.form div.fields div.field div.input input.small {
1329 1329 width: 30%;
1330 1330 }
1331 1331
1332 1332 #content div.box div.form div.fields div.field div.input input.medium {
1333 1333 width: 55%;
1334 1334 }
1335 1335
1336 1336 #content div.box div.form div.fields div.field div.input input.large {
1337 1337 width: 85%;
1338 1338 }
1339 1339
1340 1340 #content div.box div.form div.fields div.field div.input input.date {
1341 1341 width: 177px;
1342 1342 }
1343 1343
1344 1344 #content div.box div.form div.fields div.field div.input input.button {
1345 1345 background: #D4D0C8;
1346 1346 border-top: 1px solid #FFF;
1347 1347 border-left: 1px solid #FFF;
1348 1348 border-right: 1px solid #404040;
1349 1349 border-bottom: 1px solid #404040;
1350 1350 color: #000;
1351 1351 margin: 0;
1352 1352 padding: 4px 8px;
1353 1353 }
1354 1354
1355 1355 #content div.box div.form div.fields div.field div.textarea {
1356 1356 border-top: 1px solid #b3b3b3;
1357 1357 border-left: 1px solid #b3b3b3;
1358 1358 border-right: 1px solid #eaeaea;
1359 1359 border-bottom: 1px solid #eaeaea;
1360 1360 margin: 0 0 0 200px;
1361 1361 padding: 10px;
1362 1362 }
1363 1363
1364 1364 #content div.box div.form div.fields div.field div.textarea-editor {
1365 1365 border: 1px solid #ddd;
1366 1366 padding: 0;
1367 1367 }
1368 1368
1369 1369 #content div.box div.form div.fields div.field div.textarea textarea {
1370 1370 width: 100%;
1371 1371 height: 220px;
1372 1372 overflow: hidden;
1373 1373 background: #FFF;
1374 1374 color: #000;
1375 1375 font-size: 11px;
1376 1376 outline: none;
1377 1377 border-width: 0;
1378 1378 margin: 0;
1379 1379 padding: 0;
1380 1380 }
1381 1381
1382 1382 #content div.box-left div.form div.fields div.field div.textarea textarea, #content div.box-right div.form div.fields div.field div.textarea textarea {
1383 1383 width: 100%;
1384 1384 height: 100px;
1385 1385 }
1386 1386
1387 1387 #content div.box div.form div.fields div.field div.textarea table {
1388 1388 width: 100%;
1389 1389 border: none;
1390 1390 margin: 0;
1391 1391 padding: 0;
1392 1392 }
1393 1393
1394 1394 #content div.box div.form div.fields div.field div.textarea table td {
1395 1395 background: #DDD;
1396 1396 border: none;
1397 1397 padding: 0;
1398 1398 }
1399 1399
1400 1400 #content div.box div.form div.fields div.field div.textarea table td table {
1401 1401 width: auto;
1402 1402 border: none;
1403 1403 margin: 0;
1404 1404 padding: 0;
1405 1405 }
1406 1406
1407 1407 #content div.box div.form div.fields div.field div.textarea table td table td {
1408 1408 font-size: 11px;
1409 1409 padding: 5px 5px 5px 0;
1410 1410 }
1411 1411
1412 1412 #content div.box div.form div.fields div.field input[type=text]:focus,
1413 1413 #content div.box div.form div.fields div.field input[type=password]:focus,
1414 1414 #content div.box div.form div.fields div.field input[type=file]:focus,
1415 1415 #content div.box div.form div.fields div.field textarea:focus,
1416 1416 #content div.box div.form div.fields div.field select:focus,
1417 1417 .reviewer_ac input:focus {
1418 1418 background: #f6f6f6;
1419 1419 border-color: #666;
1420 1420 }
1421 1421
1422 1422 .reviewer_ac {
1423 1423 padding: 10px
1424 1424 }
1425 1425
1426 1426 div.form div.fields div.field div.button {
1427 1427 margin: 0;
1428 1428 padding: 0 0 0 8px;
1429 1429 }
1430 1430 #content div.box table.noborder {
1431 1431 border: 1px solid transparent;
1432 1432 }
1433 1433
1434 1434 #content div.box table {
1435 1435 width: 100%;
1436 1436 border-collapse: separate;
1437 1437 margin: 0;
1438 1438 padding: 0;
1439 1439 border: 1px solid #eee;
1440 1440 -webkit-border-radius: 4px;
1441 1441 border-radius: 4px;
1442 1442 }
1443 1443
1444 1444 #content div.box table th {
1445 1445 background: #eee;
1446 1446 border-bottom: 1px solid #ddd;
1447 1447 padding: 5px 0px 5px 5px;
1448 1448 text-align: left;
1449 1449 }
1450 1450
1451 1451 #content div.box table th.left {
1452 1452 text-align: left;
1453 1453 }
1454 1454
1455 1455 #content div.box table th.right {
1456 1456 text-align: right;
1457 1457 }
1458 1458
1459 1459 #content div.box table th.center {
1460 1460 text-align: center;
1461 1461 }
1462 1462
1463 1463 #content div.box table th.selected {
1464 1464 vertical-align: middle;
1465 1465 padding: 0;
1466 1466 }
1467 1467
1468 1468 #content div.box table td {
1469 1469 background: #fff;
1470 1470 border-bottom: 1px solid #cdcdcd;
1471 1471 vertical-align: middle;
1472 1472 padding: 5px;
1473 1473 }
1474 1474
1475 1475 #content div.box table tr.selected td {
1476 1476 background: #FFC;
1477 1477 }
1478 1478
1479 1479 #content div.box table td.selected {
1480 1480 width: 3%;
1481 1481 text-align: center;
1482 1482 vertical-align: middle;
1483 1483 padding: 0;
1484 1484 }
1485 1485
1486 1486 #content div.box table td.action {
1487 1487 width: 45%;
1488 1488 text-align: left;
1489 1489 }
1490 1490
1491 1491 #content div.box table td.date {
1492 1492 width: 33%;
1493 1493 text-align: center;
1494 1494 }
1495 1495
1496 1496 #content div.box div.action {
1497 1497 float: right;
1498 1498 background: #FFF;
1499 1499 text-align: right;
1500 1500 margin: 10px 0 0;
1501 1501 padding: 0;
1502 1502 }
1503 1503
1504 1504 #content div.box div.action select {
1505 1505 font-size: 11px;
1506 1506 margin: 0;
1507 1507 }
1508 1508
1509 1509 #content div.box div.action .ui-selectmenu {
1510 1510 margin: 0;
1511 1511 padding: 0;
1512 1512 }
1513 1513
1514 1514 #content div.box div.pagination {
1515 1515 height: 1%;
1516 1516 clear: both;
1517 1517 overflow: hidden;
1518 1518 margin: 10px 0 0;
1519 1519 padding: 0;
1520 1520 }
1521 1521
1522 1522 #content div.box div.pagination ul.pager {
1523 1523 float: right;
1524 1524 text-align: right;
1525 1525 margin: 0;
1526 1526 padding: 0;
1527 1527 }
1528 1528
1529 1529 #content div.box div.pagination ul.pager li {
1530 1530 height: 1%;
1531 1531 float: left;
1532 1532 list-style: none;
1533 1533 background: #ebebeb url("../images/pager.png") repeat-x;
1534 1534 border-top: 1px solid #dedede;
1535 1535 border-left: 1px solid #cfcfcf;
1536 1536 border-right: 1px solid #c4c4c4;
1537 1537 border-bottom: 1px solid #c4c4c4;
1538 1538 color: #4A4A4A;
1539 1539 font-weight: 700;
1540 1540 margin: 0 0 0 4px;
1541 1541 padding: 0;
1542 1542 }
1543 1543
1544 1544 #content div.box div.pagination ul.pager li.separator {
1545 1545 padding: 6px;
1546 1546 }
1547 1547
1548 1548 #content div.box div.pagination ul.pager li.current {
1549 1549 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1550 1550 border-top: 1px solid #ccc;
1551 1551 border-left: 1px solid #bebebe;
1552 1552 border-right: 1px solid #b1b1b1;
1553 1553 border-bottom: 1px solid #afafaf;
1554 1554 color: #515151;
1555 1555 padding: 6px;
1556 1556 }
1557 1557
1558 1558 #content div.box div.pagination ul.pager li a {
1559 1559 height: 1%;
1560 1560 display: block;
1561 1561 float: left;
1562 1562 color: #515151;
1563 1563 text-decoration: none;
1564 1564 margin: 0;
1565 1565 padding: 6px;
1566 1566 }
1567 1567
1568 1568 #content div.box div.pagination ul.pager li a:hover,
1569 1569 #content div.box div.pagination ul.pager li a:active {
1570 1570 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1571 1571 border-top: 1px solid #ccc;
1572 1572 border-left: 1px solid #bebebe;
1573 1573 border-right: 1px solid #b1b1b1;
1574 1574 border-bottom: 1px solid #afafaf;
1575 1575 margin: -1px;
1576 1576 }
1577 1577
1578 1578 #content div.box div.pagination-right {
1579 1579 float: right;
1580 1580 }
1581 1581
1582 1582 #content div.box div.pagination-wh {
1583 1583 height: 1%;
1584 1584 overflow: hidden;
1585 1585 text-align: right;
1586 1586 margin: 10px 0 0;
1587 1587 padding: 0;
1588 1588 }
1589 1589
1590 1590 #content div.box div.pagination-wh > :first-child {
1591 1591 border-radius: 4px 0px 0px 4px;
1592 1592 }
1593 1593
1594 1594 #content div.box div.pagination-wh > :last-child {
1595 1595 border-radius: 0px 4px 4px 0px;
1596 1596 border-right: 1px solid #cfcfcf;
1597 1597 }
1598 1598
1599 1599 #content div.box div.pagination-wh a,
1600 1600 #content div.box div.pagination-wh span.pager_dotdot,
1601 1601 #content div.box div.pagination-wh span.yui-pg-previous,
1602 1602 #content div.box div.pagination-wh span.yui-pg-last,
1603 1603 #content div.box div.pagination-wh span.yui-pg-next,
1604 1604 #content div.box div.pagination-wh span.yui-pg-first {
1605 1605 height: 1%;
1606 1606 float: left;
1607 1607 background: #ebebeb url("../images/pager.png") repeat-x;
1608 1608 border-top: 1px solid #dedede;
1609 1609 border-left: 1px solid #cfcfcf;
1610 1610 border-bottom: 1px solid #c4c4c4;
1611 1611 color: #4A4A4A;
1612 1612 font-weight: 700;
1613 1613 padding: 6px;
1614 1614 }
1615 1615
1616 1616 #content div.box div.pagination-wh span.pager_curpage {
1617 1617 height: 1%;
1618 1618 float: left;
1619 1619 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1620 1620 border-top: 1px solid #ccc;
1621 1621 border-left: 1px solid #bebebe;
1622 1622 border-bottom: 1px solid #afafaf;
1623 1623 color: #515151;
1624 1624 font-weight: 700;
1625 1625 padding: 6px;
1626 1626 }
1627 1627
1628 1628 #content div.box div.pagination-wh a:hover, #content div.box div.pagination-wh a:active {
1629 1629 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1630 1630 border-top: 1px solid #ccc;
1631 1631 border-left: 1px solid #bebebe;
1632 1632 border-bottom: 1px solid #afafaf;
1633 1633 text-decoration: none;
1634 1634 }
1635 1635
1636 1636 #content div.box div.traffic div.legend {
1637 1637 clear: both;
1638 1638 overflow: hidden;
1639 1639 border-bottom: 1px solid #ddd;
1640 1640 margin: 0 0 10px;
1641 1641 padding: 0 0 10px;
1642 1642 }
1643 1643
1644 1644 #content div.box div.traffic div.legend h6 {
1645 1645 float: left;
1646 1646 border: none;
1647 1647 margin: 0;
1648 1648 padding: 0;
1649 1649 }
1650 1650
1651 1651 #content div.box div.traffic div.legend li {
1652 1652 list-style: none;
1653 1653 float: left;
1654 1654 font-size: 11px;
1655 1655 margin: 0;
1656 1656 padding: 0 8px 0 4px;
1657 1657 }
1658 1658
1659 1659 #content div.box div.traffic div.legend li.visits {
1660 1660 border-left: 12px solid #edc240;
1661 1661 }
1662 1662
1663 1663 #content div.box div.traffic div.legend li.pageviews {
1664 1664 border-left: 12px solid #afd8f8;
1665 1665 }
1666 1666
1667 1667 #content div.box div.traffic table {
1668 1668 width: auto;
1669 1669 }
1670 1670
1671 1671 #content div.box div.traffic table td {
1672 1672 background: transparent;
1673 1673 border: none;
1674 1674 padding: 2px 3px 3px;
1675 1675 }
1676 1676
1677 1677 #content div.box div.traffic table td.legendLabel {
1678 1678 padding: 0 3px 2px;
1679 1679 }
1680 1680
1681 1681 #content div.box #summary {
1682 1682 margin-right: 200px;
1683 1683 }
1684 1684
1685 1685 #summary-menu-stats {
1686 1686 float: left;
1687 1687 width: 180px;
1688 1688 position: absolute;
1689 1689 top: 0;
1690 1690 right: 0;
1691 1691 }
1692 1692
1693 1693 #summary-menu-stats ul {
1694 1694 margin: 0 10px;
1695 1695 display: block;
1696 1696 background-color: #f9f9f9;
1697 1697 border: 1px solid #d1d1d1;
1698 1698 border-radius: 4px;
1699 1699 }
1700 1700
1701 1701 #content #summary-menu-stats li {
1702 1702 border-top: 1px solid #d1d1d1;
1703 1703 padding: 0;
1704 1704 }
1705 1705
1706 1706 #content #summary-menu-stats li:hover {
1707 1707 background: #f0f0f0;
1708 1708 }
1709 1709
1710 1710 #content #summary-menu-stats li:first-child {
1711 1711 border-top: none;
1712 1712 }
1713 1713
1714 1714 #summary-menu-stats a.followers { background-image: url('../images/icons/heart.png')}
1715 1715 #summary-menu-stats a.forks { background-image: url('../images/icons/arrow_divide.png')}
1716 1716 #summary-menu-stats a.settings { background-image: url('../images/icons/cog_edit.png')}
1717 1717 #summary-menu-stats a.feed { background-image: url('../images/icons/rss_16.png')}
1718 1718 #summary-menu-stats a.repo-size { background-image: url('../images/icons/server.png')}
1719 1719
1720 1720 #summary-menu-stats a {
1721 1721 display: block;
1722 1722 padding: 12px 30px;
1723 1723 background-repeat: no-repeat;
1724 1724 background-position: 10px 50%;
1725 1725 padding-right: 10px;
1726 1726 }
1727 1727
1728 1728 #repo_size_2.loaded {
1729 1729 margin-left: 30px;
1730 1730 display: block;
1731 1731 padding-right: 10px;
1732 1732 padding-bottom: 7px;
1733 1733 }
1734 1734
1735 1735 #summary-menu-stats a:hover {
1736 1736 text-decoration: none;
1737 1737 }
1738 1738
1739 1739 #summary-menu-stats a span {
1740 1740 background-color: #DEDEDE;
1741 1741 color: 888 !important;
1742 1742 border-radius: 4px;
1743 1743 padding: 2px 4px;
1744 1744 font-size: 10px;
1745 1745 }
1746 1746
1747 1747 #summary .metatag {
1748 1748 display: inline-block;
1749 1749 padding: 3px 5px;
1750 1750 margin-bottom: 3px;
1751 1751 margin-right: 1px;
1752 1752 border-radius: 5px;
1753 1753 }
1754 1754
1755 1755 #content div.box #summary p {
1756 1756 margin-bottom: -5px;
1757 1757 width: 600px;
1758 1758 white-space: pre-wrap;
1759 1759 }
1760 1760
1761 1761 #content div.box #summary p:last-child {
1762 1762 margin-bottom: 9px;
1763 1763 }
1764 1764
1765 1765 #content div.box #summary p:first-of-type {
1766 1766 margin-top: 9px;
1767 1767 }
1768 1768
1769 1769 .metatag {
1770 1770 display: inline-block;
1771 1771 margin-right: 1px;
1772 1772 -webkit-border-radius: 4px 4px 4px 4px;
1773 1773 -khtml-border-radius: 4px 4px 4px 4px;
1774 1774 border-radius: 4px 4px 4px 4px;
1775 1775
1776 1776 border: solid 1px #9CF;
1777 1777 padding: 2px 3px 2px 3px !important;
1778 1778 background-color: #DEF;
1779 1779 }
1780 1780
1781 1781 .metatag[tag="dead"] {
1782 1782 background-color: #E44;
1783 1783 }
1784 1784
1785 1785 .metatag[tag="stale"] {
1786 1786 background-color: #EA4;
1787 1787 }
1788 1788
1789 1789 .metatag[tag="featured"] {
1790 1790 background-color: #AEA;
1791 1791 }
1792 1792
1793 1793 .metatag[tag="requires"] {
1794 1794 background-color: #9CF;
1795 1795 }
1796 1796
1797 1797 .metatag[tag="recommends"] {
1798 1798 background-color: #BDF;
1799 1799 }
1800 1800
1801 1801 .metatag[tag="lang"] {
1802 1802 background-color: #FAF474;
1803 1803 }
1804 1804
1805 1805 .metatag[tag="license"] {
1806 1806 border: solid 1px #9CF;
1807 1807 background-color: #DEF;
1808 1808 target-new: tab !important;
1809 1809 }
1810 1810 .metatag[tag="see"] {
1811 1811 border: solid 1px #CBD;
1812 1812 background-color: #EDF;
1813 1813 }
1814 1814
1815 1815 a.metatag[tag="license"]:hover {
1816 1816 background-color: #003367;
1817 1817 color: #FFF;
1818 1818 text-decoration: none;
1819 1819 }
1820 1820
1821 1821 #summary .desc {
1822 1822 white-space: pre;
1823 1823 width: 100%;
1824 1824 }
1825 1825
1826 1826 #summary .repo_name {
1827 1827 font-size: 1.6em;
1828 1828 font-weight: bold;
1829 1829 vertical-align: baseline;
1830 1830 clear: right
1831 1831 }
1832 1832
1833 1833 #footer {
1834 1834 clear: both;
1835 1835 overflow: hidden;
1836 1836 text-align: right;
1837 1837 margin: 0;
1838 1838 padding: 0 10px 4px;
1839 1839 margin: -10px 0 0;
1840 1840 }
1841 1841
1842 1842 #footer div#footer-inner {
1843 1843 background-color: #003B76;
1844 1844 background-repeat: repeat-x;
1845 1845 background-image: -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1846 1846 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1847 1847 background-image: -ms-linear-gradient( top, #003b76, #00376e);
1848 1848 background-image: -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1849 1849 background-image: -webkit-linear-gradient( top, #003b76, #00376e));
1850 1850 background-image: -o-linear-gradient( top, #003b76, #00376e));
1851 1851 background-image: linear-gradient(to bottom, #003b76, #00376e);
1852 1852 filter: progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1853 1853 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1854 1854 -webkit-border-radius: 4px 4px 4px 4px;
1855 1855 -khtml-border-radius: 4px 4px 4px 4px;
1856 1856 border-radius: 4px 4px 4px 4px;
1857 1857 }
1858 1858
1859 1859 #footer div#footer-inner p {
1860 1860 padding: 15px 25px 15px 0;
1861 1861 color: #FFF;
1862 1862 font-weight: 700;
1863 1863 }
1864 1864
1865 1865 #footer div#footer-inner .footer-link {
1866 1866 float: left;
1867 1867 padding-left: 10px;
1868 1868 }
1869 1869
1870 1870 #footer div#footer-inner .footer-link a, #footer div#footer-inner .footer-link-right a {
1871 1871 color: #FFF;
1872 1872 }
1873 1873
1874 1874 #login div.title {
1875 1875 clear: both;
1876 1876 overflow: hidden;
1877 1877 position: relative;
1878 1878 background-color: #003B76;
1879 1879 background-repeat: repeat-x;
1880 1880 background-image: -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1881 1881 background-image: -moz-linear-gradient( top, #003b76, #00376e);
1882 1882 background-image: -ms-linear-gradient( top, #003b76, #00376e);
1883 1883 background-image: -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1884 1884 background-image: -webkit-linear-gradient( top, #003b76, #00376e));
1885 1885 background-image: -o-linear-gradient( top, #003b76, #00376e));
1886 1886 background-image: linear-gradient(to bottom, #003b76, #00376e);
1887 1887 filter: progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1888 1888 margin: 0 auto;
1889 1889 padding: 0;
1890 1890 }
1891 1891
1892 1892 #login div.inner {
1893 1893 background: #FFF url("../images/login.png") no-repeat top left;
1894 1894 border-top: none;
1895 1895 border-bottom: none;
1896 1896 margin: 0 auto;
1897 1897 padding: 20px;
1898 1898 }
1899 1899
1900 1900 #login div.form div.fields div.field div.label {
1901 1901 width: 173px;
1902 1902 float: left;
1903 1903 text-align: right;
1904 1904 margin: 2px 10px 0 0;
1905 1905 padding: 5px 0 0 5px;
1906 1906 }
1907 1907
1908 1908 #login div.form div.fields div.field div.input input {
1909 1909 background: #FFF;
1910 1910 border-top: 1px solid #b3b3b3;
1911 1911 border-left: 1px solid #b3b3b3;
1912 1912 border-right: 1px solid #eaeaea;
1913 1913 border-bottom: 1px solid #eaeaea;
1914 1914 color: #000;
1915 1915 font-size: 11px;
1916 1916 margin: 0;
1917 1917 padding: 7px 7px 6px;
1918 1918 }
1919 1919
1920 1920 #login div.form div.fields div.buttons {
1921 1921 clear: both;
1922 1922 overflow: hidden;
1923 1923 border-top: 1px solid #DDD;
1924 1924 text-align: right;
1925 1925 margin: 0;
1926 1926 padding: 10px 0 0;
1927 1927 }
1928 1928
1929 1929 #login div.form div.links {
1930 1930 clear: both;
1931 1931 overflow: hidden;
1932 1932 margin: 10px 0 0;
1933 1933 padding: 0 0 2px;
1934 1934 }
1935 1935
1936 1936 .user-menu {
1937 1937 margin: 0px !important;
1938 1938 float: left;
1939 1939 }
1940 1940
1941 1941 .user-menu .container {
1942 1942 padding: 0px 4px 0px 4px;
1943 1943 margin: 0px 0px 0px 0px;
1944 1944 }
1945 1945
1946 1946 .user-menu .gravatar {
1947 1947 margin: 0px 0px 0px 0px;
1948 1948 cursor: pointer;
1949 1949 }
1950 1950 .user-menu .gravatar.enabled {
1951 1951 background-color: #FDF784 !important;
1952 1952 }
1953 1953 .user-menu .gravatar:hover {
1954 1954 background-color: #FDF784 !important;
1955 1955 }
1956 1956 #quick_login {
1957 1957 min-height: 110px;
1958 1958 padding: 4px;
1959 1959 position: absolute;
1960 1960 right: 0;
1961 1961 background-color: #003B76;
1962 1962 background-repeat: repeat-x;
1963 1963 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
1964 1964 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1965 1965 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1966 1966 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
1967 1967 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
1968 1968 background-image: -o-linear-gradient(top, #003b76, #00376e);
1969 1969 background-image: linear-gradient(to bottom, #003b76, #00376e);
1970 1970 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
1971 1971
1972 1972 z-index: 999;
1973 1973 -webkit-border-radius: 0px 0px 4px 4px;
1974 1974 -khtml-border-radius: 0px 0px 4px 4px;
1975 1975 border-radius: 0px 0px 4px 4px;
1976 1976 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1977 1977
1978 1978 overflow: hidden;
1979 1979 }
1980 1980 #quick_login h4 {
1981 1981 color: #fff;
1982 1982 padding: 5px 0px 5px 14px;
1983 1983 }
1984 1984
1985 1985 #quick_login .password_forgoten {
1986 1986 padding-right: 0px;
1987 1987 padding-top: 0px;
1988 1988 text-align: left;
1989 1989 }
1990 1990
1991 1991 #quick_login .password_forgoten a {
1992 1992 font-size: 10px;
1993 1993 color: #fff;
1994 1994 padding: 0px !important;
1995 1995 line-height: 20px !important;
1996 1996 }
1997 1997
1998 1998 #quick_login .register {
1999 1999 padding-right: 10px;
2000 2000 padding-top: 5px;
2001 2001 text-align: left;
2002 2002 }
2003 2003
2004 2004 #quick_login .register a {
2005 2005 font-size: 10px;
2006 2006 color: #fff;
2007 2007 padding: 0px !important;
2008 2008 line-height: 20px !important;
2009 2009 }
2010 2010
2011 2011 #quick_login .submit {
2012 2012 margin: -20px 0 0 0px;
2013 2013 position: absolute;
2014 2014 right: 15px;
2015 2015 }
2016 2016
2017 2017 #quick_login .links_left {
2018 2018 float: left;
2019 2019 margin-right: 130px;
2020 2020 width: 170px;
2021 2021 }
2022 2022 #quick_login .links_right {
2023 2023
2024 2024 position: absolute;
2025 2025 right: 0;
2026 2026 }
2027 2027 #quick_login .full_name {
2028 2028 color: #FFFFFF;
2029 2029 font-weight: bold;
2030 2030 padding: 3px 3px 3px 6px;
2031 2031 }
2032 2032 #quick_login .big_gravatar {
2033 2033 padding: 4px 0px 0px 6px;
2034 2034 }
2035 2035 #quick_login .notifications {
2036 2036 padding: 2px 0px 0px 6px;
2037 2037 color: #FFFFFF;
2038 2038 font-weight: bold;
2039 2039 line-height: 10px !important;
2040 2040 }
2041 2041 #quick_login .notifications a,
2042 2042 #quick_login .unread a {
2043 2043 color: #FFFFFF;
2044 2044 display: block;
2045 2045 padding: 0px !important;
2046 2046 }
2047 2047 #quick_login .notifications a:hover,
2048 2048 #quick_login .unread a:hover {
2049 2049 background-color: inherit !important;
2050 2050 }
2051 2051 #quick_login .email, #quick_login .unread {
2052 2052 color: #FFFFFF;
2053 2053 padding: 3px 3px 3px 6px;
2054 2054 }
2055 2055 #quick_login .links .logout {
2056 2056 }
2057 2057
2058 2058 #quick_login div.form div.fields {
2059 2059 padding-top: 2px;
2060 2060 padding-left: 10px;
2061 2061 }
2062 2062
2063 2063 #quick_login div.form div.fields div.field {
2064 2064 padding: 5px;
2065 2065 }
2066 2066
2067 2067 #quick_login div.form div.fields div.field div.label label {
2068 2068 color: #fff;
2069 2069 padding-bottom: 3px;
2070 2070 }
2071 2071
2072 2072 #quick_login div.form div.fields div.field div.input input {
2073 2073 width: 236px;
2074 2074 background: #FFF;
2075 2075 border-top: 1px solid #b3b3b3;
2076 2076 border-left: 1px solid #b3b3b3;
2077 2077 border-right: 1px solid #eaeaea;
2078 2078 border-bottom: 1px solid #eaeaea;
2079 2079 color: #000;
2080 2080 font-size: 11px;
2081 2081 margin: 0;
2082 2082 padding: 5px 7px 4px;
2083 2083 }
2084 2084
2085 2085 #quick_login div.form div.fields div.buttons {
2086 2086 clear: both;
2087 2087 overflow: hidden;
2088 2088 text-align: right;
2089 2089 margin: 0;
2090 2090 padding: 5px 14px 0px 5px;
2091 2091 }
2092 2092
2093 2093 #quick_login div.form div.links {
2094 2094 clear: both;
2095 2095 overflow: hidden;
2096 2096 margin: 10px 0 0;
2097 2097 padding: 0 0 2px;
2098 2098 }
2099 2099
2100 2100 #quick_login ol.links {
2101 2101 display: block;
2102 2102 font-weight: bold;
2103 2103 list-style: none outside none;
2104 2104 text-align: right;
2105 2105 }
2106 2106 #quick_login ol.links li {
2107 2107 line-height: 27px;
2108 2108 margin: 0;
2109 2109 padding: 0;
2110 2110 color: #fff;
2111 2111 display: block;
2112 2112 float: none !important;
2113 2113 }
2114 2114
2115 2115 #quick_login ol.links li a {
2116 2116 color: #fff;
2117 2117 display: block;
2118 2118 padding: 2px;
2119 2119 }
2120 2120 #quick_login ol.links li a:HOVER {
2121 2121 background-color: inherit !important;
2122 2122 }
2123 2123
2124 2124 #register div.title {
2125 2125 clear: both;
2126 2126 overflow: hidden;
2127 2127 position: relative;
2128 2128 background-color: #003B76;
2129 2129 background-repeat: repeat-x;
2130 2130 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2131 2131 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2132 2132 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2133 2133 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2134 2134 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2135 2135 background-image: -o-linear-gradient(top, #003b76, #00376e);
2136 2136 background-image: linear-gradient(to bottom, #003b76, #00376e);
2137 2137 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
2138 2138 endColorstr='#00376e', GradientType=0 );
2139 2139 margin: 0 auto;
2140 2140 padding: 0;
2141 2141 }
2142 2142
2143 2143 #register div.inner {
2144 2144 background: #FFF;
2145 2145 border-top: none;
2146 2146 border-bottom: none;
2147 2147 margin: 0 auto;
2148 2148 padding: 20px;
2149 2149 }
2150 2150
2151 2151 #register div.form div.fields div.field div.label {
2152 2152 width: 135px;
2153 2153 float: left;
2154 2154 text-align: right;
2155 2155 margin: 2px 10px 0 0;
2156 2156 padding: 5px 0 0 5px;
2157 2157 }
2158 2158
2159 2159 #register div.form div.fields div.field div.input input {
2160 2160 width: 300px;
2161 2161 background: #FFF;
2162 2162 border-top: 1px solid #b3b3b3;
2163 2163 border-left: 1px solid #b3b3b3;
2164 2164 border-right: 1px solid #eaeaea;
2165 2165 border-bottom: 1px solid #eaeaea;
2166 2166 color: #000;
2167 2167 font-size: 11px;
2168 2168 margin: 0;
2169 2169 padding: 7px 7px 6px;
2170 2170 }
2171 2171
2172 2172 #register div.form div.fields div.buttons {
2173 2173 clear: both;
2174 2174 overflow: hidden;
2175 2175 border-top: 1px solid #DDD;
2176 2176 text-align: left;
2177 2177 margin: 0;
2178 2178 padding: 10px 0 0 150px;
2179 2179 }
2180 2180
2181 2181 #register div.form div.activation_msg {
2182 2182 padding-top: 4px;
2183 2183 padding-bottom: 4px;
2184 2184 }
2185 2185
2186 2186 #journal .journal_day {
2187 2187 font-size: 20px;
2188 2188 padding: 10px 0px;
2189 2189 border-bottom: 2px solid #DDD;
2190 2190 margin-left: 10px;
2191 2191 margin-right: 10px;
2192 2192 }
2193 2193
2194 2194 #journal .journal_container {
2195 2195 padding: 5px;
2196 2196 clear: both;
2197 2197 margin: 0px 5px 0px 10px;
2198 2198 }
2199 2199
2200 2200 #journal .journal_action_container {
2201 2201 padding-left: 38px;
2202 2202 }
2203 2203
2204 2204 #journal .journal_user {
2205 2205 color: #747474;
2206 2206 font-size: 14px;
2207 2207 font-weight: bold;
2208 2208 height: 30px;
2209 2209 }
2210 2210
2211 2211 #journal .journal_user.deleted {
2212 2212 color: #747474;
2213 2213 font-size: 14px;
2214 2214 font-weight: normal;
2215 2215 height: 30px;
2216 2216 font-style: italic;
2217 2217 }
2218 2218
2219 2219
2220 2220 #journal .journal_icon {
2221 2221 clear: both;
2222 2222 float: left;
2223 2223 padding-right: 4px;
2224 2224 padding-top: 3px;
2225 2225 }
2226 2226
2227 2227 #journal .journal_action {
2228 2228 padding-top: 4px;
2229 2229 min-height: 2px;
2230 2230 float: left
2231 2231 }
2232 2232
2233 2233 #journal .journal_action_params {
2234 2234 clear: left;
2235 2235 padding-left: 22px;
2236 2236 }
2237 2237
2238 2238 #journal .journal_repo {
2239 2239 float: left;
2240 2240 margin-left: 6px;
2241 2241 padding-top: 3px;
2242 2242 }
2243 2243
2244 2244 #journal .date {
2245 2245 clear: both;
2246 2246 color: #777777;
2247 2247 font-size: 11px;
2248 2248 padding-left: 22px;
2249 2249 }
2250 2250
2251 2251 #journal .journal_repo .journal_repo_name {
2252 2252 font-weight: bold;
2253 2253 font-size: 1.1em;
2254 2254 }
2255 2255
2256 2256 #journal .compare_view {
2257 2257 padding: 5px 0px 5px 0px;
2258 2258 width: 95px;
2259 2259 }
2260 2260
2261 2261 .journal_highlight {
2262 2262 font-weight: bold;
2263 2263 padding: 0 2px;
2264 2264 vertical-align: bottom;
2265 2265 }
2266 2266
2267 2267 .trending_language_tbl, .trending_language_tbl td {
2268 2268 border: 0 !important;
2269 2269 margin: 0 !important;
2270 2270 padding: 0 !important;
2271 2271 }
2272 2272
2273 2273 .trending_language_tbl, .trending_language_tbl tr {
2274 2274 border-spacing: 1px;
2275 2275 }
2276 2276
2277 2277 .trending_language {
2278 2278 background-color: #003367;
2279 2279 color: #FFF;
2280 2280 display: block;
2281 2281 min-width: 20px;
2282 2282 text-decoration: none;
2283 2283 height: 12px;
2284 2284 margin-bottom: 0px;
2285 2285 margin-left: 5px;
2286 2286 white-space: pre;
2287 2287 padding: 3px;
2288 2288 }
2289 2289
2290 2290 h3.files_location {
2291 2291 font-size: 1.8em;
2292 2292 font-weight: 700;
2293 2293 border-bottom: none !important;
2294 2294 margin: 10px 0 !important;
2295 2295 }
2296 2296
2297 2297 #files_data dl dt {
2298 2298 float: left;
2299 2299 width: 60px;
2300 2300 margin: 0 !important;
2301 2301 padding: 5px;
2302 2302 }
2303 2303
2304 2304 #files_data dl dd {
2305 2305 margin: 0 !important;
2306 2306 padding: 5px !important;
2307 2307 }
2308 2308
2309 2309 .file_history {
2310 2310 padding-top: 10px;
2311 2311 font-size: 16px;
2312 2312 }
2313 2313 .file_author {
2314 2314 float: left;
2315 2315 }
2316 2316
2317 2317 .file_author .item {
2318 2318 float: left;
2319 2319 padding: 5px;
2320 2320 color: #888;
2321 2321 }
2322 2322
2323 2323 .tablerow0 {
2324 2324 background-color: #F8F8F8;
2325 2325 }
2326 2326
2327 2327 .tablerow1 {
2328 2328 background-color: #FFFFFF;
2329 2329 }
2330 2330
2331 2331 .changeset_id {
2332 2332 color: #666666;
2333 2333 margin-right: -3px;
2334 2334 }
2335 2335
2336 2336 .changeset_hash {
2337 2337 color: #000000;
2338 2338 }
2339 2339
2340 2340 #changeset_content {
2341 2341 border-left: 1px solid #CCC;
2342 2342 border-right: 1px solid #CCC;
2343 2343 border-bottom: 1px solid #CCC;
2344 2344 padding: 5px;
2345 2345 }
2346 2346
2347 2347 #changeset_compare_view_content {
2348 2348 border: 1px solid #CCC;
2349 2349 padding: 5px;
2350 2350 }
2351 2351
2352 2352 #changeset_content .container {
2353 2353 min-height: 100px;
2354 2354 font-size: 1.2em;
2355 2355 overflow: hidden;
2356 2356 }
2357 2357
2358 2358 #changeset_compare_view_content .compare_view_commits {
2359 2359 width: auto !important;
2360 2360 }
2361 2361
2362 2362 #changeset_compare_view_content .compare_view_commits td {
2363 2363 padding: 0px 0px 0px 12px !important;
2364 2364 }
2365 2365
2366 2366 #changeset_content .container .right {
2367 2367 float: right;
2368 2368 width: 20%;
2369 2369 text-align: right;
2370 2370 }
2371 2371
2372 2372 #changeset_content .container .message {
2373 2373 white-space: pre-wrap;
2374 2374 }
2375 2375 #changeset_content .container .message a:hover {
2376 2376 text-decoration: none;
2377 2377 }
2378 2378 .cs_files .cur_cs {
2379 2379 margin: 10px 2px;
2380 2380 font-weight: bold;
2381 2381 }
2382 2382
2383 2383 .cs_files .node {
2384 2384 float: left;
2385 2385 }
2386 2386
2387 2387 .cs_files .changes {
2388 2388 float: right;
2389 2389 color: #003367;
2390 2390 }
2391 2391
2392 2392 .cs_files .changes .added {
2393 2393 background-color: #BBFFBB;
2394 2394 float: left;
2395 2395 text-align: center;
2396 2396 font-size: 9px;
2397 2397 padding: 2px 0px 2px 0px;
2398 2398 }
2399 2399
2400 2400 .cs_files .changes .deleted {
2401 2401 background-color: #FF8888;
2402 2402 float: left;
2403 2403 text-align: center;
2404 2404 font-size: 9px;
2405 2405 padding: 2px 0px 2px 0px;
2406 2406 }
2407 /*new binary*/
2408 .cs_files .changes .bin1 {
2407 /*new binary
2408 NEW_FILENODE = 1
2409 DEL_FILENODE = 2
2410 MOD_FILENODE = 3
2411 RENAMED_FILENODE = 4
2412 CHMOD_FILENODE = 5
2413 BIN_FILENODE = 6
2414 */
2415 .cs_files .changes .bin {
2409 2416 background-color: #BBFFBB;
2410 2417 float: left;
2411 2418 text-align: center;
2412 2419 font-size: 9px;
2413 2420 padding: 2px 0px 2px 0px;
2414 2421 }
2422 .cs_files .changes .bin.bin1 {
2423 background-color: #BBFFBB;
2424 }
2415 2425
2416 2426 /*deleted binary*/
2417 .cs_files .changes .bin2 {
2427 .cs_files .changes .bin.bin2 {
2418 2428 background-color: #FF8888;
2419 float: left;
2420 text-align: center;
2421 font-size: 9px;
2422 padding: 2px 0px 2px 0px;
2423 2429 }
2424 2430
2425 2431 /*mod binary*/
2426 .cs_files .changes .bin3 {
2432 .cs_files .changes .bin.bin3 {
2427 2433 background-color: #DDDDDD;
2428 float: left;
2429 text-align: center;
2430 font-size: 9px;
2431 padding: 2px 0px 2px 0px;
2432 2434 }
2433 2435
2434 2436 /*rename file*/
2435 .cs_files .changes .bin4 {
2437 .cs_files .changes .bin.bin4 {
2438 background-color: #6D99FF;
2439 }
2440
2441 /*rename file*/
2442 .cs_files .changes .bin.bin4 {
2436 2443 background-color: #6D99FF;
2437 float: left;
2438 text-align: center;
2439 font-size: 9px;
2440 padding: 2px 0px 2px 0px;
2441 }
2442
2444 }
2445
2446 /*chmod file*/
2447 .cs_files .changes .bin.bin5 {
2448 background-color: #6D99FF;
2449 }
2443 2450
2444 2451 .cs_files .cs_added, .cs_files .cs_A {
2445 2452 background: url("../images/icons/page_white_add.png") no-repeat scroll
2446 2453 3px;
2447 2454 height: 16px;
2448 2455 padding-left: 20px;
2449 2456 margin-top: 7px;
2450 2457 text-align: left;
2451 2458 }
2452 2459
2453 2460 .cs_files .cs_changed, .cs_files .cs_M {
2454 2461 background: url("../images/icons/page_white_edit.png") no-repeat scroll
2455 2462 3px;
2456 2463 height: 16px;
2457 2464 padding-left: 20px;
2458 2465 margin-top: 7px;
2459 2466 text-align: left;
2460 2467 }
2461 2468
2462 2469 .cs_files .cs_removed, .cs_files .cs_D {
2463 2470 background: url("../images/icons/page_white_delete.png") no-repeat
2464 2471 scroll 3px;
2465 2472 height: 16px;
2466 2473 padding-left: 20px;
2467 2474 margin-top: 7px;
2468 2475 text-align: left;
2469 2476 }
2470 2477
2471 2478 .table {
2472 2479 position: relative;
2473 2480 }
2474 2481
2475 2482 #graph {
2476 2483 position: relative;
2477 2484 overflow: hidden;
2478 2485 }
2479 2486
2480 2487 #graph_nodes {
2481 2488 position: absolute;
2482 2489 }
2483 2490
2484 2491 #graph_content,
2485 2492 #graph .info_box,
2486 2493 #graph .container_header {
2487 2494 margin-left: 100px;
2488 2495 }
2489 2496
2490 2497 #graph_content {
2491 2498 position: relative;
2492 2499 }
2493 2500
2494 2501 #graph .container_header {
2495 2502 padding: 10px;
2496 2503 height: 25px;
2497 2504 }
2498 2505
2499 2506 #graph_content #rev_range_container {
2500 2507 float: left;
2501 2508 margin: 0px 0px 0px 3px;
2502 2509 }
2503 2510
2504 2511 #graph_content #rev_range_clear {
2505 2512 float: left;
2506 2513 margin: 0px 0px 0px 3px;
2507 2514 }
2508 2515
2509 2516 #graph_content #changesets {
2510 2517 table-layout: fixed;
2511 2518 border-collapse: collapse;
2512 2519 border-left: none;
2513 2520 border-right: none;
2514 2521 border-color: #cdcdcd;
2515 2522 }
2516 2523
2517 2524 #graph_content #changesets td {
2518 2525 overflow: hidden;
2519 2526 text-overflow: ellipsis;
2520 2527 white-space: nowrap;
2521 2528 height: 31px;
2522 2529 border-color: #cdcdcd;
2523 2530 text-align: left;
2524 2531 }
2525 2532
2526 2533 #graph_content .container .checkbox {
2527 2534 width: 12px;
2528 2535 font-size: 0.85em;
2529 2536 }
2530 2537
2531 2538 #graph_content .container .status {
2532 2539 width: 14px;
2533 2540 font-size: 0.85em;
2534 2541 }
2535 2542
2536 2543 #graph_content .container .author {
2537 2544 width: 105px;
2538 2545 }
2539 2546
2540 2547 #graph_content .container .hash {
2541 2548 width: 100px;
2542 2549 font-size: 0.85em;
2543 2550 }
2544 2551
2545 2552 #graph_content #changesets .container .date {
2546 2553 width: 76px;
2547 2554 color: #666;
2548 2555 font-size: 10px;
2549 2556 }
2550 2557
2551 2558 #graph_content #changesets .container .right {
2552 2559 width: 120px;
2553 2560 padding-right: 0px;
2554 2561 overflow: visible;
2555 2562 position: relative;
2556 2563 }
2557 2564
2558 2565 #graph_content .container .mid {
2559 2566 padding: 0;
2560 2567 }
2561 2568
2562 2569 #graph_content .log-container {
2563 2570 position: relative;
2564 2571 }
2565 2572
2566 2573 #graph_content .container .changeset_range {
2567 2574 float: left;
2568 2575 margin: 6px 3px;
2569 2576 }
2570 2577
2571 2578 #graph_content .container .author img {
2572 2579 vertical-align: middle;
2573 2580 }
2574 2581
2575 2582 #graph_content .container .author .user {
2576 2583 color: #444444;
2577 2584 }
2578 2585
2579 2586 #graph_content .container .mid .message {
2580 2587 white-space: pre-wrap;
2581 2588 padding: 0;
2582 2589 overflow: hidden;
2583 2590 height: 1.1em;
2584 2591 }
2585 2592
2586 2593 #graph_content .container .extra-container {
2587 2594 display: block;
2588 2595 position: absolute;
2589 2596 top: -15px;
2590 2597 right: 0;
2591 2598 padding-left: 5px;
2592 2599 background: #FFFFFF;
2593 2600 height: 41px;
2594 2601 }
2595 2602
2596 2603 #graph_content .comments-container,
2597 2604 #shortlog_data .comments-container,
2598 2605 #graph_content .logtags {
2599 2606 display: block;
2600 2607 float: left;
2601 2608 overflow: hidden;
2602 2609 padding: 0;
2603 2610 margin: 0;
2604 2611 }
2605 2612
2606 2613 #graph_content .comments-container {
2607 2614 margin: 0.8em 0;
2608 2615 margin-right: 0.5em;
2609 2616 }
2610 2617
2611 2618 #graph_content .tagcontainer {
2612 2619 width: 80px;
2613 2620 position: relative;
2614 2621 float: right;
2615 2622 height: 100%;
2616 2623 top: 7px;
2617 2624 margin-left: 0.5em;
2618 2625 }
2619 2626
2620 2627 #graph_content .logtags {
2621 2628 min-width: 80px;
2622 2629 height: 1.1em;
2623 2630 position: absolute;
2624 2631 left: 0px;
2625 2632 width: auto;
2626 2633 top: 0px;
2627 2634 }
2628 2635
2629 2636 #graph_content .logtags.tags {
2630 2637 top: 14px;
2631 2638 }
2632 2639
2633 2640 #graph_content .logtags:hover {
2634 2641 overflow: visible;
2635 2642 position: absolute;
2636 2643 width: auto;
2637 2644 right: 0;
2638 2645 left: initial;
2639 2646 }
2640 2647
2641 2648 #graph_content .logtags .booktag,
2642 2649 #graph_content .logtags .tagtag {
2643 2650 float: left;
2644 2651 line-height: 1em;
2645 2652 margin-bottom: 1px;
2646 2653 margin-right: 1px;
2647 2654 padding: 1px 3px;
2648 2655 font-size: 10px;
2649 2656 }
2650 2657
2651 2658 #graph_content .container .mid .message a:hover {
2652 2659 text-decoration: none;
2653 2660 }
2654 2661
2655 2662 .revision-link {
2656 2663 color: #3F6F9F;
2657 2664 font-weight: bold !important;
2658 2665 }
2659 2666
2660 2667 .issue-tracker-link {
2661 2668 color: #3F6F9F;
2662 2669 font-weight: bold !important;
2663 2670 }
2664 2671
2665 2672 .changeset-status-container {
2666 2673 padding-right: 5px;
2667 2674 margin-top: 1px;
2668 2675 float: right;
2669 2676 height: 14px;
2670 2677 }
2671 2678 .code-header .changeset-status-container {
2672 2679 float: left;
2673 2680 padding: 2px 0px 0px 2px;
2674 2681 }
2675 2682 .changeset-status-container .changeset-status-lbl {
2676 2683 color: rgb(136, 136, 136);
2677 2684 float: left;
2678 2685 padding: 3px 4px 0px 0px
2679 2686 }
2680 2687 .code-header .changeset-status-container .changeset-status-lbl {
2681 2688 float: left;
2682 2689 padding: 0px 4px 0px 0px;
2683 2690 }
2684 2691 .changeset-status-container .changeset-status-ico {
2685 2692 float: left;
2686 2693 }
2687 2694 .code-header .changeset-status-container .changeset-status-ico, .container .changeset-status-ico {
2688 2695 float: left;
2689 2696 }
2690 2697
2691 2698 #graph_content .comments-cnt {
2692 2699 color: rgb(136, 136, 136);
2693 2700 padding: 5px 0;
2694 2701 }
2695 2702
2696 2703 #shortlog_data .comments-cnt {
2697 2704 color: rgb(136, 136, 136);
2698 2705 padding: 3px 0;
2699 2706 }
2700 2707
2701 2708 #graph_content .comments-cnt a,
2702 2709 #shortlog_data .comments-cnt a {
2703 2710 background-image: url('../images/icons/comments.png');
2704 2711 background-repeat: no-repeat;
2705 2712 background-position: 100% 50%;
2706 2713 padding: 5px 0;
2707 2714 padding-right: 20px;
2708 2715 }
2709 2716
2710 2717 .right .changes {
2711 2718 clear: both;
2712 2719 }
2713 2720
2714 2721 .right .changes .changed_total {
2715 2722 display: block;
2716 2723 float: right;
2717 2724 text-align: center;
2718 2725 min-width: 45px;
2719 2726 cursor: pointer;
2720 2727 color: #444444;
2721 2728 background: #FEA;
2722 2729 -webkit-border-radius: 0px 0px 0px 6px;
2723 2730 border-radius: 0px 0px 0px 6px;
2724 2731 padding: 1px;
2725 2732 }
2726 2733
2727 2734 .right .changes .added, .changed, .removed {
2728 2735 display: block;
2729 2736 padding: 1px;
2730 2737 color: #444444;
2731 2738 float: right;
2732 2739 text-align: center;
2733 2740 min-width: 15px;
2734 2741 }
2735 2742
2736 2743 .right .changes .added {
2737 2744 background: #CFC;
2738 2745 }
2739 2746
2740 2747 .right .changes .changed {
2741 2748 background: #FEA;
2742 2749 }
2743 2750
2744 2751 .right .changes .removed {
2745 2752 background: #FAA;
2746 2753 }
2747 2754
2748 2755 .right .merge {
2749 2756 padding: 1px 3px 1px 3px;
2750 2757 background-color: #fca062;
2751 2758 font-size: 10px;
2752 2759 color: #ffffff;
2753 2760 text-transform: uppercase;
2754 2761 white-space: nowrap;
2755 2762 -webkit-border-radius: 3px;
2756 2763 border-radius: 3px;
2757 2764 margin-right: 2px;
2758 2765 }
2759 2766
2760 2767 .right .parent {
2761 2768 color: #666666;
2762 2769 clear: both;
2763 2770 }
2764 2771 .right .logtags {
2765 2772 line-height: 2.2em;
2766 2773 }
2767 2774 .branchtag, .logtags .tagtag, .logtags .booktag {
2768 2775 margin: 0px 2px;
2769 2776 }
2770 2777
2771 2778 .branchtag,
2772 2779 .tagtag,
2773 2780 .booktag,
2774 2781 .spantag {
2775 2782 padding: 1px 3px 1px 3px;
2776 2783 font-size: 10px;
2777 2784 color: #336699;
2778 2785 white-space: nowrap;
2779 2786 -webkit-border-radius: 4px;
2780 2787 border-radius: 4px;
2781 2788 border: 1px solid #d9e8f8;
2782 2789 line-height: 1.5em;
2783 2790 }
2784 2791
2785 2792 #graph_content .branchtag,
2786 2793 #graph_content .tagtag,
2787 2794 #graph_content .booktag {
2788 2795 margin: 1.1em 0;
2789 2796 margin-right: 0.5em;
2790 2797 }
2791 2798
2792 2799 .branchtag,
2793 2800 .tagtag,
2794 2801 .booktag {
2795 2802 float: left;
2796 2803 }
2797 2804
2798 2805 .right .logtags .branchtag,
2799 2806 .right .logtags .tagtag,
2800 2807 .right .logtags .booktag,
2801 2808 .right .merge {
2802 2809 float: right;
2803 2810 line-height: 1em;
2804 2811 margin: 1px 1px !important;
2805 2812 display: block;
2806 2813 }
2807 2814
2808 2815 .booktag {
2809 2816 border-color: #46A546;
2810 2817 color: #46A546;
2811 2818 }
2812 2819
2813 2820 .tagtag {
2814 2821 border-color: #62cffc;
2815 2822 color: #62cffc;
2816 2823 }
2817 2824
2818 2825 .logtags .branchtag a:hover,
2819 2826 .logtags .branchtag a,
2820 2827 .branchtag a,
2821 2828 .branchtag a:hover {
2822 2829 text-decoration: none;
2823 2830 color: inherit;
2824 2831 }
2825 2832 .logtags .tagtag {
2826 2833 padding: 1px 3px 1px 3px;
2827 2834 background-color: #62cffc;
2828 2835 font-size: 10px;
2829 2836 color: #ffffff;
2830 2837 white-space: nowrap;
2831 2838 -webkit-border-radius: 3px;
2832 2839 border-radius: 3px;
2833 2840 }
2834 2841
2835 2842 .tagtag a,
2836 2843 .tagtag a:hover,
2837 2844 .logtags .tagtag a,
2838 2845 .logtags .tagtag a:hover {
2839 2846 text-decoration: none;
2840 2847 color: inherit;
2841 2848 }
2842 2849 .logbooks .booktag, .logbooks .booktag, .logtags .booktag, .logtags .booktag {
2843 2850 padding: 1px 3px 1px 3px;
2844 2851 background-color: #46A546;
2845 2852 font-size: 10px;
2846 2853 color: #ffffff;
2847 2854 white-space: nowrap;
2848 2855 -webkit-border-radius: 3px;
2849 2856 border-radius: 3px;
2850 2857 }
2851 2858 .logbooks .booktag, .logbooks .booktag a, .right .logtags .booktag, .logtags .booktag a {
2852 2859 color: #ffffff;
2853 2860 }
2854 2861
2855 2862 .logbooks .booktag, .logbooks .booktag a:hover,
2856 2863 .logtags .booktag, .logtags .booktag a:hover,
2857 2864 .booktag a,
2858 2865 .booktag a:hover {
2859 2866 text-decoration: none;
2860 2867 color: inherit;
2861 2868 }
2862 2869 div.browserblock {
2863 2870 overflow: hidden;
2864 2871 border: 1px solid #ccc;
2865 2872 background: #f8f8f8;
2866 2873 font-size: 100%;
2867 2874 line-height: 125%;
2868 2875 padding: 0;
2869 2876 -webkit-border-radius: 6px 6px 0px 0px;
2870 2877 border-radius: 6px 6px 0px 0px;
2871 2878 }
2872 2879
2873 2880 div.browserblock .browser-header {
2874 2881 background: #FFF;
2875 2882 padding: 10px 0px 15px 0px;
2876 2883 width: 100%;
2877 2884 }
2878 2885
2879 2886 div.browserblock .browser-nav {
2880 2887 float: left
2881 2888 }
2882 2889
2883 2890 div.browserblock .browser-branch {
2884 2891 float: left;
2885 2892 }
2886 2893
2887 2894 div.browserblock .browser-branch label {
2888 2895 color: #4A4A4A;
2889 2896 vertical-align: text-top;
2890 2897 }
2891 2898
2892 2899 div.browserblock .browser-header span {
2893 2900 margin-left: 5px;
2894 2901 font-weight: 700;
2895 2902 }
2896 2903
2897 2904 div.browserblock .browser-search {
2898 2905 clear: both;
2899 2906 padding: 8px 8px 0px 5px;
2900 2907 height: 20px;
2901 2908 }
2902 2909
2903 2910 div.browserblock #node_filter_box {
2904 2911 }
2905 2912
2906 2913 div.browserblock .search_activate {
2907 2914 float: left
2908 2915 }
2909 2916
2910 2917 div.browserblock .add_node {
2911 2918 float: left;
2912 2919 padding-left: 5px;
2913 2920 }
2914 2921
2915 2922 div.browserblock .search_activate a:hover, div.browserblock .add_node a:hover {
2916 2923 text-decoration: none !important;
2917 2924 }
2918 2925
2919 2926 div.browserblock .browser-body {
2920 2927 background: #EEE;
2921 2928 border-top: 1px solid #CCC;
2922 2929 }
2923 2930
2924 2931 table.code-browser {
2925 2932 border-collapse: collapse;
2926 2933 width: 100%;
2927 2934 }
2928 2935
2929 2936 table.code-browser tr {
2930 2937 margin: 3px;
2931 2938 }
2932 2939
2933 2940 table.code-browser thead th {
2934 2941 background-color: #EEE;
2935 2942 height: 20px;
2936 2943 font-size: 1.1em;
2937 2944 font-weight: 700;
2938 2945 text-align: left;
2939 2946 padding-left: 10px;
2940 2947 }
2941 2948
2942 2949 table.code-browser tbody td {
2943 2950 padding-left: 10px;
2944 2951 height: 20px;
2945 2952 }
2946 2953
2947 2954 table.code-browser .browser-file {
2948 2955 background: url("../images/icons/document_16.png") no-repeat scroll 3px;
2949 2956 height: 16px;
2950 2957 padding-left: 20px;
2951 2958 text-align: left;
2952 2959 }
2953 2960 .diffblock .changeset_header {
2954 2961 height: 16px;
2955 2962 }
2956 2963 .diffblock .changeset_file {
2957 2964 background: url("../images/icons/file.png") no-repeat scroll 3px;
2958 2965 text-align: left;
2959 2966 float: left;
2960 2967 padding: 2px 0px 2px 22px;
2961 2968 }
2962 2969 .diffblock .diff-menu-wrapper {
2963 2970 float: left;
2964 2971 }
2965 2972
2966 2973 .diffblock .diff-menu {
2967 2974 position: absolute;
2968 2975 background: none repeat scroll 0 0 #FFFFFF;
2969 2976 border-color: #003367 #666666 #666666;
2970 2977 border-right: 1px solid #666666;
2971 2978 border-style: solid solid solid;
2972 2979 border-width: 1px;
2973 2980 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
2974 2981 margin-top: 5px;
2975 2982 margin-left: 1px;
2976 2983
2977 2984 }
2978 2985 .diffblock .diff-actions {
2979 2986 padding: 2px 0px 0px 2px;
2980 2987 float: left;
2981 2988 }
2982 2989 .diffblock .diff-menu ul li {
2983 2990 padding: 0px 0px 0px 0px !important;
2984 2991 }
2985 2992 .diffblock .diff-menu ul li a {
2986 2993 display: block;
2987 2994 padding: 3px 8px 3px 8px !important;
2988 2995 }
2989 2996 .diffblock .diff-menu ul li a:hover {
2990 2997 text-decoration: none;
2991 2998 background-color: #EEEEEE;
2992 2999 }
2993 3000 table.code-browser .browser-dir {
2994 3001 background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
2995 3002 height: 16px;
2996 3003 padding-left: 20px;
2997 3004 text-align: left;
2998 3005 }
2999 3006
3000 3007 table.code-browser .submodule-dir {
3001 3008 background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
3002 3009 height: 16px;
3003 3010 padding-left: 20px;
3004 3011 text-align: left;
3005 3012 }
3006 3013
3007 3014
3008 3015 .box .search {
3009 3016 clear: both;
3010 3017 overflow: hidden;
3011 3018 margin: 0;
3012 3019 padding: 0 20px 10px;
3013 3020 }
3014 3021
3015 3022 .box .search div.search_path {
3016 3023 background: none repeat scroll 0 0 #EEE;
3017 3024 border: 1px solid #CCC;
3018 3025 color: blue;
3019 3026 margin-bottom: 10px;
3020 3027 padding: 10px 0;
3021 3028 }
3022 3029
3023 3030 .box .search div.search_path div.link {
3024 3031 font-weight: 700;
3025 3032 margin-left: 25px;
3026 3033 }
3027 3034
3028 3035 .box .search div.search_path div.link a {
3029 3036 color: #003367;
3030 3037 cursor: pointer;
3031 3038 text-decoration: none;
3032 3039 }
3033 3040
3034 3041 #path_unlock {
3035 3042 color: red;
3036 3043 font-size: 1.2em;
3037 3044 padding-left: 4px;
3038 3045 }
3039 3046
3040 3047 .info_box span {
3041 3048 margin-left: 3px;
3042 3049 margin-right: 3px;
3043 3050 }
3044 3051
3045 3052 .info_box .rev {
3046 3053 color: #003367;
3047 3054 font-size: 1.6em;
3048 3055 font-weight: bold;
3049 3056 vertical-align: sub;
3050 3057 }
3051 3058
3052 3059 .info_box input#at_rev, .info_box input#size {
3053 3060 background: #FFF;
3054 3061 border-top: 1px solid #b3b3b3;
3055 3062 border-left: 1px solid #b3b3b3;
3056 3063 border-right: 1px solid #eaeaea;
3057 3064 border-bottom: 1px solid #eaeaea;
3058 3065 color: #000;
3059 3066 font-size: 12px;
3060 3067 margin: 0;
3061 3068 padding: 1px 5px 1px;
3062 3069 }
3063 3070
3064 3071 .info_box input#view {
3065 3072 text-align: center;
3066 3073 padding: 4px 3px 2px 2px;
3067 3074 }
3068 3075
3069 3076 .yui-overlay, .yui-panel-container {
3070 3077 visibility: hidden;
3071 3078 position: absolute;
3072 3079 z-index: 2;
3073 3080 }
3074 3081
3075 3082 #tip-box {
3076 3083 position: absolute;
3077 3084
3078 3085 background-color: #FFF;
3079 3086 border: 2px solid #003367;
3080 3087 font: 100% sans-serif;
3081 3088 width: auto;
3082 3089 opacity: 1;
3083 3090 padding: 8px;
3084 3091
3085 3092 white-space: pre-wrap;
3086 3093 -webkit-border-radius: 8px 8px 8px 8px;
3087 3094 -khtml-border-radius: 8px 8px 8px 8px;
3088 3095 border-radius: 8px 8px 8px 8px;
3089 3096 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3090 3097 -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3091 3098 }
3092 3099
3093 3100 .hl-tip-box {
3094 3101 visibility: hidden;
3095 3102 position: absolute;
3096 3103 color: #666;
3097 3104 background-color: #FFF;
3098 3105 border: 2px solid #003367;
3099 3106 font: 100% sans-serif;
3100 3107 width: auto;
3101 3108 opacity: 1;
3102 3109 padding: 8px;
3103 3110 white-space: pre-wrap;
3104 3111 -webkit-border-radius: 8px 8px 8px 8px;
3105 3112 -khtml-border-radius: 8px 8px 8px 8px;
3106 3113 border-radius: 8px 8px 8px 8px;
3107 3114 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3108 3115 }
3109 3116
3110 3117
3111 3118 .mentions-container {
3112 3119 width: 90% !important;
3113 3120 }
3114 3121 .mentions-container .yui-ac-content {
3115 3122 width: 100% !important;
3116 3123 }
3117 3124
3118 3125 .ac {
3119 3126 vertical-align: top;
3120 3127 }
3121 3128
3122 3129 .ac .yui-ac {
3123 3130 position: inherit;
3124 3131 font-size: 100%;
3125 3132 }
3126 3133
3127 3134 .ac .perm_ac {
3128 3135 width: 20em;
3129 3136 }
3130 3137
3131 3138 .ac .yui-ac-input {
3132 3139 width: 100%;
3133 3140 }
3134 3141
3135 3142 .ac .yui-ac-container {
3136 3143 position: absolute;
3137 3144 top: 1.6em;
3138 3145 width: auto;
3139 3146 }
3140 3147
3141 3148 .ac .yui-ac-content {
3142 3149 position: absolute;
3143 3150 border: 1px solid gray;
3144 3151 background: #fff;
3145 3152 z-index: 9050;
3146 3153 }
3147 3154
3148 3155 .ac .yui-ac-shadow {
3149 3156 position: absolute;
3150 3157 width: 100%;
3151 3158 background: #000;
3152 3159 opacity: .10;
3153 3160 filter: alpha(opacity = 10);
3154 3161 z-index: 9049;
3155 3162 margin: .3em;
3156 3163 }
3157 3164
3158 3165 .ac .yui-ac-content ul {
3159 3166 width: 100%;
3160 3167 margin: 0;
3161 3168 padding: 0;
3162 3169 z-index: 9050;
3163 3170 }
3164 3171
3165 3172 .ac .yui-ac-content li {
3166 3173 cursor: default;
3167 3174 white-space: nowrap;
3168 3175 margin: 0;
3169 3176 padding: 2px 5px;
3170 3177 height: 18px;
3171 3178 z-index: 9050;
3172 3179 display: block;
3173 3180 width: auto !important;
3174 3181 }
3175 3182
3176 3183 .ac .yui-ac-content li .ac-container-wrap {
3177 3184 width: auto;
3178 3185 }
3179 3186
3180 3187 .ac .yui-ac-content li.yui-ac-prehighlight {
3181 3188 background: #B3D4FF;
3182 3189 z-index: 9050;
3183 3190 }
3184 3191
3185 3192 .ac .yui-ac-content li.yui-ac-highlight {
3186 3193 background: #556CB5;
3187 3194 color: #FFF;
3188 3195 z-index: 9050;
3189 3196 }
3190 3197 .ac .yui-ac-bd {
3191 3198 z-index: 9050;
3192 3199 }
3193 3200
3194 3201 .reposize {
3195 3202 background: url("../images/icons/server.png") no-repeat scroll 3px;
3196 3203 height: 16px;
3197 3204 width: 20px;
3198 3205 cursor: pointer;
3199 3206 display: block;
3200 3207 float: right;
3201 3208 margin-top: 2px;
3202 3209 }
3203 3210
3204 3211 #repo_size {
3205 3212 display: block;
3206 3213 margin-top: 4px;
3207 3214 color: #666;
3208 3215 float: right;
3209 3216 }
3210 3217
3211 3218 .locking_locked {
3212 3219 background: #FFF url("../images/icons/block_16.png") no-repeat scroll 3px;
3213 3220 height: 16px;
3214 3221 width: 20px;
3215 3222 cursor: pointer;
3216 3223 display: block;
3217 3224 float: right;
3218 3225 margin-top: 2px;
3219 3226 }
3220 3227
3221 3228 .locking_unlocked {
3222 3229 background: #FFF url("../images/icons/accept.png") no-repeat scroll 3px;
3223 3230 height: 16px;
3224 3231 width: 20px;
3225 3232 cursor: pointer;
3226 3233 display: block;
3227 3234 float: right;
3228 3235 margin-top: 2px;
3229 3236 }
3230 3237
3231 3238 .currently_following {
3232 3239 padding-left: 10px;
3233 3240 padding-bottom: 5px;
3234 3241 }
3235 3242
3236 3243 .add_icon {
3237 3244 background: url("../images/icons/add.png") no-repeat scroll 3px;
3238 3245 padding-left: 20px;
3239 3246 padding-top: 0px;
3240 3247 text-align: left;
3241 3248 }
3242 3249
3243 3250 .accept_icon {
3244 3251 background: url("../images/icons/accept.png") no-repeat scroll 3px;
3245 3252 padding-left: 20px;
3246 3253 padding-top: 0px;
3247 3254 text-align: left;
3248 3255 }
3249 3256
3250 3257 .edit_icon {
3251 3258 background: url("../images/icons/application_form_edit.png") no-repeat scroll 3px;
3252 3259 padding-left: 20px;
3253 3260 padding-top: 0px;
3254 3261 text-align: left;
3255 3262 }
3256 3263
3257 3264 .delete_icon {
3258 3265 background: url("../images/icons/delete.png") no-repeat scroll 3px;
3259 3266 padding-left: 20px;
3260 3267 padding-top: 0px;
3261 3268 text-align: left;
3262 3269 }
3263 3270
3264 3271 .refresh_icon {
3265 3272 background: url("../images/icons/arrow_refresh.png") no-repeat scroll
3266 3273 3px;
3267 3274 padding-left: 20px;
3268 3275 padding-top: 0px;
3269 3276 text-align: left;
3270 3277 }
3271 3278
3272 3279 .pull_icon {
3273 3280 background: url("../images/icons/connect.png") no-repeat scroll 3px;
3274 3281 padding-left: 20px;
3275 3282 padding-top: 0px;
3276 3283 text-align: left;
3277 3284 }
3278 3285
3279 3286 .rss_icon {
3280 3287 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
3281 3288 padding-left: 20px;
3282 3289 padding-top: 4px;
3283 3290 text-align: left;
3284 3291 font-size: 8px
3285 3292 }
3286 3293
3287 3294 .atom_icon {
3288 3295 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
3289 3296 padding-left: 20px;
3290 3297 padding-top: 4px;
3291 3298 text-align: left;
3292 3299 font-size: 8px
3293 3300 }
3294 3301
3295 3302 .archive_icon {
3296 3303 background: url("../images/icons/compress.png") no-repeat scroll 3px;
3297 3304 padding-left: 20px;
3298 3305 text-align: left;
3299 3306 padding-top: 1px;
3300 3307 }
3301 3308
3302 3309 .start_following_icon {
3303 3310 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3304 3311 padding-left: 20px;
3305 3312 text-align: left;
3306 3313 padding-top: 0px;
3307 3314 }
3308 3315
3309 3316 .stop_following_icon {
3310 3317 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3311 3318 padding-left: 20px;
3312 3319 text-align: left;
3313 3320 padding-top: 0px;
3314 3321 }
3315 3322
3316 3323 .action_button {
3317 3324 border: 0;
3318 3325 display: inline;
3319 3326 }
3320 3327
3321 3328 .action_button:hover {
3322 3329 border: 0;
3323 3330 text-decoration: underline;
3324 3331 cursor: pointer;
3325 3332 }
3326 3333
3327 3334 #switch_repos {
3328 3335 position: absolute;
3329 3336 height: 25px;
3330 3337 z-index: 1;
3331 3338 }
3332 3339
3333 3340 #switch_repos select {
3334 3341 min-width: 150px;
3335 3342 max-height: 250px;
3336 3343 z-index: 1;
3337 3344 }
3338 3345
3339 3346 .breadcrumbs {
3340 3347 border: medium none;
3341 3348 color: #FFF;
3342 3349 float: left;
3343 3350 font-weight: 700;
3344 3351 font-size: 14px;
3345 3352 margin: 0;
3346 3353 padding: 11px 0 11px 10px;
3347 3354 }
3348 3355
3349 3356 .breadcrumbs .hash {
3350 3357 text-transform: none;
3351 3358 color: #fff;
3352 3359 }
3353 3360
3354 3361 .breadcrumbs a {
3355 3362 color: #FFF;
3356 3363 }
3357 3364
3358 3365 .flash_msg {
3359 3366 }
3360 3367
3361 3368 .flash_msg ul {
3362 3369 }
3363 3370
3364 3371 .error_red {
3365 3372 color: red;
3366 3373 }
3367 3374
3368 3375 .error_msg {
3369 3376 background-color: #c43c35;
3370 3377 background-repeat: repeat-x;
3371 3378 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35) );
3372 3379 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3373 3380 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3374 3381 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35) );
3375 3382 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3376 3383 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3377 3384 background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
3378 3385 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35', GradientType=0 );
3379 3386 border-color: #c43c35 #c43c35 #882a25;
3380 3387 }
3381 3388
3382 3389 .error_msg a {
3383 3390 text-decoration: underline;
3384 3391 }
3385 3392
3386 3393 .warning_msg {
3387 3394 color: #404040 !important;
3388 3395 background-color: #eedc94;
3389 3396 background-repeat: repeat-x;
3390 3397 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94) );
3391 3398 background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
3392 3399 background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
3393 3400 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94) );
3394 3401 background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
3395 3402 background-image: -o-linear-gradient(top, #fceec1, #eedc94);
3396 3403 background-image: linear-gradient(to bottom, #fceec1, #eedc94);
3397 3404 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0 );
3398 3405 border-color: #eedc94 #eedc94 #e4c652;
3399 3406 }
3400 3407
3401 3408 .warning_msg a {
3402 3409 text-decoration: underline;
3403 3410 }
3404 3411
3405 3412 .success_msg {
3406 3413 background-color: #57a957;
3407 3414 background-repeat: repeat-x !important;
3408 3415 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957) );
3409 3416 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3410 3417 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3411 3418 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957) );
3412 3419 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3413 3420 background-image: -o-linear-gradient(top, #62c462, #57a957);
3414 3421 background-image: linear-gradient(to bottom, #62c462, #57a957);
3415 3422 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0 );
3416 3423 border-color: #57a957 #57a957 #3d773d;
3417 3424 }
3418 3425
3419 3426 .success_msg a {
3420 3427 text-decoration: underline;
3421 3428 color: #FFF !important;
3422 3429 }
3423 3430
3424 3431 .notice_msg {
3425 3432 background-color: #339bb9;
3426 3433 background-repeat: repeat-x;
3427 3434 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9) );
3428 3435 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3429 3436 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3430 3437 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9) );
3431 3438 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3432 3439 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3433 3440 background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
3434 3441 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0 );
3435 3442 border-color: #339bb9 #339bb9 #22697d;
3436 3443 }
3437 3444
3438 3445 .notice_msg a {
3439 3446 text-decoration: underline;
3440 3447 }
3441 3448
3442 3449 .success_msg, .error_msg, .notice_msg, .warning_msg {
3443 3450 font-size: 12px;
3444 3451 font-weight: 700;
3445 3452 min-height: 14px;
3446 3453 line-height: 14px;
3447 3454 margin-bottom: 10px;
3448 3455 margin-top: 0;
3449 3456 display: block;
3450 3457 overflow: auto;
3451 3458 padding: 6px 10px 6px 10px;
3452 3459 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3453 3460 position: relative;
3454 3461 color: #FFF;
3455 3462 border-width: 1px;
3456 3463 border-style: solid;
3457 3464 -webkit-border-radius: 4px;
3458 3465 border-radius: 4px;
3459 3466 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3460 3467 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3461 3468 }
3462 3469
3463 3470 #msg_close {
3464 3471 background: transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
3465 3472 cursor: pointer;
3466 3473 height: 16px;
3467 3474 position: absolute;
3468 3475 right: 5px;
3469 3476 top: 5px;
3470 3477 width: 16px;
3471 3478 }
3472 3479 div#legend_data {
3473 3480 padding-left: 10px;
3474 3481 }
3475 3482 div#legend_container table {
3476 3483 border: none !important;
3477 3484 }
3478 3485 div#legend_container table, div#legend_choices table {
3479 3486 width: auto !important;
3480 3487 }
3481 3488
3482 3489 table#permissions_manage {
3483 3490 width: 0 !important;
3484 3491 }
3485 3492
3486 3493 table#permissions_manage span.private_repo_msg {
3487 3494 font-size: 0.8em;
3488 3495 opacity: 0.6;
3489 3496 }
3490 3497
3491 3498 table#permissions_manage td.private_repo_msg {
3492 3499 font-size: 0.8em;
3493 3500 }
3494 3501
3495 3502 table#permissions_manage tr#add_perm_input td {
3496 3503 vertical-align: middle;
3497 3504 }
3498 3505
3499 3506 div.gravatar {
3500 3507 background-color: #FFF;
3501 3508 float: left;
3502 3509 margin-right: 0.7em;
3503 3510 padding: 1px 1px 1px 1px;
3504 3511 line-height: 0;
3505 3512 -webkit-border-radius: 3px;
3506 3513 -khtml-border-radius: 3px;
3507 3514 border-radius: 3px;
3508 3515 }
3509 3516
3510 3517 div.gravatar img {
3511 3518 -webkit-border-radius: 2px;
3512 3519 -khtml-border-radius: 2px;
3513 3520 border-radius: 2px;
3514 3521 }
3515 3522
3516 3523 #header, #content, #footer {
3517 3524 min-width: 978px;
3518 3525 }
3519 3526
3520 3527 #content {
3521 3528 clear: both;
3522 3529 padding: 10px 10px 14px 10px;
3523 3530 }
3524 3531
3525 3532 #content.hover {
3526 3533 padding: 55px 10px 14px 10px !important;
3527 3534 }
3528 3535
3529 3536 #content div.box div.title div.search {
3530 3537 border-left: 1px solid #316293;
3531 3538 }
3532 3539
3533 3540 #content div.box div.title div.search div.input input {
3534 3541 border: 1px solid #316293;
3535 3542 }
3536 3543
3537 3544 .ui-btn {
3538 3545 color: #515151;
3539 3546 background-color: #DADADA;
3540 3547 background-repeat: repeat-x;
3541 3548 background-image: -khtml-gradient(linear, left top, left bottom, from(#F4F4F4),to(#DADADA) );
3542 3549 background-image: -moz-linear-gradient(top, #F4F4F4, #DADADA);
3543 3550 background-image: -ms-linear-gradient(top, #F4F4F4, #DADADA);
3544 3551 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #F4F4F4),color-stop(100%, #DADADA) );
3545 3552 background-image: -webkit-linear-gradient(top, #F4F4F4, #DADADA) );
3546 3553 background-image: -o-linear-gradient(top, #F4F4F4, #DADADA) );
3547 3554 background-image: linear-gradient(to bottom, #F4F4F4, #DADADA);
3548 3555 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F4F4F4', endColorstr='#DADADA', GradientType=0);
3549 3556
3550 3557 border-top: 1px solid #DDD;
3551 3558 border-left: 1px solid #c6c6c6;
3552 3559 border-right: 1px solid #DDD;
3553 3560 border-bottom: 1px solid #c6c6c6;
3554 3561 color: #515151;
3555 3562 outline: none;
3556 3563 margin: 0px 3px 3px 0px;
3557 3564 -webkit-border-radius: 4px 4px 4px 4px !important;
3558 3565 -khtml-border-radius: 4px 4px 4px 4px !important;
3559 3566 border-radius: 4px 4px 4px 4px !important;
3560 3567 cursor: pointer !important;
3561 3568 padding: 3px 3px 3px 3px;
3562 3569 background-position: 0 -15px;
3563 3570
3564 3571 }
3565 3572
3566 3573 .ui-btn.disabled {
3567 3574 color: #999;
3568 3575 }
3569 3576
3570 3577 .ui-btn.xsmall {
3571 3578 padding: 1px 2px 1px 1px;
3572 3579 }
3573 3580
3574 3581 .ui-btn.large {
3575 3582 padding: 6px 12px;
3576 3583 }
3577 3584
3578 3585 .ui-btn.clone {
3579 3586 padding: 5px 2px 6px 1px;
3580 3587 margin: 0px 0px 3px -4px;
3581 3588 -webkit-border-radius: 0px 4px 4px 0px !important;
3582 3589 -khtml-border-radius: 0px 4px 4px 0px !important;
3583 3590 border-radius: 0px 4px 4px 0px !important;
3584 3591 width: 100px;
3585 3592 text-align: center;
3586 3593 display: inline-block;
3587 3594 position: relative;
3588 3595 top: -2px;
3589 3596 }
3590 3597 .ui-btn:focus {
3591 3598 outline: none;
3592 3599 }
3593 3600 .ui-btn:hover {
3594 3601 background-position: 0 -15px;
3595 3602 text-decoration: none;
3596 3603 color: #515151;
3597 3604 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 3px #FFFFFF !important;
3598 3605 }
3599 3606
3600 3607 .ui-btn.disabled:hover {
3601 3608 background-position: 0;
3602 3609 color: #999;
3603 3610 text-decoration: none;
3604 3611 box-shadow: none !important;
3605 3612 }
3606 3613
3607 3614 .ui-btn.red {
3608 3615 color: #fff;
3609 3616 background-color: #c43c35;
3610 3617 background-repeat: repeat-x;
3611 3618 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));
3612 3619 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3613 3620 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3614 3621 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));
3615 3622 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3616 3623 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3617 3624 background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
3618 3625 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
3619 3626 border-color: #c43c35 #c43c35 #882a25;
3620 3627 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3621 3628 }
3622 3629
3623 3630
3624 3631 .ui-btn.blue {
3625 3632 color: #fff;
3626 3633 background-color: #339bb9;
3627 3634 background-repeat: repeat-x;
3628 3635 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));
3629 3636 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3630 3637 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3631 3638 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));
3632 3639 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3633 3640 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3634 3641 background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
3635 3642 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);
3636 3643 border-color: #339bb9 #339bb9 #22697d;
3637 3644 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3638 3645 }
3639 3646
3640 3647 .ui-btn.green {
3641 3648 background-color: #57a957;
3642 3649 background-repeat: repeat-x;
3643 3650 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));
3644 3651 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3645 3652 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3646 3653 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));
3647 3654 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3648 3655 background-image: -o-linear-gradient(top, #62c462, #57a957);
3649 3656 background-image: linear-gradient(to bottom, #62c462, #57a957);
3650 3657 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
3651 3658 border-color: #57a957 #57a957 #3d773d;
3652 3659 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3653 3660 }
3654 3661
3655 3662 .ui-btn.blue.hidden {
3656 3663 display: none;
3657 3664 }
3658 3665
3659 3666 .ui-btn.active {
3660 3667 font-weight: bold;
3661 3668 }
3662 3669
3663 3670 ins, div.options a:hover {
3664 3671 text-decoration: none;
3665 3672 }
3666 3673
3667 3674 img,
3668 3675 #header #header-inner #quick li a:hover span.normal,
3669 3676 #content div.box div.form div.fields div.field div.textarea table td table td a,
3670 3677 #clone_url,
3671 3678 #clone_url_id
3672 3679 {
3673 3680 border: none;
3674 3681 }
3675 3682
3676 3683 img.icon, .right .merge img {
3677 3684 vertical-align: bottom;
3678 3685 }
3679 3686
3680 3687 #header ul#logged-user, #content div.box div.title ul.links,
3681 3688 #content div.box div.message div.dismiss,
3682 3689 #content div.box div.traffic div.legend ul {
3683 3690 float: right;
3684 3691 margin: 0;
3685 3692 padding: 0;
3686 3693 }
3687 3694
3688 3695 #header #header-inner #home, #header #header-inner #logo,
3689 3696 #content div.box ul.left, #content div.box ol.left,
3690 3697 div#commit_history,
3691 3698 div#legend_data, div#legend_container, div#legend_choices {
3692 3699 float: left;
3693 3700 }
3694 3701
3695 3702 #header #header-inner #quick li #quick_login,
3696 3703 #header #header-inner #quick li:hover ul ul,
3697 3704 #header #header-inner #quick li:hover ul ul ul,
3698 3705 #header #header-inner #quick li:hover ul ul ul ul,
3699 3706 #content #left #menu ul.closed, #content #left #menu li ul.collapsed, .yui-tt-shadow {
3700 3707 display: none;
3701 3708 }
3702 3709
3703 3710 #header #header-inner #quick li:hover #quick_login,
3704 3711 #header #header-inner #quick li:hover ul, #header #header-inner #quick li li:hover ul, #header #header-inner #quick li li li:hover ul, #header #header-inner #quick li li li li:hover ul, #content #left #menu ul.opened, #content #left #menu li ul.expanded {
3705 3712 display: block;
3706 3713 }
3707 3714
3708 3715 #content div.graph {
3709 3716 padding: 0 10px 10px;
3710 3717 }
3711 3718
3712 3719 #content div.box div.title ul.links li a:hover,
3713 3720 #content div.box div.title ul.links li.ui-tabs-selected a {
3714 3721
3715 3722 background: #6388ad; /* Old browsers */
3716 3723 background: -moz-linear-gradient(top, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.1) 100%); /* FF3.6+ */
3717 3724 background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,0.1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */
3718 3725 background: -webkit-linear-gradient(top, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.1) 100%); /* Chrome10+,Safari5.1+ */
3719 3726 background: -o-linear-gradient(top, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.1) 100%); /* Opera 11.10+ */
3720 3727 background: -ms-linear-gradient(top, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.1) 100%); /* IE10+ */
3721 3728 background: linear-gradient(to bottom, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.1) 100%); /* W3C */
3722 3729 /*filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#88bfe8', endColorstr='#70b0e0',GradientType=0 ); /* IE6-9 */*/
3723 3730 }
3724 3731
3725 3732 #content div.box ol.lower-roman, #content div.box ol.upper-roman, #content div.box ol.lower-alpha, #content div.box ol.upper-alpha, #content div.box ol.decimal {
3726 3733 margin: 10px 24px 10px 44px;
3727 3734 }
3728 3735
3729 3736 #content div.box div.form, #content div.box div.table, #content div.box div.traffic {
3730 3737 position: relative;
3731 3738 clear: both;
3732 3739 margin: 0;
3733 3740 padding: 0 20px 10px;
3734 3741 }
3735 3742
3736 3743 #content div.box div.form div.fields, #login div.form, #login div.form div.fields, #register div.form, #register div.form div.fields {
3737 3744 clear: both;
3738 3745 overflow: hidden;
3739 3746 margin: 0;
3740 3747 padding: 0;
3741 3748 }
3742 3749
3743 3750 #content div.box div.form div.fields div.field div.label span, #login div.form div.fields div.field div.label span, #register div.form div.fields div.field div.label span {
3744 3751 height: 1%;
3745 3752 display: block;
3746 3753 color: #363636;
3747 3754 margin: 0;
3748 3755 padding: 2px 0 0;
3749 3756 }
3750 3757
3751 3758 #content div.box div.form div.fields div.field div.input input.error, #login div.form div.fields div.field div.input input.error, #register div.form div.fields div.field div.input input.error {
3752 3759 background: #FBE3E4;
3753 3760 border-top: 1px solid #e1b2b3;
3754 3761 border-left: 1px solid #e1b2b3;
3755 3762 border-right: 1px solid #FBC2C4;
3756 3763 border-bottom: 1px solid #FBC2C4;
3757 3764 }
3758 3765
3759 3766 #content div.box div.form div.fields div.field div.input input.success, #login div.form div.fields div.field div.input input.success, #register div.form div.fields div.field div.input input.success {
3760 3767 background: #E6EFC2;
3761 3768 border-top: 1px solid #cebb98;
3762 3769 border-left: 1px solid #cebb98;
3763 3770 border-right: 1px solid #c6d880;
3764 3771 border-bottom: 1px solid #c6d880;
3765 3772 }
3766 3773
3767 3774 #content div.box-left div.form div.fields div.field div.textarea, #content div.box-right div.form div.fields div.field div.textarea, #content div.box div.form div.fields div.field div.select select, #content div.box table th.selected input, #content div.box table td.selected input {
3768 3775 margin: 0;
3769 3776 }
3770 3777
3771 3778 #content div.box-left div.form div.fields div.field div.select, #content div.box-left div.form div.fields div.field div.checkboxes, #content div.box-left div.form div.fields div.field div.radios, #content div.box-right div.form div.fields div.field div.select, #content div.box-right div.form div.fields div.field div.checkboxes, #content div.box-right div.form div.fields div.field div.radios {
3772 3779 margin: 0 0 0 0px !important;
3773 3780 padding: 0;
3774 3781 }
3775 3782
3776 3783 #content div.box div.form div.fields div.field div.select, #content div.box div.form div.fields div.field div.checkboxes, #content div.box div.form div.fields div.field div.radios {
3777 3784 margin: 0 0 0 200px;
3778 3785 padding: 0;
3779 3786 }
3780 3787
3781 3788 #content div.box div.form div.fields div.field div.select a:hover, #content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover, #content div.box div.action a:hover {
3782 3789 color: #000;
3783 3790 text-decoration: none;
3784 3791 }
3785 3792
3786 3793 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus, #content div.box div.action a.ui-selectmenu-focus {
3787 3794 border: 1px solid #666;
3788 3795 }
3789 3796
3790 3797 #content div.box div.form div.fields div.field div.checkboxes div.checkbox, #content div.box div.form div.fields div.field div.radios div.radio {
3791 3798 clear: both;
3792 3799 overflow: hidden;
3793 3800 margin: 0;
3794 3801 padding: 8px 0 2px;
3795 3802 }
3796 3803
3797 3804 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input, #content div.box div.form div.fields div.field div.radios div.radio input {
3798 3805 float: left;
3799 3806 margin: 0;
3800 3807 }
3801 3808
3802 3809 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label, #content div.box div.form div.fields div.field div.radios div.radio label {
3803 3810 height: 1%;
3804 3811 display: block;
3805 3812 float: left;
3806 3813 margin: 2px 0 0 4px;
3807 3814 }
3808 3815
3809 3816 div.form div.fields div.field div.button input,
3810 3817 #content div.box div.form div.fields div.buttons input
3811 3818 div.form div.fields div.buttons input,
3812 3819 #content div.box div.action div.button input {
3813 3820 font-size: 11px;
3814 3821 font-weight: 700;
3815 3822 margin: 0;
3816 3823 }
3817 3824
3818 3825 input.ui-button {
3819 3826 background: #e5e3e3 url("../images/button.png") repeat-x;
3820 3827 border-top: 1px solid #DDD;
3821 3828 border-left: 1px solid #c6c6c6;
3822 3829 border-right: 1px solid #DDD;
3823 3830 border-bottom: 1px solid #c6c6c6;
3824 3831 color: #515151 !important;
3825 3832 outline: none;
3826 3833 margin: 0;
3827 3834 padding: 6px 12px;
3828 3835 -webkit-border-radius: 4px 4px 4px 4px;
3829 3836 -khtml-border-radius: 4px 4px 4px 4px;
3830 3837 border-radius: 4px 4px 4px 4px;
3831 3838 box-shadow: 0 1px 0 #ececec;
3832 3839 cursor: pointer;
3833 3840 }
3834 3841
3835 3842 input.ui-button:hover {
3836 3843 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3837 3844 border-top: 1px solid #ccc;
3838 3845 border-left: 1px solid #bebebe;
3839 3846 border-right: 1px solid #b1b1b1;
3840 3847 border-bottom: 1px solid #afafaf;
3841 3848 }
3842 3849
3843 3850 div.form div.fields div.field div.highlight, #content div.box div.form div.fields div.buttons div.highlight {
3844 3851 display: inline;
3845 3852 }
3846 3853
3847 3854 #content div.box div.form div.fields div.buttons, div.form div.fields div.buttons {
3848 3855 margin: 10px 0 0 200px;
3849 3856 padding: 0;
3850 3857 }
3851 3858
3852 3859 #content div.box-left div.form div.fields div.buttons, #content div.box-right div.form div.fields div.buttons, div.box-left div.form div.fields div.buttons, div.box-right div.form div.fields div.buttons {
3853 3860 margin: 10px 0 0;
3854 3861 }
3855 3862
3856 3863 #content div.box table td.user, #content div.box table td.address {
3857 3864 width: 10%;
3858 3865 text-align: center;
3859 3866 }
3860 3867
3861 3868 #content div.box div.action div.button, #login div.form div.fields div.field div.input div.link, #register div.form div.fields div.field div.input div.link {
3862 3869 text-align: right;
3863 3870 margin: 6px 0 0;
3864 3871 padding: 0;
3865 3872 }
3866 3873
3867 3874 #content div.box div.action div.button input.ui-state-hover, #login div.form div.fields div.buttons input.ui-state-hover, #register div.form div.fields div.buttons input.ui-state-hover {
3868 3875 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3869 3876 border-top: 1px solid #ccc;
3870 3877 border-left: 1px solid #bebebe;
3871 3878 border-right: 1px solid #b1b1b1;
3872 3879 border-bottom: 1px solid #afafaf;
3873 3880 color: #515151;
3874 3881 margin: 0;
3875 3882 padding: 6px 12px;
3876 3883 }
3877 3884
3878 3885 #content div.box div.pagination div.results, #content div.box div.pagination-wh div.results {
3879 3886 text-align: left;
3880 3887 float: left;
3881 3888 margin: 0;
3882 3889 padding: 0;
3883 3890 }
3884 3891
3885 3892 #content div.box div.pagination div.results span, #content div.box div.pagination-wh div.results span {
3886 3893 height: 1%;
3887 3894 display: block;
3888 3895 float: left;
3889 3896 background: #ebebeb url("../images/pager.png") repeat-x;
3890 3897 border-top: 1px solid #dedede;
3891 3898 border-left: 1px solid #cfcfcf;
3892 3899 border-right: 1px solid #c4c4c4;
3893 3900 border-bottom: 1px solid #c4c4c4;
3894 3901 color: #4A4A4A;
3895 3902 font-weight: 700;
3896 3903 margin: 0;
3897 3904 padding: 6px 8px;
3898 3905 }
3899 3906
3900 3907 #content div.box div.pagination ul.pager li.disabled, #content div.box div.pagination-wh a.disabled {
3901 3908 color: #B4B4B4;
3902 3909 padding: 6px;
3903 3910 }
3904 3911
3905 3912 #login, #register {
3906 3913 width: 520px;
3907 3914 margin: 10% auto 0;
3908 3915 padding: 0;
3909 3916 }
3910 3917
3911 3918 #login div.color, #register div.color {
3912 3919 clear: both;
3913 3920 overflow: hidden;
3914 3921 background: #FFF;
3915 3922 margin: 10px auto 0;
3916 3923 padding: 3px 3px 3px 0;
3917 3924 }
3918 3925
3919 3926 #login div.color a, #register div.color a {
3920 3927 width: 20px;
3921 3928 height: 20px;
3922 3929 display: block;
3923 3930 float: left;
3924 3931 margin: 0 0 0 3px;
3925 3932 padding: 0;
3926 3933 }
3927 3934
3928 3935 #login div.title h5, #register div.title h5 {
3929 3936 color: #fff;
3930 3937 margin: 10px;
3931 3938 padding: 0;
3932 3939 }
3933 3940
3934 3941 #login div.form div.fields div.field, #register div.form div.fields div.field {
3935 3942 clear: both;
3936 3943 overflow: hidden;
3937 3944 margin: 0;
3938 3945 padding: 0 0 10px;
3939 3946 }
3940 3947
3941 3948 #login div.form div.fields div.field span.error-message, #register div.form div.fields div.field span.error-message {
3942 3949 height: 1%;
3943 3950 display: block;
3944 3951 color: red;
3945 3952 margin: 8px 0 0;
3946 3953 padding: 0;
3947 3954 max-width: 320px;
3948 3955 }
3949 3956
3950 3957 #login div.form div.fields div.field div.label label, #register div.form div.fields div.field div.label label {
3951 3958 color: #000;
3952 3959 font-weight: 700;
3953 3960 }
3954 3961
3955 3962 #login div.form div.fields div.field div.input, #register div.form div.fields div.field div.input {
3956 3963 float: left;
3957 3964 margin: 0;
3958 3965 padding: 0;
3959 3966 }
3960 3967
3961 3968 #login div.form div.fields div.field div.input input.large {
3962 3969 width: 250px;
3963 3970 }
3964 3971
3965 3972 #login div.form div.fields div.field div.checkbox, #register div.form div.fields div.field div.checkbox {
3966 3973 margin: 0 0 0 184px;
3967 3974 padding: 0;
3968 3975 }
3969 3976
3970 3977 #login div.form div.fields div.field div.checkbox label, #register div.form div.fields div.field div.checkbox label {
3971 3978 color: #565656;
3972 3979 font-weight: 700;
3973 3980 }
3974 3981
3975 3982 #login div.form div.fields div.buttons input, #register div.form div.fields div.buttons input {
3976 3983 color: #000;
3977 3984 font-size: 1em;
3978 3985 font-weight: 700;
3979 3986 margin: 0;
3980 3987 }
3981 3988
3982 3989 #changeset_content .container .wrapper, #graph_content .container .wrapper {
3983 3990 width: 600px;
3984 3991 }
3985 3992
3986 3993 #changeset_content .container .date, .ac .match {
3987 3994 font-weight: 700;
3988 3995 padding-top: 5px;
3989 3996 padding-bottom: 5px;
3990 3997 }
3991 3998
3992 3999 div#legend_container table td, div#legend_choices table td {
3993 4000 border: none !important;
3994 4001 height: 20px !important;
3995 4002 padding: 0 !important;
3996 4003 }
3997 4004
3998 4005 .q_filter_box {
3999 4006 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4000 4007 -webkit-border-radius: 4px;
4001 4008 border-radius: 4px;
4002 4009 border: 0 none;
4003 4010 color: #AAAAAA;
4004 4011 margin-bottom: -4px;
4005 4012 margin-top: -4px;
4006 4013 padding-left: 3px;
4007 4014 }
4008 4015
4009 4016 #node_filter {
4010 4017 border: 0px solid #545454;
4011 4018 color: #AAAAAA;
4012 4019 padding-left: 3px;
4013 4020 }
4014 4021
4015 4022
4016 4023 .group_members_wrap {
4017 4024 min-height: 85px;
4018 4025 padding-left: 20px;
4019 4026 }
4020 4027
4021 4028 .group_members .group_member {
4022 4029 height: 30px;
4023 4030 padding: 0px 0px 0px 0px;
4024 4031 }
4025 4032
4026 4033 .reviewers_member {
4027 4034 height: 15px;
4028 4035 padding: 0px 0px 0px 10px;
4029 4036 }
4030 4037
4031 4038 .emails_wrap {
4032 4039 padding: 0px 20px;
4033 4040 }
4034 4041
4035 4042 .emails_wrap .email_entry {
4036 4043 height: 30px;
4037 4044 padding: 0px 0px 0px 10px;
4038 4045 }
4039 4046 .emails_wrap .email_entry .email {
4040 4047 float: left
4041 4048 }
4042 4049 .emails_wrap .email_entry .email_action {
4043 4050 float: left
4044 4051 }
4045 4052
4046 4053 .ips_wrap {
4047 4054 padding: 0px 20px;
4048 4055 }
4049 4056
4050 4057 .ips_wrap .ip_entry {
4051 4058 height: 30px;
4052 4059 padding: 0px 0px 0px 10px;
4053 4060 }
4054 4061 .ips_wrap .ip_entry .ip {
4055 4062 float: left
4056 4063 }
4057 4064 .ips_wrap .ip_entry .ip_action {
4058 4065 float: left
4059 4066 }
4060 4067
4061 4068
4062 4069 /*README STYLE*/
4063 4070
4064 4071 div.readme {
4065 4072 padding: 0px;
4066 4073 }
4067 4074
4068 4075 div.readme h2 {
4069 4076 font-weight: normal;
4070 4077 }
4071 4078
4072 4079 div.readme .readme_box {
4073 4080 background-color: #fafafa;
4074 4081 }
4075 4082
4076 4083 div.readme .readme_box {
4077 4084 clear: both;
4078 4085 overflow: hidden;
4079 4086 margin: 0;
4080 4087 padding: 0 20px 10px;
4081 4088 }
4082 4089
4083 4090 div.readme .readme_box h1, div.readme .readme_box h2, div.readme .readme_box h3, div.readme .readme_box h4, div.readme .readme_box h5, div.readme .readme_box h6 {
4084 4091 border-bottom: 0 !important;
4085 4092 margin: 0 !important;
4086 4093 padding: 0 !important;
4087 4094 line-height: 1.5em !important;
4088 4095 }
4089 4096
4090 4097
4091 4098 div.readme .readme_box h1:first-child {
4092 4099 padding-top: .25em !important;
4093 4100 }
4094 4101
4095 4102 div.readme .readme_box h2, div.readme .readme_box h3 {
4096 4103 margin: 1em 0 !important;
4097 4104 }
4098 4105
4099 4106 div.readme .readme_box h2 {
4100 4107 margin-top: 1.5em !important;
4101 4108 border-top: 4px solid #e0e0e0 !important;
4102 4109 padding-top: .5em !important;
4103 4110 }
4104 4111
4105 4112 div.readme .readme_box p {
4106 4113 color: black !important;
4107 4114 margin: 1em 0 !important;
4108 4115 line-height: 1.5em !important;
4109 4116 }
4110 4117
4111 4118 div.readme .readme_box ul {
4112 4119 list-style: disc !important;
4113 4120 margin: 1em 0 1em 2em !important;
4114 4121 }
4115 4122
4116 4123 div.readme .readme_box ol {
4117 4124 list-style: decimal;
4118 4125 margin: 1em 0 1em 2em !important;
4119 4126 }
4120 4127
4121 4128 div.readme .readme_box pre, code {
4122 4129 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4123 4130 }
4124 4131
4125 4132 div.readme .readme_box code {
4126 4133 font-size: 12px !important;
4127 4134 background-color: ghostWhite !important;
4128 4135 color: #444 !important;
4129 4136 padding: 0 .2em !important;
4130 4137 border: 1px solid #dedede !important;
4131 4138 }
4132 4139
4133 4140 div.readme .readme_box pre code {
4134 4141 padding: 0 !important;
4135 4142 font-size: 12px !important;
4136 4143 background-color: #eee !important;
4137 4144 border: none !important;
4138 4145 }
4139 4146
4140 4147 div.readme .readme_box pre {
4141 4148 margin: 1em 0;
4142 4149 font-size: 12px;
4143 4150 background-color: #eee;
4144 4151 border: 1px solid #ddd;
4145 4152 padding: 5px;
4146 4153 color: #444;
4147 4154 overflow: auto;
4148 4155 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4149 4156 -webkit-border-radius: 3px;
4150 4157 border-radius: 3px;
4151 4158 }
4152 4159
4153 4160 div.readme .readme_box table {
4154 4161 display: table;
4155 4162 border-collapse: separate;
4156 4163 border-spacing: 2px;
4157 4164 border-color: gray;
4158 4165 width: auto !important;
4159 4166 }
4160 4167
4161 4168
4162 4169 /** RST STYLE **/
4163 4170
4164 4171
4165 4172 div.rst-block {
4166 4173 padding: 0px;
4167 4174 }
4168 4175
4169 4176 div.rst-block h2 {
4170 4177 font-weight: normal;
4171 4178 }
4172 4179
4173 4180 div.rst-block {
4174 4181 background-color: #fafafa;
4175 4182 }
4176 4183
4177 4184 div.rst-block {
4178 4185 clear: both;
4179 4186 overflow: hidden;
4180 4187 margin: 0;
4181 4188 padding: 0 20px 10px;
4182 4189 }
4183 4190
4184 4191 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
4185 4192 border-bottom: 0 !important;
4186 4193 margin: 0 !important;
4187 4194 padding: 0 !important;
4188 4195 line-height: 1.5em !important;
4189 4196 }
4190 4197
4191 4198
4192 4199 div.rst-block h1:first-child {
4193 4200 padding-top: .25em !important;
4194 4201 }
4195 4202
4196 4203 div.rst-block h2, div.rst-block h3 {
4197 4204 margin: 1em 0 !important;
4198 4205 }
4199 4206
4200 4207 div.rst-block h2 {
4201 4208 margin-top: 1.5em !important;
4202 4209 border-top: 4px solid #e0e0e0 !important;
4203 4210 padding-top: .5em !important;
4204 4211 }
4205 4212
4206 4213 div.rst-block p {
4207 4214 color: black !important;
4208 4215 margin: 1em 0 !important;
4209 4216 line-height: 1.5em !important;
4210 4217 }
4211 4218
4212 4219 div.rst-block ul {
4213 4220 list-style: disc !important;
4214 4221 margin: 1em 0 1em 2em !important;
4215 4222 }
4216 4223
4217 4224 div.rst-block ol {
4218 4225 list-style: decimal;
4219 4226 margin: 1em 0 1em 2em !important;
4220 4227 }
4221 4228
4222 4229 div.rst-block pre, code {
4223 4230 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4224 4231 }
4225 4232
4226 4233 div.rst-block code {
4227 4234 font-size: 12px !important;
4228 4235 background-color: ghostWhite !important;
4229 4236 color: #444 !important;
4230 4237 padding: 0 .2em !important;
4231 4238 border: 1px solid #dedede !important;
4232 4239 }
4233 4240
4234 4241 div.rst-block pre code {
4235 4242 padding: 0 !important;
4236 4243 font-size: 12px !important;
4237 4244 background-color: #eee !important;
4238 4245 border: none !important;
4239 4246 }
4240 4247
4241 4248 div.rst-block pre {
4242 4249 margin: 1em 0;
4243 4250 font-size: 12px;
4244 4251 background-color: #eee;
4245 4252 border: 1px solid #ddd;
4246 4253 padding: 5px;
4247 4254 color: #444;
4248 4255 overflow: auto;
4249 4256 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4250 4257 -webkit-border-radius: 3px;
4251 4258 border-radius: 3px;
4252 4259 }
4253 4260
4254 4261
4255 4262 /** comment main **/
4256 4263 .comments {
4257 4264 padding: 10px 20px;
4258 4265 }
4259 4266
4260 4267 .comments .comment {
4261 4268 border: 1px solid #ddd;
4262 4269 margin-top: 10px;
4263 4270 -webkit-border-radius: 4px;
4264 4271 border-radius: 4px;
4265 4272 }
4266 4273
4267 4274 .comments .comment .meta {
4268 4275 background: #f8f8f8;
4269 4276 padding: 4px;
4270 4277 border-bottom: 1px solid #ddd;
4271 4278 height: 18px;
4272 4279 }
4273 4280
4274 4281 .comments .comment .meta img {
4275 4282 vertical-align: middle;
4276 4283 }
4277 4284
4278 4285 .comments .comment .meta .user {
4279 4286 font-weight: bold;
4280 4287 float: left;
4281 4288 padding: 4px 2px 2px 2px;
4282 4289 }
4283 4290
4284 4291 .comments .comment .meta .date {
4285 4292 float: left;
4286 4293 padding: 4px 4px 0px 4px;
4287 4294 }
4288 4295
4289 4296 .comments .comment .text {
4290 4297 background-color: #FAFAFA;
4291 4298 }
4292 4299 .comment .text div.rst-block p {
4293 4300 margin: 0.5em 0px !important;
4294 4301 }
4295 4302
4296 4303 .comments .comments-number {
4297 4304 padding: 0px 0px 10px 0px;
4298 4305 font-weight: bold;
4299 4306 color: #666;
4300 4307 font-size: 16px;
4301 4308 }
4302 4309
4303 4310 /** comment form **/
4304 4311
4305 4312 .status-block {
4306 4313 min-height: 80px;
4307 4314 clear: both
4308 4315 }
4309 4316
4310 4317
4311 4318 div.comment-form {
4312 4319 margin-top: 20px;
4313 4320 }
4314 4321
4315 4322 .comment-form strong {
4316 4323 display: block;
4317 4324 margin-bottom: 15px;
4318 4325 }
4319 4326
4320 4327 .comment-form textarea {
4321 4328 width: 100%;
4322 4329 height: 100px;
4323 4330 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4324 4331 }
4325 4332
4326 4333 form.comment-form {
4327 4334 margin-top: 10px;
4328 4335 margin-left: 10px;
4329 4336 }
4330 4337
4331 4338 .comment-inline-form .comment-block-ta,
4332 4339 .comment-form .comment-block-ta {
4333 4340 border: 1px solid #ccc;
4334 4341 border-radius: 3px;
4335 4342 box-sizing: border-box;
4336 4343 }
4337 4344
4338 4345 .comment-form-submit {
4339 4346 margin-top: 5px;
4340 4347 margin-left: 525px;
4341 4348 }
4342 4349
4343 4350 .file-comments {
4344 4351 display: none;
4345 4352 }
4346 4353
4347 4354 .comment-form .comment {
4348 4355 margin-left: 10px;
4349 4356 }
4350 4357
4351 4358 .comment-form .comment-help {
4352 4359 padding: 5px 5px 5px 5px;
4353 4360 color: #666;
4354 4361 }
4355 4362 .comment-form .comment-help .preview-btn,
4356 4363 .comment-form .comment-help .edit-btn {
4357 4364 float: right;
4358 4365 margin: -6px 0px 0px 0px;
4359 4366 }
4360 4367
4361 4368 .comment-form .preview-box.unloaded,
4362 4369 .comment-inline-form .preview-box.unloaded {
4363 4370 height: 50px;
4364 4371 text-align: center;
4365 4372 padding: 20px;
4366 4373 background-color: #fafafa;
4367 4374 }
4368 4375
4369 4376 .comment-form .comment-button {
4370 4377 padding-top: 5px;
4371 4378 }
4372 4379
4373 4380 .add-another-button {
4374 4381 margin-left: 10px;
4375 4382 margin-top: 10px;
4376 4383 margin-bottom: 10px;
4377 4384 }
4378 4385
4379 4386 .comment .buttons {
4380 4387 float: right;
4381 4388 margin: -1px 0px 0px 0px;
4382 4389 }
4383 4390
4384 4391
4385 4392 .show-inline-comments {
4386 4393 position: relative;
4387 4394 top: 1px
4388 4395 }
4389 4396
4390 4397 /** comment inline form **/
4391 4398 .comment-inline-form {
4392 4399 margin: 4px;
4393 4400 }
4394 4401 .comment-inline-form .overlay {
4395 4402 display: none;
4396 4403 }
4397 4404 .comment-inline-form .overlay.submitting {
4398 4405 display: block;
4399 4406 background: none repeat scroll 0 0 white;
4400 4407 font-size: 16px;
4401 4408 opacity: 0.5;
4402 4409 position: absolute;
4403 4410 text-align: center;
4404 4411 vertical-align: top;
4405 4412
4406 4413 }
4407 4414 .comment-inline-form .overlay.submitting .overlay-text {
4408 4415 width: 100%;
4409 4416 margin-top: 5%;
4410 4417 }
4411 4418
4412 4419 .comment-inline-form .clearfix,
4413 4420 .comment-form .clearfix {
4414 4421 background: #EEE;
4415 4422 -webkit-border-radius: 4px;
4416 4423 border-radius: 4px;
4417 4424 padding: 5px;
4418 4425 margin: 0px;
4419 4426 }
4420 4427
4421 4428 div.comment-inline-form {
4422 4429 padding: 4px 0px 6px 0px;
4423 4430 }
4424 4431
4425 4432 .comment-inline-form strong {
4426 4433 display: block;
4427 4434 margin-bottom: 15px;
4428 4435 }
4429 4436
4430 4437 .comment-inline-form textarea {
4431 4438 width: 100%;
4432 4439 height: 100px;
4433 4440 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4434 4441 }
4435 4442
4436 4443 form.comment-inline-form {
4437 4444 margin-top: 10px;
4438 4445 margin-left: 10px;
4439 4446 }
4440 4447
4441 4448 .comment-inline-form-submit {
4442 4449 margin-top: 5px;
4443 4450 margin-left: 525px;
4444 4451 }
4445 4452
4446 4453 .file-comments {
4447 4454 display: none;
4448 4455 }
4449 4456
4450 4457 .comment-inline-form .comment {
4451 4458 margin-left: 10px;
4452 4459 }
4453 4460
4454 4461 .comment-inline-form .comment-help {
4455 4462 padding: 5px 5px 5px 5px;
4456 4463 color: #666;
4457 4464 }
4458 4465
4459 4466 .comment-inline-form .comment-help .preview-btn,
4460 4467 .comment-inline-form .comment-help .edit-btn {
4461 4468 float: right;
4462 4469 margin: -6px 0px 0px 0px;
4463 4470 }
4464 4471
4465 4472 .comment-inline-form .comment-button {
4466 4473 padding-top: 5px;
4467 4474 }
4468 4475
4469 4476 /** comment inline **/
4470 4477 .inline-comments {
4471 4478 padding: 10px 20px;
4472 4479 }
4473 4480
4474 4481 .inline-comments div.rst-block {
4475 4482 clear: both;
4476 4483 overflow: hidden;
4477 4484 margin: 0;
4478 4485 padding: 0 20px 0px;
4479 4486 }
4480 4487 .inline-comments .comment {
4481 4488 border: 1px solid #ddd;
4482 4489 -webkit-border-radius: 4px;
4483 4490 border-radius: 4px;
4484 4491 margin: 3px 3px 5px 5px;
4485 4492 background-color: #FAFAFA;
4486 4493 }
4487 4494 .inline-comments .add-comment {
4488 4495 padding: 2px 4px 8px 5px;
4489 4496 }
4490 4497
4491 4498 .inline-comments .comment-wrapp {
4492 4499 padding: 1px;
4493 4500 }
4494 4501 .inline-comments .comment .meta {
4495 4502 background: #f8f8f8;
4496 4503 padding: 4px;
4497 4504 border-bottom: 1px solid #ddd;
4498 4505 height: 20px;
4499 4506 }
4500 4507
4501 4508 .inline-comments .comment .meta img {
4502 4509 vertical-align: middle;
4503 4510 }
4504 4511
4505 4512 .inline-comments .comment .meta .user {
4506 4513 font-weight: bold;
4507 4514 float: left;
4508 4515 padding: 3px;
4509 4516 }
4510 4517
4511 4518 .inline-comments .comment .meta .date {
4512 4519 float: left;
4513 4520 padding: 3px;
4514 4521 }
4515 4522
4516 4523 .inline-comments .comment .text {
4517 4524 background-color: #FAFAFA;
4518 4525 }
4519 4526
4520 4527 .inline-comments .comments-number {
4521 4528 padding: 0px 0px 10px 0px;
4522 4529 font-weight: bold;
4523 4530 color: #666;
4524 4531 font-size: 16px;
4525 4532 }
4526 4533 .inline-comments-button .add-comment {
4527 4534 margin: 2px 0px 8px 5px !important
4528 4535 }
4529 4536
4530 4537 .notification-paginator {
4531 4538 padding: 0px 0px 4px 16px;
4532 4539 }
4533 4540
4534 4541 #context-pages .pull-request span,
4535 4542 .menu_link_notifications {
4536 4543 padding: 4px 4px !important;
4537 4544 text-align: center;
4538 4545 color: #888 !important;
4539 4546 background-color: #DEDEDE !important;
4540 4547 border-radius: 4px !important;
4541 4548 -webkit-border-radius: 4px !important;
4542 4549 }
4543 4550
4544 4551 #context-pages .forks span,
4545 4552 .menu_link_notifications {
4546 4553 padding: 4px 4px !important;
4547 4554 text-align: center;
4548 4555 color: #888 !important;
4549 4556 background-color: #DEDEDE !important;
4550 4557 border-radius: 4px !important;
4551 4558 -webkit-border-radius: 4px !important;
4552 4559 }
4553 4560
4554 4561
4555 4562 .notification-header {
4556 4563 padding-top: 6px;
4557 4564 }
4558 4565 .notification-header .desc {
4559 4566 font-size: 16px;
4560 4567 height: 24px;
4561 4568 float: left
4562 4569 }
4563 4570 .notification-list .container.unread {
4564 4571 background: none repeat scroll 0 0 rgba(255, 255, 180, 0.6);
4565 4572 }
4566 4573 .notification-header .gravatar {
4567 4574 background: none repeat scroll 0 0 transparent;
4568 4575 padding: 0px 0px 0px 8px;
4569 4576 }
4570 4577 .notification-list .container .notification-header .desc {
4571 4578 font-weight: bold;
4572 4579 font-size: 17px;
4573 4580 }
4574 4581 .notification-table {
4575 4582 border: 1px solid #ccc;
4576 4583 -webkit-border-radius: 6px 6px 6px 6px;
4577 4584 border-radius: 6px 6px 6px 6px;
4578 4585 clear: both;
4579 4586 margin: 0px 20px 0px 20px;
4580 4587 }
4581 4588 .notification-header .delete-notifications {
4582 4589 float: right;
4583 4590 padding-top: 8px;
4584 4591 cursor: pointer;
4585 4592 }
4586 4593 .notification-header .read-notifications {
4587 4594 float: right;
4588 4595 padding-top: 8px;
4589 4596 cursor: pointer;
4590 4597 }
4591 4598 .notification-subject {
4592 4599 clear: both;
4593 4600 border-bottom: 1px solid #eee;
4594 4601 padding: 5px 0px 5px 38px;
4595 4602 }
4596 4603
4597 4604 .notification-body {
4598 4605 clear: both;
4599 4606 margin: 34px 2px 2px 8px
4600 4607 }
4601 4608
4602 4609 /****
4603 4610 PULL REQUESTS
4604 4611 *****/
4605 4612 .pullrequests_section_head {
4606 4613 padding: 10px 10px 10px 0px;
4607 4614 font-size: 16px;
4608 4615 font-weight: bold;
4609 4616 }
4610 4617
4611 4618 h3.closed,
4612 4619 #pullrequests_container li.closed a
4613 4620 {
4614 4621 color: #555;
4615 4622 background: #eee;
4616 4623 }
4617 4624
4618 4625 div.pr-title {
4619 4626 font-size: 1.6em;
4620 4627 }
4621 4628
4622 4629 div.pr {
4623 4630 border-bottom: 1px solid #DDD;
4624 4631 margin: 0px 20px;
4625 4632 padding: 10px 4px;
4626 4633 }
4627 4634 div.pr-closed {
4628 4635 background-color: rgba(245,245,245,0.5);
4629 4636 }
4630 4637
4631 4638 span.pr-closed-tag {
4632 4639 margin-bottom: 1px;
4633 4640 margin-right: 1px;
4634 4641 padding: 1px 3px;
4635 4642 font-size: 10px;
4636 4643 padding: 1px 3px 1px 3px;
4637 4644 font-size: 10px;
4638 4645 color: #336699;
4639 4646 white-space: nowrap;
4640 4647 -webkit-border-radius: 4px;
4641 4648 border-radius: 4px;
4642 4649 border: 1px solid #d9e8f8;
4643 4650 line-height: 1.5em;
4644 4651 }
4645 4652
4646 4653 /****
4647 4654 PERMS
4648 4655 *****/
4649 4656 #perms .perms_section_head {
4650 4657 padding: 10px 10px 10px 0px;
4651 4658 font-size: 16px;
4652 4659 font-weight: bold;
4653 4660 }
4654 4661
4655 4662 #perms .perm_tag {
4656 4663 padding: 1px 3px 1px 3px;
4657 4664 font-size: 10px;
4658 4665 font-weight: bold;
4659 4666 text-transform: uppercase;
4660 4667 white-space: nowrap;
4661 4668 -webkit-border-radius: 3px;
4662 4669 border-radius: 3px;
4663 4670 }
4664 4671
4665 4672 #perms .perm_tag.admin {
4666 4673 background-color: #B94A48;
4667 4674 color: #ffffff;
4668 4675 }
4669 4676
4670 4677 #perms .perm_tag.write {
4671 4678 background-color: #DB7525;
4672 4679 color: #ffffff;
4673 4680 }
4674 4681
4675 4682 #perms .perm_tag.read {
4676 4683 background-color: #468847;
4677 4684 color: #ffffff;
4678 4685 }
4679 4686
4680 4687 #perms .perm_tag.none {
4681 4688 background-color: #bfbfbf;
4682 4689 color: #ffffff;
4683 4690 }
4684 4691
4685 4692 .perm-gravatar {
4686 4693 vertical-align: middle;
4687 4694 padding: 2px;
4688 4695 }
4689 4696 .perm-gravatar-ac {
4690 4697 vertical-align: middle;
4691 4698 padding: 2px;
4692 4699 width: 14px;
4693 4700 height: 14px;
4694 4701 }
4695 4702
4696 4703 /*****************************************************************************
4697 4704 DIFFS CSS
4698 4705 ******************************************************************************/
4699 4706 .diff-collapse {
4700 4707 text-align: center;
4701 4708 margin-bottom: -15px;
4702 4709 }
4703 4710 .diff-collapse-button {
4704 4711 cursor: pointer;
4705 4712 color: #666;
4706 4713 font-size: 16px;
4707 4714 }
4708 4715 .diff-container {
4709 4716
4710 4717 }
4711 4718
4712 4719 .diff-container.hidden {
4713 4720 display: none;
4714 4721 overflow: hidden;
4715 4722 }
4716 4723
4717 4724
4718 4725 div.diffblock {
4719 4726 overflow: auto;
4720 4727 padding: 0px;
4721 4728 border: 1px solid #ccc;
4722 4729 background: #f8f8f8;
4723 4730 font-size: 100%;
4724 4731 line-height: 100%;
4725 4732 /* new */
4726 4733 line-height: 125%;
4727 4734 -webkit-border-radius: 6px 6px 0px 0px;
4728 4735 border-radius: 6px 6px 0px 0px;
4729 4736 }
4730 4737 div.diffblock.margined {
4731 4738 margin: 0px 20px 0px 20px;
4732 4739 }
4733 4740 div.diffblock .code-header {
4734 4741 border-bottom: 1px solid #CCCCCC;
4735 4742 background: #EEEEEE;
4736 4743 padding: 10px 0 10px 0;
4737 4744 height: 14px;
4738 4745 }
4739 4746
4740 4747 div.diffblock .code-header.banner {
4741 4748 border-bottom: 1px solid #CCCCCC;
4742 4749 background: #EEEEEE;
4743 4750 height: 14px;
4744 4751 margin: 0px 95px 0px 95px;
4745 4752 padding: 3px 3px 11px 3px;
4746 4753 }
4747 4754
4748 4755 div.diffblock .code-header-title {
4749 4756 padding: 0px 0px 10px 5px !important;
4750 4757 margin: 0 !important;
4751 4758 }
4752 4759 div.diffblock .code-header .hash {
4753 4760 float: left;
4754 4761 padding: 2px 0 0 2px;
4755 4762 }
4756 4763 div.diffblock .code-header .date {
4757 4764 float: left;
4758 4765 text-transform: uppercase;
4759 4766 padding: 2px 0px 0px 2px;
4760 4767 }
4761 4768 div.diffblock .code-header div {
4762 4769 margin-left: 4px;
4763 4770 font-weight: bold;
4764 4771 font-size: 14px;
4765 4772 }
4766 4773
4767 4774 div.diffblock .parents {
4768 4775 float: left;
4769 4776 height: 26px;
4770 4777 width: 100px;
4771 4778 font-size: 10px;
4772 4779 font-weight: 400;
4773 4780 vertical-align: middle;
4774 4781 padding: 0px 2px 2px 2px;
4775 4782 background-color: #eeeeee;
4776 4783 border-bottom: 1px solid #CCCCCC;
4777 4784 }
4778 4785
4779 4786 div.diffblock .children {
4780 4787 float: right;
4781 4788 height: 26px;
4782 4789 width: 100px;
4783 4790 font-size: 10px;
4784 4791 font-weight: 400;
4785 4792 vertical-align: middle;
4786 4793 text-align: right;
4787 4794 padding: 0px 2px 2px 2px;
4788 4795 background-color: #eeeeee;
4789 4796 border-bottom: 1px solid #CCCCCC;
4790 4797 }
4791 4798
4792 4799 div.diffblock .code-body {
4793 4800 background: #FFFFFF;
4794 4801 }
4795 4802 div.diffblock pre.raw {
4796 4803 background: #FFFFFF;
4797 4804 color: #000000;
4798 4805 }
4799 4806 table.code-difftable {
4800 4807 border-collapse: collapse;
4801 4808 width: 99%;
4802 4809 border-radius: 0px !important;
4803 4810 }
4804 4811 table.code-difftable td {
4805 4812 padding: 0 !important;
4806 4813 background: none !important;
4807 4814 border: 0 !important;
4808 4815 vertical-align: baseline !important
4809 4816 }
4810 4817 table.code-difftable .context {
4811 4818 background: none repeat scroll 0 0 #DDE7EF;
4812 4819 }
4813 4820 table.code-difftable .add {
4814 4821 background: none repeat scroll 0 0 #DDFFDD;
4815 4822 }
4816 4823 table.code-difftable .add ins {
4817 4824 background: none repeat scroll 0 0 #AAFFAA;
4818 4825 text-decoration: none;
4819 4826 }
4820 4827 table.code-difftable .del {
4821 4828 background: none repeat scroll 0 0 #FFDDDD;
4822 4829 }
4823 4830 table.code-difftable .del del {
4824 4831 background: none repeat scroll 0 0 #FFAAAA;
4825 4832 text-decoration: none;
4826 4833 }
4827 4834
4828 4835 /** LINE NUMBERS **/
4829 4836 table.code-difftable .lineno {
4830 4837
4831 4838 padding-left: 2px;
4832 4839 padding-right: 2px;
4833 4840 text-align: right;
4834 4841 width: 32px;
4835 4842 -moz-user-select: none;
4836 4843 -webkit-user-select: none;
4837 4844 border-right: 1px solid #CCC !important;
4838 4845 border-left: 0px solid #CCC !important;
4839 4846 border-top: 0px solid #CCC !important;
4840 4847 border-bottom: none !important;
4841 4848 vertical-align: middle !important;
4842 4849
4843 4850 }
4844 4851 table.code-difftable .lineno.new {
4845 4852 }
4846 4853 table.code-difftable .lineno.old {
4847 4854 }
4848 4855 table.code-difftable .lineno a {
4849 4856 color: #747474 !important;
4850 4857 font: 11px "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace !important;
4851 4858 letter-spacing: -1px;
4852 4859 text-align: right;
4853 4860 padding-right: 2px;
4854 4861 cursor: pointer;
4855 4862 display: block;
4856 4863 width: 32px;
4857 4864 }
4858 4865
4859 4866 table.code-difftable .lineno-inline {
4860 4867 background: none repeat scroll 0 0 #FFF !important;
4861 4868 padding-left: 2px;
4862 4869 padding-right: 2px;
4863 4870 text-align: right;
4864 4871 width: 30px;
4865 4872 -moz-user-select: none;
4866 4873 -webkit-user-select: none;
4867 4874 }
4868 4875
4869 4876 /** CODE **/
4870 4877 table.code-difftable .code {
4871 4878 display: block;
4872 4879 width: 100%;
4873 4880 }
4874 4881 table.code-difftable .code td {
4875 4882 margin: 0;
4876 4883 padding: 0;
4877 4884 }
4878 4885 table.code-difftable .code pre {
4879 4886 margin: 0;
4880 4887 padding: 0;
4881 4888 height: 17px;
4882 4889 line-height: 17px;
4883 4890 }
4884 4891
4885 4892
4886 4893 .diffblock.margined.comm .line .code:hover {
4887 4894 background-color: #FFFFCC !important;
4888 4895 cursor: pointer !important;
4889 4896 background-image: url("../images/icons/comment_add.png") !important;
4890 4897 background-repeat: no-repeat !important;
4891 4898 background-position: right !important;
4892 4899 background-position: 0% 50% !important;
4893 4900 }
4894 4901 .diffblock.margined.comm .line .code.no-comment:hover {
4895 4902 background-image: none !important;
4896 4903 cursor: auto !important;
4897 4904 background-color: inherit !important;
4898 4905 }
4899 4906
4900 4907 div.comment:target>.comment-wrapp {
4901 4908 border: solid 2px #ee0 !important;
4902 4909 }
4903 4910
4904 4911 .lineno:target a {
4905 4912 border: solid 2px #ee0 !important;
4906 4913 margin: -2px;
4907 4914 }
@@ -1,66 +1,64 b''
1 1 ## -*- coding: utf-8 -*-
2 2 ##usage:
3 3 ## <%namespace name="diff_block" file="/changeset/diff_block.html"/>
4 4 ## ${diff_block.diff_block(change)}
5 5 ##
6 6 <%def name="diff_block(change)">
7 7 <div class="diff-collapse">
8 8 <span target="${'diff-container-%s' % (id(change))}" class="diff-collapse-button">&uarr; ${_('Collapse diff')} &uarr;</span>
9 9 </div>
10 10 <div class="diff-container" id="${'diff-container-%s' % (id(change))}">
11 11 %for FID,(cs1, cs2, change, path, diff, stats) in change.iteritems():
12 ##%if op !='removed':
13 12 <div id="${FID}_target" style="clear:both;margin-top:25px"></div>
14 13 <div id="${FID}" class="diffblock margined comm">
15 14 <div class="code-header">
16 15 <div class="changeset_header">
17 16 <div class="changeset_file">
18 17 ${h.link_to_if(change!='D',h.safe_unicode(path),h.url('files_home',repo_name=c.repo_name,
19 18 revision=cs2,f_path=h.safe_unicode(path)))}
20 19 </div>
21 20 <div class="diff-actions">
22 21 <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(path),diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}" class="tooltip" title="${h.tooltip(_('Show full diff for this file'))}"><img class="icon" src="${h.url('/images/icons/page_white_go.png')}"/></a>
23 22 <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(path),diff2=cs2,diff1=cs1,diff='raw')}" class="tooltip" title="${h.tooltip(_('Raw diff'))}"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
24 23 <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(path),diff2=cs2,diff1=cs1,diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}"><img class="icon" src="${h.url('/images/icons/page_save.png')}"/></a>
25 24 ${c.ignorews_url(request.GET, h.FID(cs2,path))}
26 25 ${c.context_url(request.GET, h.FID(cs2,path))}
27 26 </div>
28 27 <span style="float:right;margin-top:-3px">
29 28 <label>
30 29 ${_('Show inline comments')}
31 30 ${h.checkbox('',checked="checked",class_="show-inline-comments",id_for=h.FID(cs2,path))}
32 31 </label>
33 32 </span>
34 33 </div>
35 34 </div>
36 35 <div class="code-body">
37 36 <div class="full_f_path" path="${h.safe_unicode(path)}"></div>
38 37 ${diff|n}
39 38 </div>
40 39 </div>
41 ##%endif
42 40 %endfor
43 41 </div>
44 42 </%def>
45 43
46 44 <%def name="diff_block_simple(change)">
47 45
48 46 %for op,filenode_path,diff in change:
49 47 <div id="${h.FID('',filenode_path)}_target" style="clear:both;margin-top:25px"></div>
50 48 <div id="${h.FID('',filenode_path)}" class="diffblock margined comm">
51 49 <div class="code-header">
52 50 <div class="changeset_header">
53 51 <div class="changeset_file">
54 52 ${h.safe_unicode(filenode_path)} |
55 53 <a class="spantag" href="${h.url('files_home', repo_name=c.other_repo.repo_name, f_path=filenode_path, revision=c.org_ref)}" title="${_('Show file at latest version in this repo')}">${c.org_ref_type}@${h.short_id(c.org_ref) if c.org_ref_type=='rev' else c.org_ref}</a> -&gt;
56 54 <a class="spantag" href="${h.url('files_home', repo_name=c.repo_name, f_path=filenode_path, revision=c.other_ref)}" title="${_('Show file at initial version in this repo')}">${c.other_ref_type}@${h.short_id(c.other_ref) if c.other_ref_type=='rev' else c.other_ref}</a>
57 55 </div>
58 56 </div>
59 57 </div>
60 58 <div class="code-body">
61 59 <div class="full_f_path" path="${h.safe_unicode(filenode_path)}"></div>
62 60 ${diff|n}
63 61 </div>
64 62 </div>
65 63 %endfor
66 64 </%def>
@@ -1,552 +1,556 b''
1 1 diff --git a/img/baseline-10px.png b/img/baseline-10px.png
2 2 new file mode 100644
3 3 index 0000000000000000000000000000000000000000..16095dcbf5c9ea41caeb1e3e41d647d425222ed1
4 4 GIT binary patch
5 5 literal 152
6 6 zc%17D@N?(olHy`uVBq!ia0vp^j6j^i!3HGVb)pi0lw^r(L`iUdT1k0gQ7VIDN`6wR
7 7 zf@f}GdTLN=VoGJ<$y6JlA}dc9$B>F!Nx%O8w`Ue+77%bXFxq5j_~-xsZV_1~1zCBH
8 8 y)y@U((_~Lrb!=|_@`K?vV_&A58+!u-Gs6x+MGjBnI|qTLFnGH9xvX<aXaWHBd@WW0
9 9
10 10 diff --git a/img/baseline-20px.png b/img/baseline-20px.png
11 11 deleted file mode 100644
12 Binary file img/baseline-20px.png has changed
12 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000
13 GIT binary patch
14 literal 0
15 Hc$@<O00001
16
13 17 diff --git a/index.html b/index.html
14 18 --- a/index.html
15 19 +++ b/index.html
16 20 @@ -2,7 +2,7 @@
17 21 <html lang="en">
18 22 <head>
19 23 <meta charset="utf-8">
20 24 - <title>Baseline</title>
21 25 + <title>Twitter Baseline</title>
22 26
23 27 <!-- // Less.js at the ready! -->
24 28 <link rel="stylesheet/less" type="text/css" media="all" href="less/baseline.less" />
25 29 @@ -11,6 +11,7 @@
26 30 <!-- // jQuery! -->
27 31 <script type="text/javascript" src="http://code.jquery.com/jquery-1.5.2.min.js"></script>
28 32 <script type="text/javascript" src="http://tablesorter.com/jquery.tablesorter.min.js"></script>
29 33 + <script type="text/javascript" src="js/jquery/hashgrid.js"></script>
30 34 <script type="text/javascript">
31 35 $(document).ready(function(){
32 36 // Active state in top nav
33 37 @@ -36,7 +37,7 @@
34 38 <!--
35 39 <style>
36 40 body {
37 41 - background: url(img/baseline-20px.png) repeat 0 0, url(img/grid-940px.png) repeat-y top center;
38 42 + background: url(img/baseline-10px.png) repeat 0 0, url(img/grid-940px.png) repeat-y top center;
39 43 background-color: #fff;
40 44 }
41 45 </style>
42 46 diff --git a/js/global.js b/js/global.js
43 47 deleted file mode 100644
44 48 --- a/js/global.js
45 49 +++ /dev/null
46 50 @@ -1,75 +0,0 @@
47 51 -$(document).ready(function(){
48 52 - // Get Heights
49 53 - windowHeight = $(window).height();
50 54 - documentHeight = $(document).height();
51 55 - sidebarHeight = windowHeight - 40;
52 56 - containerHeight = windowHeight - 40;
53 57 -
54 58 - // Get Widths
55 59 - windowWidth = $(window).width();
56 60 - containerWidth = windowWidth - 200;
57 61 -
58 62 - if (windowHeight < containerHeight) {
59 63 -
60 64 - // Set Dimensions for default state (before resize)
61 65 - $('div#sidebar').css({
62 66 - height: sidebarHeight
63 67 - });
64 68 - $('div#container').css({
65 69 - width: containerWidth,
66 70 - height: containerHeight
67 71 - });
68 72 -
69 73 - } else {
70 74 -
71 75 - // During resize, set widths
72 76 - $(window).resize(function() {
73 77 - console.log('Window Height: ' + $(window).height() + ', Sidebar Height:' + ($(window).height() - 40));
74 78 -
75 79 - // Get Heights
76 80 - windowHeight = $(window).height();
77 81 - sidebarHeight = windowHeight - 40;
78 82 - containerHeight = windowHeight - 40;
79 83 -
80 84 - // Get Widths
81 85 - windowWidth = $(window).width();
82 86 - containerWidth = windowWidth - 200;
83 87 -
84 88 - // Set Dimensions for default state (before resize)
85 89 - $('div#sidebar').css({
86 90 - height: sidebarHeight
87 91 - });
88 92 - $('div#container').css({
89 93 - width: containerWidth,
90 94 - height: containerHeight
91 95 - });
92 96 - });
93 97 - // console.log('omgz window is less than container so... fuck.');
94 98 - $('div#sidebar').css({
95 99 - height: documentHeight - 40
96 100 - });
97 101 -
98 102 - }
99 103 -
100 104 -
101 105 -
102 106 -/*
103 107 - // Toggle Calendars
104 108 - $('div#sidebar ul li a').click(function() {
105 109 - if ($(this).is('#toggleMonthView')) {
106 110 - console.log('toggle month');
107 111 - $(this).addClass('active');
108 112 - $('#toggleListView').removeClass('active');
109 113 - $('table#monthView').show();
110 114 - $('table#listView').hide();
111 115 - } else {
112 116 - console.log('toggle list');
113 117 - $(this).addClass('active');
114 118 - $('#toggleMonthView').removeClass('active');
115 119 - $('table#listView').show();
116 120 - $('table#monthView').hide();
117 121 - }
118 122 - return false;
119 123 - });
120 124 -*/
121 125 -});
122 126 diff --git a/js/jquery/hashgrid.js b/js/jquery/hashgrid.js
123 127 new file mode 100755
124 128 --- /dev/null
125 129 +++ b/js/jquery/hashgrid.js
126 130 @@ -0,0 +1,340 @@
127 131 +/**
128 132 + * hashgrid (jQuery version)
129 133 + * http://github.com/dotjay/hashgrid
130 134 + * Version 5, 3 Nov 2010
131 135 + * Written by Jon Gibbins, dotjay.co.uk, accessibility.co.uk
132 136 + * Contibutors:
133 137 + * Sean Coates, seancoates.com
134 138 + * Phil Dokas, jetless.org
135 139 + *
136 140 + * // Using a basic #grid setup
137 141 + * var grid = new hashgrid();
138 142 + *
139 143 + * // Using #grid with a custom id (e.g. #mygrid)
140 144 + * var grid = new hashgrid("mygrid");
141 145 + *
142 146 + * // Using #grid with additional options
143 147 + * var grid = new hashgrid({
144 148 + * id: 'mygrid', // id for the grid container
145 149 + * modifierKey: 'alt', // optional 'ctrl', 'alt' or 'shift'
146 150 + * showGridKey: 's', // key to show the grid
147 151 + * holdGridKey: 'enter', // key to hold the grid in place
148 152 + * foregroundKey: 'f', // key to toggle foreground/background
149 153 + * jumpGridsKey: 'd', // key to cycle through the grid classes
150 154 + * numberOfGrids: 2, // number of grid classes used
151 155 + * classPrefix: 'class', // prefix for the grid classes
152 156 + * cookiePrefix: 'mygrid' // prefix for the cookie name
153 157 + * });
154 158 + */
155 159 +if (typeof jQuery == "undefined") {
156 160 + alert("Hashgrid: jQuery not loaded. Make sure it's linked to your pages.");
157 161 +}
158 162 +
159 163 +
160 164 +/**
161 165 + * hashgrid overlay
162 166 + */
163 167 +var hashgrid = function(set) {
164 168 +
165 169 + var options = {
166 170 + id: 'grid', // id for the grid container
167 171 + modifierKey: null, // optional 'ctrl', 'alt' or 'shift'
168 172 + showGridKey: 'g', // key to show the grid
169 173 + holdGridKey: 'h', // key to hold the grid in place
170 174 + foregroundKey: 'f', // key to toggle foreground/background
171 175 + jumpGridsKey: 'j', // key to cycle through the grid classes
172 176 + numberOfGrids: 1, // number of grid classes used
173 177 + classPrefix: 'grid-', // prefix for the grid classes
174 178 + cookiePrefix: 'hashgrid'// prefix for the cookie name
175 179 + };
176 180 + var overlayOn = false,
177 181 + sticky = false,
178 182 + overlayZState = 'B',
179 183 + overlayZBackground = -1,
180 184 + overlayZForeground = 9999,
181 185 + classNumber = 1;
182 186 +
183 187 + // Apply options
184 188 + if (typeof set == 'object') {
185 189 + var k;
186 190 + for (k in set) options[k] = set[k];
187 191 + }
188 192 + else if (typeof set == 'string') {
189 193 + options.id = set;
190 194 + }
191 195 +
192 196 + // Remove any conflicting overlay
193 197 + if ($('#' + options.id).length > 0) {
194 198 + $('#' + options.id).remove();
195 199 + }
196 200 +
197 201 + // Create overlay, hidden before adding to DOM
198 202 + var overlayEl = $('<div></div>');
199 203 + overlayEl
200 204 + .attr('id', options.id)
201 205 + .css({
202 206 + display: 'none',
203 207 + 'pointer-events': 'none'
204 208 + });
205 209 + $("body").prepend(overlayEl);
206 210 + var overlay = $('#' + options.id);
207 211 +
208 212 + // Unless a custom z-index is set, ensure the overlay will be behind everything
209 213 + if (overlay.css('z-index') == 'auto') overlay.css('z-index', overlayZBackground);
210 214 +
211 215 + // Override the default overlay height with the actual page height
212 216 + var pageHeight = parseFloat($(document).height());
213 217 + overlay.height(pageHeight);
214 218 +
215 219 + // Add the first grid line so that we can measure it
216 220 + overlay.append('<div id="' + options.id + '-horiz" class="horiz first-line">');
217 221 +
218 222 + // Position off-screen and display to calculate height
219 223 + var top = overlay.css("top");
220 224 + overlay.css({
221 225 + top: "-999px",
222 226 + display: "block"
223 227 + });
224 228 +
225 229 + // Calculate the number of grid lines needed
226 230 + var line = $('#' + options.id + '-horiz'),
227 231 + lineHeight = line.outerHeight();
228 232 +
229 233 + // Hide and reset top
230 234 + overlay.css({
231 235 + display: "none",
232 236 + top: top
233 237 + });
234 238 +
235 239 + // Break on zero line height
236 240 + if (lineHeight <= 0) return true;
237 241 +
238 242 + // Add the remaining grid lines
239 243 + var i, numGridLines = Math.floor(pageHeight / lineHeight);
240 244 + for (i = numGridLines - 1; i >= 1; i--) {
241 245 + overlay.append('<div class="horiz"></div>');
242 246 + }
243 247 +
244 248 + // vertical grid
245 249 + overlay.append($('<div class="vert-container"></div>'));
246 250 + var overlayVert = overlay.children('.vert-container');
247 251 + var gridWidth = overlay.width();
248 252 + overlayVert.css({width: gridWidth, position: 'absolute', top: 0});
249 253 + overlayVert.append('<div class="vert first-line">&nbsp;</div>');
250 254 +
251 255 + // 30 is an arbitrarily large number...
252 256 + // can't calculate the margin width properly
253 257 + for (i = 0; i < 30; i++) {
254 258 + overlayVert.append('<div class="vert">&nbsp;</div>');
255 259 + }
256 260 +
257 261 + overlayVert.children()
258 262 + .height(pageHeight)
259 263 + .css({display: 'inline-block'});
260 264 +
261 265 + // Check for saved state
262 266 + var overlayCookie = readCookie(options.cookiePrefix + options.id);
263 267 + if (typeof overlayCookie == 'string') {
264 268 + var state = overlayCookie.split(',');
265 269 + state[2] = Number(state[2]);
266 270 + if ((typeof state[2] == 'number') && !isNaN(state[2])) {
267 271 + classNumber = state[2].toFixed(0);
268 272 + overlay.addClass(options.classPrefix + classNumber);
269 273 + }
270 274 + if (state[1] == 'F') {
271 275 + overlayZState = 'F';
272 276 + overlay.css('z-index', overlayZForeground);
273 277 + }
274 278 + if (state[0] == '1') {
275 279 + overlayOn = true;
276 280 + sticky = true;
277 281 + showOverlay();
278 282 + }
279 283 + }
280 284 + else {
281 285 + overlay.addClass(options.classPrefix + classNumber);
282 286 + }
283 287 +
284 288 + // Keyboard controls
285 289 + $(document).bind('keydown', keydownHandler);
286 290 + $(document).bind('keyup', keyupHandler);
287 291 +
288 292 + /**
289 293 + * Helpers
290 294 + */
291 295 +
292 296 + function getModifier(e) {
293 297 + if (options.modifierKey == null) return true; // Bypass by default
294 298 + var m = true;
295 299 + switch(options.modifierKey) {
296 300 + case 'ctrl':
297 301 + m = (e.ctrlKey ? e.ctrlKey : false);
298 302 + break;
299 303 +
300 304 + case 'alt':
301 305 + m = (e.altKey ? e.altKey : false);
302 306 + break;
303 307 +
304 308 + case 'shift':
305 309 + m = (e.shiftKey ? e.shiftKey : false);
306 310 + break;
307 311 + }
308 312 + return m;
309 313 + }
310 314 +
311 315 + function getKey(e) {
312 316 + var k = false, c = (e.keyCode ? e.keyCode : e.which);
313 317 + // Handle keywords
314 318 + if (c == 13) k = 'enter';
315 319 + // Handle letters
316 320 + else k = String.fromCharCode(c).toLowerCase();
317 321 + return k;
318 322 + }
319 323 +
320 324 + function saveState() {
321 325 + createCookie(options.cookiePrefix + options.id, (sticky ? '1' : '0') + ',' + overlayZState + ',' + classNumber, 1);
322 326 + }
323 327 +
324 328 + function showOverlay() {
325 329 + overlay.show();
326 330 + overlayVert.css({width: overlay.width()});
327 331 + // hide any vertical blocks that aren't at the top of the viewport
328 332 + overlayVert.children('.vert').each(function () {
329 333 + $(this).css('display','inline-block');
330 334 + if ($(this).offset().top > 0) {
331 335 + $(this).hide();
332 336 + }
333 337 + });
334 338 + }
335 339 +
336 340 + /**
337 341 + * Event handlers
338 342 + */
339 343 +
340 344 + function keydownHandler(e) {
341 345 + var source = e.target.tagName.toLowerCase();
342 346 + if ((source == 'input') || (source == 'textarea') || (source == 'select')) return true;
343 347 + var m = getModifier(e);
344 348 + if (!m) return true;
345 349 + var k = getKey(e);
346 350 + if (!k) return true;
347 351 + switch(k) {
348 352 + case options.showGridKey:
349 353 + if (!overlayOn) {
350 354 + showOverlay();
351 355 + overlayOn = true;
352 356 + }
353 357 + else if (sticky) {
354 358 + overlay.hide();
355 359 + overlayOn = false;
356 360 + sticky = false;
357 361 + saveState();
358 362 + }
359 363 + break;
360 364 + case options.holdGridKey:
361 365 + if (overlayOn && !sticky) {
362 366 + // Turn sticky overlay on
363 367 + sticky = true;
364 368 + saveState();
365 369 + }
366 370 + break;
367 371 + case options.foregroundKey:
368 372 + if (overlayOn) {
369 373 + // Toggle sticky overlay z-index
370 374 + if (overlay.css('z-index') == overlayZForeground) {
371 375 + overlay.css('z-index', overlayZBackground);
372 376 + overlayZState = 'B';
373 377 + }
374 378 + else {
375 379 + overlay.css('z-index', overlayZForeground);
376 380 + overlayZState = 'F';
377 381 + }
378 382 + saveState();
379 383 + }
380 384 + break;
381 385 + case options.jumpGridsKey:
382 386 + if (overlayOn && (options.numberOfGrids > 1)) {
383 387 + // Cycle through the available grids
384 388 + overlay.removeClass(options.classPrefix + classNumber);
385 389 + classNumber++;
386 390 + if (classNumber > options.numberOfGrids) classNumber = 1;
387 391 + overlay.addClass(options.classPrefix + classNumber);
388 392 + showOverlay();
389 393 + if (/webkit/.test( navigator.userAgent.toLowerCase() )) {
390 394 + forceRepaint();
391 395 + }
392 396 + saveState();
393 397 + }
394 398 + break;
395 399 + }
396 400 + }
397 401 +
398 402 + function keyupHandler(e) {
399 403 + var m = getModifier(e);
400 404 + if (!m) return true;
401 405 + var k = getKey(e);
402 406 + if (!k) return true;
403 407 + if ((k == options.showGridKey) && !sticky) {
404 408 + overlay.hide();
405 409 + overlayOn = false;
406 410 + }
407 411 + }
408 412 +
409 413 + /**
410 414 + * Cookie functions
411 415 + *
412 416 + * By Peter-Paul Koch:
413 417 + * http://www.quirksmode.org/js/cookies.html
414 418 + */
415 419 + function createCookie(name,value,days) {
416 420 + if (days) {
417 421 + var date = new Date();
418 422 + date.setTime(date.getTime()+(days*24*60*60*1000));
419 423 + var expires = "; expires="+date.toGMTString();
420 424 + }
421 425 + else var expires = "";
422 426 + document.cookie = name+"="+value+expires+"; path=/";
423 427 + }
424 428 +
425 429 + function readCookie(name) {
426 430 + var nameEQ = name + "=";
427 431 + var ca = document.cookie.split(';');
428 432 + for(var i=0;i < ca.length;i++) {
429 433 + var c = ca[i];
430 434 + while (c.charAt(0)==' ') c = c.substring(1,c.length);
431 435 + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
432 436 + }
433 437 + return null;
434 438 + }
435 439 +
436 440 + function eraseCookie(name) {
437 441 + createCookie(name,"",-1);
438 442 + }
439 443 +
440 444 + /**
441 445 + * Forces a repaint (because WebKit has issues)
442 446 + * http://www.sitepoint.com/forums/showthread.php?p=4538763
443 447 + * http://www.phpied.com/the-new-game-show-will-it-reflow/
444 448 + */
445 449 + function forceRepaint() {
446 450 + var ss = document.styleSheets[0];
447 451 + try {
448 452 + ss.addRule('.xxxxxx', 'position: relative');
449 453 + ss.removeRule(ss.rules.length - 1);
450 454 + } catch(e){}
451 455 + }
452 456 +
453 457 +}
454 458 +
455 459 +
456 460 +/**
457 461 + * You can call hashgrid from your own code, but it's loaded here as
458 462 + * an example for your convenience.
459 463 + */
460 464 +$(document).ready(function() {
461 465 +
462 466 + var grid = new hashgrid({
463 467 + numberOfGrids: 2
464 468 + });
465 469 +
466 470 +});
467 471 diff --git a/less/docs.less b/less/docs.less
468 472 --- a/less/docs.less
469 473 +++ b/less/docs.less
470 474 @@ -1,3 +1,10 @@
471 475 +body {
472 476 + #gradient > .vertical-three-colors(#eee, #fff, 0.15, #fff);
473 477 + background-attachment: fixed;
474 478 + background-position: 0 40px;
475 479 + position: relative;
476 480 +}
477 481 +
478 482 // Give us some love
479 483 header,
480 484 section,
481 485 @@ -77,3 +84,30 @@
482 486 section {
483 487 margin-bottom: 40px;
484 488 }
485 489 +
486 490 +// Hashgrid grid
487 491 +/**
488 492 + * Grid
489 493 + */
490 494 +#grid {
491 495 + width: 980px;
492 496 + position: absolute;
493 497 + top: 0;
494 498 + left: 50%;
495 499 + margin-left: -490px;
496 500 +}
497 501 +#grid div.vert {
498 502 + width: 39px;
499 503 + border: solid darkturquoise;
500 504 + border-width: 0 1px;
501 505 + margin-right: 19px;
502 506 +}
503 507 +#grid div.vert.first-line {
504 508 + margin-left: 19px;
505 509 +}
506 510 +#grid div.horiz {
507 511 + height: 19px;
508 512 + border-bottom: 1px dotted rgba(255,0,0,.25);
509 513 + margin: 0;
510 514 + padding: 0;
511 515 +}
512 516 diff --git a/less/scaffolding.less b/less/scaffolding.less
513 517 --- a/less/scaffolding.less
514 518 +++ b/less/scaffolding.less
515 519 @@ -7,7 +7,7 @@
516 520 -------------------------------------------------- */
517 521
518 522 div.row {
519 523 - .clearfix;
520 524 + .clearfix();
521 525 div.span1 { .columns(1); }
522 526 div.span2 { .columns(2); }
523 527 div.span3 { .columns(3); }
524 528 @@ -34,8 +34,6 @@
525 529 background-color: #fff;
526 530 }
527 531 body {
528 532 - #gradient > .vertical-three-colors(#eee, #fff, 0.25, #fff);
529 533 - background-attachment: fixed;
530 534 margin: 0;
531 535 .sans-serif(normal,14px,20px);
532 536 color: @gray;
533 537 diff --git a/readme.markdown b/readme.markdown
534 538 --- a/readme.markdown
535 539 +++ b/readme.markdown
536 540 @@ -1,13 +1,4 @@
537 541 TODOS
538 542
539 543 -* Write "Using Twitter BP" section
540 544 -** Two ways to use: LESS.js or compiled
541 545 -** Not meant to be 100% bulletproof, but is 90% bulletproof (stats?)
542 546 -** Advanced framework for fast prototyping, internal app development, bootstraping new websites
543 547 -** Can be easily modified to provide more legacy support
544 548 -
545 549 -* Add grid examples back in
546 550 * Cross browser checks? Show this anywhere?
547 551 -* Add layouts section back in
548 552 -
549 553 -* Point JS libraries to public library links instead of within the repo
550 554 \ No newline at end of file
551 555 +* Add layouts section back in
552 556 \ No newline at end of file
@@ -1,3 +1,6 b''
1 1 diff --git a/US Warszawa.jpg b/US Warszawa.jpg
2 2 deleted file mode 100755
3 Binary file US Warszawa.jpg has changed
3 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000
4 GIT binary patch
5 literal 0
6 Hc$@<O00001
@@ -1,86 +1,251 b''
1 1 from __future__ import with_statement
2 2 import os
3 3 import unittest
4 4 from rhodecode.tests import *
5 5 from rhodecode.lib.diffs import DiffProcessor, NEW_FILENODE, DEL_FILENODE, \
6 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE
6 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
7 7
8 8 dn = os.path.dirname
9 9 FIXTURES = os.path.join(dn(dn(os.path.abspath(__file__))), 'fixtures')
10 10
11 11 DIFF_FIXTURES = {
12 12 'hg_diff_add_single_binary_file.diff': [
13 (u'US Warszawa.jpg', 'A', ['b', NEW_FILENODE]),
13 ('US Warszawa.jpg', 'A',
14 {'added': 0,
15 'deleted': 0,
16 'binary': True,
17 'ops': {NEW_FILENODE: 'new file 100755',
18 BIN_FILENODE: 'binary diff not shown'}}),
14 19 ],
15 20 'hg_diff_mod_single_binary_file.diff': [
16 (u'US Warszawa.jpg', 'M', ['b', MOD_FILENODE]),
21 ('US Warszawa.jpg', 'M',
22 {'added': 0,
23 'deleted': 0,
24 'binary': True,
25 'ops': {MOD_FILENODE: 'modified file',
26 BIN_FILENODE: 'binary diff not shown'}}),
27 ],
28
29 'hg_diff_mod_single_file_and_rename_and_chmod.diff': [
30 ('README', 'M',
31 {'added': 3,
32 'deleted': 0,
33 'binary': False,
34 'ops': {MOD_FILENODE: 'modified file',
35 RENAMED_FILENODE: 'file renamed from README.rst to README',
36 CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
37 ],
38 'hg_diff_rename_and_chmod_file.diff': [
39 ('README', 'M',
40 {'added': 3,
41 'deleted': 0,
42 'binary': False,
43 'ops': {MOD_FILENODE: 'modified file',
44 BIN_FILENODE: 'binary diff not shown'}}),
17 45 ],
18 46 'hg_diff_del_single_binary_file.diff': [
19 (u'US Warszawa.jpg', 'D', ['b', DEL_FILENODE]),
47 ('US Warszawa.jpg', 'D',
48 {'added': 0,
49 'deleted': 0,
50 'binary': True,
51 'ops': {DEL_FILENODE: 'deleted file',
52 BIN_FILENODE: 'binary diff not shown'}}),
53 ],
54 'hg_diff_chmod_and_mod_single_binary_file.diff': [
55 ('gravatar.png', 'M',
56 {'added': 0,
57 'deleted': 0,
58 'binary': True,
59 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
60 BIN_FILENODE: 'binary diff not shown'}}),
61 ],
62 'hg_diff_chmod.diff': [
63 ('file', 'M',
64 {'added': 0,
65 'deleted': 0,
66 'binary': True,
67 'ops': {CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
68 ],
69 'hg_diff_rename_file.diff': [
70 ('file_renamed', 'M',
71 {'added': 0,
72 'deleted': 0,
73 'binary': True,
74 'ops': {RENAMED_FILENODE: 'file renamed from file to file_renamed'}}),
75 ],
76 'hg_diff_rename_and_chmod_file.diff': [
77 ('README', 'M',
78 {'added': 0,
79 'deleted': 0,
80 'binary': True,
81 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
82 RENAMED_FILENODE: 'file renamed from README.rst to README'}}),
20 83 ],
21 84 'hg_diff_binary_and_normal.diff': [
22 (u'img/baseline-10px.png', 'A', ['b', NEW_FILENODE]),
23 (u'js/jquery/hashgrid.js', 'A', [340, 0]),
24 (u'index.html', 'M', [3, 2]),
25 (u'less/docs.less', 'M', [34, 0]),
26 (u'less/scaffolding.less', 'M', [1, 3]),
27 (u'readme.markdown', 'M', [1, 10]),
28 (u'img/baseline-20px.png', 'D', ['b', DEL_FILENODE]),
29 (u'js/global.js', 'D', [0, 75])
30 ],
31 'hg_diff_chmod.diff': [
32 (u'file', 'M', ['b', CHMOD_FILENODE]),
33 ],
34 'hg_diff_rename_file.diff': [
35 (u'file_renamed', 'M', ['b', RENAMED_FILENODE]),
85 ('img/baseline-10px.png', 'A',
86 {'added': 0,
87 'deleted': 0,
88 'binary': True,
89 'ops': {NEW_FILENODE: 'new file 100644',
90 BIN_FILENODE: 'binary diff not shown'}}),
91 ('js/jquery/hashgrid.js', 'A',
92 {'added': 340,
93 'deleted': 0,
94 'binary': False,
95 'ops': {NEW_FILENODE: 'new file 100755'}}),
96 ('index.html', 'M',
97 {'added': 3,
98 'deleted': 2,
99 'binary': False,
100 'ops': {MOD_FILENODE: 'modified file'}}),
101 ('less/docs.less', 'M',
102 {'added': 34,
103 'deleted': 0,
104 'binary': False,
105 'ops': {MOD_FILENODE: 'modified file'}}),
106 ('less/scaffolding.less', 'M',
107 {'added': 1,
108 'deleted': 3,
109 'binary': False,
110 'ops': {MOD_FILENODE: 'modified file'}}),
111 ('readme.markdown', 'M',
112 {'added': 1,
113 'deleted': 10,
114 'binary': False,
115 'ops': {MOD_FILENODE: 'modified file'}}),
116 ('img/baseline-20px.png', 'D',
117 {'added': 0,
118 'deleted': 0,
119 'binary': True,
120 'ops': {DEL_FILENODE: 'deleted file',
121 BIN_FILENODE: 'binary diff not shown'}}),
122 ('js/global.js', 'D',
123 {'added': 0,
124 'deleted': 75,
125 'binary': False,
126 'ops': {DEL_FILENODE: 'deleted file'}})
36 127 ],
37 128 'git_diff_chmod.diff': [
38 (u'work-horus.xls', 'M', ['b', CHMOD_FILENODE]),
129 ('work-horus.xls', 'M',
130 {'added': 0,
131 'deleted': 0,
132 'binary': True,
133 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755'}})
39 134 ],
40 135 'git_diff_rename_file.diff': [
41 (u'file.xls', 'M', ['b', RENAMED_FILENODE]),
136 ('file.xls', 'M',
137 {'added': 0,
138 'deleted': 0,
139 'binary': True,
140 'ops': {RENAMED_FILENODE: 'file renamed from work-horus.xls to file.xls'}})
42 141 ],
43 142 'git_diff_mod_single_binary_file.diff': [
44 ('US Warszawa.jpg', 'M', ['b', MOD_FILENODE])
45
143 ('US Warszawa.jpg', 'M',
144 {'added': 0,
145 'deleted': 0,
146 'binary': True,
147 'ops': {MOD_FILENODE: 'modified file',
148 BIN_FILENODE: 'binary diff not shown'}})
46 149 ],
47 150 'git_diff_binary_and_normal.diff': [
48 (u'img/baseline-10px.png', 'A', ['b', NEW_FILENODE]),
49 (u'js/jquery/hashgrid.js', 'A', [340, 0]),
50 (u'index.html', 'M', [3, 2]),
51 (u'less/docs.less', 'M', [34, 0]),
52 (u'less/scaffolding.less', 'M', [1, 3]),
53 (u'readme.markdown', 'M', [1, 10]),
54 (u'img/baseline-20px.png', 'D', ['b', DEL_FILENODE]),
55 (u'js/global.js', 'D', [0, 75])
151 ('img/baseline-10px.png', 'A',
152 {'added': 0,
153 'deleted': 0,
154 'binary': True,
155 'ops': {NEW_FILENODE: 'new file 100644',
156 BIN_FILENODE: 'binary diff not shown'}}),
157 ('js/jquery/hashgrid.js', 'A',
158 {'added': 340,
159 'deleted': 0,
160 'binary': False,
161 'ops': {NEW_FILENODE: 'new file 100755'}}),
162 ('index.html', 'M',
163 {'added': 3,
164 'deleted': 2,
165 'binary': False,
166 'ops': {MOD_FILENODE: 'modified file'}}),
167 ('less/docs.less', 'M',
168 {'added': 34,
169 'deleted': 0,
170 'binary': False,
171 'ops': {MOD_FILENODE: 'modified file'}}),
172 ('less/scaffolding.less', 'M',
173 {'added': 1,
174 'deleted': 3,
175 'binary': False,
176 'ops': {MOD_FILENODE: 'modified file'}}),
177 ('readme.markdown', 'M',
178 {'added': 1,
179 'deleted': 10,
180 'binary': False,
181 'ops': {MOD_FILENODE: 'modified file'}}),
182 ('img/baseline-20px.png', 'D',
183 {'added': 0,
184 'deleted': 0,
185 'binary': True,
186 'ops': {DEL_FILENODE: 'deleted file',
187 BIN_FILENODE: 'binary diff not shown'}}),
188 ('js/global.js', 'D',
189 {'added': 0,
190 'deleted': 75,
191 'binary': False,
192 'ops': {DEL_FILENODE: 'deleted file'}}),
56 193 ],
57 194 'diff_with_diff_data.diff': [
58 (u'vcs/backends/base.py', 'M', [18, 2]),
59 (u'vcs/backends/git/repository.py', 'M', [46, 15]),
60 (u'vcs/backends/hg.py', 'M', [22, 3]),
61 (u'vcs/tests/test_git.py', 'M', [5, 5]),
62 (u'vcs/tests/test_repository.py', 'M', [174, 2])
195 ('vcs/backends/base.py', 'M',
196 {'added': 18,
197 'deleted': 2,
198 'binary': False,
199 'ops': {MOD_FILENODE: 'modified file'}}),
200 ('vcs/backends/git/repository.py', 'M',
201 {'added': 46,
202 'deleted': 15,
203 'binary': False,
204 'ops': {MOD_FILENODE: 'modified file'}}),
205 ('vcs/backends/hg.py', 'M',
206 {'added': 22,
207 'deleted': 3,
208 'binary': False,
209 'ops': {MOD_FILENODE: 'modified file'}}),
210 ('vcs/tests/test_git.py', 'M',
211 {'added': 5,
212 'deleted': 5,
213 'binary': False,
214 'ops': {MOD_FILENODE: 'modified file'}}),
215 ('vcs/tests/test_repository.py', 'M',
216 {'added': 174,
217 'deleted': 2,
218 'binary': False,
219 'ops': {MOD_FILENODE: 'modified file'}}),
63 220 ],
64 221 # 'large_diff.diff': [
65 #
222 # ('.hgignore', 'A', {'deleted': 0, 'binary': False, 'added': 3, 'ops': {1: 'new file 100644'}}),
223 # ('MANIFEST.in', 'A', {'deleted': 0, 'binary': False, 'added': 3, 'ops': {1: 'new file 100644'}}),
224 # ('README.txt', 'A', {'deleted': 0, 'binary': False, 'added': 19, 'ops': {1: 'new file 100644'}}),
225 # ('development.ini', 'A', {'deleted': 0, 'binary': False, 'added': 116, 'ops': {1: 'new file 100644'}}),
226 # ('docs/index.txt', 'A', {'deleted': 0, 'binary': False, 'added': 19, 'ops': {1: 'new file 100644'}}),
227 # ('ez_setup.py', 'A', {'deleted': 0, 'binary': False, 'added': 276, 'ops': {1: 'new file 100644'}}),
228 # ('hgapp.py', 'A', {'deleted': 0, 'binary': False, 'added': 26, 'ops': {1: 'new file 100644'}}),
229 # ('hgwebdir.config', 'A', {'deleted': 0, 'binary': False, 'added': 21, 'ops': {1: 'new file 100644'}}),
230 # ('pylons_app.egg-info/PKG-INFO', 'A', {'deleted': 0, 'binary': False, 'added': 10, 'ops': {1: 'new file 100644'}}),
231 # ('pylons_app.egg-info/SOURCES.txt', 'A', {'deleted': 0, 'binary': False, 'added': 33, 'ops': {1: 'new file 100644'}}),
232 # ('pylons_app.egg-info/dependency_links.txt', 'A', {'deleted': 0, 'binary': False, 'added': 1, 'ops': {1: 'new file 100644'}}),
233 # #TODO:
66 234 # ],
67 235
68
69 236 }
70 237
71 238
72 def _diff_checker(fixture):
73 with open(os.path.join(FIXTURES, fixture)) as f:
239 class DiffLibTest(unittest.TestCase):
240
241 @parameterized.expand([(x,) for x in DIFF_FIXTURES])
242 def test_diff(self, diff_fixture):
243
244 with open(os.path.join(FIXTURES, diff_fixture)) as f:
74 245 diff = f.read()
75 246
76 247 diff_proc = DiffProcessor(diff)
77 248 diff_proc_d = diff_proc.prepare()
78 249 data = [(x['filename'], x['operation'], x['stats']) for x in diff_proc_d]
79 expected_data = DIFF_FIXTURES[fixture]
80
81 assert expected_data == data
82
83
84 def test_parse_diff():
85 for fixture in DIFF_FIXTURES:
86 yield _diff_checker, fixture
250 expected_data = DIFF_FIXTURES[diff_fixture]
251 self.assertListEqual(expected_data, data)
General Comments 0
You need to be logged in to leave comments. Login now