/**
    Project: CheckTree jQuery Plugin
    Version: 0.2
    Project Website: http://static.geewax.org/checktree/
    Author: JJ Geewax <jj@geewax.org>
    
    License:
        The CheckTree jQuery plugin is currently available for use in all personal or 
        commercial projects under both MIT and GPL licenses. This means that you can choose 
        the license that best suits your project, and use it accordingly.
*/
(function(jQuery) {
jQuery.fn.checkTree = function(settings) {

    settings = jQuery.extend({
        /* Callbacks
            The callbacks should be functions that take one argument. The checkbox tree
            will return the jQuery wrapped LI element of the item that was checked/expanded.
        */
        onExpand: null,
        onCollapse: null,
        onCheck: null,
        onUnCheck: null,
        onHalfCheck: null,
        onLabelHoverOver: null,
        onLabelHoverOut: null,
        
        /* Valid choices: 'expand', 'check' */
        labelAction: "expand",
        
        // Debug (currently does nothing)
        debug: false
    }, settings);

    var $tree = this;

    $tree.find("li")
        // Hide all of the sub-trees
        .find("ul")
            .hide()
        .end()
        
        // Hide all checkbox inputs
        .find(":checkbox")
            .change(function() {
                // Fired when the children of this checkbox have changed.
                // Children can change the state of a parent based on what they do as a group.
                var $all = jQuery(this).siblings("ul").find(":checkbox");
                var $checked = $all.filter(":checked");
                
                // All children are checked
                if ($all.length == $checked.length) {
                    jQuery(this)
                        .attr("checked", "checked")
                        .siblings(".checkbox")
                            .removeClass("half_checked")
                            .addClass("checked")
                    ;
                    // Fire parent's onCheck callback
                    if (settings.onCheck) settings.onCheck(jQuery(this).parent());
                }
                
                // All children are unchecked
                else if($checked.length == 0) {
                    jQuery(this)
                        .attr("checked", "")
                        .siblings(".checkbox")
                            .removeClass("checked")
                            .removeClass("half_checked")
                    ;
                    // Fire parent's onUnCheck callback
                    if (settings.onUnCheck) settings.onUnCheck(jQuery(this).parent());
                }
                
                // Some children are checked, makes the parent in a half checked state.
                else { 
                    // Fire parent's onHalfCheck callback only if it's going to change
                    if (settings.onHalfCheck && !jQuery(this).siblings(".checkbox").hasClass("half_checked"))
                        settings.onHalfCheck(jQuery(this).parent());
                    
                    jQuery(this)
                        .attr("checked", "")
                        .siblings(".checkbox")
                            .removeClass("checked")
                            .addClass("half_checked")
                    ;
                }
            })
            .attr("checked", "")
            .hide()
        .end()
        
        
        .find("label")
            // Clicking the labels should expand the children
            .click(function() {
                var action = settings.labelAction;
                switch(settings.labelAction) {
                    case 'expand':
                        jQuery(this).siblings(".arrow").click();
                        break;
                    case 'check':
                        jQuery(this).siblings(".checkbox").click();
                        break;
                }
            })
            
            // Add a hover class to the labels when hovering
            .hover(
                function() { 
                    jQuery(this).addClass("hover");
                    if (settings.onLabelHoverOver) settings.onLabelHoverOver(jQuery(this).parent());
                },
                function() {
                    jQuery(this).removeClass("hover");
                    if (settings.onLabelHoverOut) settings.onLabelHoverOut(jQuery(this).parent());
                }
            )
        .end()
        
        .each(function() {
            // Create the image for the arrow (to expand and collapse the hidden trees)
            var $arrow = jQuery('<div class="arrow"></div>');
            
            // If it has children:
            if (jQuery(this).is(":has(ul)")) {
                $arrow.addClass("collapsed"); // Should start collapsed
                
                // When you click the image, toggle the child list
                $arrow.click(function() {
                    jQuery(this).siblings("ul").toggle();
                    
                    if (jQuery(this).hasClass("collapsed")) {
                        //toggled = settings.expandedarrow;
                        jQuery(this)
                            .addClass("expanded")
                            .removeClass("collapsed")
                        ;
                        if (settings.onExpand) settings.onExpand(jQuery(this).parent());
                    }
                    else {
                        //toggled = settings.collapsedarrow;
                        jQuery(this)
                            .addClass("collapsed")
                            .removeClass("expanded")
                        ;
                        if (settings.onCollapse) settings.onCollapse(jQuery(this).parent());
                    }
                });
            }
            
            // Create the image for the checkbox next to the label
            var $checkbox = jQuery('<div class="checkbox"></div>');
            
            // When you click the checkbox, it should do the checking/unchecking
            $checkbox.click(function() {
                // Make the current class checked
                jQuery(this)
                    // if it's half checked, its now either checked or unchecked
                    .removeClass("half_checked")
                    .toggleClass("checked")
                    
                    // Send a click event to the checkbox to toggle it as well
                    .siblings(":checkbox").click()
                ;
                
                // Check/uncheck children depending on our status.
                if (jQuery(this).hasClass("checked")) {
                    // Fire the check callback for this parent
                    if (settings.onCheck) settings.onCheck(jQuery(this).parent());
                    
                    jQuery(this).siblings("ul").find(".checkbox").not(".checked")
                        .removeClass("half_checked")
                        .addClass("checked")
                        .each(function() {
                            if (settings.onCheck) settings.onCheck(jQuery(this).parent());
                        })
                        .siblings(":checkbox")
                            .attr("checked", "checked")
                    ;
                }
                else {
                    // Fire the uncheck callback for this parent
                    if (settings.onUnCheck) settings.onUnCheck(jQuery(this).parent());
                    
                    jQuery(this).siblings("ul").find(".checkbox").filter(".checked")
                        .removeClass("half_checked")
                        .removeClass("checked")
                        .each(function() {
                            if (settings.onUnCheck) settings.onUnCheck(jQuery(this).parent());
                        })
                        .siblings(":checkbox")
                            .attr("checked", "")
                    ;
                }
                // Tell our parent checkbox that we've changed
                jQuery(this).parents("ul").siblings(":checkbox").change();
            });
            
            // Prepend the arrow and checkbox images to the front of the LI
            jQuery(this)
                .prepend($checkbox)
                .prepend($arrow)
            ;
        })
    ;

    return $tree;
};
})(jQuery);
