Commit 197f9abedfd689ba46bd12fa219e43f30996e23a

Authored by Sato Hiroyuki
1 parent b8f070f0

Covert to coffee.

Showing 1 changed file with 254 additions and 379 deletions   Show diff stats
app/assets/javascripts/branch-graph.js.coffee
1 -!function(){ 1 +class BranchGraph
  2 + constructor: (@element, @options) ->
  3 + @preparedCommits = {}
  4 + @mtime = 0
  5 + @mspace = 0
  6 + @parents = {}
  7 + @colors = ["#000"]
  8 + @load()
2 9
3 - var BranchGraph = function(element, options){  
4 - this.element = element;  
5 - this.options = options;  
6 -  
7 - this.preparedCommits = {};  
8 - this.mtime = 0;  
9 - this.mspace = 0;  
10 - this.parents = {};  
11 - this.colors = ["#000"];  
12 -  
13 - this.load();  
14 - };  
15 -  
16 - BranchGraph.prototype.load = function(){  
17 - $.ajax({  
18 - url: this.options.url,  
19 - method: 'get',  
20 - dataType: 'json',  
21 - success: $.proxy(function(data){  
22 - $('.loading', this.element).hide();  
23 - this.prepareData(data.days, data.commits);  
24 - this.buildGraph();  
25 - }, this)  
26 - });  
27 - };  
28 -  
29 - BranchGraph.prototype.prepareData = function(days, commits){  
30 - this.days = days;  
31 - this.dayCount = days.length;  
32 - this.commits = commits;  
33 - this.commitCount = commits.length;  
34 -  
35 - this.collectParents();  
36 -  
37 - this.mtime += 4;  
38 - this.mspace += 10;  
39 - for (var i = 0; i < this.commitCount; i++) {  
40 - if (this.commits[i].id in this.parents) {  
41 - this.commits[i].isParent = true;  
42 - }  
43 - this.preparedCommits[this.commits[i].id] = this.commits[i];  
44 - }  
45 - this.collectColors();  
46 - };  
47 -  
48 - BranchGraph.prototype.collectParents = function(){  
49 - for (var i = 0; i < this.commitCount; i++) {  
50 - for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {  
51 - this.parents[this.commits[i].parents[j][0]] = true;  
52 - }  
53 - this.mtime = Math.max(this.mtime, this.commits[i].time);  
54 - this.mspace = Math.max(this.mspace, this.commits[i].space);  
55 - }  
56 - };  
57 -  
58 - BranchGraph.prototype.collectColors = function(){  
59 - for (var k = 0; k < this.mspace; k++) {  
60 - this.colors.push(Raphael.getColor(.8));  
61 - // Skipping a few colors in the spectrum to get more contrast between colors  
62 - Raphael.getColor();Raphael.getColor();  
63 - }  
64 - }; 10 + load: ->
  11 + $.ajax
  12 + url: @options.url
  13 + method: "get"
  14 + dataType: "json"
  15 + success: $.proxy((data) ->
  16 + $(".loading", @element).hide()
  17 + @prepareData data.days, data.commits
  18 + @buildGraph()
  19 + , this)
65 20
66 - BranchGraph.prototype.buildGraph = function(){  
67 - var graphWidth = $(this.element).width()  
68 - , ch = this.mspace * 20 + 100  
69 - , cw = Math.max(graphWidth, this.mtime * 20 + 260)  
70 - , r = Raphael(this.element.get(0), cw, ch)  
71 - , top = r.set()  
72 - , cuday = 0  
73 - , cumonth = ""  
74 - , offsetX = 20  
75 - , offsetY = 60  
76 - , barWidth = Math.max(graphWidth, this.dayCount * 20 + 320)  
77 - , scrollLeft = cw;  
78 -  
79 - this.raphael = r;  
80 -  
81 - r.rect(0, 0, barWidth, 20).attr({fill: "#222"});  
82 - r.rect(0, 20, barWidth, 20).attr({fill: "#444"});  
83 -  
84 - for (mm = 0; mm < this.dayCount; mm++) {  
85 - if(this.days[mm] != null){  
86 - if(cuday != this.days[mm][0]){  
87 - // Dates  
88 - r.text(offsetX + mm * 20, 31, this.days[mm][0]).attr({  
89 - font: "12px Monaco, monospace", 21 + prepareData: (@days, @commits) ->
  22 + @collectParents()
  23 + @mtime += 4
  24 + @mspace += 10
  25 +
  26 + for c in @commits
  27 + c.isParent = true if c.id of @parents
  28 + @preparedCommits[c.id] = c
  29 +
  30 + @collectColors()
  31 +
  32 + collectParents: ->
  33 + for c in @commits
  34 + @mtime = Math.max(@mtime, c.time)
  35 + @mspace = Math.max(@mspace, c.space)
  36 + for p in c.parents
  37 + @parents[p[0]] = true
  38 +
  39 + collectColors: ->
  40 + k = 0
  41 + while k < @mspace
  42 + @colors.push Raphael.getColor(.8)
  43 + # Skipping a few colors in the spectrum to get more contrast between colors
  44 + Raphael.getColor()
  45 + Raphael.getColor()
  46 + k++
  47 +
  48 + buildGraph: ->
  49 + graphWidth = $(@element).width()
  50 + ch = @mspace * 20 + 100
  51 + cw = Math.max(graphWidth, @mtime * 20 + 260)
  52 + r = Raphael(@element.get(0), cw, ch)
  53 + top = r.set()
  54 + cuday = 0
  55 + cumonth = ""
  56 + offsetX = 20
  57 + offsetY = 60
  58 + barWidth = Math.max(graphWidth, @days.length * 20 + 320)
  59 + scrollLeft = cw
  60 + @raphael = r
  61 + r.rect(0, 0, barWidth, 20).attr fill: "#222"
  62 + r.rect(0, 20, barWidth, 20).attr fill: "#444"
  63 +
  64 + for day, mm in @days
  65 + if cuday isnt day[0]
  66 + # Dates
  67 + r.text(offsetX + mm * 20, 31, day[0])
  68 + .attr(
  69 + font: "12px Monaco, monospace"
90 fill: "#DDD" 70 fill: "#DDD"
91 - });  
92 - cuday = this.days[mm][0];  
93 - }  
94 - if(cumonth != this.days[mm][1]){  
95 - // Months  
96 - r.text(offsetX + mm * 20, 11, this.days[mm][1]).attr({  
97 - font: "12px Monaco, monospace", 71 + )
  72 + cuday = day[0]
  73 +
  74 + if cumonth isnt day[1]
  75 + # Months
  76 + r.text(offsetX + mm * 20, 11, day[1])
  77 + .attr(
  78 + font: "12px Monaco, monospace"
98 fill: "#EEE" 79 fill: "#EEE"
99 - });  
100 - cumonth = this.days[mm][1];  
101 - }  
102 - }  
103 - }  
104 -  
105 - for (i = 0; i < this.commitCount; i++) {  
106 - var x = offsetX + 20 * this.commits[i].time  
107 - , y = offsetY + 10 * this.commits[i].space  
108 - , c  
109 - , ps;  
110 -  
111 - // Draw dot  
112 - r.circle(x, y, 3).attr({  
113 - fill: this.colors[this.commits[i].space], 80 + )
  81 + cumonth = day[1]
  82 +
  83 + for commit in @commits
  84 + x = offsetX + 20 * commit.time
  85 + y = offsetY + 10 * commit.space
  86 + # Draw dot
  87 + r.circle(x, y, 3).attr(
  88 + fill: @colors[commit.space]
114 stroke: "none" 89 stroke: "none"
115 - });  
116 -  
117 - // Draw lines  
118 - for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {  
119 - c = this.preparedCommits[this.commits[i].parents[j][0]];  
120 - ps = this.commits[i].parents[j][1];  
121 - var cx = offsetX + 20 * c.time  
122 - , cy = offsetY + 10 * c.space  
123 - , psy = offsetY + 10 * ps;  
124 - if (c.space == this.commits[i].space && c.space == ps) {  
125 - r.path([  
126 - "M", x, y,  
127 - "L", cx, cy  
128 - ]).attr({  
129 - stroke: this.colors[c.space], 90 + )
  91 +
  92 + # Draw lines
  93 + for parent in commit.parents
  94 + parentCommit = @preparedCommits[parent[0]]
  95 + parentX = offsetX + 20 * parentCommit.time
  96 + parentY1 = offsetY + 10 * parentCommit.space
  97 + parentY2 = offsetY + 10 * parent[1]
  98 + if parentCommit.space is commit.space and parentCommit.space is parent[1]
  99 + r.path(["M", x, y, "L", parentX, parentY1]).attr(
  100 + stroke: @colors[parentCommit.space]
130 "stroke-width": 2 101 "stroke-width": 2
131 - }); 102 + )
  103 +
  104 + else if parentCommit.space < commit.space
  105 + if y is parentY2
  106 + r.path(["M", x - 5, y, "l-5,-2,0,4,5,-2", "L", x - 10, y, "L", x - 15, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr(
  107 + stroke: @colors[commit.space]
  108 + "stroke-width": 2
  109 + )
  110 +
  111 + else
  112 + r.path(["M", x - 3, y - 6, "l-4,-3,4,-2,0,5", "L", x - 5, y - 10, "L", x - 10, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr(
  113 + stroke: @colors[commit.space]
  114 + "stroke-width": 2
  115 + )
132 116
133 - } else if (c.space < this.commits[i].space) {  
134 - if (y == psy) {  
135 - r.path([  
136 - "M", x - 5, y,  
137 - "l-5,-2,0,4,5,-2",  
138 - "L", x - 10, y,  
139 - "L", x - 15, psy,  
140 - "L", cx + 5, psy,  
141 - "L", cx, cy])  
142 - .attr({  
143 - stroke: this.colors[this.commits[i].space],  
144 - "stroke-width": 2  
145 - });  
146 - } else {  
147 - r.path([  
148 - "M", x - 3, y - 6,  
149 - "l-4,-3,4,-2,0,5",  
150 - "L", x - 5, y - 10,  
151 - "L", x - 10, psy,  
152 - "L", cx + 5, psy,  
153 - "L", cx, cy])  
154 - .attr({  
155 - stroke: this.colors[this.commits[i].space],  
156 - "stroke-width": 2  
157 - });  
158 - }  
159 - } else {  
160 - r.path([  
161 - "M", x - 3, y + 6,  
162 - "l-4,3,4,2,0,-5",  
163 - "L", x - 5, y + 10,  
164 - "L", x - 10, psy,  
165 - "L", cx + 5, psy,  
166 - "L", cx, cy])  
167 - .attr({  
168 - stroke: this.colors[c.space], 117 + else
  118 + r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5", "L", x - 5, y + 10, "L", x - 10, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr(
  119 + stroke: @colors[parentCommit.space]
169 "stroke-width": 2 120 "stroke-width": 2
170 - });  
171 - }  
172 - }  
173 -  
174 - if (this.commits[i].refs) {  
175 - this.appendLabel(x, y, this.commits[i].refs);  
176 - }  
177 -  
178 - // mark commit and displayed in the center  
179 - if (this.commits[i].id == this.options.commit_id) {  
180 - r.path([  
181 - 'M', x, y - 5,  
182 - 'L', x + 4, y - 15,  
183 - 'L', x - 4, y - 15,  
184 - 'Z'  
185 - ]).attr({  
186 - "fill": "#000",  
187 - "fill-opacity": .7,  
188 - "stroke": "none"  
189 - });  
190 - scrollLeft = x - graphWidth / 2;  
191 - } 121 + )
192 122
193 - this.appendAnchor(top, this.commits[i], x, y);  
194 - }  
195 - top.toFront();  
196 - this.element.scrollLeft(scrollLeft);  
197 - this.bindEvents();  
198 - };  
199 -  
200 - BranchGraph.prototype.bindEvents = function(){  
201 - var drag = {}  
202 - , element = this.element;  
203 -  
204 - var dragger = function(event){  
205 - element.scrollLeft(drag.sl - (event.clientX - drag.x));  
206 - element.scrollTop(drag.st - (event.clientY - drag.y));  
207 - };  
208 -  
209 - element.on({  
210 - mousedown: function (event) {  
211 - drag = {  
212 - x: event.clientX,  
213 - y: event.clientY,  
214 - st: element.scrollTop(),  
215 - sl: element.scrollLeft()  
216 - };  
217 - $(window).on('mousemove', dragger);  
218 - }  
219 - });  
220 - $(window).on({  
221 - mouseup: function(){  
222 - //bars.animate({opacity: 0}, 300);  
223 - $(window).off('mousemove', dragger);  
224 - },  
225 - keydown: function(event){  
226 - if(event.keyCode == 37){  
227 - // left  
228 - element.scrollLeft( element.scrollLeft() - 50);  
229 - }  
230 - if(event.keyCode == 38){  
231 - // top  
232 - element.scrollTop( element.scrollTop() - 50);  
233 - }  
234 - if(event.keyCode == 39){  
235 - // right  
236 - element.scrollLeft( element.scrollLeft() + 50);  
237 - }  
238 - if(event.keyCode == 40){  
239 - // bottom  
240 - element.scrollTop( element.scrollTop() + 50);  
241 - }  
242 - }  
243 - });  
244 - };  
245 -  
246 - BranchGraph.prototype.appendLabel = function(x, y, refs){  
247 - var r = this.raphael  
248 - , shortrefs = refs  
249 - , text, textbox, rect;  
250 -  
251 - if (shortrefs.length > 17){  
252 - // Truncate if longer than 15 chars  
253 - shortrefs = shortrefs.substr(0,15) + "…";  
254 - }  
255 -  
256 - text = r.text(x+5, y+8 + 10, shortrefs).attr({  
257 - font: "10px Monaco, monospace",  
258 - fill: "#FFF", 123 + @appendLabel x, y, commit.refs if commit.refs
  124 +
  125 + # Mark commit and displayed in the center
  126 + if commit.id is @options.commit_id
  127 + r.path(["M", x, y - 5, "L", x + 4, y - 15, "L", x - 4, y - 15, "Z"]).attr(
  128 + fill: "#000"
  129 + "fill-opacity": .7
  130 + stroke: "none"
  131 + )
  132 +
  133 + scrollLeft = x - graphWidth / 2
  134 +
  135 + @appendAnchor top, commit, x, y
  136 +
  137 + top.toFront()
  138 + @element.scrollLeft scrollLeft
  139 + @bindEvents()
  140 +
  141 + bindEvents: ->
  142 + drag = {}
  143 + element = @element
  144 + dragger = (event) ->
  145 + element.scrollLeft drag.sl - (event.clientX - drag.x)
  146 + element.scrollTop drag.st - (event.clientY - drag.y)
  147 +
  148 + element.on mousedown: (event) ->
  149 + drag =
  150 + x: event.clientX
  151 + y: event.clientY
  152 + st: element.scrollTop()
  153 + sl: element.scrollLeft()
  154 + $(window).on "mousemove", dragger
  155 +
  156 + $(window).on
  157 + mouseup: ->
  158 + $(window).off "mousemove", dragger
  159 + keydown: (event) ->
  160 + # left
  161 + element.scrollLeft element.scrollLeft() - 50 if event.keyCode is 37
  162 + # top
  163 + element.scrollTop element.scrollTop() - 50 if event.keyCode is 38
  164 + # right
  165 + element.scrollLeft element.scrollLeft() + 50 if event.keyCode is 39
  166 + # bottom
  167 + element.scrollTop element.scrollTop() + 50 if event.keyCode is 40
  168 +
  169 + appendLabel: (x, y, refs) ->
  170 + r = @raphael
  171 + shortrefs = refs
  172 + # Truncate if longer than 15 chars
  173 + shortrefs = shortrefs.substr(0, 15) + "…" if shortrefs.length > 17
  174 + text = r.text(x + 5, y + 8 + 10, shortrefs).attr(
  175 + font: "10px Monaco, monospace"
  176 + fill: "#FFF"
259 title: refs 177 title: refs
260 - }); 178 + )
  179 + textbox = text.getBBox()
  180 + text.transform ["t", textbox.height / -4, textbox.width / 2 + 5, "r90"]
  181 + # Create rectangle based on the size of the textbox
  182 + rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr(
  183 + fill: "#000"
  184 + "fill-opacity": .7
  185 + stroke: "none"
  186 + )
  187 + triangle = r.path(["M", x, y + 5, "L", x + 4, y + 15, "L", x - 4, y + 15, "Z"]).attr(
  188 + fill: "#000"
  189 + "fill-opacity": .7
  190 + stroke: "none"
  191 + )
  192 + # Rotate and reposition rectangle over text
  193 + rect.transform ["r", 90, x, y, "t", 15, -9]
  194 + # Set text to front
  195 + text.toFront()
261 196
262 - textbox = text.getBBox();  
263 - text.transform([  
264 - 't', textbox.height/-4, textbox.width/2 + 5,  
265 - 'r90'  
266 - ]);  
267 -  
268 - // Create rectangle based on the size of the textbox  
269 - rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr({  
270 - "fill": "#000",  
271 - "fill-opacity": .7,  
272 - "stroke": "none"  
273 - });  
274 -  
275 - triangle = r.path([  
276 - 'M', x, y + 5,  
277 - 'L', x + 4, y + 15,  
278 - 'L', x - 4, y + 15,  
279 - 'Z'  
280 - ]).attr({  
281 - "fill": "#000",  
282 - "fill-opacity": .7,  
283 - "stroke": "none"  
284 - });  
285 -  
286 - // Rotate and reposition rectangle over text  
287 - rect.transform([  
288 - 'r', 90, x, y,  
289 - 't', 15, -9  
290 - ]);  
291 -  
292 - // Set text to front  
293 - text.toFront();  
294 - };  
295 -  
296 - BranchGraph.prototype.appendAnchor = function(top, commit, x, y) {  
297 - var r = this.raphael  
298 - , options = this.options  
299 - , anchor;  
300 - anchor = r.circle(x, y, 10).attr({  
301 - fill: "#000",  
302 - opacity: 0, 197 + appendAnchor: (top, commit, x, y) ->
  198 + r = @raphael
  199 + options = @options
  200 + anchor = r.circle(x, y, 10).attr(
  201 + fill: "#000"
  202 + opacity: 0
303 cursor: "pointer" 203 cursor: "pointer"
304 - })  
305 - .click(function(){  
306 - window.open(options.commit_url.replace('%s', commit.id), '_blank');  
307 - })  
308 - .hover(function(){  
309 - this.tooltip = r.commitTooltip(x, y + 5, commit);  
310 - top.push(this.tooltip.insertBefore(this));  
311 - }, function(){  
312 - this.tooltip && this.tooltip.remove() && delete this.tooltip;  
313 - });  
314 - top.push(anchor);  
315 - };  
316 -  
317 - this.BranchGraph = BranchGraph;  
318 -  
319 -}(this);  
320 -Raphael.fn.commitTooltip = function(x, y, commit){  
321 - var icon, nameText, idText, messageText  
322 - , boxWidth = 300  
323 - , boxHeight = 200;  
324 -  
325 - icon = this.image(commit.author.icon, x, y, 20, 20);  
326 - nameText = this.text(x + 25, y + 10, commit.author.name);  
327 - idText = this.text(x, y + 35, commit.id);  
328 - messageText = this.text(x, y + 50, commit.message);  
329 -  
330 - textSet = this.set(icon, nameText, idText, messageText).attr({  
331 - "text-anchor": "start",  
332 - "font": "12px Monaco, monospace"  
333 - });  
334 -  
335 - nameText.attr({  
336 - "font": "14px Arial", 204 + ).click(->
  205 + window.open options.commit_url.replace("%s", commit.id), "_blank"
  206 + ).hover(->
  207 + @tooltip = r.commitTooltip(x, y + 5, commit)
  208 + top.push @tooltip.insertBefore(this)
  209 + , ->
  210 + @tooltip and @tooltip.remove() and delete @tooltip
  211 + )
  212 + top.push anchor
  213 +
  214 +Raphael::commitTooltip = (x, y, commit) ->
  215 + icon = undefined
  216 + nameText = undefined
  217 + idText = undefined
  218 + messageText = undefined
  219 + boxWidth = 300
  220 + boxHeight = 200
  221 + icon = @image(commit.author.icon, x, y, 20, 20)
  222 + nameText = @text(x + 25, y + 10, commit.author.name)
  223 + idText = @text(x, y + 35, commit.id)
  224 + messageText = @text(x, y + 50, commit.message)
  225 + textSet = @set(icon, nameText, idText, messageText).attr(
  226 + "text-anchor": "start"
  227 + font: "12px Monaco, monospace"
  228 + )
  229 + nameText.attr(
  230 + font: "14px Arial"
337 "font-weight": "bold" 231 "font-weight": "bold"
338 - });  
339 -  
340 - idText.attr({  
341 - "fill": "#AAA"  
342 - });  
343 -  
344 - textWrap(messageText, boxWidth - 50); 232 + )
345 233
346 - var rect = this.rect(x - 10, y - 10, boxWidth, 100, 4).attr({  
347 - "fill": "#FFF",  
348 - "stroke": "#000",  
349 - "stroke-linecap": "round", 234 + idText.attr fill: "#AAA"
  235 + @textWrap messageText, boxWidth - 50
  236 + rect = @rect(x - 10, y - 10, boxWidth, 100, 4).attr(
  237 + fill: "#FFF"
  238 + stroke: "#000"
  239 + "stroke-linecap": "round"
350 "stroke-width": 2 240 "stroke-width": 2
351 - });  
352 - var tooltip = this.set(rect, textSet); 241 + )
  242 + tooltip = @set(rect, textSet)
  243 + rect.attr(
  244 + height: tooltip.getBBox().height + 10
  245 + width: tooltip.getBBox().width + 10
  246 + )
  247 +
  248 + tooltip.transform ["t", 20, 20]
  249 + tooltip
353 250
354 - rect.attr({  
355 - "height" : tooltip.getBBox().height + 10,  
356 - "width" : tooltip.getBBox().width + 10  
357 - });  
358 -  
359 - tooltip.transform([  
360 - 't', 20, 20  
361 - ]);  
362 -  
363 - return tooltip;  
364 -}; 251 +Raphael::textWrap = (t, width) ->
  252 + content = t.attr("text")
  253 + abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  254 + t.attr text: abc
  255 + letterWidth = t.getBBox().width / abc.length
  256 + t.attr text: content
  257 + words = content.split(" ")
  258 + x = 0
  259 + s = []
365 260
366 -function textWrap(t, width) {  
367 - var content = t.attr("text");  
368 - var abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";  
369 - t.attr({  
370 - "text" : abc  
371 - });  
372 - var letterWidth = t.getBBox().width / abc.length;  
373 -  
374 - t.attr({  
375 - "text" : content  
376 - }); 261 + for word in words
  262 + if x + (word.length * letterWidth) > width
  263 + s.push "\n"
  264 + x = 0
  265 + x += word.length * letterWidth
  266 + s.push word + " "
377 267
378 - var words = content.split(" ");  
379 - var x = 0, s = [];  
380 - for ( var i = 0; i < words.length; i++) { 268 + t.attr text: s.join("")
  269 + b = t.getBBox()
  270 + h = Math.abs(b.y2) - Math.abs(b.y) + 1
  271 + t.attr y: b.y + h
381 272
382 - var l = words[i].length;  
383 - if (x + (l * letterWidth) > width) {  
384 - s.push("\n");  
385 - x = 0;  
386 - }  
387 - x += l * letterWidth;  
388 - s.push(words[i] + " ");  
389 - }  
390 - t.attr({  
391 - "text" : s.join("")  
392 - });  
393 - var b = t.getBBox()  
394 - , h = Math.abs(b.y2) - Math.abs(b.y) + 1;  
395 - t.attr({  
396 - "y": b.y + h  
397 - });  
398 -} 273 +@BranchGraph = BranchGraph