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 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 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 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 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 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 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 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 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 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
... ...