var _sfImageCache = [];
var _sfControlsData = [];
function SF_DOMBase( m_oParams )
{
	var self = this;
	var UTIL = m_oParams.UTIL;
	
	this.TargetSelf = "_self";
	this.TargetBlank = "_blank";
    
	// Enums
	this.EEventNames = 
	{
	 /* abstract */
	}
	var nMAX = 0;
	this.EExecCommands = 
    {
        BackColor : nMAX++,
        ForeColor : nMAX++,
        SaveAs : nMAX++,
        Cut : nMAX++,
        Copy : nMAX++,
	    Paste : nMAX++,
	    SelectAll : nMAX++,
	    InsertImage : nMAX++,
	    InsertHorizontalRule : nMAX++,
	    UnLink : nMAX++,
	    Bold : nMAX++,
	    Italic : nMAX++,
	    Underline : nMAX++,
	    StrikeThrough : nMAX++,
	    JustifyLeft : nMAX++,
	    JustifyRight: nMAX++,
	    JustifyCenter : nMAX++,
	    JustifyNone : nMAX++,
	    InsertUnorderedList : nMAX++,
	    InsertOrderedList : nMAX++,	    
	    Indent : nMAX++,
	    Outdent : nMAX++,
	    Superscript : nMAX++,
	    Subscript : nMAX++,
	    Justifyfull : nMAX++,
	    RemoveFormat : nMAX++,
	    FontName : nMAX++,
	    FontSize : nMAX++,
	    CreateLink : nMAX++,
	    Undo : nMAX++,
	    Redo : nMAX++,
	    
	    MAX  : nMAX
    };    
    nMAX = 0;
	
	this.ExecCommandIDs =
	[
	    "BackColor",
	    "ForeColor",
	    "saveas",
	    "cut",
	    "copy",
	    "paste",
	    "selectAll",
	    "insertimage",
	    "inserthorizontalrule",
	    "Unlink",
	    "Bold",
	    "italic",
	    "underline",
	    "strikethrough",
	    "justifyleft",
	    "justifyright",
	    "justifycenter",
	    "justifynone",
	    "insertunorderedlist",
	    "insertorderedlist",
	    "indent",
	    "outdent",
	    "superscript",
	    "subscript",
	    "justifyfull",
	    "removeformat",
	    "fontname",
	    "fontsize",
	    "CreateLink",
	    "Undo",
	    "Redo"
	]
	
	this.GetCommandID = function( eCmd )
	{
	    var sRes = self.ExecCommandIDs[ eCmd ];
	    return sRes;
	}
	
	this.IsIE = function()
	{
	    return false;
	}
	
	this.IsMoz = function()
	{
	    return false;
	}
	
	this.IsOpera = function()
	{
	    return false;
	}
	
	this.GetIsDocReady = function( fnPred )
	{
	    var bRes = fnPred ? fnPred() : true;
	    return bRes;
	}	

	this.RegisterOnCreate = function( fn, fnPred )
	{
	    if( self.GetIsDocReady( fnPred ) )
	    {
	        fn();
	    }
	    else
	    {
	        function fnOnLoaded()
	        {
	            self.DetachEvent( self.EEventNames.Load, fnOnLoaded, window );
	            if( fnPred )
	            {
	                fnPred();
	            }
	            fn();
	        }
			self.AttachEvent( self.EEventNames.Load, fnOnLoaded, window );
	    }
	}
	
	this.EventCancel = function( oEv )
	{
	}
	
	this.ControlRefresh = function( oScriptObj, sArg )
	{
	    if( oScriptObj && oScriptObj.callback && oScriptObj.enableCallback )
	    {
	        oScriptObj.callback( "refresh_" + sArg );
	    }
	}
	
	this.IsCompleteMouseOut = function( oEl, oEv )
	{
		return true;
	}
	
	this.IsElContaining = function( oEl, oCont )
	{
		return oCont.contains( oEl );
	}
	
	this.SetControlData = function( oEl, oData )
	{
	    _sfControlsData[ oEl.id ] = oData;
	}
	
	this.GetControlData = function( oEl )
	{
	    return _sfControlsData[ oEl.id ];
	}
	
	this.AddAtlasFeatures = function( oEl, fnDisposeHandler )
	{
		oEl.dispose = function()
		{
		    if( fnDisposeHandler )
		    {
		        fnDisposeHandler();
		    }
		    self.RemoveAtlasFeatures( oEl );
		}

	    // OBSOLETE: used in ATLAS pre September 2006
	    if( !oEl.control )
	    {
	        oEl.control = {};
	    }

	    oEl.control.dispose = oEl.dispose;
		
		oEl.control.get_id = function()
		{
		    return oEl.id;
		}
	}
	
	this.RemoveAtlasFeatures = function( oEl )
	{
	    if( oEl && oEl.control )
	    {
	        oEl.dispose = null;
	        oEl.control.dispose = null;
	        oEl.control.get_id = null;
	        oEl.control = null;
	     }
	}

	this.IsMouseOnEl = function( oEl, oEv )
	{
		var bRes = false;
		
		if( oEl && oEl.tagName &&( null != self.GetParentEl(oEl) ) )
		{
		   var oElRtStl = self.GetRTStyle( oEl );
			if( oElRtStl &&
				( oElRtStl.visibility != "hidden" )&&
				( oElRtStl.display != "none" ) )
			{
				var oOffsetEl = self.GetOffset( oEl, self.GetTopmostLayoutEl() );
				var oOffsetEvent = self.GetOffsetCoordsEvent( oEv );
				var nTop = oOffsetEl.y;
				var nDown = oOffsetEl.y + oEl.offsetHeight;
				var nLeft = oOffsetEl.x;
				var nRight = oOffsetEl.x + oEl.offsetWidth;
				
				var x = oOffsetEvent.x;
				var y = oOffsetEvent.y;
				bRes = ( x < nRight )&&( x > nLeft )&&( y < nDown )&&( y > nTop );
			}
		}
		return bRes;
	}
	
	// Is mouse inside element (without scrollbars and borders)
    this.IsMouseInsideEl = function( oEl, oEv )
    {
	    var bRes = false;
    	
	    if( oEl && oEl.tagName &&( null != self.GetParentEl(oEl) ) )
	    {
	        var oElRtStl = self.GetRTStyle( oEl );
		    if( oElRtStl && ( oElRtStl.visibility != "hidden" ) && ( oElRtStl.display != "none" ) )
		    {
		        var oOffsetEl = self.GetOffset( oEl, self.GetTopmostLayoutEl() );
			    var oOffsetEvent = self.GetOffsetCoordsEvent( oEv );
    			
			    var nTop = oOffsetEl.y + self.GetElementExtWidth( oEl, true );
			    var nDown = nTop + self.GetElementInHeight( oEl );
			    var nLeft = oOffsetEl.x + self.GetElementExtWidth( oEl, true );
			    var nRight = nLeft + self.GetElementInWidth( oEl );
    			
			    var nScrollHoriz =  self.GetScrollbarWidth( oEl, true );
			    var nScrollVert =  self.GetScrollbarWidth( oEl, false );
    			
                nRight -= nScrollVert;
                nDown -= nScrollHoriz;

			    var x = oOffsetEvent.x;
			    var y = oOffsetEvent.y;
			    bRes = ( x < nRight ) && ( x > nLeft ) && ( y < nDown ) && ( y > nTop );
		    }
	    }
	    return bRes;
    }
    
    // Get element inside width
    this.GetElementInWidth = function( oEl )
    {
        var nInsideWidth = oEl.offsetWidth - self.GetElementExtWidth( oEl, true ) - - self.GetElementExtWidth( oEl, false );
        return nInsideWidth;
    }

    // Get element inside height
    this.GetElementInHeight = function( oEl )
    {
        var nInsideHeight = oEl.offsetHeight - self.GetElementExtHeight( oEl, true ) - self.GetElementExtHeight( oEl, false );
        return nInsideHeight;
    }
	
	this.GetEventSrcElement = function( oEv )
	{
		return oEv.srcElement;
	}

	this.IsFloaterDetached = function()
	{
		return false;
	}
	
	// DOM
	this.GetElsByTagName = function( sTag, oCont )
	{
		if (!oCont) oCont = window.document;
		return oCont.getElementsByTagName( sTag );
	}

	this.GetElByTagName = function( sTag, oCont )
	{
		var aEls = this.GetElsByTagName( sTag, oCont );
		return aEls.length > 0 ? aEls[ 0 ] : null;
	}
	
	this.GetFirstChild = function( oEl )
	{
		return oEl.firstChild;
	}

	this.GetChildren = function( oEl )
	{
		return oEl.childNodes;
	}
	
	this.GetChildsByPredicate = function( oEl, fnPred )
	{
	    var oRes = [];
		var oChildren = self.GetChildren( oEl );
		if( oChildren )
		{
			for( var n = 0; n < oChildren.length; ++n )
			{
				var oChild = oChildren[n];
				if( oChild && fnPred( oChild ) )
				{
					oRes[ oRes.length ] = oChild;					
				}
			}
		}
		return oRes;	
	}
	
	this.GetChildsElements = function( oEl )
	{
		function fnPred( oEl )
		{
			var bRes = !!oEl.tagName;
			return bRes;
		}
		return self.GetChildsByPredicate( oEl, fnPred );
	}
	
	this.GetParentEl = function( oEl )
	{
		return oEl.parentNode;
	}
	
	this.RemoveNode = function( oChildNode )
	{
		var oParentEl = oChildNode.parentNode;
		if ( oParentEl )
		{
			oParentEl.removeChild( oChildNode );
		}
	}
	
	this.DestroyTree = function( oEl )
    {
        var oChilds = self.GetChildren( oEl );
        for (var i = oChilds.length - 1; i >= 0; i--) 
        {        
            var oNode = oChilds[i];
            if( oNode.control && oNode.control.dispose )
            {
				oNode.control.dispose();
			}				
            self.DestroyTree( oNode );
        }
        self.RemoveNode( oEl );
    }
	
	this.EnsureImg = function( sURL )
	{
		if (sURL)
		{			
			if (!_sfImageCache[sURL])
			{
				var oImg = new Image();
				oImg.src = sURL;
				_sfImageCache[sURL] = oImg;
			}
			return _sfImageCache[sURL].src;
		}
		return sURL;
	}
	
	// Layout
	this.GetLostOffset = function( oSubEl )
	{
		return null;
	}
	
	this.GetOffset = function( oEl, oCont )
	{
		return null;
	}	
	
	this.GetOffsetCoordsEvent = function ( oEv )
	{
		var nX = 0, nY = 0;
		var oTopmostEl = self.GetTopmostLayoutEl();
		nX += oEv.clientX + oTopmostEl.scrollLeft;
		nY += oEv.clientY + oTopmostEl.scrollTop;

		return { x: nX, y: nY };
	}
	
	this.GetTopmostLayoutEl = function()
	{
		var oEl = document.body;
		return oEl;
	}
	
	this.GetScrollbarWidth = function( oEl, bHorizontal )
	{
		return 16;
	}
	
	this.IsClipSlow = function()
	{
		return false;
	}
	
	this.GetRTStyle = function( oEl )
	{
		return oEl.style;
	}
	
	this.GetElementExtWidth = function( oEl, bLeft )
	{
	}

	this.GetElementExtHeight = function( oEl, bTop )
	{
	}
	
	this.GetLostOffsetLeft = function()
	{
		return 0;
	}
	
	this.GetElementWidthMargin = function( oEl, bLeft )
	{
		return 0;				
	}
	
	this.GetElementHeightMargin = function( oEl, bTop )
	{
		return 0;				
	}
	
	this.CalculateSubpanelOffset = function( oOffsetParentEl, nParentElWidth, nParentElHeight, nSubElWidth, nSubElHeight, nSubPanOffsetX, nSubPanOffsetY, bVert, bRTL, bOffsetEvent )
	{
		var nX = 0, nY = 0;
		
		var oTopmostEl = self.GetTopmostLayoutEl();
		var oElOffsetXY = oOffsetParentEl;				
		var nDocScrollTop = oTopmostEl.scrollTop;
		var nDocScrollLeft = oTopmostEl.scrollLeft;		
		
		var nFreeRight = oTopmostEl.clientWidth - oElOffsetXY.x + nDocScrollLeft - nSubPanOffsetX ;
		var nFreeLeft = oElOffsetXY.x - nDocScrollLeft - nSubPanOffsetX;
		var nFreeTop = oElOffsetXY.y - nDocScrollTop - nSubPanOffsetY;
		var	nFreeBottom = oTopmostEl.clientHeight - oElOffsetXY.y + nDocScrollTop - nSubPanOffsetY;
		
		var bIsFitInRight = false, bIsFitInLeft = false;
		if( bVert )
		{
			if( nSubElWidth < nFreeRight - nParentElWidth )
			{
				bIsFitInRight = true;
			}
			if( nSubElWidth < nFreeLeft )
			{
				bIsFitInLeft = true;
			}
				
			if( bRTL )
			{
				if( bIsFitInLeft )
				{
					nX = - nSubElWidth - nSubPanOffsetX ;
				}
				else
				{
					if( bIsFitInRight )
					{
						nX = nParentElWidth + nSubPanOffsetX;
					}
					else
					{
						nX = nDocScrollLeft + oTopmostEl.clientWidth - oElOffsetXY.x - nSubElWidth;
					}
				}
			}
			else
			{
				if( bIsFitInRight )
				{
					nX = nParentElWidth + nSubPanOffsetX
				}
				else
				{
					if( bIsFitInLeft )
					{
						nX = - nSubElWidth - nSubPanOffsetX ;
					}
					else
					{
						nX = nDocScrollLeft - oElOffsetXY.x;
					}
				}
			}
			
			if( nSubElHeight < nFreeBottom )
			{
				nY = nSubPanOffsetY ;			
			}
			else
			{
				if( nSubElHeight < nFreeTop + nParentElHeight )
				{
					nY = nParentElHeight- nSubElHeight - nSubPanOffsetY ;
				}
				else
				{
					nY = nDocScrollTop - oElOffsetXY.y ;
				}
			}
		}
		else
		{
			if( nSubElWidth < nFreeRight )
			{
				bIsFitInRight = true;
			}
			if( nSubElWidth < nFreeLeft + nParentElWidth )
			{
				bIsFitInLeft = true;
			}
			
			if( bRTL )
			{
				if( bIsFitInLeft )
				{
					nX = nParentElWidth- nSubElWidth - nSubPanOffsetX ;
				}
				else
				{
					if( bIsFitInRight )
					{
						nX = nSubPanOffsetX;
					}
					else
					{
						nX = nDocScrollLeft + oTopmostEl.clientWidth - oElOffsetXY.x - nSubElWidth;
					}
				}
			}
			else
			{
				if( bIsFitInRight )
				{
					nX = nSubPanOffsetX;
				}
				else
				{
					if( bIsFitInLeft )
					{
						nX = nParentElWidth - nSubElWidth - nSubPanOffsetX ;
					}
					else
					{
						nX = nDocScrollLeft - oElOffsetXY.x ;	
					}
				}
			}
			
			if( nSubElHeight < nFreeBottom - nParentElHeight )
			{
				nY = nParentElHeight + nSubPanOffsetY ;			
			}
			else
			{
				if( nSubElHeight < nFreeTop )
				{
					nY = - nSubElHeight - nSubPanOffsetY ;
				}
				else
				{
					nY = nDocScrollTop - oElOffsetXY.y ;
				}
			}
		}
		return { x: nX, y: nY }; 
	}
	
	this.GetElementExtHeight = function( oEl, bTop )
	{
		return 0;
	}
	
	this.GetElementExtWidth = function( oEl, bLeft )
	{
		return 0;
	}
	
	//return difference of the element properties style.height and element offsetHeight
	this.GetHeightDifference = function( oEl )
	{
		var nRes = self.GetElementExtHeight( oEl, false ) + self.GetElementExtHeight( oEl, true );
		return nRes;
	}
	
	//return difference of the element properties style.height and element offsetHeight
	this.GetWidthDifference = function( oEl )
	{
		var nRes = self.GetElementExtWidth( oEl, false ) + self.GetElementExtWidth( oEl, true );
		return nRes;
	}
	
	//return true, when it is differences  properties style.height and element offsetHeight
	this.GetIsOffsetWidthDeviating = function()
	{
		return ( document.compatMode == "CSS1Compat" );
	}

	this.SetHeight = function( oEl, nOffsetHeight )
	{
	    if( ( null != oEl )&& oEl.style )
	    {
	        var nHeight = nOffsetHeight - self.GetHeightDifference( oEl );
	        if( nHeight > 0 )
	        {
	            oEl.style.height = nHeight + "px";
	        }
	    }
	}
	
	this.SetWidth = function( oEl, nOffsetWidth )
	{
	    if( ( null != oEl )&& oEl.style )
	    {
	        var nWidth = nOffsetWidth - self.GetWidthDifference( oEl );
	        if( nWidth > 0 )
	        {
	            oEl.style.width = nWidth + "px";
	        }
        }
	}

	this.ClearAlign = function( oEl )
	{
	}

	this.ImportStyles = function( oTrgWnd )
	{
		var oTrgDoc = oTrgWnd.document;
		var aStyles = document.styleSheets;
		for (var i = 0; i < aStyles.length; ++i)
		{
			var oSS = aStyles[i];
			if (oSS && oSS.cssText)
			{
				var oTrgStl = oTrgDoc.createStyleSheet();
				var oRule = oSS.rules[0];
				if (oRule){ oRule.selectorText ? oTrgStl.cssText = oSS.cssText : oTrgStl.addRule("*", oSS.cssText) }
			}
		}
	}
	
	this.GetIsTargetSelf = function( sTarget )
	{
		var bRes = true;
		if( sTarget )
		{
			bRes = self.TargetSelf == sTarget;
		}
		
		return bRes;
	}
	
	this.NavigateTo = function( sUrl, sTarget )
	{
        var bRes = false;
		if( sUrl )
		{
			if( self.GetIsTargetSelf( sTarget ) )
			{
				bRes = true;
				location.href = sUrl;
			}
			else if( sTarget == self.TargetBlank )
			{
				bRes = true;
				window.open( sUrl, self.TargetBlank );				
			}
			else if( sTarget )
			{
			 	var frmTarget = self.FindFrame( sTarget );
				if( frmTarget )
				{
					bRes = true;
					frmTarget.location.href = sUrl;
				}
			}
		}
		
		return bRes;
	}
	
	this.FindFrame = function( sFrmName, oWnd )
	{
	    var oFindWnd = ( null == oWnd )? window : oWnd;
	    var frmTarget = oFindWnd.frames[sFrmName];
	    if( !frmTarget && oFindWnd.parent && !( oFindWnd === oFindWnd.top ) )
	    {
	        frmTarget = self.FindFrame( sFrmName, oFindWnd.parent );
	    }
	     
	    return  frmTarget;
	}

	this.GetText = function( oEl )
	{
		var sRes = "";
		
		var sHTML = oEl.innerHTML;
		if( null != sHTML )
		{
			var aoKeyVal = [];
			aoKeyVal[ aoKeyVal.length ] = { key: /<br[ ^>]*?>/g, value: "\n" };
			aoKeyVal[ aoKeyVal.length ] = { key: /<tr[ ^>]*?>/g, value: "\n" };
			aoKeyVal[ aoKeyVal.length ] = { key: /<th[ ^>]*?>/g, value: " " };
			aoKeyVal[ aoKeyVal.length ] = { key: /<td[ ^>]*?>/g, value: " " };
			aoKeyVal[ aoKeyVal.length ] = { key: /<.*?>/g,	value: "" };
			
			for( var c=0; c<aoKeyVal.length; ++c )
			{
				var oKeyVal = aoKeyVal[ c ];
				if( !sHTML )
				{
					break;
				}
				
				sHTML = sHTML.replace( oKeyVal.key, oKeyVal.value );
			}
			
			sRes = UTIL.HTMLDecode( sHTML );
		}
		
		return sRes;
	}
	
	// Calculates tab index of El based on position in DOM hierarchy
	this.CalculateTabIndex = function( oEl )
	{
	    return null;
	}
}

