##// END OF EJS Templates
template: use Bootstrap tooltips and popover instead of handmade tooltips...
domruf -
r6394:1ab38cd7 default
parent child Browse files
Show More
@@ -1,473 +1,473 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.controllers.changeset
16 16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 changeset controller showing changes between revisions
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Apr 25, 2010
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28 import logging
29 29 import traceback
30 30 from collections import defaultdict
31 31
32 32 from pylons import tmpl_context as c, request, response
33 33 from pylons.i18n.translation import _
34 34 from webob.exc import HTTPFound, HTTPForbidden, HTTPBadRequest, HTTPNotFound
35 35
36 36 from kallithea.lib.utils import jsonify
37 37 from kallithea.lib.vcs.exceptions import RepositoryError, \
38 38 ChangesetDoesNotExistError, EmptyRepositoryError
39 39
40 40 from kallithea.lib.compat import json
41 41 import kallithea.lib.helpers as h
42 42 from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
43 43 NotAnonymous
44 44 from kallithea.lib.base import BaseRepoController, render
45 45 from kallithea.lib.utils import action_logger
46 46 from kallithea.lib.compat import OrderedDict
47 47 from kallithea.lib import diffs
48 48 from kallithea.model.db import ChangesetComment, ChangesetStatus
49 49 from kallithea.model.comment import ChangesetCommentsModel
50 50 from kallithea.model.changeset_status import ChangesetStatusModel
51 51 from kallithea.model.meta import Session
52 52 from kallithea.model.repo import RepoModel
53 53 from kallithea.lib.diffs import LimitedDiffContainer
54 54 from kallithea.lib.exceptions import StatusChangeOnClosedPullRequestError
55 55 from kallithea.lib.vcs.backends.base import EmptyChangeset
56 56 from kallithea.lib.utils2 import safe_unicode
57 57 from kallithea.lib.graphmod import graph_data
58 58
59 59 log = logging.getLogger(__name__)
60 60
61 61
62 62 def _update_with_GET(params, GET):
63 63 for k in ['diff1', 'diff2', 'diff']:
64 64 params[k] += GET.getall(k)
65 65
66 66
67 67 def anchor_url(revision, path, GET):
68 68 fid = h.FID(revision, path)
69 69 return h.url.current(anchor=fid, **dict(GET))
70 70
71 71
72 72 def get_ignore_ws(fid, GET):
73 73 ig_ws_global = GET.get('ignorews')
74 74 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
75 75 if ig_ws:
76 76 try:
77 77 return int(ig_ws[0].split(':')[-1])
78 78 except ValueError:
79 79 raise HTTPBadRequest()
80 80 return ig_ws_global
81 81
82 82
83 83 def _ignorews_url(GET, fileid=None):
84 84 fileid = str(fileid) if fileid else None
85 85 params = defaultdict(list)
86 86 _update_with_GET(params, GET)
87 87 lbl = _('Show whitespace')
88 88 ig_ws = get_ignore_ws(fileid, GET)
89 89 ln_ctx = get_line_ctx(fileid, GET)
90 90 # global option
91 91 if fileid is None:
92 92 if ig_ws is None:
93 93 params['ignorews'] += [1]
94 94 lbl = _('Ignore whitespace')
95 95 ctx_key = 'context'
96 96 ctx_val = ln_ctx
97 97 # per file options
98 98 else:
99 99 if ig_ws is None:
100 100 params[fileid] += ['WS:1']
101 101 lbl = _('Ignore whitespace')
102 102
103 103 ctx_key = fileid
104 104 ctx_val = 'C:%s' % ln_ctx
105 105 # if we have passed in ln_ctx pass it along to our params
106 106 if ln_ctx:
107 107 params[ctx_key] += [ctx_val]
108 108
109 109 params['anchor'] = fileid
110 110 icon = h.literal('<i class="icon-strike"></i>')
111 return h.link_to(icon, h.url.current(**params), title=lbl, class_='tooltip')
111 return h.link_to(icon, h.url.current(**params), title=lbl, **{'data-toggle': 'tooltip'})
112 112
113 113
114 114 def get_line_ctx(fid, GET):
115 115 ln_ctx_global = GET.get('context')
116 116 if fid:
117 117 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
118 118 else:
119 119 _ln_ctx = filter(lambda k: k.startswith('C'), GET)
120 120 ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
121 121 if ln_ctx:
122 122 ln_ctx = [ln_ctx]
123 123
124 124 if ln_ctx:
125 125 retval = ln_ctx[0].split(':')[-1]
126 126 else:
127 127 retval = ln_ctx_global
128 128
129 129 try:
130 130 return int(retval)
131 131 except Exception:
132 132 return 3
133 133
134 134
135 135 def _context_url(GET, fileid=None):
136 136 """
137 137 Generates url for context lines
138 138
139 139 :param fileid:
140 140 """
141 141
142 142 fileid = str(fileid) if fileid else None
143 143 ig_ws = get_ignore_ws(fileid, GET)
144 144 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
145 145
146 146 params = defaultdict(list)
147 147 _update_with_GET(params, GET)
148 148
149 149 # global option
150 150 if fileid is None:
151 151 if ln_ctx > 0:
152 152 params['context'] += [ln_ctx]
153 153
154 154 if ig_ws:
155 155 ig_ws_key = 'ignorews'
156 156 ig_ws_val = 1
157 157
158 158 # per file option
159 159 else:
160 160 params[fileid] += ['C:%s' % ln_ctx]
161 161 ig_ws_key = fileid
162 162 ig_ws_val = 'WS:%s' % 1
163 163
164 164 if ig_ws:
165 165 params[ig_ws_key] += [ig_ws_val]
166 166
167 167 lbl = _('Increase diff context to %(num)s lines') % {'num': ln_ctx}
168 168
169 169 params['anchor'] = fileid
170 170 icon = h.literal('<i class="icon-sort"></i>')
171 return h.link_to(icon, h.url.current(**params), title=lbl, class_='tooltip')
171 return h.link_to(icon, h.url.current(**params), title=lbl, **{'data-toggle': 'tooltip'})
172 172
173 173
174 174 # Could perhaps be nice to have in the model but is too high level ...
175 175 def create_comment(text, status, f_path, line_no, revision=None, pull_request_id=None, closing_pr=None):
176 176 """Comment functionality shared between changesets and pullrequests"""
177 177 f_path = f_path or None
178 178 line_no = line_no or None
179 179
180 180 comment = ChangesetCommentsModel().create(
181 181 text=text,
182 182 repo=c.db_repo.repo_id,
183 183 author=c.authuser.user_id,
184 184 revision=revision,
185 185 pull_request=pull_request_id,
186 186 f_path=f_path,
187 187 line_no=line_no,
188 188 status_change=ChangesetStatus.get_status_lbl(status) if status else None,
189 189 closing_pr=closing_pr,
190 190 )
191 191
192 192 return comment
193 193
194 194
195 195 class ChangesetController(BaseRepoController):
196 196
197 197 def __before__(self):
198 198 super(ChangesetController, self).__before__()
199 199 c.affected_files_cut_off = 60
200 200
201 201 def __load_data(self):
202 202 repo_model = RepoModel()
203 203 c.users_array = repo_model.get_users_js()
204 204 c.user_groups_array = repo_model.get_user_groups_js()
205 205
206 206 def _index(self, revision, method):
207 207 c.pull_request = None
208 208 c.anchor_url = anchor_url
209 209 c.ignorews_url = _ignorews_url
210 210 c.context_url = _context_url
211 211 c.fulldiff = fulldiff = request.GET.get('fulldiff')
212 212 #get ranges of revisions if preset
213 213 rev_range = revision.split('...')[:2]
214 214 enable_comments = True
215 215 c.cs_repo = c.db_repo
216 216 try:
217 217 if len(rev_range) == 2:
218 218 enable_comments = False
219 219 rev_start = rev_range[0]
220 220 rev_end = rev_range[1]
221 221 rev_ranges = c.db_repo_scm_instance.get_changesets(start=rev_start,
222 222 end=rev_end)
223 223 else:
224 224 rev_ranges = [c.db_repo_scm_instance.get_changeset(revision)]
225 225
226 226 c.cs_ranges = list(rev_ranges)
227 227 if not c.cs_ranges:
228 228 raise RepositoryError('Changeset range returned empty result')
229 229
230 230 except (ChangesetDoesNotExistError, EmptyRepositoryError):
231 231 log.debug(traceback.format_exc())
232 232 msg = _('Such revision does not exist for this repository')
233 233 h.flash(msg, category='error')
234 234 raise HTTPNotFound()
235 235
236 236 c.changes = OrderedDict()
237 237
238 238 c.lines_added = 0 # count of lines added
239 239 c.lines_deleted = 0 # count of lines removes
240 240
241 241 c.changeset_statuses = ChangesetStatus.STATUSES
242 242 comments = dict()
243 243 c.statuses = []
244 244 c.inline_comments = []
245 245 c.inline_cnt = 0
246 246
247 247 # Iterate over ranges (default changeset view is always one changeset)
248 248 for changeset in c.cs_ranges:
249 249 if method == 'show':
250 250 c.statuses.extend([ChangesetStatusModel().get_status(
251 251 c.db_repo.repo_id, changeset.raw_id)])
252 252
253 253 # Changeset comments
254 254 comments.update((com.comment_id, com)
255 255 for com in ChangesetCommentsModel()
256 256 .get_comments(c.db_repo.repo_id,
257 257 revision=changeset.raw_id))
258 258
259 259 # Status change comments - mostly from pull requests
260 260 comments.update((st.comment_id, st.comment)
261 261 for st in ChangesetStatusModel()
262 262 .get_statuses(c.db_repo.repo_id,
263 263 changeset.raw_id, with_revisions=True)
264 264 if st.comment_id is not None)
265 265
266 266 inlines = ChangesetCommentsModel() \
267 267 .get_inline_comments(c.db_repo.repo_id,
268 268 revision=changeset.raw_id)
269 269 c.inline_comments.extend(inlines)
270 270
271 271 cs2 = changeset.raw_id
272 272 cs1 = changeset.parents[0].raw_id if changeset.parents else EmptyChangeset().raw_id
273 273 context_lcl = get_line_ctx('', request.GET)
274 274 ign_whitespace_lcl = get_ignore_ws('', request.GET)
275 275
276 276 _diff = c.db_repo_scm_instance.get_diff(cs1, cs2,
277 277 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
278 278 diff_limit = self.cut_off_limit if not fulldiff else None
279 279 diff_processor = diffs.DiffProcessor(_diff,
280 280 vcs=c.db_repo_scm_instance.alias,
281 281 format='gitdiff',
282 282 diff_limit=diff_limit)
283 283 file_diff_data = []
284 284 if method == 'show':
285 285 _parsed = diff_processor.prepare()
286 286 c.limited_diff = False
287 287 if isinstance(_parsed, LimitedDiffContainer):
288 288 c.limited_diff = True
289 289 for f in _parsed:
290 290 st = f['stats']
291 291 c.lines_added += st['added']
292 292 c.lines_deleted += st['deleted']
293 293 filename = f['filename']
294 294 fid = h.FID(changeset.raw_id, filename)
295 295 url_fid = h.FID('', filename)
296 296 diff = diff_processor.as_html(enable_comments=enable_comments,
297 297 parsed_lines=[f])
298 298 file_diff_data.append((fid, url_fid, f['operation'], f['old_filename'], filename, diff, st))
299 299 else:
300 300 # downloads/raw we only need RAW diff nothing else
301 301 diff = diff_processor.as_raw()
302 302 file_diff_data.append(('', None, None, None, diff, None))
303 303 c.changes[changeset.raw_id] = (cs1, cs2, file_diff_data)
304 304
305 305 #sort comments in creation order
306 306 c.comments = [com for com_id, com in sorted(comments.items())]
307 307
308 308 # count inline comments
309 309 for __, lines in c.inline_comments:
310 310 for comments in lines.values():
311 311 c.inline_cnt += len(comments)
312 312
313 313 if len(c.cs_ranges) == 1:
314 314 c.changeset = c.cs_ranges[0]
315 315 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
316 316 for x in c.changeset.parents])
317 317 if method == 'download':
318 318 response.content_type = 'text/plain'
319 319 response.content_disposition = 'attachment; filename=%s.diff' \
320 320 % revision[:12]
321 321 return diff
322 322 elif method == 'patch':
323 323 response.content_type = 'text/plain'
324 324 c.diff = safe_unicode(diff)
325 325 return render('changeset/patch_changeset.html')
326 326 elif method == 'raw':
327 327 response.content_type = 'text/plain'
328 328 return diff
329 329 elif method == 'show':
330 330 self.__load_data()
331 331 if len(c.cs_ranges) == 1:
332 332 return render('changeset/changeset.html')
333 333 else:
334 334 c.cs_ranges_org = None
335 335 c.cs_comments = {}
336 336 revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
337 337 c.jsdata = json.dumps(graph_data(c.db_repo_scm_instance, revs))
338 338 return render('changeset/changeset_range.html')
339 339
340 340 @LoginRequired()
341 341 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
342 342 'repository.admin')
343 343 def index(self, revision, method='show'):
344 344 return self._index(revision, method=method)
345 345
346 346 @LoginRequired()
347 347 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
348 348 'repository.admin')
349 349 def changeset_raw(self, revision):
350 350 return self._index(revision, method='raw')
351 351
352 352 @LoginRequired()
353 353 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
354 354 'repository.admin')
355 355 def changeset_patch(self, revision):
356 356 return self._index(revision, method='patch')
357 357
358 358 @LoginRequired()
359 359 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
360 360 'repository.admin')
361 361 def changeset_download(self, revision):
362 362 return self._index(revision, method='download')
363 363
364 364 @LoginRequired()
365 365 @NotAnonymous()
366 366 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
367 367 'repository.admin')
368 368 @jsonify
369 369 def comment(self, repo_name, revision):
370 370 assert request.environ.get('HTTP_X_PARTIAL_XHR')
371 371
372 372 status = request.POST.get('changeset_status')
373 373 text = request.POST.get('text', '').strip()
374 374
375 375 c.comment = create_comment(
376 376 text,
377 377 status,
378 378 revision=revision,
379 379 f_path=request.POST.get('f_path'),
380 380 line_no=request.POST.get('line'),
381 381 )
382 382
383 383 # get status if set !
384 384 if status:
385 385 # if latest status was from pull request and it's closed
386 386 # disallow changing status ! RLY?
387 387 try:
388 388 ChangesetStatusModel().set_status(
389 389 c.db_repo.repo_id,
390 390 status,
391 391 c.authuser.user_id,
392 392 c.comment,
393 393 revision=revision,
394 394 dont_allow_on_closed_pull_request=True,
395 395 )
396 396 except StatusChangeOnClosedPullRequestError:
397 397 log.debug('cannot change status on %s with closed pull request', revision)
398 398 raise HTTPBadRequest()
399 399
400 400 action_logger(self.authuser,
401 401 'user_commented_revision:%s' % revision,
402 402 c.db_repo, self.ip_addr, self.sa)
403 403
404 404 Session().commit()
405 405
406 406 data = {
407 407 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
408 408 }
409 409 if c.comment is not None:
410 410 data.update(c.comment.get_dict())
411 411 data.update({'rendered_text':
412 412 render('changeset/changeset_comment_block.html')})
413 413
414 414 return data
415 415
416 416 @LoginRequired()
417 417 @NotAnonymous()
418 418 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
419 419 'repository.admin')
420 420 @jsonify
421 421 def delete_comment(self, repo_name, comment_id):
422 422 co = ChangesetComment.get_or_404(comment_id)
423 423 if co.repo.repo_name != repo_name:
424 424 raise HTTPNotFound()
425 425 owner = co.author_id == c.authuser.user_id
426 426 repo_admin = h.HasRepoPermissionAny('repository.admin')(repo_name)
427 427 if h.HasPermissionAny('hg.admin')() or repo_admin or owner:
428 428 ChangesetCommentsModel().delete(comment=co)
429 429 Session().commit()
430 430 return True
431 431 else:
432 432 raise HTTPForbidden()
433 433
434 434 @LoginRequired()
435 435 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
436 436 'repository.admin')
437 437 @jsonify
438 438 def changeset_info(self, repo_name, revision):
439 439 if request.is_xhr:
440 440 try:
441 441 return c.db_repo_scm_instance.get_changeset(revision)
442 442 except ChangesetDoesNotExistError as e:
443 443 return EmptyChangeset(message=str(e))
444 444 else:
445 445 raise HTTPBadRequest()
446 446
447 447 @LoginRequired()
448 448 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
449 449 'repository.admin')
450 450 @jsonify
451 451 def changeset_children(self, repo_name, revision):
452 452 if request.is_xhr:
453 453 changeset = c.db_repo_scm_instance.get_changeset(revision)
454 454 result = {"results": []}
455 455 if changeset.children:
456 456 result = {"results": changeset.children}
457 457 return result
458 458 else:
459 459 raise HTTPBadRequest()
460 460
461 461 @LoginRequired()
462 462 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
463 463 'repository.admin')
464 464 @jsonify
465 465 def changeset_parents(self, repo_name, revision):
466 466 if request.is_xhr:
467 467 changeset = c.db_repo_scm_instance.get_changeset(revision)
468 468 result = {"results": []}
469 469 if changeset.parents:
470 470 result = {"results": changeset.parents}
471 471 return result
472 472 else:
473 473 raise HTTPBadRequest()
@@ -1,1205 +1,1204 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 Helper functions
16 16
17 17 Consists of functions to typically be used within templates, but also
18 18 available to Controllers. This module is available to both as 'h'.
19 19 """
20 20 import hashlib
21 21 import StringIO
22 22 import logging
23 23 import re
24 24 import urlparse
25 25 import textwrap
26 26
27 27 from beaker.cache import cache_region
28 28 from pygments.formatters.html import HtmlFormatter
29 29 from pygments import highlight as code_highlight
30 30 from pylons.i18n.translation import _
31 31
32 32 from webhelpers.html import literal, HTML, escape
33 33 from webhelpers.html.tags import checkbox, end_form, hidden, link_to, \
34 34 select, submit, text, password, textarea, radio, form as insecure_form
35 35 from webhelpers.number import format_byte_size
36 36 from webhelpers.pylonslib import Flash as _Flash
37 37 from webhelpers.pylonslib.secure_form import secure_form, authentication_token
38 38 from webhelpers.text import chop_at, truncate, wrap_paragraphs
39 39 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
40 40 convert_boolean_attrs, NotGiven, _make_safe_id_component
41 41
42 42 from kallithea.config.routing import url
43 43 from kallithea.lib.annotate import annotate_highlight
44 44 from kallithea.lib.pygmentsutils import get_custom_lexer
45 45 from kallithea.lib.utils2 import str2bool, safe_unicode, safe_str, \
46 46 time_to_datetime, AttributeDict, safe_int, MENTIONS_REGEX
47 47 from kallithea.lib.markup_renderer import url_re
48 48 from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError
49 49 from kallithea.lib.vcs.backends.base import BaseChangeset, EmptyChangeset
50 50
51 51 log = logging.getLogger(__name__)
52 52
53 53
54 54 def canonical_url(*args, **kargs):
55 55 '''Like url(x, qualified=True), but returns url that not only is qualified
56 56 but also canonical, as configured in canonical_url'''
57 57 from kallithea import CONFIG
58 58 try:
59 59 parts = CONFIG.get('canonical_url', '').split('://', 1)
60 60 kargs['host'] = parts[1].split('/', 1)[0]
61 61 kargs['protocol'] = parts[0]
62 62 except IndexError:
63 63 kargs['qualified'] = True
64 64 return url(*args, **kargs)
65 65
66 66 def canonical_hostname():
67 67 '''Return canonical hostname of system'''
68 68 from kallithea import CONFIG
69 69 try:
70 70 parts = CONFIG.get('canonical_url', '').split('://', 1)
71 71 return parts[1].split('/', 1)[0]
72 72 except IndexError:
73 73 parts = url('home', qualified=True).split('://', 1)
74 74 return parts[1].split('/', 1)[0]
75 75
76 76 def html_escape(s):
77 77 """Return string with all html escaped.
78 78 This is also safe for javascript in html but not necessarily correct.
79 79 """
80 80 return (s
81 81 .replace('&', '&amp;')
82 82 .replace(">", "&gt;")
83 83 .replace("<", "&lt;")
84 84 .replace('"', "&quot;")
85 85 .replace("'", "&apos;")
86 86 )
87 87
88 88 def shorter(s, size=20, firstline=False, postfix='...'):
89 89 """Truncate s to size, including the postfix string if truncating.
90 90 If firstline, truncate at newline.
91 91 """
92 92 if firstline:
93 93 s = s.split('\n', 1)[0].rstrip()
94 94 if len(s) > size:
95 95 return s[:size - len(postfix)] + postfix
96 96 return s
97 97
98 98
99 99 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
100 100 """
101 101 Reset button
102 102 """
103 103 _set_input_attrs(attrs, type, name, value)
104 104 _set_id_attr(attrs, id, name)
105 105 convert_boolean_attrs(attrs, ["disabled"])
106 106 return HTML.input(**attrs)
107 107
108 108 reset = _reset
109 109 safeid = _make_safe_id_component
110 110
111 111
112 112 def FID(raw_id, path):
113 113 """
114 114 Creates a unique ID for filenode based on it's hash of path and revision
115 115 it's safe to use in urls
116 116
117 117 :param raw_id:
118 118 :param path:
119 119 """
120 120
121 121 return 'C-%s-%s' % (short_id(raw_id), hashlib.md5(safe_str(path)).hexdigest()[:12])
122 122
123 123
124 124 class _FilesBreadCrumbs(object):
125 125
126 126 def __call__(self, repo_name, rev, paths):
127 127 if isinstance(paths, str):
128 128 paths = safe_unicode(paths)
129 129 url_l = [link_to(repo_name, url('files_home',
130 130 repo_name=repo_name,
131 131 revision=rev, f_path=''),
132 132 class_='ypjax-link')]
133 133 paths_l = paths.split('/')
134 134 for cnt, p in enumerate(paths_l):
135 135 if p != '':
136 136 url_l.append(link_to(p,
137 137 url('files_home',
138 138 repo_name=repo_name,
139 139 revision=rev,
140 140 f_path='/'.join(paths_l[:cnt + 1])
141 141 ),
142 142 class_='ypjax-link'
143 143 )
144 144 )
145 145
146 146 return literal('/'.join(url_l))
147 147
148 148 files_breadcrumbs = _FilesBreadCrumbs()
149 149
150 150
151 151 class CodeHtmlFormatter(HtmlFormatter):
152 152 """
153 153 My code Html Formatter for source codes
154 154 """
155 155
156 156 def wrap(self, source, outfile):
157 157 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
158 158
159 159 def _wrap_code(self, source):
160 160 for cnt, it in enumerate(source):
161 161 i, t = it
162 162 t = '<span id="L%s">%s</span>' % (cnt + 1, t)
163 163 yield i, t
164 164
165 165 def _wrap_tablelinenos(self, inner):
166 166 dummyoutfile = StringIO.StringIO()
167 167 lncount = 0
168 168 for t, line in inner:
169 169 if t:
170 170 lncount += 1
171 171 dummyoutfile.write(line)
172 172
173 173 fl = self.linenostart
174 174 mw = len(str(lncount + fl - 1))
175 175 sp = self.linenospecial
176 176 st = self.linenostep
177 177 la = self.lineanchors
178 178 aln = self.anchorlinenos
179 179 nocls = self.noclasses
180 180 if sp:
181 181 lines = []
182 182
183 183 for i in range(fl, fl + lncount):
184 184 if i % st == 0:
185 185 if i % sp == 0:
186 186 if aln:
187 187 lines.append('<a href="#%s%d" class="special">%*d</a>' %
188 188 (la, i, mw, i))
189 189 else:
190 190 lines.append('<span class="special">%*d</span>' % (mw, i))
191 191 else:
192 192 if aln:
193 193 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
194 194 else:
195 195 lines.append('%*d' % (mw, i))
196 196 else:
197 197 lines.append('')
198 198 ls = '\n'.join(lines)
199 199 else:
200 200 lines = []
201 201 for i in range(fl, fl + lncount):
202 202 if i % st == 0:
203 203 if aln:
204 204 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
205 205 else:
206 206 lines.append('%*d' % (mw, i))
207 207 else:
208 208 lines.append('')
209 209 ls = '\n'.join(lines)
210 210
211 211 # in case you wonder about the seemingly redundant <div> here: since the
212 212 # content in the other cell also is wrapped in a div, some browsers in
213 213 # some configurations seem to mess up the formatting...
214 214 if nocls:
215 215 yield 0, ('<table class="%stable">' % self.cssclass +
216 216 '<tr><td><div class="linenodiv" '
217 217 'style="background-color: #f0f0f0; padding-right: 10px">'
218 218 '<pre style="line-height: 125%">' +
219 219 ls + '</pre></div></td><td id="hlcode" class="code">')
220 220 else:
221 221 yield 0, ('<table class="%stable">' % self.cssclass +
222 222 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
223 223 ls + '</pre></div></td><td id="hlcode" class="code">')
224 224 yield 0, dummyoutfile.getvalue()
225 225 yield 0, '</td></tr></table>'
226 226
227 227
228 228 _whitespace_re = re.compile(r'(\t)|( )(?=\n|</div>)')
229 229
230 230 def _markup_whitespace(m):
231 231 groups = m.groups()
232 232 if groups[0]:
233 233 return '<u>\t</u>'
234 234 if groups[1]:
235 235 return ' <i></i>'
236 236
237 237 def markup_whitespace(s):
238 238 return _whitespace_re.sub(_markup_whitespace, s)
239 239
240 240 def pygmentize(filenode, **kwargs):
241 241 """
242 242 pygmentize function using pygments
243 243
244 244 :param filenode:
245 245 """
246 246 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
247 247 return literal(markup_whitespace(
248 248 code_highlight(filenode.content, lexer, CodeHtmlFormatter(**kwargs))))
249 249
250 250
251 251 def pygmentize_annotation(repo_name, filenode, **kwargs):
252 252 """
253 253 pygmentize function for annotation
254 254
255 255 :param filenode:
256 256 """
257 257
258 258 color_dict = {}
259 259
260 260 def gen_color(n=10000):
261 261 """generator for getting n of evenly distributed colors using
262 262 hsv color and golden ratio. It always return same order of colors
263 263
264 264 :returns: RGB tuple
265 265 """
266 266
267 267 def hsv_to_rgb(h, s, v):
268 268 if s == 0.0:
269 269 return v, v, v
270 270 i = int(h * 6.0) # XXX assume int() truncates!
271 271 f = (h * 6.0) - i
272 272 p = v * (1.0 - s)
273 273 q = v * (1.0 - s * f)
274 274 t = v * (1.0 - s * (1.0 - f))
275 275 i = i % 6
276 276 if i == 0:
277 277 return v, t, p
278 278 if i == 1:
279 279 return q, v, p
280 280 if i == 2:
281 281 return p, v, t
282 282 if i == 3:
283 283 return p, q, v
284 284 if i == 4:
285 285 return t, p, v
286 286 if i == 5:
287 287 return v, p, q
288 288
289 289 golden_ratio = 0.618033988749895
290 290 h = 0.22717784590367374
291 291
292 292 for _unused in xrange(n):
293 293 h += golden_ratio
294 294 h %= 1
295 295 HSV_tuple = [h, 0.95, 0.95]
296 296 RGB_tuple = hsv_to_rgb(*HSV_tuple)
297 297 yield map(lambda x: str(int(x * 256)), RGB_tuple)
298 298
299 299 cgenerator = gen_color()
300 300
301 301 def get_color_string(cs):
302 302 if cs in color_dict:
303 303 col = color_dict[cs]
304 304 else:
305 305 col = color_dict[cs] = cgenerator.next()
306 306 return "color: rgb(%s)! important;" % (', '.join(col))
307 307
308 308 def url_func(repo_name):
309 309
310 310 def _url_func(changeset):
311 311 author = escape(changeset.author)
312 312 date = changeset.date
313 313 message = escape(changeset.message)
314 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
315 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
316 "</b> %s<br/></div>") % (author, date, message)
314 tooltip_html = ("<b>Author:</b> %s<br/>"
315 "<b>Date:</b> %s</b><br/>"
316 "<b>Message:</b> %s") % (author, date, message)
317 317
318 318 lnk_format = show_id(changeset)
319 319 uri = link_to(
320 320 lnk_format,
321 321 url('changeset_home', repo_name=repo_name,
322 322 revision=changeset.raw_id),
323 323 style=get_color_string(changeset.raw_id),
324 class_='safe-html-title',
325 title=tooltip_html,
326 **{'data-toggle': 'tooltip'}
324 **{'data-toggle': 'popover',
325 'data-content': tooltip_html}
327 326 )
328 327
329 328 uri += '\n'
330 329 return uri
331 330 return _url_func
332 331
333 332 return literal(markup_whitespace(annotate_highlight(filenode, url_func(repo_name), **kwargs)))
334 333
335 334
336 335 class _Message(object):
337 336 """A message returned by ``Flash.pop_messages()``.
338 337
339 338 Converting the message to a string returns the message text. Instances
340 339 also have the following attributes:
341 340
342 341 * ``message``: the message text.
343 342 * ``category``: the category specified when the message was created.
344 343 """
345 344
346 345 def __init__(self, category, message):
347 346 self.category = category
348 347 self.message = message
349 348
350 349 def __str__(self):
351 350 return self.message
352 351
353 352 __unicode__ = __str__
354 353
355 354 def __html__(self):
356 355 return escape(safe_unicode(self.message))
357 356
358 357 class Flash(_Flash):
359 358
360 359 def __call__(self, message, category=None, ignore_duplicate=False, logf=None):
361 360 """
362 361 Show a message to the user _and_ log it through the specified function
363 362
364 363 category: notice (default), warning, error, success
365 364 logf: a custom log function - such as log.debug
366 365
367 366 logf defaults to log.info, unless category equals 'success', in which
368 367 case logf defaults to log.debug.
369 368 """
370 369 if logf is None:
371 370 logf = log.info
372 371 if category == 'success':
373 372 logf = log.debug
374 373
375 374 logf('Flash %s: %s', category, message)
376 375
377 376 super(Flash, self).__call__(message, category, ignore_duplicate)
378 377
379 378 def pop_messages(self):
380 379 """Return all accumulated messages and delete them from the session.
381 380
382 381 The return value is a list of ``Message`` objects.
383 382 """
384 383 from pylons import session
385 384 messages = session.pop(self.session_key, [])
386 385 session.save()
387 386 return [_Message(*m) for m in messages]
388 387
389 388 flash = Flash()
390 389
391 390 #==============================================================================
392 391 # SCM FILTERS available via h.
393 392 #==============================================================================
394 393 from kallithea.lib.vcs.utils import author_name, author_email
395 394 from kallithea.lib.utils2 import credentials_filter, age as _age
396 395
397 396 age = lambda x, y=False: _age(x, y)
398 397 capitalize = lambda x: x.capitalize()
399 398 email = author_email
400 399 short_id = lambda x: x[:12]
401 400 hide_credentials = lambda x: ''.join(credentials_filter(x))
402 401
403 402
404 403 def show_id(cs):
405 404 """
406 405 Configurable function that shows ID
407 406 by default it's r123:fffeeefffeee
408 407
409 408 :param cs: changeset instance
410 409 """
411 410 from kallithea import CONFIG
412 411 def_len = safe_int(CONFIG.get('show_sha_length', 12))
413 412 show_rev = str2bool(CONFIG.get('show_revision_number', False))
414 413
415 414 raw_id = cs.raw_id[:def_len]
416 415 if show_rev:
417 416 return 'r%s:%s' % (cs.revision, raw_id)
418 417 else:
419 418 return raw_id
420 419
421 420
422 421 def fmt_date(date):
423 422 if date:
424 423 return date.strftime("%Y-%m-%d %H:%M:%S").decode('utf8')
425 424
426 425 return ""
427 426
428 427
429 428 def is_git(repository):
430 429 if hasattr(repository, 'alias'):
431 430 _type = repository.alias
432 431 elif hasattr(repository, 'repo_type'):
433 432 _type = repository.repo_type
434 433 else:
435 434 _type = repository
436 435 return _type == 'git'
437 436
438 437
439 438 def is_hg(repository):
440 439 if hasattr(repository, 'alias'):
441 440 _type = repository.alias
442 441 elif hasattr(repository, 'repo_type'):
443 442 _type = repository.repo_type
444 443 else:
445 444 _type = repository
446 445 return _type == 'hg'
447 446
448 447
449 448 @cache_region('long_term', 'user_or_none')
450 449 def user_or_none(author):
451 450 """Try to match email part of VCS committer string with a local user - or return None"""
452 451 from kallithea.model.db import User
453 452 email = author_email(author)
454 453 if email:
455 454 return User.get_by_email(email, cache=True) # cache will only use sql_cache_short
456 455 return None
457 456
458 457 def email_or_none(author):
459 458 """Try to match email part of VCS committer string with a local user.
460 459 Return primary email of user, email part of the specified author name, or None."""
461 460 if not author:
462 461 return None
463 462 user = user_or_none(author)
464 463 if user is not None:
465 464 return user.email # always use main email address - not necessarily the one used to find user
466 465
467 466 # extract email from the commit string
468 467 email = author_email(author)
469 468 if email:
470 469 return email
471 470
472 471 # No valid email, not a valid user in the system, none!
473 472 return None
474 473
475 474 def person(author, show_attr="username"):
476 475 """Find the user identified by 'author', return one of the users attributes,
477 476 default to the username attribute, None if there is no user"""
478 477 from kallithea.model.db import User
479 478 # attr to return from fetched user
480 479 person_getter = lambda usr: getattr(usr, show_attr)
481 480
482 481 # if author is already an instance use it for extraction
483 482 if isinstance(author, User):
484 483 return person_getter(author)
485 484
486 485 user = user_or_none(author)
487 486 if user is not None:
488 487 return person_getter(user)
489 488
490 489 # Still nothing? Just pass back the author name if any, else the email
491 490 return author_name(author) or email(author)
492 491
493 492
494 493 def person_by_id(id_, show_attr="username"):
495 494 from kallithea.model.db import User
496 495 # attr to return from fetched user
497 496 person_getter = lambda usr: getattr(usr, show_attr)
498 497
499 498 #maybe it's an ID ?
500 499 if str(id_).isdigit() or isinstance(id_, int):
501 500 id_ = int(id_)
502 501 user = User.get(id_)
503 502 if user is not None:
504 503 return person_getter(user)
505 504 return id_
506 505
507 506
508 507 def boolicon(value):
509 508 """Returns boolean value of a value, represented as small html image of true/false
510 509 icons
511 510
512 511 :param value: value
513 512 """
514 513
515 514 if value:
516 515 return HTML.tag('i', class_="icon-ok")
517 516 else:
518 517 return HTML.tag('i', class_="icon-minus-circled")
519 518
520 519
521 520 def action_parser(user_log, feed=False, parse_cs=False):
522 521 """
523 522 This helper will action_map the specified string action into translated
524 523 fancy names with icons and links
525 524
526 525 :param user_log: user log instance
527 526 :param feed: use output for feeds (no html and fancy icons)
528 527 :param parse_cs: parse Changesets into VCS instances
529 528 """
530 529
531 530 action = user_log.action
532 531 action_params = ' '
533 532
534 533 x = action.split(':')
535 534
536 535 if len(x) > 1:
537 536 action, action_params = x
538 537
539 538 def get_cs_links():
540 539 revs_limit = 3 # display this amount always
541 540 revs_top_limit = 50 # show upto this amount of changesets hidden
542 541 revs_ids = action_params.split(',')
543 542 deleted = user_log.repository is None
544 543 if deleted:
545 544 return ','.join(revs_ids)
546 545
547 546 repo_name = user_log.repository.repo_name
548 547
549 548 def lnk(rev, repo_name):
550 549 lazy_cs = False
551 550 title_ = None
552 551 url_ = '#'
553 552 if isinstance(rev, BaseChangeset) or isinstance(rev, AttributeDict):
554 553 if rev.op and rev.ref_name:
555 554 if rev.op == 'delete_branch':
556 555 lbl = _('Deleted branch: %s') % rev.ref_name
557 556 elif rev.op == 'tag':
558 557 lbl = _('Created tag: %s') % rev.ref_name
559 558 else:
560 559 lbl = 'Unknown operation %s' % rev.op
561 560 else:
562 561 lazy_cs = True
563 562 lbl = rev.short_id[:8]
564 563 url_ = url('changeset_home', repo_name=repo_name,
565 564 revision=rev.raw_id)
566 565 else:
567 566 # changeset cannot be found - it might have been stripped or removed
568 567 lbl = rev[:12]
569 568 title_ = _('Changeset %s not found') % lbl
570 569 if parse_cs:
571 return link_to(lbl, url_, title=title_, class_='tooltip')
570 return link_to(lbl, url_, title=title_, **{'data-toggle': 'tooltip'})
572 571 return link_to(lbl, url_, class_='lazy-cs' if lazy_cs else '',
573 572 **{'data-raw_id':rev.raw_id, 'data-repo_name':repo_name})
574 573
575 574 def _get_op(rev_txt):
576 575 _op = None
577 576 _name = rev_txt
578 577 if len(rev_txt.split('=>')) == 2:
579 578 _op, _name = rev_txt.split('=>')
580 579 return _op, _name
581 580
582 581 revs = []
583 582 if len(filter(lambda v: v != '', revs_ids)) > 0:
584 583 repo = None
585 584 for rev in revs_ids[:revs_top_limit]:
586 585 _op, _name = _get_op(rev)
587 586
588 587 # we want parsed changesets, or new log store format is bad
589 588 if parse_cs:
590 589 try:
591 590 if repo is None:
592 591 repo = user_log.repository.scm_instance
593 592 _rev = repo.get_changeset(rev)
594 593 revs.append(_rev)
595 594 except ChangesetDoesNotExistError:
596 595 log.error('cannot find revision %s in this repo', rev)
597 596 revs.append(rev)
598 597 else:
599 598 _rev = AttributeDict({
600 599 'short_id': rev[:12],
601 600 'raw_id': rev,
602 601 'message': '',
603 602 'op': _op,
604 603 'ref_name': _name
605 604 })
606 605 revs.append(_rev)
607 606 cs_links = [" " + ', '.join(
608 607 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
609 608 )]
610 609 _op1, _name1 = _get_op(revs_ids[0])
611 610 _op2, _name2 = _get_op(revs_ids[-1])
612 611
613 612 _rev = '%s...%s' % (_name1, _name2)
614 613
615 614 compare_view = (
616 615 ' <div class="compare_view" data-toggle="tooltip" title="%s">'
617 616 '<a href="%s">%s</a> </div>' % (
618 617 _('Show all combined changesets %s->%s') % (
619 618 revs_ids[0][:12], revs_ids[-1][:12]
620 619 ),
621 620 url('changeset_home', repo_name=repo_name,
622 621 revision=_rev
623 622 ),
624 623 _('Compare view')
625 624 )
626 625 )
627 626
628 627 # if we have exactly one more than normally displayed
629 628 # just display it, takes less space than displaying
630 629 # "and 1 more revisions"
631 630 if len(revs_ids) == revs_limit + 1:
632 631 cs_links.append(", " + lnk(revs[revs_limit], repo_name))
633 632
634 633 # hidden-by-default ones
635 634 if len(revs_ids) > revs_limit + 1:
636 635 uniq_id = revs_ids[0]
637 636 html_tmpl = (
638 637 '<span> %s <a class="show_more" id="_%s" '
639 638 'href="#more">%s</a> %s</span>'
640 639 )
641 640 if not feed:
642 641 cs_links.append(html_tmpl % (
643 642 _('and'),
644 643 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
645 644 _('revisions')
646 645 )
647 646 )
648 647
649 648 if not feed:
650 649 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
651 650 else:
652 651 html_tmpl = '<span id="%s"> %s </span>'
653 652
654 653 morelinks = ', '.join(
655 654 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
656 655 )
657 656
658 657 if len(revs_ids) > revs_top_limit:
659 658 morelinks += ', ...'
660 659
661 660 cs_links.append(html_tmpl % (uniq_id, morelinks))
662 661 if len(revs) > 1:
663 662 cs_links.append(compare_view)
664 663 return ''.join(cs_links)
665 664
666 665 def get_fork_name():
667 666 repo_name = action_params
668 667 url_ = url('summary_home', repo_name=repo_name)
669 668 return _('Fork name %s') % link_to(action_params, url_)
670 669
671 670 def get_user_name():
672 671 user_name = action_params
673 672 return user_name
674 673
675 674 def get_users_group():
676 675 group_name = action_params
677 676 return group_name
678 677
679 678 def get_pull_request():
680 679 from kallithea.model.db import PullRequest
681 680 pull_request_id = action_params
682 681 nice_id = PullRequest.make_nice_id(pull_request_id)
683 682
684 683 deleted = user_log.repository is None
685 684 if deleted:
686 685 repo_name = user_log.repository_name
687 686 else:
688 687 repo_name = user_log.repository.repo_name
689 688
690 689 return link_to(_('Pull request %s') % nice_id,
691 690 url('pullrequest_show', repo_name=repo_name,
692 691 pull_request_id=pull_request_id))
693 692
694 693 def get_archive_name():
695 694 archive_name = action_params
696 695 return archive_name
697 696
698 697 # action : translated str, callback(extractor), icon
699 698 action_map = {
700 699 'user_deleted_repo': (_('[deleted] repository'),
701 700 None, 'icon-trashcan'),
702 701 'user_created_repo': (_('[created] repository'),
703 702 None, 'icon-plus'),
704 703 'user_created_fork': (_('[created] repository as fork'),
705 704 None, 'icon-fork'),
706 705 'user_forked_repo': (_('[forked] repository'),
707 706 get_fork_name, 'icon-fork'),
708 707 'user_updated_repo': (_('[updated] repository'),
709 708 None, 'icon-pencil'),
710 709 'user_downloaded_archive': (_('[downloaded] archive from repository'),
711 710 get_archive_name, 'icon-download-cloud'),
712 711 'admin_deleted_repo': (_('[delete] repository'),
713 712 None, 'icon-trashcan'),
714 713 'admin_created_repo': (_('[created] repository'),
715 714 None, 'icon-plus'),
716 715 'admin_forked_repo': (_('[forked] repository'),
717 716 None, 'icon-fork'),
718 717 'admin_updated_repo': (_('[updated] repository'),
719 718 None, 'icon-pencil'),
720 719 'admin_created_user': (_('[created] user'),
721 720 get_user_name, 'icon-user'),
722 721 'admin_updated_user': (_('[updated] user'),
723 722 get_user_name, 'icon-user'),
724 723 'admin_created_users_group': (_('[created] user group'),
725 724 get_users_group, 'icon-pencil'),
726 725 'admin_updated_users_group': (_('[updated] user group'),
727 726 get_users_group, 'icon-pencil'),
728 727 'user_commented_revision': (_('[commented] on revision in repository'),
729 728 get_cs_links, 'icon-comment'),
730 729 'user_commented_pull_request': (_('[commented] on pull request for'),
731 730 get_pull_request, 'icon-comment'),
732 731 'user_closed_pull_request': (_('[closed] pull request for'),
733 732 get_pull_request, 'icon-ok'),
734 733 'push': (_('[pushed] into'),
735 734 get_cs_links, 'icon-move-up'),
736 735 'push_local': (_('[committed via Kallithea] into repository'),
737 736 get_cs_links, 'icon-pencil'),
738 737 'push_remote': (_('[pulled from remote] into repository'),
739 738 get_cs_links, 'icon-move-up'),
740 739 'pull': (_('[pulled] from'),
741 740 None, 'icon-move-down'),
742 741 'started_following_repo': (_('[started following] repository'),
743 742 None, 'icon-heart'),
744 743 'stopped_following_repo': (_('[stopped following] repository'),
745 744 None, 'icon-heart-empty'),
746 745 }
747 746
748 747 action_str = action_map.get(action, action)
749 748 if feed:
750 749 action = action_str[0].replace('[', '').replace(']', '')
751 750 else:
752 751 action = action_str[0] \
753 752 .replace('[', '<b>') \
754 753 .replace(']', '</b>')
755 754
756 755 action_params_func = lambda: ""
757 756
758 757 if callable(action_str[1]):
759 758 action_params_func = action_str[1]
760 759
761 760 def action_parser_icon():
762 761 action = user_log.action
763 762 action_params = None
764 763 x = action.split(':')
765 764
766 765 if len(x) > 1:
767 766 action, action_params = x
768 767
769 768 ico = action_map.get(action, ['', '', ''])[2]
770 769 html = """<i class="%s"></i>""" % ico
771 770 return literal(html)
772 771
773 772 # returned callbacks we need to call to get
774 773 return [lambda: literal(action), action_params_func, action_parser_icon]
775 774
776 775
777 776
778 777 #==============================================================================
779 778 # PERMS
780 779 #==============================================================================
781 780 from kallithea.lib.auth import HasPermissionAny, \
782 781 HasRepoPermissionAny, HasRepoGroupPermissionAny
783 782
784 783
785 784 #==============================================================================
786 785 # GRAVATAR URL
787 786 #==============================================================================
788 787 def gravatar_div(email_address, cls='', size=30, **div_attributes):
789 788 """Return an html literal with a div around a gravatar if they are enabled.
790 789 Extra keyword parameters starting with 'div_' will get the prefix removed
791 790 and '_' changed to '-' and be used as attributes on the div. The default
792 791 class is 'gravatar'.
793 792 """
794 793 from pylons import tmpl_context as c
795 794 if not c.visual.use_gravatar:
796 795 return ''
797 796 if 'div_class' not in div_attributes:
798 797 div_attributes['div_class'] = "gravatar"
799 798 attributes = []
800 799 for k, v in sorted(div_attributes.items()):
801 800 assert k.startswith('div_'), k
802 801 attributes.append(' %s="%s"' % (k[4:].replace('_', '-'), escape(v)))
803 802 return literal("""<div%s>%s</div>""" %
804 803 (''.join(attributes),
805 804 gravatar(email_address, cls=cls, size=size)))
806 805
807 806 def gravatar(email_address, cls='', size=30):
808 807 """return html element of the gravatar
809 808
810 809 This method will return an <img> with the resolution double the size (for
811 810 retina screens) of the image. If the url returned from gravatar_url is
812 811 empty then we fallback to using an icon.
813 812
814 813 """
815 814 from pylons import tmpl_context as c
816 815 if not c.visual.use_gravatar:
817 816 return ''
818 817
819 818 src = gravatar_url(email_address, size * 2)
820 819
821 820 if src:
822 821 # here it makes sense to use style="width: ..." (instead of, say, a
823 822 # stylesheet) because we using this to generate a high-res (retina) size
824 823 html = ('<img alt="" class="{cls}" style="width: {size}px; height: {size}px" src="{src}"/>'
825 824 .format(cls=cls, size=size, src=src))
826 825
827 826 else:
828 827 # if src is empty then there was no gravatar, so we use a font icon
829 828 html = ("""<i class="icon-user {cls}" style="font-size: {size}px;"></i>"""
830 829 .format(cls=cls, size=size, src=src))
831 830
832 831 return literal(html)
833 832
834 833 def gravatar_url(email_address, size=30, default=''):
835 834 # doh, we need to re-import those to mock it later
836 835 from kallithea.config.routing import url
837 836 from kallithea.model.db import User
838 837 from pylons import tmpl_context as c
839 838 if not c.visual.use_gravatar:
840 839 return ""
841 840
842 841 _def = 'anonymous@kallithea-scm.org' # default gravatar
843 842 email_address = email_address or _def
844 843
845 844 if email_address == _def:
846 845 return default
847 846
848 847 parsed_url = urlparse.urlparse(url.current(qualified=True))
849 848 url = (c.visual.gravatar_url or User.DEFAULT_GRAVATAR_URL ) \
850 849 .replace('{email}', email_address) \
851 850 .replace('{md5email}', hashlib.md5(safe_str(email_address).lower()).hexdigest()) \
852 851 .replace('{netloc}', parsed_url.netloc) \
853 852 .replace('{scheme}', parsed_url.scheme) \
854 853 .replace('{size}', safe_str(size))
855 854 return url
856 855
857 856
858 857 def changed_tooltip(nodes):
859 858 """
860 859 Generates a html string for changed nodes in changeset page.
861 860 It limits the output to 30 entries
862 861
863 862 :param nodes: LazyNodesGenerator
864 863 """
865 864 if nodes:
866 865 pref = ': <br/> '
867 866 suf = ''
868 867 if len(nodes) > 30:
869 868 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
870 869 return literal(pref + '<br/> '.join([safe_unicode(x.path)
871 870 for x in nodes[:30]]) + suf)
872 871 else:
873 872 return ': ' + _('No files')
874 873
875 874
876 875 def fancy_file_stats(stats):
877 876 """
878 877 Displays a fancy two colored bar for number of added/deleted
879 878 lines of code on file
880 879
881 880 :param stats: two element list of added/deleted lines of code
882 881 """
883 882 from kallithea.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
884 883 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
885 884
886 885 a, d = stats['added'], stats['deleted']
887 886 width = 100
888 887
889 888 if stats['binary']:
890 889 #binary mode
891 890 lbl = ''
892 891 bin_op = 1
893 892
894 893 if BIN_FILENODE in stats['ops']:
895 894 lbl = 'bin+'
896 895
897 896 if NEW_FILENODE in stats['ops']:
898 897 lbl += _('new file')
899 898 bin_op = NEW_FILENODE
900 899 elif MOD_FILENODE in stats['ops']:
901 900 lbl += _('mod')
902 901 bin_op = MOD_FILENODE
903 902 elif DEL_FILENODE in stats['ops']:
904 903 lbl += _('del')
905 904 bin_op = DEL_FILENODE
906 905 elif RENAMED_FILENODE in stats['ops']:
907 906 lbl += _('rename')
908 907 bin_op = RENAMED_FILENODE
909 908
910 909 #chmod can go with other operations
911 910 if CHMOD_FILENODE in stats['ops']:
912 911 _org_lbl = _('chmod')
913 912 lbl += _org_lbl if lbl.endswith('+') else '+%s' % _org_lbl
914 913
915 914 #import ipdb;ipdb.set_trace()
916 915 b_d = '<div class="bin bin%s progress-bar" style="width:100%%">%s</div>' % (bin_op, lbl)
917 916 b_a = '<div class="bin bin1" style="width:0%"></div>'
918 917 return literal('<div style="width:%spx" class="progress">%s%s</div>' % (width, b_a, b_d))
919 918
920 919 t = stats['added'] + stats['deleted']
921 920 unit = float(width) / (t or 1)
922 921
923 922 # needs > 9% of width to be visible or 0 to be hidden
924 923 a_p = max(9, unit * a) if a > 0 else 0
925 924 d_p = max(9, unit * d) if d > 0 else 0
926 925 p_sum = a_p + d_p
927 926
928 927 if p_sum > width:
929 928 #adjust the percentage to be == 100% since we adjusted to 9
930 929 if a_p > d_p:
931 930 a_p = a_p - (p_sum - width)
932 931 else:
933 932 d_p = d_p - (p_sum - width)
934 933
935 934 a_v = a if a > 0 else ''
936 935 d_v = d if d > 0 else ''
937 936
938 937 d_a = '<div class="added progress-bar" style="width:%s%%">%s</div>' % (
939 938 a_p, a_v
940 939 )
941 940 d_d = '<div class="deleted progress-bar" style="width:%s%%">%s</div>' % (
942 941 d_p, d_v
943 942 )
944 943 return literal('<div class="pull-right progress" style="width:%spx">%s%s</div>' % (width, d_a, d_d))
945 944
946 945
947 946 _URLIFY_RE = re.compile(r'''
948 947 # URL markup
949 948 (?P<url>%s) |
950 949 # @mention markup
951 950 (?P<mention>%s) |
952 951 # Changeset hash markup
953 952 (?<!\w|[-_])
954 953 (?P<hash>[0-9a-f]{12,40})
955 954 (?!\w|[-_]) |
956 955 # Markup of *bold text*
957 956 (?:
958 957 (?:^|(?<=\s))
959 958 (?P<bold> [*] (?!\s) [^*\n]* (?<!\s) [*] )
960 959 (?![*\w])
961 960 ) |
962 961 # "Stylize" markup
963 962 \[see\ \=&gt;\ *(?P<seen>[a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\] |
964 963 \[license\ \=&gt;\ *(?P<license>[a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\] |
965 964 \[(?P<tagtype>requires|recommends|conflicts|base)\ \=&gt;\ *(?P<tagvalue>[a-zA-Z0-9\-\/]*)\] |
966 965 \[(?:lang|language)\ \=&gt;\ *(?P<lang>[a-zA-Z\-\/\#\+]*)\] |
967 966 \[(?P<tag>[a-z]+)\]
968 967 ''' % (url_re.pattern, MENTIONS_REGEX.pattern),
969 968 re.VERBOSE | re.MULTILINE | re.IGNORECASE)
970 969
971 970
972 971
973 972 def urlify_text(s, repo_name=None, link_=None, truncate=None, stylize=False, truncatef=truncate):
974 973 """
975 974 Parses given text message and make literal html with markup.
976 975 The text will be truncated to the specified length.
977 976 Hashes are turned into changeset links to specified repository.
978 977 URLs links to what they say.
979 978 Issues are linked to given issue-server.
980 979 If link_ is provided, all text not already linking somewhere will link there.
981 980 """
982 981
983 982 def _replace(match_obj):
984 983 url = match_obj.group('url')
985 984 if url is not None:
986 985 return '<a href="%(url)s">%(url)s</a>' % {'url': url}
987 986 mention = match_obj.group('mention')
988 987 if mention is not None:
989 988 return '<b>%s</b>' % mention
990 989 hash_ = match_obj.group('hash')
991 990 if hash_ is not None and repo_name is not None:
992 991 from kallithea.config.routing import url # doh, we need to re-import url to mock it later
993 992 return '<a class="revision-link" href="%(url)s">%(hash)s</a>' % {
994 993 'url': url('changeset_home', repo_name=repo_name, revision=hash_),
995 994 'hash': hash_,
996 995 }
997 996 bold = match_obj.group('bold')
998 997 if bold is not None:
999 998 return '<b>*%s*</b>' % _urlify(bold[1:-1])
1000 999 if stylize:
1001 1000 seen = match_obj.group('seen')
1002 1001 if seen:
1003 1002 return '<div class="metatag" data-tag="see">see =&gt; %s</div>' % seen
1004 1003 license = match_obj.group('license')
1005 1004 if license:
1006 1005 return '<div class="metatag" data-tag="license"><a href="http:\/\/www.opensource.org/licenses/%s">%s</a></div>' % (license, license)
1007 1006 tagtype = match_obj.group('tagtype')
1008 1007 if tagtype:
1009 1008 tagvalue = match_obj.group('tagvalue')
1010 1009 return '<div class="metatag" data-tag="%s">%s =&gt; <a href="/%s">%s</a></div>' % (tagtype, tagtype, tagvalue, tagvalue)
1011 1010 lang = match_obj.group('lang')
1012 1011 if lang:
1013 1012 return '<div class="metatag" data-tag="lang">%s</div>' % lang
1014 1013 tag = match_obj.group('tag')
1015 1014 if tag:
1016 1015 return '<div class="metatag" data-tag="%s">%s</div>' % (tag, tag)
1017 1016 return match_obj.group(0)
1018 1017
1019 1018 def _urlify(s):
1020 1019 """
1021 1020 Extract urls from text and make html links out of them
1022 1021 """
1023 1022 return _URLIFY_RE.sub(_replace, s)
1024 1023
1025 1024 if truncate is None:
1026 1025 s = s.rstrip()
1027 1026 else:
1028 1027 s = truncatef(s, truncate, whole_word=True)
1029 1028 s = html_escape(s)
1030 1029 s = _urlify(s)
1031 1030 if repo_name is not None:
1032 1031 s = urlify_issues(s, repo_name)
1033 1032 if link_ is not None:
1034 1033 # make href around everything that isn't a href already
1035 1034 s = linkify_others(s, link_)
1036 1035 s = s.replace('\r\n', '<br/>').replace('\n', '<br/>')
1037 1036 return literal(s)
1038 1037
1039 1038
1040 1039 def linkify_others(t, l):
1041 1040 """Add a default link to html with links.
1042 1041 HTML doesn't allow nesting of links, so the outer link must be broken up
1043 1042 in pieces and give space for other links.
1044 1043 """
1045 1044 urls = re.compile(r'(\<a.*?\<\/a\>)',)
1046 1045 links = []
1047 1046 for e in urls.split(t):
1048 1047 if e.strip() and not urls.match(e):
1049 1048 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
1050 1049 else:
1051 1050 links.append(e)
1052 1051
1053 1052 return ''.join(links)
1054 1053
1055 1054
1056 1055 # Global variable that will hold the actual urlify_issues function body.
1057 1056 # Will be set on first use when the global configuration has been read.
1058 1057 _urlify_issues_f = None
1059 1058
1060 1059
1061 1060 def urlify_issues(newtext, repo_name):
1062 1061 """Urlify issue references according to .ini configuration"""
1063 1062 global _urlify_issues_f
1064 1063 if _urlify_issues_f is None:
1065 1064 from kallithea import CONFIG
1066 1065 from kallithea.model.db import URL_SEP
1067 1066 assert CONFIG['sqlalchemy.url'] # make sure config has been loaded
1068 1067
1069 1068 # Build chain of urlify functions, starting with not doing any transformation
1070 1069 tmp_urlify_issues_f = lambda s: s
1071 1070
1072 1071 issue_pat_re = re.compile(r'issue_pat(.*)')
1073 1072 for k in CONFIG.keys():
1074 1073 # Find all issue_pat* settings that also have corresponding server_link and prefix configuration
1075 1074 m = issue_pat_re.match(k)
1076 1075 if m is None:
1077 1076 continue
1078 1077 suffix = m.group(1)
1079 1078 issue_pat = CONFIG.get(k)
1080 1079 issue_server_link = CONFIG.get('issue_server_link%s' % suffix)
1081 1080 issue_prefix = CONFIG.get('issue_prefix%s' % suffix)
1082 1081 if issue_pat and issue_server_link and issue_prefix:
1083 1082 log.debug('issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_prefix)
1084 1083 else:
1085 1084 log.error('skipping incomplete issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_prefix)
1086 1085 continue
1087 1086
1088 1087 # Wrap tmp_urlify_issues_f with substitution of this pattern, while making sure all loop variables (and compiled regexpes) are bound
1089 1088 issue_re = re.compile(issue_pat)
1090 1089 def issues_replace(match_obj,
1091 1090 issue_server_link=issue_server_link, issue_prefix=issue_prefix):
1092 1091 leadingspace = ' ' if match_obj.group().startswith(' ') else ''
1093 1092 issue_id = ''.join(match_obj.groups())
1094 1093 issue_url = issue_server_link.replace('{id}', issue_id)
1095 1094 issue_url = issue_url.replace('{repo}', repo_name)
1096 1095 issue_url = issue_url.replace('{repo_name}', repo_name.split(URL_SEP)[-1])
1097 1096 return (
1098 1097 '%(leadingspace)s<a class="issue-tracker-link" href="%(url)s">'
1099 1098 '%(issue-prefix)s%(id-repr)s'
1100 1099 '</a>'
1101 1100 ) % {
1102 1101 'leadingspace': leadingspace,
1103 1102 'url': issue_url,
1104 1103 'id-repr': issue_id,
1105 1104 'issue-prefix': issue_prefix,
1106 1105 'serv': issue_server_link,
1107 1106 }
1108 1107 tmp_urlify_issues_f = (lambda s,
1109 1108 issue_re=issue_re, issues_replace=issues_replace, chain_f=tmp_urlify_issues_f:
1110 1109 issue_re.sub(issues_replace, chain_f(s)))
1111 1110
1112 1111 # Set tmp function globally - atomically
1113 1112 _urlify_issues_f = tmp_urlify_issues_f
1114 1113
1115 1114 return _urlify_issues_f(newtext)
1116 1115
1117 1116
1118 1117 def render_w_mentions(source, repo_name=None):
1119 1118 """
1120 1119 Render plain text with revision hashes and issue references urlified
1121 1120 and with @mention highlighting.
1122 1121 """
1123 1122 s = safe_unicode(source)
1124 1123 s = urlify_text(s, repo_name=repo_name)
1125 1124 return literal('<div class="formatted-fixed">%s</div>' % s)
1126 1125
1127 1126
1128 1127 def short_ref(ref_type, ref_name):
1129 1128 if ref_type == 'rev':
1130 1129 return short_id(ref_name)
1131 1130 return ref_name
1132 1131
1133 1132 def link_to_ref(repo_name, ref_type, ref_name, rev=None):
1134 1133 """
1135 1134 Return full markup for a href to changeset_home for a changeset.
1136 1135 If ref_type is branch it will link to changelog.
1137 1136 ref_name is shortened if ref_type is 'rev'.
1138 1137 if rev is specified show it too, explicitly linking to that revision.
1139 1138 """
1140 1139 txt = short_ref(ref_type, ref_name)
1141 1140 if ref_type == 'branch':
1142 1141 u = url('changelog_home', repo_name=repo_name, branch=ref_name)
1143 1142 else:
1144 1143 u = url('changeset_home', repo_name=repo_name, revision=ref_name)
1145 1144 l = link_to(repo_name + '#' + txt, u)
1146 1145 if rev and ref_type != 'rev':
1147 1146 l = literal('%s (%s)' % (l, link_to(short_id(rev), url('changeset_home', repo_name=repo_name, revision=rev))))
1148 1147 return l
1149 1148
1150 1149 def changeset_status(repo, revision):
1151 1150 from kallithea.model.changeset_status import ChangesetStatusModel
1152 1151 return ChangesetStatusModel().get_status(repo, revision)
1153 1152
1154 1153
1155 1154 def changeset_status_lbl(changeset_status):
1156 1155 from kallithea.model.db import ChangesetStatus
1157 1156 return ChangesetStatus.get_status_lbl(changeset_status)
1158 1157
1159 1158
1160 1159 def get_permission_name(key):
1161 1160 from kallithea.model.db import Permission
1162 1161 return dict(Permission.PERMS).get(key)
1163 1162
1164 1163
1165 1164 def journal_filter_help():
1166 1165 return _(textwrap.dedent('''
1167 1166 Example filter terms:
1168 1167 repository:vcs
1169 1168 username:developer
1170 1169 action:*push*
1171 1170 ip:127.0.0.1
1172 1171 date:20120101
1173 1172 date:[20120101100000 TO 20120102]
1174 1173
1175 1174 Generate wildcards using '*' character:
1176 1175 "repository:vcs*" - search everything starting with 'vcs'
1177 1176 "repository:*vcs*" - search for repository containing 'vcs'
1178 1177
1179 1178 Optional AND / OR operators in queries
1180 1179 "repository:vcs OR repository:test"
1181 1180 "username:test AND repository:test*"
1182 1181 '''))
1183 1182
1184 1183
1185 1184 def not_mapped_error(repo_name):
1186 1185 flash(_('%s repository is not mapped to db perhaps'
1187 1186 ' it was created or renamed from the filesystem'
1188 1187 ' please run the application again'
1189 1188 ' in order to rescan repositories') % repo_name, category='error')
1190 1189
1191 1190
1192 1191 def ip_range(ip_addr):
1193 1192 from kallithea.model.db import UserIpMap
1194 1193 s, e = UserIpMap._get_ip_range(ip_addr)
1195 1194 return '%s - %s' % (s, e)
1196 1195
1197 1196
1198 1197 def form(url, method="post", **attrs):
1199 1198 """Like webhelpers.html.tags.form but automatically using secure_form with
1200 1199 authentication_token for POST. authentication_token is thus never leaked
1201 1200 in the URL."""
1202 1201 if method.lower() == 'get':
1203 1202 return insecure_form(url, method=method, **attrs)
1204 1203 # webhelpers will turn everything but GET into POST
1205 1204 return secure_form(url, method=method, **attrs)
@@ -1,4436 +1,4546 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, Lucida Sans Unicode, Arial, sans-serif;
16 16 color: #000;
17 17 margin: 0;
18 18 padding: 0;
19 19 font-size: 12px;
20 20 }
21 21
22 22 ol, ul {
23 23 list-style: none;
24 24 }
25 25
26 26 blockquote, q {
27 27 quotes: none;
28 28 }
29 29
30 30 blockquote:before, blockquote:after, q:before, q:after {
31 31 content: none;
32 32 }
33 33
34 34 :focus {
35 35 outline: 0;
36 36 }
37 37
38 38 del {
39 39 text-decoration: line-through;
40 40 }
41 41
42 42 table {
43 43 border-collapse: collapse;
44 44 border-spacing: 0;
45 45 }
46 46
47 47 html {
48 48 height: 100%;
49 49 }
50 50
51 51 a {
52 52 color: #577632;
53 53 text-decoration: none;
54 54 cursor: pointer;
55 55 }
56 56
57 57 a:hover {
58 58 color: #576622;
59 59 text-decoration: underline;
60 60 }
61 61
62 62 h1, h2, h3, h4, h5, h6,
63 63 div.h1, div.h2, div.h3, div.h4, div.h5, div.h6 {
64 64 color: #292929;
65 65 font-weight: 700;
66 66 }
67 67
68 68 h1, div.h1 {
69 69 font-size: 22px;
70 70 }
71 71
72 72 h2, div.h2 {
73 73 font-size: 20px;
74 74 }
75 75
76 76 h3, div.h3 {
77 77 font-size: 18px;
78 78 }
79 79
80 80 h4, div.h4 {
81 81 font-size: 16px;
82 82 }
83 83
84 84 h5, div.h5 {
85 85 font-size: 14px;
86 86 }
87 87
88 88 h6, div.h6 {
89 89 font-size: 11px;
90 90 }
91 91
92 92 ul.circle {
93 93 list-style-type: circle;
94 94 }
95 95
96 96 ul.disc {
97 97 list-style-type: disc;
98 98 }
99 99
100 100 ul.square {
101 101 list-style-type: square;
102 102 }
103 103
104 104 ol.lower-roman {
105 105 list-style-type: lower-roman;
106 106 }
107 107
108 108 ol.upper-roman {
109 109 list-style-type: upper-roman;
110 110 }
111 111
112 112 ol.lower-alpha {
113 113 list-style-type: lower-alpha;
114 114 }
115 115
116 116 ol.upper-alpha {
117 117 list-style-type: upper-alpha;
118 118 }
119 119
120 120 ol.decimal {
121 121 list-style-type: decimal;
122 122 }
123 123
124 124 div.color {
125 125 clear: both;
126 126 overflow: hidden;
127 127 position: absolute;
128 128 background: #FFF;
129 129 margin: 7px 0 0 60px;
130 130 padding: 1px 1px 1px 0;
131 131 }
132 132
133 133 div.color a {
134 134 width: 15px;
135 135 height: 15px;
136 136 display: block;
137 137 float: left;
138 138 margin: 0 0 0 1px;
139 139 padding: 0;
140 140 }
141 141
142 142 div.options {
143 143 clear: both;
144 144 overflow: hidden;
145 145 position: absolute;
146 146 background: #FFF;
147 147 margin: 7px 0 0 162px;
148 148 padding: 0;
149 149 }
150 150
151 151 div.options a {
152 152 height: 1%;
153 153 display: block;
154 154 text-decoration: none;
155 155 margin: 0;
156 156 padding: 3px 8px;
157 157 }
158 158
159 159 code,
160 160 .code pre,
161 161 div.readme .readme_box pre,
162 162 div.rst-block pre,
163 163 div.formatted-fixed,
164 164 #changeset_content div.message,
165 165 .CodeMirror .CodeMirror-code pre {
166 166 font-size: 12px;
167 167 font-family: Lucida Console, Consolas, Monaco, Inconsolata, Liberation Mono, monospace;
168 168 }
169 169
170 170 div.formatted-fixed {
171 171 white-space: pre-wrap;
172 172 }
173 173
174 174 .changeset_hash {
175 175 font-family: Lucida Console, Consolas, Monaco, Inconsolata, Liberation Mono, monospace;
176 176 }
177 177
178 178 .top-left-rounded-corner {
179 179 border-top-left-radius: 8px;
180 180 }
181 181
182 182 .top-right-rounded-corner {
183 183 border-top-right-radius: 8px;
184 184 }
185 185
186 186 .bottom-left-rounded-corner {
187 187 border-bottom-left-radius: 8px;
188 188 }
189 189
190 190 .bottom-right-rounded-corner {
191 191 border-bottom-right-radius: 8px;
192 192 }
193 193
194 194 .top-left-rounded-corner-mid {
195 195 border-top-left-radius: 4px;
196 196 }
197 197
198 198 .top-right-rounded-corner-mid {
199 199 border-top-right-radius: 4px;
200 200 }
201 201
202 202 .bottom-left-rounded-corner-mid {
203 203 border-bottom-left-radius: 4px;
204 204 }
205 205
206 206 .bottom-right-rounded-corner-mid {
207 207 border-bottom-right-radius: 4px;
208 208 }
209 209
210 210 .help-block {
211 211 color: #999999;
212 212 display: block;
213 213 margin: 0 0 10px;
214 214 }
215 215
216 216 .empty_data {
217 217 color: #B9B9B9;
218 218 }
219 219
220 220 .inline-comments-general.show-general-status .hidden.general-only {
221 221 display: block !important;
222 222 }
223 223
224 224 /* Bootstrap compatible */
225 225 .show {
226 226 display: block !important;
227 227 }
228 228 .hidden {
229 229 display: none !important;
230 230 }
231 231 .invisible {
232 232 visibility: hidden;
233 233 }
234 234
235 235 .truncate {
236 236 white-space: nowrap;
237 237 overflow: hidden;
238 238 text-overflow: ellipsis;
239 239 -o-text-overflow: ellipsis;
240 240 -ms-text-overflow: ellipsis;
241 241 }
242 242
243 243 .truncate.autoexpand:hover {
244 244 overflow: visible;
245 245 }
246 246
247 247 a.permalink {
248 248 visibility: hidden;
249 249 position: absolute;
250 250 margin: 3px 4px;
251 251 }
252 252
253 253 a.permalink:hover {
254 254 text-decoration: none;
255 255 }
256 256
257 257 h1:hover > a.permalink,
258 258 h2:hover > a.permalink,
259 259 h3:hover > a.permalink,
260 260 h4:hover > a.permalink,
261 261 h5:hover > a.permalink,
262 262 h6:hover > a.permalink,
263 263 div:hover > a.permalink,
264 264 div:hover > span > a.permalink {
265 265 visibility: visible;
266 266 }
267 267
268 268 nav.navbar #logo {
269 269 padding-left: 10px;
270 270 }
271 271
272 272 #content nav.navbar #logo {
273 273 padding-left: inherit;
274 274 }
275 275
276 276 nav.navbar #logo .navbar-brand img {
277 277 padding-top: 5px;
278 278 margin-right: 5px;
279 279 }
280 280
281 281 nav.navbar #logo .navbar-brand {
282 282 font-size: 20px;
283 283 color: white;
284 284 float: left;
285 285 height: 44px;
286 286 line-height: 44px;
287 287 }
288 288
289 289 #content nav.navbar #logo .navbar-brand {
290 290 height: inherit;
291 291 line-height: inherit;
292 292 }
293 293
294 294 nav.navbar ul#logged-user {
295 295 margin-bottom: 5px !important;
296 296 border-radius: 0px 0px 8px 8px;
297 297 height: 37px;
298 298 background-color: #577632;
299 299 background-repeat: repeat-x;
300 300 background-image: linear-gradient(to bottom, #577632, #577632);
301 301 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
302 302 }
303 303
304 304 nav.navbar ul#logged-user li {
305 305 list-style: none;
306 306 float: left;
307 307 margin: 8px 0 0;
308 308 padding: 4px 12px;
309 309 border-left: 1px solid #576622;
310 310 }
311 311
312 312 nav.navbar ul#logged-user li.first {
313 313 border-left: none;
314 314 margin: 4px;
315 315 }
316 316
317 317 nav.navbar ul#logged-user li.first div.gravatar {
318 318 margin-top: -2px;
319 319 }
320 320
321 321 nav.navbar ul#logged-user li.first div.account {
322 322 padding-top: 4px;
323 323 float: left;
324 324 }
325 325
326 326 nav.navbar ul#logged-user li.last {
327 327 border-right: none;
328 328 }
329 329
330 330 nav.navbar ul#logged-user li a {
331 331 color: #fff;
332 332 font-weight: 700;
333 333 text-decoration: none;
334 334 }
335 335
336 336 nav.navbar ul#logged-user li a:hover {
337 337 text-decoration: underline;
338 338 }
339 339
340 340 nav.navbar ul#logged-user li.highlight a {
341 341 color: #fff;
342 342 }
343 343
344 344 nav.navbar ul#logged-user li.highlight a:hover {
345 345 color: #FFF;
346 346 }
347 347 nav.navbar {
348 348 min-height: 44px;
349 349 clear: both;
350 350 position: relative;
351 351 background-color: #577632;
352 352 background-repeat: repeat-x;
353 353 background-image: linear-gradient(to bottom, #577632, #577632);
354 354 margin: 0;
355 355 padding: 0;
356 356 display: block;
357 357 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
358 358 border-radius: 0px 0px 4px 4px;
359 359 }
360 360
361 361 .header-pos-fix,
362 362 .anchor {
363 363 margin-top: -46px;
364 364 padding-top: 46px;
365 365 }
366 366
367 367 nav.navbar #home a {
368 368 height: 40px;
369 369 width: 46px;
370 370 display: block;
371 371 background-position: 0 0;
372 372 margin: 0;
373 373 padding: 0;
374 374 }
375 375
376 376 nav.navbar #home a:hover {
377 377 background-position: 0 -40px;
378 378 }
379 379
380 380 nav.navbar #logo {
381 381 float: left;
382 382 position: absolute;
383 383 }
384 384
385 385 nav.navbar #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 nav.navbar #logo a {
393 393 color: #fff;
394 394 text-decoration: none;
395 395 }
396 396
397 397 nav.navbar #logo a:hover {
398 398 color: #bfe3ff;
399 399 }
400 400
401 401 nav.navbar #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 nav.navbar #quick li span.short {
412 412 padding: 9px 6px 8px 6px;
413 413 }
414 414
415 415 nav.navbar #quick li span {
416 416 display: inline;
417 417 margin: 0;
418 418 }
419 419
420 420 nav.navbar #quick li span.normal {
421 421 border: none;
422 422 padding: 10px 12px 8px;
423 423 }
424 424
425 425 nav.navbar #quick li .icon {
426 426 border-left: none;
427 427 padding-left: 10px;
428 428 display: inline;
429 429 }
430 430
431 431 nav.navbar #quick li .icon img {
432 432 vertical-align: middle;
433 433 margin-bottom: 2px;
434 434 }
435 435
436 436 nav.navbar #quick ul.repo_switcher {
437 437 max-height: 275px;
438 438 overflow-x: hidden;
439 439 overflow-y: auto;
440 440 }
441 441
442 442 nav.navbar #quick ul.repo_switcher li.qfilter_rs {
443 443 padding: 2px 3px;
444 444 padding-right: 17px;
445 445 }
446 446
447 447 nav.navbar #quick ul.repo_switcher li.qfilter_rs input {
448 448 width: 100%;
449 449 border-radius: 10px;
450 450 padding: 2px 7px;
451 451 }
452 452
453 453 nav.navbar #quick .repo_switcher_type {
454 454 position: absolute;
455 455 left: 0;
456 456 top: 9px;
457 457 margin: 0px 2px 0px 2px;
458 458 }
459 459
460 460 .navbar-toggle {
461 461 display: none;
462 462 }
463 463
464 464 .groups_breadcrumbs a {
465 465 color: #fff;
466 466 }
467 467
468 468 .groups_breadcrumbs a:hover {
469 469 color: #bfe3ff;
470 470 text-decoration: none;
471 471 }
472 472
473 473 .dt_repo {
474 474 white-space: nowrap;
475 475 color: #577632;
476 476 }
477 477
478 478 .dt_repo_pending {
479 479 opacity: 0.5;
480 480 }
481 481
482 482 .dt_repo i.icon-keyhole-circled,
483 483 .dt_repo i.icon-globe
484 484 {
485 485 font-size: 16px;
486 486 vertical-align: -2px;
487 487 margin: 0px 1px 0px 3px;
488 488 }
489 489
490 490 .dt_repo a {
491 491 text-decoration: none;
492 492 }
493 493
494 494 .dt_repo .dt_repo_name:hover {
495 495 text-decoration: underline;
496 496 }
497 497
498 498 #content #left {
499 499 left: 0;
500 500 width: 280px;
501 501 position: absolute;
502 502 }
503 503
504 504 #content #right {
505 505 margin: 0 60px 10px 290px;
506 506 }
507 507
508 508 #content nav.navbar,
509 509 div.panel {
510 510 clear: both;
511 511 background: #fff;
512 512 margin: 0 0 10px;
513 513 padding: 0 0 10px;
514 514 border-radius: 4px 4px 4px 4px;
515 515 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
516 516 }
517 517
518 518 div.panel div.panel-heading {
519 519 clear: both;
520 520 overflow: hidden;
521 521 background-color: #577632;
522 522 background-repeat: repeat-x;
523 523 background-image: linear-gradient(to bottom, #577632, #577632);
524 524 margin: 0 0 20px;
525 525 padding: 10px 20px;
526 526 border-radius: 4px 4px 0 0;
527 527 }
528 528
529 529 #content div.panel div.panel-heading a {
530 530 color: #FFFFFF;
531 531 }
532 532
533 533 #content div.panel div.panel-heading ul.links li {
534 534 list-style: none;
535 535 float: left;
536 536 margin: 0;
537 537 padding: 0;
538 538 }
539 539
540 540 #content div.panel div.panel-heading ul.links li a {
541 541 font-size: 13px;
542 542 font-weight: 700;
543 543 height: 1%;
544 544 text-decoration: none;
545 545 }
546 546
547 547 #content div.panel div.panel-heading ul.links.nav-tabs li a {
548 548 float: left;
549 549 color: #fff;
550 550 margin: 0;
551 551 padding: 11px 10px 11px 10px;
552 552 }
553 553
554 554 .clearfix::before, .clearfix::after, .dl-horizontal dd::before, .dl-horizontal dd::after, .container::before, .container::after, .container-fluid::before, .container-fluid::after, .row::before, .row::after, .form-horizontal .form-group::before, .form-horizontal .form-group::after, .btn-toolbar::before, .btn-toolbar::after, .btn-group-vertical > .btn-group::before, .btn-group-vertical > .btn-group::after, .nav::before, .nav::after, .navbar::before, .navbar::after, .navbar-header::before, .navbar-header::after, .navbar-collapse::before, .navbar-collapse::after, .pager::before, .pager::after, .panel::before, .panel::after, .panel-body::before, .panel-body::after, .modal-header::before, .modal-header::after, .modal-footer::before, .modal-footer::after, td.inline-comments::before, td.inline-comments::after {
555 555 content: " ";
556 556 display: table;
557 557 }
558 558
559 559 .clearfix::after, .dl-horizontal dd::after, .container::after, .container-fluid::after, .row::after, .form-horizontal .form-group::after, .btn-toolbar::after, .btn-group-vertical > .btn-group::after, .nav::after, .navbar::after, .navbar-header::after, .navbar-collapse::after, .pager::after, .panel::after, .panel-body::after, .modal-header::after, .modal-footer::after, td.inline-comments::after {
560 560 clear: both;
561 561 }
562 562
563 563 /* avoid conflict with .container in changeset tables */
564 564 #content div.panel table .container::before,
565 565 #content div.panel table .container::after {
566 566 content: inherit;
567 567 display: inherit;
568 568 }
569 569
570 570 .pull-left {
571 571 float: left;
572 572 }
573 573
574 574 .pull-right {
575 575 float: right;
576 576 }
577 577
578 578 #content div.panel div.panel-heading .pull-left,
579 579 #content div.panel div.panel-heading .pull-left a,
580 580 #content div.panel div.panel-heading .pull-right,
581 581 #content div.panel div.panel-heading .pull-right a {
582 582 color: white;
583 583 }
584 584
585 585 #content div.panel h1,
586 586 #content div.panel h2,
587 587 #content div.panel h3,
588 588 #content div.panel h4,
589 589 #content div.panel h5,
590 590 #content div.panel h6,
591 591 #content div.panel div.h1,
592 592 #content div.panel div.h2,
593 593 #content div.panel div.h3,
594 594 #content div.panel div.h4,
595 595 #content div.panel div.h5,
596 596 #content div.panel div.h6 {
597 597 clear: both;
598 598 overflow: hidden;
599 599 margin: 8px 0 3px;
600 600 padding-bottom: 2px;
601 601 }
602 602
603 603 #content div.panel p {
604 604 color: #5f5f5f;
605 605 font-size: 12px;
606 606 line-height: 150%;
607 607 margin: 0 24px 10px;
608 608 padding: 0;
609 609 }
610 610
611 611 #content div.panel blockquote {
612 612 border-left: 4px solid #DDD;
613 613 color: #5f5f5f;
614 614 font-size: 11px;
615 615 line-height: 150%;
616 616 margin: 0 34px;
617 617 padding: 0 0 0 14px;
618 618 }
619 619
620 620 #content div.panel blockquote p {
621 621 margin: 10px 0;
622 622 padding: 0;
623 623 }
624 624
625 625 #content div.panel dl {
626 626 margin: 10px 0px;
627 627 }
628 628
629 629 #content div.panel dt {
630 630 font-size: 12px;
631 631 margin: 0;
632 632 }
633 633
634 634 #content div.panel dd {
635 635 font-size: 12px;
636 636 margin: 0;
637 637 padding: 8px 0 8px 15px;
638 638 }
639 639
640 640 #content div.panel li {
641 641 font-size: 12px;
642 642 padding: 4px 0;
643 643 }
644 644
645 645 #content div.panel ul.disc,
646 646 #content div.panel ul.circle {
647 647 margin: 10px 24px 10px 38px;
648 648 }
649 649
650 650 #content div.panel ul.square {
651 651 margin: 10px 24px 10px 40px;
652 652 }
653 653
654 654 #content div.panel img.left {
655 655 border: none;
656 656 float: left;
657 657 margin: 10px 10px 10px 0;
658 658 }
659 659
660 660 #content div.panel img.right {
661 661 border: none;
662 662 float: right;
663 663 margin: 10px 0 10px 10px;
664 664 }
665 665
666 666 #content div.panel div.messages {
667 667 clear: both;
668 668 overflow: hidden;
669 669 margin: 0 20px;
670 670 padding: 0;
671 671 }
672 672
673 673 #content div.panel div.message {
674 674 float: left;
675 675 overflow: hidden;
676 676 margin: 0;
677 677 padding: 5px 0;
678 678 white-space: pre-wrap;
679 679 }
680 680 #content div.panel #changeset_content div.message {
681 681 padding: 15px 0;
682 682 }
683 683 #content div.panel div.expand {
684 684 width: 110%;
685 685 height: 14px;
686 686 font-size: 10px;
687 687 text-align: center;
688 688 cursor: pointer;
689 689 color: #666;
690 690 background: linear-gradient(to bottom,rgba(255,255,255,0),rgba(64,96,128,0.1));
691 691 display: none;
692 692 overflow: hidden;
693 693 }
694 694 #content div.panel div.expand .expandtext {
695 695 background-color: #ffffff;
696 696 padding: 2px;
697 697 border-radius: 2px;
698 698 }
699 699
700 700 #content div.panel div.message a {
701 701 font-weight: 400 !important;
702 702 }
703 703
704 704 #content div.panel div.message div.image {
705 705 float: left;
706 706 margin: 9px 0 0 5px;
707 707 padding: 6px;
708 708 }
709 709
710 710 #content div.panel div.message div.image img {
711 711 vertical-align: middle;
712 712 margin: 0;
713 713 }
714 714
715 715 #content div.panel div.message div.text {
716 716 float: left;
717 717 margin: 0;
718 718 padding: 9px 6px;
719 719 }
720 720
721 721 #content div.panel div.message div.text h1,
722 722 #content div.panel div.message div.text h2,
723 723 #content div.panel div.message div.text h3,
724 724 #content div.panel div.message div.text h4,
725 725 #content div.panel div.message div.text h5,
726 726 #content div.panel div.message div.text h6 {
727 727 border: none;
728 728 margin: 0;
729 729 padding: 0;
730 730 }
731 731
732 732 #content div.panel div.message div.text span {
733 733 height: 1%;
734 734 display: block;
735 735 margin: 0;
736 736 padding: 5px 0 0;
737 737 }
738 738
739 739 #content div.panel div.message-error {
740 740 height: 1%;
741 741 clear: both;
742 742 overflow: hidden;
743 743 background: #FBE3E4;
744 744 border: 1px solid #FBC2C4;
745 745 color: #860006;
746 746 }
747 747
748 748 #content div.panel div.message-error h6 {
749 749 color: #860006;
750 750 }
751 751
752 752 #content div.panel div.message-warning {
753 753 height: 1%;
754 754 clear: both;
755 755 overflow: hidden;
756 756 background: #FFF6BF;
757 757 border: 1px solid #FFD324;
758 758 color: #5f5200;
759 759 }
760 760
761 761 #content div.panel div.message-warning h6 {
762 762 color: #5f5200;
763 763 }
764 764
765 765 #content div.panel div.message-notice {
766 766 height: 1%;
767 767 clear: both;
768 768 overflow: hidden;
769 769 background: #8FBDE0;
770 770 border: 1px solid #6BACDE;
771 771 color: #003863;
772 772 }
773 773
774 774 #content div.panel div.message-notice h6 {
775 775 color: #003863;
776 776 }
777 777
778 778 #content div.panel div.message-success {
779 779 height: 1%;
780 780 clear: both;
781 781 overflow: hidden;
782 782 background: #E6EFC2;
783 783 border: 1px solid #C6D880;
784 784 color: #4e6100;
785 785 }
786 786
787 787 #content div.panel div.message-success h6 {
788 788 color: #4e6100;
789 789 }
790 790
791 791 #content div.panel div.form div.form-horizontal div.form-group {
792 792 height: 1%;
793 793 min-height: 12px;
794 794 border-bottom: 1px solid #DDD;
795 795 clear: both;
796 796 padding: 0;
797 797 margin: 10px 0;
798 798 }
799 799
800 800 #content div.panel div.form div.form-horizontal div.form-group-first {
801 801 padding: 0 0 10px;
802 802 }
803 803
804 804 #content div.panel div.form div.form-horizontal div.form-group-noborder {
805 805 border-bottom: 0 !important;
806 806 }
807 807
808 808 #content div.panel div.form div.form-horizontal div.form-group span.error-message {
809 809 height: 1%;
810 810 display: inline-block;
811 811 color: red;
812 812 margin: 8px 0 0 4px;
813 813 padding: 0;
814 814 }
815 815
816 816 #content div.panel div.form div.form-horizontal div.form-group span.success {
817 817 height: 1%;
818 818 display: block;
819 819 color: #316309;
820 820 margin: 8px 0 0;
821 821 padding: 0;
822 822 }
823 823
824 824 #content div.panel div.form div.form-horizontal div.form-group > label {
825 825 margin: 0;
826 826 }
827 827
828 828 #content div.panel div.form div.form-horizontal div.form-group > label {
829 829 width: 155px;
830 830 position: absolute;
831 831 margin: 0;
832 832 padding: 0px 0 0 0px;
833 833 }
834 834
835 835 #content div.panel div.form div.form-horizontal div.form-group > label {
836 836 color: #393939;
837 837 font-weight: 700;
838 838 }
839 839
840 840 #content div.panel div.form div.form-horizontal div.form-group > div {
841 841 margin: 0 20px 0 200px;
842 842 }
843 843
844 844 #content div.panel div.form div.form-horizontal div.form-group > div.summary,
845 845 #content div.panel div.form div.form-horizontal div.form-group > div.summary-short {
846 846 margin: 10px 20px 10px 110px;
847 847 }
848 848 #content div.panel div.form div.form-horizontal div.form-group > div.summary-short input {
849 849 margin: 0;
850 850 }
851 851 #content div.panel div.form div.form-horizontal div.form-group > div.file {
852 852 margin: 0 20px 0 200px;
853 853 }
854 854
855 855 #content div.panel div.form div.form-horizontal div.form-group > label > input[type=text],
856 856 #content div.panel div.form div.form-horizontal div.form-group > label > input[type=password],
857 857 #content div.panel div.form div.form-horizontal div.form-group > div input[type=text],
858 858 #content div.panel div.form div.form-horizontal div.form-group > div input[type=password],
859 859 #content div.panel div.form div.form-horizontal div.form-group > label > textarea,
860 860 #content div.panel div.form div.form-horizontal div.form-group > div textarea,
861 861 .reviewer_ac input {
862 862 background: #FFF;
863 863 border-top: 1px solid #b3b3b3;
864 864 border-left: 1px solid #b3b3b3;
865 865 border-right: 1px solid #eaeaea;
866 866 border-bottom: 1px solid #eaeaea;
867 867 color: #000;
868 868 font-size: 12px;
869 869 margin: 5px 0 10px;
870 870 padding: 7px 7px 6px;
871 871 }
872 872
873 873 #content div.panel div.form div.form-horizontal div.form-group > div input#clone_url,
874 874 #content div.panel div.form div.form-horizontal div.form-group > div input#clone_url_id
875 875 {
876 876 font-size: 14px;
877 877 padding: 0 2px;
878 878 }
879 879
880 880 #content div.panel div.form div.form-horizontal div.form-group div.file input {
881 881 background: none repeat scroll 0 0 #FFFFFF;
882 882 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
883 883 border-style: solid;
884 884 border-width: 1px;
885 885 color: #000000;
886 886 font-size: 12px;
887 887 margin: 0;
888 888 padding: 7px 7px 6px;
889 889 }
890 890
891 891 input[readonly],
892 892 input.disabled {
893 893 background-color: #F5F5F5 !important;
894 894 }
895 895
896 896 #content div.panel div.form div.form-horizontal div.form-group > div.form-inline,
897 897 #content div.panel div.form div.form-horizontal div.form-group > div.form-inline,
898 898 #content div.panel div.form div.form-horizontal div.form-group > div.form-inline {
899 899 display: inline;
900 900 }
901 901
902 902 #content div.panel div.form div.form-horizontal div.form-group > div select.form-control,
903 903 #content div.panel div.form div.form-horizontal div.form-group > div div.form-control.select2-container,
904 904 #content div.panel div.form div.form-horizontal div.form-group > div input.form-control {
905 905 width: 100%;
906 906 margin: 5px 0 10px;
907 907 }
908 908
909 909 #content div.panel div.form div.form-horizontal div.form-group > div.form-inline select.form-control,
910 910 #content div.panel div.form div.form-horizontal div.form-group > div.form-inline div.form-control.select2-container,
911 911 #content div.panel div.form div.form-horizontal div.form-group > div.form-inline input.form-control {
912 912 width: inherit;
913 913 }
914 914
915 915 #content div.panel div.form div.form-horizontal div.form-group > div input.date {
916 916 width: 177px;
917 917 }
918 918
919 919 #content div.panel div.form div.form-horizontal div.form-group > div input.button {
920 920 background: #D4D0C8;
921 921 border-top: 1px solid #FFF;
922 922 border-left: 1px solid #FFF;
923 923 border-right: 1px solid #404040;
924 924 border-bottom: 1px solid #404040;
925 925 color: #000;
926 926 margin: 0;
927 927 padding: 4px 8px;
928 928 }
929 929
930 930 #content div.panel div.form div.form-horizontal div.form-group > div textarea {
931 931 width: 100%;
932 932 height: 220px;
933 933 overflow-y: auto;
934 934 outline: none;
935 935 }
936 936
937 937 #content div.panel div.form div.form-horizontal div.form-group input[type=text]:focus,
938 938 #content div.panel div.form div.form-horizontal div.form-group input[type=password]:focus,
939 939 #content div.panel div.form div.form-horizontal div.form-group input[type=file]:focus,
940 940 #content div.panel div.form div.form-horizontal div.form-group textarea:focus,
941 941 #content div.panel div.form div.form-horizontal div.form-group select:focus,
942 942 .reviewer_ac input:focus {
943 943 background: #f6f6f6;
944 944 border-color: #666;
945 945 }
946 946
947 947 .reviewer_ac {
948 948 padding: 10px
949 949 }
950 950
951 951 div.form div.form-horizontal div.form-group div.button {
952 952 margin: 0;
953 953 padding: 0 0 0 8px;
954 954 }
955 955
956 956 #content div.panel table.table {
957 957 border: 1px solid transparent;
958 958 }
959 959
960 960 #content div.panel table {
961 961 width: 100%;
962 962 border-collapse: separate;
963 963 margin: 0;
964 964 padding: 0;
965 965 border: 1px solid #eee;
966 966 border-radius: 4px;
967 967 }
968 968
969 969 #content div.panel table th {
970 970 background: #eee;
971 971 border-bottom: 1px solid #ddd;
972 972 padding: 5px 0px 5px 5px;
973 973 text-align: left;
974 974 }
975 975
976 976 #content div.panel table th.left {
977 977 text-align: left;
978 978 }
979 979
980 980 #content div.panel table th.right {
981 981 text-align: right;
982 982 }
983 983
984 984 #content div.panel table th.center {
985 985 text-align: center;
986 986 }
987 987
988 988 #content div.panel table th.selected {
989 989 vertical-align: middle;
990 990 padding: 0;
991 991 }
992 992
993 993 #content div.panel table td {
994 994 background: #fff;
995 995 border-bottom: 1px solid #cdcdcd;
996 996 vertical-align: middle;
997 997 padding: 5px;
998 998 }
999 999
1000 1000 #content div.panel table td.compact {
1001 1001 padding: 0;
1002 1002 }
1003 1003
1004 1004 #content div.panel table tr.selected td {
1005 1005 background: #FFC;
1006 1006 }
1007 1007
1008 1008 #content div.panel table td.selected {
1009 1009 width: 3%;
1010 1010 text-align: center;
1011 1011 vertical-align: middle;
1012 1012 padding: 0;
1013 1013 }
1014 1014
1015 1015 #content div.panel table td.action {
1016 1016 width: 45%;
1017 1017 text-align: left;
1018 1018 }
1019 1019
1020 1020 #content div.panel table td.date {
1021 1021 width: 33%;
1022 1022 text-align: center;
1023 1023 }
1024 1024
1025 1025 #content div.panel div.action {
1026 1026 float: right;
1027 1027 background: #FFF;
1028 1028 text-align: right;
1029 1029 margin: 10px 0 0;
1030 1030 padding: 0;
1031 1031 }
1032 1032
1033 1033 #content div.panel div.action select {
1034 1034 font-size: 11px;
1035 1035 margin: 0;
1036 1036 }
1037 1037
1038 1038 #content div.panel div.action .ui-selectmenu {
1039 1039 margin: 0;
1040 1040 padding: 0;
1041 1041 }
1042 1042
1043 1043 #content div.panel ul.pagination {
1044 1044 height: 1%;
1045 1045 overflow: hidden;
1046 1046 text-align: right;
1047 1047 margin: 10px 0 0;
1048 1048 padding: 0;
1049 1049 }
1050 1050
1051 1051 #content div.panel ul.pagination > li {
1052 1052 display: inline;
1053 1053 }
1054 1054
1055 1055 #content div.panel ul.pagination > :first-child {
1056 1056 border-radius: 4px 0px 0px 4px;
1057 1057 }
1058 1058
1059 1059 #content div.panel ul.pagination > :last-child {
1060 1060 border-radius: 0px 4px 4px 0px;
1061 1061 border-right: 1px solid #cfcfcf;
1062 1062 }
1063 1063
1064 1064 #content div.panel ul.pagination > li {
1065 1065 height: 1%;
1066 1066 float: left;
1067 1067 background: #ebebeb url("../images/pager.png") repeat-x;
1068 1068 border-top: 1px solid #dedede;
1069 1069 border-left: 1px solid #cfcfcf;
1070 1070 border-bottom: 1px solid #c4c4c4;
1071 1071 color: #4A4A4A;
1072 1072 font-weight: 700;
1073 1073 padding: 6px;
1074 1074 }
1075 1075
1076 1076 #content div.panel ul.pagination > li.active {
1077 1077 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1078 1078 border-top: 1px solid #ccc;
1079 1079 border-left: 1px solid #bebebe;
1080 1080 border-bottom: 1px solid #afafaf;
1081 1081 color: #515151;
1082 1082 }
1083 1083
1084 1084 #content div.panel ul.pagination > a:hover,
1085 1085 #content div.panel ul.pagination > a:active {
1086 1086 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1087 1087 border-top: 1px solid #ccc;
1088 1088 border-left: 1px solid #bebebe;
1089 1089 border-bottom: 1px solid #afafaf;
1090 1090 text-decoration: none;
1091 1091 }
1092 1092
1093 1093 #content div.panel ul.pagination > li a {
1094 1094 color: inherit;
1095 1095 text-decoration: inherit;
1096 1096 }
1097 1097
1098 1098 #content div.panel div.traffic div.legend {
1099 1099 clear: both;
1100 1100 overflow: hidden;
1101 1101 border-bottom: 1px solid #ddd;
1102 1102 margin: 0 0 10px;
1103 1103 padding: 0 0 10px;
1104 1104 }
1105 1105
1106 1106 #content div.panel div.traffic div.legend h6 {
1107 1107 float: left;
1108 1108 border: none;
1109 1109 margin: 0;
1110 1110 padding: 0;
1111 1111 }
1112 1112
1113 1113 #content div.panel div.traffic div.legend li {
1114 1114 list-style: none;
1115 1115 float: left;
1116 1116 font-size: 11px;
1117 1117 margin: 0;
1118 1118 padding: 0 8px 0 4px;
1119 1119 }
1120 1120
1121 1121 #content div.panel div.traffic div.legend li.visits {
1122 1122 border-left: 12px solid #edc240;
1123 1123 }
1124 1124
1125 1125 #content div.panel div.traffic div.legend li.pageviews {
1126 1126 border-left: 12px solid #afd8f8;
1127 1127 }
1128 1128
1129 1129 #content div.panel div.traffic table {
1130 1130 width: auto;
1131 1131 }
1132 1132
1133 1133 #content div.panel div.traffic table td {
1134 1134 background: transparent;
1135 1135 border: none;
1136 1136 padding: 2px 3px 3px;
1137 1137 }
1138 1138
1139 1139 #content div.panel div.traffic table td.legendLabel {
1140 1140 padding: 0 3px 2px;
1141 1141 }
1142 1142
1143 1143 #content div.panel #summary-panel-body {
1144 1144 position: relative;
1145 1145 }
1146 1146
1147 1147 #content div.panel #summary {
1148 1148 margin-right: 200px;
1149 1149 min-height: 240px;
1150 1150 }
1151 1151
1152 1152 #summary-menu-stats {
1153 1153 float: left;
1154 1154 width: 180px;
1155 1155 position: absolute;
1156 1156 top: 0;
1157 1157 right: 0;
1158 1158 }
1159 1159
1160 1160 #summary-menu-stats ul {
1161 1161 display: block;
1162 1162 background-color: #f9f9f9;
1163 1163 border: 1px solid #d1d1d1;
1164 1164 border-radius: 4px;
1165 1165 }
1166 1166
1167 1167 #content #summary-menu-stats li {
1168 1168 border-top: 1px solid #d1d1d1;
1169 1169 padding: 0;
1170 1170 }
1171 1171
1172 1172 #content #summary-menu-stats li:hover {
1173 1173 background: #f0f0f0;
1174 1174 }
1175 1175
1176 1176 #content #summary-menu-stats li:first-child {
1177 1177 border-top: none;
1178 1178 }
1179 1179
1180 1180 #summary-menu-stats a {
1181 1181 display: block;
1182 1182 padding: 12px 10px;
1183 1183 background-repeat: no-repeat;
1184 1184 background-position: 10px 50%;
1185 1185 padding-right: 10px;
1186 1186 }
1187 1187
1188 1188 #repo_size_2.loaded {
1189 1189 margin-left: 30px;
1190 1190 display: block;
1191 1191 padding-right: 10px;
1192 1192 padding-bottom: 7px;
1193 1193 }
1194 1194
1195 1195 #summary-menu-stats a:hover {
1196 1196 text-decoration: none;
1197 1197 }
1198 1198
1199 1199 #summary-menu-stats .badge {
1200 1200 padding: 2px 4px !important;
1201 1201 font-size: 10px;
1202 1202 }
1203 1203
1204 1204 #summary .metatag {
1205 1205 display: inline-block;
1206 1206 padding: 3px 5px;
1207 1207 margin-bottom: 3px;
1208 1208 margin-right: 1px;
1209 1209 border-radius: 5px;
1210 1210 }
1211 1211
1212 1212 #content div.panel #summary p {
1213 1213 margin-bottom: -5px;
1214 1214 width: 600px;
1215 1215 white-space: pre-wrap;
1216 1216 }
1217 1217
1218 1218 #content div.panel #summary p:last-child {
1219 1219 margin-bottom: 9px;
1220 1220 }
1221 1221
1222 1222 #content div.panel #summary p:first-of-type {
1223 1223 margin-top: 9px;
1224 1224 }
1225 1225
1226 1226 .metatag {
1227 1227 display: inline-block;
1228 1228 margin-right: 1px;
1229 1229 border-radius: 4px 4px 4px 4px;
1230 1230
1231 1231 border: solid 1px #9CF;
1232 1232 padding: 2px 3px 2px 3px !important;
1233 1233 background-color: #DEF;
1234 1234 }
1235 1235
1236 1236 .metatag[data-tag="dead"] {
1237 1237 background-color: #E44;
1238 1238 }
1239 1239
1240 1240 .metatag[data-tag="stale"] {
1241 1241 background-color: #EA4;
1242 1242 }
1243 1243
1244 1244 .metatag[data-tag="featured"] {
1245 1245 background-color: #AEA;
1246 1246 }
1247 1247
1248 1248 .metatag[data-tag="requires"] {
1249 1249 background-color: #9CF;
1250 1250 }
1251 1251
1252 1252 .metatag[data-tag="recommends"] {
1253 1253 background-color: #BDF;
1254 1254 }
1255 1255
1256 1256 .metatag[data-tag="lang"] {
1257 1257 background-color: #FAF474;
1258 1258 }
1259 1259
1260 1260 .metatag[data-tag="license"] {
1261 1261 border: solid 1px #9CF;
1262 1262 background-color: #DEF;
1263 1263 }
1264 1264 .metatag[data-tag="see"] {
1265 1265 border: solid 1px #CBD;
1266 1266 background-color: #EDF;
1267 1267 }
1268 1268
1269 1269 a.metatag[data-tag="license"]:hover {
1270 1270 background-color: #577632;
1271 1271 color: #FFF;
1272 1272 text-decoration: none;
1273 1273 }
1274 1274
1275 1275 #summary .desc {
1276 1276 white-space: pre;
1277 1277 width: 100%;
1278 1278 }
1279 1279
1280 1280 #summary .repo_name {
1281 1281 font-size: 1.6em;
1282 1282 font-weight: bold;
1283 1283 vertical-align: baseline;
1284 1284 clear: right
1285 1285 }
1286 1286
1287 1287 #footer {
1288 1288 clear: both;
1289 1289 overflow: hidden;
1290 1290 text-align: right;
1291 1291 margin: 0;
1292 1292 padding: 10px 20px;
1293 1293 margin: -10px 0 0;
1294 1294 background-color: #577632;
1295 1295 background-repeat: repeat-x;
1296 1296 background-image: linear-gradient(to bottom, #577632, #577632);
1297 1297 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1298 1298 border-radius: 4px 4px 4px 4px;
1299 1299 }
1300 1300
1301 1301 #footer > span {
1302 1302 color: #FFF;
1303 1303 font-weight: 700;
1304 1304 }
1305 1305
1306 1306 #footer .navbar-link {
1307 1307 color: #FFF;
1308 1308 }
1309 1309
1310 1310 #login div.panel-heading {
1311 1311 clear: both;
1312 1312 overflow: hidden;
1313 1313 position: relative;
1314 1314 background-color: #577632;
1315 1315 background-repeat: repeat-x;
1316 1316 background-image: linear-gradient(to bottom, #577632, #577632);
1317 1317 padding: 0;
1318 1318 }
1319 1319
1320 1320 #login .panel-body .icon-lock {
1321 1321 font-size: 100px;
1322 1322 color: #DDD;
1323 1323 position: absolute;
1324 1324 margin-left: -20px;
1325 1325 z-index: 1;
1326 1326 }
1327 1327
1328 1328 #login div.inner {
1329 1329 background: #FFF;
1330 1330 border-top: none;
1331 1331 border-bottom: none;
1332 1332 margin: 0 auto;
1333 1333 padding: 20px;
1334 1334 }
1335 1335
1336 1336 #login div.form div.form-horizontal div.form-group > label {
1337 1337 width: 173px;
1338 1338 float: left;
1339 1339 text-align: right;
1340 1340 margin: 2px 10px 0 0;
1341 1341 padding: 5px 0 0 5px;
1342 1342 }
1343 1343
1344 1344 #login div.form div.form-horizontal div.form-group div input {
1345 1345 background: #FFF;
1346 1346 border-top: 1px solid #b3b3b3;
1347 1347 border-left: 1px solid #b3b3b3;
1348 1348 border-right: 1px solid #eaeaea;
1349 1349 border-bottom: 1px solid #eaeaea;
1350 1350 color: #000;
1351 1351 font-size: 11px;
1352 1352 margin: 0;
1353 1353 padding: 7px 7px 6px;
1354 1354 }
1355 1355
1356 1356 #login div.form .buttons {
1357 1357 float: right;
1358 1358 }
1359 1359
1360 1360 #login div.form div.links {
1361 1361 clear: both;
1362 1362 overflow: hidden;
1363 1363 margin: 10px 0 0;
1364 1364 border-top: 1px solid #DDD;
1365 1365 padding: 10px 0 0;
1366 1366 }
1367 1367
1368 1368 .user-menu {
1369 1369 margin: 0px !important;
1370 1370 float: left;
1371 1371 }
1372 1372
1373 1373 .user-menu .container {
1374 1374 padding: 0px 4px 0px 4px;
1375 1375 margin: 0px 0px 0px 0px;
1376 1376 }
1377 1377
1378 1378 .user-menu .gravatar {
1379 1379 margin: 0px 0px 0px 0px;
1380 1380 cursor: pointer;
1381 1381 }
1382 1382 .user-menu .gravatar.enabled {
1383 1383 background-color: #FDF784 !important;
1384 1384 }
1385 1385 .user-menu .gravatar:hover {
1386 1386 background-color: #FDF784 !important;
1387 1387 }
1388 1388 #quick_login {
1389 1389 width: 300px;
1390 1390 min-height: 110px;
1391 1391 padding: 4px;
1392 1392 position: absolute;
1393 1393 right: 0;
1394 1394 background-color: #577632;
1395 1395 background-repeat: repeat-x;
1396 1396 background-image: linear-gradient(to bottom, #577632, #577632);
1397 1397
1398 1398 z-index: 999;
1399 1399 border-radius: 0px 0px 4px 4px;
1400 1400 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1401 1401
1402 1402 overflow: hidden;
1403 1403 }
1404 1404 #quick_login h4 {
1405 1405 color: #fff;
1406 1406 padding: 5px 0px 5px 14px;
1407 1407 }
1408 1408
1409 1409 #quick_login .password_forgoten {
1410 1410 padding-right: 0px;
1411 1411 padding-top: 0px;
1412 1412 text-align: left;
1413 1413 }
1414 1414
1415 1415 #quick_login .password_forgoten a {
1416 1416 font-size: 10px;
1417 1417 color: #fff;
1418 1418 padding: 0px !important;
1419 1419 line-height: 20px !important;
1420 1420 }
1421 1421
1422 1422 #quick_login .register {
1423 1423 padding-right: 10px;
1424 1424 padding-top: 5px;
1425 1425 text-align: left;
1426 1426 }
1427 1427
1428 1428 #quick_login .register a {
1429 1429 font-size: 10px;
1430 1430 color: #fff;
1431 1431 padding: 0px !important;
1432 1432 line-height: 20px !important;
1433 1433 }
1434 1434
1435 1435 #quick_login .submit {
1436 1436 margin: -20px 0 0 0px;
1437 1437 position: absolute;
1438 1438 right: 15px;
1439 1439 }
1440 1440
1441 1441 #quick_login > .pull-left {
1442 1442 width: 170px;
1443 1443 }
1444 1444 #quick_login > .pull-right {
1445 1445 width: 110px;
1446 1446 }
1447 1447 #quick_login .full_name {
1448 1448 color: #FFFFFF;
1449 1449 font-weight: bold;
1450 1450 padding: 3px 3px 3px 6px;
1451 1451 }
1452 1452 #quick_login .big_gravatar {
1453 1453 padding: 4px 0px 0px 6px;
1454 1454 }
1455 1455 #quick_login .notifications {
1456 1456 padding: 2px 0px 0px 6px;
1457 1457 color: #FFFFFF;
1458 1458 font-weight: bold;
1459 1459 line-height: 10px !important;
1460 1460 }
1461 1461 #quick_login .notifications a,
1462 1462 #quick_login .unread a {
1463 1463 color: #FFFFFF;
1464 1464 display: block;
1465 1465 padding: 0px !important;
1466 1466 }
1467 1467 #quick_login .notifications a:hover,
1468 1468 #quick_login .unread a:hover {
1469 1469 background-color: inherit !important;
1470 1470 }
1471 1471 #quick_login .email,
1472 1472 #quick_login .unread {
1473 1473 color: #FFFFFF;
1474 1474 padding: 3px 3px 3px 6px;
1475 1475 }
1476 1476 #quick_login .links .logout {
1477 1477 }
1478 1478
1479 1479 #quick_login div.form div.form-horizontal {
1480 1480 padding-top: 2px;
1481 1481 padding-left: 10px;
1482 1482 }
1483 1483
1484 1484 #quick_login div.form div.form-horizontal div.form-group {
1485 1485 padding: 5px;
1486 1486 }
1487 1487
1488 1488 #quick_login div.form div.form-horizontal div.form-group > label {
1489 1489 color: #fff;
1490 1490 padding-bottom: 3px;
1491 1491 }
1492 1492
1493 1493 #quick_login div.form div.form-horizontal div.form-group > div input {
1494 1494 width: 236px;
1495 1495 background: #FFF;
1496 1496 border-top: 1px solid #b3b3b3;
1497 1497 border-left: 1px solid #b3b3b3;
1498 1498 border-right: 1px solid #eaeaea;
1499 1499 border-bottom: 1px solid #eaeaea;
1500 1500 color: #000;
1501 1501 font-size: 11px;
1502 1502 margin: 0;
1503 1503 padding: 5px 7px 4px;
1504 1504 }
1505 1505
1506 1506 #quick_login div.form div.form-horizontal div.buttons {
1507 1507 clear: both;
1508 1508 overflow: hidden;
1509 1509 text-align: right;
1510 1510 margin: 0;
1511 1511 padding: 5px 14px 0px 5px;
1512 1512 }
1513 1513
1514 1514 #quick_login div.form div.links {
1515 1515 clear: both;
1516 1516 overflow: hidden;
1517 1517 margin: 10px 0 0;
1518 1518 padding: 0 0 2px;
1519 1519 }
1520 1520
1521 1521 #quick_login ol.links {
1522 1522 display: block;
1523 1523 font-weight: bold;
1524 1524 list-style: none outside none;
1525 1525 text-align: right;
1526 1526 }
1527 1527 #quick_login ol.links li {
1528 1528 line-height: 27px;
1529 1529 margin: 0;
1530 1530 padding: 0;
1531 1531 color: #fff;
1532 1532 display: block;
1533 1533 float: none !important;
1534 1534 }
1535 1535
1536 1536 #quick_login ol.links li a {
1537 1537 color: #fff;
1538 1538 display: block;
1539 1539 padding: 2px;
1540 1540 }
1541 1541 #quick_login ol.links li a:HOVER {
1542 1542 background-color: inherit !important;
1543 1543 }
1544 1544
1545 1545 #register div.panel-heading {
1546 1546 clear: both;
1547 1547 overflow: hidden;
1548 1548 position: relative;
1549 1549 background-color: #577632;
1550 1550 background-repeat: repeat-x;
1551 1551 background-image: linear-gradient(to bottom, #577632, #577632);
1552 1552 padding: 0;
1553 1553 }
1554 1554
1555 1555 #register div.inner {
1556 1556 background: #FFF;
1557 1557 border-top: none;
1558 1558 border-bottom: none;
1559 1559 margin: 0 auto;
1560 1560 padding: 20px;
1561 1561 }
1562 1562
1563 1563 #register div.form div.form-horizontal div.form-group > label {
1564 1564 width: 135px;
1565 1565 float: left;
1566 1566 text-align: right;
1567 1567 margin: 2px 10px 0 0;
1568 1568 padding: 5px 0 0 5px;
1569 1569 }
1570 1570
1571 1571 #register div.form div.form-horizontal div.form-group > div input {
1572 1572 width: 300px;
1573 1573 background: #FFF;
1574 1574 border-top: 1px solid #b3b3b3;
1575 1575 border-left: 1px solid #b3b3b3;
1576 1576 border-right: 1px solid #eaeaea;
1577 1577 border-bottom: 1px solid #eaeaea;
1578 1578 color: #000;
1579 1579 font-size: 11px;
1580 1580 margin: 0;
1581 1581 padding: 7px 7px 6px;
1582 1582 }
1583 1583
1584 1584 #register div.form div.form-horizontal div.buttons {
1585 1585 clear: both;
1586 1586 overflow: hidden;
1587 1587 border-top: 1px solid #DDD;
1588 1588 text-align: left;
1589 1589 margin: 0;
1590 1590 padding: 10px 0 0 150px;
1591 1591 }
1592 1592
1593 1593 #register div.form div.activation_msg {
1594 1594 padding-top: 4px;
1595 1595 padding-bottom: 4px;
1596 1596 }
1597 1597
1598 1598 #journal {
1599 1599 margin-left: 20px;
1600 1600 }
1601 1601
1602 1602 #journal .journal_day {
1603 1603 font-size: 20px;
1604 1604 padding: 10px 0px;
1605 1605 border-bottom: 2px solid #DDD;
1606 1606 margin-left: 10px;
1607 1607 margin-right: 10px;
1608 1608 }
1609 1609
1610 1610 #journal .journal_container {
1611 1611 clear: both;
1612 1612 }
1613 1613
1614 1614 #journal .journal_action_container {
1615 1615 padding-left: 18px;
1616 1616 }
1617 1617
1618 1618 #journal .journal_user {
1619 1619 color: #747474;
1620 1620 font-size: 14px;
1621 1621 font-weight: bold;
1622 1622 height: 30px;
1623 1623 }
1624 1624
1625 1625 #journal .journal_user.deleted {
1626 1626 color: #747474;
1627 1627 font-size: 14px;
1628 1628 font-weight: normal;
1629 1629 height: 30px;
1630 1630 font-style: italic;
1631 1631 }
1632 1632
1633 1633
1634 1634 #journal .journal_icon {
1635 1635 clear: both;
1636 1636 float: left;
1637 1637 padding-right: 4px;
1638 1638 padding-top: 3px;
1639 1639 }
1640 1640
1641 1641 #journal .journal_action {
1642 1642 padding-top: 4px;
1643 1643 min-height: 2px;
1644 1644 float: left
1645 1645 }
1646 1646
1647 1647 #journal .journal_action_params {
1648 1648 clear: left;
1649 1649 padding-left: 22px;
1650 1650 }
1651 1651
1652 1652 #journal .journal_repo {
1653 1653 float: left;
1654 1654 margin-left: 6px;
1655 1655 padding-top: 3px;
1656 1656 }
1657 1657
1658 1658 #journal .date {
1659 1659 clear: both;
1660 1660 color: #777777;
1661 1661 font-size: 11px;
1662 1662 padding-left: 22px;
1663 1663 }
1664 1664
1665 1665 #journal .journal_repo .journal_repo_name {
1666 1666 font-weight: bold;
1667 1667 font-size: 1.1em;
1668 1668 }
1669 1669
1670 1670 #journal .compare_view {
1671 1671 padding: 5px 0px 5px 0px;
1672 1672 width: 95px;
1673 1673 }
1674 1674
1675 1675 .trending_language_tbl, .trending_language_tbl td {
1676 1676 border: 0 !important;
1677 1677 margin: 0 !important;
1678 1678 padding: 0 !important;
1679 1679 }
1680 1680
1681 1681 .trending_language_tbl, .trending_language_tbl tr {
1682 1682 border-spacing: 1px;
1683 1683 }
1684 1684
1685 1685 #lang_stats .progress-bar {
1686 1686 background-color: #577632;
1687 1687 color: #FFF;
1688 1688 display: block;
1689 1689 min-width: 20px;
1690 1690 text-decoration: none;
1691 1691 height: 12px;
1692 1692 margin-bottom: 0px;
1693 1693 margin-left: 5px;
1694 1694 white-space: pre;
1695 1695 padding: 3px;
1696 1696 border-top-right-radius: 8px;
1697 1697 border-bottom-right-radius: 8px;
1698 1698 }
1699 1699
1700 1700 #lang_stats table td {
1701 1701 border-bottom: none !important;
1702 1702 padding: 1px 0 !important;
1703 1703 }
1704 1704
1705 1705 h3.files_location {
1706 1706 font-size: 1.8em;
1707 1707 font-weight: 700;
1708 1708 border-bottom: none !important;
1709 1709 margin: 10px 0 !important;
1710 1710 }
1711 1711
1712 1712 #files_data dl dt {
1713 1713 float: left;
1714 1714 width: 60px;
1715 1715 margin: 0 !important;
1716 1716 padding: 5px;
1717 1717 }
1718 1718
1719 1719 #files_data dl dd {
1720 1720 margin: 0 !important;
1721 1721 padding: 5px !important;
1722 1722 }
1723 1723
1724 1724 #files_data .codeblock #editor_container .error-message {
1725 1725 color: red;
1726 1726 padding: 10px 10px 10px 26px
1727 1727 }
1728 1728
1729 1729 .file_history {
1730 1730 padding-top: 10px;
1731 1731 font-size: 16px;
1732 1732 }
1733 1733 .file_author {
1734 1734 float: left;
1735 1735 }
1736 1736
1737 1737 .file_author .item {
1738 1738 float: left;
1739 1739 padding: 5px;
1740 1740 color: #888;
1741 1741 }
1742 1742
1743 1743 .changeset_id {
1744 1744 color: #666666;
1745 1745 margin-right: -3px;
1746 1746 }
1747 1747
1748 1748 .changeset-logical-index {
1749 1749 color: #666666;
1750 1750 font-style: italic;
1751 1751 font-size: 85%;
1752 1752 padding-right: 0.5em;
1753 1753 text-align: right;
1754 1754 }
1755 1755
1756 1756 #changeset_content {
1757 1757 border-left: 1px solid #CCC;
1758 1758 border-right: 1px solid #CCC;
1759 1759 border-bottom: 1px solid #CCC;
1760 1760 padding: 5px;
1761 1761 }
1762 1762
1763 1763 #changeset_compare_view_content {
1764 1764 border: 1px solid #CCC;
1765 1765 padding: 5px;
1766 1766 }
1767 1767
1768 1768 #changeset_content .container {
1769 1769 font-size: 1.2em;
1770 1770 overflow: hidden;
1771 1771 }
1772 1772
1773 1773 #changeset_compare_view_content .compare_view_commits {
1774 1774 width: auto !important;
1775 1775 }
1776 1776
1777 1777 #changeset_compare_view_content .compare_view_commits td {
1778 1778 padding: 0px 0px 0px 12px !important;
1779 1779 }
1780 1780
1781 1781 #changeset_content .container .right {
1782 1782 float: right;
1783 1783 width: 20%;
1784 1784 text-align: right;
1785 1785 }
1786 1786
1787 1787 #changeset_content .container .message {
1788 1788 white-space: pre-wrap;
1789 1789 }
1790 1790 #changeset_content .container .message a:hover {
1791 1791 text-decoration: none;
1792 1792 }
1793 1793 .cs_files .cur_cs {
1794 1794 margin: 10px 2px;
1795 1795 font-weight: bold;
1796 1796 }
1797 1797
1798 1798 .cs_files .node {
1799 1799 float: left;
1800 1800 }
1801 1801
1802 1802 .cs_files .changes {
1803 1803 float: right;
1804 1804 color: #577632;
1805 1805 }
1806 1806
1807 1807 .cs_files .changes .added {
1808 1808 background-color: #BBFFBB;
1809 1809 float: left;
1810 1810 text-align: center;
1811 1811 font-size: 9px;
1812 1812 padding: 2px 0px 2px 0px;
1813 1813 }
1814 1814
1815 1815 .cs_files .changes .deleted {
1816 1816 background-color: #FF8888;
1817 1817 float: left;
1818 1818 text-align: center;
1819 1819 font-size: 9px;
1820 1820 padding: 2px 0px 2px 0px;
1821 1821 }
1822 1822 /*new binary
1823 1823 NEW_FILENODE = 1
1824 1824 DEL_FILENODE = 2
1825 1825 MOD_FILENODE = 3
1826 1826 RENAMED_FILENODE = 4
1827 1827 CHMOD_FILENODE = 5
1828 1828 BIN_FILENODE = 6
1829 1829 */
1830 1830 .cs_files .changes .bin {
1831 1831 background-color: #BBFFBB;
1832 1832 float: left;
1833 1833 text-align: center;
1834 1834 font-size: 9px;
1835 1835 padding: 2px 0px 2px 0px;
1836 1836 }
1837 1837 .cs_files .changes .bin.bin1 {
1838 1838 background-color: #BBFFBB;
1839 1839 }
1840 1840
1841 1841 /*deleted binary*/
1842 1842 .cs_files .changes .bin.bin2 {
1843 1843 background-color: #FF8888;
1844 1844 }
1845 1845
1846 1846 /*mod binary*/
1847 1847 .cs_files .changes .bin.bin3 {
1848 1848 background-color: #DDDDDD;
1849 1849 }
1850 1850
1851 1851 /*rename file*/
1852 1852 .cs_files .changes .bin.bin4 {
1853 1853 background-color: #6D99FF;
1854 1854 }
1855 1855
1856 1856 /*rename file*/
1857 1857 .cs_files .changes .bin.bin4 {
1858 1858 background-color: #6D99FF;
1859 1859 }
1860 1860
1861 1861 /*chmod file*/
1862 1862 .cs_files .changes .bin.bin5 {
1863 1863 background-color: #6D99FF;
1864 1864 }
1865 1865
1866 1866 .cs_files .cs_added,
1867 1867 .cs_files .cs_A {
1868 1868 height: 16px;
1869 1869 margin-top: 7px;
1870 1870 text-align: left;
1871 1871 }
1872 1872
1873 1873 .cs_files .cs_changed,
1874 1874 .cs_files .cs_M {
1875 1875 height: 16px;
1876 1876 margin-top: 7px;
1877 1877 text-align: left;
1878 1878 }
1879 1879
1880 1880 .cs_files .cs_removed,
1881 1881 .cs_files .cs_D {
1882 1882 height: 16px;
1883 1883 margin-top: 7px;
1884 1884 text-align: left;
1885 1885 }
1886 1886
1887 1887 .cs_files .cs_renamed,
1888 1888 .cs_files .cs_R {
1889 1889 height: 16px;
1890 1890 margin-top: 7px;
1891 1891 text-align: left;
1892 1892 }
1893 1893
1894 1894 #graph {
1895 1895 position: relative;
1896 1896 overflow: hidden;
1897 1897 }
1898 1898
1899 1899 #graph_nodes {
1900 1900 position: absolute;
1901 1901 width: 100px;
1902 1902 }
1903 1903
1904 1904 #graph_content,
1905 1905 #graph .info_box,
1906 1906 #graph .container_header {
1907 1907 margin-left: 100px;
1908 1908 }
1909 1909
1910 1910 #graph_content {
1911 1911 position: relative;
1912 1912 }
1913 1913
1914 1914 #graph .container_header {
1915 1915 padding: 10px;
1916 1916 height: 25px;
1917 1917 }
1918 1918
1919 1919 #graph_content #rev_range_container {
1920 1920 float: left;
1921 1921 margin: 0px 0px 0px 3px;
1922 1922 }
1923 1923
1924 1924 #graph_content #rev_range_clear {
1925 1925 float: left;
1926 1926 margin: 0px 0px 0px 3px;
1927 1927 }
1928 1928
1929 1929 #graph_content #changesets {
1930 1930 table-layout: fixed;
1931 1931 border-collapse: collapse;
1932 1932 border-left: none;
1933 1933 border-right: none;
1934 1934 border-color: #cdcdcd;
1935 1935 }
1936 1936
1937 1937 #updaterevs-table tr.mergerow,
1938 1938 #graph_content_pr tr.mergerow,
1939 1939 #shortlog_data tr.mergerow,
1940 1940 #graph_content #changesets tr.out-of-range,
1941 1941 #graph_content #changesets tr.mergerow {
1942 1942 opacity: 0.6;
1943 1943 }
1944 1944
1945 1945 #graph_content #changesets td {
1946 1946 overflow: hidden;
1947 1947 text-overflow: ellipsis;
1948 1948 white-space: nowrap;
1949 1949 height: 31px;
1950 1950 border-color: #cdcdcd;
1951 1951 text-align: left;
1952 1952 }
1953 1953
1954 1954 #graph_content .container .checkbox {
1955 1955 width: 14px;
1956 1956 font-size: 0.85em;
1957 1957 }
1958 1958
1959 1959 #graph_content .container .status {
1960 1960 width: 14px;
1961 1961 font-size: 0.85em;
1962 1962 }
1963 1963
1964 1964 #graph_content .container .author {
1965 1965 width: 105px;
1966 1966 }
1967 1967
1968 1968 #graph_content .container .hash {
1969 1969 width: 100px;
1970 1970 font-size: 0.85em;
1971 1971 }
1972 1972
1973 1973 #graph_content #changesets .container .date {
1974 1974 width: 76px;
1975 1975 color: #666;
1976 1976 font-size: 10px;
1977 1977 }
1978 1978
1979 1979 #graph_content_pr .compare_view_commits .expand_commit,
1980 1980 #graph_content .container .expand_commit {
1981 1981 width: 24px;
1982 1982 cursor: pointer;
1983 1983 }
1984 1984
1985 1985 #graph_content #changesets .container .right {
1986 1986 width: 120px;
1987 1987 padding-right: 0px;
1988 1988 overflow: visible;
1989 1989 position: relative;
1990 1990 }
1991 1991
1992 1992 #graph_content .container .mid {
1993 1993 padding: 0;
1994 1994 }
1995 1995
1996 1996 #graph_content .log-container {
1997 1997 position: relative;
1998 1998 }
1999 1999
2000 2000 #graph_content .container #singlerange,
2001 2001 #graph_content .container .changeset_range {
2002 2002 float: left;
2003 2003 margin: 6px 3px;
2004 2004 }
2005 2005
2006 2006 #graph_content .container .author img {
2007 2007 vertical-align: middle;
2008 2008 }
2009 2009
2010 2010 #graph_content .container .author .user {
2011 2011 color: #444444;
2012 2012 }
2013 2013
2014 2014 #graph_content .container .mid .message {
2015 2015 white-space: pre-wrap;
2016 2016 padding: 0;
2017 2017 overflow: hidden;
2018 2018 height: 1.1em;
2019 2019 }
2020 2020
2021 2021 #graph_content_pr .compare_view_commits .message {
2022 2022 padding: 0 !important;
2023 2023 height: 1.1em;
2024 2024 }
2025 2025
2026 2026 #graph_content .container .mid .message.expanded,
2027 2027 #graph_content_pr .compare_view_commits .message.expanded {
2028 2028 height: auto;
2029 2029 margin: 8px 0px 8px 0px;
2030 2030 overflow: initial;
2031 2031 }
2032 2032
2033 2033 #graph_content .container .extra-container {
2034 2034 display: block;
2035 2035 position: absolute;
2036 2036 top: -15px;
2037 2037 right: 0;
2038 2038 padding-left: 5px;
2039 2039 background: #FFFFFF;
2040 2040 height: 41px;
2041 2041 }
2042 2042
2043 2043 #pull_request_overview .comments-container,
2044 2044 #changeset_compare_view_content .comments-container,
2045 2045 #graph_content .comments-container,
2046 2046 #shortlog_data .comments-container,
2047 2047 #graph_content .logtags {
2048 2048 display: block;
2049 2049 float: left;
2050 2050 overflow: hidden;
2051 2051 padding: 0;
2052 2052 margin: 0;
2053 2053 white-space: nowrap;
2054 2054 }
2055 2055
2056 2056 #graph_content .comments-container {
2057 2057 margin: 0.8em 0;
2058 2058 margin-right: 0.5em;
2059 2059 }
2060 2060
2061 2061 #graph_content .tagcontainer {
2062 2062 width: 80px;
2063 2063 position: relative;
2064 2064 float: right;
2065 2065 height: 100%;
2066 2066 top: 7px;
2067 2067 margin-left: 0.5em;
2068 2068 }
2069 2069
2070 2070 #graph_content .logtags {
2071 2071 min-width: 80px;
2072 2072 height: 1.1em;
2073 2073 position: absolute;
2074 2074 left: 0px;
2075 2075 width: auto;
2076 2076 top: 0px;
2077 2077 }
2078 2078
2079 2079 #graph_content .logtags.tags {
2080 2080 top: 14px;
2081 2081 }
2082 2082
2083 2083 #graph_content .logtags:hover {
2084 2084 overflow: visible;
2085 2085 position: absolute;
2086 2086 width: auto;
2087 2087 right: 0;
2088 2088 left: initial;
2089 2089 }
2090 2090
2091 2091 #graph_content .logtags .booktag,
2092 2092 #graph_content .logtags .tagtag {
2093 2093 float: left;
2094 2094 line-height: 1em;
2095 2095 margin-bottom: 1px;
2096 2096 margin-right: 1px;
2097 2097 padding: 1px 3px;
2098 2098 font-size: 10px;
2099 2099 }
2100 2100
2101 2101 #graph_content .container .mid .message a:hover {
2102 2102 text-decoration: none;
2103 2103 }
2104 2104
2105 2105 #compare_branches + div.panel-body .revision-link,
2106 2106 #compare_tags + div.panel-body .revision-link,
2107 2107 #compare_bookmarks + div.panel-body .revision-link,
2108 2108 div.panel-body #files_data .revision-link,
2109 2109 #repos_list_wrap .revision-link,
2110 2110 #shortlog_data .revision-link {
2111 2111 font-weight: normal !important;
2112 2112 font-family: monospace;
2113 2113 font-size: 12px;
2114 2114 color: #577632;
2115 2115 }
2116 2116
2117 2117 .revision-link {
2118 2118 color: #3F6F9F;
2119 2119 font-weight: bold !important;
2120 2120 }
2121 2121
2122 2122 .issue-tracker-link {
2123 2123 color: #3F6F9F;
2124 2124 font-weight: bold !important;
2125 2125 }
2126 2126
2127 2127 .changeset-status-container {
2128 2128 padding-right: 5px;
2129 2129 margin-top: 1px;
2130 2130 float: right;
2131 2131 height: 14px;
2132 2132 }
2133 2133 .code-header .changeset-status-container {
2134 2134 float: left;
2135 2135 padding: 2px 0px 0px 2px;
2136 2136 }
2137 2137 .code-header .changeset-status-container .changeset-status-lbl {
2138 2138 float: left;
2139 2139 padding: 0px 4px 0px 0px;
2140 2140 }
2141 2141 .changeset-status-container div.changeset-status-ico {
2142 2142 float: left;
2143 2143 }
2144 2144 .code-header .changeset-status-container .changeset-status-ico,
2145 2145 .container .changeset-status-ico {
2146 2146 float: left;
2147 2147 }
2148 2148
2149 2149 /* changeset statuses (must be the same name as the status) */
2150 2150 .changeset-status-not_reviewed {
2151 2151 color: #bababa;
2152 2152 }
2153 2153 .changeset-status-approved {
2154 2154 color: #81ba51;
2155 2155 }
2156 2156 .changeset-status-rejected {
2157 2157 color: #d06060;
2158 2158 }
2159 2159 .changeset-status-under_review {
2160 2160 color: #ffc71e;
2161 2161 }
2162 2162
2163 2163 #graph_content .comments-cnt {
2164 2164 color: rgb(136, 136, 136);
2165 2165 padding: 5px 0;
2166 2166 }
2167 2167
2168 2168 #shortlog_data .comments-cnt {
2169 2169 color: rgb(136, 136, 136);
2170 2170 padding: 3px 0;
2171 2171 }
2172 2172
2173 2173 .right .changes {
2174 2174 clear: both;
2175 2175 }
2176 2176
2177 2177 .right .changes .changed_total {
2178 2178 display: block;
2179 2179 float: right;
2180 2180 text-align: center;
2181 2181 min-width: 45px;
2182 2182 cursor: pointer;
2183 2183 color: #444444;
2184 2184 background: #FEA;
2185 2185 border-radius: 0px 0px 0px 6px;
2186 2186 padding: 1px;
2187 2187 }
2188 2188
2189 2189 .right .changes .added,
2190 2190 .changed, .removed {
2191 2191 display: block;
2192 2192 padding: 1px;
2193 2193 color: #444444;
2194 2194 float: right;
2195 2195 text-align: center;
2196 2196 min-width: 15px;
2197 2197 }
2198 2198
2199 2199 .right .changes .added {
2200 2200 background: #CFC;
2201 2201 }
2202 2202
2203 2203 .right .changes .changed {
2204 2204 background: #FEA;
2205 2205 }
2206 2206
2207 2207 .right .changes .removed {
2208 2208 background: #FAA;
2209 2209 }
2210 2210
2211 2211 .right .mergetag,
2212 2212 .right .merge {
2213 2213 padding: 1px 3px 1px 3px;
2214 2214 background-color: #fca062;
2215 2215 font-size: 10px;
2216 2216 color: #ffffff;
2217 2217 text-transform: uppercase;
2218 2218 white-space: nowrap;
2219 2219 border-radius: 3px;
2220 2220 margin-right: 2px;
2221 2221 }
2222 2222
2223 2223 .right .parent {
2224 2224 color: #666666;
2225 2225 clear: both;
2226 2226 }
2227 2227 .right .logtags {
2228 2228 line-height: 2.2em;
2229 2229 }
2230 2230 .phasetag, .bumpedtag, .divergenttag, .extincttag, .unstabletag, .repotag, .branchtag, .logtags .tagtag, .logtags .booktag {
2231 2231 margin: 0px 2px;
2232 2232 }
2233 2233
2234 2234 .phasetag,
2235 2235 .bumpedtag,
2236 2236 .divergenttag,
2237 2237 .extincttag,
2238 2238 .unstabletag,
2239 2239 .repotag,
2240 2240 .branchtag,
2241 2241 .tagtag,
2242 2242 .booktag,
2243 2243 .spantag {
2244 2244 padding: 1px 3px 1px 3px;
2245 2245 font-size: 10px;
2246 2246 color: #577632;
2247 2247 white-space: nowrap;
2248 2248 border-radius: 4px;
2249 2249 border: 1px solid #d9e8f8;
2250 2250 line-height: 1.5em;
2251 2251 }
2252 2252
2253 2253 #graph_content .phasetag,
2254 2254 #graph_content .bumpedtag,
2255 2255 #graph_content .divergenttag,
2256 2256 #graph_content .extincttag,
2257 2257 #graph_content .unstabletag,
2258 2258 #graph_content .branchtag,
2259 2259 #graph_content .tagtag,
2260 2260 #graph_content .booktag {
2261 2261 margin: 1.1em 0;
2262 2262 margin-right: 0.5em;
2263 2263 }
2264 2264
2265 2265 .phasetag,
2266 2266 .bumpedtag,
2267 2267 .divergenttag,
2268 2268 .extincttag,
2269 2269 .unstabletag,
2270 2270 .repotag,
2271 2271 .branchtag,
2272 2272 .tagtag,
2273 2273 .booktag {
2274 2274 float: left;
2275 2275 display: inline-block;
2276 2276 }
2277 2277
2278 2278 .right .logtags .phasetag,
2279 2279 .right .logtags .bumpedtag,
2280 2280 .right .logtags .divergenttag,
2281 2281 .right .logtags .extincttag,
2282 2282 .right .logtags .unstabletag,
2283 2283 .right .logtags .branchtag,
2284 2284 .right .logtags .tagtag,
2285 2285 .right .logtags .booktag,
2286 2286 .right .mergetag,
2287 2287 .right .merge {
2288 2288 float: right;
2289 2289 line-height: 1em;
2290 2290 margin: 1px 1px !important;
2291 2291 display: block;
2292 2292 }
2293 2293
2294 2294 .repotag {
2295 2295 border-color: #56A546;
2296 2296 color: #46A546;
2297 2297 font-size: 8px;
2298 2298 text-transform: uppercase;
2299 2299 }
2300 2300
2301 2301 #context-bar .repotag,
2302 2302 .repo-icons .repotag {
2303 2303 border-color: white;
2304 2304 color: white;
2305 2305 margin-top: 3px;
2306 2306 }
2307 2307
2308 2308 .repo-icons .repotag {
2309 2309 margin-top: 0px;
2310 2310 padding-top: 0px;
2311 2311 padding-bottom: 0px;
2312 2312 }
2313 2313
2314 2314 .booktag {
2315 2315 border-color: #46A546;
2316 2316 color: #46A546;
2317 2317 }
2318 2318
2319 2319 .tagtag {
2320 2320 border-color: #62cffc;
2321 2321 color: #62cffc;
2322 2322 }
2323 2323
2324 2324 .bumpedtag,
2325 2325 .divergenttag,
2326 2326 .extincttag,
2327 2327 .unstabletag {
2328 2328 background-color: #f00;
2329 2329 border-color: #600;
2330 2330 color: #fff;
2331 2331 }
2332 2332
2333 2333 .phasetag {
2334 2334 border-color: #1F14CE;
2335 2335 color: #1F14CE;
2336 2336 }
2337 2337
2338 2338 .logtags .branchtag a:hover,
2339 2339 .logtags .branchtag a,
2340 2340 .branchtag a,
2341 2341 .branchtag a:hover {
2342 2342 text-decoration: none;
2343 2343 color: inherit;
2344 2344 }
2345 2345 .logtags .tagtag {
2346 2346 padding: 1px 3px 1px 3px;
2347 2347 background-color: #62cffc;
2348 2348 font-size: 10px;
2349 2349 color: #ffffff;
2350 2350 white-space: nowrap;
2351 2351 border-radius: 3px;
2352 2352 }
2353 2353
2354 2354 .tagtag a,
2355 2355 .tagtag a:hover,
2356 2356 .logtags .tagtag a,
2357 2357 .logtags .tagtag a:hover {
2358 2358 text-decoration: none;
2359 2359 color: inherit;
2360 2360 }
2361 2361 .logbooks .booktag,
2362 2362 .logbooks .booktag,
2363 2363 .logtags .booktag,
2364 2364 .logtags .booktag {
2365 2365 padding: 1px 3px 1px 3px;
2366 2366 background-color: #46A546;
2367 2367 font-size: 10px;
2368 2368 color: #ffffff;
2369 2369 white-space: nowrap;
2370 2370 border-radius: 3px;
2371 2371 }
2372 2372 .logbooks .booktag,
2373 2373 .logbooks .booktag a,
2374 2374 .right .logtags .booktag,
2375 2375 .logtags .booktag a {
2376 2376 color: #ffffff;
2377 2377 }
2378 2378
2379 2379 .logbooks .booktag,
2380 2380 .logbooks .booktag a:hover,
2381 2381 .logtags .booktag,
2382 2382 .logtags .booktag a:hover,
2383 2383 .booktag a,
2384 2384 .booktag a:hover {
2385 2385 text-decoration: none;
2386 2386 color: inherit;
2387 2387 }
2388 2388 div.browserblock {
2389 2389 overflow: hidden;
2390 2390 border: 1px solid #ccc;
2391 2391 background: #f8f8f8;
2392 2392 font-size: 100%;
2393 2393 line-height: 125%;
2394 2394 padding: 0;
2395 2395 border-radius: 6px 6px 0px 0px;
2396 2396 }
2397 2397
2398 2398 div.browserblock .browser-header {
2399 2399 background: #FFF;
2400 2400 padding: 10px 0px 15px 0px;
2401 2401 width: 100%;
2402 2402 }
2403 2403
2404 2404 div.browserblock .browser-nav {
2405 2405 float: left
2406 2406 }
2407 2407
2408 2408 div.browserblock .browser-branch {
2409 2409 float: left;
2410 2410 }
2411 2411
2412 2412 div.browserblock .browser-branch label {
2413 2413 color: #4A4A4A;
2414 2414 vertical-align: text-top;
2415 2415 padding-right: 2px;
2416 2416 }
2417 2417
2418 2418 div.browserblock .browser-header span {
2419 2419 margin-left: 5px;
2420 2420 font-weight: 700;
2421 2421 }
2422 2422
2423 2423 div.browserblock .browser-search {
2424 2424 clear: both;
2425 2425 padding: 8px 8px 0px 5px;
2426 2426 height: 20px;
2427 2427 }
2428 2428
2429 2429 div.browserblock #node_filter_box {
2430 2430 }
2431 2431
2432 2432 div.browserblock .search_activate {
2433 2433 float: left
2434 2434 }
2435 2435
2436 2436 div.browserblock .add_node {
2437 2437 float: left;
2438 2438 padding-left: 5px;
2439 2439 }
2440 2440
2441 2441 div.browserblock .search_activate a:hover,
2442 2442 div.browserblock .add_node a:hover {
2443 2443 text-decoration: none !important;
2444 2444 }
2445 2445
2446 2446 div.browserblock .browser-body {
2447 2447 background: #EEE;
2448 2448 border-top: 1px solid #CCC;
2449 2449 }
2450 2450
2451 2451 table.code-browser {
2452 2452 border-collapse: collapse;
2453 2453 width: 100%;
2454 2454 }
2455 2455
2456 2456 table.code-browser tr {
2457 2457 margin: 3px;
2458 2458 }
2459 2459
2460 2460 table.code-browser thead th {
2461 2461 background-color: #EEE;
2462 2462 height: 20px;
2463 2463 font-size: 1.1em;
2464 2464 font-weight: 700;
2465 2465 text-align: left;
2466 2466 padding-left: 10px;
2467 2467 }
2468 2468
2469 2469 table.code-browser tbody td {
2470 2470 padding-left: 10px;
2471 2471 height: 20px;
2472 2472 }
2473 2473
2474 2474 table.code-browser .browser-file {
2475 2475 height: 16px;
2476 2476 padding-left: 5px;
2477 2477 text-align: left;
2478 2478 }
2479 2479 .diffblock .changeset_header {
2480 2480 height: 16px;
2481 2481 }
2482 2482 .diffblock .changeset_file {
2483 2483 float: left;
2484 2484 }
2485 2485 .diffblock .diff-menu-wrapper {
2486 2486 float: left;
2487 2487 }
2488 2488
2489 2489 .diffblock .diff-menu {
2490 2490 position: absolute;
2491 2491 background: none repeat scroll 0 0 #FFFFFF;
2492 2492 border-color: #577632 #666666 #666666;
2493 2493 border-right: 1px solid #666666;
2494 2494 border-style: solid solid solid;
2495 2495 border-width: 1px;
2496 2496 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
2497 2497 margin-top: 5px;
2498 2498 margin-left: 1px;
2499 2499
2500 2500 }
2501 2501 .diffblock .diff-actions {
2502 2502 float: left;
2503 2503 }
2504 2504 .diffblock .diff-actions span.no-file,
2505 2505 .diffblock .diff-actions span.arrow {
2506 2506 opacity: 0.5;
2507 2507 }
2508 2508 .diffblock .diff-actions span.arrow {
2509 2509 margin: 0 -3px;
2510 2510 }
2511 2511 .diffblock .diff-actions a i {
2512 2512 margin: 0 2px;
2513 2513 }
2514 2514 .diffblock.twoway {
2515 2515 overflow: visible;
2516 2516 }
2517 2517 .diffblock.twoway .diff-actions {
2518 2518 padding-top: 0;
2519 2519 }
2520 2520 .diffblock.twoway .diff-actions label input {
2521 2521 margin: -5px 5px 0 10px;
2522 2522 position: relative;
2523 2523 top: 3px;
2524 2524 }
2525 2525 .diffblock .diff-menu ul li {
2526 2526 padding: 0px 0px 0px 0px !important;
2527 2527 }
2528 2528 .diffblock .diff-menu ul li a {
2529 2529 display: block;
2530 2530 padding: 3px 8px 3px 8px !important;
2531 2531 }
2532 2532 .diffblock .diff-menu ul li a:hover {
2533 2533 text-decoration: none;
2534 2534 background-color: #EEEEEE;
2535 2535 }
2536 2536 table.code-browser .browser-dir {
2537 2537 height: 16px;
2538 2538 padding-left: 5px;
2539 2539 text-align: left;
2540 2540 }
2541 2541
2542 2542 table.code-browser .submodule-dir {
2543 2543 height: 16px;
2544 2544 padding-left: 5px;
2545 2545 text-align: left;
2546 2546 }
2547 2547
2548 2548 /* add some padding to the right of the file, folder, or submodule icon and
2549 2549 before the text */
2550 2550 table.code-browser i[class^='icon-'] {
2551 2551 padding-right: .3em;
2552 2552 }
2553 2553
2554 2554 .panel .search {
2555 2555 clear: both;
2556 2556 overflow: hidden;
2557 2557 margin: 0;
2558 2558 padding: 0 20px 10px;
2559 2559 }
2560 2560
2561 2561 .panel .search div.search_path {
2562 2562 background: none repeat scroll 0 0 #EEE;
2563 2563 border: 1px solid #CCC;
2564 2564 color: blue;
2565 2565 margin-bottom: 10px;
2566 2566 padding: 10px 0;
2567 2567 }
2568 2568
2569 2569 .panel .search div.search_path div.link {
2570 2570 font-weight: 700;
2571 2571 margin-left: 25px;
2572 2572 }
2573 2573
2574 2574 .panel .search div.search_path div.link a {
2575 2575 color: #577632;
2576 2576 cursor: pointer;
2577 2577 text-decoration: none;
2578 2578 }
2579 2579
2580 2580 #path_unlock {
2581 2581 color: red;
2582 2582 font-size: 1.2em;
2583 2583 padding-left: 4px;
2584 2584 }
2585 2585
2586 2586 .info_box span {
2587 2587 margin-left: 3px;
2588 2588 margin-right: 3px;
2589 2589 }
2590 2590
2591 2591 .info_box .rev {
2592 2592 color: #577632;
2593 2593 font-size: 1.6em;
2594 2594 font-weight: bold;
2595 2595 vertical-align: sub;
2596 2596 }
2597 2597
2598 2598 .info_box input#at_rev,
2599 2599 .info_box input#size {
2600 2600 background: #FFF;
2601 2601 border-top: 1px solid #b3b3b3;
2602 2602 border-left: 1px solid #b3b3b3;
2603 2603 border-right: 1px solid #eaeaea;
2604 2604 border-bottom: 1px solid #eaeaea;
2605 2605 color: #000;
2606 2606 font-size: 12px;
2607 2607 margin: 0;
2608 2608 padding: 1px 5px 1px;
2609 2609 }
2610 2610
2611 2611 .info_box input#view {
2612 2612 text-align: center;
2613 2613 padding: 4px 3px 2px 2px;
2614 2614 }
2615 2615
2616 2616 .info_box_elem {
2617 2617 display: inline-block;
2618 2618 padding: 0 2px;
2619 2619 }
2620 2620
2621 2621 .yui-overlay, .yui-panel-container {
2622 2622 visibility: hidden;
2623 2623 position: absolute;
2624 2624 z-index: 2;
2625 2625 }
2626 2626
2627 #tip-box {
2628 position: absolute;
2629
2630 background-color: #FFF;
2631 border: 2px solid #577632;
2632 font: 100% sans-serif;
2633 width: auto;
2634 opacity: 1;
2635 padding: 8px;
2636
2637 white-space: pre-wrap;
2638 border-radius: 8px 8px 8px 8px;
2639 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2640 z-index: 100000;
2641 }
2642
2643 .hl-tip-box {
2644 z-index: 1;
2645 position: absolute;
2646 color: #666;
2647 background-color: #FFF;
2648 border: 2px solid #577632;
2649 font: 100% sans-serif;
2650 width: auto;
2651 opacity: 1;
2652 padding: 8px;
2653 white-space: pre-wrap;
2654 border-radius: 8px 8px 8px 8px;
2655 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2656 }
2657
2658 2627
2659 2628 .mentions-container {
2660 2629 width: 90% !important;
2661 2630 }
2662 2631 .mentions-container .yui-ac-content {
2663 2632 width: 100% !important;
2664 2633 }
2665 2634
2666 2635 .ac {
2667 2636 vertical-align: top;
2668 2637 }
2669 2638
2670 2639 .ac .yui-ac {
2671 2640 position: inherit;
2672 2641 font-size: 100%;
2673 2642 }
2674 2643
2675 2644 .ac .perm_ac {
2676 2645 width: 20em;
2677 2646 }
2678 2647
2679 2648 .ac .yui-ac-input {
2680 2649 width: 100%;
2681 2650 }
2682 2651
2683 2652 .ac .yui-ac-container {
2684 2653 position: absolute;
2685 2654 top: 1.6em;
2686 2655 width: auto;
2687 2656 }
2688 2657
2689 2658 .ac .yui-ac-content {
2690 2659 position: absolute;
2691 2660 border: 1px solid gray;
2692 2661 background: #fff;
2693 2662 z-index: 9050;
2694 2663 }
2695 2664
2696 2665 .ac .yui-ac-shadow {
2697 2666 position: absolute;
2698 2667 width: 100%;
2699 2668 background: #000;
2700 2669 opacity: .10;
2701 2670 z-index: 9049;
2702 2671 margin: .3em;
2703 2672 }
2704 2673
2705 2674 .ac .yui-ac-content ul {
2706 2675 width: 100%;
2707 2676 margin: 0;
2708 2677 padding: 0;
2709 2678 z-index: 9050;
2710 2679 }
2711 2680
2712 2681 .ac .yui-ac-content li {
2713 2682 cursor: default;
2714 2683 white-space: nowrap;
2715 2684 margin: 0;
2716 2685 padding: 2px 5px;
2717 2686 height: 18px;
2718 2687 z-index: 9050;
2719 2688 display: block;
2720 2689 width: auto !important;
2721 2690 }
2722 2691
2723 2692 .ac .yui-ac-content li .ac-container-wrap {
2724 2693 width: auto;
2725 2694 }
2726 2695
2727 2696 .ac .yui-ac-content li.yui-ac-prehighlight {
2728 2697 background: #B3D4FF;
2729 2698 z-index: 9050;
2730 2699 }
2731 2700
2732 2701 .ac .yui-ac-content li.yui-ac-highlight {
2733 2702 background: #556CB5;
2734 2703 color: #FFF;
2735 2704 z-index: 9050;
2736 2705 }
2737 2706 .ac .yui-ac-bd {
2738 2707 z-index: 9050;
2739 2708 }
2740 2709
2741 2710 #repo_size {
2742 2711 display: block;
2743 2712 margin-top: 4px;
2744 2713 color: #666;
2745 2714 float: right;
2746 2715 }
2747 2716
2748 2717 .currently_following {
2749 2718 padding-left: 10px;
2750 2719 padding-bottom: 5px;
2751 2720 }
2752 2721
2753 2722 #switch_repos {
2754 2723 position: absolute;
2755 2724 height: 25px;
2756 2725 z-index: 1;
2757 2726 }
2758 2727
2759 2728 #switch_repos select {
2760 2729 min-width: 150px;
2761 2730 max-height: 250px;
2762 2731 z-index: 1;
2763 2732 }
2764 2733
2765 2734 .breadcrumbs {
2766 2735 border: medium none;
2767 2736 color: #FFF;
2768 2737 font-weight: 700;
2769 2738 font-size: 14px;
2770 2739 }
2771 2740
2772 2741 .breadcrumbs .hash {
2773 2742 text-transform: none;
2774 2743 color: #fff;
2775 2744 }
2776 2745
2777 2746 .flash_msg {
2778 2747 }
2779 2748
2780 2749 .flash_msg ul {
2781 2750 }
2782 2751
2783 2752 .error_red {
2784 2753 color: red;
2785 2754 }
2786 2755
2787 2756 .flash_msg .alert-error {
2788 2757 background-color: #c43c35;
2789 2758 background-repeat: repeat-x;
2790 2759 background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
2791 2760 border-color: #c43c35 #c43c35 #882a25;
2792 2761 }
2793 2762
2794 2763 .flash_msg .alert-error a {
2795 2764 text-decoration: underline;
2796 2765 }
2797 2766
2798 2767 .flash_msg .alert-warning {
2799 2768 color: #404040 !important;
2800 2769 background-color: #eedc94;
2801 2770 background-repeat: repeat-x;
2802 2771 background-image: linear-gradient(to bottom, #fceec1, #eedc94);
2803 2772 border-color: #eedc94 #eedc94 #e4c652;
2804 2773 }
2805 2774
2806 2775 .flash_msg .alert-warning a {
2807 2776 text-decoration: underline;
2808 2777 }
2809 2778
2810 2779 .flash_msg .alert-success {
2811 2780 background-color: #57a957;
2812 2781 background-repeat: repeat-x !important;
2813 2782 background-image: linear-gradient(to bottom, #62c462, #57a957);
2814 2783 border-color: #57a957 #57a957 #3d773d;
2815 2784 }
2816 2785
2817 2786 .flash_msg .alert-success a {
2818 2787 text-decoration: underline;
2819 2788 color: #FFF !important;
2820 2789 }
2821 2790
2822 2791 .flash_msg .alert-info {
2823 2792 background-color: #339bb9;
2824 2793 background-repeat: repeat-x;
2825 2794 background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
2826 2795 border-color: #339bb9 #339bb9 #22697d;
2827 2796 }
2828 2797
2829 2798 .flash_msg .alert-info a {
2830 2799 text-decoration: underline;
2831 2800 }
2832 2801
2833 2802 .flash_msg .alert-error,
2834 2803 .flash_msg .alert-warning,
2835 2804 .flash_msg .alert-success,
2836 2805 .flash_msg .alert-info {
2837 2806 font-size: 12px;
2838 2807 font-weight: 700;
2839 2808 min-height: 14px;
2840 2809 line-height: 14px;
2841 2810 margin-bottom: 10px;
2842 2811 margin-top: 0;
2843 2812 display: block;
2844 2813 overflow: auto;
2845 2814 padding: 6px 10px 6px 10px;
2846 2815 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2847 2816 position: relative;
2848 2817 color: #FFF;
2849 2818 border-width: 1px;
2850 2819 border-style: solid;
2851 2820 border-radius: 4px;
2852 2821 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
2853 2822 }
2854 2823
2855 2824 div#legend_data {
2856 2825 padding-left: 10px;
2857 2826 }
2858 2827 div#legend_container table {
2859 2828 border: none !important;
2860 2829 }
2861 2830 div#legend_container table,
2862 2831 div#legend_choices table {
2863 2832 width: auto !important;
2864 2833 }
2865 2834
2866 2835 table#permissions_manage span.private_repo_msg {
2867 2836 font-size: 0.8em;
2868 2837 opacity: 0.6;
2869 2838 }
2870 2839
2871 2840 table#permissions_manage td.private_repo_msg {
2872 2841 font-size: 0.8em;
2873 2842 }
2874 2843
2875 2844 table#permissions_manage tr#add_perm_input td {
2876 2845 vertical-align: middle;
2877 2846 }
2878 2847
2879 2848 div.gravatar {
2880 2849 float: left;
2881 2850 background-color: #FFF;
2882 2851 margin-right: 0.7em;
2883 2852 padding: 1px 1px 1px 1px;
2884 2853 line-height: 0;
2885 2854 border-radius: 3px;
2886 2855 }
2887 2856
2888 2857 div.gravatar img {
2889 2858 border-radius: 2px;
2890 2859 }
2891 2860
2892 2861 nav.navbar, #content, #footer {
2893 2862 min-width: 978px;
2894 2863 }
2895 2864
2896 2865 #content {
2897 2866 clear: both;
2898 2867 padding: 10px 10px 14px 10px;
2899 2868 }
2900 2869
2901 2870 #content.hover {
2902 2871 padding: 55px 10px 14px 10px !important;
2903 2872 }
2904 2873
2905 2874 #content div.panel div.panel-heading div.search {
2906 2875 border-left: 1px solid #576622;
2907 2876 }
2908 2877
2909 2878 #content div.panel div.panel-heading div.search > div input {
2910 2879 border: 1px solid #576622;
2911 2880 }
2912 2881
2913 2882 .label,
2914 2883 .btn {
2915 2884 color: #515151 !important;
2916 2885 background-color: #DADADA;
2917 2886 background-repeat: repeat-x;
2918 2887 background-image: linear-gradient(to bottom, #F4F4F4, #DADADA);
2919 2888
2920 2889 border-top: 1px solid #DDD;
2921 2890 border-left: 1px solid #c6c6c6;
2922 2891 border-right: 1px solid #DDD;
2923 2892 border-bottom: 1px solid #c6c6c6;
2924 2893 outline: none;
2925 2894 margin: 0 3px 0 0;
2926 2895 border-radius: 4px 4px 4px 4px !important;
2927 2896 padding: 3px 3px 3px 3px;
2928 2897 display: inline-block;
2929 2898 white-space: nowrap;
2930 2899 }
2931 2900 .btn {
2932 2901 cursor: pointer !important;
2933 2902 }
2934 2903 .label {
2935 2904 cursor: default !important;
2936 2905 }
2937 2906
2938 2907 .panel-body.settings > ul.nav-stacked {
2939 2908 float: left;
2940 2909 width: 150px;
2941 2910 margin: 20px;
2942 2911 color: #393939;
2943 2912 font-weight: 700;
2944 2913 }
2945 2914
2946 2915 .panel-body.settings > ul.nav-stacked a {
2947 2916 color: inherit;
2948 2917 }
2949 2918
2950 2919 .panel-body.settings > ul.nav-stacked li.active {
2951 2920 list-style-type: disc
2952 2921 }
2953 2922
2954 2923 .panel-body.settings > div,
2955 2924 .panel-body.settings > form {
2956 2925 float: left;
2957 2926 width: 750px;
2958 2927 margin: 10px 0 0 0;
2959 2928 border-left: 1px solid #DDDDDD;
2960 2929 }
2961 2930
2962 2931 .panel-body > div,
2963 2932 .panel-body > form {
2964 2933 padding: 0 20px 10px;
2965 2934 }
2966 2935
2967 2936 /* make .btn inputs and buttons and divs look the same */
2968 2937 button.btn,
2969 2938 input.btn {
2970 2939 font-family: inherit;
2971 2940 font-size: inherit;
2972 2941 line-height: inherit;
2973 2942 }
2974 2943
2975 2944 .btn::-moz-focus-inner {
2976 2945 border: 0;
2977 2946 padding: 0;
2978 2947 }
2979 2948
2980 2949 .btn.badge {
2981 2950 cursor: default !important;
2982 2951 }
2983 2952
2984 2953 input[disabled].btn,
2985 2954 .btn.disabled {
2986 color: #999;
2955 opacity: 0.5;
2987 2956 }
2988 2957
2989 2958 .label,
2990 2959 .btn.btn-sm {
2991 2960 padding: 3px 8px;
2992 2961 }
2993 2962
2994 2963 .btn.btn-xs {
2995 2964 padding: 1px 5px;
2996 2965 }
2997 2966
2998 2967 .btn:focus {
2999 2968 outline: none;
3000 2969 }
3001 2970 .btn:hover {
3002 2971 text-decoration: none;
3003 2972 color: #515151;
3004 2973 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 3px #FFFFFF !important;
3005 2974 }
3006 2975 .btn.badge:hover {
3007 2976 box-shadow: none !important;
3008 2977 }
3009 2978 .btn.disabled:hover {
3010 2979 background-position: 0;
3011 2980 color: #999;
3012 2981 text-decoration: none;
3013 2982 box-shadow: none !important;
3014 2983 }
3015 2984
3016 2985 .label.label-danger,
3017 2986 .btn.btn-danger {
3018 2987 color: #fff !important;
3019 2988 background-color: #c43c35;
3020 2989 background-repeat: repeat-x;
3021 2990 background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
3022 2991 border-color: #c43c35 #c43c35 #882a25;
3023 2992 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3024 2993 }
3025 2994
3026 2995 .label.label-primary,
3027 2996 .btn.btn-primary {
3028 2997 color: #fff !important;
3029 2998 background-color: #339bb9;
3030 2999 background-repeat: repeat-x;
3031 3000 background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
3032 3001 border-color: #339bb9 #339bb9 #22697d;
3033 3002 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3034 3003 }
3035 3004
3036 3005 .label.label-success,
3037 3006 .btn.btn-success {
3038 3007 color: #fff !important;
3039 3008 background-color: #57a957;
3040 3009 background-repeat: repeat-x;
3041 3010 background-image: linear-gradient(to bottom, #62c462, #57a957);
3042 3011 border-color: #57a957 #57a957 #3d773d;
3043 3012 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3044 3013 }
3045 3014
3046 3015 .label.label-warning,
3047 3016 .btn.btn-warning {
3048 3017 color: #fff !important;
3049 3018 background-color: #faa732;
3050 3019 background-repeat: repeat-x;
3051 3020 background-image: linear-gradient(to bottom, #fbb450, #f89406);
3052 3021 border-color: #f89406 #f89406 #ad6704;
3053 3022 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3054 3023 }
3055 3024
3056 3025 label.disabled {
3057 3026 color: #aaa !important;
3058 3027 }
3059 3028
3060 3029 .btn.active {
3061 3030 font-weight: bold;
3062 3031 }
3063 3032
3064 3033 .alert {
3065 3034 padding: 15px;
3066 3035 margin: 20px 0;
3067 3036 border: 1px solid transparent;
3068 3037 border-radius: 4px;
3069 3038 }
3070 3039
3071 3040 .alert-success {
3072 3041 color: #3c763d;
3073 3042 background-color: #dff0d8;
3074 3043 border-color: #d6e9c6;
3075 3044 }
3076 3045
3077 3046 .alert-info {
3078 3047 color: #31708f;
3079 3048 background-color: #d9edf7;
3080 3049 border-color: #bce8f1;
3081 3050 }
3082 3051
3083 3052 .alert-warning {
3084 3053 color: #8a6d3b;
3085 3054 background-color: #fcf8e3;
3086 3055 border-color: #faebcc;
3087 3056 }
3088 3057
3089 3058 .alert-danger {
3090 3059 color: #a94442;
3091 3060 background-color: #f2dede;
3092 3061 border-color: #ebccd1;
3093 3062 }
3094 3063
3095 3064 ins, div.options a:hover {
3096 3065 text-decoration: none;
3097 3066 }
3098 3067
3099 3068 img,
3100 3069 nav.navbar #quick li a:hover span.normal,
3101 3070 #clone_url,
3102 3071 #clone_url_id
3103 3072 {
3104 3073 border: none;
3105 3074 }
3106 3075
3107 3076 img.icon, .right .merge img {
3108 3077 vertical-align: bottom;
3109 3078 }
3110 3079
3111 3080 nav.navbar ul#logged-user,
3112 3081 #content div.panel div.panel-heading ul.links,
3113 3082 #content div.panel div.message div.dismiss,
3114 3083 #content div.panel div.traffic div.legend ul {
3115 3084 float: right;
3116 3085 margin: 0;
3117 3086 padding: 0;
3118 3087 }
3119 3088
3120 3089 nav.navbar #home,
3121 3090 nav.navbar #logo,
3122 3091 #content div.panel ul.left,
3123 3092 #content div.panel ol.left,
3124 3093 div#commit_history,
3125 3094 div#legend_data, div#legend_container, div#legend_choices {
3126 3095 float: left;
3127 3096 }
3128 3097
3129 3098 nav.navbar #quick li .dropdown-menu,
3130 3099 #content #left #menu ul.closed,
3131 3100 #content #left #menu li ul.collapsed,
3132 3101 .yui-tt-shadow {
3133 3102 display: none;
3134 3103 }
3135 3104
3136 3105 .dropdown.open .dropdown-menu,
3137 3106 #content #left #menu ul.opened,
3138 3107 #content #left #menu li ul.expanded {
3139 3108 display: block !important;
3140 3109 }
3141 3110
3142 3111 .caret:after {
3143 3112 font-family: 'kallithea';
3144 3113 content: ' \23f7'; /* triangle-down */
3145 3114 }
3146 3115
3147 3116 .branch-switcher .select2-choice,
3148 3117 .repo-switcher .select2-choice {
3149 3118 padding: 0px 8px 1px !important;
3150 3119 display: block;
3151 3120 height: 100%;
3152 3121 }
3153 3122
3154 3123 .branch-switcher .select2-container,
3155 3124 .branch-switcher .select2-choice,
3156 3125 .branch-switcher .select2-choice span,
3157 3126 .repo-switcher .select2-container,
3158 3127 .repo-switcher .select2-choice,
3159 3128 .repo-switcher .select2-choice span {
3160 3129 background: transparent !important;
3161 3130 border: 0 !important;
3162 3131 box-shadow: none !important;
3163 3132 color: #FFFFFF !important;
3164 3133 }
3165 3134
3166 3135 .branch-switcher .select2-arrow,
3167 3136 .repo-switcher .select2-arrow {
3168 3137 display: none !important;
3169 3138 }
3170 3139
3171 3140 .branch-switcher-dropdown.select2-drop.select2-drop-active,
3172 3141 .repo-switcher-dropdown.select2-drop.select2-drop-active {
3173 3142 box-shadow: none;
3174 3143 color: #fff;
3175 3144 background-color: #576622;
3176 3145 }
3177 3146
3178 3147 .branch-switcher-dropdown.select2-drop.select2-drop-active .select2-results .select2-highlighted,
3179 3148 .repo-switcher-dropdown.select2-drop.select2-drop-active .select2-results .select2-highlighted {
3180 3149 background-color: #6388ad;
3181 3150 }
3182 3151
3183 3152 #content div.graph {
3184 3153 padding: 0 10px 10px;
3185 3154 height: 450px;
3186 3155 }
3187 3156
3188 3157 #content div.panel ol.lower-roman,
3189 3158 #content div.panel ol.upper-roman,
3190 3159 #content div.panel ol.lower-alpha,
3191 3160 #content div.panel ol.upper-alpha,
3192 3161 #content div.panel ol.decimal {
3193 3162 margin: 10px 24px 10px 44px;
3194 3163 }
3195 3164
3196 3165 #content div.panel div.form div.form-horizontal div.form-group > div input.error,
3197 3166 #login div.form div.form-horizontal div.form-group > div input.error,
3198 3167 #register div.form div.form-horizontal div.form-group > div input.error {
3199 3168 background: #FBE3E4;
3200 3169 border-top: 1px solid #e1b2b3;
3201 3170 border-left: 1px solid #e1b2b3;
3202 3171 border-right: 1px solid #FBC2C4;
3203 3172 border-bottom: 1px solid #FBC2C4;
3204 3173 }
3205 3174
3206 3175 #content div.panel div.form div.form-horizontal div.form-group > div input.success,
3207 3176 #login div.form div.form-horizontal div.form-group > div input.success,
3208 3177 #register div.form div.form-horizontal div.form-group > div input.success {
3209 3178 background: #E6EFC2;
3210 3179 border-top: 1px solid #cebb98;
3211 3180 border-left: 1px solid #cebb98;
3212 3181 border-right: 1px solid #c6d880;
3213 3182 border-bottom: 1px solid #c6d880;
3214 3183 }
3215 3184
3216 3185 #content div.panel div.form div.form-horizontal div.form-group > div select,
3217 3186 #content div.panel table th.selected input,
3218 3187 #content div.panel table td.selected input {
3219 3188 margin: 0;
3220 3189 }
3221 3190
3222 3191 #content div.panel div.form div.form-horizontal div.form-group > div {
3223 3192 margin: 10px 20px 10px 200px;
3224 3193 padding: 0;
3225 3194 }
3226 3195
3227 3196 #content div.panel div.form div.form-horizontal div.form-group > div a:hover,
3228 3197 #content div.panel div.form div.form-horizontal div.form-group > div a.ui-selectmenu:hover,
3229 3198 #content div.panel div.action a:hover {
3230 3199 color: #000;
3231 3200 text-decoration: none;
3232 3201 }
3233 3202
3234 3203 #content div.panel div.form div.form-horizontal div.form-group > div a.ui-selectmenu-focus,
3235 3204 #content div.panel div.action a.ui-selectmenu-focus {
3236 3205 border: 1px solid #666;
3237 3206 }
3238 3207
3239 3208 #content div.panel div.form div.form-horizontal div.form-group > div div.checkbox {
3240 3209 clear: both;
3241 3210 min-height: 12px;
3242 3211 padding: 0;
3243 3212 margin: 14px 0 10px;
3244 3213 }
3245 3214
3246 3215 #content div.panel div.form div.form-horizontal div.form-group > div div.checkbox input {
3247 3216 float: left;
3248 3217 margin: -4px 5px 0px 0px;
3249 3218 }
3250 3219
3251 3220 div.form div.form-horizontal div.form-group div.button input,
3252 3221 #content div.panel div.form div.form-horizontal div.buttons input,
3253 3222 div.form div.form-horizontal div.buttons input,
3254 3223 #content div.panel div.action div.button input {
3255 3224 font-size: 11px;
3256 3225 font-weight: 700;
3257 3226 margin: 0;
3258 3227 }
3259 3228
3260 3229 div.form div.form-horizontal div.form-group div.highlight,
3261 3230 #content div.panel div.form div.form-horizontal div.buttons div.highlight {
3262 3231 display: inline;
3263 3232 }
3264 3233
3265 3234 #content div.panel div.form div.form-horizontal div.buttons,
3266 3235 div.form div.form-horizontal div.buttons {
3267 3236 margin: 10px 10px 0 200px;
3268 3237 padding: 0;
3269 3238 }
3270 3239
3271 3240 #content div.panel table td.user,
3272 3241 #content div.panel table td.address {
3273 3242 width: 10%;
3274 3243 text-align: center;
3275 3244 }
3276 3245
3277 3246 #content div.panel div.action div.button,
3278 3247 #login div.form div.form-horizontal div.form-group > div div.link,
3279 3248 #register div.form div.form-horizontal div.form-group > div div.link {
3280 3249 text-align: right;
3281 3250 margin: 6px 0 0;
3282 3251 padding: 0;
3283 3252 }
3284 3253
3285 3254 #login, #register {
3286 3255 width: 520px;
3287 3256 margin: 10% auto 0;
3288 3257 padding: 0;
3289 3258 }
3290 3259
3291 3260 #login div.color,
3292 3261 #register div.color {
3293 3262 clear: both;
3294 3263 overflow: hidden;
3295 3264 background: #FFF;
3296 3265 margin: 10px auto 0;
3297 3266 padding: 3px 3px 3px 0;
3298 3267 }
3299 3268
3300 3269 #login div.color a,
3301 3270 #register div.color a {
3302 3271 width: 20px;
3303 3272 height: 20px;
3304 3273 display: block;
3305 3274 float: left;
3306 3275 margin: 0 0 0 3px;
3307 3276 padding: 0;
3308 3277 }
3309 3278
3310 3279 #login div.panel-heading h5,
3311 3280 #register div.panel-heading h5 {
3312 3281 color: #fff;
3313 3282 margin: 10px;
3314 3283 padding: 0;
3315 3284 }
3316 3285
3317 3286 #login div.form div.form-horizontal div.form-group,
3318 3287 #register div.form div.form-horizontal div.form-group {
3319 3288 clear: both;
3320 3289 overflow: hidden;
3321 3290 margin: 0;
3322 3291 padding: 0 0 10px;
3323 3292 }
3324 3293
3325 3294 #login div.form div.form-horizontal div.form-group span.error-message,
3326 3295 #register div.form div.form-horizontal div.form-group span.error-message {
3327 3296 height: 1%;
3328 3297 display: block;
3329 3298 color: red;
3330 3299 margin: 8px 0 0;
3331 3300 padding: 0;
3332 3301 max-width: 320px;
3333 3302 }
3334 3303
3335 3304 #login div.form div.form-horizontal div.form-group label,
3336 3305 #register div.form div.form-horizontal div.form-group > label {
3337 3306 color: #000;
3338 3307 font-weight: 700;
3339 3308 }
3340 3309
3341 3310 #login div.form div.form-horizontal div.form-group div,
3342 3311 #register div.form div.form-horizontal div.form-group > div {
3343 3312 float: left;
3344 3313 margin: 0;
3345 3314 padding: 0;
3346 3315 }
3347 3316
3348 3317 #login div.form div.form-horizontal div.form-group div.checkbox,
3349 3318 #register div.form div.form-horizontal div.form-group div.checkbox {
3350 3319 margin: 0 0 0 184px;
3351 3320 padding: 0;
3352 3321 }
3353 3322
3354 3323 #login div.form div.form-horizontal div.form-group div.checkbox label,
3355 3324 #register div.form div.form-horizontal div.form-group div.checkbox label {
3356 3325 color: #565656;
3357 3326 font-weight: 700;
3358 3327 }
3359 3328
3360 3329 #login div.form div.buttons input,
3361 3330 #register div.form div.form-horizontal div.buttons input {
3362 3331 color: #000;
3363 3332 font-size: 1em;
3364 3333 font-weight: 700;
3365 3334 margin: 0;
3366 3335 }
3367 3336
3368 3337 #changeset_content .container .wrapper,
3369 3338 #graph_content .container .wrapper {
3370 3339 width: 600px;
3371 3340 }
3372 3341
3373 3342 #changeset_content .container .date,
3374 3343 .ac .match {
3375 3344 font-weight: 700;
3376 3345 padding-top: 5px;
3377 3346 padding-bottom: 5px;
3378 3347 }
3379 3348
3380 3349 div#legend_container table td,
3381 3350 div#legend_choices table td {
3382 3351 border: none !important;
3383 3352 height: 20px !important;
3384 3353 padding: 0 !important;
3385 3354 }
3386 3355
3387 3356 .q_filter_box {
3388 3357 border-radius: 4px;
3389 3358 border: 0 none;
3390 3359 margin-bottom: -4px;
3391 3360 margin-top: -4px;
3392 3361 padding-left: 3px;
3393 3362 }
3394 3363
3395 3364 #node_filter {
3396 3365 border: 0px solid #545454;
3397 3366 color: #AAAAAA;
3398 3367 padding-left: 3px;
3399 3368 }
3400 3369
3401 3370 .reviewer_status {
3402 3371 float: left;
3403 3372 }
3404 3373
3405 3374 .reviewers_member {
3406 3375 height: 15px;
3407 3376 padding: 0px 0px 0px 10px;
3408 3377 }
3409 3378
3410 3379 .emails_wrap .email_entry {
3411 3380 height: 30px;
3412 3381 padding: 0px 0px 0px 10px;
3413 3382 }
3414 3383 .emails_wrap .email_entry .email {
3415 3384 float: left
3416 3385 }
3417 3386 .emails_wrap .email_entry .email_action {
3418 3387 float: left
3419 3388 }
3420 3389
3421 3390 .ips_wrap .ip_entry {
3422 3391 height: 30px;
3423 3392 padding: 0px 0px 0px 10px;
3424 3393 }
3425 3394 .ips_wrap .ip_entry .ip {
3426 3395 float: left
3427 3396 }
3428 3397 .ips_wrap .ip_entry .ip_action {
3429 3398 float: left
3430 3399 }
3431 3400
3432 3401
3433 3402 /*README STYLE*/
3434 3403
3435 3404 div.readme {
3436 3405 padding: 0px;
3437 3406 }
3438 3407
3439 3408 div.readme h2 {
3440 3409 font-weight: normal;
3441 3410 }
3442 3411
3443 3412 div.readme .readme_box {
3444 3413 background-color: #fafafa;
3445 3414 }
3446 3415
3447 3416 div.readme .readme_box {
3448 3417 clear: both;
3449 3418 overflow: hidden;
3450 3419 margin: 0;
3451 3420 padding: 0 20px 10px;
3452 3421 }
3453 3422
3454 3423 div.readme .readme_box h1,
3455 3424 div.readme .readme_box h2,
3456 3425 div.readme .readme_box h3,
3457 3426 div.readme .readme_box h4,
3458 3427 div.readme .readme_box h5,
3459 3428 div.readme .readme_box h6 {
3460 3429 border-bottom: 0 !important;
3461 3430 margin: 0 !important;
3462 3431 padding: 0 !important;
3463 3432 line-height: 1.5em !important;
3464 3433 }
3465 3434
3466 3435
3467 3436 div.readme .readme_box h1:first-child {
3468 3437 padding-top: .25em !important;
3469 3438 }
3470 3439
3471 3440 div.readme .readme_box h2,
3472 3441 div.readme .readme_box h3 {
3473 3442 margin: 1em 0 !important;
3474 3443 }
3475 3444
3476 3445 div.readme .readme_box h2 {
3477 3446 margin-top: 1.5em !important;
3478 3447 border-top: 4px solid #e0e0e0 !important;
3479 3448 padding-top: .5em !important;
3480 3449 }
3481 3450
3482 3451 div.readme .readme_box p {
3483 3452 color: black !important;
3484 3453 margin: 1em 0 !important;
3485 3454 line-height: 1.5em !important;
3486 3455 }
3487 3456
3488 3457 div.readme .readme_box ul {
3489 3458 list-style: disc !important;
3490 3459 margin: 1em 0 1em 2em !important;
3491 3460 }
3492 3461
3493 3462 div.readme .readme_box ol {
3494 3463 list-style: decimal;
3495 3464 margin: 1em 0 1em 2em !important;
3496 3465 }
3497 3466
3498 3467 div.readme .readme_box code {
3499 3468 font-size: 12px !important;
3500 3469 background-color: ghostWhite !important;
3501 3470 color: #444 !important;
3502 3471 padding: 0 .2em !important;
3503 3472 border: 1px solid #dedede !important;
3504 3473 }
3505 3474
3506 3475 div.readme .readme_box pre code {
3507 3476 padding: 0 !important;
3508 3477 font-size: 12px !important;
3509 3478 background-color: #eee !important;
3510 3479 border: none !important;
3511 3480 }
3512 3481
3513 3482 div.readme .readme_box pre {
3514 3483 margin: 1em 0;
3515 3484 font-size: 12px;
3516 3485 background-color: #eee;
3517 3486 border: 1px solid #ddd;
3518 3487 padding: 5px;
3519 3488 color: #444;
3520 3489 overflow: auto;
3521 3490 border-radius: 3px;
3522 3491 }
3523 3492
3524 3493 div.readme .readme_box table {
3525 3494 display: table;
3526 3495 border-collapse: separate;
3527 3496 border-spacing: 2px;
3528 3497 border-color: gray;
3529 3498 width: auto !important;
3530 3499 }
3531 3500
3532 3501
3533 3502 /** RST STYLE **/
3534 3503
3535 3504
3536 3505 div.rst-block {
3537 3506 padding: 0px;
3538 3507 }
3539 3508
3540 3509 div.rst-block h2 {
3541 3510 font-weight: normal;
3542 3511 }
3543 3512
3544 3513 div.rst-block {
3545 3514 background-color: #fafafa;
3546 3515 }
3547 3516
3548 3517 div.rst-block {
3549 3518 clear: both;
3550 3519 overflow: hidden;
3551 3520 margin: 0;
3552 3521 padding: 0 20px;
3553 3522 }
3554 3523
3555 3524 div.rst-block h1,
3556 3525 div.rst-block h2,
3557 3526 div.rst-block h3,
3558 3527 div.rst-block h4,
3559 3528 div.rst-block h5,
3560 3529 div.rst-block h6 {
3561 3530 border-bottom: 0 !important;
3562 3531 margin: 0 !important;
3563 3532 padding: 0 !important;
3564 3533 line-height: 1.5em !important;
3565 3534 }
3566 3535
3567 3536
3568 3537 div.rst-block h1:first-child {
3569 3538 padding-top: .25em !important;
3570 3539 }
3571 3540
3572 3541 div.rst-block h2,
3573 3542 div.rst-block h3 {
3574 3543 margin: 1em 0 !important;
3575 3544 }
3576 3545
3577 3546 div.rst-block h2 {
3578 3547 margin-top: 1.5em !important;
3579 3548 border-top: 4px solid #e0e0e0 !important;
3580 3549 padding-top: .5em !important;
3581 3550 }
3582 3551
3583 3552 div.rst-block p {
3584 3553 color: black !important;
3585 3554 margin: 1em 0 !important;
3586 3555 line-height: 1.5em !important;
3587 3556 }
3588 3557
3589 3558 div.rst-block ul {
3590 3559 list-style: disc !important;
3591 3560 margin: 1em 0 1em 2em !important;
3592 3561 }
3593 3562
3594 3563 div.rst-block ol {
3595 3564 list-style: decimal;
3596 3565 margin: 1em 0 1em 2em !important;
3597 3566 }
3598 3567
3599 3568 div.rst-block code {
3600 3569 font-size: 12px !important;
3601 3570 background-color: ghostWhite !important;
3602 3571 color: #444 !important;
3603 3572 padding: 0 .2em !important;
3604 3573 border: 1px solid #dedede !important;
3605 3574 }
3606 3575
3607 3576 div.rst-block pre code {
3608 3577 padding: 0 !important;
3609 3578 font-size: 12px !important;
3610 3579 background-color: #eee !important;
3611 3580 border: none !important;
3612 3581 }
3613 3582
3614 3583 div.rst-block pre {
3615 3584 margin: 1em 0;
3616 3585 font-size: 12px;
3617 3586 background-color: #eee;
3618 3587 border: 1px solid #ddd;
3619 3588 padding: 5px;
3620 3589 color: #444;
3621 3590 overflow: auto;
3622 3591 border-radius: 3px;
3623 3592 }
3624 3593
3625 3594
3626 3595 /** comment main **/
3627 3596 .comments {
3628 3597 padding: 10px 20px;
3629 3598 max-width: 978px;
3630 3599 }
3631 3600
3632 3601 .comments .comment .comment-wrapp {
3633 3602 border: 1px solid #ddd;
3634 3603 margin-top: 10px;
3635 3604 border-radius: 4px;
3636 3605 }
3637 3606
3638 3607 .comments .comment .meta {
3639 3608 background: #f8f8f8;
3640 3609 padding: 4px;
3641 3610 border-bottom: 1px solid #ddd;
3642 3611 min-height: 18px;
3643 3612 overflow: auto;
3644 3613 }
3645 3614
3646 3615 .comments .comment .meta img {
3647 3616 vertical-align: middle;
3648 3617 }
3649 3618
3650 3619 .comments .comment .meta .user {
3651 3620 font-weight: bold;
3652 3621 float: left;
3653 3622 padding: 4px 2px 2px 2px;
3654 3623 }
3655 3624
3656 3625 .comments .comment .meta .date {
3657 3626 float: left;
3658 3627 padding: 4px 4px 0px 4px;
3659 3628 }
3660 3629
3661 3630 .comments .comment .text {
3662 3631 background-color: #FAFAFA;
3663 3632 margin: 6px;
3664 3633 }
3665 3634
3666 3635 .comments-number {
3667 3636 padding: 0px 20px 10px;
3668 3637 font-weight: bold;
3669 3638 color: #666;
3670 3639 font-size: 16px;
3671 3640 }
3672 3641
3673 3642 .automatic-comment {
3674 3643 font-style: italic;
3675 3644 }
3676 3645
3677 3646 /** comment form **/
3678 3647
3679 3648 .status-block {
3680 3649 margin: 5px;
3681 3650 clear: both
3682 3651 }
3683 3652
3684 3653 .comment-form textarea {
3685 3654 width: 100%;
3686 3655 height: 100px;
3687 3656 font-family: Lucida Console, Consolas, Monaco, Inconsolata, Liberation Mono, monospace;
3688 3657 }
3689 3658
3690 3659 form.comment-form {
3691 3660 margin-top: 10px;
3692 3661 margin-left: 10px;
3693 3662 }
3694 3663
3695 3664 .comment-inline-form .comment-block-ta,
3696 3665 .comment-form .comment-block-ta {
3697 3666 border: 1px solid #ccc;
3698 3667 border-radius: 3px;
3699 3668 box-sizing: border-box;
3700 3669 }
3701 3670
3702 3671 .comment-form-submit {
3703 3672 margin-top: 5px;
3704 3673 margin-left: 525px;
3705 3674 }
3706 3675
3707 3676 .file-comments {
3708 3677 display: none;
3709 3678 }
3710 3679
3711 3680 .comment-form .comment {
3712 3681 margin-left: 10px;
3713 3682 }
3714 3683
3715 3684 .comment-form .comment-help {
3716 3685 padding: 5px 5px 5px 5px;
3717 3686 color: #666;
3718 3687 }
3719 3688
3720 3689 .comment-form .comment-button {
3721 3690 padding-top: 5px;
3722 3691 }
3723 3692
3724 3693 .add-another-button {
3725 3694 margin-left: 10px;
3726 3695 margin-top: 10px;
3727 3696 margin-bottom: 10px;
3728 3697 }
3729 3698
3730 3699 .comment .buttons {
3731 3700 float: right;
3732 3701 margin: -1px 0px 0px 0px;
3733 3702 }
3734 3703
3735 3704 .show-inline-comments {
3736 3705 position: relative;
3737 3706 top: 1px
3738 3707 }
3739 3708
3740 3709 /** comment inline form **/
3741 3710 .comment-inline-form {
3742 3711 margin: 4px;
3743 3712 max-width: 978px;
3744 3713 }
3745 3714 .comment-inline-form .submitting-overlay {
3746 3715 display: none;
3747 3716 height: 0;
3748 3717 text-align: center;
3749 3718 font-size: 16px;
3750 3719 opacity: 0.5;
3751 3720 }
3752 3721
3753 3722 .comment-inline-form .clearfix,
3754 3723 .comment-form .clearfix {
3755 3724 background: #EEE;
3756 3725 border-radius: 4px;
3757 3726 padding: 5px;
3758 3727 margin: 0px;
3759 3728 }
3760 3729
3761 3730 div.comment-inline-form {
3762 3731 padding: 4px 0px 6px 0px;
3763 3732 }
3764 3733
3765 3734 .comment-inline-form textarea {
3766 3735 width: 100%;
3767 3736 height: 100px;
3768 3737 font-family: Lucida Console, Consolas, Monaco, Inconsolata, Liberation Mono, monospace;
3769 3738 }
3770 3739
3771 3740 form.comment-inline-form {
3772 3741 margin-top: 10px;
3773 3742 margin-left: 10px;
3774 3743 }
3775 3744
3776 3745 .comment-inline-form-submit {
3777 3746 margin-top: 5px;
3778 3747 margin-left: 525px;
3779 3748 }
3780 3749
3781 3750 .file-comments {
3782 3751 display: none;
3783 3752 }
3784 3753
3785 3754 .comment-inline-form .comment {
3786 3755 margin-left: 10px;
3787 3756 }
3788 3757
3789 3758 .comment-inline-form .comment-help {
3790 3759 padding: 5px 5px 5px 5px;
3791 3760 color: #666;
3792 3761 }
3793 3762
3794 3763 .comment-inline-form .comment-button {
3795 3764 padding-top: 5px;
3796 3765 }
3797 3766
3798 3767 /** comment inline **/
3799 3768 .inline-comments {
3800 3769 padding: 0 20px;
3801 3770 }
3802 3771
3803 3772 .inline-comments .comment .comment-wrapp {
3804 3773 max-width: 978px;
3805 3774 border: 1px solid #ddd;
3806 3775 border-radius: 4px;
3807 3776 margin: 3px 3px 5px 5px;
3808 3777 background-color: #FAFAFA;
3809 3778 }
3810 3779
3811 3780 .inline-comments .add-button-row {
3812 3781 padding: 2px 4px 8px 5px;
3813 3782 }
3814 3783
3815 3784 .inline-comments .comment .meta {
3816 3785 background: #f8f8f8;
3817 3786 padding: 4px;
3818 3787 border-bottom: 1px solid #ddd;
3819 3788 min-height: 20px;
3820 3789 overflow: auto;
3821 3790 }
3822 3791
3823 3792 .inline-comments .comment .meta img {
3824 3793 vertical-align: middle;
3825 3794 }
3826 3795
3827 3796 .inline-comments .comment .meta .user {
3828 3797 font-weight: bold;
3829 3798 float: left;
3830 3799 padding: 3px;
3831 3800 }
3832 3801
3833 3802 .inline-comments .comment .meta .date {
3834 3803 float: left;
3835 3804 padding: 3px;
3836 3805 }
3837 3806
3838 3807 .inline-comments .comment .text {
3839 3808 background-color: #FAFAFA;
3840 3809 margin: 6px;
3841 3810 }
3842 3811
3843 3812 .inline-comments .comments-number {
3844 3813 padding: 0px 0px 10px 0px;
3845 3814 font-weight: bold;
3846 3815 color: #666;
3847 3816 font-size: 16px;
3848 3817 }
3849 3818
3850 3819 input.status_change_radio {
3851 3820 margin: 2px 0 5px 15px;
3852 3821 vertical-align: middle;
3853 3822 }
3854 3823
3855 3824 .badge {
3856 3825 padding: 4px 4px !important;
3857 3826 text-align: center;
3858 3827 color: #888 !important;
3859 3828 background-color: #DEDEDE !important;
3860 3829 border-radius: 4px !important;
3861 3830 }
3862 3831
3863 3832 .notification-header {
3864 3833 padding-top: 6px;
3865 3834 }
3866 3835 .notification-header .desc {
3867 3836 font-size: 16px;
3868 3837 height: 24px;
3869 3838 float: left
3870 3839 }
3871 3840 .notification-list .container.unread {
3872 3841 background: none repeat scroll 0 0 rgba(255, 255, 180, 0.6);
3873 3842 }
3874 3843 .notification-header .gravatar {
3875 3844 background: none repeat scroll 0 0 transparent;
3876 3845 padding: 0px 0px 0px 8px;
3877 3846 }
3878 3847 .notification-list .container .notification-header .desc {
3879 3848 font-weight: bold;
3880 3849 font-size: 17px;
3881 3850 }
3882 3851 .notification-header .delete-notifications {
3883 3852 float: right;
3884 3853 padding-top: 8px;
3885 3854 cursor: pointer;
3886 3855 }
3887 3856 .notification-header .read-notifications {
3888 3857 float: right;
3889 3858 padding-top: 8px;
3890 3859 cursor: pointer;
3891 3860 }
3892 3861 .notification-subject {
3893 3862 clear: both;
3894 3863 border-bottom: 1px solid #eee;
3895 3864 padding: 5px 0px;
3896 3865 }
3897 3866
3898 3867 .notification-body {
3899 3868 clear: both;
3900 3869 margin: 34px 2px 2px 8px
3901 3870 }
3902 3871
3903 3872 /****
3904 3873 PULL REQUESTS
3905 3874 *****/
3906 3875 .pullrequests_section_head {
3907 3876 padding: 10px 10px 10px 0px;
3908 3877 margin: 0 20px;
3909 3878 font-size: 16px;
3910 3879 font-weight: bold;
3911 3880 }
3912 3881
3913 3882 div.pr-details-title.closed {
3914 3883 color: #555;
3915 3884 background: #eee;
3916 3885 }
3917 3886
3918 3887 div.pr {
3919 3888 margin: 0px 20px;
3920 3889 padding: 4px 4px;
3921 3890 }
3922 3891 div.pr-desc {
3923 3892 margin: 0px 20px;
3924 3893 }
3925 3894 tr.pr-closed td {
3926 3895 background-color: #eee !important;
3927 3896 color: #555 !important;
3928 3897 }
3929 3898
3930 3899 span.pr-closed-tag {
3931 3900 margin-bottom: 1px;
3932 3901 margin-right: 1px;
3933 3902 padding: 1px 3px;
3934 3903 font-size: 10px;
3935 3904 padding: 1px 3px 1px 3px;
3936 3905 font-size: 10px;
3937 3906 color: #577632;
3938 3907 white-space: nowrap;
3939 3908 border-radius: 4px;
3940 3909 border: 1px solid #d9e8f8;
3941 3910 line-height: 1.5em;
3942 3911 }
3943 3912
3944 3913 .pr-box {
3945 3914 max-width: 978px;
3946 3915 }
3947 3916
3948 3917 #s2id_org_ref,
3949 3918 #s2id_other_ref,
3950 3919 #s2id_org_repo,
3951 3920 #s2id_other_repo {
3952 3921 min-width: 150px;
3953 3922 margin: 5px;
3954 3923 }
3955 3924
3956 3925 #pr-summary .msg-div {
3957 3926 margin: 5px 0;
3958 3927 }
3959 3928
3960 3929 /****
3961 3930 PERMS
3962 3931 *****/
3963 3932 #perms .perms_section_head {
3964 3933 padding: 10px 10px 10px 0px;
3965 3934 font-size: 16px;
3966 3935 font-weight: bold;
3967 3936 text-transform: capitalize;
3968 3937 }
3969 3938
3970 3939 #perms .perms_section_head label {
3971 3940 margin-left: 10px;
3972 3941 }
3973 3942
3974 3943 #perms .perm_tag {
3975 3944 position: relative;
3976 3945 top: -2px;
3977 3946 padding: 3px 3px 1px 3px;
3978 3947 font-size: 10px;
3979 3948 font-weight: bold;
3980 3949 text-transform: uppercase;
3981 3950 white-space: nowrap;
3982 3951 border-radius: 3px;
3983 3952 }
3984 3953
3985 3954 #perms .perm_tag.admin {
3986 3955 background-color: #B94A48;
3987 3956 color: #ffffff;
3988 3957 }
3989 3958
3990 3959 #perms .perm_tag.write {
3991 3960 background-color: #DB7525;
3992 3961 color: #ffffff;
3993 3962 }
3994 3963
3995 3964 #perms .perm_tag.read {
3996 3965 background-color: #468847;
3997 3966 color: #ffffff;
3998 3967 }
3999 3968
4000 3969 #perms .perm_tag.none {
4001 3970 background-color: #bfbfbf;
4002 3971 color: #ffffff;
4003 3972 }
4004 3973
4005 3974 input.perm_filter {
4006 3975 position: relative;
4007 3976 top: 2px;
4008 3977 }
4009 3978
4010 3979 .perm-gravatar {
4011 3980 vertical-align: middle;
4012 3981 padding: 2px;
4013 3982 }
4014 3983 .perm-gravatar-ac {
4015 3984 vertical-align: middle;
4016 3985 padding: 2px;
4017 3986 width: 14px;
4018 3987 height: 14px;
4019 3988 }
4020 3989
4021 3990 /*****************************************************************************
4022 3991 DIFFS CSS
4023 3992 ******************************************************************************/
4024 3993 .diff-collapse {
4025 3994 text-align: center;
4026 3995 margin-bottom: -15px;
4027 3996 }
4028 3997 .diff-collapse-button {
4029 3998 cursor: pointer;
4030 3999 color: #666;
4031 4000 font-size: 16px;
4032 4001 }
4033 4002 .diff-container {
4034 4003
4035 4004 }
4036 4005
4037 4006 .compare-revision-selector {
4038 4007 font-weight: bold;
4039 4008 font-size: 14px;
4040 4009 }
4041 4010 .compare-revision-selector > div {
4042 4011 display: inline-block;
4043 4012 margin-left: 8px;
4044 4013 vertical-align: middle;
4045 4014 }
4046 4015 .compare-revision-selector .btn {
4047 4016 margin-bottom: 0;
4048 4017 }
4049 4018
4050 4019
4051 4020 div.diffblock {
4052 4021 overflow: auto;
4053 4022 padding: 0px;
4054 4023 border: 1px solid #ccc;
4055 4024 background: #f8f8f8;
4056 4025 font-size: 100%;
4057 4026 line-height: 100%;
4058 4027 /* new */
4059 4028 line-height: 125%;
4060 4029 border-radius: 6px 6px 0px 0px;
4061 4030 }
4062 4031
4063 4032 .compare-revision-selector,
4064 4033 div.diffblock .code-header {
4065 4034 border-bottom: 1px solid #CCCCCC;
4066 4035 background: #EEEEEE;
4067 4036 padding: 10px 0 10px 0;
4068 4037 min-height: 14px;
4069 4038 }
4070 4039
4071 4040 div.diffblock .code-header.banner {
4072 4041 border-bottom: 1px solid #CCCCCC;
4073 4042 background: #EEEEEE;
4074 4043 height: 14px;
4075 4044 margin: 0;
4076 4045 padding: 3px 100px 11px 100px;
4077 4046 }
4078 4047
4079 4048 div.diffblock .code-header-title {
4080 4049 padding: 0px 0px 10px 5px !important;
4081 4050 margin: 0 !important;
4082 4051 }
4083 4052 div.diffblock .code-header .hash {
4084 4053 float: left;
4085 4054 padding: 2px 0 0 2px;
4086 4055 }
4087 4056 div.diffblock .code-header .date {
4088 4057 float: left;
4089 4058 text-transform: uppercase;
4090 4059 padding: 2px 0px 0px 2px;
4091 4060 }
4092 4061 div.diffblock .code-header div {
4093 4062 margin-left: 4px;
4094 4063 font-weight: bold;
4095 4064 font-size: 14px;
4096 4065 }
4097 4066
4098 4067 div.diffblock .parents {
4099 4068 float: left;
4100 4069 min-height: 26px;
4101 4070 font-size: 10px;
4102 4071 font-weight: 400;
4103 4072 vertical-align: middle;
4104 4073 padding: 0px 2px 2px 2px;
4105 4074 background-color: #eeeeee;
4106 4075 border-bottom: 1px solid #CCCCCC;
4107 4076 }
4108 4077
4109 4078 div.diffblock .children {
4110 4079 float: right;
4111 4080 min-height: 26px;
4112 4081 font-size: 10px;
4113 4082 font-weight: 400;
4114 4083 vertical-align: middle;
4115 4084 text-align: right;
4116 4085 padding: 0px 2px 2px 2px;
4117 4086 background-color: #eeeeee;
4118 4087 border-bottom: 1px solid #CCCCCC;
4119 4088 }
4120 4089
4121 4090 div.diffblock .code-body {
4122 4091 background: #FFFFFF;
4123 4092 clear: both;
4124 4093 }
4125 4094 div.diffblock pre.raw {
4126 4095 background: #FFFFFF;
4127 4096 color: #000000;
4128 4097 }
4129 4098 table.code-difftable {
4130 4099 border-collapse: collapse;
4131 4100 width: 99%;
4132 4101 border-radius: 0px !important;
4133 4102 }
4134 4103 table.code-difftable td {
4135 4104 padding: 0 !important;
4136 4105 background: none !important;
4137 4106 border: 0 !important;
4138 4107 vertical-align: baseline !important
4139 4108 }
4140 4109 table.code-difftable .context {
4141 4110 background: none repeat scroll 0 0 #DDE7EF;
4142 4111 color: #999;
4143 4112 }
4144 4113 table.code-difftable .add {
4145 4114 background: none repeat scroll 0 0 #DDFFDD;
4146 4115 }
4147 4116 table.code-difftable .add ins {
4148 4117 background: none repeat scroll 0 0 #AAFFAA;
4149 4118 text-decoration: none;
4150 4119 }
4151 4120 table.code-difftable .del {
4152 4121 background: none repeat scroll 0 0 #FFDDDD;
4153 4122 }
4154 4123 table.code-difftable .del del {
4155 4124 background: none repeat scroll 0 0 #FFAAAA;
4156 4125 text-decoration: none;
4157 4126 }
4158 4127
4159 4128 table.code-highlighttable div.code-highlight pre u:before,
4160 4129 table.code-difftable td.code pre u:before {
4161 4130 content: "\21a6";
4162 4131 display: inline-block;
4163 4132 width: 0;
4164 4133 }
4165 4134 table.code-highlighttable div.code-highlight pre u.cr:before,
4166 4135 table.code-difftable td.code pre u.cr:before {
4167 4136 content: "\21a4";
4168 4137 display: inline-block;
4169 4138 color: rgba(0,0,0,0.5);
4170 4139 }
4171 4140 table.code-highlighttable div.code-highlight pre u,
4172 4141 table.code-difftable td.code pre u {
4173 4142 color: rgba(0,0,0,0.15);
4174 4143 }
4175 4144 table.code-highlighttable div.code-highlight pre i,
4176 4145 table.code-difftable td.code pre i {
4177 4146 border-style: solid;
4178 4147 border-left-width: 1px;
4179 4148 color: rgba(0,0,0,0.5);
4180 4149 }
4181 4150
4182 4151 /** LINE NUMBERS **/
4183 4152 table.code-difftable .lineno {
4184 4153 padding-left: 2px;
4185 4154 padding-right: 2px !important;
4186 4155 width: 30px;
4187 4156 -moz-user-select: none;
4188 4157 -webkit-user-select: none;
4189 4158 border-right: 1px solid #CCC !important;
4190 4159 border-left: 0px solid #CCC !important;
4191 4160 border-top: 0px solid #CCC !important;
4192 4161 border-bottom: none !important;
4193 4162 vertical-align: middle !important;
4194 4163 text-align: center;
4195 4164 }
4196 4165 table.code-difftable .lineno.new {
4197 4166 text-align: right;
4198 4167 }
4199 4168 table.code-difftable .lineno.old {
4200 4169 text-align: right;
4201 4170 }
4202 4171 table.code-difftable .lineno a {
4203 4172 color: #aaa !important;
4204 4173 font: 11px Lucida Console, Consolas, Monaco, Inconsolata, Liberation Mono, monospace !important;
4205 4174 letter-spacing: -1px;
4206 4175 padding-left: 10px;
4207 4176 padding-right: 8px;
4208 4177 box-sizing: border-box;
4209 4178 cursor: pointer;
4210 4179 display: block;
4211 4180 width: 100%;
4212 4181 }
4213 4182
4214 4183 table.code-difftable .line:hover .lineno a {
4215 4184 color: #333 !important;
4216 4185 }
4217 4186
4218 4187 table.code-difftable .lineno-inline {
4219 4188 background: none repeat scroll 0 0 #FFF !important;
4220 4189 padding-left: 2px;
4221 4190 padding-right: 2px;
4222 4191 text-align: right;
4223 4192 width: 30px;
4224 4193 -moz-user-select: none;
4225 4194 -webkit-user-select: none;
4226 4195 }
4227 4196
4228 4197 /** CODE **/
4229 4198 table.code-difftable .code {
4230 4199 display: block;
4231 4200 width: 100%;
4232 4201 }
4233 4202 table.code-difftable .code td {
4234 4203 margin: 0;
4235 4204 padding: 0;
4236 4205 }
4237 4206 table.code-difftable .code pre {
4238 4207 margin: 0 0 0 12px;
4239 4208 padding: 0;
4240 4209 min-height: 17px;
4241 4210 line-height: 17px;
4242 4211 white-space: pre-wrap;
4243 4212 }
4244 4213
4245 4214 table.code-difftable .del .code pre:before {
4246 4215 content: "-";
4247 4216 color: #800;
4248 4217 float: left;
4249 4218 left: -1em;
4250 4219 position: relative;
4251 4220 width: 0;
4252 4221 }
4253 4222
4254 4223 table.code-difftable .add .code pre:before {
4255 4224 content: "+";
4256 4225 color: #080;
4257 4226 float: left;
4258 4227 left: -1em;
4259 4228 position: relative;
4260 4229 width: 0;
4261 4230 }
4262 4231
4263 4232 table.code-difftable .unmod .code pre:before {
4264 4233 content: " ";
4265 4234 float: left;
4266 4235 left: -1em;
4267 4236 position: relative;
4268 4237 width: 0;
4269 4238 }
4270 4239
4271 4240 .add-bubble {
4272 4241 position: relative;
4273 4242 display: none;
4274 4243 float: left;
4275 4244 width: 0px;
4276 4245 height: 0px;
4277 4246 left: -8px;
4278 4247 box-sizing: border-box;
4279 4248 }
4280 4249
4281 4250 /* comment bubble, only visible when in a commentable diff */
4282 4251 .commentable-diff tr.line.add:hover td .add-bubble,
4283 4252 .commentable-diff tr.line.del:hover td .add-bubble,
4284 4253 .commentable-diff tr.line.unmod:hover td .add-bubble {
4285 4254 display: block;
4286 4255 z-index: 1;
4287 4256 }
4288 4257
4289 4258 .add-bubble div {
4290 4259 background: #577632;
4291 4260 width: 16px;
4292 4261 height: 16px;
4293 4262 cursor: pointer;
4294 4263 padding: 0 2px 2px 0.5px;
4295 4264 border: 1px solid #577632;
4296 4265 border-radius: 3px;
4297 4266 box-sizing: border-box;
4298 4267 }
4299 4268
4300 4269 .add-bubble div:before {
4301 4270 font-size: 14px;
4302 4271 color: #ffffff;
4303 4272 font-family: "kallithea";
4304 4273 content: '\1f5ea';
4305 4274 }
4306 4275
4307 4276 .add-bubble div:hover {
4308 4277 transform: scale(1.2, 1.2);
4309 4278 }
4310 4279
4311 4280 /* show some context of link targets - but only works when the link target
4312 4281 can be extended with any visual difference */
4313 4282 div.comment:target:before {
4314 4283 display: block;
4315 4284 height: 100px;
4316 4285 margin: -100px 0 0;
4317 4286 content: "";
4318 4287 }
4319 4288
4320 4289 div.comment:target>.comment-wrapp {
4321 4290 border: solid 2px #ee0 !important;
4322 4291 margin: 2px 2px 4px 4px;
4323 4292 }
4324 4293
4325 4294 .lineno:target a {
4326 4295 border: solid 2px #ee0 !important;
4327 4296 margin: -2px;
4328 4297 }
4329 4298
4330 4299 .btn-image-diff-show,
4331 4300 .btn-image-diff-swap {
4332 4301 margin: 5px;
4333 4302 }
4334 4303
4335 4304 .img-diff {
4336 4305 max-width: 45%;
4337 4306 height: auto;
4338 4307 margin: 5px;
4339 4308 /* http://lea.verou.me/demos/css3-patterns.html */
4340 4309 background-image:
4341 4310 linear-gradient(45deg, #888 25%, transparent 25%, transparent),
4342 4311 linear-gradient(-45deg, #888 25%, transparent 25%, transparent),
4343 4312 linear-gradient(45deg, transparent 75%, #888 75%),
4344 4313 linear-gradient(-45deg, transparent 75%, #888 75%);
4345 4314 background-size: 10px 10px;
4346 4315 background-color: #999;
4347 4316 }
4348 4317
4349 4318 .img-preview {
4350 4319 max-width: 100%;
4351 4320 height: auto;
4352 4321 margin: 5px;
4353 4322 }
4354 4323
4355 4324 div.comment-prev-next-links div.prev-comment,
4356 4325 div.comment-prev-next-links div.next-comment {
4357 4326 display: inline-block;
4358 4327 min-width: 150px;
4359 4328 margin: 3px 6px;
4360 4329 }
4361 4330
4362 4331 body table.dataTable thead .sorting {
4363 4332 background-image: none;
4364 4333 }
4365 4334 body table.dataTable thead .sorting_asc {
4366 4335 background-image: none;
4367 4336 }
4368 4337 body table.dataTable thead .sorting_desc {
4369 4338 background-image: none;
4370 4339 }
4371 4340 body table.dataTable thead .sorting_asc_disabled {
4372 4341 background-image: none;
4373 4342 }
4374 4343 body table.dataTable thead .sorting_desc_disabled {
4375 4344 background-image: none;
4376 4345 }
4377 4346
4378 4347 body table.dataTable thead .sorting_asc::after {
4379 4348 font-family: "kallithea";
4380 4349 content: "\23f6";
4381 4350 }
4382 4351 body table.dataTable thead .sorting_desc::after {
4383 4352 font-family: "kallithea";
4384 4353 content: "\23f7";
4385 4354 }
4386 4355
4387 4356 .dataTables_wrapper .dataTables_left {
4388 4357 float: left !important;
4389 4358 }
4390 4359
4391 4360 .dataTables_wrapper .dataTables_right {
4392 4361 float: right;
4393 4362 }
4394 4363
4395 4364 .dataTables_wrapper .dataTables_right > div {
4396 4365 padding-left: 30px;
4397 4366 }
4398 4367
4399 4368 .dataTables_wrapper .dataTables_info {
4400 4369 clear: none;
4401 4370 padding-top: 1em;
4402 4371 }
4403 4372
4404 4373 .dataTables_wrapper .dataTables_paginate {
4405 4374 padding-top: 0;
4406 4375 }
4407 4376
4408 4377 .dataTables_wrapper .dataTables_paginate > a.paginate_button {
4409 4378 padding-top: 1em;
4410 4379 border: 0 !important;
4411 4380 }
4412 4381
4413 4382 .text-muted {
4414 4383 color: #777777;
4415 4384 }
4416 4385
4417 4386 .grid_edit a {
4418 4387 text-decoration: none;
4419 4388 }
4420 4389
4421 4390 .changes_txt {
4422 4391 clear: both;
4423 4392 }
4424 4393
4425 4394 .text-nowrap {
4426 4395 white-space: nowrap;
4427 4396 }
4428 4397
4429 4398 div.codeblock div.code-header div.author {
4430 4399 height: auto;
4431 4400 min-height: 25px;
4432 4401 }
4433 4402
4434 4403 ul.user_group_member li {
4435 4404 clear: both;
4436 4405 }
4406
4407 .tooltip {
4408 position: absolute;
4409 z-index: 1070;
4410 display: block;
4411 font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
4412 font-size: 12px;
4413 font-style: normal;
4414 font-weight: 400;
4415 line-height: 1.42857143;
4416 text-align: left;
4417 text-align: start;
4418 text-decoration: none;
4419 text-shadow: none;
4420 text-transform: none;
4421 letter-spacing: normal;
4422 word-break: normal;
4423 word-spacing: normal;
4424 word-wrap: normal;
4425 white-space: normal;
4426 }
4427 .tooltip-arrow {
4428 position: absolute;
4429 width: 0;
4430 height: 0;
4431 border-color: transparent;
4432 border-style: solid;
4433 }
4434 .tooltip-inner {
4435 max-width: 200px;
4436 padding: 3px 8px;
4437 color: #fff;
4438 text-align: center;
4439 background-color: #000;
4440 border-radius: 4px;
4441 }
4442
4443 .tooltip.top {
4444 padding: 5px 0;
4445 margin-top: -3px;
4446 }
4447 .tooltip.top .tooltip-arrow {
4448 bottom: 0;
4449 left: 50%;
4450 margin-left: -5px;
4451 border-width: 5px 5px 0;
4452 border-top-color: #000;
4453 }
4454
4455 .tooltip.bottom {
4456 padding: 5px 0;
4457 margin-top: 3px;
4458 }
4459 .tooltip.bottom .tooltip-arrow {
4460 top: 0;
4461 left: 50%;
4462 margin-left: -5px;
4463 border-width: 0 5px 5px;
4464 border-bottom-color: #000;
4465 }
4466
4467
4468 .popover {
4469 position: absolute;
4470 top: 0;
4471 left: 0;
4472 z-index: 1060;
4473 max-width: 276px;
4474 padding: 1px;
4475 background-color: #fff;
4476 background-clip: padding-box;
4477 border: 1px solid #ccc;
4478 border: 1px solid rgba(0,0,0,.2);
4479 border-radius: 6px;
4480 box-shadow: 0 5px 10px rgba(0,0,0,.2);
4481 font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
4482 font-size: 12px;
4483 font-style: normal;
4484 font-weight: 400;
4485 line-height: 1.42857143;
4486 text-align: left;
4487 text-align: start;
4488 text-decoration: none;
4489 text-shadow: none;
4490 text-transform: none;
4491 letter-spacing: normal;
4492 word-break: normal;
4493 word-spacing: normal;
4494 word-wrap: normal;
4495 white-space: normal;
4496 }
4497
4498 .popover > .arrow {
4499 border-width: 11px;
4500 }
4501 .popover > .arrow,
4502 .popover > .arrow::after {
4503 position: absolute;
4504 display: block;
4505 width: 0;
4506 height: 0;
4507 border-color: transparent;
4508 border-style: solid;
4509 }
4510
4511 .popover-title {
4512 padding: 8px 14px;
4513 margin: 0;
4514 font-size: 14px;
4515 background-color: #f7f7f7;
4516 border-bottom: 1px solid #ebebeb;
4517 border-radius: 5px 5px 0 0;
4518 }
4519
4520 .popover-content {
4521 padding: 9px 14px;
4522 }
4523
4524 .popover.top {
4525 margin-top: -10px;
4526 }
4527 .popover.top > .arrow {
4528 bottom: -11px;
4529 left: 50%;
4530 margin-left: -11px;
4531 border-top-color: #999;
4532 border-top-color: rgba(0,0,0,.25);
4533 border-bottom-width: 0;
4534 }
4535
4536 .popover.bottom {
4537 margin-top: 10px;
4538 }
4539 .popover.bottom > .arrow {
4540 top: -11px;
4541 left: 50%;
4542 margin-left: -11px;
4543 border-top-width: 0;
4544 border-bottom-color: #999;
4545 border-bottom-color: rgba(0,0,0,.25);
4546 }
@@ -1,1519 +1,1480 b''
1 1 /**
2 2 Kallithea JS Files
3 3 **/
4 4 'use strict';
5 5
6 6 if (typeof console == "undefined" || typeof console.log == "undefined"){
7 7 console = { log: function() {} }
8 8 }
9 9
10 10 /**
11 11 * INJECT .format function into String
12 12 * Usage: "My name is {0} {1}".format("Johny","Bravo")
13 13 * Return "My name is Johny Bravo"
14 14 * Inspired by https://gist.github.com/1049426
15 15 */
16 16 String.prototype.format = function() {
17 17 function format() {
18 18 var str = this;
19 19 var len = arguments.length+1;
20 20 var safe = undefined;
21 21 var arg = undefined;
22 22
23 23 // For each {0} {1} {n...} replace with the argument in that position. If
24 24 // the argument is an object or an array it will be stringified to JSON.
25 25 for (var i=0; i < len; arg = arguments[i++]) {
26 26 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
27 27 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
28 28 }
29 29 return str;
30 30 }
31 31
32 32 // Save a reference of what may already exist under the property native.
33 33 // Allows for doing something like: if("".format.native) { /* use native */ }
34 34 format.native = String.prototype.format;
35 35
36 36 // Replace the prototype property
37 37 return format;
38 38
39 39 }();
40 40
41 41 String.prototype.strip = function(char) {
42 42 if(char === undefined){
43 43 char = '\\s';
44 44 }
45 45 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
46 46 }
47 47
48 48 String.prototype.lstrip = function(char) {
49 49 if(char === undefined){
50 50 char = '\\s';
51 51 }
52 52 return this.replace(new RegExp('^'+char+'+'),'');
53 53 }
54 54
55 55 String.prototype.rstrip = function(char) {
56 56 if(char === undefined){
57 57 char = '\\s';
58 58 }
59 59 return this.replace(new RegExp(''+char+'+$'),'');
60 60 }
61 61
62 62 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
63 63 under MIT license / public domain, see
64 64 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
65 65 if(!Array.prototype.indexOf) {
66 66 Array.prototype.indexOf = function (searchElement, fromIndex) {
67 67 if ( this === undefined || this === null ) {
68 68 throw new TypeError( '"this" is null or not defined' );
69 69 }
70 70
71 71 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
72 72
73 73 fromIndex = +fromIndex || 0;
74 74
75 75 if (Math.abs(fromIndex) === Infinity) {
76 76 fromIndex = 0;
77 77 }
78 78
79 79 if (fromIndex < 0) {
80 80 fromIndex += length;
81 81 if (fromIndex < 0) {
82 82 fromIndex = 0;
83 83 }
84 84 }
85 85
86 86 for (;fromIndex < length; fromIndex++) {
87 87 if (this[fromIndex] === searchElement) {
88 88 return fromIndex;
89 89 }
90 90 }
91 91
92 92 return -1;
93 93 };
94 94 }
95 95
96 96 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
97 97 under MIT license / public domain, see
98 98 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
99 99 if (!Array.prototype.filter)
100 100 {
101 101 Array.prototype.filter = function(fun /*, thisArg */)
102 102 {
103 103 if (this === void 0 || this === null)
104 104 throw new TypeError();
105 105
106 106 var t = Object(this);
107 107 var len = t.length >>> 0;
108 108 if (typeof fun !== "function")
109 109 throw new TypeError();
110 110
111 111 var res = [];
112 112 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
113 113 for (var i = 0; i < len; i++)
114 114 {
115 115 if (i in t)
116 116 {
117 117 var val = t[i];
118 118
119 119 // NOTE: Technically this should Object.defineProperty at
120 120 // the next index, as push can be affected by
121 121 // properties on Object.prototype and Array.prototype.
122 122 // But that method's new, and collisions should be
123 123 // rare, so use the more-compatible alternative.
124 124 if (fun.call(thisArg, val, i, t))
125 125 res.push(val);
126 126 }
127 127 }
128 128
129 129 return res;
130 130 };
131 131 }
132 132
133 133 /**
134 134 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
135 135 * which is copyright Stephane Klein and was made available under the BSD License.
136 136 *
137 137 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
138 138 */
139 139 var pyroutes = (function() {
140 140 var matchlist = {};
141 141 var sprintf = (function() {
142 142 function get_type(variable) {
143 143 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
144 144 }
145 145 function str_repeat(input, multiplier) {
146 146 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
147 147 return output.join('');
148 148 }
149 149
150 150 var str_format = function() {
151 151 if (!str_format.cache.hasOwnProperty(arguments[0])) {
152 152 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
153 153 }
154 154 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
155 155 };
156 156
157 157 str_format.format = function(parse_tree, argv) {
158 158 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
159 159 for (i = 0; i < tree_length; i++) {
160 160 node_type = get_type(parse_tree[i]);
161 161 if (node_type === 'string') {
162 162 output.push(parse_tree[i]);
163 163 }
164 164 else if (node_type === 'array') {
165 165 match = parse_tree[i]; // convenience purposes only
166 166 if (match[2]) { // keyword argument
167 167 arg = argv[cursor];
168 168 for (k = 0; k < match[2].length; k++) {
169 169 if (!arg.hasOwnProperty(match[2][k])) {
170 170 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
171 171 }
172 172 arg = arg[match[2][k]];
173 173 }
174 174 }
175 175 else if (match[1]) { // positional argument (explicit)
176 176 arg = argv[match[1]];
177 177 }
178 178 else { // positional argument (implicit)
179 179 arg = argv[cursor++];
180 180 }
181 181
182 182 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
183 183 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
184 184 }
185 185 switch (match[8]) {
186 186 case 'b': arg = arg.toString(2); break;
187 187 case 'c': arg = String.fromCharCode(arg); break;
188 188 case 'd': arg = parseInt(arg, 10); break;
189 189 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
190 190 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
191 191 case 'o': arg = arg.toString(8); break;
192 192 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
193 193 case 'u': arg = Math.abs(arg); break;
194 194 case 'x': arg = arg.toString(16); break;
195 195 case 'X': arg = arg.toString(16).toUpperCase(); break;
196 196 }
197 197 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
198 198 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
199 199 pad_length = match[6] - String(arg).length;
200 200 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
201 201 output.push(match[5] ? arg + pad : pad + arg);
202 202 }
203 203 }
204 204 return output.join('');
205 205 };
206 206
207 207 str_format.cache = {};
208 208
209 209 str_format.parse = function(fmt) {
210 210 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
211 211 while (_fmt) {
212 212 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
213 213 parse_tree.push(match[0]);
214 214 }
215 215 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
216 216 parse_tree.push('%');
217 217 }
218 218 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
219 219 if (match[2]) {
220 220 arg_names |= 1;
221 221 var field_list = [], replacement_field = match[2], field_match = [];
222 222 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
223 223 field_list.push(field_match[1]);
224 224 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
225 225 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
226 226 field_list.push(field_match[1]);
227 227 }
228 228 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
229 229 field_list.push(field_match[1]);
230 230 }
231 231 else {
232 232 throw('[sprintf] huh?');
233 233 }
234 234 }
235 235 }
236 236 else {
237 237 throw('[sprintf] huh?');
238 238 }
239 239 match[2] = field_list;
240 240 }
241 241 else {
242 242 arg_names |= 2;
243 243 }
244 244 if (arg_names === 3) {
245 245 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
246 246 }
247 247 parse_tree.push(match);
248 248 }
249 249 else {
250 250 throw('[sprintf] huh?');
251 251 }
252 252 _fmt = _fmt.substring(match[0].length);
253 253 }
254 254 return parse_tree;
255 255 };
256 256
257 257 return str_format;
258 258 })();
259 259
260 260 var vsprintf = function(fmt, argv) {
261 261 argv.unshift(fmt);
262 262 return sprintf.apply(null, argv);
263 263 };
264 264 return {
265 265 'url': function(route_name, params) {
266 266 var result = route_name;
267 267 if (typeof(params) != 'object'){
268 268 params = {};
269 269 }
270 270 if (matchlist.hasOwnProperty(route_name)) {
271 271 var route = matchlist[route_name];
272 272 // param substitution
273 273 for(var i=0; i < route[1].length; i++) {
274 274 if (!params.hasOwnProperty(route[1][i]))
275 275 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
276 276 }
277 277 result = sprintf(route[0], params);
278 278
279 279 var ret = [];
280 280 //extra params => GET
281 281 for(var param in params){
282 282 if (route[1].indexOf(param) == -1){
283 283 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
284 284 }
285 285 }
286 286 var _parts = ret.join("&");
287 287 if(_parts){
288 288 result = result +'?'+ _parts
289 289 }
290 290 }
291 291
292 292 return result;
293 293 },
294 294 'register': function(route_name, route_tmpl, req_params) {
295 295 if (typeof(req_params) != 'object') {
296 296 req_params = [];
297 297 }
298 298 var keys = [];
299 299 for (var i=0; i < req_params.length; i++) {
300 300 keys.push(req_params[i]);
301 301 }
302 302 matchlist[route_name] = [
303 303 unescape(route_tmpl),
304 304 keys
305 305 ]
306 306 },
307 307 '_routes': function(){
308 308 return matchlist;
309 309 }
310 310 }
311 311 })();
312 312
313 313
314 314 /* Invoke all functions in callbacks */
315 315 var _run_callbacks = function(callbacks){
316 316 if (callbacks !== undefined){
317 317 var _l = callbacks.length;
318 318 for (var i=0;i<_l;i++){
319 319 var func = callbacks[i];
320 320 if(typeof(func)=='function'){
321 321 try{
322 322 func();
323 323 }catch (err){};
324 324 }
325 325 }
326 326 }
327 327 }
328 328
329 329 /**
330 330 * turns objects into GET query string
331 331 */
332 332 var _toQueryString = function(o) {
333 333 if(typeof o !== 'object') {
334 334 return false;
335 335 }
336 336 var _p, _qs = [];
337 337 for(_p in o) {
338 338 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
339 339 }
340 340 return _qs.join('&');
341 341 };
342 342
343 343 /**
344 344 * Load HTML into DOM using Ajax
345 345 *
346 346 * @param $target: load html async and place it (or an error message) here
347 347 * @param success: success callback function
348 348 * @param args: query parameters to pass to url
349 349 */
350 350 function asynchtml(url, $target, success, args){
351 351 if(args===undefined){
352 352 args=null;
353 353 }
354 354 $target.html(_TM['Loading ...']).css('opacity','0.3');
355 355
356 356 return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
357 357 .done(function(html) {
358 358 $target.html(html);
359 359 $target.css('opacity','1.0');
360 360 //execute the given original callback
361 361 if (success !== undefined && success) {
362 362 success();
363 363 }
364 364 })
365 365 .fail(function(jqXHR, textStatus, errorThrown) {
366 366 if (textStatus == "abort")
367 367 return;
368 368 $target.html('<span class="error_red">ERROR: {0}</span>'.format(textStatus));
369 369 $target.css('opacity','1.0');
370 370 })
371 371 ;
372 372 };
373 373
374 374 var ajaxGET = function(url, success, failure) {
375 375 if(failure === undefined) {
376 376 failure = function(jqXHR, textStatus, errorThrown) {
377 377 if (textStatus != "abort")
378 378 alert("Ajax GET error: " + textStatus);
379 379 };
380 380 }
381 381 return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
382 382 .done(success)
383 383 .fail(failure);
384 384 };
385 385
386 386 var ajaxPOST = function(url, postData, success, failure) {
387 387 postData['_authentication_token'] = _authentication_token;
388 388 var postData = _toQueryString(postData);
389 389 if(failure === undefined) {
390 390 failure = function(jqXHR, textStatus, errorThrown) {
391 391 if (textStatus != "abort")
392 392 alert("Error posting to server: " + textStatus);
393 393 };
394 394 }
395 395 return $.ajax({url: url, data: postData, type: 'POST', headers: {'X-PARTIAL-XHR': '1'}, cache: false})
396 396 .done(success)
397 397 .fail(failure);
398 398 };
399 399
400 400
401 401 /**
402 402 * activate .show_more links
403 403 * the .show_more must have an id that is the the id of an element to hide prefixed with _
404 404 * the parentnode will be displayed
405 405 */
406 406 var show_more_event = function(){
407 407 $('.show_more').click(function(e){
408 408 var el = e.currentTarget;
409 409 $('#' + el.id.substring(1)).hide();
410 410 $(el.parentNode).show();
411 411 });
412 412 };
413 413
414 /**
415 * activate .lazy-cs mouseover for showing changeset tooltip
416 */
417 var show_changeset_tooltip = function(){
418 $('.lazy-cs').mouseover(function(e){
419 var $target = $(e.currentTarget);
420 var rid = $target.data('raw_id');
421 var repo_name = $target.data('repo_name');
422 if(rid && !$target.hasClass('tooltip')){
423 _show_tooltip(e, _TM['loading ...']);
424 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": rid});
425 ajaxGET(url, function(json){
426 $target.addClass('tooltip');
427 _show_tooltip(e, json['message']);
428 _activate_tooltip($target);
429 });
430 }
431 });
432 };
433 414
434 415 var _onSuccessFollow = function(target){
435 416 var $target = $(target);
436 417 var $f_cnt = $('#current_followers_count');
437 418 if ($target.hasClass('follow')) {
438 419 $target.removeClass('follow').addClass('following');
439 420 $target.prop('title', _TM['Stop following this repository']);
440 421 if ($f_cnt.html()) {
441 422 var cnt = Number($f_cnt.html())+1;
442 423 $f_cnt.html(cnt);
443 424 }
444 425 } else {
445 426 $target.removeClass('following').addClass('follow');
446 427 $target.prop('title', _TM['Start following this repository']);
447 428 if ($f_cnt.html()) {
448 429 var cnt = Number($f_cnt.html())-1;
449 430 $f_cnt.html(cnt);
450 431 }
451 432 }
452 433 }
453 434
454 435 var toggleFollowingRepo = function(target, follows_repository_id){
455 436 var args = 'follows_repository_id=' + follows_repository_id;
456 437 args += '&amp;_authentication_token=' + _authentication_token;
457 438 $.post(TOGGLE_FOLLOW_URL, args, function(data){
458 439 _onSuccessFollow(target);
459 440 });
460 441 return false;
461 442 };
462 443
463 444 var showRepoSize = function(target, repo_name){
464 445 var args = '_authentication_token=' + _authentication_token;
465 446
466 447 if(!$("#" + target).hasClass('loaded')){
467 448 $("#" + target).html(_TM['Loading ...']);
468 449 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
469 450 $.post(url, args, function(data) {
470 451 $("#" + target).html(data);
471 452 $("#" + target).addClass('loaded');
472 453 });
473 454 }
474 455 return false;
475 456 };
476 457
477 458 /**
478 * tooltips
459 * load tooltips dynamically based on data attributes, used for .lazy-cs changeset links
479 460 */
480
481 var tooltip_activate = function(){
482 $(document).ready(_init_tooltip);
483 };
484
485 var _activate_tooltip = function($tt){
486 $tt.mouseover(_show_tooltip);
487 $tt.mousemove(_move_tooltip);
488 $tt.mouseout(_close_tooltip);
489 };
461 var get_changeset_tooltip = function() {
462 var $target = $(this);
463 var tooltip = $target.data('tooltip');
464 if (!tooltip) {
465 var raw_id = $target.data('raw_id');
466 var repo_name = $target.data('repo_name');
467 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": raw_id});
490 468
491 var _init_tooltip = function(){
492 var $tipBox = $('#tip-box');
493 if(!$tipBox.length){
494 $tipBox = $('<div id="tip-box"></div>');
495 $(document.body).append($tipBox);
469 $.ajax(url, {
470 async: false,
471 success: function(data) {
472 tooltip = data["message"];
473 }
474 });
475 $target.data('tooltip', tooltip);
496 476 }
497
498 $tipBox.hide();
499 $tipBox.css('position', 'absolute');
500 $tipBox.css('max-width', '600px');
501
502 _activate_tooltip($('[data-toggle="tooltip"]'));
477 return tooltip;
503 478 };
504 479
505 var _show_tooltip = function(e, tipText, safe){
506 e.stopImmediatePropagation();
507 var el = e.currentTarget;
508 var $el = $(el);
509 if(tipText){
510 // just use it
511 } else if(el.tagName.toLowerCase() === 'img'){
512 tipText = el.alt ? el.alt : '';
513 } else {
514 tipText = el.title ? el.title : '';
515 safe = safe || $el.hasClass("safe-html-title");
480 /**
481 * activate tooltips and popups
482 */
483 var tooltip_activate = function(){
484 function placement(p, e){
485 if(e.getBoundingClientRect().top > 2*$(window).height()/3){
486 return 'top';
487 }else{
488 return 'bottom';
489 }
516 490 }
517
518 if(tipText !== ''){
519 // save org title
520 $el.attr('tt_title', tipText);
521 // reset title to not show org tooltips
522 $el.prop('title', '');
523
524 var $tipBox = $('#tip-box');
525 if (safe) {
526 $tipBox.html(tipText);
527 } else {
528 $tipBox.text(tipText);
529 }
530 $tipBox.css('display', 'block');
531 }
491 $(document).ready(function(){
492 $('[data-toggle="tooltip"]').tooltip({
493 placement: placement
494 });
495 $('[data-toggle="popover"]').popover({
496 html: true,
497 container: 'body',
498 placement: placement,
499 trigger: 'hover',
500 template: '<div class="popover cs-popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
501 });
502 $('.lazy-cs').tooltip({
503 title: get_changeset_tooltip,
504 placement: placement
505 });
506 });
532 507 };
533 508
534 var _move_tooltip = function(e){
535 e.stopImmediatePropagation();
536 var $tipBox = $('#tip-box');
537 $tipBox.css('top', (e.pageY + 15) + 'px');
538 $tipBox.css('left', (e.pageX + 15) + 'px');
539 };
540
541 var _close_tooltip = function(e){
542 e.stopImmediatePropagation();
543 var $tipBox = $('#tip-box');
544 $tipBox.hide();
545 var el = e.currentTarget;
546 $(el).prop('title', $(el).attr('tt_title'));
547 };
548 509
549 510 /**
550 511 * Quick filter widget
551 512 *
552 513 * @param target: filter input target
553 514 * @param nodes: list of nodes in html we want to filter.
554 515 * @param display_element function that takes current node from nodes and
555 516 * does hide or show based on the node
556 517 */
557 518 var q_filter = (function() {
558 519 var _namespace = {};
559 520 var namespace = function (target) {
560 521 if (!(target in _namespace)) {
561 522 _namespace[target] = {};
562 523 }
563 524 return _namespace[target];
564 525 };
565 526 return function (target, $nodes, display_element) {
566 527 var $nodes = $nodes;
567 528 var $q_filter_field = $('#' + target);
568 529 var F = namespace(target);
569 530
570 531 $q_filter_field.keyup(function (e) {
571 532 clearTimeout(F.filterTimeout);
572 533 F.filterTimeout = setTimeout(F.updateFilter, 600);
573 534 });
574 535
575 536 F.filterTimeout = null;
576 537
577 538 F.updateFilter = function () {
578 539 // Reset timeout
579 540 F.filterTimeout = null;
580 541
581 542 var obsolete = [];
582 543
583 544 var req = $q_filter_field.val().toLowerCase();
584 545
585 546 var showing = 0;
586 547 $nodes.each(function () {
587 548 var n = this;
588 549 var target_element = display_element(n);
589 550 if (req && n.innerHTML.toLowerCase().indexOf(req) == -1) {
590 551 $(target_element).hide();
591 552 }
592 553 else {
593 554 $(target_element).show();
594 555 showing += 1;
595 556 }
596 557 });
597 558
598 559 $('#repo_count').html(showing);
599 560 /* FIXME: don't hardcode */
600 561 }
601 562 }
602 563 })();
603 564
604 565
605 566 /**
606 567 * Comment handling
607 568 */
608 569
609 570 // move comments to their right location, inside new trs
610 571 function move_comments($anchorcomments) {
611 572 $anchorcomments.each(function(i, anchorcomment) {
612 573 var $anchorcomment = $(anchorcomment);
613 574 var target_id = $anchorcomment.data('target-id');
614 575 var $comment_div = _get_add_comment_div(target_id);
615 576 var f_path = $anchorcomment.data('f_path');
616 577 var line_no = $anchorcomment.data('line_no');
617 578 if ($comment_div[0]) {
618 579 $comment_div.append($anchorcomment.children());
619 580 if (f_path && line_no) {
620 581 _comment_div_append_add($comment_div, f_path, line_no);
621 582 } else {
622 583 _comment_div_append_form($comment_div, f_path, line_no);
623 584 }
624 585 } else {
625 586 $anchorcomment.before("Comment to {0} line {1} which is outside the diff context:".format(f_path || '?', line_no || '?'));
626 587 }
627 588 });
628 589 linkInlineComments($('.firstlink'), $('.comment:first-child'));
629 590 }
630 591
631 592 // comment bubble was clicked - insert new tr and show form
632 593 function show_comment_form($bubble) {
633 594 var children = $bubble.closest('tr.line').children('[id]');
634 595 var line_td_id = children[children.length - 1].id;
635 596 var $comment_div = _get_add_comment_div(line_td_id);
636 597 var f_path = $bubble.closest('[data-f_path]').data('f_path');
637 598 var parts = line_td_id.split('_');
638 599 var line_no = parts[parts.length-1];
639 600 comment_div_state($comment_div, f_path, line_no, true);
640 601 }
641 602
642 603 // return comment div for target_id - add it if it doesn't exist yet
643 604 function _get_add_comment_div(target_id) {
644 605 var comments_box_id = 'comments-' + target_id;
645 606 var $comments_box = $('#' + comments_box_id);
646 607 if (!$comments_box.length) {
647 608 var html = '<tr><td id="{0}" colspan="3" class="inline-comments"></td></tr>'.format(comments_box_id);
648 609 $('#' + target_id).closest('tr').after(html);
649 610 $comments_box = $('#' + comments_box_id);
650 611 }
651 612 return $comments_box;
652 613 }
653 614
654 615 // Set $comment_div state - showing or not showing form and Add button.
655 616 // An Add button is shown on non-empty forms when no form is shown.
656 617 // The form is controlled by show_form - if undefined, form is only shown for general comments.
657 618 function comment_div_state($comment_div, f_path, line_no, show_form_opt) {
658 619 var show_form = show_form_opt !== undefined ? show_form_opt : !f_path && !line_no;
659 620 var $forms = $comment_div.children('.comment-inline-form');
660 621 var $buttonrow = $comment_div.children('.add-button-row');
661 622 var $comments = $comment_div.children('.comment');
662 623 $forms.remove();
663 624 $buttonrow.remove();
664 625 if (show_form) {
665 626 _comment_div_append_form($comment_div, f_path, line_no);
666 627 } else if ($comments.length) {
667 628 _comment_div_append_add($comment_div, f_path, line_no);
668 629 }
669 630 }
670 631
671 632 // append an Add button to $comment_div and hook it up to show form
672 633 function _comment_div_append_add($comment_div, f_path, line_no) {
673 634 var addlabel = TRANSLATION_MAP['Add Another Comment'];
674 635 var $add = $('<div class="add-button-row"><span class="btn btn-default btn-xs add-button">{0}</span></div>'.format(addlabel));
675 636 $comment_div.append($add);
676 637 $add.children('.add-button').click(function(e) {
677 638 comment_div_state($comment_div, f_path, line_no, true);
678 639 });
679 640 }
680 641
681 642 // append a comment form to $comment_div
682 643 function _comment_div_append_form($comment_div, f_path, line_no) {
683 644 var $form_div = $('#comment-inline-form-template').children()
684 645 .clone()
685 646 .addClass('comment-inline-form');
686 647 $comment_div.append($form_div);
687 648 var $form = $comment_div.find("form");
688 649 var $textarea = $form.find('textarea');
689 650 var $mentions_container = $form.find('div.mentions-container');
690 651
691 652 $form.submit(function(e) {
692 653 e.preventDefault();
693 654
694 655 var text = $textarea.val();
695 656 var review_status = $form.find('input:radio[name=changeset_status]:checked').val();
696 657 var pr_close = $form.find('input:checkbox[name=save_close]:checked').length ? 'on' : '';
697 658 var pr_delete = $form.find('input:checkbox[name=save_delete]:checked').length ? 'delete' : '';
698 659
699 660 if (!text && !review_status && !pr_close && !pr_delete) {
700 661 alert("Please provide a comment");
701 662 return false;
702 663 }
703 664
704 665 if (pr_delete) {
705 666 if (text || review_status || pr_close) {
706 667 alert('Cannot delete pull request while making other changes');
707 668 return false;
708 669 }
709 670 if (!confirm('Confirm to delete this pull request')) {
710 671 return false;
711 672 }
712 673 var comments = $('.comment').size();
713 674 if (comments > 0 &&
714 675 !confirm('Confirm again to delete this pull request with {0} comments'.format(comments))) {
715 676 return false;
716 677 }
717 678 }
718 679
719 680 $form.find('.submitting-overlay').show();
720 681
721 682 var postData = {
722 683 'text': text,
723 684 'f_path': f_path,
724 685 'line': line_no,
725 686 'changeset_status': review_status,
726 687 'save_close': pr_close,
727 688 'save_delete': pr_delete
728 689 };
729 690 var success = function(json_data) {
730 691 if (pr_delete) {
731 692 location = json_data['location'];
732 693 } else {
733 694 $comment_div.append(json_data['rendered_text']);
734 695 comment_div_state($comment_div, f_path, line_no);
735 696 linkInlineComments($('.firstlink'), $('.comment:first-child'));
736 697 if ((review_status || pr_close) && !f_path && !line_no) {
737 698 // Page changed a lot - reload it after closing the submitted form
738 699 comment_div_state($comment_div, f_path, line_no, false);
739 700 location.reload(true);
740 701 }
741 702 }
742 703 };
743 704 ajaxPOST(AJAX_COMMENT_URL, postData, success);
744 705 });
745 706
746 707 // create event for hide button
747 708 $form.find('.hide-inline-form').click(function(e) {
748 709 comment_div_state($comment_div, f_path, line_no);
749 710 });
750 711
751 712 tooltip_activate();
752 713 if ($textarea.length > 0) {
753 714 MentionsAutoComplete($textarea, $mentions_container, _USERS_AC_DATA);
754 715 }
755 716 if (f_path) {
756 717 $textarea.focus();
757 718 }
758 719 }
759 720
760 721
761 722 function deleteComment(comment_id) {
762 723 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
763 724 var postData = {};
764 725 var success = function(o) {
765 726 $('#comment-'+comment_id).remove();
766 727 // Ignore that this might leave a stray Add button (or have a pending form with another comment) ...
767 728 }
768 729 ajaxPOST(url, postData, success);
769 730 }
770 731
771 732
772 733 /**
773 734 * Double link comments
774 735 */
775 736 var linkInlineComments = function($firstlinks, $comments){
776 737 if ($comments.length > 0) {
777 738 $firstlinks.html('<a href="#{0}">First comment</a>'.format($comments.prop('id')));
778 739 }
779 740 if ($comments.length <= 1) {
780 741 return;
781 742 }
782 743
783 744 $comments.each(function(i, e){
784 745 var prev = '';
785 746 if (i > 0){
786 747 var prev_anchor = $($comments.get(i-1)).prop('id');
787 748 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
788 749 }
789 750 var next = '';
790 751 if (i+1 < $comments.length){
791 752 var next_anchor = $($comments.get(i+1)).prop('id');
792 753 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
793 754 }
794 755 $(this).find('.comment-prev-next-links').html(
795 756 '<div class="prev-comment">{0}</div>'.format(prev) +
796 757 '<div class="next-comment">{0}</div>'.format(next));
797 758 });
798 759 }
799 760
800 761 /* activate files.html stuff */
801 762 var fileBrowserListeners = function(current_url, node_list_url, url_base){
802 763 var current_url_branch = "?branch=__BRANCH__";
803 764
804 765 $('#stay_at_branch').on('click',function(e){
805 766 if(e.currentTarget.checked){
806 767 var uri = current_url_branch;
807 768 uri = uri.replace('__BRANCH__',e.currentTarget.value);
808 769 window.location = uri;
809 770 }
810 771 else{
811 772 window.location = current_url;
812 773 }
813 774 });
814 775
815 776 var $node_filter = $('#node_filter');
816 777
817 778 var filterTimeout = null;
818 779 var nodes = null;
819 780
820 781 var initFilter = function(){
821 782 $('#node_filter_box_loading').show();
822 783 $('#search_activate_id').hide();
823 784 $('#add_node_id').hide();
824 785 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
825 786 .done(function(json) {
826 787 nodes = json.nodes;
827 788 $('#node_filter_box_loading').hide();
828 789 $('#node_filter_box').show();
829 790 $node_filter.focus();
830 791 if($node_filter.hasClass('init')){
831 792 $node_filter.val('');
832 793 $node_filter.removeClass('init');
833 794 }
834 795 })
835 796 .fail(function() {
836 797 console.log('fileBrowserListeners initFilter failed to load');
837 798 })
838 799 ;
839 800 }
840 801
841 802 var updateFilter = function(e) {
842 803 return function(){
843 804 // Reset timeout
844 805 filterTimeout = null;
845 806 var query = e.currentTarget.value.toLowerCase();
846 807 var match = [];
847 808 var matches = 0;
848 809 var matches_max = 20;
849 810 if (query != ""){
850 811 for(var i=0;i<nodes.length;i++){
851 812 var pos = nodes[i].name.toLowerCase().indexOf(query);
852 813 if(query && pos != -1){
853 814 matches++
854 815 //show only certain amount to not kill browser
855 816 if (matches > matches_max){
856 817 break;
857 818 }
858 819
859 820 var n = nodes[i].name;
860 821 var t = nodes[i].type;
861 822 var n_hl = n.substring(0,pos)
862 823 + "<b>{0}</b>".format(n.substring(pos,pos+query.length))
863 824 + n.substring(pos+query.length);
864 825 var new_url = url_base.replace('__FPATH__',n);
865 826 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
866 827 }
867 828 if(match.length >= matches_max){
868 829 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
869 830 break;
870 831 }
871 832 }
872 833 }
873 834 if(query != ""){
874 835 $('#tbody').hide();
875 836 $('#tbody_filtered').show();
876 837
877 838 if (match.length==0){
878 839 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
879 840 }
880 841
881 842 $('#tbody_filtered').html(match.join(""));
882 843 }
883 844 else{
884 845 $('#tbody').show();
885 846 $('#tbody_filtered').hide();
886 847 }
887 848 }
888 849 };
889 850
890 851 $('#filter_activate').click(function(){
891 852 initFilter();
892 853 });
893 854 $node_filter.click(function(){
894 855 if($node_filter.hasClass('init')){
895 856 $node_filter.val('');
896 857 $node_filter.removeClass('init');
897 858 }
898 859 });
899 860 $node_filter.keyup(function(e){
900 861 clearTimeout(filterTimeout);
901 862 filterTimeout = setTimeout(updateFilter(e),600);
902 863 });
903 864 };
904 865
905 866
906 867 var initCodeMirror = function(textarea_id, baseUrl, resetUrl){
907 868 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
908 869 mode: "null",
909 870 lineNumbers: true,
910 871 indentUnit: 4,
911 872 autofocus: true
912 873 });
913 874 CodeMirror.modeURL = baseUrl + "/codemirror/mode/%N/%N.js";
914 875
915 876 $('#reset').click(function(e){
916 877 window.location=resetUrl;
917 878 });
918 879
919 880 $('#file_enable').click(function(){
920 881 $('#editor_container').show();
921 882 $('#upload_file_container').hide();
922 883 $('#filename_container').show();
923 884 $('#mimetype_header').show();
924 885 });
925 886
926 887 $('#upload_file_enable').click(function(){
927 888 $('#editor_container').hide();
928 889 $('#upload_file_container').show();
929 890 $('#filename_container').hide();
930 891 $('#mimetype_header').hide();
931 892 });
932 893
933 894 return myCodeMirror
934 895 };
935 896
936 897 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
937 898 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
938 899 }
939 900
940 901
941 902 var _getIdentNode = function(n){
942 903 //iterate thrugh nodes until matching interesting node
943 904
944 905 if (typeof n == 'undefined'){
945 906 return -1
946 907 }
947 908
948 909 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
949 910 return n
950 911 }
951 912 else{
952 913 return _getIdentNode(n.parentNode);
953 914 }
954 915 };
955 916
956 917 /* generate links for multi line selects that can be shown by files.html page_highlights.
957 918 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
958 919 var getSelectionLink = function(e) {
959 920 //get selection from start/to nodes
960 921 if (typeof window.getSelection != "undefined") {
961 922 var s = window.getSelection();
962 923
963 924 var from = _getIdentNode(s.anchorNode);
964 925 var till = _getIdentNode(s.focusNode);
965 926
966 927 var f_int = parseInt(from.id.replace('L',''));
967 928 var t_int = parseInt(till.id.replace('L',''));
968 929
969 930 var yoffset = 35;
970 931 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
971 932 if (ranges[0] > ranges[1]){
972 933 //highlight from bottom
973 934 yoffset = -yoffset;
974 935 ranges = [ranges[1], ranges[0]];
975 936 }
976 937 var $hl_div = $('div#linktt');
977 938 // if we select more than 2 lines
978 939 if (ranges[0] != ranges[1]){
979 940 if ($hl_div.length) {
980 941 $hl_div.html('');
981 942 } else {
982 943 $hl_div = $('<div id="linktt" class="hl-tip-box">');
983 944 $('body').prepend($hl_div);
984 945 }
985 946
986 947 $hl_div.append($('<a>').html(_TM['Selection Link']).prop('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
987 948 var xy = $(till).offset();
988 949 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
989 950 $hl_div.show();
990 951 }
991 952 else{
992 953 $hl_div.hide();
993 954 }
994 955 }
995 956 };
996 957
997 958 var deleteNotification = function(url, notification_id, callbacks){
998 959 var success = function(o){
999 960 $("#notification_"+notification_id).remove();
1000 961 _run_callbacks(callbacks);
1001 962 };
1002 963 var failure = function(o){
1003 964 alert("deleteNotification failure");
1004 965 };
1005 966 var postData = {};
1006 967 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1007 968 ajaxPOST(sUrl, postData, success, failure);
1008 969 };
1009 970
1010 971 var readNotification = function(url, notification_id, callbacks){
1011 972 var success = function(o){
1012 973 var $obj = $("#notification_"+notification_id);
1013 974 $obj.removeClass('unread');
1014 975 $obj.find('.read-notification').remove();
1015 976 _run_callbacks(callbacks);
1016 977 };
1017 978 var failure = function(o){
1018 979 alert("readNotification failure");
1019 980 };
1020 981 var postData = {};
1021 982 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1022 983 ajaxPOST(sUrl, postData, success, failure);
1023 984 };
1024 985
1025 986 /**
1026 987 * Autocomplete functionality
1027 988 */
1028 989
1029 990 // Custom search function for the DataSource of users
1030 991 var autocompleteMatchUsers = function (sQuery, myUsers) {
1031 992 // Case insensitive matching
1032 993 var query = sQuery.toLowerCase();
1033 994 var i = 0;
1034 995 var l = myUsers.length;
1035 996 var matches = [];
1036 997
1037 998 // Match against each name of each contact
1038 999 for (; i < l; i++) {
1039 1000 var contact = myUsers[i];
1040 1001 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1041 1002 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1042 1003 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1043 1004 matches[matches.length] = contact;
1044 1005 }
1045 1006 }
1046 1007 return matches;
1047 1008 };
1048 1009
1049 1010 // Custom search function for the DataSource of userGroups
1050 1011 var autocompleteMatchGroups = function (sQuery, myGroups) {
1051 1012 // Case insensitive matching
1052 1013 var query = sQuery.toLowerCase();
1053 1014 var i = 0;
1054 1015 var l = myGroups.length;
1055 1016 var matches = [];
1056 1017
1057 1018 // Match against each name of each group
1058 1019 for (; i < l; i++) {
1059 1020 var matched_group = myGroups[i];
1060 1021 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1061 1022 matches[matches.length] = matched_group;
1062 1023 }
1063 1024 }
1064 1025 return matches;
1065 1026 };
1066 1027
1067 1028 // Helper highlight function for the formatter
1068 1029 var autocompleteHighlightMatch = function (full, snippet, matchindex) {
1069 1030 return full.substring(0, matchindex)
1070 1031 + "<span class='match'>"
1071 1032 + full.substr(matchindex, snippet.length)
1072 1033 + "</span>" + full.substring(matchindex + snippet.length);
1073 1034 };
1074 1035
1075 1036 // Return html snippet for showing the provided gravatar url
1076 1037 var gravatar = function(gravatar_lnk, size, cssclass) {
1077 1038 if (!gravatar_lnk) {
1078 1039 return '';
1079 1040 }
1080 1041 if (gravatar_lnk == 'default') {
1081 1042 return '<i class="icon-user {1}" style="font-size: {0}px;"></i>'.format(size, cssclass);
1082 1043 }
1083 1044 return '<img alt="" class="{2}" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, gravatar_lnk, cssclass);
1084 1045 }
1085 1046
1086 1047 var autocompleteGravatar = function(res, gravatar_lnk, size, group) {
1087 1048 var elem;
1088 1049 if (group !== undefined) {
1089 1050 elem = '<i class="perm-gravatar-ac icon-users"></i>';
1090 1051 } else {
1091 1052 elem = gravatar(gravatar_lnk, size, "perm-gravatar-ac");
1092 1053 }
1093 1054 return '<div class="ac-container-wrap">{0}{1}</div>'.format(elem, res);
1094 1055 }
1095 1056
1096 1057 // Custom formatter to highlight the matching letters
1097 1058 var autocompleteFormatter = function (oResultData, sQuery, sResultMatch) {
1098 1059 var query = sQuery.toLowerCase();
1099 1060
1100 1061 // group
1101 1062 if (oResultData.grname != undefined) {
1102 1063 var grname = oResultData.grname;
1103 1064 var grmembers = oResultData.grmembers;
1104 1065 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1105 1066 var grprefix = "{0}: ".format(_TM['Group']);
1106 1067 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1107 1068
1108 1069 if (grnameMatchIndex > -1) {
1109 1070 return autocompleteGravatar(grprefix + autocompleteHighlightMatch(grname, query, grnameMatchIndex) + grsuffix, null, null, true);
1110 1071 }
1111 1072 return autocompleteGravatar(grprefix + oResultData.grname + grsuffix, null, null, true);
1112 1073
1113 1074 // users
1114 1075 } else if (oResultData.nname != undefined) {
1115 1076 var fname = oResultData.fname || "";
1116 1077 var lname = oResultData.lname || "";
1117 1078 var nname = oResultData.nname;
1118 1079
1119 1080 // Guard against null value
1120 1081 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1121 1082 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1122 1083 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1123 1084 displayfname, displaylname, displaynname, displayname;
1124 1085
1125 1086 if (fnameMatchIndex > -1) {
1126 1087 displayfname = autocompleteHighlightMatch(fname, query, fnameMatchIndex);
1127 1088 } else {
1128 1089 displayfname = fname;
1129 1090 }
1130 1091
1131 1092 if (lnameMatchIndex > -1) {
1132 1093 displaylname = autocompleteHighlightMatch(lname, query, lnameMatchIndex);
1133 1094 } else {
1134 1095 displaylname = lname;
1135 1096 }
1136 1097
1137 1098 if (nnameMatchIndex > -1) {
1138 1099 displaynname = autocompleteHighlightMatch(nname, query, nnameMatchIndex);
1139 1100 } else {
1140 1101 displaynname = nname;
1141 1102 }
1142 1103
1143 1104 displayname = displaynname;
1144 1105 if (displayfname && displaylname) {
1145 1106 displayname = "{0} {1} ({2})".format(displayfname, displaylname, displayname);
1146 1107 }
1147 1108
1148 1109 return autocompleteGravatar(displayname, oResultData.gravatar_lnk, oResultData.gravatar_size);
1149 1110 } else {
1150 1111 return '';
1151 1112 }
1152 1113 };
1153 1114
1154 1115 // Generate a basic autocomplete instance that can be tweaked further by the caller
1155 1116 var autocompleteCreate = function ($inputElement, $container, matchFunc) {
1156 1117 var datasource = new YAHOO.util.FunctionDataSource(matchFunc);
1157 1118
1158 1119 var autocomplete = new YAHOO.widget.AutoComplete($inputElement[0], $container[0], datasource);
1159 1120 autocomplete.useShadow = false;
1160 1121 autocomplete.resultTypeList = false;
1161 1122 autocomplete.animVert = false;
1162 1123 autocomplete.animHoriz = false;
1163 1124 autocomplete.animSpeed = 0.1;
1164 1125 autocomplete.formatResult = autocompleteFormatter;
1165 1126
1166 1127 return autocomplete;
1167 1128 }
1168 1129
1169 1130 var SimpleUserAutoComplete = function ($inputElement, $container, users_list) {
1170 1131
1171 1132 var matchUsers = function (sQuery) {
1172 1133 return autocompleteMatchUsers(sQuery, users_list);
1173 1134 }
1174 1135
1175 1136 var userAC = autocompleteCreate($inputElement, $container, matchUsers);
1176 1137
1177 1138 // Handler for selection of an entry
1178 1139 var itemSelectHandler = function (sType, aArgs) {
1179 1140 var myAC = aArgs[0]; // reference back to the AC instance
1180 1141 var elLI = aArgs[1]; // reference to the selected LI element
1181 1142 var oData = aArgs[2]; // object literal of selected item's result data
1182 1143 myAC.getInputEl().value = oData.nname;
1183 1144 };
1184 1145 userAC.itemSelectEvent.subscribe(itemSelectHandler);
1185 1146 }
1186 1147
1187 1148 var MembersAutoComplete = function ($inputElement, $container, users_list, groups_list) {
1188 1149
1189 1150 var matchAll = function (sQuery) {
1190 1151 var u = autocompleteMatchUsers(sQuery, users_list);
1191 1152 var g = autocompleteMatchGroups(sQuery, groups_list);
1192 1153 return u.concat(g);
1193 1154 };
1194 1155
1195 1156 var membersAC = autocompleteCreate($inputElement, $container, matchAll);
1196 1157
1197 1158 // Handler for selection of an entry
1198 1159 var itemSelectHandler = function (sType, aArgs) {
1199 1160 var nextId = $inputElement.prop('id').split('perm_new_member_name_')[1];
1200 1161 var myAC = aArgs[0]; // reference back to the AC instance
1201 1162 var elLI = aArgs[1]; // reference to the selected LI element
1202 1163 var oData = aArgs[2]; // object literal of selected item's result data
1203 1164 //fill the autocomplete with value
1204 1165 if (oData.nname != undefined) {
1205 1166 //users
1206 1167 myAC.getInputEl().value = oData.nname;
1207 1168 $('#perm_new_member_type_'+nextId).val('user');
1208 1169 } else {
1209 1170 //groups
1210 1171 myAC.getInputEl().value = oData.grname;
1211 1172 $('#perm_new_member_type_'+nextId).val('users_group');
1212 1173 }
1213 1174 };
1214 1175 membersAC.itemSelectEvent.subscribe(itemSelectHandler);
1215 1176 }
1216 1177
1217 1178 var MentionsAutoComplete = function ($inputElement, $container, users_list) {
1218 1179
1219 1180 var matchUsers = function (sQuery) {
1220 1181 var org_sQuery = sQuery;
1221 1182 if(this.mentionQuery == null){
1222 1183 return []
1223 1184 }
1224 1185 sQuery = this.mentionQuery;
1225 1186 return autocompleteMatchUsers(sQuery, users_list);
1226 1187 }
1227 1188
1228 1189 var mentionsAC = autocompleteCreate($inputElement, $container, matchUsers);
1229 1190 mentionsAC.suppressInputUpdate = true;
1230 1191 // Overwrite formatResult to take into account mentionQuery
1231 1192 mentionsAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1232 1193 var org_sQuery = sQuery;
1233 1194 if (this.dataSource.mentionQuery != null) {
1234 1195 sQuery = this.dataSource.mentionQuery;
1235 1196 }
1236 1197 return autocompleteFormatter(oResultData, sQuery, sResultMatch);
1237 1198 }
1238 1199
1239 1200 // Handler for selection of an entry
1240 1201 if(mentionsAC.itemSelectEvent){
1241 1202 mentionsAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1242 1203 var myAC = aArgs[0]; // reference back to the AC instance
1243 1204 var elLI = aArgs[1]; // reference to the selected LI element
1244 1205 var oData = aArgs[2]; // object literal of selected item's result data
1245 1206 //Replace the mention name with replaced
1246 1207 var re = new RegExp();
1247 1208 var org = myAC.getInputEl().value;
1248 1209 var chunks = myAC.dataSource.chunks
1249 1210 // replace middle chunk(the search term) with actuall match
1250 1211 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1251 1212 '@'+oData.nname+' ');
1252 1213 myAC.getInputEl().value = chunks.join('');
1253 1214 myAC.getInputEl().focus(); // Y U NO WORK !?
1254 1215 });
1255 1216 }
1256 1217
1257 1218 // in this keybuffer we will gather current value of search !
1258 1219 // since we need to get this just when someone does `@` then we do the
1259 1220 // search
1260 1221 mentionsAC.dataSource.chunks = [];
1261 1222 mentionsAC.dataSource.mentionQuery = null;
1262 1223
1263 1224 mentionsAC.get_mention = function(msg, max_pos) {
1264 1225 var org = msg;
1265 1226 // Must match utils2.py MENTIONS_REGEX.
1266 1227 // Only matching on string up to cursor, so it must end with $
1267 1228 var re = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$');
1268 1229 var chunks = [];
1269 1230
1270 1231 // cut first chunk until current pos
1271 1232 var to_max = msg.substr(0, max_pos);
1272 1233 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1273 1234 var msg2 = to_max.substr(at_pos);
1274 1235
1275 1236 chunks.push(org.substr(0,at_pos)); // prefix chunk
1276 1237 chunks.push(msg2); // search chunk
1277 1238 chunks.push(org.substr(max_pos)); // postfix chunk
1278 1239
1279 1240 // clean up msg2 for filtering and regex match
1280 1241 var msg2 = msg2.lstrip(' ').lstrip('\n');
1281 1242
1282 1243 if(re.test(msg2)){
1283 1244 var unam = re.exec(msg2)[1];
1284 1245 return [unam, chunks];
1285 1246 }
1286 1247 return [null, null];
1287 1248 };
1288 1249
1289 1250 $inputElement.keyup(function(e){
1290 1251 var currentMessage = $inputElement.val();
1291 1252 var currentCaretPosition = $inputElement[0].selectionStart;
1292 1253
1293 1254 var unam = mentionsAC.get_mention(currentMessage, currentCaretPosition);
1294 1255 var curr_search = null;
1295 1256 if(unam[0]){
1296 1257 curr_search = unam[0];
1297 1258 }
1298 1259
1299 1260 mentionsAC.dataSource.chunks = unam[1];
1300 1261 mentionsAC.dataSource.mentionQuery = curr_search;
1301 1262 });
1302 1263 }
1303 1264
1304 1265 var addReviewMember = function(id,fname,lname,nname,gravatar_link,gravatar_size){
1305 1266 var displayname = nname;
1306 1267 if ((fname != "") && (lname != "")) {
1307 1268 displayname = "{0} {1} ({2})".format(fname, lname, nname);
1308 1269 }
1309 1270 var gravatarelm = gravatar(gravatar_link, gravatar_size, "");
1310 1271 // WARNING: the HTML below is duplicate with
1311 1272 // kallithea/templates/pullrequests/pullrequest_show.html
1312 1273 // If you change something here it should be reflected in the template too.
1313 1274 var element = (
1314 1275 ' <li id="reviewer_{2}">\n'+
1315 1276 ' <span class="reviewers_member">\n'+
1316 1277 ' <span class="reviewer_status" data-toggle="tooltip" title="not_reviewed">\n'+
1317 1278 ' <i class="icon-circle changeset-status-not_reviewed"></i>\n'+
1318 1279 ' </span>\n'+
1319 1280 (gravatarelm ?
1320 1281 ' {0}\n' :
1321 1282 '')+
1322 1283 ' <span>{1}</span>\n'+
1323 1284 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1324 1285 ' <a href="#" class="reviewer_member_remove" onclick="removeReviewMember({2})">\n'+
1325 1286 ' <i class="icon-minus-circled"></i>\n'+
1326 1287 ' </a> (add not saved)\n'+
1327 1288 ' </span>\n'+
1328 1289 ' </li>\n'
1329 1290 ).format(gravatarelm, displayname, id);
1330 1291 // check if we don't have this ID already in
1331 1292 var ids = [];
1332 1293 $('#review_members').find('li').each(function() {
1333 1294 ids.push(this.id);
1334 1295 });
1335 1296 if(ids.indexOf('reviewer_'+id) == -1){
1336 1297 //only add if it's not there
1337 1298 $('#review_members').append(element);
1338 1299 }
1339 1300 }
1340 1301
1341 1302 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1342 1303 var $li = $('#reviewer_{0}'.format(reviewer_id));
1343 1304 $li.find('div div').css("text-decoration", "line-through");
1344 1305 $li.find('input').prop('name', 'review_members_removed');
1345 1306 $li.find('.reviewer_member_remove').replaceWith('&nbsp;(remove not saved)');
1346 1307 }
1347 1308
1348 1309 /* activate auto completion of users as PR reviewers */
1349 1310 var PullRequestAutoComplete = function ($inputElement, $container, users_list) {
1350 1311
1351 1312 var matchUsers = function (sQuery) {
1352 1313 return autocompleteMatchUsers(sQuery, users_list);
1353 1314 };
1354 1315
1355 1316 var reviewerAC = autocompleteCreate($inputElement, $container, matchUsers);
1356 1317 reviewerAC.suppressInputUpdate = true;
1357 1318
1358 1319 // Handler for selection of an entry
1359 1320 if(reviewerAC.itemSelectEvent){
1360 1321 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1361 1322 var myAC = aArgs[0]; // reference back to the AC instance
1362 1323 var elLI = aArgs[1]; // reference to the selected LI element
1363 1324 var oData = aArgs[2]; // object literal of selected item's result data
1364 1325
1365 1326 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1366 1327 oData.gravatar_lnk, oData.gravatar_size);
1367 1328 myAC.getInputEl().value = '';
1368 1329 });
1369 1330 }
1370 1331 }
1371 1332
1372 1333
1373 1334 var addPermAction = function(_html, users_list, groups_list){
1374 1335 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1375 1336 var next_id = $('.new_members').length;
1376 1337 $last_node.before($('<tr class="new_members">').append(_html.format(next_id)));
1377 1338 MembersAutoComplete($("#perm_new_member_name_"+next_id),
1378 1339 $("#perm_container_"+next_id), users_list, groups_list);
1379 1340 }
1380 1341
1381 1342 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1382 1343 var success = function (o) {
1383 1344 $('#' + field_id).remove();
1384 1345 };
1385 1346 var failure = function (o) {
1386 1347 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1387 1348 };
1388 1349 var query_params = {};
1389 1350 // put extra data into POST
1390 1351 if (extra_data !== undefined && (typeof extra_data === 'object')){
1391 1352 for(var k in extra_data){
1392 1353 query_params[k] = extra_data[k];
1393 1354 }
1394 1355 }
1395 1356
1396 1357 if (obj_type=='user'){
1397 1358 query_params['user_id'] = obj_id;
1398 1359 query_params['obj_type'] = 'user';
1399 1360 }
1400 1361 else if (obj_type=='user_group'){
1401 1362 query_params['user_group_id'] = obj_id;
1402 1363 query_params['obj_type'] = 'user_group';
1403 1364 }
1404 1365
1405 1366 ajaxPOST(url, query_params, success, failure);
1406 1367 };
1407 1368
1408 1369 /* Multi selectors */
1409 1370
1410 1371 var MultiSelectWidget = function(selected_id, available_id, form_id){
1411 1372 var $availableselect = $('#' + available_id);
1412 1373 var $selectedselect = $('#' + selected_id);
1413 1374
1414 1375 //fill available only with those not in selected
1415 1376 var $selectedoptions = $selectedselect.children('option');
1416 1377 $availableselect.children('option').filter(function(i, e){
1417 1378 for(var j = 0, node; node = $selectedoptions[j]; j++){
1418 1379 if(node.value == e.value){
1419 1380 return true;
1420 1381 }
1421 1382 }
1422 1383 return false;
1423 1384 }).remove();
1424 1385
1425 1386 $('#add_element').click(function(e){
1426 1387 $selectedselect.append($availableselect.children('option:selected'));
1427 1388 });
1428 1389 $('#remove_element').click(function(e){
1429 1390 $availableselect.append($selectedselect.children('option:selected'));
1430 1391 });
1431 1392
1432 1393 $('#'+form_id).submit(function(){
1433 1394 $selectedselect.children('option').each(function(i, e){
1434 1395 e.selected = 'selected';
1435 1396 });
1436 1397 });
1437 1398 }
1438 1399
1439 1400
1440 1401 /**
1441 1402 Branch Sorting callback for select2, modifying the filtered result so prefix
1442 1403 matches come before matches in the line.
1443 1404 **/
1444 1405 var branchSort = function(results, container, query) {
1445 1406 if (query.term) {
1446 1407 return results.sort(function (a, b) {
1447 1408 // Put closed branches after open ones (a bit of a hack ...)
1448 1409 var aClosed = a.text.indexOf("(closed)") > -1,
1449 1410 bClosed = b.text.indexOf("(closed)") > -1;
1450 1411 if (aClosed && !bClosed) {
1451 1412 return 1;
1452 1413 }
1453 1414 if (bClosed && !aClosed) {
1454 1415 return -1;
1455 1416 }
1456 1417
1457 1418 // Put early (especially prefix) matches before later matches
1458 1419 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1459 1420 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1460 1421 if (aPos < bPos) {
1461 1422 return -1;
1462 1423 }
1463 1424 if (bPos < aPos) {
1464 1425 return 1;
1465 1426 }
1466 1427
1467 1428 // Default sorting
1468 1429 if (a.text > b.text) {
1469 1430 return 1;
1470 1431 }
1471 1432 if (a.text < b.text) {
1472 1433 return -1;
1473 1434 }
1474 1435 return 0;
1475 1436 });
1476 1437 }
1477 1438 return results;
1478 1439 };
1479 1440
1480 1441 var prefixFirstSort = function(results, container, query) {
1481 1442 if (query.term) {
1482 1443 return results.sort(function (a, b) {
1483 1444 // if parent node, no sorting
1484 1445 if (a.children != undefined || b.children != undefined) {
1485 1446 return 0;
1486 1447 }
1487 1448
1488 1449 // Put prefix matches before matches in the line
1489 1450 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1490 1451 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1491 1452 if (aPos === 0 && bPos !== 0) {
1492 1453 return -1;
1493 1454 }
1494 1455 if (bPos === 0 && aPos !== 0) {
1495 1456 return 1;
1496 1457 }
1497 1458
1498 1459 // Default sorting
1499 1460 if (a.text > b.text) {
1500 1461 return 1;
1501 1462 }
1502 1463 if (a.text < b.text) {
1503 1464 return -1;
1504 1465 }
1505 1466 return 0;
1506 1467 });
1507 1468 }
1508 1469 return results;
1509 1470 };
1510 1471
1511 1472 /* Helper for jQuery DataTables */
1512 1473
1513 1474 var updateRowCountCallback = function updateRowCountCallback($elem, onlyDisplayed) {
1514 1475 return function drawCallback() {
1515 1476 var info = this.api().page.info(),
1516 1477 count = onlyDisplayed === true ? info.recordsDisplay : info.recordsTotal;
1517 1478 $elem.html(count);
1518 1479 }
1519 1480 };
@@ -1,64 +1,63 b''
1 1 ## -*- coding: utf-8 -*-
2 2 %if c.users_log:
3 3 <table class="table">
4 4 <tr>
5 5 <th class="left">${_('Username')}</th>
6 6 <th class="left">${_('Action')}</th>
7 7 <th class="left">${_('Repository')}</th>
8 8 <th class="left">${_('Date')}</th>
9 9 <th class="left">${_('From IP')}</th>
10 10 </tr>
11 11
12 12 %for cnt,l in enumerate(c.users_log):
13 13 <tr class="parity${cnt%2}">
14 14 <td>
15 15 %if l.user is not None:
16 16 ${h.link_to(l.user.username,h.url('edit_user', id=l.user.user_id))}
17 17 %else:
18 18 ${l.username}
19 19 %endif
20 20 </td>
21 21 <td>${h.action_parser(l)[0]()}
22 22 <div class="journal_action_params">
23 23 ${h.literal(h.action_parser(l)[1]())}
24 24 </div>
25 25 </td>
26 26 <td>
27 27 %if l.repository is not None:
28 28 ${h.link_to(l.repository.repo_name,h.url('summary_home',repo_name=l.repository.repo_name))}
29 29 %else:
30 30 ${l.repository_name}
31 31 %endif
32 32 </td>
33 33
34 34 <td>${h.fmt_date(l.action_date)}</td>
35 35 <td>${l.user_ip}</td>
36 36 </tr>
37 37 %endfor
38 38 </table>
39 39
40 40 <script type="text/javascript">
41 41 $(document).ready(function(){
42 42 var $user_log = $('#user_log');
43 43 $user_log.on('click','.pager_link',function(e){
44 44 asynchtml(e.target.href, $user_log, function(){
45 45 show_more_event();
46 46 tooltip_activate();
47 show_changeset_tooltip();
48 47 });
49 48 e.preventDefault();
50 49 });
51 50 $user_log.on('click','.show_more',function(e){
52 51 var el = e.target;
53 52 $('#'+el.id.substring(1)).show();
54 53 $(el.parentNode).hide();
55 54 });
56 55 });
57 56 </script>
58 57
59 58 <ul class="pagination">
60 59 ${c.users_log.pager()}
61 60 </ul>
62 61 %else:
63 62 ${_('No actions yet')}
64 63 %endif
@@ -1,136 +1,135 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html>
3 3 <html xmlns="http://www.w3.org/1999/xhtml">
4 4 <head>
5 5 <title><%block name="title"/><%block name="branding_title"/></title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <meta name="robots" content="index, nofollow"/>
8 8 <link rel="icon" href="${h.url('/images/favicon.ico')}" type="image/x-icon" />
9 9
10 10 ## CSS ###
11 11 <link rel="stylesheet" type="text/css" href="${h.url('/css/jquery.dataTables.css', ver=c.kallithea_version)}"/>
12 12 <link rel="stylesheet" type="text/css" href="${h.url('/js/select2/select2.css', ver=c.kallithea_version)}"/>
13 13 <link rel="stylesheet" type="text/css" href="${h.url('/css/pygments.css', ver=c.kallithea_version)}"/>
14 14 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css', ver=c.kallithea_version)}" media="screen"/>
15 15 <link rel="stylesheet" type="text/css" href="${h.url('/css/contextbar.css', ver=c.kallithea_version)}" media="screen"/>
16 16 <link rel="stylesheet" type="text/css" href="${h.url('/fontello/css/kallithea.css', ver=c.kallithea_version)}">
17 17 <%block name="css_extra"/>
18 18
19 19 ## JAVASCRIPT ##
20 20 <script type="text/javascript">
21 21 ## JS translations map
22 22 var TRANSLATION_MAP = {
23 23 'Add Another Comment':'${_("Add Another Comment")}',
24 24 'Stop following this repository':"${_('Stop following this repository')}",
25 25 'Start following this repository':"${_('Start following this repository')}",
26 26 'Group':"${_('Group')}",
27 27 'members':"${_('members')}",
28 28 'Loading ...':"${_('Loading ...')}",
29 29 'loading ...':"${_('loading ...')}",
30 30 'Search truncated': "${_('Search truncated')}",
31 31 'No matching files': "${_('No matching files')}",
32 32 'Open New Pull Request from {0}': "${_('Open New Pull Request from {0}')}",
33 33 'Open New Pull Request for {0} &rarr; {1}': "${h.literal(_('Open New Pull Request for {0} &rarr; {1}'))}",
34 34 'Show Selected Changesets {0} &rarr; {1}': "${h.literal(_('Show Selected Changesets {0} &rarr; {1}'))}",
35 35 'Selection Link': "${_('Selection Link')}",
36 36 'Collapse Diff': "${_('Collapse Diff')}",
37 37 'Expand Diff': "${_('Expand Diff')}",
38 38 'Failed to revoke permission': "${_('Failed to revoke permission')}",
39 39 'Confirm to revoke permission for {0}: {1} ?': "${_('Confirm to revoke permission for {0}: {1} ?')}",
40 40 'Enabled': "${_('Enabled')}",
41 41 'Disabled': "${_('Disabled')}",
42 42 'Select changeset': "${_('Select changeset')}",
43 43 'Specify changeset': "${_('Specify changeset')}",
44 44 'MSG_SORTASC': "${_('Click to sort ascending')}",
45 45 'MSG_SORTDESC': "${_('Click to sort descending')}",
46 46 'MSG_EMPTY': "${_('No records found.')}",
47 47 'MSG_ERROR': "${_('Data error.')}",
48 48 'MSG_LOADING': "${_('Loading...')}"
49 49 };
50 50 var _TM = TRANSLATION_MAP;
51 51
52 52 var TOGGLE_FOLLOW_URL = "${h.url('toggle_following')}";
53 53
54 54 var REPO_NAME = "";
55 55 %if hasattr(c, 'repo_name'):
56 56 var REPO_NAME = "${c.repo_name}";
57 57 %endif
58 58
59 59 var _authentication_token = "${h.authentication_token()}";
60 60 </script>
61 61 <script type="text/javascript" src="${h.url('/js/yui.2.9.js', ver=c.kallithea_version)}"></script>
62 62 <script type="text/javascript" src="${h.url('/js/jquery.min.js', ver=c.kallithea_version)}"></script>
63 63 <script type="text/javascript" src="${h.url('/js/jquery.dataTables.min.js', ver=c.kallithea_version)}"></script>
64 64 <script type="text/javascript" src="${h.url('/js/bootstrap.js', ver=c.kallithea_version)}"></script>
65 65 <script type="text/javascript" src="${h.url('/js/select2/select2.js', ver=c.kallithea_version)}"></script>
66 66 <script type="text/javascript" src="${h.url('/js/yui.flot.js', ver=c.kallithea_version)}"></script>
67 67 <script type="text/javascript" src="${h.url('/js/native.history.js', ver=c.kallithea_version)}"></script>
68 68 <script type="text/javascript" src="${h.url('/js/base.js', ver=c.kallithea_version)}"></script>
69 69 ## EXTRA FOR JS
70 70 <%block name="js_extra"/>
71 71 <script type="text/javascript">
72 72 (function(window,undefined){
73 73 var History = window.History; // Note: We are using a capital H instead of a lower h
74 74 if ( !History.enabled ) {
75 75 // History.js is disabled for this browser.
76 76 // This is because we can optionally choose to support HTML4 browsers or not.
77 77 return false;
78 78 }
79 79 })(window);
80 80
81 81 $(document).ready(function(){
82 82 tooltip_activate();
83 83 show_more_event();
84 show_changeset_tooltip();
85 84 // routes registration
86 85 pyroutes.register('home', "${h.url('home')}", []);
87 86 pyroutes.register('new_gist', "${h.url('new_gist')}", []);
88 87 pyroutes.register('gists', "${h.url('gists')}", []);
89 88 pyroutes.register('new_repo', "${h.url('new_repo')}", []);
90 89
91 90 pyroutes.register('summary_home', "${h.url('summary_home', repo_name='%(repo_name)s')}", ['repo_name']);
92 91 pyroutes.register('changelog_home', "${h.url('changelog_home', repo_name='%(repo_name)s')}", ['repo_name']);
93 92 pyroutes.register('files_home', "${h.url('files_home', repo_name='%(repo_name)s',revision='%(revision)s',f_path='%(f_path)s')}", ['repo_name', 'revision', 'f_path']);
94 93 pyroutes.register('edit_repo', "${h.url('edit_repo', repo_name='%(repo_name)s')}", ['repo_name']);
95 94 pyroutes.register('edit_repo_perms', "${h.url('edit_repo_perms', repo_name='%(repo_name)s')}", ['repo_name']);
96 95 pyroutes.register('pullrequest_home', "${h.url('pullrequest_home', repo_name='%(repo_name)s')}", ['repo_name']);
97 96
98 97 pyroutes.register('toggle_following', "${h.url('toggle_following')}");
99 98 pyroutes.register('changeset_info', "${h.url('changeset_info', repo_name='%(repo_name)s', revision='%(revision)s')}", ['repo_name', 'revision']);
100 99 pyroutes.register('repo_size', "${h.url('repo_size', repo_name='%(repo_name)s')}", ['repo_name']);
101 100 pyroutes.register('repo_refs_data', "${h.url('repo_refs_data', repo_name='%(repo_name)s')}", ['repo_name']);
102 101 });
103 102 </script>
104 103
105 104 <%block name="head_extra"/>
106 105 </head>
107 106 <body>
108 107 <nav class="navbar navbar-inverse">
109 108 <div>
110 109 <div class="navbar-header" id="logo">
111 110 <a class="navbar-brand" href="${h.url('home')}">
112 111 <img class="pull-left" src="${h.url('/images/kallithea-logo.svg')}" alt="Kallithea"/>
113 112 %if c.site_name:
114 113 <span class="branding">${c.site_name}</span>
115 114 %endif
116 115 </a>
117 116 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false">
118 117 <span class="sr-only">Toggle navigation</span>
119 118 <span class="icon-bar"></span>
120 119 <span class="icon-bar"></span>
121 120 <span class="icon-bar"></span>
122 121 </button>
123 122 </div>
124 123 <div id="navbar" class="navbar-collapse collapse">
125 124 <%block name="header_menu"/>
126 125 </div>
127 126 </div>
128 127 </nav>
129 128
130 129 ${next.body()}
131 130
132 131 %if c.ga_code:
133 132 ${h.literal(c.ga_code)}
134 133 %endif
135 134 </body>
136 135 </html>
@@ -1,161 +1,161 b''
1 1 ## DATA TABLE RE USABLE ELEMENTS
2 2 ## usage:
3 3 ## <%namespace name="dt" file="/data_table/_dt_elements.html"/>
4 4
5 5 <%namespace name="base" file="/base/base.html"/>
6 6
7 7 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)">
8 8 <%
9 9 def get_name(name,short_name=short_name):
10 10 if short_name:
11 11 return name.split('/')[-1]
12 12 else:
13 13 return name
14 14 %>
15 15 <div class="dt_repo ${'dt_repo_pending' if rstate == 'repo_state_pending' else ''}">
16 16 ##NAME
17 17 <a href="${h.url('edit_repo' if admin else 'summary_home', repo_name=name)}">
18 18
19 19 ##TYPE OF REPO
20 20 ${base.repotag(rtype)}
21 21
22 22 ##PRIVATE/PUBLIC
23 23 %if private and c.visual.show_private_icon:
24 24 <i class="icon-keyhole-circled" title="${_('Private repository')}"></i>
25 25 %elif not private and c.visual.show_public_icon:
26 26 <i class="icon-globe" title="${_('Public repository')}"></i>
27 27 %else:
28 28 <span style="margin: 0px 8px 0px 8px"></span>
29 29 %endif
30 30 <span class="dt_repo_name">${get_name(name)}</span>
31 31 </a>
32 32 %if fork_of:
33 33 <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-fork"></i></a>
34 34 %endif
35 35 %if rstate == 'repo_state_pending':
36 36 <i class="icon-wrench" title="${_('Repository creation in progress...')}"></i>
37 37 %endif
38 38 </div>
39 39 </%def>
40 40
41 41 <%def name="last_change(last_change)">
42 42 <span data-toggle="tooltip" title="${h.fmt_date(last_change)}" date="${last_change}">${h.age(last_change)}</span>
43 43 </%def>
44 44
45 45 <%def name="revision(name,rev,tip,author,last_msg)">
46 46 <div>
47 47 %if rev >= 0:
48 <a data-toggle="tooltip" title="${'%s:\n\n%s' % (h.escape(author), h.escape(last_msg))}" class="revision-link safe-html-title" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a>
48 <a data-toggle="popover" title="${author | entity}" data-content="${last_msg | entity}" class="hash" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a>
49 49 %else:
50 50 ${_('No changesets yet')}
51 51 %endif
52 52 </div>
53 53 </%def>
54 54
55 55 <%def name="rss(name)">
56 56 %if c.authuser.username != 'default':
57 57 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i></a>
58 58 %else:
59 59 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a>
60 60 %endif
61 61 </%def>
62 62
63 63 <%def name="atom(name)">
64 64 %if c.authuser.username != 'default':
65 65 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i></a>
66 66 %else:
67 67 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a>
68 68 %endif
69 69 </%def>
70 70
71 71 <%def name="repo_actions(repo_name, super_user=True)">
72 72 <div>
73 73 <div class="grid_edit pull-left">
74 74 <a href="${h.url('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
75 75 <i class="icon-pencil"></i> ${h.submit('edit_%s' % repo_name,_('Edit'),class_="btn btn-default btn-xs")}
76 76 </a>
77 77 </div>
78 78 <div class="grid_delete pull-left">
79 79 ${h.form(h.url('delete_repo', repo_name=repo_name))}
80 80 <i class="icon-minus-circled" style="color:#FF4444"></i>
81 81 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-default btn-xs",
82 82 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
83 83 ${h.end_form()}
84 84 </div>
85 85 </div>
86 86 </%def>
87 87
88 88 <%def name="repo_state(repo_state)">
89 89 <div>
90 90 %if repo_state == u'repo_state_pending':
91 91 <div class="label label-info">${_('Creating')}</div>
92 92 %elif repo_state == u'repo_state_created':
93 93 <div class="label label-success">${_('Created')}</div>
94 94 %else:
95 95 <div class="label label-danger" title="${repo_state}">invalid</div>
96 96 %endif
97 97 </div>
98 98 </%def>
99 99
100 100 <%def name="user_actions(user_id, username)">
101 101 <div class="grid_edit pull-left">
102 102 <a href="${h.url('edit_user',id=user_id)}" title="${_('Edit')}">
103 103 <i class="icon-pencil"></i> ${h.submit('edit_%s' % username,_('Edit'),class_="btn btn-default btn-xs")}
104 104 </a>
105 105 </div>
106 106 <div class="grid_delete pull-left">
107 107 ${h.form(h.url('delete_user', id=user_id))}
108 108 <i class="icon-minus-circled" style="color:#FF4444"></i>
109 109 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-default btn-xs",
110 110 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
111 111 ${h.end_form()}
112 112 </div>
113 113 </%def>
114 114
115 115 <%def name="user_group_actions(user_group_id, user_group_name)">
116 116 <div class="grid_edit pull-left">
117 117 <a href="${h.url('edit_users_group', id=user_group_id)}" title="${_('Edit')}">
118 118 <i class="icon-pencil"></i> ${h.submit('edit_%s' % user_group_name,_('Edit'),class_="btn btn-default btn-xs", id_="submit_user_group_edit")}
119 119 </a>
120 120 </div>
121 121 <div class="grid_delete pull-left">
122 122 ${h.form(h.url('delete_users_group', id=user_group_id))}
123 123 <i class="icon-minus-circled" style="color:#FF4444"></i>
124 124 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-default btn-xs",
125 125 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
126 126 ${h.end_form()}
127 127 </div>
128 128 </%def>
129 129
130 130 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
131 131 <div class="grid_edit pull-left">
132 132 <a href="${h.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}">
133 133 <i class="icon-pencil"></i> ${h.submit('edit_%s' % repo_group_name, _('Edit'),class_="btn btn-default btn-xs")}
134 134 </a>
135 135 </div>
136 136 <div class="grid_delete pull-left">
137 137 ${h.form(h.url('delete_repos_group', group_name=repo_group_name))}
138 138 <i class="icon-minus-circled" style="color:#FF4444"></i>
139 139 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-default btn-xs",
140 140 onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
141 141 ${h.end_form()}
142 142 </div>
143 143 </%def>
144 144
145 145 <%def name="user_name(user_id, username)">
146 146 ${h.link_to(username,h.url('edit_user', id=user_id))}
147 147 </%def>
148 148
149 149 <%def name="repo_group_name(repo_group_name, children_groups)">
150 150 <div class="text-nowrap">
151 151 <a href="${h.url('repos_group_home',group_name=repo_group_name)}">
152 152 <i class="icon-folder" title="${_('Repository group')}"></i> ${h.literal(' &raquo; '.join(children_groups))}</a>
153 153 </div>
154 154 </%def>
155 155
156 156 <%def name="user_group_name(user_group_id, user_group_name)">
157 157 <div class="text-nowrap">
158 158 <a href="${h.url('edit_users_group', id=user_group_id)}">
159 159 <i class="icon-users" title="${_('User group')}"></i> ${user_group_name}</a>
160 160 </div>
161 161 </%def>
@@ -1,99 +1,99 b''
1 1 <div id="node_history" style="padding: 0px 0px 10px 0px">
2 2 <div>
3 3 <div class="pull-left">
4 4 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
5 5 ${h.hidden('diff2',c.changeset.raw_id)}
6 6 ${h.hidden('diff1')}
7 7 ${h.submit('diff',_('Diff to Revision'),class_="btn btn-default btn-sm")}
8 8 ${h.submit('show_rev',_('Show at Revision'),class_="btn btn-default btn-sm")}
9 9 ${h.hidden('annotate', c.annotate)}
10 10 ${h.link_to(_('Show Full History'),h.url('changelog_file_home',repo_name=c.repo_name, revision=c.changeset.raw_id, f_path=c.f_path),class_="btn btn-default btn-sm")}
11 11 ${h.link_to(_('Show Authors'),'#',class_="btn btn-default btn-sm" ,id="show_authors")}
12 12 ${h.end_form()}
13 13 </div>
14 14 <div id="file_authors" class="file_author" style="clear:both; display: none"></div>
15 15 </div>
16 16 <div style="clear:both"></div>
17 17 </div>
18 18
19 19
20 20 <div id="body" class="codeblock">
21 21 <div class="code-header">
22 22 <div class="stats">
23 <div class="pull-left">
23 <div class="pull-left">
24 24 <div class="left img"><i class="icon-doc-inv"></i></div>
25 25 <div class="left item"><pre data-toggle="tooltip" title="${h.fmt_date(c.changeset.date)}">${h.link_to(h.show_id(c.changeset),h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</pre></div>
26 26 <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
27 27 <div class="left item last"><pre>${c.file.mimetype}</pre></div>
28 </div>
29 <div class="pull-right buttons">
28 </div>
29 <div class="pull-right buttons">
30 30 %if c.annotate:
31 31 ${h.link_to(_('Show Source'), h.url('files_home', repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
32 32 %else:
33 33 ${h.link_to(_('Show Annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
34 34 %endif
35 35 ${h.link_to(_('Show as Raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
36 36 ${h.link_to(_('Download as Raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
37 37 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
38 38 %if c.on_branch_head and not c.file.is_binary:
39 39 ${h.link_to(_('Edit on Branch: %s') % c.changeset.branch, h.url('files_edit_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path, anchor='edit'),class_="btn btn-default btn-xs")}
40 40 ${h.link_to(_('Delete'), h.url('files_delete_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path, anchor='edit'),class_="btn btn-danger btn-xs")}
41 41 %elif c.on_branch_head and c.file.is_binary:
42 ${h.link_to(_('Edit'), '#', class_="btn btn-default btn-xs disabled tooltip", title=_('Editing binary files not allowed'))}
42 ${h.link_to(_('Edit'), '#', class_="btn btn-default btn-xs disabled", title=_('Editing binary files not allowed'),**{'data-toggle':'tooltip'})}
43 43 ${h.link_to(_('Delete'), h.url('files_delete_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path, anchor='edit'),class_="btn btn-danger btn-xs")}
44 44 %else:
45 ${h.link_to(_('Edit'), '#', class_="btn btn-default btn-xs disabled tooltip", title=_('Editing files allowed only when on branch head revision'))}
46 ${h.link_to(_('Delete'), '#', class_="btn btn-danger btn-xs disabled tooltip", title=_('Deleting files allowed only when on branch head revision'))}
45 ${h.link_to(_('Edit'), '#', class_="btn btn-default btn-xs disabled", title=_('Editing files allowed only when on branch head revision'),**{'data-toggle':'tooltip'})}
46 ${h.link_to(_('Delete'), '#', class_="btn btn-danger btn-xs disabled", title=_('Deleting files allowed only when on branch head revision'),**{'data-toggle':'tooltip'})}
47 47 %endif
48 48 %endif
49 </div>
49 </div>
50 50 </div>
51 51 <div class="author">
52 52 ${h.gravatar_div(h.email_or_none(c.changeset.author), size=16)}
53 53 <div title="${c.changeset.author}" class="user">${h.person(c.changeset.author)}</div>
54 54 </div>
55 55 <div class="commit">${h.urlify_text(c.changeset.message,c.repo_name)}</div>
56 56 </div>
57 57 <div class="code-body">
58 58 %if c.file.is_browser_compatible_image():
59 59 <img src="${h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}" class="img-preview"/>
60 60 %elif c.file.is_binary:
61 61 <div style="padding:5px">
62 62 ${_('Binary file (%s)') % c.file.mimetype}
63 63 </div>
64 64 %else:
65 65 %if c.file.size < c.cut_off_limit or c.fulldiff:
66 66 %if c.annotate:
67 67 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
68 68 %else:
69 69 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
70 70 %endif
71 71 %else:
72 72 <h4>
73 73 ${_('File is too big to display.')}
74 74 %if c.annotate:
75 75 ${h.link_to(_('Show full annotation anyway.'), h.url.current(fulldiff=1, **request.GET.mixed()))}
76 76 %else:
77 77 ${h.link_to(_('Show as raw.'), h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
78 78 %endif
79 79 </h4>
80 80 %endif
81 81 %endif
82 82 </div>
83 83 </div>
84 84
85 85 <script>
86 86 $(document).ready(function(){
87 87 // fake html5 history state
88 88 var _State = {
89 89 url: "${h.url.current()}",
90 90 data: {
91 91 node_list_url: node_list_url.replace('__REV__',"${c.changeset.raw_id}").replace('__FPATH__', "${h.safe_unicode(c.file.path)}"),
92 92 url_base: url_base.replace('__REV__',"${c.changeset.raw_id}"),
93 93 rev:"${c.changeset.raw_id}",
94 94 f_path: "${h.safe_unicode(c.file.path)}"
95 95 }
96 96 }
97 97 callbacks(_State); // defined in files.html, main callbacks. Triggered in pjax calls
98 98 });
99 99 </script>
@@ -1,41 +1,40 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%block name="title">
5 5 ${_('%s Followers') % c.repo_name}
6 6 </%block>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${_('Followers')}
10 10 </%def>
11 11
12 12 <%block name="header_menu">
13 13 ${self.menu('repositories')}
14 14 </%block>
15 15
16 16 <%def name="main()">
17 17 ${self.repo_context_bar('followers')}
18 18 <div class="panel panel-primary">
19 19 <div class="panel-heading clearfix">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 <div class="panel-body">
23 23 <div id="followers">
24 24 <%include file='followers_data.html'/>
25 25 </div>
26 26 </div>
27 27 </div>
28 28 <script type="text/javascript">
29 29 $(document).ready(function(){
30 30 var $followers = $('#followers');
31 31 $followers.on('click','.pager_link',function(e){
32 32 asynchtml(e.target.href, $followers, function(){
33 33 show_more_event();
34 34 tooltip_activate();
35 show_changeset_tooltip();
36 35 });
37 36 e.preventDefault();
38 37 });
39 38 });
40 39 </script>
41 40 </%def>
@@ -1,41 +1,40 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%block name="title">
5 5 ${_('%s Forks') % c.repo_name}
6 6 </%block>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${_('Forks')}
10 10 </%def>
11 11
12 12 <%block name="header_menu">
13 13 ${self.menu('repositories')}
14 14 </%block>
15 15
16 16 <%def name="main()">
17 17 ${self.repo_context_bar('showforks')}
18 18 <div class="panel panel-primary">
19 19 <div class="panel-heading clearfix">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 <div class="panel-body">
23 23 <div id="forks">
24 24 <%include file='forks_data.html'/>
25 25 </div>
26 26 </div>
27 27 </div>
28 28 <script type="text/javascript">
29 29 $(document).ready(function(){
30 30 var $forks = $('#forks');
31 31 $forks.on('click','.pager_link',function(e){
32 32 asynchtml(e.target.href, $forks, function(){
33 33 show_more_event();
34 34 tooltip_activate();
35 show_changeset_tooltip();
36 35 });
37 36 e.preventDefault();
38 37 });
39 38 });
40 39 </script>
41 40 </%def>
@@ -1,90 +1,88 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3 <%block name="title">
4 4 ${_('Journal')}
5 5 </%block>
6 6 <%def name="breadcrumbs_links()">
7 7 <form id="filter_form" class="pull-left form-inline">
8 8 <input class="form-control q_filter_box ${'' if c.search_term else 'initial'}" id="j_filter" size="15" type="text" name="filter" value="${c.search_term or _('quick filter...')}"/>
9 9 <span data-toggle="tooltip" title="${h.journal_filter_help()}">?</span>
10 10 <input type='submit' value="${_('Filter')}" class="btn btn-default btn-xs"/>
11 11 ${_('Journal')} - ${ungettext('%s Entry', '%s Entries', c.journal_pager.item_count) % (c.journal_pager.item_count)}
12 12 </form>
13 13 </%def>
14 14 <%block name="header_menu">
15 15 ${self.menu('journal')}
16 16 </%block>
17 17 <%block name="head_extra">
18 18 <link href="${h.url('journal_atom', api_key=c.authuser.api_key)}" rel="alternate" title="${_('ATOM journal feed')}" type="application/atom+xml" />
19 19 <link href="${h.url('journal_rss', api_key=c.authuser.api_key)}" rel="alternate" title="${_('RSS journal feed')}" type="application/rss+xml" />
20 20 </%block>
21 21
22 22 <%def name="main()">
23 23 <div class="panel panel-primary">
24 24 <div class="panel-heading clearfix">
25 25 <div class="pull-left">
26 26 ${self.breadcrumbs()}
27 27 </div>
28 28 <div class="pull-right panel-title">
29 29 <a href="${h.url('my_account_watched')}"><i class="icon-eye"></i> ${_('Watched Repositories')}</a>
30 30 <a href="${h.url('my_account_repos')}"><i class="icon-database"></i> ${_('My Repositories')}</a>
31 31 <a id="refresh" href="${h.url('journal')}"><i class="icon-arrows-cw"></i></a>
32 32 <a href="${h.url('journal_atom', api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i></a>
33 33 </div>
34 34 </div>
35 35 <div id="journal" class="panel-body">
36 36 <%include file='journal_data.html'/>
37 37 </div>
38 38 </div>
39 39
40 40 <script type="text/javascript">
41 41
42 42 $('#j_filter').click(function(){
43 43 var $jfilter = $('#j_filter');
44 44 if($jfilter.hasClass('initial')){
45 45 $jfilter.val('');
46 46 }
47 47 });
48 48 var fix_j_filter_width = function(len){
49 49 $('#j_filter').css('width', Math.max(80, len*6.50)+'px');
50 50 };
51 51 $('#j_filter').keyup(function(){
52 52 fix_j_filter_width($('#j_filter').val().length);
53 53 });
54 54 $('#filter_form').submit(function(e){
55 55 e.preventDefault();
56 56 var val = $('#j_filter').val();
57 57 window.location = "${url.current(filter='__FILTER__')}".replace('__FILTER__',val);
58 58 });
59 59 fix_j_filter_width($('#j_filter').val().length);
60 60
61 61 $('#refresh').click(function(e){
62 62 asynchtml("${h.url.current(filter=c.search_term)}", $("#journal"), function(){
63 63 show_more_event();
64 64 tooltip_activate();
65 show_changeset_tooltip();
66 65 });
67 66 e.preventDefault();
68 67 });
69 68
70 69 </script>
71 70
72 71 <script type="text/javascript">
73 72 $(document).ready(function(){
74 73 var $journal = $('#journal');
75 74 $journal.on('click','.pager_link',function(e){
76 75 asynchtml(e.target.href, $journal, function(){
77 76 show_more_event();
78 77 tooltip_activate();
79 show_changeset_tooltip();
80 78 });
81 79 e.preventDefault();
82 80 });
83 81 $('#journal').on('click','.show_more',function(e){
84 82 var el = e.target;
85 83 $('#'+el.id.substring(1)).show();
86 84 $(el.parentNode).hide();
87 85 });
88 86 });
89 87 </script>
90 88 </%def>
General Comments 0
You need to be logged in to leave comments. Login now