diff --git a/lam/lib/html.inc b/lam/lib/html.inc
index 7bff8a1d..b29a1090 100644
--- a/lam/lib/html.inc
+++ b/lam/lib/html.inc
@@ -1219,6 +1219,8 @@ class htmlSelect extends htmlElement {
protected $tableRowsToHide = array();
/** list of enclosing table rows to show when checked */
protected $tableRowsToShow = array();
+ /** dynamic scrolling */
+ private $dynamicScrolling = false;
/**
* Constructor.
@@ -1349,6 +1351,10 @@ class htmlSelect extends htmlElement {
* @param array $elements list of options
*/
private function printOptionsHTML($elements) {
+ if ($this->dynamicScrolling) {
+ echo "\n";
+ return;
+ }
// sorting
if ($this->sortElements) {
if ($this->hasDescriptiveElements) {
@@ -1568,6 +1574,24 @@ class htmlSelect extends htmlElement {
return 'tr';
}
+ /**
+ * Enable dynamic scrolling. This limits the number of select options to 10000 by dynamically adding/removing options.
+ * This will not be enabled when optgroups are used or the option size is less than 10000.
+ */
+ public function enableDynamicScrolling() {
+ // not possible for optgroups and smaller option lists
+ if ((sizeof($this->elements) < 10000) || $this->containsOptgroups) {
+ return;
+ }
+ $this->dynamicScrolling = true;
+ $elementData = array();
+ foreach ($this->elements as $key => $value) {
+ $elementData[] = array('label' => $key, 'value' => $value);
+ }
+ $this->addDataAttribute('dynamic-options', json_encode($elementData));
+ $this->cssClasses[] = 'lam-dynamicOptions';
+ }
+
}
/**
diff --git a/lam/templates/lib/500_lam.js b/lam/templates/lib/500_lam.js
index f302cafb..50ca2932 100644
--- a/lam/templates/lib/500_lam.js
+++ b/lam/templates/lib/500_lam.js
@@ -1111,6 +1111,139 @@ window.lam.html.preventEnter = function() {
});
}
+window.lam.dynamicSelect = window.lam.dynamicSelect || {};
+
+/**
+ * Activates dynamic selection for all marked select fields.
+ */
+window.lam.dynamicSelect.activate = function() {
+ var dynamicSelects = jQuery('.lam-dynamicOptions');
+ dynamicSelects.each(function() {
+ var selectField = jQuery(this);
+ selectField.data('option-height', selectField.find("option").height());
+ selectField.data('select-height', selectField.height());
+ selectField.data('select-last-scroll-top', 0);
+ selectField.data('select-current-scroll', 0);
+ selectField.html('');
+ var options = selectField.data('dynamic-options');
+ var maxOptions = 3000;
+ var numOfOptionBeforeToLoadNextSet = 10;
+ var numberOfOptionsToLoad = 200;
+ for (var i = 0; i < maxOptions; i++) {
+ selectField.append(window.lam.dynamicSelect.createOption(options[i], i));
+ }
+ if (options.length > maxOptions) {
+ // activate scrolling logic only if enough options are set
+ selectField.scroll(function(event) {
+ window.lam.dynamicSelect.onScroll(selectField, event, maxOptions, numOfOptionBeforeToLoadNextSet, numberOfOptionsToLoad);
+ });
+ }
+ });
+}
+
+/**
+ * Creates an option field inside the select.
+ *
+ * @param data option data
+ * @param index index in list of all options
+ * @returns option
+ */
+window.lam.dynamicSelect.createOption = function(data, index) {
+ var newOption = jQuery(document.createElement("option"));
+ newOption.attr('value', data.value);
+ newOption.data('index', index);
+ newOption.text(data.label);
+ return newOption;
+}
+
+/**
+ * Onscroll event.
+ *
+ * @param selectField select field
+ * @param event event
+ * @param maxOptions maximum options to show
+ * @param numOfOptionBeforeToLoadNextSet number of options to reach before end of list
+ * @param numberOfOptionsToLoad number of options to add
+ */
+window.lam.dynamicSelect.onScroll = function(selectField, event, maxOptions, numOfOptionBeforeToLoadNextSet, numberOfOptionsToLoad) {
+ var scrollTop = selectField.scrollTop();
+ var totalHeight = selectField.find("option").length * selectField.data('option-height');
+ var lastScrollTop = selectField.data('select-last-scroll-top');
+ var selectBoxHeight = selectField.data('select-height');
+ var singleOptionHeight = selectField.data('option-height');
+ var currentScroll = scrollTop + selectBoxHeight;
+ selectField.data('select-current-scroll-top', scrollTop);
+ if ((scrollTop >= lastScrollTop)
+ && ((currentScroll + (numOfOptionBeforeToLoadNextSet * singleOptionHeight)) >= totalHeight)) {
+ window.lam.dynamicSelect.loadNextOptions(selectField, maxOptions, numberOfOptionsToLoad);
+ }
+ else if ((scrollTop <= lastScrollTop)
+ && ((scrollTop - (numOfOptionBeforeToLoadNextSet * singleOptionHeight)) <= 0)) {
+ window.lam.dynamicSelect.loadPreviousOptions(selectField, maxOptions, numberOfOptionsToLoad);
+ }
+ selectField.data('select-last-scroll-top', scrollTop);
+}
+
+/**
+ * Loads the next bunch of options at the end.
+ *
+ * @param selectField selct field
+ * @param maxOptions maximum options to show
+ * @param numberOfOptionsToLoad number of options to add
+ */
+window.lam.dynamicSelect.loadNextOptions = function(selectField, maxOptions, numberOfOptionsToLoad) {
+ var selectBoxHeight = selectField.data('select-height');
+ var singleOptionHeight = selectField.data('option-height');
+ var currentScrollPosition = selectField.data('select-current-scroll-top') + selectBoxHeight;
+ var options = selectField.data('dynamic-options');
+ var lastIndex = selectField.children().last().data('index');
+ for (var toAdd = 0; toAdd < numberOfOptionsToLoad; toAdd++) {
+ var addPos = lastIndex + 1 + toAdd;
+ if (options[addPos] === undefined) {
+ break;
+ }
+ selectField.append(window.lam.dynamicSelect.createOption(options[addPos], addPos));
+ }
+ var numberOfOptions = selectField.children().length;
+ var toRemove = numberOfOptions - maxOptions;
+ if (toRemove > 0) {
+ selectField.children().slice(0, toRemove).remove();
+ }
+ else {
+ toRemove = 0;
+ }
+ selectField.scrollTop(currentScrollPosition - selectBoxHeight - (toRemove * singleOptionHeight));
+}
+
+/**
+ * Loads the next bunch of options at the beginning.
+ *
+ * @param selectField selct field
+ * @param maxOptions maximum options to show
+ * @param numberOfOptionsToLoad number of options to add
+ */
+window.lam.dynamicSelect.loadPreviousOptions = function(selectField, maxOptions, numberOfOptionsToLoad) {
+ var singleOptionHeight = selectField.data('option-height');
+ var currentScrollPosition = selectField.data('select-current-scroll-top');
+ var options = selectField.data('dynamic-options');
+ var lastIndex = selectField.children().first().data('index');
+ var added = 0;
+ for (var toAdd = 0; toAdd < numberOfOptionsToLoad; toAdd++) {
+ var addPos = lastIndex - 1 - toAdd;
+ if (options[addPos] === undefined) {
+ break;
+ }
+ added++;
+ selectField.prepend(window.lam.dynamicSelect.createOption(options[addPos], addPos));
+ }
+ var numberOfOptions = selectField.children().length;
+ var toRemove = numberOfOptions - maxOptions;
+ if (toRemove > 0) {
+ selectField.children().slice(maxOptions).remove();
+ }
+ selectField.scrollTop(currentScrollPosition + (added * singleOptionHeight));
+}
+
window.lam.selfservice = window.lam.selfservice || {};
/**
@@ -1173,6 +1306,7 @@ jQuery(document).ready(function() {
window.lam.tools.schema.select();
window.lam.html.activateLightboxes();
window.lam.html.preventEnter();
+ window.lam.dynamicSelect.activate();
});
/**