/* Copyright (c) 2006 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
 */

jQuery.fn.extend({
	/**
	 * Apply the mousewheel event to the elements in the jQuery object.
	 * The up and down handler functions should be prepared to take the 
	 * event object and a param called 'delta'. The 'delta' param is a number
	 * either > 0 or < 0. > 0 = up and < 0 = down.
	 *
	 * Firefox and Opera has some issues preventing default and just using
	 * event.preventDefault() will not work. Instead if you need
	 * to prevent the default action, just pass in 'true' for the preventDefault
	 * param.
	 *
	 * The method takes two functions. The first is fired only on when the
	 * mousewheel is moved up. The second is fired only when the mouswheel is 
	 * moved down.
	 *
	 * Only one handler function should be used per an element. 
	 * Meaning call $().mousewheel only once per an element.
	 *
	 * @example $("p").mousewheel(function(event, delta){
	 *   // do something on mousewheel scroll up
	 * }, function(event, delta) {
	 *   // do something on mousewheel scroll down
	 * });
	 *
	 * @name mousewheel
	 * @type jQuery
	 * @param Function upHandler The function to call when the mousewheel is moved up. Should take two params: event and delta.
	 * @param Function downHandler The function to call when the mousewheel is moved down. Should take two params: event and delta.
	 * @param Boolean preventDefault Should the default action be prevented?
	 * @cat Events
	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
	 */
	mousewheel: function(up, down, preventDefault) {
		return this.hover(
			function() {
				jQuery.event.mousewheel.giveFocus(this, up, down, preventDefault);
			},
			function() {
				jQuery.event.mousewheel.removeFocus(this);
			}
		);
	},
	
	/**
	 * Apply the mousewheeldown event to the elements in the jQuery object.
	 * The handler function should be prepared to take the event object
	 * and a param called 'delta'. The 'delta' param is a number
	 * either > 0 or < 0. > 0 = up and < 0 = down.
	 *
	 * The handler function is only called if the mousewheel is moved down.
	 *
	 * Firefox and Opera has some issues preventing default and just using
	 * event.preventDefault() will not work. Instead if you need
	 * to prevent the default action, just pass in 'true' for the preventDefault
	 * param.
	 *
	 * Only one handler function should be used per an element. 
	 * Meaning call $().mousewheel only once per an element. This will
	 * overwrite any previous mousewheel event, even a mousewheelup event.
	 * To get a seperate up and down function just use mousewheel and pass
	 * two functions: the first is the up handler and the second is the 
	 * down handler.
	 *
	 * @example $("p").mousewheeldown(function(event, delta){
	 *   //do something on mousewheel scroll down
	 * });
	 *
	 * @name mousewheeldown
	 * @type jQuery
	 * @param Function handler The function to call when the mousewheel is moved down. Should take two params: event and delta.
	 * @param Boolean preventDefault Should the default action be prevented?
	 * @cat Events
	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
	 */
	mousewheeldown: function(fn, preventDefault) {
		return this.mousewheel(function(){}, fn, preventDefault);
	},
	
	/**
	 * Apply the mousewheelup event to the elements in the jQuery object.
	 * The handler function should be prepared to take the event object
	 * and a param called 'delta'. The 'delta' param is a number
	 * either > 0 or < 0. > 0 = up and < 0 = down.
	 *
	 * The handler function is only called if the mousewheel is moved down.
	 *
	 * Firefox and Opera has some issues preventing default and just using
	 * event.preventDefault() will not work. Instead if you need
	 * to prevent the default action, just pass in 'true' for the preventDefault
	 * param.
	 *
	 * Only one handler function should be used per an element. 
	 * Meaning call $().mousewheel only once per an element. This will
	 * overwrite any previous mousewheel event, even a mousewheeldown event.
	 * To get a seperate up and down function just use mousewheel and pass
	 * two functions: the first is the up handler and the second is the 
	 * down handler.
	 *
	 * @example $("p").mousewheeldown(function(event, delta){
	 *   //do something on mousewheel scroll down
	 * });
	 *
	 * @name mousewheelup
	 * @type jQuery
	 * @param Function handler The function to call when the mousewheel is moved up. Should take two params: event and delta.
	 * @param Boolean preventDefault Should the default action be prevented?
	 * @cat Events
	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
	 */
	mousewheelup: function(fn, preventDefault) {
		return this.mousewheel(fn, function(){}, preventDefault);
	},
	
	/**
	 * This method removes the applied mousewheel event from the elements
	 * in the jQuery object. The $().mousewheel and $().unmousewheel is only
	 * capable of handling one handler function per an element. Therefore,
	 * there is no need to pass anything to this method.
	 *
	 * @name unmousewheel
	 * @type jQuery
	 * @cat Events
	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
	 */
	unmousewheel: function() {
		return this.each(function() {
			jQuery(this).unmouseover().unmouseout();
			jQuery.event.mousewheel.removeFocus(this);
		});
	},

	/**
	 * This method removes the applied mousewheel event from the elements
	 * in the jQuery object. The $().mousewheel and $().unmousewheel is only
	 * capable of handling one handler function per an element. Therefore,
	 * there is no need to pass anything to this method.
	 *
	 * @name unmousewheeldown
	 * @type jQuery
	 * @cat Events
	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
	 */
	unmousewheeldown: jQuery.fn.unmousewheel,
	
	/**
	 * This method removes the applied mousewheel event from the elements
	 * in the jQuery object. The $().mousewheel and $().unmousewheel is only
	 * capable of handling one handler function per an element. Therefore,
	 * there is no need to pass anything to this method.
	 *
	 * @name unmousewheelup
	 * @type jQuery
	 * @cat Events
	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
	 */
	unmousewheelup: jQuery.fn.unmousewheel
});


