/*

graphics.js 

Graphics library for JavaScript.

#############################################################################
#                                                                           #
# Author: Patrik Lundin                                                     #
# Email : patrik.lundin@ebox.tninet.se                                      #
#         patrik.lundin@hotmail.com                                         #
# URL   : http://user.tninet.se/~jml288p/                                   #
# Date  : 1998-05-24                                                        #
# Ver   : 1.0b3                                                             #
#                                                                           #
# Copyright: Copyright (c) 1998, Patrik Lundin, all rights reserved.        #
# License  : You are free to use, copy, modify and redistribute the library #
#            provided that you keep the note about author, email, version,  #
#            date, copyright and license unchanged.                         #
#            If you modify the script in any way make a note of the changes #
#            here and write your email adress, name and the date beside the #
#            note, also please inform the original author of the changes.   #
#                                                                           #
# This Note must always remain unchanged !                                  #
#                                                                           #
#############################################################################


Description:
-----------


A library for performing simple graphics in JavaScript.

This library works only in Netscape 4.0 and higher which
supports the layer and ilayer tags.

To import the library use a line like:

<script language="JavaScript1.1" src="graphics.js"></script>

in the header of a document, assuming here that the graphics.js
file will be in the same directory as the page that will be using it,
if not modify the path as needed.

To test wether the users browser supports layers before calling
any of the graphics functions, use a script like this :

var ok = true;
if( ! document.layers )
{
  // .. no layer support .. warn the user.
  alert( "This browser does not support layers" );
  ok = false;
}

Then in the rest of your script test the ok variable before
calling any of the functions in the library.





Usage:
------

Call the getGraphics method with a layer object or a window object
as argument, then use the object returned to draw.

Example:

var g = getGraphics( document.layerName );

g.drawLine(10,10,50,50);
g.drawCircle(100,100,35);
// .. etc..


// To draw several items to memory 
// before drawing onto the layer :

g.wait();   // wait until flush is called
g.drawLine(10,10,50,50);
g.drawCircle(100,100,35);
g.flush();  // everything is drawed




Methods :
--------

(*) denotes a required parameter.

In general only the parameters marked as required must 
be sent to the function.

You can set the non required fields to null or 
just ignore them.


drawLine(x1,y1,x2,y2,pen,color)

   	Draws a line from (x1,y1) to (x2,y2) on the layer area.

	x1,y1,x2,y2  - coordinates to draw between (*).
	pen          - thickness of pencil to draw with in pixels.
	color        - color to draw with as hexadecimal triplet, rrggbb.
	
drawImage(url,x,y,width,height)

	"Draws" an image at the specified location.
	
	url          - url to picture (*)
	x,y          - upper left corner of picture (*)
	width        - image width
	height       - image height

drawRect(x,y,width,height,pen,color)

	Draws an rectangle, with upper left corner at (x,y).

	x,y          - upper left corner of rectangle (*).
	width        - width of rectangle (*)
	height       - height of rectangle (*)
	pen          - thickness of pencil to draw with in pixels.
	color        - color to draw with as hexadecimal triplet, rrggbb.

fillRect(x,y,width,height,color)

	Draws and fills a rectangle with upper left corner at (x,y).

	x,y          - upper left corner of rectangle (*).
	width        - width of rectangle (*)
	height       - height of rectangle (*)
	color        - color to draw with as hexadecimal triplet, rrggbb.

drawString(x,y,str,bgcolor)

	Draws and fills a rectangle with upper left corner at (x,y).

	x,y            - upper left corner of rectangle (*).
	str            - string to draw, can be html code (*).
	bgcolor        - background of layer as hexadecimal triplet, rrggbb.
			 If omitted the layer will be transparent.


drawCircle(x,y,radius,pen,color)

	Draws a circle with centerpoint at (x,y) on the layer area.

	
	x,y          - centerpoint of circle (*).
	radius       - the radius of this circle (*)
	pen          - thickness of pencil to draw with in pixels.
	color        - color to draw with as hexadecimal triplet, rrggbb.

fillCircle(x,y,radius,color)

	Draws and fills a circle with centerpoint at (x,y) on the layer area.

	
	x,y          - centerpoint of circle (*).
	radius       - the radius of this circle (*).
	color        - color to draw with as hexadecimal triplet, rrggbb.


drawPolygon(xarr,yarr,pen,color)

	Draws a polygon described by the coordinates in the arrays xarr and yarr.
	The arrays are matched to form coordinate pairs.
	
	xarr         - array containing x coordinates (*)
	yarr         - array containing y coordinates (*)
	pen          - thickness of pencil to draw with in pixels.
	color        - color to draw with as hexadecimal triplet, rrggbb.


clearMemory()
	
	Clears the memory for this graphics object.

clearArea()

	Clears the draw area ( layer ) associated with this graphics object.

clear()

	Clears both the memory and the draw area in this graphics object.


wait() 

	Stops all drawing to the draw area until flush() is called.
	Use waitState() to check wether the graphics object is in wait state.


waitState()

	Returns true if the graphics object is in wait state, false otherwise.


flush(keep)

	Draws everything currently in the graphics objects memory to the draw area.
	Flush resets the waitstate so a call to waitState() will return false
	and all drawing will be drawed directly.
	
	keep - boolean indicating wether the waitstate should be kept.
	       If omitted the state will be set to normal again and calls
	       to waitState() will then return false.

*/





