﻿
/*
	SkyScrapper Module
	2009.03.12

	★ 기본 개체 생성시 인자

	arrObjList : 스크롤시 움직일 Object 의 정보가 담긴 Json 을 담은 배열

	strSelfID : 지금 현재 FloatTop 개체를 만들어서 변수에 넣었을 것이다. -_-; 그 이름이다. 예를 들어
			 var aa = new FloatTop( ... 이렇게 만들었다면 "aa" 를 넣어주면 됨.

	★ arrObjList 의 배열 데이터가 될 Json 속성

	strObjID : Object ID

	intStyleTop :	움직일 개체의 style.top 값. 미지정시 0. (pixel)
					css 로 지정된 top 속성은 자바스크립트에서 읽지 못하므로 지정해줌.

	intIndentTop :	화면스크롤시 움직일 개체가, 화면 최상단에서 몇 픽셀 밑에 위치할 것인지. 미지정시 10 (pixel)
					즉 개체가 스크롤시 화면을 따라 내려갔을때, 이 값이 만약 10 이라면 화면 최상단에서 10px 아래 떠있게 된다.

	intBottomBound :이 개체가 어느 위치까지 내려갈 수 있는지 지정. 미지정시 document 개체의 offsetHeight 를 지정.
					움직일 개체의 현재 (document 전체에서의) offsetTop + offsetHeight 가 이 값보다 크다면, offsetTop 을 intBottomBound - offsetHeight 로 고정.

	intInterval :	setTimeout 간격 시간값. ms
					시간의 간격 (Term, Delay) 를 의미하므로 이 값이 커지면 개체 이동속도가 느려지고,
					이 값이 작아지면 개체 이동속도가 빨라짐.

					IntervalSeconds ↑ , intMoveSpeed ↓ : 개체 이동속도 느려짐
					IntervalSeconds ↓ , intMoveSpeed ↑ : 개체 이동속도 빨라짐

	intMoveSpeed :	intMoveSpeed : 
					움직이는 개체가 한번 동작할 때 움직이는 거리를 계산하기 위해 사용. 수치가 커질수록 움직이는 거리가 커져서 개체의 속도가 빨라진다.

	★ 선언

	var 변수이름 = new FloatTop(움직일 Object의 Json 정보가 담긴 배열, "현재개체이름");

	★ 사용 예

	var objFloat;
	var _window_onload = window.onload;

	window.onload = function () 
	{
		// 1안 : 속성을 모두 쓴다.
		var arrFloatObj = [
			{ 
			  strObjID : "bible_sky"
			, intStyleTop : 0
			, intIndentTop : 10
			, intBottomBound : 6000
			, intInterval : 30
			, intMoveSpeed : 300 
			}
		,	{ 
			  strObjID : "scPhoto"
			, intStyleTop : 0
			, intIndentTop : 10
			, intBottomBound : 6000
			, intInterval : 30
			, intMoveSpeed : 300 
			}
		];

		// 2안 : 움직일 개체의 ID 만 지정하고 나머지는 디폴트로 쓴다.
		var arrFloatObj = [
			{ strObjID : "bible_sky" }
		,	{ strObjID : "scPhoto" }
		];

		objFloat = new FloatTop(arrFloatObj, "objFloat");
		objFloat.initObj();

		if (_window_onload) _window_onload();
	}

	이런식으로 onload 이벤트에 사용해도 되겠다.

*/

////
//	FloatTop 개체 정의
//
function FloatTop(arrObjList, strSelfID) {

	this.getList = function () {
		return arrObjList;
	}

	this.getFloatObj = function (intObjIndex) {
		return document.getElementById(arrObjList[intObjIndex].strObjID);
	}

	/*
		현재 동작하고 있는 개체의 instance name, setTimeout 할 시에 개체이름이 필요하기 때문에 입력받음
	*/
	this.getSelfID = function () { 
		return strSelfID; 
	};

	this.docType = 0;		// 0 : xhtml 1.0, 1 : html 4.01

	/*
		기본 옵션 끝
	*************************************************************************************************** */

	/*
		개체 속도 (intMoveSpeed) 의 최대값, 이 값보다 intMoveSpeed 의 입력값이 크면 이 값으로 고정
	*/
	var intMoveSpeedBound = 1000;
	this.getMoveSpeedBound = function () {
		return intMoveSpeedBound;
	}
	this.setMoveSpeedBound = function (intNewBound) {
		var regChkNum = /[^\d]/gi;
		if (!regChkNum.test(intNewBound)) intMoveSpeedBound = intNewBound;
	}

	/*
		최저속도 이동픽셀 값 : 개체가 스크롤된 목적지로 이동해서 거의 도달했을때, 이 값이 지정되어있으면
		목적지와 개체위치의 간격이 이 값 이하가 되었을 때, 한번의 setTimeout 시 1픽셀씩만 움직이게 된다.
		목적지에 거의 도달했을 때 천천히 움직이게 되는 효과.
	*/
	var intMod = 5;
	this.getDefaultMod = function () { return intMod; }
	this.setDefaultMod = function (newMod) { intMod = newMod; }
}

