rrrspec-web 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/assets/javascripts/helpers.coffee +27 -0
  4. data/assets/javascripts/index.coffee +75 -0
  5. data/{app/js → assets/javascripts}/models.coffee +41 -69
  6. data/assets/javascripts/tasksets.coffee +400 -0
  7. data/{app/js → assets/javascripts}/vendor/backbone-min.js +0 -0
  8. data/{app/js → assets/javascripts}/vendor/backbone-min.map +0 -0
  9. data/{app/js → assets/javascripts}/vendor/backbone.js +0 -0
  10. data/assets/javascripts/vendor/handlebars-v1.3.0.js +2746 -0
  11. data/{app/js → assets/javascripts}/vendor/jquery-1.10.2.js +0 -0
  12. data/{app/js → assets/javascripts}/vendor/jquery-1.10.2.min.js +0 -0
  13. data/{app/js → assets/javascripts}/vendor/jquery-1.10.2.min.map +0 -0
  14. data/{app/js → assets/javascripts}/vendor/moment.min.js +0 -0
  15. data/{app/js → assets/javascripts}/vendor/underscore-min.js +0 -0
  16. data/{app/js → assets/javascripts}/vendor/underscore-min.map +0 -0
  17. data/{app/js → assets/javascripts}/vendor/underscore.js +0 -0
  18. data/assets/stylesheets/application.sass +188 -0
  19. data/lib/rrrspec/web/api.rb +61 -0
  20. data/lib/rrrspec/web/app.rb +9 -30
  21. data/lib/rrrspec/web/configuration.rb +1 -0
  22. data/lib/rrrspec/web/persistent_models.rb +7 -2
  23. data/lib/rrrspec/web/version.rb +1 -1
  24. data/rrrspec-web.gemspec +9 -7
  25. data/spec/rrrspec/web/api_spec.rb +278 -47
  26. data/tasks/assets.rake +3 -6
  27. data/views/index.haml +4 -14
  28. data/views/taskset.haml +61 -68
  29. data/views/user.haml +2 -2
  30. metadata +108 -84
  31. data/app/css/application.sass +0 -124
  32. data/app/css/vendor/bootstrap-theme.css +0 -384
  33. data/app/css/vendor/bootstrap-theme.min.css +0 -1
  34. data/app/css/vendor/bootstrap.css +0 -6805
  35. data/app/css/vendor/bootstrap.min.css +0 -9
  36. data/app/js/index.coffee +0 -51
  37. data/app/js/tasksets.coffee +0 -305
  38. data/app/js/vendor/bootstrap.js +0 -1999
  39. data/app/js/vendor/bootstrap.min.js +0 -6
  40. data/app/js/vendor/mustache.js +0 -551
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 277ae2e299b382cb2256456b336e2a4289ae05bb
4
- data.tar.gz: 263d5aa2200ddfed889fd73841d0aef5cb8cd7cf
3
+ metadata.gz: 835ae4268801f3543334a082fd65379340cdb734
4
+ data.tar.gz: 4dbebd2858d865f3b4912496ae3089fbace9e9c1
5
5
  SHA512:
