/*! Copyright (c) 2005-2008 SAS Institute, Inc.  All rights reserved. */

/* ------------------------------------------------------------------------ *
 * Purpose: Contains common javascript functionality meant to be            
 *          reused in order to perform certain functions consistently       
 *          across CDD space.  
 *
 *          Ideally this common functionality should be compatible across
 *          major browsers.                                             
 *                                                                          
 * ------------------------------------------------------------------------ */
 

sas.provides("sas_Common");
// Determine Browser
// xOp7 = Opera v7,  xOp5or6= Opera v5 or v6
// xIE = Internet Explorer, xNN4 = Netscape 4
// xIE7 = Internet Explorer 7
//$$todo: remove these checks
xOp7=false,xOp5or6=false,xIE=false,xNN4=false,xIE7=false,xIE8=false; xIE6 = false;
xFF=false;xFF2=false;xFF3=false;

var xUA=navigator.userAgent.toLowerCase();
if(window.opera){
  xOp7=(xUA.indexOf('opera 7')!=-1 || xUA.indexOf('opera/7')!=-1);
  if (!xOp7) xOp5or6=(xUA.indexOf('opera 5')!=-1 || xUA.indexOf('opera/5')!=-1 || xUA.indexOf('opera 6')!=-1 || xUA.indexOf('opera/6')!=-1);
}
else if(document.layers) xNN4=true;
else if(xUA.indexOf('firefox') != -1){
	xFF = true;
	if(xUA.indexOf('firefox/2') != -1){
		xFF2 = true;
	}
	else if(xUA.indexOf('firefox/3') != -1){
		xFF3 = true;
	}
}
else {	xIE=document.all && xUA.indexOf('msie')!=-1 && parseInt(navigator.appVersion)>=4;
		//FIX S0349743 : Added for IE7 detection
		var rv = -1;
		if(xIE)
		{ 	var re  = new RegExp("msie ([0-9]{1,}[\.0-9]{0,})");
			if (re.exec(xUA) != null) rv = parseFloat( RegExp.$1 );				
			if(rv > -1 && rv >= 7.0) xIE7=true;
            if(rv > -1 && rv >= 8.0) xIE8=true;
		}	
		//END FIX S0349743 : Added for IE7 detection
		// Added for IE6 check.
		if(xIE){
			xIE6 = (parseInt(xUA.substr(xUA.indexOf("msie")+"msie".length))==6);			
		}
}

//Uses the notion of "Leading" and "Trailing" to abstract left and right for RTL
sas.LEADING=true, sas.TRAILING=false;

/* ------------------------------------------------------------- *
 *  Get the current style for an element in browser
 *  independant fashion.  IE uses currentStyle while Mozilla
 *  currently uses window.getComputedStyle(...)
 *  For attributes that include a hyphen,
 *  use style2 as the unhyphenated property name
 * ------------------------------------------------------------- */
function sas_getCurrentStyle(element, style, style2)
{
	var styleValue="";
	if(element!=null)
	{
		if (document.defaultView && document.defaultView.getComputedStyle)
	    {
	    	styleValue=document.defaultView.getComputedStyle(element,null).getPropertyValue(style);
		}
		else
		{
			if(element.currentStyle)
			{
				styleValue=element.currentStyle[style2 || style];
			}
		}
	}
	
	return styleValue;
}


/* ------------------------------------------------------------- *
 * --Begin createXMLHttpRequest--
 *
 *  Attempt to create an xmlhttprequest for communicating
 *  with server
 * ------------------------------------------------------------- */
function sas_createXMLHttpRequest()
{
	var xmlhttp;
	if (typeof XMLHttpRequest != "undefined")
	{
		xmlhttp = new XMLHttpRequest();
	}
	else
	{
	    try
	    {
            xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); // IE
	    }
        catch (e)
        {
	        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); // IE
        }
	}
	return xmlhttp;
}




/* ------------------------------------------------------------- *
 * --Begin sas_registerEventHandler--
 *
 *  Attempt to register an event listener in cross browser
 *  fashion.
 *
 * Parm: element - object on which to register the handler.
 * Parm: eventType - event type to register this handler for. 
 *                   don't use 'on' prefix as this will be added
 *                   automatically.  'click' rather than 'onclick'.
 * Parm: listener - the function to register as the handler.
 * Parm: capture - specifies whether the handler should capture events.
 *
 * Example Useage...
 *  sas_registerEventHandler(document, 'click', handleClick, false);
 *
 * ------------------------------------------------------------- */
function sas_registerEventHandler ( element, eventType, listener, capture )
{
    if (element != null && eventType!=null && listener!=null) 
    {
      if( element.attachEvent != null) // IE way
      {
        element.attachEvent( "on" + eventType, listener);
      }
      else
      {
        element.addEventListener( eventType, listener, capture );
      }
    }
}

/* ------------------------------------------------------------- *
 * --Begin sas_unRegisterEventHandler--
 *
 *  Attempt to remove an event listener in cross browser
 *  fashion.
 *
 * Parm: element - object on which to unregister event handler
 * Parm: eventType - event type to unregister this handler for. 
 *                   don't use 'on' prefix as this will be added
 *                   automatically.  'click' rather than 'onclick'.
 * Parm: listener - the function to unregister as the handler.
 * Parm: capture - specifies whether the handler being removed 
 *                 captures events.
 *
 * Example Useage...
 *  sas_unRegisterEventHandler(document, 'click', handleClick, false);
 *
 * ------------------------------------------------------------- */
function sas_unRegisterEventHandler ( element, eventType, listener, capture )
{
    if (element != null && eventType!=null && listener!=null) 
    {
      if( element.attachEvent != null) // IE way
      {
        element.detachEvent( "on" + eventType, listener);
      }
      else
      {
        element.removeEventListener( eventType, listener, capture );
      }
    }
}


/* ------------------------------------------------------------- *
 * --Begin sas_autoPosition--
 *
 *  Attempt to automatically do an absolute positioning on an html
 *  element relative to some other element.  Will attempt several
 *  predefined relative positions until it finds one that doesn't
 *  cause the element to be clipped.  If none of the predefined
 *  positions work without clipping then we just dump the element
 *  anywhere.
 *
 * Example Useage...
 *  relativeToElement = document.getElementById("MyImageId");
 *  DivToPosition = document.getELementById("MyPopupMenu");
 *  sas_autoPosition(DivToPosition, relativeToELement);
 *
 * ------------------------------------------------------------- */
function sas_autoPosition(elementToPosition, relativeToElement)
{
	var relativeToCoordinates = new sas_Coordinates(relativeToElement);
	relativeToCoordinates.setRelativeToSharedBreakoutBox(elementToPosition);

	// lets try several positions which we would consider ideal and
	// if none of them work without clipping then we'll have to resort to 
	// just dumping it somewhere even though it does clip
	
//sas_log_println('-----Start sas_autoPosition-------');
	// 1. try positioning elementToPosition left edge aligned with left edge
		// of relativeToElement and top edge aligned with bottomtop edge of relativeToElement.
		var proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.LEADING, sas.LEADING);
		var proposedTop = relativeToCoordinates.getY() + relativeToCoordinates.getHeight();
//sas_log_println('-----Attempt #1-------');
	if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
	{
		// 2.  try positioning element right edge aligned with right edge
		// of relativeToElement and top edge aligned with bottom edge of relativeToElement
		proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.TRAILING, sas.TRAILING);
		proposedTop = relativeToCoordinates.getY() + relativeToCoordinates.getHeight();
//sas_log_println('-----Attempt #2-------');		
		if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
		{
			// 3. try positioning elementToPosition left edge aligned with left edge of
			// relativeToElement area and bottom edge aligned with top edge of relativeToElement
			proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.LEADING, sas.LEADING);
			proposedTop = relativeToCoordinates.getY() - elementToPosition.offsetHeight;
//sas_log_println('-----Attempt #3-------');
			if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
			{
				// 3. try positioning elementToPosition right edge aligned with right edge
				// of relativeToElement image and bottom edge aligned with top edge of relativeToElement
				proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.TRAILING, sas.TRAILING);
				proposedTop = relativeToCoordinates.getY() - elementToPosition.offsetHeight;
//sas_log_println('-----Attempt #4-------');
				if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
				{
					// 5. try positioning elementToPosition right edge aligned with left edge
					// of relativeToElement and center aligned with center of relativeToElement
					proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.LEADING, sas.TRAILING);
					proposedTop = relativeToCoordinates.getY() - (elementToPosition.offsetHeight/2) + (relativeToCoordinates.getWidth()/2);
//sas_log_println('-----Attempt #5-------');
					if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
					{
						// 6. try positioning elementToPosition left edge aligned with right edge
						// of relativeToElement and center aligned with center of relativeToElement
						proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.TRAILING, sas.LEADING);
						proposedTop = relativeToCoordinates.getY() - (elementToPosition.offsetHeight/2) + (relativeToCoordinates.getWidth()/2);