////
//	initObj() : 기본 옵션 설정.
//	2009.03.11
//	이동시킬 개체의 position 과 위치를 설정, 
//	Top 과 개체의 최소간격 크기가 없으면 10px 로
//	개체가 내려갈 수 있는 한계값이 없으면 문서 Height 최대값으로 설정.
//
FloatTop.prototype.initObj = function () {

	var arrList = this.getList();

	for (var i = 0; i < arrList.length; i++) {

		var objFloat = document.getElementById(arrList[i].strObjID);

		// FloatStyleTop 값이 입력되지 않았으면 0 을 지정.
		arrList[i].intStyleTop = checkNull(arrList[i].intStyleTop, 0);

		// FloatStyleTop 을 지정한 대로 실제 개체의 style 값을 조정해준다.
		objFloat.style.top = arrList[i].intStyleTop + "px";

		arrList[i].intInterval = checkNull(arrList[i].intInterval, 30);
		arrList[i].intMoveSpeed = checkNull(arrList[i].intMoveSpeed, 300);
		arrList[i].intIndentTop = checkNull(arrList[i].intIndentTop, 10);
		arrList[i].intBottomBound = checkNull(arrList[i].intBottomBound, getMaxHeight(this.docType));

		arrList[i].intOffsetTop = getBounds(objFloat).top;
		arrList[i].intHeight = objFloat.offsetHeight;

		arrList[i].intTopBound = arrList[i].intOffsetTop - arrList[i].intIndentTop;
		arrList[i].intOffsetModifier = arrList[i].intOffsetTop - arrList[i].intStyleTop;

		arrList[i].intSelfIndex = i;

		/*
			setTimeout 으로 이동시 한번에 몇 픽셀 이동할 것인가 결정하는 값.
			setTimeout 시마다 현재 개체의 위치와 이동할 목적지의 픽셀 차이를 고려하여 계산되어 조금씩 바뀐다.

			즉, maxspeed 값을 위에 정의된 intMoveSpeed 속도로 나눈 결과값으로, 현 개체 위치와 목적지사이의
			픽셀 간격을 나누어서 한번에 이동할 픽셀 크기를 계산한다.
		*/
		arrList[i].intMod = 0;

		/*
			setTimeout 시에 Timeout ID 값을 받아서 저장한다.
		*/
		arrList[i].strIntervalID = null;
	}

	var ftf = this;

	/*
		onScroll 이벤트가 window 에 일어날 때마다 이동 이벤트를 발생시킨다.
	*/
	var arrList = ftf.getList();

	$.each(arrList, function(index, value) {
		$(window).bind("scroll", function() {
			var intGotoPos = value.intStyleTop;

			if (getTop() >= value.intTopBound) 
				intGotoPos = getTop() - value.intOffsetModifier + value.intIndentTop;

			if (getTop() + value.intHeight > value.intBottomBound)
				intGotoPos = value.intBottomBound - value.intHeight - value.intOffsetModifier;

			ftf.gotoPos(value, intGotoPos);
		});
	});

	// 만약에 페이지 로딩이 처음 될때, 스크롤바가 최상단에 위치하고 있지 않다면 스크롤을 하기전엔 배너가 위에 안보이는 상태로 남는다.
	// 그래서 강제로 이벤트를 한번 실행해줌.
//	window.onscroll();
}

