Skip to content

Commit 5cac8be

Browse files
committed
Fix #1665 : searchable-option-list bulk operations are slow
1 parent d0d685e commit 5cac8be

File tree

2 files changed

+78
-43
lines changed

2 files changed

+78
-43
lines changed

opengrok-indexer/src/main/java/org/opengrok/indexer/web/Scripts.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
/*
2121
* Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved.
22-
* Portions Copyright (c) 2017, Chris Fraire <cfraire@me.com>.
22+
* Portions Copyright (c) 2017, 2020, Chris Fraire <cfraire@me.com>.
2323
*/
2424
package org.opengrok.indexer.web;
2525

@@ -111,7 +111,7 @@ public String toHtml() {
111111
SCRIPTS.put("jquery-tablesorter", new FileScript("js/jquery-tablesorter-2.26.6.min.js", 12));
112112
SCRIPTS.put("tablesorter-parsers", new FileScript("js/tablesorter-parsers-0.0.2.min.js", 13));
113113
SCRIPTS.put("tablesorter-parsers" + DEBUG_SUFFIX, new FileScript("js/tablesorter-parsers-0.0.2.js", 13));
114-
SCRIPTS.put("searchable-option-list", new FileScript("js/searchable-option-list-2.0.8.min.js", 14));
114+
SCRIPTS.put("searchable-option-list", new FileScript("js/searchable-option-list-2.0.9.min.js", 14));
115115
SCRIPTS.put("utils", new FileScript("js/utils-0.0.34.min.js", 15));
116116
SCRIPTS.put("utils" + DEBUG_SUFFIX, new FileScript("js/utils-0.0.34.js", 15));
117117
SCRIPTS.put("repos", new FileScript("js/repos-0.0.2.min.js", 20));

opengrok-web/src/main/webapp/js/searchable-option-list-2.0.8.js renamed to opengrok-web/src/main/webapp/js/searchable-option-list-2.0.9.js

Lines changed: 76 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* https://pbauerochse.github.io/searchable-option-list/
55
*
66
* Copyright 2015, Patrick Bauerochse
7+
* Portions Copyright (c) 2020, Chris Fraire <cfraire@me.com>.
78
*
89
* Licensed under the MIT license:
910
* http://www.opensource.org/licenses/MIT
@@ -149,6 +150,9 @@
149150

150151
// initialize the plugin
151152
init: function () {
153+
this.numSelected = 0;
154+
this.valMap = null;
155+
this.bulkMode = false;
152156
this.config = $.extend(true, {}, this.defaults, this.options, this.metadata);
153157

154158
var originalName = this._getNameAttribute(),
@@ -988,14 +992,22 @@
988992
// e.g. $('#myPreviousSelectWhichNowIsSol').val()
989993
if (this.$originalElement && this.$originalElement.prop('tagName').toLowerCase() === 'select') {
990994
var self = this;
991-
this.$originalElement.find('option').each(function (index, item) {
992-
var $currentOriginalOption = $(item);
993-
if ($currentOriginalOption.val() === $changeItem.val()) {
994-
$currentOriginalOption.prop('selected', $changeItem.prop('checked'));
995+
if (this.valMap == null) {
996+
this.$originalElement.find('option').each(function (index, item) {
997+
var $currentOriginalOption = $(item);
998+
if ($currentOriginalOption.val() === $changeItem.val()) {
999+
$currentOriginalOption.prop('selected', $changeItem.prop('checked'));
1000+
self.$originalElement.trigger('change');
1001+
return false; // stop the loop
1002+
}
1003+
});
1004+
} else {
1005+
var mappedVal = this.valMap.get($changeItem.val());
1006+
if (mappedVal) {
1007+
mappedVal.prop('selected', $changeItem.prop('checked'));
9951008
self.$originalElement.trigger('change');
996-
return;
9971009
}
998-
});
1010+
}
9991011
}
10001012

10011013
if ($changeItem.prop('checked')) {
@@ -1019,16 +1031,34 @@
10191031
}
10201032
},
10211033

1034+
_setXItemsSelected: function() {
1035+
if (this.config.maxShow !== 0 && this.numSelected > this.config.maxShow) {
1036+
var xItemsText = this.config.texts.itemsSelected.replace('{$a}',
1037+
this.numSelected - this.config.maxShow);
1038+
this.$xItemsSelected.html('<div class="sol-selected-display-item-text">' +
1039+
xItemsText + '<div>');
1040+
this.$showSelectionContainer.append(this.$xItemsSelected);
1041+
this.$xItemsSelected.show();
1042+
} else {
1043+
this.$xItemsSelected.hide();
1044+
}
1045+
},
1046+
10221047
_addSelectionDisplayItem: function ($changedItem) {
10231048
var solOptionItem = $changedItem.data('sol-item'),
10241049
$existingDisplayItem = solOptionItem.displaySelectionItem,
10251050
$displayItemText;
10261051

1027-
if (!$existingDisplayItem) {
1052+
this.numSelected = 1 + this.numSelected;
1053+
1054+
if (this.config.maxShow !== 0 && this.numSelected > this.config.maxShow) {
1055+
if (!this.bulkMode) {
1056+
this._setXItemsSelected();
1057+
}
1058+
} else {
10281059
/*
10291060
* Modified for OpenGrok in 2016, 2019.
10301061
*/
1031-
var selected = this.$showSelectionContainer.children('.sol-selected-display-item');
10321062
var label = solOptionItem.label;
10331063
if ($changedItem.data('messages-available')) {
10341064
label += ' <span class="';
@@ -1065,17 +1095,7 @@
10651095
})
10661096
.prependTo($existingDisplayItem);
10671097
}
1068-
/*
1069-
* Modified for OpenGrok in 2016.
1070-
*/
1071-
if (this.config.maxShow != 0 && selected.length + 1 > this.config.maxShow) {
1072-
var xitemstext = this.config.texts.itemsSelected.replace('{$a}', selected.length + 1 - this.config.maxShow);
1073-
this.$xItemsSelected.html('<div class="sol-selected-display-item-text">' + xitemstext + '<div>');
1074-
this.$showSelectionContainer.append(this.$xItemsSelected);
1075-
this.$xItemsSelected.show();
1076-
$existingDisplayItem.hide();
1077-
}
1078-
1098+
10791099
solOptionItem.displaySelectionItem = $existingDisplayItem;
10801100
}
10811101
},
@@ -1084,32 +1104,15 @@
10841104
var solOptionItem = $changedItem.data('sol-item'),
10851105
$myDisplayItem = solOptionItem.displaySelectionItem;
10861106

1087-
if ($myDisplayItem) {
1088-
/*
1089-
* Modified for OpenGrok in 2016.
1090-
*/
1091-
var selected = this.$showSelectionContainer.children('.sol-selected-display-item');
1092-
if (this.config.maxShow != 0 && selected.length - 1 > this.config.maxShow) {
1093-
var xitemstext = this.config.texts.itemsSelected.replace('{$a}', selected.length - 1 - this.config.maxShow);
1094-
this.$xItemsSelected.html('<div class="sol-selected-display-item-text">' + xitemstext + '<div>');
1095-
this.$showSelectionContainer.append(this.$xItemsSelected);
1096-
this.$xItemsSelected.show();
1097-
} else {
1098-
this.$xItemsSelected.hide();
1099-
}
1107+
this.numSelected = this.numSelected - 1;
11001108

1101-
if ($myDisplayItem.is(":visible")) {
1102-
$myDisplayItem
1103-
.siblings('.sol-selected-display-item')
1104-
.not(":visible")
1105-
.not(this.$xItemsSelected)
1106-
.first()
1107-
.show();
1108-
}
1109-
1109+
if ($myDisplayItem) {
11101110
$myDisplayItem.remove();
11111111
solOptionItem.displaySelectionItem = undefined;
11121112
}
1113+
if (!this.bulkMode) {
1114+
this._setXItemsSelected();
1115+
}
11131116
},
11141117

11151118
_setNoResultsItemVisible: function (visible) {
@@ -1130,6 +1133,17 @@
11301133
}
11311134
},
11321135

