<html>
	<body>
	
	<table>
		<tr>
			<td>
				<canvas id='a' width='600' height='600' style='border:2px solid black;'>
				</canvas>
			</td>
			<td valign="top">
				<div style="border:2px solid black;background-color:#fed;width:300;height:450;">
					<div id="infobox" style="padding:4px;">
					Change values in form to update curve and information box.<br>

					<form>
					
					Function: 
					<select id="function" name="function" >

							<option value="L 100 100 200 360">Line</option>
							<option value="Q 200 300 140 260 400 200">Quadratic Curve</option>
							<option value="C 160 220 440 120 220 440 360 500">Cubic Curve</option>
							<option value="S 60 80 120 240 300 260">Circle Segment</option>

					</select>			
					<button type="button" onclick="newbutton();">NEW</button>
					
					<br>Operations:<br> 
					<select style="width:150px;" size="20" id="operations" name="operations">
					</select>			
					
					<br>
					<button type="button" onclick="moveupbutton();">MOVE UP</button>
					<button type="button" onclick="movedownbutton();">MOVE DOWN</button>
					<button type="button" onclick="deletebutton();">DELETE</button>
					<br>
							
					</form>		

					</div>
				</div>
			</td>			
		</tr>
	</table>	
	
	</div>



	<script lang='Javascript'>
		
		var mx=100,my=100,clickstate=0;

		function newbutton()
		{
				var funclist;
				var oplist;
				
				funclist=document.getElementById('function');
				oplist=document.getElementById('operations');
				
				oplist.innerHTML+="<option value='"+funclist.options[funclist.selectedIndex].value+"'>"+funclist.options[funclist.selectedIndex].text+"</option>";
				
				// If first assign first coordinate to startx and starty
				if(oplist.length==1){
						setpoint(oplist.length-1,1,startx,starty);
						coord=makepoint(startx,starty);
						setpoint(oplist.length-1,2,coord.x,coord.y);
				}else{
						// If any other set first coordinate to last coordinate of last item
						coord=getpoint(oplist.length-2,-1);
						setpoint(oplist.length-1,1,coord.x,coord.y);
						coord=makepoint(coord.x,coord.y);
						setpoint(oplist.length-1,2,coord.x,coord.y);
				}
				// For rest of control points
				if(funclist.selectedIndex==1||funclist.selectedIndex==2||funclist.selectedIndex==3){
						coord=makepoint(coord.x,coord.y);
						setpoint(oplist.length-1,3,coord.x,coord.y);
				}						
				if(funclist.selectedIndex==2){
						coord=makepoint(coord.x,coord.y);
						setpoint(oplist.length-1,4,coord.x,coord.y);
				}						

		}
		
		function deletebutton()
		{
				var elSel = document.getElementById('operations');
  			var i=0;
  			for (i=elSel.length-1;i>=0;i--) {
    				if (elSel.options[i].selected) {
      				elSel.remove(i);
    				}
  			}
		}
		
		function moveupbutton()
		{
				var elSel = document.getElementById('operations');
				var ind=elSel.selectedIndex;
				var val;
				var tex;

				if(elSel.selectedIndex>0){
						
						val=elSel.options[ind].value;
						tex=elSel.options[ind].text;

						elSel.options[ind].value=elSel.options[ind-1].value;
						elSel.options[ind].text=elSel.options[ind-1].text;
						
						elSel.options[ind-1].value=val;
						tex=elSel.options[ind-1].text=tex;
							
						elSel.selectedIndex--;				
				}
		}

		function movedownbutton()
		{
				var elSel = document.getElementById('operations');
				var ind=elSel.selectedIndex;
				var val;
				var tex;

				if(elSel.selectedIndex<elSel.length-1){
						
						val=elSel.options[ind].value;
						tex=elSel.options[ind].text;

						elSel.options[ind].value=elSel.options[ind+1].value;
						elSel.options[ind].text=elSel.options[ind+1].text;
						
						elSel.options[ind+1].value=val;
						tex=elSel.options[ind+1].text=tex;
							
						elSel.selectedIndex++;				
				}
				
		}

		function ev_mouseup(ev)
		{
				clickstate=0;
				cpsel=-1;
				cpno=0;
		}
		
		function ev_mousedown(ev)
		{
				clickstate=1;
		}
		
		function ev_mousemove (ev) 
		{
			  var cx,cy=0;
			  if (ev.layerX||ev.layerX==0) { // Firefox
				    cx=ev.layerX-acanvas.offsetLeft;
				    cy=ev.layerY-acanvas.offsetTop;
			  } else if (ev.offsetX || ev.offsetX == 0) { // Opera
				    cx=ev.offsetX-acanvas.offsetLeft;
				    cy=ev.offsetY-acanvas.offsetTop;
			  }
			  
			  coord=findPos(acanvas);

				if(clickstate==1){
						mx=cx+coord.x;
						my=cy+coord.y;
				}		
				
				gridx=Math.round((cx-(gridsize/2.0))/gridsize)*gridsize;
				gridy=Math.round((cy-(gridsize/2.0))/gridsize)*gridsize;				
		}


		// Fix scrolling on touch devices
		var ScrollFix = function(elem) {
		    // Variables to track inputs
		    var startY, startTopScroll;
		
		    elem = elem || document.querySelector(elem);
		
		    // If there is no element, then do nothing  
		    if(!elem)
		        return;
		
		    // Handle the start of interactions
		    elem.addEventListener('touchstart', function(event){
		        startY = event.touches[0].pageY;
		        startTopScroll = elem.scrollTop;
		
		        if(startTopScroll <= 0)
		            elem.scrollTop = 1;
		
		        if(startTopScroll + elem.offsetHeight >= elem.scrollHeight)
		            elem.scrollTop = elem.scrollHeight - elem.offsetHeight - 1;
		    }, false);
		};

		var acanvas=document.getElementById('a');
		var context=acanvas.getContext("2d");		

		acanvas.addEventListener('mousemove', ev_mousemove, false);
		acanvas.addEventListener('mouseup', ev_mouseup, false);
		acanvas.addEventListener('mousedown', ev_mousedown, false);
		
		setTimeout("foo();",20);
		
		var gridsize=20;
		var gridx,gridy=0;

		function dashedline(sx,sy,ex,ey,dashlen,linewidth,col)
		{
			
//				point("#f88",sx,sy);
//				point("#f88",dx,dy);
			
			var dx=ex-sx;
			var dy=ey-sy;
							
			len=Math.sqrt((dx*dx)+(dy*dy));
			notimes=Math.round(len/dashlen);
			
			dx=dx/notimes;
			dy=dy/notimes;
			
			context.lineWidth = linewidth;
			context.strokeStyle=col;

			context.beginPath();

			var xk,yk;
			xk=sx;
			yk=sy;
			xh=dx/2.0;
			yh=dy/2.0;
			for(var i=0;i<notimes;i++){

					context.moveTo(xk,yk);				
					context.lineTo(xk+xh,yk+yh);
				
					xk+=dx;
					yk+=dy;
			}
							
			context.stroke();
				
		}
								
		function point(col,x,y)
		{
				context.strokeStyle="#000";
				context.lineWidth = 1;
			
				context.fillStyle=col;
				context.fillRect(x-4,y-4,8,8);		
				context.strokeRect(x-4,y-4,8,8);						
		}
		
		function highlight(px,py)
		{
				context.strokeStyle="#aaa";
				context.lineWidth = 1;
			
				context.strokeRect(px-8,py-8,16,16);						
				
		}
		
		// Arcto only works if both x1 and y2 are on circle border
		function arcto(x0,y0,x1,y1,x2,y2)
		{

				var r = Math.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
				var x = x0-r;
				var y = y0-r;
				var startAngle = (180/Math.PI*Math.atan2(y1-y0, x1-x0));
				var endAngle = (180/Math.PI*Math.atan2(y2-y0, x2-x0));
				
				context.arc(x0, y0, r, 0, Math.PI*2.0, 1.0);

		}

		// Draws 90 degree arc
		function arcdeg(x1,y1,x2,y2,x3,y3)
		{
					

					// First quadrant positive positive			
					dashedline(x1,y1,x2,y2,8,1.0,"#999");
					dashedline(x3,y3,x2,y2,8,1.0,"#999");					

					point("#ff5",x1,y1);
					point("#f55",x2,y2);
					point("#f55",x3,y3);
												
					k=(y3-y1)/(x3-x1);					
										
					yk=y1+((x2-x1)*k);
					
					rx=x3-x1;
					ry=y3-y1;

					point("#f5f",x2,yk);

					context.strokeStyle = '#49f';
					context.lineWidth   = 1.0;					
					context.beginPath();					
					context.moveTo(x1,y1);
					for(i=0;i<48;i++){
							if(y3>=y1){
									if(yk>=y2){
											context.lineTo(x1+(Math.sin(((Math.PI/96.0)*-i)+(Math.PI*1.0))*rx),y3+(Math.cos(((Math.PI/96.0)*-i)+(Math.PI*1.0))*ry));
									}else{
											context.lineTo(x3+(Math.sin(((Math.PI/96.0)*i)+(Math.PI*1.5))*rx),y1+(Math.cos(((Math.PI/96.0)*i)+(Math.PI*1.5))*ry));
									}
							}else{
									if(yk<=y2){
											context.lineTo(x1+(Math.sin(((Math.PI/96.0)*-i)+(Math.PI*1.0))*rx),y3+(Math.cos(((Math.PI/96.0)*-i)+(Math.PI*1.0))*ry));
									}else{
											context.lineTo(x3+(Math.sin(((Math.PI/96.0)*i)+(Math.PI*1.5))*rx),y1+(Math.cos(((Math.PI/96.0)*i)+(Math.PI*1.5))*ry));
									}							
							}
					}
					context.stroke();

		}
		
		// Get point of operation
		function getpoint(opno,cpno)
		{
				var xc=-1;
				var yc=-1;

				// Get operations
				var elSel = document.getElementById('operations');
 
				// Bounds checking of operation, if outside bounds do nothing. 
				if(opno>=0&&opno<elSel.length){
						currop=elSel.options[opno].value;
						var operation=currop.split(" ");
			  		
			  		if(cpno==-1){
								xc=parseInt(operation[operation.length-2]);
								yc=parseInt(operation[operation.length-1]);			  		
			  		}else if(cpno>=1&&((cpno*2)+2)<operation.length){
								xc=parseInt(operation[(cpno*2)+1]);
								yc=parseInt(operation[(cpno*2)+2]);					
			  		}
				}

		  	// Return both coordinates as an object!
		  	return {
		        x : xc,
		        y : yc
		    };					

		}
		
		// Set point of operation 
		function setpoint(opno,cpno,xval,yval)
		{
				// Get operations
				var elSel = document.getElementById('operations');
 
				// Bounds checking of operation, if outside bounds do nothing. 
				if(opno>=0&&opno<elSel.length){
						
						currop=elSel.options[opno].value;
						var operation=currop.split(" ");
												
						// We want to update last control point
						if(cpno==-1){
								operation[operation.length-2]=xval;
								operation[operation.length-1]=yval;
						}												
						// Any later control point
						if(cpno>0){
								operation[((cpno-1)*2)+1]=xval;
								operation[((cpno-1)*2)+2]=yval;					
						}

						currop=operation.join(" ");	
						elSel.options[opno].value=currop;
						
						context.strokeStyle = '#ddd';
						context.fillStyle='#000';
						context.font = "bold 16px Arial";
						context.fillText(opno+" "+cpno+" "+xval+" "+yval+" "+currop, 4, 556);
						
				}
		}
		
		function makepoint(x,y)
		{
				var xc=x+(Math.floor((Math.random()*6)-3.0)*20);
				var yc=y+(Math.floor((Math.random()*6)-3.0)*20);
				
				if(xc<20) xc=20;
				if(yc<20) yc=20;
	
				if(xc>580) xc=580;
				if(yc>580) yc=580;
				
				if(xc==x&&yc==y){
						xc+=20;
						yc+=20;
				
				}
	
		  	// Return both coordinates as an object!
		  	return {
		        x : xc,
		        y : yc
		    };
		}		
		
		function findPos(obj) {
			var curleft = curtop = 0;
			if (obj.offsetParent) {
				curleft = obj.offsetLeft
				curtop = obj.offsetTop
				while (obj = obj.offsetParent) {
					curleft += obj.offsetLeft
					curtop += obj.offsetTop
				}
			}
			return {
						x:curleft,
						y:curtop
				}
		}
		
		
		var startx=40;
		var starty=60;
		var endx=400;
		var endy=560;
		
		var cpno=0;					// cpno is the number of the selected control point
		var cpsel=-1;

		function foo()
		{
				context.clearRect(0,0,600,600);
				
				// Draw grid lines				
				context.strokeStyle = '#ddd';
				context.lineWidth   = 0.5;
				context.beginPath();
				for(i=0;i<600;i+=20){
						context.moveTo(i,0);
						context.lineTo(i,600);
						context.moveTo(0,i);
						context.lineTo(600,i);
				}
				context.stroke();			
				
				// Draw Crosshair
				context.beginPath();
				context.strokeStyle = '#444';
				context.lineWidth   = 1.0;
				context.moveTo(gridx-gridsize,gridy);
				context.lineTo(gridx+gridsize,gridy);
				context.moveTo(gridx,gridy-gridsize);
				context.lineTo(gridx,gridy+gridsize);
				context.stroke();											

				// Mark start and end points
				point("#f52",startx,starty);
				point("#5f2",endx,endy);

				// This is our operations loop

				var originalop;
				var elSel = document.getElementById('operations');
  			var i=elSel.length-1;
  			
  			while(i>=0){

						currop=elSel.options[i].value;

						// Start of operation loop
						var operation=currop.split(" ");
						opcode=operation[0];

						context.strokeStyle = '#49f';

/*		
						if(i==0){
								context.strokeStyle = '#49f';
						}else if(i==1){
								context.strokeStyle = '#f49';						
						}else if(i==2){
								context.strokeStyle = '#9f4';				
						}
						context.lineWidth   = 1.0;
*/
						
						// Get coordinates depending on opcode
						if(opcode=="L"||opcode=="Q"||opcode=="C"||opcode=="S"){
								p1x=parseInt(operation[1]);
								p1y=parseInt(operation[2]);
						}
						if(opcode=="L"||opcode=="Q"||opcode=="C"||opcode=="S"){
								p2x=parseInt(operation[3]);
								p2y=parseInt(operation[4]);
						}
						if(opcode=="Q"||opcode=="C"||opcode=="S"){
								p3x=parseInt(operation[5]);
								p3y=parseInt(operation[6]);
						}
						if(opcode=="C"){
								p4x=parseInt(operation[7]);
								p4y=parseInt(operation[8]);
						}
						
						// Draw operation
						
						if(opcode=="L"){
								context.beginPath();
								context.moveTo(p1x,p1y);
								context.lineTo(p2x,p2y);
								context.stroke();			
								point("#25f",p1x,p1y);
								point("#f52",p2x,p2y);
		
						}else if(opcode=="Q"){
								context.beginPath();
								context.moveTo(p1x,p1y);
								context.quadraticCurveTo(p2x,p2y,p3x,p3y);
								context.stroke();			
								dashedline(p1x,p1y,p2x,p2y,8,1.0,"#aaa");
								dashedline(p2x,p2y,p3x,p3y,8,1.0,"#aaa");												
								point("#f52",p1x,p1y);
								point("#f52",p2x,p2y);
								point("#f52",p3x,p3y);
						}else if(opcode=="C"){
								context.beginPath();
								context.moveTo(p1x,p1y);
								context.bezierCurveTo(p2x,p2y,p3x,p3y,p4x,p4y);
								context.stroke();			
								dashedline(p1x,p1y,p2x,p2y,8,1.0,"#aaa");
								dashedline(p2x,p2y,p3x,p3y,8,1.0,"#aaa");						
								dashedline(p3x,p3y,p4x,p4y,8,1.0,"#aaa");												
								point("#f52",p1x,p1y);
								point("#f52",p2x,p2y);
								point("#f52",p3x,p3y);
								point("#f52",p4x,p4y);
						}else if(opcode=="S"){
								arcdeg(p1x,p1y,p2x,p2y,p3x,p3y);
						}
		
						// Highlight and detect click store selected operation and control point
						if(cpsel==-1){
								if(gridx==p1x&&gridy==p1y&&!(p1x==startx&&p1y==starty)){
										// First point either is first point which is unmovable or also moves the last point of previous operation
										point("#ff8",p1x,p1y);
										highlight(p1x,p1y);
										cpno=1;
										if(clickstate==1) cpsel=i;
								}
								if(gridx==p2x&&gridy==p2y){
										// Second point is control point in all but line
										point("#ff8",p2x,p2y);										
										highlight(p2x,p2y);
										cpno=2;
										if(clickstate==1) cpsel=i;
								}
								if(opcode=="Q"||opcode=="C"||opcode=="S"){
										if(gridx==p3x&&gridy==p3y){
												// Second point is control point in all but line
												point("#ff8",p3x,p3y);										
												highlight(p3x,p3y);
												cpno=3;
												if(clickstate==1) cpsel=i;
										}
								}
								if(opcode=="C"){
										if(gridx==p4x&&gridy==p4y){
												// Second point is control point in all but line
												point("#ff8",p4x,p4y);										
												highlight(p4x,p4y);
												cpno=4;
												if(clickstate==1) cpsel=i;
										}
								}
						}
								
						// We have selected a control point and we want to move it
						if(cpsel>=0&&i==cpsel&&cpno>0&&clickstate==1){

								var operation=currop.split(" ");
								if(cpno==1){															
										operation[1]=gridx;
										operation[2]=gridy;
								}else if(cpno==2){
										operation[3]=gridx;
										operation[4]=gridy;						
								}else if(cpno==3){
										operation[5]=gridx;
										operation[6]=gridy;						
								}else if(cpno==4){
										operation[7]=gridx;
										operation[8]=gridy;						
								}
								currop=operation.join(" ");	

								elSel.options[i].value=currop;
								
								// First point move last point of previous
								if(cpno==1) setpoint(i-1,-1,gridx,gridy);
								// If Last point move first point of next
								if((opcode=="L"&&cpno==2)||(opcode=="Q"&&cpno==3)||(opcode=="S"&&cpno==3)||(opcode=="C"&&cpno==4)) setpoint(i+1,1,gridx,gridy);

								context.strokeStyle = '#ddd';
								context.fillStyle='#000';
								context.font = "bold 16px Arial";
								context.fillText(currop+"::"+cpsel+" "+cpno+"::"+opcode+" "+p1x+" "+p1y+" "+p2x+" "+p2y+" "+p1x+" "+p1y+" "+gridx+" "+gridy, 4, 576);

						}


//						elSel.options[i].value=currop;

						i--;
						
  			}
  			
 /*
 
 		First point immovible
 		-----
 		Delete operation
 		Move Up
 		Move Down
 		Move Control Points
 
 */				

/*
				context.fillStyle='#000';
				context.font = "bold 16px Arial";
*/
//				context.fillText(currop+"::"+cpsel+" "+cpno+"::"+opcode+" "+p1x+" "+p1y+" "+p2x+" "+p2y+" "+gridx+" "+gridy, 4, 576);
//				context.fillText(currop+"::"+cpsel+" "+cpno+"::"+opcode+" "+p1x+" "+p1y+" "+p2x+" "+p2y+" "+gridx+" "+gridy, 4, 576);
//				context.fillText(currop+"::"+cpsel+" "+cpno+"::"+opcode+" "+p1x+" "+p1y+" "+p2x+" "+p2y+" "+gridx+" "+gridy, 4, 576);

				setTimeout("foo();",100);
				
	  }
	  
					
	</script>

	</body>
</html>
