/*
Author: John Botica
Version: 1.0
*/
(function($){
    var ScrollBar = function(p,o){
        var self = this;
        
        //extend the pane and content elements
        this.pane = $(p);
        this.content = $(this.pane.children()[0]);
        
        //scrollbar elements created                
        this.pane.append('<div class="scrollbarWrapper"><div class="scrollbar"></div></div>');
        this.barwrapper = $(this.pane.find('.scrollbarWrapper')[0]);
        this.scrollbarElem = this.barwrapper.find('.scrollbar');
        
        //default options
        this.options = {
            scrollspeed: 10,
            arrows: false,
            arrowPosition: 'split'
        };
        //default values
        this.value = 0;
        this.handleProportion = 0;
        this.handleSize = 0;
        this.scrollBarOffset = 0;
        this.arrowHeight = 0;
        
        var UA = navigator.userAgent.toLowerCase();
			Browser = {
				chrome: UA.match(/chrome/) ? true : false,
				firefox: UA.match(/firefox/) ? true : false,
				firefox2: UA.match(/firefox\/2/) ? true : false,
				firefox30: UA.match(/firefox\/3\.0/) ? true : false,
				msie: UA.match(/msie/) ? true : false,
				msie6: (UA.match(/msie 6/) && !UA.match(/msie 7|8/)) ? true : false,
				msie7: UA.match(/msie 7/) ? true : false,
				msie8: UA.match(/msie 8/) ? true : false,
				opera: UA.match(/opera/) ? true : false,
				safari: (UA.match(/safari/) && !UA.match(/chrome/)) ? true : false
			};
		for(var b in Browser){
			if(Browser[b] === true){
				Browser._this = b;
			}
		}
		if(Browser.chrome === true) {
			Browser.version = UA.match(/chrome\/([0-9\.]+)/)[1];
		}
		if(Browser.firefox === true) {
			Browser.version = UA.match(/firefox\/([0-9\.]+)/)[1];
		}
		if(Browser.msie === true) {
			Browser.version = UA.match(/msie ([0-9\.]+)/)[1];
		}
		if(Browser.opera === true) {
			Browser.version = UA.match(/version\/([0-9\.]+)/)[1];
		}
		if(Browser.safari === true) {
			Browser.version = UA.match(/version\/([0-9\.]+)/)[1];
		}
        
        //size function - sets the size of the scrollbar elements
        this.size = function(){
            var remainder = Math.max(0,(self.content.height() - self.pane.height() + self.arrowHeight));
    		var proportion = remainder / Math.max(1,self.content.height());
            self.handleProportion = (100 - Math.round(proportion * 100)) / 100;
    		self.handleSize = Math.max(10,Math.ceil(self.pane.height() - (proportion * self.pane.height())));
    		self.scrollbar.find('.ui-slider-handle').css({
    			height: self.handleSize+'px',
    			'margin-bottom': (0-self.handleSize/2)+'px'
    		});
            self.barwrapper.css({
                height: self.barwrapper.parent().height()+'px'
            });
            
            if (self.content.height() > self.pane.height()) {
                self.scrollbar.css({
                    height: (Math.max(0,self.barwrapper.height() - self.handleSize - self.arrowHeight))+'px',
                    top: ((self.handleSize/2) + self.scrollBarOffset)+'px'
                });
            }else{
                self.scrollbar.css({
                    height: (Math.max(0,self.barwrapper.height() - self.arrowHeight))+'px',
                    top: ((self.handleSize/2) + self.scrollBarOffset)+'px'
                });
            }               
        };
        //scroll function
        this.scroll = function(){
            if (typeof($.event.special.mousewheel) != "undefined") {
                self.pane.bind("mousewheel",function(event){
                    var delta = event.detail ? event.detail : event.wheelDelta;
                    event.preventDefault(); 
                    if(Browser.msie || Browser.safari || Browser.chrome){
						delta = 0 - delta;
					}                   
                    if(self.content.height() > self.pane.height()){
                        if (self.value != self.scrollbar.slider('option', 'min') || self.value != self.scrollbar.slider('option', 'max')) {
                            if (delta < 0) {
                                // scroll down
                                self.step(self.scrollbar.slider('value') + Math.ceil(self.handleProportion * self.options.scrollspeed));
                            }
                            else 
                            if (delta > 0) {
                                // scroll up
                                self.step(self.scrollbar.slider('value') - Math.ceil(self.handleProportion * self.options.scrollspeed));
                            }
                        }
                    }
                });
            }  
        };
        //Reset function
        this.reset = function(){
            var remainder = Math.abs(self.content.height() - self.pane.height());            
    		var topVal = self.content.css('top') == 'auto' ? 0 : Math.abs(parseInt(self.content.css('top')));
    		var percentage = Math.round(100 - ((topVal / remainder) * 100));                    
            self.scrollbar.slider("value", percentage);
        };
        //Reflow function - reflows and recalculates the content when the Pane container resizes
        //Works for fluid height elements
        this.reflow = function(){
            var showing = self.content.height() + parseInt( self.content.css('top') );
    		var gap = self.pane.height() - showing;
    		if(gap > 0){
    			self.content.css('top', Math.min(0,parseInt( self.content.css('top') ) + gap));
    		}
        };
        //Step function - can be called externally to set the position of the scrollbar and content
        //takes a value of 0-100
        this.step = function(value){
            self.scrollbar.slider('value',value);
            self.content.css('top', Math.round( ((100 - self.scrollbar.slider('value')) / 100) * ( self.pane.height() - self.content.height() )) + 'px');
            self.value = self.scrollbar.slider('value');
        };
        //CreateArrows function
        this.createArrows = function(){
            var arrowUp = '<a href="#up" class="scrollBarArrow up">Up</a>';
            var arrowDown = '<a href="#down" class="scrollBarArrow down">Down</a>';
            self.barwrapper.append(arrowUp + arrowDown);
            
            //assign Arrows
            var upArrow = self.barwrapper.find('.scrollBarArrow.up')[0];
            var downArrow = self.barwrapper.find('.scrollBarArrow.down')[0];
            
            var upArrowHeight = parseInt($(upArrow).css('height').replace("px", "")) + parseInt($(upArrow).css('paddingTop').replace("px", "")) + parseInt($(upArrow).css('paddingBottom').replace("px", ""));
            var downArrowHeight = parseInt($(downArrow).css('height').replace("px", "")) + parseInt($(downArrow).css('paddingTop').replace("px", "")) + parseInt($(downArrow).css('paddingBottom').replace("px", ""));
            
            self.arrowPosition = self.options.arrowPosition;
            self.arrowHeight = upArrowHeight + downArrowHeight;
            
            //set arrow css values based off option
            if(self.arrowPosition === 'bottom'){
                self.upArrowTopPos = 'auto';
                self.upArrowBottomPos = upArrowHeight+'px';
                self.downArrowTopPos = 'auto';
                self.downArrowBottomPos = '0px';
                self.scrollBarOffset = 0;
                
            }else if(self.arrowPosition === 'top'){
                self.upArrowTopPos = '0px';
                self.upArrowBottomPos = 'auto';
                self.downArrowTopPos = downArrowHeight+'px';
                self.downArrowBottomPos = 'auto';
                self.scrollBarOffset = self.arrowHeight;
                
            }else if(self.arrowPosition === 'split'){
                self.upArrowTopPos = '0px';
                self.upArrowBottomPos = 'auto';
                self.downArrowTopPos = 'auto';
                self.downArrowBottomPos = '0px';
                self.scrollBarOffset = upArrowHeight;
            }
            
            //set arrow css position based off option
            $(upArrow).css({
                top:self.upArrowTopPos,
                bottom:self.upArrowBottomPos,
                zIndex:15
            });
            $(downArrow).css({
                top:self.downArrowTopPos,
                bottom:self.downArrowBottomPos,
                zIndex:15
            });
            
            //set iteration variable used for scaling scroll speed
            var iteration = 0;
            //bind mouseenter/mouseleave event to the up arrow
            $(upArrow).mouseenter(function(){
                upArrow.timer = setInterval(function(){
                    if(upArrow.mousedown === true){
                        var itVal = Math.floor(iteration/5);
                        var itResult;
                        if(iteration > 10){
                            itResult = itVal;
                        }else{
                            itResult = 0;
                        }
                        self.step(self.value + 1 + itResult);
                        iteration++;
                    }
                },50);    
            }).mouseleave(function(){
                clearInterval(upArrow.timer);
            });
            //bind mouseenter/mouseleave event to the down arrow
            $(downArrow).mouseenter(function(){
                downArrow.timer = setInterval(function(){
                    if(downArrow.mousedown === true){
                        var itVal = Math.floor(iteration/5);
                        var itResult;
                        if(iteration > 10){
                            itResult = itVal;
                        }else{
                            itResult = 0;
                        }
                        self.step(self.value - 1 - itResult);
                        iteration++;
                    }
                },50);    
            }).mouseleave(function(){
                clearInterval(downArrow.timer);
            });
            
            //bind mousedown and mouseup events to the arrows
            self.barwrapper.find('.scrollBarArrow').mousedown(function(e){
                e.preventDefault();
                this.mousedown = true;            
                return false;                        
            }).mouseup(function(e){
                e.preventDefault();
                this.mousedown = false;
                iteration = 0;
                return false;
            }).click(function(c){c.preventDefault()});
        };
        
        //Initialization function
        var initialize = function(o){
            if(typeof(o) != "undefined"){
    			for(var key in o){
    				self.options[key] = o[key];
    			}
    		}
            
            //Assign Slider
            self.scrollbar = self.scrollbarElem.slider({
                orientation: 'vertical',
                value:100,
                slide:function(e, ui){
    				if(self.content.height() > self.pane.height()){
                        self.content.css('top', Math.round( ((100 - ui.value) / 100) * ( self.pane.height() - self.content.height() )) + 'px');
                        self.value = self.scrollbar.slider('value');
                    }else{
                        self.content.css('top', 0);
                        self.value = self.scrollbar.slider('value');
                    }
    			}
            });
            //Set Value
            self.value = self.scrollbar.slider('value');
            
            //append slider handle for css rounded corners and gradient
            var theHandleInner = $(self.scrollbar.find('.ui-slider-handle')[0]);
            theHandleInner.append('<span class="inner">&nbsp;</span>');
            
            //change overflow to hidden now that slider handles the scrolling
            self.pane.css('overflow','hidden');
            self.content.css({
                position: 'absolute',
                top: 0,
                left:0
            });
            //check if arrows set to true and create
            if(self.options.arrows === true){                     
                self.createArrows();
            }
            //set scrollbar sizes
            self.size();
            //bind scroll event
            self.scroll();
            //compare height to hide/show on initiation
            if(self.content.height() < self.pane.height()){
                self.barwrapper.hide();
            }else{
                self.barwrapper.show();
            }      
        };
        //resize function that can be called to recalculate the size of the scrollbar
        this.resize = function(){
            self.size();
            self.reset();
            self.reflow();
            if(self.content.height() < self.pane.height()){
                self.barwrapper.hide();
            }else{
                self.barwrapper.show();
            }
        };
        $(window).resize(function() {
            self.resize();
        });
        //run initialization function
        initialize(o);                                
    };
    
    
    $.fn.scrollbar = function(opts){
		var returnArr = [];
		for(var i=0; i<this.length; i++){
			if(!this[i].scrollbar){
				this[i].scrollbar = new ScrollBar(this[i],opts);
			}
			returnArr.push(this[i].scrollbar);
		}
		return returnArr.length > 1 ? returnArr : returnArr[0];
	};    
})(jQuery);
