mirror of
https://github.com/ansible/awx.git
synced 2026-03-23 03:45:01 -02:30
First attempt at adding a graph to job detail page.
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
"jquery": true,
|
"jquery": true,
|
||||||
"esnext": true,
|
"esnext": true,
|
||||||
"globalstrict": true,
|
"globalstrict": true,
|
||||||
"globals": { "angular":false, "alert":false, "$AnsibleConfig":true, "$basePath":true, "jsyaml":false, "_":true },
|
"globals": { "angular":false, "alert":false, "$AnsibleConfig":true, "$basePath":true, "jsyaml":false, "_":false, "d3":false, "Donut3D":false },
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"quotmark": false,
|
"quotmark": false,
|
||||||
"smarttabs": true,
|
"smarttabs": true,
|
||||||
|
|||||||
@@ -269,7 +269,14 @@ function JobDetailController ($scope, $compile, $routeParams, ClearScope, Breadc
|
|||||||
'box-shadow': 'none',
|
'box-shadow': 'none',
|
||||||
'height': 'auto'
|
'height': 'auto'
|
||||||
});
|
});
|
||||||
$('#job-summary-container').css({ "width": "41.66666667%", "padding-right": "15px", "z-index": 0 }).show();
|
$('#job-summary-container').css({
|
||||||
|
"width": "41.66666667%",
|
||||||
|
"padding-left": "7px",
|
||||||
|
"padding-right": "15px",
|
||||||
|
"z-index": 0
|
||||||
|
});
|
||||||
|
setTimeout(function() { $('#job-summary-container .job_well').height($('#job-detail-container').height() - 18); }, 500);
|
||||||
|
$('#job-summary-container').show();
|
||||||
}
|
}
|
||||||
// Detail table height adjusting. First, put page height back to 'normal'.
|
// Detail table height adjusting. First, put page height back to 'normal'.
|
||||||
$('#plays-table-detail').height(150);
|
$('#plays-table-detail').height(150);
|
||||||
|
|||||||
@@ -40,9 +40,9 @@
|
|||||||
angular.module('JobDetailHelper', ['Utilities', 'RestServices'])
|
angular.module('JobDetailHelper', ['Utilities', 'RestServices'])
|
||||||
|
|
||||||
.factory('DigestEvents', ['UpdatePlayStatus', 'UpdateHostStatus', 'UpdatePlayChild', 'AddHostResult', 'SelectPlay', 'SelectTask',
|
.factory('DigestEvents', ['UpdatePlayStatus', 'UpdateHostStatus', 'UpdatePlayChild', 'AddHostResult', 'SelectPlay', 'SelectTask',
|
||||||
'GetHostCount', 'GetElapsed', 'UpdateTaskStatus',
|
'GetHostCount', 'GetElapsed', 'UpdateTaskStatus', 'DrawGraph',
|
||||||
function(UpdatePlayStatus, UpdateHostStatus, UpdatePlayChild, AddHostResult, SelectPlay, SelectTask, GetHostCount, GetElapsed,
|
function(UpdatePlayStatus, UpdateHostStatus, UpdatePlayChild, AddHostResult, SelectPlay, SelectTask, GetHostCount, GetElapsed,
|
||||||
UpdateTaskStatus) {
|
UpdateTaskStatus, DrawGraph) {
|
||||||
return function(params) {
|
return function(params) {
|
||||||
|
|
||||||
var scope = params.scope,
|
var scope = params.scope,
|
||||||
@@ -156,6 +156,7 @@ function(UpdatePlayStatus, UpdateHostStatus, UpdatePlayChild, AddHostResult, Sel
|
|||||||
scope: scope,
|
scope: scope,
|
||||||
id: event.id
|
id: event.id
|
||||||
});
|
});
|
||||||
|
DrawGraph({ scope: scope });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.event === 'runner_on_unreachable') {
|
if (event.event === 'runner_on_unreachable') {
|
||||||
@@ -229,6 +230,7 @@ function(UpdatePlayStatus, UpdateHostStatus, UpdatePlayChild, AddHostResult, Sel
|
|||||||
});
|
});
|
||||||
scope.job_status.status = (event.failed) ? 'failed' : 'successful';
|
scope.job_status.status = (event.failed) ? 'failed' : 'successful';
|
||||||
scope.job_status.status_class = "";
|
scope.job_status.status_class = "";
|
||||||
|
DrawGraph({ scope: scope });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -768,18 +770,66 @@ function(UpdatePlayStatus, UpdateHostStatus, UpdatePlayChild, AddHostResult, Sel
|
|||||||
};
|
};
|
||||||
}])
|
}])
|
||||||
|
|
||||||
.factory('GetHostSummary', [ function() {
|
.factory('DrawGraph', ['Rest', 'GetBasePath', 'ProcessErrors', function(Rest, GetBasePath, ProcessErrors) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
dark = 0, failed = 0, changed = 0, ok = 0,
|
||||||
|
svg_height, svg_width, graph_data, svg, url;
|
||||||
|
|
||||||
}])
|
svg_width = $('#graph-section').width();
|
||||||
|
svg_height = 300;
|
||||||
|
if ($('#graph-section svg').length === 0) {
|
||||||
|
svg = d3.select("#graph-section").append("svg").attr("width", svg_width).attr("height", svg_height);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
svg = d3.select("#graph-section svg");
|
||||||
|
}
|
||||||
|
svg.append("g").attr("id","completedHostsDonutNew");
|
||||||
|
$('#completedHostsDonutNew').hide();
|
||||||
|
|
||||||
.factory('DrawGraph', [ function() {
|
if (scope.removeRenderGraph) {
|
||||||
/*var salesData=[
|
scope.removeRenderGraph();
|
||||||
{label:"OK", color:"#9ED89E"},
|
}
|
||||||
{label:"Changed", color:"#DC3912"},
|
scope.removeRenderGraph = scope.$on('RenderGraph', function() {
|
||||||
{label:"Failed", color:"#DA4D49;"},
|
Donut3D.draw("completedHostsDonutNew", graph_data, Math.floor(svg_width / 2), 150, 130, 100, 15, 0.4);
|
||||||
{label:"Skipped", color:"#D4D4D4"},
|
$('#completedHostsDonut').remove();
|
||||||
{label:"Unreachable", color:""}
|
$('#completedHostsDonutNew').attr('id','completedHostsDonut');
|
||||||
];*/
|
$('#completedHostsDonut').show();
|
||||||
|
});
|
||||||
|
|
||||||
|
url = GetBasePath('jobs') + scope.job_id + '/job_host_summaries/';
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get()
|
||||||
|
.success(function(data) {
|
||||||
|
if (data.count) {
|
||||||
|
data.results.forEach(function(row) {
|
||||||
|
if (row.dark) {
|
||||||
|
dark ++;
|
||||||
|
}
|
||||||
|
else if (row.failures) {
|
||||||
|
failed++;
|
||||||
|
}
|
||||||
|
else if (row.changed) {
|
||||||
|
changed++;
|
||||||
|
}
|
||||||
|
else if (row.ok) {
|
||||||
|
ok++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
graph_data = [
|
||||||
|
{ label: 'OK', value: ok, color: '#9ED89E' },
|
||||||
|
{ label: 'Changed', value: changed, color: '#FFC773' },
|
||||||
|
{ label: 'Failed', value: failed, color: '#DA4D49' },
|
||||||
|
{ label: 'Unreachable', value: dark, color: '#A9A9A9' }
|
||||||
|
];
|
||||||
|
scope.$emit('RenderGraph');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function(data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
}])
|
}])
|
||||||
|
|
||||||
.factory('FilterAllByHostName', ['Rest', 'GetBasePath', 'ProcessErrors', 'SelectPlay', function(Rest, GetBasePath, ProcessErrors, SelectPlay) {
|
.factory('FilterAllByHostName', ['Rest', 'GetBasePath', 'ProcessErrors', 'SelectPlay', function(Rest, GetBasePath, ProcessErrors, SelectPlay) {
|
||||||
@@ -787,7 +837,7 @@ function(UpdatePlayStatus, UpdateHostStatus, UpdatePlayChild, AddHostResult, Sel
|
|||||||
var scope = params.scope,
|
var scope = params.scope,
|
||||||
host = params.host,
|
host = params.host,
|
||||||
job_id = scope.job_id,
|
job_id = scope.job_id,
|
||||||
url = GetBasePath('jobs') + job_id + '/job_events/?event__icontains=runner&host_name__icontains=' + host;
|
url = GetBasePath('jobs') + job_id + '/job_events/?event__icontains=runner&host_name__icontains=' + host + '&parent__isnull=false';
|
||||||
|
|
||||||
scope.search_all_tasks = [];
|
scope.search_all_tasks = [];
|
||||||
scope.search_all_plays = [];
|
scope.search_all_plays = [];
|
||||||
@@ -811,17 +861,22 @@ function(UpdatePlayStatus, UpdateHostStatus, UpdatePlayChild, AddHostResult, Sel
|
|||||||
Rest.setUrl(url);
|
Rest.setUrl(url);
|
||||||
Rest.get()
|
Rest.get()
|
||||||
.success(function(data) {
|
.success(function(data) {
|
||||||
data.results.forEach(function(row) {
|
if (data.count > 0) {
|
||||||
if (row.parent) {
|
data.results.forEach(function(row) {
|
||||||
scope.search_all_plays.push(row.parent);
|
if (row.parent) {
|
||||||
|
scope.search_all_plays.push(row.parent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (scope.search_all_plays.length > 0) {
|
||||||
|
scope.search_all_plays.sort();
|
||||||
|
scope.activePlay = scope.search_all_plays[scope.search_all_plays.length - 1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope.activePlay = null;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
if (scope.search_all_plays.length > 0) {
|
|
||||||
scope.search_all_plays.sort();
|
|
||||||
scope.activePlay = scope.search_all_plays[scope.search_all_plays.length - 1];
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
scope.activePlay = null;
|
scope.search_all_plays.push(0);
|
||||||
}
|
}
|
||||||
scope.$emit('AllPlaysReady');
|
scope.$emit('AllPlaysReady');
|
||||||
})
|
})
|
||||||
@@ -834,13 +889,18 @@ function(UpdatePlayStatus, UpdateHostStatus, UpdatePlayChild, AddHostResult, Sel
|
|||||||
Rest.setUrl(url);
|
Rest.setUrl(url);
|
||||||
Rest.get()
|
Rest.get()
|
||||||
.success(function(data) {
|
.success(function(data) {
|
||||||
data.results.forEach(function(row) {
|
if (data.count > 0) {
|
||||||
if (row.parent) {
|
data.results.forEach(function(row) {
|
||||||
scope.search_all_tasks.push(row.parent);
|
if (row.parent) {
|
||||||
|
scope.search_all_tasks.push(row.parent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (scope.search_all_tasks.length > 0) {
|
||||||
|
scope.search_all_tasks.sort();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
if (scope.search_all_tasks.length > 0) {
|
else {
|
||||||
scope.search_all_tasks.sort();
|
scope.search_all_tasks.push(0);
|
||||||
}
|
}
|
||||||
scope.$emit('AllTasksReady');
|
scope.$emit('AllTasksReady');
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -142,6 +142,9 @@
|
|||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
padding-right: 7px;
|
padding-right: 7px;
|
||||||
width: 58.33333333%;
|
width: 58.33333333%;
|
||||||
|
.well {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#job-summary-container {
|
#job-summary-container {
|
||||||
@@ -303,3 +306,35 @@ label.small-label {
|
|||||||
.mCSB_container {
|
.mCSB_container {
|
||||||
margin-right: 18px;
|
margin-right: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#graph-section {
|
||||||
|
position: relative;
|
||||||
|
.legend {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
i:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path.slice{
|
||||||
|
stroke-width:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
polyline{
|
||||||
|
opacity: .3;
|
||||||
|
stroke: black;
|
||||||
|
stroke-width: 2px;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg text.percent{
|
||||||
|
fill:white;
|
||||||
|
text-anchor:middle;
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
|||||||
123
awx/ui/static/lib/d3Donut/d3Donut.js
vendored
Normal file
123
awx/ui/static/lib/d3Donut/d3Donut.js
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
!function(){
|
||||||
|
var Donut3D={};
|
||||||
|
|
||||||
|
function pieTop(d, rx, ry, ir ){
|
||||||
|
if(d.endAngle - d.startAngle == 0 ) return "M 0 0";
|
||||||
|
var sx = rx*Math.cos(d.startAngle),
|
||||||
|
sy = ry*Math.sin(d.startAngle),
|
||||||
|
ex = rx*Math.cos(d.endAngle),
|
||||||
|
ey = ry*Math.sin(d.endAngle);
|
||||||
|
|
||||||
|
var ret =[];
|
||||||
|
ret.push("M",sx,sy,"A",rx,ry,"0",(d.endAngle-d.startAngle > Math.PI? 1: 0),"1",ex,ey,"L",ir*ex,ir*ey);
|
||||||
|
ret.push("A",ir*rx,ir*ry,"0",(d.endAngle-d.startAngle > Math.PI? 1: 0), "0",ir*sx,ir*sy,"z");
|
||||||
|
return ret.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function pieOuter(d, rx, ry, h ){
|
||||||
|
var startAngle = (d.startAngle > Math.PI ? Math.PI : d.startAngle);
|
||||||
|
var endAngle = (d.endAngle > Math.PI ? Math.PI : d.endAngle);
|
||||||
|
|
||||||
|
var sx = rx*Math.cos(startAngle),
|
||||||
|
sy = ry*Math.sin(startAngle),
|
||||||
|
ex = rx*Math.cos(endAngle),
|
||||||
|
ey = ry*Math.sin(endAngle);
|
||||||
|
|
||||||
|
var ret =[];
|
||||||
|
ret.push("M",sx,h+sy,"A",rx,ry,"0 0 1",ex,h+ey,"L",ex,ey,"A",rx,ry,"0 0 0",sx,sy,"z");
|
||||||
|
return ret.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function pieInner(d, rx, ry, h, ir ){
|
||||||
|
var startAngle = (d.startAngle < Math.PI ? Math.PI : d.startAngle);
|
||||||
|
var endAngle = (d.endAngle < Math.PI ? Math.PI : d.endAngle);
|
||||||
|
|
||||||
|
var sx = ir*rx*Math.cos(startAngle),
|
||||||
|
sy = ir*ry*Math.sin(startAngle),
|
||||||
|
ex = ir*rx*Math.cos(endAngle),
|
||||||
|
ey = ir*ry*Math.sin(endAngle);
|
||||||
|
|
||||||
|
var ret =[];
|
||||||
|
ret.push("M",sx, sy,"A",ir*rx,ir*ry,"0 0 1",ex,ey, "L",ex,h+ey,"A",ir*rx, ir*ry,"0 0 0",sx,h+sy,"z");
|
||||||
|
return ret.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPercent(d){
|
||||||
|
return (d.endAngle-d.startAngle > 0.2 ?
|
||||||
|
Math.round(1000*(d.endAngle-d.startAngle)/(Math.PI*2))/10+'%' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
Donut3D.transition = function(id, data, rx, ry, h, ir){
|
||||||
|
function arcTweenInner(a) {
|
||||||
|
var i = d3.interpolate(this._current, a);
|
||||||
|
this._current = i(0);
|
||||||
|
return function(t) { return pieInner(i(t), rx+0.5, ry+0.5, h, ir); };
|
||||||
|
}
|
||||||
|
function arcTweenTop(a) {
|
||||||
|
var i = d3.interpolate(this._current, a);
|
||||||
|
this._current = i(0);
|
||||||
|
return function(t) { return pieTop(i(t), rx, ry, ir); };
|
||||||
|
}
|
||||||
|
function arcTweenOuter(a) {
|
||||||
|
var i = d3.interpolate(this._current, a);
|
||||||
|
this._current = i(0);
|
||||||
|
return function(t) { return pieOuter(i(t), rx-.5, ry-.5, h); };
|
||||||
|
}
|
||||||
|
function textTweenX(a) {
|
||||||
|
var i = d3.interpolate(this._current, a);
|
||||||
|
this._current = i(0);
|
||||||
|
return function(t) { return 0.6*rx*Math.cos(0.5*(i(t).startAngle+i(t).endAngle)); };
|
||||||
|
}
|
||||||
|
function textTweenY(a) {
|
||||||
|
var i = d3.interpolate(this._current, a);
|
||||||
|
this._current = i(0);
|
||||||
|
return function(t) { return 0.6*rx*Math.sin(0.5*(i(t).startAngle+i(t).endAngle)); };
|
||||||
|
}
|
||||||
|
|
||||||
|
var _data = d3.layout.pie().sort(null).value(function(d) {return d.value;})(data);
|
||||||
|
|
||||||
|
d3.select("#"+id).selectAll(".innerSlice").data(_data)
|
||||||
|
.transition().duration(750).attrTween("d", arcTweenInner);
|
||||||
|
|
||||||
|
d3.select("#"+id).selectAll(".topSlice").data(_data)
|
||||||
|
.transition().duration(750).attrTween("d", arcTweenTop);
|
||||||
|
|
||||||
|
d3.select("#"+id).selectAll(".outerSlice").data(_data)
|
||||||
|
.transition().duration(750).attrTween("d", arcTweenOuter);
|
||||||
|
|
||||||
|
d3.select("#"+id).selectAll(".percent").data(_data).transition().duration(750)
|
||||||
|
.attrTween("x",textTweenX).attrTween("y",textTweenY).text(getPercent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Donut3D.draw=function(id, data, x /*center x*/, y/*center y*/,
|
||||||
|
rx/*radius x*/, ry/*radius y*/, h/*height*/, ir/*inner radius*/){
|
||||||
|
|
||||||
|
var _data = d3.layout.pie().sort(null).value(function(d) {return d.value;})(data);
|
||||||
|
|
||||||
|
var slices = d3.select("#"+id).append("g").attr("transform", "translate(" + x + "," + y + ")")
|
||||||
|
.attr("class", "slices");
|
||||||
|
|
||||||
|
slices.selectAll(".innerSlice").data(_data).enter().append("path").attr("class", "innerSlice")
|
||||||
|
.style("fill", function(d) { return d3.hsl(d.data.color).darker(0.7); })
|
||||||
|
.attr("d",function(d){ return pieInner(d, rx+0.5,ry+0.5, h, ir);})
|
||||||
|
.each(function(d){this._current=d;});
|
||||||
|
|
||||||
|
slices.selectAll(".topSlice").data(_data).enter().append("path").attr("class", "topSlice")
|
||||||
|
.style("fill", function(d) { return d.data.color; })
|
||||||
|
.style("stroke", function(d) { return d.data.color; })
|
||||||
|
.attr("d",function(d){ return pieTop(d, rx, ry, ir);})
|
||||||
|
.each(function(d){this._current=d;});
|
||||||
|
|
||||||
|
slices.selectAll(".outerSlice").data(_data).enter().append("path").attr("class", "outerSlice")
|
||||||
|
.style("fill", function(d) { return d3.hsl(d.data.color).darker(0.7); })
|
||||||
|
.attr("d",function(d){ return pieOuter(d, rx-.5,ry-.5, h);})
|
||||||
|
.each(function(d){this._current=d;});
|
||||||
|
|
||||||
|
slices.selectAll(".percent").data(_data).enter().append("text").attr("class", "percent")
|
||||||
|
.attr("x",function(d){ return 0.6*rx*Math.cos(0.5*(d.startAngle+d.endAngle));})
|
||||||
|
.attr("y",function(d){ return 0.6*ry*Math.sin(0.5*(d.startAngle+d.endAngle));})
|
||||||
|
.text(getPercent).each(function(d){this._current=d;});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Donut3D = Donut3D;
|
||||||
|
}();
|
||||||
@@ -204,7 +204,11 @@
|
|||||||
</div><!-- section -->
|
</div><!-- section -->
|
||||||
|
|
||||||
<div id="graph-section">
|
<div id="graph-section">
|
||||||
|
<div class="header">
|
||||||
|
<div class="title">Host Status Overview</div>
|
||||||
|
<div class="legend"><i class="fa fa-circle successful-hosts-color"></i> Successful <i class="fa fa-circle changed-hosts-color"></i> Changed
|
||||||
|
<i class="fa fa-circle unreachable-hosts-color"></i> Unreachable <i class="fa fa-circle failed-hosts-color"></i> Failed</div>
|
||||||
|
</div>
|
||||||
</div><!-- graph section -->
|
</div><!-- graph section -->
|
||||||
</div>
|
</div>
|
||||||
</div><!-- col-md-5 -->
|
</div><!-- col-md-5 -->
|
||||||
|
|||||||
@@ -405,132 +405,7 @@
|
|||||||
<script src="{{ STATIC_URL }}lib/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js"></script>
|
<script src="{{ STATIC_URL }}lib/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js"></script>
|
||||||
<script scr="{{ STATIC_URL }}lib/lib/jQuery.dotdotdot/src/js/jquery.dotdotdot.min.js"></script>
|
<script scr="{{ STATIC_URL }}lib/lib/jQuery.dotdotdot/src/js/jquery.dotdotdot.min.js"></script>
|
||||||
<script src="{{ STATIC_URL }}lib/d3js/build/d3.v3.min.js"></script>
|
<script src="{{ STATIC_URL }}lib/d3js/build/d3.v3.min.js"></script>
|
||||||
|
<script src="{{ STATIC_URL }}lib/d3Donut/d3Donut.js"></script>
|
||||||
<script>
|
|
||||||
!function(){
|
|
||||||
var Donut3D={};
|
|
||||||
|
|
||||||
function pieTop(d, rx, ry, ir ){
|
|
||||||
if(d.endAngle - d.startAngle == 0 ) return "M 0 0";
|
|
||||||
var sx = rx*Math.cos(d.startAngle),
|
|
||||||
sy = ry*Math.sin(d.startAngle),
|
|
||||||
ex = rx*Math.cos(d.endAngle),
|
|
||||||
ey = ry*Math.sin(d.endAngle);
|
|
||||||
|
|
||||||
var ret =[];
|
|
||||||
ret.push("M",sx,sy,"A",rx,ry,"0",(d.endAngle-d.startAngle > Math.PI? 1: 0),"1",ex,ey,"L",ir*ex,ir*ey);
|
|
||||||
ret.push("A",ir*rx,ir*ry,"0",(d.endAngle-d.startAngle > Math.PI? 1: 0), "0",ir*sx,ir*sy,"z");
|
|
||||||
return ret.join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
function pieOuter(d, rx, ry, h ){
|
|
||||||
var startAngle = (d.startAngle > Math.PI ? Math.PI : d.startAngle);
|
|
||||||
var endAngle = (d.endAngle > Math.PI ? Math.PI : d.endAngle);
|
|
||||||
|
|
||||||
var sx = rx*Math.cos(startAngle),
|
|
||||||
sy = ry*Math.sin(startAngle),
|
|
||||||
ex = rx*Math.cos(endAngle),
|
|
||||||
ey = ry*Math.sin(endAngle);
|
|
||||||
|
|
||||||
var ret =[];
|
|
||||||
ret.push("M",sx,h+sy,"A",rx,ry,"0 0 1",ex,h+ey,"L",ex,ey,"A",rx,ry,"0 0 0",sx,sy,"z");
|
|
||||||
return ret.join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
function pieInner(d, rx, ry, h, ir ){
|
|
||||||
var startAngle = (d.startAngle < Math.PI ? Math.PI : d.startAngle);
|
|
||||||
var endAngle = (d.endAngle < Math.PI ? Math.PI : d.endAngle);
|
|
||||||
|
|
||||||
var sx = ir*rx*Math.cos(startAngle),
|
|
||||||
sy = ir*ry*Math.sin(startAngle),
|
|
||||||
ex = ir*rx*Math.cos(endAngle),
|
|
||||||
ey = ir*ry*Math.sin(endAngle);
|
|
||||||
|
|
||||||
var ret =[];
|
|
||||||
ret.push("M",sx, sy,"A",ir*rx,ir*ry,"0 0 1",ex,ey, "L",ex,h+ey,"A",ir*rx, ir*ry,"0 0 0",sx,h+sy,"z");
|
|
||||||
return ret.join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPercent(d){
|
|
||||||
return (d.endAngle-d.startAngle > 0.2 ?
|
|
||||||
Math.round(1000*(d.endAngle-d.startAngle)/(Math.PI*2))/10+'%' : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
Donut3D.transition = function(id, data, rx, ry, h, ir){
|
|
||||||
function arcTweenInner(a) {
|
|
||||||
var i = d3.interpolate(this._current, a);
|
|
||||||
this._current = i(0);
|
|
||||||
return function(t) { return pieInner(i(t), rx+0.5, ry+0.5, h, ir); };
|
|
||||||
}
|
|
||||||
function arcTweenTop(a) {
|
|
||||||
var i = d3.interpolate(this._current, a);
|
|
||||||
this._current = i(0);
|
|
||||||
return function(t) { return pieTop(i(t), rx, ry, ir); };
|
|
||||||
}
|
|
||||||
function arcTweenOuter(a) {
|
|
||||||
var i = d3.interpolate(this._current, a);
|
|
||||||
this._current = i(0);
|
|
||||||
return function(t) { return pieOuter(i(t), rx-.5, ry-.5, h); };
|
|
||||||
}
|
|
||||||
function textTweenX(a) {
|
|
||||||
var i = d3.interpolate(this._current, a);
|
|
||||||
this._current = i(0);
|
|
||||||
return function(t) { return 0.6*rx*Math.cos(0.5*(i(t).startAngle+i(t).endAngle)); };
|
|
||||||
}
|
|
||||||
function textTweenY(a) {
|
|
||||||
var i = d3.interpolate(this._current, a);
|
|
||||||
this._current = i(0);
|
|
||||||
return function(t) { return 0.6*rx*Math.sin(0.5*(i(t).startAngle+i(t).endAngle)); };
|
|
||||||
}
|
|
||||||
|
|
||||||
var _data = d3.layout.pie().sort(null).value(function(d) {return d.value;})(data);
|
|
||||||
|
|
||||||
d3.select("#"+id).selectAll(".innerSlice").data(_data)
|
|
||||||
.transition().duration(750).attrTween("d", arcTweenInner);
|
|
||||||
|
|
||||||
d3.select("#"+id).selectAll(".topSlice").data(_data)
|
|
||||||
.transition().duration(750).attrTween("d", arcTweenTop);
|
|
||||||
|
|
||||||
d3.select("#"+id).selectAll(".outerSlice").data(_data)
|
|
||||||
.transition().duration(750).attrTween("d", arcTweenOuter);
|
|
||||||
|
|
||||||
d3.select("#"+id).selectAll(".percent").data(_data).transition().duration(750)
|
|
||||||
.attrTween("x",textTweenX).attrTween("y",textTweenY).text(getPercent);
|
|
||||||
}
|
|
||||||
|
|
||||||
Donut3D.draw=function(id, data, x /*center x*/, y/*center y*/,
|
|
||||||
rx/*radius x*/, ry/*radius y*/, h/*height*/, ir/*inner radius*/){
|
|
||||||
|
|
||||||
var _data = d3.layout.pie().sort(null).value(function(d) {return d.value;})(data);
|
|
||||||
|
|
||||||
var slices = d3.select("#"+id).append("g").attr("transform", "translate(" + x + "," + y + ")")
|
|
||||||
.attr("class", "slices");
|
|
||||||
|
|
||||||
slices.selectAll(".innerSlice").data(_data).enter().append("path").attr("class", "innerSlice")
|
|
||||||
.style("fill", function(d) { return d3.hsl(d.data.color).darker(0.7); })
|
|
||||||
.attr("d",function(d){ return pieInner(d, rx+0.5,ry+0.5, h, ir);})
|
|
||||||
.each(function(d){this._current=d;});
|
|
||||||
|
|
||||||
slices.selectAll(".topSlice").data(_data).enter().append("path").attr("class", "topSlice")
|
|
||||||
.style("fill", function(d) { return d.data.color; })
|
|
||||||
.style("stroke", function(d) { return d.data.color; })
|
|
||||||
.attr("d",function(d){ return pieTop(d, rx, ry, ir);})
|
|
||||||
.each(function(d){this._current=d;});
|
|
||||||
|
|
||||||
slices.selectAll(".outerSlice").data(_data).enter().append("path").attr("class", "outerSlice")
|
|
||||||
.style("fill", function(d) { return d3.hsl(d.data.color).darker(0.7); })
|
|
||||||
.attr("d",function(d){ return pieOuter(d, rx-.5,ry-.5, h);})
|
|
||||||
.each(function(d){this._current=d;});
|
|
||||||
|
|
||||||
slices.selectAll(".percent").data(_data).enter().append("text").attr("class", "percent")
|
|
||||||
.attr("x",function(d){ return 0.6*rx*Math.cos(0.5*(d.startAngle+d.endAngle));})
|
|
||||||
.attr("y",function(d){ return 0.6*ry*Math.sin(0.5*(d.startAngle+d.endAngle));})
|
|
||||||
.text(getPercent).each(function(d){this._current=d;});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Donut3D = Donut3D;
|
|
||||||
}();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// When user clicks on main tab, fire the matching Angular route
|
// When user clicks on main tab, fire the matching Angular route
|
||||||
|
|||||||
Reference in New Issue
Block a user