/*
 * jQuery UI Slider 1.8.1 handle overlap patch by chadnorwood.com
 *
 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Slider
 *
 * Depends:
 * jquery.ui.core.js
 * jquery.ui.mouse.js
 * jquery.ui.widget.js
 */
(function($){

    // number of pages in a slider
    // (how many times can you page up/down to go through the whole range)
    var numPages = 5;
    
    $.widget("ui.slider", $.ui.mouse, {
    
        widgetEventPrefix: "slide",
        
        options: {
            animate: false,
            distance: 0,
            max: 100,
            min: 0,
            orientation: "horizontal",
            range: false,
            step: 1,
            value: 0,
            values: null
        },
        
        _create: function(){
            var self = this, o = this.options;
            
            this._keySliding = false;
            this._mouseSliding = false;
            this._animateOff = true;
            this._handleIndex = null;
            this._detectOrientation();
            this._mouseInit();
            
            this.element.addClass("ui-slider" +
            " ui-slider-" +
            this.orientation +
            " ui-widget" +
            " ui-widget-content" +
            " ui-corner-all");
            
            if (o.disabled) {
                this.element.addClass("ui-slider-disabled ui-disabled");
            }
            
            this.range = $([]);
            
            if (o.range) {
                if (o.range === true) {
                    this.range = $("<div></div>");
                    if (!o.values) {
                        o.values = [this._valueMin(), this._valueMin()];
                    }
                    if (o.values.length && o.values.length !== 2) {
                        o.values = [o.values[0], o.values[0]];
                    }
                }
                else {
                    this.range = $("<div></div>");
                }
                
                this.range.appendTo(this.element).addClass("ui-slider-range");
                
                if (o.range === "min" || o.range === "max") {
                    this.range.addClass("ui-slider-range-" + o.range);
                }
                
                // note: this isn't the most fittingly semantic framework class for this element,
                // but worked best visually with a variety of themes
                this.range.addClass("ui-widget-header");
            }
            
            if ($(".ui-slider-handle", this.element).length === 0) {
                $("<a href='#'></a>").appendTo(this.element).addClass("ui-slider-handle");
            }
            
            if (o.values && o.values.length) {
                while ($(".ui-slider-handle", this.element).length < o.values.length) {
                    $("<a href='#'></a>").appendTo(this.element).addClass("ui-slider-handle");
                }
            }
            
            this.handles = $(".ui-slider-handle", this.element).addClass("ui-state-default" +
            " ui-corner-all");
            
            this.handle = this.handles.eq(0);
            
            this.handles.add(this.range).filter("a").click(function(event){
                event.preventDefault();
            }).hover(function(){
                if (!o.disabled) {
                    $(this).addClass("ui-state-hover");
                }
            }, function(){
                $(this).removeClass("ui-state-hover");
            }).focus(function(){
                if (!o.disabled) {
                    $(".ui-slider .ui-state-focus").removeClass("ui-state-focus");
                    $(this).addClass("ui-state-focus");
                }
                else {
                    $(this).blur();
                }
            }).blur(function(){
                $(this).removeClass("ui-state-focus");
            });
            
            this.handles.each(function(i){
                $(this).data("index.ui-slider-handle", i);
            });
            
            this.handles.keydown(function(event){
                var ret = true, index = $(this).data("index.ui-slider-handle"), allowed, curVal, newVal, step;
                
                if (self.options.disabled) {
                    return;
                }
                
                switch (event.keyCode) {
                    case $.ui.keyCode.HOME:
                    case $.ui.keyCode.END:
                    case $.ui.keyCode.PAGE_UP:
                    case $.ui.keyCode.PAGE_DOWN:
                    case $.ui.keyCode.UP:
                    case $.ui.keyCode.RIGHT:
                    case $.ui.keyCode.DOWN:
                    case $.ui.keyCode.LEFT:
                        ret = false;
                        if (!self._keySliding) {
                            self._keySliding = true;
                            $(this).addClass("ui-state-active");
                            allowed = self._start(event, index);
                            if (allowed === false) {
                                return;
                            }
                        }
                        break;
                }
                
                step = self.options.step;
                if (self.options.values && self.options.values.length) {
                    curVal = newVal = self.values(index);
                }
                else {
                    curVal = newVal = self.value();
                }
                
                switch (event.keyCode) {
                    case $.ui.keyCode.HOME:
                        newVal = self._valueMin();
                        break;
                    case $.ui.keyCode.END:
                        newVal = self._valueMax();
                        break;
                    case $.ui.keyCode.PAGE_UP:
                        newVal = curVal + ((self._valueMax() - self._valueMin()) / numPages);
                        break;
                    case $.ui.keyCode.PAGE_DOWN:
                        newVal = curVal - ((self._valueMax() - self._valueMin()) / numPages);
                        break;
                    case $.ui.keyCode.UP:
                    case $.ui.keyCode.RIGHT:
                        if (curVal === self._valueMax()) {
                            return;
                        }
                        newVal = curVal + step;
                        break;
                    case $.ui.keyCode.DOWN:
                    case $.ui.keyCode.LEFT:
                        if (curVal === self._valueMin()) {
                            return;
                        }
                        newVal = curVal - step;
                        break;
                }
                
                self._slide(event, index, newVal);
                
                return ret;
                
            }).keyup(function(event){
                var index = $(this).data("index.ui-slider-handle");
                
                if (self._keySliding) {
                    self._keySliding = false;
                    self._stop(event, index);
                    self._change(event, index);
                    $(this).removeClass("ui-state-active");
                }
                
            });
            
            
            this._sliderPixels = (this.orientation === "vertical") ? this.element.outerHeight() : this.element.outerWidth();
            //console.log("sliderPixels : " + this._sliderPixels);
            this._handleSizePixels = (this.orientation === "vertical") ? this.handles.outerHeight() : this.handles.outerWidth();
            //console.log("_handleSizePixels : " + this._handleSizePixels);
            //this._handleSizePercent = 100 * this._handleSizePixels / this._sliderPixels;
            this._handleSizePercent = 8;
            //console.log("_handleSizePercent : " + this._handleSizePercent);
            //console.log('slider.chad.js:_create sliderPixels='+this._sliderPixels +' _handleSizePixels='+this._handleSizePixels +' _handleSizePercent='+this._handleSizePercent );
            
            this._refreshValue();
            
            this._animateOff = false;
        },
        
        destroy: function(){
            this.handles.remove();
            this.range.remove();
            
            this.element.removeClass("ui-slider" +
            " ui-slider-horizontal" +
            " ui-slider-vertical" +
            " ui-slider-disabled" +
            " ui-widget" +
            " ui-widget-content" +
            " ui-corner-all").removeData("slider").unbind(".slider");
            
            this._mouseDestroy();
            
            return this;
        },
        
        _mouseCapture: function(event){
			
            var o = this.options, position, normValue, percentValue, direction, distance, closestHandle, self, index, allowed, offset, mouseOverHandle;
            
            if (o.disabled) {
                return false;
            }
            this.elementSize = {
                width: this.element.outerWidth(),
                height: this.element.outerHeight()
            };
            this.elementOffset = this.element.offset();
            
            // find closest handle to mouse
            position = {
                x: event.pageX,
                y: event.pageY
            };
            percentValue = 100 * this._percentValueFromMouse(position);
            distance = 106; // any number greater than 100 percent
            direction = (this.orientation === "vertical") ? 'bottom' : 'left';
            
            self = this;
            this.handles.each(function(i){
            	
            	
                var thisDistance = percentValue - (parseFloat($(this).css(direction))/event.currentTarget.clientWidth*100);
                if (thisDistance > 0) {
                    thisDistance = thisDistance - self._handleSizePercent; // account for the closer edge of handle
                    if (thisDistance < 0) {
                        thisDistance = 0; // clicked inside handle
                    }
                }
                else {
                    thisDistance = -thisDistance;
                }
                
                //console.log('slider.chad.js:_mouseCapture i='+i +' distance='+distance +' thisDistance='+thisDistance +' percentValue='+percentValue +' self._handleSizePercent='+self._handleSizePercent );
                //console.log('distance : ' + distance + ', thisDistance : ' + thisDistance);
                if (distance > thisDistance) {
                    distance = thisDistance;
                    closestHandle = $(this);
                    index = i;
                    //console.log('handle : ' + index);
                }
				
            });
            
            // NOT NEEDED - workaround for bug #3736 (if both handles of a range are at 0,
            // the first is always used as the one with least distance,
            // and moving it is obviously prevented by preventing negative ranges)
            //if( o.range === true && this.values(1) === o.min ) {
            // index += 1;
            // closestHandle = $( this.handles[index] );
            //}

            
            
            allowed = this._start(event, index);
            if (allowed === false) {
                return false;
            }
            this._mouseSliding = true;
            this._handleIndex = index;
            closestHandle.addClass("ui-state-active").focus();
            offset = closestHandle.offset();
            mouseOverHandle = !$(event.target).parents().andSelf().is(".ui-slider-handle");
            
            this._clickOffset = mouseOverHandle ? {
                left: 0,
                top: 0
            } : {
                left: event.pageX - offset.left - (closestHandle.width() / 2),
                top: event.pageY - offset.top -
                (closestHandle.height() / 2) -
                (parseInt(closestHandle.css("borderTopWidth"), 10) || 0) -
                (parseInt(closestHandle.css("borderBottomWidth"), 10) || 0) +
                (parseInt(closestHandle.css("marginTop"), 10) || 0)
            };
            
            normValue = this._normValueFromMouse(position);
            
            this._slide(event, index, normValue);
            this._animateOff = true;
            return true;
        },
        
        _mouseStart: function(event){
            return true;
        },
        
        _mouseDrag: function(event){
			
            var position = {
                x: event.pageX,
                y: event.pageY
            }, normValue = this._normValueFromMouse(position);
            
            this._slide(event, this._handleIndex, normValue);
            
            return false;
        },
        
        _mouseStop: function(event){
            this.handles.removeClass("ui-state-active");
            this._mouseSliding = false;
            
            this._stop(event, this._handleIndex);
            this._change(event, this._handleIndex);
            
            this._handleIndex = null;
            this._clickOffset = null;
            this._animateOff = false;
            
            return false;
        },
        
        _detectOrientation: function(){
            this.orientation = (this.options.orientation === "vertical") ? "vertical" : "horizontal";
        },
        
        _normValueFromMouse: function(position){
            var pixelMouse, pixelRangeSlider, percentMouse, valueTotal, valueMouse;
            
            pixelRangeSlider = this._sliderPixels - (2 * this._handleSizePixels);
            
            if (this.orientation === "horizontal") {
                // pixelMouse gets pixels of Handle Center, even if user clicks edge of handle
                pixelMouse = position.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0);
                percentMouse = (pixelMouse - this._handleSizePixels * (0.5 + this._handleIndex)) / pixelRangeSlider;
            }
            else {
                pixelMouse = position.y - this.elementOffset.top;
                percentMouse = (pixelMouse - this._handleSizePixels * (0.5 + (1 - this._handleIndex))) / pixelRangeSlider;
            }
            
            if (percentMouse > 1) {
                percentMouse = 1;
            }
            if (percentMouse < 0) {
                percentMouse = 0;
            }
            if (this.orientation === "vertical") {
                percentMouse = 1 - percentMouse;
            }
            
            valueTotal = this._valueMax() - this._valueMin();
            valueMouse = this._valueMin() + percentMouse * valueTotal;
            
            //console.log('slider.chad.js:_normValueFromMouse i='+this._handleIndex +' _sliderPixels='+this._sliderPixels +' pixelRangeSlider='+pixelRangeSlider
            // +" x="+position.x+" y="+position.y +' elementOffset.top='+this.elementOffset.top +' pixelMouse='+Math.round(pixelMouse)
            // +' -- percentMouse='+percentMouse +' valueTotal='+valueTotal +' valueMouse='+valueMouse );
            
            return this._trimAlignValue(valueMouse);
        },
        
        _percentValueFromMouse: function(position){
            var pixelTotal, pixelMouse, percentMouse, valueTotal, valueMouse;
            
            if (this.orientation === "horizontal") {
                pixelTotal = this.elementSize.width;
                pixelMouse = position.x - this.elementOffset.left;
            }
            else {
                pixelTotal = this.elementSize.height;
                pixelMouse = position.y - this.elementOffset.top;
            }
            
            percentMouse = (pixelMouse / pixelTotal);
            if (percentMouse > 1) {
                percentMouse = 1;
            }
            if (percentMouse < 0) {
                percentMouse = 0;
            }
            if (this.orientation === "vertical") {
                percentMouse = 1 - percentMouse;
            }
            
            // console.log('slider.chad.js:_percentValueFromMouse _sliderPixels='+this._sliderPixels
            // +" y="+position.y +' elementOffset.top='+this.elementOffset.top +' pixelMouse='+pixelMouse +' pixelTotal='+pixelTotal
            // +' -- percentMouse='+percentMouse );
            
            return parseFloat(percentMouse.toFixed(5));
        },
        
        
        _start: function(event, index){
            var uiHash = {
                handle: this.handles[index],
                value: this.value()
            };
            if (this.options.values && this.options.values.length) {
                uiHash.value = this.values(index);
                uiHash.values = this.values();
            }
            return this._trigger("start", event, uiHash);
        },
        
        _slide: function(event, index, newVal){
        	
            var otherVal, newValues, allowed;
            
            if (this.options.values && this.options.values.length) {
                otherVal = this.values(index ? 0 : 1);
                
                if ((this.options.values.length === 2 && this.options.range === true) &&
                ((index === 0 && newVal > otherVal) || (index === 1 && newVal < otherVal))) {
                    newVal = otherVal;
                 }
                
                if (newVal !== this.values(index)) {
                    //console.log('slider.chad.js:_slide handle_index='+index+' origVal='+this.values(index)+' newVal='+newVal+' otherVal='+otherVal);
                    newValues = this.values();
                    newValues[index] = newVal;
                    // A slide can be canceled by returning false from the slide callback
                    allowed = this._trigger("slide", event, {
                        handle: this.handles[index],
                        value: newVal,
                        values: newValues
                    });
                    otherVal = this.values(index ? 0 : 1);
                    if (allowed !== false) {
                        this.values(index, newVal, true);
                    }
                }
            }
            else {
                if (newVal !== this.value()) {
                    // A slide can be canceled by returning false from the slide callback
                    allowed = this._trigger("slide", event, {
                        handle: this.handles[index],
                        value: newVal
                    });
                    if (allowed !== false) {
                        this.value(newVal);
                    }
                }
            }
        },
        
        _stop: function(event, index){
            var uiHash = {
                handle: this.handles[index],
                value: this.value()
            };
            if (this.options.values && this.options.values.length) {
                uiHash.value = this.values(index);
                uiHash.values = this.values();
            }
            
            this._trigger("stop", event, uiHash);
        },
        
        _change: function(event, index){
            if (!this._keySliding && !this._mouseSliding) {
                var uiHash = {
                    handle: this.handles[index],
                    value: this.value()
                };
                if (this.options.values && this.options.values.length) {
                    uiHash.value = this.values(index);
                    uiHash.values = this.values();
                }
                
                this._trigger("change", event, uiHash);
            }
        },
        
        value: function(newValue){
            if (arguments.length) {
                this.options.value = this._trimAlignValue(newValue);
                this._refreshValue();
                this._change(null, 0);
            }
            
            return this._value();
        },
        
        values: function(index, newValue){
            var vals, newValues, i;
            
            if (arguments.length > 1) {
                this.options.values[index] = this._trimAlignValue(newValue);
                this._refreshValue();
                this._change(null, index);
            }
            
            if (arguments.length) {
                if ($.isArray(arguments[0])) {
                    vals = this.options.values;
                    newValues = arguments[0];
                    for (i = 0; i < vals.length; i += 1) {
                        vals[i] = this._trimAlignValue(newValues[i]);
                        this._change(null, i);
                    }
                    this._refreshValue();
                }
                else {
                    if (this.options.values && this.options.values.length) {
                        return this._values(index);
                    }
                    else {
                        return this.value();
                    }
                }
            }
            else {
                return this._values();
            }
        },
        
        _setOption: function(key, value){
            var i, valsLength = 0;
            
            if ($.isArray(this.options.values)) {
                valsLength = this.options.values.length;
            }
            
            $.Widget.prototype._setOption.apply(this, arguments);
            
            switch (key) {
                case "disabled":
                    if (value) {
                        this.handles.filter(".ui-state-focus").blur();
                        this.handles.removeClass("ui-state-hover");
                        this.handles.attr("disabled", "disabled");
                        this.element.addClass("ui-disabled");
                    }
                    else {
                        this.handles.removeAttr("disabled");
                        this.element.removeClass("ui-disabled");
                    }
                    break;
                case "orientation":
                    this._detectOrientation();
                    this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-" + this.orientation);
                    this._refreshValue();
                    break;
                case "value":
                    this._animateOff = true;
                    this._refreshValue();
                    this._change(null, 0);
                    this._animateOff = false;
                    break;
                case "values":
                    this._animateOff = true;
                    this._refreshValue();
                    for (i = 0; i < valsLength; i += 1) {
                        this._change(null, i);
                    }
                    this._animateOff = false;
                    break;
            }
        },
        
        //internal value getter
        // _value() returns value trimmed by min and max, aligned by step
        _value: function(){
            var val = this.options.value;
            val = this._trimAlignValue(val);
            
            return val;
        },
        
        //internal values getter
        // _values() returns array of values trimmed by min and max, aligned by step
        // _values( index ) returns single value trimmed by min and max, aligned by step
        _values: function(index){
            var val, vals, i;
            
            if (arguments.length) {
                val = this.options.values[index];
                val = this._trimAlignValue(val);
                
                return val;
            }
            else {
                // .slice() creates a copy of the array
                // this copy gets trimmed by min and max and then returned
                vals = this.options.values.slice();
                for (i = 0; i < vals.length; i += 1) {
                    vals[i] = this._trimAlignValue(vals[i]);
                }
                
                return vals;
            }
        },
        
        // returns the step-aligned value that val is closest to, between (inclusive) min and max
        _trimAlignValue: function(val){
            if (val < this._valueMin()) {
                return this._valueMin();
            }
            if (val > this._valueMax()) {
                return this._valueMax();
            }
            var step = this.options.step, valModStep = val % step, alignValue = val - valModStep;
            
            if (valModStep >= (step / 2)) {
                alignValue += step;
            }
            
            // Since JavaScript has problems with large floats, round
            // the final value to 5 digits after the decimal point (see #4124)
            return parseFloat(alignValue.toFixed(5));
        },
        
        _valueMin: function(){
            return this.options.min;
        },
        
        _valueMax: function(){
            return this.options.max;
        },
        
        _refreshValue: function(){
            var oRange = this.options.range, o = this.options, self = this, animate = (!this._animateOff) ? o.animate : false, valPercent, _set = {}, lastValPercent = 0, pixelPercent, val2pixel, h0Percent, h1Percent, rangeOffsetPercent, rangeSizePercent, value, valueMin, valueMax;
            
            if (this.options.values && this.options.values.length) {
                this.handles.each(function(i, j){
                
                    valPercent = (self.values(i) - self._valueMin()) / (self._valueMax() - self._valueMin()) * 100;
                    val2pixel = (self._sliderPixels - 2 * self._handleSizePixels) / self._sliderPixels;
                    
                    pixelPercent = valPercent * val2pixel;
                    h0Percent = Math.round(pixelPercent);
                    h1Percent = Math.round(pixelPercent + self._handleSizePercent);
                    
                    //console.log(h0Percent + " : " + h1Percent + " : " + (pixelPercent));
                    rangeOffsetPercent = Math.floor(pixelPercent + self._handleSizePercent);
                    rangeSizePercent = Math.ceil(self._handleSizePercent * 1.0 + // extend range into handle to make sure its long enough
                    val2pixel * (valPercent - lastValPercent));
                    
                //    console.log('slider.chad.js:_refreshValue index='+i +' val='+self.values(i) +' valPercent='+valPercent
                 //   +' rangeOffsetPercent='+rangeOffsetPercent+' rangeSizePercent='+rangeSizePercent +' h0Percent='+h0Percent +' h1Percent='+h1Percent
                  //  +' pixelPercent='+pixelPercent +' handlePercent='+self._handleSizePixels);
                    
                    // update the handle
                    _set[self.orientation === "horizontal" ? "left" : "bottom"] = (i ? h1Percent : h0Percent)-3 + "%";
                    $(this).stop(1, 1)[animate ? "animate" : "css"](_set, o.animate);
                    
                    // update the range in middle
                    
                    if (self.options.range === true) {
                        if (self.orientation === "horizontal") {
                            if (i === 0) {
                                self.range.stop(1, 1)[animate ? "animate" : "css"]({
                                    left: rangeOffsetPercent + "%"
                                }, o.animate);
                            }
                            if (i === 1) {
                                self.range[animate ? "animate" : "css"]({
                                    width: rangeSizePercent + "%"
                                }, {
                                    queue: false,
                                    duration: o.animate
                                });
                            }
                        }
                        else {
                        	 
                            if (i === 0) {
                                self.range.stop(1, 1)[animate ? "animate" : "css"]({
                                    bottom: rangeOffsetPercent + "%"
                                }, o.animate);
                            }
                            if (i === 1) {
                                //self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
                                self.range[animate ? "animate" : "css"]({
                                    height: rangeSizePercent + "%"
                                }, {
                                    queue: false,
                                    duration: o.animate
                                });
                            }
                        }
                    }
                    lastValPercent = valPercent;
                });
            }
            else {
                value = this.value();
                valueMin = this._valueMin();
                valueMax = this._valueMax();
                valPercent = (valueMax !== valueMin) ? (value - valueMin) / (valueMax - valueMin) * 100 : 0;
                _set[self.orientation === "horizontal" ? "left" : "bottom"] = valPercent + "%";
                this.handle.stop(1, 1)[animate ? "animate" : "css"](_set, o.animate);
                
                if (oRange === "min" && this.orientation === "horizontal") {
                    this.range.stop(1, 1)[animate ? "animate" : "css"]({
                        width: valPercent + "%"
                    }, o.animate);
                }
                if (oRange === "max" && this.orientation === "horizontal") {
                    this.range[animate ? "animate" : "css"]({
                        width: (100 - valPercent) + "%"
                    }, {
                        queue: false,
                        duration: o.animate
                    });
                }
                if (oRange === "min" && this.orientation === "vertical") {
                    this.range.stop(1, 1)[animate ? "animate" : "css"]({
                        height: valPercent + "%"
                    }, o.animate);
                }
                if (oRange === "max" && this.orientation === "vertical") {
                    this.range[animate ? "animate" : "css"]({
                        height: (100 - valPercent) + "%"
                    }, {
                        queue: false,
                        duration: o.animate
                    });
                }
            }
        }
        
    });
    
    $.extend($.ui.slider, {
        version: "1.8.1"
    });
    
}(jQuery));