function SF_CSSBase( m_oParams )
{
	// panel states
	var c_nStateNone	= 0;
	var c_nStateHover	= 1;
	var c_nStateDown	= 2;
	var c_nStateExpanded= 3;	

	var c_asItemClsParts = 
	[
		"Default",
		"Hover",
		"Active",
		"Expanded",
		
		"Pushed",
		"Disabled",
	];

	var c_asClsChildEl = 
	[
		"tdSFStdImgCont",	// cell holding image
		"tdSFStdTextCont",	// cell holding text
		"divSFStdImgCont",	// intermediary <div>-container for image
	];

	var self = this;
	var DOM = m_oParams.DOM;	
	
	this.TransitionFilterNone		= -1;
	this.TransitionFilterFade		= -1;
	this.TransitionFilterDissolve	= -1;
	this.TransitionFilterPixelate	= -1;
	this.TransitionFilterWipeDown	= -1;
	this.TransitionFilterWipeLeft	= -1;
	this.TransitionFilterWipeRight	= -1;
	this.TransitionFilterWipeUp		= -1;
	
	this.DisplayNone = 0;
	this.DisplayInline = 1;
	this.DisplayBlock = 2;
	this.DisplayEmpty = 3;
	
	var c_asDisplayValue =
	[
		"none",
		"inline",
		"block",
		"",
	];
    
	this.SetItemClass = function( oEl, nState, nDefaultState )
	{
		var nDefState = ( null == nDefaultState ) ? c_nStateNone : nDefaultState;
		if (!oEl.ClassBase)
			oEl.ClassBase = oEl.className;
		
		var sClsEx = "";
		if (nDefaultState != nState)
		{
			var sAttr = "Class" + c_asItemClsParts[ nDefState ];
			var sDefClass = DOM.GetAttribute( oEl, sAttr );
			sClsEx = " " + sDefClass + "_" + c_asItemClsParts[ nState ];
		}

		var sCls = oEl.ClassBase + sClsEx;
		if (sCls != oEl.className)
			oEl.className = sCls;
	}

	this.SetLookClass = function( oEl, nState, nDefaultState )
	{
		var nDefState = ( null == nDefaultState ) ? c_nStateNone : nDefaultState;
		var sAttr = "Class" + c_asItemClsParts[ nState ];
		var sCls = DOM.GetAttribute( oEl, sAttr );
		if (!sCls && nDefaultState != nState)
		{
			sAttr = "Class" + c_asItemClsParts[ nDefState ];
			sCls = DOM.GetAttribute( oEl, sAttr );
		}
		if (sCls != oEl.className)
		{
			oEl.className = sCls ? sCls : "";
		}
	}
	
	this.SetImgSrc = function( oImg, nState, sAttrBase )
	{
		var sAttr = sAttrBase + c_asItemClsParts[ nState ];
		var sSrc = DOM.GetAttribute( oImg, sAttr );
		if (!sSrc && c_nStateNone != nState)
		{
			switch (nState)
			{
				case c_nStateDown:
				case c_nStateExpanded:
				{
					this.SetImgSrc( oImg, c_nStateHover, sAttrBase );
					return;
				}
				default:
				{
					this.SetImgSrc( oImg, c_nStateNone, sAttrBase );
					return;
				}
			}
		}

		if (oImg.SrcAttr != sSrc)
		{
			oImg.SrcAttr = (sSrc ? sSrc : "");
			this.SetVisible( oImg, oImg.SrcAttr );
			oImg.src = DOM.EnsureImg( oImg.SrcAttr );
		}
	}
	
	this.SetVisible = function( oEl, bVal )
	{
		var oElStl = oEl.style;
		oElStl.visibility = (bVal) ? "" : "hidden";
	}
	
	this.SetDisplay = function( oEl, nDisplay )
	{
		var oElStl = oEl.style;
		if ( null == nDisplay )
		{
			nDisplay = self.DisplayEmpty;
		}
		oElStl.display = c_asDisplayValue[ nDisplay ];
	}

	this.SetZIndex = function( oEl, nIdx )
	{
		var oElStl = oEl.style;
		oElStl.zIndex = (null != nIdx ? nIdx : "");
	}
	
	this.SetOpacity = function ( oEl, nIdx )
	{
	}
	
	this.SetFocusSetting = function( oEl, oValue )
	{
		return null;
	}
	
	this.GetTransitionFilter = function( nVal )
	{
		return "";
	}	
	
	function GetStateMask( aStates )
	{
		var dwMask = 0;
		for (var i = 0; i < aStates.length; ++i)
		{
			if (aStates[i])
			{
				dwMask |= (1 << i);
			}
		}
		return dwMask;
	}
	
	function GetClassFromStates( aStates )
	{
	}
	
	function GetStatesFromClass( sClass )
	{
		var aStates = [];
		for (var i = 0; i < c_asPanClsParts.length; ++i)
		{
			aStates[i] = ( (sClass.indexOf( c_asPanClsParts[i] ) > 0) ? 1 : 0 );
		}
		return aStates;
	}
}