////
//	getBrowserCode : 브라우저를 판별해 특정 코드를 반환.
//	2009.03.13
//
function getBrowserCode() {
	var strBrowser = navigator.userAgent.toLowerCase();

	if (strBrowser.indexOf("msie") > -1) 
		return 0;
	else if (strBrowser.indexOf("safari") > -1 || strBrowser.indexOf("opera") > -1 )
		return 1;
	else if (strBrowser.indexOf("firefox") > -1)
		return 2;
	else
		return 99;
}

////
//	getMaxHeight : 각 브라우저 타입별로, 현재 문서의 Height 길이를 구함.
//	2009.03.13
//
function getMaxHeight (intDocType) {
	var intBrowser = getBrowserCode();

	if (intBrowser == 0 && intDocType == 1)
		return document.documentElement.scrollHeight;
	else if ((intBrowser == 0 && intDocType == 0) || intBrowser == 1)
		return document.body.scrollHeight;
	else if (intBrowser == 2)
		return document.documentElement.offsetHeight;
	else 
		return document.body.offsetHeight;
}

////
//	checkNull : Json 으로 입력된 값들이 없을때, 기본값을 지정해주기 위해 사용.
//	2009.03.11
//
function checkNull (varValue, varDefaultValue) {
	return (varValue == undefined || varValue == null) ? varDefaultValue : varValue;
}

////
//	getTop : 스크롤되는 페이지에서 현재 화면의 상단이, 문서 전체에서 어느 위치인지 픽셀값을 알아내는 함수
//
function getTop () {

	var intBrowser = getBrowserCode();

	if (intBrowser == 0 && this.docType == 0) {
		return document.body.scrollTop;
	}
	else if (document.documentElement.scrollTop) {
		return document.documentElement.scrollTop;
	} 
	else if (window.pageYOffset) {
		return window.pageYOffset;
	} 
	else {
		return 0;
	}
}

////
//	getBounds : 개체의 top, left, width, height 반환 함수
//	2009.03.05
//	출처 : http://techbug.tistory.com/108
//
function getBounds(obj) {
	// var obj = this.getFloatObj(intObjIndex);
	if (!obj) return undefined;

	var tempObj = new Object();

	if (obj.getBoundingClientRect) {
		var box = obj.getBoundingClientRect();

		/*
			getBoundingClientRect : 
			이 개체의 top 값은 현재 값을 구하려는 개체 (이하 obj) 의 document 에 대한 전체 offsetTop 값이 아니며,
			document 에 대한 offsetTop 값을 기준으로 한 상대값이다.
			즉, obj 의 document 에 대한 top 값, 예를 들어 100px 라 하고 현재 window.scrollTop 값, 즉 스크롤되어서 윈도우의 최상단이 document의
			어떤 위치인지를 200px 라고 하면 -100 이 나온다. 즉 현 document 전체에 대한 Absolute offsetTop 값을 구하고 싶다면, 
			window.scrollTop + getBoundingClientRect.top 값이 바로 그 값이다. (여기선 scrollTop 구하는 함수가 getTop())
		*/
		tempObj.top = box.top + getTop();
		tempObj.left = box.left;
		tempObj.width = box.right - box.left;
		tempObj.height = box.bottom - box.top + 1; // + 1 : Moz 와 맞춤
	}
	else if (document.getBoxObjectFor) {
		var box = document.getBoxObjectFor(obj);

		tempObj.top = box.y;
		tempObj.left = box.x;
		tempObj.width = box.width;
		tempObj.height = box.height;
	}
	else {
		tempObj.top = obj.offsetTop;
		tempObj.left = obj.offsetLeft;
		tempObj.width = obj.offsetWidth;
		tempObj.height = obj.offsetHeight + 3; // +3 : Moz 와 맞춤

		var parent = obj.offsetParent;
		if (parent != obj) {
			while (parent) {
				tempObj.top += parent.offsetTop;
				tempObj.left += parent.offsetLeft;

				parent = parent.offsetParent;
			}
		}

		// 오페라와 사파리의 absolute position 의 경우, body의 offsetTop 을 잘못 계산, 보정
		// 동동 : 이게 어느 버전을 기준인건지..? 같은 엔진의 크롬은 문제가 없는지? 확인해볼 필요가 있음.
		var ua = navigator.userAgent.toLowerCase();
		if ((ua.indexOf("opera")!= -1 || ua.indexOf("safari") != -1) && obj.style.position == "absolute") {
			tempObj.top -= document.body.offsetTop;
		}
	}

	return tempObj;
}

