/**
 * 스크롤  Javascript 클래스 목록
 * 	- ScrollAtom
 *	- ScrollRow
 *	- ScrollEvent
 *  - ScrollBar
 */

/**
 * 스크롤 아톰 클래스
 * 스크롤 행 클래스를 이용해서 스크롤 기능을 수행합니다.
 *
 * 스크롤 행 인텍스 참고 
 *  - 0부터 : this.m_arRowList, html row id
 *  - 1부터 : 행번호, 일련번호
 */
 
function ScrollAtom (strVarName, // 1
					nScriptIndex, // 2
					nSQLIndex,
					nDBIndex,
					nMaxRow, // 3
					nPageRow, // 4
					nRowCount, // 5
					nColumnCount, // 6
					bSerialAutoUpdate, // 7
					bSureInputScroll, // 8
					bRemoveRow, // 9
					bAddRow,	//10
					nScrolltHeight,	// 11
					bVanish)	// 12
{
	this.Atom (strVarName, "", strVarName, -1, nScriptIndex, nSQLIndex, nDBIndex, bVanish);
	
	this.m_strRowID = strVarName + "_ScrollRow_";
	
	this.m_nMaxRow = nMaxRow;						// 최대 표시 행수
	this.m_nPageRow = nPageRow;						// 페이지당 표시 행 수
	this.m_nRowCount = nRowCount; // 매트릭스형 웹스크롤 행수
	this.m_nColumnCount = nColumnCount; // 매트릭스형 웹스크롤 열수
	this.m_bSureInputScroll = bSureInputScroll;
	this.m_arSureInputScrollAtomList = new Array();	// 유효행이 체크된 입력란 리스트
	
	this.m_arRowList = new Array();					// 스크롤 행 리스트
	this.m_arDeleteRowList = new Array();			// 스크롤 삭제행 리스트
	this.m_nSelectedRowIndex = 0;					// 선택된 행 인덱스
	this.m_strSelectedAtomVarName = "";	// 선택된 아톰의 VarName
	
	this.m_bRemoveRow = bRemoveRow;		// 행 삭제 단축 사용여부
	this.m_bAddRow = bAddRow;			// 행 추가 단축 사용 여부
	
	this.m_nLastValidRowIndex = -1;					// 플래그가 F, I, U인 마지막 행 인덱스를 저장합니다.
	
	this.m_bSerialAutoUpdate = bSerialAutoUpdate;	// 일련번호 자동수정
	
	this.m_heAtom = document.getElementById(strVarName);
	
	this.m_nScrollTop = 0; //스크롤의 현재 위치 저장, 위/아래 이동시 제어
	this.m_nStartHtmlRowIndex = 0; //현재 HTML을 가진 스크롤의 row 인덱스
	this.m_bEventIgnoreFlag = false; //스크롤 이벤트 무시하는 경우 설정 플래그
	
	this.m_heScrollBar = document.getElementById("__SCROLL_BAR_" + strVarName);
	
	this.m_nScrollCurrentHeight = nScrolltHeight;
}

ScrollAtom.prototype = new Atom ();
ScrollAtom.prototype.constructor = Atom;

ScrollAtom.prototype.getSelectedRowIndex = function ()
{
	return this.m_nSelectedRowIndex;
}

ScrollAtom.prototype.getLastValidRowIndex = function ()
{
	return this.m_nLastValidRowIndex;
}

ScrollAtom.prototype.getOperationRowSize = function ()
{
	return this.getLastValidRowIndex() + 1;
}

ScrollAtom.prototype.getScrollHeight = function()
{
	return this.m_nScrollCurrentHeight;
}

ScrollAtom.prototype.getHTML = function ()
{
	// 형만 맞춰놓앗음	
}

ScrollAtom.prototype.getRowHeight = function ()
{
	return this.m_heAtom.style.pixelHeight / this.m_nPageRow;
}

ScrollAtom.prototype.getBindedAtom = function (strVarName, nRowIndex)
{
	var objRow = this.m_arRowList[nRowIndex];
	if (null == objRow)
	{
		return null;
	}
	
	return objRow.getBindedAtom(strVarName);
}

/**
 * 아톰리스트에 추가
 */
ScrollAtom.prototype.putAtom = function ()
{
	ScrollAtom._atoms[this.m_strVarName] = this;
}

ScrollAtom.prototype.setRefVarName = function (strRefVarName)
{
	// 형만 맞춰놓앗음
}

ScrollAtom.prototype.getSelectedAtomVarName = function ()
{
	return this.m_strSelectedAtomVarName
}

ScrollAtom.prototype.handleResult = function (xnAtom, nOccurEventType)
{
	if (null == xnAtom)
	{
		return;
	}
	
	this._handleResult(xnAtom, nOccurEventType);
}

ScrollAtom.prototype.getSelectBindedAtomPrefix = function (strVarName)
{
	var objAtom = this.getBindedAtom(strVarName, this.m_nSelectedRowIndex);
	
	if (objAtom.getPrefix)
	{
		return objAtom.getPrefix();
	}
	return "";
}

ScrollAtom.prototype.getSelectBindedAtomValue = function (strVarName)
{
	var objAtom = this.getBindedAtom(strVarName, this.m_nSelectedRowIndex);
	
	return objAtom.getValue();
}

ScrollAtom.prototype.getSelectBindedAtomFieldName = function (strVarName)
{
	var objAtom = this.getBindedAtom(strVarName, this.m_nSelectedRowIndex);
	
	return objAtom.getFieldName();
}

/**
 * 스크롤에 묶인 아톰의 모든행의 값을 배열로 리턴 한다.
 */
ScrollAtom.prototype.getAllBindedAtomValue = function (strVarName)
{
	var arResult = new Array();
	
	for (var i = 0; i < this.m_arRowList.length; i++)
	{
		var objRow = this.m_arRowList[i];
		
		var objAtom = objRow.getBindedAtom(strVarName);
		
		if (null != objAtom)
		{
			var strValue = objAtom.getValue();
			arResult.push(strValue);
		}
	}
	
	return arResult;
}

ScrollAtom.prototype.init = function ()
{
	this._dropRow();
	this._initRow();
	this._createHtmlRow();
}

// 처음에 아톰 리스트를 만들때만 호출합니다.
ScrollAtom.prototype.addBindedAtom = function (objAtom)
{
	var objScrollRow = this.m_arRowList[0];
	if (null == objScrollRow)
	{
		objScrollRow = new ScrollRow(this.m_strRowID, 0, true);
		objScrollRow.initItemNum();
		
		this.m_arRowList[0] = objScrollRow;
	}
	
	objScrollRow.addBindedAtom(objAtom);
	
	if (objAtom.isSureInputScroll())
	{	
		this.m_arSureInputScrollAtomList.push(objAtom.getVarName());
	}
}

/**
 * 스크롤에 묶인 아톰의 값이 변하면 해당 행의 플래그를 바꿉니다.
 */
ScrollAtom.prototype.modifyRow = function (nRowIndex, nOccurEventType, strBindVarName)
{
	// 데이터 입력란에서는 this.m_nSelectedRowIndex가 onFocus시점에서 설정되어 있습니다.
	// 다른 아톰에서는 this.m_nSelectedRowIndex가 설정되지 않습니다.
	if (0 <= nRowIndex)
	{
		var bChangeFlag = (Define.OCCUR_CHANGE_FLAG == nOccurEventType)?true:false;
		this._modifyRow(this.m_arRowList, nRowIndex, nOccurEventType, strBindVarName, bChangeFlag);
	}
}

ScrollAtom.prototype.modifyRowExcludeOperation = function (nRowIndex, nOccurEventType)
{
	// 데이터 입력란에서는 this.m_nSelectedRowIndex가 onFocus시점에서 설정되어 있습니다.
	// 다른 아톰에서는 this.m_nSelectedRowIndex가 설정되지 않습니다.
	if (0 <= nRowIndex)
	{
		this._modifyRowExcludeOperation(this.m_arRowList, nRowIndex, nOccurEventType);
	}
}

/**
 * 서버에 보낼 요청 XML을 만듭니다.
 * 아톰
 * @param xnAtom 스크롤 아톰 값이 추가될 노드
 */
ScrollAtom.prototype.makeRequest = function (xnAtomRequest)
{
	var xnAtom = XmlLib.createChild(xnAtomRequest, "Scroll");
	
	xnAtom.setAttribute("VarName", this.m_strVarName);
	
	this._makeSureInputScrollInfo(xnAtom);
	
	this._makeBindedAtomInfo(xnAtom);
	
	this._makeRowRequest(xnAtom);
	
	this._makeDeleteRowRequest(xnAtom);
}

ScrollAtom.prototype.checkSureInput = function()
{
	var nRowIndex = 0;
	try
	{
		var arRowList = this.m_arRowList;
		var nLen = this.m_nLastValidRowIndex + 1;
		for ( ; nRowIndex < nLen; nRowIndex = nRowIndex + 1)
		{
			var objRow = arRowList[nRowIndex];
			// throw ~~
			objRow.checkSureInput();
		}
	}
	catch (e)
	{
		var strMessage = "스크롤 필수 입력항목에 자료가 없습니다.\n" +
				"[스크롤:" + this.m_strVarName + " - " + (nRowIndex + 1) + "행] " + e.message;
		e.message = strMessage;
		throw e;
	}
}

/**
 * 스크롤의 행들의 유효성을 체크한다. 
 */
ScrollAtom.prototype.checkSureInputScroll = function ()
{
	if (false == this.m_bSureInputScroll)
	{
		return;
	}
	
	if (0 < this._getValidRowCount())
	{
		return;
	}
	else
	{
		// 모든 행이 유효 하지 않다면, 예외를 던진다
		throw new SureInputScrollException(this.m_strVarName);
	}
}

/**
 * 스크롤에 묶인 아톰의 행 번호를 가져 옵니다.
 * 이벤트가 발생되는 시점에만 사용됩니다. 다른 경우에는 사용하면 안됩니다.
 * @param heAtom 발생한 이벤트의 해당 html 객체
 * @return 성공: html 객체가 묶인 스크롤의 행번호 실패 : -1
 */
ScrollAtom.prototype.getRowIndex = function (heAtom)
{
	if (heAtom && heAtom.parentNode && heAtom.parentNode.id)
	{
		var strRowID = heAtom.parentNode.id;
		var nIndex = strRowID.indexOf("_ScrollRow_");
		if (-1 < nIndex)
		{
			var nLastIndex = strRowID.lastIndexOf("_");
			return this.m_nStartHtmlRowIndex + parseInt(strRowID.substring(nLastIndex + 1));
		}
	}
	
	return -1;
}


/**
 * 현재 행에서 다음 보여질 행으로 HTML포인터를 이동한다.
 * @param nPagePos 다음 보여질 행번호
 */
