##// END OF EJS Templates
Highlight changes inside diff lines (#7139)....
Jean-Philippe Lang -
r4974:21c79827ff73
parent child
Show More
@@ -0,0 +1,46
1 --- partials.txt Wed Jan 19 12:06:17 2011
2 +++ partials.1.txt Wed Jan 19 12:06:10 2011
3 @@ -1,31 +1,31 @@
4 -Lorem ipsum dolor sit amet, consectetur adipiscing elit
5 +Lorem ipsum dolor sit amet, consectetur adipiscing xx
6 Praesent et sagittis dui. Vivamus ac diam diam
7 -Ut sed auctor justo
8 +xxx auctor justo
9 Suspendisse venenatis sollicitudin magna quis suscipit
10 -Sed blandit gravida odio ac ultrices
11 +Sed blandit gxxxxa odio ac ultrices
12 Morbi rhoncus est ut est aliquam tempus
13 -Morbi id nisi vel felis tincidunt tempus
14 +Morbi id nisi vel felis xx tempus
15 Mauris auctor sagittis ante eu luctus
16 -Fusce commodo felis sed ligula congue molestie
17 +Fusce commodo felis sed ligula congue
18 Lorem ipsum dolor sit amet, consectetur adipiscing elit
19 -Praesent et sagittis dui. Vivamus ac diam diam
20 +et sagittis dui. Vivamus ac diam diam
21 Ut sed auctor justo
22 Suspendisse venenatis sollicitudin magna quis suscipit
23 Sed blandit gravida odio ac ultrices
24
25 -Lorem ipsum dolor sit amet, consectetur adipiscing elit
26 -Praesent et sagittis dui. Vivamus ac diam diam
27 +Lorem ipsum dolor sit amet, xxxx adipiscing elit
28 Ut sed auctor justo
29 Suspendisse venenatis sollicitudin magna quis suscipit
30 Sed blandit gravida odio ac ultrices
31 -Morbi rhoncus est ut est aliquam tempus
32 +Morbi rhoncus est ut est xxxx tempus
33 +New line
34 Morbi id nisi vel felis tincidunt tempus
35 Mauris auctor sagittis ante eu luctus
36 Fusce commodo felis sed ligula congue molestie
37
38 -Lorem ipsum dolor sit amet, consectetur adipiscing elit
39 -Praesent et sagittis dui. Vivamus ac diam diam
40 -Ut sed auctor justo
41 +Lorem ipsum dolor sit amet, xxxxtetur adipiscing elit
42 +Praesent et xxxxx. Vivamus ac diam diam
43 +Ut sed auctor
44 Suspendisse venenatis sollicitudin magna quis suscipit
45 Sed blandit gravida odio ac ultrices
46 Morbi rhoncus est ut est aliquam tempus
@@ -1,67 +1,57
1 1 <% diff = Redmine::UnifiedDiff.new(diff, :type => diff_type, :max_lines => Setting.diff_max_lines_displayed.to_i) -%>
2
2 3 <% diff.each do |table_file| -%>
3 4 <div class="autoscroll">
4 <% if diff_type == 'sbs' -%>
5 <% if diff.diff_type == 'sbs' -%>
5 6 <table class="filecontent">
6 7 <thead>
7 8 <tr><th colspan="4" class="filename"><%=to_utf8 table_file.file_name %></th></tr>
8 9 </thead>
9 10 <tbody>
10 <% prev_line_left, prev_line_right = nil, nil -%>
11 <% table_file.keys.sort.each do |key| -%>
12 <% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
11 <% table_file.each_line do |spacing, line| -%>
12 <% if spacing -%>
13 13 <tr class="spacing">
14 <th class="line-num">...</th><td></td><th class="line-num">...</th><td></td>
14 <th class="line-num">...</th><td></td><th class="line-num">...</th><td></td>
15 </tr>
15 16 <% end -%>
16 17 <tr>
17 <th class="line-num"><%= table_file[key].nb_line_left %></th>
18 <td class="line-code <%= table_file[key].type_diff_left %>">
19 <pre><%=to_utf8 table_file[key].line_left %></pre>
18 <th class="line-num"><%= line.nb_line_left %></th>
19 <td class="line-code <%= line.type_diff_left %>">
20 <pre><%=to_utf8 line.html_line_left %></pre>
20 21 </td>
21 <th class="line-num"><%= table_file[key].nb_line_right %></th>
22 <td class="line-code <%= table_file[key].type_diff_right %>">
23 <pre><%=to_utf8 table_file[key].line_right %></pre>
22 <th class="line-num"><%= line.nb_line_right %></th>
23 <td class="line-code <%= line.type_diff_right %>">
24 <pre><%=to_utf8 line.html_line_right %></pre>
24 25 </td>
25 26 </tr>
26 <% prev_line_left, prev_line_right = table_file[key].nb_line_left.to_i, table_file[key].nb_line_right.to_i -%>
27 27 <% end -%>
28 28 </tbody>
29 29 </table>
30 30
31 31 <% else -%>
32 <table class="filecontent syntaxhl">
32 <table class="filecontent">
33 33 <thead>
34 34 <tr><th colspan="3" class="filename"><%=to_utf8 table_file.file_name %></th></tr>
35 35 </thead>
36 36 <tbody>
37 <% prev_line_left, prev_line_right = nil, nil -%>
38 <% table_file.keys.sort.each do |key, line| %>
39 <% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
37 <% table_file.each_line do |spacing, line| %>
38 <% if spacing -%>
40 39 <tr class="spacing">
41 <th class="line-num">...</th><th class="line-num">...</th><td></td>
40 <th class="line-num">...</th><th class="line-num">...</th><td></td>
42 41 </tr>
43 42 <% end -%>
44 43 <tr>
45 <th class="line-num"><%= table_file[key].nb_line_left %></th>
46 <th class="line-num"><%= table_file[key].nb_line_right %></th>
47 <% if table_file[key].line_left.empty? -%>
48 <td class="line-code <%= table_file[key].type_diff_right %>">
49 <pre><%=to_utf8 table_file[key].line_right %></pre>
44 <th class="line-num"><%= line.nb_line_left %></th>
45 <th class="line-num"><%= line.nb_line_right %></th>
46 <td class="line-code <%= line.type_diff %>">
47 <pre><%=to_utf8 line.html_line %></pre>
50 48 </td>
51 <% else -%>
52 <td class="line-code <%= table_file[key].type_diff_left %>">
53 <pre><%=to_utf8 table_file[key].line_left %></pre>
54 </td>
55 <% end -%>
56 49 </tr>
57 <% prev_line_left = table_file[key].nb_line_left.to_i if table_file[key].nb_line_left.to_i > 0 -%>
58 <% prev_line_right = table_file[key].nb_line_right.to_i if table_file[key].nb_line_right.to_i > 0 -%>
59 50 <% end -%>
60 51 </tbody>
61 52 </table>
62 53 <% end -%>
63
64 54 </div>
65 55 <% end -%>
66 56
67 57 <%= l(:text_diff_truncated) if diff.truncated? %>
@@ -1,198 +1,258
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 module Redmine
19 19 # Class used to parse unified diffs
20 class UnifiedDiff < Array
20 class UnifiedDiff < Array
21 attr_reader :diff_type
22
21 23 def initialize(diff, options={})
22 24 options.assert_valid_keys(:type, :max_lines)
23 25 diff = diff.split("\n") if diff.is_a?(String)
24 diff_type = options[:type] || 'inline'
26 @diff_type = options[:type] || 'inline'
25 27 lines = 0
26 28 @truncated = false
27 diff_table = DiffTable.new(diff_type)
29 diff_table = DiffTable.new(@diff_type)
28 30 diff.each do |line|
29 31 line_encoding = nil
30 32 if line.respond_to?(:force_encoding)
31 33 line_encoding = line.encoding
32 34 # TODO: UTF-16 and Japanese CP932 which is imcompatible with ASCII
33 35 # In Japan, diffrence between file path encoding
34 36 # and file contents encoding is popular.
35 37 line.force_encoding('ASCII-8BIT')
36 38 end
37 39 unless diff_table.add_line line
38 40 line.force_encoding(line_encoding) if line_encoding
39 41 self << diff_table if diff_table.length > 0
40 42 diff_table = DiffTable.new(diff_type)
41 43 end
42 44 lines += 1
43 45 if options[:max_lines] && lines > options[:max_lines]
44 46 @truncated = true
45 47 break
46 48 end
47 49 end
48 50 self << diff_table unless diff_table.empty?
49 51 self
50 52 end
51 53
52 54 def truncated?; @truncated; end
53 55 end
54 56
55 57 # Class that represents a file diff
56 class DiffTable < Hash
57 attr_reader :file_name, :line_num_l, :line_num_r
58 class DiffTable < Array
59 attr_reader :file_name
58 60
59 61 # Initialize with a Diff file and the type of Diff View
60 62 # The type view must be inline or sbs (side_by_side)
61 63 def initialize(type="inline")
62 64 @parsing = false
63 @nb_line = 1
64 @start = false
65 @before = 'same'
66 @second = true
65 @added = 0
66 @removed = 0
67 67 @type = type
68 68 end
69 69
70 70 # Function for add a line of this Diff
71 71 # Returns false when the diff ends
72 72 def add_line(line)
73 73 unless @parsing
74 74 if line =~ /^(---|\+\+\+) (.*)$/
75 75 @file_name = $2
76 76 elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
77 77 @line_num_l = $2.to_i
78 78 @line_num_r = $5.to_i
79 79 @parsing = true
80 80 end
81 81 else
82 82 if line =~ /^[^\+\-\s@\\]/
83 83 @parsing = false
84 84 return false
85 85 elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
86 86 @line_num_l = $2.to_i
87 87 @line_num_r = $5.to_i
88 88 else
89 @nb_line += 1 if parse_line(line, @type)
89 parse_line(line, @type)
90 90 end
91 91 end
92 92 return true
93 93 end
94
95 def each_line
96 prev_line_left, prev_line_right = nil, nil
97 each do |line|
98 spacing = prev_line_left && prev_line_right && (line.nb_line_left != prev_line_left+1) && (line.nb_line_right != prev_line_right+1)
99 yield spacing, line
100 prev_line_left = line.nb_line_left.to_i if line.nb_line_left.to_i > 0
101 prev_line_right = line.nb_line_right.to_i if line.nb_line_right.to_i > 0
102 end
103 end
94 104
95 105 def inspect
96 106 puts '### DIFF TABLE ###'
97 107 puts "file : #{file_name}"
98 108 self.each do |d|
99 109 d.inspect
100 110 end
101 111 end
102 112
103 private
104 # Test if is a Side By Side type
105 def sbs?(type, func)
106 if @start and type == "sbs"
107 if @before == func and @second
108 tmp_nb_line = @nb_line
109 self[tmp_nb_line] = Diff.new
110 else
111 @second = false
112 tmp_nb_line = @start
113 @start += 1
114 @nb_line -= 1
115 end
116 else
117 tmp_nb_line = @nb_line
118 @start = @nb_line
119 self[tmp_nb_line] = Diff.new
120 @second = true
121 end
122 unless self[tmp_nb_line]
123 @nb_line += 1
124 self[tmp_nb_line] = Diff.new
125 else
126 self[tmp_nb_line]
127 end
128 end
113 private
129 114
130 115 # Escape the HTML for the diff
131 116 def escapeHTML(line)
132 117 CGI.escapeHTML(line)
133 118 end
119
120 def diff_for_added_line
121 if @type == 'sbs' && @removed > 0 && @added < @removed
122 self[-(@removed - @added)]
123 else
124 diff = Diff.new
125 self << diff
126 diff
127 end
128 end
134 129
135 130 def parse_line(line, type="inline")
136 131 if line[0, 1] == "+"
137 diff = sbs? type, 'add'
138 @before = 'add'
132 diff = diff_for_added_line
139 133 diff.line_right = escapeHTML line[1..-1]
140 134 diff.nb_line_right = @line_num_r
141 135 diff.type_diff_right = 'diff_in'
142 136 @line_num_r += 1
137 @added += 1
143 138 true
144 139 elsif line[0, 1] == "-"
145 diff = sbs? type, 'remove'
146 @before = 'remove'
147 diff.line_left = escapeHTML line[1..-1]
148 diff.nb_line_left = @line_num_l
149 diff.type_diff_left = 'diff_out'
150 @line_num_l += 1
151 true
152 elsif line[0, 1] =~ /\s/
153 @before = 'same'
154 @start = false
155 140 diff = Diff.new
156 diff.line_right = escapeHTML line[1..-1]
157 diff.nb_line_right = @line_num_r
158 141 diff.line_left = escapeHTML line[1..-1]
159 142 diff.nb_line_left = @line_num_l
160 self[@nb_line] = diff
143 diff.type_diff_left = 'diff_out'
144 self << diff
161 145 @line_num_l += 1
162 @line_num_r += 1
146 @removed += 1
163 147 true
164 elsif line[0, 1] = "\\"
148 else
149 write_offsets
150 if line[0, 1] =~ /\s/
151 diff = Diff.new
152 diff.line_right = escapeHTML line[1..-1]
153 diff.nb_line_right = @line_num_r
154 diff.line_left = escapeHTML line[1..-1]
155 diff.nb_line_left = @line_num_l
156 self << diff
157 @line_num_l += 1
158 @line_num_r += 1
159 true
160 elsif line[0, 1] = "\\"
165 161 true
166 162 else
167 163 false
168 164 end
169 165 end
170 166 end
167
168 def write_offsets
169 if @added > 0 && @added == @removed
170 @added.times do |i|
171 line = self[-(1 + i)]
172 removed = (@type == 'sbs') ? line : self[-(1 + @added + i)]
173 offsets = offsets(removed.line_left, line.line_right)
174 removed.offsets = line.offsets = offsets
175 end
176 end
177 @added = 0
178 @removed = 0
179 end
180
181 def offsets(line_left, line_right)
182 if line_left.present? && line_right.present? && line_left != line_right
183 max = [line_left.size, line_right.size].min
184 starting = 0
185 while starting < max && line_left[starting] == line_right[starting]
186 starting += 1
187 end
188 ending = -1
189 while ending >= -(max - starting) && line_left[ending] == line_right[ending]
190 ending -= 1
191 end
192 unless starting == 0 && ending == -1
193 [starting, ending]
194 end
195 end
196 end
197 end
171 198
172 199 # A line of diff
173 200 class Diff
174 201 attr_accessor :nb_line_left
175 202 attr_accessor :line_left
176 203 attr_accessor :nb_line_right
177 204 attr_accessor :line_right
178 205 attr_accessor :type_diff_right
179 206 attr_accessor :type_diff_left
207 attr_accessor :offsets
180 208
181 209 def initialize()
182 210 self.nb_line_left = ''
183 211 self.nb_line_right = ''
184 212 self.line_left = ''
185 213 self.line_right = ''
186 214 self.type_diff_right = ''
187 215 self.type_diff_left = ''
188 216 end
217
218 def type_diff
219 type_diff_right == 'diff_in' ? type_diff_right : type_diff_left
220 end
221
222 def line
223 type_diff_right == 'diff_in' ? line_right : line_left
224 end
225
226 def html_line_left
227 if offsets
228 line_left.dup.insert(offsets.first, '<span>').insert(offsets.last, '</span>')
229 else
230 line_left
231 end
232 end
233
234 def html_line_right
235 if offsets
236 line_right.dup.insert(offsets.first, '<span>').insert(offsets.last, '</span>')
237 else
238 line_right
239 end
240 end
241
242 def html_line
243 if offsets
244 line.dup.insert(offsets.first, '<span>').insert(offsets.last, '</span>')
245 else
246 line
247 end
248 end
189 249
190 250 def inspect
191 251 puts '### Start Line Diff ###'
192 252 puts self.nb_line_left
193 253 puts self.line_left
194 254 puts self.nb_line_right
195 255 puts self.line_right
196 256 end
197 257 end
198 258 end
@@ -1,961 +1,963
1 1 html {overflow-y:scroll;}
2 2 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
3 3
4 4 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
5 5 h1 {margin:0; padding:0; font-size: 24px;}
6 6 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
7 7 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
8 8 h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
9 9
10 10 /***** Layout *****/
11 11 #wrapper {background: white;}
12 12
13 13 #top-menu {background: #2C4056; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
14 14 #top-menu ul {margin: 0; padding: 0;}
15 15 #top-menu li {
16 16 float:left;
17 17 list-style-type:none;
18 18 margin: 0px 0px 0px 0px;
19 19 padding: 0px 0px 0px 0px;
20 20 white-space:nowrap;
21 21 }
22 22 #top-menu a {color: #fff; margin-right: 8px; font-weight: bold;}
23 23 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
24 24
25 25 #account {float:right;}
26 26
27 27 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
28 28 #header a {color:#f8f8f8;}
29 29 #header h1 a.ancestor { font-size: 80%; }
30 30 #quick-search {float:right;}
31 31
32 32 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
33 33 #main-menu ul {margin: 0; padding: 0;}
34 34 #main-menu li {
35 35 float:left;
36 36 list-style-type:none;
37 37 margin: 0px 2px 0px 0px;
38 38 padding: 0px 0px 0px 0px;
39 39 white-space:nowrap;
40 40 }
41 41 #main-menu li a {
42 42 display: block;
43 43 color: #fff;
44 44 text-decoration: none;
45 45 font-weight: bold;
46 46 margin: 0;
47 47 padding: 4px 10px 4px 10px;
48 48 }
49 49 #main-menu li a:hover {background:#759FCF; color:#fff;}
50 50 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
51 51
52 52 #admin-menu ul {margin: 0; padding: 0;}
53 53 #admin-menu li {margin: 0; padding: 0 0 12px 0; list-style-type:none;}
54 54
55 55 #admin-menu a { background-position: 0% 40%; background-repeat: no-repeat; padding-left: 20px; padding-top: 2px; padding-bottom: 3px;}
56 56 #admin-menu a.projects { background-image: url(../images/projects.png); }
57 57 #admin-menu a.users { background-image: url(../images/user.png); }
58 58 #admin-menu a.groups { background-image: url(../images/group.png); }
59 59 #admin-menu a.roles { background-image: url(../images/database_key.png); }
60 60 #admin-menu a.trackers { background-image: url(../images/ticket.png); }
61 61 #admin-menu a.issue_statuses { background-image: url(../images/ticket_edit.png); }
62 62 #admin-menu a.workflows { background-image: url(../images/ticket_go.png); }
63 63 #admin-menu a.custom_fields { background-image: url(../images/textfield.png); }
64 64 #admin-menu a.enumerations { background-image: url(../images/text_list_bullets.png); }
65 65 #admin-menu a.settings { background-image: url(../images/changeset.png); }
66 66 #admin-menu a.plugins { background-image: url(../images/plugin.png); }
67 67 #admin-menu a.info { background-image: url(../images/help.png); }
68 68 #admin-menu a.server_authentication { background-image: url(../images/server_key.png); }
69 69
70 70 #main {background-color:#EEEEEE;}
71 71
72 72 #sidebar{ float: right; width: 22%; position: relative; z-index: 9; padding: 0; margin: 0;}
73 73 * html #sidebar{ width: 22%; }
74 74 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
75 75 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
76 76 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
77 77 #sidebar .contextual { margin-right: 1em; }
78 78
79 79 #content { width: 75%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; }
80 80 * html #content{ width: 75%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
81 81 html>body #content { min-height: 600px; }
82 82 * html body #content { height: 600px; } /* IE */
83 83
84 84 #main.nosidebar #sidebar{ display: none; }
85 85 #main.nosidebar #content{ width: auto; border-right: 0; }
86 86
87 87 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
88 88
89 89 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
90 90 #login-form table td {padding: 6px;}
91 91 #login-form label {font-weight: bold;}
92 92 #login-form input#username, #login-form input#password { width: 300px; }
93 93
94 94 input#openid_url { background: url(../images/openid-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; padding-left: 18px; }
95 95
96 96 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
97 97
98 98 /***** Links *****/
99 99 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
100 100 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
101 101 a img{ border: 0; }
102 102
103 103 a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
104 104
105 105 /***** Tables *****/
106 106 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
107 107 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
108 108 table.list td { vertical-align: top; }
109 109 table.list td.id { width: 2%; text-align: center;}
110 110 table.list td.checkbox { width: 15px; padding: 0px;}
111 111 table.list td.buttons { width: 15%; white-space:nowrap; text-align: right; }
112 112 table.list td.buttons a { padding-right: 0.6em; }
113 113 table.list caption { text-align: left; padding: 0.5em 0.5em 0.5em 0; }
114 114
115 115 tr.project td.name a { white-space:nowrap; }
116 116
117 117 tr.project.idnt td.name span {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
118 118 tr.project.idnt-1 td.name {padding-left: 0.5em;}
119 119 tr.project.idnt-2 td.name {padding-left: 2em;}
120 120 tr.project.idnt-3 td.name {padding-left: 3.5em;}
121 121 tr.project.idnt-4 td.name {padding-left: 5em;}
122 122 tr.project.idnt-5 td.name {padding-left: 6.5em;}
123 123 tr.project.idnt-6 td.name {padding-left: 8em;}
124 124 tr.project.idnt-7 td.name {padding-left: 9.5em;}
125 125 tr.project.idnt-8 td.name {padding-left: 11em;}
126 126 tr.project.idnt-9 td.name {padding-left: 12.5em;}
127 127
128 128 tr.issue { text-align: center; white-space: nowrap; }
129 129 tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal; }
130 130 tr.issue td.subject { text-align: left; }
131 131 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
132 132
133 133 tr.issue.idnt td.subject a {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
134 134 tr.issue.idnt-1 td.subject {padding-left: 0.5em;}
135 135 tr.issue.idnt-2 td.subject {padding-left: 2em;}
136 136 tr.issue.idnt-3 td.subject {padding-left: 3.5em;}
137 137 tr.issue.idnt-4 td.subject {padding-left: 5em;}
138 138 tr.issue.idnt-5 td.subject {padding-left: 6.5em;}
139 139 tr.issue.idnt-6 td.subject {padding-left: 8em;}
140 140 tr.issue.idnt-7 td.subject {padding-left: 9.5em;}
141 141 tr.issue.idnt-8 td.subject {padding-left: 11em;}
142 142 tr.issue.idnt-9 td.subject {padding-left: 12.5em;}
143 143
144 144 tr.entry { border: 1px solid #f8f8f8; }
145 145 tr.entry td { white-space: nowrap; }
146 146 tr.entry td.filename { width: 30%; }
147 147 tr.entry td.size { text-align: right; font-size: 90%; }
148 148 tr.entry td.revision, tr.entry td.author { text-align: center; }
149 149 tr.entry td.age { text-align: right; }
150 150 tr.entry.file td.filename a { margin-left: 16px; }
151 151
152 152 tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
153 153 tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
154 154
155 155 tr.changeset td.author { text-align: center; width: 15%; }
156 156 tr.changeset td.committed_on { text-align: center; width: 15%; }
157 157
158 158 table.files tr.file td { text-align: center; }
159 159 table.files tr.file td.filename { text-align: left; padding-left: 24px; }
160 160 table.files tr.file td.digest { font-size: 80%; }
161 161
162 162 table.members td.roles, table.memberships td.roles { width: 45%; }
163 163
164 164 tr.message { height: 2.6em; }
165 165 tr.message td.subject { padding-left: 20px; }
166 166 tr.message td.created_on { white-space: nowrap; }
167 167 tr.message td.last_message { font-size: 80%; white-space: nowrap; }
168 168 tr.message.locked td.subject { background: url(../images/locked.png) no-repeat 0 1px; }
169 169 tr.message.sticky td.subject { background: url(../images/bullet_go.png) no-repeat 0 1px; font-weight: bold; }
170 170
171 171 tr.version.closed, tr.version.closed a { color: #999; }
172 172 tr.version td.name { padding-left: 20px; }
173 173 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70%; }
174 174 tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; white-space:nowrap; }
175 175
176 176 tr.user td { width:13%; }
177 177 tr.user td.email { width:18%; }
178 178 tr.user td { white-space: nowrap; }
179 179 tr.user.locked, tr.user.registered { color: #aaa; }
180 180 tr.user.locked a, tr.user.registered a { color: #aaa; }
181 181
182 182 tr.wiki-page-version td.updated_on, tr.wiki-page-version td.author {text-align:center;}
183 183
184 184 tr.time-entry { text-align: center; white-space: nowrap; }
185 185 tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
186 186 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
187 187 td.hours .hours-dec { font-size: 0.9em; }
188 188
189 189 table.plugins td { vertical-align: middle; }
190 190 table.plugins td.configure { text-align: right; padding-right: 1em; }
191 191 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
192 192 table.plugins span.description { display: block; font-size: 0.9em; }
193 193 table.plugins span.url { display: block; font-size: 0.9em; }
194 194
195 195 table.list tbody tr.group td { padding: 0.8em 0 0.5em 0.3em; font-weight: bold; border-bottom: 1px solid #ccc; }
196 196 table.list tbody tr.group span.count { color: #aaa; font-size: 80%; }
197 197
198 198 table.list tbody tr:hover { background-color:#ffffdd; }
199 199 table.list tbody tr.group:hover { background-color:inherit; }
200 200 table td {padding:2px;}
201 201 table p {margin:0;}
202 202 .odd {background-color:#f6f7f8;}
203 203 .even {background-color: #fff;}
204 204
205 205 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
206 206 a.sort.asc { background-image: url(../images/sort_asc.png); }
207 207 a.sort.desc { background-image: url(../images/sort_desc.png); }
208 208
209 209 table.attributes { width: 100% }
210 210 table.attributes th { vertical-align: top; text-align: left; }
211 211 table.attributes td { vertical-align: top; }
212 212
213 213 table.boards a.board, h3.comments { background: url(../images/comment.png) no-repeat 0% 50%; padding-left: 20px; }
214 214
215 215 td.center {text-align:center;}
216 216
217 217 h3.version { background: url(../images/package.png) no-repeat 0% 50%; padding-left: 20px; }
218 218
219 219 div.issues h3 { background: url(../images/ticket.png) no-repeat 0% 50%; padding-left: 20px; }
220 220 div.members h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
221 221 div.news h3 { background: url(../images/news.png) no-repeat 0% 50%; padding-left: 20px; }
222 222 div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padding-left: 20px; }
223 223
224 224 #watchers ul {margin: 0; padding: 0;}
225 225 #watchers li {list-style-type:none;margin: 0px 2px 0px 0px; padding: 0px 0px 0px 0px;}
226 226 #watchers select {width: 95%; display: block;}
227 227 #watchers a.delete {opacity: 0.4;}
228 228 #watchers a.delete:hover {opacity: 1;}
229 229 #watchers img.gravatar {vertical-align: middle;margin: 0 4px 2px 0;}
230 230
231 231 .highlight { background-color: #FCFD8D;}
232 232 .highlight.token-1 { background-color: #faa;}
233 233 .highlight.token-2 { background-color: #afa;}
234 234 .highlight.token-3 { background-color: #aaf;}
235 235
236 236 .box{
237 237 padding:6px;
238 238 margin-bottom: 10px;
239 239 background-color:#f6f6f6;
240 240 color:#505050;
241 241 line-height:1.5em;
242 242 border: 1px solid #e4e4e4;
243 243 }
244 244
245 245 div.square {
246 246 border: 1px solid #999;
247 247 float: left;
248 248 margin: .3em .4em 0 .4em;
249 249 overflow: hidden;
250 250 width: .6em; height: .6em;
251 251 }
252 252 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
253 253 .contextual input, .contextual select {font-size:0.9em;}
254 254 .message .contextual { margin-top: 0; }
255 255
256 256 .splitcontentleft{float:left; width:49%;}
257 257 .splitcontentright{float:right; width:49%;}
258 258 form {display: inline;}
259 259 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
260 260 fieldset {border: 1px solid #e4e4e4; margin:0;}
261 261 legend {color: #484848;}
262 262 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
263 263 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
264 264 blockquote blockquote { margin-left: 0;}
265 265 acronym { border-bottom: 1px dotted; cursor: help; }
266 266 textarea.wiki-edit { width: 99%; }
267 267 li p {margin-top: 0;}
268 268 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
269 269 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
270 270 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
271 271 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
272 272
273 273 div.issue div.subject div div { padding-left: 16px; }
274 274 div.issue div.subject p {margin: 0; margin-bottom: 0.1em; font-size: 90%; color: #999;}
275 275 div.issue div.subject>div>p { margin-top: 0.5em; }
276 276 div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;}
277 277
278 278 #issue_tree table.issues { border: 0; }
279 279 #issue_tree td.checkbox {display:none;}
280 280
281 281 fieldset.collapsible { border-width: 1px 0 0 0; font-size: 0.9em; }
282 282 fieldset.collapsible legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
283 283 fieldset.collapsible.collapsed legend { background-image: url(../images/arrow_collapsed.png); }
284 284
285 285 fieldset#date-range p { margin: 2px 0 2px 0; }
286 286 fieldset#filters table { border-collapse: collapse; }
287 287 fieldset#filters table td { padding: 0; vertical-align: middle; }
288 288 fieldset#filters tr.filter { height: 2em; }
289 289 fieldset#filters td.add-filter { text-align: right; vertical-align: top; }
290 290 .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
291 291
292 292 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
293 293 div#issue-changesets div.changeset { padding: 4px;}
294 294 div#issue-changesets div.changeset { border-bottom: 1px solid #ddd; }
295 295 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
296 296
297 297 div#activity dl, #search-results { margin-left: 2em; }
298 298 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
299 299 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
300 300 div#activity dt.me .time { border-bottom: 1px solid #999; }
301 301 div#activity dt .time { color: #777; font-size: 80%; }
302 302 div#activity dd .description, #search-results dd .description { font-style: italic; }
303 303 div#activity span.project:after, #search-results span.project:after { content: " -"; }
304 304 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
305 305
306 306 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
307 307
308 308 div#search-results-counts {float:right;}
309 309 div#search-results-counts ul { margin-top: 0.5em; }
310 310 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
311 311
312 312 dt.issue { background-image: url(../images/ticket.png); }
313 313 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
314 314 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
315 315 dt.issue-note { background-image: url(../images/ticket_note.png); }
316 316 dt.changeset { background-image: url(../images/changeset.png); }
317 317 dt.news { background-image: url(../images/news.png); }
318 318 dt.message { background-image: url(../images/message.png); }
319 319 dt.reply { background-image: url(../images/comments.png); }
320 320 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
321 321 dt.attachment { background-image: url(../images/attachment.png); }
322 322 dt.document { background-image: url(../images/document.png); }
323 323 dt.project { background-image: url(../images/projects.png); }
324 324 dt.time-entry { background-image: url(../images/time.png); }
325 325
326 326 #search-results dt.issue.closed { background-image: url(../images/ticket_checked.png); }
327 327
328 328 div#roadmap .related-issues { margin-bottom: 1em; }
329 329 div#roadmap .related-issues td.checkbox { display: none; }
330 330 div#roadmap .wiki h1:first-child { display: none; }
331 331 div#roadmap .wiki h1 { font-size: 120%; }
332 332 div#roadmap .wiki h2 { font-size: 110%; }
333 333
334 334 div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
335 335 div#version-summary fieldset { margin-bottom: 1em; }
336 336 div#version-summary .total-hours { text-align: right; }
337 337
338 338 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
339 339 table#time-report tbody tr { font-style: italic; color: #777; }
340 340 table#time-report tbody tr.last-level { font-style: normal; color: #555; }
341 341 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
342 342 table#time-report .hours-dec { font-size: 0.9em; }
343 343
344 344 form .attributes { margin-bottom: 8px; }
345 345 form .attributes p { padding-top: 1px; padding-bottom: 2px; }
346 346 form .attributes select { min-width: 50%; }
347 347
348 348 ul.projects { margin: 0; padding-left: 1em; }
349 349 ul.projects.root { margin: 0; padding: 0; }
350 350 ul.projects ul.projects { border-left: 3px solid #e0e0e0; }
351 351 ul.projects li.root { list-style-type:none; margin-bottom: 1em; }
352 352 ul.projects li.child { list-style-type:none; margin-top: 1em;}
353 353 ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
354 354 .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
355 355
356 356 #tracker_project_ids ul { margin: 0; padding-left: 1em; }
357 357 #tracker_project_ids li { list-style-type:none; }
358 358
359 359 ul.properties {padding:0; font-size: 0.9em; color: #777;}
360 360 ul.properties li {list-style-type:none;}
361 361 ul.properties li span {font-style:italic;}
362 362
363 363 .total-hours { font-size: 110%; font-weight: bold; }
364 364 .total-hours span.hours-int { font-size: 120%; }
365 365
366 366 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
367 367 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
368 368
369 369 #workflow_copy_form select { width: 200px; }
370 370
371 371 textarea#custom_field_possible_values {width: 99%}
372 372
373 373 .pagination {font-size: 90%}
374 374 p.pagination {margin-top:8px;}
375 375
376 376 /***** Tabular forms ******/
377 377 .tabular p{
378 378 margin: 0;
379 379 padding: 5px 0 8px 0;
380 380 padding-left: 180px; /*width of left column containing the label elements*/
381 381 height: 1%;
382 382 clear:left;
383 383 }
384 384
385 385 html>body .tabular p {overflow:hidden;}
386 386
387 387 .tabular label{
388 388 font-weight: bold;
389 389 float: left;
390 390 text-align: right;
391 391 margin-left: -180px; /*width of left column*/
392 392 width: 175px; /*width of labels. Should be smaller than left column to create some right
393 393 margin*/
394 394 }
395 395
396 396 .tabular label.floating{
397 397 font-weight: normal;
398 398 margin-left: 0px;
399 399 text-align: left;
400 400 width: 270px;
401 401 }
402 402
403 403 .tabular label.block{
404 404 font-weight: normal;
405 405 margin-left: 0px !important;
406 406 text-align: left;
407 407 float: none;
408 408 display: block;
409 409 width: auto;
410 410 }
411 411
412 412 .tabular label.inline{
413 413 float:none;
414 414 margin-left: 5px !important;
415 415 width: auto;
416 416 }
417 417
418 418 input#time_entry_comments { width: 90%;}
419 419
420 420 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
421 421
422 422 .tabular.settings p{ padding-left: 300px; }
423 423 .tabular.settings label{ margin-left: -300px; width: 295px; }
424 424 .tabular.settings textarea { width: 99%; }
425 425
426 426 fieldset.settings label { display: block; }
427 427 .parent { padding-left: 20px; }
428 428
429 429 .required {color: #bb0000;}
430 430 .summary {font-style: italic;}
431 431
432 432 #attachments_fields input[type=text] {margin-left: 8px; }
433 433
434 434 div.attachments { margin-top: 12px; }
435 435 div.attachments p { margin:4px 0 2px 0; }
436 436 div.attachments img { vertical-align: middle; }
437 437 div.attachments span.author { font-size: 0.9em; color: #888; }
438 438
439 439 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
440 440 .other-formats span + span:before { content: "| "; }
441 441
442 442 a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
443 443
444 444 /* Project members tab */
445 445 div#tab-content-members .splitcontentleft, div#tab-content-memberships .splitcontentleft, div#tab-content-users .splitcontentleft { width: 64% }
446 446 div#tab-content-members .splitcontentright, div#tab-content-memberships .splitcontentright, div#tab-content-users .splitcontentright { width: 34% }
447 447 div#tab-content-members fieldset, div#tab-content-memberships fieldset, div#tab-content-users fieldset { padding:1em; margin-bottom: 1em; }
448 448 div#tab-content-members fieldset legend, div#tab-content-memberships fieldset legend, div#tab-content-users fieldset legend { font-weight: bold; }
449 449 div#tab-content-members fieldset label, div#tab-content-memberships fieldset label, div#tab-content-users fieldset label { display: block; }
450 450 div#tab-content-members fieldset div, div#tab-content-users fieldset div { max-height: 400px; overflow:auto; }
451 451
452 452 table.members td.group { padding-left: 20px; background: url(../images/group.png) no-repeat 0% 50%; }
453 453
454 454 input#principal_search, input#user_search {width:100%}
455 455
456 456 * html div#tab-content-members fieldset div { height: 450px; }
457 457
458 458 /***** Flash & error messages ****/
459 459 #errorExplanation, div.flash, .nodata, .warning {
460 460 padding: 4px 4px 4px 30px;
461 461 margin-bottom: 12px;
462 462 font-size: 1.1em;
463 463 border: 2px solid;
464 464 }
465 465
466 466 div.flash {margin-top: 8px;}
467 467
468 468 div.flash.error, #errorExplanation {
469 469 background: url(../images/exclamation.png) 8px 50% no-repeat;
470 470 background-color: #ffe3e3;
471 471 border-color: #dd0000;
472 472 color: #880000;
473 473 }
474 474
475 475 div.flash.notice {
476 476 background: url(../images/true.png) 8px 5px no-repeat;
477 477 background-color: #dfffdf;
478 478 border-color: #9fcf9f;
479 479 color: #005f00;
480 480 }
481 481
482 482 div.flash.warning {
483 483 background: url(../images/warning.png) 8px 5px no-repeat;
484 484 background-color: #FFEBC1;
485 485 border-color: #FDBF3B;
486 486 color: #A6750C;
487 487 text-align: left;
488 488 }
489 489
490 490 .nodata, .warning {
491 491 text-align: center;
492 492 background-color: #FFEBC1;
493 493 border-color: #FDBF3B;
494 494 color: #A6750C;
495 495 }
496 496
497 497 #errorExplanation ul { font-size: 0.9em;}
498 498 #errorExplanation h2, #errorExplanation p { display: none; }
499 499
500 500 /***** Ajax indicator ******/
501 501 #ajax-indicator {
502 502 position: absolute; /* fixed not supported by IE */
503 503 background-color:#eee;
504 504 border: 1px solid #bbb;
505 505 top:35%;
506 506 left:40%;
507 507 width:20%;
508 508 font-weight:bold;
509 509 text-align:center;
510 510 padding:0.6em;
511 511 z-index:100;
512 512 filter:alpha(opacity=50);
513 513 opacity: 0.5;
514 514 }
515 515
516 516 html>body #ajax-indicator { position: fixed; }
517 517
518 518 #ajax-indicator span {
519 519 background-position: 0% 40%;
520 520 background-repeat: no-repeat;
521 521 background-image: url(../images/loading.gif);
522 522 padding-left: 26px;
523 523 vertical-align: bottom;
524 524 }
525 525
526 526 /***** Calendar *****/
527 527 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
528 528 table.cal thead th {width: 14%; background-color:#EEEEEE; padding: 4px; }
529 529 table.cal thead th.week-number {width: auto;}
530 530 table.cal tbody tr {height: 100px;}
531 531 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
532 532 table.cal td.week-number { background-color:#EEEEEE; padding: 4px; border:none; font-size: 1em;}
533 533 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
534 534 table.cal td.odd p.day-num {color: #bbb;}
535 535 table.cal td.today {background:#ffffdd;}
536 536 table.cal td.today p.day-num {font-weight: bold;}
537 537 table.cal .starting a, p.cal.legend .starting {background: url(../images/bullet_go.png) no-repeat -1px -2px; padding-left:16px;}
538 538 table.cal .ending a, p.cal.legend .ending {background: url(../images/bullet_end.png) no-repeat -1px -2px; padding-left:16px;}
539 539 table.cal .starting.ending a, p.cal.legend .starting.ending {background: url(../images/bullet_diamond.png) no-repeat -1px -2px; padding-left:16px;}
540 540 p.cal.legend span {display:block;}
541 541
542 542 /***** Tooltips ******/
543 543 .tooltip{position:relative;z-index:24;}
544 544 .tooltip:hover{z-index:25;color:#000;}
545 545 .tooltip span.tip{display: none; text-align:left;}
546 546
547 547 div.tooltip:hover span.tip{
548 548 display:block;
549 549 position:absolute;
550 550 top:12px; left:24px; width:270px;
551 551 border:1px solid #555;
552 552 background-color:#fff;
553 553 padding: 4px;
554 554 font-size: 0.8em;
555 555 color:#505050;
556 556 }
557 557
558 558 /***** Progress bar *****/
559 559 table.progress {
560 560 border: 1px solid #D7D7D7;
561 561 border-collapse: collapse;
562 562 border-spacing: 0pt;
563 563 empty-cells: show;
564 564 text-align: center;
565 565 float:left;
566 566 margin: 1px 6px 1px 0px;
567 567 }
568 568
569 569 table.progress td { height: 0.9em; }
570 570 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
571 571 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
572 572 table.progress td.open { background: #FFF none repeat scroll 0%; }
573 573 p.pourcent {font-size: 80%;}
574 574 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
575 575
576 576 /***** Tabs *****/
577 577 #content .tabs {height: 2.6em; margin-bottom:1.2em; position:relative; overflow:hidden;}
578 578 #content .tabs ul {margin:0; position:absolute; bottom:0; padding-left:1em; width: 2000px; border-bottom: 1px solid #bbbbbb;}
579 579 #content .tabs ul li {
580 580 float:left;
581 581 list-style-type:none;
582 582 white-space:nowrap;
583 583 margin-right:8px;
584 584 background:#fff;
585 585 position:relative;
586 586 margin-bottom:-1px;
587 587 }
588 588 #content .tabs ul li a{
589 589 display:block;
590 590 font-size: 0.9em;
591 591 text-decoration:none;
592 592 line-height:1.3em;
593 593 padding:4px 6px 4px 6px;
594 594 border: 1px solid #ccc;
595 595 border-bottom: 1px solid #bbbbbb;
596 596 background-color: #eeeeee;
597 597 color:#777;
598 598 font-weight:bold;
599 599 }
600 600
601 601 #content .tabs ul li a:hover {
602 602 background-color: #ffffdd;
603 603 text-decoration:none;
604 604 }
605 605
606 606 #content .tabs ul li a.selected {
607 607 background-color: #fff;
608 608 border: 1px solid #bbbbbb;
609 609 border-bottom: 1px solid #fff;
610 610 }
611 611
612 612 #content .tabs ul li a.selected:hover {
613 613 background-color: #fff;
614 614 }
615 615
616 616 div.tabs-buttons { position:absolute; right: 0; width: 48px; height: 24px; background: white; bottom: 0; border-bottom: 1px solid #bbbbbb; }
617 617
618 618 button.tab-left, button.tab-right {
619 619 font-size: 0.9em;
620 620 cursor: pointer;
621 621 height:24px;
622 622 border: 1px solid #ccc;
623 623 border-bottom: 1px solid #bbbbbb;
624 624 position:absolute;
625 625 padding:4px;
626 626 width: 20px;
627 627 bottom: -1px;
628 628 }
629 629
630 630 button.tab-left {
631 631 right: 20px;
632 632 background: #eeeeee url(../images/bullet_arrow_left.png) no-repeat 50% 50%;
633 633 }
634 634
635 635 button.tab-right {
636 636 right: 0;
637 637 background: #eeeeee url(../images/bullet_arrow_right.png) no-repeat 50% 50%;
638 638 }
639 639
640 640 /***** Auto-complete *****/
641 641 div.autocomplete {
642 642 position:absolute;
643 643 width:400px;
644 644 margin:0;
645 645 padding:0;
646 646 }
647 647 div.autocomplete ul {
648 648 list-style-type:none;
649 649 margin:0;
650 650 padding:0;
651 651 }
652 652 div.autocomplete ul li {
653 653 list-style-type:none;
654 654 display:block;
655 655 margin:-1px 0 0 0;
656 656 padding:2px;
657 657 cursor:pointer;
658 658 font-size: 90%;
659 659 border: 1px solid #ccc;
660 660 border-left: 1px solid #ccc;
661 661 border-right: 1px solid #ccc;
662 662 background-color:white;
663 663 }
664 664 div.autocomplete ul li.selected { background-color: #ffb;}
665 665 div.autocomplete ul li span.informal {
666 666 font-size: 80%;
667 667 color: #aaa;
668 668 }
669 669
670 670 #parent_issue_candidates ul li {width: 500px;}
671 671 #related_issue_candidates ul li {width: 500px;}
672 672
673 673 /***** Diff *****/
674 674 .diff_out { background: #fcc; }
675 .diff_out span { background: #faa; }
675 676 .diff_in { background: #cfc; }
677 .diff_in span { background: #afa; }
676 678
677 679 .text-diff {
678 680 padding: 1em;
679 681 background-color:#f6f6f6;
680 682 color:#505050;
681 683 border: 1px solid #e4e4e4;
682 684 }
683 685
684 686 /***** Wiki *****/
685 687 div.wiki table {
686 688 border: 1px solid #505050;
687 689 border-collapse: collapse;
688 690 margin-bottom: 1em;
689 691 }
690 692
691 693 div.wiki table, div.wiki td, div.wiki th {
692 694 border: 1px solid #bbb;
693 695 padding: 4px;
694 696 }
695 697
696 698 div.wiki .external {
697 699 background-position: 0% 60%;
698 700 background-repeat: no-repeat;
699 701 padding-left: 12px;
700 702 background-image: url(../images/external.png);
701 703 }
702 704
703 705 div.wiki a.new {
704 706 color: #b73535;
705 707 }
706 708
707 709 div.wiki pre {
708 710 margin: 1em 1em 1em 1.6em;
709 711 padding: 2px 2px 2px 0;
710 712 background-color: #fafafa;
711 713 border: 1px solid #dadada;
712 714 width:auto;
713 715 overflow-x: auto;
714 716 overflow-y: hidden;
715 717 }
716 718
717 719 div.wiki ul.toc {
718 720 background-color: #ffffdd;
719 721 border: 1px solid #e4e4e4;
720 722 padding: 4px;
721 723 line-height: 1.2em;
722 724 margin-bottom: 12px;
723 725 margin-right: 12px;
724 726 margin-left: 0;
725 727 display: table
726 728 }
727 729 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
728 730
729 731 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
730 732 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
731 733 div.wiki ul.toc ul { margin: 0; padding: 0; }
732 734 div.wiki ul.toc li { list-style-type:none; margin: 0;}
733 735 div.wiki ul.toc li li { margin-left: 1.5em; }
734 736 div.wiki ul.toc li li li { font-size: 0.8em; }
735 737
736 738 div.wiki ul.toc a {
737 739 font-size: 0.9em;
738 740 font-weight: normal;
739 741 text-decoration: none;
740 742 color: #606060;
741 743 }
742 744 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
743 745
744 746 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
745 747 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
746 748 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
747 749
748 750 div.wiki img { vertical-align: middle; }
749 751
750 752 /***** My page layout *****/
751 753 .block-receiver {
752 754 border:1px dashed #c0c0c0;
753 755 margin-bottom: 20px;
754 756 padding: 15px 0 15px 0;
755 757 }
756 758
757 759 .mypage-box {
758 760 margin:0 0 20px 0;
759 761 color:#505050;
760 762 line-height:1.5em;
761 763 }
762 764
763 765 .handle {
764 766 cursor: move;
765 767 }
766 768
767 769 a.close-icon {
768 770 display:block;
769 771 margin-top:3px;
770 772 overflow:hidden;
771 773 width:12px;
772 774 height:12px;
773 775 background-repeat: no-repeat;
774 776 cursor:pointer;
775 777 background-image:url('../images/close.png');
776 778 }
777 779
778 780 a.close-icon:hover {
779 781 background-image:url('../images/close_hl.png');
780 782 }
781 783
782 784 /***** Gantt chart *****/
783 785 .gantt_hdr {
784 786 position:absolute;
785 787 top:0;
786 788 height:16px;
787 789 border-top: 1px solid #c0c0c0;
788 790 border-bottom: 1px solid #c0c0c0;
789 791 border-right: 1px solid #c0c0c0;
790 792 text-align: center;
791 793 overflow: hidden;
792 794 }
793 795
794 796 .gantt_subjects { font-size: 0.8em; }
795 797 .gantt_subjects div { line-height:16px;height:16px;overflow:hidden;white-space:nowrap;text-overflow: ellipsis; }
796 798
797 799 .task {
798 800 position: absolute;
799 801 height:8px;
800 802 font-size:0.8em;
801 803 color:#888;
802 804 padding:0;
803 805 margin:0;
804 806 line-height:16px;
805 807 white-space:nowrap;
806 808 }
807 809
808 810 .task.label {width:100%;}
809 811 .task.label.project, .task.label.version { font-weight: bold; }
810 812
811 813 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
812 814 .task_done { background:#00c600 url(../images/task_done.png); border: 1px solid #00c600; }
813 815 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
814 816
815 817 .task_todo.parent { background: #888; border: 1px solid #888; height: 3px;}
816 818 .task_late.parent, .task_done.parent { height: 3px;}
817 819 .task.parent.marker.starting { position: absolute; background: url(../images/task_parent_end.png) no-repeat 0 0; width: 8px; height: 16px; margin-left: -4px; left: 0px; top: -1px;}
818 820 .task.parent.marker.ending { position: absolute; background: url(../images/task_parent_end.png) no-repeat 0 0; width: 8px; height: 16px; margin-left: -4px; right: 0px; top: -1px;}
819 821
820 822 .version.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
821 823 .version.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
822 824 .version.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
823 825 .version.marker { background-image:url(../images/version_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
824 826
825 827 .project.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
826 828 .project.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
827 829 .project.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
828 830 .project.marker { background-image:url(../images/project_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
829 831
830 832 .version-behind-schedule a, .issue-behind-schedule a {color: #f66914;}
831 833 .version-overdue a, .issue-overdue a, .project-overdue a {color: #f00;}
832 834
833 835 /***** Icons *****/
834 836 .icon {
835 837 background-position: 0% 50%;
836 838 background-repeat: no-repeat;
837 839 padding-left: 20px;
838 840 padding-top: 2px;
839 841 padding-bottom: 3px;
840 842 }
841 843
842 844 .icon-add { background-image: url(../images/add.png); }
843 845 .icon-edit { background-image: url(../images/edit.png); }
844 846 .icon-copy { background-image: url(../images/copy.png); }
845 847 .icon-duplicate { background-image: url(../images/duplicate.png); }
846 848 .icon-del { background-image: url(../images/delete.png); }
847 849 .icon-move { background-image: url(../images/move.png); }
848 850 .icon-save { background-image: url(../images/save.png); }
849 851 .icon-cancel { background-image: url(../images/cancel.png); }
850 852 .icon-multiple { background-image: url(../images/table_multiple.png); }
851 853 .icon-folder { background-image: url(../images/folder.png); }
852 854 .open .icon-folder { background-image: url(../images/folder_open.png); }
853 855 .icon-package { background-image: url(../images/package.png); }
854 856 .icon-user { background-image: url(../images/user.png); }
855 857 .icon-projects { background-image: url(../images/projects.png); }
856 858 .icon-help { background-image: url(../images/help.png); }
857 859 .icon-attachment { background-image: url(../images/attachment.png); }
858 860 .icon-history { background-image: url(../images/history.png); }
859 861 .icon-time { background-image: url(../images/time.png); }
860 862 .icon-time-add { background-image: url(../images/time_add.png); }
861 863 .icon-stats { background-image: url(../images/stats.png); }
862 864 .icon-warning { background-image: url(../images/warning.png); }
863 865 .icon-fav { background-image: url(../images/fav.png); }
864 866 .icon-fav-off { background-image: url(../images/fav_off.png); }
865 867 .icon-reload { background-image: url(../images/reload.png); }
866 868 .icon-lock { background-image: url(../images/locked.png); }
867 869 .icon-unlock { background-image: url(../images/unlock.png); }
868 870 .icon-checked { background-image: url(../images/true.png); }
869 871 .icon-details { background-image: url(../images/zoom_in.png); }
870 872 .icon-report { background-image: url(../images/report.png); }
871 873 .icon-comment { background-image: url(../images/comment.png); }
872 874 .icon-summary { background-image: url(../images/lightning.png); }
873 875 .icon-server-authentication { background-image: url(../images/server_key.png); }
874 876 .icon-issue { background-image: url(../images/ticket.png); }
875 877 .icon-zoom-in { background-image: url(../images/zoom_in.png); }
876 878 .icon-zoom-out { background-image: url(../images/zoom_out.png); }
877 879
878 880 .icon-file { background-image: url(../images/files/default.png); }
879 881 .icon-file.text-plain { background-image: url(../images/files/text.png); }
880 882 .icon-file.text-x-c { background-image: url(../images/files/c.png); }
881 883 .icon-file.text-x-csharp { background-image: url(../images/files/csharp.png); }
882 884 .icon-file.text-x-php { background-image: url(../images/files/php.png); }
883 885 .icon-file.text-x-ruby { background-image: url(../images/files/ruby.png); }
884 886 .icon-file.text-xml { background-image: url(../images/files/xml.png); }
885 887 .icon-file.image-gif { background-image: url(../images/files/image.png); }
886 888 .icon-file.image-jpeg { background-image: url(../images/files/image.png); }
887 889 .icon-file.image-png { background-image: url(../images/files/image.png); }
888 890 .icon-file.image-tiff { background-image: url(../images/files/image.png); }
889 891 .icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
890 892 .icon-file.application-zip { background-image: url(../images/files/zip.png); }
891 893 .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); }
892 894
893 895 img.gravatar {
894 896 padding: 2px;
895 897 border: solid 1px #d5d5d5;
896 898 background: #fff;
897 899 }
898 900
899 901 div.issue img.gravatar {
900 902 float: right;
901 903 margin: 0 0 0 1em;
902 904 padding: 5px;
903 905 }
904 906
905 907 div.issue table img.gravatar {
906 908 height: 14px;
907 909 width: 14px;
908 910 padding: 2px;
909 911 float: left;
910 912 margin: 0 0.5em 0 0;
911 913 }
912 914
913 915 h2 img.gravatar {
914 916 padding: 3px;
915 917 margin: -2px 4px -4px 0;
916 918 vertical-align: top;
917 919 }
918 920
919 921 h4 img.gravatar {
920 922 padding: 3px;
921 923 margin: -6px 0 -4px 0;
922 924 vertical-align: top;
923 925 }
924 926
925 927 td.username img.gravatar {
926 928 margin: 0 0.5em 0 0;
927 929 vertical-align: top;
928 930 }
929 931
930 932 #activity dt img.gravatar {
931 933 float: left;
932 934 margin: 0 1em 1em 0;
933 935 }
934 936
935 937 /* Used on 12px Gravatar img tags without the icon background */
936 938 .icon-gravatar {
937 939 float: left;
938 940 margin-right: 4px;
939 941 }
940 942
941 943 #activity dt,
942 944 .journal {
943 945 clear: left;
944 946 }
945 947
946 948 .journal-link {
947 949 float: right;
948 950 }
949 951
950 952 h2 img { vertical-align:middle; }
951 953
952 954 .hascontextmenu { cursor: context-menu; }
953 955
954 956 /***** Media print specific styles *****/
955 957 @media print {
956 958 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
957 959 #main { background: #fff; }
958 960 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
959 961 #wiki_add_attachment { display:none; }
960 962 .hide-when-print { display: none; }
961 963 }
@@ -1,99 +1,156
1 1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../../test_helper', __FILE__)
19 19
20 20 class Redmine::UnifiedDiffTest < ActiveSupport::TestCase
21 21
22 22 def setup
23 23 end
24 24
25 25 def test_subversion_diff
26 26 diff = Redmine::UnifiedDiff.new(read_diff_fixture('subversion.diff'))
27 27 # number of files
28 28 assert_equal 4, diff.size
29 29 assert diff.detect {|file| file.file_name =~ %r{^config/settings.yml}}
30 30 end
31 31
32 32 def test_truncate_diff
33 33 diff = Redmine::UnifiedDiff.new(read_diff_fixture('subversion.diff'), :max_lines => 20)
34 34 assert_equal 2, diff.size
35 35 end
36 36
37 def test_inline_partials
38 diff = Redmine::UnifiedDiff.new(read_diff_fixture('partials.diff'))
39 assert_equal 1, diff.size
40 diff = diff.first
41 assert_equal 43, diff.size
42
43 assert_equal [51, -1], diff[0].offsets
44 assert_equal [51, -1], diff[1].offsets
45 assert_equal 'Lorem ipsum dolor sit amet, consectetur adipiscing <span>elit</span>', diff[0].html_line
46 assert_equal 'Lorem ipsum dolor sit amet, consectetur adipiscing <span>xx</span>', diff[1].html_line
47
48 assert_nil diff[2].offsets
49 assert_equal 'Praesent et sagittis dui. Vivamus ac diam diam', diff[2].html_line
50
51 assert_equal [0, -14], diff[3].offsets
52 assert_equal [0, -14], diff[4].offsets
53 assert_equal '<span>Ut sed</span> auctor justo', diff[3].html_line
54 assert_equal '<span>xxx</span> auctor justo', diff[4].html_line
55
56 assert_equal [13, -19], diff[6].offsets
57 assert_equal [13, -19], diff[7].offsets
58
59 assert_equal [24, -8], diff[9].offsets
60 assert_equal [24, -8], diff[10].offsets
61
62 assert_equal [37, -1], diff[12].offsets
63 assert_equal [37, -1], diff[13].offsets
64
65 assert_equal [0, -38], diff[15].offsets
66 assert_equal [0, -38], diff[16].offsets
67 end
68
69 def test_side_by_side_partials
70 diff = Redmine::UnifiedDiff.new(read_diff_fixture('partials.diff'), :type => 'sbs')
71 assert_equal 1, diff.size
72 diff = diff.first
73 assert_equal 32, diff.size
74
75 assert_equal [51, -1], diff[0].offsets
76 assert_equal 'Lorem ipsum dolor sit amet, consectetur adipiscing <span>elit</span>', diff[0].html_line_left
77 assert_equal 'Lorem ipsum dolor sit amet, consectetur adipiscing <span>xx</span>', diff[0].html_line_right
78
79 assert_nil diff[1].offsets
80 assert_equal 'Praesent et sagittis dui. Vivamus ac diam diam', diff[1].html_line_left
81 assert_equal 'Praesent et sagittis dui. Vivamus ac diam diam', diff[1].html_line_right
82
83 assert_equal [0, -14], diff[2].offsets
84 assert_equal '<span>Ut sed</span> auctor justo', diff[2].html_line_left
85 assert_equal '<span>xxx</span> auctor justo', diff[2].html_line_right
86
87 assert_equal [13, -19], diff[4].offsets
88 assert_equal [24, -8], diff[6].offsets
89 assert_equal [37, -1], diff[8].offsets
90 assert_equal [0, -38], diff[10].offsets
91
92 end
93
37 94 def test_line_starting_with_dashes
38 95 diff = Redmine::UnifiedDiff.new(<<-DIFF
39 96 --- old.txt Wed Nov 11 14:24:58 2009
40 97 +++ new.txt Wed Nov 11 14:25:02 2009
41 98 @@ -1,8 +1,4 @@
42 99 -Lines that starts with dashes:
43 100 -
44 101 -------------------------
45 102 --- file.c
46 103 -------------------------
47 104 +A line that starts with dashes:
48 105
49 106 and removed.
50 107
51 108 @@ -23,4 +19,4 @@
52 109
53 110
54 111
55 112 -Another chunk of change
56 113 +Another chunk of changes
57 114
58 115 DIFF
59 116 )
60 117 assert_equal 1, diff.size
61 118 end
62 119
63 120 def test_one_line_new_files
64 121 diff = Redmine::UnifiedDiff.new(<<-DIFF
65 122 diff -r 000000000000 -r ea98b14f75f0 README1
66 123 --- /dev/null
67 124 +++ b/README1
68 125 @@ -0,0 +1,1 @@
69 126 +test1
70 127 diff -r 000000000000 -r ea98b14f75f0 README2
71 128 --- /dev/null
72 129 +++ b/README2
73 130 @@ -0,0 +1,1 @@
74 131 +test2
75 132 diff -r 000000000000 -r ea98b14f75f0 README3
76 133 --- /dev/null
77 134 +++ b/README3
78 135 @@ -0,0 +1,3 @@
79 136 +test4
80 137 +test5
81 138 +test6
82 139 diff -r 000000000000 -r ea98b14f75f0 README4
83 140 --- /dev/null
84 141 +++ b/README4
85 142 @@ -0,0 +1,3 @@
86 143 +test4
87 144 +test5
88 145 +test6
89 146 DIFF
90 147 )
91 148 assert_equal 4, diff.size
92 149 end
93 150
94 151 private
95 152
96 153 def read_diff_fixture(filename)
97 154 File.new(File.join(File.dirname(__FILE__), '/../../../fixtures/diffs', filename)).read
98 155 end
99 156 end
General Comments 0
You need to be logged in to leave comments. Login now