/**
  * Class for cross browser layers.
  * Supports Ns4, Ie4 - Ie6, and Ns6 (except for get height and width).
  *
  * TODO: - make getHeight and getWidth work properly in Ns6
  *       - refactor other libraries to get event capturing working in Ns6
  *
  * Depends on the StyleDescription.js library to do some style handling stuff.
  */

	/**
	 * Constructor: can create new layers or wrap existing layers.
	 * @param aId: the id of the DIV or LAYER tag. If the layer already exists, 
	 *             AbstractLayer wraps it; if not, a new layer is created. 
	 * @param aWidth: the desired width of the layer -- only used when 
	 *                creating new layers.
	 */
	function LinklistAbstractLayer(aId, aWidth) {
		this.iId = aId;
				
		if(gIe4) {
			// if the layer does not exist
			if(!document.all.tags('DIV')[aId]) {
				// write a new one into the document
				document.body.insertAdjacentHTML("beforeEnd", "<div id=\"" + aId + "\" style=\"position: absolute; width: " + aWidth + "px;\"></div>");
			} 
			this.iDivRef = document.all.tags('DIV')[aId];
			this.iStyle = this.iDivRef.style;
			
		} else if (gNs4) {
			// if the layer does not exist
			if(!document.layers[aId]) {
				// write a new one into the document
				document.layers[aId] = new Layer(aWidth);
			}	
			this.iDivRef = document.layers[aId];
			this.iStyle = this.iDivRef;
			// set HTML properties for NS only	
			// generic layer head
			this.iNsHeaderHTML = "<html><body>";
			// init innerHTML for use by addStyleRules()
			this.iNsInnerHTML = null;
			
		} else if (gNs6) {
			// if the layer does not exist
			if(!document.getElementsByTagName("DIV")[aId]) {
				// write a new one into the document
				var lLayer = document.createElement("DIV");
				lLayer.setAttribute("id", aId);
				lLayer.setAttribute("style", "position: absolute; width: " + aWidth + "px");
				document.getElementsByTagName("BODY")[0].appendChild(lLayer);
			}
			this.iDivRef = document.getElementById(aId);	
			this.iStyle = this.iDivRef.style;	
			
		} else {
			// for debugging purposes...
			alert("this browser not supported by AbstractLayer");
		}		
	}
	
	/**
	 * Get a reference to the layer or div object.
	 * @returns: a reference to the layer
	 */
	LinklistAbstractLayer.prototype.getRef = function() {
		return this.iDivRef;
	}		
	
	/**
	 * Get the id of the layer or div wrapped by AbstractLayer.
	 * @returns: the id
	 *
	 * This is a leftover from Walt's code -- 
	 * is it ever used or can we get rid of it?
	 */
	LinklistAbstractLayer.prototype.getName = function() {
		return this.iId;
	}			
	
	/**
	 * Moves the layer to co-ordinates relative to the document origin.
	 * @param aX: x co-ordinate
	 * @param aY: y co-ordinate	 
	 */  
	LinklistAbstractLayer.prototype.moveTo = function(aX, aY) {
		var lX = parseInt(aX);
		var lY = parseInt(aY);
	
		this.iStyle.left = lX;
		this.iStyle.top = lY;
	}
	
	/**
	 * Get the left (x) position of the layer.
	 *
	 * NOTE: Walt used to return .pixelLeft for IE
	 *       but I can't tell if there was any point.
	 *
	 * @returns: the postion of then layer relative 
	 *           to the left edge of the document
	 */
	LinklistAbstractLayer.prototype.getLeft = function() {
		return parseInt(this.iStyle.left);
	}	
		
	/**
	 * Get the top (y) position of the layer.
	 *
	 * NOTE: Walt used to return .pixelTop for IE
	 *       but I can't tell if there was any point.
	 *	 
	 * @returns: the postion of then layer relative 
	 *           to the top edge of the document
	 */
	LinklistAbstractLayer.prototype.getTop = function() {
		return parseInt(this.iStyle.top);
	}	
		
	/**
	 * Get the _visible_ width of the layer.
	 *
	 * TODO: get it working in Ns6.
	 *
	 * @returns: the width described above
	 */	
	LinklistAbstractLayer.prototype.getWidth = function() {
		if(gNs4) {     
			var lClipWidth = parseInt(this.iStyle.clip.width);
			var lDocWidth = parseInt(this.iDivRef.document.width);
			if (lDocWidth == 0 && lDocWidth != lClipWidth) {
				return lClipWidth;
			} else {
				return lDocWidth;
			}	
		} else if (gNs6) {
			alert("getWidth not yet supported by ns6");
		} else {		
			return this.iDivRef.scrollWidth;
		}	
	}	
	
	/**
	 * Get the _visible_ height of the layer.
	 *
	 * TODO: get it working in Ns6.
	 *	 
	 * @returns: the height described above
	 */	
	LinklistAbstractLayer.prototype.getHeight = function() {
		if(gNs4) {
			var lClipHeight = parseInt(this.iStyle.clip.height);
			var lDocHeight = parseInt(this.iDivRef.document.height);
			if (lDocHeight == 0 && lDocHeight != lClipHeight) {
				return lClipHeight;
			} else {
				return lDocHeight;
			}	
		} else if (gNs6) {
			alert("getHeight not yet supported by ns6");
		} else {	
			return parseInt(this.iDivRef.offsetHeight);
		}
	}		
	
	/**
	 * Clip the layer so that its visible area is a 
	 * rectangle of a size less than the layer's actual area.
	 * @param aLeft: x pos of top left corner of the viewport.
	 * @param aTop: y pos of top left corner of the viewport.	
	 * @param aRight: x pos of bottom right corner of the viewport.
	 * @param aBottom: y pos of bottom right corner of the viewport.		  
	 */	
	LinklistAbstractLayer.prototype.clip = function(aLeft, aTop, aRight, aBottom) {
		if(gNs4) {
			var lLeft = (aLeft == null) ? 0 : aLeft;
			var lTop = (aTop == null) ? 0 : aTop;
			var lRight = aRight;
			var lBottom = aBottom;
			if (aLeft != null) this.iStyle.clip.left = lLeft;
			if (aTop != null) this.iStyle.clip.top = lTop;
			if (aRight != null) this.iStyle.clip.right = lRight;
			if (aBottom != null) this.iStyle.clip.bottom = lBottom;		
		} else {
			this.iStyle.clip = "rect(" + aTop + " " + aRight + " " + aBottom + " " + aLeft + ")";
		}		
	}	
	

	/**
	 * Add style rules to the layer.
	 * Styles set in this manner are cumulative and cannot be explicitly removed,
	 * though they can be overwritten by using the same selector twice.
	 *
	 * NOTE: In Ns4 this relies on a browser bug that fails
	 * to clear old styles, and is therefore a bit of a hack. 
	 *
	 * Additionally, style changes in Ns4 only take effect when the layer is redrawn,
	 * so if you call setInnerHtml() before you set the style, you'll have to call it
	 * a second time to make the style stick.
	 *
	 * @param aStyle: style rules for the whole layer, e.g. "margin-left: 0px; border: 2px"
	 * IMPORTANT: do not end the aStyle param with a semicolon!
	 *
	 * TODO: parse out semicolons that appear on the end...
	 */
	LinklistAbstractLayer.prototype.addStyleRules = function(aStyle) {
		if(gNs4) {
			this.iNsHeaderHTML = "<html><body style='" + aStyle + "'>";
			this.setInnerHTML(this.iNsInnerHTML);
		} else if(gNs6) {
			var lStyle = this.iDivRef.getAttribute("style") + aStyle;
			this.iDivRef.setAttribute("style", lStyle);
		} else {
			var lSheets = document.styleSheets; 
			lSheets[lSheets.length - 1].addRule("#" + this.iDivRef.id, aStyle);
		}
	}		
	
	/**
	 * Set the HTML content of the layer.
	 * @param aHTML: the HTML to display.
	 */	
	LinklistAbstractLayer.prototype.setInnerHTML = function(aHTML) {
		if(gNs4) {
			var lHTML;
			if(this.iNsHTMLStyle) {
				// convert the CSS in iNsHTMLStyle to HTML
				lHTML = this.iNsHTMLStyle.applyTo(aHTML);
			} else {
				lHTML = aHTML;
			}			
			this.iDivRef.document.open();
			this.iDivRef.document.write(this.iNsHeaderHTML);
			this.iDivRef.document.write(lHTML);
			this.iDivRef.document.write("</body></html>");
			this.iDivRef.document.close();
			// save innerHTML in case addStyleRules() needs it	
			this.iNsInnerHTML = lHTML;
			if(this.iNsHTMLStyle) {
				this.setBGColor(this.iNsHTMLStyle.getBackgroundColor());
			}			
		} else {
			this.iDivRef.innerHTML = aHTML;
		}
	}	
	
	
	/**	
  	 * Assigns a StyleDescription obj to the layer so that Ns4 browsers can
	 * use CSS with tables and other unsupported elements. The CSS within is
	 * converted to HTML when setInnerHTML() is called.
	 *
	 * Could this perhaps be replaced / absorbed by addStyleRules()?
	 *
	 * @param: a StyleDescription object.
	 */
	LinklistAbstractLayer.prototype.setStyle = function(aStyleDescription) {
		if(gNs4) {
			this.iNsHTMLStyle = aStyleDescription;
		}	
	}
	
	/**
	 * Is the layer hidden or visible?
	 * @returns: true if the layer is visible.
	 */
	LinklistAbstractLayer.prototype.isVisible = function() {
		if(gNs4 && this.iStyle.visibility == "show") {
			return true;
		} else if(!gNs4 && this.iStyle.visibility == "visible") {
			return true;
		} else {
			return false;
		}
	}		
	
	/**
	 * Hide the layer.
	 */	
	LinklistAbstractLayer.prototype.hide = function() {
		if(gNs4) {
			this.iStyle.visibility = "hide";
		} else {
			this.iStyle.visibility = "hidden";
		}		
	}	
	
	/**
	 * Show the layer.
	 */	
	LinklistAbstractLayer.prototype.show = function() {
		if(gNs4) {
			this.iStyle.visibility = "show";
		} else {
			this.iStyle.visibility = "visible";
		}					
	}	
	
	/**
	 * Get the z-index of the layer.
	 * @returns: the z-index.
	 */	
	LinklistAbstractLayer.prototype.getZIndex = function() {
		return parseInt(this.iStyle.zIndex);		
	}	
	
	/**
	 * Set the z-index of the layer.
	 * @param: the z-index to set.
	 */	
	LinklistAbstractLayer.prototype.setZIndex = function(aIndex) {
		this.iStyle.zIndex = parseInt(aIndex);	
	}	

	/**
	 * Set the background colour of the layer.
	 * @param: the colour as a websafe hex number prefixed 
	 *         by a hash (e.g. #ff0000), or the "transparent" keyword
	 */	
	LinklistAbstractLayer.prototype.setBGColor = function(aHexColor) {
		if(gNs4) {
			if(aHexColor== "transparent") {
				this.iStyle.bgColor = null;
			} else {
				this.iStyle.bgColor = aHexColor;
			}	
		} else {
			this.iStyle.backgroundColor = aHexColor;	
		}
	}
	
	/**
	 * Get the background colour of the layer.
	 * @returns: the colour as a hex number prefixed 
	 *         by a hash (e.g. #ff0000), or the "transparent" keyword
	 */	
	LinklistAbstractLayer.prototype.getBGColor = function() {
		var lHexColour = "transparent";
		if(gNs4) {
			// if not transparent
			if(this.iStyle.bgColor != null) {
				// convert Ns4 decimal value to hex
				lHexColour = "#" + this.iStyle.bgColor.toString(16);
			}	
		} else {
			lHexColour = this.iStyle.backgroundColor;	
		}
		return lHexColour;
	}	

