/*
 * Confidential and Proprietary for Stellent, Inc.
 *
 * This computer program contains valuable, confidential, and
 * proprietary information.  Disclosure, use, or reproduction
 * without the written authorization of Stellent is prohibited.
 * This unpublished work by Stellent is protected by the laws
 * of the United States and other countries.  If publication
 * of this computer program should occur, the following notice
 * shall apply:
 *
 * Copyright (c) 2006 Stellent, Inc.
 * All rights reserved.
 *
 * $Id$
 */

if (typeof(idc) == 'undefined')
{
    idc = {};
}

/**
 * @class Defines an interface that allows graphical corner effects (typically rounding) to be applied
 * to an associated DOM element without using graphic files. The constructor parameters define the
 * target element, the corners on which to apply the effects, and the particular effect to apply.
 * 
 * The roundCorners() method that executes the effects was kept outside of the constructor to provide
 * more flexibility in the sequence of object instantiation and execution of the DOM effects.
 * 
 * The 'patterns' object contains a set of objects used to define patterns of stacked 'shim' elements
 * that use variations in margin, height, and border values to create the appearance of a shaped corner.
 * The style attributes of background and border are extracted from the associated parent element's
 * computed style values, and appropriately applied to the shim elements to create the effect. So all
 * you need to do is use CSS to define the style for the parent element, create a new 'Rounded' object,
 * and call the roundedCorners() method to render the effects.
 * 
 * Usage and limitations:
 * 
 * The target element must be a block-level element (typcially a <div>). Direct decendents of the target
 * element must also be block-level elements (it is recommended that the target element contain a single
 * top-level child element to be used as a container for all content -- although some configurations using
 * multiple top-level child elements may work, this has not been tested).
 * 
 * The effects will work with bordered and non-bordered objects. All borders are assumed to be 1px width
 * (greater flexibility may be added in the future). When defining borders in style sheets, the CSS shortcut
 * 'border' attribute must be used (e.g. 'border: 1px solid #FF3300') as opposed to defining border width,
 * style, and color using the separate CSS attributes. To eliminate the border from a single side of an
 * object you must individually set its width to zero (e.g. 'border-bottom-width:0px') as opposed to setting
 * border style to 'none'.
 * 
 * The class supports the use of background images for some applications. Positioning compensation is
 * used to render the background smoothly from the top of the upper shim group to the bottom of the 
 * content area; the bottom shim group has the background color (not image) applied. Trial and 
 * error may be required to see if a particular background will work (most top-to-bottom effects, such
 * as gradients will work well, using a background color value that matches the color at the bottom of
 * the gradient). Subsequent versions may improve flexibility of background usage.
 * 
 * @constructor
 * Create new Rounded object.
 * @param {String} elmtId The id of the container in which this TrayDisplay table is to be rendered/defined.
 * @param {String} corners (optional) A string indicating which corners to round [all|top|bottom|tl|tr|bl|br]
 * @param {String} cornerPattern (optional) A string matching the name of a pattern object [small|large]
 * If the corners prameter is omitted, the default is 'all'.
 * If the cornerPattern parameter is omitted, the default is 'small'.
 */
idc.Rounded = function(elmtId, corners, cornerPattern)
{
	this.elmtId = elmtId;
	this.elmt = document.getElementById(elmtId);
	this.cornerPattern = (typeof cornerPattern != "undefined") ? cornerPattern : "small";
	this.roundTL = false;
	this.roundTR = false;
	this.roundBL = false;
	this.roundBR = false;
	this.backgroundColor = null;
	this.backgroundImage = null;
	this.backgroundRepeat = null;
	this.borderColor = null;
	this.borderStyle = null;
	this.borderTopWidth = null;
	this.borderBottomWidth = null;
	this.borderLeftWidth = null;
	this.borderRightWidth = null;
	this.stdShimElmt = null;
	this.topGroupElmt = null;
	this.bottomGroupElmt = null;
	this.groupHeight = 0;			
		
	corners = (typeof corners != "undefined") ? corners.toLowerCase() : "all";
	this.setCornerFlags(corners);
	
	this.getStyleValues();
	
	this.createStdShimElmt();
	
  window[elmtId] = this; // Global reference to the object based upon the elmtId
}