ScrollAtom.prototype.moveHtmlRow = function (nRowPos)
{
	if (0 > nRowPos
		|| this.m_nMaxRow < (nRowPos + this.m_nPageRow)
		|| (null == this.m_arRowList && 0 == this.m_arRowList.length))
	{
		return;
	}
	
	var nDirection = 0;
	
	if (this.m_nStartHtmlRowIndex > nRowPos)
	{
		nDirection = ScrollAtom.MOVEUP;
	}
	else if (nRowPos > this.m_nStartHtmlRowIndex)
	{
		nDirection = ScrollAtom.MOVEDOWN;
	}
	
	if (nRowPos == this.m_nStartHtmlRowIndex
		|| (ScrollAtom.MOVEUP == nDirection && 0 == this.m_nStartHtmlRowIndex))
	{
		return;
	}
	
	if ((ScrollAtom.MOVEDOWN == nDirection)
		&& (nRowPos + this.m_nPageRow >= this.m_arRowList.length))
	{
		this._expandRow(nRowPos + this.m_nPageRow - (this.m_arRowList.length - 1));
	}
	
	if (ScrollAtom.MOVEDOWN == nDirection)
	{
		for (var i = this.m_nStartHtmlRowIndex + (this.m_nPageRow-1), j = nRowPos + (this.m_nPageRow-1); i >= this.m_nStartHtmlRowIndex ; i=i-1, j=j-1)
		{
			var objTargetRow = this.m_arRowList[j];
			if (null != objTargetRow)
			{
				objTargetRow.moveHtmlRow(this.m_arRowList[i]);
				objTargetRow.setItemNum(-1);
				
			}
		}
	}
	else if (ScrollAtom.MOVEUP == nDirection)
	{
		for (var i = this.m_nStartHtmlRowIndex, j = nRowPos, k = this.m_nStartHtmlRowIndex + (this.m_nPageRow-1); i<=k ; i=i+1, j=j+1)
		{
			var objTargetRow = this.m_arRowList[j];
			if (null != objTargetRow)
			{
				objTargetRow.moveHtmlRow(this.m_arRowList[i]);
				objTargetRow.setItemNum(-1);
			}
		}
	}
	
	this.m_nStartHtmlRowIndex = nRowPos;
}

ScrollAtom.prototype.onFocus = function (heAtom, objEvent)
{
	this.m_nSelectedRowIndex = this.getRowIndex(heAtom);
	var strVarName = heAtom.id;

	// _초점얻음 스크립트
	if (-1 != ScriptAtomEvent.onFocus(this.m_nScriptIndex, this.m_nSelectedRowIndex, strVarName))
	{
		ScriptAtomEvent.onFocusAfter(this.m_nScriptIndex, this.m_nSelectedRowIndex, strVarName);
	}
}

ScrollAtom.prototype.onBlur = function (heAtom, objEvent)
{
	this.m_nSelectedRowIndex = this.getRowIndex(heAtom);
	var strVarName = heAtom.id;
	
	// _초점상실 스크립트
	if (-1 != ScriptAtomEvent.onBlur(this.m_nScriptIndex, this.m_nSelectedRowIndex, strVarName))
	{
		ScriptAtomEvent.onBlurAfter(this.m_nScriptIndex, this.m_nSelectedRowIndex, strVarName);
	}
}

ScrollAtom.prototype.onClick = function (strSelectedAtomVarName, heAtom, objEvent)
{
	this.m_nSelectedRowIndex = this.getRowIndex(heAtom);
	
	this.m_strSelectedAtomVarName = strSelectedAtomVarName;
	
	// _누름 스크립트
	if (-1 != ScriptAtomEvent.onClick(this.m_nScriptIndex, 6, this.m_nSelectedRowIndex, this.m_strSelectedAtomVarName))
	{
		ScriptAtomEvent.onClickAfter(this.m_nScriptIndex, this.m_nSelectedRowIndex, this.m_strSelectedAtomVarName);
	}
}

ScrollAtom.prototype.onDblClick = function (heAtom, objEvent)
{
	this.m_nSelectedRowIndex = this.getRowIndex(heAtom);
	var strVarName = heAtom.id;

	// _두번누름 스크립트
	if (-1 != ScriptAtomEvent.onDblClick(this.m_nScriptIndex, this.m_nSelectedRowIndex, strVarName))
	{
		ScriptAtomEvent.onDblClickAfter(this.m_nScriptIndex, this.m_nSelectedRowIndex, strVarName);
	}
}

ScrollAtom.prototype.onKeyDown = function (heAtom, objEvent)
{
	// keycode 73 : I 
	if (true == this.m_bAddRow && 73 == objEvent.keyCode && objEvent.ctrlKey)
	{			
		this._onScriptInsertRow(objEvent);
	}
	
	// keycode 68 : D
	if (true == this.m_bRemoveRow && 68 == objEvent.keyCode && objEvent.ctrlKey)
	{
		this._onScriptDeleteRow(objEvent);
	}
}

/**
 * 선택표시 (f9 기능)
 */
ScrollAtom.prototype.vanish = function ()
{
	if (this.m_bVanish)
	{
		// 현재 감춤 상태이면 표시한다.
		if ("hidden" == this.m_heScrollBar.style.visibility)
		{
			this.m_heScrollBar.style.visibility = "visible";
		}
		else	// 표시된 상태이면 감춘다.
		{
			this.m_heScrollBar.style.visibility = "hidden";
		}
	}
	
	for (var nRowIndex = 0; nRowIndex < this.m_arRowList.length; nRowIndex=nRowIndex+1)
	{
		var objRow = this.m_arRowList[nRowIndex];
		
		objRow.vanish();
	}
}

/**
 * 웹 스크롤 인지의 여부를 반환한다
 */
ScrollAtom.prototype.isWebScroll = function ()
{
	return false;
}

ScrollAtom.prototype.isWebDisplay = function ()
{
	return false;
}

/**
 * 스크롤의 유효행수를 나타 내어준다.
 * 사각형 그리기 또는 입력란 아톰중에 해당 스크롤 아톰의 변수명을 참조 변수 명으로 
 * 가지고 있는 아톰을 찾아서 행 수를  써준다.
 */
ScrollAtom.prototype.viewValidRowCount = function ()
{
	var objAtom = ScrollAtom.findCountViewerAtom(this.m_strVarName);
	
	if (null == objAtom)
	{
		return;
	}
	
	objAtom.setValue(this._getValidRowCount().toString());
}

ScrollAtom.prototype.moveNextRow = function ()
{
	if (this.m_nSelectedRowIndex == this.m_nLastValidRowIndex)
	{
		//스크롤 행을 추가하는것 구현해야함
	}
	
	this.m_nSelectedRowIndex ++;
}

ScrollAtom.prototype.movePrevRow = function ()
{
	if (0 < this.m_nSelectedRowIndex)
	{
		this.m_nSelectedRowIndex --;
	}
}

ScrollAtom.prototype.onclickIndex = function (bExecuteSearch)
{
	if (bExecuteSearch)
	{
		this._executeSearch();
	}
}

/**
 * 스크롤행중 유효한 행의 수를 반환한다.
 * @return nValidRowCount
 */
ScrollAtom.prototype._getValidRowCount = function ()
{
	var nValidRowCount = 0;

	var arRowList = this.m_arRowList;
	var nLen = this.m_nLastValidRowIndex + 1;
	
	for (var nRowIndex = 0; nRowIndex < nLen; nRowIndex=nRowIndex+1)
	{
		var objRow = arRowList[nRowIndex];
		
		if (null != objRow)
		{
			// 유효한 스크롤 데이터 인가?
			// 하나라도 유효한 행이 있다면 유효!!
			if (this._isValidRow(objRow))
			{
				nValidRowCount = nValidRowCount + 1;
			}
		}
	}

	return nValidRowCount;
}

ScrollAtom.prototype._isValidRow = function (objRow)
{
	if (null == objRow)
	{
		return false;
	}
	
	return objRow.checkSureInputScroll();
}

ScrollAtom.prototype._onScriptInsertRow = function (objEvent)
{
	if (true == this.m_bSerialAutoUpdate)
	{
		if (-1 == ScriptAtomEvent.onInsertRow(this.m_nScriptIndex, this.m_nSelectedRowIndex))
		{
			objEvent.returnValue = false;
			return;
		}
		
		if (true == this.m_bSerialAutoUpdate)
		{
			this._insertRow(this.m_arRowList, this.m_nSelectedRowIndex);
		}
	}
}

ScrollAtom.prototype._onScriptDeleteRow = function (objEvent)
{
	if (-1 == ScriptAtomEvent.onDeleteRow(this.m_nScriptIndex, this.m_nSelectedRowIndex))
	{
		objEvent.returnValue = false;
		return;
	}
	else
	{
		ScriptAtomEvent.onDeleteRowAfter(this.m_nScriptIndex, this.m_nSelectedRowIndex);
	}
	
	this._deleteRow(this.m_nSelectedRowIndex);
}

/**
 * 스크롤 결과를 처리합니다.
 *
 * 문서열림, 버튼 기능(앞장/뒷장)
 *
 * @param xnAtom 결과 XML
 */
ScrollAtom.prototype._handleResult = function (xnAtom, nOccurEventType)
{
	var xnDataRowList = XmlLib.selectNodeList(xnAtom, "DataRow");
	if (null == xnDataRowList)
		return;
	
	var arRowList = this.m_arRowList;
	
	var nResultLen = xnDataRowList.length;
	var xnDataRow = null;
	var objRow = null;
	
	//모두지우기 상태가 설정되어있으면 기존의 스크롤 값을 모두 지우고 새로운 스크롤 값을 반영한다.
	//모두지우기 상태가 설정되는 경우는 
	//1.스크롤.모두지우기() 스크립트가 실행된 경우,
	//2.기본키동작으로 스크롤의 값이 채워진 경우 등이다.
	var strDeleteFlag = xnAtom.getAttribute("DeleteAll");
	if ("Y" == strDeleteFlag && 0 < this.m_nLastValidRowIndex)
	{
		this._deleteAllRow();		// 화면을 비운다.
	}
	
	var nLastLen = arRowList.length;
	this._expandRow(nResultLen - nLastLen);
	
	for (var i = 0; i < nResultLen; i=i+1)
	{
		xnDataRow = xnDataRowList[i];
		objRow = arRowList[i];
		
		if (null == objRow)
			continue;
		objRow.setValue(xnDataRow, nOccurEventType);
	}
	
	this._updateLastValidRowIndex();
}

ScrollAtom.prototype._makeSureInputScrollInfo = function (xnAtom)
{
	var strSureInputScrollAtomList = this.m_arSureInputScrollAtomList.join(",");
	
	xnAtom.setAttribute("SureInputScrollAtomList", strSureInputScrollAtomList);
	xnAtom.setAttribute("SelectedRow", this.m_nSelectedRowIndex);
}

/**
 * SQL객체 외부쿼리 실행시 SELECT로 검색된 값을 스크롤에 넣어주기 위해
 * 스크롤에 속한 아톰의 정보를 보내준다.
 */	
