/* Copyright (c) 2006 Kelvin Luck (kelvin AT kelvinluck DOT com || http://www.kelvinluck.com)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * See http://kelvinluck.com/assets/jquery/jScrollPane/
 * $Id$
 */

/**
 * Replace the vertical scroll bars on any matched elements with a fancy
 * styleable (via CSS) version. With JS disabled the elements will
 * gracefully degrade to the browsers own implementation of overflow:auto.
 * If the mousewheel plugin has been included on the page then the scrollable areas will also
 * respond to the mouse wheel.
 *
 * @example jQuery(".scroll-pane").jScrollPane();
 *
 * @name jScrollPane
 * @type jQuery
 * @param Object	settings	hash with options, described below.
 *								scrollbarWidth	-	The width of the generated scrollbar in pixels
 *								scrollbarMargin	-	The amount of space to leave on the side of the scrollbar in pixels
 *								wheelSpeed		-	The speed the pane will scroll in response to the mouse wheel in pixels
 *								showArrows		-	Whether to display arrows for the user to scroll with
 *								arrowSize		-	The height of the arrow buttons if showArrows=true
 *								animateTo		-	Whether to animate when calling scrollTo and scrollBy
 *								dragMinHeight	-	The minimum height to allow the drag bar to be
 *								dragMaxHeight	-	The maximum height to allow the drag bar to be
 *								animateInterval	-	The interval in milliseconds to update an animating scrollPane (default 100)
 *								animateStep		-	The amount to divide the remaining scroll distance by when animating (default 3)
 *								maintainPosition-	Whether you want the contents of the scroll pane to maintain it's position when you re-initialise it - so it doesn't scroll as you add more content (default true)
 *								tabIndex		-	The tabindex for this jScrollPane to control when it is tabbed to when navigating via keyboard (default 0)
 *								enableKeyboardNavigation - Whether to allow keyboard scrolling of this jScrollPane when it is focused (default true)
 *								animateToInternalLinks - Whether the move to an internal link (e.g. when it's focused by tabbing or by a hash change in the URL) should be animated or instant (default false)
 *								scrollbarOnLeft	-	Display the scrollbar on the left side?  (needs stylesheet changes, see examples.html)
 *								reinitialiseOnImageLoad - Whether the jScrollPane should automatically re-initialise itself when any contained images are loaded (default false)
 * @return jQuery
 * @cat Plugins/jScrollPane
 * @author Kelvin Luck (kelvin AT kelvinluck DOT com || http://www.kelvinluck.com)
 */