function getGraphics(area)
{
	isDrawable( area );

	return( new Graphics(area) );
}



function gdline(){

	var area = this.area;

	if( ! isDrawable( area ) ){
		return;
	}

	var xx1 = isNaN(parseInt(gdline.arguments[0])) ? 0 : parseInt(gdline.arguments[0]);
	var yy1 = isNaN(parseInt(gdline.arguments[1])) ? 0 : parseInt(gdline.arguments[1]);
	var xx2 = isNaN(parseInt(gdline.arguments[2])) ? 0 : parseInt(gdline.arguments[2]);
	var yy2 = isNaN(parseInt(gdline.arguments[3])) ? 0 : parseInt(gdline.arguments[3]);
	var th = isNaN(parseInt(gdline.arguments[4])) ? 1 : parseInt(gdline.arguments[4]);
	var color = ( gdline.arguments[5] == null || gdline.arguments[5] == "" ) ? "000000" : gdline.arguments[5];
	var mem = this.memory;

	var allcode = this.memory.allcode;

	allcode += hdline(xx1,yy1,xx2,yy2,th,color);

	this.memory.allcode = allcode;
	if( ! this.gwst )
	{
		drawAll( allcode, area );
	}
}







function gdimage(){

	var area = this.area;

	if( ! isDrawable( area ) ){
		return;
	}

	var url = gdimage.arguments[0] != null ? gdimage.arguments[0] : "";
	var xx = isNaN(parseInt(gdimage.arguments[1])) ? 0 : parseInt(gdimage.arguments[1]);
	var yy = isNaN(parseInt(gdimage.arguments[2])) ? 0 : parseInt(gdimage.arguments[2]);
	var width = isNaN(parseInt(gdimage.arguments[3])) ? "" : "width=" + parseInt(gdimage.arguments[3]);
	var height =  isNaN(parseInt(gdimage.arguments[4])) ? "" : "height=" + parseInt(gdimage.arguments[4]);
	var mem = this.memory;

	var allcode = this.memory.allcode;
	
	allcode += "<layer visibility=show left=" + xx + " top=" + yy + "><img src=" + url + " " + width + " " + height + "></layer>"; 	

	this.memory.allcode = allcode;
	if( ! this.gwst )
	{
		drawAll( allcode, area );
	}
}





