fix #835 Collecton selection bug in multiselect menu

This commit is contained in:
Nicolas Le Goff
2012-07-19 18:15:34 +02:00
parent b3ac109d71
commit 650945c642

View File

@@ -10,7 +10,7 @@
* http://www.gnu.org/licenses/gpl.html
*/
(function($){
$.fn.multiSelect = function(opts){
opts = $.extend({}, $.fn.multiSelect.defaults, opts);
@@ -18,22 +18,22 @@
return new MultiSelect(this, opts);
});
};
// counter to dynamically generate label/option IDs if they don't exist
var multiselectID = 0;
var MultiSelect = function(select,o){
var $select = $original = $(select),
$options, $header, $labels,
html = [],
optgroups = [],
isDisabled = $select.is(':disabled'),
var $select = $original = $(select),
$options, $header, $labels,
html = [],
optgroups = [],
isDisabled = $select.is(':disabled'),
id = select.id || 'ui-multiselect-'+multiselectID++; // unique ID for the label & option tags
html.push('<a id="'+ id +'" class="ui-multiselect ui-widget ui-state-default ui-corner-all' + (isDisabled || o.disabled ? ' ui-state-disabled' : '') + '" style="margin-left: 10px;margin-right: 10px;height:20px;">');
html.push('<input readonly="readonly" type="text" class="" value="'+ o.allSelectedText +'" title="'+ select.title +'" /></a>');
html.push('<div class="ui-multiselect-options' + (o.shadow ? ' ui-multiselect-shadow' : '') + ' ui-widget ui-widget-content ui-corner-all" style="margin-left: 10px;margin-right: 10px;border:1px solid #212121;">');
if(o.showHeader){
html.push('<div class="ui-widget-header ui-helper-clearfix ui-corner-all ui-multiselect-header" >');
html.push('<ul class="ui-helper-reset">');
@@ -43,44 +43,44 @@
html.push('</ul>');
html.push('</div>');
}
// build options
html.push('<ul class="ui-multiselect-checkboxes ui-helper-reset">');
/*
/*
If this select is disabled, remove the actual disabled attribute and let themeroller's .ui-state-disabled class handle it.
This is a workaround for jQuery bug #6211 where options in webkit inherit the select's disabled property. This
won't achieve the same level of 'disabled' behavior (the checkboxes will still be present in form submission),
but it at least gives you a way to emulate the UI.
won't achieve the same level of 'disabled' behavior (the checkboxes will still be present in form submission),
but it at least gives you a way to emulate the UI.
*/
if(isDisabled){
$select.removeAttr("disabled");
}
$select.find('option').each(function(i){
var $this = $(this),
title = $this.html(),
value = this.value,
inputID = this.id || "ui-multiselect-"+id+"-option-"+i,
$parent = $this.parent(),
hasOptGroup = $parent.is('optgroup'),
isDisabled = $this.is(':disabled'),
var $this = $(this),
title = $this.html(),
value = this.value,
inputID = this.id || "ui-multiselect-"+id+"-option-"+i,
$parent = $this.parent(),
hasOptGroup = $parent.is('optgroup'),
isDisabled = $this.is(':disabled'),
labelClasses = ['ui-corner-all'];
if(hasOptGroup){
var label = $parent.attr('label');
if($.inArray(label,optgroups) === -1){
html.push('<li class="ui-multiselect-optgroup-label"><a href="#" class="ui-multiselect-all" style="color:#FF9000;float:left;width:350px;">' + label + '</a><a href="#" style="float:right;" class="ui-multiselect-toggleclose ui-icon ui-icon-triangle-1-s"></a></li>');
optgroups.push(label);
}
}
if(value.length > 0){
if(isDisabled){
labelClasses.push('ui-state-disabled');
}
html.push('<li class="' + (isDisabled ? 'ui-multiselect-disabled' : '') +'">');
html.push('<label for="'+inputID+'" class="'+labelClasses.join(' ')+'"><input id="'+inputID+'" type="'+(o.multiple ? 'checkbox' : 'radio')+'" checked="checked" name="'+select.name+'" value="'+value+'" title="'+title+'"');
if($this.is(':selected')){
@@ -99,49 +99,49 @@
$options = $select.next('div.ui-multiselect-options');
$header = $options.find('div.ui-multiselect-header');
$labels = $options.find('label').not('.ui-state-disabled');
//hide by default
$options.find('li.ui-multiselect-optgroup-label a.ui-multiselect-toggleclose').parent().nextUntil('li.ui-multiselect-optgroup-label').find('label').hide();
// calculate widths
var iconWidth = $select.find('span.ui-icon').outerWidth(), inputWidth = $original.outerWidth(), totalWidth = inputWidth+iconWidth;
if( /\d/.test(o.minWidth) && totalWidth < o.minWidth){
inputWidth = o.minWidth-iconWidth;
totalWidth = o.minWidth;
}
// set widths
$select.width(totalWidth).find('input').width(inputWidth - 6);
// build header links
if(o.showHeader){
$header.find('a').click(function(e){
var $this = $(this);
// close link
if($this.hasClass('ui-multiselect-close')){
$options.trigger('close');
// check all / uncheck all
} else {
var checkAll = $this.hasClass('ui-multiselect-all');
$options.trigger('toggleChecked', [(checkAll ? true : false)]);
checkAll ? $options.find('li.ui-multiselect-optgroup-label a.ui-multiselect-all').css("color", "#FF9000") : $options.find('li.ui-multiselect-optgroup-label a.ui-multiselect-all').css("color", "white") ;
checkAll ? $options.find('li.ui-multiselect-optgroup-label a.ui-multiselect-all').css("color", "#FF9000") : $options.find('li.ui-multiselect-optgroup-label a.ui-multiselect-all').css("color", "white") ;
o[ checkAll ? 'onCheckAll' : 'onUncheckAll']['call'](this);
}
e.preventDefault();
});
}
var updateSelected = function(){
var $inputs = $labels.find('input'),
$checked = $inputs.filter(':checked'),
$tot = $inputs.length,
value = '',
numChecked = $checked.length;
if(numChecked === 0){
value = o.noneSelectedText;
} else if($tot == numChecked ) {
@@ -155,11 +155,11 @@
value = o.selectedText.replace('#', numChecked).replace('#', $inputs.length);
}
}
$select.find('input').val(value);
return value;
};
// the select box events
$select.bind({
click: function(){
@@ -194,12 +194,12 @@
$(this).removeClass('ui-state-focus');
}
});
// bind custom events to the options div
$options.bind({
'close': function(e, others){
others = others || false;
// hides all other options but the one clicked
if(others === true){
$('div.ui-multiselect-options')
@@ -208,7 +208,7 @@
.prev('a.ui-multiselect')
.removeClass('ui-state-active')
.trigger('mouseout');
// hides the clicked options
} else {
$select.removeClass('ui-state-active').trigger('mouseout');
@@ -216,25 +216,25 @@
}
},
'open': function(e, closeOthers){
// bail if this widget is disabled
if($select.hasClass('ui-state-disabled')){
return;
}
// use position() if inside ui-widget-content, because offset() won't cut it.
var offset = $select.position(),
$container = $options.find('ul:last'),
$container = $options.find('ul:last'),
top, width;
// calling select is active
$select.addClass('ui-state-active');
// hide all other options
if(closeOthers || typeof closeOthers === 'undefined'){
$options.trigger('close', [true]);
}
// calculate positioning
if(o.position === 'middle'){
top = ( offset.top+($select.height()/2)-($options.outerHeight()/2) );
@@ -243,58 +243,58 @@
} else {
top = (offset.top+$select.outerHeight());
}
// calculate the width of the options menu
width = $select.width()-parseInt($options.css('padding-left'),10)-parseInt($options.css('padding-right'),10);
// select the first option
$labels.filter('label:first').trigger('mouseenter').trigger('focus');
// show the options div + position it
$options.css({
$options.css({
position: 'absolute',
top: top+'px',
left: offset.left+'px',
width: width+'px'
}).show();
// set the scroll of the checkbox container
$container.scrollTop(0);
// set the height of the checkbox container
if(o.maxHeight){
$container.css('height', o.maxHeight);
}
o.onOpen.call($options[0]);
},
'toggle': function(){
$options.trigger( $(this).is(':hidden') ? 'open' : 'close' );
},
'traverse': function(e, start, keycode){
var $start = $(start),
var $start = $(start),
moveToLast = (keycode === 38 || keycode === 37) ? true : false,
// select the first li that isn't an optgroup label / disabled
$next = $start.parent()[moveToLast ? 'prevAll' : 'nextAll']('li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)')[ moveToLast ? 'last' : 'first']();
// if at the first/last element
if(!$next.length){
var $container = $options.find("ul:last");
// move to the first/last
$options.find('label')[ moveToLast ? 'last' : 'first' ]().trigger('mouseover');
// set scroll position
$container.scrollTop( moveToLast ? $container.height() : 0 );
} else {
$next.find('label').trigger('mouseenter');
}
},
'toggleChecked': function(e, flag, group){
var $inputs = (group && group.length) ? group : $labels.find('input');
$inputs.not(':disabled').attr('checked', (flag ? 'checked' : ''));
$inputs.not(':disabled').attr('checked', flag);
updateSelected();
}
})
@@ -309,19 +309,19 @@
{
$(this).css("color", "rgb(255,255,255)");
}
var $checkboxes = $(this).parent().nextUntil('li.ui-multiselect-optgroup-label').find('input');
$options.trigger('toggleChecked', [ ($checkboxes.filter(':checked').length === $checkboxes.length) ? false : true, $checkboxes]);Y
$options.trigger('toggleChecked', [ ($checkboxes.filter(':checked').length === $checkboxes.length) ? false : true, $checkboxes]);
o.onOptgroupToggle.call(this, $checkboxes.get());
e.preventDefault();
});
$options.find('li.ui-multiselect-optgroup-label a.ui-multiselect-toggleclose')
.click(function(e){
var $label = $(this).parent().nextUntil('li.ui-multiselect-optgroup-label').find('label');
// optgroup label toggle support
// optgroup label toggle support
$label.toggle();
e.preventDefault();
});
@@ -343,7 +343,7 @@
/**************************************************************************/
// labels/checkbox events
$labels.bind({
/*mouseenter: function(){
@@ -355,14 +355,14 @@
case 27: // esc
$options.trigger('close');
break;
case 38: // up
case 40: // down
case 37: // left
case 39: // right
$options.trigger('traverse', [this, e.keyCode]);
break;
case 13: // enter
e.preventDefault();
$(this).click();
@@ -378,23 +378,23 @@
// remove the original input element
$original.remove();
// apply bgiframe if available
if($.fn.bgiframe){
$options.bgiframe();
}
// open by default?
if(o.state === 'open'){
$options.trigger('open', [false]);
}
// update the number of selected elements when the page initially loads, and use that as the defaultValue. necessary for form resets when options are pre-selected.
$select.find('input')[0].defaultValue = updateSelected();
return $select;
};
// close each select when clicking on any other element/anywhere else on the page
$(document).bind('click', function(e){
var $target = $(e.target);
@@ -420,7 +420,7 @@
fadeSpeed: 200,
disabled: false,
state: 'closed',
multiple: true,
multiple: true,
onCheck: function(){}, /* when an individual checkbox is clicked */
onOpen: function(){}, /* when the select menu is opened */
onCheckAll: function(){}, /* when the check all link is clicked */