houston-roadmaps 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/Gemfile +24 -0
  4. data/Gemfile.lock +358 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +31 -0
  7. data/Rakefile +25 -0
  8. data/app/assets/images/houston/roadmaps/.gitkeep +0 -0
  9. data/app/assets/javascripts/houston/roadmaps/app.coffee +2 -0
  10. data/app/assets/javascripts/houston/roadmaps/application.js +14 -0
  11. data/app/assets/javascripts/houston/roadmaps/handlebars_helpers.coffee +22 -0
  12. data/app/assets/javascripts/houston/roadmaps/jquery_extensions.coffee +4 -0
  13. data/app/assets/javascripts/houston/roadmaps/models/goal.coffee +11 -0
  14. data/app/assets/javascripts/houston/roadmaps/models/milestone.coffee +124 -0
  15. data/app/assets/javascripts/houston/roadmaps/models/roadmap.coffee +16 -0
  16. data/app/assets/javascripts/houston/roadmaps/models/viewport.coffee +3 -0
  17. data/app/assets/javascripts/houston/roadmaps/resize_listener.js +67 -0
  18. data/app/assets/javascripts/houston/roadmaps/views/_gantt_chart.coffee +224 -0
  19. data/app/assets/javascripts/houston/roadmaps/views/_show_milestone_view.coffee +204 -0
  20. data/app/assets/javascripts/houston/roadmaps/views/edit_gantt_chart.coffee +405 -0
  21. data/app/assets/javascripts/houston/roadmaps/views/edit_milestone_view.coffee +97 -0
  22. data/app/assets/javascripts/houston/roadmaps/views/edit_roadmap_view.coffee +80 -0
  23. data/app/assets/javascripts/houston/roadmaps/views/gantt_thumbnail_view.coffee +173 -0
  24. data/app/assets/javascripts/houston/roadmaps/views/goal_view.coffee +18 -0
  25. data/app/assets/javascripts/houston/roadmaps/views/project_goals_view.coffee +30 -0
  26. data/app/assets/javascripts/houston/roadmaps/views/roadmap_history_view.coffee +67 -0
  27. data/app/assets/javascripts/houston/roadmaps/views/roadmap_view.coffee +12 -0
  28. data/app/assets/javascripts/houston/roadmaps/views/roadmaps_view.coffee +29 -0
  29. data/app/assets/stylesheets/houston/roadmaps/application.css +13 -0
  30. data/app/assets/stylesheets/houston/roadmaps/colors.scss.erb +41 -0
  31. data/app/assets/stylesheets/houston/roadmaps/goals.scss +20 -0
  32. data/app/assets/stylesheets/houston/roadmaps/milestone.scss +58 -0
  33. data/app/assets/stylesheets/houston/roadmaps/roadmap.scss +276 -0
  34. data/app/assets/stylesheets/houston/roadmaps/roadmap_history.scss +42 -0
  35. data/app/assets/stylesheets/houston/roadmaps/roadmaps.scss +89 -0
  36. data/app/assets/templates/houston/roadmaps/goals/edit.hbs +6 -0
  37. data/app/assets/templates/houston/roadmaps/goals/index.hbs +9 -0
  38. data/app/assets/templates/houston/roadmaps/goals/new.hbs +27 -0
  39. data/app/assets/templates/houston/roadmaps/goals/show.hbs +6 -0
  40. data/app/assets/templates/houston/roadmaps/milestone/edit.hbs +67 -0
  41. data/app/assets/templates/houston/roadmaps/milestone/show.hbs +53 -0
  42. data/app/assets/templates/houston/roadmaps/milestone/ticket.hbs +28 -0
  43. data/app/assets/templates/houston/roadmaps/roadmap/goal.hbs +6 -0
  44. data/app/assets/templates/houston/roadmaps/roadmap/history.hbs +22 -0
  45. data/app/assets/templates/houston/roadmaps/roadmap/show.hbs +12 -0
  46. data/app/assets/templates/houston/roadmaps/roadmaps/index.hbs +2 -0
  47. data/app/assets/templates/houston/roadmaps/roadmaps/show.hbs +11 -0
  48. data/app/controllers/houston/roadmaps/api/v1/roadmap_controller.rb +20 -0
  49. data/app/controllers/houston/roadmaps/dashboard_controller.rb +34 -0
  50. data/app/controllers/houston/roadmaps/milestones_controller.rb +118 -0
  51. data/app/controllers/houston/roadmaps/project_goals_controller.rb +24 -0
  52. data/app/controllers/houston/roadmaps/roadmap_milestones_controller.rb +36 -0
  53. data/app/controllers/houston/roadmaps/roadmaps_controller.rb +77 -0
  54. data/app/helpers/houston/roadmaps/application_helper.rb +4 -0
  55. data/app/models/roadmap.rb +9 -0
  56. data/app/models/roadmap_commit.rb +35 -0
  57. data/app/models/roadmap_milestone.rb +29 -0
  58. data/app/models/roadmap_milestone_version.rb +8 -0
  59. data/app/presenters/houston/roadmaps/milestone_api_presenter.rb +27 -0
  60. data/app/presenters/houston/roadmaps/milestone_presenter.rb +74 -0
  61. data/app/presenters/houston/roadmaps/roadmap_milestone_presenter.rb +70 -0
  62. data/app/presenters/houston/roadmaps/roadmap_presenter.rb +37 -0
  63. data/app/presenters/houston/roadmaps/ticket_presenter.rb +20 -0
  64. data/app/views/houston/roadmaps/dashboard/show.html.erb +42 -0
  65. data/app/views/houston/roadmaps/milestones/show.html.erb +40 -0
  66. data/app/views/houston/roadmaps/project_goals/index.html.erb +42 -0
  67. data/app/views/houston/roadmaps/roadmaps/_form.html.erb +29 -0
  68. data/app/views/houston/roadmaps/roadmaps/edit.html.erb +8 -0
  69. data/app/views/houston/roadmaps/roadmaps/history.html.erb +41 -0
  70. data/app/views/houston/roadmaps/roadmaps/index.html.erb +24 -0
  71. data/app/views/houston/roadmaps/roadmaps/new.html.erb +7 -0
  72. data/app/views/houston/roadmaps/roadmaps/show.html.erb +27 -0
  73. data/app/views/houston/roadmaps/roadmaps/show_editable.html.erb +35 -0
  74. data/app/views/layouts/houston/roadmaps/application.html.erb +10 -0
  75. data/app/views/layouts/houston/roadmaps/dashboard.html.erb +9 -0
  76. data/bin/rails +8 -0
  77. data/config/database.yml +13 -0
  78. data/config/initializers/add_navigation_renderer.rb +13 -0
  79. data/config/routes.rb +37 -0
  80. data/db/.keep +0 -0
  81. data/db/migrate/20140831210254_add_band_to_milestones.rb +5 -0
  82. data/db/migrate/20140907212311_add_end_date_to_milestones.rb +19 -0
  83. data/db/migrate/20140916230539_add_locked_to_milestone.rb +5 -0
  84. data/db/migrate/20140927154728_add_closed_ticket_count_to_milestones.rb +11 -0
  85. data/db/migrate/20140929024130_create_roadmap_commits.rb +29 -0
  86. data/db/migrate/20141012023628_add_user_id_to_milestone_versions.rb +11 -0
  87. data/db/migrate/20150102192805_add_lanes_to_milestones.rb +5 -0
  88. data/db/migrate/20150119155145_add_goal_and_feedback_query_to_milestones.rb +6 -0
  89. data/db/migrate/20150524203903_add_project_id_to_roadmap_commits.rb +17 -0
  90. data/db/migrate/20150603203744_add_timestamps_to_roadmap_commits.rb +20 -0
  91. data/db/migrate/20160206214746_rename_roadmap_feature_to_milestones.rb +25 -0
  92. data/db/migrate/20160207154530_create_roadmaps.rb +125 -0
  93. data/db/structure.sql +2557 -0
  94. data/houston-roadmaps.gemspec +29 -0
  95. data/lib/houston-roadmaps.rb +1 -0
  96. data/lib/houston/roadmaps.rb +15 -0
  97. data/lib/houston/roadmaps/configuration.rb +14 -0
  98. data/lib/houston/roadmaps/engine.rb +27 -0
  99. data/lib/houston/roadmaps/milestone_ext.rb +14 -0
  100. data/lib/houston/roadmaps/project_ext.rb +20 -0
  101. data/lib/houston/roadmaps/railtie.rb +17 -0
  102. data/lib/houston/roadmaps/version.rb +5 -0
  103. data/lib/tasks/roadmap_tasks.rake +4 -0
  104. data/test/acceptance/houston_dummy_test.rb +17 -0
  105. data/test/dummy/houston.rb +23 -0
  106. data/test/fixtures/projects.yml +3 -0
  107. data/test/fixtures/users.yml +10 -0
  108. data/test/test_helper.rb +43 -0
  109. data/test/unit/fixtures_test.rb +11 -0
  110. metadata +227 -0
