|
|
/*
|
|
|
* 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 ( 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 = $("#chg_"+(self.commit.idx+1))
|
|
|
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 = $("#chg_"+(self.commit.idx+2))
|
|
|
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 = $("#chg_"+(self.idx+1))
|
|
|
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 = $("#chg_1").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(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 ) );
|
|
|
|