ScrollAtom.prototype._makeBindedAtomInfo = function (xnAtom)
{
	var strVarNameList = "";
	var strAtomTypeList = "";
	
	var objAtomList = this.m_arRowList[0].getBindedAtomList();
	for (var strKey in objAtomList)
	{
		var objAtom = objAtomList[strKey];
		
		strVarNameList += objAtom.getVarName() + ",";
		
		strAtomTypeList += objAtom.getAtomType() + ",";
	}
	
	xnAtom.setAttribute("VarNameList", strVarNameList.substring(0, strVarNameList.length));
	xnAtom.setAttribute("AtomTypeList", strAtomTypeList.substring(0, strAtomTypeList.length));
	xnAtom.setAttribute("SelectedAtomVarName", this.m_strSelectedAtomVarName);
}

/**
 * 행에 대한 요청 XML을 만듭니다.
 *
 * @param xnAtom 행 값이 추가될 스크롤 아톰 노드 
 */
ScrollAtom.prototype._makeRowRequest = function (xnAtom)
{
	var arRowList = this.m_arRowList;
	var nLen = this.m_nLastValidRowIndex + 1;
	for (var i = 0; i < nLen; i=i+1)
	{
		var xnRow = XmlLib.createChild(xnAtom, "DataRow");
		
		var objRow = arRowList[i];
		if (null == objRow || objRow.isNone())
		{
			continue;
		}
		
		objRow.makeRequest(xnRow, this.m_strVarName);
	}
}

/**
 * 삭제된 행에 대한 요청 XML을 만듭니다.
 *
 * @param xnAtom 행 값이 추가될 스크롤 아톰 노드
 */
ScrollAtom.prototype._makeDeleteRowRequest = function (xnAtom)
{
	var xnDeleteRow = XmlLib.createChild(xnAtom, "DeleteRow");
	
	var arDeleteRowList = this.m_arDeleteRowList;
	var nLen = arDeleteRowList.length;
	for (var i = 0; i < nLen; i=i+1)
	{
		var objRow = arDeleteRowList[i];
		
		var xnRow = XmlLib.createChild(xnDeleteRow, "DataRow");
		
		objRow.makeRequest(xnRow, this.m_strVarName);
	}
}

/**
 * 스크롤에 묶인 아톰을 초기화 합니다.
 */	
ScrollAtom.prototype._initRow = function ()
{
	this.m_arRowList[0].initRow(true);
	this.m_nLastValidRowIndex = -1;
}

/**
 * 현재 가지고 있는 행을 모두 지웁니다.
 */
ScrollAtom.prototype._dropRow = function ()
{
	var arRowList = this.m_arRowList;
	
	this.moveHtmlRow(0);
	
	var nLen = arRowList.length;
	for (var i = nLen - 1; i > 0; i=i-1)
	{
		this._remove(arRowList, i);
	}
	this.m_arDeleteRowList.length = 0;
}

/*
* 스크롤 초기화시 처음 보여지는 행만큼 html태그에 대응되는 rowList를 만듭니다.
*/
ScrollAtom.prototype._createHtmlRow = function ()
{
	var arRowList = this.m_arRowList;
	
	// 스크롤버튼형, 매트릭스형은 플로팅모델
	if (1 == this.m_nWebScrollType || 2 == this.m_nWebScrollType)
	{
		var heFirstRow = arRowList[0].getHTML();
		heFirstRow.style.cssFloat = "left";
		heFirstRow.style.styleFloat = "left";
	}
	
	for (var i = arRowList.length, len = this.m_nPageRow; i < len; i=i+1)
	{
		var objRow = arRowList[arRowList.length - 1];
		var objNewRow = objRow.clone(i, true);
		this._append(arRowList, objNewRow, objRow);
		objNewRow.setHTMLInfo(this.m_strRowID, i);
	}
}

/*
* html을 가지지 않는 rowList를 생성합니다.
* @param nCount 생성하고자 하는 행 개수
*/
ScrollAtom.prototype._expandRow = function (nCount)
{
	if (0 > nCount)
		return;
		
	var arRowList = this.m_arRowList;
	
	for (var i = arRowList.length, nLen = (i + nCount); i < nLen; i=i+1)
	{
		var objRow = arRowList[arRowList.length - 1];
		
		var objNewRow = objRow.clone(i,false);
		arRowList.push(objNewRow);
	}
}

ScrollAtom.prototype._executeSearch = function ()
{
	GlobalField.setServiceName("InquiryAction");
	GlobalField.setServiceEventName("Normal");
	GlobalField.setEventVarName(this.m_strVarName);
	
	var xnRequest = MakeRequest.createRequestNode();
	var xnResultDoc = null;
	try
	{
		MakeRequest.makeServiceRequest(xnRequest);
		
		// 스크롤에 관계설정이 될수 있는 아톰들의 요청을 같이 만든다
		// 관계설정된 아톰의 값이 필요 하기 때문에
		MakeRequest.makeTouchAtomRequest(xnRequest);
		
		var xnAtomRequest = MakeRequest.getAtomRequestNode(xnRequest);
		
		if (null != xnAtomRequest)
		{
			this.makeRequest(xnAtomRequest);
			
			xnResultDoc = PQService.executeService(xnRequest.ownerDocument);
		}
	}
	catch (e)
	{
		HandleException(e);
		return;
	}
	
	if (null != xnResultDoc && null != xnResultDoc.documentElement)
	{
		var xnScrollList = XmlLib.selectNodeList(xnResultDoc.documentElement, "//Scroll");
		
		for (var i = 0; i < xnScrollList.length; i++)
		{
			if (this.m_strVarName == xnScrollList[i].getAttribute("VarName"))
			{
				this.handleResult(xnScrollList[i], Define.NO_OCCUR_EVENT);
			} 
		}
	}
}

/**
 * 행 수정
 * 일련번호와 Flag를 수정합니다.
 *
 * @param arRowList
 * @param nSelectedRowIndex
 */
ScrollAtom.prototype._modifyRow = function (arRowList, nSelectedRowIndex, nOccurEventType, strBindVarName, bChangeFlag)
{
	var objRow = arRowList[nSelectedRowIndex];
	objRow.setSerial(nSelectedRowIndex);
	
	// 행이 유효행이 되면, 외부 아톰 값을 가져오는 연산식을 수행한다.
	var bPrevNone = false;
	if (objRow.isNone())
	{
		bPrevNone = true;
	}
	
	objRow.updateFlag(bChangeFlag);
	this._updateLastValidRowIndex();
	
	if (bPrevNone)
	{
		// 유효행이 되는 시점에서 한번만 동작한다
		objRow.executeOuterOperation(nSelectedRowIndex);
	}
	
	if (null != strBindVarName || 0 < strBindVarName.length)
	{ 
		objRow.executeChangedOperation(nSelectedRowIndex, strBindVarName);
	}
	
	this.viewValidRowCount();
}

/**
 * 행 수정, 단 연산식을 수행하지 않습니다.
 * 
 * 일련번호와 Flag를 수정합니다.
 *
 * @param arRowList
 * @param nSelectedRowIndex
 */
ScrollAtom.prototype._modifyRowExcludeOperation = function (arRowList, nSelectedRowIndex, nOccurEventType)
{
	var objRow = arRowList[nSelectedRowIndex];
	objRow.setSerial(nSelectedRowIndex);
	objRow.updateFlag(false);
	this._updateLastValidRowIndex();
	
	this.viewValidRowCount();
}

/**
 * 행 추가
 *
 * @param arRowList
 * @param nSelectedRowIndex
 */

ScrollAtom.prototype._insertRow = function (arRowList, nSelectedRowIndex)
{
	var objRow = arRowList[nSelectedRowIndex];
	
	var objNewRow = objRow.clone(nSelectedRowIndex,false);
	objNewRow.setHTML(null);
	
	objNewRow.initRow(false);
	
	objNewRow.setSerial(nSelectedRowIndex);
	objNewRow.setFlag(ScrollRow.INSERT);
	objNewRow.executeOuterOperation(nSelectedRowIndex);
	
	this._insert(objNewRow, nSelectedRowIndex);
	this._setRowInfo(nSelectedRowIndex + 1);
	
	this._updateLastValidRowIndex();
	this.viewValidRowCount();
}

/**
 * 행 삭제
 * @param nSelectedRowIndex
 * 
 */
ScrollAtom.prototype._deleteRow = function (nSelectedRowIndex)
{
	var objRow = this.m_arRowList[nSelectedRowIndex];

	// 빈 행, 새로 추가한 행은 쿼리를 날리지 않도록 삭제행 리스트에 저장하지 않습니다.
	if (objRow.isFirst() || objRow.isUpdate())
	{
		this.m_arDeleteRowList.push(objRow);
	}
	
	this._removeRow(nSelectedRowIndex);
	this._setRowInfo(nSelectedRowIndex);
	
	this._updateLastValidRowIndex();
	this.viewValidRowCount();
}

/**
* 모든행을 지운다. (스크롤.모두지우기)
*/
ScrollAtom.prototype._deleteAllRow = function ()
{
	var objRow = null;
	for (var i = 0, len = this.m_arRowList.length; i < len; i=i+1)
	{
		objRow = this.m_arRowList[0];
		
		// 빈 행, 새로 추가한 행은 쿼리를 날리지 않도록 삭제행 리스트에 저장하지 않습니다.
		if (objRow.isFirst() || objRow.isUpdate())
		{
			this.m_arDeleteRowList.push(objRow);
		}
		this._removeRow(0);
	}
	
	this._setRowInfo(0);
	this._updateLastValidRowIndex();
	this.viewValidRowCount();
}

/**
 * 이벤트가 발생한 행의 다음 모든 행에 대한 행 정보를 설정합니다.
 *
 * @param arRowList 행 리스트
 * @param nIndex 정보 설정 시작 인덱스
 */
ScrollAtom.prototype._setRowInfo = function (nIndex)
{
	var arRowList = this.m_arRowList;
	var nLen = arRowList.length;
	for (var i = nIndex; i < nLen; i=i+1)
	{
		var objRow = arRowList[i];
		
		if (objRow.isNone())
		{
			continue;
		}
		
		if (this.m_bSerialAutoUpdate)
		{
			objRow.setItemNum(i);
			objRow.setSerial(i);
		
			objRow.updateFlag(false);
		}
	}
}

/**
 * 추가
 * @param arRowList 행 리스트
 * @param objNewRow 추가할 행
 */
ScrollAtom.prototype._append = function (arRowList, objNewRow, objRow)
{
	var heNewRow = objNewRow.getHTML();
	
	if (null != heNewRow)
	{
		var hePager = document.getElementById("__WEBATOM_PAGECONTROL_" + this.m_strVarName);
		
		if (hePager &&
			(0 == this.m_nWebScrollType || 2 == this.m_nWebScrollType))
		{
			
			this.m_heAtom.insertBefore(heNewRow, hePager)			
		}
		else
		{
			this.m_heAtom.appendChild(heNewRow);
		}
		
		// 문자방송아톰 : 좌우 스크롤, 좌우 슬라이드 이면 Row를 일렬로 만든다.
		if (1 == this.m_nEffectType || 3 == this.m_nEffectType)
		{
			var objRowHTML = objRow.getHTML();
			
			heNewRow.style.position = "absolute";
			heNewRow.style.top = objRowHTML.offsetTop;
			heNewRow.style.left = objRowHTML.offsetLeft + objRowHTML.offsetWidth;
		}
	}

	arRowList.push(objNewRow);
}

