/**
@library
Allows for skinnable, customized vertical scrollbars to be placed on your web page(s).

@howto
<pre>
1) Create a &lt;div&gt; ("myScrollableDiv") which must:
	- have a unique id
	- be positioned relative or absolute, and 
	- have a set width and height in px.
	
2) Optionally create a single child &lt;div&gt; of myScrollableDiv which will act as the Content Container Div.  This 
   is recommended to speed up the loading of your document as it will prevent a DOM rebuild and redraw during 
   scroller.init().

3) Put your scroll-needy contents into myScrollableDiv (or into your Content Container Div if you chose to do step 2).  

   Note that you can also replace or manipulate your scrollable contents at any time via DHTML, and the scroller will 
   stay in sync with the changes.

4) Style your page's scrollbars as desired using the following css selectors:
    .scrollee               {the Content Container Div created during Step 4 if you did not choose to perform Step 2}
    .scroller               {the root &lt;div&gt; for the scrollbar that scroller.init() creates}
    .scrollerTop            {the top (up) button of the scrollbar}
    .scrollerBottom         {the bottom (down) button of the scrollbar}
    .scrollerRange          {the area between Top and Bottom in which the Thumb can travel}
    .scrollerRangeTop       {the top &lt;div&gt; of the scrollable range}
    .scrollerRangeBottom    {the bottom &lt;div&gt; of the scrollable range}
    .scrollerThumb          {the Thumb}
    .scrollerThumbTop       {the top &lt;div&gt; of the Thumb}
    .scrollerThumbTop       {the bottom &lt;div&gt; of the Thumb}

	scroller.init() takes care of basic styling of the scrollbar; your only job as part of this step is to
	skin the scrollbar with background colors and/or images.

5) Set scroller's properties.

4) Call scroller.init(document.getElementById('myScrollableDiv'))
</pre>

@examples
<p>
	<a href="https://account-test.station.sony.com/reg/primaryRegistration!input.action?devMode=NONE&platform=lp2">URAM</a>
	uses the scroller object.  
</p>

<p>
	A VM macro renders this HTML:
	<pre>
&lt;div id="uramScroller"&gt;
    &lt;div id="uramScrollee"&gt;
        &lt;div style="margin:2px 2px 3px 2px;"&gt;
            &lt;!-- SCROLLABLE CONTENTS GO HERE --&gt;      
        &lt;/div&gt;
    &lt;/div&gt;&lt;!--uramScrollee--&gt;
&lt;/div&gt;&lt;!--uramScroller--&gt;
	</pre>
</p>
<p>
	The following Image Slices were created for this scrollbar: 
	<ul>
		<li><a href="https://account-test.station.sony.com/images/scrollbar/uram_up.gif">uram_up.gif</a></li>
		<li><a href="https://account-test.station.sony.com/images/scrollbar/uram_thumb.gif">uram_thumb.gif</a></li>
		<li><a href="https://account-test.station.sony.com/images/scrollbar/uram_down.gif">uram_down.gif</a></li>
	</ul>
</p>

<p>
	<a href="https://account-test.station.sony.com/styles/lp2flows.css">https://account-test.station.sony.com/styles/lp2flows.css</a>
	does the minimal styling needed -- it sets #uramScroller to position:relative and provides background images.
	
	<pre>
#uramScroller {
    position:relative;
	overflow:hidden;
}

.scrollerTop {
	background-image:url(../images/scrollbar/uram_up.gif);
}
.scrollerRange {
	background-color:#e2ecf5;
}
.scrollerBottom {
	background-image:url(../images/scrollbar/uram_down.gif);
}
.scrollerThumbTop, .scrollerThumbMiddle, .scrollerThumbBottom {
	background-image:url(../images/scrollbar/uram_thumb.gif);
}
	</pre>	
</p>
<p>
	Lastly, the scroller object is configured and init() is called:
	<pre>
&lt;script&gt;
	scroller.widthScrollBar = 15;
	scroller.heightEndButtons = 15;
	scroller.heightThumbCap = 8;		
	scroller.mouseOverImgX = -16;
	scroller.mouseDownImgX = -32;		
	scroller.init($("uramScroller"), $("uramScrollee"));
&lt;/script&gt;
	</pre>
</p>
*/

