/*!
 * jQuery Mousewheel 3.2.2
 * Copyright OpenJS Foundation and other contributors
 */

( function( factory ) {
    "use strict";

    if ( typeof define === "function" && define.amd ) {

        // AMD. Register as an anonymous module.
        define( [ "jquery" ], factory );
    } else if ( typeof exports === "object" ) {

        // Node/CommonJS style for Browserify
        module.exports = factory;
    } else {

        // Browser globals
        factory( jQuery );
    }
} )( function( $ ) {
    "use strict";

    var nullLowestDeltaTimeout, lowestDelta,
        modernEvents = !!$.fn.on,
        toFix  = [ "wheel", "mousewheel", "DOMMouseScroll", "MozMousePixelScroll" ],
        toBind = ( "onwheel" in window.document || window.document.documentMode >= 9 ) ?
            [ "wheel" ] : [ "mousewheel", "DomMouseScroll", "MozMousePixelScroll" ],
        slice  = Array.prototype.slice;

    if ( $.event.fixHooks ) {
        for ( var i = toFix.length; i; ) {
            $.event.fixHooks[ toFix[ --i ] ] = $.event.mouseHooks;
        }
    }

    var special = $.event.special.mousewheel = {
        version: "3.2.2",

        setup: function() {
            if ( this.addEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.addEventListener( toBind[ --i ], handler, false );
                }
            } else {
                this.onmousewheel = handler;
            }

            // Store the line height and page height for this particular element
            $.data( this, "mousewheel-line-height", special.getLineHeight( this ) );
            $.data( this, "mousewheel-page-height", special.getPageHeight( this ) );
        },

        teardown: function() {
            if ( this.removeEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.removeEventListener( toBind[ --i ], handler, false );
                }
            } else {
                this.onmousewheel = null;
            }

            // Clean up the data we added to the element
            $.removeData( this, "mousewheel-line-height" );
            $.removeData( this, "mousewheel-page-height" );
        },

        getLineHeight: function( elem ) {
            var $elem = $( elem ),
                $parent = $elem[ "offsetParent" in $.fn ? "offsetParent" : "parent" ]();
            if ( !$parent.length ) {
                $parent = $( "body" );
            }
            return parseInt( $parent.css( "fontSize" ), 10 ) ||
                parseInt( $elem.css( "fontSize" ), 10 ) || 16;
        },

        getPageHeight: function( elem ) {
            return $( elem ).height();
        },

        settings: {
            adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
            normalizeOffset: true  // calls getBoundingClientRect for each event
        }
    };

    $.fn.extend( {
        mousewheel: function( fn ) {
            return fn ?
                this[ modernEvents ? "on" : "bind" ]( "mousewheel", fn ) :
                this.trigger( "mousewheel" );
        },

        unmousewheel: function( fn ) {
            return this[ modernEvents ? "off" : "unbind" ]( "mousewheel", fn );
        }
    } );


    function handler( event ) {
        var orgEvent   = event || window.event,
            args       = slice.call( arguments, 1 ),
            delta      = 0,
            deltaX     = 0,
            deltaY     = 0,
            absDelta   = 0;
        event = $.event.fix( orgEvent );
        event.type = "mousewheel";

        // Old school scrollwheel delta
        if ( "detail" in orgEvent ) {
            deltaY = orgEvent.detail * -1;
        }
        if ( "wheelDelta" in orgEvent ) {
            deltaY = orgEvent.wheelDelta;
        }
        if ( "wheelDeltaY" in orgEvent ) {
            deltaY = orgEvent.wheelDeltaY;
        }
        if ( "wheelDeltaX" in orgEvent ) {
            deltaX = orgEvent.wheelDeltaX * -1;
        }

        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
        if ( "axis" in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
            deltaX = deltaY * -1;
            deltaY = 0;
        }

        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatability
        delta = deltaY === 0 ? deltaX : deltaY;

        // New school wheel delta (wheel event)
        if ( "deltaY" in orgEvent ) {
            deltaY = orgEvent.deltaY * -1;
            delta  = deltaY;
        }
        if ( "deltaX" in orgEvent ) {
            deltaX = orgEvent.deltaX;
            if ( deltaY === 0 ) {
                delta  = deltaX * -1;
            }
        }

        // No change actually happened, no reason to go any further
        if ( deltaY === 0 && deltaX === 0 ) {
            return;
        }

        // Need to convert lines and pages to pixels if we aren't already in pixels
        // There are three delta modes:
        //   * deltaMode 0 is by pixels, nothing to do
        //   * deltaMode 1 is by lines
        //   * deltaMode 2 is by pages
        if ( orgEvent.deltaMode === 1 ) {
            var lineHeight = $.data( this, "mousewheel-line-height" );
            delta  *= lineHeight;
            deltaY *= lineHeight;
            deltaX *= lineHeight;
        } else if ( orgEvent.deltaMode === 2 ) {
            var pageHeight = $.data( this, "mousewheel-page-height" );
            delta  *= pageHeight;
            deltaY *= pageHeight;
            deltaX *= pageHeight;
        }

        // Store lowest absolute delta to normalize the delta values
        absDelta = Math.max( Math.abs( deltaY ), Math.abs( deltaX ) );

        if ( !lowestDelta || absDelta < lowestDelta ) {
            lowestDelta = absDelta;

            // Adjust older deltas if necessary
            if ( shouldAdjustOldDeltas( orgEvent, absDelta ) ) {
                lowestDelta /= 40;
            }
        }

        // Adjust older deltas if necessary
        if ( shouldAdjustOldDeltas( orgEvent, absDelta ) ) {

            // Divide all the things by 40!
            delta  /= 40;
            deltaX /= 40;
            deltaY /= 40;
        }

        // Get a whole, normalized value for the deltas
        delta  = Math[ delta  >= 1 ? "floor" : "ceil" ]( delta  / lowestDelta );
        deltaX = Math[ deltaX >= 1 ? "floor" : "ceil" ]( deltaX / lowestDelta );
        deltaY = Math[ deltaY >= 1 ? "floor" : "ceil" ]( deltaY / lowestDelta );

        // Normalise offsetX and offsetY properties
        if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
            var boundingRect = this.getBoundingClientRect();
            event.offsetX = event.clientX - boundingRect.left;
            event.offsetY = event.clientY - boundingRect.top;
        }

        // Add information to the event object
        event.deltaX = deltaX;
        event.deltaY = deltaY;
        event.deltaFactor = lowestDelta;

        // Go ahead and set deltaMode to 0 since we converted to pixels
        // Although this is a little odd since we overwrite the deltaX/Y
        // properties with normalized deltas.
        event.deltaMode = 0;

        // Add event and delta to the front of the arguments
        args.unshift( event, delta, deltaX, deltaY );

        // Clear out lowestDelta after sometime to better
        // handle multiple device types that give different
        // a different lowestDelta
        // Ex: trackpad = 3 and mouse wheel = 120
        if ( nullLowestDeltaTimeout ) {
            window.clearTimeout( nullLowestDeltaTimeout );
        }
        nullLowestDeltaTimeout = window.setTimeout( function() {
            lowestDelta = null;
        }, 200 );

        return ( $.event.dispatch || $.event.handle ).apply( this, args );
    }

    function shouldAdjustOldDeltas( orgEvent, absDelta ) {

        // If this is an older event and the delta is divisible by 120,
        // then we are assuming that the browser is treating this as an
        // older mouse wheel event and that we should divide the deltas
        // by 40 to try and get a more usable deltaFactor.
        // Side note, this actually impacts the reported scroll distance
        // in older browsers and can cause scrolling to be slower than native.
        // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
        return special.settings.adjustOldDeltas && orgEvent.type === "mousewheel" &&
            absDelta % 120 === 0;
    }

} );
