mirror of
https://github.com/klaussilveira/gitlist.git
synced 2025-11-17 03:00:59 +01:00
Merge branch 'master' of https://github.com/guiyomh/gitlist
This commit is contained in:
1
boot.php
1
boot.php
@@ -9,5 +9,6 @@ $app->mount('', new GitList\Controller\BlobController());
|
|||||||
$app->mount('', new GitList\Controller\CommitController());
|
$app->mount('', new GitList\Controller\CommitController());
|
||||||
$app->mount('', new GitList\Controller\TreeController());
|
$app->mount('', new GitList\Controller\TreeController());
|
||||||
$app->mount('', new GitList\Controller\NetworkController());
|
$app->mount('', new GitList\Controller\NetworkController());
|
||||||
|
$app->mount('', new GitList\Controller\TreeGraphController());
|
||||||
|
|
||||||
return $app;
|
return $app;
|
||||||
|
|||||||
73
src/GitList/Controller/TreeGraphController.php
Normal file
73
src/GitList/Controller/TreeGraphController.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace GitList\Controller;
|
||||||
|
|
||||||
|
use Silex\Application;
|
||||||
|
use Silex\ControllerProviderInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
class TreeGraphController implements ControllerProviderInterface
|
||||||
|
{
|
||||||
|
public function connect(Application $app)
|
||||||
|
{
|
||||||
|
$route = $app['controllers_factory'];
|
||||||
|
|
||||||
|
$route->get(
|
||||||
|
'{repo}/treegraph/{commitishPath}',
|
||||||
|
function ($repo, $commitishPath) use ($app) {
|
||||||
|
/** @var \GitList\Git\Repository $repository */
|
||||||
|
$repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo);
|
||||||
|
|
||||||
|
$command = 'log --graph --date-order --all -C -M -n 100 --date=iso ' .
|
||||||
|
'--pretty=format:"B[%d] C[%H] D[%ad] A[%an] E[%ae] H[%h] S[%s]"';
|
||||||
|
$rawRows = $repository->getClient()->run($repository, $command);
|
||||||
|
$rawRows = explode("\n", $rawRows);
|
||||||
|
$graphItems = array();
|
||||||
|
|
||||||
|
foreach ($rawRows as $row) {
|
||||||
|
if (preg_match("/^(.+?)(\s(B\[(.*?)\])? C\[(.+?)\] D\[(.+?)\] A\[(.+?)\] E\[(.+?)\] H\[(.+?)\] S\[(.+?)\])?$/", $row, $output)) {
|
||||||
|
if (!isset($output[4])) {
|
||||||
|
$graphItems[] = array(
|
||||||
|
"relation"=>$output[1]
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$graphItems[] = array(
|
||||||
|
"relation"=>$output[1],
|
||||||
|
"branch"=>$output[4],
|
||||||
|
"rev"=>$output[5],
|
||||||
|
"date"=>$output[6],
|
||||||
|
"author"=>$output[7],
|
||||||
|
"author_email"=>$output[8],
|
||||||
|
"short_rev"=>$output[9],
|
||||||
|
"subject"=>preg_replace('/(^|\s)(#[[:xdigit:]]+)(\s|$)/', '$1<a href="$2">$2</a>$3', $output[10])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($commitishPath === null) {
|
||||||
|
$commitishPath = $repository->getHead();
|
||||||
|
}
|
||||||
|
|
||||||
|
list($branch, $file) = $app['util.routing']->parseCommitishPathParam($commitishPath, $repo);
|
||||||
|
list($branch, $file) = $app['util.repository']->extractRef($repository, $branch, $file);
|
||||||
|
|
||||||
|
return $app['twig']->render(
|
||||||
|
'treegraph.twig',
|
||||||
|
array(
|
||||||
|
'repo' => $repo,
|
||||||
|
'branch' => $branch,
|
||||||
|
'commitishPath' => $commitishPath,
|
||||||
|
'graphItems' => $graphItems,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)->assert('repo', $app['util.routing']->getRepositoryRegex())
|
||||||
|
->assert('commitishPath', $app['util.routing']->getCommitishPathRegex())
|
||||||
|
->value('commitishPath', null)
|
||||||
|
->convert('commitishPath', 'escaper.argument:escape')
|
||||||
|
->bind('treegraph');
|
||||||
|
|
||||||
|
return $route;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
themes/bootstrap3/css/gitgraph.css
Normal file
14
themes/bootstrap3/css/gitgraph.css
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
body {font:13.34px/1.4 helvetica,arial,freesans,clean,sans-serif;}
|
||||||
|
em {font-style:normal;}
|
||||||
|
|
||||||
|
#git-graph-container{ clear: both}
|
||||||
|
#rev-container, #rel-container {float:left;}
|
||||||
|
#git-graph-container li {list-style-type:none;height:28px;line-height:27px;overflow:hidden;}
|
||||||
|
#git-graph-container li .node-relation {font-family:'Bitstream Vera Sans Mono', 'Courier', monospace;}
|
||||||
|
#git-graph-container li .author {color:#666666;}
|
||||||
|
#git-graph-container li .time {color:#999999;font-size:80%}
|
||||||
|
#git-graph-container li a {color:#000000;}
|
||||||
|
#git-graph-container li a em {color:#BB0000;border-bottom:1px dotted #BBBBBB;text-decoration:none;font-style:normal;}
|
||||||
|
|
||||||
|
#rev-list {margin:0;padding:0 5px 0 0;}
|
||||||
|
#graph-raw-list {margin:0px;}
|
||||||
17
themes/bootstrap3/js/draw.js
Normal file
17
themes/bootstrap3/js/draw.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
$(document).ready(function () {
|
||||||
|
var graphList = [];
|
||||||
|
|
||||||
|
if (!document.getElementById('graph-canvas')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#graph-raw-list li span.node-relation").each(function () {
|
||||||
|
graphList.push($(this).text());
|
||||||
|
})
|
||||||
|
|
||||||
|
gitGraph(document.getElementById('graph-canvas'), graphList);
|
||||||
|
|
||||||
|
if ($("#rev-container")) {
|
||||||
|
$("#rev-container").css("width", $('#git-graph-container').width() - $('#graph-canvas').width());
|
||||||
|
}
|
||||||
|
})
|
||||||
399
themes/bootstrap3/js/gitgraph.js
Normal file
399
themes/bootstrap3/js/gitgraph.js
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Terrence Lee <kill889@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the <organization> nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var gitGraph = function (canvas, rawGraphList, config) {
|
||||||
|
if (!canvas.getContext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof config === "undefined") {
|
||||||
|
config = {
|
||||||
|
unitSize: 28,
|
||||||
|
lineWidth: 3,
|
||||||
|
nodeRadius: 6
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var flows = [];
|
||||||
|
var graphList = [];
|
||||||
|
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
var init = function () {
|
||||||
|
var maxWidth = 0;
|
||||||
|
var i;
|
||||||
|
var l = rawGraphList.length;
|
||||||
|
var row;
|
||||||
|
var midStr;
|
||||||
|
|
||||||
|
for (i = 0; i < l; i++) {
|
||||||
|
midStr = rawGraphList[i].replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
|
||||||
|
|
||||||
|
maxWidth = Math.max(midStr.replace(/(\_|\s)/g, "").length, maxWidth);
|
||||||
|
|
||||||
|
row = midStr.split("");
|
||||||
|
|
||||||
|
graphList.unshift(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.width = maxWidth * config.unitSize;
|
||||||
|
canvas.height = graphList.length * config.unitSize;
|
||||||
|
|
||||||
|
ctx.lineWidth = config.lineWidth;
|
||||||
|
ctx.lineJoin = "round";
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
};
|
||||||
|
|
||||||
|
var genRandomStr = function () {
|
||||||
|
var chars = "0123456789ABCDEF";
|
||||||
|
var stringLength = 6;
|
||||||
|
var randomString = '', rnum, i;
|
||||||
|
for (i = 0; i < stringLength; i++) {
|
||||||
|
rnum = Math.floor(Math.random() * chars.length);
|
||||||
|
randomString += chars.substring(rnum, rnum + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return randomString;
|
||||||
|
};
|
||||||
|
|
||||||
|
var findFlow = function (id) {
|
||||||
|
var i = flows.length;
|
||||||
|
|
||||||
|
while (i-- && flows[i].id !== id) {}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
var findColomn = function (symbol, row) {
|
||||||
|
var i = row.length;
|
||||||
|
|
||||||
|
while (i-- && row[i] !== symbol) {}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
var findBranchOut = function (row) {
|
||||||
|
if (!row) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = row.length;
|
||||||
|
|
||||||
|
while (i-- &&
|
||||||
|
!(row[i - 1] && row[i] === "/" && row[i - 1] === "|") &&
|
||||||
|
!(row[i - 2] && row[i] === "_" && row[i - 2] === "|")) {}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
var genNewFlow = function () {
|
||||||
|
var newId;
|
||||||
|
|
||||||
|
do {
|
||||||
|
newId = genRandomStr();
|
||||||
|
} while (findFlow(newId) !== -1);
|
||||||
|
|
||||||
|
return {id:newId, color:"#" + newId};
|
||||||
|
};
|
||||||
|
|
||||||
|
//draw method
|
||||||
|
var drawLineRight = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y + config.unitSize / 2);
|
||||||
|
ctx.lineTo(x + config.unitSize, y + config.unitSize / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawLineUp = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y + config.unitSize / 2);
|
||||||
|
ctx.lineTo(x, y - config.unitSize / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawNode = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
|
||||||
|
drawLineUp(x, y, color);
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, config.nodeRadius, 0, Math.PI * 2, true);
|
||||||
|
ctx.fill();
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawLineIn = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + config.unitSize, y + config.unitSize / 2);
|
||||||
|
ctx.lineTo(x, y - config.unitSize / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawLineOut = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y + config.unitSize / 2);
|
||||||
|
ctx.lineTo(x + config.unitSize, y - config.unitSize / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
var draw = function (graphList) {
|
||||||
|
var colomn, colomnIndex, prevColomn, condenseIndex;
|
||||||
|
var x, y;
|
||||||
|
var color;
|
||||||
|
var nodePos, outPos;
|
||||||
|
var tempFlow;
|
||||||
|
var prevRowLength = 0;
|
||||||
|
var flowSwapPos = -1;
|
||||||
|
var lastLinePos;
|
||||||
|
var i, k, l;
|
||||||
|
var condenseCurrentLength, condensePrevLength = 0, condenseNextLength = 0;
|
||||||
|
|
||||||
|
var inlineIntersect = false;
|
||||||
|
|
||||||
|
//initiate for first row
|
||||||
|
for (i = 0, l = graphList[0].length; i < l; i++) {
|
||||||
|
if (graphList[0][i] !== "_" && graphList[0][i] !== " ") {
|
||||||
|
flows.push(genNewFlow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
y = canvas.height - config.unitSize * 0.5;
|
||||||
|
|
||||||
|
//iterate
|
||||||
|
for (i = 0, l = graphList.length; i < l; i++) {
|
||||||
|
x = config.unitSize * 0.5;
|
||||||
|
|
||||||
|
currentRow = graphList[i];
|
||||||
|
nextRow = graphList[i + 1];
|
||||||
|
prevRow = graphList[i - 1];
|
||||||
|
|
||||||
|
flowSwapPos = -1;
|
||||||
|
|
||||||
|
condenseCurrentLength = currentRow.filter(function (val) {
|
||||||
|
return (val !== " " && val !== "_")
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
if (nextRow) {
|
||||||
|
condenseNextLength = nextRow.filter(function (val) {
|
||||||
|
return (val !== " " && val !== "_")
|
||||||
|
}).length;
|
||||||
|
} else {
|
||||||
|
condenseNextLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//pre process begin
|
||||||
|
//use last row for analysing
|
||||||
|
if (prevRow) {
|
||||||
|
if (!inlineIntersect) {
|
||||||
|
//intersect might happen
|
||||||
|
for (colomnIndex = 0; colomnIndex < prevRowLength; colomnIndex++) {
|
||||||
|
if (prevRow[colomnIndex + 1] &&
|
||||||
|
(prevRow[colomnIndex] === "/" && prevRow[colomnIndex + 1] === "|") ||
|
||||||
|
((prevRow[colomnIndex] === "_" && prevRow[colomnIndex + 1] === "|") &&
|
||||||
|
(prevRow[colomnIndex + 2] === "/"))) {
|
||||||
|
|
||||||
|
flowSwapPos = colomnIndex;
|
||||||
|
|
||||||
|
//swap two flow
|
||||||
|
tempFlow = {id:flows[flowSwapPos].id, color:flows[flowSwapPos].color};
|
||||||
|
|
||||||
|
flows[flowSwapPos].id = flows[flowSwapPos + 1].id;
|
||||||
|
flows[flowSwapPos].color = flows[flowSwapPos + 1].color;
|
||||||
|
|
||||||
|
flows[flowSwapPos + 1].id = tempFlow.id;
|
||||||
|
flows[flowSwapPos + 1].color = tempFlow.color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condensePrevLength < condenseCurrentLength &&
|
||||||
|
((nodePos = findColomn("*", currentRow)) !== -1 &&
|
||||||
|
(findColomn("_", currentRow) === -1))) {
|
||||||
|
|
||||||
|
flows.splice(nodePos - 1, 0, genNewFlow());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevRowLength > currentRow.length &&
|
||||||
|
(nodePos = findColomn("*", prevRow)) !== -1) {
|
||||||
|
|
||||||
|
if (findColomn("_", currentRow) === -1 &&
|
||||||
|
findColomn("/", currentRow) === -1 &&
|
||||||
|
findColomn("\\", currentRow) === -1) {
|
||||||
|
|
||||||
|
flows.splice(nodePos + 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //done with the previous row
|
||||||
|
|
||||||
|
prevRowLength = currentRow.length; //store for next round
|
||||||
|
colomnIndex = 0; //reset index
|
||||||
|
condenseIndex = 0;
|
||||||
|
condensePrevLength = 0;
|
||||||
|
while (colomnIndex < currentRow.length) {
|
||||||
|
colomn = currentRow[colomnIndex];
|
||||||
|
|
||||||
|
if (colomn !== " " && colomn !== "_") {
|
||||||
|
++condensePrevLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colomn === " " &&
|
||||||
|
currentRow[colomnIndex + 1] &&
|
||||||
|
currentRow[colomnIndex + 1] === "_" &&
|
||||||
|
currentRow[colomnIndex - 1] &&
|
||||||
|
currentRow[colomnIndex - 1] === "|") {
|
||||||
|
|
||||||
|
currentRow.splice(colomnIndex, 1);
|
||||||
|
|
||||||
|
currentRow[colomnIndex] = "/";
|
||||||
|
colomn = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
//create new flow only when no intersetc happened
|
||||||
|
if (flowSwapPos === -1 &&
|
||||||
|
colomn === "/" &&
|
||||||
|
currentRow[colomnIndex - 1] &&
|
||||||
|
currentRow[colomnIndex - 1] === "|") {
|
||||||
|
|
||||||
|
flows.splice(condenseIndex, 0, genNewFlow());
|
||||||
|
}
|
||||||
|
|
||||||
|
//change \ and / to | when it's in the last position of the whole row
|
||||||
|
if (colomn === "/" || colomn === "\\") {
|
||||||
|
if (!(colomn === "/" && findBranchOut(nextRow) === -1)) {
|
||||||
|
if ((lastLinePos = Math.max(findColomn("|", currentRow),
|
||||||
|
findColomn("*", currentRow))) !== -1 &&
|
||||||
|
(lastLinePos < colomnIndex - 1)) {
|
||||||
|
|
||||||
|
while (currentRow[++lastLinePos] === " ") {}
|
||||||
|
|
||||||
|
if (lastLinePos === colomnIndex) {
|
||||||
|
currentRow[colomnIndex] = "|";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colomn === "*" &&
|
||||||
|
prevRow &&
|
||||||
|
prevRow[condenseIndex + 1] === "\\") {
|
||||||
|
flows.splice(condenseIndex + 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colomn !== " ") {
|
||||||
|
++condenseIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
++colomnIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
condenseCurrentLength = currentRow.filter(function (val) {
|
||||||
|
return (val !== " " && val !== "_")
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
//do some clean up
|
||||||
|
if (flows.length > condenseCurrentLength) {
|
||||||
|
flows.splice(condenseCurrentLength, flows.length - condenseCurrentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
colomnIndex = 0;
|
||||||
|
|
||||||
|
//a little inline analysis and draw process
|
||||||
|
while (colomnIndex < currentRow.length) {
|
||||||
|
colomn = currentRow[colomnIndex];
|
||||||
|
prevColomn = currentRow[colomnIndex - 1];
|
||||||
|
|
||||||
|
if (currentRow[colomnIndex] === " ") {
|
||||||
|
currentRow.splice(colomnIndex, 1);
|
||||||
|
x += config.unitSize;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//inline interset
|
||||||
|
if ((colomn === "_" || colomn === "/") &&
|
||||||
|
currentRow[colomnIndex - 1] === "|" &&
|
||||||
|
currentRow[colomnIndex - 2] === "_") {
|
||||||
|
|
||||||
|
inlineIntersect = true;
|
||||||
|
|
||||||
|
tempFlow = flows.splice(colomnIndex - 2, 1)[0];
|
||||||
|
flows.splice(colomnIndex - 1, 0, tempFlow);
|
||||||
|
currentRow.splice(colomnIndex - 2, 1);
|
||||||
|
|
||||||
|
colomnIndex = colomnIndex - 1;
|
||||||
|
} else {
|
||||||
|
inlineIntersect = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
color = flows[colomnIndex].color;
|
||||||
|
|
||||||
|
switch (colomn) {
|
||||||
|
case "_" :
|
||||||
|
drawLineRight(x, y, color);
|
||||||
|
|
||||||
|
x += config.unitSize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "*" :
|
||||||
|
drawNode(x, y, color);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "|" :
|
||||||
|
drawLineUp(x, y, color);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "/" :
|
||||||
|
if (prevColomn &&
|
||||||
|
(prevColomn === "/" ||
|
||||||
|
prevColomn === " ")) {
|
||||||
|
x -= config.unitSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawLineOut(x, y, color);
|
||||||
|
|
||||||
|
x += config.unitSize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "\\" :
|
||||||
|
drawLineIn(x, y, color);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++colomnIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
y -= config.unitSize;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
init();
|
||||||
|
draw(graphList);
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
<title>{% block title %}Welcome!{% endblock %}</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ app.request.basepath }}/themes/{{ app.theme }}/css/style.css">
|
<link rel="stylesheet" type="text/css" href="{{ app.request.basepath }}/themes/{{ app.theme }}/css/style.css">
|
||||||
<link rel="stylesheet" type="text/css" href="{{ app.request.basepath }}/themes/{{ app.theme }}/css/fontawesome.css">
|
<link rel="stylesheet" type="text/css" href="{{ app.request.basepath }}/themes/{{ app.theme }}/css/fontawesome.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ app.request.basepath }}/themes/{{ app.theme }}/css/gitgraph.css">
|
||||||
<link rel="shortcut icon" type="image/png" href="{{ app.request.basepath }}/themes/{{ app.theme }}/img/favicon.png" />
|
<link rel="shortcut icon" type="image/png" href="{{ app.request.basepath }}/themes/{{ app.theme }}/img/favicon.png" />
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/html5.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/html5.js"></script>
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
{% block body %}{% endblock %}
|
{% block body %}{% endblock %}
|
||||||
|
{% block javascripts %}
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/jquery.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/jquery.js"></script>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/raphael.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/raphael.js"></script>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/bootstrap.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/bootstrap.js"></script>
|
||||||
@@ -21,5 +23,8 @@
|
|||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/table.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/table.js"></script>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/main.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/main.js"></script>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/networkGraph.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/networkGraph.js"></script>
|
||||||
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/gitgraph.js"></script>
|
||||||
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/draw.js"></script>
|
||||||
|
{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
48
themes/bootstrap3/twig/treegraph.twig
Normal file
48
themes/bootstrap3/twig/treegraph.twig
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{% extends 'layout_page.twig' %}
|
||||||
|
|
||||||
|
{% set page = 'treegraph' %}
|
||||||
|
|
||||||
|
{% block title %}GitList{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% include 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Graph', path:''}]} %}
|
||||||
|
<div class="network-view">
|
||||||
|
<div class="network-header">
|
||||||
|
<div class="meta">Graph of {{ repo }} </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="git-graph-container">
|
||||||
|
<div id="rel-container">
|
||||||
|
<canvas id="graph-canvas" width="100px">
|
||||||
|
<ul id="graph-raw-list">
|
||||||
|
{% for item in graphItems %}
|
||||||
|
<li><span class="node-relation">{{ item.relation }}</span></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
<div style="float:left;" id="rev-container">
|
||||||
|
<ul id="rev-list">
|
||||||
|
{% for item in graphItems %}
|
||||||
|
<li>
|
||||||
|
{% if item.rev is defined %}
|
||||||
|
<a id="{{ item.short_rev }}" class="btn btn-default btn-sm" href="{{ path('commit', {repo: repo, commit: item.rev}) }}"> {{ item.short_rev }} </a>
|
||||||
|
<strong> {{ item.branch }} </strong>
|
||||||
|
<em>{{ item.subject }}</em> by
|
||||||
|
<span class="author">{{ item.author }} <{{ item.author_email }}></span>
|
||||||
|
<span class="time">{{ item.date }}</span>;
|
||||||
|
{% else %}
|
||||||
|
<span/>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both"><!-- --></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
{% endblock %}
|
||||||
14
themes/default/css/gitgraph.css
Normal file
14
themes/default/css/gitgraph.css
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
body {font:13.34px/1.4 helvetica,arial,freesans,clean,sans-serif;}
|
||||||
|
em {font-style:normal;}
|
||||||
|
|
||||||
|
#git-graph-container{ clear: both}
|
||||||
|
#rev-container, #rel-container {float:left;}
|
||||||
|
#git-graph-container li {list-style-type:none;height:28px;line-height:27px;overflow:hidden;}
|
||||||
|
#git-graph-container li .node-relation {font-family:'Bitstream Vera Sans Mono', 'Courier', monospace;}
|
||||||
|
#git-graph-container li .author {color:#666666;}
|
||||||
|
#git-graph-container li .time {color:#999999;font-size:80%}
|
||||||
|
#git-graph-container li a {color:#000000;}
|
||||||
|
#git-graph-container li a em {color:#BB0000;border-bottom:1px dotted #BBBBBB;text-decoration:none;font-style:normal;}
|
||||||
|
|
||||||
|
#rev-list {margin:0;padding:0 5px 0 0;}
|
||||||
|
#graph-raw-list {margin:0px;}
|
||||||
17
themes/default/js/draw.js
Normal file
17
themes/default/js/draw.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
$(document).ready(function () {
|
||||||
|
var graphList = [];
|
||||||
|
|
||||||
|
if (!document.getElementById('graph-canvas')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#graph-raw-list li span.node-relation").each(function () {
|
||||||
|
graphList.push($(this).text());
|
||||||
|
})
|
||||||
|
|
||||||
|
gitGraph(document.getElementById('graph-canvas'), graphList);
|
||||||
|
|
||||||
|
if ($("#rev-container")) {
|
||||||
|
$("#rev-container").css("width", $('#git-graph-container').width() - $('#graph-canvas').width());
|
||||||
|
}
|
||||||
|
})
|
||||||
399
themes/default/js/gitgraph.js
Normal file
399
themes/default/js/gitgraph.js
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Terrence Lee <kill889@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the <organization> nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var gitGraph = function (canvas, rawGraphList, config) {
|
||||||
|
if (!canvas.getContext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof config === "undefined") {
|
||||||
|
config = {
|
||||||
|
unitSize: 28,
|
||||||
|
lineWidth: 3,
|
||||||
|
nodeRadius: 6
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var flows = [];
|
||||||
|
var graphList = [];
|
||||||
|
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
var init = function () {
|
||||||
|
var maxWidth = 0;
|
||||||
|
var i;
|
||||||
|
var l = rawGraphList.length;
|
||||||
|
var row;
|
||||||
|
var midStr;
|
||||||
|
|
||||||
|
for (i = 0; i < l; i++) {
|
||||||
|
midStr = rawGraphList[i].replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
|
||||||
|
|
||||||
|
maxWidth = Math.max(midStr.replace(/(\_|\s)/g, "").length, maxWidth);
|
||||||
|
|
||||||
|
row = midStr.split("");
|
||||||
|
|
||||||
|
graphList.unshift(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.width = maxWidth * config.unitSize;
|
||||||
|
canvas.height = graphList.length * config.unitSize;
|
||||||
|
|
||||||
|
ctx.lineWidth = config.lineWidth;
|
||||||
|
ctx.lineJoin = "round";
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
};
|
||||||
|
|
||||||
|
var genRandomStr = function () {
|
||||||
|
var chars = "0123456789ABCDEF";
|
||||||
|
var stringLength = 6;
|
||||||
|
var randomString = '', rnum, i;
|
||||||
|
for (i = 0; i < stringLength; i++) {
|
||||||
|
rnum = Math.floor(Math.random() * chars.length);
|
||||||
|
randomString += chars.substring(rnum, rnum + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return randomString;
|
||||||
|
};
|
||||||
|
|
||||||
|
var findFlow = function (id) {
|
||||||
|
var i = flows.length;
|
||||||
|
|
||||||
|
while (i-- && flows[i].id !== id) {}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
var findColomn = function (symbol, row) {
|
||||||
|
var i = row.length;
|
||||||
|
|
||||||
|
while (i-- && row[i] !== symbol) {}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
var findBranchOut = function (row) {
|
||||||
|
if (!row) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = row.length;
|
||||||
|
|
||||||
|
while (i-- &&
|
||||||
|
!(row[i - 1] && row[i] === "/" && row[i - 1] === "|") &&
|
||||||
|
!(row[i - 2] && row[i] === "_" && row[i - 2] === "|")) {}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
var genNewFlow = function () {
|
||||||
|
var newId;
|
||||||
|
|
||||||
|
do {
|
||||||
|
newId = genRandomStr();
|
||||||
|
} while (findFlow(newId) !== -1);
|
||||||
|
|
||||||
|
return {id:newId, color:"#" + newId};
|
||||||
|
};
|
||||||
|
|
||||||
|
//draw method
|
||||||
|
var drawLineRight = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y + config.unitSize / 2);
|
||||||
|
ctx.lineTo(x + config.unitSize, y + config.unitSize / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawLineUp = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y + config.unitSize / 2);
|
||||||
|
ctx.lineTo(x, y - config.unitSize / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawNode = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
|
||||||
|
drawLineUp(x, y, color);
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, config.nodeRadius, 0, Math.PI * 2, true);
|
||||||
|
ctx.fill();
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawLineIn = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + config.unitSize, y + config.unitSize / 2);
|
||||||
|
ctx.lineTo(x, y - config.unitSize / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawLineOut = function (x, y, color) {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y + config.unitSize / 2);
|
||||||
|
ctx.lineTo(x + config.unitSize, y - config.unitSize / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
var draw = function (graphList) {
|
||||||
|
var colomn, colomnIndex, prevColomn, condenseIndex;
|
||||||
|
var x, y;
|
||||||
|
var color;
|
||||||
|
var nodePos, outPos;
|
||||||
|
var tempFlow;
|
||||||
|
var prevRowLength = 0;
|
||||||
|
var flowSwapPos = -1;
|
||||||
|
var lastLinePos;
|
||||||
|
var i, k, l;
|
||||||
|
var condenseCurrentLength, condensePrevLength = 0, condenseNextLength = 0;
|
||||||
|
|
||||||
|
var inlineIntersect = false;
|
||||||
|
|
||||||
|
//initiate for first row
|
||||||
|
for (i = 0, l = graphList[0].length; i < l; i++) {
|
||||||
|
if (graphList[0][i] !== "_" && graphList[0][i] !== " ") {
|
||||||
|
flows.push(genNewFlow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
y = canvas.height - config.unitSize * 0.5;
|
||||||
|
|
||||||
|
//iterate
|
||||||
|
for (i = 0, l = graphList.length; i < l; i++) {
|
||||||
|
x = config.unitSize * 0.5;
|
||||||
|
|
||||||
|
currentRow = graphList[i];
|
||||||
|
nextRow = graphList[i + 1];
|
||||||
|
prevRow = graphList[i - 1];
|
||||||
|
|
||||||
|
flowSwapPos = -1;
|
||||||
|
|
||||||
|
condenseCurrentLength = currentRow.filter(function (val) {
|
||||||
|
return (val !== " " && val !== "_")
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
if (nextRow) {
|
||||||
|
condenseNextLength = nextRow.filter(function (val) {
|
||||||
|
return (val !== " " && val !== "_")
|
||||||
|
}).length;
|
||||||
|
} else {
|
||||||
|
condenseNextLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//pre process begin
|
||||||
|
//use last row for analysing
|
||||||
|
if (prevRow) {
|
||||||
|
if (!inlineIntersect) {
|
||||||
|
//intersect might happen
|
||||||
|
for (colomnIndex = 0; colomnIndex < prevRowLength; colomnIndex++) {
|
||||||
|
if (prevRow[colomnIndex + 1] &&
|
||||||
|
(prevRow[colomnIndex] === "/" && prevRow[colomnIndex + 1] === "|") ||
|
||||||
|
((prevRow[colomnIndex] === "_" && prevRow[colomnIndex + 1] === "|") &&
|
||||||
|
(prevRow[colomnIndex + 2] === "/"))) {
|
||||||
|
|
||||||
|
flowSwapPos = colomnIndex;
|
||||||
|
|
||||||
|
//swap two flow
|
||||||
|
tempFlow = {id:flows[flowSwapPos].id, color:flows[flowSwapPos].color};
|
||||||
|
|
||||||
|
flows[flowSwapPos].id = flows[flowSwapPos + 1].id;
|
||||||
|
flows[flowSwapPos].color = flows[flowSwapPos + 1].color;
|
||||||
|
|
||||||
|
flows[flowSwapPos + 1].id = tempFlow.id;
|
||||||
|
flows[flowSwapPos + 1].color = tempFlow.color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condensePrevLength < condenseCurrentLength &&
|
||||||
|
((nodePos = findColomn("*", currentRow)) !== -1 &&
|
||||||
|
(findColomn("_", currentRow) === -1))) {
|
||||||
|
|
||||||
|
flows.splice(nodePos - 1, 0, genNewFlow());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevRowLength > currentRow.length &&
|
||||||
|
(nodePos = findColomn("*", prevRow)) !== -1) {
|
||||||
|
|
||||||
|
if (findColomn("_", currentRow) === -1 &&
|
||||||
|
findColomn("/", currentRow) === -1 &&
|
||||||
|
findColomn("\\", currentRow) === -1) {
|
||||||
|
|
||||||
|
flows.splice(nodePos + 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //done with the previous row
|
||||||
|
|
||||||
|
prevRowLength = currentRow.length; //store for next round
|
||||||
|
colomnIndex = 0; //reset index
|
||||||
|
condenseIndex = 0;
|
||||||
|
condensePrevLength = 0;
|
||||||
|
while (colomnIndex < currentRow.length) {
|
||||||
|
colomn = currentRow[colomnIndex];
|
||||||
|
|
||||||
|
if (colomn !== " " && colomn !== "_") {
|
||||||
|
++condensePrevLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colomn === " " &&
|
||||||
|
currentRow[colomnIndex + 1] &&
|
||||||
|
currentRow[colomnIndex + 1] === "_" &&
|
||||||
|
currentRow[colomnIndex - 1] &&
|
||||||
|
currentRow[colomnIndex - 1] === "|") {
|
||||||
|
|
||||||
|
currentRow.splice(colomnIndex, 1);
|
||||||
|
|
||||||
|
currentRow[colomnIndex] = "/";
|
||||||
|
colomn = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
//create new flow only when no intersetc happened
|
||||||
|
if (flowSwapPos === -1 &&
|
||||||
|
colomn === "/" &&
|
||||||
|
currentRow[colomnIndex - 1] &&
|
||||||
|
currentRow[colomnIndex - 1] === "|") {
|
||||||
|
|
||||||
|
flows.splice(condenseIndex, 0, genNewFlow());
|
||||||
|
}
|
||||||
|
|
||||||
|
//change \ and / to | when it's in the last position of the whole row
|
||||||
|
if (colomn === "/" || colomn === "\\") {
|
||||||
|
if (!(colomn === "/" && findBranchOut(nextRow) === -1)) {
|
||||||
|
if ((lastLinePos = Math.max(findColomn("|", currentRow),
|
||||||
|
findColomn("*", currentRow))) !== -1 &&
|
||||||
|
(lastLinePos < colomnIndex - 1)) {
|
||||||
|
|
||||||
|
while (currentRow[++lastLinePos] === " ") {}
|
||||||
|
|
||||||
|
if (lastLinePos === colomnIndex) {
|
||||||
|
currentRow[colomnIndex] = "|";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colomn === "*" &&
|
||||||
|
prevRow &&
|
||||||
|
prevRow[condenseIndex + 1] === "\\") {
|
||||||
|
flows.splice(condenseIndex + 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colomn !== " ") {
|
||||||
|
++condenseIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
++colomnIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
condenseCurrentLength = currentRow.filter(function (val) {
|
||||||
|
return (val !== " " && val !== "_")
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
//do some clean up
|
||||||
|
if (flows.length > condenseCurrentLength) {
|
||||||
|
flows.splice(condenseCurrentLength, flows.length - condenseCurrentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
colomnIndex = 0;
|
||||||
|
|
||||||
|
//a little inline analysis and draw process
|
||||||
|
while (colomnIndex < currentRow.length) {
|
||||||
|
colomn = currentRow[colomnIndex];
|
||||||
|
prevColomn = currentRow[colomnIndex - 1];
|
||||||
|
|
||||||
|
if (currentRow[colomnIndex] === " ") {
|
||||||
|
currentRow.splice(colomnIndex, 1);
|
||||||
|
x += config.unitSize;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//inline interset
|
||||||
|
if ((colomn === "_" || colomn === "/") &&
|
||||||
|
currentRow[colomnIndex - 1] === "|" &&
|
||||||
|
currentRow[colomnIndex - 2] === "_") {
|
||||||
|
|
||||||
|
inlineIntersect = true;
|
||||||
|
|
||||||
|
tempFlow = flows.splice(colomnIndex - 2, 1)[0];
|
||||||
|
flows.splice(colomnIndex - 1, 0, tempFlow);
|
||||||
|
currentRow.splice(colomnIndex - 2, 1);
|
||||||
|
|
||||||
|
colomnIndex = colomnIndex - 1;
|
||||||
|
} else {
|
||||||
|
inlineIntersect = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
color = flows[colomnIndex].color;
|
||||||
|
|
||||||
|
switch (colomn) {
|
||||||
|
case "_" :
|
||||||
|
drawLineRight(x, y, color);
|
||||||
|
|
||||||
|
x += config.unitSize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "*" :
|
||||||
|
drawNode(x, y, color);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "|" :
|
||||||
|
drawLineUp(x, y, color);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "/" :
|
||||||
|
if (prevColomn &&
|
||||||
|
(prevColomn === "/" ||
|
||||||
|
prevColomn === " ")) {
|
||||||
|
x -= config.unitSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawLineOut(x, y, color);
|
||||||
|
|
||||||
|
x += config.unitSize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "\\" :
|
||||||
|
drawLineIn(x, y, color);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++colomnIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
y -= config.unitSize;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
init();
|
||||||
|
draw(graphList);
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>{{ app.title }}{% if app.title %} - {% endif %}{% block title %}Welcome!{% endblock %}</title>
|
<title>{{ app.title }}{% if app.title %} - {% endif %}{% block title %}Welcome!{% endblock %}</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ app.request.basepath }}/themes/{{ app.theme }}/css/style.css">
|
<link rel="stylesheet" type="text/css" href="{{ app.request.basepath }}/themes/{{ app.theme }}/css/style.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ app.request.basepath }}/themes/{{ app.theme }}/css/gitgraph.css">
|
||||||
<link rel="shortcut icon" type="image/png" href="{{ app.request.basepath }}/themes/{{ app.theme }}/img/favicon.png" />
|
<link rel="shortcut icon" type="image/png" href="{{ app.request.basepath }}/themes/{{ app.theme }}/img/favicon.png" />
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/html5.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/html5.js"></script>
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
{% block body %}{% endblock %}
|
{% block body %}{% endblock %}
|
||||||
|
{% block javascripts %}
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/jquery.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/jquery.js"></script>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/raphael.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/raphael.js"></script>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/bootstrap.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/bootstrap.js"></script>
|
||||||
@@ -21,5 +23,8 @@
|
|||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/list.min.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/list.min.js"></script>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/main.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/main.js"></script>
|
||||||
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/networkGraph.js"></script>
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/networkGraph.js"></script>
|
||||||
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/gitgraph.js"></script>
|
||||||
|
<script src="{{ app.request.basepath }}/themes/{{ app.theme }}/js/draw.js"></script>
|
||||||
|
{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,4 +3,5 @@
|
|||||||
<li{% if page in ['commits', 'searchcommits'] %} class="active"{% endif %}><a href="{{ path('commits', {repo: repo, commitishPath: branch}) }}">Commits</a></li>
|
<li{% if page in ['commits', 'searchcommits'] %} class="active"{% endif %}><a href="{{ path('commits', {repo: repo, commitishPath: branch}) }}">Commits</a></li>
|
||||||
<li{% if page == 'stats' %} class="active"{% endif %}><a href="{{ path('stats', {repo: repo, branch: branch}) }}">Stats</a></li>
|
<li{% if page == 'stats' %} class="active"{% endif %}><a href="{{ path('stats', {repo: repo, branch: branch}) }}">Stats</a></li>
|
||||||
<li{% if page == 'network' %} class="active"{% endif %}><a href="{{ path('network', {repo: repo, branch: branch}) }}">Network</a></li>
|
<li{% if page == 'network' %} class="active"{% endif %}><a href="{{ path('network', {repo: repo, branch: branch}) }}">Network</a></li>
|
||||||
|
<li{% if page == 'treegraph' %} class="active"{% endif %}><a href="{{ path('treegraph', {repo: repo, branch: branch}) }}">Graph</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
48
themes/default/twig/treegraph.twig
Normal file
48
themes/default/twig/treegraph.twig
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{% extends 'layout_page.twig' %}
|
||||||
|
|
||||||
|
{% set page = 'treegraph' %}
|
||||||
|
|
||||||
|
{% block title %}GitList{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% include 'breadcrumb.twig' with {breadcrumbs: [{dir: 'Graph', path:''}]} %}
|
||||||
|
<div class="network-view">
|
||||||
|
<div class="network-header">
|
||||||
|
<div class="meta">Graph of {{ repo }} </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="git-graph-container">
|
||||||
|
<div id="rel-container">
|
||||||
|
<canvas id="graph-canvas" width="100px">
|
||||||
|
<ul id="graph-raw-list">
|
||||||
|
{% for item in graphItems %}
|
||||||
|
<li><span class="node-relation">{{ item.relation }}</span></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
<div style="float:left;" id="rev-container">
|
||||||
|
<ul id="rev-list">
|
||||||
|
{% for item in graphItems %}
|
||||||
|
<li>
|
||||||
|
{% if item.rev is defined %}
|
||||||
|
<a id="{{ item.short_rev }}" class="btn btn-small" href="{{ path('commit', {repo: repo, commit: item.rev}) }}"> {{ item.short_rev }} </a>
|
||||||
|
<strong> {{ item.branch }} </strong>
|
||||||
|
<em>{{ item.subject }}</em> by
|
||||||
|
<span class="author">{{ item.author }} <{{ item.author_email }}></span>
|
||||||
|
<span class="time">{{ item.date }}</span>;
|
||||||
|
{% else %}
|
||||||
|
<span/>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both"><!-- --></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user