idc.Rounded.VERSION = "1.0";
idc.Rounded.NAME = "idc.Rounded";


idc.Rounded.prototype =
{
/**
 * Executes the routines that generate and insert the DOM elements used to create the graphical corner
 * effects. Takes no parameters.
 */  
  roundCorners: function()
  {		
		if (this.elmt != null)
		{
			// Selectively remove style attributes from target element (attributes of the shim and content
			// elements will be used to represent these styles instead).
			this.elmt.style.backgroundColor = "transparent";
			this.elmt.style.backgroundImage = "none";
			if (this.borderStyle == "solid")
			{
				this.elmt.style.borderLeftStyle = "none";
				this.elmt.style.borderRightStyle = "none";
				if (this.roundTL || this.roundTR)
				{
					this.elmt.style.borderTopStyle = "none";
				}
				if (this.roundBL || this.roundBR)
				{
					this.elmt.style.borderBottomStyle = "none";
				}				
			}
			
			// Apply target element style atrributes to top-level content elements
			for (var i=0; i<this.elmt.childNodes.length; i++)
			{
				var childElmt = this.elmt.childNodes[i];
				if (childElmt.nodeType == 1)
				{
					childElmt.style.backgroundColor = this.backgroundColor;
					childElmt.style.backgroundImage = this.backgroundImage;
					childElmt.style.backgroundRepeat = this.backgroundRepeat;
					childElmt.style.backgroundPosition = "0px -" + this.groupHeight + "px";
					if (this.borderStyle == "solid")
					{
							childElmt.style.borderLeft = this.borderLeftWidth + " solid " + this.borderColor;	
							childElmt.style.borderRight = this.borderLeftWidth + " solid " + this.borderColor;
					}
				}
			}			
			
			// Create/insert top element group
			if (this.roundTL || this.roundTR)
			{
				this.topGroupElmt = document.createElement("div");
				this.topGroupElmt.style.display = "block";
				this.topGroupElmt.style.background = "transparent";
				this.createShims(this.topGroupElmt);
				this.elmt.insertBefore(this.topGroupElmt, this.elmt.firstChild);
			}
	
			// Create/insert bottom element group
			if (this.roundBL || this.roundBR)
			{			
				this.bottomGroupElmt = document.createElement("div");
				this.bottomGroupElmt.style.display = "block";
				this.bottomGroupElmt.style.background = "transparent";
				this.createShims(this.bottomGroupElmt);
				this.elmt.appendChild(this.bottomGroupElmt);
			}			
		}				
  },
  
/**
 * A group of objects used to define different corner effects. The 'cornerPattern' parameter of the
 * constructor must correspond to the name of one of these patterns. Each pattern object contains an
 * array that represents style attributes of shim elements that will be inserted into the parent element
 * to produce the graphical corner effects. The elements are defined in vertical sequence from outer-most
 * to inner-most. The outer-most element should always have its 'borderWidth' property defined as 'null'.
 * Since all effects are symmetrical on both axes, the pattern objects define the effects for a single
 * corner in-general; the 'corners' parameter of the constructor determines which corners receive these
 * effects. The effect will be identical on all corners specified.
 * Note that it is also possible to create patterns that render shapes other than a plain circular radius.
 * @type Object *
 */  
  patterns:
  {
  	small:
  	[
  		{ margin: "2px", height: "1px", borderWidth: null },
  		{ margin: "1px", height: "1px", borderWidth: "1px" }
  	],
  	large:
  	[
  		{ margin: "5px", height: "1px", borderWidth: null },
  		{ margin: "3px", height: "1px", borderWidth: "2px" },
  		{ margin: "2px", height: "1px", borderWidth: "1px" },
  		{ margin: "1px", height: "2px", borderWidth: "1px" }
  	],
  	xLarge:
  	[
  		{ margin: "15px", height: "1px", borderWidth: null },
  		{ margin: "12px", height: "1px", borderWidth: "3px" },
  		{ margin: "10px", height: "1px", borderWidth: "2px" },
  		{ margin: "8px", height: "1px", borderWidth: "2px" },
  		{ margin: "7px", height: "1px", borderWidth: "1px" },
  		{ margin: "6px", height: "1px", borderWidth: "1px" },
  		{ margin: "5px", height: "1px", borderWidth: "1px" },  
  		{ margin: "4px", height: "1px", borderWidth: "1px" },
  		{ margin: "3px", height: "2px", borderWidth: "1px" },
  		{ margin: "2px", height: "2px", borderWidth: "1px" },
  		{ margin: "1px", height: "3px", borderWidth: "1px" }
  	]    	
  },    
  
/**
 * Code used to generate and insert the shim elements into the top or bottom group element
 * @private
 */  
  createShims: function(groupElmt)
  {
  	var pattern = this.patterns[this.cornerPattern];
  	var group = (groupElmt == this.topGroupElmt) ? "top" : "bottom";
  	var roundLeft = ((group == "top" && this.roundTL) || (group == "bottom" && this.roundBL));
  	var roundRight = ((group == "top" && this.roundTR) || (group == "bottom" && this.roundBR));
  	var accumulatedShimHeight = 0;  	
  	
  	for (var i=0; i<pattern.length; i++)
  	{  		
  		var shim = this.stdShimElmt.cloneNode(false);
  		var backgroundLeftAdjust = 0;  		
  		
  		shim.className = (i == 0) ? "outerShim" : "innerShim";
  		shim.style.height = pattern[i].height;
  		
  		if (roundLeft)
  		{
  			shim.style.marginLeft = pattern[i].margin;
  		}
  		if (roundRight)
  		{
  			shim.style.marginRight = pattern[i].margin;
  		}
  		
  		var bWidth;
  		if (this.borderStyle == "solid")
  		{
  			bWidth = (roundLeft) ? pattern[i].borderWidth : this.borderLeftWidth;
  			if (bWidth != null && this.borderLeftWidth != "0px")
  			{
  				shim.style.borderLeft = bWidth + " solid " + this.borderColor;
  				
  				if (this.backgroundImage != "none" && group == "top")
  				{
  					backgroundLeftAdjust += parseInt(bWidth) - parseInt(this.borderLeftWidth);
  				}
  			}
 			
  			bWidth = (roundRight) ? pattern[i].borderWidth : this.borderRightWidth;
  			if (bWidth != null && this.borderRightWidth != "0px")
  			{
  				shim.style.borderRight = bWidth + " solid " + this.borderColor;
  			}
  			
  			if (shim.className == "outerShim")
  			{
  				if ((group == "top" && this.borderTopWidth != "0px") ||
  						(group == "bottom" && this.borderBottomWidth != "0px"))
  				{
  					shim.style.backgroundColor = this.borderColor;
  				}
  				
  				shim.style.backgroundImage = "none";
  			}
  		}  		  		
  		
  		if (this.backgroundImage != "none")
  		{
  			if (group == "top")
  			{
  				
  				backgroundLeftAdjust += parseInt(shim.style.marginLeft);
  				shim.style.backgroundPosition = "-" + backgroundLeftAdjust + "px -" + accumulatedShimHeight + "px";
  			}
  			else
  			{
  				shim.style.backgroundImage = "none";
  			}
  		}  		
  		
  		if (shim.className == "outerShim" && this.borderStyle == "solid")
  		{
  			// Do not add total shim height for this case
  		}
  		else
  		{
  			accumulatedShimHeight += parseInt(pattern[i].height);
  		}  		  		  		 		
	
  		if (group == "top" || i == 0)
  		{
  			groupElmt.appendChild(shim);
  		}
  		else
  		{
  			groupElmt.insertBefore(shim, groupElmt.firstChild);
  		}
  	}
  	this.groupHeight = accumulatedShimHeight;
  },  
  
/**
 * Code that sets flag values to specify the corners on which to apply effects. Values are set
 * based upon the 'corners' parameter passed to the constructor.
 * @private
 */   
  setCornerFlags: function(corners)
  {
		if (corners.indexOf("all") > -1 || corners == "")
		{
			this.roundTL = true;
			this.roundTR = true;
			this.roundBL = true;
			this.roundBR = true;		
		}
		else
		{
			if (corners.indexOf("top") > -1 || corners.indexOf("tl") > -1)
			{
				this.roundTL = true;
			}
			if (corners.indexOf("top") > -1 || corners.indexOf("tr") > -1)
			{
				this.roundTR = true;
			}
			if (corners.indexOf("bottom") > -1 || corners.indexOf("bl") > -1)
			{
				this.roundBL = true;
			}
			if (corners.indexOf("bottom") > -1 || corners.indexOf("br") > -1)
			{
				this.roundBR = true;
			}
		}
  },
  
/**
 * Code used to extract computed style values from the associated parent element
 * @private
 */  
  getStyleValues: function()
  {
	  if (this.elmt != null)
	  {
		  if (this.elmt.currentStyle) // IE proprietary method
		  {
		    var elmtStyle = this.elmt.currentStyle;
		    this.borderColor = elmtStyle.borderColor;
		    this.borderStyle = elmtStyle.borderStyle;
		  }
		  else if (document.defaultView && document.defaultView.getComputedStyle) // W3C method
		  {
		    var elmtStyle = document.defaultView.getComputedStyle(this.elmt, null);
		    this.borderColor = elmtStyle.borderTopColor;
		    this.borderStyle = elmtStyle.borderTopStyle;
		  }
		  else
		    return;
		    
	    this.backgroundColor = elmtStyle.backgroundColor;
	    this.backgroundImage = elmtStyle.backgroundImage;
	    this.backgroundRepeat = elmtStyle.backgroundRepeat;	
	    this.borderTopWidth = elmtStyle.borderTopWidth;
	    this.borderBottomWidth = elmtStyle.borderBottomWidth;
	    this.borderLeftWidth = elmtStyle.borderLeftWidth;
	    this.borderRightWidth = elmtStyle.borderRightWidth;
	
		  // Convert rgb(n,n,n) value to hex format
		  // (necessary since Firefox returns computed background color values in rgb() string format)
		  if (this.backgroundColor.indexOf("rgb") > -1)
		  {
		  	this.backgroundColor = idc.Rounded.rgbStringToHex(this.backgroundColor);
		  }
		  if (this.borderColor.indexOf("rgb") > -1)
		  {
		  	this.borderColor = idc.Rounded.rgbStringToHex(this.borderColor);
		  }
	  }   
  },
  
/**
 * Creates a standard shim element that can used as a cloning template to speed creation of
 * the specific shims.
 * @private
 */  
  createStdShimElmt: function()
  {
    this.stdShimElmt = document.createElement("div");
    this.stdShimElmt.style.display = "block";
    this.stdShimElmt.style.height = "1px";
    this.stdShimElmt.style.fontSize = "1px";
    this.stdShimElmt.style.margin = "0px";
    this.stdShimElmt.style.backgroundColor = this.backgroundColor;
    if (this.backgroundImage != "none")
    {
    	this.stdShimElmt.style.backgroundImage = this.backgroundImage;
    	this.stdShimElmt.style.backgroundRepeat = this.backgroundRepeat;
    }
    this.stdShimElmt.style.overflow = "hidden";
    var nbsp = document.createTextNode("&nbsp;");
    this.stdShimElmt.appendChild(nbsp);
  }
}

/**
 * Utility used to convert the rgb string value (e.g. 'rgb(0,0,0)') returned by some browsers into
 * a hex value.
 */
idc.Rounded.rgbStringToHex = function(rgbValueStr)
{
  var parsedRgbStr = "";
  var hexStr = "#";

  for (var i=0; i<rgbValueStr.length-1; i++)
  {
    if (!isNaN(parseInt(rgbValueStr.charAt(i))) || rgbValueStr.charAt(i) == ",") 
      parsedRgbStr += rgbValueStr.charAt(i);
  }
  
  rgbArray = parsedRgbStr.split(",");
  
  for (var i=0; i<rgbArray.length; i++)
  {
    rgbArray[i] = parseInt(rgbArray[i]).toString(16);
    if (rgbArray[i].length == 1) rgbArray[i] = "0" + rgbArray[i];
    hexStr += rgbArray[i];
  }

  return hexStr;
}