/**
 * 삽입
 * @param objNewRow 추가할 행
 * @param nIndex 삽입 할 인덱스
 */
ScrollAtom.prototype._insert = function (objNewRow, nIndex)
{
	this.m_arRowList.splice(nIndex, 0, objNewRow);
	
	//행을 추가 하면 기존의 rowIndex도 바뀌게 되므로 바뀐뒤의 rowIndex기준
	//으로 묶인 모든 아톰의 rowIndex를 재설정한다.	
	for (var i = nIndex, len = this.m_arRowList.length; i < len; i=i+1)
	{
		this.m_arRowList[i].setRowIndex(i);
	}
	//추가한 행의 다음행의 html을 이동시킨다.
	//nIndex = 추가한 행의 index
	//len = 추가한행의 보이는 위치의 가장 마지막 위치
	for (var i = nIndex, len = this.m_nPageRow + this.m_nStartHtmlRowIndex; i <= len ; i=i+1)
	{
		this.m_arRowList[i].moveHtmlRow(this.m_arRowList[i+1]);
		this.m_arRowList[i].setItemNum(-1);
	}
}

/**
 * 삭제
 * @param arRowList 행 리스트
 * @param nIndex 삭제 할 인덱스
 */
ScrollAtom.prototype._remove = function (arRowList, nIndex)
{
	var objRow = arRowList[nIndex];
	var heAtom = objRow.getHTML();
	if (null != heAtom)
	{
		this.m_heAtom.removeChild(heAtom);
	}
	
	arRowList.splice(nIndex, 1);
}
/**
* 행삭제
* @param removeRow 삭제할 행 인덱스
*/ 
ScrollAtom.prototype._removeRow = function (nIndex)
{
	var objRow = this.m_arRowList[nIndex];
	//html이 사라지면 안되므로 삭제한 후에 행이 모자라지 않도록 expand시킨다.
	if (this.m_arRowList.length <= nIndex + this.m_nPageRow)
	{
		this._expandRow(this.m_nPageRow);
	}
	
	////삭제하기 전에 삭제할 행의 html을 이동시켜 행을 비운다.
	for (var i = this.m_nStartHtmlRowIndex + this.m_nPageRow, len = this.m_nPageRow - (nIndex- this.m_nStartHtmlRowIndex); i>nIndex; i=i-1)
	{
		this.m_arRowList[i].moveHtmlRow(this.m_arRowList[i-1]);
		if (false == this.m_bSerialAutoUpdate)
		{
			//TODO: 일련번호 자동수정이 아닐때, 삭제후 행번호 설정이 구현되지 않았음
		}
	}
	
	this.m_arRowList.splice(nIndex, 1);
	
	////삭제후 바뀐 행 인덱스 기준으로 rowIndex를 다시 설정
	for (var i = nIndex, len = this.m_arRowList.length; i < len; i=i+1)
	{
		this.m_arRowList[i].setRowIndex(i);
	}
}

/**
 * 마지막으로 값을 가지고 있는 행의 인덱스를 찾아서 저장합니다. 
 */
ScrollAtom.prototype._updateLastValidRowIndex = function ()
{
	var arRowList = this.m_arRowList;
	var nLen = arRowList.length;
	for (var i = 0; i < nLen; i=i+1)
	{
		var objRow = arRowList[i];
		if (!objRow.isNone())
		{
			this.m_nLastValidRowIndex = i;
		}
	}
}

ScrollAtom.prototype.hideComboAtomOptionList = function ()
{
	for (var nRowIndex = 0; nRowIndex < this.m_arRowList.length; nRowIndex++)
	{
		var objRow = this.m_arRowList[nRowIndex];
		
		objRow.hideComboAtomOptionList();
	}
}


////////////////////////////////////////////////////////////
// static field

ScrollAtom.MOVEUP = -1; 
ScrollAtom.MOVEDOWN = 1; 

/**
 * 아톰리스트
 */
ScrollAtom._atoms = new Object();
	
ScrollAtom.init = function ()
{
	for (var strVarName in ScrollAtom._atoms)
	{
		var objAtom = ScrollAtom._atoms[strVarName];
		
		objAtom.init();
		
		if (!objAtom.isWebScroll() && !objAtom.isWebDisplay())
		{
			var objScrollBar = ScrollBar.getScrollBar(strVarName);
			if (null != objScrollBar)
			{
				objScrollBar.clear();
			}
		}
	}
}

ScrollAtom.getAtom = function (strVarName)
{
	return ScrollAtom._atoms[strVarName];
}

ScrollAtom.prototype.getAtomType = function ()
{
	return "ScrollAtom";
}

ScrollAtom.getAtomByBindedVarName = function (strBindedVar)
{
	for (strVarName in ScrollAtom._atoms)
	{
		var objScrollAtom = ScrollAtom._atoms[strVarName];
		
		if (null != objScrollAtom)
		{
			var nRowIndex = objScrollAtom.getSelectedRowIndex();
			
			var objBindedAtom = objScrollAtom.getBindedAtom(strBindedVar, nRowIndex);
			
			if (null != objBindedAtom)
			{
				return objBindedAtom;
			}
		}
	}
	
	return null;
}


/**
 * 스크롤 아톰의 라인수를 나타 내줄 아톰을 찾는다.
 * 사각형 그리기 아톰 또는 입력란 아톰중에서, 스크롤의 변수명을 참조 변수명으로 가지고 있는 아톰을 찾는다. 
 * (빌더의 논리는 사각형 그리기 아톰을 먼저, 그다음 입력란 아톰을 찾게 되어 있다.)
 * @param strVarName (스크롤 아톰의 변수명)
 * @return objAtom (해당 스크롤 아톰의 라인수를 나타 내여줄 아톰)
 */
ScrollAtom.findCountViewerAtom = function (strVarName)
{
	var objAtom = null;
	
	if (ContainsRectangleAtom())
	{
		objAtom = RectangleAtom.getRefAtom(strVarName);
	}
	
	if (null != objAtom)
	{
		return objAtom;
	}
	
	if (ContainsInputDataAtom())
	{
		objAtom = InputDataAtom.getRefAtom(strVarName);
	}
	
	return objAtom;
}

/**
 * 스크롤 아톰의 행을 이동한다.
 * @param strKey 스크롤 아톰 변수명
 * @param nRowIndex 스크롤 행번호
 */
ScrollAtom.moveScrollRow = function (strKey, nRowIndex)
{
	var objScrollAtom = ScrollAtom.getAtom(strKey);
	if(null != objScrollAtom)
	{
		objScrollAtom.moveHtmlRow(nRowIndex);
	}
}
/*
* TODO: 연산식에 사용.. 수정되어야 할 부분
*/

ScrollAtom.makeRequest = function (xnAtomRequest)
{
	for (var strVarName in ScrollAtom._atoms)
	{
		var objAtom = ScrollAtom.getAtom(strVarName);
		if (null == objAtom)
		{
			continue;
		}
		
		objAtom.makeRequest(xnAtomRequest);
	}
}

/*
* 스크롤이 묶인 아톰의 필수입력을 체크한다.
*/
ScrollAtom.checkSureInput = function ()
{
	for (var strVarName in ScrollAtom._atoms)
	{
		var objScrollAtom = ScrollAtom._atoms[strVarName];
		if (null == objScrollAtom)
		{
			continue;
		}
		
		objScrollAtom.checkSureInput();
	}
}

ScrollAtom.checkSureInputScroll = function ()
{
	for (var strVarName in ScrollAtom._atoms)
	{
		var objScrollAtom = ScrollAtom._atoms[strVarName];
		if (null == objScrollAtom)
		{
			continue;
		}
		
		objScrollAtom.checkSureInputScroll();
	}
}

/**
 * 아톰리스트에서, 첫번째 아톰을 찾아 반환한다
 */
ScrollAtom.getFirstAtom = function ()
{
	var objAtom = null;
	
	for (var strVarName in ScrollAtom._atoms)
	{
		objAtom = ScrollAtom._atoms[strVarName];
		
		return objAtom;
	}
	
	return objAtom;
}

/**
 * 스크롤 유효행수 표시를 갱신한다.
 */
ScrollAtom.refreshValidRowCount = function ()
{
	for (var strVarName in ScrollAtom._atoms)
	{
		var objScrollAtom = ScrollAtom._atoms[strVarName];
		if (null != objScrollAtom)
		{
			objScrollAtom.viewValidRowCount();
		}
	}
}

/**
 * 스크롤에 묶인 아톰을 아톰변수명으로 찾는다.
 * @param strBindedAtomVarName 스크롤에 묶인 아톰 변수명.
 * @return 스크롤에 묶인 아톰.
 */
ScrollAtom.findBindedAtom = function (strBindedAtomVarName)
{
	for (var strScrollVarName in ScrollAtom._atoms)
	{
		var objScrollAtom = ScrollAtom.getAtom(strScrollVarName);
		if (null != objScrollAtom)
		{
			var objAtom = objScrollAtom.getBindedAtom(
				strBindedAtomVarName, objScrollAtom.getSelectedRowIndex());
			if (null != objAtom)
			{
				return objAtom;
			}
		}
	}
	
	return null;
}

ScrollAtom.onKeyDown = function (strScrollName, heAtom, objEvent)
{
	objEvent = HTMLLib.getEvent(objEvent);
	
	var objScrollAtom = ScrollAtom.getAtom(strScrollName);
	if (null != objScrollAtom)
	{
		objScrollAtom.onKeyDown(heAtom, objEvent);
	}
}

ScrollAtom.hideComboAtomOptionList = function ()
{
	for (var strScrollVarName in ScrollAtom._atoms)
	{
		var objScrollAtom = ScrollAtom.getAtom(strScrollVarName);
		if (null != objScrollAtom)
		{
			objScrollAtom.hideComboAtomOptionList();
		}
	}
}

/**
 * 스크롤 행 클래스
 * 스크롤 행 정보와 스크롤에 묶인 아톰을 관리합니다.
 *
 * @author 조영운
 * @version 2006년 3월 2일
 *
 * 스크롤 행 인텍스 참고
 *  - 0부터 : this.m_arRowList, html row id
 *  - 1부터 : 행번호, 일련번호
 */
 
function ScrollRow (strRowID, nRowIndex, bIsHtml)
{
	this.m_strFlag = ScrollRow.NONE;
	
	this.m_nOrignalSerial = 0;	// 원본(DB에서 가져온) 일련번호
	this.m_nSerial = 0;			// 현재(수정된) 일련번호
	
	this.m_htBindedAtomList = new Hashtable();
	
	this.m_nRowIndex = nRowIndex; 
	this.m_strRowID = strRowID;
	
	this.m_heRow = (true == bIsHtml) ? document.getElementById(strRowID + nRowIndex) : null;
	
	this.m_heItemNum = null;
}

/**
 * 객체 clone 메소드 입니다.
 * @param nRowIndex 만들 행번호  bIsHtml html 값을 설정할지 결정  
 * 
 */