(function(b){b.jScrollPane={active:[]};b.fn.jScrollPane=function(c){c=b.extend({},b.fn.jScrollPane.defaults,c);var l=function(){return false};return this.each(function(){var d=b(this);d.css("overflow","hidden");var N=this;if(b(this).parent().is(".jScrollPaneContainer")){var O=c.maintainPosition?d.position().top:0,h=b(this).parent(),k=h.innerWidth(),g=h.outerHeight(),o=g;b(">.jScrollPaneTrack, >.jScrollArrowUp, >.jScrollArrowDown",h).remove();d.css({top:0})}else{O=0;this.originalPadding=d.css("paddingTop")+ " "+d.css("paddingRight")+" "+d.css("paddingBottom")+" "+d.css("paddingLeft");this.originalSidePaddingTotal=(parseInt(d.css("paddingLeft"))||0)+(parseInt(d.css("paddingRight"))||0);k=d.innerWidth();o=g=d.innerHeight();var i=b("<div></div>").attr({className:"jScrollPaneContainer"}).css({height:g+"px",width:k+"px"});c.enableKeyboardNavigation&&i.attr("tabindex",c.tabIndex);d.wrap(i);b(document).bind("emchange",function(){d.jScrollPane(c)})}if(c.reinitialiseOnImageLoad){var t=b.data(N,"jScrollPaneImagesToLoad")|| b("img",d),P=[];t.length&&t.each(function(a,e){b(this).bind("load readystatechange",function(){if(b.inArray(a,P)==-1){P.push(e);t=b.grep(t,function(ba){return ba!=e});b.data(N,"jScrollPaneImagesToLoad",t);var f=b.extend(c,{reinitialiseOnImageLoad:false});d.jScrollPane(f)}}).each(function(){if(this.complete||this.complete===undefined)this.src=this.src})})}h={height:"auto",width:k-c.scrollbarWidth-c.scrollbarMargin-this.originalSidePaddingTotal+"px"};if(c.scrollbarOnLeft)h.paddingLeft=c.scrollbarMargin+ c.scrollbarWidth+"px";else h.paddingRight=c.scrollbarMargin+"px";d.css(h);var u=d.outerHeight(),y=g/u;if(y<0.99){i=d.parent();i.append(b("<div></div>").attr({className:"jScrollPaneTrack"}).css({width:c.scrollbarWidth+"px"}).append(b("<div></div>").attr({className:"jScrollPaneDrag"}).css({width:c.scrollbarWidth+"px"}).append(b("<div></div>").attr({className:"jScrollPaneDragTop"}).css({width:c.scrollbarWidth+"px"}),b("<div></div>").attr({className:"jScrollPaneDragBottom"}).css({width:c.scrollbarWidth+ "px"}))));k=b(">.jScrollPaneTrack",i);var z=b(">.jScrollPaneTrack .jScrollPaneDrag",i),v,p=[],q,r=function(){if(q>4||q%4==0)m(j+v*J);q++};c.enableKeyboardNavigation&&i.bind("keydown.jscrollpane",function(a){switch(a.keyCode){case 38:v=-1;q=0;r();p[p.length]=setInterval(r,100);return false;case 40:v=1;q=0;r();p[p.length]=setInterval(r,100);return false;case 33:case 34:return false;default:}}).bind("keyup.jscrollpane",function(a){if(a.keyCode==38||a.keyCode==40){for(a=0;a<p.length;a++)clearInterval(p[a]); return false}});if(c.showArrows){var A,Q,R=function(){b("html").unbind("mouseup",R);A.removeClass("jScrollActiveArrowButton");clearInterval(Q)},S=function(){b("html").bind("mouseup",R);A.addClass("jScrollActiveArrowButton");q=0;r();Q=setInterval(r,100)};i.append(b("<a></a>").attr({href:"javascript:;",className:"jScrollArrowUp",tabindex:-1}).css({width:c.scrollbarWidth+"px"}).html("Scroll up").bind("mousedown",function(){A=b(this);v=-1;S();this.blur();return false}).bind("click",l),b("<a></a>").attr({href:"javascript:;", className:"jScrollArrowDown",tabindex:-1}).css({width:c.scrollbarWidth+"px"}).html("Scroll down").bind("mousedown",function(){A=b(this);v=1;S();this.blur();return false}).bind("click",l));var T=b(">.jScrollArrowUp",i),U=b(">.jScrollArrowDown",i);if(c.arrowSize){o=g-c.arrowSize-c.arrowSize;k.css({height:o+"px",top:c.arrowSize+"px"})}else{h=T.height();c.arrowSize=h;o=g-h-U.height();k.css({height:o+"px",top:h+"px"})}}var K=b(this).css({position:"absolute",overflow:"visible"}),w,n,J,j=0,B=y*g/2,C=function(a, e){var f=e=="X"?"Left":"Top";return a["page"+e]||a["client"+e]+(document.documentElement["scroll"+f]||document.body["scroll"+f])||0},D=function(){return false},F=function(){E();w=z.offset(false);w.top-=j;n=o-z[0].offsetHeight;J=2*c.wheelSpeed*n/u},V=function(){b("html").unbind("mouseup",V).unbind("mousemove",W);B=y*g/2;b.browser.msie&&b("html").unbind("dragstart",D).unbind("selectstart",D)},m=function(a){j=a=a<0?0:a>n?n:a;z.css({top:a+"px"});var e=a/n;d.data("jScrollPanePosition",(g-u)*-e);K.css({top:(g- u)*e+"px"});d.trigger("scroll");if(c.showArrows){T[a==0?"addClass":"removeClass"]("disabled");U[a==n?"addClass":"removeClass"]("disabled")}},W=function(a){m(C(a,"Y")-w.top-B)};h=Math.max(Math.min(y*(g-c.arrowSize*2),c.dragMaxHeight),c.dragMinHeight);z.css({height:h+"px"}).bind("mousedown",function(a){F();B=C(a,"Y")-j-w.top;b("html").bind("mouseup",V).bind("mousemove",W);b.browser.msie&&b("html").bind("dragstart",D).bind("selectstart",D);return false});var X,G,Y,Z=function(){if(G>8||G%4==0)m(j-(j- Y)/2);G++},$=function(){clearInterval(X);b("html").unbind("mouseup",$).unbind("mousemove",L)},L=function(a){Y=C(a,"Y")-w.top-B};k.bind("mousedown",function(a){F();L(a);G=0;b("html").bind("mouseup",$).bind("mousemove",L);X=setInterval(Z,100);Z();return false});i.bind("mousewheel",function(a,e){e=e||(a.wheelDelta?a.wheelDelta/120:a.detail?-a.detail/3:0);F();E();a=j;m(j-e*J);return a==j});var H,M;function ca(){var a=(H-j)/c.animateStep;if(a>1||a<-1)m(j+a);else{m(H);E()}}var E=function(){if(M){clearInterval(M); delete H}},s=function(a,e){if(typeof a=="string"){$e=b(a,d);if(!$e.length)return;a=$e.offset().top-d.offset().top}i.scrollTop(0);E();var f=u-g;a=a>f?f:a;d.data("jScrollPaneMaxScroll",f);a=a/f*n;if(e||!c.animateTo)m(a);else{H=a;M=setInterval(ca,c.animateInterval)}};d[0].scrollTo=s;d[0].scrollBy=function(a){var e=-parseInt(K.css("top"))||0;s(e+a)};F();s(-O,true);b("*",this).bind("focus",function(){for(var a=b(this),e=0;a[0]!=d[0];){e+=a.position().top;a=a.offsetParent()}a=-parseInt(K.css("top"))||0; var f=a+g;if(!(e>a&&e<f)){f=e-c.scrollbarMargin;if(e>a)f+=b(this).height()+15+c.scrollbarMargin-g;s(f)}});if(location.hash)setTimeout(function(){s(location.hash)},b.browser.safari?100:0);b(document).bind("click",function(a){$target=b(a.target);if($target.is("a")){var e=$target.attr("href");if(e&&e.substr(0,1)=="#"&&e.length>1)setTimeout(function(){s(e,!c.animateToInternalLinks)},b.browser.safari?100:0)}});function da(){b(document).bind("mousemove.jScrollPaneDragging",ea);b(document).bind("mouseup.jScrollPaneDragging", fa)}var I,x;function ga(){direction=I<0?-1:1;d[0].scrollBy(I/2)}function aa(){if(x){clearInterval(x);x=undefined}}function ea(a){var e=d.parent().offset().top,f=e+g;a=C(a,"Y");I=a<e?a-e:a>f?a-f:0;if(I==0)aa();else x||(x=setInterval(ga,100))}function fa(){b(document).unbind("mousemove.jScrollPaneDragging").unbind("mouseup.jScrollPaneDragging");aa()}i.bind("mousedown.jScrollPane",da);b.jScrollPane.active.push(d[0])}else{d.css({height:g+"px",width:k-this.originalSidePaddingTotal+"px",padding:this.originalPadding}); d[0].scrollTo=d[0].scrollBy=function(){};d.parent().unbind("mousewheel").unbind("mousedown.jScrollPane").unbind("keydown.jscrollpane").unbind("keyup.jscrollpane")}})};b.fn.jScrollPaneRemove=function(){b(this).each(function(){$this=b(this);var c=$this.parent();if(c.is(".jScrollPaneContainer")){$this.css({top:"",height:"",width:"",padding:"",overflow:"",position:""});c.after($this).remove()}})};b.fn.jScrollPane.defaults={scrollbarWidth:10,scrollbarMargin:5,wheelSpeed:18,showArrows:false,arrowSize:0, animateTo:false,dragMinHeight:1,dragMaxHeight:99999,animateInterval:100,animateStep:3,maintainPosition:true,scrollbarOnLeft:false,reinitialiseOnImageLoad:false,tabIndex:0,enableKeyboardNavigation:true,animateToInternalLinks:false};b(window).bind("unload",function(){for(var c=b.jScrollPane.active,l=0;l<c.length;l++)c[l].scrollTo=c[l].scrollBy=null})})(jQuery);