/*! jQuery-ui-Slider-Pips - v1.10.6 - 2015-07-18
* Copyright (c) 2015 Simon Goellner <simey.me@gmail.com>; Licensed MIT */

// PIPS

(function($) {

    "use strict";

    var extensionMethods = {

        pips: function( settings ) {

            var slider = this, i, j, p, collection = "",
                mousedownHandlers,
                min = slider._valueMin(),
                max = slider._valueMax(),
                value = slider._value(),
                values = slider._values(),
                pips = ( max - min ) / slider.options.step,
                $handles = slider.element.find(".ui-slider-handle"),
                $pips;

            var options = {

                first: "label",
                // "label", "pip", false

                last: "label",
                // "label", "pip", false

                rest: "pip",
                // "label", "pip", false

                labels: false,
                // [array], { first: "string", rest: [array], last: "string" }, false

                prefix: "",
                // "", string

                suffix: "",
                // "", string

                step: ( pips > 100 ) ? Math.floor( pips * 0.05 ) : 1,
                // number

                formatLabel: function(value) {
                    return this.prefix + value + this.suffix;
                }
                // function
                // must return a value to display in the pip labels

            };

            if ( $.type( settings ) === "object" || $.type( settings ) === "undefined"  ) {
                $.extend( options, settings );
            } else {
                if ( settings === "destroy" ) {
                    destroy();
                }
                return;
            }


            // we don't want the step ever to be a floating point.
            slider.options.pipStep = Math.round( options.step );

            // get rid of all pips that might already exist.
            slider.element
                .off( ".selectPip" )
                .addClass("ui-slider-pips")
                .find(".ui-slider-pip")
                .remove();

            // small object with functions for marking pips as selected.

            var selectPip = {

                single: function(value) {

                    this.resetClasses();

                    $pips
                        .filter(".ui-slider-pip-" + this.classLabel(value) )
                        .addClass("ui-slider-pip-selected");

                    if ( slider.options.range ) {

                        $pips.each(function(k,v) {

                            var pipVal = $(v).children(".ui-slider-label").data("value");

                            if (( slider.options.range === "min" && pipVal < value ) ||
                                ( slider.options.range === "max" && pipVal > value )) {

                                $(v).addClass("ui-slider-pip-inrange");

                            }

                        });

                    }

                },

                range: function(values) {

                    this.resetClasses();

                    for( i = 0; i < values.length; i++ ) {

                        $pips
                            .filter(".ui-slider-pip-" + this.classLabel(values[i]) )
                            .addClass("ui-slider-pip-selected-" + (i+1) );

                    }

                    if ( slider.options.range ) {

                        $pips.each(function(k,v) {

                            var pipVal = $(v).children(".ui-slider-label").data("value");

                            if( pipVal > values[0] && pipVal < values[1] ) {

                                $(v).addClass("ui-slider-pip-inrange");

                            }

                        });

                    }

                },

                classLabel: function(value) {

                    return value.toString().replace(".","-");

                },

                resetClasses: function() {

                    var regex = /(^|\s*)(ui-slider-pip-selected|ui-slider-pip-inrange)(-{1,2}\d+|\s|$)/gi;

                    $pips.removeClass( function (index, css) {
                        return ( css.match(regex) || [] ).join(" ");
                    });

                }

            };

            function getClosestHandle( val ) {

                var h, k,
                    sliderVals,
                    comparedVals,
                    closestVal,
                    tempHandles = [],
                    closestHandle = 0;

                if( values && values.length ) {

                    // get the current values of the slider handles
                    sliderVals = slider.element.slider("values");

                    // find the offset value from the `val` for each
                    // handle, and store it in a new array
                    comparedVals = $.map( sliderVals, function(v) {
                        return Math.abs( v - val );
                    });

                    // figure out the closest handles to the value
                    closestVal = Math.min.apply( Math, comparedVals );

                    // If a comparedVal is the closestVal, then
                    // set the value accordingly, and set the closest handle.
                    for( h = 0; h < comparedVals.length; h++ ) {
                        if( comparedVals[h] === closestVal ) {
                            tempHandles.push(h);
                        }
                    }

                    // set the closest handle to the first handle in array,
                    // just incase we have no _lastChangedValue to compare to.
                    closestHandle = tempHandles[0];

                    // now we want to find out if any of the closest handles were
                    // the last changed handle, if so we specify that handle to change
                    for( k = 0; k < tempHandles.length; k++ ) {
                        if( slider._lastChangedValue === tempHandles[k] ) {
                            closestHandle = tempHandles[k];
                        }
                    }

                }

                return closestHandle;

            }

            function destroy() {

                slider.element
                    .off(".selectPip")
                    .on("mousedown.slider", slider.element.data("mousedown-original") )
                    .removeClass("ui-slider-pips")
                    .find(".ui-slider-pip")
                    .remove();

            }

            // when we click on a label, we want to make sure the
            // slider's handle actually goes to that label!
            // so we check all the handles and see which one is closest
            // to the label we clicked. If 2 handles are equidistant then
            // we move both of them. We also want to trigger focus on the
            // handle.

            // without this method the label is just treated like a part
            // of the slider and there's no accuracy in the selected value
            
            function labelClick( label ) {

                if (slider.option("disabled")) {
                    return;
                }

                var val = $(label).data("value"),
                    indexToChange = getClosestHandle( val );

                if ( values && values.length ) {

                    slider.element.slider("values", indexToChange, val );

                } else {

                    slider.element.slider("value", val );

                }

                slider._lastChangedValue = indexToChange;

            }

            // method for creating a pip. We loop this for creating all
            // the pips.

            function createPip( which ) {

                var label,
                    percent,
                    number = which,
                    classes = "ui-slider-pip",
                    css = "";

                if ( "first" === which ) { number = 0; }
                else if ( "last" === which ) { number = pips; }

                // labelValue is the actual value of the pip based on the min/step
                var labelValue = min + ( slider.options.step * number );

                // classLabel replaces any decimals with hyphens
                var classLabel = labelValue.toString().replace(".","-");

                // We need to set the human-readable label to either the
                // corresponding element in the array, or the appropriate
                // item in the object... or an empty string.

                if( $.type(options.labels) === "array" ) {
                    label = options.labels[number] || "";
                }

                else if( $.type( options.labels ) === "object" ) {

                    // set first label
                    if( "first" === which ) {
                        label = options.labels.first || "";
                    }

                    // set last label
                    else if( "last" === which ) {
                        label = options.labels.last || "";
                    }

                    // set other labels, but our index should start at -1
                    // because of the first pip.
                    else if( $.type( options.labels.rest ) === "array" ) {
                        label = options.labels.rest[ number - 1 ] || "";
                    } 

                    // urrggh, the options must be f**ked, just show nothing.
                    else {
                        label = labelValue;
                    }
                }

                else {

                    label = labelValue;

                }



                // First Pip on the Slider
                if ( "first" === which ) {

                    percent = "0%";

                    classes += " ui-slider-pip-first";
                    classes += ( "label" === options.first ) ? " ui-slider-pip-label" : "";
                    classes += ( false === options.first ) ? " ui-slider-pip-hide" : "";

                // Last Pip on the Slider
                } else if ( "last" === which ) {

                    percent = "100%";

                    classes += " ui-slider-pip-last";
                    classes += ( "label" === options.last ) ? " ui-slider-pip-label" : "";
                    classes += ( false === options.last ) ? " ui-slider-pip-hide" : "";

                // All other Pips
                } else {

                    percent = ((100/pips) * which).toFixed(4) + "%";

                    classes += ( "label" === options.rest ) ? " ui-slider-pip-label" : "";
                    classes += ( false === options.rest ) ? " ui-slider-pip-hide" : "";

                }

                classes += " ui-slider-pip-" + classLabel;


                // add classes for the initial-selected values.
                if ( values && values.length ) {

                    for( i = 0; i < values.length; i++ ) {

                        if ( labelValue === values[i] ) {

                            classes += " ui-slider-pip-initial-" + (i+1);
                            classes += " ui-slider-pip-selected-" + (i+1);

                        }

                    }

                    if ( slider.options.range ) {

                        if( labelValue > values[0] && 
                            labelValue < values[1] ) {

                            classes += " ui-slider-pip-inrange";

                        }

                    }

                } else {

                    if ( labelValue === value ) {

                        classes += " ui-slider-pip-initial";
                        classes += " ui-slider-pip-selected";

                    }

                    if ( slider.options.range ) {

                        if (( slider.options.range === "min" && labelValue < value ) ||
                            ( slider.options.range === "max" && labelValue > value )) {

                            classes += " ui-slider-pip-inrange";

                        }

                    }

                }



                css = ( slider.options.orientation === "horizontal" ) ?
                    "left: "+ percent :
                    "bottom: "+ percent;


                // add this current pip to the collection
                return  "<span class=\""+classes+"\" style=\""+css+"\">"+
                            "<span class=\"ui-slider-line\"></span>"+
                            "<span class=\"ui-slider-label\" data-value=\""+labelValue+"\">"+ options.formatLabel(label) +"</span>"+
                        "</span>";

            }

            // create our first pip
            collection += createPip("first");

            // for every stop in the slider; we create a pip.
            for( p = 1; p < pips; p++ ) {
                if( p % slider.options.pipStep === 0 ) {
                    collection += createPip( p );
                }
            }

            // create our last pip
            collection += createPip("last");

            // append the collection of pips.
            slider.element.append( collection );

            // store the pips for setting classes later.
            $pips = slider.element.find(".ui-slider-pip");



            // store the mousedown handlers for later, just in case we reset
            // the slider, the handler would be lost!
            
            if ( $._data( slider.element.get(0), "events").mousedown && 
                $._data( slider.element.get(0), "events").mousedown.length ) {

                mousedownHandlers = $._data( slider.element.get(0), "events").mousedown;

            } else {

                mousedownHandlers = slider.element.data("mousedown-handlers");

            }

            slider.element.data("mousedown-handlers", mousedownHandlers.slice() );

            // loop through all the mousedown handlers on the slider,
            // and store the original namespaced (.slider) event handler so
            // we can trigger it later.
            for( j = 0; j < mousedownHandlers.length; j++ ) {
                if( mousedownHandlers[j].namespace === "slider" ) {
                    slider.element.data("mousedown-original", mousedownHandlers[j].handler );
                }
            }

            // unbind the mousedown.slider event, because it interferes with
            // the labelClick() method (stops smooth animation), and decide
            // if we want to trigger the original event based on which element
            // was clicked.
            slider.element
                .off("mousedown.slider")
                .on("mousedown.selectPip", function(e) {

                    var $target = $(e.target),
                        closest = getClosestHandle( $target.data("value") ),
                        $handle = $handles.eq( closest );

                    $handle.addClass("ui-state-active");

                    if( $target.is(".ui-slider-label") ) {

                        labelClick( $target );

                        slider.element
                            .one("mouseup.selectPip", function() {

                                $handle
                                    .removeClass("ui-state-active")
                                    .focus();

                            });

                    } else {

                        var originalMousedown = slider.element.data("mousedown-original");
                        originalMousedown(e);

                    }

            });




            slider.element.on( "slide.selectPip slidechange.selectPip", function(e,ui) {

                var $slider = $(this),
                    value = $slider.slider("value"), 
                    values = $slider.slider("values");

                if ( ui ) {

                    value = ui.value;
                    values = ui.values;

                }

                if ( values && values.length ) {

                    selectPip.range( values );

                } else {

                    selectPip.single( value );

                }

            });




        },








// FLOATS

        float: function( settings ) {

            var i,
                slider = this,
                min = slider._valueMin(),
                max = slider._valueMax(),
                value = slider._value(),
                values = slider._values(),
                tipValues = [],
                $handles = slider.element.find(".ui-slider-handle");

            var options = {

                handle: true,
                // false

                pips: false,
                // true

                labels: false,
                // [array], { first: "string", rest: [array], last: "string" }, false

                prefix: "",
                // "", string

                suffix: "",
                // "", string

                event: "slidechange slide",
                // "slidechange", "slide", "slidechange slide"

                formatLabel: function(value) {
                    return this.prefix + value + this.suffix;
                }
                // function
                // must return a value to display in the floats

            };

            if ( $.type( settings ) === "object" || $.type( settings ) === "undefined"  ) {
                $.extend( options, settings );
            } else {
                if ( settings === "destroy" ) {
                    destroy();
                }
                return;
            }




            if ( value < min ) { 
                value = min; 
            }

            if ( value > max ) { 
                value = max; 
            }

            if ( values && values.length ) {

                for( i = 0; i < values.length; i++ ) {

                    if ( values[i] < min ) { 
                        values[i] = min; 
                    }

                    if ( values[i] > max ) { 
                        values[i] = max; 
                    }

                }

            }

            // add a class for the CSS
            slider.element
                .addClass("ui-slider-float")
                .find(".ui-slider-tip, .ui-slider-tip-label")
                .remove();



            function destroy() {

                slider.element
                    .off(".sliderFloat")
                    .removeClass("ui-slider-float")
                    .find(".ui-slider-tip, .ui-slider-tip-label")
                    .remove();

            }


            function getPipLabels( values ) {

                // when checking the array we need to divide
                // by the step option, so we store those values here.

                var vals = [],
                    steppedVals = $.map( values, function(v) {
                        return Math.ceil(( v - min ) / slider.options.step);
                    });

                // now we just get the values we need to return
                // by looping through the values array and assigning the
                // label if it exists.

                if( $.type( options.labels ) === "array" ) {

                    for( i = 0; i < values.length; i++ ) {

                        vals[i] = options.labels[ steppedVals[i] ] || values[i];

                    }

                }

                else if( $.type( options.labels ) === "object" ) {

                    for( i = 0; i < values.length; i++ ) {

                        if( values[i] === min ) {
                            vals[i] = options.labels.first || min;
                        }

                        else if( values[i] === max ) {
                            vals[i] = options.labels.last || max;
                        }

                        else if( $.type( options.labels.rest ) === "array" ) {
                            vals[i] = options.labels.rest[ steppedVals[i] - 1 ] || values[i];
                        } 

                        else {
                            vals[i] = values[i];
                        }

                    }

                }

                else {

                    for( i = 0; i < values.length; i++ ) {

                        vals[i] = values[i];

                    }

                }

                return vals;

            }

            // apply handle tip if settings allows.
            if ( options.handle ) {

                // We need to set the human-readable label to either the
                // corresponding element in the array, or the appropriate
                // item in the object... or an empty string.

                tipValues = ( values && values.length ) ? 
                    getPipLabels( values ) :
                    getPipLabels( [ value ] );

                for( i = 0; i < tipValues.length; i++ ) {

                    $handles
                        .eq( i )
                        .append( $("<span class=\"ui-slider-tip\">"+ options.formatLabel(tipValues[i]) +"</span>") );

                }

            }

            if ( options.pips ) {

                // if this slider also has pip-labels, we make those into tips, too.
                slider.element.find(".ui-slider-label").each(function(k,v) {

                    var $this = $(v),
                        val = [ $this.data("value") ],
                        label,
                        $tip;


                    label = options.formatLabel( getPipLabels( val )[0] );

                    // create a tip element
                    $tip =
                        $("<span class=\"ui-slider-tip-label\">" + label + "</span>")
                            .insertAfter( $this );

                });

            }

            // check that the event option is actually valid against our
            // own list of the slider's events.
            if ( options.event !== "slide" &&
                options.event !== "slidechange" &&
                options.event !== "slide slidechange" &&
                options.event !== "slidechange slide" ) {

                options.event = "slidechange slide";

            }

            // when slider changes, update handle tip label.
            slider.element
                .off(".sliderFloat")
                .on( options.event + ".sliderFloat", function( e, ui ) {

                    var uiValue = ( $.type( ui.value ) === "array" ) ? ui.value : [ ui.value ],
                        val = options.formatLabel( getPipLabels( uiValue )[0] );

                    $(ui.handle)
                        .find(".ui-slider-tip")
                        .html( val );

                });

        }

    };

    $.extend(true, $.ui.slider.prototype, extensionMethods);

})(jQuery);