6
- metadata.gz: 02e13c86dd5e08ee7f7be6122d586606e931180e178d98909f6e24870c933a53ef3438ac46f8d6624bcb5d2ce1319f1ba7ba1db1383042a4c18fe4afdca7f123
7
- data.tar.gz: 05e9e2a072f98dfaf702dc53d0c318c0d0f4699fb93a3bdd5dc246eae27161d3e692babab41ce02495d1b90fa7ef87282f5ba8c32abc745943f109adce525ea9
6
+ metadata.gz: 21535194f756e7243f6376c101905bed8d1dc5e4a289aa77a63e3e9dffe43ac6c2ec0393904dfccd5a23f4230889c8f1994704a22121374a758311deed2cdae0
7
+ data.tar.gz: 91e8cc2b487d8e9ab99ae481992ac1b3a88dc4379c0a707a2ba9b3436eac66feaa973e677ed4894bdf40981c5237f29e9187008ceed888d3105ced00e7e043ae
@@ -0,0 +1 @@
1
+ public
@@ -0,0 +1,27 @@
1
+ formatDate = (date)->
2
+ if date
3
+ moment(date).format("YYYY-MM-DD HH:mm:ss z")
4
+ else
5
+ ""
6
+
7
+ formatDateWithHumanized = (date) ->
8
+ if date
9
+ "#{moment(date).format("YYYY-MM-DD HH:mm:ss z")} (#{moment(date).fromNow()})"
10
+ else
11
+ ""
12
+
13
+ formatDuration = (start, finish)->
14
+ moment.duration(moment(finish).diff(moment(start))).humanize()
15
+
16
+ formatDateWithDuration = (date, start)->
17
+ if date && start
18
+ "#{formatDate(date)} (#{formatDuration(start, date)})"
19
+ else if date
20
+ "#{formatDate(date)}"
21
+ else
22
+ ""
23
+
24
+ Handlebars.registerHelper('date', formatDate)
25
+ Handlebars.registerHelper('dateWithHumanized', formatDateWithHumanized)
26
+ Handlebars.registerHelper('duration', formatDuration)
27
+ Handlebars.registerHelper('dateWithDuration', formatDateWithDuration)
@@ -0,0 +1,75 @@
1
+ #= require vendor/jquery-1.10.2
2
+ #= require vendor/handlebars-v1.3.0
3
+ #= require vendor/moment.min
4
+ #= require vendor/underscore
5
+ #= require vendor/backbone
6
+ #= require bootstrap
7
+ #= require helpers
8
+ #= require models
9
+
10
+ $(->
11
+ class TasksetsView extends Backbone.View
12
+ initialize: (options) ->
13
+ @subviews = []
14
+ @listenTo(@collection, "add", @appendItem)
15
+ @listenTo(@collection, "reset", @resetItems)
16
+ if @collection.hasPages
17
+ @$('.previous').click(=>
18
+ if @collection.hasPrevious()
19
+ @$('.tasksets').empty()
20
+ @subviews = []
21
+ @collection.fetchPreviousPage(success: -> @render)
22
+ @updatePager()
23
+ )
24
+ @$('.next').click(=>
25
+ @$('.tasksets').empty()
26
+ @subviews = []
27
+ @collection.fetchNextPage(success: -> @render)
28
+ @updatePager()
29
+ )
30
+ @resetItems(@collection)
31
+
32
+ appendItem: (model) ->
33
+ view = new TasksetView({model: model})
34
+ @subviews.push(view)
35
+ view.render()
36
+ @$('.tasksets').append(view.$el)
37
+
38
+ resetItems: (collection) ->
39
+ @collection = collection
40
+ @$('.tasksets').empty()
41
+ @subviews = []
42
+ for model in collection.models
43
+ @appendItem(model)
44
+
45
+ render: ->
46
+ for view in @subviews
47
+ view.render()
48
+ @updatePager()
49
+
50
+ updatePager: ->
51
+ if @collection.hasPages
52
+ @$('.pagenum').text("Page #{@collection.currentPage}")
53
+ if @collection.hasPrevious()
54
+ @$('.previous').removeClass('disabled')
55
+ else
56
+ @$('.previous').addClass('disabled')
57
+
58
+ class TasksetView extends Backbone.View
59
+ tagName: 'li'
60
+ className: 'list-group-item'
61
+ template: Handlebars.compile($('#taskset-template').html())
62
+
63
+ render: ->
64
+ @$el.html(@template(@model.attributes))
65
+
66
+ actives = new ActiveTasksets()
67
+ actives.fetch()
68
+ activesView = new TasksetsView({collection: actives, el: '.active-tasksets'})
69
+ activesView.render()
70
+
71
+ recents = new RecentTasksets()
72
+ recents.fetch()
73
+ recentsView = new TasksetsView({collection: recents, el: '.recent-tasksets'})
74
+ recentsView.render()
75
+ )
@@ -1,40 +1,39 @@
1
- dateFormat = (date)->
2
- moment(date).format("YYYY-MM-DD HH:mm:ss z")
3
-
4
- dateDuration = (start, finish)->
5
- moment.duration(moment(finish).diff(moment(start))).humanize()
6
-
7
1
  class @Taskset extends Backbone.Model
8
- url: -> "/v1/tasksets/#{encodeURIComponent(@get('key'))}"
2
+ url: -> "/v2/tasksets/#{encodeURIComponent(@get('key'))}"
9
3
 
10
4
  parse: (obj, options) ->
11
5
  obj.created_at = new Date(obj.created_at) if obj.created_at
12
6
  obj.finished_at = new Date(obj.finished_at) if obj.finished_at
13
- obj.slaves = new Slaves(obj.slaves, {parse: true, silent: false})
7
+ obj.log_text = obj.log
8
+
14
9
  obj.tasks = new Tasks(obj.tasks, {parse: true, silent: false})
15
10
  obj.worker_logs = new WorkerLogs(obj.worker_logs, {parse: true, silent: false})
11
+ obj.worker_logs.tasksetId = obj.id
12
+ obj.slaves = new Slaves(obj.slaves, {parse: true, silent: false})
13
+ obj.slaves.tasksetId = obj.id
16
14
  obj
17
15
 
18
- isFull: -> !!@get('is_full')
19
- isFinished: -> !!@get('finished_at')
20
-
21
- forTemplate: ->
22
- j = @toJSON()
23
- j['duration'] = dateDuration(j['created_at'], j['finished_at'])
24
- if created_at = j['created_at']
25
- j['created_at'] = dateFormat(created_at)
26
- j['created_at_humanized'] = moment(created_at).fromNow()
27
- j['finished_at'] = dateFormat(j['finished_at'])
28
- j
16
+ isFinished: ->
17
+ status = @get('status')
18
+ return status == 'succeeded' || status == 'cancelled' || status == 'failed'
19
+ outputFetched: -> !!@get('log')
20
+ fetchOutput: ->
21
+ $.getJSON("/v2/tasksets/#{@attributes.id}/log", (data)=>
22
+ @attributes.log = data.log
23
+ )
29
24
 
30
25
  class @Task extends Backbone.Model
31
- url: -> "/v1/tasks/#{encodeURIComponent(@get('key'))}"
26
+ url: -> "/v2/tasks/#{@get('id')}"
32
27
  parse: (obj, options) ->
33
28
  if obj.trials
34
29
  obj.trials = _.filter(obj.trials, (trial) -> trial['status'])
35
30
  obj.trials = _.map(obj.trials, (trial) -> new Trial(trial, {parse: true}))
36
31
  obj
37
32
 
33
+ isSuccess: ->
34
+ status = @get('status')
35
+ return status == 'passed' || status == 'pending'
36
+
38
37
  numExamples: ->
39
38
  passed = null
40
39
  pending = null
@@ -53,9 +52,6 @@ class @Task extends Backbone.Model
53
52
  return [0, 0, 0]
54
53
  return [preferred.get('passed'), preferred.get('pending'), preferred.get('failed')]
55
54
 
56
- forTemplate: ->
57
- @toJSON()
58
-
59
55
  class @Tasks extends Backbone.Collection
60
56
  initialize: (options) ->
61
57
  @numTask = 0
@@ -88,65 +84,55 @@ class @Tasks extends Backbone.Collection
88
84
  @numFailedExample += failed
89
85
 
90
86
  class @Trial extends Backbone.Model
91
- url: -> "/v1/trials/#{encodeURIComponent(@get('key'))}"
92
87
  parse: (obj, options) ->
93
88
  obj.started_at = new Date(obj.started_at) if obj.started_at
94
89
  obj.finished_at = new Date(obj.finished_at) if obj.finished_at
95
90
  obj
