Feature #12122 » gantt-progress-line-html-r10659.diff
| app/views/gantts/show.html.erb | ||
|---|---|---|
| 1 |
<% content_for :header_tags do %> |
|
| 2 |
<%= javascript_include_tag 'raphael' %> |
|
| 3 |
<%= javascript_include_tag 'gantt_progress_line' %> |
|
| 4 |
<% end %> |
|
| 5 |
<%= javascript_tag do %> |
|
| 6 |
$(document).ready(drawGanttLinesHandler); |
|
| 7 |
$(window).resize(drawGanttLinesHandler); |
|
| 8 |
<% end %> |
|
| 1 | 9 |
<% @gantt.view = self %> |
| 2 | 10 |
<h2><%= @query.new_record? ? l(:label_gantt) : h(@query.name) %></h2> |
| 3 | 11 | |
| ... | ... | |
| 228 | 236 |
style += "width:10px;" |
| 229 | 237 |
style += "border-left: 1px dashed red;" |
| 230 | 238 |
%> |
| 231 |
<%= content_tag(:div, ' '.html_safe, :style => style) %> |
|
| 239 |
<%= content_tag(:div, ' '.html_safe, :style => style, :id => 'today_red_line') %>
|
|
| 232 | 240 |
<% end %> |
| 233 | ||
| 241 |
<% |
|
| 242 |
style = "" |
|
| 243 |
style += "position: absolute;" |
|
| 244 |
style += "height: #{g_height}px;"
|
|
| 245 |
style += "top: #{headers_height + 1}px;"
|
|
| 246 |
style += "left: 0px;" |
|
| 247 |
style += "width: #{g_width - 1}px;"
|
|
| 248 |
%> |
|
| 249 |
<%= content_tag(:div, '', :style => style, :id => "gantt_lines") %> |
|
| 234 | 250 |
</div> |
| 235 | 251 |
</td> |
| 236 | 252 |
</tr> |
| lib/redmine/helpers/gantt.rb | ||
|---|---|---|
| 288 | 288 |
html_class << 'icon icon-package ' |
| 289 | 289 |
html_class << (version.behind_schedule? ? 'version-behind-schedule' : '') << " " |
| 290 | 290 |
html_class << (version.overdue? ? 'version-overdue' : '') |
| 291 |
html_class << ' version-closed' unless version.open? |
|
| 292 |
if version.start_date && version.due_date && version.completed_pourcent |
|
| 293 |
progress_date = calc_progress_date(version.start_date, |
|
| 294 |
version.due_date, version.completed_pourcent) |
|
| 295 |
html_class << ' behind-start-date' if progress_date < self.date_from |
|
| 296 |
html_class << ' over-end-date' if progress_date > self.date_to |
|
| 297 |
end |
|
| 291 | 298 |
s = view.link_to_version(version).html_safe |
| 292 | 299 |
subject = view.content_tag(:span, s, |
| 293 | 300 |
:class => html_class).html_safe |
| 294 |
html_subject(options, subject, :css => "version-name") |
|
| 301 |
html_subject(options, subject, :css => "version-name", |
|
| 302 |
:id => "version-#{version.id}")
|
|
| 295 | 303 |
when :image |
| 296 | 304 |
image_subject(options, version.to_s_with_project) |
| 297 | 305 |
when :pdf |
| ... | ... | |
| 312 | 320 |
label = h("#{version.project} -") + label unless @project && @project == version.project
|
| 313 | 321 |
case options[:format] |
| 314 | 322 |
when :html |
| 315 |
html_task(options, coords, :css => "version task", :label => label, :markers => true) |
|
| 323 |
html_task(options, coords, :css => "version task", |
|
| 324 |
:label => label, :markers => true, :version => version) |
|
| 316 | 325 |
when :image |
| 317 | 326 |
image_task(options, coords, :label => label, :markers => true, :height => 3) |
| 318 | 327 |
when :pdf |
| ... | ... | |
| 335 | 344 |
css_classes << ' issue-overdue' if issue.overdue? |
| 336 | 345 |
css_classes << ' issue-behind-schedule' if issue.behind_schedule? |
| 337 | 346 |
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to |
| 347 |
css_classes << ' issue-closed' if issue.closed? |
|
| 348 |
if issue.start_date && issue.due_before && issue.done_ratio |
|
| 349 |
progress_date = calc_progress_date(issue.start_date, |
|
| 350 |
issue.due_before, issue.done_ratio) |
|
| 351 |
css_classes << ' behind-start-date' if progress_date < self.date_from |
|
| 352 |
css_classes << ' over-end-date' if progress_date > self.date_to |
|
| 353 |
end |
|
| 338 | 354 |
s = "".html_safe |
| 339 | 355 |
if issue.assigned_to.present? |
| 340 | 356 |
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name |
| ... | ... | |
| 346 | 362 |
s << view.link_to_issue(issue).html_safe |
| 347 | 363 |
subject = view.content_tag(:span, s, :class => css_classes).html_safe |
| 348 | 364 |
html_subject(options, subject, :css => "issue-subject", |
| 349 |
:title => issue.subject) + "\n" |
|
| 365 |
:title => issue.subject, :id => "issue-#{issue.id}") + "\n"
|
|
| 350 | 366 |
when :image |
| 351 | 367 |
image_subject(options, issue.subject) |
| 352 | 368 |
when :pdf |
| ... | ... | |
| 609 | 625 |
coords[:bar_end] = self.date_to - self.date_from + 1 |
| 610 | 626 |
end |
| 611 | 627 |
if progress |
| 612 |
progress_date = start_date + (end_date - start_date + 1) * (progress / 100.0)
|
|
| 628 |
progress_date = calc_progress_date(start_date, end_date, progress)
|
|
| 613 | 629 |
if progress_date > self.date_from && progress_date > start_date |
| 614 | 630 |
if progress_date < self.date_to |
| 615 | 631 |
coords[:bar_progress_end] = progress_date - self.date_from |
| ... | ... | |
| 636 | 652 |
coords |
| 637 | 653 |
end |
| 638 | 654 | |
| 655 |
def calc_progress_date(start_date, end_date, progress) |
|
| 656 |
start_date + (end_date - start_date + 1) * (progress / 100.0) |
|
| 657 |
end |
|
| 658 | ||
| 639 | 659 |
# Sorts a collection of issues by start_date, due_date, id for gantt rendering |
| 640 | 660 |
def sort_issues!(issues) |
| 641 | 661 |
issues.sort! { |a, b| gantt_issue_compare(a, b, issues) }
|
| ... | ... | |
| 676 | 696 |
def html_subject(params, subject, options={})
|
| 677 | 697 |
style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
|
| 678 | 698 |
style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
|
| 679 |
output = view.content_tag('div', subject,
|
|
| 699 |
output = view.content_tag(:div, subject,
|
|
| 680 | 700 |
:class => options[:css], :style => style, |
| 681 |
:title => options[:title]) |
|
| 701 |
:title => options[:title], |
|
| 702 |
:id => options[:id]) |
|
| 682 | 703 |
@subjects << output |
| 683 | 704 |
output |
| 684 | 705 |
end |
| ... | ... | |
| 712 | 733 |
style << "top:#{params[:top]}px;"
|
| 713 | 734 |
style << "left:#{coords[:bar_start]}px;"
|
| 714 | 735 |
style << "width:#{width}px;"
|
| 736 |
html_id = "task-todo-issue-#{options[:issue].id}" if options[:issue]
|
|
| 737 |
html_id = "task-todo-version-#{options[:version].id}" if options[:version]
|
|
| 715 | 738 |
output << view.content_tag(:div, ' '.html_safe, |
| 716 | 739 |
:style => style, |
| 717 |
:class => "#{options[:css]} task_todo")
|
|
| 740 |
:class => "#{options[:css]} task_todo",
|
|
| 741 |
:id => html_id) |
|
| 718 | 742 |
if coords[:bar_late_end] |
| 719 | 743 |
width = coords[:bar_late_end] - coords[:bar_start] - 2 |
| 720 | 744 |
style = "" |
| ... | ... | |
| 731 | 755 |
style << "top:#{params[:top]}px;"
|
| 732 | 756 |
style << "left:#{coords[:bar_start]}px;"
|
| 733 | 757 |
style << "width:#{width}px;"
|
| 758 |
html_id = "task-done-issue-#{options[:issue].id}" if options[:issue]
|
|
| 759 |
html_id = "task-done-version-#{options[:version].id}" if options[:version]
|
|
| 734 | 760 |
output << view.content_tag(:div, ' '.html_safe, |
| 735 | 761 |
:style => style, |
| 736 |
:class => "#{options[:css]} task_done")
|
|
| 762 |
:class => "#{options[:css]} task_done",
|
|
| 763 |
:id => html_id) |
|
| 737 | 764 |
end |
| 738 | 765 |
end |
| 739 | 766 |
# Renders the markers |
| public/javascripts/gantt_progress_line.js | ||
|---|---|---|
| 1 |
var draw_gantt_line = null; |
|
| 2 | ||
| 3 |
function drawGanttLines(holder) {
|
|
| 4 |
if(draw_gantt_line != null) |
|
| 5 |
draw_gantt_line.clear(); |
|
| 6 |
else |
|
| 7 |
draw_gantt_line = Raphael(holder); |
|
| 8 |
var arr = new Array(); |
|
| 9 |
var today_left = $('#today_red_line').position().left;
|
|
| 10 |
arr.push({left: today_left, top: 0});
|
|
| 11 |
var line_top = $("#gantt_lines").position().top;
|
|
| 12 |
var line_right = $("#gantt_lines").width();
|
|
| 13 |
$.each($('div.issue-subject, div.version-name'), function(index, element) {
|
|
| 14 |
var t0 = $(element).position().top - line_top ; |
|
| 15 |
var h = ($(element).height() / 9); |
|
| 16 |
var t1 = t0 - h; |
|
| 17 |
var t2 = t0 + (h * 8); |
|
| 18 |
var t = t0 + (h * 3); |
|
| 19 |
var ci = $(element).children('span').hasClass('issue-closed');
|
|
| 20 |
var cv = $(element).children('span').hasClass('version-closed');
|
|
| 21 |
if (ci || cv) {
|
|
| 22 |
arr.push({left: today_left, top: t});
|
|
| 23 |
} else {
|
|
| 24 |
var issue_done = $("#task-done-" + $(element).attr("id"));
|
|
| 25 |
var is_behind_start = $(element).children('span').hasClass('behind-start-date');
|
|
| 26 |
var is_over_end = $(element).children('span').hasClass('over-end-date');
|
|
| 27 |
if (is_over_end) {
|
|
| 28 |
arr.push({left: line_right, top: t1, is_right_edge: true});
|
|
| 29 |
arr.push({left: line_right, top: t2, is_right_edge: true, none_stroke: true});
|
|
| 30 |
} else if (issue_done.size() > 0) {
|
|
| 31 |
var l = issue_done.first().position().left + |
|
| 32 |
issue_done.first().width(); |
|
| 33 |
arr.push({left: l, top: t});
|
|
| 34 |
} else if (is_behind_start) {
|
|
| 35 |
arr.push({left: 0 , top: t1, is_left_edge: true});
|
|
| 36 |
arr.push({left: 0 , top: t2, is_left_edge: true, none_stroke: true});
|
|
| 37 |
} else {
|
|
| 38 |
var l1 = today_left; |
|
| 39 |
var issue_todo = $("#task-todo-" + $(element).attr("id"));
|
|
| 40 |
if (issue_todo.size() > 0){
|
|
| 41 |
l1 = issue_todo.first().position().left; |
|
| 42 |
} |
|
| 43 |
arr.push({left: Math.min(today_left, l1), top: t});
|
|
| 44 |
} |
|
| 45 |
} |
|
| 46 |
}); |
|
| 47 |
var i; |
|
| 48 |
for(i = 1 ; i < arr.length ; i++) {
|
|
| 49 |
if (!("none_stroke" in arr[i]) &&
|
|
| 50 |
(!("is_right_edge" in arr[i - 1] && "is_right_edge" in arr[i]) &&
|
|
| 51 |
!("is_left_edge" in arr[i - 1] && "is_left_edge" in arr[i]))
|
|
| 52 |
) {
|
|
| 53 |
draw_gantt_line.path(["M",arr[i - 1].left, arr[i - 1].top, "L", arr[i].left, arr[i].top]) |
|
| 54 |
.attr({stroke: "#ff0000", "stroke-width": 2});
|
|
| 55 |
} |
|
| 56 |
} |
|
| 57 |
} |
|
| 58 | ||
| 59 |
function drawGanttLinesHandler() {
|
|
| 60 |
drawGanttLines(document.getElementById('gantt_lines'));
|
|
| 61 |
} |
|