/**
* This javascript provides function for drawing pie chart
* @class lexisnexis.component.drawPieChart
*/
jQuery.namespace('lexisnexis.component');
/**
* Draw the pie chart
* @method lexisnexis.component.drawPieChart
*/
lexisnexis.component.drawPieChart = function (divId, data, configValues, jsonData) {
try {
dataSet=jQuery.parseJSON(data);
}catch(err) {
dataSet=data;
}
try {
config=jQuery.parseJSON(configValues);
}catch(err) {
config=configValues;
}
var defaultConfig = {
canvasWidth : 380, canvasHeight : 290, viewboxLeft : 0, viewboxTop : 0,
outerRadius : 80, innerRadius : 0,
contentOffsetX : 0, contentOffsetY : 0,
legendBulletOffsetX : 20, legendBulletOffsetY : -5, legendTextOffset : 20, textVerticalSpace : 35, legendVerticalOffset : 30, legendWrappedTextOffsetY : 14,
enableLinks : false, enableChart : true, labelText : true, legend : false, legendLabel : true, legendLinkLabel : '', linkURL : '', isPdf : false,
toolTipArcId : 'tooltipArc', toolTipTextId : 'tooltipText', disabledTextColor : '#bababa'
};
$.extend(defaultConfig, config);
defaultConfig.enableChart = jsonData == null || typeof jsonData.enabled == 'undefined' || jsonData.enabled;
// x and y coordinates for the pie center.
var pieCenterX = defaultConfig.outerRadius;
var pieCenterY = defaultConfig.outerRadius;
var enableLinks = defaultConfig.enableLinks;
var isStratification = (typeof defaultConfig.isStratification != 'undefined' && defaultConfig.isStratification);
var legendLabel = defaultConfig.legendLabel;
var emptyArr = [];
// The WKHMTLTOPDF webkit does not honor certain attributes when rendering arcs at values of 100%.
// STROKE and STROKE-WIDTH suffer. Hence 'singleColor' will be used to determine the border color
// of the arc. This eliminates the white line that appears in a single pie wedge at 100%.
var singleColor = 'white';
//variable used for specifying arc value
var numSlices=0;
var maxLength = 21;
dataSet.forEach(function (d,i) {
if(parseFloat(d.value)>0){
emptyArr.push(true);
numSlices++;
singleColor = (numSlices < 2 ? d.color : 'white');
}else{
emptyArr.push(false);
}
if (!isStratification) {
d.lines = wordwrap(d.label, maxLength);
}
});
var isEmpty = emptyArr.indexOf(true) < 0;
// Creating x and y scale to match the pie circumference
function calcMid(d) {
var angle = d.startAngle + ((d.endAngle - d.startAngle) / 2);
var r = defaultConfig.outerRadius+10;
return {
x: r * Math.cos(angle),
y: r * Math.sin(angle)
};
};
var responsiveClass = "ln-piechart-responsive";
if (isStratification) {
responsiveClass = "ln-strat-piechart-responsive";
}
// Creating svg container element - all drawing components will be wrapped inside this container.
var canvas=d3.select(divId).classed("ln-svg-container", true).append("svg:svg").classed(responsiveClass, true);
// Data to be used. Define viewport for the canvas, or svg container.
var vboxAttr = [defaultConfig.viewboxLeft,defaultConfig.viewboxTop,defaultConfig.canvasWidth,defaultConfig.canvasHeight].join(' ');
var groupAll = canvas.data([dataSet]).attr('viewBox',vboxAttr).attr('perserveAspectRatio','xMinYMin meet')
.style("overflow","visible")
// Adding group to hold our pie chart and legend.
.append("svg:g");
var groupLabel = null;
var groupPieChart = null;
var arc = 0;
if(dataSet.length > 0){
if (defaultConfig.labelText) {
var linkClass = defaultConfig.enableChart ? 'ln-pieChart-label-legend' : '';
// Create the legend label group.
groupLabel = groupAll.append("svg:g");
var elem = groupLabel;
if(legendLabel && enableLinks && defaultConfig.enableChart){
linkClass = 'ln-pieChart-label-legend-underLine';
elem = groupLabel.append("svg:a")
.attr("xlink:href", LN.getContextPath() + defaultConfig.linkURL);
}
elem.append("text")
.attr("text-anchor", "center")
.attr("x", 0)
.attr("y", 0)
.attr("dx", 0)
.attr("dy", "5px")
.attr("class", linkClass)
.text(defaultConfig.legendLinkLabel);
if (!defaultConfig.enableChart) {
elem.selectAll("text").style("fill", defaultConfig.disabledTextColor);
}
}
}
if(dataSet.length > 0 && defaultConfig.enableChart){
groupPieChart = groupAll.append("svg:g");
// Creating the arc both inner as well as outer
arc = d3.svg.arc().outerRadius(defaultConfig.outerRadius);
// Creating the pie layout to use the value from the dataset
var pie = d3.layout.pie().value(function(d) {
return d.value;
});
if (enableLinks) {
// Creating slices in the group
arcs = groupPieChart.selectAll("g.slice")
// Pie to be used as the data. The pie will create the start and end angles for the data passed to it earlier.
.data(pie)
// Adding links to the slices.
.enter().append("svg:a")
.attr("xlink:href", function(d) { return LN.getContextPath()+d.data.link; })
.on("mouseover", function(d) {
d3.select(this).select('text').style('text-decoration', 'underline');
})
.on("mouseleave", function(d) {
d3.select(this).select('text').style('text-decoration', 'none');
})
.on("mouseout", function(d) {
d3.select(this).select('text').style('text-decoration', 'none');
});
}else{
arcs = groupPieChart.selectAll("g.slice").data(pie).enter();
}
// Setting the color to each of the slices.
arcs.append("svg:path")
.style("fill", function(d) { return d.data.color; })
.attr("id", function(d) { return divId+"_"+d.data.label; })
.attr("stroke-width", function(){
return "1.5";
})
.attr("stroke", function(d) {
return singleColor;
})
.attr("d", arc)
.on("mouseover", function(d){ mouseOverArcCallback(d.value, d.data.label, d.data.color, defaultConfig, this); })
.on("mouseleave", function(d) {
var text = $('#'+defaultConfig.toolTipArcId).text();
if (text.indexOf(d.data.label) > -1) {
removeToolTip(defaultConfig.toolTipArcId);
}
})
.on("mouseout", function(d){ mouseOutArcCallback(this, d.value); });
arcs.append("svg:text")
// Set Outer Coordinate
.attr("transform", function(d) {
d.outerRadius = defaultConfig.outerRadius-10;
d.innerRadius = defaultConfig.outerRadius/3;
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.attr("dy", ".28em")
.text(function (d) {
return numeral(d.value).format('0.00%');
})
.attr('class','ln-piechart-label')
.style('display', function (d) {
var bb = this.getBBox(),
center = arc.centroid(d);
var topLeft = {
x : center[0] + bb.x,
y : center[1] + bb.y
};
var topRight = {
x : topLeft.x + bb.width,
y : topLeft.y
};
var bottomLeft = {
x : topLeft.x,
y : topLeft.y + bb.height
};
var bottomRight = {
x : topLeft.x + bb.width,
y : topLeft.y + bb.height
};
d.visible = pointIsInArc(topLeft, d, arc) &&
pointIsInArc(topRight, d, arc) &&
pointIsInArc(bottomLeft, d, arc) &&
pointIsInArc(bottomRight, d, arc);
d.outerRadius = defaultConfig.outerRadius;
d.innerRadius = defaultConfig.innerRadius;
return d.visible ? null : "none";
})
.on("mouseover", function(d){
var id="path[id='"+divId+"_"+d.data.label+"']";
d3.select(id);
var val=getData(d, d.data.label);
mouseOverTextCallback(val.value, val.label, val.color, defaultConfig, id);
})
.on("mouseleave", function(d){
var text = $('#'+defaultConfig.toolTipTextId).html();
if ($('<div/>').html(text).text().indexOf(d.data.label) > -1) {
removeToolTip(defaultConfig.toolTipTextId);
}
});
} else {
// Render a simple circle with a gray border if chart is disabled.
groupPieChart = groupAll.append("svg:g");
groupPieChart.append("circle").attr("r", defaultConfig.outerRadius)
.attr("fill", "#fff").attr("stroke", "#8e8e8e")
.attr("stroke-width", "1.5")
.attr("id", function(d) { return divId+"_DISABLED"; });
}
function pointIsInArc(pt, ptData, d3Arc) {
// Center of the arc is assumed to be 0,0
// (pt.x, pt.y) are assumed to be relative to the center
var r1 = arc.innerRadius()(ptData),
r2 = arc.outerRadius()(ptData),
theta1 = arc.startAngle()(ptData),
theta2 = arc.endAngle()(ptData);
var dist = pt.x * pt.x + pt.y * pt.y,
angle = Math.atan2(pt.x, -pt.y);
angle = (angle < 0) ? (angle + Math.PI * 2) : angle;
return (r1 * r1 <= dist) && (dist <= r2 * r2) &&
(theta1 <= angle) && (angle <= theta2);
}
// Adding bullets for the legend
var valueOffsetY = 0;
var groupLegend = null;
if (!isStratification) {
groupLegend = groupAll.append("svg:g");
groupLegend.selectAll("legendRect")
// Adding bullets to the svg.
.data(dataSet).enter().append("svg:rect")
// Setting the circle's x and y coordinates for the center of the legend circle.
.attr("x", 0)
.attr("y", function(d, i) {
var thisY = i*defaultConfig.textVerticalSpace + defaultConfig.legendBulletOffsetY + valueOffsetY;
valueOffsetY += (defaultConfig.isPdf && !isStratification && d.lines.length>1) ? defaultConfig.legendWrappedTextOffsetY : 0;
return thisY;
})
.attr("width", 10)
.attr("height", 10)
.attr("stroke-width", ".5")
// Setting color to match the pie color
.style("fill", function(d) { return d.color; })
.style("stroke","black")
.attr("r", 5);
if (!defaultConfig.enableChart) {
groupLegend.selectAll("rect").style("fill", defaultConfig.disabledTextColor).style("stroke", defaultConfig.disabledTextColor);
}
}
// Setting hyperlinks for the legend text as well.
if (isStratification) {
LN.debug('inside stratification');
var colorStyle = "", borderStyle = "";
if (!defaultConfig.enableChart) {
colorStyle = "color:"+defaultConfig.disabledTextColor+";";
}
var $legendDiv = $('<div class="pull-left ln-legendDiv" style="margin-left:'+defaultConfig.legendBulletOffsetX+';'+colorStyle+'"></div>');
var $parent = $(divId).parent();
$parent.find('.ln-legendDiv').remove();
var elem = $legendDiv.appendTo($parent);
var legendLabel;
dataSet.forEach(function (d,i) {
LN.debug(d.value);
colorStyle = !defaultConfig.enableChart ? defaultConfig.disabledTextColor : d.color;
borderStyle = !defaultConfig.enableChart ? "border:none;" : "";
legendLabel = ['<div><div style="margin-right:',defaultConfig.legendTextOffset,';background-color:',colorStyle,';',borderStyle,'" class="ln-display-block ln-strat-bullet"></div>'].join('');
if (defaultConfig.enableChart && enableLinks && !isEmpty && d.value > 0.0){
LN.debug('inside enable links');
legendLabel = [legendLabel,
'<a id="legendId_',i,'" href="', LN.getContextPath(), d.link, '" class="ln-pieChart-links">',d.label,'</a></div>'].join('');
} else {
legendLabel = [legendLabel,
'<span id="legendId_',i,'" class="ln-strat-pieChart-noLinks">',d.label,'</span></div>'].join('');
}
$(legendLabel).appendTo($legendDiv);
if (defaultConfig.enableChart) {
$legendDiv.find('a#legendId_'+i)
.on("mouseover", function(e) {
d3.event = e;
var id="path[id='"+divId+"_"+this.textContent+"']";
d3.select(id);
var val=getData(d, this.textContent);
mouseOverArcCallback(val.value, '', val.color, defaultConfig, id);
d3.event = null;
})
.on("mouseleave", function(e) { d3.event = e; removeToolTip(defaultConfig.toolTipArcId); d3.event = null; })
.on("mouseout", function(e){
d3.event = e;
var id= "path[id='"+divId+"_"+this.textContent+"']";
mouseOutArcCallback(id, d.value);
d3.event = null;
});
}
});
$(divId).css('padding-bottom','60%');
} else {
valueOffsetY = 0;
var labelClass = 'ln-pieChart-enableLinks';
dataSet.forEach(function (d,i) {
LN.debug(d.value);
var elem = groupLegend;
if (defaultConfig.enableChart && enableLinks && !isEmpty && d.value > 0.0){
LN.debug('inside enable links');
elem = groupLegend.append("svg:a")
.attr("xlink:href", LN.getContextPath()+d.link);
}
var textElem = elem.append("text")
.attr("text-anchor", "center")
.attr("x", defaultConfig.legendTextOffset)
.attr("y", i*defaultConfig.textVerticalSpace + valueOffsetY)
.attr("dx", 0)
.attr("dy", "5px").each(function() {
var textSeparation = 0.40;
var textNode = d3.select(this);
var x = textNode.attr("x"),
y = textNode.attr("y");
for(var i = 0; i < d.lines.length; i++) {
if (i==0) {
textNode.text(null);
}
textNode.append('tspan').attr('x',x).attr('y',y).attr('dy', textSeparation + 'em').text(d.lines[i]);
y=parseInt(y) + defaultConfig.legendWrappedTextOffsetY;
textSeparation = 0.40;
}
if (defaultConfig.isPdf) {
textNode.append('tspan').attr('x',x).attr('y',y).attr('dy', textSeparation + 'em').text(numeral(d.value).format('0.00%'));
}
valueOffsetY += (defaultConfig.isPdf && d.lines.length>1) ? defaultConfig.legendWrappedTextOffsetY : 0;
});
if (defaultConfig.enableChart) {
textElem.attr('class',labelClass)
.on("mouseover", function(d) {
if(d.value > 0.0){
d3.select(this).selectAll('tspan').style('text-decoration','underline');
}
var id="path[id='"+divId+"_"+this.textContent+"']";
d3.select(id);
var val=getData(d, this.textContent);
mouseOverArcCallback(val.value, '', val.color, defaultConfig, id);
})
.on("mouseleave", function(d) {
if(d.value > 0.0){
d3.select(this).selectAll('tspan').style('text-decoration','none');
}
removeToolTip(defaultConfig.toolTipArcId);
})
.on("mouseout", function(d){
if(d.value > 0.0){
d3.select(this).selectAll('tspan').style('text-decoration','none');
}
var id= "path[id='"+divId+"_"+this.textContent+"']";
mouseOutArcCallback(id, d.value);
});
} else {
textElem.attr("fill",defaultConfig.disabledTextColor);
}
});
}
var gElem = null;
gElem = $(groupLabel);
var gLblBBox = gElem != null && gElem.length > 0 ? $(gElem[0])[0].getBBox() : null;
gElem = $(groupPieChart);
var gPieBBox = gElem != null && gElem.length > 0 ? $(groupPieChart[0])[0].getBBox() : null;
// Setting the center of pie3
var offsetX = defaultConfig.contentOffsetX;
var offsetY = defaultConfig.contentOffsetY;
groupAll.attr("transform", ["translate(", offsetX, ',', offsetY, ")"].join(''));
offsetX = defaultConfig.legendBulletOffsetX;
offsetY = defaultConfig.legendVerticalOffset;
if (gPieBBox != null) {
offsetX += isStratification ? '' : pieCenterX*2;
offsetY += isStratification ? pieCenterY*2 : 0;
}
if (!isStratification) {
groupLegend.attr("transform", ["translate(", offsetX, ',', offsetY, ")"].join(''));
}
if (groupLabel != null) {
offsetY = 0;
// offsetX was set from previous operation.
groupLabel.attr("transform", ["translate(", offsetX, ',', offsetY, ")"].join(''));
}
if (groupPieChart != null) {
offsetX = pieCenterX;
offsetY = pieCenterY;
offsetY += isStratification ? (gLblBBox != null ? gLblBBox.height + defaultConfig.textVerticalSpace : 0)
: defaultConfig.legendVerticalOffset + defaultConfig.legendBulletOffsetY;
groupPieChart.attr("transform", ['translate(', offsetX, ',', offsetY, ')'].join(''));
}
function wordwrap(text, max) {
var regex = new RegExp(".{0,"+max+"}(?:\\s|$)","g");
var lines = [];
var line;
while ((line = regex.exec(text))!="") {
lines.push(line);
}
return lines;
}
function getData(d, text) {
var value=0;
$.each(d, function(i) {
if(!d.label){
if(text.indexOf(d[i].label) == 0){
value= d[i];
}
}else if(text.indexOf(d.label) == 0){
value=d;
}
});
return value;
}
function tabulate(divId, data){
var table = d3.select(divId).append("table").attr("border","2").attr("width","100%"),
tbody = table.append("tbody");
data.forEach(function (d,i) {
var rows = tbody.append("tr");
// Create a cell in each row for each column
rows.append("td").text(numeral( d.value).format('0.00%') );
rows.append("td").text(d.label);
});
return table;
}
function removeToolTip(id){
if (id != null && $('#'+id).length > 0) {
$('#'+id).remove();
}
}
function mouseOutArcCallback(id, value){
var thisColor = singleColor;
if (value > 0 && value < 1) {
thisColor = "white";
}
d3.select(id).transition()
.attr("d", arc)
.attr("stroke", thisColor);
}
function mouseOverArcCallback(value, label, color, defaultConfig, id){
mouseOverCallback(value, label, color, defaultConfig, id, defaultConfig.toolTipArcId);
}
function mouseOverTextCallback(value, label, color, defaultConfig, id){
mouseOverCallback(value, label, color, defaultConfig, id, defaultConfig.toolTipTextId);
}
function mouseOverCallback(value, label, color, defaultConfig, id, toolTipId){
removeToolTip(toolTipId);
div = d3.select("body").append("div");
div.attr("class", "ln-chart-tooltip")
.attr("id", toolTipId);
var arcOver = d3.svg.arc().outerRadius(defaultConfig.outerRadius);
$(id).closest('svg').find('path').each(function() {
mouseOutArcCallback(this, value);
});
if (value > 0) {
arcOver = d3.svg.arc().outerRadius(defaultConfig.outerRadius + 10);
d3.select(id).transition()
.duration(1000)
.attr("d", arcOver);
}
var tip='';
label = (label) ? ["<br/>",label].join('') : '';
tip = div.html([numeral(value).format('0.00%'),label].join(''));
tip.style("left", (d3.event.pageX+12) + "px")
.style("top", (d3.event.pageY-10) + "px")
.style("border-color", color)
.style("opacity", 1)
.style("display","block");
}
};