ScrollRow.prototype.clone = function (nRowIndex, bSetHtml)
{
	var objNewRow = new ScrollRow(this.m_strRowID, nRowIndex, bSetHtml);
	var heNewRow = null;
	if (false != bSetHtml)
	{
		if (null != this.m_heRow)
		{
			heNewRow = this.m_heRow.cloneNode(false);
			objNewRow.setHTML(heNewRow);
		}
	}
	else
	{
		objNewRow.setHTML(null);
	}
	
	if (null != this.m_heItemNum && false != bSetHtml)
	{
		var heNewItemNum = this.m_heItemNum.cloneNode(true);
		objNewRow.setItemNumHTML(heNewItemNum);
	}
	
	//데코 계열 아톰(사각형, 둥근사각형, 타원, 직선, 사선, 그림삽입 등)부터 그려져야하므로 먼저 처리.
	for (var strKey in this.m_htBindedAtomList)
	{
		var objAtom = this.m_htBindedAtomList[strKey];
		
		if ("RectangleAtom" == objAtom.getAtomType() || 
			"Line" == objAtom.getAtomType() ||
			"DecorImageAtom" == objAtom.getAtomType())
		{
			var objNewAtom = objAtom.clone(bSetHtml);
			objNewAtom.init();
			
			if (false != bSetHtml)
			{
				var heNewAtom = objNewAtom.getHTML();
				if (null != heNewAtom)
				{
					heNewRow.appendChild(heNewAtom);
				}
			}
			
			objNewRow.addBindedAtom(objNewAtom);
		}
	}
			
	for (var strKey in this.m_htBindedAtomList)
	{
		var objAtom = this.m_htBindedAtomList[strKey];
		
		if ("RectangleAtom" == objAtom.getAtomType() || 
			"Line" == objAtom.getAtomType() ||
			"DecorImageAtom" == objAtom.getAtomType())
		{
			continue;
		}
		
		var objNewAtom = null;
		if ("Combo" == objAtom.getAtomType())
		{
			objNewAtom = objAtom.clone(bSetHtml, nRowIndex);
		}
		else
		{
			objNewAtom = objAtom.clone(bSetHtml);
		}
		
		objNewAtom.init();
		if (false != bSetHtml)
		{
			var heNewAtom = objNewAtom.getHTML();
			if (null != heNewAtom)
			{
				heNewRow.appendChild(heNewAtom);
			}
		}
		
		objNewRow.addBindedAtom(objNewAtom);
	}
	
	return objNewRow;
}

////////////////////////////////////////
// getter / setter

ScrollRow.prototype.getFlag = function ()
{
	return this.m_strFlag;
}

ScrollRow.prototype.getHTML = function ()
{
	return this.m_heRow;
}

ScrollRow.prototype.getBindedAtom = function (strVarName)
{
	return this.m_htBindedAtomList[strVarName];
}

ScrollRow.prototype.getBindedAtomList = function ()
{
	return this.m_htBindedAtomList;
}

ScrollRow.prototype.setFlag = function (strFlag)
{
	this.m_strFlag = strFlag;
}

ScrollRow.prototype.setHTML = function (heRow)
{
	this.m_heRow = heRow;
}

ScrollRow.prototype.setSerial = function (nRowIndex)
{
	this.m_nSerial = nRowIndex + 1;
}

ScrollRow.prototype.getSerial = function ()
{
	return this.m_nSerial;
}

ScrollRow.prototype.setHTMLInfo = function (strName, nIndex)
{
	this._setID(strName, nIndex);
	this.setItemNum(nIndex);
}

ScrollRow.prototype.setRowIndex = function (nRowIndex)
{
	this.m_nRowIndex = nRowIndex;
	
	var htBindedAtomList = this.m_htBindedAtomList;
	for (var strKey in htBindedAtomList)
	{
		var objAtom = htBindedAtomList[strKey];
		objAtom.setRowIndex(nRowIndex);
	}
}

ScrollRow.prototype.getRowIndex = function ()
{
	return this.m_nRowIndex;
}

ScrollRow.prototype.setItemNumHTML = function (heNewItemNum)
{
	this.m_heItemNum = heNewItemNum;
	if (null != this.m_heRow && null != heNewItemNum)
		this.m_heRow.appendChild(heNewItemNum);
}

/**
* 행번호 HTML 객체 반환
* @return 행번호 HTML객체
*/
ScrollRow.prototype.getItemNumHTML = function ()
{
	if (null != this.m_heItemNum)
	{
		return this.m_heItemNum;
	}
	return null;
}

ScrollRow.prototype.setValue = function (xnDataRow, nOccurEventType)
{
	if (null == xnDataRow)
	{
		return;
	}
	
	this._handleResult(xnDataRow, nOccurEventType);
}

////////////////////////////////////////
// public method

/**
 * 행에 대한 요청 XML을 만듭니다.
 * @param xnAtom 행 값이 추가될 스크롤 아톰 노드 
 * @param strScrollName
 */
ScrollRow.prototype.makeRequest = function (xnRow, strScrollName)
{
	this._makeRequestFlag(xnRow);
	this._makeRequestSerial(xnRow, strScrollName);
	
	for (var strVarName in this.m_htBindedAtomList)
	{
		var objBindedAtom = this.m_htBindedAtomList[strVarName];
		
		if (null != objBindedAtom.makeRequest)
		{
			objBindedAtom.makeRequest(xnRow, true);
		}
	}
}

/**
 * 필수입력 체크
 */
ScrollRow.prototype.checkSureInput = function ()
{
	var htBindedAtomList = this.m_htBindedAtomList;
	for (var strKey in htBindedAtomList)
	{
		var objBindedAtom = htBindedAtomList[strKey];
		if(objBindedAtom.isSureInput() || objBindedAtom.isSureInputScroll())
		{
			// 필수입력에서 예외발생시  SureInputException throw 
			objBindedAtom.checkSureInput();
		}
	}
}
/**
 * 스크롤의 유효행을 체크 합니다. 
 * @return 스크롤에 묶인 아톰중 하나라도 요효하지 않으면 false, 모두 유효 하면 true;
 */
ScrollRow.prototype.checkSureInputScroll = function ()
{
	var htBindedAtomList = this.m_htBindedAtomList;
	// 유효한 행인가??
	var bValidRow = true;
	
	for (var strKey in htBindedAtomList)
	{
		var objBindedAtom = htBindedAtomList[strKey];
		
		if(objBindedAtom.isSureInputScroll())
		{
			// 바인드된 아톰중 하나라도 유효 하지 않다면 유효하지않은 행.. 
			if (false == objBindedAtom.checkSureInputScroll())
			{
				bValidRow = false;
				break;
			}
		}
	}
	
	return bValidRow;
}

ScrollRow.prototype.isEmpty = function ()
{
	var htBindedAtomList = this.m_htBindedAtomList;
	
	for (var strKey in htBindedAtomList)
	{
		var objBindedAtom = htBindedAtomList[strKey];
		
		if (null != objBindedAtom && objBindedAtom.checkSureInputScroll())
		{
			return false;
		}
	}
	
	return true;
}
/**
 * 스크롤에 묶인 아톰을 리스트에 추가합니다.
 *
 * @param objAtom 스크롤에 묶인 아톰
 */
ScrollRow.prototype.addBindedAtom = function (objAtom)
{
	objAtom.setRowIndex(this.m_nRowIndex);
	
	var strVarName = objAtom.getVarName();
	this.m_htBindedAtomList[strVarName] = objAtom;
}

/**
 * 행번호 아톰값을 설정합니다.
 *
 * @param nRowIndex 행 인덱스 
 */
ScrollRow.prototype.initItemNum = function ()
{
	if (null == this.m_heRow)
		return;
		
	var heBindedAtomList = this.m_heRow.childNodes;
	var nLen = heBindedAtomList.length;
	for (var i = 0; i < nLen; i=i+1)
	{
		var heBindedAtom = heBindedAtomList[i];
		if ("__ITEMNUM" == heBindedAtom.id)
		{
			this.m_heItemNum = heBindedAtom;
			break;
		}
	}
}

/**
 * 스크롤에 묶인 아톰을 초기화합니다.
 */
ScrollRow.prototype.initRow = function ()
{
	this.m_strFlag = ScrollRow.NONE;
	this.m_nOrignalSerial = 0;
	this.m_nSerial = 0;	
		
	var htBindedAtomList = this.m_htBindedAtomList;
	for (var strKey in htBindedAtomList)
	{
		htBindedAtomList[strKey].init();
	}
}

/**
 * 스크롤에 묶인 입력란 연산식 동작
 *  1. 유효행이 되면 스크롤에 묶이지 않은 아톰의 값을 전달 받는 연산식만 수행
 *  2. 스크롤에 묶인 입력란의 값이 변경되면
 *     2.1 연산식에 의해 값을 전달하는 입력란일 경우에만 연산식 수행
 */
/**
 * 새로운 행이 추가되면 스크롤에 묶인 아톰의 연산식을 실행시킵니다.
 * 외부의 아톰에서 값을 가져와야 하는 연산식만 수행하고, 스크롤 내부 연산식은 수행하지 않음
 */
ScrollRow.prototype.executeOuterOperation = function (nScrollRowIndex)
{
	var htBindedAtomList = this.m_htBindedAtomList;
	
	for (var strKey in htBindedAtomList)
	{
		var objAtom = htBindedAtomList[strKey];
		if (objAtom.isOperation && objAtom.isOperation() && objAtom.isRelatedOperation())
		{
			PQOperation.executeScrollOuterOperation(strKey, nScrollRowIndex);
		}
	}
}

/**
 * 값이 변경된 아톰만 연산식을 실행시킨다.
 */
ScrollRow.prototype.executeChangedOperation = function (nScrollRowIndex, strBindVarName)
{
	var objAtom = this.m_htBindedAtomList[strBindVarName];
	if (objAtom.isRelatedOperation())
	{
		PQOperation.executeScrollChangeOperation(strBindVarName, nScrollRowIndex);
	}
}


/**
 * 행의 플래그를 설정합니다.
 *
 * @param bBindedAtomModified 스크롤에 묶인 아톰의 값이 변경되었으면 true, Ctrl+I, Ctrl+D이면 false
 */
ScrollRow.prototype.updateFlag = function (bChageFlag)
{
	if (bChageFlag) 
	{
		if (this.isFirst())
		{
			this.setFlag(ScrollRow.UPDATE);
		}					
	}
	else
	{
		if (this.isNone())
		{
			this.setFlag(ScrollRow.INSERT);
		}
		
					
		if (this.isFirst())
		{
			this.setFlag(ScrollRow.UPDATE);
		}
		else if (this.isInsert() && false != this.isEmpty())
		{
			this.setFlag(ScrollRow.NONE);
		}
	}
}

ScrollRow.prototype.isNone = function ()
{
	if (ScrollRow.NONE == this.m_strFlag)
	{
		return true;
	}
	
	return false;
}

ScrollRow.prototype.isFirst = function ()
{
	if (ScrollRow.FIRST == this.m_strFlag)
	{
		return true;
	}
	
	return false;
}

ScrollRow.prototype.isInsert = function ()
{
	if (ScrollRow.INSERT == this.m_strFlag)
	{
		return true;
	}
	
	return false;
}

