|
function groupBarChart(config) { |
|
function setReSizeEvent(data) { |
|
var resizeTimer; |
|
window.removeEventListener('resize', function () { |
|
}); |
|
window.addEventListener('resize', function (event) { |
|
|
|
if (resizeTimer !== false) { |
|
clearTimeout(resizeTimer); |
|
} |
|
resizeTimer = setTimeout(function () { |
|
$(data.mainDiv).empty(); |
|
drawgroupBarChartChart(data); |
|
clearTimeout(resizeTimer); |
|
}, 500); |
|
}); |
|
} |
|
drawgroupBarChartChart(config); |
|
setReSizeEvent(config); |
|
} |
|
function creategroupBarChartLegend(mainDiv, columnsInfo, colorRange) { |
|
var z = d3.scaleOrdinal() |
|
.range(colorRange); |
|
var mainDivName = mainDiv.substr(1, mainDiv.length); |
|
$(mainDiv).before("<div id='Legend_" + mainDivName + "' style='margin-top:0; margin-bottom:0;'></div>"); |
|
var keys = Object.keys(columnsInfo); |
|
keys.forEach(function (d) { |
|
var cloloCode = z(d); |
|
$("#Legend_" + mainDivName).append("<span class='team-graph team1' style='display: inline-block; margin-right:10px;'>\ |
|
<span style='background:" + cloloCode + ";width: 10px;height: 10px;display: inline-block;vertical-align: middle;'> </span>\ |
|
<span style='padding-top: 0;font-family:Source Sans Pro, sans-serif;font-size: 13px;display: inline;'>" + columnsInfo[d] + " </span>\ |
|
</span>"); |
|
}); |
|
} |
|
|
|
function drawgroupBarChartChart(config) { |
|
var data = config.data; |
|
var columnsInfo = config.columnsInfo; |
|
var xAxis = config.xAxis; |
|
var yAxis = config.yAxis; |
|
var colorRange = config.colorRange; |
|
var mainDiv = config.mainDiv; |
|
var mainDivName = mainDiv.substr(1, mainDiv.length); |
|
var label = config.label; |
|
var requireLegend = config.requireLegend; |
|
d3.select(mainDiv).append("svg").attr("width", $(mainDiv).width()).attr("height", $(mainDiv).height() * 0.9); |
|
var svg = d3.select(mainDiv + " svg"), |
|
margin = { top: 20, right: 20, bottom: 30, left: 40 }, |
|
width = +svg.attr("width") - margin.left - margin.right, |
|
height = +svg.attr("height") - margin.top - margin.bottom; |
|
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
if (requireLegend != null && requireLegend != undefined && requireLegend != false) { |
|
$("#Legend_" + mainDivName).remove(); |
|
creategroupBarChartLegend(mainDiv, columnsInfo, colorRange); |
|
|
|
} |
|
var x0 = d3.scaleBand() |
|
.rangeRound([0, width]) |
|
.paddingInner(0.1); |
|
|
|
var x1 = d3.scaleBand() |
|
.padding(0.05); |
|
|
|
var y = d3.scaleLinear() |
|
.rangeRound([height, 0]); |
|
|
|
var z = d3.scaleOrdinal() |
|
.range(colorRange); |
|
|
|
var keys = Object.keys(columnsInfo); |
|
x0.domain(data.map(function (d) { |
|
return d[xAxis]; |
|
})); |
|
x1.domain(keys).rangeRound([0, x0.bandwidth()]); |
|
y.domain([0, d3.max(data, function (d) { |
|
return d3.max(keys, function (key) { |
|
return d[key]; |
|
}); |
|
})]).nice(); |
|
|
|
var element = g.append("g") |
|
.selectAll("g") |
|
.data(data) |
|
.enter().append("g") |
|
.attr("transform", function (d) { |
|
return "translate(" + x0(d[xAxis]) + ",0)"; |
|
}); |
|
|
|
var rect = element.selectAll("rect") |
|
.data(function (d, i) { |
|
return keys.map(function (key) { |
|
return { key: key, value: d[key], index: key + "_" + i + "_" + d[xAxis] }; |
|
}); |
|
}) |
|
.enter().append("rect") |
|
.attr("x", function (d) { |
|
return x1(d.key); |
|
}) |
|
.attr("y", function (d) { |
|
return y(d.value); |
|
}) |
|
.attr("width", x1.bandwidth()) |
|
.attr("data-index", function (d, i) { |
|
return d.index; |
|
}) |
|
.attr("height", function (d) { |
|
return height - y(d.value); |
|
}) |
|
.attr("fill", function (d) { |
|
return z(d.key); |
|
}); |
|
//CBT:add tooltips |
|
var self = {}; |
|
self.svg = svg; |
|
self.cssPrefix = "groupBar0_"; |
|
self.data = data; |
|
self.keys = keys; |
|
self.height = height; |
|
self.width = width; |
|
self.label = label; |
|
self.yAxis = yAxis; |
|
self.xAxis = xAxis; |
|
barTooltip.addTooltips(self); |
|
|
|
rect.on("mouseover", function () { |
|
var currentEl = d3.select(this); |
|
var index = currentEl.attr("data-index"); |
|
barTooltip.showTooltip(self, index); |
|
}); |
|
|
|
rect.on("mouseout", function () { |
|
var currentEl = d3.select(this); |
|
var index = currentEl.attr("data-index"); |
|
barTooltip.hideTooltip(self, index); |
|
}); |
|
|
|
rect.on("mousemove", function () { |
|
barTooltip.moveTooltip(self); |
|
}); |
|
|
|
var circleRadius = x1.bandwidth() * 0.4; |
|
var circles = element.selectAll("circle") |
|
.data(function (d) { |
|
var teamWiseData = keys.map(function (key) { |
|
return { key: key, value: d[key], circles: d["circle_" + key], over: d.over }; |
|
}); |
|
|
|
var dataObj = []; |
|
teamWiseData.forEach(function (teamData) { |
|
if (teamData.circles != undefined && teamData.circles != 0 && teamData.circles.length > 0) { |
|
teamData.circles.forEach(function (data, i) { |
|
var tempObj = {}; |
|
tempObj["key"] = teamData["key"]; |
|
tempObj["value"] = teamData["value"]; |
|
tempObj["circles"] = [data]; |
|
tempObj["over"] = teamData["over"]; |
|
tempObj["circleNumber"] = i; |
|
dataObj.push(tempObj); |
|
}); |
|
} else { |
|
teamData["circleNumber"] = 0; |
|
dataObj.push(teamData); |
|
} |
|
}); |
|
return dataObj; |
|
}) |
|
.enter().append("circle") |
|
.attr("cx", function (d) { |
|
return x1(d.key) + (x1.bandwidth() / 2); |
|
}) |
|
.attr("cy", function (d) { |
|
if ((y(d.value) - (circleRadius * 0.9)) - (d.circleNumber * (circleRadius * 2)) < 0) { |
|
return (y(d.value) + (circleRadius)) + (d.circleNumber * (circleRadius * 2)); |
|
} else { |
|
return (y(d.value) - (circleRadius * 0.9)) - (d.circleNumber * (circleRadius * 2)); |
|
} |
|
}) |
|
.attr("r", circleRadius)//x1.bandwidth() * 0.37) |
|
.attr("fill", "#fff") |
|
.attr("stroke", function (d) { |
|
return z(d.key); |
|
}) |
|
.attr("data", function (d) { |
|
var data = {}; |
|
data["over"] = d.over; |
|
data["runs"] = d.value; |
|
if (d.circles != undefined && d.circles.length > 0) { |
|
data["circles"] = d.circles |
|
} |
|
else { |
|
data["circles"] = []; |
|
} |
|
return JSON.stringify(data); |
|
}) |
|
.attr("stroke-width", "2px").attr("display", function (d) { |
|
if (d.circles == undefined || d.circles.length <= 0) { |
|
return "none"; |
|
} else { |
|
return "block"; |
|
} |
|
}); |
|
|
|
circles.on("mouseover", function () { |
|
var currentEl = d3.select(this); |
|
|
|
currentEl.attr("r", parseFloat(currentEl.attr("r")) + 1); |
|
var fadeInSpeed = 120; |
|
d3.select("#circletooltip_" + mainDivName) |
|
.transition() |
|
.duration(fadeInSpeed) |
|
.style("opacity", function () { |
|
return 1; |
|
}); |
|
d3.select("#circletooltip_" + mainDivName) |
|
.attr("transform", function (d) { |
|
var mouseCoords = d3.mouse(this.parentNode); |
|
var xCo = 0; |
|
if (mouseCoords[0] + 10 >= width * 0.80) { |
|
xCo = mouseCoords[0] - parseFloat(d3.selectAll("#circletooltipRect_" + mainDivName) |
|
.attr("width")); |
|
} else { |
|
xCo = mouseCoords[0] + 10; |
|
} |
|
var x = xCo; |
|
var yCo = 0; |
|
if (mouseCoords[0] + 10 >= width * 0.80) { |
|
yCo = mouseCoords[1] + 10; |
|
} else { |
|
yCo = mouseCoords[1]; |
|
} |
|
var x = xCo; |
|
var y = yCo; |
|
return "translate(" + x + "," + y + ")"; |
|
}); |
|
//CBT:calculate tooltips text |
|
var wicketData = JSON.parse(currentEl.attr("data")); |
|
var tooltipsText = ""; |
|
d3.selectAll("#circletooltipText_" + mainDivName).text(""); |
|
var yPos = 0; |
|
d3.selectAll("#circletooltipText_" + mainDivName).append("tspan").attr("x", 0).attr("y", yPos * 10).attr("dy", "1.9em").text(label.xAxis + ": " + wicketData.over); |
|
yPos = yPos + 1; |
|
d3.selectAll("#circletooltipText_" + mainDivName).append("tspan").attr("x", 0).attr("y", yPos * 10).attr("dy", "1.9em").text(label.yAxis + ": " + wicketData.runs); |
|
wicketData.circles.forEach(function (d, i) { |
|
yPos = yPos + 1; |
|
d3.selectAll("#circletooltipText_" + mainDivName).append("tspan").attr("x", 0).attr("y", yPos * 10).attr("dy", "1.9em").text(d["commentary"] + "(" + d.fall_of_wicket + ")"); |
|
}); |
|
//CBT:calculate width of the text based on characters |
|
var dims = helpers.getDimensions("circletooltipText_" + mainDivName); |
|
d3.selectAll("#circletooltipText_" + mainDivName + " tspan") |
|
.attr("x", dims.w + 2); |
|
|
|
d3.selectAll("#circletooltipRect_" + mainDivName) |
|
.attr("width", dims.w + 10) |
|
.attr("height", dims.h + 20); |
|
|
|
}); |
|
circles.on("mousemove", function () { |
|
var currentEl = d3.select(this); |
|
d3.selectAll("#circletooltip_" + mainDivName) |
|
.attr("transform", function (d) { |
|
var mouseCoords = d3.mouse(this.parentNode); |
|
var xCo = 0; |
|
if (mouseCoords[0] + 10 >= width * 0.80) { |
|
xCo = mouseCoords[0] - parseFloat(d3.selectAll("#circletooltipRect_" + mainDivName) |
|
.attr("width")); |
|
} else { |
|
xCo = mouseCoords[0] + 10; |
|
} |
|
var yCo = 0; |
|
if (mouseCoords[0] + 10 >= width * 0.80) { |
|
yCo = mouseCoords[1] + 10; |
|
} else { |
|
yCo = mouseCoords[1]; |
|
} |
|
var x = xCo; |
|
var y = yCo; |
|
return "translate(" + x + "," + y + ")"; |
|
}); |
|
}); |
|
circles.on("mouseout", function () { |
|
var currentEl = d3.select(this); |
|
currentEl.attr("r", parseFloat(currentEl.attr("r")) - 1); |
|
d3.select("#circletooltip_" + mainDivName) |
|
.style("opacity", function () { |
|
return 0; |
|
}); |
|
d3.select("#circletooltip_" + mainDivName).attr("transform", function (d, i) { |
|
// klutzy, but it accounts for tooltip padding which could push it onscreen |
|
var x = -500; |
|
var y = -500; |
|
return "translate(" + x + "," + y + ")"; |
|
}); |
|
}); |
|
|
|
//CBT:circle tooltips start |
|
var circleTooltipg = g.append("g") |
|
.attr("font-family", "sans-serif") |
|
.attr("font-size", 10) |
|
.attr("text-anchor", "end") |
|
.attr("id", "circletooltip_" + mainDivName) |
|
.attr("style", "opacity:0") |
|
.attr("transform", "translate(-500,-500)"); |
|
|
|
circleTooltipg.append("rect") |
|
.attr("id", "circletooltipRect_" + mainDivName) |
|
.attr("x", 0) |
|
.attr("width", 120) |
|
.attr("height", 80) |
|
.attr("opacity", 0.71) |
|
.style("fill", "#000000"); |
|
|
|
circleTooltipg |
|
.append("text") |
|
.attr("id", "circletooltipText_" + mainDivName) |
|
.attr("x", 30) |
|
.attr("y", 15) |
|
.attr("fill", function () { |
|
return "#fff" |
|
}) |
|
.style("font-size", function (d) { |
|
return 10; |
|
}) |
|
.style("font-family", function (d) { |
|
return "arial"; |
|
}) |
|
.text(function (d, i) { |
|
return ""; |
|
}); |
|
|
|
g.append("g") |
|
.attr("class", "axis") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(d3.axisBottom(x0)) |
|
.append("text") |
|
.attr("x", width / 2) |
|
.attr("y", margin.bottom * 0.9) |
|
.attr("dx", "0.32em") |
|
.attr("fill", "#000") |
|
.attr("font-weight", "bold") |
|
.attr("text-anchor", "start") |
|
.text(label.xAxis); |
|
|
|
g.append("g") |
|
.attr("class", "axis") |
|
.call(d3.axisLeft(y).ticks(null, "s")) |
|
.append("text") |
|
.attr("x", 0) |
|
.attr("y", 6) |
|
.attr("dy", "0.71em") |
|
.attr("fill", "#000") |
|
.attr("transform", "rotate(-90)") |
|
.attr("font-weight", "bold") |
|
.text(label.yAxis); |
|
|
|
} |
|
var helpers = { |
|
getDimensions: function (id) { |
|
var el = document.getElementById(id); |
|
var w = 0, h = 0; |
|
if (el) { |
|
var dimensions = el.getBBox(); |
|
w = dimensions.width; |
|
h = dimensions.height; |
|
} else { |
|
console.log("error: getDimensions() " + id + " not found."); |
|
} |
|
return { w: w, h: h }; |
|
} |
|
} |
|
var barTooltip = { |
|
addTooltips: function (pie) { |
|
var keys = pie.keys; |
|
// group the label groups (label, percentage, value) into a single element for simpler positioning |
|
var element = pie.svg.append("g") |
|
.selectAll("g") |
|
.data(pie.data) |
|
.enter().append("g") |
|
.attr("class", function (d, i) { |
|
return pie.cssPrefix + "tooltips" + "_" + i |
|
}); |
|
|
|
tooltips = element.selectAll("g") |
|
.data(function (d, i) { |
|
return keys.map(function (key) { |
|
return { key: key, value: d[key], index: key + "_" + i + "_" + d[pie.xAxis] }; |
|
}); |
|
}) |
|
.enter() |
|
.append("g") |
|
.attr("class", pie.cssPrefix + "tooltip") |
|
.attr("id", function (d, i) { |
|
return pie.cssPrefix + "tooltip" + d.index; |
|
}) |
|
.style("opacity", 0) |
|
.append("rect") |
|
.attr("rx", 2) |
|
.attr("ry", 2) |
|
.attr("x", -2) |
|
.attr("opacity", 0.71) |
|
.style("fill", "#000000"); |
|
|
|
element.selectAll("g") |
|
.data(function (d, i) { |
|
return keys.map(function (key) { |
|
return { key: key, value: d[key], index: key + "_" + i + "_" + d[pie.xAxis] }; |
|
}); |
|
}) |
|
.append("text") |
|
.attr("fill", function (d) { |
|
return "#efefef" |
|
}) |
|
.style("font-size", function (d) { |
|
return 10; |
|
}) |
|
.style("font-family", function (d) { |
|
return "arial"; |
|
}) |
|
.text(function (d, i) { |
|
var caption = "Runs:{runs}"; |
|
return barTooltip.replacePlaceholders(pie, caption, i, { |
|
runs: d.value, |
|
}); |
|
}); |
|
|
|
element.selectAll("g rect") |
|
.attr("width", function (d, i) { |
|
var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index); |
|
return dims.w + (2 * 4); |
|
}) |
|
.attr("height", function (d, i) { |
|
var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index); |
|
return dims.h + (2 * 4); |
|
}) |
|
.attr("y", function (d, i) { |
|
var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index); |
|
return -(dims.h / 2) + 1; |
|
}); |
|
}, |
|
|
|
showTooltip: function (pie, index) { |
|
var fadeInSpeed = 250; |
|
if (barTooltip.currentTooltip === index) { |
|
fadeInSpeed = 1; |
|
} |
|
|
|
barTooltip.currentTooltip = index; |
|
d3.select("#" + pie.cssPrefix + "tooltip" + index) |
|
.transition() |
|
.duration(fadeInSpeed) |
|
.style("opacity", function () { |
|
return 1; |
|
}); |
|
|
|
barTooltip.moveTooltip(pie); |
|
}, |
|
|
|
moveTooltip: function (pie) { |
|
d3.selectAll("#" + pie.cssPrefix + "tooltip" + barTooltip.currentTooltip) |
|
.attr("transform", function (d) { |
|
var mouseCoords = d3.mouse(this.parentNode); |
|
var x = mouseCoords[0] + 4 + 2; |
|
var y = mouseCoords[1] - (2 * 4) - 2; |
|
return "translate(" + x + "," + y + ")"; |
|
}); |
|
}, |
|
|
|
hideTooltip: function (pie, index) { |
|
d3.select("#" + pie.cssPrefix + "tooltip" + index) |
|
.style("opacity", function () { |
|
return 0; |
|
}); |
|
|
|
// move the tooltip offscreen. This ensures that when the user next mouseovers the segment the hidden |
|
// element won't interfere |
|
d3.select("#" + pie.cssPrefix + "tooltip" + barTooltip.currentTooltip) |
|
.attr("transform", function (d, i) { |
|
// klutzy, but it accounts for tooltip padding which could push it onscreen |
|
var x = pie.width + 1000; |
|
var y = pie.height + 1000; |
|
return "translate(" + x + "," + y + ")"; |
|
}); |
|
}, |
|
|
|
replacePlaceholders: function (pie, str, index, replacements) { |
|
var replacer = function () { |
|
return function (match) { |
|
var placeholder = arguments[1]; |
|
if (replacements.hasOwnProperty(placeholder)) { |
|
return replacements[arguments[1]]; |
|
} else { |
|
return arguments[0]; |
|
} |
|
}; |
|
}; |
|
return str.replace(/\{(\w+)\}/g, replacer(replacements)); |
|
} |
|
}; |
|
|