/// <reference path="jquery-1.3.2-vsdoc2.js"/>
// The above reference gives jQuery intellisense with Visual Studio.
// You must also:
// 1. Install VS 2008 SP1.
// 2. Install VS 2008 Patch KB958502 to Support "-vsdoc.js" Intellisense Files:
//    http://code.msdn.microsoft.com/KB958502/Release/ProjectReleases.aspx?ReleaseId=1736
// 4. Download the jQuery-vsdoc.js file
//    Save the jquery-vsdoc.js file next to your jquery.js file in your project.
// Note: Intellisense for $ will not work inside this construct:
//       function($) { })(jQuery); - Typical for plugins. The reason is here: 
//       http://docs.jquery.com/Using_jQuery_with_Other_Libraries
//       As a work around can use jQuery instead of $ and you get full intellisense.
// Nigel Snow 7 Dec 2009
//==================================================================================================      
(function($) {
    /*
    * ui.dropdownchecktree
    *
    * Copyright (c) 2009 Nigel Snow - RBI 
    *
    */
    // The dropdown check tree jQuery plugin transforms a regular div html element into a dropdown check tree.
    $.widget("ui.dropdownchecktree", {
        // Creates the drop container that holds the tree
        _createTreeControl: function() {
            var options = this.options, self = this, containerControl = this.containerControl;

            // Create the holder for the tree control
            var treeControl = $("<div/>");

            // Set the classes for the tree control
            treeControl.addClass("ui-dropdownchecktree-dropcontainer-wrapper");

            // Absolute positioning so we can move it around and ensure that it displays over
            // everything
            treeControl.css({
                position: 'absolute',
                zIndex: 1000
            });

            // Hide the tree to begin with
            treeControl.hide();

            // Create the toolbar
            self._createTreeControlToolbar(treeControl);

            // Create a container to put the jQuery tree in and put it in the tree control
            var container = $("<div/>");
            treeControl.append(container);

            // Set the classes for the container
            container.addClass("ui-dropdownchecktree-dropcontainer")
                        .css("overflow-y", "auto");

            // Set the options for the jQuery tree.
            // The options.data comes from the constructor for this plugin
            var treeOptions = {
                ui: { theme_path: options.treeThemePath, theme_name: "checkbox", animation: 500 },
                plugins: { checkbox: {} },
                types: { "default": { draggable: false} },
                data: options.data
            };

            // Put in the jQuery tree
            container.tree(treeOptions);

            // Keep a reference to the jQueryTree
            this.jQueryTree = jQuery.tree.reference(container);

            // A click recieved by the treeControl should not be propogated.
            // Tried a number of techniques but event.stopPropagation
            // was stopping the tree from receiving click events.
            // A property on the treeControl could not be changed in the mouseenter.
            // The final solution was to add a class that can be used to detect
            // if a hide should be ignored or not.
            treeControl.bind("mouseenter", function() {
                var treeControl = $(this);
                treeControl.addClass("preventHide");
            });

            treeControl.bind("mouseleave", function() {
                var treeControl = $(this);
                treeControl.removeClass("preventHide");
            });

            // flag that tells if the tree container is shown or not
            containerControl.treeControlIsDisplaying = false;

            // Place the tree control into the document, not as a child of the 
            // container because we don't want to interupt the flow of the HTML
            $(document.body).append(treeControl);

            // Hold a top level pointer to the tree control
            self.treeControl = treeControl;
        },

        // Creates the tree control toolbar
        _createTreeControlToolbar: function(treeControl) {
            var self = this, options = this.options;

            // Create the unordered list to hold the toolbar
            var ulToolbar = $("<ul class='ui-dropdownchecktree-toolbar'/>");

            // Put the toolbar into the treeControl
            treeControl.append(ulToolbar);

            // Add the left edge of the toolbar
            var leftToolbar = $("<li class='left'/>");
            ulToolbar.append(leftToolbar);

            // Add the middle of the toolbar
            var middleToolbar = $("<li class='middle'><p>" + options.treeToolbarTitle + "</p></li>");
            ulToolbar.append(middleToolbar);

            // Add the right edge of the toolbar
            var rightToolbar = $("<li title='Close' class='right'/>");
            ulToolbar.append(rightToolbar);

            // Set the width of the middle toolbar, 20 is the combined width of the left and right images
            middleToolbar.width(options.dropDownWidth - 20);

            // Wire in the close button
            rightToolbar.click(function(event) {
                // Avoid the prevent hide
                $.ui.dropdownchecktree.forceClose = true;
                self._hideTreeControl();
                $.ui.dropdownchecktree.forceClose = false;
                return false;
            });

        },

        _createSelectFakerControl: function() {
            var self = this, options = this.options, containerControl = this.containerControl;

            // the controls is wrapped in a span with inline-block display
            var selectFakerControl = $("<span/>");
            selectFakerControl.addClass("ui-dropdownchecktree-wrapper");
            selectFakerControl.css({
                display: "inline-block",
                cursor: "default"
            });

            // the actual control, can be styled to set the border and drop right image
            var control = $("<span/>");
            control.addClass("ui-dropdownchecktree");
            control.css({
                display: "inline-block"
            });
            selectFakerControl.append(control);

            // the text container keeps the control text that is build from the selected (checked) items
            var textContainer = $("<span/>");
            textContainer.addClass("ui-dropdownchecktree-text")
            textContainer.css({
                display: "inline-block",
                overflow: "hidden"
            });

            textContainer.text(options.watermark).addClass("watermarked");
            control.append(textContainer);

            // add the hover styles to the control
            selectFakerControl.hover(function() {
                if (!self.disabled) {
                    control.toggleClass("ui-dropdownchecktree-hover")
                }
            }, function() {
                if (!self.disabled) {
                    control.toggleClass("ui-dropdownchecktree-hover")
                }
            });

            // clicking on the control toggles the drop container
            selectFakerControl.click(function(event) {
                if (!self.disabled) {
                    event.stopPropagation();
                    if (self.treeControlIsDisplaying) {
                        // We don't pass self as this method is also called when the document is clicked
                        self._hideTreeControl();
                    }
                    else {
                        // Pass self so we know the instance of the control.
                        self._showTreeControl(self);
                    }
                }
                return false;

            });

            // Append the faker control to the container
            containerControl.append(selectFakerControl);

            // Hold a top level pointer to the selectFakerControl
            self.selectFakerControl = selectFakerControl;

        },

        _hideTreeControl: function(event) {
            // Grab the instance of the dropdownchecktree from the namespaced global variable.
            // This is necessary because when the _hideTreeControl method is called in the scenario when
            // the document receives a click event then we need to be able to access the instance that we
            // want to hide the control for.
            var instance = $.ui.dropdownchecktree.instance;
            if (null != instance) {
                // We have a valid instance.... we don't want to hide if the mouse is in the 
                // tree control so check for the preventHide class...
                if (!instance.treeControl.is(".preventHide") || $.ui.dropdownchecktree.forceClose) {

                    // The mouse is not in the tree control or we are forcing a close

                    // Hide the tree control...
                    // In ie6 the the animation takes a long time to initialise, so we just hide it
                    if (rbiBrowserIsIe6()) {
                        instance.treeControl.hide();
                    }
                    else {
                        instance.treeControl.slideUp();
                    }

                    // Set the class so that it looks inactive
                    instance.selectFakerControl.rbiFindExpectedSingleItem(".ui-dropdownchecktree").toggleClass("ui-dropdownchecktree-active");

                    // Record the fact that we are NOT displaying the tree control
                    instance.treeControlIsDisplaying = false;

                    // Clear the instance of the dropdownchecktree from the namespaced global variable.
                    $.ui.dropdownchecktree.instance = null;

                    $(document).unbind("click", instance._hideTreeControl);
                    instance.containerControl.trigger("blur");
                    instance._updateSelectedItems(instance);
                    //disable search buttons while dropdown is visible
                    this.submitSearchButton = $("input.submitsearch");
                    this.submitSearchButton.removeAttr("disabled");
                }
            }

        },

        _showTreeControl: function(instance) {
            // If the namespaced global variable instance is not null then 
            // we are out of sync so hide the control.
            if (null != $.ui.dropdownchecktree.instance) {
                instance._hideTreeControl();
            }

            // Place the tree control just below the faker control            
            var topValue = instance.selectFakerControl.offset().top + instance.selectFakerControl.outerHeight();
            var leftValue = instance.selectFakerControl.offset().left;
            instance.treeControl.css({
                top: topValue + "px",
                left: leftValue + "px"
            });

            // Show the tree control
            // In ie6 the the animation takes a long time to initialise, so we just show it
            if (rbiBrowserIsIe6()) {
                instance.treeControl.show();
            }
            else {
                instance.treeControl.slideDown();
            }

            // Set the class so that it looks active
            instance.selectFakerControl.rbiFindExpectedSingleItem(".ui-dropdownchecktree").toggleClass("ui-dropdownchecktree-active");

            // Record the fact that we are displaying the tree control
            instance.treeControlIsDisplaying = true;

            // Set the namespaced global variable instance to be the instance of the control.
            // This is necessary because when the _hideTreeControl method is called in the scenario when
            // the document receives a click event then we need to be able to access the instance that we
            // want to hide the control for.
            $.ui.dropdownchecktree.instance = instance;

            // When the document is clicked we want to hide the tree control so pass in the address of the
            // _hideTreeControl method to the bind.
            $(document).bind("click", instance._hideTreeControl);

            // Give the containerControl focus.
            instance.containerControl.trigger("focus");


            //disable search buttons while dropdown is visible
            this.submitSearchButton = $("input.submitsearch");
            this.submitSearchButton.attr("disabled", "true");

        },

        // Updates the text, tooltip and selected item id hidden field depending on the checked (selected) items
        _updateSelectedItems: function(instance) {
            var jQueryTree = instance.jQueryTree, selectFakerControl = instance.selectFakerControl, containerControl = instance.containerControl;

            // Get a collection of the checked items.
            var checkedItems = jQuery.tree.plugins.checkbox.get_checked(jQueryTree);

            // We will build up a list of both the ids and the text values that have been checked
            var idList = "";
            var valueList = "";

            // Walk the list of checked items.
            for (n = 0; n < checkedItems.length; n++) {

                // Get a handle on the checked item.
                var checkedItem = $(checkedItems[n]);

                // Get the id and text value of the checked item
                var checkedItemId = checkedItem.attr('id');
                // Ignore the first character, it is not a space because jQuery.trim won't remove it 
                // but is looks like a space.
                var checkedItemText = checkedItem.text().substring(1);
                // If the Id begins with a # then this is a root item so we ignore it
                if (checkedItemId.substring(0, 1) != '#') {
                    // We are not ignoring it now so add it to the list.
                    // Is this not the first item in the list...
                    if (idList.length != 0) {
                        // We need to add a separator...
                        idList += "#";
                        valueList += ", ";
                    }
                    // Add the items to the list
                    idList += checkedItemId;
                    valueList += checkedItemText
                }
            }

            // Update the selected Ids
            instance.selectedIdsControl.val(idList);

            // If there are no countries selected then disable the country selector and clear the value back to its watermark
            if (idList == "") {
                $rbi_searchControl._setLocationToNoCitySelectedState();
            }
            else {
                $rbi_searchControl._setLocationToCitySelectedState();
            }

            // Update the values controls
            instance._updateValuesControls(instance, valueList);

        },

        // Updates the values of the values control
        _updateValuesControls: function(instance, valueList) {

            var selectFakerControl = instance.selectFakerControl, options = this.options;

            // Update the control label
            var controlLabel = selectFakerControl.rbiFindExpectedSingleItem(".ui-dropdownchecktree-text");
            var maxNumberOfCharactersToDisplay = instance.options.maxNumberOfCharactersToDisplay;
            var stringToDisplay = instance.options.watermark;

            if (valueList.length != 0) {
                if (valueList.length > maxNumberOfCharactersToDisplay) {
                    stringToDisplay = valueList.substring(0, maxNumberOfCharactersToDisplay) + "...";
                }
                else {
                    stringToDisplay = valueList;
                }

                // We have something to display.
                // Set the text and remove the watermark
                controlLabel.text(stringToDisplay);
                controlLabel.removeClass("watermarked");

                // Set the tooltip
                controlLabel.attr("title", valueList);
            }
            else {
                // There is nothing selected
                // Set the watermark
                controlLabel.text(options.watermark).addClass("watermarked");
                controlLabel.attr("title", "");

            }

            // Update the list of selected ids
            instance.selectedValuesControlId.val(valueList);

        },

        // Set the size of the control and of the drop container
        _setSize: function() {
            var options = this.options, treeControl = this.treeControl, selectFakerControl = this.selectFakerControl;

            var controlWidth = parseInt(options.width);
            selectFakerControl.rbiFindExpectedSingleItem(".ui-dropdownchecktree-text").css({
                width: controlWidth + "px"
            });

            // for the drop container get the actual (outer) width of the control.
            // this can be different than the set one depening on paddings, borders etc set on the control
            var controlOuterWidth = selectFakerControl.outerWidth();

            var dropHeight = parseInt(options.dropDownHeight);
            var dropWidth = options.dropDownWidth;

            treeControl.css({
                width: dropWidth + "px",
                height: dropHeight + "px"
            });

            treeControl.rbiFindExpectedSingleItem(".ui-dropdownchecktree-dropcontainer").css({
                height: dropHeight + "px"
            });
        },

        _setStateOfSelectedValues: function(selectedIdsControl) {
            var self = this, options = this.options, jQueryTree = this.jQueryTree;

            var selectedValuesArray = selectedIdsControl.val().split("#");

            // Build up a list of selected values, so that we can set the selected values
            // based on the selected id's
            var valueList = "";

            // Walk the list of selected id's.
            for (n = 0; n < selectedValuesArray.length; n++) {

                // Find the node element that matches the id, start looking from the
                // tree control. Note that in the DOM the tree control is not a child of
                // self - this is so we do not interrupt the flow of HTML.
                var nodeToCheck = self.treeControl.rbiFindExpectedSingleItem("#" + selectedValuesArray[n]);

                // Check the node
                jQuery.tree.plugins.checkbox.check(nodeToCheck);

                // Get the value of the node we have just checked, ignore the first character, 
                // it is not a space because jQuery.trim won't remove it 
                // but is looks like a space.
                var checkedItemText = nodeToCheck.text().substring(1);

                if (valueList.length != 0) {
                    // We need to add a separator...
                    valueList += ", ";
                }

                // Add the item to the list
                valueList += checkedItemText

            }
        },

        // Initializes the plugin
        _init: function() {
            var self = this, options = this.options;

            // Set a global reference so other jQuery controls can access this.
            $rbi_dropdownchecktree = self;

            // containerControl is the select on which the plugin is applied
            var containerControl = self.element;
            self.containerControl = containerControl;

            // Create the control that fakes the drop down
            self._createSelectFakerControl();

            // Create the tree control
            self._createTreeControl();

            // set the sizes of control and drop container
            self._setSize();

            // Make sure we've been told the selectedValuesControlId
            if (options.selectedValuesControlId == null) {
                alert("Error: I must be told about selectedValuesControlId");
            }

            // Make sure we've been told the selectedIdsControlId
            if (options.selectedIdsControlId == null) {
                alert("Error: I must be told about selectedIdsControlId");
            }

            // Get a handle on the selectedIdsControlId
            self.selectedIdsControl = self.element.rbiFindExpectedSingleItem('#' + options.selectedIdsControlId);

            // Get a handle on the selectedValuesControlId
            self.selectedValuesControlId = self.element.rbiFindExpectedSingleItem('#' + options.selectedValuesControlId);


            // If there are any selected values then make sure that the tree reflects the values...
            if (self.selectedIdsControl.val().length != 0) {
                self._setStateOfSelectedValues(self.selectedIdsControl);
                self._updateSelectedItems(self);
            }

        },

        enable: function() {
            this.selectFakerControl.rbiFindExpectedSingleItem(".ui-dropdownchecktree").removeClass("ui-dropdownchecktree-disabled");
            this.disabled = false;
        },
        disable: function() {
            this.selectFakerControl.rbiFindExpectedSingleItem(".ui-dropdownchecktree").addClass("ui-dropdownchecktree-disabled");
            this.disabled = true;
        },
        destroy: function() {
            $.widget.prototype.destroy.apply(this, arguments);
            this.containerControl.css("display", this.initialDisplay);
            this.containerControl.attr("multiple", this.initialMultiple);
            this.selectFakerControl.unbind().remove();
            this.treeControl.remove();
        }
    });

    $.extend($.ui.dropdownchecktree, {
        defaults: {
            maxNumberOfCharactersToDisplay: 20,
            treeToolbarTitle: "Select countries",
            treeThemePath: null,
            watermark: "Select",
            width: 50,
            dropDownWidth: 100,
            dropDownHeight: 100,
            selectedIdsControlId: null,
            selectedValuesControlId: null,
            data: null
        }
    });

})(jQuery);