function gdrect(){

	var area = this.area;

	if( ! isDrawable( area ) ){
		return;
	}

	var xx = isNaN(parseInt(gdrect.arguments[0])) ? 0 : parseInt(gdrect.arguments[0]);
	var yy = isNaN(parseInt(gdrect.arguments[1])) ? 0 : parseInt(gdrect.arguments[1]);
	var width = isNaN(parseInt(gdrect.arguments[2])) ? 1 : parseInt(gdrect.arguments[2]);
	var height =  isNaN(parseInt(gdrect.arguments[3])) ? 1 : parseInt(gdrect.arguments[3]);
	var th = isNaN(parseInt(gdrect.arguments[4])) ? 1 : parseInt(gdrect.arguments[4]);
	var color = ( gdrect.arguments[5] == null || gdrect.arguments[5] == "" ) ? "000000" : gdrect.arguments[5];
	var mem = this.memory;

	var allcode = this.memory.allcode;

	allcode += "<layer visibility=show width=" + width + " height=" + th + " left=" + xx + " top=" + yy + " bgcolor=" + color + "></layer>";
	allcode += "<layer visibility=show width=" + width + " height=" + th + " left=" + xx + " top=" + (yy+height) + " bgcolor=" + color + "></layer>";
	allcode += "<layer visibility=show width=" + th + " height=" + height + " left=" + xx + " top=" + yy + " bgcolor=" + color + "></layer>";
	allcode += "<layer visibility=show width=" + th + " height=" + height + " left=" + (xx+width) + " top=" + yy + " bgcolor=" + color + "></layer>";
	allcode += "<layer visibility=show width=" + th + " height=" + th + " left=" + (xx+width) + " top=" + (yy+height) + " bgcolor=" + color + "></layer>";

	this.memory.allcode = allcode;
	if( ! this.gwst )
	{
		drawAll( allcode, area );
	}
}





function gfrect(){

	var area = this.area;

	if( ! isDrawable( area ) ){
		return;
	}

	var xx = isNaN(parseInt(gfrect.arguments[0])) ? 0 : parseInt(gfrect.arguments[0]);
	var yy = isNaN(parseInt(gfrect.arguments[1])) ? 0 : parseInt(gfrect.arguments[1]);
	var width = isNaN(parseInt(gfrect.arguments[2])) ? 1 : parseInt(gfrect.arguments[2]);
	var height =  isNaN(parseInt(gfrect.arguments[3])) ? 1 : parseInt(gfrect.arguments[3]);
	var color =  ( gfrect.arguments[4] == null || gfrect.arguments[4] == "" ) ? "000000" : gfrect.arguments[4];
	var mem = this.memory;

	var allcode = this.memory.allcode;

	allcode += "<layer visibility=show width=" + width + " height=" + height + " left=" + xx + " top=" + yy + " bgcolor=" + color + "></layer>";

	this.memory.allcode = allcode;
	if( ! this.gwst )
	{
		drawAll( allcode, area );
	}
}




function gdcircle(){

	var area = this.area;

	if( ! isDrawable( area ) ){
		return;
	}

	var xx = isNaN(parseInt(gdcircle.arguments[0])) ? 0 : parseInt(gdcircle.arguments[0]);
	var yy = isNaN(parseInt(gdcircle.arguments[1])) ? 0 : parseInt(gdcircle.arguments[1]);
	var radius = isNaN(parseInt(gdcircle.arguments[2])) ? 1 : parseInt(gdcircle.arguments[2]);
	var th = isNaN(parseInt(gdcircle.arguments[3])) ? 1 : parseInt(gdcircle.arguments[3]);
	var color = ( gdcircle.arguments[4] == null || gdcircle.arguments[4] == "" ) ? "000000" : gdcircle.arguments[4];
	var mem = this.memory;

	var allcode = this.memory.allcode;
	
	var full = Math.PI;
	var ang =  full / ( radius * 3 );
	var c = 0;
	var xr = 0;
	var yr = 0;
	var yr2 = 0;
	var h = 0;
	var code = "";

	while( c <= ( full + ang ) ){
		xr = xx + radius * Math.cos( c );
		yr = yy + radius * Math.sin( c );
		yr2 = yy + radius * Math.sin( c + Math.PI );
		h = yr - yr2;
		code += "<layer visibility=show width=" + th + " height=" + th  + " left=" + xr + " top=" + yr + " bgcolor=" + color + "></layer>"
			+  "<layer visibility=show width=" + th + " height=" + th  + " left=" + xr + " top=" + yr2 + " bgcolor=" + color + "></layer>";
		c += ang;
	}
	
	allcode += code;

	this.memory.allcode = allcode;
	if( ! this.gwst )
	{
		drawAll( allcode, area );
	}
}