var scroller = {
	/* PUBLIC PROPERTIES, BUT YOU'RE NOT LIKELY TO CHANGE THEM */
	classContent : "scrollee",
	classBar : "scroller",
	classBarTop : "scrollerTop",
	classBarBottom : "scrollerBottom",
	classBarRange : "scrollerRange",
	classBarRangeTop : "scrollerRangeTop",
	classBarRangeBottom : "scrollerRangeBottom",
	classBarThumb : "scrollerThumb",
	classBarThumbTop : "scrollerThumbTop",
	classBarThumbMid : "scrollerThumbMiddle",
	classBarThumbBottom : "scrollerThumbBottom",

	/** widthScrollBar - 
		Should be set to the width in pixels of your Thumb and Up/Down button image slices.
	
		@property
		@return Integer
		@default 16
	*/
	widthScrollBar : 16,

	/** heightEndButtons - 
		Should be set to the height in pixels of your Up/Down button image slices.
	
		@property
		@return Integer
		@default 16
	*/
	heightEndButtons : 16,

	/** heightRangeCap - 
		If you'd like to use special graphics for the top and bottom ends of the area in which
		the scrollbar thumb travels, you can style classes .scrollerRangeTop and .scrollerRangeBottom with the image slice(s)
		and set this property to match the height of the slice(s).
	
		@property
		@return Integer
		@default 8
	*/
	heightRangeCap : 8,	

	/** heightThumbCap - 
		If you'd like to use special graphics for the top and bottom ends of the scrollbar thumb, 
		you can style classes .scrollerThumbTop and .scrollerThumbBottom with the image slice(s)
		and set this property to match the height of the slice(s).
	
		@property
		@return Integer
		@default 8
	*/
	heightThumbCap : 8,

	/** mouseOverImgX - 
		Used by scroller to decide where to set the backgroundPositionX of your Thumb and Buttons
		when you hover your mouse over them.
	
		@property
		@return Integer
		@default null
	*/
	mouseOverImgX : null,

	/** mouseDownImgX - 
		Used by scroller.js to decide where to set the backgroundPositionX of your Thumb and Buttons
		when you hold your left mouse button down over them.
	
		@property
		@return Integer
		@default null
	*/
	mouseDownImgX : null,
	
	jumpBy : 51,

	/* PRIVATE PROPERTIES */
	_activeScrollerId : null,	//contains a value only when mouse is over the scrollable box.
	_lastActiveScrollerId : null,
	_isMouseDown : null,
	_isKeyDown : null,
	_isThumbDown : null,
	_timeoutHandle : null,

	/** init - 
		This method must be called once for each &lt;div&gt; that you'd like to give a scrollbar to.
		You'll usually call this method during the loading of your page.
		
		@method	
		@param {HTMLElement} ele The DIV that you want to make scrollable.
		@param {HTMLElement} contentDiv - Single child DIV of @ele
		@return void
	*/
	init : function(ele, contentDiv) {
		if(ele.isScroller) {
			scroller.sizeThumb(ele);
			return;
		}
		ele.isScroller=true;
		ele.style.overflow = "hidden";

		if(!contentDiv) contentDiv = ele.contentDiv;
		if(!contentDiv) {
			tmpHtml = ele.innerHTML;
			ele.innerHTML = "";

			contentDiv = document.createElement("div");

			contentDiv = ele.appendChild(contentDiv);
			contentDiv.innerHTML = tmpHtml;
			ele.contentDiv = contentDiv;
		}
		if(!ele.contentDiv) ele.contentDiv = contentDiv;
					try
		  {

		contentDiv.className = this.classContent;
		contentDiv.style.position="absolute";
		contentDiv.style.overflow="visible";
		contentDiv.style.top="0px";
		contentDiv.style.left="0px";
		contentDiv.style.width = (ele.offsetWidth - scroller.widthScrollBar) + "px";
			  }
catch(err)
  {
  txt="There was an error on this page.\n\n"
  txt+="Error description: " + err.description + "\n\n"
  txt+="Click OK to continue.\n\n"
  alert(txt)
  }
		if(!ele.scrollBarDiv) {
			//scrollbar root
			var tmpDiv = document.createElement("div");
			tmpDiv.className = scroller.classBar;
			tmpDiv = ele.appendChild(tmpDiv);
			tmpDiv.style.position = "absolute";			
			tmpDiv.style.top = "0px";
			tmpDiv.style.left = (ele.offsetWidth - scroller.widthScrollBar) + "px";
			tmpDiv.style.width = scroller.widthScrollBar + "px";
			tmpDiv.style.height = ele.offsetHeight + "px";
			ele.scrollBarDiv = tmpDiv;
			
			//top button
			tmpDiv = document.createElement("div");
			tmpDiv.className = scroller.classBarTop;
			tmpDiv.style.height = scroller.heightEndButtons + "px";
			tmpDiv.style.overflow="hidden";
			tmpDiv = ele.scrollBarDiv.appendChild(tmpDiv);
			tmpDiv.onmousedown = scroller.mouseDown;
			tmpDiv.rootScrollerId = ele.id;
			
			//range in which thumb can travel
			tmpDiv = document.createElement("div");
			tmpDiv.className = scroller.classBarRange;
			ele.scrollRangeHeight = ele.offsetHeight - (scroller.heightEndButtons * 2);
			tmpDiv.style.position="relative";
			tmpDiv.style.height = ele.scrollRangeHeight + "px";
			tmpDiv.style.overflow="hidden";
			tmpDiv = ele.scrollBarDiv.appendChild(tmpDiv);
			tmpDiv.onmousedown = scroller.mouseDown;
			tmpDiv.rootScrollerId = ele.id;
			ele.scrollRangeDiv = tmpDiv;
			
			//top of range (for skinning bg image)
			tmpDiv2 = document.createElement("div");
			tmpDiv2 = tmpDiv.appendChild(tmpDiv2);
			tmpDiv2.className = scroller.classBarRangeTop;
			
			tmpDiv2.style.height = (ele.scrollRangeHeight - scroller.heightRangeCap) + "px";
			tmpDiv2.style.overflow="hidden";
			tmpDiv2.rootScrollerId = ele.id;
			ele.scrollRangeTopDiv = tmpDiv2;

			//bottom of range (for skinning bg image)
			tmpDiv2 = document.createElement("div");
			tmpDiv2 = tmpDiv.appendChild(tmpDiv2);
			tmpDiv2.className = scroller.classBarRangeBottom;
			tmpDiv2.style.height = scroller.heightRangeCap + "px";
			tmpDiv2.style.overflow="hidden";
			tmpDiv2.rootScrollerId = ele.id;
			ele.scrollRangeBottomDiv = tmpDiv2;
			
			//the Thumb
			tmpDiv2 = document.createElement("div");
			tmpDiv2.className = scroller.classBarThumb;
			tmpDiv2.style.position="absolute";
			tmpDiv2.style.width="100%";
			tmpDiv2 = tmpDiv.appendChild(tmpDiv2);
			tmpDiv2.onmouseover=scroller.mouseOver;
			tmpDiv2.onmousedown = scroller.mouseDown;
			ele.thumbDiv = tmpDiv2;
			Drag.init(ele.thumbDiv, null, 0, 0, 0, ele.scrollRangeHeight - 10);
			ele.thumbDiv.onDrag = function(x, y) {scroller.onThumbDrag(this, y);};
			tmpDiv2.rootScrollerId = ele.id;

			//top of thumb (for skinning bg image)
			tmpDiv = document.createElement("div");
			tmpDiv.className = scroller.classBarThumbTop;
			tmpDiv.style.fontSize="1px";
			tmpDiv.style.lineHeight="1px";
			tmpDiv.rootScrollerId = ele.id;
			tmpDiv = tmpDiv2.appendChild(tmpDiv);
			ele.thumbTop = tmpDiv;

			//middle of thumb (for skinning bg image)
			tmpDiv = document.createElement("div");
			tmpDiv.className = scroller.classBarThumbMid;
			tmpDiv.style.fontSize="1px";
			tmpDiv.style.lineHeight="1px";
			tmpDiv.rootScrollerId = ele.id;
			tmpDiv = tmpDiv2.appendChild(tmpDiv);
			ele.thumbMid = tmpDiv;

			//bottom of thumb (for skinning bg image)
			tmpDiv = document.createElement("div");
			tmpDiv.className = scroller.classBarThumbBottom;
			tmpDiv.style.fontSize="1px";
			tmpDiv.style.lineHeight="1px";
			tmpDiv.rootScrollerId = ele.id;
			tmpDiv = tmpDiv2.appendChild(tmpDiv);
			ele.thumbBot = tmpDiv;
			
			//bottom button
			tmpDiv = document.createElement("div");
			tmpDiv.className = scroller.classBarBottom;
			tmpDiv.style.height = scroller.heightEndButtons + "px";
			tmpDiv.style.overflow="hidden";
			tmpDiv.rootScrollerId = ele.id;
			tmpDiv = ele.scrollBarDiv.appendChild(tmpDiv);						
			tmpDiv.onmousedown = scroller.mouseDown;

		}

		scroller.sizeThumb(ele);

		$attachEv(document, "mouseup", scroller.mouseUp);
		$attachEv(ele, "DOMMouseScroll", scroller.mouseWheel);
		$attachEv(ele, "mouseover", scroller.mouseOver);
		$attachEv(ele, "mouseout", scroller.mouseOut);
		$attachEv(ele, "scroll", scroller.onForcedScroll);
		if(typeof document.onactivate != "undefined") {
			$attachEv(ele.contentDiv, "activate", scroller.childFocused);
		}
//		else {
//			$attachEv(ele, "focus", scroller.childFocused);
//		}			
	},

	topRelTo : function(ele,par) {
		var oTop = 0;
		par=ele;
		while(par) {
			oTop += par.offsetTop;
			par=par.offsetParent;
			if(par==root.contentDiv) break;
		}
		return oTop;
	},

	childFocused : function(evt) {
		evt=$ev(evt); ele=evt.target; 
		root = scroller.getParentScroller(ele);
		if(typeof root=="undefined") return;
		
		if(typeof ele != "undefined") {
			var oX = scroller.topRelTo(ele,root.contentDiv);
			if (-root.contentDiv.offsetTop > oX) {
				//focus has moved to an element which is 
				//currently hidden (the user has scrolled down past it a bit).

				//if we're about to scroll very near the top (within 15px), 
				//let's just go all the way to the top.
				if(oX < 15) {
					scroller.moveContentBy(root, root.contentDiv.offsetTop);
				}
				else {
					scroller.moveContentBy(root, -(-root.contentDiv.offsetTop - oX));
				}
			}
		}
	},

	mouseOver : function(evt) {
		evt=$ev(evt); ele=evt.target; if(ele==null) return; root = scroller.getParentScroller(ele);
//document.title="mouseOver," + (ele.id || ele.nodeName) + "," + (root.id || "null");
		if(root) {
			scroller._activeScrollerId = root.id;
			scroller._lastActiveScrollerId = scroller._activeScrollerId;
		}
		switch(ele.className) {
			case scroller.classBarTop:
			case scroller.classBarBottom:
				offset = (scroller._isMouseDown) ? scroller.mouseDownImgX : scroller.mouseOverImgX;
				if(offset != null) ele.style.backgroundPosition = offset + "px 0px";
				break;
				
			case scroller.classBarThumbTop:
			case scroller.classBarThumbMid:
			case scroller.classBarThumbBottom:
				if(!scroller._isThumbDown && scroller.mouseOverImgX) scroller.moveThumbBGs(root,scroller.mouseOverImgX);
				evt.preventDefault();
				break;
			default:
		}
	},
	mouseOut : function(evt) {
		evt=$ev(evt); ele=evt.target; if(ele==null) return; root = scroller.getParentScroller(ele);

		switch(ele.className) {
			case scroller.classBarTop:
			case scroller.classBarBottom:
				scroller._isMouseDown=false;
				ele.style.backgroundPosition = "0px 0px";
				break;
							
			case scroller.classBarThumbTop:
			case scroller.classBarThumbMid:
			case scroller.classBarThumbBottom:
				if(!scroller._isThumbDown && scroller.mouseOverImgX) {
					evt=$ev(evt);
					var ele=evt.srcElement; root = $(ele.rootScrollerId);
					scroller.moveThumbBGs(root, 0);
				}
				break;
			default:
				if(ele == root) scroller._activeScrollerId=null;
				break;
		}
	},

	mouseDown : function(evt) {
		evt=$ev(evt), ele=evt.srcElement; root = scroller.getParentScroller(ele);
		
		var incr = null;
		scroller._isMouseDown=true;

		switch(ele.className) {
			case scroller.classBarTop:
			case scroller.classBarBottom:
				incr = (ele.className==scroller.classBarTop) ? -scroller.jumpBy : scroller.jumpBy; 
				if(scroller.mouseDownImgX) {
					ele.style.backgroundPosition = scroller.mouseDownImgX + "px 0px";
				}
				
				break;

			case scroller.classBarRangeTop:
			case scroller.classBarRangeBottom:
				if(evt.y < root.thumbDiv.offsetTop)
					incr = -(root.offsetHeight - scroller.jumpBy);
				else
					incr = (root.offsetHeight - scroller.jumpBy);
				break;

			case scroller.classBarThumbTop:
			case scroller.classBarThumbMid:
			case scroller.classBarThumbBottom:
				if(scroller.mouseDownImgX) {
					scroller._isThumbDown=true;
					scroller.moveThumbBGs(root, scroller.mouseDownImgX);
				}
				evt.cancelBubble=true;
				break;
		}
		if(incr) {
			window.setTimeout("scroller.moveContentBy($('" + root.id + "')," + incr + ");", 1);			
			window.setTimeout("scroller.moveContentBy($('" + root.id + "')," + incr + ", true);", 250);			
		}
	},
	mouseUp : function(evt) {
		if(!scroller._isMouseDown) return;
		evt=$ev(evt);
		var ele=evt.srcElement;

		scroller._isMouseDown=false;
		if(scroller._timeoutHandle) {
			window.clearTimeout(scroller._timeoutHandle);
			scroller._timeoutHandle=null;
		}

		if(scroller._isThumbDown) {
			var root = $(scroller._lastActiveScrollerId);
			if(scroller.mouseOverImgX && (ele.className==scroller.classBarThumbMid || ele.className == scroller.classBarThumbTop || ele.className == scroller.classBarThumbBottom)) {
				scroller.moveThumbBGs(root, scroller.mouseOverImgX);
			}
			else {
				scroller.moveThumbBGs(root, 0);
			}		
			scroller._isThumbDown=false;
			root=null;
		}
		ele=null;
	},

	mouseWheel : function(evt) {
		if(scroller._activeScrollerId && !$(scroller._activeScrollerId).noScrolling) {
			evt=$ev(evt);
			if(evt.target.nodeName.toLowerCase()=="select") return;
			if(evt.wheelDirection)
				scroller.moveContentBy($(scroller._activeScrollerId),-(scroller.jumpBy * evt.wheelDirection));
			evt.preventDefault();
		}
	},

	moveContentBy : function(ele,incr,checkMouseKeyDown) {
		if(checkMouseKeyDown) {
			if (scroller._isMouseDown || scroller._isKeyDown)
				scroller._timeoutHandle = window.setTimeout("scroller.moveContentBy($('" + ele.id + "')," + incr + ", true)",60);
			else
				return;
		}
		
		y = -ele.contentDiv.offsetTop + incr;
		if (y < 0) y = 0;
		else if (y > root.maxContentY) y=root.maxContentY;
		if(y==ele.lastContentY) return;
		ele.lastContentY=y;
		ele.contentDiv.style.top = -y + "px";

		//set thumb top such that [Thumb Top / Thumb Max Top = Content Top / Content Max Top]
		var thumbY = parseInt((ele.maxThumbY * y) / ele.maxContentY);
		ele.thumbDiv.style.top = thumbY + "px";
	},

	moveThumbBGs : function(root,offset) {
		root.thumbTop.style.backgroundPosition = offset + "px 0%";
		root.thumbMid.style.backgroundPosition= offset + "px 50%";
		root.thumbBot.style.backgroundPosition = offset + "px 100%";
	},
	
	onThumbDrag : function(ele, y) {
		if(ele.noScrolling) return;		
		var root = $(ele.rootScrollerId);
		if (y < 0) y = 0;
		else if (y > root.maxThumbY) y=root.maxThumbY;

		if (ele.offsetTop != y) ele.style.top = y + "px";
		if(y==root.lastThumbY) return;
		root.lastThumbY=y;

		//set content div top such that [Content Top / Content Max Top = Thumb Top / Thumb Max Top]
		y = parseInt((root.maxContentY * y) / root.maxThumbY);
		root.contentDiv.style.top = (y * -1) + "px";
		root=null;
	},

	/* called internally only */
	sizeThumb : function(ele) {
		if(ele.offsetHeight >= ele.contentDiv.offsetHeight) {
			ele.noScrolling=true;
			ele.scrollBarDiv.style.display="none";
			ele.contentDiv.style.width=ele.offsetWidth + "px";
		}
		else if (ele.noScrolling) {
			ele.noScrolling=false;
			ele.contentDiv.style.width=(ele.offsetWidth - scroller.widthScrollBar) + "px";
			ele.scrollBarDiv.style.display="block";
		}
		
		if(!ele.noScrolling) {
			ele.scrollBarDiv.style.height = ele.offsetHeight + "px";
			//set thumb height such that [Thumb Height / scrollable range Height = RootDiv.Height / Content Div.Height]
			var thumbHeight = parseInt((ele.scrollRangeHeight * ele.offsetHeight) / ele.contentDiv.offsetHeight);
			if(thumbHeight < (scroller.heightThumbCap * 3)) thumbHeight = scroller.heightThumbCap * 3;
		
			ele.thumbTop.style.height = scroller.heightThumbCap + "px";
			ele.thumbMid.style.height = (thumbHeight - (scroller.heightThumbCap * 2)) + "px";
			ele.thumbBot.style.height = scroller.heightThumbCap + "px";
			scroller.moveThumbBGs(ele, 0);

			ele.thumbHeight = thumbHeight;
			ele.maxThumbY = ele.scrollRangeHeight - thumbHeight;
			ele.maxContentY = ele.contentDiv.offsetHeight - ele.offsetHeight;
	
			//set thumb top such that [Thumb Top / Thumb Max Top = Content Top / Content Max Top]
			var thumbY = parseInt((ele.maxThumbY * -ele.contentDiv.offsetTop) / ele.maxContentY);
			if(ele.thumbDiv.offsetTop != thumbY) ele.thumbDiv.style.top = thumbY + "px";				
		}
		ele.contentHeight = ele.contentDiv.offsetHeight;
		ele.lastOffsetHeight = ele.offsetHeight;
		scroller.pollContentHeight(ele);
	},

	pollContentHeight : function(ele) {
		if(ele.contentDiv.offsetHeight != ele.contentHeight) {
			scroller.sizeThumb(ele);
		}
		else if (ele.lastOffsetHeight != ele.offsetHeight && ele.offsetHeight > 0) {
			ele.scrollRangeHeight = ele.offsetHeight - (scroller.heightEndButtons * 2);
			ele.scrollRangeDiv.style.height = ele.scrollRangeHeight + "px";
			ele.scrollRangeTopDiv.style.height = (ele.scrollRangeHeight - scroller.heightRangeCap) + "px";
			ele.scrollRangeBottomDiv.style.height = scroller.heightRangeCap + "px";
			scroller.sizeThumb(ele);
		}
		
		window.setTimeout("scroller.pollContentHeight($('" + ele.id + "'))", 350);
	},

	onForcedScroll : function(evt) {
		//captures root div onscroll event when user tabs down into
		//a focusable element which is below the viewable area.		
		evt=$ev(evt);ele=evt.target;
		var sT = ele.scrollTop;

		if(sT != 0) {
			ele.scrollTop = 0;
			scroller.moveContentBy(ele, sT);
		}
	},
	
	getParentScroller : function(ele) {
		if(ele.rootScrollerId) return $(ele.rootScrollerId);
		if(ele.contentDiv) return ele;
		ele=DOM.getParentByClass(ele,scroller.classContent);
		if(ele) return ele.parentNode;
	}
};

