mermaid/src/diagrams/gantt/ganttRenderer.js

421 lines
13 KiB
JavaScript
Raw Normal View History

2015-02-20 16:22:37 +01:00
var gantt = require('./parser/gantt').parser;
gantt.yy = require('./ganttDb');
var d3 = require('../../d3');
var moment = require('moment');
2015-02-08 20:07:15 +01:00
var daysInChart;
var conf = {
titleTopMargin: 25,
barHeight: 20,
barGap: 4,
topPadding: 50,
sidePadding: 75,
gridLineStartPadding: 35,
fontSize: 11,
fontFamily: '"Open-Sans", "sans-serif"'
};
module.exports.setConf = function (cnf) {
var keys = Object.keys(cnf);
keys.forEach(function (key) {
conf[key] = cnf[key];
});
};
var w;
2015-02-20 16:22:37 +01:00
module.exports.draw = function (text, id) {
gantt.yy.clear();
gantt.parse(text);
var elem = document.getElementById(id);
2015-06-16 10:40:08 +02:00
w = elem.parentElement.offsetWidth;
console.log('id='+id,' w='+w);
console.log(elem.parentElement);
if (typeof w === 'undefined') {
w = 1200;
}
var taskArray = gantt.yy.getTasks();
// Set height based on number of tasks
var h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding;
elem.setAttribute('height', "100%");
// Set viewBox
elem.setAttribute('viewBox','0 0 '+w+' '+h);
var svg = d3.select('#' + id);
2015-02-08 20:07:15 +01:00
2015-02-20 16:22:37 +01:00
var dateFormat = d3.time.format("%Y-%m-%d");
var startDate = d3.min(taskArray, function (d) {
return d.startTime;
});
var endDate = d3.max(taskArray, function (d) {
return d.endTime;
});
2015-02-20 16:22:37 +01:00
// Set timescale
var timeScale = d3.time.scale()
.domain([d3.min(taskArray, function (d) {
return d.startTime;
2015-02-20 16:22:37 +01:00
}),
d3.max(taskArray, function (d) {
return d.endTime;
2015-02-20 16:22:37 +01:00
})])
.rangeRound([0, w - 150]);
//.nice(d3.time.monday);
2015-02-08 20:07:15 +01:00
2015-02-20 16:22:37 +01:00
var categories = [];
daysInChart = moment.duration(endDate-startDate).asDays();
2015-02-08 20:07:15 +01:00
2015-02-20 16:22:37 +01:00
for (var i = 0; i < taskArray.length; i++) {
categories.push(taskArray[i].type);
}
var catsUnfiltered = categories; //for vert labels
2015-02-08 20:07:15 +01:00
2015-02-20 16:22:37 +01:00
categories = checkUnique(categories);
2015-02-08 20:07:15 +01:00
2015-02-20 16:22:37 +01:00
makeGant(taskArray, w, h);
2015-02-08 20:07:15 +01:00
2015-02-20 16:22:37 +01:00
var title = svg.append("text")
.text(gantt.yy.getTitle())
.attr("x", w / 2)
.attr("y", conf.titleTopMargin)
.attr('class', 'titleText');
2015-02-08 20:07:15 +01:00
2015-02-20 16:22:37 +01:00
function makeGant(tasks, pageWidth, pageHeight) {
2015-02-08 20:07:15 +01:00
var barHeight = conf.barHeight;
var gap = barHeight + conf.barGap;
var topPadding = conf.topPadding;
var sidePadding = conf.sidePadding;
2015-02-08 20:07:15 +01:00
2015-02-20 16:22:37 +01:00
var colorScale = d3.scale.linear()
.domain([0, categories.length])
.range(["#00B9FA", "#F95002"])
.interpolate(d3.interpolateHcl);
2015-02-08 20:07:15 +01:00
2015-02-20 16:22:37 +01:00
makeGrid(sidePadding, topPadding, pageWidth, pageHeight);
drawRects(tasks, gap, topPadding, sidePadding, barHeight, colorScale, pageWidth, pageHeight);
vertLabels(gap, topPadding, sidePadding, barHeight, colorScale);
2015-02-25 00:07:13 +01:00
drawToday(sidePadding, topPadding, pageWidth, pageHeight);
2015-02-08 20:07:15 +01:00
}
2015-02-20 16:22:37 +01:00
function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
var bigRects = svg.append("g")
.selectAll("rect")
.data(theArray)
.enter()
.append("rect")
.attr("x", 0)
.attr("y", function (d, i) {
return i * theGap + theTopPad - 2;
})
.attr("width", function (d) {
return w - theSidePad / 2;
})
.attr("height", theGap)
.attr('class', function (d) {
2015-02-20 16:22:37 +01:00
for (var i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
return 'section section' + (i % conf.numberSectionStyles);
2015-02-20 16:22:37 +01:00
}
2015-02-08 20:07:15 +01:00
}
return 'section section0';
});
2015-02-20 16:22:37 +01:00
var rectangles = svg.append('g')
.selectAll("rect")
.data(theArray)
.enter();
var innerRects = rectangles.append("rect")
.attr("rx", 3)
.attr("ry", 3)
.attr("x", function (d) {
return timeScale(d.startTime) + theSidePad;
})
.attr("y", function (d, i) {
return i * theGap + theTopPad;
})
.attr("width", function (d) {
return (timeScale(d.endTime) - timeScale(d.startTime));
})
.attr("height", theBarHeight)
.attr('class', function (d) {
var res = 'task ';
var secNum = 0;
for (var i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
secNum = (i % conf.numberSectionStyles);
}
2015-02-20 16:22:37 +01:00
}
if(d.active){
if (d.crit) {
return res + ' activeCrit'+secNum;
}else{
return res + ' active'+secNum;
}
}
if (d.done) {
if (d.crit) {
return res + ' doneCrit'+secNum;
}else{
return res + ' done'+secNum;
}
}
if (d.crit) {
return res + ' crit'+secNum;
}
return res + ' task'+secNum;
})
;
2015-02-20 16:22:37 +01:00
var rectText = rectangles.append("text")
.text(function (d) {
return d.task;
})
.attr("font-size",conf.fontSize)
//.attr("font-family",conf.fontFamily)
2015-02-20 16:22:37 +01:00
.attr("x", function (d) {
var startX = timeScale(d.startTime),
endX = timeScale(d.endTime),
textWidth = this.getBBox().width;
// Check id text width > width of rectangle
if (textWidth > (endX - startX)) {
if (endX + textWidth + 1.5*conf.sidePadding> w) {
return startX + theSidePad - 5;
} else {
return endX + theSidePad + 5;
}
} else {
return (endX - startX) / 2 + startX + theSidePad;
}
2015-02-20 16:22:37 +01:00
})
.attr("y", function (d, i) {
return i * theGap + (conf.barHeight / 2) + (conf.fontSize / 2 - 2) + theTopPad;
2015-02-20 16:22:37 +01:00
})
//.attr("text-anchor", "middle")
2015-02-20 16:22:37 +01:00
.attr("text-height", theBarHeight)
.attr("class", function (d) {
var startX = timeScale(d.startTime),
endX = timeScale(d.endTime),
textWidth = this.getBBox().width;
var secNum = 0;
for (var i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
secNum = (i % conf.numberSectionStyles);
}
}
var taskType = '';
if(d.active){
if (d.crit) {
taskType = 'activeCritText'+secNum;
}else{
taskType = 'activeText'+secNum;
}
}
if (d.done) {
if (d.crit) {
taskType = taskType + ' doneCritText'+secNum;
}else{
taskType = taskType + ' doneText'+secNum;
}
}else{
if (d.crit) {
taskType = taskType + ' critText'+secNum;
}
}
// Check id text width > width of rectangle
if (textWidth > (endX - startX)) {
if (endX + textWidth + 1.5*conf.sidePadding > w) {
return 'taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType;
} else {
return 'taskTextOutsideRight taskTextOutside' + secNum+ ' ' + taskType;
}
} else {
return 'taskText taskText' + secNum+ ' ' + taskType;
}
});
2015-02-20 16:22:37 +01:00
}
function makeGrid(theSidePad, theTopPad, w, h) {
var pre = [
[".%L", function (d) {
return d.getMilliseconds();
}],
[":%S", function (d) {
return d.getSeconds();
}],
// Within a hour
["h1 %I:%M", function (d) {
return d.getMinutes();
}]];
var post = [
["%Y", function () {
return true;
}]];
var mid = [
// Within a day
["%I:%M", function (d) {
return d.getHours();
}],
// Day within a week (not monday)
["%a %d", function (d) {
//return d.getDay() ==1;
return d.getDay() && d.getDate() != 1;
}],
// within a month
["%b %d", function (d) {
return d.getDate() != 1;
}],
// Month
["%B", function (d) {
return d.getMonth();
}]
];
var formatter;
if(typeof conf.axisFormatter !== 'undefined'){
mid = [];
conf.axisFormatter.forEach(function(item){
var n = [];
n[0] = item[0];
n[1] = item[1];
mid.push(n);
});
}
formatter = pre.concat(mid).concat(post);
2015-02-20 16:22:37 +01:00
var xAxis = d3.svg.axis()
2015-02-25 00:07:13 +01:00
.scale(timeScale)
.orient('bottom')
.tickSize(-h + theTopPad + conf.gridLineStartPadding, 0, 0)
.tickFormat(d3.time.format.multi(formatter))
2015-02-25 00:07:13 +01:00
;
2015-02-20 16:22:37 +01:00
if(daysInChart >7 && daysInChart<230){
xAxis = xAxis.ticks(d3.time.monday.range);
}
2015-02-20 16:22:37 +01:00
var grid = svg.append('g')
.attr('class', 'grid')
.attr('transform', 'translate(' + theSidePad + ', ' + (h - 50) + ')')
.call(xAxis)
.selectAll("text")
.style("text-anchor", "middle")
.attr("fill", "#000")
.attr("stroke", "none")
.attr("font-size", 10)
.attr("dy", "1em");
}
function vertLabels(theGap, theTopPad, theSidePad, theBarHeight, theColorScale) {
var numOccurances = [];
var prevGap = 0;
for (var i = 0; i < categories.length; i++) {
numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)];
}
var axisText = svg.append("g") //without doing this, impossible to put grid lines behind text
.selectAll("text")
.data(numOccurances)
.enter()
.append("text")
.text(function (d) {
return d[0];
})
.attr("x", 10)
.attr("y", function (d, i) {
if (i > 0) {
for (var j = 0; j < i; j++) {
prevGap += numOccurances[i - 1][1];
// console.log(prevGap);
return d[1] * theGap / 2 + prevGap * theGap + theTopPad;
}
} else {
return d[1] * theGap / 2 + theTopPad;
}
})
.attr('class', function (d) {
2015-02-20 16:22:37 +01:00
for (var i = 0; i < categories.length; i++) {
if (d[0] === categories[i]) {
return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles);
2015-02-20 16:22:37 +01:00
}
}
return 'sectionTitle';
2015-02-20 16:22:37 +01:00
});
}
2015-02-08 20:07:15 +01:00
2015-02-25 00:07:13 +01:00
function drawToday(theSidePad, theTopPad, w, h) {
var todayG = svg.append('g')
.attr('class', 'today');
2015-02-25 00:07:13 +01:00
var today = new Date();
2015-02-25 00:07:13 +01:00
var todayLine = todayG.append("line")
.attr("x1", timeScale(today) + theSidePad)
.attr("x2", timeScale(today) + theSidePad)
2015-02-25 00:07:13 +01:00
.attr("y1", conf.titleTopMargin)
.attr("y2", h-conf.titleTopMargin)
.attr('class', 'today')
;
}
2015-02-08 20:07:15 +01:00
//from this stackexchange question: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
2015-02-20 16:22:37 +01:00
function checkUnique(arr) {
var hash = {}, result = [];
for (var i = 0, l = arr.length; i < l; ++i) {
if (!hash.hasOwnProperty(arr[i])) { //it works with objects! in FF, at least
hash[arr[i]] = true;
result.push(arr[i]);
}
2015-02-08 20:07:15 +01:00
}
2015-02-20 16:22:37 +01:00
return result;
2015-02-08 20:07:15 +01:00
}
//from this stackexchange question: http://stackoverflow.com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array
2015-02-20 16:22:37 +01:00
function getCounts(arr) {
var i = arr.length, // var to loop over
obj = {}; // obj to store results
while (i) {
obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
}
2015-02-20 16:22:37 +01:00
return obj;
}
2015-02-08 20:07:15 +01:00
// get specific from everything
2015-02-20 16:22:37 +01:00
function getCount(word, arr) {
return getCounts(arr)[word] || 0;
}
};