function gfcircle(){

	var area = this.area;

	if( ! isDrawable( area ) ){
		return;
	}

	var xx = isNaN(parseInt(gfcircle.arguments[0])) ? 0 : parseInt(gfcircle.arguments[0]);
	var yy = isNaN(parseInt(gfcircle.arguments[1])) ? 0 : parseInt(gfcircle.arguments[1]);
	var radius = isNaN(parseInt(gfcircle.arguments[2])) ? 1 : parseInt(gfcircle.arguments[2]);
	var color = ( gfcircle.arguments[3] == null || gfcircle.arguments[3] == "" ) ? "000000" : gfcircle.arguments[3];
	var mem = this.memory;

	var allcode = this.memory.allcode;
	
	var full = Math.PI;
	var ang =  full / ( radius * 3 );
	var c = 0;
	var xr = 0;
	var yr = 0;
	var yr2 = 0;
	var h = 0;
	var th = 0;
	var lx = xx;
	var code = "";
	
	while( c <= full ){
		xr = xx + radius * Math.cos( c );
		yr = yy + radius * Math.sin( c );
		yr2 = yy + radius * Math.sin( c + Math.PI );
		h = yr - yr2;
		th = Math.abs( xr - lx );
		lx = xr;
		if( th < 1 || th > 2){
			th = 1;
		}else if( th > 1 ){
			th = 2;
		}	
			
		code += "<layer visibility=show width=" + th + " height=" + h  + " left=" + xr + " top=" + yr2 + " bgcolor=" + color + "></layer>";
		c += ang;
	}
	
	allcode += code;

	this.memory.allcode = allcode;
	if( ! this.gwst )
	{
		drawAll( allcode, area );
	}
}


function gdstring(){

	var area = this.area;

	if( ! isDrawable( area ) ){
		return;
	}

	var xx = isNaN(parseInt(gdstring.arguments[0])) ? 0 : parseInt(gdstring.arguments[0]);
	var yy = isNaN(parseInt(gdstring.arguments[1])) ? 0 : parseInt(gdstring.arguments[1]);
	var dstr = gdstring.arguments[2];
	var bgcolor = gdstring.arguments[3] == null ? "" : "bgcolor=" + gdstring.arguments[3];
	var mem = this.memory;

	var allcode = this.memory.allcode;
	
	allcode += "<layer visibility=show left=" + xx + " top=" + yy + " " + bgcolor + ">" + dstr + "</layer>";
	
	this.memory.allcode = allcode;
	if( ! this.gwst )
	{
		drawAll( allcode, area );
	}
}


function gdpoly()
{
	var area = this.area;

	if( ! isDrawable( area ) ){
		return;
	}

	var xarr = gdpoly.arguments[0];
	var yarr = gdpoly.arguments[1];
	var th = isNaN(parseInt(gdpoly.arguments[2])) ? 1 : parseInt(gdpoly.arguments[2]);
	var color = ( gdpoly.arguments[3] == null || gdpoly.arguments[3] == "" ) ? "000000" : gdpoly.arguments[3];
	var mem = this.memory;
	
	var allcode = this.memory.allcode;
		
	var alen = xarr.length < yarr.length ? xarr.length : yarr.length;
	var i = 0;

	while( i < ( alen - 1 ) ){
		allcode += hdline(parseInt(xarr[ i ]),parseInt(yarr[ i ]),parseInt(xarr[ i + 1 ]),parseInt(yarr[ i + 1 ]),th,color);
		i++;
	}

	this.memory.allcode = allcode;
	if( ! this.gwst )
	{
		drawAll( allcode, area );
	}
}


