Commit b8f070f0d17f20240376e48aa8dcad23c1d23ece

Authored by Sato Hiroyuki
1 parent a29c883b

Rename to coffee.

app/assets/javascripts/branch-graph.js
... ... @@ -1,398 +0,0 @@
1   -!function(){
2   -
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   - };
65   -
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",
90   - 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",
98   - 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],
114   - 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],
130   - "stroke-width": 2
131   - });
132   -
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],
169   - "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   - }
192   -
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",
259   - title: refs
260   - });
261   -
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,
303   - 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",
337   - "font-weight": "bold"
338   - });
339   -
340   - idText.attr({
341   - "fill": "#AAA"
342   - });
343   -
344   - textWrap(messageText, boxWidth - 50);
345   -
346   - var rect = this.rect(x - 10, y - 10, boxWidth, 100, 4).attr({
347   - "fill": "#FFF",
348   - "stroke": "#000",
349   - "stroke-linecap": "round",
350   - "stroke-width": 2
351   - });
352   - var tooltip = this.set(rect, textSet);
353   -
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   -};
365   -
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   - });
377   -
378   - var words = content.split(" ");
379   - var x = 0, s = [];
380   - for ( var i = 0; i < words.length; i++) {
381   -
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   -}
app/assets/javascripts/branch-graph.js.coffee 0 → 100644
... ... @@ -0,0 +1,398 @@
  1 +!function(){
  2 +
  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 + };
  65 +
  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",
  90 + 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",
  98 + 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],
  114 + 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],
  130 + "stroke-width": 2
  131 + });
  132 +
  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],
  169 + "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 + }
  192 +
  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",
  259 + title: refs
  260 + });
  261 +
  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,
  303 + 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",
  337 + "font-weight": "bold"
  338 + });
  339 +
  340 + idText.attr({
  341 + "fill": "#AAA"
  342 + });
  343 +
  344 + textWrap(messageText, boxWidth - 50);
  345 +
  346 + var rect = this.rect(x - 10, y - 10, boxWidth, 100, 4).attr({
  347 + "fill": "#FFF",
  348 + "stroke": "#000",
  349 + "stroke-linecap": "round",
  350 + "stroke-width": 2
  351 + });
  352 + var tooltip = this.set(rect, textSet);
  353 +
  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 +};
  365 +
  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 + });
  377 +
  378 + var words = content.split(" ");
  379 + var x = 0, s = [];
  380 + for ( var i = 0; i < words.length; i++) {
  381 +
  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 +}
... ...