##// END OF EJS Templates
Adds day numbers to HTML and PDF gantt (#3034)....
Jean-Philippe Lang -
r14256:f9425cbaf37e
parent child
Show More
@@ -1,334 +1,372
1 1 <% @gantt.view = self %>
2 2 <div class="contextual">
3 3 <% if !@query.new_record? && @query.editable_by?(User.current) %>
4 4 <%= link_to l(:button_edit), edit_query_path(@query, :gantt => 1), :class => 'icon icon-edit' %>
5 5 <%= delete_link query_path(@query, :gantt => 1) %>
6 6 <% end %>
7 7 </div>
8 8
9 9 <h2><%= @query.new_record? ? l(:label_gantt) : @query.name %></h2>
10 10
11 11 <%= form_tag({:controller => 'gantts', :action => 'show',
12 12 :project_id => @project, :month => params[:month],
13 13 :year => params[:year], :months => params[:months]},
14 14 :method => :get, :id => 'query_form') do %>
15 15 <%= hidden_field_tag 'set_filter', '1' %>
16 16 <%= hidden_field_tag 'gantt', '1' %>
17 17 <fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
18 18 <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
19 19 <div style="<%= @query.new_record? ? "" : "display: none;" %>">
20 20 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
21 21 </div>
22 22 </fieldset>
23 23 <fieldset class="collapsible collapsed">
24 24 <legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
25 25 <div style="display: none;">
26 26 <table>
27 27 <tr>
28 28 <td>
29 29 <fieldset>
30 30 <legend><%= l(:label_related_issues) %></legend>
31 31 <label for="draw_relations">
32 32 <%= check_box 'query', 'draw_relations', :id => 'draw_relations' %>
33 33 <% rels = [IssueRelation::TYPE_BLOCKS, IssueRelation::TYPE_PRECEDES] %>
34 34 <% rels.each do |rel| %>
35 35 <% color = Redmine::Helpers::Gantt::DRAW_TYPES[rel][:color] %>
36 36 <%= content_tag(:span, '&nbsp;&nbsp;&nbsp;'.html_safe,
37 37 :style => "background-color: #{color}") %>
38 38 <%= l(IssueRelation::TYPES[rel][:name]) %>
39 39 <% end %>
40 40 </label>
41 41 </fieldset>
42 42 </td>
43 43 <td>
44 44 <fieldset>
45 45 <legend><%= l(:label_gantt_progress_line) %></legend>
46 46 <label for="draw_progress_line">
47 47 <%= check_box 'query', 'draw_progress_line', :id => 'draw_progress_line' %>
48 48 <%= l(:label_display) %>
49 49 </label>
50 50 </fieldset>
51 51 </td>
52 52 </tr>
53 53 </table>
54 54 </div>
55 55 </fieldset>
56 56
57 57 <p class="contextual">
58 58 <%= gantt_zoom_link(@gantt, :in) %>
59 59 <%= gantt_zoom_link(@gantt, :out) %>
60 60 </p>
61 61
62 62 <p class="buttons">
63 63 <%= text_field_tag 'months', @gantt.months, :size => 2 %>
64 64 <%= l(:label_months_from) %>
65 65 <%= select_month(@gantt.month_from, :prefix => "month", :discard_type => true) %>
66 66 <%= select_year(@gantt.year_from, :prefix => "year", :discard_type => true) %>
67 67 <%= hidden_field_tag 'zoom', @gantt.zoom %>
68 68
69 69 <%= link_to_function l(:button_apply), '$("#query_form").submit()',
70 70 :class => 'icon icon-checked' %>
71 71 <%= link_to l(:button_clear), { :project_id => @project, :set_filter => 1 },
72 72 :class => 'icon icon-reload' %>
73 73 <% if @query.new_record? && User.current.allowed_to?(:save_queries, @project, :global => true) %>
74 74 <%= link_to_function l(:button_save),
75 75 "$('#query_form').attr('action', '#{ @project ? new_project_query_path(@project) : new_query_path }').submit();",
76 76 :class => 'icon icon-save' %>
77 77 <% end %>
78 78 </p>
79 79 <% end %>
80 80
81 81 <%= error_messages_for 'query' %>
82 82 <% if @query.valid? %>
83 83 <%
84 84 zoom = 1
85 85 @gantt.zoom.times { zoom = zoom * 2 }
86 86
87 87 subject_width = 330
88 88 header_height = 18
89 89
90 90 headers_height = header_height
91 91 show_weeks = false
92 92 show_days = false
93 show_day_num = false
93 94
94 95 if @gantt.zoom > 1
95 96 show_weeks = true
96 97 headers_height = 2 * header_height
97 98 if @gantt.zoom > 2
98 99 show_days = true
99 100 headers_height = 3 * header_height
101 if @gantt.zoom > 3
102 show_day_num = true
103 headers_height = 4 * header_height
104 end
100 105 end
101 106 end
102 107
103 108 # Width of the entire chart
104 109 g_width = ((@gantt.date_to - @gantt.date_from + 1) * zoom).to_i
105 110 @gantt.render(:top => headers_height + 8,
106 111 :zoom => zoom,
107 112 :g_width => g_width,
108 113 :subject_width => subject_width)
109 114 g_height = [(20 * (@gantt.number_of_rows + 6)) + 150, 206].max
110 115 t_height = g_height + headers_height
111 116 %>
112 117
113 118 <% if @gantt.truncated %>
114 119 <p class="warning"><%= l(:notice_gantt_chart_truncated, :max => @gantt.max_rows) %></p>
115 120 <% end %>
116 121
117 122 <table style="width:100%; border:0; border-collapse: collapse;">
118 123 <tr>
119 124 <td style="width:<%= subject_width %>px; padding:0px;">
120 125 <%
121 126 style = ""
122 127 style += "position:relative;"
123 128 style += "height: #{t_height + 24}px;"
124 129 style += "width: #{subject_width + 1}px;"
125 130 %>
126 131 <%= content_tag(:div, :style => style) do %>
127 132 <%
128 133 style = ""
129 134 style += "right:-2px;"
130 135 style += "width: #{subject_width}px;"
131 136 style += "height: #{headers_height}px;"
132 137 style += 'background: #eee;'
133 138 %>
134 139 <%= content_tag(:div, "", :style => style, :class => "gantt_hdr") %>
135 140 <%
136 141 style = ""
137 142 style += "right:-2px;"
138 143 style += "width: #{subject_width}px;"
139 144 style += "height: #{t_height}px;"
140 145 style += 'border-left: 1px solid #c0c0c0;'
141 146 style += 'overflow: hidden;'
142 147 %>
143 148 <%= content_tag(:div, "", :style => style, :class => "gantt_hdr") %>
144 149 <%= content_tag(:div, :class => "gantt_subjects") do %>
145 150 <%= @gantt.subjects.html_safe %>
146 151 <% end %>
147 152 <% end %>
148 153 </td>
149 154
150 155 <td>
151 156 <div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;" id="gantt_area">
152 157 <%
153 158 style = ""
154 159 style += "width: #{g_width - 1}px;"
155 160 style += "height: #{headers_height}px;"
156 161 style += 'background: #eee;'
157 162 %>
158 163 <%= content_tag(:div, '&nbsp;'.html_safe, :style => style, :class => "gantt_hdr") %>
159 164
160 165 <% ###### Months headers ###### %>
161 166 <%
162 167 month_f = @gantt.date_from
163 168 left = 0
164 169 height = (show_weeks ? header_height : header_height + g_height)
165 170 %>
166 171 <% @gantt.months.times do %>
167 172 <%
168 173 width = (((month_f >> 1) - month_f) * zoom - 1).to_i
169 174 style = ""
170 175 style += "left: #{left}px;"
171 176 style += "width: #{width}px;"
172 177 style += "height: #{height}px;"
173 178 %>
174 179 <%= content_tag(:div, :style => style, :class => "gantt_hdr") do %>
175 180 <%= link_to "#{month_f.year}-#{month_f.month}",
176 181 @gantt.params.merge(:year => month_f.year, :month => month_f.month),
177 182 :title => "#{month_name(month_f.month)} #{month_f.year}" %>
178 183 <% end %>
179 184 <%
180 185 left = left + width + 1
181 186 month_f = month_f >> 1
182 187 %>
183 188 <% end %>
184 189
185 190 <% ###### Weeks headers ###### %>
186 191 <% if show_weeks %>
187 192 <%
188 193 left = 0
189 194 height = (show_days ? header_height - 1 : header_height - 1 + g_height)
190 195 %>
191 196 <% if @gantt.date_from.cwday == 1 %>
192 197 <%
193 198 # @date_from is monday
194 199 week_f = @gantt.date_from
195 200 %>
196 201 <% else %>
197 202 <%
198 203 # find next monday after @date_from
199 204 week_f = @gantt.date_from + (7 - @gantt.date_from.cwday + 1)
200 205 width = (7 - @gantt.date_from.cwday + 1) * zoom - 1
201 206 style = ""
202 207 style += "left: #{left}px;"
203 208 style += "top: 19px;"
204 209 style += "width: #{width}px;"
205 210 style += "height: #{height}px;"
206 211 %>
207 212 <%= content_tag(:div, '&nbsp;'.html_safe,
208 213 :style => style, :class => "gantt_hdr") %>
209 214 <% left = left + width + 1 %>
210 215 <% end %>
211 216 <% while week_f <= @gantt.date_to %>
212 217 <%
213 218 width = ((week_f + 6 <= @gantt.date_to) ?
214 219 7 * zoom - 1 :
215 220 (@gantt.date_to - week_f + 1) * zoom - 1).to_i
216 221 style = ""
217 222 style += "left: #{left}px;"
218 223 style += "top: 19px;"
219 224 style += "width: #{width}px;"
220 225 style += "height: #{height}px;"
221 226 %>
222 227 <%= content_tag(:div, :style => style, :class => "gantt_hdr") do %>
223 228 <%= content_tag(:small) do %>
224 229 <%= week_f.cweek if width >= 16 %>
225 230 <% end %>
226 231 <% end %>
227 232 <%
228 233 left = left + width + 1
229 234 week_f = week_f + 7
230 235 %>
231 236 <% end %>
232 237 <% end %>
233 238
239 <% ###### Day numbers headers ###### %>
240 <% if show_day_num %>
241 <%
242 left = 0
243 height = g_height + header_height*2 - 1
244 wday = @gantt.date_from.cwday
245 day_num = @gantt.date_from
246 %>
247 <% (@gantt.date_to - @gantt.date_from + 1).to_i.times do %>
248 <%
249 width = zoom - 1
250 style = ""
251 style += "left:#{left}px;"
252 style += "top:37px;"
253 style += "width:#{width}px;"
254 style += "height:#{height}px;"
255 style += "font-size:0.7em;"
256 clss = "gantt_hdr"
257 clss << " nwday" if @gantt.non_working_week_days.include?(wday)
258 %>
259 <%= content_tag(:div, :style => style, :class => clss) do %>
260 <%= day_num.day %>
261 <% end %>
262 <%
263 left = left + width+1
264 day_num = day_num + 1
265 wday = wday + 1
266 wday = 1 if wday > 7
267 %>
268 <% end %>
269 <% end %>
270
234 271 <% ###### Days headers ####### %>
235 272 <% if show_days %>
236 273 <%
237 274 left = 0
238 275 height = g_height + header_height - 1
276 top = (show_day_num ? 55 : 37)
239 277 wday = @gantt.date_from.cwday
240 278 %>
241 279 <% (@gantt.date_to - @gantt.date_from + 1).to_i.times do %>
242 280 <%
243 281 width = zoom - 1
244 282 style = ""
245 283 style += "left: #{left}px;"
246 style += "top:37px;"
284 style += "top: #{top}px;"
247 285 style += "width: #{width}px;"
248 286 style += "height: #{height}px;"
249 287 style += "font-size:0.7em;"
250 288 clss = "gantt_hdr"
251 289 clss << " nwday" if @gantt.non_working_week_days.include?(wday)
252 290 %>
253 291 <%= content_tag(:div, :style => style, :class => clss) do %>
254 292 <%= day_letter(wday) %>
255 293 <% end %>
256 294 <%
257 295 left = left + width + 1
258 296 wday = wday + 1
259 297 wday = 1 if wday > 7
260 298 %>
261 299 <% end %>
262 300 <% end %>
263 301
264 302 <%= @gantt.lines.html_safe %>
265 303
266 304 <% ###### Today red line (excluded from cache) ###### %>
267 305 <% if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
268 306 <%
269 307 today_left = (((Date.today - @gantt.date_from + 1) * zoom).floor() - 1).to_i
270 308 style = ""
271 309 style += "position: absolute;"
272 310 style += "height: #{g_height}px;"
273 311 style += "top: #{headers_height + 1}px;"
274 312 style += "left: #{today_left}px;"
275 313 style += "width:10px;"
276 314 style += "border-left: 1px dashed red;"
277 315 %>
278 316 <%= content_tag(:div, '&nbsp;'.html_safe, :style => style, :id => 'today_line') %>
279 317 <% end %>
280 318 <%
281 319 style = ""
282 320 style += "position: absolute;"
283 321 style += "height: #{g_height}px;"
284 322 style += "top: #{headers_height + 1}px;"
285 323 style += "left: 0px;"
286 324 style += "width: #{g_width - 1}px;"
287 325 %>
288 326 <%= content_tag(:div, '', :style => style, :id => "gantt_draw_area") %>
289 327 </div>
290 328 </td>
291 329 </tr>
292 330 </table>
293 331
294 332 <table style="width:100%">
295 333 <tr>
296 334 <td style="text-align:left;">
297 335 <%= link_to_content_update("\xc2\xab " + l(:label_previous),
298 336 params.merge(@gantt.params_previous),
299 337 :accesskey => accesskey(:previous)) %>
300 338 </td>
301 339 <td style="text-align:right;">
302 340 <%= link_to_content_update(l(:label_next) + " \xc2\xbb",
303 341 params.merge(@gantt.params_next),
304 342 :accesskey => accesskey(:next)) %>
305 343 </td>
306 344 </tr>
307 345 </table>
308 346
309 347 <% other_formats_links do |f| %>
310 348 <%= f.link_to 'PDF', :url => params.merge(@gantt.params) %>
311 349 <%= f.link_to('PNG', :url => params.merge(@gantt.params)) if @gantt.respond_to?('to_image') %>
312 350 <% end %>
313 351 <% end # query.valid? %>
314 352
315 353 <% content_for :sidebar do %>
316 354 <%= render :partial => 'issues/sidebar' %>
317 355 <% end %>
318 356
319 357 <% html_title(l(:label_gantt)) -%>
320 358
321 359 <% content_for :header_tags do %>
322 360 <%= javascript_include_tag 'raphael' %>
323 361 <%= javascript_include_tag 'gantt' %>
324 362 <% end %>
325 363
326 364 <%= javascript_tag do %>
327 365 var issue_relation_type = <%= raw Redmine::Helpers::Gantt::DRAW_TYPES.to_json %>;
328 366 $(document).ready(drawGanttHandler);
329 367 $(window).resize(drawGanttHandler);
330 368 $(function() {
331 369 $("#draw_relations").change(drawGanttHandler);
332 370 $("#draw_progress_line").change(drawGanttHandler);
333 371 });
334 372 <% end %>
@@ -1,929 +1,954
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 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 module Helpers
20 20 # Simple class to handle gantt chart data
21 21 class Gantt
22 22 class MaxLinesLimitReached < Exception
23 23 end
24 24
25 25 include ERB::Util
26 26 include Redmine::I18n
27 27 include Redmine::Utils::DateCalculation
28 28
29 29 # Relation types that are rendered
30 30 DRAW_TYPES = {
31 31 IssueRelation::TYPE_BLOCKS => { :landscape_margin => 16, :color => '#F34F4F' },
32 32 IssueRelation::TYPE_PRECEDES => { :landscape_margin => 20, :color => '#628FEA' }
33 33 }.freeze
34 34
35 35 # :nodoc:
36 36 # Some utility methods for the PDF export
37 37 class PDF
38 38 MaxCharactorsForSubject = 45
39 39 TotalWidth = 280
40 40 LeftPaneWidth = 100
41 41
42 42 def self.right_pane_width
43 43 TotalWidth - LeftPaneWidth
44 44 end
45 45 end
46 46
47 47 attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months, :truncated, :max_rows
48 48 attr_accessor :query
49 49 attr_accessor :project
50 50 attr_accessor :view
51 51
52 52 def initialize(options={})
53 53 options = options.dup
54 54 if options[:year] && options[:year].to_i >0
55 55 @year_from = options[:year].to_i
56 56 if options[:month] && options[:month].to_i >=1 && options[:month].to_i <= 12
57 57 @month_from = options[:month].to_i
58 58 else
59 59 @month_from = 1
60 60 end
61 61 else
62 62 @month_from ||= Date.today.month
63 63 @year_from ||= Date.today.year
64 64 end
65 65 zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i
66 66 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2
67 67 months = (options[:months] || User.current.pref[:gantt_months]).to_i
68 68 @months = (months > 0 && months < 25) ? months : 6
69 69 # Save gantt parameters as user preference (zoom and months count)
70 70 if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] ||
71 71 @months != User.current.pref[:gantt_months]))
72 72 User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
73 73 User.current.preference.save
74 74 end
75 75 @date_from = Date.civil(@year_from, @month_from, 1)
76 76 @date_to = (@date_from >> @months) - 1
77 77 @subjects = ''
78 78 @lines = ''
79 79 @number_of_rows = nil
80 80 @truncated = false
81 81 if options.has_key?(:max_rows)
82 82 @max_rows = options[:max_rows]
83 83 else
84 84 @max_rows = Setting.gantt_items_limit.blank? ? nil : Setting.gantt_items_limit.to_i
85 85 end
86 86 end
87 87
88 88 def common_params
89 89 { :controller => 'gantts', :action => 'show', :project_id => @project }
90 90 end
91 91
92 92 def params
93 93 common_params.merge({:zoom => zoom, :year => year_from,
94 94 :month => month_from, :months => months})
95 95 end
96 96
97 97 def params_previous
98 98 common_params.merge({:year => (date_from << months).year,
99 99 :month => (date_from << months).month,
100 100 :zoom => zoom, :months => months})
101 101 end
102 102
103 103 def params_next
104 104 common_params.merge({:year => (date_from >> months).year,
105 105 :month => (date_from >> months).month,
106 106 :zoom => zoom, :months => months})
107 107 end
108 108
109 109 # Returns the number of rows that will be rendered on the Gantt chart
110 110 def number_of_rows
111 111 return @number_of_rows if @number_of_rows
112 112 rows = projects.inject(0) {|total, p| total += number_of_rows_on_project(p)}
113 113 rows > @max_rows ? @max_rows : rows
114 114 end
115 115
116 116 # Returns the number of rows that will be used to list a project on
117 117 # the Gantt chart. This will recurse for each subproject.
118 118 def number_of_rows_on_project(project)
119 119 return 0 unless projects.include?(project)
120 120 count = 1
121 121 count += project_issues(project).size
122 122 count += project_versions(project).size
123 123 count
124 124 end
125 125
126 126 # Renders the subjects of the Gantt chart, the left side.
127 127 def subjects(options={})
128 128 render(options.merge(:only => :subjects)) unless @subjects_rendered
129 129 @subjects
130 130 end
131 131
132 132 # Renders the lines of the Gantt chart, the right side
133 133 def lines(options={})
134 134 render(options.merge(:only => :lines)) unless @lines_rendered
135 135 @lines
136 136 end
137 137
138 138 # Returns issues that will be rendered
139 139 def issues
140 140 @issues ||= @query.issues(
141 141 :include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
142 142 :order => "#{Project.table_name}.lft ASC, #{Issue.table_name}.id ASC",
143 143 :limit => @max_rows
144 144 )
145 145 end
146 146
147 147 # Returns a hash of the relations between the issues that are present on the gantt
148 148 # and that should be displayed, grouped by issue ids.
149 149 def relations
150 150 return @relations if @relations
151 151 if issues.any?
152 152 issue_ids = issues.map(&:id)
153 153 @relations = IssueRelation.
154 154 where(:issue_from_id => issue_ids, :issue_to_id => issue_ids, :relation_type => DRAW_TYPES.keys).
155 155 group_by(&:issue_from_id)
156 156 else
157 157 @relations = {}
158 158 end
159 159 end
160 160
161 161 # Return all the project nodes that will be displayed
162 162 def projects
163 163 return @projects if @projects
164 164 ids = issues.collect(&:project).uniq.collect(&:id)
165 165 if ids.any?
166 166 # All issues projects and their visible ancestors
167 167 @projects = Project.visible.
168 168 joins("LEFT JOIN #{Project.table_name} child ON #{Project.table_name}.lft <= child.lft AND #{Project.table_name}.rgt >= child.rgt").
169 169 where("child.id IN (?)", ids).
170 170 order("#{Project.table_name}.lft ASC").
171 171 uniq.
172 172 to_a
173 173 else
174 174 @projects = []
175 175 end
176 176 end
177 177
178 178 # Returns the issues that belong to +project+
179 179 def project_issues(project)
180 180 @issues_by_project ||= issues.group_by(&:project)
181 181 @issues_by_project[project] || []
182 182 end
183 183
184 184 # Returns the distinct versions of the issues that belong to +project+
185 185 def project_versions(project)
186 186 project_issues(project).collect(&:fixed_version).compact.uniq
187 187 end
188 188
189 189 # Returns the issues that belong to +project+ and are assigned to +version+
190 190 def version_issues(project, version)
191 191 project_issues(project).select {|issue| issue.fixed_version == version}
192 192 end
193 193
194 194 def render(options={})
195 195 options = {:top => 0, :top_increment => 20,
196 196 :indent_increment => 20, :render => :subject,
197 197 :format => :html}.merge(options)
198 198 indent = options[:indent] || 4
199 199 @subjects = '' unless options[:only] == :lines
200 200 @lines = '' unless options[:only] == :subjects
201 201 @number_of_rows = 0
202 202 begin
203 203 Project.project_tree(projects) do |project, level|
204 204 options[:indent] = indent + level * options[:indent_increment]
205 205 render_project(project, options)
206 206 end
207 207 rescue MaxLinesLimitReached
208 208 @truncated = true
209 209 end
210 210 @subjects_rendered = true unless options[:only] == :lines
211 211 @lines_rendered = true unless options[:only] == :subjects
212 212 render_end(options)
213 213 end
214 214
215 215 def render_project(project, options={})
216 216 render_object_row(project, options)
217 217 increment_indent(options) do
218 218 # render issue that are not assigned to a version
219 219 issues = project_issues(project).select {|i| i.fixed_version.nil?}
220 220 render_issues(issues, options)
221 221 # then render project versions and their issues
222 222 versions = project_versions(project)
223 223 self.class.sort_versions!(versions)
224 224 versions.each do |version|
225 225 render_version(project, version, options)
226 226 end
227 227 end
228 228 end
229 229
230 230 def render_version(project, version, options={})
231 231 render_object_row(version, options)
232 232 increment_indent(options) do
233 233 issues = version_issues(project, version)
234 234 render_issues(issues, options)
235 235 end
236 236 end
237 237
238 238 def render_issues(issues, options={})
239 239 self.class.sort_issues!(issues)
240 240 ancestors = []
241 241 issues.each do |issue|
242 242 while ancestors.any? && !issue.is_descendant_of?(ancestors.last)
243 243 ancestors.pop
244 244 decrement_indent(options)
245 245 end
246 246 render_object_row(issue, options)
247 247 unless issue.leaf?
248 248 ancestors << issue
249 249 increment_indent(options)
250 250 end
251 251 end
252 252 decrement_indent(options, ancestors.size)
253 253 end
254 254
255 255 def render_object_row(object, options)
256 256 class_name = object.class.name.downcase
257 257 send("subject_for_#{class_name}", object, options) unless options[:only] == :lines
258 258 send("line_for_#{class_name}", object, options) unless options[:only] == :subjects
259 259 options[:top] += options[:top_increment]
260 260 @number_of_rows += 1
261 261 if @max_rows && @number_of_rows >= @max_rows
262 262 raise MaxLinesLimitReached
263 263 end
264 264 end
265 265
266 266 def render_end(options={})
267 267 case options[:format]
268 268 when :pdf
269 269 options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
270 270 end
271 271 end
272 272
273 273 def increment_indent(options, factor=1)
274 274 options[:indent] += options[:indent_increment] * factor
275 275 if block_given?
276 276 yield
277 277 decrement_indent(options, factor)
278 278 end
279 279 end
280 280
281 281 def decrement_indent(options, factor=1)
282 282 increment_indent(options, -factor)
283 283 end
284 284
285 285 def subject_for_project(project, options)
286 286 subject(project.name, options, project)
287 287 end
288 288
289 289 def line_for_project(project, options)
290 290 # Skip projects that don't have a start_date or due date
291 291 if project.is_a?(Project) && project.start_date && project.due_date
292 292 label = project.name
293 293 line(project.start_date, project.due_date, nil, true, label, options, project)
294 294 end
295 295 end
296 296
297 297 def subject_for_version(version, options)
298 298 subject(version.to_s_with_project, options, version)
299 299 end
300 300
301 301 def line_for_version(version, options)
302 302 # Skip versions that don't have a start_date
303 303 if version.is_a?(Version) && version.due_date && version.start_date
304 304 label = "#{h(version)} #{h(version.completed_percent.to_f.round)}%"
305 305 label = h("#{version.project} -") + label unless @project && @project == version.project
306 306 line(version.start_date, version.due_date, version.completed_percent, true, label, options, version)
307 307 end
308 308 end
309 309
310 310 def subject_for_issue(issue, options)
311 311 subject(issue.subject, options, issue)
312 312 end
313 313
314 314 def line_for_issue(issue, options)
315 315 # Skip issues that don't have a due_before (due_date or version's due_date)
316 316 if issue.is_a?(Issue) && issue.due_before
317 317 label = "#{issue.status.name} #{issue.done_ratio}%"
318 318 markers = !issue.leaf?
319 319 line(issue.start_date, issue.due_before, issue.done_ratio, markers, label, options, issue)
320 320 end
321 321 end
322 322
323 323 def subject(label, options, object=nil)
324 324 send "#{options[:format]}_subject", options, label, object
325 325 end
326 326
327 327 def line(start_date, end_date, done_ratio, markers, label, options, object=nil)
328 328 options[:zoom] ||= 1
329 329 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
330 330 coords = coordinates(start_date, end_date, done_ratio, options[:zoom])
331 331 send "#{options[:format]}_task", options, coords, markers, label, object
332 332 end
333 333
334 334 # Generates a gantt image
335 335 # Only defined if RMagick is avalaible
336 336 def to_image(format='PNG')
337 337 date_to = (@date_from >> @months) - 1
338 338 show_weeks = @zoom > 1
339 339 show_days = @zoom > 2
340 340 subject_width = 400
341 341 header_height = 18
342 342 # width of one day in pixels
343 343 zoom = @zoom * 2
344 344 g_width = (@date_to - @date_from + 1) * zoom
345 345 g_height = 20 * number_of_rows + 30
346 346 headers_height = (show_weeks ? 2 * header_height : header_height)
347 347 height = g_height + headers_height
348 348 imgl = Magick::ImageList.new
349 349 imgl.new_image(subject_width + g_width + 1, height)
350 350 gc = Magick::Draw.new
351 351 gc.font = Redmine::Configuration['rmagick_font_path'] || ""
352 352 # Subjects
353 353 gc.stroke('transparent')
354 354 subjects(:image => gc, :top => (headers_height + 20), :indent => 4, :format => :image)
355 355 # Months headers
356 356 month_f = @date_from
357 357 left = subject_width
358 358 @months.times do
359 359 width = ((month_f >> 1) - month_f) * zoom
360 360 gc.fill('white')
361 361 gc.stroke('grey')
362 362 gc.stroke_width(1)
363 363 gc.rectangle(left, 0, left + width, height)
364 364 gc.fill('black')
365 365 gc.stroke('transparent')
366 366 gc.stroke_width(1)
367 367 gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}")
368 368 left = left + width
369 369 month_f = month_f >> 1
370 370 end
371 371 # Weeks headers
372 372 if show_weeks
373 373 left = subject_width
374 374 height = header_height
375 375 if @date_from.cwday == 1
376 376 # date_from is monday
377 377 week_f = date_from
378 378 else
379 379 # find next monday after date_from
380 380 week_f = @date_from + (7 - @date_from.cwday + 1)
381 381 width = (7 - @date_from.cwday + 1) * zoom
382 382 gc.fill('white')
383 383 gc.stroke('grey')
384 384 gc.stroke_width(1)
385 385 gc.rectangle(left, header_height, left + width, 2 * header_height + g_height - 1)
386 386 left = left + width
387 387 end
388 388 while week_f <= date_to
389 389 width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom
390 390 gc.fill('white')
391 391 gc.stroke('grey')
392 392 gc.stroke_width(1)
393 393 gc.rectangle(left.round, header_height, left.round + width, 2 * header_height + g_height - 1)
394 394 gc.fill('black')
395 395 gc.stroke('transparent')
396 396 gc.stroke_width(1)
397 397 gc.text(left.round + 2, header_height + 14, week_f.cweek.to_s)
398 398 left = left + width
399 399 week_f = week_f + 7
400 400 end
401 401 end
402 402 # Days details (week-end in grey)
403 403 if show_days
404 404 left = subject_width
405 405 height = g_height + header_height - 1
406 406 wday = @date_from.cwday
407 407 (date_to - @date_from + 1).to_i.times do
408 408 width = zoom
409 409 gc.fill(non_working_week_days.include?(wday) ? '#eee' : 'white')
410 410 gc.stroke('#ddd')
411 411 gc.stroke_width(1)
412 412 gc.rectangle(left, 2 * header_height, left + width, 2 * header_height + g_height - 1)
413 413 left = left + width
414 414 wday = wday + 1
415 415 wday = 1 if wday > 7
416 416 end
417 417 end
418 418 # border
419 419 gc.fill('transparent')
420 420 gc.stroke('grey')
421 421 gc.stroke_width(1)
422 422 gc.rectangle(0, 0, subject_width + g_width, headers_height)
423 423 gc.stroke('black')
424 424 gc.rectangle(0, 0, subject_width + g_width, g_height + headers_height - 1)
425 425 # content
426 426 top = headers_height + 20
427 427 gc.stroke('transparent')
428 428 lines(:image => gc, :top => top, :zoom => zoom,
429 429 :subject_width => subject_width, :format => :image)
430 430 # today red line
431 431 if Date.today >= @date_from and Date.today <= date_to
432 432 gc.stroke('red')
433 433 x = (Date.today - @date_from + 1) * zoom + subject_width
434 434 gc.line(x, headers_height, x, headers_height + g_height - 1)
435 435 end
436 436 gc.draw(imgl)
437 437 imgl.format = format
438 438 imgl.to_blob
439 439 end if Object.const_defined?(:Magick)
440 440
441 441 def to_pdf
442 442 pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language)
443 443 pdf.SetTitle("#{l(:label_gantt)} #{project}")
444 444 pdf.alias_nb_pages
445 445 pdf.footer_date = format_date(Date.today)
446 446 pdf.AddPage("L")
447 447 pdf.SetFontStyle('B', 12)
448 448 pdf.SetX(15)
449 449 pdf.RDMCell(PDF::LeftPaneWidth, 20, project.to_s)
450 450 pdf.Ln
451 451 pdf.SetFontStyle('B', 9)
452 452 subject_width = PDF::LeftPaneWidth
453 453 header_height = 5
454 454 headers_height = header_height
455 455 show_weeks = false
456 456 show_days = false
457 457 if self.months < 7
458 458 show_weeks = true
459 459 headers_height = 2 * header_height
460 460 if self.months < 3
461 461 show_days = true
462 462 headers_height = 3 * header_height
463 if self.months < 2
464 show_day_num = true
465 headers_height = 4 * header_height
466 end
463 467 end
464 468 end
465 469 g_width = PDF.right_pane_width
466 470 zoom = (g_width) / (self.date_to - self.date_from + 1)
467 471 g_height = 120
468 472 t_height = g_height + headers_height
469 473 y_start = pdf.GetY
470 474 # Months headers
471 475 month_f = self.date_from
472 476 left = subject_width
473 477 height = header_height
474 478 self.months.times do
475 479 width = ((month_f >> 1) - month_f) * zoom
476 480 pdf.SetY(y_start)
477 481 pdf.SetX(left)
478 482 pdf.RDMCell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
479 483 left = left + width
480 484 month_f = month_f >> 1
481 485 end
482 486 # Weeks headers
483 487 if show_weeks
484 488 left = subject_width
485 489 height = header_height
486 490 if self.date_from.cwday == 1
487 491 # self.date_from is monday
488 492 week_f = self.date_from
489 493 else
490 494 # find next monday after self.date_from
491 495 week_f = self.date_from + (7 - self.date_from.cwday + 1)
492 496 width = (7 - self.date_from.cwday + 1) * zoom-1
493 497 pdf.SetY(y_start + header_height)
494 498 pdf.SetX(left)
495 499 pdf.RDMCell(width + 1, height, "", "LTR")
496 500 left = left + width + 1
497 501 end
498 502 while week_f <= self.date_to
499 503 width = (week_f + 6 <= self.date_to) ? 7 * zoom : (self.date_to - week_f + 1) * zoom
500 504 pdf.SetY(y_start + header_height)
501 505 pdf.SetX(left)
502 506 pdf.RDMCell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
503 507 left = left + width
504 508 week_f = week_f + 7
505 509 end
506 510 end
511 # Day numbers headers
512 if show_day_num
513 left = subject_width
514 height = header_height
515 day_num = self.date_from
516 wday = self.date_from.cwday
517 pdf.SetFontStyle('B', 7)
518 (self.date_to - self.date_from + 1).to_i.times do
519 width = zoom
520 pdf.SetY(y_start + header_height * 2)
521 pdf.SetX(left)
522 pdf.SetTextColor(non_working_week_days.include?(wday) ? 150 : 0)
523 pdf.RDMCell(width, height, day_num.day.to_s, "LTR", 0, "C")
524 left = left + width
525 day_num = day_num + 1
526 wday = wday + 1
527 wday = 1 if wday > 7
528 end
529 end
507 530 # Days headers
508 531 if show_days
509 532 left = subject_width
510 533 height = header_height
511 534 wday = self.date_from.cwday
512 535 pdf.SetFontStyle('B', 7)
513 536 (self.date_to - self.date_from + 1).to_i.times do
514 537 width = zoom
515 pdf.SetY(y_start + 2 * header_height)
538 pdf.SetY(y_start + header_height * (show_day_num ? 3 : 2))
516 539 pdf.SetX(left)
540 pdf.SetTextColor(non_working_week_days.include?(wday) ? 150 : 0)
517 541 pdf.RDMCell(width, height, day_name(wday).first, "LTR", 0, "C")
518 542 left = left + width
519 543 wday = wday + 1
520 544 wday = 1 if wday > 7
521 545 end
522 546 end
523 547 pdf.SetY(y_start)
524 548 pdf.SetX(15)
549 pdf.SetTextColor(0)
525 550 pdf.RDMCell(subject_width + g_width - 15, headers_height, "", 1)
526 551 # Tasks
527 552 top = headers_height + y_start
528 553 options = {
529 554 :top => top,
530 555 :zoom => zoom,
531 556 :subject_width => subject_width,
532 557 :g_width => g_width,
533 558 :indent => 0,
534 559 :indent_increment => 5,
535 560 :top_increment => 5,
536 561 :format => :pdf,
537 562 :pdf => pdf
538 563 }
539 564 render(options)
540 565 pdf.Output
541 566 end
542 567
543 568 private
544 569
545 570 def coordinates(start_date, end_date, progress, zoom=nil)
546 571 zoom ||= @zoom
547 572 coords = {}
548 573 if start_date && end_date && start_date < self.date_to && end_date > self.date_from
549 574 if start_date > self.date_from
550 575 coords[:start] = start_date - self.date_from
551 576 coords[:bar_start] = start_date - self.date_from
552 577 else
553 578 coords[:bar_start] = 0
554 579 end
555 580 if end_date < self.date_to
556 581 coords[:end] = end_date - self.date_from
557 582 coords[:bar_end] = end_date - self.date_from + 1
558 583 else
559 584 coords[:bar_end] = self.date_to - self.date_from + 1
560 585 end
561 586 if progress
562 587 progress_date = calc_progress_date(start_date, end_date, progress)
563 588 if progress_date > self.date_from && progress_date > start_date
564 589 if progress_date < self.date_to
565 590 coords[:bar_progress_end] = progress_date - self.date_from
566 591 else
567 592 coords[:bar_progress_end] = self.date_to - self.date_from + 1
568 593 end
569 594 end
570 595 if progress_date < Date.today
571 596 late_date = [Date.today, end_date].min
572 597 if late_date > self.date_from && late_date > start_date
573 598 if late_date < self.date_to
574 599 coords[:bar_late_end] = late_date - self.date_from + 1
575 600 else
576 601 coords[:bar_late_end] = self.date_to - self.date_from + 1
577 602 end
578 603 end
579 604 end
580 605 end
581 606 end
582 607 # Transforms dates into pixels witdh
583 608 coords.keys.each do |key|
584 609 coords[key] = (coords[key] * zoom).floor
585 610 end
586 611 coords
587 612 end
588 613
589 614 def calc_progress_date(start_date, end_date, progress)
590 615 start_date + (end_date - start_date + 1) * (progress / 100.0)
591 616 end
592 617
593 618 def self.sort_issues!(issues)
594 619 issues.sort! {|a, b| sort_issue_logic(a) <=> sort_issue_logic(b)}
595 620 end
596 621
597 622 def self.sort_issue_logic(issue)
598 623 julian_date = Date.new()
599 624 ancesters_start_date = []
600 625 current_issue = issue
601 626 begin
602 627 ancesters_start_date.unshift([current_issue.start_date || julian_date, current_issue.id])
603 628 current_issue = current_issue.parent
604 629 end while (current_issue)
605 630 ancesters_start_date
606 631 end
607 632
608 633 def self.sort_versions!(versions)
609 634 versions.sort!
610 635 end
611 636
612 637 def pdf_new_page?(options)
613 638 if options[:top] > 180
614 639 options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
615 640 options[:pdf].AddPage("L")
616 641 options[:top] = 15
617 642 options[:pdf].Line(15, options[:top] - 0.1, PDF::TotalWidth, options[:top] - 0.1)
618 643 end
619 644 end
620 645
621 646 def html_subject_content(object)
622 647 case object
623 648 when Issue
624 649 issue = object
625 650 css_classes = ''
626 651 css_classes << ' issue-overdue' if issue.overdue?
627 652 css_classes << ' issue-behind-schedule' if issue.behind_schedule?
628 653 css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
629 654 css_classes << ' issue-closed' if issue.closed?
630 655 if issue.start_date && issue.due_before && issue.done_ratio
631 656 progress_date = calc_progress_date(issue.start_date,
632 657 issue.due_before, issue.done_ratio)
633 658 css_classes << ' behind-start-date' if progress_date < self.date_from
634 659 css_classes << ' over-end-date' if progress_date > self.date_to
635 660 end
636 661 s = "".html_safe
637 662 if issue.assigned_to.present?
638 663 assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
639 664 s << view.avatar(issue.assigned_to,
640 665 :class => 'gravatar icon-gravatar',
641 666 :size => 10,
642 667 :title => assigned_string).to_s.html_safe
643 668 end
644 669 s << view.link_to_issue(issue).html_safe
645 670 view.content_tag(:span, s, :class => css_classes).html_safe
646 671 when Version
647 672 version = object
648 673 html_class = ""
649 674 html_class << 'icon icon-package '
650 675 html_class << (version.behind_schedule? ? 'version-behind-schedule' : '') << " "
651 676 html_class << (version.overdue? ? 'version-overdue' : '')
652 677 html_class << ' version-closed' unless version.open?
653 678 if version.start_date && version.due_date && version.completed_percent
654 679 progress_date = calc_progress_date(version.start_date,
655 680 version.due_date, version.completed_percent)
656 681 html_class << ' behind-start-date' if progress_date < self.date_from
657 682 html_class << ' over-end-date' if progress_date > self.date_to
658 683 end
659 684 s = view.link_to_version(version).html_safe
660 685 view.content_tag(:span, s, :class => html_class).html_safe
661 686 when Project
662 687 project = object
663 688 html_class = ""
664 689 html_class << 'icon icon-projects '
665 690 html_class << (project.overdue? ? 'project-overdue' : '')
666 691 s = view.link_to_project(project).html_safe
667 692 view.content_tag(:span, s, :class => html_class).html_safe
668 693 end
669 694 end
670 695
671 696 def html_subject(params, subject, object)
672 697 style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
673 698 style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
674 699 content = html_subject_content(object) || subject
675 700 tag_options = {:style => style}
676 701 case object
677 702 when Issue
678 703 tag_options[:id] = "issue-#{object.id}"
679 704 tag_options[:class] = "issue-subject"
680 705 tag_options[:title] = object.subject
681 706 when Version
682 707 tag_options[:id] = "version-#{object.id}"
683 708 tag_options[:class] = "version-name"
684 709 when Project
685 710 tag_options[:class] = "project-name"
686 711 end
687 712 output = view.content_tag(:div, content, tag_options)
688 713 @subjects << output
689 714 output
690 715 end
691 716
692 717 def pdf_subject(params, subject, options={})
693 718 pdf_new_page?(params)
694 719 params[:pdf].SetY(params[:top])
695 720 params[:pdf].SetX(15)
696 721 char_limit = PDF::MaxCharactorsForSubject - params[:indent]
697 722 params[:pdf].RDMCell(params[:subject_width] - 15, 5,
698 723 (" " * params[:indent]) +
699 724 subject.to_s.sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'),
700 725 "LR")
701 726 params[:pdf].SetY(params[:top])
702 727 params[:pdf].SetX(params[:subject_width])
703 728 params[:pdf].RDMCell(params[:g_width], 5, "", "LR")
704 729 end
705 730
706 731 def image_subject(params, subject, options={})
707 732 params[:image].fill('black')
708 733 params[:image].stroke('transparent')
709 734 params[:image].stroke_width(1)
710 735 params[:image].text(params[:indent], params[:top] + 2, subject)
711 736 end
712 737
713 738 def issue_relations(issue)
714 739 rels = {}
715 740 if relations[issue.id]
716 741 relations[issue.id].each do |relation|
717 742 (rels[relation.relation_type] ||= []) << relation.issue_to_id
718 743 end
719 744 end
720 745 rels
721 746 end
722 747
723 748 def html_task(params, coords, markers, label, object)
724 749 output = ''
725 750
726 751 css = "task " + case object
727 752 when Project
728 753 "project"
729 754 when Version
730 755 "version"
731 756 when Issue
732 757 object.leaf? ? 'leaf' : 'parent'
733 758 else
734 759 ""
735 760 end
736 761
737 762 # Renders the task bar, with progress and late
738 763 if coords[:bar_start] && coords[:bar_end]
739 764 width = coords[:bar_end] - coords[:bar_start] - 2
740 765 style = ""
741 766 style << "top:#{params[:top]}px;"
742 767 style << "left:#{coords[:bar_start]}px;"
743 768 style << "width:#{width}px;"
744 769 html_id = "task-todo-issue-#{object.id}" if object.is_a?(Issue)
745 770 html_id = "task-todo-version-#{object.id}" if object.is_a?(Version)
746 771 content_opt = {:style => style,
747 772 :class => "#{css} task_todo",
748 773 :id => html_id}
749 774 if object.is_a?(Issue)
750 775 rels = issue_relations(object)
751 776 if rels.present?
752 777 content_opt[:data] = {"rels" => rels.to_json}
753 778 end
754 779 end
755 780 output << view.content_tag(:div, '&nbsp;'.html_safe, content_opt)
756 781 if coords[:bar_late_end]
757 782 width = coords[:bar_late_end] - coords[:bar_start] - 2
758 783 style = ""
759 784 style << "top:#{params[:top]}px;"
760 785 style << "left:#{coords[:bar_start]}px;"
761 786 style << "width:#{width}px;"
762 787 output << view.content_tag(:div, '&nbsp;'.html_safe,
763 788 :style => style,
764 789 :class => "#{css} task_late")
765 790 end
766 791 if coords[:bar_progress_end]
767 792 width = coords[:bar_progress_end] - coords[:bar_start] - 2
768 793 style = ""
769 794 style << "top:#{params[:top]}px;"
770 795 style << "left:#{coords[:bar_start]}px;"
771 796 style << "width:#{width}px;"
772 797 html_id = "task-done-issue-#{object.id}" if object.is_a?(Issue)
773 798 html_id = "task-done-version-#{object.id}" if object.is_a?(Version)
774 799 output << view.content_tag(:div, '&nbsp;'.html_safe,
775 800 :style => style,
776 801 :class => "#{css} task_done",
777 802 :id => html_id)
778 803 end
779 804 end
780 805 # Renders the markers
781 806 if markers
782 807 if coords[:start]
783 808 style = ""
784 809 style << "top:#{params[:top]}px;"
785 810 style << "left:#{coords[:start]}px;"
786 811 style << "width:15px;"
787 812 output << view.content_tag(:div, '&nbsp;'.html_safe,
788 813 :style => style,
789 814 :class => "#{css} marker starting")
790 815 end
791 816 if coords[:end]
792 817 style = ""
793 818 style << "top:#{params[:top]}px;"
794 819 style << "left:#{coords[:end] + params[:zoom]}px;"
795 820 style << "width:15px;"
796 821 output << view.content_tag(:div, '&nbsp;'.html_safe,
797 822 :style => style,
798 823 :class => "#{css} marker ending")
799 824 end
800 825 end
801 826 # Renders the label on the right
802 827 if label
803 828 style = ""
804 829 style << "top:#{params[:top]}px;"
805 830 style << "left:#{(coords[:bar_end] || 0) + 8}px;"
806 831 style << "width:15px;"
807 832 output << view.content_tag(:div, label,
808 833 :style => style,
809 834 :class => "#{css} label")
810 835 end
811 836 # Renders the tooltip
812 837 if object.is_a?(Issue) && coords[:bar_start] && coords[:bar_end]
813 838 s = view.content_tag(:span,
814 839 view.render_issue_tooltip(object).html_safe,
815 840 :class => "tip")
816 841 style = ""
817 842 style << "position: absolute;"
818 843 style << "top:#{params[:top]}px;"
819 844 style << "left:#{coords[:bar_start]}px;"
820 845 style << "width:#{coords[:bar_end] - coords[:bar_start]}px;"
821 846 style << "height:12px;"
822 847 output << view.content_tag(:div, s.html_safe,
823 848 :style => style,
824 849 :class => "tooltip")
825 850 end
826 851 @lines << output
827 852 output
828 853 end
829 854
830 855 def pdf_task(params, coords, markers, label, object)
831 856 cell_height_ratio = params[:pdf].get_cell_height_ratio()
832 857 params[:pdf].set_cell_height_ratio(0.1)
833 858
834 859 height = 2
835 860 height /= 2 if markers
836 861 # Renders the task bar, with progress and late
837 862 if coords[:bar_start] && coords[:bar_end]
838 863 params[:pdf].SetY(params[:top] + 1.5)
839 864 params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
840 865 params[:pdf].SetFillColor(200, 200, 200)
841 866 params[:pdf].RDMCell(coords[:bar_end] - coords[:bar_start], height, "", 0, 0, "", 1)
842 867 if coords[:bar_late_end]
843 868 params[:pdf].SetY(params[:top] + 1.5)
844 869 params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
845 870 params[:pdf].SetFillColor(255, 100, 100)
846 871 params[:pdf].RDMCell(coords[:bar_late_end] - coords[:bar_start], height, "", 0, 0, "", 1)
847 872 end
848 873 if coords[:bar_progress_end]
849 874 params[:pdf].SetY(params[:top] + 1.5)
850 875 params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
851 876 params[:pdf].SetFillColor(90, 200, 90)
852 877 params[:pdf].RDMCell(coords[:bar_progress_end] - coords[:bar_start], height, "", 0, 0, "", 1)
853 878 end
854 879 end
855 880 # Renders the markers
856 881 if markers
857 882 if coords[:start]
858 883 params[:pdf].SetY(params[:top] + 1)
859 884 params[:pdf].SetX(params[:subject_width] + coords[:start] - 1)
860 885 params[:pdf].SetFillColor(50, 50, 200)
861 886 params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1)
862 887 end
863 888 if coords[:end]
864 889 params[:pdf].SetY(params[:top] + 1)
865 890 params[:pdf].SetX(params[:subject_width] + coords[:end] - 1)
866 891 params[:pdf].SetFillColor(50, 50, 200)
867 892 params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1)
868 893 end
869 894 end
870 895 # Renders the label on the right
871 896 if label
872 897 params[:pdf].SetX(params[:subject_width] + (coords[:bar_end] || 0) + 5)
873 898 params[:pdf].RDMCell(30, 2, label)
874 899 end
875 900
876 901 params[:pdf].set_cell_height_ratio(cell_height_ratio)
877 902 end
878 903
879 904 def image_task(params, coords, markers, label, object)
880 905 height = 6
881 906 height /= 2 if markers
882 907 # Renders the task bar, with progress and late
883 908 if coords[:bar_start] && coords[:bar_end]
884 909 params[:image].fill('#aaa')
885 910 params[:image].rectangle(params[:subject_width] + coords[:bar_start],
886 911 params[:top],
887 912 params[:subject_width] + coords[:bar_end],
888 913 params[:top] - height)
889 914 if coords[:bar_late_end]
890 915 params[:image].fill('#f66')
891 916 params[:image].rectangle(params[:subject_width] + coords[:bar_start],
892 917 params[:top],
893 918 params[:subject_width] + coords[:bar_late_end],
894 919 params[:top] - height)
895 920 end
896 921 if coords[:bar_progress_end]
897 922 params[:image].fill('#00c600')
898 923 params[:image].rectangle(params[:subject_width] + coords[:bar_start],
899 924 params[:top],
900 925 params[:subject_width] + coords[:bar_progress_end],
901 926 params[:top] - height)
902 927 end
903 928 end
904 929 # Renders the markers
905 930 if markers
906 931 if coords[:start]
907 932 x = params[:subject_width] + coords[:start]
908 933 y = params[:top] - height / 2
909 934 params[:image].fill('blue')
910 935 params[:image].polygon(x - 4, y, x, y - 4, x + 4, y, x, y + 4)
911 936 end
912 937 if coords[:end]
913 938 x = params[:subject_width] + coords[:end] + params[:zoom]
914 939 y = params[:top] - height / 2
915 940 params[:image].fill('blue')
916 941 params[:image].polygon(x - 4, y, x, y - 4, x + 4, y, x, y + 4)
917 942 end
918 943 end
919 944 # Renders the label on the right
920 945 if label
921 946 params[:image].fill('black')
922 947 params[:image].text(params[:subject_width] + (coords[:bar_end] || 0) + 5,
923 948 params[:top] + 1,
924 949 label)
925 950 end
926 951 end
927 952 end
928 953 end
929 954 end
@@ -1,1245 +1,1245
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;padding: 2px 10px 1px 0px;margin: 0 0 10px 0;}
5 5 #content h1, h2, h3, h4 {color: #555;}
6 6 h2, .wiki h1 {font-size: 20px;}
7 7 h3, .wiki h2 {font-size: 16px;}
8 8 h4, .wiki h3 {font-size: 13px;}
9 9 h4 {border-bottom: 1px dotted #bbb;}
10 10 pre, code {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;}
11 11
12 12 /***** Layout *****/
13 13 #wrapper {background: white;}
14 14
15 15 #top-menu {background: #3E5B76; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
16 16 #top-menu ul {margin: 0; padding: 0;}
17 17 #top-menu li {
18 18 float:left;
19 19 list-style-type:none;
20 20 margin: 0px 0px 0px 0px;
21 21 padding: 0px 0px 0px 0px;
22 22 white-space:nowrap;
23 23 }
24 24 #top-menu a {color: #fff; margin-right: 8px; font-weight: bold;}
25 25 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
26 26
27 27 #account {float:right;}
28 28
29 29 #header {min-height:5.3em;margin:0;background-color:#628DB6;color:#f8f8f8; padding: 4px 8px 20px 6px; position:relative;}
30 30 #header a {color:#f8f8f8;}
31 31 #header h1 a.ancestor { font-size: 80%; }
32 32 #quick-search {float:right;}
33 33
34 34 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
35 35 #main-menu ul {margin: 0; padding: 0;}
36 36 #main-menu li {
37 37 float:left;
38 38 list-style-type:none;
39 39 margin: 0px 2px 0px 0px;
40 40 padding: 0px 0px 0px 0px;
41 41 white-space:nowrap;
42 42 }
43 43 #main-menu li a {
44 44 display: block;
45 45 color: #fff;
46 46 text-decoration: none;
47 47 font-weight: bold;
48 48 margin: 0;
49 49 padding: 4px 10px 4px 10px;
50 50 }
51 51 #main-menu li a:hover {background:#759FCF; color:#fff;}
52 52 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
53 53
54 54 #admin-menu ul {margin: 0; padding: 0;}
55 55 #admin-menu li {margin: 0; padding: 0 0 6px 0; list-style-type:none;}
56 56
57 57 #admin-menu a { background-position: 0% 40%; background-repeat: no-repeat; padding-left: 20px; padding-top: 2px; padding-bottom: 3px;}
58 58 #admin-menu a.projects { background-image: url(../images/projects.png); }
59 59 #admin-menu a.users { background-image: url(../images/user.png); }
60 60 #admin-menu a.groups { background-image: url(../images/group.png); }
61 61 #admin-menu a.roles { background-image: url(../images/database_key.png); }
62 62 #admin-menu a.trackers { background-image: url(../images/ticket.png); }
63 63 #admin-menu a.issue_statuses { background-image: url(../images/ticket_edit.png); }
64 64 #admin-menu a.workflows { background-image: url(../images/ticket_go.png); }
65 65 #admin-menu a.custom_fields { background-image: url(../images/textfield.png); }
66 66 #admin-menu a.enumerations { background-image: url(../images/text_list_bullets.png); }
67 67 #admin-menu a.settings { background-image: url(../images/changeset.png); }
68 68 #admin-menu a.plugins { background-image: url(../images/plugin.png); }
69 69 #admin-menu a.info { background-image: url(../images/help.png); }
70 70 #admin-menu a.server_authentication { background-image: url(../images/server_key.png); }
71 71
72 72 #main {background-color:#EEEEEE;}
73 73
74 74 #sidebar{ float: right; width: 22%; position: relative; z-index: 9; padding: 0; margin: 0;}
75 75 * html #sidebar{ width: 22%; }
76 76 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
77 77 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
78 78 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
79 79 #sidebar .contextual { margin-right: 1em; }
80 80 #sidebar ul {margin: 0; padding: 0;}
81 81 #sidebar ul li {list-style-type:none;margin: 0px 2px 0px 0px; padding: 0px 0px 0px 0px;}
82 82
83 83 #content { width: 75%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; }
84 84 * html #content{ width: 75%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
85 85 html>body #content { min-height: 600px; }
86 86 * html body #content { height: 600px; } /* IE */
87 87
88 88 #main.nosidebar #sidebar{ display: none; }
89 89 #main.nosidebar #content{ width: auto; border-right: 0; }
90 90
91 91 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
92 92
93 93 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
94 94 #login-form table td {padding: 6px;}
95 95 #login-form label {font-weight: bold;}
96 96 #login-form input#username, #login-form input#password { width: 300px; }
97 97
98 98 div.modal { border-radius:5px; background:#fff; z-index:50; padding:4px;}
99 99 div.modal h3.title {display:none;}
100 100 div.modal p.buttons {text-align:right; margin-bottom:0;}
101 101 div.modal .box p {margin: 0.3em 0;}
102 102
103 103 input#openid_url { background: url(../images/openid-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; padding-left: 18px; }
104 104
105 105 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
106 106
107 107 /***** Links *****/
108 108 a, a:link, a:visited{ color: #169; text-decoration: none; }
109 109 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
110 110 a img{ border: 0; }
111 111
112 112 a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
113 113 a.project.closed, a.project.closed:link, a.project.closed:visited { color: #999; }
114 114 a.user.locked, a.user.locked:link, a.user.locked:visited {color: #999;}
115 115
116 116 #sidebar a.selected {line-height:1.7em; padding:1px 3px 2px 2px; margin-left:-2px; background-color:#9DB9D5; color:#fff; border-radius:2px;}
117 117 #sidebar a.selected:hover {text-decoration:none;}
118 118 #admin-menu a {line-height:1.7em;}
119 119 #admin-menu a.selected {padding-left: 20px !important; background-position: 2px 40%;}
120 120
121 121 a.collapsible {padding-left: 12px; background: url(../images/arrow_expanded.png) no-repeat -3px 40%;}
122 122 a.collapsible.collapsed {background: url(../images/arrow_collapsed.png) no-repeat -5px 40%;}
123 123
124 124 a#toggle-completed-versions {color:#999;}
125 125 /***** Tables *****/
126 126 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
127 127 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
128 128 table.list td {text-align:center; vertical-align:top; padding-right:10px;}
129 129 table.list td.id { width: 2%; text-align: center;}
130 130 table.list td.name, table.list td.description, table.list td.subject, table.list td.comments, table.list td.roles {text-align: left;}
131 131 table.list td.tick {width:15%}
132 132 table.list td.checkbox { width: 15px; padding: 2px 0 0 0; }
133 133 table.list td.checkbox input {padding:0px;}
134 134 table.list td.buttons { width: 15%; white-space:nowrap; text-align: right; }
135 135 table.list td.buttons a { padding-right: 0.6em; }
136 136 table.list td.buttons img {vertical-align:middle;}
137 137 table.list td.reorder {width:15%; white-space:nowrap; text-align:center; }
138 138 table.list caption { text-align: left; padding: 0.5em 0.5em 0.5em 0; }
139 139
140 140 tr.project td.name a { white-space:nowrap; }
141 141 tr.project.closed, tr.project.archived { color: #aaa; }
142 142 tr.project.closed a, tr.project.archived a { color: #aaa; }
143 143
144 144 tr.project.idnt td.name span {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
145 145 tr.project.idnt-1 td.name {padding-left: 0.5em;}
146 146 tr.project.idnt-2 td.name {padding-left: 2em;}
147 147 tr.project.idnt-3 td.name {padding-left: 3.5em;}
148 148 tr.project.idnt-4 td.name {padding-left: 5em;}
149 149 tr.project.idnt-5 td.name {padding-left: 6.5em;}
150 150 tr.project.idnt-6 td.name {padding-left: 8em;}
151 151 tr.project.idnt-7 td.name {padding-left: 9.5em;}
152 152 tr.project.idnt-8 td.name {padding-left: 11em;}
153 153 tr.project.idnt-9 td.name {padding-left: 12.5em;}
154 154
155 155 tr.issue { text-align: center; white-space: nowrap; }
156 156 tr.issue td.subject, tr.issue td.category, td.assigned_to, tr.issue td.string, tr.issue td.text, tr.issue td.relations, tr.issue td.parent { white-space: normal; }
157 157 tr.issue td.relations { text-align: left; }
158 158 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
159 159 tr.issue td.relations span {white-space: nowrap;}
160 160 table.issues td.description {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;}
161 161 table.issues td.description pre {white-space:normal;}
162 162
163 163 tr.issue.idnt td.subject a {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
164 164 tr.issue.idnt-1 td.subject {padding-left: 0.5em;}
165 165 tr.issue.idnt-2 td.subject {padding-left: 2em;}
166 166 tr.issue.idnt-3 td.subject {padding-left: 3.5em;}
167 167 tr.issue.idnt-4 td.subject {padding-left: 5em;}
168 168 tr.issue.idnt-5 td.subject {padding-left: 6.5em;}
169 169 tr.issue.idnt-6 td.subject {padding-left: 8em;}
170 170 tr.issue.idnt-7 td.subject {padding-left: 9.5em;}
171 171 tr.issue.idnt-8 td.subject {padding-left: 11em;}
172 172 tr.issue.idnt-9 td.subject {padding-left: 12.5em;}
173 173
174 174 table.issue-report {table-layout:fixed;}
175 175
176 176 tr.entry { border: 1px solid #f8f8f8; }
177 177 tr.entry td { white-space: nowrap; }
178 178 tr.entry td.filename {width:30%; text-align:left;}
179 179 tr.entry td.filename_no_report {width:70%; text-align:left;}
180 180 tr.entry td.size { text-align: right; font-size: 90%; }
181 181 tr.entry td.revision, tr.entry td.author { text-align: center; }
182 182 tr.entry td.age { text-align: right; }
183 183 tr.entry.file td.filename a { margin-left: 16px; }
184 184 tr.entry.file td.filename_no_report a { margin-left: 16px; }
185 185
186 186 tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
187 187 tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
188 188
189 189 tr.changeset { height: 20px }
190 190 tr.changeset ul, ol { margin-top: 0px; margin-bottom: 0px; }
191 191 tr.changeset td.revision_graph { width: 15%; background-color: #fffffb; }
192 192 tr.changeset td.author { text-align: center; width: 15%; white-space:nowrap;}
193 193 tr.changeset td.committed_on { text-align: center; width: 15%; white-space:nowrap;}
194 194
195 195 table.files tbody th {text-align:left;}
196 196 table.files tr.file td.filename { text-align: left; padding-left: 24px; }
197 197 table.files tr.file td.digest { font-size: 80%; }
198 198
199 199 table.members td.roles, table.memberships td.roles { width: 45%; }
200 200
201 201 tr.message { height: 2.6em; }
202 202 tr.message td.subject { padding-left: 20px; }
203 203 tr.message td.created_on { white-space: nowrap; }
204 204 tr.message td.last_message { font-size: 80%; white-space: nowrap; }
205 205 tr.message.locked td.subject { background: url(../images/locked.png) no-repeat 0 1px; }
206 206 tr.message.sticky td.subject { background: url(../images/bullet_go.png) no-repeat 0 1px; font-weight: bold; }
207 207
208 208 tr.version.closed, tr.version.closed a { color: #999; }
209 209 tr.version td.name { padding-left: 20px; }
210 210 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70%; }
211 211 tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; white-space:nowrap; }
212 212
213 213 tr.user td {width:13%;white-space: nowrap;}
214 214 td.username, td.firstname, td.lastname, td.email {text-align:left !important;}
215 215 tr.user td.email { width:18%; }
216 216 tr.user.locked, tr.user.registered { color: #aaa; }
217 217 tr.user.locked a, tr.user.registered a { color: #aaa; }
218 218
219 219 table.permissions td.role {color:#999;font-size:90%;font-weight:normal !important;text-align:center;vertical-align:bottom;}
220 220
221 221 tr.wiki-page-version td.updated_on, tr.wiki-page-version td.author {text-align:center;}
222 222
223 223 tr.time-entry { text-align: center; white-space: nowrap; }
224 224 tr.time-entry td.issue, tr.time-entry td.comments, tr.time-entry td.subject, tr.time-entry td.activity { text-align: left; white-space: normal; }
225 225 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
226 226 td.hours .hours-dec { font-size: 0.9em; }
227 227
228 228 table.plugins td { vertical-align: middle; }
229 229 table.plugins td.configure { text-align: right; padding-right: 1em; }
230 230 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
231 231 table.plugins span.description { display: block; font-size: 0.9em; }
232 232 table.plugins span.url { display: block; font-size: 0.9em; }
233 233
234 234 table.list tbody tr.group td { padding: 0.8em 0 0.5em 0.3em; font-weight: bold; border-bottom: 1px solid #ccc; text-align:left; }
235 235 table.list tbody tr.group span.count {position:relative; top:-1px; color:#fff; font-size:10px; background:#9DB9D5; padding:0px 6px 1px 6px; border-radius:3px; margin-left:4px;}
236 236 tr.group a.toggle-all { color: #aaa; font-size: 80%; font-weight: normal; display:none;}
237 237 tr.group:hover a.toggle-all { display:inline;}
238 238 a.toggle-all:hover {text-decoration:none;}
239 239
240 240 table.list tbody tr:hover { background-color:#ffffdd; }
241 241 table.list tbody tr.group:hover { background-color:inherit; }
242 242 table td {padding:2px;}
243 243 table p {margin:0;}
244 244 .odd {background-color:#f6f7f8;}
245 245 .even {background-color: #fff;}
246 246
247 247 tr.builtin td.name {font-style:italic;}
248 248
249 249 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
250 250 a.sort.asc { background-image: url(../images/sort_asc.png); }
251 251 a.sort.desc { background-image: url(../images/sort_desc.png); }
252 252
253 253 table.attributes { width: 100% }
254 254 table.attributes th { vertical-align: top; text-align: left; }
255 255 table.attributes td { vertical-align: top; }
256 256
257 257 table.boards a.board, h3.comments { background: url(../images/comment.png) no-repeat 0% 50%; padding-left: 20px; }
258 258 table.boards td.last-message {text-align:left;font-size:80%;}
259 259
260 260 table.messages td.last_message {text-align:left;}
261 261
262 262 #query_form_content {font-size:90%;}
263 263
264 264 table.query-columns {
265 265 border-collapse: collapse;
266 266 border: 0;
267 267 }
268 268
269 269 table.query-columns td.buttons {
270 270 vertical-align: middle;
271 271 text-align: center;
272 272 }
273 273 table.query-columns td.buttons input[type=button] {width:35px;}
274 274
275 275 td.center {text-align:center;}
276 276
277 277 h3.version { background: url(../images/package.png) no-repeat 0% 50%; padding-left: 20px; }
278 278
279 279 div.issues h3 { background: url(../images/ticket.png) no-repeat 0% 50%; padding-left: 20px; }
280 280 div.members h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
281 281 div.news h3 { background: url(../images/news.png) no-repeat 0% 50%; padding-left: 20px; }
282 282 div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padding-left: 20px; }
283 283
284 284 #watchers select {width: 95%; display: block;}
285 285 #watchers a.delete {opacity: 0.4; vertical-align: middle;}
286 286 #watchers a.delete:hover {opacity: 1;}
287 287 #watchers img.gravatar {margin: 0 4px 2px 0;}
288 288
289 289 span#watchers_inputs {overflow:auto; display:block;}
290 290 span.search_for_watchers {display:block;}
291 291 span.search_for_watchers, span.add_attachment {font-size:80%; line-height:2.5em;}
292 292 span.search_for_watchers a, span.add_attachment a {padding-left:16px; background: url(../images/bullet_add.png) no-repeat 0 50%; }
293 293
294 294
295 295 .highlight { background-color: #FCFD8D;}
296 296 .highlight.token-1 { background-color: #faa;}
297 297 .highlight.token-2 { background-color: #afa;}
298 298 .highlight.token-3 { background-color: #aaf;}
299 299
300 300 .box{
301 301 padding:6px;
302 302 margin-bottom: 10px;
303 303 background-color:#f6f6f6;
304 304 color:#505050;
305 305 line-height:1.5em;
306 306 border: 1px solid #e4e4e4;
307 307 word-wrap: break-word;
308 308 border-radius: 3px;
309 309 }
310 310
311 311 div.square {
312 312 border: 1px solid #999;
313 313 float: left;
314 314 margin: .3em .4em 0 .4em;
315 315 overflow: hidden;
316 316 width: .6em; height: .6em;
317 317 }
318 318 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
319 319 .contextual input, .contextual select {font-size:0.9em;}
320 320 .message .contextual { margin-top: 0; }
321 321
322 322 .splitcontent {overflow:auto;}
323 323 .splitcontentleft{float:left; width:49%;}
324 324 .splitcontentright{float:right; width:49%;}
325 325 form {display: inline;}
326 326 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
327 327 fieldset {border: 1px solid #e4e4e4; margin:0;}
328 328 legend {color: #484848;}
329 329 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
330 330 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
331 331 blockquote blockquote { margin-left: 0;}
332 332 abbr, span.field-description[title] { border-bottom: 1px dotted #aaa; cursor: help; }
333 333 textarea.wiki-edit {width:99%; resize:vertical;}
334 334 li p {margin-top: 0;}
335 335 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px; border: 1px solid #d7d7d7; border-radius:3px;}
336 336 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
337 337 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
338 338 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
339 339 .ltr {direction:ltr !important; unicode-bidi:bidi-override;}
340 340 .rtl {direction:rtl !important; unicode-bidi:bidi-override;}
341 341
342 342 div.issue div.subject div div { padding-left: 16px; }
343 343 div.issue div.subject p {margin: 0; margin-bottom: 0.1em; font-size: 90%; color: #999;}
344 344 div.issue div.subject>div>p { margin-top: 0.5em; }
345 345 div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;}
346 346 div.issue span.private, div.journal span.private { position:relative; bottom: 2px; text-transform: uppercase; background: #d22; color: #fff; font-weight:bold; padding: 0px 2px 0px 2px; font-size: 60%; margin-right: 2px; border-radius: 2px;}
347 347 div.issue .next-prev-links {color:#999;}
348 348 div.issue table.attributes th {width:22%;}
349 349 div.issue table.attributes td {width:28%;}
350 350
351 351 #issue_tree table.issues, #relations table.issues { border: 0; }
352 352 #issue_tree td.checkbox, #relations td.checkbox {display:none;}
353 353 #relations td.buttons {padding:0;}
354 354
355 355 fieldset.collapsible {border-width: 1px 0 0 0;}
356 356 fieldset.collapsible>legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
357 357 fieldset.collapsible.collapsed>legend { background-image: url(../images/arrow_collapsed.png); }
358 358
359 359 fieldset#date-range p { margin: 2px 0 2px 0; }
360 360 fieldset#filters table { border-collapse: collapse; }
361 361 fieldset#filters table td { padding: 0; vertical-align: middle; }
362 362 fieldset#filters tr.filter { height: 2.1em; }
363 363 fieldset#filters td.field { width:230px; }
364 364 fieldset#filters td.operator { width:180px; }
365 365 fieldset#filters td.operator select {max-width:170px;}
366 366 fieldset#filters td.values { white-space:nowrap; }
367 367 fieldset#filters td.values select {min-width:130px;}
368 368 fieldset#filters td.values input {height:1em;}
369 369 fieldset#filters td.add-filter { text-align: right; vertical-align: top; }
370 370
371 371 .toggle-multiselect {background: url(../images/bullet_toggle_plus.png) no-repeat 0% 40%; padding-left:8px; margin-left:0; cursor:pointer;}
372 372 .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
373 373
374 374 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
375 375 div#issue-changesets div.changeset { padding: 4px;}
376 376 div#issue-changesets div.changeset { border-bottom: 1px solid #ddd; }
377 377 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
378 378
379 379 .journal ul.details img {margin:0 0 -3px 4px;}
380 380 div.journal {overflow:auto;}
381 381 div.journal.private-notes {border-left:2px solid #d22; padding-left:4px; margin-left:-6px;}
382 382
383 383 div#activity dl, #search-results { margin-left: 2em; }
384 384 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
385 385 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
386 386 div#activity dt.me .time { border-bottom: 1px solid #999; }
387 387 div#activity dt .time { color: #777; font-size: 80%; }
388 388 div#activity dd .description, #search-results dd .description { font-style: italic; }
389 389 div#activity span.project:after, #search-results span.project:after { content: " -"; }
390 390 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
391 391 div#activity dt.grouped {margin-left:5em;}
392 392 div#activity dd.grouped {margin-left:9em;}
393 393
394 394 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
395 395
396 396 div#search-results-counts {float:right;}
397 397 div#search-results-counts ul { margin-top: 0.5em; }
398 398 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
399 399
400 400 dt.issue { background-image: url(../images/ticket.png); }
401 401 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
402 402 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
403 403 dt.issue-note { background-image: url(../images/ticket_note.png); }
404 404 dt.changeset { background-image: url(../images/changeset.png); }
405 405 dt.news { background-image: url(../images/news.png); }
406 406 dt.message { background-image: url(../images/message.png); }
407 407 dt.reply { background-image: url(../images/comments.png); }
408 408 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
409 409 dt.attachment { background-image: url(../images/attachment.png); }
410 410 dt.document { background-image: url(../images/document.png); }
411 411 dt.project { background-image: url(../images/projects.png); }
412 412 dt.time-entry { background-image: url(../images/time.png); }
413 413
414 414 #search-results dt.issue.closed { background-image: url(../images/ticket_checked.png); }
415 415
416 416 div#roadmap .related-issues { margin-bottom: 1em; }
417 417 div#roadmap .related-issues td.checkbox { display: none; }
418 418 div#roadmap .wiki h1:first-child { display: none; }
419 419 div#roadmap .wiki h1 { font-size: 120%; }
420 420 div#roadmap .wiki h2 { font-size: 110%; }
421 421 body.controller-versions.action-show div#roadmap .related-issues {width:70%;}
422 422
423 423 div#version-summary { float:right; width:28%; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
424 424 div#version-summary fieldset { margin-bottom: 1em; }
425 425 div#version-summary fieldset.time-tracking table { width:100%; }
426 426 div#version-summary th, div#version-summary td.total-hours { text-align: right; }
427 427
428 428 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
429 429 table#time-report tbody tr.subtotal { font-style: italic; color:#777;}
430 430 table#time-report tbody tr.subtotal td.hours { color:#b0b0b0; }
431 431 table#time-report tbody tr.total { font-weight: bold; background-color:#EEEEEE; border-top:1px solid #e4e4e4;}
432 432 table#time-report .hours-dec { font-size: 0.9em; }
433 433
434 434 div.wiki-page .contextual a {opacity: 0.4}
435 435 div.wiki-page .contextual a:hover {opacity: 1}
436 436
437 437 form .attributes select { width: 60%; }
438 438 input#issue_subject, input#document_title { width: 99%; }
439 439 select#issue_done_ratio { width: 95px; }
440 440
441 441 ul.projects {margin:0; padding-left:1em;}
442 442 ul.projects ul {padding-left:1.6em;}
443 443 ul.projects.root {margin:0; padding:0;}
444 444 ul.projects li {list-style-type:none;}
445 445
446 446 #projects-index ul.projects ul.projects { border-left: 3px solid #e0e0e0; padding-left:1em;}
447 447 #projects-index ul.projects li.root {margin-bottom: 1em;}
448 448 #projects-index ul.projects li.child {margin-top: 1em;}
449 449 #projects-index ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
450 450 .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
451 451
452 452 #notified-projects>ul, #tracker_project_ids>ul, #custom_field_project_ids>ul {max-height:250px; overflow-y:auto;}
453 453
454 454 #related-issues li img {vertical-align:middle;}
455 455
456 456 ul.properties {padding:0; font-size: 0.9em; color: #777;}
457 457 ul.properties li {list-style-type:none;}
458 458 ul.properties li span {font-style:italic;}
459 459
460 460 .total-hours { font-size: 110%; font-weight: bold; }
461 461 .total-hours span.hours-int { font-size: 120%; }
462 462
463 463 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
464 464 #user_login, #user_firstname, #user_lastname, #user_mail, #my_account_form select, #user_form select, #user_identity_url { width: 90%; }
465 465
466 466 #workflow_copy_form select { width: 200px; }
467 467 table.transitions td.enabled {background: #bfb;}
468 468 #workflow_form table select {font-size:90%; max-width:100px;}
469 469 table.fields_permissions td.readonly {background:#ddd;}
470 470 table.fields_permissions td.required {background:#d88;}
471 471
472 472 select.expandable {vertical-align:top;}
473 473
474 474 textarea#custom_field_possible_values {width: 95%; resize:vertical}
475 475 textarea#custom_field_default_value {width: 95%; resize:vertical}
476 476
477 477 input#content_comments {width: 99%}
478 478
479 479 p.pagination {margin-top:8px; font-size: 90%}
480 480
481 481 #search-form fieldset p {margin:0.2em 0;}
482 482
483 483 /***** Tabular forms ******/
484 484 .tabular p{
485 485 margin: 0;
486 486 padding: 3px 0 3px 0;
487 487 padding-left: 180px; /* width of left column containing the label elements */
488 488 min-height: 1.8em;
489 489 clear:left;
490 490 }
491 491
492 492 html>body .tabular p {overflow:hidden;}
493 493
494 494 .tabular input, .tabular select {max-width:95%}
495 495 .tabular textarea {width:95%; resize:vertical;}
496 496
497 497 .tabular label{
498 498 font-weight: bold;
499 499 float: left;
500 500 text-align: right;
501 501 /* width of left column */
502 502 margin-left: -180px;
503 503 /* width of labels. Should be smaller than left column to create some right margin */
504 504 width: 175px;
505 505 }
506 506
507 507 .tabular label.floating{
508 508 font-weight: normal;
509 509 margin-left: 0px;
510 510 text-align: left;
511 511 width: 270px;
512 512 }
513 513
514 514 .tabular label.block{
515 515 font-weight: normal;
516 516 margin-left: 0px !important;
517 517 text-align: left;
518 518 float: none;
519 519 display: block;
520 520 width: auto !important;
521 521 }
522 522
523 523 .tabular label.inline{
524 524 font-weight: normal;
525 525 float:none;
526 526 margin-left: 5px !important;
527 527 width: auto;
528 528 }
529 529
530 530 label.no-css {
531 531 font-weight: inherit;
532 532 float:none;
533 533 text-align:left;
534 534 margin-left:0px;
535 535 width:auto;
536 536 }
537 537 input#time_entry_comments { width: 90%;}
538 538
539 539 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
540 540
541 541 .tabular.settings p{ padding-left: 300px; }
542 542 .tabular.settings label{ margin-left: -300px; width: 295px; }
543 543 .tabular.settings textarea { width: 99%; }
544 544
545 545 .settings.enabled_scm table {width:100%}
546 546 .settings.enabled_scm td.scm_name{ font-weight: bold; }
547 547
548 548 fieldset.settings label { display: block; }
549 549 fieldset#notified_events .parent { padding-left: 20px; }
550 550
551 551 span.required {color: #bb0000;}
552 552 .summary {font-style: italic;}
553 553
554 554 .check_box_group {
555 555 display:block;
556 556 width:95%;
557 557 max-height:300px;
558 558 overflow-y:auto;
559 559 padding:2px 4px 4px 2px;
560 560 background:#fff;
561 561 border:1px solid #9EB1C2;
562 562 border-radius:2px
563 563 }
564 564 .check_box_group label {
565 565 font-weight: normal;
566 566 margin-left: 0px !important;
567 567 text-align: left;
568 568 float: none;
569 569 display: block;
570 570 width: auto;
571 571 }
572 572 .check_box_group.bool_cf {border:0; background:inherit;}
573 573 .check_box_group.bool_cf label {display: inline;}
574 574
575 575 #attachments_fields input.description {margin-left:4px; width:340px;}
576 576 #attachments_fields span {display:block; white-space:nowrap;}
577 577 #attachments_fields input.filename {border:0; height:1.8em; width:250px; color:#555; background-color:inherit; background:url(../images/attachment.png) no-repeat 1px 50%; padding-left:18px;}
578 578 #attachments_fields .ajax-waiting input.filename {background:url(../images/hourglass.png) no-repeat 0px 50%;}
579 579 #attachments_fields .ajax-loading input.filename {background:url(../images/loading.gif) no-repeat 0px 50%;}
580 580 #attachments_fields div.ui-progressbar { width: 100px; height:14px; margin: 2px 0 -5px 8px; display: inline-block; }
581 581 a.remove-upload {background: url(../images/delete.png) no-repeat 1px 50%; width:1px; display:inline-block; padding-left:16px;}
582 582 a.remove-upload:hover {text-decoration:none !important;}
583 583
584 584 div.fileover { background-color: lavender; }
585 585
586 586 div.attachments { margin-top: 12px; }
587 587 div.attachments p { margin:4px 0 2px 0; }
588 588 div.attachments img { vertical-align: middle; }
589 589 div.attachments span.author { font-size: 0.9em; color: #888; }
590 590
591 591 div.thumbnails {margin-top:0.6em;}
592 592 div.thumbnails div {background:#fff;border:2px solid #ddd;display:inline-block;margin-right:2px;}
593 593 div.thumbnails img {margin: 3px;}
594 594
595 595 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
596 596 .other-formats span + span:before { content: "| "; }
597 597
598 598 a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
599 599
600 600 em.info {font-style:normal;font-size:90%;color:#888;display:block;}
601 601 em.info.error {padding-left:20px; background:url(../images/exclamation.png) no-repeat 0 50%;}
602 602
603 603 textarea.text_cf {width:95%; resize:vertical;}
604 604 input.string_cf, input.link_cf {width:95%;}
605 605 select.bool_cf {width:auto !important;}
606 606
607 607 #tab-content-modules fieldset p {margin:3px 0 4px 0;}
608 608
609 609 #tab-content-users .splitcontentleft {width: 64%;}
610 610 #tab-content-users .splitcontentright {width: 34%;}
611 611 #tab-content-users fieldset {padding:1em; margin-bottom: 1em;}
612 612 #tab-content-users fieldset legend {font-weight: bold;}
613 613 #tab-content-users fieldset label {display: block;}
614 614 #tab-content-users #principals {max-height: 400px; overflow: auto;}
615 615
616 616 #users_for_watcher {height: 200px; overflow:auto;}
617 617 #users_for_watcher label {display: block;}
618 618
619 619 table.members td.name {padding-left: 20px;}
620 620 table.members td.group, table.members td.groupnonmember, table.members td.groupanonymous {background: url(../images/group.png) no-repeat 0% 1px;}
621 621
622 622 input#principal_search, input#user_search {width:90%}
623 623 .roles-selection label {display:inline-block; width:210px;}
624 624
625 625 input.autocomplete {
626 626 background: #fff url(../images/magnifier.png) no-repeat 2px 50%; padding-left:20px !important;
627 627 border:1px solid #9EB1C2; border-radius:2px; height:1.5em;
628 628 }
629 629 input.autocomplete.ajax-loading {
630 630 background-image: url(../images/loading.gif);
631 631 }
632 632
633 633 .role-visibility {padding-left:2em;}
634 634
635 635 .objects-selection {
636 636 height: 300px;
637 637 overflow: auto;
638 638 }
639 639
640 640 .objects-selection label {
641 641 display: block;
642 642 }
643 643
644 644 .objects-selection>div {
645 645 column-count: auto;
646 646 column-width: 200px;
647 647 -webkit-column-count: auto;
648 648 -webkit-column-width: 200px;
649 649 -webkit-column-gap : 0.5rem;
650 650 -webkit-column-rule: 1px solid #ccc;
651 651 -moz-column-count: auto;
652 652 -moz-column-width: 200px;
653 653 -moz-column-gap : 0.5rem;
654 654 -moz-column-rule: 1px solid #ccc;
655 655 }
656 656
657 657 /***** Flash & error messages ****/
658 658 #errorExplanation, div.flash, .nodata, .warning, .conflict {
659 659 padding: 4px 4px 4px 30px;
660 660 margin-bottom: 12px;
661 661 font-size: 1.1em;
662 662 border: 2px solid;
663 663 border-radius: 3px;
664 664 }
665 665
666 666 div.flash {margin-top: 8px;}
667 667
668 668 div.flash.error, #errorExplanation {
669 669 background: url(../images/exclamation.png) 8px 50% no-repeat;
670 670 background-color: #ffe3e3;
671 671 border-color: #dd0000;
672 672 color: #880000;
673 673 }
674 674
675 675 div.flash.notice {
676 676 background: url(../images/true.png) 8px 5px no-repeat;
677 677 background-color: #dfffdf;
678 678 border-color: #9fcf9f;
679 679 color: #005f00;
680 680 }
681 681
682 682 div.flash.warning, .conflict {
683 683 background: url(../images/warning.png) 8px 5px no-repeat;
684 684 background-color: #FFEBC1;
685 685 border-color: #FDBF3B;
686 686 color: #A6750C;
687 687 text-align: left;
688 688 }
689 689
690 690 .nodata, .warning {
691 691 text-align: center;
692 692 background-color: #FFEBC1;
693 693 border-color: #FDBF3B;
694 694 color: #A6750C;
695 695 }
696 696
697 697 #errorExplanation ul { font-size: 0.9em;}
698 698 #errorExplanation h2, #errorExplanation p { display: none; }
699 699
700 700 .conflict-details {font-size:80%;}
701 701
702 702 /***** Ajax indicator ******/
703 703 #ajax-indicator {
704 704 position: absolute; /* fixed not supported by IE */
705 705 background-color:#eee;
706 706 border: 1px solid #bbb;
707 707 top:35%;
708 708 left:40%;
709 709 width:20%;
710 710 font-weight:bold;
711 711 text-align:center;
712 712 padding:0.6em;
713 713 z-index:100;
714 714 opacity: 0.5;
715 715 }
716 716
717 717 html>body #ajax-indicator { position: fixed; }
718 718
719 719 #ajax-indicator span {
720 720 background-position: 0% 40%;
721 721 background-repeat: no-repeat;
722 722 background-image: url(../images/loading.gif);
723 723 padding-left: 26px;
724 724 vertical-align: bottom;
725 725 }
726 726
727 727 /***** Calendar *****/
728 728 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
729 729 table.cal thead th {width: 14%; background-color:#EEEEEE; padding: 4px; }
730 730 table.cal thead th.week-number {width: auto;}
731 731 table.cal tbody tr {height: 100px;}
732 732 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
733 733 table.cal td.week-number { background-color:#EEEEEE; padding: 4px; border:none; font-size: 1em;}
734 734 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
735 735 table.cal td.odd p.day-num {color: #bbb;}
736 736 table.cal td.today {background:#ffffdd;}
737 737 table.cal td.today p.day-num {font-weight: bold;}
738 738 table.cal .starting a, p.cal.legend .starting {background: url(../images/bullet_go.png) no-repeat -1px -2px; padding-left:16px;}
739 739 table.cal .ending a, p.cal.legend .ending {background: url(../images/bullet_end.png) no-repeat -1px -2px; padding-left:16px;}
740 740 table.cal .starting.ending a, p.cal.legend .starting.ending {background: url(../images/bullet_diamond.png) no-repeat -1px -2px; padding-left:16px;}
741 741 p.cal.legend span {display:block;}
742 742
743 743 /***** Tooltips ******/
744 744 .tooltip{position:relative;z-index:24;}
745 745 .tooltip:hover{z-index:25;color:#000;}
746 746 .tooltip span.tip{display: none; text-align:left;}
747 747
748 748 div.tooltip:hover span.tip{
749 749 display:block;
750 750 position:absolute;
751 751 top:12px; left:24px; width:270px;
752 752 border:1px solid #555;
753 753 background-color:#fff;
754 754 padding: 4px;
755 755 font-size: 0.8em;
756 756 color:#505050;
757 757 }
758 758
759 759 img.ui-datepicker-trigger {
760 760 cursor: pointer;
761 761 vertical-align: middle;
762 762 margin-left: 4px;
763 763 }
764 764
765 765 /***** Progress bar *****/
766 766 table.progress {
767 767 border-collapse: collapse;
768 768 border-spacing: 0pt;
769 769 empty-cells: show;
770 770 text-align: center;
771 771 float:left;
772 772 margin: 1px 6px 1px 0px;
773 773 }
774 774
775 775 table.progress td { height: 1em; }
776 776 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
777 777 table.progress td.done { background: #D3EDD3 none repeat scroll 0%; }
778 778 table.progress td.todo { background: #eee none repeat scroll 0%; }
779 779 p.percent {font-size: 80%;}
780 780 p.progress-info {clear: left; font-size: 80%; margin-top:-4px; color:#777;}
781 781
782 782 #roadmap table.progress td { height: 1.2em; }
783 783 /***** Tabs *****/
784 784 #content .tabs {height: 2.6em; margin-bottom:1.2em; position:relative; overflow:hidden;}
785 785 #content .tabs ul {margin:0; position:absolute; bottom:0; padding-left:0.5em; width: 2000px; border-bottom: 1px solid #bbbbbb;}
786 786 #content .tabs ul li {
787 787 float:left;
788 788 list-style-type:none;
789 789 white-space:nowrap;
790 790 margin-right:4px;
791 791 background:#fff;
792 792 position:relative;
793 793 margin-bottom:-1px;
794 794 }
795 795 #content .tabs ul li a{
796 796 display:block;
797 797 font-size: 0.9em;
798 798 text-decoration:none;
799 799 line-height:1.3em;
800 800 padding:4px 6px 4px 6px;
801 801 border: 1px solid #ccc;
802 802 border-bottom: 1px solid #bbbbbb;
803 803 background-color: #f6f6f6;
804 804 color:#999;
805 805 font-weight:bold;
806 806 border-top-left-radius:3px;
807 807 border-top-right-radius:3px;
808 808 }
809 809
810 810 #content .tabs ul li a:hover {
811 811 background-color: #ffffdd;
812 812 text-decoration:none;
813 813 }
814 814
815 815 #content .tabs ul li a.selected {
816 816 background-color: #fff;
817 817 border: 1px solid #bbbbbb;
818 818 border-bottom: 1px solid #fff;
819 819 color:#444;
820 820 }
821 821
822 822 #content .tabs ul li a.selected:hover {background-color: #fff;}
823 823
824 824 div.tabs-buttons { position:absolute; right: 0; width: 48px; height: 24px; background: white; bottom: 0; border-bottom: 1px solid #bbbbbb; }
825 825
826 826 button.tab-left, button.tab-right {
827 827 font-size: 0.9em;
828 828 cursor: pointer;
829 829 height:24px;
830 830 border: 1px solid #ccc;
831 831 border-bottom: 1px solid #bbbbbb;
832 832 position:absolute;
833 833 padding:4px;
834 834 width: 20px;
835 835 bottom: -1px;
836 836 }
837 837
838 838 button.tab-left {
839 839 right: 20px;
840 840 background: #eeeeee url(../images/bullet_arrow_left.png) no-repeat 50% 50%;
841 841 border-top-left-radius:3px;
842 842 }
843 843
844 844 button.tab-right {
845 845 right: 0;
846 846 background: #eeeeee url(../images/bullet_arrow_right.png) no-repeat 50% 50%;
847 847 border-top-right-radius:3px;
848 848 }
849 849
850 850 /***** Diff *****/
851 851 .diff_out { background: #fcc; }
852 852 .diff_out span { background: #faa; }
853 853 .diff_in { background: #cfc; }
854 854 .diff_in span { background: #afa; }
855 855
856 856 .text-diff {
857 857 padding: 1em;
858 858 background-color:#f6f6f6;
859 859 color:#505050;
860 860 border: 1px solid #e4e4e4;
861 861 }
862 862
863 863 /***** Wiki *****/
864 864 div.wiki table {
865 865 border-collapse: collapse;
866 866 margin-bottom: 1em;
867 867 }
868 868
869 869 div.wiki table, div.wiki td, div.wiki th {
870 870 border: 1px solid #bbb;
871 871 padding: 4px;
872 872 }
873 873
874 874 div.wiki .noborder, div.wiki .noborder td, div.wiki .noborder th {border:0;}
875 875
876 876 div.wiki .external {
877 877 background-position: 0% 60%;
878 878 background-repeat: no-repeat;
879 879 padding-left: 12px;
880 880 background-image: url(../images/external.png);
881 881 }
882 882
883 883 div.wiki a {word-wrap: break-word;}
884 884 div.wiki a.new {color: #b73535;}
885 885
886 886 div.wiki ul, div.wiki ol {margin-bottom:1em;}
887 887 div.wiki li>ul, div.wiki li>ol {margin-bottom: 0;}
888 888
889 889 div.wiki pre {
890 890 margin: 1em 1em 1em 1.6em;
891 891 padding: 8px;
892 892 background-color: #fafafa;
893 893 border: 1px solid #e2e2e2;
894 894 border-radius: 3px;
895 895 width:auto;
896 896 overflow-x: auto;
897 897 overflow-y: hidden;
898 898 }
899 899
900 900 div.wiki ul.toc {
901 901 background-color: #ffffdd;
902 902 border: 1px solid #e4e4e4;
903 903 padding: 4px;
904 904 line-height: 1.2em;
905 905 margin-bottom: 12px;
906 906 margin-right: 12px;
907 907 margin-left: 0;
908 908 display: table
909 909 }
910 910 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
911 911
912 912 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
913 913 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
914 914 div.wiki ul.toc ul { margin: 0; padding: 0; }
915 915 div.wiki ul.toc li {list-style-type:none; margin: 0; font-size:12px;}
916 916 div.wiki ul.toc li li {margin-left: 1.5em; font-size:10px;}
917 917 div.wiki ul.toc a {
918 918 font-size: 0.9em;
919 919 font-weight: normal;
920 920 text-decoration: none;
921 921 color: #606060;
922 922 }
923 923 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
924 924
925 925 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
926 926 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
927 927 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
928 928
929 929 div.wiki img {vertical-align:middle; max-width:100%;}
930 930
931 931 /***** My page layout *****/
932 932 .block-receiver {
933 933 border:1px dashed #c0c0c0;
934 934 margin-bottom: 20px;
935 935 padding: 15px 0 15px 0;
936 936 }
937 937
938 938 .mypage-box {
939 939 margin:0 0 20px 0;
940 940 color:#505050;
941 941 line-height:1.5em;
942 942 }
943 943
944 944 .handle {cursor: move;}
945 945
946 946 a.close-icon {
947 947 display:block;
948 948 margin-top:3px;
949 949 overflow:hidden;
950 950 width:12px;
951 951 height:12px;
952 952 background-repeat: no-repeat;
953 953 cursor:pointer;
954 954 background-image:url('../images/close.png');
955 955 }
956 956 a.close-icon:hover {background-image:url('../images/close_hl.png');}
957 957
958 958 /***** Gantt chart *****/
959 959 .gantt_hdr {
960 960 position:absolute;
961 961 top:0;
962 962 height:16px;
963 963 border-top: 1px solid #c0c0c0;
964 964 border-bottom: 1px solid #c0c0c0;
965 965 border-right: 1px solid #c0c0c0;
966 966 text-align: center;
967 967 overflow: hidden;
968 968 }
969 969
970 .gantt_hdr.nwday {background-color:#f1f1f1;}
970 .gantt_hdr.nwday {background-color:#f1f1f1; color:#999;}
971 971
972 972 .gantt_subjects { font-size: 0.8em; }
973 973 .gantt_subjects div { line-height:16px;height:16px;overflow:hidden;white-space:nowrap;text-overflow: ellipsis; }
974 974
975 975 .task {
976 976 position: absolute;
977 977 height:8px;
978 978 font-size:0.8em;
979 979 color:#888;
980 980 padding:0;
981 981 margin:0;
982 982 line-height:16px;
983 983 white-space:nowrap;
984 984 }
985 985
986 986 .task.label {width:100%;}
987 987 .task.label.project, .task.label.version { font-weight: bold; }
988 988
989 989 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
990 990 .task_done { background:#00c600 url(../images/task_done.png); border: 1px solid #00c600; }
991 991 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
992 992
993 993 .task_todo.parent { background: #888; border: 1px solid #888; height: 3px;}
994 994 .task_late.parent, .task_done.parent { height: 3px;}
995 995 .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;}
996 996 .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;}
997 997
998 998 .version.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
999 999 .version.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
1000 1000 .version.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
1001 1001 .version.marker { background-image:url(../images/version_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
1002 1002
1003 1003 .project.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
1004 1004 .project.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
1005 1005 .project.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
1006 1006 .project.marker { background-image:url(../images/project_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
1007 1007
1008 1008 .version-behind-schedule a, .issue-behind-schedule a {color: #f66914;}
1009 1009 .version-overdue a, .issue-overdue a, .project-overdue a {color: #f00;}
1010 1010
1011 1011 /***** Icons *****/
1012 1012 .icon {
1013 1013 background-position: 0% 50%;
1014 1014 background-repeat: no-repeat;
1015 1015 padding-left: 20px;
1016 1016 padding-top: 2px;
1017 1017 padding-bottom: 3px;
1018 1018 }
1019 1019
1020 1020 .icon-add { background-image: url(../images/add.png); }
1021 1021 .icon-edit { background-image: url(../images/edit.png); }
1022 1022 .icon-copy { background-image: url(../images/copy.png); }
1023 1023 .icon-duplicate { background-image: url(../images/duplicate.png); }
1024 1024 .icon-del { background-image: url(../images/delete.png); }
1025 1025 .icon-move { background-image: url(../images/move.png); }
1026 1026 .icon-save { background-image: url(../images/save.png); }
1027 1027 .icon-cancel { background-image: url(../images/cancel.png); }
1028 1028 .icon-multiple { background-image: url(../images/table_multiple.png); }
1029 1029 .icon-folder { background-image: url(../images/folder.png); }
1030 1030 .open .icon-folder { background-image: url(../images/folder_open.png); }
1031 1031 .icon-package { background-image: url(../images/package.png); }
1032 1032 .icon-user { background-image: url(../images/user.png); }
1033 1033 .icon-projects { background-image: url(../images/projects.png); }
1034 1034 .icon-help { background-image: url(../images/help.png); }
1035 1035 .icon-attachment { background-image: url(../images/attachment.png); }
1036 1036 .icon-history { background-image: url(../images/history.png); }
1037 1037 .icon-time { background-image: url(../images/time.png); }
1038 1038 .icon-time-add { background-image: url(../images/time_add.png); }
1039 1039 .icon-stats { background-image: url(../images/stats.png); }
1040 1040 .icon-warning { background-image: url(../images/warning.png); }
1041 1041 .icon-fav { background-image: url(../images/fav.png); }
1042 1042 .icon-fav-off { background-image: url(../images/fav_off.png); }
1043 1043 .icon-reload { background-image: url(../images/reload.png); }
1044 1044 .icon-lock { background-image: url(../images/locked.png); }
1045 1045 .icon-unlock { background-image: url(../images/unlock.png); }
1046 1046 .icon-checked { background-image: url(../images/true.png); }
1047 1047 .icon-details { background-image: url(../images/zoom_in.png); }
1048 1048 .icon-report { background-image: url(../images/report.png); }
1049 1049 .icon-comment { background-image: url(../images/comment.png); }
1050 1050 .icon-summary { background-image: url(../images/lightning.png); }
1051 1051 .icon-server-authentication { background-image: url(../images/server_key.png); }
1052 1052 .icon-issue { background-image: url(../images/ticket.png); }
1053 1053 .icon-zoom-in { background-image: url(../images/zoom_in.png); }
1054 1054 .icon-zoom-out { background-image: url(../images/zoom_out.png); }
1055 1055 .icon-passwd { background-image: url(../images/textfield_key.png); }
1056 1056 .icon-test { background-image: url(../images/bullet_go.png); }
1057 1057 .icon-email-add { background-image: url(../images/email_add.png); }
1058 1058
1059 1059 .icon-file { background-image: url(../images/files/default.png); }
1060 1060 .icon-file.text-plain { background-image: url(../images/files/text.png); }
1061 1061 .icon-file.text-x-c { background-image: url(../images/files/c.png); }
1062 1062 .icon-file.text-x-csharp { background-image: url(../images/files/csharp.png); }
1063 1063 .icon-file.text-x-java { background-image: url(../images/files/java.png); }
1064 1064 .icon-file.text-x-javascript { background-image: url(../images/files/js.png); }
1065 1065 .icon-file.text-x-php { background-image: url(../images/files/php.png); }
1066 1066 .icon-file.text-x-ruby { background-image: url(../images/files/ruby.png); }
1067 1067 .icon-file.text-xml { background-image: url(../images/files/xml.png); }
1068 1068 .icon-file.text-css { background-image: url(../images/files/css.png); }
1069 1069 .icon-file.text-html { background-image: url(../images/files/html.png); }
1070 1070 .icon-file.image-gif { background-image: url(../images/files/image.png); }
1071 1071 .icon-file.image-jpeg { background-image: url(../images/files/image.png); }
1072 1072 .icon-file.image-png { background-image: url(../images/files/image.png); }
1073 1073 .icon-file.image-tiff { background-image: url(../images/files/image.png); }
1074 1074 .icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
1075 1075 .icon-file.application-zip { background-image: url(../images/files/zip.png); }
1076 1076 .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); }
1077 1077
1078 1078 img.gravatar {
1079 1079 padding: 2px;
1080 1080 border: solid 1px #d5d5d5;
1081 1081 background: #fff;
1082 1082 vertical-align: middle;
1083 1083 }
1084 1084
1085 1085 div.issue img.gravatar {
1086 1086 float: left;
1087 1087 margin: 0 6px 0 0;
1088 1088 padding: 5px;
1089 1089 }
1090 1090
1091 1091 div.issue table img.gravatar {
1092 1092 height: 14px;
1093 1093 width: 14px;
1094 1094 padding: 2px;
1095 1095 float: left;
1096 1096 margin: 0 0.5em 0 0;
1097 1097 }
1098 1098
1099 1099 h2 img.gravatar {margin: -2px 4px -4px 0;}
1100 1100 h3 img.gravatar {margin: -4px 4px -4px 0;}
1101 1101 h4 img.gravatar {margin: -6px 4px -4px 0;}
1102 1102 td.username img.gravatar {margin: 0 0.5em 0 0; vertical-align: top;}
1103 1103 #activity dt img.gravatar {float: left; margin: 0 1em 1em 0;}
1104 1104 /* Used on 12px Gravatar img tags without the icon background */
1105 1105 .icon-gravatar {float: left; margin-right: 4px;}
1106 1106
1107 1107 #activity dt, .journal {clear: left;}
1108 1108
1109 1109 .journal-link {float: right;}
1110 1110
1111 1111 h2 img { vertical-align:middle; }
1112 1112
1113 1113 .hascontextmenu { cursor: context-menu; }
1114 1114
1115 1115 .sample-data {border:1px solid #ccc; border-collapse:collapse; background-color:#fff; margin:0.5em;}
1116 1116 .sample-data td {border:1px solid #ccc; padding: 2px 4px; font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;}
1117 1117 .sample-data tr:first-child td {font-weight:bold; text-align:center;}
1118 1118
1119 1119 .ui-progressbar {position: relative;}
1120 1120 #progress-label {
1121 1121 position: absolute; left: 50%; top: 4px;
1122 1122 font-weight: bold;
1123 1123 color: #555; text-shadow: 1px 1px 0 #fff;
1124 1124 }
1125 1125
1126 1126 /* Custom JQuery styles */
1127 1127 .ui-datepicker-title select {width:70px !important; margin-top:-2px !important; margin-right:4px !important;}
1128 1128
1129 1129
1130 1130 /************* CodeRay styles *************/
1131 1131 .syntaxhl div {display: inline;}
1132 1132 .syntaxhl .code pre { overflow: auto }
1133 1133
1134 1134 .syntaxhl .annotation { color:#007 }
1135 1135 .syntaxhl .attribute-name { color:#b48 }
1136 1136 .syntaxhl .attribute-value { color:#700 }
1137 1137 .syntaxhl .binary { color:#549 }
1138 1138 .syntaxhl .binary .char { color:#325 }
1139 1139 .syntaxhl .binary .delimiter { color:#325 }
1140 1140 .syntaxhl .char { color:#D20 }
1141 1141 .syntaxhl .char .content { color:#D20 }
1142 1142 .syntaxhl .char .delimiter { color:#710 }
1143 1143 .syntaxhl .class { color:#258; font-weight:bold }
1144 1144 .syntaxhl .class-variable { color:#369 }
1145 1145 .syntaxhl .color { color:#0A0 }
1146 1146 .syntaxhl .comment { color:#385 }
1147 1147 .syntaxhl .comment .char { color:#385 }
1148 1148 .syntaxhl .comment .delimiter { color:#385 }
1149 1149 .syntaxhl .constant { color:#258; font-weight:bold }
1150 1150 .syntaxhl .decorator { color:#B0B }
1151 1151 .syntaxhl .definition { color:#099; font-weight:bold }
1152 1152 .syntaxhl .delimiter { color:black }
1153 1153 .syntaxhl .directive { color:#088; font-weight:bold }
1154 1154 .syntaxhl .docstring { color:#D42; }
1155 1155 .syntaxhl .doctype { color:#34b }
1156 1156 .syntaxhl .done { text-decoration: line-through; color: gray }
1157 1157 .syntaxhl .entity { color:#800; font-weight:bold }
1158 1158 .syntaxhl .error { color:#F00; background-color:#FAA }
1159 1159 .syntaxhl .escape { color:#666 }
1160 1160 .syntaxhl .exception { color:#C00; font-weight:bold }
1161 1161 .syntaxhl .float { color:#06D }
1162 1162 .syntaxhl .function { color:#06B; font-weight:bold }
1163 1163 .syntaxhl .function .delimiter { color:#024; font-weight:bold }
1164 1164 .syntaxhl .global-variable { color:#d70 }
1165 1165 .syntaxhl .hex { color:#02b }
1166 1166 .syntaxhl .id { color:#33D; font-weight:bold }
1167 1167 .syntaxhl .include { color:#B44; font-weight:bold }
1168 1168 .syntaxhl .inline { background-color: hsla(0,0%,0%,0.07); color: black }
1169 1169 .syntaxhl .inline-delimiter { font-weight: bold; color: #666 }
1170 1170 .syntaxhl .instance-variable { color:#33B }
1171 1171 .syntaxhl .integer { color:#06D }
1172 1172 .syntaxhl .imaginary { color:#f00 }
1173 1173 .syntaxhl .important { color:#D00 }
1174 1174 .syntaxhl .key { color: #606 }
1175 1175 .syntaxhl .key .char { color: #60f }
1176 1176 .syntaxhl .key .delimiter { color: #404 }
1177 1177 .syntaxhl .keyword { color:#939; font-weight:bold }
1178 1178 .syntaxhl .label { color:#970; font-weight:bold }
1179 1179 .syntaxhl .local-variable { color:#950 }
1180 1180 .syntaxhl .map .content { color:#808 }
1181 1181 .syntaxhl .map .delimiter { color:#40A}
1182 1182 .syntaxhl .map { background-color:hsla(200,100%,50%,0.06); }
1183 1183 .syntaxhl .namespace { color:#707; font-weight:bold }
1184 1184 .syntaxhl .octal { color:#40E }
1185 1185 .syntaxhl .operator { }
1186 1186 .syntaxhl .predefined { color:#369; font-weight:bold }
1187 1187 .syntaxhl .predefined-constant { color:#069 }
1188 1188 .syntaxhl .predefined-type { color:#0a8; font-weight:bold }
1189 1189 .syntaxhl .preprocessor { color:#579 }
1190 1190 .syntaxhl .pseudo-class { color:#00C; font-weight:bold }
1191 1191 .syntaxhl .regexp { background-color:hsla(300,100%,50%,0.06); }
1192 1192 .syntaxhl .regexp .content { color:#808 }
1193 1193 .syntaxhl .regexp .delimiter { color:#404 }
1194 1194 .syntaxhl .regexp .modifier { color:#C2C }
1195 1195 .syntaxhl .reserved { color:#080; font-weight:bold }
1196 1196 .syntaxhl .shell { background-color:hsla(120,100%,50%,0.06); }
1197 1197 .syntaxhl .shell .content { color:#2B2 }
1198 1198 .syntaxhl .shell .delimiter { color:#161 }
1199 1199 .syntaxhl .string .char { color: #46a }
1200 1200 .syntaxhl .string .content { color: #46a }
1201 1201 .syntaxhl .string .delimiter { color: #46a }
1202 1202 .syntaxhl .string .modifier { color: #46a }
1203 1203 .syntaxhl .symbol { color:#d33 }
1204 1204 .syntaxhl .symbol .content { color:#d33 }
1205 1205 .syntaxhl .symbol .delimiter { color:#d33 }
1206 1206 .syntaxhl .tag { color:#070; font-weight:bold }
1207 1207 .syntaxhl .type { color:#339; font-weight:bold }
1208 1208 .syntaxhl .value { color: #088 }
1209 1209 .syntaxhl .variable { color:#037 }
1210 1210
1211 1211 .syntaxhl .insert { background: hsla(120,100%,50%,0.12) }
1212 1212 .syntaxhl .delete { background: hsla(0,100%,50%,0.12) }
1213 1213 .syntaxhl .change { color: #bbf; background: #007 }
1214 1214 .syntaxhl .head { color: #f8f; background: #505 }
1215 1215 .syntaxhl .head .filename { color: white; }
1216 1216
1217 1217 .syntaxhl .delete .eyecatcher { background-color: hsla(0,100%,50%,0.2); border: 1px solid hsla(0,100%,45%,0.5); margin: -1px; border-bottom: none; border-top-left-radius: 5px; border-top-right-radius: 5px; }
1218 1218 .syntaxhl .insert .eyecatcher { background-color: hsla(120,100%,50%,0.2); border: 1px solid hsla(120,100%,25%,0.5); margin: -1px; border-top: none; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; }
1219 1219
1220 1220 .syntaxhl .insert .insert { color: #0c0; background:transparent; font-weight:bold }
1221 1221 .syntaxhl .delete .delete { color: #c00; background:transparent; font-weight:bold }
1222 1222 .syntaxhl .change .change { color: #88f }
1223 1223 .syntaxhl .head .head { color: #f4f }
1224 1224
1225 1225 /***** Media print specific styles *****/
1226 1226 @media print {
1227 1227 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
1228 1228 #main { background: #fff; }
1229 1229 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
1230 1230 #wiki_add_attachment { display:none; }
1231 1231 .hide-when-print { display: none; }
1232 1232 .autoscroll {overflow-x: visible;}
1233 1233 table.list {margin-top:0.5em;}
1234 1234 table.list th, table.list td {border: 1px solid #aaa;}
1235 1235 }
1236 1236
1237 1237 /* Accessibility specific styles */
1238 1238 .hidden-for-sighted {
1239 1239 position:absolute;
1240 1240 left:-10000px;
1241 1241 top:auto;
1242 1242 width:1px;
1243 1243 height:1px;
1244 1244 overflow:hidden;
1245 1245 }
General Comments 0
You need to be logged in to leave comments. Login now