Source: massshift.js

class MassShifts {
	constructor(){
		this.SEQUENCEDATA_ID = "#sequencedata";
	}
	/**
	 * Get the sequence entered from the HTML.
	 */
	getSequenceFromUI(){
		var seq = $(this.SEQUENCEDATA_ID).val().trim();
		seq = seq.toUpperCase();//set the sequence to be upper case automatically -- for user convenience
		
		let massShiftList = [] ;
		[seq,massShiftList]= this.getMassShiftList(seq);
		/**
		 * Remove spaces if exists between sequences
		 */
		seq = seq.replace(/ +/g, "");
		completeCalData.sequence = seq;
		return [seq,massShiftList] ;
	}
	/**
	 * @param {string} seq - an argument with mass shift changes embeded in [] square bracket.
	 * @return {string} parsedseq - sequence after removing the mass
	 * Shifts. 
	 * @returns {Array} massShiftList - Array with {position,mass} position-position at which 
	 * mass shift occured, mass- mass shift value.
	 */
	getMassShiftList(seq){
		let massShiftList = [] ;
		let parsedseq = "";
		let splitStr = seq.split(/\[(.*?)\]/);
		let splitArraylen = splitStr.length;
		let position = 0 ;
		
		for(let i = 0 ; i<splitArraylen;i++)
		{
			if(isNaN(splitStr[i]))
			{
				parsedseq = parsedseq + splitStr[i] ;
				position = position + splitStr[i].length ;
			}
			else
			{
				let mass = parseFloat(splitStr[i]);
				/**
				 * remove 1 as the data starts from 0 and length starts from 1
				 */
				let tempPosition = position - 1;
				//Initially set the bg_color to null
				let shiftobj = {mass:mass,position:tempPosition,bg_color:null};
				/**
				 * when the split occur at the end we get an extra "" in 
				 * the list. This is to check if the mass is numeric.
				 */
				if(!isNaN(mass))
				{
					massShiftList.push(shiftobj);
				}
			}
		}
		return [parsedseq,massShiftList] ;
	}
	/**
	 * @param {integer} shiftPosition - contains the position of the new mass
	 * shift entered in text box on click of any amino acid.
	 * @param {integer} massShiftVal - contains value of the mass entered.
	 * @param {Array} massShiftList - contains list of all existing mass 
	 * shifts with positions.
	 * @return {Array} with the new mass Shift value and position entered
	 * or changes the existing mass shift value.
	 */
	appendtoMassShiftList(shiftPosition,massShiftVal,massShiftList,bg_color){
		let newMassShiftList = [];
		let len = massShiftList.length;
		let matchFound = false ;
		for(let i=0; i<len;i++)
		{
			if(shiftPosition == massShiftList[i].position)
			{
				massShiftList[i].mass = massShiftVal;
				massShiftList[i].bg_color = bg_color;
				matchFound = true ;
			}
		}
		if(!matchFound)
		{
			let tempShiftObj = {mass:massShiftVal,position:shiftPosition,bg_color:bg_color};
			massShiftList.push(tempShiftObj);
			matchFound = false;
		}
		let newlen = massShiftList.length;
		for(let j=0; j<newlen; j++)
		{
			if(massShiftList[j].mass != 0)
			{
				newMassShiftList.push(massShiftList[j]);
			}
		}
		return newMassShiftList ;
	}
	/**
	 * Get the fixed ptm mass list with position
	 * @param {string} seq - plain sequence entered by the user.
	 * @return {Array} - Returns an Array of fixed mass shift with positions.
	 */
	getFixedMassList(seq){
		let fixedShiftList = [];
		let fixedPtmAcid = null;
		let fixedPtmMass = null;
		let fixedMassShiftList = this.getFixedPtmChecklist();
		let fixedMassLen = fixedMassShiftList.length;
		for(let k=0; k<fixedMassLen; k++)
		{
			fixedPtmAcid = fixedMassShiftList[k].acid;
			fixedPtmMass = fixedMassShiftList[k].mass;
			if( fixedPtmAcid != null && fixedPtmMass != null)
			{
				let seqln = seq.length ;
				for(let i = 0 ; i<seqln;i++)
				{
					if(seq[i] == fixedPtmAcid)
					{
						let tempObj = {position:i,mass:fixedPtmMass,bg_color:null}
						fixedShiftList.push(tempObj);
					}
				}
			}
		}
		return fixedShiftList ;
	}
	/**
	 * Remove the removed fixed mass from fixed mass list
	 * @param {string} sequence - sequence without mass shifts
	 * @param {Array} fixedMassShiftList - Fixed mass shift list
	 * @param {Char} removeAcid - mass to be removed of a specific Acid
	 */
	removeFixedMassList(sequence,fixedMassShiftList,removeAcid)
	{
		let newList  = [];
		let len = fixedMassShiftList.length;
		removeAcid = removeAcid.toUpperCase()
		for(let i=0;i<len;i++)
		{
			let pos = fixedMassShiftList[i].position ;
			if(sequence[pos] != removeAcid)
			{
				newList.push(fixedMassShiftList[i]);
			}
		}
		return newList ;
	}
	/**
	 * This returns combined List of both Fixed and user entered mass shifts
	 * @param {Array} massShiftList - List of all the mass Shifts from sequence
	 * @param {Array} fixedMassShiftList - List of all selected fixed masses
	 * @returns {Array} combinedMassShiftList - List of combined lists by checking 
	 * over lapping posiitons
	 */
	getCombinedMassShiftList(massShiftList,fixedMassShiftList){
		let combinedMassShiftList = massShiftList;
		let fixedMasslen = fixedMassShiftList.length;
		let massShiftlen = massShiftList.length;
		
		for(let i=0; i<fixedMasslen ; i++)
		{
			let matched = false;
			for(let j=0;j<massShiftlen;j++)
			{
				/**
				 * Check if both has mass shift at common position and over ride the mass shift with fixed mass shift
				 */
				if(combinedMassShiftList[j].position == fixedMassShiftList[i].position)
				{
					combinedMassShiftList[j].mass = fixedMassShiftList[i].mass ;
					combinedMassShiftList[j].bg_color = fixedMassShiftList[i].bg_color ;
					matched = true;
					break;
				}
			}
			/**
			 * If no match found then copy to the new combined list
			 */
			if(!matched)
			{
				combinedMassShiftList.push(fixedMassShiftList[i]);
			}
		}
		
		return combinedMassShiftList ;
	}
	/**
	 * @return {Array} FixedPtmList - return all the selected fixed ptms with acid and mass
	 */
	getFixedPtmChecklist()
	{
		let FixedPtmList = [];
		let divs = $( ".fixedptms").get();
		$( ".fixedptms" ).each(function( index ) {
		  let acid = $( this ).find('#fixedptmacid').val().toUpperCase();
		  let mass = parseFloat($( this ).find('#fixedptmmass').val());
		  if(acid.length !=0  && mass.length != 0 && !isNaN(mass))
		  	{
				let tempfixedptm = {acid:acid,mass:mass}
				FixedPtmList.push(tempfixedptm);
			}
		});
		return FixedPtmList;
	}
	/**
	 * forms the seq with all the mass lists and selected fixed ptms
	 * @param {string} seq - sequence with only aminoacids and without mass lists embedded
	 * @param {Array} massShiftList - List with all the combined mass shifts
	 * @return {string} newSeq - sequence with mass shifts embedded in []
	 */
	formSequence(seq,massShiftList){
		let newSeq = seq;
		let len = massShiftList.length; 
		let seqLen = seq.length ;
		let count = 0;
		/**
		 * sorting the lists with position
		 */
		massShiftList.sort(function(x,y){
			return d3.ascending(x.position, y.position);
		})
		for(let i=0;i<len;i++)
		{
			let newSeqlen = newSeq.length ;
			/**
			 * Dont show when the mass is 0 in the string
			 */
			if(massShiftList[i].mass != 0){
				if(i == 0)
				{
					/**
					 * Add +1 as we need to append the mass after the current position
					 */
					let tempPosition = massShiftList[i].position+1 ;
					newSeq = newSeq.slice(0, tempPosition) + "["+ massShiftList[i].mass + "]"+newSeq.slice(tempPosition, newSeqlen);
				}
				else
				{
					/**
					 * Form the mass between []
					 */
					let tempString = "["+massShiftList[i-1].mass+"]";
					count = count + tempString.length;
					/**
					 * add +1 as the position need to be added after 
					 * the position of the acid.
					 */
					let tempPosition = massShiftList[i].position + 1 + count ;
					newSeq = newSeq.slice(0, tempPosition) + "["+ massShiftList[i].mass + "]" + newSeq.slice(tempPosition, newSeqlen);
				}
			}
		}
		return newSeq ;
	}
	/**
	 * write the sequence with embedded mass in [] to the screen(sequence box)
	 * @param {string} seqToUI - sequence with mass shifts embedded in []
	 */
	writeSeqToTextBox(seqToUI){
		$(this.SEQUENCEDATA_ID).val(seqToUI);
	}

}