/*!
 * Overrides to jQuery methods (must be included after jQuery itself).
 *
 * Copyright (c) 2009 Atlassian
 * http://www.atlassian.com/
 * 
 * Dual licensed under the MIT and GPL licenses.
 * http://docs.jquery.com/License
 */

(function ($) {

    var $fnlive = $.fn.live;
    var regexmouse = /^(click|dblclick|mousedown|mouseup|mousemove|mouseover|mouseout)/;

    /** map of: selector -> live event type -> { handlers: [], decorated: [] } */
    var fns = {};

    var ensureArrays = function (selector, type) {
        if (!fns[selector]) {
            fns[selector] = {};
        }
        if (!fns[selector][type]) {
            fns[selector][type] = {
                handlers: [],
                decorated: []
            };
        }
    };

    /**
     * Override live events to ignore all mouse events caused by the right mouse button, i.e.,
     * click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout.
     *
     * @param type see jQuery.live documentation
     * @param fn see jQuery.live documentation
     * @param allowrmb (optional) whether to allow the right mouse button to trigger live events (defaults to false)
     */
    $.fn.live = function (type, fn, allowrmb) {
        allowrmb = allowrmb || false;

        ensureArrays(this.selector, type);
        var handlers = fns[this.selector][type].handlers;
        var decorated = fns[this.selector][type].decorated;
        var index, boundFn;

        if ((index = $.inArray(fn, handlers)) >= 0) {
            boundFn = decorated[index];
        } else {
            if (regexmouse.test(type)) {
                boundFn = function (e) {
                    if (allowrmb || e.button !== 2) {
                        return fn.call(this, e);
                    }
                };
            } else {
                boundFn = fn;
            }

            handlers.push(fn);
            decorated.push(boundFn);
        }

        return $fnlive.call(this, type, boundFn);
    };

    var $fndie = $.fn.die;
    $.fn.die = function (type, fn) {
        var args = $.makeArray(arguments);

        ensureArrays(this.selector, type);
        var handlers = fns[this.selector][type].handlers;
        var decorated = fns[this.selector][type].decorated;
        var index;

        if (args.length === 0) {
            delete fns[this.selector];
            return $fndie.call(this);
        } else if (args.length === 1) {
            delete fns[this.selector][type];
            return $fndie.call(this, type);
        } else if ((index = $.inArray(fn, handlers)) >= 0) {
            var boundFn = decorated[index];
            handlers.splice(index, 1);
            decorated.splice(index, 1);
            return $fndie.call(this, type, boundFn);
        } else {
            // Logically, fn hasn't been bound under type for this.selector, but
            // call it anyway to allow for chaining and any magic jQuery might do.
            return $fndie.apply(this, args);
        }
    };

})(jQuery);