/**
 * The manager for the mousewheel event.
 * Handles the switching of focus for the
 * different elements that have the
 * mousewheel event attached to them.
 *
 * @private
 * @cat Core	
 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
 */
jQuery.event.mousewheel = {
	/**
	 * Give the element passed the mousewheel event focus.
	 * If the element already has mousewheel events attached
	 * to it, they will be removed before adding them back.
	 *
	 * @private
	 * @name giveFocus
	 * @param HTMLElement element The DOM node to apply the mousewheel event too.
	 * @param Function handler The function to call when onmousewheel fires.
	 * @param Boolean preventDefault Should the default action be prevented?.
	 * @see jQuery.fn.mousewheel
	 * @cat Core
	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
	 */
	giveFocus: function(el, up, down, preventDefault) {
		if (el._handleMousewheel) jQuery(el).unmousewheel();
		
		if (preventDefault == window.undefined && down && down.constructor != Function) {
			preventDefault = down;
			down = null;
		}
		
		el._handleMousewheel = function(event) {
			if (!event) event = window.event;
			if (preventDefault) // cannot call any methods before preventing default (firefox)
				if (event.preventDefault) event.preventDefault();
				else event.returnValue = false;
			var delta = 0;
			if (event.wheelDelta) {
				delta = event.wheelDelta/120;
				if (window.opera) delta = -delta;
			} else if (event.detail) {
				delta = -event.detail/3;
			}
			if (up && (delta > 0 || !down))
				up.apply(el, [event, delta]);
			else if (down && delta < 0)
				down.apply(el, [event, delta]);
		};
		
		if (window.addEventListener)
			window.addEventListener('DOMMouseScroll', el._handleMousewheel, false);
		window.onmousewheel = document.onmousewheel = el._handleMousewheel;
	},
	
	/**
	 * Removes the mousewheel event focus from the
	 * element passed in.
	 *
	 * @private
	 * @name removeFocus
	 * @see jQuery.fn.unmousewheel
	 * @cat Core
	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
	 */
	removeFocus: function(el) {
		if (!el._handleMousewheel) return;
		
		if (window.removeEventListener)
			window.removeEventListener('DOMMouseScroll', el._handleMousewheel, false);
		window.onmousewheel = document.onmousewheel = null;
		el._handleMousewheel = null;
	}
};