//sas_log_println('-----Attempt #6-------');
						if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
						{
							// 7. last ditch effort... Could probably write another attempt that tries to figure out how much space is available
							// on each side of the relativeToELement and optimize where the elementToPosition goes for the last try. 
							// Just dump it at our ideal spot (#1) even though it will clip
							proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.LEADING, sas.LEADING);
							proposedTop = relativeToCoordinates.getY() + relativeToCoordinates.getHeight();
//sas_log_println('-----Attempt #7 (Last Ditch)-------');							
						}
					}					
				
				}
				
			}
		}
		
	}			

    elementToPosition.style.left = proposedLeft+'px';
    elementToPosition.style.top = proposedTop+'px';
//sas_log_println('-----End sas_autoPosition-------');

}
/* ------------------------------------------------------------- *
 * --End sas_autoPosition--
 * ------------------------------------------------------------- */


/* ------------------------------------------------------------- *
 * --Begin sas_autoPositionHorizontally--
 *
 *  Attempt to automatically do an absolute positioning on an html
 *  element relative to some other element.  This version
 *  is meant for items which have a preference of positioning 
 *  horizontally relative to an item.  Such items include
 *  submenus or popups off of vertical menus.
 *  If we can't find suitable horizontal alignment then we just
 *  run through as if it weren't a horizontal preference until
 *  we hit a good position or give up.
 *
 * Example Useage...
 *  relativeToElement = document.getElementById("MyImageId");
 *  DivToPosition = document.getELementById("MyPopupMenu");
 *  sas_autoPositionHorizontally(DivToPosition, relativeToELement);
 *
 * ------------------------------------------------------------- */
function sas_autoPositionHorizontally(elementToPosition, relativeToElement)
{
	var relativeToCoordinates = new sas_Coordinates(relativeToElement);
	relativeToCoordinates.setRelativeToSharedBreakoutBox(elementToPosition);	

	// lets try several positions which we would consider ideal and
	// if none of them work without clipping then we'll have to resort to 
	// just dumping it somewhere (0,0)??
	

	// 1. try positioning elementToPosition left edge indented from right edge
	// of relativeToElement and top edge aligned with top edge of relativeToElement.
	var proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.TRAILING, sas.LEADING);
	var proposedTop = relativeToCoordinates.getY();

	if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
	{
		// 2.  try positioning element right edge indented from left edge
		// of relativeToElement and top edge aligned with top edge of relativeToElement
		proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.LEADING, sas.TRAILING);
		proposedTop = relativeToCoordinates.getY();
		
		if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
		{

			// 3. try positioning elementToPosition left edge indented from right edge
			// of relativeToElement and bottom edge aligned with top edge of relativeToElement.
			proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.TRAILING, sas.LEADING);
			proposedTop = relativeToCoordinates.getY() - elementToPosition.offsetHeight;

			if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
			{

				// 4.  try positioning element right edge indented from left edge
				// of relativeToElement and bottom edge aligned with top edge of relativeToElement
				proposedLeft = sas_alignSides( relativeToCoordinates, elementToPosition, sas.LEADING, sas.TRAILING);
				proposedTop = relativeToCoordinates.getY() - elementToPosition.offsetHeight;
				
				if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true)
				{
					// if the preferred submenu alignments didn't work go through
					// the position posibilities for non submenus. 
					sas_autoPosition(elementToPosition, relativeToElement);
					return;
				}
			}
		}
		
	}			

    elementToPosition.style.left = proposedLeft+'px';
    elementToPosition.style.top = proposedTop+'px';


}
/* ------------------------------------------------------------- *
 * --End sas_autoPositionHorizontal--
 * ------------------------------------------------------------- */


/* ------------------------------------------------------------- *
 * --Begin sas_checkClipping--
 *
 *  js function to try and determine whether placing a given html element
 *  at a specified set of absolute coordinates would cause the element
 *  to be clipped.
 *
 * Example Useage...
 *  if(sas_checkClipping(palette, proposedLeft, proposedTop)==true)
 *    this would clip so figure out somewhere else to place it.
 *
 * ------------------------------------------------------------- */
