##// END OF EJS Templates
codeblocks: add new code token rendering function that...
dan -
r1025:8ba7d016 default
parent child Browse files
Show More
@@ -0,0 +1,330 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.lib.codeblocks import (
24 tokenize_string, split_token_stream, rollup_tokenstream,
25 render_tokenstream)
26 from pygments.lexers import get_lexer_by_name
27
28
29 class TestTokenizeString(object):
30
31 python_code = '''
32 import this
33
34 var = 6
35 print "this"
36
37 '''
38
39 def test_tokenize_as_python(self):
40 lexer = get_lexer_by_name('python')
41 tokens = list(tokenize_string(self.python_code, lexer))
42
43 assert tokens == [
44 ('', u'\n'),
45 ('', u' '),
46 ('kn', u'import'),
47 ('', u' '),
48 ('nn', u'this'),
49 ('', u'\n'),
50 ('', u'\n'),
51 ('', u' '),
52 ('n', u'var'),
53 ('', u' '),
54 ('o', u'='),
55 ('', u' '),
56 ('mi', u'6'),
57 ('', u'\n'),
58 ('', u' '),
59 ('k', u'print'),
60 ('', u' '),
61 ('s2', u'"'),
62 ('s2', u'this'),
63 ('s2', u'"'),
64 ('', u'\n'),
65 ('', u'\n'),
66 ('', u' ')
67 ]
68
69 def test_tokenize_as_text(self):
70 lexer = get_lexer_by_name('text')
71 tokens = list(tokenize_string(self.python_code, lexer))
72
73 assert tokens == [
74 ('',
75 u'\n import this\n\n var = 6\n print "this"\n\n ')
76 ]
77
78
79 class TestSplitTokenStream(object):
80
81 def test_split_token_stream(self):
82 lines = list(split_token_stream(
83 [('type1', 'some\ntext'), ('type2', 'more\n')]))
84
85 assert lines == [
86 [('type1', u'some')],
87 [('type1', u'text'), ('type2', u'more')],
88 [('type2', u'')],
89 ]
90
91 def test_split_token_stream_other_char(self):
92 lines = list(split_token_stream(
93 [('type1', 'some\ntext'), ('type2', 'more\n')],
94 split_string='m'))
95
96 assert lines == [
97 [('type1', 'so')],
98 [('type1', 'e\ntext'), ('type2', '')],
99 [('type2', 'ore\n')],
100 ]
101
102 def test_split_token_stream_without_char(self):
103 lines = list(split_token_stream(
104 [('type1', 'some\ntext'), ('type2', 'more\n')],
105 split_string='z'))
106
107 assert lines == [
108 [('type1', 'some\ntext'), ('type2', 'more\n')]
109 ]
110
111 def test_split_token_stream_single(self):
112 lines = list(split_token_stream(
113 [('type1', '\n')], split_string='\n'))
114
115 assert lines == [
116 [('type1', '')],
117 [('type1', '')],
118 ]
119
120 def test_split_token_stream_single_repeat(self):
121 lines = list(split_token_stream(
122 [('type1', '\n\n\n')], split_string='\n'))
123
124 assert lines == [
125 [('type1', '')],
126 [('type1', '')],
127 [('type1', '')],
128 [('type1', '')],
129 ]
130
131 def test_split_token_stream_multiple_repeat(self):
132 lines = list(split_token_stream(
133 [('type1', '\n\n'), ('type2', '\n\n')], split_string='\n'))
134
135 assert lines == [
136 [('type1', '')],
137 [('type1', '')],
138 [('type1', ''), ('type2', '')],
139 [('type2', '')],
140 [('type2', '')],
141 ]
142
143
144 class TestRollupTokens(object):
145
146 @pytest.mark.parametrize('tokenstream,output', [
147 ([],
148 []),
149 ([('A', 'hell'), ('A', 'o')], [
150 ('A', [
151 ('', 'hello')]),
152 ]),
153 ([('A', 'hell'), ('B', 'o')], [
154 ('A', [
155 ('', 'hell')]),
156 ('B', [
157 ('', 'o')]),
158 ]),
159 ([('A', 'hel'), ('A', 'lo'), ('B', ' '), ('A', 'there')], [
160 ('A', [
161 ('', 'hello')]),
162 ('B', [
163 ('', ' ')]),
164 ('A', [
165 ('', 'there')]),
166 ]),
167 ])
168 def test_rollup_tokenstream_without_ops(self, tokenstream, output):
169 assert list(rollup_tokenstream(tokenstream)) == output
170
171 @pytest.mark.parametrize('tokenstream,output', [
172 ([],
173 []),
174 ([('A', '', 'hell'), ('A', '', 'o')], [
175 ('A', [
176 ('', 'hello')]),
177 ]),
178 ([('A', '', 'hell'), ('B', '', 'o')], [
179 ('A', [
180 ('', 'hell')]),
181 ('B', [
182 ('', 'o')]),
183 ]),
184 ([('A', '', 'h'), ('B', '', 'e'), ('C', '', 'y')], [
185 ('A', [
186 ('', 'h')]),
187 ('B', [
188 ('', 'e')]),
189 ('C', [
190 ('', 'y')]),
191 ]),
192 ([('A', '', 'h'), ('A', '', 'e'), ('C', '', 'y')], [
193 ('A', [
194 ('', 'he')]),
195 ('C', [
196 ('', 'y')]),
197 ]),
198 ([('A', 'ins', 'h'), ('A', 'ins', 'e')], [
199 ('A', [
200 ('ins', 'he')
201 ]),
202 ]),
203 ([('A', 'ins', 'h'), ('A', 'del', 'e')], [
204 ('A', [
205 ('ins', 'h'),
206 ('del', 'e')
207 ]),
208 ]),
209 ([('A', 'ins', 'h'), ('B', 'del', 'e'), ('B', 'del', 'y')], [
210 ('A', [
211 ('ins', 'h'),
212 ]),
213 ('B', [
214 ('del', 'ey'),
215 ]),
216 ]),
217 ([('A', 'ins', 'h'), ('A', 'del', 'e'), ('B', 'del', 'y')], [
218 ('A', [
219 ('ins', 'h'),
220 ('del', 'e'),
221 ]),
222 ('B', [
223 ('del', 'y'),
224 ]),
225 ]),
226 ([('A', '', 'some'), ('A', 'ins', 'new'), ('A', '', 'name')], [
227 ('A', [
228 ('', 'some'),
229 ('ins', 'new'),
230 ('', 'name'),
231 ]),
232 ]),
233 ])
234 def test_rollup_tokenstream_with_ops(self, tokenstream, output):
235 assert list(rollup_tokenstream(tokenstream)) == output
236
237
238 class TestRenderTokenStream(object):
239
240 @pytest.mark.parametrize('tokenstream,output', [
241 (
242 [],
243 '',
244 ),
245 (
246 [('', '', u'')],
247 '<span></span>',
248 ),
249 (
250 [('', '', u'text')],
251 '<span>text</span>',
252 ),
253 (
254 [('A', '', u'')],
255 '<span class="A"></span>',
256 ),
257 (
258 [('A', '', u'hello')],
259 '<span class="A">hello</span>',
260 ),
261 (
262 [('A', '', u'hel'), ('A', '', u'lo')],
263 '<span class="A">hello</span>',
264 ),
265 (
266 [('A', '', u'two\n'), ('A', '', u'lines')],
267 '<span class="A">two<nl>\n</nl>lines</span>',
268 ),
269 (
270 [('A', '', u'\nthree\n'), ('A', '', u'lines')],
271 '<span class="A"><nl>\n</nl>three<nl>\n</nl>lines</span>',
272 ),
273 (
274 [('', '', u'\n'), ('A', '', u'line')],
275 '<span><nl>\n</nl></span><span class="A">line</span>',
276 ),
277 (
278 [('', 'ins', u'\n'), ('A', '', u'line')],
279 '<span><ins><nl>\n</nl></ins></span><span class="A">line</span>',
280 ),
281 (
282 [('A', '', u'hel'), ('A', 'ins', u'lo')],
283 '<span class="A">hel<ins>lo</ins></span>',
284 ),
285 (
286 [('A', '', u'hel'), ('A', 'ins', u'l'), ('A', 'ins', u'o')],
287 '<span class="A">hel<ins>lo</ins></span>',
288 ),
289 (
290 [('A', '', u'hel'), ('A', 'ins', u'l'), ('A', 'del', u'o')],
291 '<span class="A">hel<ins>l</ins><del>o</del></span>',
292 ),
293 (
294 [('A', '', u'hel'), ('B', '', u'lo')],
295 '<span class="A">hel</span><span class="B">lo</span>',
296 ),
297 (
298 [('A', '', u'hel'), ('B', 'ins', u'lo')],
299 '<span class="A">hel</span><span class="B"><ins>lo</ins></span>',
300 ),
301 ])
302 def test_render_tokenstream_with_ops(self, tokenstream, output):
303 html = render_tokenstream(tokenstream)
304 assert html == output
305
306 @pytest.mark.parametrize('tokenstream,output', [
307 (
308 [('A', u'hel'), ('A', u'lo')],
309 '<span class="A">hello</span>',
310 ),
311 (
312 [('A', u'hel'), ('A', u'l'), ('A', u'o')],
313 '<span class="A">hello</span>',
314 ),
315 (
316 [('A', u'hel'), ('A', u'l'), ('A', u'o')],
317 '<span class="A">hello</span>',
318 ),
319 (
320 [('A', u'hel'), ('B', u'lo')],
321 '<span class="A">hel</span><span class="B">lo</span>',
322 ),
323 (
324 [('A', u'hel'), ('B', u'lo')],
325 '<span class="A">hel</span><span class="B">lo</span>',
326 ),
327 ])
328 def test_render_tokenstream_without_ops(self, tokenstream, output):
329 html = render_tokenstream(tokenstream)
330 assert html == output
@@ -1,147 +1,214 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2016 RhodeCode GmbH
3 # Copyright (C) 2011-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21 import logging
22 from itertools import groupby
22 from itertools import groupby
23
23
24 from pygments import lex
24 from pygments import lex
25 # PYGMENTS_TOKEN_TYPES is used in a hot loop keep attribute lookups to a minimum
25 from pygments.formatters.html import _get_ttype_class as pygment_token_class
26 from pygments.token import STANDARD_TYPES as PYGMENTS_TOKEN_TYPES
26 from rhodecode.lib.helpers import get_lexer_for_filenode, html_escape
27 from rhodecode.lib.utils2 import AttributeDict
28 from rhodecode.lib.vcs.nodes import FileNode
29 from pygments.lexers import get_lexer_by_name
30
31 plain_text_lexer = get_lexer_by_name(
32 'text', stripall=False, stripnl=False, ensurenl=False)
33
34
35 log = logging.getLogger()
27
36
28 from rhodecode.lib.helpers import get_lexer_for_filenode
29
37
30 def tokenize_file(content, lexer):
38 def filenode_as_lines_tokens(filenode, lexer=None):
39 lexer = lexer or get_lexer_for_filenode(filenode)
40 log.debug('Generating file node pygment tokens for %s, %s', lexer, filenode)
41 tokens = tokenize_string(filenode.content, get_lexer_for_filenode(filenode))
42 lines = split_token_stream(tokens, split_string='\n')
43 rv = list(lines)
44 return rv
45
46
47 def tokenize_string(content, lexer):
31 """
48 """
32 Use pygments to tokenize some content based on a lexer
49 Use pygments to tokenize some content based on a lexer
33 ensuring all original new lines and whitespace is preserved
50 ensuring all original new lines and whitespace is preserved
34 """
51 """
35
52
36 lexer.stripall = False
53 lexer.stripall = False
37 lexer.stripnl = False
54 lexer.stripnl = False
38 lexer.ensurenl = False
55 lexer.ensurenl = False
39 return lex(content, lexer)
56 for token_type, token_text in lex(content, lexer):
57 yield pygment_token_class(token_type), token_text
40
58
41
59
42 def pygment_token_class(token_type):
60 def split_token_stream(tokens, split_string=u'\n'):
43 """ Convert a pygments token type to html class name """
44
45 fname = PYGMENTS_TOKEN_TYPES.get(token_type)
46 if fname:
47 return fname
48
49 aname = ''
50 while fname is None:
51 aname = '-' + token_type[-1] + aname
52 token_type = token_type.parent
53 fname = PYGMENTS_TOKEN_TYPES.get(token_type)
54
55 return fname + aname
56
57
58 def tokens_as_lines(tokens, split_string=u'\n'):
59 """
61 """
60 Take a list of (TokenType, text) tuples and split them by a string
62 Take a list of (TokenType, text) tuples and split them by a string
61
63
62 eg. [(TEXT, 'some\ntext')] => [(TEXT, 'some'), (TEXT, 'text')]
64 >>> split_token_stream([(TEXT, 'some\ntext'), (TEXT, 'more\n')])
65 [(TEXT, 'some'), (TEXT, 'text'),
66 (TEXT, 'more'), (TEXT, 'text')]
63 """
67 """
64
68
65 buffer = []
69 buffer = []
66 for token_type, token_text in tokens:
70 for token_class, token_text in tokens:
67 parts = token_text.split(split_string)
71 parts = token_text.split(split_string)
68 for part in parts[:-1]:
72 for part in parts[:-1]:
69 buffer.append((token_type, part))
73 buffer.append((token_class, part))
70 yield buffer
74 yield buffer
71 buffer = []
75 buffer = []
72
76
73 buffer.append((token_type, parts[-1]))
77 buffer.append((token_class, parts[-1]))
74
78
75 if buffer:
79 if buffer:
76 yield buffer
80 yield buffer
77
81
78
82
79 def filenode_as_lines_tokens(filenode):
80 """
81 Return a generator of lines with pygment tokens for a filenode eg:
82
83 [
84 (1, line1_tokens_list),
85 (2, line1_tokens_list]),
86 ]
87 """
88
89 return enumerate(
90 tokens_as_lines(
91 tokenize_file(
92 filenode.content, get_lexer_for_filenode(filenode)
93 )
94 ),
95 1)
96
97
98 def filenode_as_annotated_lines_tokens(filenode):
83 def filenode_as_annotated_lines_tokens(filenode):
99 """
84 """
100 Take a file node and return a list of annotations => lines, if no annotation
85 Take a file node and return a list of annotations => lines, if no annotation
101 is found, it will be None.
86 is found, it will be None.
102
87
103 eg:
88 eg:
104
89
105 [
90 [
106 (annotation1, [
91 (annotation1, [
107 (1, line1_tokens_list),
92 (1, line1_tokens_list),
108 (2, line2_tokens_list),
93 (2, line2_tokens_list),
109 ]),
94 ]),
110 (annotation2, [
95 (annotation2, [
111 (3, line1_tokens_list),
96 (3, line1_tokens_list),
112 ]),
97 ]),
113 (None, [
98 (None, [
114 (4, line1_tokens_list),
99 (4, line1_tokens_list),
115 ]),
100 ]),
116 (annotation1, [
101 (annotation1, [
117 (5, line1_tokens_list),
102 (5, line1_tokens_list),
118 (6, line2_tokens_list),
103 (6, line2_tokens_list),
119 ])
104 ])
120 ]
105 ]
121 """
106 """
122
107
108 commit_cache = {} # cache commit_getter lookups
123
109
124 # cache commit_getter lookups
125 commit_cache = {}
126 def _get_annotation(commit_id, commit_getter):
110 def _get_annotation(commit_id, commit_getter):
127 if commit_id not in commit_cache:
111 if commit_id not in commit_cache:
128 commit_cache[commit_id] = commit_getter()
112 commit_cache[commit_id] = commit_getter()
129 return commit_cache[commit_id]
113 return commit_cache[commit_id]
130
114
131 annotation_lookup = {
115 annotation_lookup = {
132 line_no: _get_annotation(commit_id, commit_getter)
116 line_no: _get_annotation(commit_id, commit_getter)
133 for line_no, commit_id, commit_getter, line_content
117 for line_no, commit_id, commit_getter, line_content
134 in filenode.annotate
118 in filenode.annotate
135 }
119 }
136
120
137 annotations_lines = ((annotation_lookup.get(line_no), line_no, tokens)
121 annotations_lines = ((annotation_lookup.get(line_no), line_no, tokens)
138 for line_no, tokens
122 for line_no, tokens
139 in filenode_as_lines_tokens(filenode))
123 in enumerate(filenode_as_lines_tokens(filenode), 1))
140
124
141 grouped_annotations_lines = groupby(annotations_lines, lambda x: x[0])
125 grouped_annotations_lines = groupby(annotations_lines, lambda x: x[0])
142
126
143 for annotation, group in grouped_annotations_lines:
127 for annotation, group in grouped_annotations_lines:
144 yield (
128 yield (
145 annotation, [(line_no, tokens)
129 annotation, [(line_no, tokens)
146 for (_, line_no, tokens) in group]
130 for (_, line_no, tokens) in group]
147 )
131 )
132
133
134 def render_tokenstream(tokenstream):
135 result = []
136 for token_class, token_ops_texts in rollup_tokenstream(tokenstream):
137
138 if token_class:
139 result.append(u'<span class="%s">' % token_class)
140 else:
141 result.append(u'<span>')
142
143 for op_tag, token_text in token_ops_texts:
144
145 if op_tag:
146 result.append(u'<%s>' % op_tag)
147
148 escaped_text = html_escape(token_text)
149 escaped_text = escaped_text.replace('\n', '<nl>\n</nl>')
150
151 result.append(escaped_text)
152
153 if op_tag:
154 result.append(u'</%s>' % op_tag)
155
156 result.append(u'</span>')
157
158 html = ''.join(result)
159 return html
160
161
162 def rollup_tokenstream(tokenstream):
163 """
164 Group a token stream of the format:
165
166 ('class', 'op', 'text')
167 or
168 ('class', 'text')
169
170 into
171
172 [('class1',
173 [('op1', 'text'),
174 ('op2', 'text')]),
175 ('class2',
176 [('op3', 'text')])]
177
178 This is used to get the minimal tags necessary when
179 rendering to html eg for a token stream ie.
180
181 <span class="A"><ins>he</ins>llo</span>
182 vs
183 <span class="A"><ins>he</ins></span><span class="A">llo</span>
184
185 If a 2 tuple is passed in, the output op will be an empty string.
186
187 eg:
188
189 >>> rollup_tokenstream([('classA', '', 'h'),
190 ('classA', 'del', 'ell'),
191 ('classA', '', 'o'),
192 ('classB', '', ' '),
193 ('classA', '', 'the'),
194 ('classA', '', 're'),
195 ])
196
197 [('classA', [('', 'h'), ('del', 'ell'), ('', 'o')],
198 ('classB', [('', ' ')],
199 ('classA', [('', 'there')]]
200
201 """
202 if tokenstream and len(tokenstream[0]) == 2:
203 tokenstream = ((t[0], '', t[1]) for t in tokenstream)
204
205 result = []
206 for token_class, op_list in groupby(tokenstream, lambda t: t[0]):
207 ops = []
208 for token_op, token_text_list in groupby(op_list, lambda o: o[1]):
209 text_buffer = []
210 for t_class, t_op, t_text in token_text_list:
211 text_buffer.append(t_text)
212 ops.append((token_op, ''.join(text_buffer)))
213 result.append((token_class, ops))
214 return result
@@ -1,749 +1,753 b''
1 // Default styles
1 // Default styles
2
2
3 .diff-collapse {
3 .diff-collapse {
4 margin: @padding 0;
4 margin: @padding 0;
5 text-align: right;
5 text-align: right;
6 }
6 }
7
7
8 .diff-container {
8 .diff-container {
9 margin-bottom: @space;
9 margin-bottom: @space;
10
10
11 .diffblock {
11 .diffblock {
12 margin-bottom: @space;
12 margin-bottom: @space;
13 }
13 }
14
14
15 &.hidden {
15 &.hidden {
16 display: none;
16 display: none;
17 overflow: hidden;
17 overflow: hidden;
18 }
18 }
19 }
19 }
20
20
21 .compare_view_files {
21 .compare_view_files {
22
22
23 .diff-container {
23 .diff-container {
24
24
25 .diffblock {
25 .diffblock {
26 margin-bottom: 0;
26 margin-bottom: 0;
27 }
27 }
28 }
28 }
29 }
29 }
30
30
31 div.diffblock .sidebyside {
31 div.diffblock .sidebyside {
32 background: #ffffff;
32 background: #ffffff;
33 }
33 }
34
34
35 div.diffblock {
35 div.diffblock {
36 overflow-x: auto;
36 overflow-x: auto;
37 overflow-y: hidden;
37 overflow-y: hidden;
38 clear: both;
38 clear: both;
39 padding: 0px;
39 padding: 0px;
40 background: @grey6;
40 background: @grey6;
41 border: @border-thickness solid @grey5;
41 border: @border-thickness solid @grey5;
42 -webkit-border-radius: @border-radius @border-radius 0px 0px;
42 -webkit-border-radius: @border-radius @border-radius 0px 0px;
43 border-radius: @border-radius @border-radius 0px 0px;
43 border-radius: @border-radius @border-radius 0px 0px;
44
44
45
45
46 .comments-number {
46 .comments-number {
47 float: right;
47 float: right;
48 }
48 }
49
49
50 // BEGIN CODE-HEADER STYLES
50 // BEGIN CODE-HEADER STYLES
51
51
52 .code-header {
52 .code-header {
53 background: @grey6;
53 background: @grey6;
54 padding: 10px 0 10px 0;
54 padding: 10px 0 10px 0;
55 height: auto;
55 height: auto;
56 width: 100%;
56 width: 100%;
57
57
58 .hash {
58 .hash {
59 float: left;
59 float: left;
60 padding: 2px 0 0 2px;
60 padding: 2px 0 0 2px;
61 }
61 }
62
62
63 .date {
63 .date {
64 float: left;
64 float: left;
65 text-transform: uppercase;
65 text-transform: uppercase;
66 padding: 4px 0px 0px 2px;
66 padding: 4px 0px 0px 2px;
67 }
67 }
68
68
69 div {
69 div {
70 margin-left: 4px;
70 margin-left: 4px;
71 }
71 }
72
72
73 div.compare_header {
73 div.compare_header {
74 min-height: 40px;
74 min-height: 40px;
75 margin: 0;
75 margin: 0;
76 padding: 0 @padding;
76 padding: 0 @padding;
77
77
78 .drop-menu {
78 .drop-menu {
79 float:left;
79 float:left;
80 display: block;
80 display: block;
81 margin:0 0 @padding 0;
81 margin:0 0 @padding 0;
82 }
82 }
83
83
84 .compare-label {
84 .compare-label {
85 float: left;
85 float: left;
86 clear: both;
86 clear: both;
87 display: inline-block;
87 display: inline-block;
88 min-width: 5em;
88 min-width: 5em;
89 margin: 0;
89 margin: 0;
90 padding: @button-padding @button-padding @button-padding 0;
90 padding: @button-padding @button-padding @button-padding 0;
91 font-family: @text-semibold;
91 font-family: @text-semibold;
92 }
92 }
93
93
94 .compare-buttons {
94 .compare-buttons {
95 float: left;
95 float: left;
96 margin: 0;
96 margin: 0;
97 padding: 0 0 @padding;
97 padding: 0 0 @padding;
98
98
99 .btn {
99 .btn {
100 margin: 0 @padding 0 0;
100 margin: 0 @padding 0 0;
101 }
101 }
102 }
102 }
103 }
103 }
104
104
105 }
105 }
106
106
107 .parents {
107 .parents {
108 float: left;
108 float: left;
109 width: 100px;
109 width: 100px;
110 font-weight: 400;
110 font-weight: 400;
111 vertical-align: middle;
111 vertical-align: middle;
112 padding: 0px 2px 0px 2px;
112 padding: 0px 2px 0px 2px;
113 background-color: @grey6;
113 background-color: @grey6;
114
114
115 #parent_link {
115 #parent_link {
116 margin: 00px 2px;
116 margin: 00px 2px;
117
117
118 &.double {
118 &.double {
119 margin: 0px 2px;
119 margin: 0px 2px;
120 }
120 }
121
121
122 &.disabled{
122 &.disabled{
123 margin-right: @padding;
123 margin-right: @padding;
124 }
124 }
125 }
125 }
126 }
126 }
127
127
128 .children {
128 .children {
129 float: right;
129 float: right;
130 width: 100px;
130 width: 100px;
131 font-weight: 400;
131 font-weight: 400;
132 vertical-align: middle;
132 vertical-align: middle;
133 text-align: right;
133 text-align: right;
134 padding: 0px 2px 0px 2px;
134 padding: 0px 2px 0px 2px;
135 background-color: @grey6;
135 background-color: @grey6;
136
136
137 #child_link {
137 #child_link {
138 margin: 0px 2px;
138 margin: 0px 2px;
139
139
140 &.double {
140 &.double {
141 margin: 0px 2px;
141 margin: 0px 2px;
142 }
142 }
143
143
144 &.disabled{
144 &.disabled{
145 margin-right: @padding;
145 margin-right: @padding;
146 }
146 }
147 }
147 }
148 }
148 }
149
149
150 .changeset_header {
150 .changeset_header {
151 height: 16px;
151 height: 16px;
152
152
153 & > div{
153 & > div{
154 margin-right: @padding;
154 margin-right: @padding;
155 }
155 }
156 }
156 }
157
157
158 .changeset_file {
158 .changeset_file {
159 text-align: left;
159 text-align: left;
160 float: left;
160 float: left;
161 padding: 0;
161 padding: 0;
162
162
163 a{
163 a{
164 display: inline-block;
164 display: inline-block;
165 margin-right: 0.5em;
165 margin-right: 0.5em;
166 }
166 }
167
167
168 #selected_mode{
168 #selected_mode{
169 margin-left: 0;
169 margin-left: 0;
170 }
170 }
171 }
171 }
172
172
173 .diff-menu-wrapper {
173 .diff-menu-wrapper {
174 float: left;
174 float: left;
175 }
175 }
176
176
177 .diff-menu {
177 .diff-menu {
178 position: absolute;
178 position: absolute;
179 background: none repeat scroll 0 0 #FFFFFF;
179 background: none repeat scroll 0 0 #FFFFFF;
180 border-color: #003367 @grey3 @grey3;
180 border-color: #003367 @grey3 @grey3;
181 border-right: 1px solid @grey3;
181 border-right: 1px solid @grey3;
182 border-style: solid solid solid;
182 border-style: solid solid solid;
183 border-width: @border-thickness;
183 border-width: @border-thickness;
184 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
184 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
185 margin-top: 5px;
185 margin-top: 5px;
186 margin-left: 1px;
186 margin-left: 1px;
187 }
187 }
188
188
189 .diff-actions, .editor-actions {
189 .diff-actions, .editor-actions {
190 float: left;
190 float: left;
191
191
192 input{
192 input{
193 margin: 0 0.5em 0 0;
193 margin: 0 0.5em 0 0;
194 }
194 }
195 }
195 }
196
196
197 // END CODE-HEADER STYLES
197 // END CODE-HEADER STYLES
198
198
199 // BEGIN CODE-BODY STYLES
199 // BEGIN CODE-BODY STYLES
200
200
201 .code-body {
201 .code-body {
202 background: white;
202 background: white;
203 padding: 0;
203 padding: 0;
204 background-color: #ffffff;
204 background-color: #ffffff;
205 position: relative;
205 position: relative;
206 max-width: none;
206 max-width: none;
207 box-sizing: border-box;
207 box-sizing: border-box;
208 // TODO: johbo: Parent has overflow: auto, this forces the child here
208 // TODO: johbo: Parent has overflow: auto, this forces the child here
209 // to have the intended size and to scroll. Should be simplified.
209 // to have the intended size and to scroll. Should be simplified.
210 width: 100%;
210 width: 100%;
211 overflow-x: auto;
211 overflow-x: auto;
212 }
212 }
213
213
214 pre.raw {
214 pre.raw {
215 background: white;
215 background: white;
216 color: @grey1;
216 color: @grey1;
217 }
217 }
218 // END CODE-BODY STYLES
218 // END CODE-BODY STYLES
219
219
220 }
220 }
221
221
222
222
223 table.code-difftable {
223 table.code-difftable {
224 border-collapse: collapse;
224 border-collapse: collapse;
225 width: 99%;
225 width: 99%;
226 border-radius: 0px !important;
226 border-radius: 0px !important;
227
227
228 td {
228 td {
229 padding: 0 !important;
229 padding: 0 !important;
230 background: none !important;
230 background: none !important;
231 border: 0 !important;
231 border: 0 !important;
232 }
232 }
233
233
234 .context {
234 .context {
235 background: none repeat scroll 0 0 #DDE7EF;
235 background: none repeat scroll 0 0 #DDE7EF;
236 }
236 }
237
237
238 .add {
238 .add {
239 background: none repeat scroll 0 0 #DDFFDD;
239 background: none repeat scroll 0 0 #DDFFDD;
240
240
241 ins {
241 ins {
242 background: none repeat scroll 0 0 #AAFFAA;
242 background: none repeat scroll 0 0 #AAFFAA;
243 text-decoration: none;
243 text-decoration: none;
244 }
244 }
245 }
245 }
246
246
247 .del {
247 .del {
248 background: none repeat scroll 0 0 #FFDDDD;
248 background: none repeat scroll 0 0 #FFDDDD;
249
249
250 del {
250 del {
251 background: none repeat scroll 0 0 #FFAAAA;
251 background: none repeat scroll 0 0 #FFAAAA;
252 text-decoration: none;
252 text-decoration: none;
253 }
253 }
254 }
254 }
255
255
256 /** LINE NUMBERS **/
256 /** LINE NUMBERS **/
257 .lineno {
257 .lineno {
258 padding-left: 2px !important;
258 padding-left: 2px !important;
259 padding-right: 2px;
259 padding-right: 2px;
260 text-align: right;
260 text-align: right;
261 width: 32px;
261 width: 32px;
262 -moz-user-select: none;
262 -moz-user-select: none;
263 -webkit-user-select: none;
263 -webkit-user-select: none;
264 border-right: @border-thickness solid @grey5 !important;
264 border-right: @border-thickness solid @grey5 !important;
265 border-left: 0px solid #CCC !important;
265 border-left: 0px solid #CCC !important;
266 border-top: 0px solid #CCC !important;
266 border-top: 0px solid #CCC !important;
267 border-bottom: none !important;
267 border-bottom: none !important;
268
268
269 a {
269 a {
270 &:extend(pre);
270 &:extend(pre);
271 text-align: right;
271 text-align: right;
272 padding-right: 2px;
272 padding-right: 2px;
273 cursor: pointer;
273 cursor: pointer;
274 display: block;
274 display: block;
275 width: 32px;
275 width: 32px;
276 }
276 }
277 }
277 }
278
278
279 .context {
279 .context {
280 cursor: auto;
280 cursor: auto;
281 &:extend(pre);
281 &:extend(pre);
282 }
282 }
283
283
284 .lineno-inline {
284 .lineno-inline {
285 background: none repeat scroll 0 0 #FFF !important;
285 background: none repeat scroll 0 0 #FFF !important;
286 padding-left: 2px;
286 padding-left: 2px;
287 padding-right: 2px;
287 padding-right: 2px;
288 text-align: right;
288 text-align: right;
289 width: 30px;
289 width: 30px;
290 -moz-user-select: none;
290 -moz-user-select: none;
291 -webkit-user-select: none;
291 -webkit-user-select: none;
292 }
292 }
293
293
294 /** CODE **/
294 /** CODE **/
295 .code {
295 .code {
296 display: block;
296 display: block;
297 width: 100%;
297 width: 100%;
298
298
299 td {
299 td {
300 margin: 0;
300 margin: 0;
301 padding: 0;
301 padding: 0;
302 }
302 }
303
303
304 pre {
304 pre {
305 margin: 0;
305 margin: 0;
306 padding: 0;
306 padding: 0;
307 margin-left: .5em;
307 margin-left: .5em;
308 }
308 }
309 }
309 }
310 }
310 }
311
311
312
312
313 // Comments
313 // Comments
314
314
315 div.comment:target {
315 div.comment:target {
316 border-left: 6px solid @comment-highlight-color;
316 border-left: 6px solid @comment-highlight-color;
317 padding-left: 3px;
317 padding-left: 3px;
318 margin-left: -9px;
318 margin-left: -9px;
319 }
319 }
320
320
321 //TODO: anderson: can't get an absolute number out of anything, so had to put the
321 //TODO: anderson: can't get an absolute number out of anything, so had to put the
322 //current values that might change. But to make it clear I put as a calculation
322 //current values that might change. But to make it clear I put as a calculation
323 @comment-max-width: 1065px;
323 @comment-max-width: 1065px;
324 @pr-extra-margin: 34px;
324 @pr-extra-margin: 34px;
325 @pr-border-spacing: 4px;
325 @pr-border-spacing: 4px;
326 @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing;
326 @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing;
327
327
328 // Pull Request
328 // Pull Request
329 .cs_files .code-difftable {
329 .cs_files .code-difftable {
330 border: @border-thickness solid @grey5; //borders only on PRs
330 border: @border-thickness solid @grey5; //borders only on PRs
331
331
332 .comment-inline-form,
332 .comment-inline-form,
333 div.comment {
333 div.comment {
334 width: @pr-comment-width;
334 width: @pr-comment-width;
335 }
335 }
336 }
336 }
337
337
338 // Changeset
338 // Changeset
339 .code-difftable {
339 .code-difftable {
340 .comment-inline-form,
340 .comment-inline-form,
341 div.comment {
341 div.comment {
342 width: @comment-max-width;
342 width: @comment-max-width;
343 }
343 }
344 }
344 }
345
345
346 //Style page
346 //Style page
347 @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding;
347 @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding;
348 #style-page .code-difftable{
348 #style-page .code-difftable{
349 .comment-inline-form,
349 .comment-inline-form,
350 div.comment {
350 div.comment {
351 width: @comment-max-width - @style-extra-margin;
351 width: @comment-max-width - @style-extra-margin;
352 }
352 }
353 }
353 }
354
354
355 #context-bar > h2 {
355 #context-bar > h2 {
356 font-size: 20px;
356 font-size: 20px;
357 }
357 }
358
358
359 #context-bar > h2> a {
359 #context-bar > h2> a {
360 font-size: 20px;
360 font-size: 20px;
361 }
361 }
362 // end of defaults
362 // end of defaults
363
363
364 .file_diff_buttons {
364 .file_diff_buttons {
365 padding: 0 0 @padding;
365 padding: 0 0 @padding;
366
366
367 .drop-menu {
367 .drop-menu {
368 float: left;
368 float: left;
369 margin: 0 @padding 0 0;
369 margin: 0 @padding 0 0;
370 }
370 }
371 .btn {
371 .btn {
372 margin: 0 @padding 0 0;
372 margin: 0 @padding 0 0;
373 }
373 }
374 }
374 }
375
375
376 .code-body.textarea.editor {
376 .code-body.textarea.editor {
377 max-width: none;
377 max-width: none;
378 padding: 15px;
378 padding: 15px;
379 }
379 }
380
380
381 td.injected_diff{
381 td.injected_diff{
382 max-width: 1178px;
382 max-width: 1178px;
383 overflow-x: auto;
383 overflow-x: auto;
384 overflow-y: hidden;
384 overflow-y: hidden;
385
385
386 div.diff-container,
386 div.diff-container,
387 div.diffblock{
387 div.diffblock{
388 max-width: 100%;
388 max-width: 100%;
389 }
389 }
390
390
391 div.code-body {
391 div.code-body {
392 max-width: 1124px;
392 max-width: 1124px;
393 overflow-x: auto;
393 overflow-x: auto;
394 overflow-y: hidden;
394 overflow-y: hidden;
395 padding: 0;
395 padding: 0;
396 }
396 }
397 div.diffblock {
397 div.diffblock {
398 border: none;
398 border: none;
399 }
399 }
400
400
401 &.inline-form {
401 &.inline-form {
402 width: 99%
402 width: 99%
403 }
403 }
404 }
404 }
405
405
406
406
407 table.code-difftable {
407 table.code-difftable {
408 width: 100%;
408 width: 100%;
409 }
409 }
410
410
411 /** PYGMENTS COLORING **/
411 /** PYGMENTS COLORING **/
412 div.codeblock {
412 div.codeblock {
413
413
414 // TODO: johbo: Added interim to get rid of the margin around
414 // TODO: johbo: Added interim to get rid of the margin around
415 // Select2 widgets. This needs further cleanup.
415 // Select2 widgets. This needs further cleanup.
416 margin-top: @padding;
416 margin-top: @padding;
417
417
418 overflow: auto;
418 overflow: auto;
419 padding: 0px;
419 padding: 0px;
420 border: @border-thickness solid @grey5;
420 border: @border-thickness solid @grey5;
421 background: @grey6;
421 background: @grey6;
422 .border-radius(@border-radius);
422 .border-radius(@border-radius);
423
423
424 #remove_gist {
424 #remove_gist {
425 float: right;
425 float: right;
426 }
426 }
427
427
428 .author {
428 .author {
429 clear: both;
429 clear: both;
430 vertical-align: middle;
430 vertical-align: middle;
431 font-family: @text-bold;
431 font-family: @text-bold;
432 }
432 }
433
433
434 .btn-mini {
434 .btn-mini {
435 float: left;
435 float: left;
436 margin: 0 5px 0 0;
436 margin: 0 5px 0 0;
437 }
437 }
438
438
439 .code-header {
439 .code-header {
440 padding: @padding;
440 padding: @padding;
441 border-bottom: @border-thickness solid @grey5;
441 border-bottom: @border-thickness solid @grey5;
442
442
443 .rc-user {
443 .rc-user {
444 min-width: 0;
444 min-width: 0;
445 margin-right: .5em;
445 margin-right: .5em;
446 }
446 }
447
447
448 .stats {
448 .stats {
449 clear: both;
449 clear: both;
450 margin: 0 0 @padding 0;
450 margin: 0 0 @padding 0;
451 padding: 0;
451 padding: 0;
452 .left {
452 .left {
453 float: left;
453 float: left;
454 clear: left;
454 clear: left;
455 max-width: 75%;
455 max-width: 75%;
456 margin: 0 0 @padding 0;
456 margin: 0 0 @padding 0;
457
457
458 &.item {
458 &.item {
459 margin-right: @padding;
459 margin-right: @padding;
460 &.last { border-right: none; }
460 &.last { border-right: none; }
461 }
461 }
462 }
462 }
463 .buttons { float: right; }
463 .buttons { float: right; }
464 .author {
464 .author {
465 height: 25px; margin-left: 15px; font-weight: bold;
465 height: 25px; margin-left: 15px; font-weight: bold;
466 }
466 }
467 }
467 }
468
468
469 .commit {
469 .commit {
470 margin: 5px 0 0 26px;
470 margin: 5px 0 0 26px;
471 font-weight: normal;
471 font-weight: normal;
472 white-space: pre-wrap;
472 white-space: pre-wrap;
473 }
473 }
474 }
474 }
475
475
476 .message {
476 .message {
477 position: relative;
477 position: relative;
478 margin: @padding;
478 margin: @padding;
479
479
480 .codeblock-label {
480 .codeblock-label {
481 margin: 0 0 1em 0;
481 margin: 0 0 1em 0;
482 }
482 }
483 }
483 }
484
484
485 .code-body {
485 .code-body {
486 padding: @padding;
486 padding: @padding;
487 background-color: #ffffff;
487 background-color: #ffffff;
488 min-width: 100%;
488 min-width: 100%;
489 box-sizing: border-box;
489 box-sizing: border-box;
490 // TODO: johbo: Parent has overflow: auto, this forces the child here
490 // TODO: johbo: Parent has overflow: auto, this forces the child here
491 // to have the intended size and to scroll. Should be simplified.
491 // to have the intended size and to scroll. Should be simplified.
492 width: 100%;
492 width: 100%;
493 overflow-x: auto;
493 overflow-x: auto;
494 }
494 }
495 }
495 }
496
496
497 .code-highlighttable,
497 .code-highlighttable,
498 div.codeblock {
498 div.codeblock {
499
499
500 &.readme {
500 &.readme {
501 background-color: white;
501 background-color: white;
502 }
502 }
503
503
504 .markdown-block table {
504 .markdown-block table {
505 border-collapse: collapse;
505 border-collapse: collapse;
506
506
507 th,
507 th,
508 td {
508 td {
509 padding: .5em;
509 padding: .5em;
510 border: @border-thickness solid @border-default-color;
510 border: @border-thickness solid @border-default-color;
511 }
511 }
512 }
512 }
513
513
514 table {
514 table {
515 border: 0px;
515 border: 0px;
516 margin: 0;
516 margin: 0;
517 letter-spacing: normal;
517 letter-spacing: normal;
518
518
519
519
520 td {
520 td {
521 border: 0px;
521 border: 0px;
522 vertical-align: top;
522 vertical-align: top;
523 }
523 }
524 }
524 }
525 }
525 }
526
526
527 div.codeblock .code-header .search-path { padding: 0 0 0 10px; }
527 div.codeblock .code-header .search-path { padding: 0 0 0 10px; }
528 div.search-code-body {
528 div.search-code-body {
529 background-color: #ffffff; padding: 5px 0 5px 10px;
529 background-color: #ffffff; padding: 5px 0 5px 10px;
530 pre {
530 pre {
531 .match { background-color: #faffa6;}
531 .match { background-color: #faffa6;}
532 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
532 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
533 }
533 }
534 .code-highlighttable {
534 .code-highlighttable {
535 border-collapse: collapse;
535 border-collapse: collapse;
536
536
537 tr:hover {
537 tr:hover {
538 background: #fafafa;
538 background: #fafafa;
539 }
539 }
540 td.code {
540 td.code {
541 padding-left: 10px;
541 padding-left: 10px;
542 }
542 }
543 td.line {
543 td.line {
544 border-right: 1px solid #ccc !important;
544 border-right: 1px solid #ccc !important;
545 padding-right: 10px;
545 padding-right: 10px;
546 text-align: right;
546 text-align: right;
547 font-family: "Lucida Console",Monaco,monospace;
547 font-family: "Lucida Console",Monaco,monospace;
548 span {
548 span {
549 white-space: pre-wrap;
549 white-space: pre-wrap;
550 color: #666666;
550 color: #666666;
551 }
551 }
552 }
552 }
553 }
553 }
554 }
554 }
555
555
556 div.annotatediv { margin-left: 2px; margin-right: 4px; }
556 div.annotatediv { margin-left: 2px; margin-right: 4px; }
557 .code-highlight {
557 .code-highlight {
558 margin: 0; padding: 0; border-left: @border-thickness solid @grey5;
558 margin: 0; padding: 0; border-left: @border-thickness solid @grey5;
559 pre, .linenodiv pre { padding: 0 5px; margin: 0; }
559 pre, .linenodiv pre { padding: 0 5px; margin: 0; }
560 pre div:target {background-color: @comment-highlight-color !important;}
560 pre div:target {background-color: @comment-highlight-color !important;}
561 }
561 }
562
562
563 .linenos a { text-decoration: none; }
563 .linenos a { text-decoration: none; }
564
564
565 .CodeMirror-selected { background: @rchighlightblue; }
565 .CodeMirror-selected { background: @rchighlightblue; }
566 .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; }
566 .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; }
567 .CodeMirror ::selection { background: @rchighlightblue; }
567 .CodeMirror ::selection { background: @rchighlightblue; }
568 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
568 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
569
569
570 .code { display: block; border:0px !important; }
570 .code { display: block; border:0px !important; }
571 .code-highlight, /* TODO: dan: merge codehilite into code-highlight */
571 .code-highlight, /* TODO: dan: merge codehilite into code-highlight */
572 .codehilite {
572 .codehilite {
573 .hll { background-color: #ffffcc }
573 .hll { background-color: #ffffcc }
574 .c { color: #408080; font-style: italic } /* Comment */
574 .c { color: #408080; font-style: italic } /* Comment */
575 .err, .codehilite .err { border: @border-thickness solid #FF0000 } /* Error */
575 .err, .codehilite .err { border: @border-thickness solid #FF0000 } /* Error */
576 .k { color: #008000; font-weight: bold } /* Keyword */
576 .k { color: #008000; font-weight: bold } /* Keyword */
577 .o { color: #666666 } /* Operator */
577 .o { color: #666666 } /* Operator */
578 .cm { color: #408080; font-style: italic } /* Comment.Multiline */
578 .cm { color: #408080; font-style: italic } /* Comment.Multiline */
579 .cp { color: #BC7A00 } /* Comment.Preproc */
579 .cp { color: #BC7A00 } /* Comment.Preproc */
580 .c1 { color: #408080; font-style: italic } /* Comment.Single */
580 .c1 { color: #408080; font-style: italic } /* Comment.Single */
581 .cs { color: #408080; font-style: italic } /* Comment.Special */
581 .cs { color: #408080; font-style: italic } /* Comment.Special */
582 .gd { color: #A00000 } /* Generic.Deleted */
582 .gd { color: #A00000 } /* Generic.Deleted */
583 .ge { font-style: italic } /* Generic.Emph */
583 .ge { font-style: italic } /* Generic.Emph */
584 .gr { color: #FF0000 } /* Generic.Error */
584 .gr { color: #FF0000 } /* Generic.Error */
585 .gh { color: #000080; font-weight: bold } /* Generic.Heading */
585 .gh { color: #000080; font-weight: bold } /* Generic.Heading */
586 .gi { color: #00A000 } /* Generic.Inserted */
586 .gi { color: #00A000 } /* Generic.Inserted */
587 .go { color: #808080 } /* Generic.Output */
587 .go { color: #808080 } /* Generic.Output */
588 .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
588 .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
589 .gs { font-weight: bold } /* Generic.Strong */
589 .gs { font-weight: bold } /* Generic.Strong */
590 .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
590 .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
591 .gt { color: #0040D0 } /* Generic.Traceback */
591 .gt { color: #0040D0 } /* Generic.Traceback */
592 .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
592 .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
593 .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
593 .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
594 .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
594 .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
595 .kp { color: #008000 } /* Keyword.Pseudo */
595 .kp { color: #008000 } /* Keyword.Pseudo */
596 .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
596 .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
597 .kt { color: #B00040 } /* Keyword.Type */
597 .kt { color: #B00040 } /* Keyword.Type */
598 .m { color: #666666 } /* Literal.Number */
598 .m { color: #666666 } /* Literal.Number */
599 .s { color: #BA2121 } /* Literal.String */
599 .s { color: #BA2121 } /* Literal.String */
600 .na { color: #7D9029 } /* Name.Attribute */
600 .na { color: #7D9029 } /* Name.Attribute */
601 .nb { color: #008000 } /* Name.Builtin */
601 .nb { color: #008000 } /* Name.Builtin */
602 .nc { color: #0000FF; font-weight: bold } /* Name.Class */
602 .nc { color: #0000FF; font-weight: bold } /* Name.Class */
603 .no { color: #880000 } /* Name.Constant */
603 .no { color: #880000 } /* Name.Constant */
604 .nd { color: #AA22FF } /* Name.Decorator */
604 .nd { color: #AA22FF } /* Name.Decorator */
605 .ni { color: #999999; font-weight: bold } /* Name.Entity */
605 .ni { color: #999999; font-weight: bold } /* Name.Entity */
606 .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
606 .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
607 .nf { color: #0000FF } /* Name.Function */
607 .nf { color: #0000FF } /* Name.Function */
608 .nl { color: #A0A000 } /* Name.Label */
608 .nl { color: #A0A000 } /* Name.Label */
609 .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
609 .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
610 .nt { color: #008000; font-weight: bold } /* Name.Tag */
610 .nt { color: #008000; font-weight: bold } /* Name.Tag */
611 .nv { color: #19177C } /* Name.Variable */
611 .nv { color: #19177C } /* Name.Variable */
612 .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
612 .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
613 .w { color: #bbbbbb } /* Text.Whitespace */
613 .w { color: #bbbbbb } /* Text.Whitespace */
614 .mf { color: #666666 } /* Literal.Number.Float */
614 .mf { color: #666666 } /* Literal.Number.Float */
615 .mh { color: #666666 } /* Literal.Number.Hex */
615 .mh { color: #666666 } /* Literal.Number.Hex */
616 .mi { color: #666666 } /* Literal.Number.Integer */
616 .mi { color: #666666 } /* Literal.Number.Integer */
617 .mo { color: #666666 } /* Literal.Number.Oct */
617 .mo { color: #666666 } /* Literal.Number.Oct */
618 .sb { color: #BA2121 } /* Literal.String.Backtick */
618 .sb { color: #BA2121 } /* Literal.String.Backtick */
619 .sc { color: #BA2121 } /* Literal.String.Char */
619 .sc { color: #BA2121 } /* Literal.String.Char */
620 .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
620 .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
621 .s2 { color: #BA2121 } /* Literal.String.Double */
621 .s2 { color: #BA2121 } /* Literal.String.Double */
622 .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
622 .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
623 .sh { color: #BA2121 } /* Literal.String.Heredoc */
623 .sh { color: #BA2121 } /* Literal.String.Heredoc */
624 .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
624 .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
625 .sx { color: #008000 } /* Literal.String.Other */
625 .sx { color: #008000 } /* Literal.String.Other */
626 .sr { color: #BB6688 } /* Literal.String.Regex */
626 .sr { color: #BB6688 } /* Literal.String.Regex */
627 .s1 { color: #BA2121 } /* Literal.String.Single */
627 .s1 { color: #BA2121 } /* Literal.String.Single */
628 .ss { color: #19177C } /* Literal.String.Symbol */
628 .ss { color: #19177C } /* Literal.String.Symbol */
629 .bp { color: #008000 } /* Name.Builtin.Pseudo */
629 .bp { color: #008000 } /* Name.Builtin.Pseudo */
630 .vc { color: #19177C } /* Name.Variable.Class */
630 .vc { color: #19177C } /* Name.Variable.Class */
631 .vg { color: #19177C } /* Name.Variable.Global */
631 .vg { color: #19177C } /* Name.Variable.Global */
632 .vi { color: #19177C } /* Name.Variable.Instance */
632 .vi { color: #19177C } /* Name.Variable.Instance */
633 .il { color: #666666 } /* Literal.Number.Integer.Long */
633 .il { color: #666666 } /* Literal.Number.Integer.Long */
634 }
634 }
635
635
636 /* customized pre blocks for markdown/rst */
636 /* customized pre blocks for markdown/rst */
637 pre.literal-block, .codehilite pre{
637 pre.literal-block, .codehilite pre{
638 padding: @padding;
638 padding: @padding;
639 border: 1px solid @grey6;
639 border: 1px solid @grey6;
640 .border-radius(@border-radius);
640 .border-radius(@border-radius);
641 background-color: @grey7;
641 background-color: @grey7;
642 }
642 }
643
643
644
644
645 /* START NEW CODE BLOCK CSS */
645 /* START NEW CODE BLOCK CSS */
646
646
647 @cb-line-height: 18px;
648 @cb-line-code-padding: 10px;
649
647 table.cb {
650 table.cb {
648 width: 100%;
651 width: 100%;
649 border-collapse: collapse;
652 border-collapse: collapse;
650 margin-bottom: 10px;
653 margin-bottom: 10px;
651
654
652 * {
655 * {
653 box-sizing: border-box;
656 box-sizing: border-box;
654 }
657 }
655
658
656 /* intentionally general selector since .cb-line-selected must override it
659 /* intentionally general selector since .cb-line-selected must override it
657 and they both use !important since the td itself may have a random color
660 and they both use !important since the td itself may have a random color
658 generated by annotation blocks. TLDR: if you change it, make sure
661 generated by annotation blocks. TLDR: if you change it, make sure
659 annotated block selection and line selection in file view still work */
662 annotated block selection and line selection in file view still work */
660 .cb-line-fresh .cb-content {
663 .cb-line-fresh .cb-content {
661 background: white !important;
664 background: white !important;
662 }
665 }
663
666
664 tr.cb-annotate {
667 tr.cb-annotate {
665 border-top: 1px solid #eee;
668 border-top: 1px solid #eee;
666
669
667 &+ .cb-line {
670 &+ .cb-line {
668 border-top: 1px solid #eee;
671 border-top: 1px solid #eee;
669 }
672 }
670
673
671 &:first-child {
674 &:first-child {
672 border-top: none;
675 border-top: none;
673 &+ .cb-line {
676 &+ .cb-line {
674 border-top: none;
677 border-top: none;
675 }
678 }
676 }
679 }
677 }
680 }
678
681
679 td {
682 td {
680 vertical-align: top;
683 vertical-align: top;
681 padding: 2px 10px;
684 padding: 0;
682
685
683 &.cb-content {
686 &.cb-content {
684 white-space: pre-wrap;
685 font-family: @font-family-monospace;
686 font-size: 12.35px;
687 font-size: 12.35px;
687
688
688 span {
689 span.cb-code {
690 line-height: @cb-line-height;
691 padding-left: @cb-line-code-padding;
692 display: block;
693 white-space: pre-wrap;
694 font-family: @font-family-monospace;
689 word-break: break-word;
695 word-break: break-word;
690 }
696 }
691 }
697 }
692
698
693 &.cb-lineno {
699 &.cb-lineno {
694 padding: 0;
700 padding: 0;
695 height: 1px; /* this allows the <a> link to fill to 100% height of the td */
696 width: 50px;
701 width: 50px;
697 color: rgba(0, 0, 0, 0.3);
702 color: rgba(0, 0, 0, 0.3);
698 text-align: right;
703 text-align: right;
699 border-right: 1px solid #eee;
704 border-right: 1px solid #eee;
700 font-family: @font-family-monospace;
705 font-family: @font-family-monospace;
701
706
702 a::before {
707 a::before {
703 content: attr(data-line-no);
708 content: attr(data-line-no);
704 }
709 }
705 &.cb-line-selected {
710 &.cb-line-selected a {
706 background: @comment-highlight-color !important;
711 background: @comment-highlight-color !important;
707 }
712 }
708
713
709 a {
714 a {
710 display: block;
715 display: block;
711 height: 100%;
716 padding-right: @cb-line-code-padding;
717 line-height: @cb-line-height;
712 color: rgba(0, 0, 0, 0.3);
718 color: rgba(0, 0, 0, 0.3);
713 padding: 0 10px; /* vertical padding is 0 so that height: 100% works */
714 line-height: 18px; /* use this instead of vertical padding */
715 }
719 }
716 }
720 }
717
721
718 &.cb-content {
722 &.cb-content {
719 &.cb-line-selected {
723 &.cb-line-selected .cb-code {
720 background: @comment-highlight-color !important;
724 background: @comment-highlight-color !important;
721 }
725 }
722 }
726 }
723
727
724 &.cb-annotate-info {
728 &.cb-annotate-info {
725 width: 320px;
729 width: 320px;
726 min-width: 320px;
730 min-width: 320px;
727 max-width: 320px;
731 max-width: 320px;
728 padding: 5px 2px;
732 padding: 5px 2px;
729 font-size: 13px;
733 font-size: 13px;
730
734
731 strong.cb-annotate-message {
735 strong.cb-annotate-message {
732 padding: 5px 0;
736 padding: 5px 0;
733 white-space: pre-line;
737 white-space: pre-line;
734 display: inline-block;
738 display: inline-block;
735 }
739 }
736 .rc-user {
740 .rc-user {
737 float: none;
741 float: none;
738 padding: 0 6px 0 17px;
742 padding: 0 6px 0 17px;
739 min-width: auto;
743 min-width: auto;
740 min-height: auto;
744 min-height: auto;
741 }
745 }
742 }
746 }
743
747
744 &.cb-annotate-revision {
748 &.cb-annotate-revision {
745 cursor: pointer;
749 cursor: pointer;
746 text-align: right;
750 text-align: right;
747 }
751 }
748 }
752 }
749 }
753 }
@@ -1,70 +1,68 b''
1 <%def name="render_line(line_num, tokens,
1 <%def name="render_line(line_num, tokens,
2 annotation=None,
2 annotation=None,
3 bgcolor=None)">
3 bgcolor=None)">
4 <%
4 <%
5 # avoid module lookups for performance
5 from rhodecode.lib.codeblocks import render_tokenstream
6 from rhodecode.lib.codeblocks import pygment_token_class
6 # avoid module lookup for performance
7 from rhodecode.lib.helpers import html_escape
7 html_escape = h.html_escape
8 %>
8 %>
9 <tr class="cb-line cb-line-fresh"
9 <tr class="cb-line cb-line-fresh"
10 %if annotation:
10 %if annotation:
11 data-revision="${annotation.revision}"
11 data-revision="${annotation.revision}"
12 %endif
12 %endif
13 >
13 >
14 <td class="cb-lineno" id="L${line_num}">
14 <td class="cb-lineno" id="L${line_num}">
15 <a data-line-no="${line_num}" href="#L${line_num}"></a>
15 <a data-line-no="${line_num}" href="#L${line_num}"></a>
16 </td>
16 </td>
17 <td class="cb-content cb-content-fresh"
17 <td class="cb-content cb-content-fresh"
18 %if bgcolor:
18 %if bgcolor:
19 style="background: ${bgcolor}"
19 style="background: ${bgcolor}"
20 %endif
20 %endif
21 >${
21 >
22 ''.join(
22 ## newline at end is necessary for highlight to work when line is empty
23 '<span class="%s">%s</span>' %
23 ## and for copy pasting code to work as expected
24 (pygment_token_class(token_type), html_escape(token_text))
24 <span class="cb-code">${render_tokenstream(tokens)|n}${'\n'}</span>
25 for token_type, token_text in tokens) + '\n' | n
25 </td>
26 }</td>
27 ## this ugly list comp is necessary for performance
28 </tr>
26 </tr>
29 </%def>
27 </%def>
30
28
31 <%def name="render_annotation_lines(annotation, lines, color_hasher)">
29 <%def name="render_annotation_lines(annotation, lines, color_hasher)">
32 <%
30 <%
33 rowspan = len(lines) + 1 # span the line's <tr> and annotation <tr>
31 rowspan = len(lines) + 1 # span the line's <tr> and annotation <tr>
34 %>
32 %>
35 %if not annotation:
33 %if not annotation:
36 <tr class="cb-annotate">
34 <tr class="cb-annotate">
37 <td class="cb-annotate-message" rowspan="${rowspan}"></td>
35 <td class="cb-annotate-message" rowspan="${rowspan}"></td>
38 <td class="cb-annotate-revision" rowspan="${rowspan}"></td>
36 <td class="cb-annotate-revision" rowspan="${rowspan}"></td>
39 </tr>
37 </tr>
40 %else:
38 %else:
41 <tr class="cb-annotate">
39 <tr class="cb-annotate">
42 <td class="cb-annotate-info tooltip"
40 <td class="cb-annotate-info tooltip"
43 rowspan="${rowspan}"
41 rowspan="${rowspan}"
44 title="Author: ${annotation.author | entity}<br>Date: ${annotation.date}<br>Message: ${annotation.message | entity}"
42 title="Author: ${annotation.author | entity}<br>Date: ${annotation.date}<br>Message: ${annotation.message | entity}"
45 >
43 >
46 ${h.gravatar_with_user(annotation.author, 16) | n}
44 ${h.gravatar_with_user(annotation.author, 16) | n}
47 <strong class="cb-annotate-message">${
45 <strong class="cb-annotate-message">${
48 h.truncate(annotation.message, len(lines) * 30)
46 h.truncate(annotation.message, len(lines) * 30)
49 }</strong>
47 }</strong>
50 </td>
48 </td>
51 <td
49 <td
52 class="cb-annotate-revision"
50 class="cb-annotate-revision"
53 rowspan="${rowspan}"
51 rowspan="${rowspan}"
54 data-revision="${annotation.revision}"
52 data-revision="${annotation.revision}"
55 onclick="$('[data-revision=${annotation.revision}]').toggleClass('cb-line-fresh')"
53 onclick="$('[data-revision=${annotation.revision}]').toggleClass('cb-line-fresh')"
56 style="background: ${color_hasher(annotation.raw_id)}">
54 style="background: ${color_hasher(annotation.raw_id)}">
57 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=annotation.raw_id)}">
55 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=annotation.raw_id)}">
58 r${annotation.revision}
56 r${annotation.revision}
59 </a>
57 </a>
60 </td>
58 </td>
61 </tr>
59 </tr>
62 %endif
60 %endif
63
61
64 %for line_num, tokens in lines:
62 %for line_num, tokens in lines:
65 ${render_line(line_num, tokens,
63 ${render_line(line_num, tokens,
66 bgcolor=color_hasher(annotation and annotation.raw_id or ''),
64 bgcolor=color_hasher(annotation and annotation.raw_id or ''),
67 annotation=annotation,
65 annotation=annotation,
68 )}
66 )}
69 %endfor
67 %endfor
70 </%def>
68 </%def>
@@ -1,82 +1,82 b''
1 <%namespace name="sourceblock" file="/codeblocks/source.html"/>
1 <%namespace name="sourceblock" file="/codeblocks/source.html"/>
2
2
3 <div id="codeblock" class="codeblock">
3 <div id="codeblock" class="codeblock">
4 <div class="codeblock-header">
4 <div class="codeblock-header">
5 <div class="stats">
5 <div class="stats">
6 <span> <strong>${c.file}</strong></span>
6 <span> <strong>${c.file}</strong></span>
7 <span> | ${c.file.lines()[0]} ${ungettext('line', 'lines', c.file.lines()[0])}</span>
7 <span> | ${c.file.lines()[0]} ${ungettext('line', 'lines', c.file.lines()[0])}</span>
8 <span> | ${h.format_byte_size_binary(c.file.size)}</span>
8 <span> | ${h.format_byte_size_binary(c.file.size)}</span>
9 <span> | ${c.file.mimetype} </span>
9 <span> | ${c.file.mimetype} </span>
10 <span class="item last"> | ${h.get_lexer_for_filenode(c.file).__class__.__name__}</span>
10 <span class="item last"> | ${h.get_lexer_for_filenode(c.file).__class__.__name__}</span>
11 </div>
11 </div>
12 <div class="buttons">
12 <div class="buttons">
13 <a id="file_history_overview" href="#">
13 <a id="file_history_overview" href="#">
14 ${_('History')}
14 ${_('History')}
15 </a>
15 </a>
16 <a id="file_history_overview_full" style="display: none" href="${h.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
16 <a id="file_history_overview_full" style="display: none" href="${h.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
17 ${_('Show Full History')}
17 ${_('Show Full History')}
18 </a> |
18 </a> |
19 %if c.annotate:
19 %if c.annotate:
20 ${h.link_to(_('Source'), h.url('files_home', repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
20 ${h.link_to(_('Source'), h.url('files_home', repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
21 %else:
21 %else:
22 ${h.link_to(_('Annotation'), h.url('files_annotate_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
22 ${h.link_to(_('Annotation'), h.url('files_annotate_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
23 %endif
23 %endif
24 | ${h.link_to(_('Raw'), h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
24 | ${h.link_to(_('Raw'), h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
25 | <a href="${h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}">
25 | <a href="${h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}">
26 ${_('Download')}
26 ${_('Download')}
27 </a>
27 </a>
28
28
29 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
29 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
30 |
30 |
31 %if c.on_branch_head and c.branch_or_raw_id and not c.file.is_binary:
31 %if c.on_branch_head and c.branch_or_raw_id and not c.file.is_binary:
32 <a href="${h.url('files_edit_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">
32 <a href="${h.url('files_edit_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">
33 ${_('Edit on Branch:%s') % c.branch_or_raw_id}
33 ${_('Edit on Branch:%s') % c.branch_or_raw_id}
34 </a>
34 </a>
35 | <a class="btn-danger btn-link" href="${h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">${_('Delete')}
35 | <a class="btn-danger btn-link" href="${h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">${_('Delete')}
36 </a>
36 </a>
37 %elif c.on_branch_head and c.branch_or_raw_id and c.file.is_binary:
37 %elif c.on_branch_head and c.branch_or_raw_id and c.file.is_binary:
38 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing binary files not allowed'))}
38 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing binary files not allowed'))}
39 | ${h.link_to(_('Delete'), h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit'),class_="btn-danger btn-link")}
39 | ${h.link_to(_('Delete'), h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit'),class_="btn-danger btn-link")}
40 %else:
40 %else:
41 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing files allowed only when on branch head commit'))}
41 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing files allowed only when on branch head commit'))}
42 | ${h.link_to(_('Delete'), '#', class_="btn btn-danger btn-link disabled tooltip", title=_('Deleting files allowed only when on branch head commit'))}
42 | ${h.link_to(_('Delete'), '#', class_="btn btn-danger btn-link disabled tooltip", title=_('Deleting files allowed only when on branch head commit'))}
43 %endif
43 %endif
44 %endif
44 %endif
45 </div>
45 </div>
46 </div>
46 </div>
47 <div id="file_history_container"></div>
47 <div id="file_history_container"></div>
48 <div class="code-body">
48 <div class="code-body">
49 %if c.file.is_binary:
49 %if c.file.is_binary:
50 <div>
50 <div>
51 ${_('Binary file (%s)') % c.file.mimetype}
51 ${_('Binary file (%s)') % c.file.mimetype}
52 </div>
52 </div>
53 %else:
53 %else:
54 % if c.file.size < c.cut_off_limit:
54 % if c.file.size < c.cut_off_limit:
55 %if c.renderer and not c.annotate:
55 %if c.renderer and not c.annotate:
56 ${h.render(c.file.content, renderer=c.renderer)}
56 ${h.render(c.file.content, renderer=c.renderer)}
57 %else:
57 %else:
58 <table class="cb codehilite">
58 <table class="cb codehilite">
59 %if c.annotate:
59 %if c.annotate:
60 <% color_hasher = h.color_hasher() %>
60 <% color_hasher = h.color_hasher() %>
61 %for annotation, lines in c.annotated_lines:
61 %for annotation, lines in c.annotated_lines:
62 ${sourceblock.render_annotation_lines(annotation, lines, color_hasher)}
62 ${sourceblock.render_annotation_lines(annotation, lines, color_hasher)}
63 %endfor
63 %endfor
64 %else:
64 %else:
65 %for line_num, tokens in c.lines:
65 %for line_num, tokens in enumerate(c.lines, 1):
66 ${sourceblock.render_line(line_num, tokens)}
66 ${sourceblock.render_line(line_num, tokens)}
67 %endfor
67 %endfor
68 %endif
68 %endif
69 </table>
69 </table>
70 </div>
70 </div>
71 %endif
71 %endif
72 %else:
72 %else:
73 ${_('File is too big to display')} ${h.link_to(_('Show as raw'),
73 ${_('File is too big to display')} ${h.link_to(_('Show as raw'),
74 h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
74 h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
75 %endif
75 %endif
76 %endif
76 %endif
77 </div>
77 </div>
78 </div>
78 </div>
79
79
80 <script>
80 <script>
81 var source_page = true;
81 var source_page = true;
82 </script>
82 </script>
General Comments 0
You need to be logged in to leave comments. Login now