Add gantt chart for monitoring. Fix multiple bugs.

Update role assignment API.
Fix flavor.
Fix partition data type.
Fix wizard prev/next button bug.

Change-Id: I4e93f2f16b5f9612ad2ba1c3371fd928e7102f7f
This commit is contained in:
jiahuay 2014-09-02 15:33:41 -07:00
parent 3fb5c3204b
commit 459269cec8
13 changed files with 506 additions and 72 deletions

View File

@ -71,3 +71,42 @@ treechart rect[data-state='warning'] {
treechart rect[data-state='error'] {
fill: red;
}
/* gantt chart */
ganttchart .chart {
font-family: Arial, sans-serif;
font-size: 12px;
margin-top: 30px;
}
ganttchart text {
font-family: "Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,Verdana,sans-serif !important;
}
ganttchart .axis path, ganttchart .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
ganttchart .bar {
fill: #33b5e5;
}
ganttchart .bar-failed {
fill: #CC0000;
}
ganttchart .bar-running {
fill: #669900;
}
ganttchart .bar-succeeded {
fill: #33b5e5;
}
ganttchart .bar-killed {
fill: #ffbb33;
}

View File

@ -202,7 +202,7 @@ select {
.border-radius-4 {
border-radius: 4px !important;
}
textarea, input[type="text"], input[type="password"] {
textarea, input[type="text"], input[type="password"], input[type="number"] {
-webkit-appearance: none;
/* Remove inner shadow from inputs on mobile iOS */
/*background: inherit;*/
@ -519,6 +519,10 @@ td.center-align {
padding-right: 20px !important;
/*padding-top:7px !important;*/
}
.side-margin-3 {
margin-left: 3px !important;
margin-right: 3px !important;
}
.top-margin-14 {
margin-top: 14px;
}

View File

@ -54,6 +54,7 @@
<script type="text/javascript" src="src/app/server/server.js"></script>
<script type="text/javascript" src="src/common/charts.js"></script>
<script type="text/javascript" src="src/common/findservers/findservers.js"></script>
<script type="text/javascript" src="src/common/gantt-chart-d3v2.js"></script>
</head>
<body ng-app="compass" ng-controller="appController">

View File

@ -23,25 +23,47 @@ compassAppDev.run(function($httpBackend, settings, $http) {
"display": "OpenStack",
"os_installer": "cobbler",
"package_installer": "chef",
"roles": [{
"display_name": "Compute",
"name": "os-compute-worker"
}, {
"display_name": "Controller",
"name": "os-controller"
}, {
"display_name": "Network",
"name": "os-network"
}, {
"display_name": "Storage",
"name": "os-block-storage-worker"
}],
"supported_oses": [{
"name": "CentOs",
"id": 1
}, {
"name": "Ubuntu",
"id": 2
}],
"flavors": [{
"display_name": "allinone",
"id": 1,
"name": "allinone",
"roles": [{
"display_name": "Compute",
"name": "os-compute-worker"
}, {
"display_name": "Controller",
"name": "os-controller"
}, {
"display_name": "Network",
"name": "os-network"
}, {
"display_name": "Storage",
"name": "os-block-storage-worker"
}]
}, {
"display_name": "multiroles",
"id": 2,
"name": "multiroles",
"roles": [{
"display_name": "Compute",
"name": "os-compute-worker"
}, {
"display_name": "Controller",
"name": "os-controller"
}, {
"display_name": "Network",
"name": "os-network"
}, {
"display_name": "Storage",
"name": "os-block-storage-worker"
}]
}]
}, {
"id": 2,
@ -434,7 +456,7 @@ compassAppDev.run(function($httpBackend, settings, $http) {
"create_by": "user@someemail.com",
"create_at": "2014-3-25 12:00:00",
"updated_at": "2014-3-26 13:00:00",
" links": [{
"links": [{
"href": "/clusters/1",
"rel": "self"
}, {
@ -452,7 +474,7 @@ compassAppDev.run(function($httpBackend, settings, $http) {
"create_by": "user@someemail.com",
"create_at": "2014-3-25 12:00:00",
"updated_at": "2014-3-28 14:00:00",
" links": [{
"links": [{
"href": "/clusters/1",
"rel": "self"
}, {
@ -470,7 +492,7 @@ compassAppDev.run(function($httpBackend, settings, $http) {
"create_by": "user@someemail.com",
"create_at": "2014-3-25 12:00:00",
"updated_at": "2014-5-26 09:00:00",
" links": [{
"links": [{
"href": "/clusters/1",
"rel": "self"
}, {
@ -488,7 +510,7 @@ compassAppDev.run(function($httpBackend, settings, $http) {
"create_by": "user@someemail.com",
"create_at": "2014-3-25 12:00:00",
"updated_at": "2014-3-19 08:00:00",
" links": [{
"links": [{
"href": "/clusters/1",
"rel": "self"
}, {
@ -506,7 +528,7 @@ compassAppDev.run(function($httpBackend, settings, $http) {
"create_by": "user@someemail.com",
"create_at": "2014-4-25 12:00:00",
"updated_at": "2014-2-27 20:00:00",
" links": [{
"links": [{
"href": "/clusters/2",
"rel": "self"
}, {
@ -530,7 +552,19 @@ compassAppDev.run(function($httpBackend, settings, $http) {
"create_by": "user@someemail.com",
"create_at": "2014-3-25 12:00:00",
"updated_at": "2014-3-26 13:00:00",
" links": [{
"flavor": {
"roles": [{
"display_name": "Compute",
"name": "os-compute-worker"
}, {
"display_name": "Controller",
"name": "os-controller"
}, {
"display_name": "Network",
"name": "os-network"
}]
},
"links": [{
"href": "/clusters/" + id,
"rel": "self"
}, {
@ -837,6 +871,11 @@ compassAppDev.run(function($httpBackend, settings, $http) {
return [200, hosts, {}];
});
$httpBackend.whenPUT(/\.*\/clusters\/([0-9]|[1-9][0-9])\/hosts\/([0-9]|[1-9][0-9])$/).respond(function(method, url, data) {
var updateData = JSON.parse(data);
return [200, updateData, {}];
})
$httpBackend.whenPUT(/\.*\/clusters\/[1-9][0-9]*\/hosts\/[1-9][0-9]*\/config/).respond(function(method, url, data) {
console.log(method, url, data);
var config = JSON.parse(data);

View File

@ -5,26 +5,32 @@
</div>
<div class="col-lg-10" ng-controller="alertsCtrl" style="width: 100%;">
<tabset class="ng-isolate-scope" style="width: 100%;">
<tab heading="Alerts"> Alerts
<form class="form-inline ng-valid">
<select class="form-control ng-pristine ng-valid" ng-model="renderer" ng-change="rendererChanged(renderer)" >
<option ng-repeat="render in renderers" value="{{render}}"> {{render}} </option>
</select>
<select class="form-control ng-valid" ng-model="uri" ng-change="uriChanged(uri)">
<option ng-repeat="uri in uris" value="{{uri.v}}"> {{uri.displayName}} </option>
</select>
<button class="btn btn-primary" ng-click="changeSeriesData(0)">Generate Alarm Report</button>
</form>
<div class="panel-body" id="alarmsGraphContainer"> </div>
</tab>
<tab heading="Alarms">
<tab-heading>
<i class="glyphicon glyphicon-bell"></i> Alarms!
</tab-heading>
Alarms - TBD
</tab>
<tab heading="Event Viewer"> Event Viewer - TBD
</tab>
</tabset>
<tabset style="width: 100%;">
<tab heading="Alerts">
<form class="form-inline">
<select class="form-control" ng-model="renderer" ng-change="rendererChanged(renderer)">
<option ng-repeat="render in renderers" value="{{render}}">{{render}}</option>
</select>
<select class="form-control" ng-model="uri" ng-change="uriChanged(uri)">
<option ng-repeat="uri in uris" value="{{uri.v}}">{{uri.displayName}}</option>
</select>
<button class="btn btn-primary" ng-click="changeSeriesData(0)">Generate Alarm Report</button>
</form>
<div class="panel-body" id="alarmsGraphContainer">
<ganttchart data="alerts" hosts="hosts"></ganttchart>
</div>
</tab>
<tab>
<tab-heading>
<i class="ace-icon fa fa-bell"></i>Alarms
</tab-heading>
Alarms - TBD
</tab>
<tab>
<tab-heading>
Event Viewer
</tab-heading>
Event Viewer - TBD
</tab>
</tabset>
</div>

View File

@ -40,6 +40,36 @@ angular.module('compass.monitoring', [
{"start":1406774590378,"end":1406850781190}
]}
]};
$scope.alerts = [
{"startDate":new Date("Sun Dec 09 01:36:45 EST 2012"),"endDate":new Date("Sun Dec 09 02:36:45 EST 2012"),"name":"host-01","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 04:56:32 EST 2012"),"endDate":new Date("Sun Dec 09 06:35:47 EST 2012"),"name":"host-05","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 06:29:53 EST 2012"),"endDate":new Date("Sun Dec 09 06:34:04 EST 2012"),"name":"host-02","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 05:35:21 EST 2012"),"endDate":new Date("Sun Dec 09 06:21:22 EST 2012"),"name":"host-01","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 05:00:06 EST 2012"),"endDate":new Date("Sun Dec 09 05:05:07 EST 2012"),"name":"host-03","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 03:46:59 EST 2012"),"endDate":new Date("Sun Dec 09 04:54:19 EST 2012"),"name":"host-01","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 04:02:45 EST 2012"),"endDate":new Date("Sun Dec 09 04:48:56 EST 2012"),"name":"host-02","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 03:27:35 EST 2012"),"endDate":new Date("Sun Dec 09 03:58:43 EST 2012"),"name":"host-03","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 01:40:11 EST 2012"),"endDate":new Date("Sun Dec 09 03:26:35 EST 2012"),"name":"host-04","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 03:00:03 EST 2012"),"endDate":new Date("Sun Dec 09 03:09:51 EST 2012"),"name":"host-05","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 01:21:00 EST 2012"),"endDate":new Date("Sun Dec 09 02:51:42 EST 2012"),"name":"host-01","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 01:08:42 EST 2012"),"endDate":new Date("Sun Dec 09 01:33:42 EST 2012"),"name":"host-04","status":"CRITICAL"},
{"startDate":new Date("Sun Dec 09 00:27:15 EST 2012"),"endDate":new Date("Sun Dec 09 00:54:56 EST 2012"),"name":"host-04","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 00:29:48 EST 2012"),"endDate":new Date("Sun Dec 09 00:44:50 EST 2012"),"name":"host-01","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 07:39:21 EST 2012"),"endDate":new Date("Sun Dec 09 07:43:22 EST 2012"),"name":"host-03","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 07:00:06 EST 2012"),"endDate":new Date("Sun Dec 09 07:05:07 EST 2012"),"name":"host-02","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 08:46:59 EST 2012"),"endDate":new Date("Sun Dec 09 09:54:19 EST 2012"),"name":"host-02","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 09:02:45 EST 2012"),"endDate":new Date("Sun Dec 09 09:48:56 EST 2012"),"name":"host-01","status":"WARNING"},
{"startDate":new Date("Sun Dec 09 08:27:35 EST 2012"),"endDate":new Date("Sun Dec 09 08:58:43 EST 2012"),"name":"host-05","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 08:40:11 EST 2012"),"endDate":new Date("Sun Dec 09 08:46:35 EST 2012"),"name":"host-03","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 08:00:03 EST 2012"),"endDate":new Date("Sun Dec 09 08:09:51 EST 2012"),"name":"host-02","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 10:21:00 EST 2012"),"endDate":new Date("Sun Dec 09 10:51:42 EST 2012"),"name":"host-04","status":"SUCCEEDED"},
{"startDate":new Date("Sun Dec 09 11:08:42 EST 2012"),"endDate":new Date("Sun Dec 09 11:33:42 EST 2012"),"name":"host-04","status":"CRITICAL"},
{"startDate":new Date("Sun Dec 09 12:27:15 EST 2012"),"endDate":new Date("Sun Dec 09 12:54:56 EST 2012"),"name":"host-02","status":"SUCCEEDED"},
{"startDate":new Date("Sat Dec 08 23:12:24 EST 2012"),"endDate":new Date("Sun Dec 09 00:26:13 EST 2012"),"name":"host-01","status":"UNKNOWN"}];
$scope.hosts = ["host-01", "host-02", "host-03", "host-04", "host-05"];
}
])

View File

@ -5,14 +5,15 @@
</div>
<div class="col-lg-10" ng-controller="moniOverviewCtrl" style="width: 100%;">
<tabset class="ng-isolate-scope" style="width: 100%;">
<tab heading="Physical View" > Layer 2/3 Topology
<treechart id="physicalTopo" data="physicalTopoData" count="serverCount" style="width: 100%; height: 1280px; transition: 0.4s all ease-out;"> </treechart>
</tab>
<tab heading="Service View" > Service Diagram
<circlepacking id="logicalTopo" data="logicalTopoData" style="display: block; margin-left: auto; margin-right: auto; width: 100%; height: 1280px; transition: 0.4s all ease-out;"></circlepacking>
</tab>
</tabset>
<tabset class="ng-isolate-scope" style="width: 100%;">
<tab heading="Physical View">
Layer 2/3 Topology
<treechart id="physicalTopo" data="physicalTopoData" count="serverCount" style="width: 100%; height: 1280px; transition: 0.4s all ease-out;"></treechart>
</tab>
<tab heading="Service View">
Service Diagram
<circlepacking id="logicalTopo" data="logicalTopoData" style="display: block; margin-left: auto; margin-right: auto; width: 100%; height: 1280px; transition: 0.4s all ease-out;"></circlepacking>
</tab>
</tabset>
</div>

View File

@ -173,6 +173,10 @@ angular.module('compass.services', [])
return $http.get(settings.apiUrlBase + '/clusters/' + clusterId + '/hosts');
};
this.updateClusterHost = function(clusterId, hostId, data) {
return $http.put(settings.apiUrlBase + '/clusters/' + clusterId + '/hosts/' + hostId, angular.toJson(data));
};
this.updateClusterHostConfig = function(clusterId, hostId, config) {
return $http.put(settings.apiUrlBase + '/clusters/' + clusterId + '/hosts/' + hostId + '/config', angular.toJson(config));
};
@ -217,7 +221,7 @@ angular.module('compass.services', [])
wizard.commit = {};
wizard.servers = [];
wizard.allServers = [];
wizard.adapter = {}; //
//wizard.adapter = {}; //
wizard.generalConfig = {};
wizard.subnetworks = [];
wizard.routingtable = [];
@ -282,7 +286,7 @@ angular.module('compass.services', [])
wizard.getServers = function() {
return angular.copy(wizard.servers);
};
/*
wizard.setAdapter = function(adapter) { ////
wizard.adapter = adapter;
};
@ -290,7 +294,7 @@ angular.module('compass.services', [])
wizard.getAdapter = function() { /////
return angular.copy(wizard.adapter);
};
*/
wizard.setGeneralConfig = function(config) {
wizard.generalConfig = config;
};

View File

@ -16,15 +16,15 @@
<input ng-model="newPartition.mount_point" type="text" placeholder="Mount Point" class="input-medium">
</td>
<td>
<input ng-model="newPartition.percentage" type="text" placeholder="Size Percentage" class="input-medium">
<input ng-model="newPartition.percentage" type="number" min="1" max="100" placeholder="Size Percentage" class="input-medium">
</td>
<td>
<input ng-model="newPartition.max_size" type="text" placeholder="Max Size" class="input-medium">
<input ng-model="newPartition.max_size" type="number" min="1" max="100" placeholder="Max Size" class="input-medium">
</td>
<td>
<span ng-click="addPartition()" class="action" style="padding-left: 31px;" >
<i class="fa fa-plus-circle bigger-140 blue"S></i>
</span>
<span ng-click="addPartition()" class="action" style="padding-left: 31px;" >
<i class="fa fa-plus-circle bigger-140 blue"S></i>
</span>
</td>
</tr>
<tr ng-repeat="(key, data) in partition">
@ -33,11 +33,11 @@
</td>
<td>
<span ng-hide="editPartitionMode">{{data.percentage}}</span>
<input ng-show="editPartitionMode" ng-model="data.percentage" type="text" placeholder="Size Percentage" class="input-medium">
<input ng-show="editPartitionMode" ng-model="data.percentage" type="number" min="1" max="100" placeholder="Size Percentage" class="input-medium">
</td>
<td>
<span ng-hide="editPartitionMode">{{data.max_size}}</span>
<input ng-show="editPartitionMode" ng-model="data.max_size" type="text" placeholder="Max Size" class="input-medium">
<input ng-show="editPartitionMode" ng-model="data.max_size" type="number" min="1" max="100" placeholder="Max Size" class="input-medium">
</td>
<td>
<!--edit button-->

View File

@ -53,7 +53,7 @@
</div>
</div>
<div class="pull-right">
<span>
<span class="opacity-zero">
<input type="text" placeholder="HA VIP" ng-model="ha_vip">
</span>
<button class="btn btn-sm btn-info" ng-init="autoAssignRoles.isCollapsed = true;" ng-click="autoAssignRoles.isCollapsed = !autoAssignRoles.isCollapsed">

View File

@ -79,7 +79,6 @@ angular.module('compass.wizard', [
return wizardFactory.getCommitState()
}, function(newCommitState, oldCommitState) {
if (newCommitState != oldCommitState && newCommitState.name == $scope.steps[$scope.currentStep - 1].name) {
if (newCommitState.state == "success") {
console.warn("### catch success in wizardCtrl ###", newCommitState, oldCommitState);
if (newCommitState.name == "review") {
@ -191,15 +190,14 @@ angular.module('compass.wizard', [
};
$scope.stepForward = function() {
$scope.triggerCommit($scope.currentStep);
$scope.pendingStep = $scope.currentStep + 1;;
$scope.triggerCommit($scope.currentStep);
};
// go to previous step
$scope.stepBackward = function() {
$scope.triggerCommit($scope.currentStep);
$scope.pendingStep = $scope.currentStep - 1;
$scope.triggerCommit($scope.currentStep);
};
// go to step by stepId
@ -1106,12 +1104,10 @@ angular.module('compass.wizard', [
angular.forEach(server.roles, function(role) {
roles.push(role.name);
});
var config = {
"package_config": {
"roles": roles
}
var data = {
"roles": roles
};
var updateRoles = dataService.updateClusterHostConfig(cluster.id, server.id, config).then(function(configData) {
var updateRoles = dataService.updateClusterHost(cluster.id, server.id, data).then(function(configData) {
// success callback
}, function(response) {
// error callback

View File

@ -471,4 +471,97 @@ angular.module('compass.charts', [])
}
}
});
})
app.directive('ganttchart', function() {
return {
restrict: 'E',
scope: {
data: '=',
hosts: '='
},
template: '<div class="pull-right"><button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain(\'1hr\')">1 HR</button>'
+ '<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain(\'3hr\')">3 HR</button>'
+ '<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain(\'6hr\')">6 HR</button>'
+ '<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain(\'1day\')">1 DAY</button>'
+ '<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain(\'1week\')">1 WEEK</button>'
+ '</div>'
+ '<div class="clear-fix"></div>',
link: function(scope, element, attrs) {
var tasks = scope.data;
var hostnames = scope.hosts;
var taskStatus = {
"SUCCEEDED": "bar",
"CRITICAL": "bar-failed",
"WARNING": "bar-running",
"UNKNOWN": "bar-killed"
};
tasks.sort(function(a, b) {
return a.endDate - b.endDate;
});
var maxDate = tasks[tasks.length - 1].endDate;
tasks.sort(function(a, b) {
return a.startDate - b.startDate;
});
var minDate = tasks[0].startDate;
var format = "%H:%M";
var timeDomainString = "1day";
var gantt = d3.gantt().taskTypes(hostnames).taskStatus(taskStatus).tickFormat(format).height(450).width(800);
scope.changeTimeDomain = function(timeDomainString) {
this.timeDomainString = timeDomainString;
switch (timeDomainString) {
case "1hr":
format = "%H:%M:%S";
gantt.timeDomain([d3.time.hour.offset(getEndDate(), -1), getEndDate()]);
break;
case "3hr":
format = "%H:%M";
gantt.timeDomain([d3.time.hour.offset(getEndDate(), -3), getEndDate()]);
break;
case "6hr":
format = "%H:%M";
gantt.timeDomain([d3.time.hour.offset(getEndDate(), -6), getEndDate()]);
break;
case "1day":
format = "%H:%M";
gantt.timeDomain([d3.time.day.offset(getEndDate(), -1), getEndDate()]);
break;
case "1week":
format = "%a %H:%M";
gantt.timeDomain([d3.time.day.offset(getEndDate(), -7), getEndDate()]);
break;
default:
format = "%H:%M"
}
gantt.tickFormat(format);
gantt.redraw(tasks);
}
gantt.timeDomainMode("fixed");
scope.changeTimeDomain(timeDomainString);
gantt(tasks);
function getEndDate() {
var lastEndDate = Date.now();
if (tasks.length > 0) {
lastEndDate = tasks[tasks.length - 1].endDate;
}
return lastEndDate;
}
}
}
})

View File

@ -0,0 +1,221 @@
/**
* @author Dimitry Kudrayvtsev
* @version 2.0
*/
d3.gantt = function() {
var FIT_TIME_DOMAIN_MODE = "fit";
var FIXED_TIME_DOMAIN_MODE = "fixed";
var margin = {
top : 20,
right : 40,
bottom : 20,
left : 150
};
var timeDomainStart = d3.time.day.offset(new Date(),-3);
var timeDomainEnd = d3.time.hour.offset(new Date(),+3);
var timeDomainMode = FIT_TIME_DOMAIN_MODE;// fixed or fit
var taskTypes = [];
var taskStatus = [];
var height = document.body.clientHeight - margin.top - margin.bottom-5;
var width = document.body.clientWidth - margin.right - margin.left-5;
var tickFormat = "%H:%M";
var keyFunction = function(d) {
return d.startDate + d.name + d.endDate;
};
var rectTransform = function(d) {
return "translate(" + x(d.startDate) + "," + y(d.name) + ")";
};
var x = d3.time.scale().domain([ timeDomainStart, timeDomainEnd ]).range([ 0, width ]).clamp(true);
var y = d3.scale.ordinal().domain(taskTypes).rangeRoundBands([ 0, height - margin.top - margin.bottom ], .1);
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.time.format(tickFormat)).tickSubdivide(true)
.tickSize(8).tickPadding(8);
var yAxis = d3.svg.axis().scale(y).orient("left").tickSize(0);
var initTimeDomain = function() {
if (timeDomainMode === FIT_TIME_DOMAIN_MODE) {
if (tasks === undefined || tasks.length < 1) {
timeDomainStart = d3.time.day.offset(new Date(), -3);
timeDomainEnd = d3.time.hour.offset(new Date(), +3);
return;
}
tasks.sort(function(a, b) {
return a.endDate - b.endDate;
});
timeDomainEnd = tasks[tasks.length - 1].endDate;
tasks.sort(function(a, b) {
return a.startDate - b.startDate;
});
timeDomainStart = tasks[0].startDate;
}
};
var initAxis = function() {
x = d3.time.scale().domain([ timeDomainStart, timeDomainEnd ]).range([ 0, width ]).clamp(true);
y = d3.scale.ordinal().domain(taskTypes).rangeRoundBands([ 0, height - margin.top - margin.bottom ], .1);
xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.time.format(tickFormat)).tickSubdivide(true)
.tickSize(8).tickPadding(8);
yAxis = d3.svg.axis().scale(y).orient("left").tickSize(0);
};
function gantt(tasks) {
initTimeDomain();
initAxis();
var svg = d3.select("ganttchart")
.append("svg")
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "gantt-chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
svg.selectAll(".chart")
.data(tasks, keyFunction).enter()
.append("rect")
.attr("rx", 5)
.attr("ry", 5)
.attr("class", function(d){
if(taskStatus[d.status] == null){ return "bar";}
return taskStatus[d.status];
})
.attr("y", 0)
.attr("transform", rectTransform)
.attr("height", function(d) { return y.rangeBand(); })
.attr("width", function(d) {
return (x(d.endDate) - x(d.startDate));
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + (height - margin.top - margin.bottom) + ")")
.transition()
.call(xAxis);
svg.append("g").attr("class", "y axis").transition().call(yAxis);
return gantt;
};
gantt.redraw = function(tasks) {
initTimeDomain();
initAxis();
var svg = d3.select("svg");
var ganttChartGroup = svg.select(".gantt-chart");
var rect = ganttChartGroup.selectAll("rect").data(tasks, keyFunction);
rect.enter()
.insert("rect",":first-child")
.attr("rx", 5)
.attr("ry", 5)
.attr("class", function(d){
if(taskStatus[d.status] == null){ return "bar";}
return taskStatus[d.status];
})
.transition()
.attr("y", 0)
.attr("transform", rectTransform)
.attr("height", function(d) { return y.rangeBand(); })
.attr("width", function(d) {
return (x(d.endDate) - x(d.startDate));
});
rect.transition()
.attr("transform", rectTransform)
.attr("height", function(d) { return y.rangeBand(); })
.attr("width", function(d) {
return (x(d.endDate) - x(d.startDate));
});
rect.exit().remove();
svg.select(".x").transition().call(xAxis);
svg.select(".y").transition().call(yAxis);
return gantt;
};
gantt.margin = function(value) {
if (!arguments.length)
return margin;
margin = value;
return gantt;
};
gantt.timeDomain = function(value) {
if (!arguments.length)
return [ timeDomainStart, timeDomainEnd ];
timeDomainStart = +value[0], timeDomainEnd = +value[1];
return gantt;
};
/**
* @param {string}
* vale The value can be "fit" - the domain fits the data or
* "fixed" - fixed domain.
*/
gantt.timeDomainMode = function(value) {
if (!arguments.length)
return timeDomainMode;
timeDomainMode = value;
return gantt;
};
gantt.taskTypes = function(value) {
if (!arguments.length)
return taskTypes;
taskTypes = value;
return gantt;
};
gantt.taskStatus = function(value) {
if (!arguments.length)
return taskStatus;
taskStatus = value;
return gantt;
};
gantt.width = function(value) {
if (!arguments.length)
return width;
width = +value;
return gantt;
};
gantt.height = function(value) {
if (!arguments.length)
return height;
height = +value;
return gantt;
};
gantt.tickFormat = function(value) {
if (!arguments.length)
return tickFormat;
tickFormat = value;
return gantt;
};
return gantt;
};