git-visualiser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +15 -0
  2. data/bin/git_vis +19 -0
  3. data/lib/application/application.rb +82 -0
  4. data/lib/application/coffee_engine.rb +20 -0
  5. data/lib/application/images/ajax-loader.gif +0 -0
  6. data/lib/application/javascripts/branch_graph.coffee +424 -0
  7. data/lib/application/javascripts/commit_graph.coffee +442 -0
  8. data/lib/application/javascripts/common.coffee +50 -0
  9. data/lib/application/javascripts/d3.min.js +5 -0
  10. data/lib/application/javascripts/data_convert.coffee +56 -0
  11. data/lib/application/javascripts/jquery.min.js +6 -0
  12. data/lib/application/javascripts/moment.min.js +6 -0
  13. data/lib/application/javascripts/namespace.coffee +2 -0
  14. data/lib/application/sass_engine.rb +14 -0
  15. data/lib/application/stylesheets/scss/app.scss +172 -0
  16. data/lib/application/stylesheets/scss/flat_ui/_config.sass +16 -0
  17. data/lib/application/stylesheets/scss/flat_ui/_icon-font-24.sass +91 -0
  18. data/lib/application/stylesheets/scss/flat_ui/_icon-font.sass +91 -0
  19. data/lib/application/stylesheets/scss/flat_ui/_mixins.sass +96 -0
  20. data/lib/application/stylesheets/scss/flat_ui/_spaces.sass +129 -0
  21. data/lib/application/stylesheets/scss/flat_ui/flat-ui.sass +39 -0
  22. data/lib/application/stylesheets/scss/flat_ui/modules/_btn.sass +73 -0
  23. data/lib/application/stylesheets/scss/flat_ui/modules/_checkbox-and-radio.sass +86 -0
  24. data/lib/application/stylesheets/scss/flat_ui/modules/_demo.sass +228 -0
  25. data/lib/application/stylesheets/scss/flat_ui/modules/_footer.sass +57 -0
  26. data/lib/application/stylesheets/scss/flat_ui/modules/_input.sass +66 -0
  27. data/lib/application/stylesheets/scss/flat_ui/modules/_login.sass +95 -0
  28. data/lib/application/stylesheets/scss/flat_ui/modules/_navbar.sass +152 -0
  29. data/lib/application/stylesheets/scss/flat_ui/modules/_pager.sass +56 -0
  30. data/lib/application/stylesheets/scss/flat_ui/modules/_pagination.sass +75 -0
  31. data/lib/application/stylesheets/scss/flat_ui/modules/_palette.sass +88 -0
  32. data/lib/application/stylesheets/scss/flat_ui/modules/_progress.sass +29 -0
  33. data/lib/application/stylesheets/scss/flat_ui/modules/_select.sass +163 -0
  34. data/lib/application/stylesheets/scss/flat_ui/modules/_share.sass +34 -0
  35. data/lib/application/stylesheets/scss/flat_ui/modules/_tagsinput.sass +91 -0
  36. data/lib/application/stylesheets/scss/flat_ui/modules/_tile.sass +42 -0
  37. data/lib/application/stylesheets/scss/flat_ui/modules/_todo.sass +77 -0
  38. data/lib/application/stylesheets/scss/flat_ui/modules/_toggle.sass +85 -0
  39. data/lib/application/stylesheets/scss/flat_ui/modules/_tooltip.sass +45 -0
  40. data/lib/application/stylesheets/scss/flat_ui/modules/_type.sass +43 -0
  41. data/lib/application/stylesheets/scss/flat_ui/modules/_ui-slider.sass +44 -0
  42. data/lib/application/stylesheets/scss/flat_ui/modules/_video.sass +358 -0
  43. data/lib/application/views/authors_list.haml +11 -0
  44. data/lib/application/views/index.haml +48 -0
  45. data/lib/application/visualisation.rb +156 -0
  46. metadata +87 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDBlYWUyNDk3NmY2MWM0Mzc1ZGEwYmJmNWFiYThhOGM5NjJjM2I4Yw==
