ganttRenderer.js

var gantt = require('./parser/gantt').parser;
gantt.yy = require('./ganttDb');
var d3 = require('../../d3');
var moment = require('moment');
var log = require('../../logger').create();

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;
module.exports.draw = function (text, id) {
    gantt.yy.clear();
    gantt.parse(text);

    var elem = document.getElementById(id);
    w = elem.parentElement.offsetWidth;

    if (typeof w === 'undefined') {
        w = 1200;
    }

    if(typeof conf.useWidth !== 'undefined'){
        w = conf.useWidth;
    }

    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);

    
    
    
    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;
    });
    

Set timescale

    var timeScale = d3.time.scale()
        .domain([d3.min(taskArray, function (d) {
            return d.startTime;
        }),
            d3.max(taskArray, function (d) {
                return d.endTime;
            })])
        .rangeRound([0, w - 150]);

.nice(d3.time.monday);


    var categories = [];
    
    daysInChart = moment.duration(endDate-startDate).asDays();

    for (var i = 0; i < taskArray.length; i++) {
        categories.push(taskArray[i].type);
    }

    var catsUnfiltered = categories; //for vert labels

    categories = checkUnique(categories);


    makeGant(taskArray, w, h);
    if(typeof conf.useWidth !== 'undefined'){
        elem.setAttribute('width', w);

    }

    var title = svg.append("text")
        .text(gantt.yy.getTitle())
        .attr("x", w / 2)
        .attr("y", conf.titleTopMargin)
        .attr('class', 'titleText');


    function makeGant(tasks, pageWidth, pageHeight) {

        var barHeight = conf.barHeight;
        var gap = barHeight + conf.barGap;
        var topPadding = conf.topPadding;
        var sidePadding = conf.sidePadding;

        var colorScale = d3.scale.linear()
            .domain([0, categories.length])
            .range(["#00B9FA", "#F95002"])
            .interpolate(d3.interpolateHcl);

        makeGrid(sidePadding, topPadding, pageWidth, pageHeight);
        drawRects(tasks, gap, topPadding, sidePadding, barHeight, colorScale, pageWidth, pageHeight);
        vertLabels(gap, topPadding, sidePadding, barHeight, colorScale);
        drawToday(sidePadding, topPadding, pageWidth, pageHeight);

    }


    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) {
                for (var i = 0; i < categories.length; i++) {
                    if (d.type === categories[i]) {
                        return 'section section' + (i % conf.numberSectionStyles);
                    }
                }
                return 'section section0';
            });


        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);
                        }
                    }
                    
                    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;
                })
            ;


        var rectText = rectangles.append("text")
            .text(function (d) {
                return d.task;
            })
            .attr("font-size",conf.fontSize)

.attr("font-family",conf.fontFamily)

            .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;
                }
            })
            .attr("y", function (d, i) {
                return i * theGap + (conf.barHeight / 2) + (conf.fontSize / 2 - 2) + theTopPad;
            })

.attr("text-anchor", "middle")

            .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;
                }
            });

    }


    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);

        var xAxis = d3.svg.axis()
                .scale(timeScale)
                .orient('bottom')
                .tickSize(-h + theTopPad + conf.gridLineStartPadding, 0, 0)
                .tickFormat(d3.time.format.multi(formatter))
            ;

        if(daysInChart >7 && daysInChart<230){
            xAxis = xAxis.ticks(d3.time.monday.range);
        }

        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];

log.debug(prevGap);

                        return d[1] * theGap / 2 + prevGap * theGap + theTopPad;
                    }
                } else {
                    return d[1] * theGap / 2 + theTopPad;
                }
            })
            .attr('class', function (d) {
                for (var i = 0; i < categories.length; i++) {
                    if (d[0] === categories[i]) {
                        return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles);
                    }
                }
                return 'sectionTitle';
            });

    }

    function drawToday(theSidePad, theTopPad, w, h) {
        var todayG = svg.append('g')
            .attr('class', 'today');

        var today = new Date();

        var todayLine = todayG.append("line")
                .attr("x1", timeScale(today) + theSidePad)
                .attr("x2", timeScale(today) + theSidePad)
                .attr("y1", conf.titleTopMargin)
                .attr("y2", h-conf.titleTopMargin)
                .attr('class', 'today')
            ;
    }


from this stackexchange question: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript

    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]);
            }
        }
        return result;
    }


from this stackexchange question: http://stackoverflow.com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array

    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
        }
        return obj;
    }


get specific from everything

    function getCount(word, arr) {
        return getCounts(arr)[word] || 0;
    }
};