ScrollRow.prototype.isUpdate = function ()
{
	if (ScrollRow.UPDATE == this.m_strFlag)
	{
		return true;
	}
	
	return false;
}

ScrollRow.prototype.isDelete = function ()
{
	if (ScrollRow.DELETE == this.m_strFlag)
	{
		return true;
	}
	
	return false;
}

ScrollRow.prototype.setDisplay = function (bBlock)
{
	if (null == this.m_heRow)
	{
		return;
	}
		
	if (bBlock)
	{
		this.m_heRow.style.display = "block";
	}
	else
	{
		this.m_heRow.style.display = "none";
	}
}

/*
* 스크롤의 HTML에 들어가는 행의 HTML 포인터를 이동합니다.
* @param objSourceRow HTML포인터를 가지고 올 행
*/
ScrollRow.prototype.moveHtmlRow = function (objSourceRow)
{
	if (null == objSourceRow)
		return;			
	
	var htSourceBindedAtomList = objSourceRow.getBindedAtomList();
	for (var strKey in htSourceBindedAtomList)
	{
		var objSourceAtom = htSourceBindedAtomList[strKey];
		if (null != objSourceAtom)
		{
			var heSourceAtom = objSourceAtom.getHTML(); 
			if (null != heSourceAtom)
			{	
				if ("Combo" == objSourceAtom.getAtomType())
				{
					var heBody = objSourceAtom.getBodyHTML();
					var heHeader = objSourceAtom.getHeaderHTML();
					var heHeaderEdit = objSourceAtom.getHeaderEditHTML();
					var heHeaderButton = objSourceAtom.getHeaderButtonHTML();
					var heOptionList = objSourceAtom.getOptionListHTML();
					
					this.m_htBindedAtomList[strKey].setHTML(heSourceAtom, heBody, heHeader, heHeaderEdit, heHeaderButton, heOptionList); 
					objSourceAtom.setHTML(null, null, null, null, null, null);
					this.m_htBindedAtomList[strKey].displayValue();
				}
				else
				{
					this.m_htBindedAtomList[strKey].setHTML(heSourceAtom); 
					objSourceAtom.setHTML(null);
					this.m_htBindedAtomList[strKey].displayValue();
				}
			}
		}
	}
	
	var heRow = objSourceRow.getHTML();
	if (null != heRow)
	{
		this.m_heRow = heRow;
		this.m_heItemNum = objSourceRow.getItemNumHTML();
		
		objSourceRow.setItemNumHTML(null);
		objSourceRow.setHTML(null);
	}
}

/**
 * 선택표시 (f9 기능)
 */
ScrollRow.prototype.vanish = function ()
{
	for (var strKey in this.m_htBindedAtomList)
	{
		var objAtom = this.m_htBindedAtomList[strKey];
		
		objAtom.vanish();
	}
}

ScrollRow.prototype.setItemNum = function (nIndex)
{
	if (-1 == nIndex)
	{
		nIndex = this.m_nRowIndex;
	}
	
	if (null != this.m_heItemNum)
	{
		this.m_heItemNum.firstChild.firstChild.firstChild.innerText = nIndex + 1;
	}
}

ScrollRow.prototype.hideComboAtomOptionList = function ()
{
	for (var strKey in this.m_htBindedAtomList)
	{
		var objAtom = this.m_htBindedAtomList[strKey];
		if ("Combo" == objAtom.getAtomType())
		{
			objAtom.hideOptionList();			
		}
	}
}

////////////////////////////////////////
// private method

/**
 * 각 행에 대한 결과를 처리합니다.
 *
 * @param xnDataRow 행에 대한 결과 XML
 */
ScrollRow.prototype._handleResult = function (xnDataRow, nOccurEventType)
{
	var xnBindedAtomList = xnDataRow.childNodes;
	if (null == xnBindedAtomList)
		return;
	
	// 조회동작 결과 처리시 OnChange가 발생하기 않는데
	// 조회동작으로 만든 ROW의 경우에는 Flag를 여기서 설정한다. 값은 "F"
	// 기타 다른 동작은 _modifyRow에서 flag 설정을 한다.
	if (Define.NO_OCCUR_EVENT == nOccurEventType)
	{
		this._handleResultFlag(xnDataRow);
	}
	
	var nLen = xnBindedAtomList.length;
	for (var i = 0; i < nLen; i=i+1)
	{
		var xnBindedAtom = xnBindedAtomList[i];
		var strVarName = xnBindedAtom.getAttribute("VarName");
		var strValue = XmlLib.getTextValue(xnBindedAtom);
		
		if (("Serial" == xnBindedAtom.nodeName))
		{
			var strOriginSerial = xnBindedAtom.getAttribute("OriginSerial");
			this._handleResultSerial(strOriginSerial, strValue);
			continue;
		}
		
		var objBindedAtom = this.m_htBindedAtomList[strVarName];
		if (null != objBindedAtom)
		{
			// 스크롤 검색결과 처리시 버튼에는 setValue()하지 않는다.
			if ("Button" == objBindedAtom.getAtomType())
			{
				continue;
			}
			
			// setAtomValue()에서 연산식이 발생하므로 값 설정 전에 연산식 실행 여부를 설정해줘야한다.
			// Applet에서 연산식을 수행하였기 때문에 handleResult에서는 연산식 수행하지 않습니다.
			if (objBindedAtom.setIsOperation)
			{					
				objBindedAtom.setIsOperation(false);
			}
			
			if ("CheckAtom" == objBindedAtom.getAtomType())
			{
				objBindedAtom.handleResult(xnBindedAtom);
			}
			else
			{
				// 직선, 사선은 setValue()가 없음
				if (null != objBindedAtom.setValue)
				{
					objBindedAtom.setValue(strValue, nOccurEventType);
					if (null != objBindedAtom.m_heAtom)
					{
						objBindedAtom.m_heAtom.style.zIndex = 101;
					}
				}
			}
			
			if (objBindedAtom.setIsOperation)
			{					
				objBindedAtom.setIsOperation(true);
			}
		}
	}
}

ScrollRow.prototype.setDataValue = function (arDataRow, arVariableList, nOccurEventType)
{
	if (Define.NO_OCCUR_EVENT == nOccurEventType)
	{
		this.m_strFlag = "I";
	}
	
	var nCount = (arDataRow.length <= arVariableList.length) ? arDataRow.length : arVariableList.length;
	
	for (var nCol = 0; nCol < nCount; nCol+=1)
	{
		var strVarName = arVariableList[nCol].getVarName();
		var strAtomType = arVariableList[nCol].getAtomType();
		var strValue = arDataRow[nCol];
		
//		if (("Serial" == xnBindedAtom.nodeName))
//		{
//			var strOriginSerial = xnBindedAtom.getAttribute("OriginSerial");
//			this._handleResultSerial(strOriginSerial, strValue);
//			continue;
//		}
		
		var objBindedAtom = this.m_htBindedAtomList[strVarName];
		if (null != objBindedAtom)
		{
			// 스크롤 검색결과 처리시 버튼에는 setValue()하지 않는다.
			if ("Button" == objBindedAtom.getAtomType())
			{
				continue;
			}
			
			// setAtomValue()에서 연산식이 발생하므로 값 설정 전에 연산식 실행 여부를 설정해줘야한다.
			// Applet에서 연산식을 수행하였기 때문에 handleResult에서는 연산식 수행하지 않습니다.
			if (objBindedAtom.setIsOperation)
			{					
				objBindedAtom.setIsOperation(false);
			}
			
			if ("CheckAtom" == objBindedAtom.getAtomType())
			{
				//objBindedAtom.handleResult(xnBindedAtom);
			}
			else
			{
				// 직선, 사선은 setValue()가 없음
				if (null != objBindedAtom.setValue)
				{
					objBindedAtom.setValue(strValue, nOccurEventType);
				}
			}
			
			if (objBindedAtom.setIsOperation)
			{					
				objBindedAtom.setIsOperation(true);
			}
		}
	}
}

/**
 * 결과행 플래그를 처리합니다.
 *
 * @param xnDataRow 행에 대한 결과 XML
 */
ScrollRow.prototype._handleResultFlag = function (xnDataRow)
{
	var strFlag = xnDataRow.getAttribute("Flag");
	if (null != strFlag && 0 < strFlag.length)
	{
		this.m_strFlag = strFlag
	}
}

/**
 * 결과행 일련번호를 처리합니다.
 *
 * @param strValue 일련번호 값
 */
ScrollRow.prototype._handleResultSerial = function (strOriginSerial, strValue)
{
	if (null != strOriginSerial && 0 < strOriginSerial.length)
	{
		this.m_nOrignalSerial = parseInt(strOriginSerial);
	}
	if (null != strValue && 0 < strValue.length)
	{
		this.m_nSerial = parseInt(strValue);
	}
}

/**
 * 요청 행 플래그를 설정합니다.
 *
 * @param xnRow 요청 행 XML
 */
ScrollRow.prototype._makeRequestFlag = function (xnRow)
{
	xnRow.setAttribute("Flag", this.m_strFlag);
}

/**
 * 요청 행 일련번호를 설정합니다.
 *
 * @param xnRow 요청 행 XML
 * @param strScrollName 
 */
ScrollRow.prototype._makeRequestSerial = function (xnRow, strScrollName)
{
	var xnSerial = XmlLib.createChild(xnRow, "Serial");
	
	xnSerial.setAttribute("VarName", strScrollName);
	xnSerial.setAttribute("OriginSerial", this.m_nOrignalSerial);
	
	XmlLib.setTextValue(xnSerial, this.m_nSerial);
}

/**
 * 행 HTML ID를 설정합니다.
 *
 * @param strName
 * @param nIndex
 */
ScrollRow.prototype._setID = function (strName, nIndex)
{
	var strID = strName + nIndex;
	if (null != this.m_heRow)
	{
		this.m_heRow.id = strID;
		this.m_heRow.name = strID;
	}
}


////////////////////////////////////////
// static member

ScrollRow.NONE = "N";
ScrollRow.FIRST = "F";
ScrollRow.INSERT = "I";
ScrollRow.UPDATE = "U";
ScrollRow.DELETE = "D";


/**
 * 스크롤_누름 이벤트
 *
 * @param strScrollName 스크롤아톰 변수명
 * @param heAtom 이벤트가 발생한 html element
 * @param objEvent html 이벤트 객체, FF에서 전달됨
 */
function ScrollOnClick (strScrollName, strVarName, m_heAtom, objEvent)
{
	objEvent = HTMLLib.getEvent(objEvent);
	
	var objScrollAtom = ScrollAtom.getAtom(strScrollName);
	if (null != objScrollAtom)
	{
		objScrollAtom.onClick(strVarName, m_heAtom, objEvent);
	}
}

/**
 * 스크롤_두번누름 이벤트
 *
 * @param strScrollName 스크롤아톰 변수명
 * @param heAtom 이벤트가 발생한 html element
 * @param objEvent html 이벤트 객체, FF에서 전달됨
 */
