Skip to content

Instantly share code, notes, and snippets.

@greglockwood
Forked from orrery/index.html
Last active December 17, 2015 22:29
Show Gist options
  • Save greglockwood/5682846 to your computer and use it in GitHub Desktop.
Save greglockwood/5682846 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
<script>
// moment.js
// version : 1.6.0
// author : Tim Wood
// license : MIT
// momentjs.com
(function(a,b){function D(a,b){this._d=a,this._isUTC=!!b}function E(a){return a<0?Math.ceil(a):Math.floor(a)}function F(a){var b=this._data={},c=a.years||a.y||0,d=a.months||a.M||0,e=a.weeks||a.w||0,f=a.days||a.d||0,g=a.hours||a.h||0,h=a.minutes||a.m||0,i=a.seconds||a.s||0,j=a.milliseconds||a.ms||0;this._milliseconds=j+i*1e3+h*6e4+g*36e5,this._days=f+e*7,this._months=d+c*12,b.milliseconds=j%1e3,i+=E(j/1e3),b.seconds=i%60,h+=E(i/60),b.minutes=h%60,g+=E(h/60),b.hours=g%24,f+=E(g/24),f+=e*7,b.days=f%30,d+=E(f/30),b.months=d%12,c+=E(d/12),b.years=c}function G(a,b){var c=a+"";while(c.length<b)c="0"+c;return c}function H(a,b,c){var d=b._milliseconds,e=b._days,f=b._months,g;d&&a._d.setTime(+a+d*c),e&&a.date(a.date()+e*c),f&&(g=a.date(),a.date(1).month(a.month()+f*c).date(Math.min(g,a.daysInMonth())))}function I(a){return Object.prototype.toString.call(a)==="[object Array]"}function J(b){return new a(b[0],b[1]||0,b[2]||1,b[3]||0,b[4]||0,b[5]||0,b[6]||0)}function K(b,d){function q(d){var l,r;switch(d){case"M":return e+1;case"Mo":return e+1+o(e+1);case"MM":return G(e+1,2);case"MMM":return c.monthsShort[e];case"MMMM":return c.months[e];case"D":return f;case"Do":return f+o(f);case"DD":return G(f,2);case"DDD":return l=new a(g,e,f),r=new a(g,0,1),~~((l-r)/864e5+1.5);case"DDDo":return l=q("DDD"),l+o(l);case"DDDD":return G(q("DDD"),3);case"d":return h;case"do":return h+o(h);case"ddd":return c.weekdaysShort[h];case"dddd":return c.weekdays[h];case"w":return l=new a(g,e,f-h+5),r=new a(l.getFullYear(),0,4),~~((l-r)/864e5/7+1.5);case"wo":return l=q("w"),l+o(l);case"ww":return G(q("w"),2);case"YY":return G(g%100,2);case"YYYY":return g;case"a":return p?p(i,j,!1):i>11?"pm":"am";case"A":return p?p(i,j,!0):i>11?"PM":"AM";case"H":return i;case"HH":return G(i,2);case"h":return i%12||12;case"hh":return G(i%12||12,2);case"m":return j;case"mm":return G(j,2);case"s":return k;case"ss":return G(k,2);case"S":return~~(n/100);case"SS":return G(~~(n/10),2);case"SSS":return G(n,3);case"Z":return(m<0?"-":"+")+G(~~(Math.abs(m)/60),2)+":"+G(~~(Math.abs(m)%60),2);case"ZZ":return(m<0?"-":"+")+G(~~(10*Math.abs(m)/6),4);case"L":case"LL":case"LLL":case"LLLL":case"LT":return K(b,c.longDateFormat[d]);default:return d.replace(/(^\[)|(\\)|\]$/g,"")}}var e=b.month(),f=b.date(),g=b.year(),h=b.day(),i=b.hours(),j=b.minutes(),k=b.seconds(),m=-b.zone(),n=b.milliseconds(),o=c.ordinal,p=c.meridiem;return d.replace(l,q)}function L(a){switch(a){case"S":return n;case"SS":return q;case"SSS":case"DDDD":return r;case"YYYY":return s;case"DDD":return p;case"MMM":case"MMMM":case"ddd":case"dddd":case"a":case"A":return t;case"Z":case"ZZ":return u;case"T":return v;case"MM":case"DD":case"dd":case"YY":case"HH":case"hh":case"mm":case"ss":case"M":case"D":case"d":case"H":case"h":case"m":case"s":return o;default:return new RegExp(a.replace("\\",""))}}function M(a,b,d,e){var f;switch(a){case"M":case"MM":d[1]=~~b-1;break;case"MMM":case"MMMM":for(f=0;f<12;f++)if(c.monthsParse[f].test(b)){d[1]=f;break}break;case"D":case"DD":case"DDD":case"DDDD":d[2]=~~b;break;case"YY":b=~~b,d[0]=b+(b>70?1900:2e3);break;case"YYYY":d[0]=~~Math.abs(b);break;case"a":case"A":e.isPm=b.toLowerCase()==="pm";break;case"H":case"HH":case"h":case"hh":d[3]=~~b;break;case"m":case"mm":d[4]=~~b;break;case"s":case"ss":d[5]=~~b;break;case"S":d[6]=~~b*100;break;case"SS":d[6]=~~b*10;break;case"SSS":d[6]=~~b;break;case"Z":case"ZZ":e.isUTC=!0,f=(b+"").match(z),f&&f[1]&&(e.tzh=~~f[1]),f&&f[2]&&(e.tzm=~~f[2]),f&&f[0]==="+"&&(e.tzh=-e.tzh,e.tzm=-e.tzm)}}function N(b,c){var d=[0,0,1,0,0,0,0],e={tzh:0,tzm:0},f=c.match(l),g,h;for(g=0;g<f.length;g++)h=(L(f[g]).exec(b)||[0])[0],b=b.replace(L(f[g]),""),M(f[g],h,d,e);return e.isPm&&d[3]<12&&(d[3]+=12),e.isPm===!1&&d[3]===12&&(d[3]=0),d[3]+=e.tzh,d[4]+=e.tzm,e.isUTC?new a(a.UTC.apply({},d)):J(d)}function O(a,b){var c=Math.min(a.length,b.length),d=Math.abs(a.length-b.length),e=0,f;for(f=0;f<c;f++)~~a[f]!==~~b[f]&&e++;return e+d}function P(a,b){var c,d=a.match(m),e,f=99,g,h,i;for(g=0;g<b.length;g++)h=N(a,b[g]),e=K(new D(h),b[g]).match(m),i=O(d,e),i<f&&(f=i,c=h);return c}function Q(b){var c="YYYY-MM-DDT",d;if(w.exec(b)){for(d=0;d<3;d++)if(y[d][1].exec(b)){c+=y[d][0];break}return u.exec(b)?N(b,c+" Z"):N(b,c)}return new a(b)}function R(a,b,d,e){var f=c.relativeTime[a];return typeof f=="function"?f(b||1,!!d,a,e):f.replace(/%d/i,b||1)}function S(a,b){var c=e(Math.abs(a)/1e3),d=e(c/60),f=e(d/60),g=e(f/24),h=e(g/365),i=c<45&&["s",c]||d===1&&["m"]||d<45&&["mm",d]||f===1&&["h"]||f<22&&["hh",f]||g===1&&["d"]||g<=25&&["dd",g]||g<=45&&["M"]||g<345&&["MM",e(g/30)]||h===1&&["y"]||["yy",h];return i[2]=b,i[3]=a>0,R.apply({},i)}function T(a,b){c.fn[a]=function(a){var c=this._isUTC?"UTC":"";return a!=null?(this._d["set"+c+b](a),this):this._d["get"+c+b]()}}function U(a){c.duration.fn[a]=function(){return this._data[a]}}function V(a,b){c.duration.fn["as"+a]=function(){return+this/b}}var c,d="1.6.0",e=Math.round,f,g={},h="en",i=typeof module!="undefined",j="months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem".split("|"),k=/^\/?Date\((\-?\d+)/i,l=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|zz?|ZZ?|LT|LL?L?L?)/g,m=/([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,n=/\d/,o=/\d\d?/,p=/\d{1,3}/,q=/\d\d/,r=/\d{3}/,s=/\d{4}/,t=/[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+/i,u=/[\+\-]\d\d:?\d\d/i,v=/T/i,w=/^\s*\d{4}-\d\d-\d\d(T(\d\d(:\d\d(:\d\d)?)?)?([\+\-]\d\d:?\d\d)?)?/,x="YYYY-MM-DDTHH:mm:ssZ",y=[["HH:mm:ss",/T\d\d:\d\d:\d\d/],["HH:mm",/T\d\d:\d\d/],["HH",/T\d\d/]],z=/([\+\-]|\d\d)/gi,A="Month|Date|Hours|Minutes|Seconds|Milliseconds".split("|"),B="years|months|days|hours|minutes|seconds|milliseconds".split("|"),C={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Weeks:6048e5,Months:2592e6,Years:31536e6};c=function(d,e){if(d===null||d==="")return null;var f,g,h;return c.isMoment(d)?(f=new a(+d._d),h=d._isUTC):e?I(e)?f=P(d,e):f=N(d,e):(g=k.exec(d),f=d===b?new a:g?new a(+g[1]):d instanceof a?d:I(d)?J(d):typeof d=="string"?Q(d):new a(d)),new D(f,h)},c.utc=function(b,d){return I(b)?new D(new a(a.UTC.apply({},b)),!0):d&&b?c(b+" +0000",d+" Z").utc():c(u.exec(b)?b:b+"+0000").utc()},c.unix=function(a){return c(a*1e3)},c.duration=function(a,b){var d=c.isDuration(a),e=typeof a=="number",f=d?a._data:e?{}:a;return e&&(b?f[b]=a:f.milliseconds=a),new F(f)},c.humanizeDuration=function(a,b,d){return c.duration(a,b).humanize(d)},c.version=d,c.defaultFormat=x,c.lang=function(a,b){var d,e,f=[];if(!a)return h;if(b){for(d=0;d<12;d++)f[d]=new RegExp("^"+b.months[d]+"|^"+b.monthsShort[d].replace(".",""),"i");b.monthsParse=b.monthsParse||f,g[a]=b}if(g[a]){for(d=0;d<j.length;d++)c[j[d]]=g[a][j[d]]||g.en[j[d]];h=a}else i&&(e=require("./lang/"+a),c.lang(a,e))},c.lang("en",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},meridiem:!1,calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(a){var b=a%10;return~~(a%100/10)===1?"th":b===1?"st":b===2?"nd":b===3?"rd":"th"}}),c.isMoment=function(a){return a instanceof D},c.isDuration=function(a){return a instanceof F},c.fn=D.prototype={clone:function(){return c(this)},valueOf:function(){return+this._d},unix:function(){return Math.floor(+this._d/1e3)},toString:function(){return this._d.toString()},toDate:function(){return this._d},utc:function(){return this._isUTC=!0,this},local:function(){return this._isUTC=!1,this},format:function(a){return K(this,a?a:c.defaultFormat)},add:function(a,b){var d=b?c.duration(+b,a):c.duration(a);return H(this,d,1),this},subtract:function(a,b){var d=b?c.duration(+b,a):c.duration(a);return H(this,d,-1),this},diff:function(a,b,d){var f=this._isUTC?c(a).utc():c(a).local(),g=(this.zone()-f.zone())*6e4,h=this._d-f._d-g,i=this.year()-f.year(),j=this.month()-f.month(),k=this.date()-f.date(),l;return b==="months"?l=i*12+j+k/30:b==="years"?l=i+(j+k/30)/12:l=b==="seconds"?h/1e3:b==="minutes"?h/6e4:b==="hours"?h/36e5:b==="days"?h/864e5:b==="weeks"?h/6048e5:h,d?l:e(l)},from:function(a,b){return c.duration(this.diff(a)).humanize(!b)},fromNow:function(a){return this.from(c(),a)},calendar:function(){var a=this.diff(c().sod(),"days",!0),b=c.calendar,d=b.sameElse,e=a<-6?d:a<-1?b.lastWeek:a<0?b.lastDay:a<1?b.sameDay:a<2?b.nextDay:a<7?b.nextWeek:d;return this.format(typeof e=="function"?e.apply(this):e)},isLeapYear:function(){var a=this.year();return a%4===0&&a%100!==0||a%400===0},isDST:function(){return this.zone()<c([this.year()]).zone()||this.zone()<c([this.year(),5]).zone()},day:function(a){var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return a==null?b:this.add({d:a-b})},sod:function(){return this.clone().hours(0).minutes(0).seconds(0).milliseconds(0)},eod:function(){return this.sod().add({d:1,ms:-1})},zone:function(){return this._isUTC?0:this._d.getTimezoneOffset()},daysInMonth:function(){return this.clone().month(this.month()+1).date(0).date()}};for(f=0;f<A.length;f++)T(A[f].toLowerCase(),A[f]);T("year","FullYear"),c.duration.fn=F.prototype={weeks:function(){return E(this.days()/7)},valueOf:function(){return this._milliseconds+this._days*864e5+this._months*2592e6},humanize:function(a){var b=+this,d=c.relativeTime,e=S(b,!a);return a&&(e=(b<=0?d.past:d.future).replace(/%s/i,e)),e}};for(f=0;f<B.length;f++)U(B[f]);for(f in C)C.hasOwnProperty(f)&&V(f,C[f]);i&&(module.exports=c),typeof window!="undefined"&&typeof ender=="undefined"&&(window.moment=c),typeof define=="function"&&define.amd&&define("moment",[],function(){return c})})(Date);
</script>
<style type="text/css">
#chart-mini {
margin: 0 auto 0px auto;
box-shadow: 3px 2px 7px rgba(0, 0, 0, 0.6);
padding: 0px 0px;
}
#chart-headertop {
height: 45px;
margin: 0 auto 0px auto;
box-shadow: 3px -2px 7px rgba(0, 0, 0, 0.6);
padding: 0px 0px;
}
#chart-container {
height: 450px;
width: 1101px;
margin: 0 auto 0px auto;
box-shadow: 3px 2px 7px rgba(0, 0, 0, 0.6);
padding: 0px 0px;
overflow-y: visible;
overflow-x: hidden;
}
.axis path, .axis line {
fill: none;
stroke: #888888;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.brush .extent {
stroke: gray;
fill: dodgerblue;
fill-opacity: .365;
}
path.link {
fill: none;
/* stroke: #3182bd; */
stroke: #888888;
stroke-width: 1px;
}
.header rect {
cursor: pointer;
fill-opacity: 1.0;
stroke: #888888;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.node rect {
cursor: pointer;
fill-opacity: 1.0;
stroke: #888888;
stroke-width: 1px;
}
.mini text {
font: 10px sans-serif;
pointer-events: none;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
}
.main text {
font: 10px sans-serif;
pointer-events: none;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
}
.axis text {
font: 10px sans-serif;
pointer-events: none;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
}
#gantt {
padding: 2em 0 2em 0;
}
</style>
<!-- link rel="stylesheet" type="text/css" href="css/gantt.css"
/-->
<title>Gantt chart</title>
</head>
<body>
<div class="container">
<section class="main">
<p>
<div id="chart-headertop"></div>
<div id="chart-container"></div>
<script type="text/javascript">
function GanttChart(jsonData, dateFormat) {
/**
*the top gap in pixels
*/
var topGap = 40;
/**
*the right gap in pixels
*/
var rightGap = 40;
/**
*the bottom gap in pixels
*/
var bottomGap = 40;
/**
*the left gap in pixels
*/
var leftGap = 40;
/**
*the horizontal size of a cell in pixels
*/
var gridSizeX = 90;
/**
*the vertical size of a cell in pixels
*/
var gridSizeY = 18;
/**
*the vertical gap between cells in pixels
*/
var nodeGap = 3;
/**
* the gap between header svg and the bottom of the header canvas
*/
var headerGap = 3;
/**
*the number of pixels to offset text horizontally within a cell.
*/
var textOffsetX = 4;
/**
*the number of pixels to offset text vertically within a cell, around the vertical mid-point.
*/
var textOffsetY = 4;
/**
*offsets the chart canvas this number of pixels to the right
*/
var chartOffsetX = 5;
/**
* transition speed in ms
*/
var transitionSpeed = 600;
/**
* the relative location in the tree grid node where the connector is positioned vertically (between 0 and 1.0)
*/
var scaleFactorConnection = 0.6;
/**
*tree grid view node rounding on X-axis
*/
var roundX = 3;
/**
* tree grid view node rounding on Y-axis
*/
var roundY = 5;
var brushHeight = 80;
var miniGap = 25;
/**
*cell color at each depth (for depth exceeding colors.length, colors start from beginning again)
*/
//http://simple.be/web/color/codes is handy
var colors = ["#f99b0c", "#f9603a ", "#f98972", "#f9baaa ", "#afbffb"];
var local = this;
local.margin = {
top: topGap,
right: rightGap,
bottom: bottomGap,
left: leftGap
};
local.width = $("#chart-container").width() - local.margin.left - local.margin.right;
local.height = $("#chart-container").height() - local.margin.top - local.margin.bottom;
$("#chart-mini").width(local.width + local.margin.left + local.margin.top);
$("#chart-headertop").width(local.width + local.margin.left + local.margin.top);
local.gridsizex = gridSizeX; // width of cell
local.gridsizey = gridSizeY; // height of cell
local.textoffsety = local.gridsizey / 2 + textOffsetY;
local.textoffsetx = textOffsetX; //local.gridsizex/2-local.gridsizey;
local.chartoffsetx = chartOffsetX;
local.nodeGap = nodeGap; // the gap between nodes in the tree view
local.headerGap = headerGap; // the gap between the header boxes and the bottom of the header canvas
local.chartoffsety = local.gridsizey + local.gridsizey / 2 + local.headerGap;
local.root;
local.nodes;
local.tree;
local.treedepth;
local.idCount = 0;
local.duration = d3.event && d3.event.altKey ? 7000 : transitionSpeed;
local.diagonal = d3.svg.diagonal().projection(function (d) {
return [d.x, d.y + local.gridsizey * scaleFactorConnection];
});
local.visitedNodeMap = {}
local.chart = initialiseChartCanvas();
local.menu = initialiseHeaderCanvas();
local.root = jsonData;
// create tree layout
local.tree = d3.layout.tree();
local.nodes = local.tree.nodes(jsonData);
local.mini = initialiseminiCanvas();
// get depth of tree.
local.treedepth = getTreeDepth(local.nodes);
createHeader(getColHeader(local.nodes));
updateTimeStampsOnNodes(local.nodes);
local.xScale = createXScale(local.nodes);
local.xScaleBrush = createXScaleBrush(local.nodes);
local.xHeaderDateAxis = d3.svg.axis()
.scale(local.xScale)
.orient('top')
.ticks(d3.time.years, 1)
.tickFormat(d3.time.format('%Y'))
.tickSize(6, 3, 0);
local.menu.append('g') // append month axes (top)
.attr('transform', "translate(" + 0 + "," + -headerGap + ")")
.attr('class', 'main axis date')
.call(local.xHeaderDateAxis)
.selectAll('text')
var xMiniYearAxis = d3.svg.axis()
.scale(local.xScaleBrush)
.orient('top')
.ticks(d3.time.weeks, 26)
.tickFormat(d3.time.format('Wk.%U'))
.tickSize(6, 3, 0);
var xMiniMonthAxis = d3.svg.axis()
.scale(local.xScaleBrush)
.orient('bottom')
.ticks(d3.time.weeks, 26)
.tickFormat(d3.time.format('%d/%m/%Y'))
.tickSize(6, 3, 0);
local.visitedNodeMap = [];
// count the levels in the mini
local.countLevels = 0;
local.nodes.forEach(function (n, i) {
n.x = local.xScaleBrush(n._start);
// no mod for root node
if (!n.parent) {
local.treeIndexOffsetCounter = 0;
n.y = 50 + local.treeIndexOffsetCounter;
local.countLevels++;
} else {
// leaf node has not been seen under this parent before, therefore
// we add it to the visitedNodeMap for leaf nodes under the same parent,
// so the y coordinate can be reused.
if (!local.visitedNodeMap[n.parent.label + n.label]) {
local.treeIndexOffsetCounter += 1;
n.y = 50 + (local.treeIndexOffsetCounter - 1);
local.visitedNodeMap[n.parent.label + n.label] = {
y: n.y
};
local.countLevels++;
}
// this leaf node under this parent has been seen before, therefore
// the calculation for the y position should use the previous y position
// for this leaf node under this parent.
// This re-uses the y coordinate of the first occurence of this leaf node
// under the same parent.
else {
n.x = local.xScaleBrush(n._start);
properNodePosition = local.visitedNodeMap[n.parent.label + n.label];
n.y = properNodePosition.y;
}
}
});
local.mini.append('g')
.attr('transform', 'translate(0,' + miniGap + ')')
.attr('class', 'mini axis year')
.call(xMiniYearAxis)
.selectAll('text')
local.mini.append('g')
.attr('transform', 'translate(0,' + miniGap + ')')
.attr('class', 'mini axis month')
.call(xMiniMonthAxis)
.selectAll('text')
//mini item rects
local.mini.append("g")
.selectAll("miniItems")
.data(local.nodes)
.enter().append("rect")
.attr("fill", function (d, i) {
return getCellColor(d.depth)
})
.attr("class", function (d, i) {
return "miniItem" + i;
})
.attr("x", function (d) {
return local.xScaleBrush(d._start);
})
.attr("y", function (d, i) {
return d.y;
})
.attr("width", function (d) {
return (local.xScaleBrush(d._end) - local.xScaleBrush(d._start));
})
.attr("height", 1);
// now we know the node count, reset the brush height.
brushHeight = local.countLevels + 60;
$("#chart-mini").height(brushHeight);
//formatNodes(local.nodes)
// can copy here for display and modification, currently just reference.
local.root = jsonData;
//brush
local.brush = d3.svg.brush()
.x(local.xScaleBrush);
local.brush.on("brush", function () {
update(local.root);
}); //update(local.root= jsonData));
local.brush.extent([local.root._start, local.root._end]);
local.mini.append("g")
.attr("class", "x brush")
.call(local.brush)
.selectAll("rect")
.attr("y", 1)
.attr("height", brushHeight - 1);
update(local.root);
// updates time stamps on nodes.
// iterates over all sub-trees, and updates time stamps on each node level.
function updateTimeStampsOnNodes(nodes) {
//assignTimeStamps(nodes[0])
assignTimeStamps(nodes[0])
}
function createXScaleBrush(nodes) {
return d3.time.scale().domain([nodes[0]._start, nodes[0]._end]).range([0, local.width + local.margin.left + 24]);
}
function createXScale(nodes) {
return d3.time.scale().domain([nodes[0]._start, nodes[0]._end]).range([(local.treedepth + 1) * local.gridsizex, local.width + local.margin.left + 24]);
}
/**
* recursively assign start and end date times to nodes.
* @param {Object} node
*/
function assignTimeStamps(node) {
if (node.children) {
for (var i = 0; i < node.children.length; i++) {
var child = node.children[i];
if (child == {}) {
continue;
}
var range = assignTimeStamps(child);
if (i == 0) {
node._start = range[0];
node._end = range[1];
} else {
if (range[0].getTime() < node._start.getTime()) {
node._start = range[0];
}
if (range[1].getTime() > node._end.getTime()) {
node._end = range[1];
}
}
}
return [node._start, node._end];
}
// no more children (leaf node expects a start time and duration)
else {
node._start = getDate(node);
node._end = addDurationAndGetDate(node);
return [node._start, node._end];
}
}
/**
* recursively assign start and end date times to nodes, where leaf nodes can have multiple activities.
* @param {Object} node
*/
function assignTimeStampsForMultiActivities(node) {
if (node.children) {
for (var i = 0; i < node.children.length; i++) {
var child = node.children[i];
if (child == {}) {
continue;
}
var range = assignTimeStampsForMultiActivities(child);
if (i == 0) {
node._start = range[0];
node._end = range[1];
} else {
if (range[0].getTime() < node._start.getTime()) {
node._start = range[0];
}
if (range[1].getTime() > node._end.getTime()) {
node._end = range[1];
}
}
}
return [node._start, node._end];
}
// no more children (leaf node expects a start time and duration)
else {
node._start = getDate(node.activities[0]);
node._end = addDurationAndGetDate(node.activities[0]);
node.activities[0]._start = node._start;
node.activities[0]._end = node._end;
for (var j = 1; j < node.activities.length; j++) {
node.activities[j]._start = getDate(node.activities[j]);
node.activities[j]._end = addDurationAndGetDate(node.activities[j]);
if (node.activities[j]._start < node.activities[j - 1]._start) {
node._start = node.activities[j]._start;
}
if (node.activities[j]._end > node.activities[j - 1]._end) {
node._end = node.activities[j]._end;
}
}
//node._start = getDate(node);
//node._end = addDurationAndGetDate(node);
return [node._start, node._end];
}
}
function createHeader(colheader) {
var maxlength = 0;
for (var i = 0; i < colheader.length; i++) {
ch = colheader[i]
if (ch != null && ch.length > maxlength) {
maxlength = ch.length;
}
}
// resets grid size based on text length (note local should get the font size from the CSS and not use an arbitrary scaling factor)
local.gridsizex = 6 * maxlength;
// now get the font size, and set local.gridsizex in relation to the max length and font size.
var header = local.menu.selectAll("g.header")
.data(colheader)
// antony - fix scaling factor
var headerEnter = header.enter().append("svg:g") // a group of elements inside an svg canvas
.attr("class", "header")
.attr("transform", function (d, i) {
return "translate(" + (local.gridsizex * i) + "," + -(local.gridsizey + 5 * local.headerGap) + ")";
})
headerEnter.append("svg:rect")
.attr("fill", function (d, i) {
return getCellColor(i)
})
.attr("width", local.gridsizex)
.attr("height", local.gridsizey * 2)
// antony - fix scaling factor
headerEnter.append("svg:text")
.attr("dx", local.textoffsetx)
.attr("dy", local.textoffsety * 1.6)
.text(function (d) {
return d;
})
}
function initialiseminiCanvas() {
brushHeight = local.nodes.length + 60;
$("#chart-mini").height(brushHeight);
var chartbrush = d3.select("#chart-mini").append("svg:svg")
.attr("width", local.width + local.margin.left + local.margin.right)
.attr("height", brushHeight)
.attr('class', 'chartbrush');
chartbrush.append('defs').append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', local.width + local.margin.left + local.margin.right) // set width of rect width of chart
.attr('height', brushHeight); // rect is mainheight high
// antony - fix scaling factor
var mini = chartbrush.append('g')
.attr("transform", "translate(" + local.chartoffsetx + "," + 0 + ")") // translate the group to these coordinates
.attr('width', local.width + local.margin.left + local.margin.right) // set width of rect width of chart
.attr('height', brushHeight)
.attr('class', 'mini'); // sets the class attribute to 'main'
return mini;
}
// initialises the header canvas.
function initialiseHeaderCanvas() {
var chartheader = d3.select("#chart-headertop").append("svg:svg")
.attr("width", local.width + local.margin.left + local.margin.right)
.attr("height", local.gridsizey + local.gridsizey * 0.5 + local.headerGap)
.attr('class', 'chartheader');
chartheader.append('defs').append('clipPath')
.attr('id', 'clipmini')
.append('rect')
.attr('width', local.width + local.margin.left + local.margin.right) // set width of rect width of chart
.attr('height', local.gridsizey + local.gridsizey * 0.5 + local.headerGap); // rect is mainheight high
var mini = chartheader.append('g')
.attr("transform", "translate(" + local.chartoffsetx + "," + (local.chartoffsety - 0.8 * local.headerGap) + ")") // translate the group to these coordinates
.attr('width', local.width + local.margin.left + local.margin.right) // set width of rect width of chart
.attr('height', local.gridsizey + local.gridsizey * 0.5 + local.headerGap) // rect is mainheight high
.attr('class', 'mini'); // sets the class attribute to 'main'
return mini;
}
// initialise
function initialiseChartCanvas() {
var chart = d3.select('#chart-container')
.append('svg:svg')
.attr('width', local.width + local.margin.right + local.margin.left)
.attr('height', local.height + local.margin.top + local.margin.bottom)
.attr('class', 'chart');
chart.append('defs').append('clipPath') // add clip path to 'defs'
.attr('id', 'clipmain') // assign id to clip path
.append('rect') // append rectangle to clip
.attr("class", "cliprect")
.attr('width', local.width) // set width of rect width of chart
.attr('height', local.height); // rect is mainheight high
// creating a grouping in the chart, which is a transform (translation left and to the top)
var main = chart.append('g')
.attr("transform", "translate(" + local.chartoffsetx + "," + local.chartoffsety + ")") // translate the group to these coordinates
.attr('width', local.width) // set height and width of main
.attr('height', local.height)
.attr('class', 'main')
return main;
}
function update(source) {
// get tree nodes.
local.nodes = local.tree.nodes(local.root);
// update the layout of nodes (removing collapsed nodes from the list in the next update pass), and do node transition animations.
updateNodeLayout(source);
// do the link transition animations.
updateLinkLayout(source);
// update the canvas height based on number of expanded tree elements.
updateCanvasHeight();
}
// Toggle children on click
// (for next update pass, only visible children will be drawn)
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
// calculate the depth of the tree.
function getTreeDepth(nodes) {
var depth = 0;
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].depth > depth) {
depth = nodes[i].depth;
}
}
return depth;
}
function updateLinkLayout(source) {
// enter new links at parent's previous location.
var link = local.chart.selectAll("path.link")
.data(local.tree.links(local.nodes), function (d) {
return d.target.id;
});
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function (d) {
var o = {
x: source.x0 + local.gridsizex * 0.5,
y: source.y0
};
return local.diagonal({
source: o,
target: o
});
})
.transition()
.duration(local.duration)
.attr("d", local.diagonal);
// Transition links to their new position.
link.transition()
.duration(local.duration)
.attr("d", local.diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition().duration(local.duration).attr("d", function (d) {
var o = {
x: source.x,
y: source.y
};
return local.diagonal({
source: o,
target: o
});
}).remove();
// update lanes
// Stash the old positions for transition.
local.nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
function openWin(text) {
popupGanttWindow = window.open('', '', 'width=200,height=100');
popupGanttWindow.document.write(text);
popupGanttWindow.focus();
}
function updateNodeLayout(source) {
// Compute the "layout".
var minExtent = local.brush.extent()[0];
var maxExtent = local.brush.extent()[1];
var visItems = local.nodes.filter(function (d) {
return d._start < maxExtent && d._end > minExtent;
});
local.mini.select(".brush").call(local.brush.extent([minExtent, maxExtent]));
local.xScale.domain([minExtent, maxExtent]);
//console.log(maxExtent-minExtent)
if ((maxExtent - minExtent) > 103452230000) {
local.xHeaderDateAxis.ticks(d3.time.months, 6).tickFormat(d3.time.format('%d/%m/%Y')).tickSize(6, 3, 0);
//x1MonthAxis.ticks(d3.time.mondays, 1).tickFormat(d3.time.format('%b - Week %W'))
} else if ((maxExtent - minExtent) > 73452230000) {
local.xHeaderDateAxis.ticks(d3.time.months, 3).tickFormat(d3.time.format('%d/%m/%Y')).tickSize(6, 3, 0);
//x1MonthAxis.ticks(d3.time.mondays, 1).tickFormat(d3.time.format('%b - Week %W'))
} else if ((maxExtent - minExtent) > 43452240000) {
local.xHeaderDateAxis.ticks(d3.time.months, 2).tickFormat(d3.time.format('%d/%m/%Y')).tickSize(6, 3, 0);
//x1MonthAxis.ticks(d3.time.mondays, 1).tickFormat(d3.time.format('%b - Week %W'))
} else if ((maxExtent - minExtent) > 20452240000) {
local.xHeaderDateAxis.ticks(d3.time.months, 1).tickFormat(d3.time.format('%d/%m/%Y')).tickSize(6, 3, 0);
//x1MonthAxis.ticks(d3.time.mondays, 1).tickFormat(d3.time.format('%b - Week %W'))
} else if ((maxExtent - minExtent) > 7452240000) {
local.xHeaderDateAxis.ticks(d3.time.weeks, 2).tickFormat(d3.time.format('%d/%m/%Y')).tickSize(6, 3, 0);
} else if ((maxExtent - minExtent) > 4452240000) {
local.xHeaderDateAxis.ticks(d3.time.weeks, 1).tickFormat(d3.time.format('%d/%m/%Y')).tickSize(6, 3, 0);
} else if ((maxExtent - minExtent) > 3052240000) {
local.xHeaderDateAxis.ticks(d3.time.days, 2).tickFormat(d3.time.format('%d/%m')).tickSize(6, 3, 0);
} else if ((maxExtent - minExtent) > 297200000) {
local.xHeaderDateAxis.ticks(d3.time.days, 1).tickFormat(d3.time.format('%d/%m')).tickSize(6, 3, 0);
} else if ((maxExtent - minExtent) > 287200000) {
local.xHeaderDateAxis.ticks(d3.time.hours, 12).tickFormat(d3.time.format('%a-%H:%M')).tickSize(6, 3, 0);
} else if ((maxExtent - minExtent) > 267200000) {
local.xHeaderDateAxis.ticks(d3.time.hours, 6).tickFormat(d3.time.format('%a-%H:%M')).tickSize(6, 3, 0);
} else if ((maxExtent - minExtent) > 247200000) {
local.xHeaderDateAxis.ticks(d3.time.hours, 4).tickFormat(d3.time.format('%a-%H:%M')).tickSize(6, 3, 0);
} else if ((maxExtent - minExtent) > 57200000) {
local.xHeaderDateAxis.ticks(d3.time.hours, 3).tickFormat(d3.time.format('%a-%H:%M')).tickSize(6, 3, 0);
} else if ((maxExtent - minExtent) > 4800000) {
local.xHeaderDateAxis.ticks(d3.time.hours, 1).tickFormat(d3.time.format('%a-%H:%M')).tickSize(6, 3, 0);
}
local.menu.select('.main.axis.date').call(local.xHeaderDateAxis);
// reset the visited node map so tree collapse updates correctly.
local.treeIndexOffsetCounter = 0;
local.visitedNodeMap = [];
local.nodes.forEach(function (n, i) {
// no mod for root node
if (!n.parent) {
n.x = n.depth * local.gridsizex;
n.x0 = n.x;
local.treeIndexOffsetCounter = 0;
n.y = (local.treeIndexOffsetCounter - 1) * local.gridsizey;
n.y0 = n.y;
} else {
// leaf node has not been seen under this parent before, therefore
// we add it to the visitedNodeMap for leaf nodes under the same parent,
// so the y coordinate can be reused.
if (!local.visitedNodeMap[n.parent.label + n.label]) {
local.treeIndexOffsetCounter += 1;
n.x = n.depth * local.gridsizex;
n.x0 = n.x;
n.y = (local.treeIndexOffsetCounter - 1) * local.gridsizey;
n.y0 = n.y;
local.visitedNodeMap[n.parent.label + n.label] = {
y: n.y,
y0: n.y0
};
}
// this leaf node under this parent has been seen before, therefore
// the calculation for the y position should use the previous y position
// for this leaf node under this parent.
// This re-uses the y coordinate of the first occurence of this leaf node
// under the same parent.
else {
n.x = n.depth * local.gridsizex;
n.x0 = n.x;
properNodePosition = local.visitedNodeMap[n.parent.label + n.label];
n.y = properNodePosition.y;
n.y0 = properNodePosition.y0;
}
}
});
// update the nodes
var node = local.chart.selectAll("g.node")
.data(local.nodes, function (d) {
if (!d.id) {
d.id = ++local.idCount;
return d.id;
}
return d.id;
});
// enter any new nodes at the parent's previous position
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + source.x0 + "," + source.y0 + ")";
})
.style("opacity", 1e-6)
// reset visited map so we only draw the guide lines once
local.visitedNodeMap = [];
nodeEnter.append("svg:line")
.attr("class", "lane")
.attr("x1", function (d) {
return 0;
})
.attr("y1", function (d) {
return local.gridsizey / 2;
})
.attr("x2", local.width + local.margin.left + local.margin.right)
.attr("y2", function (d) {
return local.gridsizey / 2;
})
.attr("stroke", function (d) {
return getCellColor(d.depth);
})
.attr("opacity", function (d) {
if (d.parent) {
if (local.visitedNodeMap[d.parent.label + d.label]) {
return 0.0;
} else {
local.visitedNodeMap[d.parent.label + d.label] = true;
return 1.0;
}
}
return 1.0;
})
.attr("stroke-width", "4px");
// need to add clip
node.selectAll(".activitybar")
.attr('x', function (d) {
return (local.xScale(d._start) - (d.depth) * local.gridsizex);
})
.attr('width', function (d) {
var time = (local.xScale(d._end) - local.xScale(d._start));
return time;
})
node.append("rect")
.attr("class", "activitybar")
.attr('x', function (d) {
return (local.xScale(d._start) - (d.depth) * local.gridsizex);
})
.attr('y', function (d) {
return 0;
})
.attr('width', function (d) {
var time = (local.xScale(d._end) - local.xScale(d._start));
if (time == 0) {
return local.width;
}
return time;
})
.attr('opacity', function (d) {
var time = (local.xScale(d._end) - local.xScale(d._start));
if (time == 0) {
return 0.3;
}
return 1.0;
})
.attr('height', function (d) {
return local.gridsizey - 4;
})
.attr('fill', function (d) {
var time = (local.xScale(d._end) - local.xScale(d._start));
if (time == 0) {
return 'grey';
}
return getCellColor(d.depth);
})
.attr("stroke", function (d) {
return "black";
})
.on("click", function (d) {
openWin("label : " + d.label + "<br>start : " + d._start + "<br>end : " + d._end);
})
.append("svg:title")
.text(function (d, i) {
var time = (local.xScale(d._end) - local.xScale(d._start));
var str = ("label : " + d.label + "\nstart : " + d._start + "\nend : " + d._end);
if (time == 0) {
return str + "\n No duration provided for this activity.";
}
return str;
});
nodeEnter.append("svg:rect")
.attr("class", "treenode")
.attr("fill", function (d) {
return getCellColor(d.depth);
})
.attr("rx", roundX)
.attr("ry", roundY)
.attr("width", local.gridsizex)
.attr("height", local.gridsizey - local.nodeGap)
.on("click", click)
nodeEnter.append("svg:text")
.attr("dx", local.textoffsetx)
.attr("dy", local.textoffsety)
.text(function (d) {
return d.label;
})
// Transition nodes to their new position.
nodeEnter.transition()
.duration(local.duration)
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
.style("opacity", 1);
node.transition()
.duration(local.duration)
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
.style("opacity", 1)
.select("rect")
.style("fill", function (d) {
return getCellColor(d)
});
// Transition exiting nodes to the parent's new position.
node.exit().transition()
.duration(local.duration)
.attr("transform", function (d) {
return "translate(" + source.x + "," + source.y + ")";
})
.style("opacity", 1e-6)
.remove();
}
function updateCanvasHeight() {
dynamicheight = local.nodes.length * local.gridsizey + 100; // 100 is padding
if (dynamicheight > local.height) {
d3.select("#chart-container svg").attr("height", dynamicheight + local.gridsizey / 2)
}
}
function getCellColor(treedepth) {
// mod function rolls over colorisations if more than depth of 6 in the tree
// (i.e. the 7th takes on the color of the first).
var index = treedepth % (colors.length);
return colors[index];
}
// returns an in order array of column headings.
function getColHeader(nodes) {
// iterate over nodes and get the type for each depth, and return it in a list.
// Expects depth order.
var headermap = {}
var headerarr = []
// get unique keys
for (var i = 0; i < nodes.length; i++) {
headermap[nodes[i].depth] = nodes[i].type;
}
// assign column titles to an array
for (key in headermap) {
headerarr.push(headermap[key])
}
return headerarr;
}
/**
* adds a duration to a JSON string date and returns a js date object (assuming duration is split by char ':' )
* @param {Object} d
*/
function addDurationAndGetDate(d) {
var splitDuration = d.duration.split(":", 3);
var minutesInMilliseconds = parseInt(splitDuration[2]) * 60 * 1000; // convert to ms
var hoursInMilliseconds = parseInt(splitDuration[1]) * 60 * 60 * 1000; // convert to ms
var daysInMilliseconds = parseInt(splitDuration[0]) * 24 * 60 * 60 * 1000; // convert to ms
// get start date from node and construct date object
// var startDate = new Date($.datepicker.formatDate(d.start,dateFormat));
var startDate = moment(d.start, dateFormat).toDate();
// construct end date by adding time in ms for start date to ms from duration of activity.
var endDate = new Date(startDate.getTime() + daysInMilliseconds + hoursInMilliseconds + minutesInMilliseconds);
return endDate;
}
// returns a date object from a JSON string date
function getDate(d) {
// var date = $.datepicker.formatDate(d.start,dateFormat)
var date = moment(d.start, dateFormat).toDate();
//console.log(d.start + " " +moment(d.start, dateFormat) + " " + date)
return date;
// return new Date(date);
}
}
$(document).ready(function () {
jsonData = {
"children": [{
"children": [{
"children": [{
"duration": "0:0:0",
"label": "1",
"start": "2002-01-01",
"type": "Subtask Lvl.3"
}, {
"duration": "0:0:0",
"label": "2",
"start": "2002-01-01",
"type": "Subtask Lvl.3"
}, {
"duration": "0:0:0",
"label": "3",
"start": "2002-01-01",
"type": "Subtask Lvl.3"
}],
"label": "Subtask01",
"type": "Subtask"
}, {
"children": [{
"duration": "32:0:34",
"label": "4",
"start": "2002-01-24",
"type": "Subtask Lvl.3"
}, {
"duration": "16:2:38",
"label": "5",
"start": "2002-03-09",
"type": "Subtask Lvl.3"
}, {
"duration": "0:0:1",
"label": "5",
"start": "2002-04-01",
"type": "Subtask Lvl.3"
}, {
"duration": "14:9:52",
"label": "5",
"start": "2002-04-14",
"type": "Subtask Lvl.3"
}, {
"duration": "20:17:2",
"label": "6",
"start": "2002-05-05",
"type": "Subtask Lvl.3"
}],
"label": "Subtask02",
"type": "Subtask"
}, {
"children": [{
"duration": "35:1:44",
"label": "7",
"start": "2002-10-13",
"type": "Subtask Lvl.3"
}, {
"duration": "17:16:15",
"label": "8",
"start": "2002-11-30",
"type": "Subtask Lvl.3"
}, {
"duration": "4:8:29",
"label": "8",
"start": "2002-12-26",
"type": "Subtask Lvl.3"
}, {
"duration": "11:13:33",
"label": "8",
"start": "2003-01-01",
"type": "Subtask Lvl.3"
}, {
"duration": "32:7:19",
"label": "9",
"start": "2003-01-17",
"type": "Subtask Lvl.3"
}],
"label": "Subtask03",
"type": "Subtask"
}, {
"children": [{
"duration": "30:7:4",
"label": "10",
"start": "2003-08-20",
"type": "Subtask Lvl.3"
}, {
"duration": "12:12:12",
"label": "10",
"start": "2003-10-01",
"type": "Subtask Lvl.3"
}, {
"duration": "42:14:38",
"label": "11",
"start": "2003-10-18",
"type": "Subtask Lvl.3"
}, {
"duration": "6:13:26",
"label": "12",
"start": "2003-12-15",
"type": "Subtask Lvl.3"
}, {
"duration": "4:8:29",
"label": "12",
"start": "2003-12-26",
"type": "Subtask Lvl.3"
}, {
"duration": "18:11:45",
"label": "12",
"start": "2004-01-01",
"type": "Subtask Lvl.3"
}, {
"duration": "4:7:42",
"label": "12",
"start": "2004-01-26",
"type": "Subtask Lvl.3"
}],
"label": "Subtask04",
"type": "Subtask"
}, {
"children": [{
"duration": "31:22:0",
"label": "13",
"start": "2004-08-18",
"type": "Subtask Lvl.3"
}, {
"duration": "19:6:57",
"label": "13",
"start": "2004-10-01",
"type": "Subtask Lvl.3"
}, {
"duration": "42:9:18",
"label": "14",
"start": "2004-10-27",
"type": "Subtask Lvl.3"
}, {
"duration": "4:8:29",
"label": "14",
"start": "2004-12-26",
"type": "Subtask Lvl.3"
}, {
"duration": "2:1:47",
"label": "14",
"start": "2005-01-01",
"type": "Subtask Lvl.3"
}, {
"duration": "48:10:31",
"label": "15",
"start": "2005-01-03",
"type": "Subtask Lvl.3"
}],
"label": "Subtask05",
"type": "Subtask"
}, {
"children": [{
"duration": "57:2:6",
"label": "16",
"start": "2005-08-17",
"type": "Subtask Lvl.3"
}, {
"duration": "56:13:27",
"label": "17",
"start": "2005-10-18",
"type": "Subtask Lvl.3"
}, {
"duration": "6:12:4",
"label": "18",
"start": "2005-12-17",
"type": "Subtask Lvl.3"
}, {
"duration": "5:13:44",
"label": "18",
"start": "2005-12-26",
"type": "Subtask Lvl.3"
}, {
"duration": "43:20:7",
"label": "18",
"start": "2006-01-01",
"type": "Subtask Lvl.3"
}],
"label": "Subtask06",
"type": "Subtask"
}, {
"children": [{
"duration": "56:4:17",
"label": "19",
"start": "2006-08-19",
"type": "Subtask Lvl.3"
}, {
"duration": "55:13:19",
"label": "20",
"start": "2006-10-18",
"type": "Subtask Lvl.3"
}, {
"duration": "7:2:8",
"label": "21",
"start": "2006-12-17",
"type": "Subtask Lvl.3"
}, {
"duration": "5:13:44",
"label": "21",
"start": "2006-12-26",
"type": "Subtask Lvl.3"
}, {
"duration": "42:4:38",
"label": "21",
"start": "2007-01-01",
"type": "Subtask Lvl.3"
}],
"label": "Subtask07",
"type": "Subtask"
}, {
"children": [{
"duration": "55:6:30",
"label": "22",
"start": "2007-08-15",
"type": "Subtask Lvl.3"
}, {
"duration": "54:13:9",
"label": "23",
"start": "2007-10-14",
"type": "Subtask Lvl.3"
}, {
"duration": "12:5:14",
"label": "24",
"start": "2007-12-11",
"type": "Subtask Lvl.3"
}, {
"duration": "5:13:44",
"label": "24",
"start": "2007-12-26",
"type": "Subtask Lvl.3"
}, {
"duration": "36:0:10",
"label": "24",
"start": "2008-01-01",
"type": "Subtask Lvl.3"
}],
"label": "Subtask08",
"type": "Subtask"
}],
"label": "Subtask 2",
"type": "Subtask"
}, {
"children": [{
"children": [{
"duration": "0:0:0",
"label": "25",
"start": "2002-01-02",
"type": "Subtask Lvl.3"
}, {
"duration": "0:0:0",
"label": "25",
"start": "2002-01-02",
"type": "Subtask Lvl.3"
}, {
"duration": "15:10:53",
"label": "26",
"start": "2002-01-02",
"type": "Subtask Lvl.3"
}],
"label": "Subtask201",
"type": "Subtask"
}, {
"children": [{
"duration": "17:5:31",
"label": "27",
"start": "2002-06-05",
"type": "Subtask Lvl.3"
}, {
"duration": "11:14:38",
"label": "27",
"start": "2002-07-01",
"type": "Subtask Lvl.3"
}, {
"duration": "2:10:16",
"label": "27",
"start": "2002-07-18",
"type": "Subtask Lvl.3"
}, {
"duration": "26:3:58",
"label": "28",
"start": "2002-07-21",
"type": "Subtask Lvl.3"
}, {
"duration": "25:19:52",
"label": "29",
"start": "2002-08-26",
"type": "Subtask Lvl.3"
}, {
"duration": "8:4:52",
"label": "29",
"start": "2002-10-01",
"type": "Subtask Lvl.3"
}],
"label": "Subtask202",
"type": "Subtask"
}, {
"children": [{
"duration": "19:1:59",
"label": "30",
"start": "2003-03-04",
"type": "Subtask Lvl.3"
}, {
"duration": "16:12:32",
"label": "30",
"start": "2003-04-13",
"type": "Subtask Lvl.3"
}, {
"duration": "33:20:21",
"label": "31",
"start": "2003-05-07",
"type": "Subtask Lvl.3"
}, {
"duration": "3:15:51",
"label": "32",
"start": "2003-06-25",
"type": "Subtask Lvl.3"
}, {
"duration": "11:14:38",
"label": "32",
"start": "2003-07-01",
"type": "Subtask Lvl.3"
}, {
"duration": "23:9:38",
"label": "32",
"start": "2003-07-18",
"type": "Subtask Lvl.3"
}],
"label": "Subtask203",
"type": "Subtask"
}, {
"children": [{
"duration": "41:11:59",
"label": "33",
"start": "2004-02-02",
"type": "Subtask Lvl.3"
}, {
"duration": "0:21:27",
"label": "33",
"start": "2004-04-13",
"type": "Subtask Lvl.3"
}, {
"duration": "38:3:56",
"label": "34",
"start": "2004-04-14",
"type": "Subtask Lvl.3"
}, {
"duration": "14:23:21",
"label": "35",
"start": "2004-06-09",
"type": "Subtask Lvl.3"
}, {
"duration": "13:1:28",
"label": "35",
"start": "2004-07-01",
"type": "Subtask Lvl.3"
}, {
"duration": "16:22:43",
"label": "35",
"start": "2004-07-20",
"type": "Subtask Lvl.3"
}, {
"duration": "3:9:9",
"label": "35",
"start": "2004-08-12",
"type": "Subtask Lvl.3"
}],
"label": "Subtask204",
"type": "Subtask"
}, {
"children": [{
"duration": "31:5:2",
"label": "36",
"start": "2005-02-25",
"type": "Subtask Lvl.3"
}, {
"duration": "17:21:34",
"label": "36",
"start": "2005-04-13",
"type": "Subtask Lvl.3"
}, {
"duration": "45:1:19",
"label": "37",
"start": "2005-05-02",
"type": "Subtask Lvl.3"
}, {
"duration": "26:4:43",
"label": "38",
"start": "2005-06-19",
"type": "Subtask Lvl.3"
}, {
"duration": "26:13:51",
"label": "38",
"start": "2005-07-19",
"type": "Subtask Lvl.3"
}],
"label": "Subtask205",
"type": "Subtask"
}, {
"children": [{
"duration": "37:21:14",
"label": "39",
"start": "2006-02-18",
"type": "Subtask Lvl.3"
}, {
"duration": "15:0:33",
"label": "39",
"start": "2006-04-13",
"type": "Subtask Lvl.3"
}, {
"duration": "48:0:41",
"label": "40",
"start": "2006-04-29",
"type": "Subtask Lvl.3"
}, {
"duration": "25:4:6",
"label": "41",
"start": "2006-06-19",
"type": "Subtask Lvl.3"
}, {
"duration": "28:20:2",
"label": "41",
"start": "2006-07-18",
"type": "Subtask Lvl.3"
}],
"label": "Subtask206",
"type": "Subtask"
}, {
"children": [{
"duration": "41:9:17",
"label": "42",
"start": "2007-02-16",
"type": "Subtask Lvl.3"
}, {
"duration": "10:15:54",
"label": "42",
"start": "2007-04-15",
"type": "Subtask Lvl.3"
}, {
"duration": "48:0:41",
"label": "43",
"start": "2007-04-26",
"type": "Subtask Lvl.3"
}, {
"duration": "27:16:10",
"label": "44",
"start": "2007-06-17",
"type": "Subtask Lvl.3"
}, {
"duration": "25:14:53",
"label": "44",
"start": "2007-07-18",
"type": "Subtask Lvl.3"
}],
"label": "Subtask207",
"type": "Subtask"
}, {
"children": [{
"duration": "46:15:54",
"label": "45",
"start": "2008-02-09",
"type": "Subtask Lvl.3"
}, {
"duration": "4:12:43",
"label": "45",
"start": "2008-04-13",
"type": "Subtask Lvl.3"
}, {
"duration": "48:0:41",
"label": "46",
"start": "2008-04-17",
"type": "Subtask Lvl.3"
}, {
"duration": "35:16:24",
"label": "47",
"start": "2008-06-08",
"type": "Subtask Lvl.3"
}, {
"duration": "16:21:37",
"label": "47",
"start": "2008-07-18",
"type": "Subtask Lvl.3"
}],
"label": "Subtask208",
"type": "Subtask Lvl.2"
}],
"label": "Subtask 1",
"type": "Subtask Lvl.1"
}],
"label": "P1",
"type": "Proj"
};
new GanttChart(jsonData, "YYYY-MM-DD");
});
</script>
<div id="chart-mini"></div>
</p>
</section>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment