diff --git a/js/jquery.multi-select.js b/js/jquery.multi-select.js index 2d70e38..db2f78c 100644 --- a/js/jquery.multi-select.js +++ b/js/jquery.multi-select.js @@ -41,9 +41,22 @@ ms.attr('id', ms.attr('id') ? ms.attr('id') : Math.ceil(Math.random()*1000)+'multiselect'); this.$container.attr('id', 'ms-'+ms.attr('id')); this.$container.addClass(that.options.cssClass); - ms.find('option').each(function(){ - that.generateLisFromOption(this); + var handler = { + selectableUl: [], + selectionUl: [], + selectableOptgroups: {}, + selectionOptgroups: {} + }; + ms.find('option').each(function(index){ + that.generateListFromOption(this, index, null, handler); }); + that.$selectableUl.prepend(handler.selectableUl.join('')); + that.$selectionUl.prepend(handler.selectionUl.join('')); + for(var optgroupId in handler.selectableOptgroups){ + that.$selectableUl.find('#optgroup-selectable-'+optgroupId+' ul.ms-optgroup').append(handler.selectableOptgroups[optgroupId].join('')); + that.$selectionUl.find('#optgroup-selection-'+optgroupId+' ul.ms-optgroup').append(handler.selectionOptgroups[optgroupId].join('')); + } + handler = null; this.$selectionUl.find('.ms-optgroup-label').hide(); @@ -95,7 +108,7 @@ } }, - 'generateLisFromOption' : function(option, index, $container){ + 'generateListFromOption' : function(option, index, $container, $handler){ var that = this, ms = that.$element, attributes = "", @@ -108,35 +121,25 @@ attributes += attr.name+'="'+attr.value+'" '; } } - var selectableLi = $('
  • '+that.escapeHTML($option.text())+'
  • '), - selectedLi = selectableLi.clone(), - value = $option.val(), - elementId = that.sanitize(value); - - selectableLi - .data('ms-value', value) - .addClass('ms-elem-selectable') - .attr('id', elementId+'-selectable'); - - selectedLi - .data('ms-value', value) - .addClass('ms-elem-selection') - .attr('id', elementId+'-selection') - .hide(); - - if ($option.attr('disabled') || ms.attr('disabled')){ - selectedLi.addClass(that.options.disabledClass); - selectableLi.addClass(that.options.disabledClass); - } + + var value = $option.val(), + elementId = that.sanitize(value), + elementText = that.escapeHTML($option.text()), + elementDisabled = $option.prop('disabled') || ms.prop('disabled'), + elementSelected = $option.prop('selected') ? true : false, + selectableLi = '
  • '+elementText+'
  • ', + selectedLi = ''; + var $optgroup = $option.parent('optgroup'); + var hasHandler = typeof $handler === 'object'; if ($optgroup.length > 0){ var optgroupLabel = $optgroup.attr('label'), optgroupId = that.sanitize(optgroupLabel), $selectableOptgroup = that.$selectableUl.find('#optgroup-selectable-'+optgroupId), $selectionOptgroup = that.$selectionUl.find('#optgroup-selection-'+optgroupId); - + if ($selectableOptgroup.length === 0){ var optgroupContainerTpl = '
  • ', optgroupTpl = ''; @@ -159,15 +162,31 @@ } that.$selectableUl.append($selectableOptgroup); that.$selectionUl.append($selectionOptgroup); + + if(hasHandler){ + $handler.selectableOptgroups[optgroupId] = []; + $handler.selectionOptgroups[optgroupId] = []; + } + } + if(hasHandler){ + index = index == undefined ? $handler.selectableOptgroups[optgroupId].length : index; + $handler.selectableOptgroups[optgroupId].splice(index, 0, selectableLi); + $handler.selectionOptgroups[optgroupId].splice(index, 0, selectedLi); + }else{ + index = index == undefined ? $selectableOptgroup.find('ul').children().length : index + 1; + $(selectableLi).insertAt(index, $selectableOptgroup.children()); + $(selectedLi).insertAt(index, $selectionOptgroup.children()); } - index = index === undefined ? $selectableOptgroup.find('ul').children().length : index + 1; - selectableLi.insertAt(index, $selectableOptgroup.children()); - selectedLi.insertAt(index, $selectionOptgroup.children()); } else { - index = index === undefined ? that.$selectableUl.children().length : index; - - selectableLi.insertAt(index, that.$selectableUl); - selectedLi.insertAt(index, that.$selectionUl); + if(hasHandler){ + index = index == undefined ? $handler.selectableUl.length : index; + $handler.selectableUl.splice(index, 0, selectableLi); + $handler.selectionUl.splice(index, 0, selectedLi); + }else{ + index = index == undefined ? that.$selectableUl.children().length : index; + $(selectableLi).insertAt(index, that.$selectableUl); + $(selectedLi).insertAt(index, that.$selectionUl); + } } }, @@ -193,7 +212,7 @@ } $option.insertAt(index, $container); - that.generateLisFromOption($option.get(0), index, option.nested); + that.generateListFromOption($option.get(0), index, option.nested); } }); }, @@ -297,7 +316,7 @@ } if ($nextElem.length > 0){ $nextElem.addClass('ms-hover'); - var scrollTo = $list.scrollTop() + $nextElem.position().top - + var scrollTo = $list.scrollTop() + $nextElem.position().top - containerHeight / 2 + elemHeight / 2; $list.scrollTop(scrollTo); @@ -354,18 +373,18 @@ }, 'select' : function(value, method){ - if (typeof value === 'string'){ value = [value]; } + if (typeof value === 'string' || typeof value === 'number'){ value = [''+value]; } var that = this, ms = this.$element, msIds = $.map(value, function(val){ return(that.sanitize(val)); }), - selectables = this.$selectableUl.find('#' + msIds.join('-selectable, #')+'-selectable').filter(':not(.'+that.options.disabledClass+')'), - selections = this.$selectionUl.find('#' + msIds.join('-selection, #') + '-selection').filter(':not(.'+that.options.disabledClass+')'), + selectables = this.$selectableUl.find('#ms-elem-' + msIds.join('-selectable, #ms-elem-')+'-selectable').filter(':not(.'+that.options.disabledClass+')'), + selections = this.$selectionUl.find('#ms-elem-' + msIds.join('-selection, #ms-elem-') + '-selection').filter(':not(.'+that.options.disabledClass+')'), options = ms.find('option:not(:disabled)').filter(function(){ return($.inArray(this.value, value) > -1); }); if (method === 'init'){ - selectables = this.$selectableUl.find('#' + msIds.join('-selectable, #')+'-selectable'), - selections = this.$selectionUl.find('#' + msIds.join('-selection, #') + '-selection'); + selectables = this.$selectableUl.find('#ms-elem-' + msIds.join('-selectable, #ms-elem-')+'-selectable'), + selections = this.$selectionUl.find('#ms-elem-' + msIds.join('-selection, #ms-elem-') + '-selection'); } if (selectables.length > 0){ @@ -410,13 +429,13 @@ }, 'deselect' : function(value){ - if (typeof value === 'string'){ value = [value]; } + if (typeof value === 'string' || typeof value === 'number'){ value = [''+value]; } var that = this, ms = this.$element, msIds = $.map(value, function(val){ return(that.sanitize(val)); }), - selectables = this.$selectableUl.find('#' + msIds.join('-selectable, #')+'-selectable'), - selections = this.$selectionUl.find('#' + msIds.join('-selection, #')+'-selection').filter('.ms-selected').filter(':not(.'+that.options.disabledClass+')'), + selectables = this.$selectableUl.find('#ms-elem-' + msIds.join('-selectable, #ms-elem-')+'-selectable'), + selections = this.$selectionUl.find('#ms-elem-' + msIds.join('-selection, #ms-elem-')+'-selection').filter('.ms-selected').filter(':not(.'+that.options.disabledClass+')'), options = ms.find('option').filter(function(){ return($.inArray(this.value, value) > -1); }); if (selections.length > 0){ diff --git a/test/spec/multiSelectSpec.js b/test/spec/multiSelectSpec.js index c27226e..e83f388 100644 --- a/test/spec/multiSelectSpec.js +++ b/test/spec/multiSelectSpec.js @@ -55,7 +55,7 @@ describe("multiSelect", function() { it ('should select the pre-selected options', function(){ $.each(selectedValues, function(index, value){ - expect($('.ms-selectable ul.ms-list li#'+sanitize(value)+'-selectable')).toBe('.ms-selected'); + expect($('.ms-selectable ul.ms-list li#ms-elem-'+sanitize(value)+'-selectable')).toBe('.ms-selected'); }); expect($('.ms-selectable ul.ms-list li.ms-selected').length).toEqual(2); }); @@ -75,7 +75,7 @@ describe("multiSelect", function() { it ('should select the disabled pre-selected options', function(){ $.each(selectedValues, function(index, value){ - expect($('.ms-selectable ul.ms-list li#'+sanitize(value)+'-selectable')).toBe('.ms-selected'); + expect($('.ms-selectable ul.ms-list li#ms-elem-'+sanitize(value)+'-selectable')).toBe('.ms-selected'); }); expect($('.ms-selectable ul.ms-list li.ms-selected').length).toEqual(3); }); @@ -95,7 +95,7 @@ describe("multiSelect", function() { it ('should not select the disabled non-selected options', function(){ $.each(selectedValues, function(index, value){ - expect($('.ms-selectable ul.ms-list li#'+sanitize(value)+'-selectable')).toBe('.ms-selected'); + expect($('.ms-selectable ul.ms-list li#ms-elem-'+sanitize(value)+'-selectable')).toBe('.ms-selected'); }); expect($('.ms-selectable ul.ms-list li.ms-selected').length).toEqual(2); }); @@ -226,7 +226,7 @@ describe("multiSelect", function() { }); it('should show the associated selected item', function(){ - expect($('#'+sanitize(value)+'-selection')).toBe(':visible'); + expect($('#ms-elem-'+sanitize(value)+'-selection')).toBe(':visible'); }); it('should trigger the original select change event', function(){ @@ -264,7 +264,7 @@ describe("multiSelect", function() { }); it('should not show the associated selected item', function(){ - expect($('#'+sanitize(value)+'-selection')).not.toBe(':visible'); + expect($('#ms-elem-'+sanitize(value)+'-selection')).not.toBe(':visible'); }); it('should not trigger the original select change event', function(){ @@ -328,7 +328,7 @@ describe("multiSelect", function() { }); it('should show associated selectable item', function(){ - expect($('#'+sanitize(value)+'-selectable')).toBe(':visible'); + expect($('#ms-elem-'+sanitize(value)+'-selectable')).toBe(':visible'); }); it('should remove the .ms-selected class to the corresponding selectable item', function(){