# Source: spectrumgraph.js

    /** maximum number of peaks and envelopes points to be renderedon to graph per each bin(range) */
    const circlesPerRange = 200;
    const peaksPerRange = 200;
    /**
     * @function SpectrumGraph
     * @description Function draws the graph, binds zoom and drag function to the graph
     * @param {String} svgId - SVG id on which the graph needed to be drawn
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     * @param {Array} peakData - contains peakList and envelope list
     * @param {Array} ionData - Contains data with mass and ACID name to plot on the graph
     */
    SpectrumGraph = function(svgId,spectrumParameters,peakData, ionData){
    	this.svg = d3.select("body").select(svgId);
      	this.id = svgId;
      	this.para = spectrumParameters;
    	this.data = peakData;
    	this.ionData = ionData;
    	let graph = this;
    	let tempid = svgId.split("#")[1];
    	this.redraw = function(mono_mz,graphFeatures){
    		this.para = compSpectrumParameters(this.data.peak_list, this.data.envelope_list, mono_mz);
    		this.para.graphFeatures = graphFeatures;
    		spectrumParameters = drawSpectrum(this.id, this.para, this.data,this.ionData);
    		//Copying as a new variable than referencing. Referencing will change the properties of parent if child properties are changes
    		if (typeof correspondingSpecParams_g !== "undefined") {
    			correspondingSpecParams_g[tempid] = jQuery.extend(true, {}, spectrumParameters);
    		}
    	}

    	this.zoomed = function () {
    		let transform = d3.event.transform;
    		let distance = transform.x - spectrumParameters.specX;
    		let ratio = transform.k / spectrumParameters.specScale;
    		spectrumParameters.specX = transform.x;
    		spectrumParameters.specScale = transform.k;
    		let mousePos = d3.mouse(this);
    		if(ratio == 1) {
    			spectrumParameters.drag(distance);
    		}
    		else{
    			spectrumParameters.zoom(mousePos[0], mousePos[1], ratio);
    		}
    		spectrumParameters = drawSpectrum(svgId, spectrumParameters, peakData, ionData);
    		//Copying as a new variable than referencing. Referencing will change the properties of parent if child properties are changes
    		if (typeof correspondingSpecParams_g !== "undefined") {
    			correspondingSpecParams_g[tempid] = jQuery.extend(true, {}, spectrumParameters);
    		}
    	}

    	this.zoom = d3.zoom()
    		.on("zoom", this.zoomed);
    	this.svg.attr("viewBox", "0 0 "+ spectrumParameters.graphFeatures.svgWidth+" "+ spectrumParameters.graphFeatures.svgHeight)
    					.attr("width", "100%")
    					.attr("height", "100%")
    					.call(this.zoom);
    	this.svg.call(this.zoom.transform, d3.zoomIdentity);

    	spectrumParameters = drawSpectrum(svgId, spectrumParameters, peakData, ionData);
    	//Copying as a new variable than referencing. Referencing will change the properties of parent if child properties are changes
    	if (typeof correspondingSpecParams_g !== "undefined") {
    		correspondingSpecParams_g[tempid] = jQuery.extend(true, {}, spectrumParameters);
    	}
    	return graph;
    }
    /**
     * @function drawTicks
     * @description Function that draws ticks on x-axis and y-axis
     * @param{HTMLBaseElement} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    drawTicks = function(svg,spectrumParameters){
    	// Creating a group under svg node with id 'ticks' under which ticks are drawn
    	this.addXTicks = svg.append("g").attr("id","ticks");
    	for(let i=0; i <= spectrumParameters.xTicks ; i++)
    	{
    		// Get the default tick width and calculate the actual tick width position based on the current minMz value on the xaxis
    		let tickWidth = spectrumParameters.getTickWidth();
    		if(tickWidth < 1 && tickWidth != 0)
    		{
    			tickWidth = (i*tickWidth + spectrumParameters.minMz) - parseFloat((i*tickWidth + spectrumParameters.minMz)%tickWidth) ;
    		}
    		else if(tickWidth != 0)
    		{
    			tickWidth = i*tickWidth + spectrumParameters.minMz - (i*tickWidth + spectrumParameters.minMz)%tickWidth ;
    		}
    		// get the x position of the tick
    		x = spectrumParameters.getPeakXPos(tickWidth);
    		// Below condition helps the ticks to be to the right of the y - axis
    		if(x >= spectrumParameters.padding.left &&
    				x <= (spectrumParameters.svgWidth - spectrumParameters.padding.right))
    		{
    			this.addXTicks.append("line")
    							.attr("x1",x)
    							.attr("y1",spectrumParameters.svgHeight -spectrumParameters.padding.bottom)
    							.attr("x2",x)
    							.attr("y2",spectrumParameters.svgHeight -spectrumParameters.padding.bottom + spectrumParameters.ticklength)
    							.attr("stroke","black")
    							.attr("stroke-width","1")
    		}
    	}
    	this.addYTicks = svg.append("g").attr("id","ticks")
    									.attr("class","ticks");
    	for(let i=0; i <= spectrumParameters.yTicks ; i++)
    	{
    		// Get the default tick height and calculate the actual tick height position
    		let tickHeight = spectrumParameters.getTickHeight();
    		tickHeight = i*tickHeight * spectrumParameters.dataMaxInte /100;
    		tickHeight = parseFloat(spectrumParameters.getPeakYPos(tickHeight)) ;
    		let y =  tickHeight;
    		if(!isNaN(y) && y >= spectrumParameters.padding.head)//y >= spectrumParameters.padding.head helps the ticks to be in the length of Y axis
    		{
    			this.addYTicks.append("line")
    						.attr("x1",spectrumParameters.padding.left)
    						.attr("y1",y)
    						.attr("x2",spectrumParameters.padding.left - spectrumParameters.ticklength)
    						.attr("y2",y)
    						.attr("stroke","black")
    						.attr("stroke-width","1")
    		}
    	}
    }
    /**
     * @function drawAxis
     * @description Function to draw x-axis and y-axis
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    drawAxis = function(svg,spectrumParameters){
    	//Draw x-axis
    	this.xAxis = svg.append("g").attr("id", "xaxis").append("line")
    					.attr("x1",spectrumParameters.padding.left)
    					.attr("y1",spectrumParameters.svgHeight -spectrumParameters.padding.bottom)
    					.attr("x2",spectrumParameters.specWidth+spectrumParameters.padding.left)
    					.attr("y2",spectrumParameters.svgHeight -spectrumParameters.padding.bottom)
    					.attr("stroke","black")
    					.attr("stroke-width","2")
    	// Draw y-axis
    	this.yAxis = svg.append("g").attr("id", "yaxis").append("line")
    					.attr("x1",spectrumParameters.padding.left)
    					.attr("y1",spectrumParameters.padding.head)
    					.attr("x2",spectrumParameters.padding.left)
    					.attr("y2",spectrumParameters.svgHeight -spectrumParameters.padding.bottom)
    					.attr("stroke","black")
    					.attr("stroke-width","2")
    }
    /**
     * @function addDatatoAxis
     * @description Function to add tick numbers on x and y axis
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    addDatatoAxis = function(svg,spectrumParameters){
    	let maxMz = spectrumParameters.maxMz;
    	let minMz = spectrumParameters.minMz ;
    	// Creating a group wih id 'axisPoints' under which the code to add tick numbers is added
    	this.xAxisData = svg.append("g")
    						.attr("id", "xAxisPoints");

    	for(let i = 0 ; i <=spectrumParameters.xTicks ; i++)
    	{
    		// Get the default tick width and calculate the actual tick width position based on the current minMz value on the xaxis
    		let tickWidth = spectrumParameters.getTickWidth();
    		if(tickWidth < 1 && tickWidth != 0)
    		{
    			tickWidth = i*tickWidth + spectrumParameters.minMz - parseFloat((i*tickWidth + spectrumParameters.minMz)%tickWidth) ;
    		}
    		else if(tickWidth != 0)
    		{
    			tickWidth = i*tickWidth + spectrumParameters.minMz - (i*tickWidth + spectrumParameters.minMz)%tickWidth ;
    		}
    		x = spectrumParameters.getPeakXPos(tickWidth);
    		let l_tickWidth = tickWidth;
    		if(x >= spectrumParameters.padding.left &&
    				x <= (spectrumParameters.svgWidth - spectrumParameters.padding.right))
    		{
    			this.xAxisData.append("text").attr("id","xtext").attr("x",x)
    						.attr("y",(spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.padding.bottom + 20))// Dividing with 1.6 to set the position of the numbers under the ticks appropriately
    						.attr("text-anchor","middle")
    						.text(function(){
    							// conditions to show more decimal values as we zoom in further and limit decimals when zoomed back
    							if(maxMz - minMz <=0.0001) return parseFloat(l_tickWidth).toFixed(6);
    							else if(maxMz - minMz <=0.001) return parseFloat(l_tickWidth).toFixed(5);
    							else if(maxMz - minMz <=0.01) return parseFloat(l_tickWidth).toFixed(4);
    							else if(maxMz - minMz <=0.1) return parseFloat(l_tickWidth).toFixed(3);
    							else if(maxMz - minMz <=1) return parseFloat(l_tickWidth).toFixed(2);
    							else if(maxMz - minMz <= 3) return parseFloat(l_tickWidth).toFixed(2)
    							else if(maxMz - minMz <= 5) return parseFloat(l_tickWidth).toFixed(1)
    							return parseInt(l_tickWidth)
    						})
    						.style("font-size","14px")
    		}
    	}
    	// Creating a group wih id 'axisPoints' under which the code to add tick numbers is added
    	this.yAxisData = svg.append("g")
    							.attr("id", "yAxisPoints");
    	for(let i = 0 ; i <= spectrumParameters.yTicks ; i++)
    	{
    		let tickHeight = 0;
    		// Get the default tick height and calculate the actual tick height position
    		tickHeight = spectrumParameters.getTickHeight();
    		let data = i*tickHeight ;
    		if(data <= 1 && data != 0) data = data.toFixed(1);
    		tickHeight = i*tickHeight * spectrumParameters.dataMaxInte /100;
    		tickHeight = parseFloat(spectrumParameters.getPeakYPos(tickHeight)) ;

    		let y =  tickHeight;
    		if(!isNaN(y) && y >= spectrumParameters.padding.head)
    		{
    			this.yAxisData.append("text").attr("class","ytext").attr("x",spectrumParameters.padding.left - spectrumParameters.ticklength)
    						.attr("y",y)
    						.attr("text-anchor","end")
    						.attr("alignment-baseline","middle")
    						.text(data + "%")
    						.style("font-size","14px")
    		}
    	}
    }
    /**
     * @function addBackGround
     * @description Function to add backGround color to the spectrum graph for MS1 spectrum at precursor mz
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    addBackGround = function(svg,spectrumParameters){
    	let svg_temp = svg.append("g")
    					.attr("id", "svg_bgColor");

    	if(!((spectrumParameters.graphFeatures.bgMinMz < spectrumParameters.minMz && spectrumParameters.graphFeatures.bgMaxMz < spectrumParameters.minMz)
    		|| (spectrumParameters.graphFeatures.bgMinMz > spectrumParameters.maxMz && spectrumParameters.graphFeatures.bgMaxMz > spectrumParameters.maxMz)))
    	{
    		let x = spectrumParameters.getPeakXPos(spectrumParameters.graphFeatures.bgMinMz);
    		let x2 = spectrumParameters.getPeakXPos(spectrumParameters.graphFeatures.bgMaxMz);
    		if(spectrumParameters.graphFeatures.bgMinMz < spectrumParameters.minMz)
    		{
    			x = spectrumParameters.getPeakXPos(spectrumParameters.minMz);
    		}
    		if(spectrumParameters.graphFeatures.bgMaxMz > spectrumParameters.maxMz)
    		{
    			x2 = spectrumParameters.getPeakXPos(spectrumParameters.maxMz);
    		}
    		svg_temp.append("rect")
    			.attr("x", x)
    			.attr("y", spectrumParameters.padding.head)
    			.attr("width", x2-x)
    			.attr("height", function(){
    				let y1 = spectrumParameters.svgHeight - spectrumParameters.padding.bottom;
    				let y2 = spectrumParameters.padding.head;
    				return y1-y2;
    			})
    			.style("fill", spectrumParameters.graphFeatures.bgColor)
    			.style("fill-opacity", ".4")
    			.style("stroke-width", "1.5px");
    	}
    }
    /**
     * @function drawPeaks
     * @description Function to draw peak lines on the graph
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     * @param {Array} peakdata - Contians both peak list and envelopelist
     */
    drawPeaks = function(svg,spectrumParameters,peakdata){
    	let peaks = svg.append("g")
        				.attr("id", "peaks");
    	  var len = peakdata.peak_list.length;
    	  // limits provide current count of number of peaks drawn on graph per bin(range)
    	  // so that we can limit tha peak count to peaksPerRange count
    	  let limits=[0,0,0,0,0,0,0,0];
    	  for(let i =0;i<len;i++)
    	  {
    		let peak = peakdata.peak_list[i];
    		let inLimit = false;
    		for(let j=0;j<(spectrumParameters.ranges.length-1);j++)
    		{
    			if(peak.mz > spectrumParameters.ranges[j] && peak.mz < spectrumParameters.ranges[j+1])
    			{
    				limits[j] = limits[j]+1;
    				if(limits[j] <= peaksPerRange) inLimit = true;
    				break;
    			}
    		}
    		if(peak.mz >= spectrumParameters.minMz && peak.mz <= spectrumParameters.maxMz && inLimit == true)
    		{
    			peaks.append("line")
    		  .attr("x1",function(d,i){
    			  return spectrumParameters.getPeakXPos(peak.mz);
    			  })
    		  .attr("y1",function(d,i){
    				let y = spectrumParameters.getPeakYPos(peak.intensity);
    				if(y<=spectrumParameters.padding.head) return spectrumParameters.padding.head ;
    				else return y ;
    			  })
    		  .attr("x2",function(d,i){
    			  	return spectrumParameters.getPeakXPos(peak.mz);
    			  })
    		  .attr("y2",spectrumParameters.svgHeight - spectrumParameters.padding.bottom )
    		  .attr("stroke","black")
    		  .attr("stroke-width","2")
    		  .on("mouseover",function(d,i){
    					onMouseOverPeak(this,peak,spectrumParameters);
    				})
    			.on("mouseout",function(d,i){
    				onPeakMouseOut(this);
    			});
    		}
    	  }
    }
    /**
     * @function addCircles
     * @description Function to add circles for the envelope data
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     * @param {Array} peakdata - Contians both peak list and envelopelist
     */
    addCircles = function(svg,spectrumParameters,peakData){
    	let circles = svg.append("g").attr("id", "circles");
    	let minPercentage = 0.0;
    	let maxIntensity = spectrumParameters.dataMaxInte ;
    	// limits provide current count of number of peaks drawn on graph per bin(range)
    	// so that we can limit tha peak count to circlesPerRange count
    	let limits=[0,0,0,0,0,0,0,0];
    	peakData.envelope_list.forEach(function(envelope_list){
    		envelope_list.env_peaks.forEach(function(env_peaks){
    			//Show only envelopes with minimum of 0.5%
    			let percentInte = env_peaks.intensity/maxIntensity * 100 ;
    			let inLimit = false;
    			for(let i=0;i<(spectrumParameters.ranges.length-1);i++)
    			{
    				if(env_peaks.mz > spectrumParameters.ranges[i] && env_peaks.mz < spectrumParameters.ranges[i+1])
    				{
    					limits[i] = limits[i]+1;
    					if(limits[i] <= circlesPerRange) inLimit = true;
    					break;
    				}
    			}
    			// Condition keeps the circles to the right of the y axis till the end of the x-axis
    			if(env_peaks.mz > spectrumParameters.minMz && env_peaks.mz <= spectrumParameters.maxMz && percentInte >= minPercentage && inLimit == true)
    			{
    				circles.append("circle")
    				.attr("id","circles")
    				.attr("cx",function(d,i){
    					return spectrumParameters.getPeakXPos(env_peaks.mz);
    				})
    				.attr("cy",function(d,i){
    					let cy = spectrumParameters.getPeakYPos(env_peaks.intensity);
    					if(cy < spectrumParameters.padding.head) return spectrumParameters.padding.head ;
    					else return cy ;
    				})
    				.attr("r",function(d,i){
    					return spectrumParameters.getCircleSize();
    				})
    				.style("fill","white")
    				.style("opacity", "0.6")
    				.style("stroke",envelope_list.color)
    				.style("stroke-width","2")
    				.on("mouseover",function(d,i){
    					onMouseOverCircle(this,envelope_list,spectrumParameters);
    				})
    				.on("mouseout",function(d,i){
    					onCircleMouseOut(this);
    				});
    			}
    		})
    	})
    }
    /**
     * @function drawIons
     * @description Function to add IONS at the top of the peaks for each cluster of envelopes
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     * @param {Array} ionData - Contians Ion list to display on the graph
     */
    drawIons = function(svg,spectrumParameters,ionData){
    	let ions = svg.append("g").attr("id", "graph_ions");
    	// Get the default tick width and calculate the actual tick width position based on the current minMz value on the xaxis
    	let tickWidth = spectrumParameters.getTickWidth();
    	ionData.forEach((element)=>{
    		if(tickWidth <= spectrumParameters.graphFeatures.tickWidthThreshholdval)
    		{
    			element.forEach((innerElement)=>{
    				placeIonOnGraph_innerFunc(innerElement);
    			})
    		}else{
    			let innerElement = element[0];
    			placeIonOnGraph_innerFunc(innerElement);
    		}
    	})

    	// Inner function to draw ions on the graph at respective position
    	function placeIonOnGraph_innerFunc(innerElement){
    		if(innerElement.mz > spectrumParameters.minMz && innerElement.mz <= spectrumParameters.maxMz)
    		{
    			ions.append("text")
    			.attr("id","graph_matched_ions")
    			.attr("x",(spectrumParameters.getPeakXPos((innerElement.mz))-spectrumParameters.graphFeatures.adjustableIonPosition))
    			.attr("y",function(d,i){
    				let y = spectrumParameters.getPeakYPos(innerElement.intensity + (0.1*innerElement.intensity));// Adding 10% to get the Ions on the max Intensity Peak
    				y = y - spectrumParameters.graphFeatures.fixedHeightOfIonAboveThePeak;
    				if(y <= spectrumParameters.padding.head) return spectrumParameters.padding.head ;
    				else return y ;
    				})
    			.style("fill","black")
    			.style("opacity", "0.6")
    			.style("stroke-width","2")
    			.text(innerElement.ion);
    		}
    	}
    }
    /**
     * @function drawSequence
     * @description Draw Sequence on spectrum graph
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    drawSequence = function(svg,spectrumParameters){
    	let seqSvg = svg.append("g").attr("id", "graph_sequence");
    	let sequenceData = spectrumParameters.graphFeatures.prefixSequenceData;
    	let x,y,text;
    	// Draw | at 0 for prefix mass list
    	x = spectrumParameters.getPeakXPos((0));
    	y = spectrumParameters.padding.head-35;
    	text = "|";
    	// Add "|" At the start of the prefix sequence
    	if(0 >= spectrumParameters.minMz && 0 <= spectrumParameters.maxMz)
    	{
    		drawAcids_innerFunc(seqSvg,x,y,text);
    	}
    	sequenceData.forEach(function(element,index,prefixSequenceData){
    		if(element.mass >= spectrumParameters.minMz && element.mass <= spectrumParameters.maxMz)
    		{
    			let x1 = spectrumParameters.getPeakXPos(0);
    			if(index != 0) x1 = spectrumParameters.getPeakXPos(prefixSequenceData[index-1].mass);
    			x2 = spectrumParameters.getPeakXPos((element.mass));
    			x = (x1+x2)/2;
    			y = spectrumParameters.padding.head-35;
    			drawAcids_innerFunc(seqSvg,x,y,element.acid);
    			drawAcids_innerFunc(seqSvg,x2,y,"|");
    		}
    	})

    	sequenceData = spectrumParameters.graphFeatures.suffixSequeceData;
    	// Draw | at water mass for suffix mass list
    	let massOfWater = 18.010564683704;
    	x = spectrumParameters.getPeakXPos(massOfWater);// Mass of water=18.010564683704
    	y = spectrumParameters.padding.head-15;
    	text = "|";
    	if(massOfWater >= spectrumParameters.minMz && massOfWater <= spectrumParameters.maxMz)
    	{
    		drawAcids_innerFunc(seqSvg,x,y,text);
    	}
    	sequenceData.forEach(function(element,index,suffixSequeceData){
    		if(element.mass >= spectrumParameters.minMz && element.mass <= spectrumParameters.maxMz)
    		{
    			let x1 = spectrumParameters.getPeakXPos(18.010564683704);// Mass of water=18.010564683704
    			if(index != 0) x1 = spectrumParameters.getPeakXPos(suffixSequeceData[index-1].mass);
    			x2 = spectrumParameters.getPeakXPos((element.mass));
    			x = (x1+x2)/2;
    			y = spectrumParameters.padding.head-15;
    			drawAcids_innerFunc(seqSvg,x,y,element.acid);
    			drawAcids_innerFunc(seqSvg,x2,y,"|");
    		}
    	})
    	function drawAcids_innerFunc(svgId,x,y,text){
    		svgId.append("text")
    			.attr("id","")
    			.attr("x",x)
    			.attr("y",y)
    			.style("fill","black")
    			.style("opacity", "0.6")
    			.style("stroke-width","2")
    			.text(text);
    	}
    }
    /**
     * @function addErrorPlot
     * @description Add Error Plot to the MonoMass Spectrum
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    addErrorPlot = function(svg, spectrumParameters){
    	//Draw x-axis
    	this.xAxis = svg.append("g").attr("id", "xaxis_errorplot").append("line")
    					.attr("x1",spectrumParameters.graphFeatures.errorplot_padding.left)
    					.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot/2 - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    					.attr("x2",spectrumParameters.graphFeatures.specWidth + spectrumParameters.graphFeatures.errorplot_padding.left)
    					.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot/2 - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    					.attr("stroke","black")
    					.style("stroke-dasharray", ("5, 3"))
    					.attr("stroke-width","1.5")
    	// Draw y-axis
    	this.yAxis = svg.append("g").attr("id", "yaxis_errorplot").append("line")
    					.attr("x1",spectrumParameters.graphFeatures.errorplot_padding.left)
    					.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    					.attr("x2",spectrumParameters.graphFeatures.errorplot_padding.left)
    					.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    					.attr("stroke","black")
    					.attr("stroke-width","1")
    }
    /**
     * @function addErrorBlock
     * @description Draw Error plot
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    addErrorBlock = function(svg, spectrumParameters){
    	let rectBlock = svg.append("g").attr("id", "rect_errorplot");
    	rectBlock.append("line")
    			.attr("x1",spectrumParameters.graphFeatures.errorplot_padding.left)
    			.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    			.attr("x2",spectrumParameters.graphFeatures.specWidth + spectrumParameters.graphFeatures.errorplot_padding.left)
    			.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    			.attr("stroke","black")
    			.attr("stroke-width","1")
    	rectBlock.append("line")
    			.attr("x1",spectrumParameters.graphFeatures.errorplot_padding.left)
    			.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    			.attr("x2",spectrumParameters.graphFeatures.specWidth + spectrumParameters.graphFeatures.errorplot_padding.left)
    			.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    			.attr("stroke","black")
    			.attr("stroke-width","1")
    	rectBlock.append("line")
    			.attr("x1",spectrumParameters.graphFeatures.svgWidth - spectrumParameters.graphFeatures.errorplot_padding.right)
    			.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    			.attr("x2",spectrumParameters.graphFeatures.svgWidth - spectrumParameters.graphFeatures.errorplot_padding.right)
    			.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot - spectrumParameters.graphFeatures.errorplot_padding.bottom)
    			.attr("stroke","black")
    			.attr("stroke-width","1")
    }
    /**
     * @function drawErrorYticks
     * @description Draw Error plot y ticks
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    drawErrorYticks = function(svg, spectrumParameters){

    	let addYTicks = svg.append("g").attr("id","yErrorTicks")
    									.attr("class","yErrorTicks");
    	let tempTick = spectrumParameters.graphFeatures.errorThreshHoldVal/spectrumParameters.errorYticks;
    	//Draw tick at 0th position
    	let y = spectrumParameters.getErrorYPos(0);
    	inner_drawYTicks(y);
    	inner_addErrorYTickValues(0,y);
    	// Draw positive ticks above error x axis
    	for(let i=1;i<=spectrumParameters.errorYticks;i++)
    	{
    		y = spectrumParameters.getErrorYPos(i*tempTick);
    		inner_drawYTicks(y);
    		inner_addErrorYTickValues(i*tempTick,y);
    	}
    	//Draw negative ticks below error x axis
    	for(let i=1;i<=spectrumParameters.errorYticks;i++)
    	{
    		y = spectrumParameters.getErrorYPos(-(i*tempTick));
    		inner_drawYTicks(y);
    		inner_addErrorYTickValues(-(i*tempTick),y);
    	}
    	function inner_drawYTicks(y){
    		if(!isNaN(y) && y >= spectrumParameters.padding.head)//y >= spectrumParameters.padding.head helps the ticks to be in the length of Y axis
    		{
    			addYTicks.append("line")
    						.attr("x1",spectrumParameters.padding.left)
    						.attr("y1",y)
    						.attr("x2",spectrumParameters.padding.left - spectrumParameters.ticklength)
    						.attr("y2",y)
    						.attr("stroke","black")
    						.attr("stroke-width","1")
    		}
    	}
    	function inner_addErrorYTickValues(data,y){
    		if(!isNaN(y) && y >= spectrumParameters.padding.head)
    		{
    			addYTicks.append("text").attr("class","ytext").attr("x",spectrumParameters.padding.left - spectrumParameters.ticklength)
    						.attr("y",y)
    						.attr("text-anchor","end")
    						.attr("alignment-baseline","middle")
    						.text(data)
    						.style("font-size","14px")
    		}
    	}
    }
    /**
     * @function drawErrorPoints
     * @description Draw Error points on the error graph
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    drawErrorPoints = function(svg, spectrumParameters){
    	let circles = svg.append("g").attr("id", "error_circles");
    	spectrumParameters.graphFeatures.errorListData.forEach((element)=>{
    		if(parseFloat(element.theoretical_mass) > spectrumParameters.minMz && parseFloat(element.theoretical_mass) <= spectrumParameters.maxMz){
    			circles.append("circle")
    			.attr("class","error_circles")
    			.attr("cx",function(d,i){
    				return spectrumParameters.getPeakXPos(parseFloat(element.theoretical_mass));
    			})
    			.attr("cy",function(d,i){
    				let cy = spectrumParameters.getErrorYPos(parseFloat(element.mass_error));
    				if(cy < spectrumParameters.padding.head) return spectrumParameters.padding.head ;
    				else return cy ;
    			})
    			.attr("r",function(d,i){
    				return 3;
    			})
    			.style("fill","black")
    			.style("opacity", "1")
    			//.style("stroke",envelope_list.color)
    			.style("stroke-width","2");
    		}
    	})
    }
    /**
     * @function addLabels
     * @description Function to add labels on x and y axis
     * @param {Node} svg -  is a html node on which the graph is being ploted
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    addLabels = function(svg, spectrumParameters){

    	svg.append("text").attr("id","label")
    						// -5 is added simply as buffer to place m/z on top of error rect plot
    						.attr("transform","translate(" + (spectrumParameters.svgWidth/2) + "," + (spectrumParameters.svgHeight-spectrumParameters.graphFeatures.padding.bottom +spectrumParameters.graphFeatures.adjustableHeightVal - 5) + ")")
    					.attr("fill","black")
    					    .attr("font-family","Helvetica Neue,Helvetica,Arial,sans-serif")
    					    .attr("font-size","16px")
    					    .text("m/z");
    	svg.append("text").attr("id","label")
    					.attr("transform", "translate("+ spectrumParameters.padding.left/3 +","+(spectrumParameters.svgHeight/2+spectrumParameters.labelAdjustVal)+")rotate(-90)")
    					.attr("fill","black")
    					    .attr("font-family","Helvetica Neue,Helvetica,Arial,sans-serif")
    					    .attr("font-size","16px")
    					    .text("Intensity");
    }
    /**
     * @function onMouseOverPeak
     * @description Function to show the data of Mass and Intensity on mouse over of peaks
     * @param {Node} this_element -  is a html node. On mouse over generates tooltip based on the current peak
     * @param {Object} - Contains mz and intensity value of the current peak
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    onMouseOverPeak = function(this_element,peak,spectrumParameters)
    {
    	let x = spectrumParameters.getPeakXPos(peak.mz);
    	let y = spectrumParameters.getPeakYPos(peak.intensity);
    	intensity =" I:"+ parseFloat(peak.intensity).toFixed(3);
    	mz = "M:"+parseFloat(peak.mz).toFixed(3);
    	y = y - spectrumParameters.mouseOverPadding.head ;
    	if(y<=spectrumParameters.mouseOverPadding.head)
    	{
    		y = spectrumParameters.mouseOverPadding.head;
    	}
    	d3.select(this_element).style("stroke","red")
    							.style("stroke-width","2");

    	let tooltipData = mz + "<br>" + intensity ;
    	/*	Rectangle to have flexible on click and on mouse actions	*/
    	var div = d3.select("body").append("div")
    							.attr("id", "MyTextMZIN")
    							.attr("class", "tooltip")

    	div.transition().duration(30)
    					.style("opacity", 2);
    	div.html(tooltipData).style("left", (d3.event.pageX + 12)  + "px")
    				.style("top", (d3.event.pageY - 28)+ "px")
    				.style("fill", "black");
    }
    /**
     * @function onMouseOverCircle
     * @description Function to show the data of Mass and Intensity on mouse over of circles
     * @param {Node} this_element - is a html node. On mouse over generates tooltip based on the current peak
     * @param {Array} envelope_list - Contains Envelope List
     * @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
     */
    onMouseOverCircle = function(this_element,envelope_list,spectrumParameters)
    {
    	let x = parseFloat(d3.select(this_element).attr("cx"));
    	let y = parseFloat(d3.select(this_element).attr("cy")) ;
    	let mass = "Mass:"+envelope_list.mono_mass.toFixed(2);
    	let charge = "Charge:"+ envelope_list.charge ;
    	y = y - spectrumParameters.mouseOverPadding.head ;
    	if(y<=spectrumParameters.mouseOverPadding.head)
    	{
    		y = spectrumParameters.mouseOverPadding.head;
    	}

    	let tooltipData = mass + "<br>" + charge ;
    	/*	Rectangle to have flexible on click and on mouse actions	*/
    	var div = d3.select("body").append("div")
    							.attr("id", "MyTextMassCharge")
    							.attr("class", "tooltip")

    	div.transition().duration(30)
    					.style("opacity", 2);
    	div.html(tooltipData).style("left", (d3.event.pageX + 12)  + "px")
    				.style("top", (d3.event.pageY - 28)+ "px")
    				.style("fill", "black");
    }

    /**
     * @function onPeakMouseOut
     * @description Function to reset to the original on mouse out of peaks
     * @param {Node} this_element - is a html node. On mouse over generates tooltip based on the current peak
     */
    onPeakMouseOut = function(this_element)
    {
    	onMouseOut();
    	d3.select(this_element).style("stroke","black");
    }
    /**
     * @function onCircleMouseOut
     * @description Function to reset to the original on mouse out of peaks
     */
    onCircleMouseOut = function(){
    	onMouseOut();
    }
    /**
     * @function onMouseOut
     * @description Function to remove the tooltips on mouseout
     */
    onMouseOut = function(){
    	d3.selectAll("#MyTextMZIN").remove();
    	d3.selectAll("#MyTextMassCharge").remove();
    }
    /**
     * Function gets invokes whenever zoom or drag is invoked and redraws the graph whenever there is zoom or draw
     * This function invokes all the functions that draw the graph
     * @param {string} svgId - Contains the Id from html on which the graph should be drawn
     * @param {object} spectrumParameters - Contains the parameters to help draw the graph
     * @param {list} peakData - Contains the data of Peaks and Envelopes to draws lines and circles on the graph
     * @param {list} ionData - Contains Ions to draw upon the peaks
     */
    function drawSpectrum(svgId, spectrumParameters, peakData,ionData){
    	let svg = d3.select("body").select(svgId);
    	// Removes all the elements under SVG group 'svgGroup' everytime there this function is called
    	svg.selectAll("#svgGroup").remove();
    	// Create a group under which all the fucntions of the graph will be appended
    	svg = svg.append("g").attr("id","svgGroup");

    	/*call onMouseOut everytime to fix onHover bug adding multiple data when mouseover and zoomed up*/
    	onMouseOut();
    	drawTicks(svg, spectrumParameters);
    	drawAxis(svg,spectrumParameters);
    	addDatatoAxis(svg,spectrumParameters);
    	addLabels(svg, spectrumParameters);
    	// Condition if background color is needed to be added for specific graph
    	if(spectrumParameters.graphFeatures.isAddbgColor)
    	{
    		addBackGround(svg, spectrumParameters);
    	}
    	drawPeaks(svg, spectrumParameters, peakData);
    	if(spectrumParameters.graphFeatures.showCircles && peakData.envelope_list != null)
    	{
    		addCircles(svg,spectrumParameters,peakData);
    	}
    	if(spectrumParameters.graphFeatures.showIons && ionData != null)
    	{
    		drawIons(svg,spectrumParameters,ionData);
    	}
    	if(spectrumParameters.graphFeatures.showSequence)
    	{
    		drawSequence(svg,spectrumParameters);
    	}
    	if(spectrumParameters.graphFeatures.addErrorPlot)
    	{
    		addErrorPlot(svg,spectrumParameters);
    		drawErrorYticks(svg,spectrumParameters);
    		drawErrorPoints(svg,spectrumParameters);
    		addErrorBlock(svg,spectrumParameters);
    	}
    	return spectrumParameters;
    }