Show More
jquery.commits-graph.js
306 lines
| 8.8 KiB
| application/javascript
|
JavascriptLexer
r2123 | /* | |||
* jQuery Commits Graph - v0.1.4 | ||||
* A jQuery plugin to display git commits graph using HTML5/Canvas. | ||||
* https://github.com/tclh123/commits-graph | ||||
* | ||||
* Copyright (c) 2014 | ||||
* MIT License | ||||
* | ||||
* Adapted to fit RhodeCode Enterprise changelog graph needs | ||||
*/ | ||||
// -- Route -------------------------------------------------------- | ||||
function Route( commit, data, options ) { | ||||
var self = this; | ||||
self._data = data; | ||||
self.commit = commit; | ||||
self.options = options; | ||||
self.from = data[0]; | ||||
self.to = data[1]; | ||||
self.branch = data[2]; | ||||
} | ||||
Route.prototype.drawRoute = function ( commit, ctx ) { | ||||
var self = this; | ||||
if (self.options.orientation === "horizontal") { | ||||
var from_x_hori = self.options.width * self.options.scaleFactor - (self.commit.idx + 0.5) * self.options.x_step * self.options.scaleFactor; | ||||
var from_y_hori = (self.from + 1) * self.options.y_step * self.options.scaleFactor; | ||||
var to_x_hori = self.options.width * self.options.scaleFactor - (self.commit.idx + 0.5 + 1) * self.options.x_step * self.options.scaleFactor; | ||||
var to_y_hori = (self.to + 1) * self.options.y_step * self.options.scaleFactor; | ||||
ctx.strokeStyle = self.commit.graph.get_color(self.branch); | ||||
ctx.beginPath(); | ||||
ctx.moveTo(from_x_hori, from_y_hori); | ||||
if (from_y_hori === to_y_hori) { | ||||
ctx.lineTo(to_x_hori, to_y_hori); | ||||
} else if (from_y_hori > to_y_hori) { | ||||
ctx.bezierCurveTo(from_x_hori - self.options.x_step * self.options.scaleFactor / 3 * 2, | ||||
from_y_hori + self.options.y_step * self.options.scaleFactor / 4, | ||||
to_x_hori + self.options.x_step * self.options.scaleFactor / 3 * 2, | ||||
to_y_hori - self.options.y_step * self.options.scaleFactor / 4, | ||||
to_x_hori, to_y_hori); | ||||
} else if (from_y_hori < to_y_hori) { | ||||
ctx.bezierCurveTo(from_x_hori - self.options.x_step * self.options.scaleFactor / 3 * 2, | ||||
from_y_hori - self.options.y_step * self.options.scaleFactor / 4, | ||||
to_x_hori + self.options.x_step * self.options.scaleFactor / 3 * 2, | ||||
to_y_hori + self.options.y_step * self.options.scaleFactor / 4, | ||||
to_x_hori, to_y_hori); | ||||
} | ||||
} else { | ||||
var from_x = self.options.width * self.options.scaleFactor - (self.from + 1) * self.options.x_step * self.options.scaleFactor; | ||||
var row = $("#sha_" + commit.sha); | ||||
if (row.length) { | ||||
var from_y = (row.offset().top + row.height() / 2 - self.options.relaOffset) * self.options.scaleFactor; | ||||
} | ||||
var to_x = self.options.width * self.options.scaleFactor - (self.to + 1) * self.options.x_step * self.options.scaleFactor; | ||||
var next_row = $("#sha_" + commit.sha).next('tr'); | ||||
if (next_row.length) { | ||||
var to_y = ((next_row.offset().top + next_row.height() / 2 - self.options.relaOffset) + 0.2) * self.options.scaleFactor; | ||||
} | ||||
ctx.strokeStyle = self.commit.graph.get_color(self.branch); | ||||
ctx.beginPath(); | ||||
ctx.moveTo(from_x, from_y); | ||||
if (from_x === to_x) { | ||||
ctx.lineTo(to_x, to_y); | ||||
} else { | ||||
ctx.bezierCurveTo(from_x - self.options.x_step * self.options.scaleFactor / 4, | ||||
from_y + self.options.y_step * self.options.scaleFactor / 3 * 2, | ||||
to_x + self.options.x_step * self.options.scaleFactor / 4, | ||||
to_y - self.options.y_step * self.options.scaleFactor / 3 * 2, | ||||
to_x, to_y); | ||||
} | ||||
} | ||||
ctx.stroke(); | ||||
}; | ||||
// -- Commit Node -------------------------------------------------------- | ||||
function Commit(graph, idx, data, options ) { | ||||
var self = this; | ||||
self._data = data; | ||||
self.graph = graph; | ||||
self.idx = idx; | ||||
self.options = options; | ||||
self.sha = data[0]; | ||||
self.dot = data[1]; | ||||
self.dot_offset = self.dot[0]; | ||||
self.dot_branch = self.dot[1]; | ||||
self.routes = $.map(data[2], function(e) { return new Route(self, e, options); }); | ||||
} | ||||
Commit.prototype.drawDot = function ( ctx ) { | ||||
var self = this; | ||||
var radius = self.options.dotRadius; // dot radius | ||||
if (self.options.orientation === "horizontal") { | ||||
var x_hori = self.options.width * self.options.scaleFactor - (self.idx + 0.5) * self.options.x_step * self.options.scaleFactor; | ||||
var y_hori = (self.dot_offset + 1) * self.options.y_step * self.options.scaleFactor; | ||||
ctx.fillStyle = self.graph.get_color(self.dot_branch); | ||||
ctx.beginPath(); | ||||
ctx.arc(x_hori, y_hori, radius * self.options.scaleFactor, 0, 2 * Math.PI, true); | ||||
} else { | ||||
var x = self.options.width * self.options.scaleFactor - (self.dot_offset + 1) * self.options.x_step * self.options.scaleFactor; | ||||
var row = $("#sha_" + self.sha); | ||||
var y = (row.offset().top + row.height() / 2 - self.options.relaOffset) * self.options.scaleFactor; | ||||
ctx.fillStyle = self.graph.get_color(self.dot_branch); | ||||
ctx.beginPath(); | ||||
ctx.arc(x, y, radius * self.options.scaleFactor, 0, 2 * Math.PI, true); | ||||
} | ||||
// ctx.stroke(); | ||||
ctx.fill(); | ||||
}; | ||||
// -- Graph Canvas -------------------------------------------------------- | ||||
function backingScale() { | ||||
if ('devicePixelRatio' in window) { | ||||
if (window.devicePixelRatio > 1) { | ||||
return window.devicePixelRatio; | ||||
} | ||||
} | ||||
return 1; | ||||
} | ||||
function GraphCanvas( data, options ) { | ||||
var self = this; | ||||
self.data = data; | ||||
self.options = options; | ||||
self.canvas = document.createElement("canvas"); | ||||
self.canvas.style.height = options.height + "px"; | ||||
self.canvas.style.width = options.width + "px"; | ||||
self.canvas.height = options.height; | ||||
self.canvas.width = options.width; | ||||
var scaleFactor = backingScale(); | ||||
if (self.options.orientation === "horizontal") { | ||||
if (scaleFactor < 1) { | ||||
self.canvas.width = self.canvas.width * scaleFactor; | ||||
self.canvas.height = self.canvas.height * scaleFactor; | ||||
} | ||||
} else { | ||||
if (scaleFactor > 1) { | ||||
self.canvas.width = self.canvas.width * scaleFactor; | ||||
self.canvas.height = self.canvas.height * scaleFactor; | ||||
} | ||||
} | ||||
self.options.scaleFactor = scaleFactor; | ||||
// or use context.scale(2,2) // not tested | ||||
self.colors = [ | ||||
"#e11d21", | ||||
//"#eb6420", | ||||
"#fbca04", | ||||
"#009800", | ||||
"#006b75", | ||||
"#207de5", | ||||
"#0052cc", | ||||
"#5319e7", | ||||
"#f7c6c7", | ||||
"#fad8c7", | ||||
"#fef2c0", | ||||
"#bfe5bf", | ||||
"#c7def8", | ||||
"#bfdadc", | ||||
"#bfd4f2", | ||||
"#d4c5f9", | ||||
"#cccccc", | ||||
"#84b6eb", | ||||
"#e6e6e6", | ||||
"#ffffff", | ||||
"#cc317c" | ||||
]; | ||||
// self.branch_color = {}; | ||||
} | ||||
GraphCanvas.prototype.toHTML = function () { | ||||
var self = this; | ||||
self.draw(); | ||||
return $(self.canvas); | ||||
}; | ||||
GraphCanvas.prototype.get_color = function (branch) { | ||||
var self = this; | ||||
var n = self.colors.length; | ||||
return self.colors[branch % n]; | ||||
}; | ||||
/* | ||||
[ | ||||
sha, | ||||
[offset, branch], //dot | ||||
[ | ||||
[from, to, branch], // route1 | ||||
[from, to, branch], // route2 | ||||
[from, to, branch], | ||||
] // routes | ||||
], | ||||
*/ | ||||
// draw | ||||
GraphCanvas.prototype.draw = function () { | ||||
var self = this, | ||||
ctx = self.canvas.getContext("2d"); | ||||
ctx.lineWidth = self.options.lineWidth; | ||||
self.options.relaOffset = $(".changelogRow").first().offset().top; | ||||
var n_commits = self.data.length; | ||||
for (var i=0; i<n_commits; i++) { | ||||
var commit = new Commit(self, i, self.data[i], self.options); | ||||
for (var j=0; j<commit.routes.length; j++) { | ||||
var route = commit.routes[j]; | ||||
route.drawRoute(commit, ctx); | ||||
} | ||||
commit.drawDot(ctx); | ||||
} | ||||
}; | ||||
// -- Function for finding the total number of branches ----------------------- | ||||
branchCount = function(data) { | ||||
var maxBranch = -1; | ||||
for (var i = 0; i < data.length; i++) { | ||||
for (var j = 0; j < data[i][2].length; j++) { | ||||
if (maxBranch < data[i][2][j][0] || maxBranch < data[i][2][j][1]) { | ||||
maxBranch = Math.max.apply(Math, [data[i][2][j][0], data[i][2][j][1]]); | ||||
} | ||||
} | ||||
} | ||||
return maxBranch + 1; | ||||
}; | ||||
// -- Graph Plugin ------------------------------------------------------------ | ||||
function Graph( element, options ) { | ||||
var self = this, | ||||
defaults = { | ||||
height: 800, | ||||
width: 200, | ||||
// y_step: 30, | ||||
y_step: 20, | ||||
x_step: 20, | ||||
orientation: "vertical", | ||||
dotRadius: 3, | ||||
lineWidth: 2, | ||||
}; | ||||
self.element = element; | ||||
self.$container = $( element ); | ||||
self.data = self.$container.data( "graph" ); | ||||
var x_step = $.extend( {}, defaults, options ).x_step; | ||||
var y_step = $.extend( {}, defaults, options ).y_step; | ||||
if (options.orientation === "horizontal") { | ||||
defaults.width = ( self.data.length + 2 ) * x_step; | ||||
defaults.height = ( branchCount(self.data) + 0.5 ) * y_step; | ||||
} else { | ||||
defaults.width = ( branchCount(self.data) + 0.5 ) * x_step; | ||||
defaults.height = ( self.data.length + 2 ) * y_step; | ||||
} | ||||
self.options = $.extend( {}, defaults, options ) ; | ||||
self._defaults = defaults; | ||||
self.applyTemplate(); | ||||
} | ||||
// Apply results to HTML template | ||||
Graph.prototype.applyTemplate = function () { | ||||
var self = this, | ||||
graphCanvas = new GraphCanvas( self.data, self.options ), | ||||
$canvas = graphCanvas.toHTML(); | ||||
$canvas.appendTo( self.$container ); | ||||
}; | ||||
// -- Attach plugin to jQuery's prototype -------------------------------------- | ||||
;( function ( $, window, undefined ) { | ||||
$.fn.commits = function ( options ) { | ||||
return this.each(function () { | ||||
$( this ).data( "plugin_commits_graph", new Graph( this, options ) ); | ||||
}); | ||||
}; | ||||
}( window.jQuery, window ) ); | ||||