<title>Chandrakant Bharatkumar Thakkar</title>
<link rel="stylesheet" href="./jquery-ui.css">
<link rel="stylesheet" href="./styles.css">
<div id="charts" style="width:800px;">
<div class="line-with-slider" style="width:800px;height:500px">
<div class="slider-component">
<div class="slider" style="margin-left:10px;margin-right:10px;">
<div id="slider-range-max" />
<svg height="450" width="780">
<a href="https://stackoverflow.com/users/7430694/chandrakant-thakkar" style="position: absolute;top: 87%;left: 77%;" target="_blank">
<img src="https://stackoverflow.com/users/flair/7430694.png" width="208" height="58" alt="profile for Chandrakant Thakkar at Stack Overflow, Q&A for professional and enthusiast programmers" title="profile for Chandrakant Thakkar at Stack Overflow, Q&A for professional and enthusiast programmers">
<script src="./jquery-latest.min.js"></script>
<script src="./jquery-ui.js"></script>
<script src="./d3.v4.min.js"></script>
<script src="./constant.js"></script>
let selectedSliderIndex = 5;
let selectedCategory = chartData.category[selectedSliderIndex];
const granularity = 'daily';
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
const margin = { left: 10, right: 10, bottom: 50, top: 30 };
let selectedDimensionValue = Object.entries(chartData.data)[0][0].toString().toLowerCase();
const ticksCalculationData = getTicksCalculation(chartData.category[0], chartData.category[chartData.category.length - 1], granularityToLabelMapping[granularity].granularityLabelForSlider, "0");
const xScale = d3.scalePoint()
.domain(chartData.category)
.range([0, width - (margin.left + margin.right)]);
const prepareChartData = getPrepareChartData(chartData.data, chartData.category, chartData.measureFieldName);
const { minValue, maxValue } = calculateMinMaxValue(prepareChartData.minMax[0], prepareChartData.minMax[1], false, ticksSize);
const yScale = d3.scaleLinear().domain([minValue, maxValue])
.range([(height) - (margin.bottom + margin.top), 0]);
const ticksValues = getTicksBalues();
function getTicksBalues() {
let binSize = (maxValue - minValue) / (ticksSize - 1);
binSize = Math.abs(binSize);
let currentValue = minValue;
while (currentValue <= maxValue) {
tickValues.push(currentValue);
currentValue = currentValue + binSize;
const tickFormat = value => {
return d3.timeFormat("%d-%b,%y")(new Date(value));
const tickYFormat = value => {
ticksCalculationData.distinctTicks.forEach((data, index) => {
sliderTicks = sliderTicks + '<div key={"ticks_" + index} class="ticks-text" style= "margin-left:' + parseFloat(xScale(chartData.category[index])).toFixed(2) + 'px;" data={' + xScale(chartData.category[index]) + '}>';
sliderTicks = sliderTicks + (Array.isArray(data.displayName) == true ? data.displayName.map((d, indexj) => '<div>' + d + '</div>') : [data.displayName]).join('');
sliderTicks = sliderTicks + '</div>';
sliderTicks = sliderTicks + '<div class="left-rect-div" style="width:' + ((xScale(chartData.category[selectedSliderIndex]) + margin.left) + "px") + ';left:' + ((-margin.left) + "px") + ';"}></div>';
d3.select(".ticks").html("");
d3.select(".ticks").html(sliderTicks);
var yAxis = d3.axisLeft(yScale).ticks(ticksSize).tickFormat(d => tickYFormat(d))
.attr("transform", "translate(" + margin.left + "," + margin.top + ")").call(yAxis);
drawChart(selectedDimensionValue, selectedSliderIndex);
function drawChart(selectedDimensionValue, selectedSliderIndex) {
const selectedCategoryData = prepareChartData.resultData[selectedCategory];
const selectedLineData = [];
Object.keys(selectedCategoryData).forEach(seriesValue => {
const seriesWiseData = [];
selectedCategoryData[seriesValue].forEach((data, index) => {
dataObj["x"] = xScale(chartData.category[index]);
dataObj["y"] = yScale(data);
dataObj["seriesValue"] = seriesValue;
seriesWiseData.push(dataObj);
if (seriesValue.toString().toLowerCase() == selectedDimensionValue) {
selectedLineData.push({ dimension: seriesValue, data: seriesWiseData });
linesData.push({ dimension: seriesValue, data: seriesWiseData });
d3.select(".line-container").remove();
.attr("class", "line-container")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
const curveType = "curveCatmullRom";
const curve = curveType && typeof d3[curveType] === "function" ? d3[curveType] : d3["curveLinear"];
.defined(d => (typeof d == "object" ? d : !isNaN(d)))
return typeof d == "object" && d.x != null ? d.x : xScale(i);
return typeof d == "object" && d.y != null ? d.y : yScale(d);
d3.select(".line-container").html("");
ticksValues.forEach(dataTicks => {
.select(".line-container")
.attr("class", "line-zero-horizontal")
.attr("y1", yScale(dataTicks))
.attr("y2", yScale(dataTicks))
.attr("x1", xScale(chartData.category[0]))
.attr("x2", xScale(chartData.category[chartData.category.length - 1]));
.select(".line-container")
.attr("class", "vertical-draggable-line")
.attr("y2", height - (margin.bottom + margin.top))
.attr("x1", xScale(selectedCategory))
.attr("x2", xScale(selectedCategory));
d3.select("svg").select(".y-axis")
.attr("transform", `translate(${margin.left + xScale(selectedCategory)},${margin.top})`);
//CBT:check y axis ticks exceed left border start
d3.select("svg").select(".y-axis").selectAll('.tick').selectAll('text').nodes().forEach(node => {
if (node.getBoundingClientRect().width > maxSize) {
maxSize = node.getBoundingClientRect().width;
if (maxSize - xScale(selectedCategory) > 0) {
d3.select("svg").select(".y-axis")
.attr("transform", `translate(${margin.left + xScale(selectedCategory) + maxSize + 10},${margin.top})`);
//CBT:check y axis ticks exceed left border end
.select(".line-container")
const className = "dimension";
linesData.forEach(dataOfLine => {
.attr("class", `line ${className} ${"line_" + dataOfLine.dimension}`)
.attr("data", JSON.stringify({ dimension: dataOfLine.dimension }))
.attr("stroke", `${color ? color : "#000"}`)
.on("click", (d, index) => {
const data = JSON.parse(d3.event.target.getAttribute("data"));
selectedDimensionValue = data.dimension.toString().toLowerCase();
drawChart(selectedDimensionValue, selectedSliderIndex);
if (selectedLineData.length == 1) {
.datum(selectedLineData[0].data)
.attr("class", `line ${className} selected`)
const lastPoint = selectedLineData[0].data[selectedLineData[0].data.length - 1];
.attr("class", 'circle selected')
.attr("cx", (lastPoint.x))
.attr("cy", (lastPoint.y))
.attr("transform", `translate(${lastPoint.x},${lastPoint.y})`);
const text = textG.append("text").style("text-anchor", "end");
const title1 = text.append("tspan")
.text(selectedDimensionValue)
let lastValue = selectedLineData[0].data[selectedLineData[0].data.length - 1].value;
lastValue = +parseFloat(lastValue).toFixed(0);
lastValue = lastValue > 0 ? `${lastValue} percent more` : `${Math.abs(lastValue)} percent less`;
const title2 = text.append("tspan")
const title1Height = title1.node().getBoundingClientRect().height;
title2.attr("y", (title1Height));
textG.node().setAttribute("transform", `translate(${lastPoint.x},${lastPoint.y - text.node().getBBox().height})`);
const widthForLeftRect = xScale(selectedCategory);
const widthForRightRect = (width - (margin.left + margin.right) - (widthForLeftRect + 2));
.select(".line-container")
.attr("class", "left-side-rect")
.attr("width", widthForLeftRect)
.attr("height", height - (margin.bottom + margin.top));
.select(".line-container")
.attr("class", "right-side-rect")
.attr("x", widthForLeftRect + 2)
.attr("width", widthForRightRect)
.attr("height", height - (margin.bottom + margin.top));
.size([widthForLeftRect, (height - (margin.top + margin.bottom))])([...linesData[0].data, ...linesData[1].data, ...linesData[2].data]);
var voronoiDiagramRightRect = d3
.size([widthForRightRect, (height - (margin.top + margin.bottom))])([...linesData[0].data, ...linesData[1].data, ...linesData[2].data]);
var voronoiRadius = widthForLeftRect;
var voronoiRadiusRightRect = widthForLeftRect;
d3.selectAll(".left-side-rect").on("mousemove", function (e) {
var [mx, my] = d3.mouse(this);
var site = voronoiDiagram.find(mx, my, voronoiRadius);
d3.selectAll("." + className).classed('on-hover', false);
let classes = d3.select(".line_" + site.data.seriesValue).attr("class");
d3.select(".line_" + site.data.seriesValue).attr("class", classes + " on-hover")
d3.selectAll(".right-side-rect").on("mousemove", function (e) {
var [mx, my] = d3.mouse(this);
var site = voronoiDiagramRightRect.find(mx, my, voronoiRadiusRightRect);
d3.selectAll("." + className).classed('on-hover', false);
let classes = d3.select(".line_" + site.data.seriesValue).attr("class");
d3.select(".line_" + site.data.seriesValue).attr("class", classes + " on-hover")
d3.selectAll(".left-side-rect")
.on("click", function (e) {
var [mx, my] = d3.mouse(this);
var site = voronoiDiagram.find(mx, my, voronoiRadius);
console.log("Data:", site.data.seriesValue);
selectedDimensionValue = site.data.seriesValue.toString().toLowerCase();
drawChart(selectedDimensionValue, selectedSliderIndex);
d3.selectAll(".right-side-rect")
.on("click", function (e) {
var [mx, my] = d3.mouse(this);
var site = voronoiDiagramRightRect.find(mx, my, voronoiRadiusRightRect);
console.log("Right Data:", site.data.seriesValue);
selectedDimensionValue = site.data.seriesValue.toString().toLowerCase();
drawChart(selectedDimensionValue, selectedSliderIndex);
.selectAll(".left-side-rect")
.on("mouseout", function (e) {
d3.selectAll("." + className).classed('on-hover', false);
.selectAll(".right-side-rect")
.on("mouseout", function (e) {
d3.selectAll("." + className).classed('on-hover', false);
$("#slider-range-max").slider({
max: chartData.category.length - 1,
value: selectedSliderIndex,
slide: function (event, ui) {
console.log("Data", ui.value);
d3.select(".left-rect-div").style("width", (parseFloat(xScale(chartData.category[ui.value]) + margin.left).toFixed(2) + "px"));
selectedSliderIndex = ui.value;
selectedCategory = chartData.category[selectedSliderIndex];
drawChart(selectedDimensionValue, selectedSliderIndex);