From 5c98d90b9e6f5d9c71100a1018f6251859be6fed Mon Sep 17 00:00:00 2001 From: Josh Caspersz <4262521+joshcaspersz@users.noreply.github.com> Date: Wed, 15 May 2019 13:26:07 +1000 Subject: [PATCH 1/5] Optimised getSelected used in updateButtonText --- dist/js/bootstrap-multiselect.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/dist/js/bootstrap-multiselect.js b/dist/js/bootstrap-multiselect.js index 2a028b77..14811059 100644 --- a/dist/js/bootstrap-multiselect.js +++ b/dist/js/bootstrap-multiselect.js @@ -1684,7 +1684,23 @@ * @returns {jQUery} */ getSelected: function() { - return $('option', this.$select).filter(":selected"); + var select = this.$select[0]; + if (select.selectedOptions !== undefined) { + return $(select.selectedOptions); + } + + // selectedIndex is the index of the first option selected or -1 if nothing is selected + if (select.selectedIndex == -1) { + return []; + } + + var selectedOptions = []; + for (var i = select.selectedIndex; i < select.length; i++) { + if (select.options[i].selected){ + selectedOptions.push(select.options[i]); + } + } + return $(selectedOptions); }, /** From f43f0c142ab93ae9a417efe36885e1ae631a2366 Mon Sep 17 00:00:00 2001 From: Josh Caspersz <4262521+joshcaspersz@users.noreply.github.com> Date: Wed, 15 May 2019 13:26:36 +1000 Subject: [PATCH 2/5] Optimised buildDropdownOptions --- dist/js/bootstrap-multiselect.js | 59 +++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/dist/js/bootstrap-multiselect.js b/dist/js/bootstrap-multiselect.js index 14811059..ede0d1a2 100644 --- a/dist/js/bootstrap-multiselect.js +++ b/dist/js/bootstrap-multiselect.js @@ -545,33 +545,33 @@ */ buildDropdownOptions: function() { + var listOptions = []; this.$select.children().each($.proxy(function(index, element) { - - var $element = $(element); + // Support optgroups and options without a group simultaneously. - var tag = $element.prop('tagName') - .toLowerCase(); + var tag = element.tagName; - if ($element.prop('value') === this.options.selectAllValue) { + if (element.value === this.options.selectAllValue) { return; } - if (tag === 'optgroup') { - this.createOptgroup(element); + if (tag === 'OPTGROUP') { + listOptions.push.apply(listOptions, this.createOptgroup(element)); } - else if (tag === 'option') { - - if ($element.data('role') === 'divider') { - this.createDivider(); - } - else { - this.createOptionValue(element); - } + else if (tag === 'OPTION') { + // TODO (if dividers required): Enable and add divider to listOptions + //if (element.getAttribute('data-role') === 'divider') { + // this.createDivider(); + //} + //else { + listOptions.push(this.createOptionValueString(element)); + //} } // Other illegal tags will be ignored. }, this)); + this.$ul[0].innerHTML += listOptions.join(''); // Bind the change event on the dropdown elements. $(this.$ul).off('change', 'li:not(.multiselect-group) input[type="checkbox"], li:not(.multiselect-group) input[type="radio"]'); @@ -927,6 +927,28 @@ } }, + /** + * Return an option string using the given select option. + * + * @param {jQuery} element + */ + createOptionValueString: function(element) { + var value = element.value; + var title = element.text; + var selected = element.selected; + var inputType = this.options.multiple ? "checkbox" : "radio"; + + var checkbox = '' : '">'); + var label = '"; + + var liClass = (selected && this.options.selectedClass ? ' class="' + this.options.selectedClass + '"' : ''); + var li = '' + label + ''; + + // TODO: Implement: element.disabled check + + return li; + }, + /** * Creates a divider using the given select option. * @@ -969,11 +991,14 @@ $li.addClass('disabled'); } - this.$ul.append($li); + // TODO: Optimize above + var optGroupOptions = [$li[0].outerHTML]; $("option", group).each($.proxy(function($, group) { - this.createOptionValue(group); + optGroupOptions.push(this.createOptionValueString(group)); }, this)) + + return optGroupOptions; }, /** From 332609a39d08e49fa6bea3c9247eae94bf0d0fcf Mon Sep 17 00:00:00 2001 From: Josh Caspersz <4262521+joshcaspersz@users.noreply.github.com> Date: Wed, 15 May 2019 13:29:00 +1000 Subject: [PATCH 3/5] Optimised dropdown show/hide --- dist/less/bootstrap-multiselect.less | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dist/less/bootstrap-multiselect.less b/dist/less/bootstrap-multiselect.less index deec16fe..f435ce92 100644 --- a/dist/less/bootstrap-multiselect.less +++ b/dist/less/bootstrap-multiselect.less @@ -133,3 +133,14 @@ span.multiselect-native-select select{ } } } + +.multiselect-container.dropdown-menu { + display: block; + visibility: hidden; + height: 0; +} + +.open > .multiselect-container.dropdown-menu { + visibility: inherit; + height: auto; +} \ No newline at end of file From ce7e3039dba9d54cc2c241d0ddc8e32d28993b98 Mon Sep 17 00:00:00 2001 From: Josh Caspersz <4262521+joshcaspersz@users.noreply.github.com> Date: Wed, 15 May 2019 13:35:29 +1000 Subject: [PATCH 4/5] Optimised search filter --- dist/js/bootstrap-multiselect.js | 2 -- dist/less/bootstrap-multiselect.less | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dist/js/bootstrap-multiselect.js b/dist/js/bootstrap-multiselect.js index ede0d1a2..cd8a5511 100644 --- a/dist/js/bootstrap-multiselect.js +++ b/dist/js/bootstrap-multiselect.js @@ -1171,11 +1171,9 @@ // Toggle current element (group or group item) according to showElement boolean. if(!showElement){ - $(element).css('display', 'none'); $(element).addClass('multiselect-filter-hidden'); } if(showElement){ - $(element).css('display', 'block'); $(element).removeClass('multiselect-filter-hidden'); } diff --git a/dist/less/bootstrap-multiselect.less b/dist/less/bootstrap-multiselect.less index f435ce92..e225ca17 100644 --- a/dist/less/bootstrap-multiselect.less +++ b/dist/less/bootstrap-multiselect.less @@ -76,11 +76,16 @@ span.multiselect-native-select select{ > li { padding: 0; + overflow: hidden; > a.multiselect-all label { font-weight: bold; } + &.multiselect-filter-hidden { + height: 0; + } + &.multiselect-group label { margin: 0; padding: 3px 20px 3px 20px; From 71e535c3dc5e998cfc04be0546c28f817bdb695f Mon Sep 17 00:00:00 2001 From: Josh Caspersz <4262521+joshcaspersz@users.noreply.github.com> Date: Wed, 15 May 2019 15:01:28 +1000 Subject: [PATCH 5/5] createOptionValueString XSS fix --- dist/js/bootstrap-multiselect.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dist/js/bootstrap-multiselect.js b/dist/js/bootstrap-multiselect.js index cd8a5511..e5446b1e 100644 --- a/dist/js/bootstrap-multiselect.js +++ b/dist/js/bootstrap-multiselect.js @@ -933,8 +933,8 @@ * @param {jQuery} element */ createOptionValueString: function(element) { - var value = element.value; - var title = element.text; + var value = this.escapeHtml(element.value); + var title = this.escapeHtml(element.text); var selected = element.selected; var inputType = this.options.multiple ? "checkbox" : "radio"; @@ -949,6 +949,26 @@ return li; }, + /** + * Escapes a string for use in HTML + * + * @param {String} value + * @returns {String} + */ + escapeHtml: function(s) { + var ESC_MAP = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + return s.replace(/[&<>'"]/g, function(c) { + return ESC_MAP[c]; + }); + }, + /** * Creates a divider using the given select option. *