function ScrollOnDblClick (strScrollName, heAtom, objEvent)
{	
	objEvent = HTMLLib.getEvent(objEvent);
		
	var objScrollAtom = ScrollAtom.getAtom(strScrollName);
	if (null != objScrollAtom)
	{
		objScrollAtom.onDblClick(heAtom, objEvent);
	}
}

/**
 * 스크롤_초점얻음 이벤트
 *
 * @param strScrollName 스크롤아톰 변수명
 * @param heAtom 이벤트가 발생한 html element
 * @param objEvent html 이벤트 객체, FF에서 전달됨
 */
function ScrollOnFocus (strScrollName, heAtom, objEvent)
{
	objEvent = HTMLLib.getEvent(objEvent);
	
	var objScrollAtom = ScrollAtom.getAtom(strScrollName);
	if (null != objScrollAtom)
	{
		objScrollAtom.onFocus(heAtom, objEvent);
	}
}

/**
 * 스크롤_초점상실 이벤트
 *
 * @param strScrollName 스크롤아톰 변수명
 * @param heAtom 이벤트가 발생한 html element
 * @param objEvent html 이벤트 객체, FF에서 전달됨
 */
function ScrollOnBlur (strScrollName, heAtom, objEvent)
{
	objEvent = HTMLLib.getEvent(objEvent);
	
	var objScrollAtom = ScrollAtom.getAtom(strScrollName);
	if (null != objScrollAtom)
	{
		objScrollAtom.onBlur(heAtom, objEvent);
	}
}

function ScrollOnScroll (strScrollName, heScrollAtom, objEvent)
	{
	objEvent = HTMLLib.getEvent(objEvent);
		
	var objScrollAtom = ScrollAtom.getAtom(strScrollName);
	if (null != objScrollAtom)
	{
		objScrollAtom.onScroll(heScrollAtom);
	}
}

function ScrollBar(strVarName)
{
	///member variable
	/*
	 *스크롤바 바디
	 */
	this.m_heScrollBar = document.getElementById("__SCROLL_BAR_BODY_" + strVarName);
	/*
	 *스크롤바 게이지
	 */
	this.m_heScrollBarGage = document.getElementById("__SCROLL_BAR_GAGE_" + strVarName);
	this.m_bDraggingGage = false; //게이지를 잡아 끄는 동작을 제어하는 플래그 변수
	this.m_nOldYPos = 0; //Y좌표 값
	
	this.m_fInterval = Number("0.0"); //스크롤 1회 이동거리
	this.m_fIntervalSum; //스크롤바 실제 이동값 합계 
	this.m_nPageRow = 0; //화면에 표시되는 열의 수
	this.m_nMaxRow = 0; //전체 행의 수
}


////////////////////////////////////////////////////////////////////////
///public
/**
 * 스크롤바의 이동후 값을 반환합니다. (변환이 되어야 함)
 * @return 스크롤바의 이동값을 반환
 */
ScrollBar.prototype.getScrollPosition = function ()
{
	var nPos = parseInt(this.m_fIntervalSum / this.m_fInterval);
	
	if (null == nPos || isNaN(nPos))
	{
		nPos = 0;
	}
	
	return nPos; 
}

/**
 * 최대행 수를 반환
 * @return 최대행 수
 */
ScrollBar.prototype.getMaxRow = function ()
{
	return this.m_nMaxRow;
}

/**
 * 드래깅 중인가?
 */
ScrollBar.prototype.isDragging = function ()
{
	return this.m_bDraggingGage;
}

/**
* 멤버 변수 초기화 
*@param nMaxRow  최대행
*@param nPageRow 한화면에 표시되는 행수
*/
ScrollBar.prototype.init = function (nMaxRow, nPageRow)
{
	if (null == this.m_heScrollBar)
		return;
		
	if (0 >= nPageRow)
	{
		nPageRow = 1;
	}
	if (0>= nMaxRow)
	{
		nMaxRow = 1;
	}
	
	this.m_nPageRow = nPageRow;
	this.m_nMaxRow = nMaxRow;
	
	nMaxRow -= nPageRow; //실제 이동행은, 최대행에서 화면에 보여지는 행을 뺀 값이다.
	
	this._setGageHeight(this._getGageSize(nMaxRow));
	
	this.m_fInterval = this._getGageInterval(nMaxRow); //한칸 이동 간격을 조정한다.
	this.m_fIntervalSum = 0.0;
	this._setGageTopPosition(0);
}

ScrollBar.prototype.clear = function ()
{
	this.m_fIntervalSum = 0.0;
	this._setGageTopPosition(0);
}

////////////////////////////////////////////////////////////////////////
///event

/*
* 게이지 마우스 다운 이벤트
*/
ScrollBar.prototype.onMouseDownGage = function (objEvent)
{
	this.m_nOldYPos = objEvent.screenY;
	this.m_bDraggingGage = true;
	this._showTooltip(objEvent.clientX, objEvent.clientY);
	objEvent.cancelBubble = true;
}

/**
 * 스크롤바의 기둥에서 마우스 다운 이벤트 핸들러
 */
ScrollBar.prototype.onScrollBarMouseDown = function (objEvent)
{
	if (null == this.m_heScrollBar || false == this._existGage())
		return;
		
	switch (objEvent.button)
	{
		case 1: //왼쪽버튼
		{
			var nTop = this._getGageTopPosition();

			if (objEvent.y <= nTop) //위 클릭
			{
				this._moveGage(this.m_nPageRow * this.m_fInterval, -1,false);
			}
			else //아래클릭
			{
				this._moveGage(this.m_nPageRow * this.m_fInterval, 1,false);
			}
		}
	}
}

/**
 * 스크롤바의 기둥에서 마우스 무브 이벤트 핸들러
 */
ScrollBar.prototype.onScrollBarMouseMove = function (objEvent)
{
	if (this.m_bDraggingGage)
	{
		if (this.m_nOldYPos < objEvent.screenY) //아래
		{
			var fInterval = objEvent.screenY - this.m_nOldYPos;
			this._moveGage (fInterval, 1, true);
		}
		else //위
		{
			var fInterval = this.m_nOldYPos - objEvent.screenY;
			this._moveGage(fInterval, -1, true);
		}
		
		this.m_nOldYPos = objEvent.screenY;
	}
}

/**
* 스크롤바의 기둥에서 마우스 업 이벤트
*/
ScrollBar.prototype.onScrollBarMouseUp = function ()
{
	this.m_bDraggingGage = false;
	
	this._hideTooltip();
}

/**
* 스크롤바의 기둥에서 마우스 휠 이벤트
*/
ScrollBar.prototype.onScrollBarMouseWheel = function (objEvent)
{
	var delta = objEvent.wheelDelta;
	var signal = (0 < delta) ? -1 : 1;
	
	this._moveGage(this.m_fInterval,signal,false);	
}

/**
 * 스크롤바의 위쪽 화살표에서 마우스 업 이벤트 핸들러
 */
ScrollBar.prototype.onTopArrowMouseDown = function ()
{
	this._moveGage(this.m_fInterval, -1, false);
}

/**
 * 스크롤바의 아래쪽 화살표에서 마우스 다운 이벤트 핸들러
 */
ScrollBar.prototype.onBottomArrowMouseDown = function ()
{
	this._moveGage(this.m_fInterval, 1, false);
}
 
ScrollBar.prototype.onBodyMouseOut = function (objEvent)
{
	if(this.m_bDraggingGage)
	{
		document.attachEvent("onmouseup",this.draggingMouseUp);
		document.attachEvent("onmousemove",this.draggingMouseMove);
	}
}

ScrollBar.prototype.draggingMouseUp = function ()
{
	var objDraggingScrollBar = ScrollBar.getDraggingScrollBar();
	
	if(null != objDraggingScrollBar )
	{
		var objDraggingScrollAtom = ScrollBar.getScrollAtom();
		objDraggingScrollBar.onScrollBarMouseUp();
		ScrollAtom.moveScrollRow(objDraggingScrollAtom.m_strVarName, objDraggingScrollBar.getScrollPosition());
		document.detachEvent("onmouseup", objDraggingScrollBar.draggingMouseUp);
		document.detachEvent("onmousemove", objDraggingScrollBar.draggingMouseMove);
	}
	
}

ScrollBar.prototype.draggingMouseMove = function ()
{
	var objDraggingScrollBar = ScrollBar.getDraggingScrollBar();
	
	if(null != objDraggingScrollBar )
	{
		var objDraggingScrollAtom = ScrollBar.getScrollAtom();
		ScrollAtom.moveScrollRow(objDraggingScrollAtom.m_strVarName, objDraggingScrollBar.getScrollPosition());
	}
}

////////////////////////////////////////////////////////////////
///private

/*
* 스크롤바 게이지가 있는가?
* @return 있으면 true, 없으면 false
*/
ScrollBar.prototype._existGage = function ()
{
	return (null != this.m_heScrollBarGage) ? true : false;
}

/*
* 스크롤바 게이지의 높이를 가져온다.
* @return int 스크롤바 게이지의 높이
*/
ScrollBar.prototype._getGageHeight = function ()
{
	if (this._existGage())
	{
		return this.m_heScrollBarGage.style.pixelHeight;
	}
	
	return 0;
}

/*
* 스크롤바 게이지의 높이를 설정한다.
* @param nHeight 높이
*/
ScrollBar.prototype._setGageHeight = function (nHeight)
{
	if (this._existGage())
	{
		this.m_heScrollBarGage.style.height = nHeight;
	}
}	

/*
* 스크롤바 게이지의 상단 위치값을 가져온다.
* @return int 스크롤바 게이지의 상단 위치값
*/
ScrollBar.prototype._getGageTopPosition = function ()
{
	if (this._existGage())
	{
		return this.m_heScrollBarGage.style.posTop;
	}
	
	return 0;
}

/*
* 스크롤바 게이지의 상단 위치값을 설정한다.
* @param nTopPosition 상단 위치값
*/
ScrollBar.prototype._setGageTopPosition = function (nTopPosition)
{
	if (this._existGage())
	{
		this.m_heScrollBarGage.style.posTop = nTopPosition;
	}
}

/**
* 스크롤 게이지를 이동시킨다.
*@param fDistance 이동거리
*@param nDirection 이동방향 ( - : 위로, +:아래로)
*@param bDragging 드래그(true), 화살표 또는 게이지 위,아래 공백을 눌렀는지 (false)
*/
ScrollBar.prototype._moveGage = function (fDistance, nDirection, bDragging)
{
	if (null == this.m_heScrollBar || 0 > fDistance) //인자 유효성검사
		return;
		
	if (bDragging)
	{
		this._moveGageByDragging(fDistance, nDirection);
	}
	else
	{
		this._moveGageByClick(fDistance, nDirection);
	}
}