96
- forTemplate: ->
97
- j = @toJSON()
98
- j['duration'] = dateDuration(j['started_at'], j['finished_at'])
99
- j['started_at'] = dateFormat(j['started_at']) if j['started_at']
100
- j['finished_at'] = dateFormat(j['finished_at']) if j['finished_at']
101
- j
91
+
92
+ outputsFetched: -> !!@attributes.stdout
93
+
94
+ fetchOutput: ->
95
+ $.getJSON("/v2/trials/#{@attributes.id}/outputs", (data)=>
96
+ @attributes.stdout = data.stdout
97
+ @attributes.stderr = data.stderr
98
+ )
102
99
 
103
100
  class @WorkerLog extends Backbone.Model
104
- url: -> "/v1/worker_logs/#{encodeURIComponent(@get('key'))}"
105
101
  parse: (obj, options) ->
106
102
  obj.started_at = new Date(obj.started_at)
107
103
  obj.rsync_finished_at = new Date(obj.rsync_finished_at) if obj.rsync_finished_at
108
104
  obj.setup_finished_at = new Date(obj.setup_finished_at) if obj.setup_finished_at
109
- obj.finished_at = new Date(obj.finished_at) if obj.finished_at
105
+ obj.rspec_finished_at = new Date(obj.rspec_finished_at) if obj.rspec_finished_at
106
+ obj.log_text = obj.log
110
107
  obj
111
108
 
112
- forTemplate: ->
113
- j = @toJSON()
114
- j['rsync_duration'] = dateDuration(j['started_at'], j['rsync_finished_at'])
115
- j['setup_duration'] = dateDuration(j['rsync_finished_at'], j['setup_finished_at'])
116
- j['test_duration'] = dateDuration(j['setup_finished_at'], j['finished_at'])
117
-
118
- j['started_at'] = dateFormat(j['started_at']) if j['started_at']
119
- j['rsync_finished_at'] = dateFormat(j['rsync_finished_at']) if j['rsync_finished_at']
120
- j['setup_finished_at'] = dateFormat(j['setup_finished_at']) if j['setup_finished_at']
121
- j['finished_at'] = dateFormat(j['finished_at']) if j['finished_at']
122
- j
123
-
124
109
  class @WorkerLogs extends Backbone.Collection
110
+ url: -> "/v2/tasksets/#{@tasksetId}/worker_logs"
125
111
  parse: (obj, options) ->
126
112
  obj.map((worker_log) -> new WorkerLog(worker_log, options))
113
+ fetched: -> !@isEmpty
127
114
 
128
115
  class @Slave extends Backbone.Model
129
- url: -> "/v1/slaves/#{encodeURIComponent(@get('key'))}"
130
- forTemplate: ->
131
- j = @toJSON()
132
- j['trials'] = _.map(j['trials'], (trial) ->
133
- trial['encoded_key'] = encodeURIComponent(trial['key'])
134
- trial
135
- )
136
- j
116
+ parse: (obj, options) ->
117
+ obj.log_text = obj.log
118
+ obj
137
119
 
138
120
  class @Slaves extends Backbone.Collection
121
+ url: -> "/v2/tasksets/#{@tasksetId}/slaves"
139
122
  parse: (obj, options) ->
140
123
  obj.map((slave) -> new Slave(slave, options))
124
+ fetched: -> !@isEmpty
141
125
 
142
126
  class @ActiveTasksets extends Backbone.Collection
143
- url: "/v1/tasksets/actives"
127
+ url: "/v2/tasksets/actives"
144
128
  parse: (obj, options) ->
145
129
  obj.map((taskset) -> new Taskset(taskset, options))
130
+ hasPages: false
146
131
 
147
132
  class @RecentTasksets extends Backbone.Collection
148
133
  currentPage: 1
149
- url: -> "/v1/tasksets/recents?page=#{@currentPage}"
134
+ url: -> "/v2/tasksets/recents?page=#{@currentPage}"
135
+ hasPages: true
150
136
 
151
137
  fetchNextPage: ->
152
138
  @currentPage++
