/*
TODO:
Bug fixes:
 - Position must not exceed the document/window boundaries, unless required, then direction is down
*/
;(function($) {
 /*
 * ui.dropdownchecklist
 *
 * Copyright (c) 2008-2009 Adrian Tosca
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and 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", {
 // Creates the drop container that keeps the items and appends it to the document
 _appendDropContainer: function() {
 var wrapper = $("<div/>");
 // the container is wrapped in a div
 wrapper.addClass("ui-dropdownchecklist-dropcontainer-wrapper");
 // initially hidden
 wrapper.css({
 position: 'absolute',
 left: "-3300",
 top: "-3300px",
 width: '3000px',
 height: '3000px'
 });
 var container = $("<ul/>"); // the actual container
 container.addClass("ui-dropdownchecklist-dropcontainer")
 .css("overflow-y", "auto");
 wrapper.append(container);
 $(document.body).append(wrapper);
 //wrapper.insertAfter(this.sourceSelect);
 // flag that tells if the drop container is shown or not
 wrapper.drop = false;
 return wrapper;
 },
 _isDropDownKeyShortcut: function(e) {
 return e.altKey && ($.ui.keyCode.DOWN == (e.keyCode || e.which));// Alt + Down Arrow
 },
 _isDroDownCloseKey: function(e) {
 return $.ui.keyCode.ESCAPE == (e.keyCode || e.which);
 },
 _handleKeyboard: function(e) {
  var self = this;
 if (self._isDropDownKeyShortcut(e)) {
  e.stopPropagation();
 self._toggleDropContainer();
 self.dropWrapper.find("input:first").focus();
 } else if (self.dropWrapper.drop && self._isDroDownCloseKey(e)) {
 self._toggleDropContainer();
 }
 },
 // 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;
 // the controls is wrapped in a span with inline-block display
 var wrapper = $("<span/>");
 wrapper.addClass("ui-dropdownchecklist-wrapper");
 wrapper.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-dropdownchecklist");
 control.css({
 display: "inline-block"
 });
 control.attr("tabIndex", 0);
 control.keyup(function(e) {self._handleKeyboard(e)});
 wrapper.append(control);
 // the text container keeps the control text that is build from the selected (checked) items
 var textContainer = $("<span/>");
 textContainer.addClass("ui-dropdownchecklist-text")
 textContainer.css({
 display: "inline-block",
 overflow: "hidden"
 });
 control.append(textContainer);
 // add the hover styles to the control
 wrapper.hover(function() {
 if (!self.disabled) {
 control.toggleClass("ui-dropdownchecklist-hover")
 }
 }, function() {
 if (!self.disabled) {
 control.toggleClass("ui-dropdownchecklist-hover")
 }
 });
 // clicking on the control toggles the drop container
 wrapper.click(function(event) {
 if (!self.disabled) {
 event.stopPropagation();
 self._toggleDropContainer();
 }
 })
 wrapper.insertAfter(sourceSelect);
 return wrapper;
 },
 // Creates a drop item that coresponds to an option element in the source select
 _createDropItem: function(index, value, text, checked, disabled, indent) {
 var self = this;
 // the item contains a div that contains a checkbox input and a span for text
 // the div
 var item = $("<li/>");
 item.addClass("ui-dropdownchecklist-item");
 item.css({whiteSpace: "nowrap"});
 var checkedString = checked ? ' checked="checked"' : '';
 var disabledString = disabled ? ' disabled="disabled"' : '';
 var idBase = (self.sourceSelect.attr("id") || "ddcl");
 var id = idBase + index;
 var checkBox;
 if (self.initialMultiple) { // the checkbox
 checkBox = $('<input type="checkbox" id="' + id + '"' + checkedString + disabledString + '/>');
 } else { // the radiobutton
 item.addClass('ui-dropdownchecklist-item-radio');
 checkBox = $('<input type="radio" id="' + id + '" name="' + idBase + '"' + checkedString + disabledString + '/>');
 }
 checkBox = checkBox.attr("index", index).val(value);
 checkBox.css('display','none');
 item.append(checkBox);
 // the text
 var label = text;
 item.append(label);
 item.attr("title", label);
 //item.attr("title", text);
 if (disabled) {
 item.addClass('ui-dropdownchecklist-item-disabled');
 }
 if (checked) {
 item.addClass('ui-dropdownchecklist-item-checked');
 }
 item.hover(function() {
 item.addClass("ui-dropdownchecklist-item-hover")
 }, function() {
 item.removeClass("ui-dropdownchecklist-item-hover")
 });
 // clicking on the checkbox synchronizes the source select
 checkBox.click(function(e) {
 e.stopPropagation();
 if (!disabled) {
 self._syncSelected($(this));
 self.sourceSelect.trigger("change", 'ddcl_internal');
 }
 });
 // check/uncheck the item on clicks on the entire item div
 var checkItem = function(e) {
 e.stopPropagation();
 if (!disabled) {
 var checked = checkBox.attr("checked");
 checkBox.attr("checked", !checked)
 self._syncSelected(checkBox);
 self.sourceSelect.trigger("change", 'ddcl_internal');
 }
 }
 item.click(checkItem);
 item.keyup(function(e) {self._handleKeyboard(e)});
 return item;
 },
	_createGroupItem: function(text) {
 var self = this;
 var group = $("<li />")
 group.addClass("ui-dropdownchecklist-group");
 group.css({whiteSpace: "nowrap"});
 group.text(text);
 // group selects options inside it
 group.click(function(e){
 e.stopPropagation();
 self._syncSelected($(this).parent());
 });

 var groupToggle = $("<span />");
 groupToggle.addClass("ui-dropdownchecklist-group-toggle close-state");
 //groupToggle.text("&nbsp;");
 groupToggle.click(function(e){
 e.stopPropagation();
 $(this).parent().siblings().toggle();
 if ($(this).hasClass("close-state")) {
 $(this).removeClass("close-state");
 } else {
 $(this).addClass("close-state");
 }
 });
 group.append(groupToggle);

 return group;
	},
 // 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, sourceSelect = this.sourceSelect, dropWrapper = this.dropWrapper;
 var dropContainerDiv = dropWrapper.find(".ui-dropdownchecklist-dropcontainer");
 dropContainerDiv.css({ "float": "left" }); // to allow getting the actual width of the container
 sourceSelect.children().each(function(index) { // when the select has groups
 var opt = $(this);
 if (opt.is("option")) {
 self._appendOption(opt, dropContainerDiv, index, false);
 } else {
 var text = opt.attr("label");
 var group = self._createGroupItem(text);

 var groupContainer = $("<div />");
 groupContainer.addClass("ui-dropdownchecklist-group-container");

 dropContainerDiv.append(groupContainer);
 self._appendOptions(opt, groupContainer, index, true);
 groupContainer.prepend(group);
 }
 });
 //self._appendOptions(sourceSelect, dropContainerDiv, false); // when no groups
 var divWidth = dropContainerDiv.outerWidth();
 var divHeight = dropContainerDiv.outerHeight();
 dropContainerDiv.css({ "float": "" }); // set it back
 return { width: divWidth, height: divHeight };
 },
 _appendOptions: function(parent, container, parentIndex, indent) {
 var self = this;
 parent.children("option").each(function(index) {
 var option = $(this);
 var childIndex = (parentIndex + "." + index);
 self._appendOption(option, container, childIndex, indent);
 });
 },
 _appendOption: function(option, container, index, indent) {
 var self = this;
 var text = option.text();
 var value = option.val();
 var selected = option.attr("selected");
	var disabled = option.attr("disabled");
 var item = self._createDropItem(index, value, text, 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 allCheckboxes = dropWrapper.find("input:not([disabled])");
 var allCheckboxes = dropWrapper.find("input");

 if (senderCheckbox.is('div')) {
 // group selects/deselects children
 var groupChecked = true;
 allCheckboxes.each(function(index) {
 if ($(this).attr("disabled")) return true;
 // find if all in group are checked
 if ((index > 0) && ($(this).parent().parent()[0] == senderCheckbox[0])) {
 if (!$(this).attr('checked')) groupChecked = false;
 }
 });
 // Check or deselect
 groupChecked = !groupChecked ? true : false;
 allCheckboxes.each(function(index) {
 if ($(this).attr("disabled")) return true;
 if ((index > 0) && ($(this).parent().parent()[0] == senderCheckbox[0])) {
 $(this).attr('checked', groupChecked);
 }
 });
 }

 if (options.firstItemChecksAll) {
 // if firstItemChecksAll is true, check all checkboxes if the first one is checked
 if (senderCheckbox.attr("index") == 0) {
 allCheckboxes.each(function(index) {
 if ($(this).attr("disabled")) return true;
 $(this).attr("checked", senderCheckbox.attr("checked"));
 });
 //allCheckboxes.attr("checked", senderCheckbox.attr("checked"));
 } else {
 // check the first checkbox if all the other checkboxes are checked
 var allChecked;
 allChecked = true;
 allCheckboxes.each(function(index) {
 if ($(this).attr("disabled")) return true;
 if (index > 0) {
 var checked = $(this).attr("checked");
 if (!checked) allChecked = false;
 }
 });
 var firstCheckbox = allCheckboxes.filter(":first");
 firstCheckbox.attr("checked", false);
 if (allChecked) {
 firstCheckbox.attr("checked", true);
 } else {
 firstCheckbox.attr("checked", false);
 }
 }
 }
 // do the actual synch with the source select
 var selectOptions = sourceSelect.get(0).options;
 allCheckboxes.each(function(index) {
 if ($(this).attr("disabled")) return true;
 var checked = $(this).attr("checked");
 $(selectOptions[index]).attr("selected", checked);
 // update graphics state
 if ( checked ) {
 $(this).parent().addClass('ui-dropdownchecklist-item-checked');
 } else {
 $(this).parent().removeClass('ui-dropdownchecklist-item-checked');
 }
 });
 // update the text shown in the control
 self._updateControlText();
 },
 _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();
 },
 // Updates the text shown in the control depending on the checked (selected) items
 _updateControlText: function() {
 var self = this, sourceSelect = this.sourceSelect, options = this.options, controlWrapper = this.controlWrapper;
 var firstSelect = sourceSelect.find("option:first");
 var allSelected = null != firstSelect && firstSelect.attr("selected");
 var selectOptions = sourceSelect.find("option");
 var text = self._formatText(selectOptions, options.firstItemChecksAll, allSelected);
 var controlLabel = controlWrapper.find(".ui-dropdownchecklist-text");
 controlLabel.attr("title", text);
 if ( self.selectedCount > 2 ) {
 // Display n selected if more than 2 options are selected
 text = self.selectedCount + ' ';
 text += options.selectedText ? options.selectedText : 'selected'
 }
 controlLabel.text(text);
 if ( !text && sourceSelect.attr("title") ) {
	controlLabel.text(sourceSelect.attr("title"));
 }
 },
 // Formats the text that is shown in the control
 _formatText: function(selectOptions, firstItemChecksAll, allSelected) {
 var self = this;
 var selectedCount = 0;
 var text;
 if (firstItemChecksAll && allSelected) {
 // just set the text from the first item
 text = selectOptions.filter(":first").attr('label');
 } else {
 // concatenate the text from the checked items
 text = "";
 selectOptions.each(function() {
 if ($(this).attr("selected")) {
 text += $(this).attr("label") ? $(this).attr("label") + ", " : $(this).text() + ", ";
 selectedCount++;
 }
 });
 if (text.length > 0) {
 text = text.substring(0, text.length - 2);
 }
 }
 self.selectedCount = selectedCount;
 return text;
 },
 // Shows and hides the drop container
 _toggleDropContainer: function() {
 var self = this, dropWrapper = this.dropWrapper, controlWrapper = this.controlWrapper;
 // hides the last shown drop container
 var hide = function() {
 var instance = $.ui.dropdownchecklist.drop;
 if (null != instance) {
 instance.dropWrapper.css({
 top: "-3300px",
 left: "-3300px"
 });
 instance.controlWrapper.find(".ui-dropdownchecklist").toggleClass("ui-dropdownchecklist-active");
 instance.dropWrapper.find("input").attr("tabIndex", -1);
 instance.dropWrapper.drop = false;
 $.ui.dropdownchecklist.drop = null;
 $(document).unbind("click", hide);
 self.sourceSelect.trigger("blur");
 }
 }
 /* TODO:
 Position must be inside the document, so calculate the edges
 */
 // shows the given drop container instance
 var show = function(instance) {
 if (null != $.ui.dropdownchecklist.drop) {
 hide();
 }
 instance.dropWrapper.css({
 top: instance.controlWrapper.offset().top + instance.controlWrapper.outerHeight() + "px",
 left: instance.controlWrapper.offset().left + "px"
 })
 var ancestorsZIndexes = 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) {
 instance.dropWrapper.css({
 zIndex: (parentZIndex+1)
 })
 }
 instance.controlWrapper.find(".ui-dropdownchecklist").toggleClass("ui-dropdownchecklist-active");
 instance.dropWrapper.find("input").attr("tabIndex", 0);
 instance.dropWrapper.drop = true;
 $.ui.dropdownchecklist.drop = instance;
 $(document).bind("click", hide);
 self.sourceSelect.trigger("focus");
 }
 if (dropWrapper.drop) {
 hide(self);
 } else {
 show(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;
 var controlWidth;
 // use the width from options if set, otherwise set the same width as the drop container
 if (options.width) {
 controlWidth = parseInt(options.width);
 } else {
 controlWidth = dropCalculatedSize.width;
 var minWidth = options.minWidth;
 // if the width is to small (usually when there are no items) set a minimum width
 if (controlWidth < minWidth) {
 controlWidth = minWidth;
 }
 else if (options.maxWidth) {
 // Set to max width if it's set and width exceeds this limit (items can exceed this limit)
 var maxWidth = parseInt(options.maxWidth);
 if (controlWidth > maxWidth) {
 controlWidth = maxWidth;
 }
 }
 }
 controlWrapper.find(".ui-dropdownchecklist-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 = controlWrapper.outerWidth();
 // the drop container height can be set from options
 var dropHeight = dropCalculatedSize.height;
 var maxDropHeight = parseInt(options.maxDropHeight);
 if ( dropHeight > maxDropHeight ) {
 dropHeight = maxDropHeight;
 }
 // ensure the drop container is not less than the control width (would be ugly)
 var dropWidth = dropCalculatedSize.width < controlOuterWidth ? controlOuterWidth : dropCalculatedSize.width;
 $(dropWrapper).css({
 width: dropWidth + "px",
 height: dropHeight + "px"
 });
 dropWrapper.find(".ui-dropdownchecklist-dropcontainer").css({
 height: dropHeight + "px"
 });
 },
 // Initializes the plugin
 _init: function() {
 var self = this, options = this.options;
 // 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");
 sourceSelect.attr("multiple", "multiple");
 self.sourceSelect = sourceSelect;
 // create the drop container where the items are shown
 var dropWrapper = self._appendDropContainer();
 self.dropWrapper = dropWrapper;
 // append the items from the source select element
 var dropCalculatedSize = self._appendItems();
 // append the control that resembles a single selection select
 var controlWrapper = self._appendControl();
 self.controlWrapper = controlWrapper;
 // updates the text shown in the control
 self._updateControlText(controlWrapper, dropWrapper, sourceSelect);
 // set the sizes of control and drop container
 self._setSize(dropCalculatedSize);
 // 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);
 }
 });
 },
 enable: function() {
 this.controlWrapper.find(".ui-dropdownchecklist").removeClass("ui-dropdownchecklist-disabled");
 this.disabled = false;
 },
 disable: function() {
 this.controlWrapper.find(".ui-dropdownchecklist").addClass("ui-dropdownchecklist-disabled");
 this.disabled = true;
 },
 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,
 minWidth: 50,
 maxWidth: null,
 selectedText: 'selected',
 bgiframe: false
 }
 });

})(jQuery);
