From 07fe0204898ae0b66ea4338c236aa05cd630cc1e Mon Sep 17 00:00:00 2001
From: Steve James <4stevejames@gmail.com>
Date: Thu, 8 Feb 2018 17:47:08 -0800
Subject: [PATCH 1/5] Max selected and misc
1. Fix/finish maxSelected functionality.
2. Fix related test.
3. Fix namespace period issue.
4. Add new event triggers requested by open PR.
---
src/jquery.multiselect.js | 72 +++++++++++++++++++++++++++------------
tests/unit/options.js | 8 ++---
2 files changed, 55 insertions(+), 25 deletions(-)
diff --git a/src/jquery.multiselect.js b/src/jquery.multiselect.js
index c389bad..63fe473 100644
--- a/src/jquery.multiselect.js
+++ b/src/jquery.multiselect.js
@@ -46,7 +46,7 @@
noneSelectedText: 'Select options', // (str | null) The text to show in the button where nothing is selected. Set to null to use the native select's placeholder text.
selectedText: '# of # selected', // (str) A "template" that indicates how to show the count of selections in the button. The "#'s" are replaced by the selection count & option count.
selectedList: 0, // (int) The actual list selections will be shown in the button when the count of selections is <= than this number.
- selectedMax: null, // (int | function) If selected count > selectedMax or if function returns 1, then message is displayed, and new selection is undone.
+ maxSelected: null, // (int | function) If selected count > maxSelected or if function returns 1, then message is displayed, and new selection is undone.
show: null, // (array) An array containing menu opening effects.
hide: null, // (array) An array containing menu closing effects.
autoOpen: false, // (true | false) If true, then the menu will be opening immediately after initialization.
@@ -122,7 +122,7 @@
// create a unique namespace for events that the widget
// factory cannot unbind automatically. Use eventNamespace if on
// jQuery UI 1.9+, and otherwise fallback to a custom string.
- this._namespaceID = this.eventNamespace || ('multiselect' + multiselectID);
+ this._namespaceID = this.eventNamespace.slice(1) || ('multiselect' + multiselectID);
// bump unique ID after assigning it to the widget instance
this.multiselectID = multiselectID++;
@@ -593,11 +593,10 @@
var $tags = $element.find('option');
var isMultiple = $element[0].multiple;
var $allInputs = self.$inputs;
- var inputCount = $allInputs.length;
var numChecked = $allInputs.filter(":checked").length;
var options = self.options;
var optionText = $input.parent().find("span")[options.htmlOptionText ? 'html' : 'text']();
- var selectedMax = options.selectedMax;
+ var maxSelected = options.maxSelected;
// bail if this input is disabled or the event is cancelled
if (input.disabled || self._trigger('click', e, { value: val, text: optionText, checked: checked }) === false) {
@@ -605,18 +604,10 @@
return;
}
- if ( selectedMax && checked
- && ( typeof selectedMax === 'function' ? !!selectedMax.call(input, $allInputs) : numChecked > selectedMax ) ) {
- var saveText = options.selectedText;
-
- // The following warning is shown in the button and then cleared after a second.
- options.selectedText = "
");
+ }
+ },
+
+ /**
+ * Flashes a message in the button caption for 1 second.
+ * Useful for very short warning messages to the user.
+ * @param {string} HTML message to show in the button.
+ */
+ buttonMessage: function(message) {
+ var self = this;
+ self.$buttonlabel.html(message);
+ setTimeout( function() {
+ self.update();
+ }, 1000 );
},
/**
@@ -1347,6 +1370,13 @@
return this.$menu;
},
+ /**
+ * @returns {string} namespaceID for use with external event handlers.
+ */
+ getNamespaceID: function() {
+ return this._namespaceID;
+ },
+
/**
* @returns {object} jQuery object for button
*/
@@ -1491,7 +1521,7 @@
break;
case 'selectedText':
case 'selectedList':
- case 'selectedMax':
+ case 'maxSelected':
case 'noneSelectedText':
case 'selectedListSeparator':
this.options[key] = value; // these all need to update immediately for the update() call
diff --git a/tests/unit/options.js b/tests/unit/options.js
index cb2889f..cff7899 100644
--- a/tests/unit/options.js
+++ b/tests/unit/options.js
@@ -82,11 +82,11 @@
el.multiselect("destroy").remove();
});
- QUnit.test("selectedMax", function (assert) {
+ QUnit.test("maxSelected", function (assert) {
var html = '';
el = $(html).appendTo("body").multiselect({
- selectedMax: 2
+ maxSelected: 2
});
var checkboxes = el.multiselect("widget").find(":checkbox");
@@ -94,7 +94,7 @@
checkboxes.eq(1).trigger('click');
checkboxes.eq(2).trigger('click');
- assert.equal(menu().find("input").filter(":checked").length, 2, 'after clicking each checkbox, count of checked restored to selectedMax of 2');
+ assert.equal(menu().find("input").filter(":checked").length, 2, 'after clicking each checkbox, count of checked restored to maxSelected of 2');
el.multiselect("destroy").remove();
});
@@ -202,7 +202,7 @@
width = "3in";
el.multiselect("option", "menuWidth", width).multiselect('refresh');
assert.equal(menu().parent().find(".ui-multiselect-menu").outerWidth(), 3 * 96.0, 'menuWidth supports strings suffixed with "in" unit as well as integer "px" values');
-
+
el.multiselect("destroy");
});
From 18264668dc235d9b0c0ccc75e40305395f0eedf2 Mon Sep 17 00:00:00 2001
From: Steve James <4stevejames@gmail.com>
Date: Thu, 8 Feb 2018 18:52:41 -0800
Subject: [PATCH 2/5] Requested Changes
---
src/jquery.multiselect.js | 30 +++++++++++++-----------------
1 file changed, 13 insertions(+), 17 deletions(-)
diff --git a/src/jquery.multiselect.js b/src/jquery.multiselect.js
index 63fe473..9df86b0 100644
--- a/src/jquery.multiselect.js
+++ b/src/jquery.multiselect.js
@@ -104,7 +104,7 @@
var options = this.options;
var classes = options.classes;
var headerOn = options.header;
- var checkAllText = options.checkAllText;
+ var checkAllText = options.maxSelected ? null : options.checkAllText;
// Do an extend here to address icons missing from options.iconSet--missing icons default to those in defaultIcons.
var iconSet = $.extend({}, defaultIcons, options.iconSet || {});
var uncheckAllText = options.uncheckAllText;
@@ -119,10 +119,9 @@
this.speed = $.fx.speeds._default;
this._isOpen = false;
- // create a unique namespace for events that the widget
- // factory cannot unbind automatically. Use eventNamespace if on
- // jQuery UI 1.9+, and otherwise fallback to a custom string.
- this._namespaceID = this.eventNamespace.slice(1) || ('multiselect' + multiselectID);
+ // Create a unique namespace for events that the widget
+ // factory cannot unbind automatically.
+ this._namespaceID = this.eventNamespace.slice(1);
// bump unique ID after assigning it to the widget instance
this.multiselectID = multiselectID++;
@@ -1272,15 +1271,8 @@
checkAll: function(e) {
this._trigger('beforeCheckAll');
-
- var maxSelected = this.options.maxSelected;
- if (maxSelected === null || maxSelected > this.$inputs.length) {
- this._toggleChecked(true);
- this._trigger('checkAll');
- }
- else {
- this.buttonMessage("
Check All Not Permitted.
");
- }
+ this._toggleChecked(true);
+ this._trigger('checkAll');
},
uncheckAll: function() {
@@ -1291,7 +1283,7 @@
// Forces the underlying single-select to have no options selected.
this.element[0].selectedIndex = -1;
}
-
+
this._trigger('uncheckAll');
},
@@ -1496,12 +1488,16 @@
case 'checkAllText':
case 'uncheckAllText':
case 'flipAllText':
- $menu.find('a.ui-multiselect-' + {'checkAllText': 'all', 'uncheckAllText': 'none', 'flipAllText': 'flip'}[key] + ' span').eq(-1).text(value); // eq(-1) finds the last span
+ if (key !== 'checkAllText' || !this.options.maxSelected) {
+ $menu.find('a.ui-multiselect-' + {'checkAllText': 'all', 'uncheckAllText': 'none', 'flipAllText': 'flip'}[key] + ' span').eq(-1).text(value); // eq(-1) finds the last span
+ }
break;
case 'checkAllIcon':
case 'uncheckAllIcon':
case 'flipAllIcon':
- $menu.find('a.ui-multiselect-' + {'checkAllIcon': 'all', 'uncheckAllIcon': 'none', 'flipAllIcon': 'flip'}[key] + ' span').eq(0).replaceWith(value); // eq(0) finds the first span
+ if (key !== 'checkAllIcon' || !this.options.maxSelected) {
+ $menu.find('a.ui-multiselect-' + {'checkAllIcon': 'all', 'uncheckAllIcon': 'none', 'flipAllIcon': 'flip'}[key] + ' span').eq(0).replaceWith(value); // eq(0) finds the first span
+ }
break;
case 'openIcon':
$menu.find('span.ui-multiselect-open').html(value);
From e21945d52ef02effba72dad557797b468ed498a5 Mon Sep 17 00:00:00 2001
From: Steve James <4stevejames@gmail.com>
Date: Thu, 8 Feb 2018 19:20:53 -0800
Subject: [PATCH 3/5] Fix oversight
---
src/jquery.multiselect.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/jquery.multiselect.js b/src/jquery.multiselect.js
index 9df86b0..aa5fc9d 100644
--- a/src/jquery.multiselect.js
+++ b/src/jquery.multiselect.js
@@ -46,7 +46,7 @@
noneSelectedText: 'Select options', // (str | null) The text to show in the button where nothing is selected. Set to null to use the native select's placeholder text.
selectedText: '# of # selected', // (str) A "template" that indicates how to show the count of selections in the button. The "#'s" are replaced by the selection count & option count.
selectedList: 0, // (int) The actual list selections will be shown in the button when the count of selections is <= than this number.
- maxSelected: null, // (int | function) If selected count > maxSelected or if function returns 1, then message is displayed, and new selection is undone.
+ maxSelected: null, // (int | null) If selected count > maxSelected, then message is displayed, and new selection is undone.
show: null, // (array) An array containing menu opening effects.
hide: null, // (array) An array containing menu closing effects.
autoOpen: false, // (true | false) If true, then the menu will be opening immediately after initialization.
From e7d4b1e370cb6ef394536675cb22d5dc79f4132c Mon Sep 17 00:00:00 2001
From: Steve James <4stevejames@gmail.com>
Date: Fri, 9 Feb 2018 00:22:42 -0800
Subject: [PATCH 4/5] Clicking optgroup w/ maxSelected in use
---
src/jquery.multiselect.js | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/jquery.multiselect.js b/src/jquery.multiselect.js
index aa5fc9d..e32eb86 100644
--- a/src/jquery.multiselect.js
+++ b/src/jquery.multiselect.js
@@ -510,6 +510,12 @@
var $inputs = $this.next('ul').find('input').filter(':visible:not(:disabled)');
var nodes = $inputs.get();
var label = this.textContent;
+
+ // if maxSelected is in use, cannot exceed it
+ var maxSelected = this.options.maxSelected;
+ if (maxSelected && (this.$inputs.filter(':checked').length + $inputs.length > maxSelected) ) {
+ return;
+ }
// trigger before callback and bail if the return is false
if (self._trigger('beforeoptgrouptoggle', e, { inputs:nodes, label:label }) === false) {
From d682b5ba0041565d40c3c5e223e8d46f54cf0910 Mon Sep 17 00:00:00 2001
From: Steve James <4stevejames@gmail.com>
Date: Sat, 10 Feb 2018 16:15:50 -0800
Subject: [PATCH 5/5] Fixed optgroups maxSelected checking
---
src/jquery.multiselect.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/jquery.multiselect.js b/src/jquery.multiselect.js
index e32eb86..cf7a7b3 100644
--- a/src/jquery.multiselect.js
+++ b/src/jquery.multiselect.js
@@ -512,8 +512,8 @@
var label = this.textContent;
// if maxSelected is in use, cannot exceed it
- var maxSelected = this.options.maxSelected;
- if (maxSelected && (this.$inputs.filter(':checked').length + $inputs.length > maxSelected) ) {
+ var maxSelected = self.options.maxSelected;
+ if (maxSelected && (self.$inputs.filter(':checked').length + $inputs.length > maxSelected) ) {
return;
}