@@ -157,21 +143,7 @@ class @RecentTasksets extends Backbone.Collection
157
143
  @currentPage--
158
144
  @fetch()
159
145
 
160
- parse: (obj, options) ->
161
- obj.map((taskset) -> new Taskset(taskset, options))
162
-
163
- class @SlaveFailedTasksets extends Backbone.Collection
164
- currentPage: 1
165
- url: -> "/v1/tasksets/failure_slaves?page=#{@currentPage}"
166
-
167
- fetchNextPage: ->
168
- @currentPage++
169
- @fetch()
170
-
171
- fetchPreviousPage: ->
172
- if @currentPage != 1
173
- @currentPage--
174
- @fetch()
146
+ hasPrevious: -> @currentPage != 1
175
147
 
176
148
  parse: (obj, options) ->
177
149
  obj.map((taskset) -> new Taskset(taskset, options))
@@ -0,0 +1,400 @@
1
+ #= require vendor/jquery-1.10.2
2
+ #= require vendor/handlebars-v1.3.0
3
+ #= require vendor/moment.min
4
+ #= require vendor/underscore
5
+ #= require vendor/backbone
6
+ #= require bootstrap
7
+ #= require helpers
8
+ #= require models
9
+
10
+ $(->
11
+ class TasksetRouter extends Backbone.Router
12
+ routes:
13
+ 'taskset': 'taskset'
14
+ 'tasks/:task_id': 'task'
15
+ 'trials/:trial_id': 'trial'
16
+ 'worker_logs/:worker_log_id': 'worker_log'
17
+ 'slaves/:slave_id': 'slave'
18
+
19
+ class TasksetView extends Backbone.View
20
+ el: '.taskset'
21
+
22
+ initialize: ->
23
+ @headView = new HeadView({model: @model})
24
+ @progressbarView = new ProgressBarView({model: @model})
25
+ @taskListView = new TaskListView({collection: @model.get('tasks')})
26
+ @workerLogListView = new WorkerLogListView({collection: @model.get('worker_logs')})
27
+ @slaveListView = new SlaveListView({collection: @model.get('slaves')})
28
+
29
+ render: ->
30
+ @headView.render()
31
+ @progressbarView.render()
32
+ @taskListView.render()
33
+ @workerLogListView.render()
34
+ @slaveListView.render()
35
+
36
+ class HeadView extends Backbone.View
37
+ el: '.head'
38
+ template: Handlebars.compile($('#head-template').html())
39
+
40
+ initialize: (options) ->
41
+ @showing = false
42
+ router.on('route:taskset', => @show())
43
+
44
+ render: ->
45
+ @$el.html(@template(@model.attributes))
46
+ @$('.panel-heading .status').addClass(@model.get('status'))
47
+
48
+ @$('.panel-title').click(=>
49
+ router.navigate("taskset")
50
+ @toggle()
51
+ )
52
+
53
+ show: ->
54
+ @showing = true
55
+ @$('.taskset-info').collapse('show')
56
+ unless @model.outputFetched()
57
+ @model.fetchOutput().done(=>
58
+ @$('.log').html(@model.get('log'))
59
+ )
60
+
61
+ hide: ->
62
+ @showing = false
63
+ @$('.taskset-info').collapse('hide')
64
+
65
+ toggle: ->
66
+ @showing = !@showing
67
+ if @showing
68
+ @show()
69
+ else
70
+ @hide()
71
+
72
+ class ProgressBarView extends Backbone.View
73
+ el: '.progressbars'
74
+
75
+ showBar: (bar, percentage, text) ->
76
+ if percentage == 0
77
+ bar.attr('style', 'width: 0%')
78
+ bar.text('')
79
+ else
80
+ bar.attr('style', "width: #{100*percentage}%")
81
+ bar.text(text)
82
+ hideBar: (bar) -> @showBar(bar, 0, '')
83
+
84
+ renderSpecBar: (tasks) ->
85
+ if @model.isFinished()
86
+ @$('.spec-progress').removeClass('progress-striped active')
87
+ @hideBar(@$('.spec-progress-bar'))
88
+ @showBar(@$('.passed-spec-bar'), tasks.numPassedTask/tasks.numTask, tasks.numPassedTask)
89
+ @showBar(@$('.pending-spec-bar'), tasks.numPendingTask/tasks.numTask, tasks.numPendingTask)
90
+ @showBar(@$('.failed-spec-bar'), tasks.numFailedTask/tasks.numTask, tasks.numFailedTask)
91
+ else
92
+ numFinishedTask = tasks.numPassedTask + tasks.numPendingTask + tasks.numFailedTask
93
+ percentage = numFinishedTask/tasks.numTask
94
+ @$('.spec-progress').addClass('progress-striped active')
95
+ @showBar(@$('.spec-progress-bar'), percentage, "#{numFinishedTask}/#{tasks.numTask} (#{100*percentage}%)")
96
+ @hideBar(@$('.passed-spec-bar'))
97
+ @hideBar(@$('.pending-spec-bar'))
98
+ @hideBar(@$('.failed-spec-bar'))
99
+
100
+ renderExampleBar: (tasks) ->
101
+ @showBar(@$('.passed-example-bar'), tasks.numPassedExample/tasks.numExample, tasks.numPassedExample)
102
+ @showBar(@$('.pending-example-bar'), tasks.numPendingExample/tasks.numExample, tasks.numPendingExample)
103
+ @showBar(@$('.failed-example-bar'), tasks.numFailedExample/tasks.numExample, tasks.numFailedExample)
104
+
105
+ render: ->
106
+ tasks = @model.get('tasks')
107
+ @renderSpecBar(tasks)
108
+ @renderExampleBar(tasks)
109
+
110
+ class TaskListView extends Backbone.View
111
+ el: '.tasks'
112
+
113
+ initialize: (options) ->
114
+ @showHeaders = false
115
+ @$('.tasks-heading').click(=>
116
+ @showHeaders = !@showHeaders
117
+ if @showHeaders
118
+ @$('.tasks-heading').text("TASKS")
119
+ for key, view of @subviews
120
+ view.showHeader()
121
+ else
122
+ @$('.tasks-heading').text("FAILED TASKS")
123
+ for key, view of @subviews
124
+ view.hideHeaderIfSuccess()
125
+ )
126
+ router.on('route:task', (taskId) =>
127
+ view = @subviews[taskId]
128
+ view.showHeader()
129
+ view.showBody()
130
+ view.scrollIntoView()
131
+ )
132
+ router.on('route:trial', (trialId) =>
133
+ for key, view of @subviews
134
+ if view.hasTrial(trialId)
135
+ view.showHeader()
136
+ view.showBody()
137
+ view.scrollIntoViewOfTrial(trialId)
138
+ break
139
+ )
140
+ @resetItems(@collection)
141
+
142
+ resetItems: (collection) ->
143
+ @collection = collection
144
+ @$('.tasks-list').html('')
145
+ @subviews = {}
146
+ @listenTo(collection, "add", @appendItem)
147
+ @listenTo(collection, "reset", @resetItems)
148
+ for model in collection.models
149
+ @appendItem(model)
150
+
151
+ appendItem: (model) ->
152
+ view = new TaskView({model: model})
153
+ @subviews[model.attributes.id] = view
154
+ view.render()
155
+ @$('.tasks-list').append(view.$el)
156
+
157
+ render: ->
158
+ for key, view in @subviews
159
+ view.render()
160
+
161
+ class TaskView extends Backbone.View
162
+ tagName: 'li'
163
+ className: 'list-group-item'
164
+ template: Handlebars.compile($('#tasks-list-template').html())
165
+
166
+ initialize: (options) ->
167
+ @subviews = {}
168
+ @bodyShowing = false
169
+
170
+ hasTrial: (trialId) -> !!@subviews[trialId]
171
+
172
+ render: ->
173
+ @$el.html(@template(@model.attributes))
174
+ @$el.addClass(@model.get('status'))
175
+ @hideHeaderIfSuccess()
176
+ @$('.tasks-list-item-header').click(=>
177
+ router.navigate("tasks/#{@model.get('id')}")
178
+ @toggleBody()
179
+ )
180
+ for trial in @model.get('trials')
181
+ @appendTrial(trial)
182
+
183
+ appendTrial: (trial) ->
184
+ view = new TrialView({model: trial})
185
+ @subviews[trial.attributes.id] = view
186
+ view.render()
187
+ @$('.trials').append(view.$el)
188
+
189
+ showHeader: ->
190
+ @$el.removeClass('hidden')
191
+
192
+ hideHeaderIfSuccess: ->
193
+ if @model.isSuccess()
194
+ @$el.addClass('hidden')
195
+
196
+ showBody: ->
197
+ @bodyShowing = true
198
+ for key, view of @subviews
199
+ view.showBody()
200
+ @$('.body').collapse('show')
201
+ @scrollIntoView()
202
+
203
+ hideBody: ->
204
+ @bodyShowing = false
205
+ @$('.body').collapse('hide')
206
+
207
+ toggleBody: ->
208
+ @bodyShowing = !@bodyShowing
209
+ if @bodyShowing
210
+ @showBody()
211
+ else
212
+ @hideBody()
213
+
214
+ scrollIntoView: -> $('html, body').animate(scrollTop: @$el.offset().top)
215
+
216
+ scrollIntoViewOfTrial: (trialId) ->
217
+ @subviews[trialId].scrollIntoView()
218
+
219
+ class TrialView extends Backbone.View
220
+ className: 'panel'
221
+ template: Handlebars.compile($('#trial-template').html())
222
+
223
+ render: ->
224
+ @$el.html(@template(@model.attributes))
225
+
226
+ showBody: ->
227
+ unless @model.outputsFetched()
228
+ @model.fetchOutput().done(=>
229
+ @$('.stdout').text(@model.get('stdout'))
230
+ @$('.stderr').text(@model.get('stderr'))
231
+ )
232
+
233
+ scrollIntoView: -> $('html, body').animate(scrollTop: @$el.offset().top)
234
+
235
+ class WorkerLogListView extends Backbone.View
236
+ el: '.worker-logs'
237
+
238
+ initialize: (options) ->
239
+ @showHeaders = false
240
+ @$('.worker-logs-heading').click(=> @toggle())
241
+ router.on('route:worker_log', (workerLogId) =>
242
+ @show().done(=>
243
+ target = @subviews[workerLogId]
244
+ target.scrollIntoView()
245
+ target.showBody()
246
+ )
247
+ )
248
+ @resetItems(@collection)
249
+
250
+ resetItems: (collection) ->
251
+ @collection = collection
252
+ @$('.worker-logs-list').html('')
253
+ @subviews = {}
254
+ @listenTo(collection, "add", @appendItem)
255
+ @listenTo(collection, "reset", @resetItems)
256
+ for model in collection.models
257
+ @appendItem(model)
258
+
259
+ appendItem: (model) ->
260
+ view = new WorkerLogView({model: model})
261
+ @subviews[model.attributes.id] = view
262
+ view.render()
263
+ @$('.worker-logs-list').append(view.$el)
264
+
265
+ render: ->
266
+ for key, view in @subviews
267
+ view.render()
268
+
269
+ show: ->
270
+ @showHeaders = true
271
+ (
272
+ unless @collection.fetched()
273
+ @collection.fetch({reset: true})
274
+ else
275
+ $.Defferred()
276
+ ).done(=> @$('.worker-logs-list').collapse('show'))
277
+
278
+ hide: ->
279
+ @showHeaders = false
280
+ @$('.worker-logs-list').collapse('hide')
281
+
282
+ toggle: ->
283
+ @showHeaders = !@showHeaders
284
+ if @showHeaders
285
+ @show()
286
+ else
287
+ @hide()
288
+
289
+ class WorkerLogView extends Backbone.View
290
+ tagName: 'li'
291
+ className: 'list-group-item'
292
+ template: Handlebars.compile($('#worker-log-template').html())
293
+
294
+ render: ->
295
+ @$el.html(@template(@model.attributes))
296
+ @$('.worker-logs-list-item-header').click(=>
297
+ router.navigate("worker_logs/#{@model.get('id')}")
298
+ @$('.body').collapse('toggle')
299
+ )
300
+
301
+ scrollIntoView: -> $('html, body').animate(scrollTop: @$el.offset().top)
302
+
303
+ showBody: ->
304
+ @$('.body').collapse('show')
305
+
306
+ class SlaveListView extends Backbone.View
307
+ el: '.slaves'
308
+
309
+ initialize: (options) ->
310
+ @showHeaders = false
311
+ @$('.slaves-heading').click(=> @toggle())
312
+ router.on('route:slave', (slaveId) =>
313
+ @show().done(=>
314
+ target = @subviews[slaveId]
315
+ target.scrollIntoView()
316
+ target.showBody()
317
+ )
318
+ )
319
+ @resetItems(@collection)
320
+
321
+ resetItems: (collection) ->
322
+ @collection = collection
323
+ @$('.slaves-list').html('')
324
+ @subviews = {}
325
+ @listenTo(collection, "add", @appendItem)
326
+ @listenTo(collection, "reset", @resetItems)
327
+ for model in collection.models
328
+ @appendItem(model)
329
+
330
+ appendItem: (model) ->
331
+ view = new SlaveView({model: model})
332
+ @subviews[model.attributes.id] = view
333
+ view.render()
334
+ @$('.slaves-list').append(view.$el)
335
+
336
+ render: ->
337
+ for key, view of @subviews
338
+ view.render()
339
+
340
+ show: ->
341
+ @showHeaders = true
342
+ (
343
+ unless @collection.fetched()
344
+ @collection.fetch({reset: true})
345
+ else
346
+ $.Defferred()
347
+ ).done(=>
348
+ @$('.slaves-list').collapse('show')
349
+ )
350
+
351
+ hide: ->
352
+ @showHeaders = false
353
+ @$('.slaves-list').collapse('hide')
354
+
355
+ toggle: ->
356
+ @showHeaders = !@showHeaders
357
+ if @showHeaders
358
+ @show()
359
+ else
360
+ @hide()
361
+
362
+ class SlaveView extends Backbone.View
363
+ tagName: 'li'
364
+ className: 'list-group-item'
365
+ template: Handlebars.compile($('#slave-template').html())
366
+
367
+ render: ->
368
+ @$el.html(@template(@model.attributes))
369
+ @$('.slaves-list-item-header').click(=>
370
+ router.navigate("slaves/#{@model.get('id')}")
371
+ @$('.body').collapse('toggle')
372
+ )
373
+ @$el.addClass(@model.get('status'))
374
+
375
+ scrollIntoView: -> $('html, body').animate(scrollTop: @$el.offset().top)
376
+
377
+ showBody: ->
378
+ @$('.body').collapse('show')
379
+
380
+ router = new TasksetRouter()
381
+ Backbone.history.start()
382
+
383
+ if document.URL.match(/\/tasksets\/(.*?)(#.*)?$/)
384
+ taskset = new Taskset({key: RegExp.$1})
385
+ taskset.fetch({
386
+ success: (model, response, options) ->
387
+ tasksetView = new TasksetView({model: model})
388
+ tasksetView.render()
389
+ tasksetView.$el.removeClass('hidden')
390
+
391
+ # Force re-route
392
+ fragment = Backbone.history.fragment
393
+ router.navigate('')
394
+ router.navigate(fragment, {trigger: true})
395
+ error: (model, response, options) ->
396
+ $('#notfound-modal').modal({keyboard: false})
397
+ })
398
+ else
399
+ $('#notfound-modal').modal({keyboard: false})
400
+ )