function hdline(xx1,yy1,xx2,yy2,th,color)
{

	var tmp = 0;
	var flip = false;

	if( xx2 < xx1  ){
		flip = true;
		tmp = xx2;
		xx2 = xx1;
		xx1 = tmp; 
		tmp = yy2;
		yy2 = yy1;
		yy1 = tmp; 
	}

	var xstep = 0;
	var ystep = 0;
	var xval = 0;
	var yval = 0;
	var allcode = "";
	var xdiff = xx2 - xx1;
	var ydiff = yy2 - yy1;
	var k = ( yy2 - yy1 ) / xdiff;
	var h = 0;
	var term = "";
	
	if( xdiff == 0 )
	{
		allcode += "<layer visibility=show width=" + th + " height=" + ydiff + " left=" + xx1 + " top=" + yy1 + " bgcolor=" + color + "></layer>";
	}
	else if( ydiff == 0 )
	{
		allcode += "<layer visibility=show width=" + xdiff + " height=" + th + " left=" + xx1 + " top=" + yy1 + " bgcolor=" + color + "></layer>";
	}
	else
	{
		h = Math.abs( ydiff ) / xdiff;
		
		if( h < 1 ){
			h = 1;
		}

		if( flip ){
			term = "xstep <= xdiff";
			xstep = 1;
		}else{
			term = "xstep < xdiff";
			xstep = 0;
		}

		while( eval( term ) )
		{
			xval = xstep + xx1;
			yval = k * xval + yy1 - k*xx1;
			allcode += "<layer visibility=show width=" + th + " height=" + h + " left=" + xval + " top=" + yval + " bgcolor=" + color + "></layer>";
			xstep += 1;
		}
	}

	return allcode;
}


function gcmem()
{
	this.memory.allcode = "";
}



function gcarea()
{
	if( ! isDrawable( this.area ) ){
		return;
	}
	this.area.document.open();
	this.area.document.writeln(" ");
	this.area.document.close();
}



function drawAll(code,area)
{
	area.document.open();
	area.document.writeln( code );
	area.document.close();
}


function isDrawable(ob)
{

	if( ob != null && ( typeof ob ==  "object" ) ){
		var ostr = ob.toString();
		ostr = ostr.toLowerCase();
		if( ostr.indexOf("layer") != -1 ||  ostr.indexOf("window") ){
			return true;
		}
	}
		
	alert("Draw area is not drawable,\nmust be layer or window object");
	return false;
}



function memob()
{
 	this.allcode = "";
}


function gclear()
{
	this.clearMemory();
	this.clearArea(); 
}


function gswst()
{
	this.gwst = true;
}

function gcwst()
{
	return( this.gwst );
}

function gflsh()
{
	var keep = gflsh.arguments[0];

	if( keep == null || ( keep != true && keep != false )){
		keep = false;
	}

	drawAll( this.memory.allcode , this.area );

	this.gwst = keep;
}


function Graphics(drawarea)
{
	this.area    = drawarea;
	this.gwst    = false;
	this.memory  = new memob();
	
	this.drawLine    = gdline;
	this.drawRect    = gdrect;
	this.drawImage   = gdimage;
	this.fillRect    = gfrect;
	this.drawCircle  = gdcircle;
	this.fillCircle  = gfcircle;
	this.drawPolygon = gdpoly;
	this.drawString  = gdstring;
	this.clearMemory = gcmem;
	this.clearArea   = gcarea;
	this.clear       = gclear;
	this.wait        = gswst;
	this.waitState   = gcwst;
	this.flush       = gflsh;
}


