-!function(){
+class BranchGraph
+ constructor: (@element, @options) ->
+ @preparedCommits = {}
+ @mtime = 0
+ @mspace = 0
+ @parents = {}
+ @colors = ["#000"]
+ @load()
- var BranchGraph = function(element, options){
- this.element = element;
- this.options = options;
-
- this.preparedCommits = {};
- this.mtime = 0;
- this.mspace = 0;
- this.parents = {};
- this.colors = ["#000"];
-
- this.load();
- };
-
- BranchGraph.prototype.load = function(){
- $.ajax({
- url: this.options.url,
- method: 'get',
- dataType: 'json',
- success: $.proxy(function(data){
- $('.loading', this.element).hide();
- this.prepareData(data.days, data.commits);
- this.buildGraph();
- }, this)
- });
- };
-
- BranchGraph.prototype.prepareData = function(days, commits){
- this.days = days;
- this.dayCount = days.length;
- this.commits = commits;
- this.commitCount = commits.length;
-
- this.collectParents();
-
- this.mtime += 4;
- this.mspace += 10;
- for (var i = 0; i < this.commitCount; i++) {
- if (this.commits[i].id in this.parents) {
- this.commits[i].isParent = true;
- }
- this.preparedCommits[this.commits[i].id] = this.commits[i];
- }
- this.collectColors();
- };
-
- BranchGraph.prototype.collectParents = function(){
- for (var i = 0; i < this.commitCount; i++) {
- for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
- this.parents[this.commits[i].parents[j][0]] = true;
- }
- this.mtime = Math.max(this.mtime, this.commits[i].time);
- this.mspace = Math.max(this.mspace, this.commits[i].space);
- }
- };
-
- BranchGraph.prototype.collectColors = function(){
- for (var k = 0; k < this.mspace; k++) {
- this.colors.push(Raphael.getColor(.8));
- // Skipping a few colors in the spectrum to get more contrast between colors
- Raphael.getColor();Raphael.getColor();
- }
- };
+ load: ->
+ $.ajax
+ url: @options.url
+ method: "get"
+ dataType: "json"
+ success: $.proxy((data) ->
+ $(".loading", @element).hide()
+ @prepareData data.days, data.commits
+ @buildGraph()
+ , this)
- BranchGraph.prototype.buildGraph = function(){
- var graphWidth = $(this.element).width()
- , ch = this.mspace * 20 + 100
- , cw = Math.max(graphWidth, this.mtime * 20 + 260)
- , r = Raphael(this.element.get(0), cw, ch)
- , top = r.set()
- , cuday = 0
- , cumonth = ""
- , offsetX = 20
- , offsetY = 60
- , barWidth = Math.max(graphWidth, this.dayCount * 20 + 320)
- , scrollLeft = cw;
-
- this.raphael = r;
-
- r.rect(0, 0, barWidth, 20).attr({fill: "#222"});
- r.rect(0, 20, barWidth, 20).attr({fill: "#444"});
-
- for (mm = 0; mm < this.dayCount; mm++) {
- if(this.days[mm] != null){
- if(cuday != this.days[mm][0]){
- // Dates
- r.text(offsetX + mm * 20, 31, this.days[mm][0]).attr({
- font: "12px Monaco, monospace",
+ prepareData: (@days, @commits) ->
+ @collectParents()
+ @mtime += 4
+ @mspace += 10
+
+ for c in @commits
+ c.isParent = true if c.id of @parents
+ @preparedCommits[c.id] = c
+
+ @collectColors()
+
+ collectParents: ->
+ for c in @commits
+ @mtime = Math.max(@mtime, c.time)
+ @mspace = Math.max(@mspace, c.space)
+ for p in c.parents
+ @parents[p[0]] = true
+
+ collectColors: ->
+ k = 0
+ while k < @mspace
+ @colors.push Raphael.getColor(.8)
+ # Skipping a few colors in the spectrum to get more contrast between colors
+ Raphael.getColor()
+ Raphael.getColor()
+ k++
+
+ buildGraph: ->
+ graphWidth = $(@element).width()
+ ch = @mspace * 20 + 100
+ cw = Math.max(graphWidth, @mtime * 20 + 260)
+ r = Raphael(@element.get(0), cw, ch)
+ top = r.set()
+ cuday = 0
+ cumonth = ""
+ offsetX = 20
+ offsetY = 60
+ barWidth = Math.max(graphWidth, @days.length * 20 + 320)
+ scrollLeft = cw
+ @raphael = r
+ r.rect(0, 0, barWidth, 20).attr fill: "#222"
+ r.rect(0, 20, barWidth, 20).attr fill: "#444"
+
+ for day, mm in @days
+ if cuday isnt day[0]
+ # Dates
+ r.text(offsetX + mm * 20, 31, day[0])
+ .attr(
+ font: "12px Monaco, monospace"
fill: "#DDD"
- });
- cuday = this.days[mm][0];
- }
- if(cumonth != this.days[mm][1]){
- // Months
- r.text(offsetX + mm * 20, 11, this.days[mm][1]).attr({
- font: "12px Monaco, monospace",
+ )
+ cuday = day[0]
+
+ if cumonth isnt day[1]
+ # Months
+ r.text(offsetX + mm * 20, 11, day[1])
+ .attr(
+ font: "12px Monaco, monospace"
fill: "#EEE"
- });
- cumonth = this.days[mm][1];
- }
- }
- }
-
- for (i = 0; i < this.commitCount; i++) {
- var x = offsetX + 20 * this.commits[i].time
- , y = offsetY + 10 * this.commits[i].space
- , c
- , ps;
-
- // Draw dot
- r.circle(x, y, 3).attr({
- fill: this.colors[this.commits[i].space],
+ )
+ cumonth = day[1]
+
+ for commit in @commits
+ x = offsetX + 20 * commit.time
+ y = offsetY + 10 * commit.space
+ # Draw dot
+ r.circle(x, y, 3).attr(
+ fill: @colors[commit.space]
stroke: "none"
- });
-
- // Draw lines
- for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
- c = this.preparedCommits[this.commits[i].parents[j][0]];
- ps = this.commits[i].parents[j][1];
- var cx = offsetX + 20 * c.time
- , cy = offsetY + 10 * c.space
- , psy = offsetY + 10 * ps;
- if (c.space == this.commits[i].space && c.space == ps) {
- r.path([
- "M", x, y,
- "L", cx, cy
- ]).attr({
- stroke: this.colors[c.space],
+ )
+
+ # Draw lines
+ for parent in commit.parents
+ parentCommit = @preparedCommits[parent[0]]
+ parentX = offsetX + 20 * parentCommit.time
+ parentY1 = offsetY + 10 * parentCommit.space
+ parentY2 = offsetY + 10 * parent[1]
+ if parentCommit.space is commit.space and parentCommit.space is parent[1]
+ r.path(["M", x, y, "L", parentX, parentY1]).attr(
+ stroke: @colors[parentCommit.space]
"stroke-width": 2
- });
+ )
+
+ else if parentCommit.space < commit.space
+ if y is parentY2
+ 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(
+ stroke: @colors[commit.space]
+ "stroke-width": 2
+ )
+
+ else
+ 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(
+ stroke: @colors[commit.space]
+ "stroke-width": 2
+ )
- } else if (c.space < this.commits[i].space) {
- if (y == psy) {
- r.path([
- "M", x - 5, y,
- "l-5,-2,0,4,5,-2",
- "L", x - 10, y,
- "L", x - 15, psy,
- "L", cx + 5, psy,
- "L", cx, cy])
- .attr({
- stroke: this.colors[this.commits[i].space],
- "stroke-width": 2
- });
- } else {
- r.path([
- "M", x - 3, y - 6,
- "l-4,-3,4,-2,0,5",
- "L", x - 5, y - 10,
- "L", x - 10, psy,
- "L", cx + 5, psy,
- "L", cx, cy])
- .attr({
- stroke: this.colors[this.commits[i].space],
- "stroke-width": 2
- });
- }
- } else {
- r.path([
- "M", x - 3, y + 6,
- "l-4,3,4,2,0,-5",
- "L", x - 5, y + 10,
- "L", x - 10, psy,
- "L", cx + 5, psy,
- "L", cx, cy])
- .attr({
- stroke: this.colors[c.space],
+ else
+ 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(
+ stroke: @colors[parentCommit.space]
"stroke-width": 2
- });
- }
- }
-
- if (this.commits[i].refs) {
- this.appendLabel(x, y, this.commits[i].refs);
- }
-
- // mark commit and displayed in the center
- if (this.commits[i].id == this.options.commit_id) {
- r.path([
- 'M', x, y - 5,
- 'L', x + 4, y - 15,
- 'L', x - 4, y - 15,
- 'Z'
- ]).attr({
- "fill": "#000",
- "fill-opacity": .7,
- "stroke": "none"
- });
- scrollLeft = x - graphWidth / 2;
- }
+ )
- this.appendAnchor(top, this.commits[i], x, y);
- }
- top.toFront();
- this.element.scrollLeft(scrollLeft);
- this.bindEvents();
- };
-
- BranchGraph.prototype.bindEvents = function(){
- var drag = {}
- , element = this.element;
-
- var dragger = function(event){
- element.scrollLeft(drag.sl - (event.clientX - drag.x));
- element.scrollTop(drag.st - (event.clientY - drag.y));
- };
-
- element.on({
- mousedown: function (event) {
- drag = {
- x: event.clientX,
- y: event.clientY,
- st: element.scrollTop(),
- sl: element.scrollLeft()
- };
- $(window).on('mousemove', dragger);
- }
- });
- $(window).on({
- mouseup: function(){
- //bars.animate({opacity: 0}, 300);
- $(window).off('mousemove', dragger);
- },
- keydown: function(event){
- if(event.keyCode == 37){
- // left
- element.scrollLeft( element.scrollLeft() - 50);
- }
- if(event.keyCode == 38){
- // top
- element.scrollTop( element.scrollTop() - 50);
- }
- if(event.keyCode == 39){
- // right
- element.scrollLeft( element.scrollLeft() + 50);
- }
- if(event.keyCode == 40){
- // bottom
- element.scrollTop( element.scrollTop() + 50);
- }
- }
- });
- };
-
- BranchGraph.prototype.appendLabel = function(x, y, refs){
- var r = this.raphael
- , shortrefs = refs
- , text, textbox, rect;
-
- if (shortrefs.length > 17){
- // Truncate if longer than 15 chars
- shortrefs = shortrefs.substr(0,15) + "…";
- }
-
- text = r.text(x+5, y+8 + 10, shortrefs).attr({
- font: "10px Monaco, monospace",
- fill: "#FFF",
+ @appendLabel x, y, commit.refs if commit.refs
+
+ # Mark commit and displayed in the center
+ if commit.id is @options.commit_id
+ r.path(["M", x, y - 5, "L", x + 4, y - 15, "L", x - 4, y - 15, "Z"]).attr(
+ fill: "#000"
+ "fill-opacity": .7
+ stroke: "none"
+ )
+
+ scrollLeft = x - graphWidth / 2
+
+ @appendAnchor top, commit, x, y
+
+ top.toFront()
+ @element.scrollLeft scrollLeft
+ @bindEvents()
+
+ bindEvents: ->
+ drag = {}
+ element = @element
+ dragger = (event) ->
+ element.scrollLeft drag.sl - (event.clientX - drag.x)
+ element.scrollTop drag.st - (event.clientY - drag.y)
+
+ element.on mousedown: (event) ->
+ drag =
+ x: event.clientX
+ y: event.clientY
+ st: element.scrollTop()
+ sl: element.scrollLeft()
+ $(window).on "mousemove", dragger
+
+ $(window).on
+ mouseup: ->
+ $(window).off "mousemove", dragger
+ keydown: (event) ->
+ # left
+ element.scrollLeft element.scrollLeft() - 50 if event.keyCode is 37
+ # top
+ element.scrollTop element.scrollTop() - 50 if event.keyCode is 38
+ # right
+ element.scrollLeft element.scrollLeft() + 50 if event.keyCode is 39
+ # bottom
+ element.scrollTop element.scrollTop() + 50 if event.keyCode is 40
+
+ appendLabel: (x, y, refs) ->
+ r = @raphael
+ shortrefs = refs
+ # Truncate if longer than 15 chars
+ shortrefs = shortrefs.substr(0, 15) + "…" if shortrefs.length > 17
+ text = r.text(x + 5, y + 8 + 10, shortrefs).attr(
+ font: "10px Monaco, monospace"
+ fill: "#FFF"
title: refs
- });
+ )
+ textbox = text.getBBox()
+ text.transform ["t", textbox.height / -4, textbox.width / 2 + 5, "r90"]
+ # Create rectangle based on the size of the textbox
+ rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr(
+ fill: "#000"
+ "fill-opacity": .7
+ stroke: "none"
+ )
+ triangle = r.path(["M", x, y + 5, "L", x + 4, y + 15, "L", x - 4, y + 15, "Z"]).attr(
+ fill: "#000"
+ "fill-opacity": .7
+ stroke: "none"
+ )
+ # Rotate and reposition rectangle over text
+ rect.transform ["r", 90, x, y, "t", 15, -9]
+ # Set text to front
+ text.toFront()
- textbox = text.getBBox();
- text.transform([
- 't', textbox.height/-4, textbox.width/2 + 5,
- 'r90'
- ]);
-
- // Create rectangle based on the size of the textbox
- rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr({
- "fill": "#000",
- "fill-opacity": .7,
- "stroke": "none"
- });
-
- triangle = r.path([
- 'M', x, y + 5,
- 'L', x + 4, y + 15,
- 'L', x - 4, y + 15,
- 'Z'
- ]).attr({
- "fill": "#000",
- "fill-opacity": .7,
- "stroke": "none"
- });
-
- // Rotate and reposition rectangle over text
- rect.transform([
- 'r', 90, x, y,
- 't', 15, -9
- ]);
-
- // Set text to front
- text.toFront();
- };
-
- BranchGraph.prototype.appendAnchor = function(top, commit, x, y) {
- var r = this.raphael
- , options = this.options
- , anchor;
- anchor = r.circle(x, y, 10).attr({
- fill: "#000",
- opacity: 0,
+ appendAnchor: (top, commit, x, y) ->
+ r = @raphael
+ options = @options
+ anchor = r.circle(x, y, 10).attr(
+ fill: "#000"
+ opacity: 0
cursor: "pointer"
- })
- .click(function(){
- window.open(options.commit_url.replace('%s', commit.id), '_blank');
- })
- .hover(function(){
- this.tooltip = r.commitTooltip(x, y + 5, commit);
- top.push(this.tooltip.insertBefore(this));
- }, function(){
- this.tooltip && this.tooltip.remove() && delete this.tooltip;
- });
- top.push(anchor);
- };
-
- this.BranchGraph = BranchGraph;
-
-}(this);
-Raphael.fn.commitTooltip = function(x, y, commit){
- var icon, nameText, idText, messageText
- , boxWidth = 300
- , boxHeight = 200;
-
- icon = this.image(commit.author.icon, x, y, 20, 20);
- nameText = this.text(x + 25, y + 10, commit.author.name);
- idText = this.text(x, y + 35, commit.id);
- messageText = this.text(x, y + 50, commit.message);
-
- textSet = this.set(icon, nameText, idText, messageText).attr({
- "text-anchor": "start",
- "font": "12px Monaco, monospace"
- });
-
- nameText.attr({
- "font": "14px Arial",
+ ).click(->
+ window.open options.commit_url.replace("%s", commit.id), "_blank"
+ ).hover(->
+ @tooltip = r.commitTooltip(x, y + 5, commit)
+ top.push @tooltip.insertBefore(this)
+ , ->
+ @tooltip and @tooltip.remove() and delete @tooltip
+ )
+ top.push anchor
+
+Raphael::commitTooltip = (x, y, commit) ->
+ icon = undefined
+ nameText = undefined
+ idText = undefined
+ messageText = undefined
+ boxWidth = 300
+ boxHeight = 200
+ icon = @image(commit.author.icon, x, y, 20, 20)
+ nameText = @text(x + 25, y + 10, commit.author.name)
+ idText = @text(x, y + 35, commit.id)
+ messageText = @text(x, y + 50, commit.message)
+ textSet = @set(icon, nameText, idText, messageText).attr(
+ "text-anchor": "start"
+ font: "12px Monaco, monospace"
+ )
+ nameText.attr(
+ font: "14px Arial"
"font-weight": "bold"
- });
-
- idText.attr({
- "fill": "#AAA"
- });
-
- textWrap(messageText, boxWidth - 50);
+ )
- var rect = this.rect(x - 10, y - 10, boxWidth, 100, 4).attr({
- "fill": "#FFF",
- "stroke": "#000",
- "stroke-linecap": "round",
+ idText.attr fill: "#AAA"
+ @textWrap messageText, boxWidth - 50
+ rect = @rect(x - 10, y - 10, boxWidth, 100, 4).attr(
+ fill: "#FFF"
+ stroke: "#000"
+ "stroke-linecap": "round"
"stroke-width": 2
- });
- var tooltip = this.set(rect, textSet);
+ )
+ tooltip = @set(rect, textSet)
+ rect.attr(
+ height: tooltip.getBBox().height + 10
+ width: tooltip.getBBox().width + 10
+ )
+
+ tooltip.transform ["t", 20, 20]
+ tooltip
- rect.attr({
- "height" : tooltip.getBBox().height + 10,
- "width" : tooltip.getBBox().width + 10
- });
-
- tooltip.transform([
- 't', 20, 20
- ]);
-
- return tooltip;
-};
+Raphael::textWrap = (t, width) ->
+ content = t.attr("text")
+ abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ t.attr text: abc
+ letterWidth = t.getBBox().width / abc.length
+ t.attr text: content
+ words = content.split(" ")
+ x = 0
+ s = []
-function textWrap(t, width) {
- var content = t.attr("text");
- var abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
- t.attr({
- "text" : abc
- });
- var letterWidth = t.getBBox().width / abc.length;
-
- t.attr({
- "text" : content
- });
+ for word in words
+ if x + (word.length * letterWidth) > width
+ s.push "\n"
+ x = 0
+ x += word.length * letterWidth
+ s.push word + " "
- var words = content.split(" ");
- var x = 0, s = [];
- for ( var i = 0; i < words.length; i++) {
+ t.attr text: s.join("")
+ b = t.getBBox()
+ h = Math.abs(b.y2) - Math.abs(b.y) + 1
+ t.attr y: b.y + h
- var l = words[i].length;
- if (x + (l * letterWidth) > width) {
- s.push("\n");
- x = 0;
- }
- x += l * letterWidth;
- s.push(words[i] + " ");
- }
- t.attr({
- "text" : s.join("")
- });
- var b = t.getBBox()
- , h = Math.abs(b.y2) - Math.abs(b.y) + 1;
- t.attr({
- "y": b.y + h
- });
-}
+@BranchGraph = BranchGraph