/**
 * Displays the provided div as a result of click on the attached object
 * will unhide it in the process too.

 * Popup function will create 2 types of popups 
 * (close_on_outside_click) where if the clickoutside event fires the popup window closes 
 * and calling button toggle (click_to_close), which only closes on clicking the selector that calls popup for that target
 * The default is close on outside click.

 Examples...
    $('#chat_settings_dropmenu_button').popup('close_on_outside_click', $('#chat_settings_dropmenu'), {'above': true} );
    $('#chat_viewers_dropmenu_button').popup('click_to_close', $('#chat_viewers_dropmenu'), {'above': true} );

    It also supports a legacy syntax. If the first element in is not a string, it assumes that it is the dom element you want to popup.
    with the close_on_outside_click type of popup.
    $('#header_nick').popup($('#self_actions')) ;
    
    It also supports a functional syntax. If the target is a function, it will create a div where the first element has .html(results)
    applied and it takes in a second function that gets called after the html has been injected. (usually a display function), it also 
    takes in a 3rd argument that should be the event that fired the popup in case you need data from that event
    
    $('#languages').popup(function(target, successFn, ev) { 
  
        $.get('/dropmenu/languages')
            .success(function(data) { 
                target.html(data) ;
                successFn(ev) ;
            })

    }, {'above': true}) ;   
    
 */



(function($) {
    // popup factory
    // it takes in a target, a few options such as side and above
    function popup_factory (target, opts) {

        opts = $.extend({'side': 'left', 'above': false, 'parent': false},
                        opts) ;

        // Start by hiding the target and setting its default position.
        target.hide() ;
        target.css({'position': 'absolute',
                    'top':       0,
                    'left':      0}) ;

        // .close causes target.hide and prevents default action
        target.find('.close').click(function(e) {
            e.preventDefault() ;
            if (target.is(':visible')) {
                target.hide() ;
            }
        }) ;

        // Escape hides
        var body = $('body');
        body.keyup(function(e) {
            if (e.keyCode === 27 &&
                target.is(':visible')) {
                    target.hide() ;
                }
        }) ;


        // Setup the display functions and bind the click and display events to the calling selector.
        return this.each(function() {
            var ele = $(this) ;

            // set the css positioning
            var position = function() {
                if (opts.parent) {
                    var pos = ele.position() ;
                } else {
                    var pos = ele.offset() ;
                }

                if (opts.above) {
                    var targetTop = pos.top - target.outerHeight() - 5 ;
                } else {
                    var targetTop = pos.top + ele.outerHeight() + 5 ;

                }

                var lalign = pos.left + ele.outerWidth();
                if (opts.side === 'left') {
                    var targetLeft = lalign - target.outerWidth();
                } else {
                    var targetLeft = pos.left ;
                }

                target.css('top', targetTop) ;
                target.css('left', targetLeft) ;
            } ;

            // if the window is resized, recalculate the position
            $(window).bind('resize', position);
            // TODO: we'd rather debounce this, but don't have underscore everywhere.
            //$(window).resize(function() { _.debounce(position, 600); });

            var display = function(e) {
                if(!target.is(':visible') ) {
                    position() ;
                    target.show() ;
                    target.css('outline', 'none');
                }
            };

            // The element thats calls us gets a click and display event binding to show the target
            ele.bind('click display', function(e) {
                e.preventDefault() ;
                e.stopPropagation();

                // if we are visible, hide us
                if (target.is(':visible')) {
                    target.hide();
                    return;
                }

                // if we have a function call and we are being asked to go visible
                // call the content function from the target with the display function attached.
                if (target._contentFn) {
                    var contentFn = target._contentFn;
                    contentFn(target, display,e) ;
                    // remove the contentFn to stop a double request once we have content
                    target._contentFn = false ;
                } else {
                    display(e) ;
                }

                if (opts.focusOnLoad) {
                    $(opts.focusOnLoad).focus();
                }

                if (opts.openFn) opts.openFn();
            }) ;

        }) ;
    }

    // Creation methods
    var methods = {

        // Closes on any click outside the target window
        close_on_outside_click: function(target, options) {
            var ele = $(this) ;
            popup_factory.call(ele, target, options);

            // if we click ouside of the target, close it
            target.bind("clickoutside", function(e){
                if (target.is(':visible')) {
                    target.hide();
                }
            });
        },

        // Closes only on the actual element that calls popup.
        click_to_close: function (target, options) {
            var ele = $(this);
            popup_factory.call(ele, target, options);
        },

        // Closes when a click is caught within it
        close_on_click: function (target, options) {
            var ele = $(this) ;
            popup_factory.call(ele, target, options);
            
            // hide when we click on elements within the target
            target.click(function() {
                target.hide() ;
            }) ;
        }
    };

    // The popup class meant to be called by consumers.
    $.fn.popup = function(type, target, options) {
        // if we do not get a type variable, shift the elments over and set the default type as
        // close on outside click. (previous default)
        if(typeof type !== "string") {
            options = target;
            target= type;
            type = 'close_on_outside_click';
        }

        // if we get a function we store a special variable on the target to make that function call when we display.
        // we also switch the target to the empty div element that the function call will display into.
        if (typeof target === "function") {
            var body = $('body');
            var contentFn = target ;
            target = $('<div style="display:none">');
            target.appendTo(body);
            target._contentFn = contentFn ;
        }

        var method ;
        if ((method = methods[type])) {
            return this.each(function() {
                method.call(this, target, options);
            }) ;

        } else {
            throw "No method with name: " + type ;
        }
    } ;

})(jQuery);
