mangroveorg/datawinners

View on GitHub
datawinners/media/javascript/dropdown_check_list/dropdownchecklist.custom.js

Summary

Maintainability
F
6 days
Test Coverage
;(function($) {
    /*
     * ui.dropdownchecklist
     *
     * Copyright (c) 2008-2010 Adrian Tosca, Copyright (c) 2010-2011 Ittrium LLC
     * Dual licensed under the MIT (MIT-LICENSE.txt) OR GPL (GPL-LICENSE.txt) licenses.
     *
     */
    // The dropdown check list jQuery plugin transforms a regular select html element into a dropdown check list.
    $.widget("ui.dropdownchecklist", {
        // Some globlals
        // $.ui.dropdownchecklist.gLastOpened - keeps track of last opened dropdowncheck list so we can close it
        // $.ui.dropdownchecklist.gIDCounter - simple counter to provide a unique ID as needed
        version: function() {
            alert('DropDownCheckList v1.4');
        },
        // Creates the drop container that keeps the items and appends it to the document
        _appendDropContainer: function( controlItem ) {
            var self = this, config = this.options;
            var wrapper = $("<div/>");
            // the container is wrapped in a div
            wrapper.addClass("ui-dropdownchecklist ui-dropdownchecklist-dropcontainer-wrapper");
            wrapper.addClass("ui-widget");
            // assign an id
            wrapper.attr("id",controlItem.attr("id") + '-ddw');
            // initially positioned way off screen to prevent it from displaying
            // NOTE absolute position to enable width/height calculation
            wrapper.css({ position: 'absolute', left: "-33000px", top: "-33000px"  });

            var container = $("<div/>"); // the actual container
            container.addClass("ui-dropdownchecklist-dropcontainer ui-widget-content");
            container.css("overflow-y", "auto");
            wrapper.append(container);
            if ( config.explicitClose != null ) {
                var closeItem = self._createButtons();
                wrapper.append(closeItem);
            }

            // insert the dropdown after the master control to try to keep the tab order intact
            // if you just add it to the end, tabbing out of the drop down takes focus off the page
            // @todo 22Sept2010 - check if size calculation is thrown off if the parent of the
            //        selector is hidden.  We may need to add it to the end of the document here,
            //        calculate the size, and then move it back into proper position???
            //$(document.body).append(wrapper);
            wrapper.insertAfter(controlItem);

            // flag that tells if the drop container is shown or not
            wrapper.isOpen = false;
            return wrapper;
        },
        // Look for browser standard 'open' on a closed selector
        _isDropDownKeyShortcut: function(e,keycode) {
            return e.altKey && ($.ui.keyCode.DOWN == keycode);// Alt + Down Arrow
        },
        // Look for key that will tell us to close the open dropdown
        _isDropDownCloseKey: function(e,keycode) {
            return ($.ui.keyCode.ESCAPE == keycode) || ($.ui.keyCode.ENTER == keycode);
        },
        // Handler to change the active focus based on a keystroke, moving some count of
        // items from the element that has the current focus
        _keyFocusChange: function(target,delta,limitToItems) {
            // Find item with current focus
            var focusables = $(":focusable");
            var index = focusables.index(target);
            if ( index >= 0 ) {
                index += delta;
                if ( limitToItems ) {
                    // Bound change to list of input elements
                    var allCheckboxes = this.dropWrapper.find("input:not([disabled])");
                    var firstIndex = focusables.index(allCheckboxes.get(0));
                    var lastIndex = focusables.index(allCheckboxes.get(allCheckboxes.length-1));
                    if ( index < firstIndex ) {
                        index = lastIndex;
                    } else if ( index > lastIndex ) {
                        index = firstIndex;
                    }
                }
                focusables.get(index).focus();
            }
        },
        // Look for navigation, open, close (wired to keyup)
        _handleKeyboard: function(e) {
            var self = this;
            var keyCode = (e.keyCode || e.which);
            if (!self.dropWrapper.isOpen && self._isDropDownKeyShortcut(e, keyCode)) {
                // Key command to open the dropdown
                e.stopImmediatePropagation();
                self._toggleDropContainer(true);
            } else if (self.dropWrapper.isOpen && self._isDropDownCloseKey(e, keyCode)) {
                // Key command to close the dropdown (but we retain focus in the control)
                e.stopImmediatePropagation();
                self._toggleDropContainer(false);
                self.controlSelector.focus();
            } else if (self.dropWrapper.isOpen
                && (e.target.type == 'checkbox')
                && ((keyCode == $.ui.keyCode.DOWN) || (keyCode == $.ui.keyCode.UP)) ) {
                // Up/Down to cycle throught the open items
                e.stopImmediatePropagation();
                self._keyFocusChange(e.target, (keyCode == $.ui.keyCode.DOWN) ? 1 : -1, true);
            } else if (self.dropWrapper.isOpen && (keyCode == $.ui.keyCode.TAB) ) {
                // I wanted to adjust normal 'tab' processing here, but research indicates
                // that TAB key processing is NOT a cancelable event. You have to use a timer
                // hack to pull the focus back to where you want it after browser tab
                // processing completes.  Not going to work for us.
                //e.stopImmediatePropagation();
                //self._keyFocusChange(e.target, (e.shiftKey) ? -1 : 1, true);
            }
        },
        // Look for change of focus
        _handleFocus: function(e,focusIn,forDropdown) {
            var self = this;
            if (forDropdown && !self.dropWrapper.isOpen) {
                // if the focus changes when the control is NOT open, mark it to show where the focus is/is not
                e.stopImmediatePropagation();
//                if (focusIn) {
//                    self.controlSelector.addClass("ui-state-hover");
//                    if ($.ui.dropdownchecklist.gLastOpened != null) {
//                        $.ui.dropdownchecklist.gLastOpened._toggleDropContainer( false );
//                    }
//                } else {
//                    self.controlSelector.removeClass("ui-state-hover");
//                }
            } else if (!forDropdown && !focusIn) {
                // The dropdown is open, and an item (NOT the dropdown) has just lost the focus.
                // we really need a reliable method to see who has the focus as we process the blur,
                // but that mechanism does not seem to exist.  Instead we rely on a delay before
                // posting the blur, with a focus event cancelling it before the delay expires.
                if ( e != null ) { e.stopImmediatePropagation(); }
                self.controlSelector.removeClass("ui-state-hover");
                self._toggleDropContainer( false );
            }
        },
        // Clear the pending change of focus, which keeps us 'in' the control
        _cancelBlur: function(e) {
            var self = this;
            if (self.blurringItem != null) {
                clearTimeout(self.blurringItem);
                self.blurringItem = null;
            }
        },
        // Creates the control that will replace the source select and appends it to the document
        // The control resembles a regular select with single selection
        _appendControl: function() {
            var self = this, sourceSelect = this.sourceSelect, options = this.options;

            // the control is wrapped in a basic container
            // inline-block at this level seems to give us better size control
            var wrapper = $("<span/>");
            wrapper.addClass("ui-dropdownchecklist ui-dropdownchecklist-selector-wrapper ui-widget");
            wrapper.css( { display: "inline-block", cursor: "default", overflow: "hidden" } );

            // assign an ID
            var baseID = sourceSelect.attr("id");
            if ((baseID == null) || (baseID == "")) {
                baseID = "ddcl-" + $.ui.dropdownchecklist.gIDCounter++;
            } else {
                baseID = "ddcl-" + baseID;
            }
            wrapper.attr("id",baseID);

            // the actual control which you can style
            // inline-block needed to enable 'width' but has interesting problems cross browser
            var control = $("<span/>");
            control.addClass("ui-dropdownchecklist-selector ui-state-default");
            control.css( { display: "inline-block", overflow: "hidden", 'white-space': 'nowrap',border:'2px inset #CCC'} );
            // Setting a tab index means we are interested in the tab sequence
            var tabIndex = sourceSelect.attr("tabIndex");
            if ( tabIndex == null ) {
                tabIndex = 0;
            } else {
                tabIndex = parseInt(tabIndex);
                if ( tabIndex < 0 ) {
                    tabIndex = 0;
                }
            }
            control.attr("tabIndex", tabIndex);
            control.keyup(function(e) {self._handleKeyboard(e);});
            control.focus(function(e) {self._handleFocus(e,true,true);});
            control.blur(function(e) {self._handleFocus(e,false,true);});
            wrapper.append(control);

            // the optional icon (which is inherently a block) which we can float
            if (options.icon != null) {
                var iconPlacement = (options.icon.placement == null) ? "left" : options.icon.placement;
                var anIcon = $("<div/>");
                anIcon.addClass("ui-icon");
                anIcon.addClass( (options.icon.toOpen != null) ? options.icon.toOpen : "ui-icon-triangle-1-e");
                anIcon.css({ 'float': iconPlacement });
                control.append(anIcon);
            }
            // the text container keeps the control text that is built from the selected (checked) items
            // inline-block needed to prevent long text from wrapping to next line when icon is active
            var textContainer = $("<span/>");
            textContainer.addClass("ui-dropdownchecklist-text");
            textContainer.css( {  display: "inline-block", 'white-space': "nowrap", overflow: "hidden",textOverflow:"ellipsis",
        padding:"5px 0 0 5px"} );
            control.append(textContainer);

            // add the hover styles to the control
            wrapper.hover(
                function() {
                    if (!self.disabled) {
                        control.addClass("ui-state-hover");
                    }
                }
                ,     function() {
                    if (!self.disabled) {
                        control.removeClass("ui-state-hover");
                    }
                }
            );
            // clicking on the control toggles the drop container
            wrapper.click(function(event) {
                if (!self.disabled) {
                    event.stopImmediatePropagation();
                    self._toggleDropContainer( !self.dropWrapper.isOpen );
                }
            });
            wrapper.insertAfter(sourceSelect);

            // Watch for a window resize and adjust the control if open
            $(window).resize(function() {
                if (!self.disabled && self.dropWrapper.isOpen) {
                    // Reopen yourself to get the position right
                    self._toggleDropContainer(true);
                }
            });
            return wrapper;
        },
        _set_ids_of_checked: function() {
            var sourceSelect = this.sourceSelect, dropWrapper = this.dropWrapper;
            var ids =  $.map(dropWrapper.find(':checkbox:checked'), function(checkbox){
                return $(checkbox).attr('id');
            });
            var data = $.map(dropWrapper.find(':checkbox:checked'), function(checkbox){
                return $(checkbox).attr('data');
            });

            sourceSelect.attr('ids', ids);
            sourceSelect.attr('data', data);
        },
        // Creates a drop item that coresponds to an option element in the source select
        _createDropItem: function(index, tabIndex, value, code, data, number, text, optCss, checked, disabled, indent) {
            var self = this, options = this.options, sourceSelect = this.sourceSelect, controlWrapper = this.controlWrapper;
            var data = data || "";
            // the item contains a div that contains a checkbox input and a lable for the text
            // the div
            var item = $("<div/>");
            item.addClass("ui-dropdownchecklist-item");
            item.css({'white-space': "nowrap"});
            var checkedString = checked ? ' checked="checked"' : '';
            var classString = disabled ? ' class="inactive"' : ' class="active"';

            // generated id must be a bit unique to keep from colliding
            var idBase = controlWrapper.attr("id");
            var id = idBase + '-i' + index;
            var checkBox;

            // all items start out disabled to keep them out of the tab order
            if (self.isMultiple) { // the checkbox
                checkBox = $('<input disabled type="checkbox" id="' + id + '"' + checkedString + classString + ' tabindex="' + tabIndex + '" />');
            } else { // the radiobutton
                checkBox = $('<input disabled type="radio" id="' + id + '" name="' + idBase + '"' + checkedString + classString + ' tabindex="' + tabIndex + '" />');
            }
            checkBox = checkBox.attr("index", index).val(value).attr('id', code).attr('data', data);
            item.append(checkBox);

            // the text
            var label_for = (typeof(code) != "undefined") ? code : id;
            var label = $("<label for=" + label_for + "/>");
            var $hit = $("<span></span>");
            label.addClass("ui-dropdownchecklist-text");
            if ( optCss != null ) label.attr('style',optCss);
            label.css({ cursor: "default" ,height:'auto'});
            label.html(text);
            if (typeof(number) != 'undefined') {
                $hit.addClass('small_grey').html(number + " "+ ngettext(gettext("people"), gettext("peoples"), parseInt(number)));
            } else {
                $hit.addClass('small_grey').html(code);
            }
            if (indent) {
                item.addClass("ui-dropdownchecklist-indent");
            }
            item.addClass("ui-state-default");
            if (disabled) {
                item.addClass("ui-state-disabled");
            }
            label.click(function(e) {e.stopImmediatePropagation();});
            item.append(label).append($hit);

            // active items display themselves with hover
            item.hover(
                function(e) {
                    var anItem = $(this);
                    if (!anItem.hasClass("ui-state-disabled")) { anItem.addClass("ui-state-hover"); }
                }
                ,     function(e) {
                    var anItem = $(this);
                    anItem.removeClass("ui-state-hover");
                }
            );
            // clicking on the checkbox synchronizes the source select
            checkBox.click(function(e) {
                var aCheckBox = $(this);
                e.stopImmediatePropagation();
                if (aCheckBox.hasClass("active") ) {
                    // Active checkboxes take active action
                    var callback = self.options.onItemClick;
                    if ($.isFunction(callback)) try {
                        callback.call(self,aCheckBox,sourceSelect.get(0));
                    } catch (ex) {
                        // reject the change on any error
                        aCheckBox.attr("checked",!aCheckBox.attr("checked"));
                        self._syncSelected(aCheckBox);
                        return;
                    }
                    self._set_ids_of_checked();
                    self._syncSelected(aCheckBox);
                    self.sourceSelect.trigger("change", 'ddcl_internal');
                    if (!self.isMultiple && options.closeRadioOnClick) {
                        self._toggleDropContainer(false);
                    }
                }
            });
            // we are interested in the focus leaving the check box
            // but we need to detect the focus leaving one check box but
            // entering another. There is no reliable way to detect who
            // received the focus on a blur, so post the blur in the future,
            // knowing we will cancel it if we capture the focus in a timely manner
            // 23Sept2010 - unfortunately, IE 7+ and Chrome like to post a blur
            //                 event to the current item with focus when the user
            //                clicks in the scroll bar. So if you have a scrollable
            //                dropdown with focus on an item, clicking in the scroll
            //                will close the drop down.
            //                I have no solution for blur processing at this time.
            /*********
             var timerFunction = function(){
             // I had a hell of a time getting setTimeout to fire this, do not try to
             // define it within the blur function
             try { self._handleFocus(null,false,false); } catch(ex){ alert('timer failed: '+ex);}
             };
             checkBox.blur(function(e) {
             self.blurringItem = setTimeout( timerFunction, 200 );
             });
             checkBox.focus(function(e) {self._cancelBlur();});
             **********/
                // check/uncheck the item on clicks on the entire item div
            item.click(function(e) {
                var anItem = $(this);
                e.stopImmediatePropagation();
                if (!anItem.hasClass("ui-state-disabled") ) {
                    // check/uncheck the underlying control
                    var aCheckBox = anItem.find("input");
                    var checked = aCheckBox.attr("checked");
                    aCheckBox.attr("checked", !checked);

                    var callback = self.options.onItemClick;
                    if ($.isFunction(callback)) try {
                        callback.call(self,aCheckBox,sourceSelect.get(0));
                    } catch (ex) {
                        // reject the change on any error
                        aCheckBox.attr("checked",checked);
                        self._syncSelected(aCheckBox);
                        return;
                    }
                    self._set_ids_of_checked();
                    self._syncSelected(aCheckBox);
                    self.sourceSelect.trigger("change", 'ddcl_internal');
                    if (!checked && !self.isMultiple && options.closeRadioOnClick) {
                        self._toggleDropContainer(false);
                    }
                } else {
                    // retain the focus even if disabled
                    anItem.focus();
                    self._cancelBlur();
                }
            });
            // do not let the focus wander around
            item.focus(function(e) {
                var anItem = $(this);
                e.stopImmediatePropagation();
            });
            item.keyup(function(e) {self._handleKeyboard(e);});
            return item;
        },
        _createGroupItem: function(text,disabled) {
            var self = this;
            var group = $("<div />");
            group.addClass("ui-dropdownchecklist-group ui-widget-header");
            if (disabled) {
                group.addClass("ui-state-disabled");
            }
            group.css({'white-space': "nowrap"});

            var label = $("<span/>");
            label.addClass("ui-dropdownchecklist-text");
            label.css( { cursor: "default" });
            label.text(text);
            group.append(label);

            // anything interesting when you click the group???
            group.click(function(e) {
                var aGroup= $(this);
                e.stopImmediatePropagation();
                // retain the focus even if no action is taken
                aGroup.focus();
                self._cancelBlur();
            });
            // do not let the focus wander around
            group.focus(function(e) {
                var aGroup = $(this);
                e.stopImmediatePropagation();
            });
            return group;
        },
        _createCloseButton: function(text) {

            //            var label = $("<span/>");
    //            label.addClass("ui-dropdownchecklist-text");
    //            label.css( { cursor: "default" });
    //            label.html(text);
    //            closeItem.append(delimiter)
    //            closeItem.append(label);
            return jQuery('<button class="btnDone ui-state-default ui-corner-all">' + text + '</button>').hover(
                function () {
                    jQuery(this).addClass('ui-state-hover');
                },
                function () {
                    jQuery(this).removeClass('ui-state-hover');
                }).css({'marginRight':'5px'});
        },
        _createClearLink: function(text) {
          var self = this;
          return $('<a/>').text(text).click(function(event) {
              event.preventDefault();
              event.stopPropagation();
              $(this).parent('div').prev().find(':checkbox').attr('checked', false);
              self._set_ids_of_checked();
              self._updateText(self.options.emptyText);
          });
        },
        _createButtons: function() {
            var self = this, options = this.options;
//            var delimiter = $("<hr/>")
            var closeItem = $("<div />");
            closeItem.addClass("ui-state-default ui-dropdownchecklist-close ui-dropdownchecklist-item");
            closeItem.css({'white-space': 'nowrap', 'text-align': 'right', 'position': 'relative'});

            if (options.explicitClear != null) {
                closeItem.append(self._createClearLink(options.explicitClear));
            }
            if (options.explicitClose != null) {
                closeItem.append(self._createCloseButton(options.explicitClose));
            }
//            // close the control on click
            closeItem.click(function(e) {
//                var aGroup= $(this);
//                e.stopImmediatePropagation();
//                // retain the focus even if no action is taken
//                aGroup.focus();
//                self._toggleDropContainer( false );
            });
//            closeItem.hover(
//                function(e) { $(this).addClass("ui-state-hover"); }
//                ,     function(e) { $(this).removeClass("ui-state-hover"); }
//            );
            // do not let the focus wander around
//            closeItem.focus(function(e) {
//                var aGroup = $(this);
//                e.stopImmediatePropagation();
//            });
            return closeItem;
        },
        // Creates the drop items and appends them to the drop container
        // Also calculates the size needed by the drop container and returns it
        _appendItems: function() {
            var self = this, config = this.options, sourceSelect = this.sourceSelect, dropWrapper = this.dropWrapper;
            var dropContainerDiv = dropWrapper.find(".ui-dropdownchecklist-dropcontainer");
            sourceSelect.children().each(function(index) { // when the select has groups
                var opt = $(this);
                if (opt.is("option")) {
                    self._appendOption(opt, dropContainerDiv, index, false, false);
                } else if (opt.is("optgroup")) {
                    var disabled = opt.attr("disabled");
                    var text = opt.attr("label");
                    if (text != "") {
                        var group = self._createGroupItem(text,disabled);
                        dropContainerDiv.append(group);
                    }
                    self._appendOptions(opt, dropContainerDiv, index, true, disabled);
                }
            });

            var divWidth = dropContainerDiv.outerWidth();
            var divHeight = dropContainerDiv.outerHeight();
            return { width: divWidth + 18, height: divHeight };
        },
        _appendOptions: function(parent, container, parentIndex, indent, forceDisabled) {
            var self = this;
            parent.children("option").each(function(index) {
                var option = $(this);
                var childIndex = (parentIndex + "." + index);
                self._appendOption(option, container, childIndex, indent, forceDisabled);
            });
        },
        _appendOption: function(option, container, index, indent, forceDisabled) {
            var self = this;
            // Note that the browsers destroy any html structure within the OPTION
            var text = option.html();
            if ( (text != null) && (text != '') ) {
                var value = option.val();
                var optCss = option.attr('style');
                var selected = option.attr("selected");
                var code = option.attr('code');
                var data = option.attr('data');
                var number = option.attr('number');
                var disabled = (forceDisabled || option.attr("disabled"));
                // Use the same tab index as the selector replacement
                var tabIndex = self.controlSelector.attr("tabindex");
                var item = self._createDropItem(index, tabIndex, value, code, data, number,text, optCss, selected, disabled, indent);
                container.append(item);
            }
        },
        // Synchronizes the items checked and the source select
        // When firstItemChecksAll option is active also synchronizes the checked items
        // senderCheckbox parameters is the checkbox input that generated the synchronization
        _syncSelected: function(senderCheckbox) {
            var self = this, options = this.options, sourceSelect = this.sourceSelect, dropWrapper = this.dropWrapper;
            var selectOptions = sourceSelect.get(0).options;
            var allCheckboxes = dropWrapper.find("input.active");
            if (options.firstItemChecksAll == 'exclusive') {
                if ((senderCheckbox == null) && $(selectOptions[0]).attr("selected") ) {
                    // Initialization call with first item active
                    allCheckboxes.attr("checked", false);
                    $(allCheckboxes[0]).attr("checked", true);
                } else if ((senderCheckbox != null) && (senderCheckbox.attr("index") == 0)) {
                    // Action on the first, so all other checkboxes NOT active
                    var firstIsActive = senderCheckbox.attr("checked");
                    allCheckboxes.attr("checked", false);
                    $(allCheckboxes[0]).attr("checked", firstIsActive);
                } else  {
                    // check the first checkbox if all the other checkboxes are checked
                    var allChecked = true;
                    var firstCheckbox = null;
                    allCheckboxes.each(function(index) {
                        if (index > 0) {
                            var checked = $(this).attr("checked");
                            if (!checked) { allChecked = false; }
                        } else {
                            firstCheckbox = $(this);
                        }
                    });
                    if ( firstCheckbox != null ) {
                        if ( allChecked ) {
                            // when all are checked, only the first left checked
                            allCheckboxes.attr("checked", false);
                        }
                        firstCheckbox.attr("checked", allChecked );
                    }
                }
            } else if (options.firstItemChecksAll) {
                if ((senderCheckbox == null) && $(selectOptions[0]).attr("selected") ) {
                    // Initialization call with first item active so force all to be active
                    allCheckboxes.attr("checked", true);
                } else if ((senderCheckbox != null) && (senderCheckbox.attr("index") == 0)) {
                    // Check all checkboxes if the first one is checked
                    allCheckboxes.attr("checked", senderCheckbox.attr("checked"));
                } else  {
                    // check the first checkbox if all the other checkboxes are checked
                    var allChecked = true;
                    var firstCheckbox = null;
                    allCheckboxes.each(function(index) {
                        if (index > 0) {
                            var checked = $(this).attr("checked");
                            if (!checked) { allChecked = false; }
                        } else {
                            firstCheckbox = $(this);
                        }
                    });
                    if ( firstCheckbox != null ) {
                        firstCheckbox.attr("checked", allChecked );
                    }
                }
            }
            // do the actual synch with the source select
            var empties = 0;
            allCheckboxes = dropWrapper.find("input");
            allCheckboxes.each(function(index) {
                var anOption = $(selectOptions[index + empties]);
                var optionText = anOption.html();
                if ( (optionText == null) || (optionText == '') ) {
                    empties += 1;
                    anOption = $(selectOptions[index + empties]);
                }
                anOption.attr("selected", $(this).prop("checked"));
            });
            // update the text shown in the control
            self._updateControlText();

            // Ensure the focus stays pointing where the user is working
            if ( senderCheckbox != null) { senderCheckbox.focus(); }
        },
        _sourceSelectChangeHandler: function(event) {
            var self = this, dropWrapper = this.dropWrapper;
            dropWrapper.find("input").val(self.sourceSelect.val());

            // update the text shown in the control
            self._updateControlText();
        },
        _updateText:function (text) {
            var controlWrapper = this.controlWrapper;
            var controlLabel = controlWrapper.find(".ui-dropdownchecklist-text");
            controlLabel.html(text);
            // the attribute needs naked text, not html
            controlLabel.attr("title", controlLabel.text());
        },
        // Updates the text shown in the control depending on the checked (selected) items
        _updateControlText: function() {
            var self = this, sourceSelect = this.sourceSelect, options = this.options;
            var firstOption = sourceSelect.find("option:first");
            var selectOptions = sourceSelect.find("option");
            var text = self._formatText(selectOptions, options.firstItemChecksAll, firstOption);
            self._updateText(text);
        },
        isAllSelected:function (selectOptions) {
            var selectedOptions = selectOptions.filter(":selected");
            return selectOptions.size() == selectedOptions.size();
        },
        // Formats the text that is shown in the control
        _formatText: function(selectOptions, firstItemChecksAll, firstOption) {
            var text;
            if ( $.isFunction(this.options.textFormatFunction) ) {
                // let the callback do the formatting, but do not allow it to fail
                try {
                    text = this.options.textFormatFunction(selectOptions);
                } catch(ex) {
                    alert( 'textFormatFunction failed: ' + ex );
                }
            } else if (firstItemChecksAll && (firstOption != null) && firstOption.attr("selected")) {
                // just set the text from the first item
                text = firstOption.html();
            } else {
                // concatenate the text from the checked items
                text = "";


                selectOptions.each(function() {
                    if ($(this).attr("selected")) {
                        if ( text != "" ) { text += ", "; }
                        /* NOTE use of .html versus .text, which can screw up ampersands for IE */
                        var optCss = $(this).attr('style');
                        var tempspan = $('<span/>');
                        tempspan.html( $(this).html() );
                        if ( optCss == null ) {
                            text += tempspan.html();
                        } else {
                            tempspan.attr('style',optCss);
                            text += $("<span/>").append(tempspan).html();
                        }
                    }
                });
                var allSelected = this.isAllSelected(selectOptions);
                if ( text == ""  || allSelected) {
                    text = (this.options.emptyText != null) ? this.options.emptyText : "&nbsp;";
                }

            }
            return text;
        },
        // Shows and hides the drop container
        _toggleDropContainer: function( makeOpen ) {
            var self = this;
            // hides the last shown drop container
            var hide = function(instance) {
                if ((instance != null) && instance.dropWrapper.isOpen ){
                    instance.dropWrapper.isOpen = false;
                    $.ui.dropdownchecklist.gLastOpened = null;

                    var config = instance.options;
                    instance.dropWrapper.css({
                        top: "-33000px",
                        left: "-33000px"
                    });
                    var aControl = instance.controlSelector;
                    aControl.removeClass("ui-state-active");
                    aControl.removeClass("ui-state-hover");

                    var anIcon = instance.controlWrapper.find(".ui-icon");
                    if ( anIcon.length > 0 ) {
                        anIcon.removeClass( (config.icon.toClose != null) ? config.icon.toClose : "ui-icon-triangle-1-s");
                        anIcon.addClass( (config.icon.toOpen != null) ? config.icon.toOpen : "ui-icon-triangle-1-e");
                    }
                    $(document).unbind("click", hide);

                    // keep the items out of the tab order by disabling them
                    instance.dropWrapper.find("input.active").attr("disabled",true);

                    // the following blur just does not fire???  because it is hidden???  because it does not have focus???
                    //instance.sourceSelect.trigger("blur");
                    //instance.sourceSelect.triggerHandler("blur");
                    if($.isFunction(config.onComplete)) { try {
                        config.onComplete.call(instance,instance.sourceSelect.get(0));
                    } catch(ex) {
                        alert( 'callback failed: ' + ex );
                    }}
                }
            };
            // shows the given drop container instance
            var show = function(instance) {
                if ( !instance.dropWrapper.isOpen ) {
                    instance.dropWrapper.isOpen = true;
                    $.ui.dropdownchecklist.gLastOpened = instance;

                    var config = instance.options;
                    /**** Issue127 (and the like) to correct positioning when parent element is relative
                     ****    This positioning only worked with simple, non-relative parent position
                     instance.dropWrapper.css({
                     top: instance.controlWrapper.offset().top + instance.controlWrapper.outerHeight() + "px",
                     left: instance.controlWrapper.offset().left + "px"
                     });
                     ****/
                    if ((config.positionHow == null) || (config.positionHow == 'absolute')) {
                        /** Floats above subsequent content, but does NOT scroll */
                        instance.dropWrapper.css({
                            position: 'absolute'
                            ,   top: instance.controlWrapper.position().top + instance.controlWrapper.outerHeight() + "px"
                            ,   left: instance.controlWrapper.position().left + "px"
                        });
                    } else if (config.positionHow == 'relative') {
                        /** Scrolls with the parent but does NOT float above subsequent content */
                        instance.dropWrapper.css({
                            position: 'relative'
                            ,   top: "0px"
                            ,   left: "0px"
                        });
                    }
                    var zIndex = 0;
                    if (config.zIndex == null) {
                        var ancestorsZIndexes = instance.controlWrapper.parents().map(
                            function() {
                                var zIndex = $(this).css("z-index");
                                return isNaN(zIndex) ? 0 : zIndex; }
                        ).get();
                        var parentZIndex = Math.max.apply(Math, ancestorsZIndexes);
                        if ( parentZIndex >= 0) zIndex = parentZIndex+1;
                    } else {
                        /* Explicit set from the optins */
                        zIndex = parseInt(config.zIndex);
                    }
                    if (zIndex > 0) {
                        instance.dropWrapper.css( { 'z-index': zIndex } );
                    }

                    var aControl = instance.controlSelector;
                    aControl.addClass("ui-state-active");
                    aControl.removeClass("ui-state-hover");

                    var anIcon = instance.controlWrapper.find(".ui-icon");
                    if ( anIcon.length > 0 ) {
                        anIcon.removeClass( (config.icon.toOpen != null) ? config.icon.toOpen : "ui-icon-triangle-1-e");
                        anIcon.addClass( (config.icon.toClose != null) ? config.icon.toClose : "ui-icon-triangle-1-s");
                    }
                    $(document).bind("click", function(e) {hide(instance);} );

                    // insert the items back into the tab order by enabling all active ones
                    var activeItems = instance.dropWrapper.find("input.active");
                    activeItems.attr("disabled",false);

                    // we want the focus on the first active input item
                    var firstActiveItem = activeItems.get(0);
                    if ( firstActiveItem != null ) {
                        firstActiveItem.focus();
                    }

                    if(self.options.eventCallback) {
                        self.options.eventCallback();
                    }

                }
            };
            if ( makeOpen ) {
                hide($.ui.dropdownchecklist.gLastOpened);
                show(self);
            } else {
                hide(self);
            }
        },
        // Set the size of the control and of the drop container
        _setSize: function(dropCalculatedSize) {
            var options = this.options, dropWrapper = this.dropWrapper, controlWrapper = this.controlWrapper;

            // use the width from config options if set, otherwise set the same width as the drop container
            var controlWidth = dropCalculatedSize.width;
            if (options.width != null) {
                controlWidth = parseInt(options.width);
            } else if (options.minWidth != null) {
                var minWidth = parseInt(options.minWidth);
                // if the width is too small (usually when there are no items) set a minimum width
                if (controlWidth < minWidth) {
                    controlWidth = minWidth;
                }
            }
            var control = this.controlSelector;
            control.css({ width: controlWidth +"px" });

            // if we size the text, then Firefox places icons to the right properly
            // and we do not wrap on long lines
            var controlText = control.find(".ui-dropdownchecklist-text");
            var controlIcon = control.find(".ui-icon");
            if ( controlIcon != null ) {
                // Must be an inner/outer/border problem, but IE6 needs an extra bit of space,
                // otherwise you can get text pushed down into a second line when icons are active
                controlWidth -= (controlIcon.outerWidth() + 4);
                controlText.css( { width: controlWidth + "px" } );
            }
            // Account for padding, borders, etc
            controlWidth = controlWrapper.outerWidth();

            // the drop container height can be set from options
            var maxDropHeight = (options.maxDropHeight != null)
                ? parseInt(options.maxDropHeight)
                : -1;
            var dropHeight = ((maxDropHeight > 0) && (dropCalculatedSize.height > maxDropHeight))
                ? maxDropHeight
                : dropCalculatedSize.height;
            // ensure the drop container is not less than the control width (would be ugly)
            var dropWidth = dropCalculatedSize.width < controlWidth ? controlWidth : dropCalculatedSize.width;

            $(dropWrapper).css({
                height: dropHeight + "px",
                width: dropWidth + "px"
            });
            dropWrapper.find(".ui-dropdownchecklist-dropcontainer").css({
                height: dropHeight + "px"
            });
        },
        // Initializes the plugin
        _init: function() {
            var self = this, options = this.options;
            if ( $.ui.dropdownchecklist.gIDCounter == null) {
                $.ui.dropdownchecklist.gIDCounter = 1;
            }
            // item blurring relies on a cancelable timer
            self.blurringItem = null;

            // sourceSelect is the select on which the plugin is applied
            var sourceSelect = self.element;
            self.initialDisplay = sourceSelect.css("display");
            sourceSelect.css("display", "none");
            self.initialMultiple = $(sourceSelect).attr("multiple");
            self.isMultiple = self.initialMultiple;
            if (options.forceMultiple != null) { self.isMultiple = options.forceMultiple; }
            $(sourceSelect).attr("multiple", true);
            self.sourceSelect = sourceSelect;

            // append the control that resembles a single selection select
            var controlWrapper = self._appendControl();
            self.controlWrapper = controlWrapper;
            self.controlSelector = controlWrapper.find(".ui-dropdownchecklist-selector");

            // create the drop container where the items are shown
            var dropWrapper = self._appendDropContainer(controlWrapper);
            self.dropWrapper = dropWrapper;

            // append the items from the source select element
            var dropCalculatedSize = self._appendItems();

            // updates the text shown in the control
            self._updateControlText(controlWrapper, dropWrapper, sourceSelect);

            // set the sizes of control and drop container
            self._setSize(dropCalculatedSize);

            // look for possible auto-check needed on first item
            if ( options.firstItemChecksAll ) {
                self._syncSelected(null);
            }
            // BGIFrame for IE6
            if (options.bgiframe && typeof self.dropWrapper.bgiframe == "function") {
                self.dropWrapper.bgiframe();
            }
            // listen for change events on the source select element
            // ensure we avoid processing internally triggered changes
            self.sourceSelect.change(function(event, eventName) {
                if (eventName != 'ddcl_internal') {
                    self._sourceSelectChangeHandler(event);
                }
            });

            $.extend(options, {width: $(sourceSelect).width()})
        },
        // Refresh the disable and check state from the underlying control
        _refreshOption: function(item,disabled,selected) {
            var aParent = item.parent();
            // account for enabled/disabled
            if ( disabled ) {
                item.attr("disabled",true);
                item.removeClass("active");
                item.addClass("inactive");
                aParent.addClass("ui-state-disabled");
            } else {
                item.attr("disabled",false);
                item.removeClass("inactive");
                item.addClass("active");
                aParent.removeClass("ui-state-disabled");
            }
            // adjust the checkbox state
            item.attr("checked",selected);
        },
        _refreshGroup: function(group,disabled) {
            if ( disabled ) {
                group.addClass("ui-state-disabled");
            } else {
                group.removeClass("ui-state-disabled");
            }
        },
        // External command to explicitly close the dropdown
        close: function() {
            this._toggleDropContainer(false);
        },
        // External command to refresh the ddcl from the underlying selector
        refresh: function() {
            var self = this, sourceSelect = this.sourceSelect, dropWrapper = this.dropWrapper;

            var allCheckBoxes = dropWrapper.find("input");
            var allGroups = dropWrapper.find(".ui-dropdownchecklist-group");

            var groupCount = 0;
            var optionCount = 0;
            sourceSelect.children().each(function(index) {
                var opt = $(this);
                var disabled = opt.attr("disabled");
                if (opt.is("option")) {
                    var selected = opt.attr("selected");
                    var anItem = $(allCheckBoxes[optionCount]);
                    self._refreshOption(anItem, disabled, selected);
                    optionCount += 1;
                } else if (opt.is("optgroup")) {
                    var text = opt.attr("label");
                    if (text != "") {
                        var aGroup = $(allGroups[groupCount]);
                        self._refreshGroup(aGroup, disabled);
                        groupCount += 1;
                    }
                    opt.children("option").each(function() {
                        var subopt = $(this);
                        var subdisabled = (disabled || subopt.attr("disabled"));
                        var selected = subopt.attr("selected");
                        var subItem = $(allCheckBoxes[optionCount]);
                        self._refreshOption(subItem, subdisabled, selected );
                        optionCount += 1;
                    });
                }
            });
            // sync will handle firstItemChecksAll and updateControlText
            self._syncSelected(null);
        },
        // External command to enable the ddcl control
        enable: function() {
            this.controlSelector.removeClass("ui-state-disabled");
            this.disabled = false;
        },
        // External command to disable the ddcl control
        disable: function() {
            this.controlSelector.addClass("ui-state-disabled");
            this.disabled = true;
        },
        // External command to destroy all traces of the ddcl control
        destroy: function() {
            $.Widget.prototype.destroy.apply(this, arguments);
            this.sourceSelect.css("display", this.initialDisplay);
            this.sourceSelect.attr("multiple", this.initialMultiple);
            this.controlWrapper.unbind().remove();
            this.dropWrapper.remove();
        }
    });

    $.extend($.ui.dropdownchecklist, {
        defaults: {
            width: null
            ,   maxDropHeight: null
            ,   firstItemChecksAll: false
            ,   closeRadioOnClick: false
            ,   minWidth: 50
            ,   positionHow: 'absolute'
            ,   bgiframe: false
            ,    explicitClose: null
        }
    });

})(jQuery);