1136+
_buildValMap: function () {
1137+
if (this.$originalElement && this.$originalElement.prop('tagName').toLowerCase() === 'select') {
1138+
var self = this;
1139+
this.valMap = new Map();
1140+
this.$originalElement.find('option').each(function (index, item) {
1141+
var $currentOriginalOption = $(item);
1142+
self.valMap.set($currentOriginalOption.val(), $currentOriginalOption);
1143+
});
1144+
}
1145+
},
1146+
11331147
isOpen: function () {
11341148
return this.$container.hasClass('sol-active');
11351149
},
@@ -1184,6 +1198,9 @@
11841198
*/
11851199
selectAll: function (/* string or undefined */optgroup) {
11861200
if (this.config.multiple) {
1201+
this._buildValMap();
1202+
this.bulkMode = true;
1203+
11871204
var $changedInputs = !optgroup ? this.$selectionContainer
11881205
: this.$selectionContainer
11891206
.find(".sol-optiongroup-label")
@@ -1200,13 +1217,20 @@
12001217
if ($.isFunction(this.config.events.onChange)) {
12011218
this.config.events.onChange.call(this, this, $changedInputs);
12021219
}
1220+
1221+
this.bulkMode = false;
1222+
this.valMap = null;
1223+
this._setXItemsSelected();
12031224
}
12041225
},
12051226
/*
12061227
* Modified for OpenGrok in 2016, 2019.
12071228
*/
12081229
invert: function () {
12091230
if (this.config.multiple) {
1231+
this._buildValMap();
1232+
this.bulkMode = true;
1233+
12101234
var $closedInputs = this.$selectionContainer
12111235
.find('input[type="checkbox"][name=project]:not([disabled], :checked)')
12121236
var $openedInputs = this.$selectionContainer
@@ -1222,13 +1246,20 @@
12221246
if ($.isFunction(this.config.events.onChange)) {
12231247
this.config.events.onChange.call(this, this, $openedInputs.add($closedInputs));
12241248
}
1249+
1250+
this.bulkMode = false;
1251+
this.valMap = null;
1252+
this._setXItemsSelected();
12251253
}
12261254
},
12271255
/*
12281256
* Modified for OpenGrok in 2016.
12291257
*/
12301258
deselectAll: function ( /* string or undefined */ optgroup) {
12311259
if (this.config.multiple) {
1260+
this._buildValMap();
1261+
this.bulkMode = true;
1262+
12321263
var $changedInputs = !optgroup ? this.$selectionContainer
12331264
: this.$selectionContainer
12341265
.find(".sol-optiongroup-label")
@@ -1245,6 +1276,10 @@
12451276
if ($.isFunction(this.config.events.onChange)) {
12461277
this.config.events.onChange.call(this, this, $changedInputs);
12471278
}
1279+
1280+
this.bulkMode = false;
1281+
this.valMap = null;
1282+
this._setXItemsSelected();
12481283
}
12491284
},
12501285

0 commit comments

Comments
 (0)