function baseChart()

in data_annotation_platform/app/static/js/makeChart.js [77:369]


function baseChart(
	selector,
	data,
	clickFunction,
	annotations,
	annotationFunction,
	divWidth,
	divHeight
) {
	if (divWidth === null || typeof divWidth === 'undefined')
		divWidth = 1200;
	if (divHeight === null || typeof divHeight === 'undefined')
		divHeight = 480;

	// preprocess the data
	data = preprocessData(data);

	var svg = d3.select(selector)
		.on("touchstart", noZoom)
		.on("touchmove", noZoom)
		.append("svg")
		.attr("width", divWidth)
		.attr("height", divHeight)
		.attr("viewBox", "-25 0 " + divWidth + " " + divHeight);

	var margin = {top: 20, right: 20, bottom: 50, left: 50};
	var width = +svg.attr("width") - margin.left - margin.right;
	var height = +svg.attr("height") - margin.top - margin.bottom;

	var [xAxis, yAxis, xScale, xScaleOrig, yScale, yScaleOrig, yDomainMin, yDomainMax] = scaleAndAxis(
		data,
		width,
		height);

	var lineObjects = [];
	for (let r=0; r<data.length; r++) {
		var lineObj = new d3.line()
			.x(function(d) { return xScale(d.X); })
			.y(function(d) { return yScale(d.Y); });
		lineObjects.push(lineObj);
	}

	var zoomX = d3.zoom()
    .scaleExtent([1, 100])
    .translateExtent([[0, 0], [width, height]])
    .extent([[0, 0], [width, height]])
	.wheelDelta(() => -d3.event.deltaY * 0.002)
    .on("zoom", zoomTransformX);

	var zoomY = d3.zoom()
		.scaleExtent([1, 100])
		.translateExtent([[0, 0], [width, height]])
		.extent([[0, 0], [width, height]])
		.wheelDelta(() => -d3.event.deltaY * 0.004)
		.on("zoom", zoomTransformY);

	var currentZoom = zoomX; // Default: X-axis zooming



	function zoomTransformX() {
		const transform = d3.event.transform;
		const mouseX = d3.mouse(svg.node())[0]; // Get mouse X position relative to the SVG
		const mouseDomainX = xScale.invert(mouseX); // Convert mouse X to domain value
	
		// Transform only the x-axis
		xScale.domain(transform.rescaleX(xScaleOrig).domain());
	
		// Adjust the domain to center around the mouse position
		const newMouseDomainX = xScale.invert(mouseX);
		const domainShift = mouseDomainX - newMouseDomainX;
		xScale.domain(xScale.domain().map(d => d + domainShift));
	
		for (let r = 0; r < data.length; r++) {
			svg.select(".line-" + r).attr("d", lineObjects[r]);
	
			// Transform the circles
			pointSets[r].data(data[r])
				.attr("cx", d => xScale(d.X))
				.attr("cy", d => yScale(d.Y));
		}
	
		// Transform annotation lines (if any)
		annoLines = gView.selectAll("line");
		annoLines._groups[0].forEach(l => {
			l.setAttribute("x1", xScale(l.getAttribute("cp_idx")));
			l.setAttribute("x2", xScale(l.getAttribute("cp_idx")));
		});
	
		svg.select(".axis--x").call(xAxis);
	}
	
	function zoomTransformY() {
		const transform = d3.event.transform;
		const mouseY = d3.mouse(svg.node())[1]; // Get mouse Y position relative to the SVG
		const mouseDomainY = yScale.invert(mouseY); // Convert mouse Y to domain value
	
		// Rescale the Y axis using yScaleOrig
		yScale.domain(transform.rescaleY(yScaleOrig).domain());
	
		// Adjust the domain to center around the mouse position
		const newMouseDomainY = yScale.invert(mouseY);
		const domainShift = mouseDomainY - newMouseDomainY;
		yScale.domain(yScale.domain().map(d => d + domainShift));
	
		for (let r = 0; r < data.length; r++) {
			svg.select(".line-" + r).attr("d", lineObjects[r]);
	
			// Transform the circles
			pointSets[r].data(data[r])
				.attr("cx", d => xScale(d.X))
				.attr("cy", d => yScale(d.Y));
		}
	
		// Transform the annotation lines (if any)
		annoLines = gView.selectAll("line");
		annoLines._groups[0].forEach(function(l) {
			l.setAttribute("y1", yScale(l.getAttribute("cp_idx")));
			l.setAttribute("y2", yScale(l.getAttribute("cp_idx")));
		});
	
		// Update Y axis
		svg.select(".axis--y").call(yAxis);
	}


	// Define drag behavior for panning
	var drag = d3.drag()
	.on("drag", function () {
		// Calculate the change in X and Y based on drag
		var dx = d3.event.dx * (xScale.domain()[1] - xScale.domain()[0]) / width;
		var dy = d3.event.dy * (yScale.domain()[1] - yScale.domain()[0]) / height;

		// Update X and Y domains
		xScale.domain([xScale.domain()[0] - dx, xScale.domain()[1] - dx]);
		yScale.domain([yScale.domain()[0] + dy, yScale.domain()[1] + dy]);

		// Update the axes
		svg.select(".axis--x").call(xAxis);
		svg.select(".axis--y").call(yAxis);

		// Update lines and points
		for (let r = 0; r < data.length; r++) {
			svg.select(".line-" + r).attr("d", lineObjects[r]);
			pointSets[r].data(data[r])
				.attr("cx", d => xScale(d.X))
				.attr("cy", d => yScale(d.Y));
		}

		// Update annotation lines (if any)
		annoLines = gView.selectAll("line");
		annoLines._groups[0].forEach(function (l) {
			l.setAttribute("x1", xScale(l.getAttribute("cp_idx")));
			l.setAttribute("x2", xScale(l.getAttribute("cp_idx")));
			l.setAttribute("y1", yScale(l.getAttribute("cp_idx")));
			l.setAttribute("y2", yScale(l.getAttribute("cp_idx")));
		});
	});
	












	var zero = xScale(0);

	// clip path
	svg.append("defs")
		.append("clipPath")
		.attr("id", "clip")
		.append("rect")
		.attr("width", width - 18)
		.attr("height", height)
		.attr("transform", "translate(" + zero + ",0)");

	// y axis
	svg.append("g")
		.attr("class", "axis axis--y")
		.attr("transform", "translate(" + zero + ",0)") // Use margin.left instead of zero
		.call(yAxis);

		// x axis
	svg.append("g")
		.attr("class", "axis axis--x")
		.attr("transform", "translate(0, " + height + ")")
		.call(xAxis);

	// x axis label
	svg.append("text")
		.attr("text-anchor", "middle")
		.attr("class", "axis-label")
		.attr("transform", "translate(" + (width - 20) + "," + (height + 50) + ")")
		.text("Time");

	// wrapper for zoom
	var gZoom = svg.append("g")
	.call(currentZoom)
	.call(drag);

	gZoom.call(currentZoom);


	// Add event listener for zoom toggle
	// document.getElementById("zoomToggle").addEventListener("change", function() {
	// 	if (this.checked) {
	// 	  // Y-axis zoom
	// 	  currentZoom = zoomY;
	// 	  document.getElementById("zoomLabel").innerText = "Change zoom mode (current mode is Y-Axis Zoom)";
	// 	} else {
	// 	  // X-axis zoom
	// 	  currentZoom = zoomX;
	// 	  document.getElementById("zoomLabel").innerText = "Change zoom mode (current mode is X-Axis Zoom)";
	// 	}
	  
	// 	// Apply the current zoom behavior
	// 	gZoom.call(currentZoom);
	//   });	  



	const zoomToggle = document.getElementById("zoomToggle");

	if (zoomToggle) {
	zoomToggle.addEventListener("change", function () {
		if (this.checked) {
		currentZoom = zoomY;
		document.getElementById("zoomLabel").innerText =
			"Change zoom mode (current mode is Y-Axis Zoom)";
		} else {
		currentZoom = zoomX;
		document.getElementById("zoomLabel").innerText =
			"Change zoom mode (current mode is X-Axis Zoom)";
		}
		gZoom.call(currentZoom);
	});
	} else {
	console.warn('zoomToggle element not found, skipping event listener.');
	}

	// rectangle for the graph area
	gZoom.append("rect")
		.attr("width", width)
		.attr("height", height);

	// view for the graph
	var gView = gZoom.append("g")
		.attr("class", "view");

	// add the line(s) to the view
	for (let r=0; r<data.length; r++) {
		gView.append("path")
			.datum(data[r])
			.attr("class", "line line-"+r)
			.attr("d", lineObjects[r]);
	}

	var pointSets = [];
	for (let r=0; r<data.length; r++) {
		var wrap = gView.append("g");
		var points = wrap.selectAll("circle")
			.data(data[r])
			.enter()
			.append("circle")
			.attr("cx", function(d) { return xScale(d.X); })
			.attr("cy", function(d) { return yScale(d.Y); })
			.attr("data_X", function(d) { return d.X; })
			.attr("data_Y", function(d) { return d.Y; })
			.attr("r", 5)
			.on("click", function(d, i) {
				d.element = this;
				return clickFunction(d, i);
			});
		pointSets.push(points);
	}

	// handle the annotations
	annotations.forEach(function(a) {
		for (i=0; i<points._groups[0].length; i++) {
			p = points._groups[0][i];
			if (p.getAttribute("data_X") != a.index)
				continue;
			var elem = d3.select(p);
			annotationFunction(a, elem, gView, xScale, yScale, yDomainMin, yDomainMax);
		}
	});
}