function sas_checkClipping(element, proposedLeft, proposedTop)
{
	// TODO should revist much of this with xhtml transitional in mind. It was all done when we 
	// were in quirks mode.
	var clip=false;

    var elementWidth = element.offsetWidth;
    var elementHeight = element.offsetHeight;

	var windowWidth;
	var windowHeight;

	if (window.innerHeight) // Mozilla
	{
		windowWidth=window.innerWidth;
		windowHeight=window.innerHeight;
	}
	else  // IE
	{
		// http://javascriptkit.com/javatutors/static2.shtml
		var iebody=(document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;	
		windowWidth = iebody.clientWidth;
		windowHeight = iebody.clientHeight;
	}
	
	// proposedLeft and proposedTop coming in are likely relative
	// to elements breaking box. For clipping check we need to 
	// convert proposedLeft and proposedTop to be relative to the
	// entire page.  so.. Get breaking box of element. and then
	// get that breaking boxes coordinates relative to the entire
	// document.  add that height and width to proposedTop and proposedLeft
	var breakoutBox = sas_getBreakoutBox(element);
	if(breakoutBox!=null)
	{
		// Note: the above statement about clipping should be checked
		// relative to entire page is generally true unless element
		// lives in a breaking box that can itself cut off content such
		// as overflows of clip, scroll, etc... so need to not adjust
		// to entire page if this is the case.
		if(sas_getCurrentStyle(breakoutBox,'overflowY')!='auto')
//		if(breakoutBox.currentStyle.overflowY!='auto')
		{
			var breakoutBoxCoordinates = new sas_Coordinates(breakoutBox);
			breakoutBoxCoordinates.setRelativeToPage();
			
			proposedLeft += breakoutBoxCoordinates.getX();
			proposedTop += breakoutBoxCoordinates.getY();
		}
	}
	    
	if(xIE7)
		proposedLeft += sas_adjustPositionForRTL();
		
	// Check the scroll state of the main browser window and adjust as needed.
	if(self.pageYOffset ||self.pageXOffset) // firefox
	{
		proposedTop = proposedTop - self.pageYOffset;
		proposedLeft = proposedLeft - self.pageXOffset;
	}
	else // explorer
	{
		var x=0;
		var y=0;
		// Explorer 6 Strict
		if (document.documentElement && (document.documentElement.scrollTop || document.documentElement.scrollLeft))
		{
			x = document.documentElement.scrollLeft;
			y = document.documentElement.scrollTop;
		}
		else
		if (document.body) // all other Explorers
		{
			x = document.body.scrollLeft;
			y = document.body.scrollTop;
		}

		proposedTop = proposedTop - y;
		proposedLeft = proposedLeft - x;
	}

	
	// if element lives in scrollable overflow area then 
	// proposedLeft and proposedTop are likely relative
	// to the virtual area (included nonvisible scroll areas)
	// in checking clipping it needs to be going against
	// the physical area instead so adjust them as needed.
    if (element != null)
    {
	    var parentComponent = element;            
	    while(parentComponent!=null)
	    {
	    
			//*** Note:  the check for scrollTop>0, scrollLeft>0 deals with any scrollable area
			// that is currently scrolled including the main browser window itself.
			//  The subsequent check for overflow settings specifically catches scroll areas OTHER
			// than the main browser window itself.

	    
			// alter proposedTop and proposedLeft to take into account scrolling space.
				// this will treat the proposed top relative to the physical
				// top/left of the overflow area instead of relative to the virtual
				// top/left of the overflow area
//			if(parentComponent.scrollTop>0 || parentComponent.scrollLeft>0)
//			{
//
//				proposedTop = proposedTop - parentComponent.scrollTop;
//				proposedLeft = proposedLeft - parentComponent.scrollLeft;
//			}

				// if the scrollable area is something other than body then
				// we need to alter the window width and height used as
				// clipping checks to be the internal scrollable area 
				// instead of the entire window.
				// settings other than auto creating scrollable areas?
				if(sas_getCurrentStyle(parentComponent,'overflowY')=='auto' ||
				   sas_getCurrentStyle(parentComponent,'overflowX')=='auto')
				{
		        	windowWidth=parentComponent.offsetWidth;
		        	windowHeight=parentComponent.offsetHeight;
	        	
	        	break;
        	}
	        parentComponent = parentComponent.offsetParent;
	    }
	}

	// will it clip on left?
	if(proposedLeft<0)
		clip = true;

	// will it clip on top?
	if(proposedTop<0)
		clip = true;
		
	// will it clip on right side?
	if(proposedLeft+elementWidth>windowWidth)
		clip = true;

	// will it clip on bottom
	if(proposedTop+elementHeight>windowHeight)
		clip = true;

//if(clip)
//	sas_log_println('INVALID!! Proposed=[' +proposedLeft + ', ' + proposedTop + '], Dimensions=['+elementWidth+', '+elementHeight+'], WindowDimensions=[' + windowWidth + ', ' + windowHeight + ']');
//else
//	sas_log_println('Valid Proposed=[' +proposedLeft + ', ' + proposedTop + '], Dimensions=['+elementWidth+', '+elementHeight+'], WindowDimensions=[' + windowWidth + ', ' + windowHeight + ']');	


	return clip;
}
/* ------------------------------------------------------------- *
 * --End sas_checkClipping--
 * ------------------------------------------------------------- */


/* ------------------------------------------------------------- *
 * --Begin sas_Coordinates--
 *
 *  js object for determining absolute coordinates of an
 * html element.  Useful for positioning one element relative
 * to another with absolute position such as popup menus,
 * Calendar popup, Color Picker popup, etc...
 *
 *
 *
 * NOTE:
 * Depending on what you plan on doing with the coordinates of a given element you may want either 
 * coordinates relative to the top/left of the page OR coordinates relative to the top/left of the
 * nearest relatively positioned containing box(breakout box).
 * 
 * This is because an absolutely positioned element withing a breakoutbox (SPAN, DIV, etc..) that
 * has a position of relative or absolute uses coordinates relative to that breakoutbox rather than to the the page.
 * breakoutbox html elements that this is true for seem to differ based on browser.  In IE it appears to
 * be supported fairly widely across the board.  In Mozilla it seems more restricted to things like Spans, Divs
 * and not supported for things like Table, TH, TD, etc...  
 *
 * Adding third case where you want coordinates of element relative to a breakoutbox for a different 
 * element. Take a popup div(p) which you want to position relative to an image (i).  If p lives inside of 
 * different breakoutbox than i then the coordinates of i relative to page or its own breakoutbox do
 * you know good.  In this case you really need to the coordinates of i relative to the first breakoutbox
 * that both i and p have in common.
 *
 *
 * default coordinates will be relative to parent breakoutbox.
 *
 * Example Useage getting coordinates relative to parent breakoutbox.
 *	var coords = new sas_Coordinates(document.getElementById("someelement"));
 *  coords.getX()
 *  coords.getY()
 *  coords.getWidth()
 *  coords.getHeight()
 *
 * Example Useage getting coordinates relative to page.
 *	var coords = new sas_Coordinates(document.getElementById("someelement"));
 *  coords.setRelativeToPage();
 *
 * Example Useage getting coordinates relative to first breakoutbox shared with
 * some other element.
 *	var coords = new sas_Coordinates(document.getElementById("someimage"));
 *  coords.setRelativeToSharedBreakoutBox("menudiv");
 *
 *  Parameters:
 *     element - the html element you want to get coordinates for
 *
 * ------------------------------------------------------------- */
function sas_Coordinates(element)
{
	this.element=element;
	
	// determine absolute coordinates of the element
	var elementLeft=0;
	var elementTop=0;

    if (element != null)
    {
	    var parentComponent = element;            
	    while(parentComponent!=null)
	    {
			// need to deal with horizontal overflows as well and
			// also deal with anytime it creates a separate container box. 
			// Settings other than auto could trigger this such as "scroll".
		if(sas_getCurrentStyle(parentComponent,'overflowY')=='auto')
//	        if(parentComponent.currentStyle.overflowY=="auto")
	        	break;

			// as we walk up the chain if we find an appropriate item with relative positioning then
			// don't count it and stop immediately so that we return coordinates relative
			// to this relative containing box
			if(sas_getCurrentStyle(parentComponent,'position')=='relative' || sas_getCurrentStyle(parentComponent,'position')=='absolute')
			{
				// IE elements which honor 'relative' position
				if(xIE)
					break;
					
				// Mozilla elements which honor 'relative' position
				if(parentComponent.tagName=='SPAN' || parentComponent.tagName=='DIV')
					break;
			}
			
	        elementLeft = elementLeft + parentComponent.offsetLeft;
	        elementTop = elementTop + parentComponent.offsetTop;
	        parentComponent = parentComponent.offsetParent;
	        
	        
	    }
        if (parentComponent==null && !xIE7)
            elementLeft += sas_adjustPositionForRTL();
	}

	this.x=elementLeft;
	this.y=elementTop;
	this.init();

	return this;
}	

function sas_Coordinates_init()
{
    if (!this.element) return;
	if(xIE)
	{
		this.width=this.element.offsetWidth;
		this.height=this.element.offsetHeight;
	}
	else
	{
		this.width=this.element.offsetWidth;
		this.height=this.element.offsetHeight;
	}
}

function sas_Coordinates_getX()
{
	return this.x;
}

function sas_Coordinates_getY()
{
	return this.y;
}
function sas_Coordinates_getWidth()
{
	return this.width;
}
function sas_Coordinates_getHeight()
{
	return this.height;
}

// comment the caveaut
function sas_Coordinates_setRelativeToSharedBreakoutBox(breakoutBoxChild)
{
	// determine absolute coordinates of the element
	var elementLeft=0;
	var elementTop=0;

	var breakoutBox = sas_getBreakoutBox(breakoutBoxChild);

    if (this.element != null)
    {
	    var parentComponent = this.element;            
	    while(parentComponent!=null && parentComponent!=breakoutBox)
	    {
	        elementLeft = elementLeft + parentComponent.offsetLeft;
	        elementTop = elementTop + parentComponent.offsetTop;
			
			// If any of the intermediary breakouts are scrollable areas then factor in
			// any scrolling that has taken place since it will already be included in offsetLeft
			// but we really don't want it included.
			if(parentComponent.scrollTop>0 || parentComponent.scrollLeft>0)
			{
				elementLeft = elementLeft - parentComponent.scrollLeft;
				elementTop = elementTop - parentComponent.scrollTop;
			}
			
	        parentComponent = parentComponent.offsetParent;
	    }
	}

	this.x=elementLeft;
	this.y=elementTop;
	this.init();
}	

function sas_Coordinates_setRelativeToPage()
{
	// determine absolute coordinates of the element
	var elementLeft=0;
	var elementTop=0;


    if (this.element != null)
    {
	    var parentComponent = this.element;            
	    while(parentComponent!=null)
	    {
	        elementLeft = elementLeft + parentComponent.offsetLeft;
	        elementTop = elementTop + parentComponent.offsetTop;
	        parentComponent = parentComponent.offsetParent;
	    }
	}

    // IE7 doesn't need this for RTL, body.offsetLeft is correct
    if (!xIE7) elementLeft += sas_adjustPositionForRTL();
	this.x=elementLeft;
	this.y=elementTop;

	this.init();
}	

sas_Coordinates.prototype.getX=sas_Coordinates_getX;
sas_Coordinates.prototype.getY=sas_Coordinates_getY;
sas_Coordinates.prototype.getWidth=sas_Coordinates_getWidth;
sas_Coordinates.prototype.getHeight=sas_Coordinates_getHeight;
sas_Coordinates.prototype.setRelativeToSharedBreakoutBox=sas_Coordinates_setRelativeToSharedBreakoutBox;
sas_Coordinates.prototype.setRelativeToPage=sas_Coordinates_setRelativeToPage;
sas_Coordinates.prototype.init=sas_Coordinates_init;

/* ------------------------------------------------------------- *
 * --End sas_Coordinates--
 * ------------------------------------------------------------- */

// $$$ Use browser API
function sas_Coordinates2(element)
{
    this.element = element;
    if (document.getBoxObjectFor)
    {
        var box = document.getBoxObjectFor(element);
        this.x      = box.x;
        this.width  = box.width;
        this.y      = box.y;
        this.height = box.height;
    }
    else if (element.getBoundingClientRect)
    {
        var box = element.getBoundingClientRect();
        this.x      = box.left;
        this.width  = box.right - box.left;
        this.y      = box.top;
        this.height = box.bottom - box.top;
    }
    else
    {
        sas_Coordinates.call(this, element);
        return;
    }
    var scrollLeft= window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
    this.x += scrollLeft;
    this.y += scrollTop;
}
sas_Coordinates2.prototype.getX=sas_Coordinates_getX;
sas_Coordinates2.prototype.getY=sas_Coordinates_getY;
sas_Coordinates2.prototype.getWidth=sas_Coordinates_getWidth;
sas_Coordinates2.prototype.getHeight=sas_Coordinates_getHeight;
sas_Coordinates2.prototype.setRelativeToSharedBreakoutBox=sas_Coordinates_setRelativeToSharedBreakoutBox;
sas_Coordinates2.prototype.setRelativeToPage=sas_Coordinates_setRelativeToPage;
sas_Coordinates2.prototype.init=sas_Coordinates_init;
//sas_Coordinates = sas_Coordinates2; // uncomment to use the browser api for coords

/* ------------------------------------------------------------- *
 *  find the container box (breakout box) for this element.
 *  This will walk up the dom tree until it finds an ancestor
 *  which causes absolute child coordinates to be relative to 
 *  the box.  This could walk all the way back up to the root
 *  document.
 * ------------------------------------------------------------- */
function sas_getBreakoutBox(element)
{
    if (element != null)
    {
	    var parentComponent = element;            
	    while(parentComponent.offsetParent!=null)
	    {
	        parentComponent = parentComponent.offsetParent;	    
			if(sas_getCurrentStyle(parentComponent,'overflowY')=='auto')
//	        if(parentComponent.currentStyle.overflowY=="auto")
	        	break;

			if(sas_getCurrentStyle(parentComponent,'position')=='relative' || sas_getCurrentStyle(parentComponent,'position')=='absolute')
			{
				// IE elements which honor 'relative' position
				if(xIE)
					break;
					
				// Mozilla elements which honor 'relative' position
				if(parentComponent.tagName=='SPAN' || parentComponent.tagName=='DIV')
					break;
			}
	    }
	}
	
	return parentComponent;
}


/* ------------------------------------------------------------- *
 * --Begin Javascript logging helpers--
 *
 * js functions for helping log from javascript without using
 * alerts.  This will open a separate window and dump all log
 * messages to this window instead of opening alert dialog.
 *
 * Example Useage:
 *
 * // print a simple debug message
 * sas_log_println("Hey I'm logging a message");
 *
 * // print all the elements and their values for a form.
 * sas_log_printFormElements(someform);
 *
 * // print all the properties for a given element.
 * sas_log_printObjectProperties(somediv, "Popup_Div");
 *
 * ------------------------------------------------------------- */
var sas_log = null;		    	//handle to log window
var sas_logging_enabled=true;  //to log or not to log... that is the question!

function sas_log_enable(enable)
{
	if (sas_logging_enabled != enable)
	{
		sas_logging_enabled = enable;
	}
}

function sas_log_println(msg, level)
{
	if(sas_logging_enabled==true)
	{
	    if (typeof console != "undefined"  &&  console.info)
	    {
	        if (!level) level=-1;
	        else msg = sas_Log.levelName[level] + ": " + msg;
	        switch (level)
	        {
	            case 1: console.error(msg); break;
	            case 2: console.warn(msg); break;
	            case 3: console.info(msg); break;
	            case 4: if (console.debug) console.debug(msg); else console.log(msg); break;
	            default: console.info(msg);
	        }
	    }
	    else
	    {
			if (level) sas_log_getLog().document.write(sas_Log.levelName[level], ": ");
			sas_log_getLog().document.write(msg, "<br>");
		    sas_log_getLog().scrollBy(0,1000000);	//scroll to the end
		}
		window.status = msg;
	}
}

function sas_log_printFormElements(form) 
{
	if(sas_logging_enabled==true)
	{
		var text = "Form["+form.name+"] Elements:<BR>\n";
		
		if (form != null && form.elements == null) 
		{
			//must be the ID for the form try to find the real form object
			form = document.getElementById(form);
		}
		
		if (form != null && form.elements != null) 
		{
			var elText;
			for (i=0; i<form.elements.length; i++) 
			{
				elText = "  " + form.elements[i].name + " = " + form.elements[i].value;
				text = text + elText + "<BR>\n";
			}
		}
		
		if (text.length > 0) 
		{
			sas_log_println(text);
		}
	}
}

function sas_sizeButtonsToWidest(elements)
{
	if (!elements || elements.length < 2)
	{
		return;
	} 
	
	//get the button tags
	var buttons = new Array();
	var buttonIndex = 0;
	
	for(x in elements)
	{
		if(elements[x].tagName && elements[x].tagName =='BUTTON')
		{
			buttons[buttonIndex++] = elements[x];
		}
		else
		{ 
			var button = elements[x].getElementsByTagName("BUTTON");
			if(button[0])
			buttons[buttonIndex++] = button[0];
		}
	}
	
	if(buttons.length >= 2)
	{
		var widestWidth = buttons[0].offsetWidth;
		
		for (x in buttons)
		{
			if(buttons[x].offsetWidth > widestWidth)
			{
				widestWidth = buttons[x].offsetWidth;
			}
		}
		if(widestWidth > 0)
		{
			for (x in buttons)
			{
				buttons[x].style.width = widestWidth +"px";
			}
		}
	}
}

function sas_log_printObjectProperties(obj, obj_name) 
{
	if(sas_logging_enabled==true)
	{
	
		var result = "Object["+obj_name+"] Properties:<br>\n";
		var props = new Array();
		for (var i in obj) 
		{
			props[props.length] = i;
		}
		props.sort();
	
		for (var i =0; i<props.length; i++ ) 
		{
			var p = props[i];
			result += obj_name + "." + p + " = ";
			try 
			{
				var v = obj[p];
				if (v == null)
					result += "<i>null</i><br>\n";
				else

					if (v.replace)
					{
						v = v.replace("<", "&lt;");
						v = v.replace(">", "&gt;");
					}

					result += v + "<br>\n";
			} catch (e) 
			{
				result += "<font color=\"RED\"><b>ERROR:</b> "+e.message+"</font><br>\n";
			}
		}
		sas_log_println(result);
	}
}



function sas_log_getLog()
{
	if (sas_log == null || sas_log.closed == true) 
	{
		sas_log = window.open("", "sas_log", "width=600,height=500,left=0px,top=0px,resizable,scrollbars=yes,status=0");
//		sas_log.document.open("text/plain");	//set document type to plain text?
		sas_log.document.bgcolor = "#FFCC00";
		sas_log.document.title = "SAS JavaScript Log";
	}
	return sas_log;
}

var sas_Log = {
    NONE: 0, ERROR: 1, WARNING: 2, INFO: 3, DEBUG: 4,
    levelName: ["NONE", "ERROR", "WARNING", "INFO", "DEBUG"],
    level: 1,
    error:   function(msg) {sas_Log.log( sas_Log.ERROR, msg);},
    warning: function(msg) {sas_Log.log( sas_Log.WARNING, msg);},
    info:    function(msg) {sas_Log.log( sas_Log.INFO, msg);},
    debug:   function(msg) {sas_Log.log( sas_Log.DEBUG, msg);},
    log: function(level, msg) {
        if (sas_Log.level>=level)
            sas_log_println(msg, level);
    }
}
if (typeof console != "undefined"  &&  console.info) sas_Log.level = sas_Log.WARNING;

/* ------------------------------------------------------------- *
 * --End Javascript logging helpers--
 * ------------------------------------------------------------- */

/* ------------------------------------------------------------- *
 * emulate parent.contains(child)
 * 
 * DEPRECATED:  Use sas.dom.containsNode instead
 * ------------------------------------------------------------- */
function sas_DOMContains( parent, child )
{
    if (!parent || !child) return false;
    if (parent.contains) // ie
        return parent.contains(child);
    if (parent.compareDocumentPosition) // w3c
        return !!(parent.compareDocumentPosition(child) & 16);
    // do it ourselves...
    if (parent==child) return false;
    while (child !=null && child != parent)
        child = child.parentNode;
    return child != null;
}


/* ------------------------------------------------------------- *
 * Swap two nodes in dom tree.  Currently there isn't good
 * cross browser support for the built in swapNode
 * 
 * DEPRECATED:  Use sas.dom.swapNodes instead
 * ------------------------------------------------------------- */
 function sas_swapNodes(node1, node2)
 {
	// if swapNode natively supported then use it.
	// At this time IE supports swapNode but Firefox doesn't
    if(node1.swapNode)  
       node1.swapNode(node2);
	else
	{
		var node1Parent = node1.parentNode;
		var node2Parent = node2.parentNode;
	
		// make a copy of node 2
		node2Copy = node2.cloneNode(true);
		
	    // replace node2 with the copy to act as a placeholder so that
	    // we don't end up with two node2 references in the dom after we
	    // replace node1 with the real node2.
	    node2Parent.replaceChild(node2Copy, node2);
	        
		// replace node 1 with node 2
		node1 = node1Parent.replaceChild(node2,node1);
		
		// replace the placeholder copy of node 2 with node 1
		node2Parent.replaceChild(node1,node2Copy);
	}	
}

/* ------------------------------------------------------------- *
 * Replace dom node with another.  Currently there isn't good
 * cross browser support for the built in replaceNode.
 *
 * DEPRECATED:  Use sas.dom.replaceNode instead 
 * ------------------------------------------------------------- */
function sas_replaceNode(originalNode, replacementNode)
{
	    if(originalNode.replaceNode)
	    	originalNode.replaceNode(replacementNode);	    
	    else
	    {
	    	originalNode.parentNode.replaceChild(replacementNode, originalNode);
	    }
}




function sas_dispatchEvent(target, type)
{
    if (target.dispatchEvent && document.createEvent)
    {
        var event = document.createEvent("HTMLEvents");
        //event.initEvent("click","true","true");
        event.initEvent(type,true,true);
        target.dispatchEvent(event);
    }
    else
        target.fireEvent("on"+type);

}
function sas_getComponent( name )
{
    var comp;

    comp = document.getElementById(name);
    if (comp==null)
    {
        try {
            comp = eval(name);
        } catch (e) {}
    }
    return comp;
}
function sas_stopPropagation(event)
{
	if(event==null)event=window.event;
		
    if (event.stopPropagation) event.stopPropagation(); // w3c
    event.cancelBubble = true; // ie
}
function sas_preventDefault(event)
{
	if(event==null)event=window.event;
		
    if (event.preventDefault) event.preventDefault(); // w3c
    event.returnValue = false; // ie
}
function sas_killEvent(event)
{
	sas_stopPropagation(event);
	sas_preventDefault(event);
}
// see http://developer.mozilla.org/en/docs/DOM:event.keyCode
function sas_keyCode(event)
{
    if (event.keyCode) return event.keyCode;
    if (event.which) return event.which;
    if (event.charCode) return event.charCode; // honors shift-key. sometimes available in mozilla?
    return null;
}
// Some constants for keycodes
var sasKeyCode =
{
    LEFT: 37,
    UP: 38,
    RIGHT: 39,
    DOWN: 40,
    SPACE_BAR: 32,
    ENTER: 13,
    ESCAPE: 27,
    TAB: 9,
    LETTERBASE: 65
};

// see http://developer.mozilla.org/en/docs/DOM:event.target
function sas_srcElement(event)
{
    if (event.srcElement) return event.srcElement;
    if (event.target) return event.target;
    return null;
}

function sas_setFocus(element)
{
    if (element!=null && element.focus)
    {
        window.setTimeout(function() {element.focus();}, 0);
    }
}

function sas_getValue(ele, valueHandler, caller, args) {
	if (!ele)
		return;
	var nodeName = ele.nodeName;
	if (!nodeName && ele.getValue) {
		return ele.getValue(valueHandler, caller, args);
	}
	else (!nodeName)
		return ele.innerHTML;
	switch (nodeName.toLowerCase()) {
		case "textarea":
			return ele.value;
		case "select":
			if (ele.type.toLowerCase() == "multiple") {
				var values = new Array;
				var options = ele.options;
				var i = 1;
				while (i <= options.length)
				{
					if (options[i].selected)
						values.push(options[i].value);
					i++;
				}
				return values;
			}
			else
				return ele.value;
		case "input":
			switch (ele.type) {
				case "checkbox":
				case "check-box":
				case "radio":
					return ele.checked;
				default:
					return ele.value;
			}
		default:
				return ele.innerHTML;
	}
}

function sas_setValue(ele, value) {
	if (!ele)
		return false;
	var nodeName = ele.nodeName;
	if (!nodeName && ele.setValue)
		return ele.setValue(value);
	switch (nodeName.toLowerCase()) {
		case "textarea":
			ele.value = value;
			break;
		case "select":
			var options = ele.options;
			for (i = 0; i < options.length; i++) {
				if (options[i].value == value)
					options[i].selected = true;
				else
					options[i].selected = false;
			}
			break;
		case "input":
			switch (ele.type) {
				case "checkbox":
				case "check-box":
				case "radio":
					ele.checked = (value == true);
					break;
				default:
					ele.value = value;
					break;
			}
			break;
		default:
				ele.innerHTML = value;
				break;
	}

}

function sas_isOnParent(parentId, search) {
	return sas_DOMContains( document.getElementById(parentId), search );
/*
   	  if (!search) return false;
   	  elem = search;
   	  try {
   	  	while ( (elem.nodeName != "BODY") ||  (elem.nodeName != "HTML"))
   	  	{
   	  		if (elem.id == parentId)
   	  			return true;
   	  		elem = elem.parentNode;
   	  	}
   	  }
   	  catch (e) {return false;} 
   	  return false;
   	  */
}

function sas_getJavaScriptObject(objId)
{

    //if (window[objId]) return window[objId];
    //if (eval(objId)) return eval(objId);

    if (window[objId+"_Obj"]) return window[objId+"_Obj"];
    try {
        sas_Log.debug("sas_getJavaScriptObject could not find " + objId + ", trying eval");
        return eval(objId+"_Obj");
    } catch (ex) {sas_Log.info("sas_getJavaScriptObject could not find " + objId);}
    return null;

}

function sas_mod(divisee,base) {
	return Math.round(divisee - (Math.floor(divisee/base)*base));
}

function sas_inherits(subclass, superclass)
{
	subclass.prototype = new superclass();
	subclass.prototype.constructor = subclass;
	subclass.superclass = superclass.prototype;

}

/**
 * Add a map of request parameters to a url.
 * Each property on the requestParms object will be added
 * with the property name as the request name
 * and the property value as the request value.
 * A value which is an array will result in multiple parameters
 * for the same name.
 * eg. {a1: 1, a2: [2,3,4]}
 * will result in "?a1=1&a2=2&a2=3&a2=4" being added to the url. 
 */
function sas_addRequestParams(url, requestParms)
{
    if (!requestParms) return url;
    for (var parm in requestParms)
    {
        var val = requestParms[parm];
        if (sas_isArray(val))
        {
            for (var i=0; i<val.length; i++)
                url = sas_addSingleRequestParam(url, parm, val[i]);
        }
        else
            url = sas_addSingleRequestParam(url, parm, val);
    }
    return url;
}
/**
 * Add a single request parms and value to a url
 */
function sas_addSingleRequestParam(url, parm, val)
{
    var first = url.indexOf("?")<0;
    url += (first? "?":"&") + parm + "=";
    if (val==null) return url;
    
    // encode and add val if present
    var encodedValue = encodeURIComponent(val);
    url += encodedValue;
    return url;
}


/**
 * Include dojo.js if and only if it hasn't already been loaded by someone else
 * This js helper is used by JavaScriptIncludeHandler. It can be used 
 * independantly but if going through JSIH you will only see one call to 
 * this function from any given page.
 */
function sas_includeDojo(dojoURI)
{
	if(typeof dojo=="undefined")
	{
		sas.requires("dojo", dojoURI);
		//document.write("<script type='text/javascript' src='"+dojoURI+"'></script>");
	}
}
/**
 * Disable default browser context menus
 */
function sas_disableBrowserContextMenus()
{
	document.oncontextmenu=function(event)
	                       {
	                       		if(event==null)event=window.event;

								// If control key was pressed while right clicking then
								// allow browser context menu to shine through.  Otherwise
								// suppress it.
	                       		if(event.ctrlKey==false)
	                       		{
									if (event.preventDefault) 
										event.preventDefault()
									else
										event.returnValue=false;  // IE
	
	                       			return false;
	                       		}
	                       		else
	                       			return true;
	                       }
}

function sas_isArray(a) {
    return sas_isObject(a) && a.constructor == Array;
}
function sas_isBoolean(a) {
    return typeof a == 'boolean';
}
function sas_isEmpty(o) {
    var i, v;
    if (sas_isObject(o)) {
        for (i in o) {
            v = o[i];
            if (sas_isUndefined(v) && sas_isFunction(v)) {
                return false;
            }
        }
    }
    return true;
}
function sas_isFunction(a) {
    return typeof a == 'function';
}
function sas_isNull(a) {
    return a === null;
}
function sas_isNumber(a) {
    return typeof a == 'number' && sas_isFinite(a);
}
function sas_isObject(a) {
    return (a && typeof a == 'object') || sas_isFunction(a);
}
function sas_isString(a) {
    return typeof a == 'string';
}
function sas_isUndefined(a) {
    return typeof a == 'undefined';
} 

/* Function that attempts to get the "Windows and Buttons" and "Color Scheme" settings in the 
 * Display Properties for the client's operating system.  This is done by creating a "Button" element,
 * adding it to the current document, and checking the button's background color.
 *
 * This function takes an object as a parameter.  This object must be some object in the document
 * that the button can be appended to in order to get the document settings.  The button will be removed
 * from the object at the end of this routine.
 *
 * Known issues:
 *				 (1)  Unable to determine between XP Olive and Blue.  Blue and Olive produce the same default settings, so
 *					  currently this routine always defaults to blue.
 *				 (2)  If a custom global background color style has been specified for buttons, something like:
 *
 *							<style>
 *									button { background-color:red }
 *							</style>
 *
 *					  this routine will return the default of XP Blue.  
 *
 * This function returns a style class suffix that is added to a style class to distinguish between
 * the different XP and Classic settings.
 *
 * Possible return values are:
 *		_Blue_Olive
 *		_Silver
 *		_Classic
 * 
 */
function sas_getDisplayPropertiesWindowsAndButtonsStyle(_obj)
{
	var testButton = document.createElement("button");
	testButton.id="but2";
	
	_obj.appendChild(testButton);   //Need to add Button to current document
	 									//in order to get current styles
	
	if(testButton.currentStyle) //Windows
	{		
		//Test background color of button to determine if xp blue, silver, olive or classic
		//ece9d8 = xp blue
		//ece9d8 = xp olive
		//e0dfe3 = xp silver
		//d4d0c8 = classic standard
		//c0c0c0 = classic classic
		
		var testBackgroundColor = testButton.currentStyle["backgroundColor"];
		
		if(testBackgroundColor.indexOf("ece9d8") > -1)  //XP Blue or Olive
			styleColor = "_Blue_Olive";
		else if(testBackgroundColor.indexOf("e0dfe3") > -1)  //XP Silver
			styleColor = "_Silver";
		else if(testBackgroundColor.indexOf("d4d0c8") > -1)  //XP Classic Standard and Vista Standard	
			styleColor = "_Standard";
		else if(testBackgroundColor.indexOf("c0c0c0") > -1)  //XP Classic Classic and Vista Classic	
			styleColor = "_Classic";
		else if(testBackgroundColor.indexOf("f0f0f0") > -1)  //Vista Basic	
			styleColor = "_Vista_Basic";
		else										   //Default to XP Blue
			styleColor = "_Blue_Olive";
	}
	else if (window.getComputedStyle) //Firefox
	{
		//Test background color of button to determine if xp blue, silver, olive or classic
		var xp_blue = "rgb(236, 233, 216)";
		var xp_olive = "rgb(236, 233, 216)";
		var xp_silver = "rgb(224, 223, 227)";
		var xp_classic = "rgb(212, 208, 200)";
		var vista_basic = "rgb(240, 240, 240)";
		var vista_classic = "rgb(192, 192, 192)";
		
		var testBackgroundColor = document.defaultView.getComputedStyle(testButton,null).getPropertyValue("background-color");
		
		if(testBackgroundColor.indexOf(xp_blue) > -1)  //XP Blue or Olive
			styleColor = "_Blue_Olive";
		else if(testBackgroundColor.indexOf(xp_silver) > -1)  //XP Silver
			styleColor = "_Silver";
		else if(testBackgroundColor.indexOf(xp_classic) > -1)  //XP Classic Standard and Vista Standard	
			styleColor = "_Standard";
		else if(testBackgroundColor.indexOf(vista_classic) > -1)  //XP Classic Classic and Vista Classic	
			styleColor = "_Classic";
		else if(testBackgroundColor.indexOf(vista_basic) > -1)  //Vista Basic
			styleColor = "_Vista_Basic";
		else										   //Default to XP Blue
			styleColor = "_Blue_Olive";
	}

	_obj.removeChild(testButton);
	return styleColor;
}

function sas_sizeWindowToContent(container) {
    var w = container.offsetWidth;
    var margin = 8;

	//FIX S0349743  FF: IE7: UNEXPECTED TOOLBAR AT TOP OF SELECTORS PARTIALLY TRUNCATES BOTTOM CONTENT
 	//This change looks good for browser security setting "Allow websites to open windows without address or status bars" 
 	//being disabled. We cannot detect security settings hence this goes as a half fix.
 	var ie7_wmargin = 0, ie7_hmargin = 0, chrome_thickness_estimated = 2, status_bar_estimated = 25, title_bar_estimated = 29;
	if(xIE7)
	{	
		ie7_hmargin = 2*chrome_thickness_estimated + status_bar_estimated + title_bar_estimated; 
		ie7_wmargin = 2*chrome_thickness_estimated;
	}
    //END FIX S0349743  FF: IE7: UNEXPECTED TOOLBAR AT TOP OF SELECTORS PARTIALLY TRUNCATES BOTTOM CONTENT
 


    // outerWidth is a Firefox int, rightMargin is a IE string
    if (window.outerWidth)
        margin = window.outerWidth - window.innerWidth;
    else
        margin = parseInt(document.body.rightMargin) + parseInt(document.body.leftMargin) -6 
        			+ ie7_wmargin;//Addition of width margin for IE7.0
    
    w += margin;

    var h = container.offsetHeight;
    margin = 8;
    if (window.outerHeight)
        margin = window.outerHeight - window.innerHeight;
    else
        margin = parseInt(document.body.topMargin) + parseInt(document.body.bottomMargin) + 10
        			+ ie7_hmargin;//Addition of height margin for IE7.0;
    h += margin;

	// This is a quick-n-dirty "fix" for defect S0261853.  Until we get this issue
	// resolved we will sit on any window.resizeTo exceptions.  Might result in window
	// not being correct size in limited cases but at least prevents user from seeing
	// javascript error.
	// IE 6 SP2 fixed above mentioned issue.  Still leaving try/catch in place as it doesn't hurt to have it
	try
	{
	    var w0 = document.body.offsetWidth; var h0 = document.body.offsetHeight;
//        if (window.sizeToContent)
//            window.sizeToContent();
//        else
	        window.resizeTo( w, h );
		sas_Log.info("after resize: " + document.body.offsetWidth + ", " + document.body.offsetHeight + " - " + w0 + ", " + h0);
	}
	catch(exception)
	{
	}
    window.focus();	//raise the window to the front - without this, the window could be lost behind other open windows
}

/**
 * Has the element been removed from the page?
 * Used for cleaning up objects which are no longer valid
 * by DragAndDrop and Resizing.
 */
function sas_isElementRemoved(el)
{
  try {
    if (!el)
        return true;
    // IE, compare el.document
    if (typeof el.document != "undefined")
    {
        if (el.document==null) return false; // has not been added to page yet
        if (el.document != document)
            return true;
        return false;
    }
    // Firefox, walk up dom looking for el.ownerDocument
    var curEl = el;
    while (curEl != null)
    {
        curEl = curEl.parentNode;
        if (curEl == el.ownerDocument)
            return false;
    }
    return true;
  } catch (e) {
    sas_Log.warning("sas_isElementRemoved(el) threw error " + e.message + ", removing");
    return true;
  }
}
/**
 * Check if an element been removed from the page,
 * using a cache to save related results
 * Used for cleaning up objects which are no longer valid
 * by DragAndDrop and Resizing.
 */
function sas_needsCleanup(id, status)
{
    // 0 = ok, 1 = bad
    if (status[id]==0) return false; // already flagged ok
    if (status[id]==1) return true; // already flagged bad

    var el = document.getElementById(id);
    var st = sas_isElementRemoved(el);
    if (!st)
    {
        status[id] = 0; // flag as good
        return false;
    }
    status[id] = 1; // flag as bad
    return true;
}


/* ------------------------------------------------------------- *
 * When an element is disabled via sas_disable we alter all the
 * child elements which support tabIndex settings to be -1 so
 * that the area is disabled from keyboard as well as mouse
 * activity.  When an element is re-enabled via sas_enable we
 * need to put all the child tabIndexes back the way they were
 * before we disabled them.  
 * This object is a placeholder for storing tabindex by elementid
 * for every elementId disabled there will be a property of the
 * same name in this object.  That property will hold an 
 * object/associative array of tab indexes.
 * ------------------------------------------------------------- */
sas_disable_tabIndexHolder = new Object();

/*
*  If user resizes the window then recalculate the positioning
*  of any disabling elements so that the thing its disabling
*  doesn't slide out from underneath it.
*  This takes advantage of the fact that sas_disable_tabIndexHolder
*  happens to be a keyed list of current disabled element ids.
*/
sas_registerEventHandler(window, 'resize', function(evt)
{
	for (disabledElementId in sas_disable_tabIndexHolder)
	{
		// redisable/position if its an actively disabled item
		if(sas_disable_tabIndexHolder[disabledElementId]!=null)
				sas_disable(disabledElementId);
	}
}, false);   

/* ------------------------------------------------------------- *
 * Accepts an html element id and disables everything within said 
 * element by placing a div over it with opacity for graying.
 * This prevents clicks from getting through and give appearance
 * of disabled.  Also goes through all descendants and makes sure
 * they are not in the tab order so that user can't circumvent 
 * disabling via keyboard.
 * ------------------------------------------------------------- */
function sas_disable( elementId )
{
   var elementToDisable = document.getElementById(elementId);
   var disablingDiv = null;
   if (elementToDisable != null)
   		disablingDiv = document.getElementById(elementToDisable.id+"_disabler");
   if(disablingDiv==null && elementToDisable != null )
   {

	   // Can't do a document.body.appendChild node until the page has finished
	   // loading. IE will crash and Firefox will just not have coordinates
	   // available yet.

	   // We really should only need to check sas.isPageFinishedLoading() here BUT
	   // since we are just beginning to expect that people use Components.init which
	   // is what builds the sas namespace and includes the isPageFinishedLoading function
	   // then we want to go ahead and add the disabler node even if that
	   // init hasn't been completed which lets us know whether page has loaded or not.
	   if(typeof sas=='undefined' || typeof sas.isPageFinishedLoading=='undefined' ||
    	  sas.isPageFinishedLoading()==true)
	   {
	       /*
	        * if disabling div/iframe don't exist then create
	        *   them and add them to the document 
	       */
		   disablingDiv = document.createElement("DIV");
		   disablingDiv.id=elementToDisable.id+"_disabler";
		   disablingDiv.className="ElementDisabler";

		   // Since we are no longer putting the disabling node on document.body we need
		   // to make sure we kill any events so they don't bubble up and get caught...
		   disablingDiv.onclick=function(event){sas_killEvent(event);};
		   disablingDiv.onmousedown=function(event){sas_killEvent(event);};		   
		   disablingDiv.onmouseup=function(event){sas_killEvent(event);};		   
		   
		   if(elementToDisable)
		   		parentEl = elementToDisable.parentNode;
		   else
		   		parentEl = document.body;
		   parentEl.appendChild(disablingDiv);
	   }
	   else
	   {
		   sas_registerEventHandler(window, 'load', function(evt){ sas.setPageFinishedLoading(true); sas_disable(elementId);}, false);
  		   return;
		}
	}
		

   if (elementToDisable) 
   {
   	  var selects = elementToDisable.getElementsByTagName("select");
   	  for (var i = 0; (select = selects[i]); i++) 
   	  {
   		select.disabled = true;
   	  }
   }

   if(disablingDiv)
   {
	   elementToDisableCoords = new sas_Coordinates(elementToDisable);
	   disablingDiv.style.top = elementToDisableCoords.getY()+"px";
	   disablingDiv.style.height = elementToDisableCoords.getHeight()+"px";
	   disablingDiv.style.left = elementToDisableCoords.getX()+"px";
	   disablingDiv.style.width = elementToDisableCoords.getWidth()+"px";   
   	   disablingDiv.style.display='block';
	   
   		//Added to solve the tabbing issue
   		var tabIndexHolder = sas_disable_tabIndexHolder[elementId];	 
		if(tabIndexHolder)
			return;
	   
	   
       // known tabs that support tabIndex property
	   var tabbableTags = new Array("A","BUTTON","INPUT","TEXTAREA","SELECT","TABLE");

       // as we go through and set all the tabIndex values to -1 store
       // the original values in this placeholder to be restored when
       // element is re-enabled.
       var tabIndexHolder = new Object();
       
	   for (var tag in tabbableTags) 
	   {
	   	  tabIndexHolder[tag] = new Object();
	      var tabbableElements = elementToDisable.getElementsByTagName(tabbableTags[tag]);

          // not just child elements but the container element as well if it is tabbable
	      if(elementToDisable.tagName==tabbableTags[tag])
 	      	tabbableElements[tabbableElements.length] = elementToDisable;

	      for (var element in tabbableElements) 
	      {
	        if(tabbableTags[tag] == 'TABLE'){// if this is for table tag.
				if(tabbableElements[element].attributes 
					&& tabbableElements[element].attributes['tabIndex'] 
					&& tabbableElements[element].attributes['tabIndex'].specified){
					// preserve its previous tab index for when its re-enabled.  Unless its tab index is -1
					// in which case enable will set it to 0 for lack of a more specific tabindex.
					tabIndexHolder[tag][element] = tabbableElements[element].tabIndex;
					tabbableElements[element].tabIndex="-1";  // remove from tab order.
   				}
	        }else{ // otherwise
				// preserve its previous tab index for when its re-enabled.  Unless its tab index is -1
				// in which case enable will set it to 0 for lack of a more specific tabindex.
					tabIndexHolder[tag][element] = tabbableElements[element].tabIndex;
		  			tabbableElements[element].tabIndex="-1";  // remove from tab order.
	        }
	      }
	   }
	   
       // store this set of original tab indexes in the global holder by id of
       // element being disabled.
 	   sas_disable_tabIndexHolder[elementId]=tabIndexHolder;
   }
   else
   {
	   /*
	    * If we had production level javascript logging this would be a good spot
	    * to log something if the disabling div doesn't exist
	   */
   }
}

/* ------------------------------------------------------------- *
 * Accepts an html element and checks to see whether a disabling
 * div has been put in place for that element via sas_disable.
 * if disabling div is in place then it hides the disabler giving
 * element the appearance of being enabled again.  It also restores
 * all the tab order information that was tucked away when
 * html element was disabled.
 * ------------------------------------------------------------- */
function sas_enable( elementId )
{
   var elementToEnable = document.getElementById(elementId);
   var disablingDiv = document.getElementById(elementToEnable.id+"_disabler");

   if (elementToEnable) {   
   	var selects = elementToEnable.getElementsByTagName("select");
   	for (var i = 0; (select = selects[i]); i++) {
   	   	select.disabled = false;
   	}
   }
   // If disabling div doesn't exist then there is nothing to enable.
   if(disablingDiv)
   {
       // hide the disabling div.
   	   disablingDiv.style.display='none';
 
       // known tabs that support tabIndex property
	   var tabbableTags = new Array("A","BUTTON","INPUT","TEXTAREA","SELECT","TABLE");

       // retrieve the original tab indexes from when this element was
       // disabled.
	   var tabIndexHolder = sas_disable_tabIndexHolder[elementId];	   

       // if no tab index holder present then there must have been
       // no tab indexes to persist when element was disabled.
	   if(tabIndexHolder)
	   {
		   for (var tag in tabbableTags) 
		   {
		      var tabbableElements = elementToEnable.getElementsByTagName(tabbableTags[tag]);

              // not just child elements but the container element as well if it is tabbable
	          if(elementToEnable.tagName==tabbableTags[tag])
 	          	tabbableElements[tabbableElements.length] = elementToEnable;

		      for (var element in tabbableElements) 
		      {
       	        if(tabbableTags[tag] == 'TABLE'){// if this is for table tag
     	        	if(tabbableElements[element].attributes 
						&& tabbableElements[element].attributes['tabIndex'] 
						&& tabbableElements[element].attributes['tabIndex'].specified){
       	        			tabbableElements[element].tabIndex=tabIndexHolder[tag][element];  // restore tabIndex
   					}
       	        }else{ // otherwise.
       	        	tabbableElements[element].tabIndex=tabIndexHolder[tag][element];
       	        }
		      }
		   }
		   
	   }
   }
   // remove the tab index state for this element
   sas_disable_tabIndexHolder[elementId]=null;
}

function sas_unEscapeSpecialCharacters(text)
{
	if(text==null)
		return;
	var s = text;
  	s = s.replace(/&amp;/g,"&");
	s = s.replace(/&quot;/g,"\"");
	s = s.replace(/&lt;/g,"<");
	s = s.replace(/&gt;/g,">");
  	return s;

}

function sas_escapeSpecialCharacters(text)
{
	if(text==null)
		return;
	var s = text;
  	s = s.replace(/&/g,"&amp;");
	s = s.replace(/\"/g,"&quot;");
	s = s.replace(/</g,"&lt;");
	s = s.replace(/>/g,"&gt;");
  	return s;

}


/*
 * Returns time zone id in the format GMT+08:00
 */
function sas_getTimeZoneID()
{
	var offsetMinutes = new Date().getTimezoneOffset();
	var timeZone = "GMT";
	if (offsetMinutes > 0){
		timeZone += "-";
	}
	else{
		timeZone += "+";
		offsetMinutes *= -1;
	}
	
	var hours = Math.floor(offsetMinutes/60);
	if (hours < 10){
		hours = "0" + hours;
	}
	var minutes = offsetMinutes%60;
	if (minutes < 10){
		minutes = "0" + minutes;
	}
	
	return timeZone + hours + ":" + minutes;	
}

/*
 * iframe is there to prevent form Selects from bleeding through the palette.  
 * the Select bleeding through issue is specific to IE6.  It doesn't happen in Firefox or IE7.
 * iframe is causing defect S0450613.
 * solution to the iframe in IE6 issue is to not include it in the 
 * template but rather create it dynamically only if in IE6.
 * This function create the iframe element.
 */
function sas_createIframe(id,zIndex,parent){
	/* Should probably check to make sure an iframe with the given id doesn't already exist */
	iframeElement = document.createElement("IFRAME");
	iframeElement.setAttribute("id", id);
	iframeElement.setAttribute("tabIndex","-1");
	iframeElement.setAttribute("frameBorder","0");
	iframeElement.setAttribute("scrolling","no");
	iframeElement.style.display='none';
	iframeElement.style.zIndex=zIndex; 
	iframeElement.style.position='absolute';
	iframeElement.src="javascript:'<html></html>';"; 
	// add this node to the parent if specified.
	if(parent){
		parent.appendChild(iframeElement);
	}else{
		// add this node to the body element.
		document.body.appendChild(iframeElement);
	}
	return iframeElement;
}

/* 
 * Returns true if the direction is 'rtl' (Right To Left)
 * 
 */	
function sas_isRTL() {
	
	return window.document.dir.toLowerCase() == "rtl";
}
/**
 * For RTL, IE positions (0,0) as the upper left corder AFTER
 * scrolling all the way to the right.  When the page is wider
 * than the browser, upper left has a negative x coordinate.
 * This function returns the amount the page has been offset.
 * An extra amount for the scrollbar may optionally be included
 */
function sas_adjustPositionForRTL(includeScrollBar)
{
    if ( !(xIE && sas_isRTL()))
        return 0;
    var rtlNode = document.body.parentNode;
    var dx = rtlNode.scrollWidth - rtlNode.clientWidth;

    sas_Log.debug("adjustForRTL" + dx
        + " : " + rtlNode.scrollWidth + " - " + rtlNode.clientWidth
        );
    if (includeScrollBar)
        dx += 17; // 17 pixels for width of scrollbar
    return dx;
}
/*
 * Set the IE property "hasLayout".
 * This is usually needed to work around IE rendering bugs (CSS hacks),
 * including one for RTL with position: relative
 */
function sas_setHasLayout(node)
{
    if (xIE)
        node.style.zoom = "1";
}

/*
 * side/elementSide == true if leading side
 * xx is the element to position, [===] is the box to position against.
 * L is Leading, T is Trailing
 *  ltr                     rtl
 *  LT     xx[========]     TL
 *  LL       [xx======]     TT
 *  TT       [======xx]     LL
 *  TL       [========]xx   LT
 */
function sas_alignSides( baseCoordinates, elementToPosition, side, elementSide )
{
    var rtl = sas_isRTL();
    if (rtl) {side = !side; elementSide = !elementSide;}

    var left = baseCoordinates.getX();

    // adjust for box width if on right side
    if (!side) // side of baseCoordinates 
        left += baseCoordinates.getWidth();

    // adjust for element width if aligning with right side
    if (!elementSide) // side of element
        left -= elementToPosition.offsetWidth;
    
    return left;
}

/*
 * Positions iframe based on the sib node to which it's providing support.
 */
function sas_PositionNShowIframe(iframe,sibNode){
	iframe.style.left = parseInt(sibNode.offsetLeft)+"px";
	iframe.style.top = parseInt(sibNode.offsetTop)+"px";

    if(sibNode.offsetWidth>0)
		iframe.style.width = (parseInt(sibNode.offsetWidth))+"px";
	else
		iframe.style.width = "0px";
	if(sibNode.offsetHeight>0)
		iframe.style.height = (parseInt(sibNode.offsetHeight))+"px";
	else
		iframe.style.height = "0px";
	iframe.style.display = 'inline';  
}
/*
 * Handle the sas.sessionExpired 
 */
function sas_checkSessionExpired(content) {
	if (content && content.indexOf("sas.sessionExpired") > -1) 
		eval(content);
	if (sas.sessionExpired) {
		if (sas.sessionExpired.message) {
			alert(sas.sessionExpired.message);
		}
		if (sas.sessionExpired.invalidSessionHandlerFunction) {
			if (typeof sas.sessionExpired.invalidSessionHandlerFunction == 'function') {
				sas.sessionExpired.invalidSessionHandlerFunction.call();
			}
			else eval (sas.sessionExpired.invalidSessionHandlerFunction);
		}
		if (sas.sessionExpired.forwardLocation) {
			window.location = sas.sessionExpired.forwardLocation;
		} 
		sas.sessionExpired = false;		
	}
	return false;

}

/**
 * Used to test whether or not a string conforms to a pattern.
 */
function sas_isRegularExpressionValid(regExprString, testStr)
{
	var regExpr = new RegExp(regExprString, "gi");
	return testStr.match(regExpr);
}


function sas_isKeyPressValid(event, regExprString)
{
	if(!event) event = window.event;
	// IE does not have an onkeypress event for editing keys like arrow  
    // and delete, but Firefox does (and backspace is considered an ascii key)
	var ok = (undefined != event.which && (0 == event.which || 8 == event.which));	// true only for Firefox non-graphic key
	if (! ok) {	// Not just an editing key?
		var key = (event.which) ? event.which : event.keyCode;
		var regExpr = new RegExp(regExprString, "gi");
		ok = String.fromCharCode(key).match(regExpr);
    }
    return ok;
	
}

// Stop onkeypress event processing
function sas_stopOnKeyPress(event) {
	if(!event) event = window.event;
	dojo.event.browser.stopEvent(event);
    return false;
}

/* 
 * Scroll a node in a DIV into view (S0473486)
 */
function sas_scrollIntoView(node, div) {
    if (!xIE)
        dojo.html.scrollIntoView(node);
    else if (div.scrollHeight >= div.clientHeight) { // check for vertical scrollbar
        var divCoordinates = new sas_Coordinates(div);
        var divBottom = div.scrollTop + divCoordinates.getHeight();
        var nodeCoordinates = new sas_Coordinates(node);
        var nodeTop = nodeCoordinates.getY();
        var scrollBarHt = divCoordinates.getHeight() - div.clientHeight; // this is an estimate and may be off by a couple pixels
        var nodeBottom = nodeTop + nodeCoordinates.getHeight();
        
        if ((divBottom - scrollBarHt) < nodeBottom)
            // press down
            div.scrollTop -= (divBottom - scrollBarHt - nodeBottom);            
        else if (div.scrollTop > nodeTop)
            // press up
            div.scrollTop = nodeTop;        
    }
}

/*
 *  Compares the number of desired digits (totalDigits) with the length of the 
 *  value being padded, and creates that many instances of zero to its left.
 *
 */
function sas_padDigits(value, totalDigits) 
{ 
    value = value.toString(); 
    var pd = ''; 
    if (totalDigits > value.length) 
    { 
        for (i=0; i < (totalDigits-value.length); i++) 
        { 
            pd += '0'; 
        } 
    } 
    return pd + value.toString(); 
} 

/*
 * Used to center an object in the dom
 */
function sas_centerObject(obj)
{
	if(!obj) return;
	if (document.documentElement){
	 	if (document.documentElement.clientWidth){
			var w = document.documentElement.clientWidth;
			try{
				if ((w - obj.clientWidth) > 0){
					obj.style.left = ((w - obj.clientWidth)/2) + 'px';
				}
				else{
					obj.style.left = '0px';
				}
			}
			catch(e){}
		}
		if (document.documentElement.clientHeight){
			var h = document.documentElement.clientHeight;
			try{
				if ((h - obj.clientHeight) > 0){
					obj.style.top = ((h - obj.clientHeight)/2) + 'px';
				}
				else{
					obj.style.top = '0px';
				}
			}
			catch(e){}
		}
	}
}

   	/* For defect S0616641 which is a hotfix for Bureau Veritas we
    	 * shifted from using a dojo event connect on the td at page load time
    	 * to an oncontextmenu on the span containing the cell text. This was
    	 * necessary to lessen the amount of js being executed for large
    	 * olap tables with popup menus on every cell.
    	 * 
    	 * The downside of that was that at the time we are writing out the
    	 * oncontextmenu the td has already been written.  By putting it on the
    	 * span you have to click only on the text and not in any of the empty
    	 * space around the texxt in the cell.
    	 * 
    	 * To alleviate that in the least risky manner we are writing out this otMoveMenu
    	 * in the oncontextmenu of the td if we think there might be an oncontextmenu on
    	 * an inner span.  otMoveMenu dig into the dom for the inner span and reassign
    	 * this td's oncontextmenu to the same thing as the span and execute it at the same
    	 * time.  The result is the popup menu clicking anywhere in the cell.
    	 */
function otMoveMenu(event,celltd)
{
  var ocm=otCheckChildrenForMenuSetup(celltd);
  if(ocm!=null)
  {
	// reassign td oncontextmenu and then execute it
  	celltd.oncontextmenu=ocm;
  	ocm(event);
  }
  else
  	celltd.oncontextmenu="";
}

function otCheckChildrenForMenuSetup(node)
{

  if(node.oncontextmenu)
  {
	if(node.oncontextmenu.toString().indexOf("_ContextMenu_Setup")!=-1)  
  		return node.oncontextmenu;
  }
  	
  // Walk the DOM looking for any children that have an oncontextmenu
  // setup to create and render a popup menu.  The oncontextmenu function
  // would be of the form *_ContextMenu_Setup*
  for(var i=0;i<node.childNodes.length;i++)
  {
        var ocm= otCheckChildrenForMenuSetup(node.childNodes[i]);
        if(ocm!=null)
           return ocm;
 	
  }
  
  return null;
}

// Also as part of S0616641... Since not all popup menus not go through
// popup attachTo method we don't have the luxury of only turning off
// standard browser right click if we are actually using right click app
// menus but instead need to turn it off globally :(
// We still have ctrl right click for the browser context menu.
sas_disableBrowserContextMenus();