5
+ data.tar.gz: !binary |-
6
+ YzEzZWFiZGMzYjUwODJlNWI4Nzc2NWRiMjRlNmUxOTEzM2MxM2I5MQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MzY1MTFhNjhlZmUxOGI5YjA1ZmI1NmNjNGE4MDBlZGQ0MGNlNjM4ZGZhOTg2
10
+ NmMyMTUzMjIxODdlZjAyNzE5MDc5MTcxMWUxZGQxZmM2N2U3MWY0MjEwYjAx
11
+ ZTMwNjk3MDVjZDk5ZDk5NjJkNDg3NjY5MjdiZDg1NjYzMjI5ZTY=
12
+ data.tar.gz: !binary |-
13
+ M2YzOGZkZDA5MjJjNTI4ZjkyZDA3YTAyZTZiMDQ5NDBiNzIzYWQ1ZTE4MTlh
14
+ MWYwOWVjMTczYTViYzM5ZGFkODAwMDE1YjVlYzA0YzhmMjgwMTczZjIxZDFl
15
+ NmRkZjhkNmFlZDc1MWJkMTdkNjhjNGMwMzQyMTExZTYzOGFkNzA=
data/bin/git_vis ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+
4
+ begin
5
+ require 'rubygems'
6
+ require 'bundler'
7
+ require 'sinatra'
8
+ require 'haml'
9
+ require 'coffee-script'
10
+ require 'sass'
11
+ require 'json'
12
+
13
+ require 'application/sass_engine'
14
+ require 'application/coffee_engine'
15
+ require 'application/visualisation'
16
+
17
+ #start the application
18
+ require 'application/application'
19
+ end
@@ -0,0 +1,82 @@
1
+ class GitVisualiser < Sinatra::Base
2
+
3
+ use SassEngine
4
+ use CoffeeEngine
5
+
6
+ set :static, true
7
+ set :public_dir, File.expand_path('..', __FILE__)
8
+
9
+ set :views, File.expand_path('../views', __FILE__)
10
+ set :haml, { :format => :html5 }
11
+
12
+ get '/' do
13
+ haml :'/index'
14
+ end
15
+
16
+ get '/branches.json' do
17
+ branches = Visualisation.branches
18
+ content_type :json
19
+ branches.to_json
20
+ end
21
+
22
+ get '/merged_branches.json' do
23
+ merged_branches = Visualisation.repo_branches_merged
24
+ content_type :json
25
+ merged_branches.to_json
26
+ end
27
+
28
+ get '/filter_branch_commits.json' do
29
+ include_commit_sha = params[:include]
30
+ exclude_commit_sha = params[:exclude]
31
+
32
+ branches = branches_include = branches_exclude = []
33
+ branches_include = Visualisation.branches_containing_commit(include_commit_sha) if include_commit_sha != ''
34
+ branches_exclude = Visualisation.branches_excluding_commit(exclude_commit_sha) if exclude_commit_sha != ''
35
+
36
+ if !branches_include == {}
37
+ branches = Hash[branches_include.to_a - branches_exclude.to_a]
38
+ else
39
+ branches = branches_exclude
40
+ end
41
+
42
+ content_type :json
43
+ branches.to_json
44
+ end
45
+
46
+ get '/author_stats.json' do
47
+ ref = params[:ref]
48
+
49
+ @authors = Visualisation.branch_author_stats(ref)
50
+ @authors = @authors.slice(0, 3) #show a max of 3 authors
51
+
52
+ haml :'/authors_list' if @authors.length > 0
53
+ end
54
+
55
+ get '/commits.json' do
56
+ ref = params[:ref]
57
+ commits = Visualisation.commits_for_branch(ref)
58
+
59
+ content_type :json
60
+ commits.to_json
61
+ end
62
+
63
+ get '/commit_diff_stats.json' do
64
+ ref = params[:ref]
65
+ file_diff_stats = Visualisation.merge_base_file_stats(ref)
66
+
67
+ content_type :json
68
+ file_diff_stats.to_json
69
+ end
70
+
71
+ helpers do
72
+
73
+ def gravatar_image(email)
74
+ hash = Digest::MD5.hexdigest(email).to_s
75
+ "<img src=\"http://www.gravatar.com/avatar/#{hash}?s=48\" />"
76
+ end
77
+
78
+ end
79
+ end
80
+
81
+ puts "Running GitVisualiser"
82
+ GitVisualiser.run!
@@ -0,0 +1,20 @@
1
+ class CoffeeEngine < Sinatra::Base
2
+
3
+ set :views, File.dirname(__FILE__) + '/javascripts'
4
+ set :public_dir, File.expand_path('..', __FILE__)
5
+
6
+ get "/javascripts/d3.min.js" do
7
+ end
8
+
9
+ get "/javascripts/jquery.min.js" do
10
+ end
11
+
12
+ get "/javascripts/moment.min.js" do
13
+ end
14
+
15
+ get "/javascripts/*.js" do
16
+ filename = params[:splat].first
17
+ coffee filename.to_sym
18
+ end
19
+
20
+ end
@@ -0,0 +1,424 @@
1
+ class BranchGraph
2
+ constructor: ->
3
+ @initializeD3()
4
+ @getGraphData()
5
+ @initializeControls()
6
+ @commitGraph = new Visualisation.CommitGraph()
7
+ # @commitGraph.load("git-vis-2")
8
+
9
+ initializeD3: ->
10
+ # set up SVG for D3
11
+ @width = $("#branches-display").width();
12
+ @height = $("#branches-display").height();
13
+ @body = d3.select("body")
14
+ @svg = @body.select("#branches-display")
15
+ .append("svg")
16
+ .attr("width", @width)
17
+ .attr("height", @height)
18
+ @lastKeyDown = -1;
19
+
20
+ getGraphData: ->
21
+ # set up initial nodes and links
22
+ # - nodes are known by 'id', not by index in array.
23
+ # - links are always source < target; edge directions are set by 'left' and 'right'.
24
+ $.get "/branches.json", (data) ->
25
+ branch_data = data
26
+ $.get "/merged_branches.json", (merge_data) ->
27
+ Visualisation.branchGraph.initGraphData(branch_data, merge_data)
28
+
29
+ initializeControls: ->
30
+ $("#apply-filters-btn").click () =>
31
+ @apply_filters()
32
+ false
33
+
34
+ initGraphData: (branch_data, merge_data) =>
35
+ @branches = branch_data.branches;
36
+ @diff_lines = branch_data.diff;
37
+
38
+ @master = undefined
39
+ #remove the master branch from branches array
40
+ @branches = $.grep(@branches, (el, i) =>
41
+ if el.name is "master"
42
+ @master = el
43
+ return false
44
+ true
45
+ )
46
+
47
+ percent_diff = 0.0
48
+ total_diff = @diff_lines.add + @diff_lines.del
49
+ average_diff = total_diff / @branches.length
50
+
51
+ @nodes = []
52
+ @links = []
53
+ @branch_names = {}
54
+ @nodes.push
55
+ id: 0
56
+ branch: @master
57
+ size: 5.0
58
+ reflexive: false
59
+ fixed: true
60
+ x: @width / 2
61
+ y: @height / 2
62
+ @branch_names["master"] = 0
63
+ $.each @branches, (i, obj) =>
64
+ #calculate the percentage diff for this branch
65
+ percent_diff = (obj.diff.add + obj.diff.del) / average_diff
66
+ @nodes.push id: i + 1, branch: obj, size: percent_diff, reflexive: false, hidden: false
67
+ @branch_names[obj.name] = i + 1
68
+
69
+ #check for merges/edges for each branch/node
70
+ @linked_nodes = {}
71
+ $.each merge_data, (base_key, base) =>
72
+ $.each base, (branch_key, merged_branch) =>
73
+ if merged_branch.left || merged_branch.right
74
+ @linked_nodes[@branch_names[base_key] + ", " + @branch_names[branch_key]] = 1
75
+ @links.push
76
+ source: @branch_names[base_key]
77
+ target: @branch_names[branch_key]
78
+ left: merged_branch.left
79
+ right: merged_branch.right
80
+ hidden: false
81
+
82
+ #store original state
83
+ @all_nodes = @nodes
84
+ @all_links = @links
85
+ @all_branches = @branches
86
+ @all_branch_names = @branch_names
87
+
88
+ @initGraph(false)
89
+
90
+ initGraph: (redraw) ->
91
+ if redraw is true
92
+ d3.select("svg").remove()
93
+ @svg = @body.select("#vis-display")
94
+ .append("svg")
95
+ .attr("width", @width)
96
+ .attr("height", @height)
97
+ @recalculate_node_sizes()
98
+
99
+ lastNodeId = @nodes.length - 1
100
+
101
+ # init D3 force layout
102
+ @force = d3.layout.force()
103
+ .nodes(@nodes)
104
+ .links(@links)
105
+ .size([@width, @height])
106
+ .linkDistance(150)
107
+ .charge(-500)
108
+ .on("tick", @tick)
109
+
110
+ # define arrow markers for graph links
111
+ @svg.append('svg:defs').append('svg:marker')
112
+ .attr('id', 'end-arrow')
113
+ .attr('viewBox', '0 -5 10 10')
114
+ .attr('refX', 6)
115
+ .attr('markerWidth', 3)
116
+ .attr('markerHeight', 3)
117
+ .attr('orient', 'auto')
118
+ .append('svg:path')
119
+ .attr('d', 'M0,-5L10,0L0,5')
120
+ .attr('fill', '#999');
121
+
122
+ @svg.append('svg:defs').append('svg:marker')
123
+ .attr('id', 'start-arrow')
124
+ .attr('viewBox', '0 -5 10 10')
125
+ .attr('refX', 4)
126
+ .attr('markerWidth', 3)
127
+ .attr('markerHeight', 3)
128
+ .attr('orient', 'auto')
129
+ .append('svg:path')
130
+ .attr('d', 'M10,-5L0,0L10,5')
131
+ .attr('fill', '#999');
132
+
133
+ # handles to link and node element groups
134
+ @path = @svg.append('svg:g').selectAll('path')
135
+ @circle = @svg.append('svg:g').selectAll('g');
136
+
137
+ # mouse event vars
138
+ @selected_node = null
139
+ @selected_link = null
140
+ @mousedown_link = null
141
+ @mousedown_node = null
142
+ @mouseup_node = null
143
+
144
+ #remove the loading div here
145
+ $("#vis-loading").hide();
146
+ @restart()
147
+
148
+ resetMouseVars: ->
149
+ @mousedown_node = null
150
+ @mouseup_node = null
151
+ @mousedown_link = null
152
+
153
+ # update force layout (called automatically each iteration)
154
+ tick: ->
155
+ # draw directed edges with proper padding from node centers
156
+ Visualisation.branchGraph.path.attr "d", (d) ->
157
+ deltaX = d.target.x - d.source.x
158
+ deltaY = d.target.y - d.source.y
159
+ dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
160
+ normX = deltaX / dist
161
+ normY = deltaY / dist
162
+ sourcePadding = node_size(d.source) + 7
163
+ targetPadding = node_size(d.target) + 7
164
+ sourceX = d.source.x + (sourcePadding * normX)
165
+ sourceY = d.source.y + (sourcePadding * normY)
166
+ targetX = d.target.x - (targetPadding * normX)
167
+ targetY = d.target.y - (targetPadding * normY)
168
+ "M" + sourceX + "," + sourceY + "L" + targetX + "," + targetY
169
+
170
+ Visualisation.branchGraph.circle.attr "transform", (d) ->
171
+ "translate(" + d.x + "," + d.y + ")"
172
+
173
+ # update graph (called when needed)
174
+ restart: ->
175
+ # path (link) group
176
+ @path = @path.data(@links)
177
+
178
+ # update existing links
179
+ @path.classed("selected", (d) ->
180
+ d is @selected_link
181
+ ).style("marker-start", (d) ->
182
+ (if d.left then "url(#start-arrow)" else "")
183
+ ).style "marker-end", (d) ->
184
+ (if d.right then "url(#end-arrow)" else "")
185
+
186
+ # add new links
187
+ @path.enter().append("svg:path").attr("class", "link").classed("selected", (d) ->
188
+ d is @selected_link
189
+ ).style("marker-start", (d) ->
190
+ (if d.left then "url(#start-arrow)" else "")
191
+ ).style("marker-end", (d) ->
192
+ (if d.right then "url(#end-arrow)" else "")
193
+ )
194
+
195
+ # remove old links
196
+ @path.exit().remove()
197
+
198
+ # circle (node) group
199
+ # NB: the function arg is crucial here! nodes are known by id, not by index!
200
+ @circle = @circle.data(@nodes, (d) ->
201
+ d.id
202
+ )
203
+
204
+ #hide filtered nodes
205
+ @circle.selectAll("circle").select((d) ->
206
+ d.branch.hidden == false ? this : null
207
+ )
208
+
209
+ # update existing nodes (reflexive & selected visual states)
210
+ @circle.selectAll("circle").style("fill", (d) ->
211
+ (if (d is @selected_node) then d3.rgb(branch_color(d)).brighter().toString() else d3.rgb(branch_color(d)))
212
+ ).classed "reflexive", (d) ->
213
+ d.reflexive
214
+
215
+ @circle.selectAll("text")
216
+ .attr("x", (d) -> node_size(d)+5)
217
+ .attr("y", (d) -> node_size(d)/2)
218
+
219
+ # add new nodes
220
+ g = @circle.enter().append("svg:g")
221
+
222
+ # reposition drag line
223
+ vis = @
224
+ g.append("svg:circle").attr("class", "node")
225
+ .attr("branch", (d) -> d.branch.name)
226
+ .attr("r", (d) -> node_size(d))
227
+ .style("fill", (d) -> (d3.rgb(branch_color(d))))
228
+ .style("stroke", (d) -> d3.rgb(branch_color(d)).darker().toString())
229
+ .classed("reflexive", (d) -> d.reflexive)
230
+ .on("mouseover", (d) ->
231
+ d3.selectAll("circle").filter((d2) -> d != d2).transition().style "opacity", "0.25"
232
+ d3.selectAll("text").filter((d2) -> d != d2).transition().style "opacity", "0.10"
233
+ d3.selectAll("path.link").filter((d2) -> d != d2).transition().style "opacity", "0.10"
234
+ vis.getAuthorStats(d.branch.name))
235
+ .on("mouseout", (d) ->
236
+ d3.selectAll("circle").transition().style "opacity", "1"
237
+ d3.selectAll("text").transition().style "opacity", "1"
238
+ d3.selectAll("path").transition().style "opacity", "1"
239
+ vis.clearAuthorStats())
240
+ .on("mousedown", (d) ->
241
+ vis.mousedown_node = d
242
+ d3.selectAll("circle").filter((d2) ->
243
+ vis.neighbouring(vis.dom_node(d)[0].__data__.id, vis.dom_node(d2)[0].__data__.id)
244
+ ).transition().style "opacity", "1"
245
+ d3.selectAll("text").filter((d2) ->
246
+ vis.neighbouring(vis.dom_node(d)[0].__data__.id, vis.dom_node(d2)[0].__data__.id)
247
+ ).transition().style "opacity", "1"
248
+ d3.selectAll("path.link").filter((d2) ->
249
+ return false if d2 is undefined
250
+ return true if d2.source == vis.mousedown_node || d2.target == vis.mousedown_node
251
+ ).transition().style "opacity", "1")
252
+ .on("dblclick", (d) ->
253
+ vis.commitGraph.load(d.branch.name)
254
+ )
255
+ .call(@force.drag())
256
+
257
+ # show node IDs
258
+ g.append("svg:text")
259
+ .attr("x", (d) -> node_size(d)+5)
260
+ .attr("y", (d) -> node_size(d)/2)
261
+ .attr("class", "name").text (d) ->
262
+ d.branch.name + " " + d.branch.diff.add + " / " + d.branch.diff.del
263
+
264
+ # remove old nodes
265
+ @circle.exit().remove()
266
+
267
+ # set the graph in motion
268
+ @force.start()
269
+
270
+ clear_filters : () ->
271
+ @nodes = @all_nodes
272
+ @links = @all_links
273
+ @branches = @all_branches
274
+ @branch_names = @all_branch_names
275
+
276
+ apply_filters: () ->
277
+ @clear_filters()
278
+
279
+ #filter branches merged with master
280
+ if $("#filter_merged_checkbox").is(":checked")
281
+ @filter_merged_with_master()
282
+
283
+ #filter branches merged with master
284
+ if $("#filter_remotes_checkbox").is(":checked")
285
+ @filter_remotes()
286
+
287
+ #filter branches by name
288
+ filter_name_query = $("#filter_names_input").val()
289
+ @filter_branch_names(filter_name_query) if filter_name_query.length > 0
290
+
291
+ additional_requests = false
292
+ #filter branches containing commit
293
+ show_commit_sha = $("#show_commit_input").val()
294
+ exclude_commit_sha = $("#exclude_commit_input").val()
295
+
296
+ if show_commit_sha.length > 0 || exclude_commit_sha.length > 0
297
+ additional_requests = true
298
+ @filter_branch_commits(show_commit_sha, exclude_commit_sha)
299
+
300
+ # if we arent making any more requests for this data then
301
+ # restart, otherwise the additional requests callback will make restart call
302
+ if !additional_requests
303
+ @restart()
304
+
305
+ dom_node: (data) ->
306
+ return if !data.branch
307
+ $("circle[branch=" + "'#{data.branch.name}'" + "]")
308
+
309
+ filter_merged_with_master: () ->
310
+ @nodes = $.grep @nodes, (node, i) =>
311
+ if node.branch.merged_with_master is true
312
+ return true if node.branch.name == "master"
313
+ @links = $.grep @links, (link, i) ->
314
+ return false if link.source == node or link.target == node
315
+ true
316
+ @branches = $.grep @branches, (branch, i) ->
317
+ return false if branch == node.branch
318
+ true
319
+ @branch_names = $.grep @branch_names, (name, i) ->
320
+ return false if name == node.branch.name
321
+ true
322
+ return false
323
+ true
324
+
325
+ filter_remotes: () ->
326
+ @nodes = $.grep @nodes, (node, i) =>
327
+ if node.branch.remote is true
328
+ return true if node.branch.name == "master"
329
+ @links = $.grep @links, (link, i) ->
330
+ return false if link.source == node or link.target == node
331
+ true
332
+ @branches = $.grep @branches, (branch, i) ->
333
+ return false if branch == node.branch
334
+ true
335
+ @branch_names = $.grep @branch_names, (name, i) ->
336
+ return false if name == node.branch.name
337
+ true
338
+ return false
339
+ true
340
+
341
+ filter_branch_names: (query) ->
342
+ @nodes = $.grep @nodes, (node, i) =>
343
+ if node.branch.name.indexOf(query) == -1
344
+ return true if node.branch.name == "master"
345
+ @links = $.grep @links, (link, i) ->
346
+ return false if link.source == node or link.target == node
347
+ true
348
+ @branches = $.grep @branches, (branch, i) ->
349
+ return false if branch == node.branch
350
+ true
351
+ @branch_names = $.grep @branch_names, (name, i) ->
352
+ return false if name == node.branch.name
353
+ true
354
+ return false
355
+ true
356
+
357
+ filter_branch_commits: (include_commit_sha, exclude_commit_sha) ->
358
+ json_data = {include: include_commit_sha, exclude: exclude_commit_sha}
359
+ $.get "/filter_branch_commits.json", json_data, (data) =>
360
+ branch_names = data
361
+ @nodes = $.grep @nodes, (node, i) =>
362
+ if $.inArray(node.branch.name, branch_names) is -1
363
+ return true if node.branch.name == "master"
364
+ @links = $.grep @links, (link, i) ->
365
+ return false if link.source == node or link.target == node
366
+ true
367
+ @branches = $.grep @branches, (branch, i) ->
368
+ return false if branch == node.branch
369
+ true
370
+ @branch_names = $.grep @branch_names, (name, i) ->
371
+ return false if name == node.branch.name
372
+ true
373
+ return false
374
+ true
375
+ @restart()
376
+
377
+ branch_color = (node) ->
378
+ return "#1f77b4" if node.branch.name is "master"
379
+ return "#9CDECD" if node.branch.merged_with_master
380
+ #color based on additions and deletions
381
+ branch_diff = node.branch.diff.add - node.branch.diff.del
382
+ if branch_diff > 100
383
+ "#6ACD72"
384
+ else if branch_diff > 0 && branch_diff <= 100
385
+ "#FFF48F"
386
+ else
387
+ "#C3554B"
388
+
389
+ node_size = (node_data) ->
390
+ rad = 5 * node_data.size
391
+
392
+ rad = 5 if rad < 5
393
+ rad = 20 if rad > 20
394
+ return rad
395
+
396
+ recalculate_node_sizes: () ->
397
+ percent_diff = 0.0
398
+ total_diff = 0.0
399
+ # recalculate total diff of all filtered branches
400
+ $.each @branches, (i, branch) ->
401
+ total_diff += branch.diff.add + branch.diff.del
402
+ average_diff = total_diff / @branches.length
403
+
404
+ # recalculate size for each node based on new average
405
+ $.each @nodes, (i, node) ->
406
+ if node.branch.name != "master"
407
+ node.size = (node.branch.diff.add + node.branch.diff.del) / average_diff
408
+
409
+ neighbouring: (node_id, other_id) ->
410
+ @linked_nodes[node_id + ", " + other_id] == 1 ||
411
+ @linked_nodes[other_id + ", " + node_id] == 1 ||
412
+ node_id == other_id
413
+
414
+ getAuthorStats: (branch_name) ->
415
+ $.get "/author_stats.json", {ref: branch_name}, (data) ->
416
+ $("#authors-list").html(data)
417
+
418
+ clearAuthorStats: ->
419
+ $("#authors-list").empty()
420
+
421
+
422
+
423
+ Visualisation.BranchGraph = BranchGraph
424
+ Visualisation.branchGraph = new BranchGraph()