@@ -0,0 +1,97 @@
1
+ class Roadmaps.EditMilestoneView extends Roadmaps.ShowMilestoneView
2
+ template: HandlebarsTemplates['houston/roadmaps/milestone/edit']
3
+
4
+ events:
5
+ 'click .remove-button': 'removeTicket'
6
+ 'click .inline-editable-link': 'startInlineEdit'
7
+ 'click .btn-cancel': 'cancelInlineEdit'
8
+ 'click #save_goal': 'saveGoal'
9
+ 'click #save_feedback_query': 'saveFeedbackQuery'
10
+
11
+ initialize: ->
12
+ @openTickets = @options.openTickets
13
+ super
14
+
15
+ render: ->
16
+ super
17
+
18
+ @newTicketView = new FindOrCreateTicketView
19
+ ticketTracker: @projectTicketTracker
20
+ tickets: @openTickets
21
+ addTicket: _.bind(@addTicket, @)
22
+ createTicket: _.bind(@createTicket, @)
23
+ @$el.find('#find_or_create_ticket_view').appendView @newTicketView
24
+
25
+ $('#tickets').sortable
26
+ handle: '.ticket-handle'
27
+ helper: (e, ui)-> ui.children().each(-> $(@).width $(@).width()); ui
28
+ stop: (e, ui)-> ui.item.children().css('width', '')
29
+ update: (e, ui)->
30
+ $tickets = $('#tickets .ticket')
31
+ ids = _.map $tickets, (el)-> +$(el).attr('data-id')
32
+ $.put "#{window.location.pathname}/ticket_order", {order: ids}
33
+ @
34
+
35
+
36
+
37
+ addTicket: (ticket)->
38
+ $.post("/roadmap/milestones/#{@id}/tickets/#{ticket.id}")
39
+ .success => @_addTicket(ticket)
40
+
41
+ createTicket: (summary)->
42
+ $.post("/roadmap/milestones/#{@id}/tickets", {summary: summary})
43
+ .success (ticket)=> @_addTicket(ticket)
44
+
45
+ _addTicket: (ticket)->
46
+ unless @tickets.get(ticket.id)
47
+ @tickets.push new Ticket(ticket)
48
+ @rerenderTickets()
49
+ @renderBurndownChart(@tickets.models)
50
+ $(".ticket[data-id=#{ticket.id}]").highlight()
51
+
52
+
53
+
54
+ removeTicket: (e)->
55
+ e.preventDefault()
56
+ e.stopImmediatePropagation()
57
+ $button = $(e.target)
58
+ $button.prop 'disabled', true
59
+ $ticket = $button.closest('.ticket')
60
+ $ticket.addClass('deleting')
61
+ id = +$ticket.attr('data-id')
62
+ $.destroy("/roadmap/milestones/#{@id}/tickets/#{id}")
63
+ .error =>
64
+ $button.prop 'disabled', false
65
+ $ticket.removeClass('deleting')
66
+ .success =>
67
+ @tickets.remove @tickets.get(id)
68
+ $ticket.remove()
69
+
70
+
71
+
72
+ startInlineEdit: (e)->
73
+ $(e.target).closest('.inline-editable')
74
+ .addClass('in-edit')
75
+ .find('input, textarea').focus()
76
+
77
+ cancelInlineEdit: (e)->
78
+ e.preventDefault()
79
+ $(e.target).closest('.inline-editable').removeClass('in-edit')
80
+
81
+ saveGoal: (e)->
82
+ goal = $('#goal').val()
83
+ $.put("/roadmap/milestones/#{@id}", milestone: {goal: goal})
84
+ .success =>
85
+ @milestone.goal = goal
86
+ @render()
87
+ .error (response)=>
88
+ alertify.error(response.responseText)
89
+
90
+ saveFeedbackQuery: (e)->
91
+ feedbackQuery = $('#feedback_query').val()
92
+ $.put("/roadmap/milestones/#{@id}", milestone: {feedback_query: feedbackQuery})
93
+ .success =>
94
+ @milestone.feedbackQuery = feedbackQuery
95
+ @render()
96
+ .error (response)=>
97
+ alertify.error(response.responseText)
@@ -0,0 +1,80 @@
1
+ class Roadmaps.EditRoadmapView extends Backbone.View
2
+ template: HandlebarsTemplates['houston/roadmaps/roadmap/show']
3
+ renderGoal: HandlebarsTemplates['houston/roadmaps/roadmap/goal']
4
+
5
+ initialize: ->
6
+ super
7
+ @roadmapId = @options.id
8
+ @milestonesUrl = "/roadmaps/#{@roadmapId}/milestones"
9
+ @milestones = @options.milestones
10
+ @goals = @options.goals
11
+
12
+ $('#reset_roadmap').click _.bind(@reset, @)
13
+ $('#save_roadmap').click _.bind(@save, @)
14
+
15
+
16
+ @milestones.bind 'change', @milestoneChanged, @
17
+ @milestones.bind 'add', @milestoneChanged, @
18
+ @milestones.bind 'remove', @milestoneChanged, @
19
+
20
+ render: ->
21
+ @$el.html @template()
22
+ @$goals = @$el.find('#goals')
23
+ @$goals_view = @$el.find('#goals_view')
24
+ @roadmap = new Roadmaps.EditGanttChart @milestones,
25
+ removeMilestoneByDroppingOn: @$goals_view
26
+ showWeekends: true
27
+ @roadmap.render()
28
+ @milestoneChanged()
29
+
30
+ setTop = =>
31
+ roadmapBottom = $('#roadmap').position().top + $('#roadmap').height() - 10
32
+ @$goals_view.css(top: roadmapBottom)
33
+ setTop()
34
+ $(window).resize(setTop)
35
+ addResizeListener $('#roadmap')[0], setTop
36
+
37
+ @
38
+
39
+ milestoneChanged: ->
40
+ @indicateIfRoadmapHasChanged()
41
+ @renderGoals()
42
+
43
+ indicateIfRoadmapHasChanged: ->
44
+ @$el.find('.buttons button')
45
+ .prop 'disabled', @milestones.changes().length is 0
46
+
47
+ renderGoals: ->
48
+ html = ""
49
+ @goals.outsideOf(@milestones.unremoved()).each (goal) =>
50
+ html += @renderGoal goal.toJSON()
51
+ @$goals.html(html)
52
+
53
+ @roadmap.dragFrom @$goals.find('.goal'), @goals
54
+ @
55
+
56
+
57
+
58
+ reset: (e)->
59
+ e.preventDefault()
60
+ @milestones.revert()
61
+
62
+ save: (e)->
63
+ e.preventDefault()
64
+ if message = prompt('Commit message:')
65
+ $buttons = $('#reset_roadmap, #save_roadmap')
66
+ changes = @milestones.changes()
67
+ $buttons.prop('disabled', true)
68
+ $.put(@milestonesUrl,
69
+ roadmap: changes
70
+ message: message)
71
+ .success =>
72
+ $buttons.prop('disabled', false)
73
+ @milestones.url = @milestonesUrl
74
+ @milestones.fetch
75
+ parse: true
76
+ wait: true
77
+ .error (response)->
78
+ $buttons.prop('disabled', false)
79
+ errors = Errors.fromResponse(response)
80
+ errors.renderToAlert()
@@ -0,0 +1,173 @@
1
+ class Roadmaps.ThumbnailGanttChart
2
+ today: new Date()
3
+
4
+ constructor: (options)->
5
+ @milestones = options.milestones
6
+ @parent = options.parent
7
+ @viewport = options.viewport
8
+ @showToday = options.showToday
9
+ @markers = options.markers
10
+ @milestones.bind 'add', @update, @
11
+ @milestones.bind 'remove', @update, @
12
+ @milestones.bind 'change', @update, @
13
+ @milestones.bind 'reset', @update, @
14
+ $(window).resize (e)=>
15
+ @updateWindow() if e.target is window
16
+
17
+ render: ->
18
+ @height = 46
19
+ @graphHeight = @height - 18
20
+
21
+ el = @parent.append('div')
22
+ .attr('class', 'roadmap-thumbnail')
23
+ .attr('style', "position: relative; height: #{@height}px;")
24
+
25
+ svg = el.append('svg')
26
+ .attr('height', @height)
27
+ .append('g')
28
+ .attr('transform', "translate(0,0)")
29
+
30
+ @roadmap = svg.append('g')
31
+ .attr('class', 'roadmap-bands')
32
+
33
+ @viewer = el.selectAll('.roadmap-thumbnail-viewer')
34
+ .data([@viewport])
35
+
36
+ view = @
37
+ @viewer.enter()
38
+ .append('div')
39
+ .attr('class', 'roadmap-thumbnail-viewer')
40
+ .attr('style', "top: 1px; height: #{@height - 2}px; left: 1px;")
41
+ .each ->
42
+ $(@).draggable
43
+ axis: 'x'
44
+ containment: 'parent'
45
+ drag: (e, ui)->
46
+ start = view.x.invert(ui.position.left)
47
+ offset = start - view.viewport.get('start')
48
+ end = new Date(+view.viewport.get('end') + offset)
49
+ view.viewport.set(start: start, end: end)
50
+ .resizable
51
+ handles: 'e'
52
+ containment: 'parent'
53
+ resize: (e, ui)->
54
+ end = view.x.invert(ui.position.left + ui.size.width)
55
+ view.viewport.set(end: end)
56
+
57
+ @xAxis = svg.append('g')
58
+ .attr('class', 'x axis')
59
+ .attr('transform', "translate(0,#{@graphHeight})")
60
+
61
+ @updateWindow()
62
+
63
+ updateWindow: ->
64
+ width = $('#roadmap').width() || 960
65
+ return if @width is width
66
+ @width = width
67
+
68
+ @parent.select('.roadmap-thumbnail').transition(150).attr('width', @width)
69
+
70
+ @update()
71
+
72
+ update: ->
73
+ visibleMilestones = _.select @milestones.toJSON(), (m)-> m.startDate and m.endDate and !m.removed
74
+
75
+ if visibleMilestones.length > 0
76
+ @startDate = 3.weeks().before d3.min(visibleMilestones, (m)-> m.startDate)
77
+ @endDate = 12.weeks().after d3.max(visibleMilestones, (m)-> m.endDate)
78
+ else
79
+ @startDate = 3.weeks().ago()
80
+ @endDate = 12.weeks().fromNow()
81
+
82
+ @startDate = @viewport.get('start') if @viewport.get('start') < @startDate
83
+ @endDate = @viewport.get('end') if @viewport.get('end') > @endDate
84
+
85
+ @x = d3.time.scale()
86
+ .domain([@startDate, @endDate])
87
+ .range([0, @width])
88
+ timeline = d3.svg.axis()
89
+ .scale(@x)
90
+ .orient('bottom')
91
+ .innerTickSize(4)
92
+ @xAxis.transition(150).call(timeline)
93
+
94
+ @viewer.attr('style', (viewport)=> "top: 1px; height: #{@height - 2}px; left: #{@x(viewport.get('start'))}px; width: #{@x(viewport.get('end')) - @x(viewport.get('start'))}px;")
95
+
96
+
97
+
98
+ milestoneBands = d3.nest()
99
+ .key (milestone)-> milestone.band
100
+ .entries(visibleMilestones)
101
+
102
+ bands = @roadmap.selectAll('.roadmap-thumbnail-band')
103
+ .data(milestoneBands, (band)-> band.key)
104
+
105
+ bands.enter()
106
+ .append('g')
107
+ .attr('class', 'roadmap-thumbnail-band')
108
+ .attr('transform', (d, i)-> "translate(0,#{2 + i * 6})")
109
+
110
+ bands.exit().remove()
111
+
112
+ milestones = bands.selectAll('.roadmap-thumbnail-milestone')
113
+ .data(((band)-> band.values), (milestone)-> milestone.cid)
114
+
115
+ # update
116
+ milestones
117
+ .attr 'class', (milestone)->
118
+ css = ['roadmap-thumbnail-milestone']
119
+ css.push 'completed' if milestone.completed
120
+ css.join(' ')
121
+ .transition(150)
122
+ .attr('height', (milestone)-> milestone.lanes * 6 - 1)
123
+ .attr('width', (milestone)=> @x(milestone.endDate) - @x(milestone.startDate))
124
+ .attr('x', (milestone)=> @x(milestone.startDate))
125
+
126
+ # enter
127
+ milestones.enter().append('rect')
128
+ .attr('rx', 1)
129
+ .attr('ry', 1)
130
+ .attr('height', (milestone)-> milestone.lanes * 6 - 1)
131
+ .attr('width', (milestone)=> @x(milestone.endDate) - @x(milestone.startDate))
132
+ .attr('x', (milestone)=> @x(milestone.startDate))
133
+ .attr 'class', (milestone)->
134
+ css = ['roadmap-thumbnail-milestone']
135
+ css.push 'completed' if milestone.completed
136
+ css.join(' ')
137
+
138
+ # exit
139
+ milestones.exit().remove()
140
+
141
+
142
+ if @showToday
143
+ todayLine = @roadmap.selectAll('.thumbnail-roadmap-today')
144
+ .data([@today])
145
+
146
+ todayLine.enter()
147
+ .append('rect')
148
+ .attr('class', 'thumbnail-roadmap-today')
149
+ .attr('height', @graphHeight - 1)
150
+ .attr('width', 1)
151
+ .attr('x', (date)=> @x(date))
152
+ .attr('y', 1)
153
+
154
+ todayLine
155
+ .attr('x', (date)=> @x(date))
156
+
157
+
158
+
159
+ markers = @roadmap.selectAll('.thumbnail-roadmap-marker')
160
+ .data(@markers)
161
+
162
+ markers.enter()
163
+ .append('rect')
164
+ .attr('class', 'thumbnail-roadmap-marker')
165
+ .attr('height', @graphHeight - 1)
166
+ .attr('width', 1)
167
+ .attr('x', ({date})=> @x(date))
168
+ .attr('y', 1)
169
+
170
+ markers
171
+ .attr('x', ({date})=> @x(date))
172
+
173
+ markers.exit().remove()
@@ -0,0 +1,18 @@
1
+ class window.GoalView extends Neat.ModelEditor
2
+ tagName: 'tr'
3
+
4
+ render: ->
5
+ super
6
+ @$el.attr 'data-cid', @model.cid
7
+ @$el.toggleClass 'new', !@model.get('id')
8
+ @
9
+
10
+ cancelEdit: (e)->
11
+ return super if @model.id
12
+
13
+ # Canceling editing a new goal cancels adding a goal
14
+ @model.collection.remove @model
15
+
16
+ onSaveError: (goal, response) ->
17
+ errors = Errors.fromResponse(response)
18
+ errors.renderToAlert()
@@ -0,0 +1,30 @@
1
+ class Roadmaps.ProjectGoalsView extends Neat.CollectionEditor
2
+ renderNewGoalModal: HandlebarsTemplates['houston/roadmaps/goals/new']
3
+ resource: 'goals'
4
+ viewPath: 'houston/roadmaps/goals'
5
+ pageSize: Infinity
6
+
7
+ initialize: ->
8
+ @project = @options.project
9
+ @can = @options.can
10
+ @goals = @collection = @options.goals
11
+ super
12
+
13
+ $('#new_goal_button').click (e) =>
14
+ @newGoal(e)
15
+
16
+
17
+
18
+ render: ->
19
+ super
20
+ $('.table-sortable').tablesorter()
21
+ @
22
+
23
+
24
+
25
+ newGoal: (e)->
26
+ e.preventDefault() if e
27
+ goal = new Roadmaps.Goal
28
+ projectId: @project.id
29
+ @goals.unshift goal
30
+ @edit @views[0]
@@ -0,0 +1,67 @@
1
+ class Roadmaps.RoadmapHistoryView extends Backbone.View
2
+ el: '#roadmap_history_view'
3
+ milestones: {}
4
+
5
+ events:
6
+ 'click :radio': 'changeCommit'
7
+
8
+ initialize: ->
9
+ @commits = @options.commits
10
+ @commitId = @options.commitId
11
+
12
+ dup = (milestones)-> _.clone(milestone) for milestone in milestones
13
+
14
+ # The current state of the Roadmap
15
+ # is the result of the last commit
16
+ currentMilestones = @options.milestones
17
+ @milestones[0] = dup(currentMilestones)
18
+
19
+ for commit in @commits
20
+ for change in commit.changes
21
+ milestone = _(currentMilestones).findWhere(id: change.milestoneId)
22
+ continue unless milestone
23
+
24
+ if change.number is 1
25
+ currentMilestones = _(currentMilestones).without(milestone)
26
+ else
27
+ for attribute, [before, after] of change.modifications
28
+ milestone[attribute] = before
29
+
30
+ @milestones[commit.id] = dup(currentMilestones)
31
+
32
+ @visibleMilestones = new Roadmaps.Milestones()
33
+ @updateRoadmap()
34
+
35
+ @roadmap = new Roadmaps.GanttChart(@visibleMilestones, @options)
36
+ @template = HandlebarsTemplates['houston/roadmaps/roadmap/history']
37
+ super
38
+
39
+ render: ->
40
+ @$el.html @template
41
+ commits: @commits
42
+
43
+ $form = @$el.find('form')
44
+ $form.css(bottom: 42) if App.meta('env') is 'development'
45
+
46
+ setTop = ->
47
+ roadmapBottom = $('#roadmap').position().top + $('#roadmap').height() - 10
48
+ $form.css(top: roadmapBottom)
49
+ setTop()
50
+ $(window).resize(setTop)
51
+ addResizeListener $('#roadmap')[0], setTop
52
+
53
+ @$el.find(':radio').first().prop('checked', true)
54
+ @roadmap.render()
55
+ @
56
+
57
+ changeCommit: (e)->
58
+ $radio = $(e.target)
59
+ $commit = $radio.closest('.roadmap-commit')
60
+ $commit.prevAll().andSelf().addClass('roadmap-commit-reverted')
61
+ $commit.nextAll().removeClass('roadmap-commit-reverted')
62
+ @commitId = +$radio.val()
63
+ @updateRoadmap()
64
+
65
+ updateRoadmap: ->
66
+ milestones = @milestones[@commitId]
67
+ @visibleMilestones.reset milestones, parse: true