////
//	getCurrentDiff : 이동해야 할 위치와 현재 개체의 Top 위치와의 픽셀 차이값을 얻는다.
//		intGotoPos : 이동할 픽셀. (top 에 지정할 값)
//
FloatTop.prototype.getCurrentDiff = function (objFloat, intGotoPos) { 
	return Math.floor(Math.abs(parseInt(objFloat.style.top, 10) - intGotoPos));
};

////
//	gotoPos		: 스크롤이 되었을때 움직이는 메뉴바를 스크롤된 위치로 옮기는 함수
//	intGotoPos	: 움직여서 도착할 위치, 현재 개체의 style.top 이 이동해야 될 픽셀값.
//
FloatTop.prototype.gotoPos = function (objFloatData, intGotoPos) {
	// 이 함수가 실행되기 이전에 메뉴바가 이미 이동하고 있었다면 중지
	if (objFloatData.strIntervalID != null) {
		clearTimeout(objFloatData.strIntervalID);
		objFloatData.strIntervalID = null;
	}

	// 한번에 몇픽셀씩 이동할 것인지 설정하고
	objFloatData.intMod = (this.getCurrentDiff(this.getFloatObj(objFloatData.intSelfIndex), intGotoPos) / 10) + 1;

	// 이동시킨다~
	objFloatData.strIntervalID = setTimeout(this.getSelfID() + ".moveFloatObj(" + objFloatData.intSelfIndex + "," + intGotoPos + ")", objFloatData.intInterval);
}

////
//	moveFloatObj : 실제로 메뉴바를 이동시키는 함수
//		intDataIndex : arrObjList 에서 현재 이동시킬 개체가 위치한 index 값.
//		intGotoPos : 개체가 움직여 도착할 위치, 개체의 style.top 값, pixel 값.
//
FloatTop.prototype.moveFloatObj = function (intDataIndex, intGotoPos) {
	var objFloatData = this.getList()[intDataIndex];
	var objFloat = this.getFloatObj(intDataIndex);

	clearTimeout(objFloatData.strIntervalID);

	/*
		움직일 개체의 현재 위치값 (전체 문서 크기를 기준으로) 을 픽셀로 얻어냄.
	*/
	var currentPos = parseInt(objFloat.style.top, 10);
	var intDiff = this.getCurrentDiff(objFloat, intGotoPos);

	/*
		현재 위치값과 움직여야될 목적지와의 픽셀값 차이를 얻어서
		그 차이값이 0 이면 (즉 목적한 Position 에 도착했으면) Timeout 걸어놓은것을 지우고 리턴 (이동 멈춤)
	*/
	// alert(this.getCurrentDiff(objFloat, intGotoPos));
	if (this.getCurrentDiff(objFloat, intGotoPos) == 0) {
		//if (objFloatData.strIntervalID != null) clearTimeout(objFloatData.strIntervalID);
		objFloatData.strIntervalID = null;

		return;
	}
	/*
		현재 위치와 목적지의 픽셀 차이값이 미리 지정해놓은 최소 차이값 미만이면 이동픽셀 간격을 1로 바꾼다.
		슬라이드 되어서 목적지 근처에 오면 천천히 움직이게 되는 효과
	*/
	else if (intDiff < this.getDefaultMod()) {
		objFloatData.intMod = 1;
	}
	/*
		그렇지 않으면 차이값을 개체 생성시 입력된 MoveSpeed 값으로 나눈 값을 한번에 이동할 이동픽셀 간격으로 지정.
	*/
	else {
		objFloatData.intMod = (intDiff / (this.getMoveSpeedBound() / objFloatData.intMoveSpeed)) + 1;
		// objFloatData.intMod = intDiff - this.getDefaultMod();
	}

	/*
		현재 위치와 목적지의 위치를 계산해 위로 움직일것인지 아래로 움직일 것인지 결정
	*/
	var intDirectionModifier = (currentPos > intGotoPos) ? -1 * objFloatData.intMod : 1 * objFloatData.intMod;

	/*
		이동시키고
	*/
	objFloat.style.top = (currentPos + intDirectionModifier) + "px";

	/*
		다음번 이동을 위해 새로 Timeout 을 걸어준다.
	*/
	objFloatData.strIntervalID = setTimeout(this.getSelfID() + ".moveFloatObj(" + intDataIndex + "," + intGotoPos + ")", objFloatData.intInterval);
}