/*
* 드래그로 스크롤바 게이지를 이동시킨다.
* @param fDistance 이동거리
* @param nDirection 이동방향
*/
ScrollBar.prototype._moveGageByDragging = function (fDistance, nDirection)
{
	if (-1 == nDirection)
	{
		var nBarTop = this.m_heScrollBar.style.pixelTop;
		var nGageTop = this._getGageTopPosition() + (fDistance * nDirection);
		if (nBarTop > nGageTop)
		{
			this._setGageTopPosition(0);
			this.m_fIntervalSum = 0;
		}
		else
		{
			this._setGageTopPosition(this._getGageTopPosition() + (fDistance * nDirection));
			this.m_fIntervalSum = parseInt(this._getGageTopPosition() / this.m_fInterval) * this.m_fInterval;
		}
	}
	else
	{
		var nBarBottom = this.m_heScrollBar.style.pixelTop + this.m_heScrollBar.style.pixelHeight;
		var nGageBottom = this._getGageTopPosition() + (fDistance * nDirection) + this._getGageHeight();
		
		//게이지가 바를 넘어갈 때
		if (nBarBottom < nGageBottom)
		{
			this._setGageTopPosition(nBarBottom - this._getGageHeight());
			this.m_fIntervalSum = this.m_heScrollBar.style.pixelHeight - this._getGageHeight();
		}
		else
		{
			this._setGageTopPosition(this._getGageTopPosition() + (fDistance * nDirection));
			this.m_fIntervalSum = parseInt(this._getGageTopPosition() / this.m_fInterval) * this.m_fInterval;
		}
	}
	
	this._changeTooltipText();
}

/*
* 버튼 클릭으로  스크롤바 게이지를 이동시킨다.
* @param fDistance 이동거리
* @param nDirection 이동방향
*/
ScrollBar.prototype._moveGageByClick = function (fDistance, nDirection)
{
	if (-1 == nDirection) //위로
	{
		var nBarTop = this.m_heScrollBar.style.pixelTop;				
		var nGageTop = parseInt(this.m_fIntervalSum + (fDistance * nDirection));
		
		//게이지가 바를 넘어갈 때
		if (nBarTop > nGageTop)
		{
			this.m_fIntervalSum = 0;
			this._setGageTopPosition(0);
		}
		else 
		{
			this.m_fIntervalSum += (fDistance * nDirection);
			this._setGageTopPosition(nGageTop);
		}
	}
	else if (1 == nDirection)//아래로
	{
		var nBarBottom = this.m_heScrollBar.style.pixelTop + this.m_heScrollBar.style.pixelHeight;
		var nGageBottom = parseInt(this.m_fIntervalSum + (fDistance * nDirection)) + this._getGageHeight();
		
		//게이지가 바를 넘어갈 때
		if (nBarBottom < nGageBottom)
		{
			this.m_fIntervalSum = this.m_heScrollBar.style.pixelHeight - this._getGageHeight();
			this._setGageTopPosition(nBarBottom - this._getGageHeight());
		}
		else
		{
			this.m_fIntervalSum += (fDistance * nDirection);
			this._setGageTopPosition(parseInt(this.m_fIntervalSum));
		}				
	}
}

/**
* 스크롤바 게이지의 높이를 가져온다.
* @param nMaxRow 최대행
* return 게이지의 높이. 최소값은 5.
*/
ScrollBar.prototype._getGageSize = function (nMaxRow)
{
	if (null == nMaxRow || 0 == nMaxRow)
		return 0;
	
	var nMaxHeight = this.m_heScrollBar.style.pixelHeight;
	var nGageHeight = nMaxHeight/ nMaxRow;		
	
	return (nGageHeight > ScrollBar.MINIMUMSIZE) ? nGageHeight : ScrollBar.MINIMUMSIZE;
}

/**
* 게이지의 1회 이동간격을 구한다.
*@return 게이지 1회 이동간격
*/
ScrollBar.prototype._getGageInterval = function (nMaxRow)
{
	//TODO: 스크롤바가 전체행 : 1행 = 스크롤 전체 높이 : 한행높이로 이동한다고 가정하고 이동해야 함
	//ex) 1024 : 1 = 200 : x .. 값이 작으므로 확대값을 설정한다.. 0 보다 작으면 

	if (0 >= nMaxRow)
		return 0;
	
	//스크롤바의 총길이
	var nMaxHeight = this.m_heScrollBar.style.pixelHeight - this._getGageHeight(); 
	
	var nResult = nMaxHeight / nMaxRow;
	
	return nResult;
}

/*
* 현재행 위치를 나타내는 툴팁을 숨긴다.
*/
ScrollBar.prototype._hideTooltip = function ()
{
	var heTooltip =	document.getElementById("__SCROLL_BAR_TOOLTIP");
	if (null != heTooltip)
	{
		heTooltip.style.display = "none";
	}
}

/*
* 현재행 위치를 나타내는 툴팁을 보여준다.
*/
ScrollBar.prototype._showTooltip = function (nPosX, nPosY)
{
	var heTooltip =	document.getElementById("__SCROLL_BAR_TOOLTIP");
	if (null != heTooltip)
	{
		var nMaxRow = this.getMaxRow();
		if (-1 == nMaxRow)
			return;
		
		heTooltip.style.pixelLeft = nPosX - 110;
		heTooltip.style.pixelTop = nPosY;
		heTooltip.style.display = "block";
		
		var strText = (this.getScrollPosition() + 1) + "/" + nMaxRow;
		heTooltip.innerText = strText;
	}
}

/*
* 툴팁의 현재행 위치를 바꾼다.
*/
ScrollBar.prototype._changeTooltipText = function ()
{
	var heTooltip =	document.getElementById("__SCROLL_BAR_TOOLTIP");
	if (null != heTooltip)
	{
		var strText = (this.getScrollPosition() + 1) + "/" + this.getMaxRow();
		heTooltip.innerText = strText;
	}
}

/**
 * 스크롤바 리스트
 */
ScrollBar._list = new Object();

/**
 * 스크롤바 리스트에서 스크롤바를 얻어온다. (스크롤만큼 스크롤바가 생겨야 함)
 @param strVarName 스크롤바 검색키 = 스크롤의 변수명(=고유함) + //TODO 그다음은 어떻게?
*/
ScrollBar.getScrollBar = function (strVarName)
{
	return ScrollBar._list[strVarName];
}

/**
 * 스크롤 리스트에서 스크롤 바가 눌려져 있는 상태인 스크롤을 찾아 리턴한다.
 */
ScrollBar.getScrollAtom = function ()
{
	for (var strVarName in ScrollAtom._atoms)
	{
		var objScrollAtom = ScrollAtom.getAtom(strVarName);
		var objScrollBar = ScrollBar.getScrollBar(strVarName)
		if (null != objScrollBar)
		{
			if (objScrollBar.isDragging())
			{
				return objScrollAtom;
			}
		}
	}
	return null;
}

/**
 * 스크롤 리스트에서 스크롤 바가 눌려져 있는 상태인 스크롤바를 찾아 리턴한다.
 */
ScrollBar.getDraggingScrollBar = function ()
{
	for (var strVarName in ScrollAtom._atoms)
	{
		var objScrollBar = ScrollBar.getScrollBar(strVarName)
		if (null != objScrollBar)
		{
			if (objScrollBar.isDragging())
			{
				return objScrollBar;
			}
		}
	}
	return null;
}

/**
 * 스크롤바 리스트에 스크롤바를 추가한다.
 * @param strKey 스크롤바 검색키
 * @param objScrollBar 생성된 스크롤바
 */
ScrollBar.addScrollBar = function (strKey, objScrollBar)
{
	ScrollBar._list[strKey] = objScrollBar;
}

/**
 * 게이지 마우스 클릭 이벤트
 */
ScrollBar.onMouseDownGage = function (strVarName, objEvent)
{
	var objScrollBar = ScrollBar.getScrollBar(strVarName);
	if (null != objScrollBar)
	{
		objScrollBar.onMouseDownGage(objEvent);
	}
}

/**
* 스크롤바의 기둥에서 마우스 다운 이벤트 핸들러
*/
ScrollBar.onBodyMouseDown = function (strVarName, objEvent)
{		
	var objScrollBar = ScrollBar.getScrollBar(strVarName);
	if (null != objScrollBar)
	{
		objScrollBar.onScrollBarMouseDown(objEvent);
		ScrollAtom.moveScrollRow(strVarName, objScrollBar.getScrollPosition());
	}
}

/**
* 스크롤바의 기둥에서 마우스 무브 이벤트 핸들러
* @param strVarName 스크롤바 식별자(스크롤 참조변수명)
*/
ScrollBar.onBodyMouseMove = function (strVarName, objEvent)
{
	var objScrollBar = ScrollBar.getScrollBar(strVarName);
	if (null != objScrollBar)
	{
		objScrollBar.onScrollBarMouseMove(objEvent);
		ScrollAtom.moveScrollRow(strVarName, objScrollBar.getScrollPosition());
	}
}

/**
* 스크롤바의 기둥에서 마우스 업 이벤트 핸들러
* @param strVarName 스크롤바 식별자(스크롤 참조변수명)
*/
ScrollBar.onBodyMouseUp = function (strVarName)
{
	var objScrollBar = ScrollBar.getScrollBar(strVarName);
	if (null != objScrollBar)
	{
		objScrollBar.onScrollBarMouseUp();
		ScrollAtom.moveScrollRow(strVarName, objScrollBar.getScrollPosition());
	}
	
}

/**
* 스크롤바의 기둥에서 마우스 휠 이벤트 핸들러
* @param strVarName 스크롤바 식별자(스크롤 참조변수명)
*/
ScrollBar.onBodyMouseWheel = function (strVarName, objEvent)
{
	var objScrollBar = ScrollBar.getScrollBar(strVarName);
	if (null != objScrollBar)
	{
		objScrollBar.onScrollBarMouseWheel(objEvent);
		ScrollAtom.moveScrollRow(strVarName, objScrollBar.getScrollPosition());
	}
}


/**
 * 스크롤바의 위쪽 화살표에서 마우스 업 이벤트 핸들러
 * @param strVarName 스크롤바 식별자(스크롤 참조변수명)
*/
ScrollBar.onTopMouseDown = function (strVarName)
{
	var objScrollBar = ScrollBar.getScrollBar(strVarName);
	if (null != objScrollBar)
	{
		objScrollBar.onTopArrowMouseDown();
		ScrollAtom.moveScrollRow(strVarName, objScrollBar.getScrollPosition());
	}
}

/**
 * 스크롤바의 아래쪽 화살표에서 마우스 다운 이벤트 핸들러
 * @param strVarName 스크롤바 식별자(스크롤 참조변수명)
 */
ScrollBar.onBottomMouseDown = function (strVarName)
{
	var objScrollBar = ScrollBar.getScrollBar(strVarName);
	if (null != objScrollBar)
	{
		objScrollBar.onBottomArrowMouseDown();
		ScrollAtom.moveScrollRow(strVarName, objScrollBar.getScrollPosition());
	}
}

ScrollBar.onBodyMouseOut = function (strVarName, objEvent)
{		
	var objScrollBar = ScrollBar.getScrollBar(strVarName);
	if (null != objScrollBar)
	{
		objScrollBar.onBodyMouseOut(objEvent);
		ScrollAtom.moveScrollRow(strVarName, objScrollBar.getScrollPosition());
	}
}

/*
* 스크롤바의 게이지의 높이 최소값
*/
ScrollBar.MINIMUMSIZE = 5; 
