From c17e5df46f104fb12c47f0b625c868f08841f7e9 Mon Sep 17 00:00:00 2001
From: Daniel Haag <daniel.haag@uibk.ac.at>
Date: Wed, 6 Jul 2016 08:46:11 +0200
Subject: [PATCH] OPENOLAT-330: new parameters inputClear, inputDropdown,
 highlight and limit for the FlexiAutoCompleterController, now using bootstrap
 input-group for the autocompleter input element (dropdown and clear buttons)
 therefore needing some css fixes

---
 ...CreateCourseRepositoryEntryController.java |  2 +-
 .../AutoCompleterController.java              |  3 +
 .../FlexiAutoCompleterController.java         | 66 +++++++++++++++++--
 .../autocompletion/_content/autocomplete.html | 37 ++++++++---
 .../themes/light/modules/_autocomplete.scss   | 12 +++-
 .../static/themes/light/modules/_icons.scss   |  3 +-
 6 files changed, 108 insertions(+), 15 deletions(-)

diff --git a/src/main/java/at/ac/uibk/course/CreateCourseRepositoryEntryController.java b/src/main/java/at/ac/uibk/course/CreateCourseRepositoryEntryController.java
index f47d6d153d3..adaceb9acbb 100644
--- a/src/main/java/at/ac/uibk/course/CreateCourseRepositoryEntryController.java
+++ b/src/main/java/at/ac/uibk/course/CreateCourseRepositoryEntryController.java
@@ -245,7 +245,7 @@ public class CreateCourseRepositoryEntryController extends FormBasicController
 		autocompleterC = new FlexiAutoCompleterController(ureq,
 				getWindowControl(), provider,
 				translator.translate("form.autocomplete.not.found"), false,
-				true, 60, 3, null, mainForm);
+				true, 60, 0, null, mainForm, true, true, true, 10);
 		autocompleterC.setFormElement(false);
 		listenTo(autocompleterC);
 
diff --git a/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/AutoCompleterController.java b/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/AutoCompleterController.java
index d1c85ebcbc2..97dbd8aedd2 100644
--- a/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/AutoCompleterController.java
+++ b/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/AutoCompleterController.java
@@ -115,7 +115,10 @@ public class AutoCompleterController extends BasicController {
 		myContent.contextPut("minChars", Integer.valueOf(minChars));
 		myContent.contextPut("inputValue", "");
 		myContent.contextPut("inputClear", Boolean.FALSE);
+		myContent.contextPut("inputDropdown", Boolean.FALSE);
 		myContent.contextPut("inputReadonly", Boolean.FALSE);
+		myContent.contextPut("highlight", Boolean.FALSE);
+		myContent.contextPut("limit", 5);
 		// Create a mapper for the server responses for a given input
 		mapper = new AutoCompleterMapper(noResults, showDisplayKey, gprovider);
 			
diff --git a/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/FlexiAutoCompleterController.java b/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/FlexiAutoCompleterController.java
index 9e8d7d5652e..80cf77eb692 100644
--- a/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/FlexiAutoCompleterController.java
+++ b/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/FlexiAutoCompleterController.java
@@ -74,8 +74,13 @@ public class FlexiAutoCompleterController extends FormBasicController {
 	private boolean allowNewValues;
 	private boolean formElement;
 
+	private boolean showClear = false;
+	private boolean showDropdown = false;
+	private boolean highlight = false;
+	private int limit = 5;
+
 	/**
-	 * Constructor to create an auto completer controller
+	 * Constructor to create an auto completer controller (deprecated) 
 	 * 
 	 * @param ureq
 	 *            The user request object
@@ -97,6 +102,8 @@ public class FlexiAutoCompleterController extends FormBasicController {
 	 *            The minimum number of characters the user has to enter to
 	 *            perform a search
 	 * @param label
+	 *            The label of the form element
+	 * @deprecated
 	 */
 	public FlexiAutoCompleterController(UserRequest ureq, WindowControl wControl, ListProvider provider, String noresults,
 			final boolean showDisplayKey, int inputWidth, int minChars, String label) {
@@ -106,12 +113,57 @@ public class FlexiAutoCompleterController extends FormBasicController {
 		setupAutoCompleter(ureq, flc, noresults, showDisplayKey, inputWidth, minChars, label);
 		setFormElement(true);
 	}
-	
+
+	/**
+	 * Constructor to create an auto completer controller
+	 * 
+	 * @param ureq
+	 *            The user request object
+	 * @param wControl
+	 *            The window control object
+	 * @param provider
+	 *            The provider that can be called to return the search-results
+	 *            for a given search query
+	 * @param noResults
+	 *            The translated value to display when no results are found,
+	 *            e.g. "no matches found" or "-no users found-". When a NULL
+	 *            value is provided, the controller will use a generic message.
+	 * @param showDisplayKey
+	 *            true: show the key for each record; false: don't show the key,
+	 *            only the value
+	 * @param allowNewValues
+	 *            true: allow values not in the suggested items list
+	 * @param inputWidth
+	 *            The input field width in characters
+	 * @param minChars
+	 *            The minimum number of characters the user has to enter to
+	 *            perform a search
+	 * @param label
+	 *            The label of the form element
+	 * @param externalMainForm
+	 *            The form this AutoCompleter controller is embedded into
+	 */
 	public FlexiAutoCompleterController(UserRequest ureq, WindowControl wControl, ListProvider provider, String noresults,
 			final boolean showDisplayKey, final boolean allowNewValues, int inputWidth, int minChars, String label, Form externalMainForm) {
 		super(ureq, wControl, LAYOUT_CUSTOM, "autocomplete", externalMainForm);
+		setup(ureq, provider, noresults, showDisplayKey, allowNewValues, inputWidth, minChars, label, false, false, false, 5);
+	}
+
+	public FlexiAutoCompleterController(UserRequest ureq, WindowControl wControl, ListProvider provider, String noresults,
+			final boolean showDisplayKey, final boolean allowNewValues, int inputWidth, int minChars, String label, Form externalMainForm,
+			final boolean showClear, final boolean showDropdown, final boolean highlight, int limit) {
+		super(ureq, wControl, LAYOUT_CUSTOM, "autocomplete", externalMainForm);
+		setup(ureq, provider, noresults, showDisplayKey, allowNewValues, inputWidth, minChars, label, showClear, showDropdown, highlight, limit);
+	}
+
+	protected void setup(UserRequest ureq, ListProvider provider, String noresults, boolean showDisplayKey, boolean allowNewValues,
+			int inputWidth, int minChars, String label, boolean showClear, boolean showDropdown, boolean highlight, int limit) {
 		this.gprovider = provider;
 		this.allowNewValues = allowNewValues;
+		this.showClear = showClear;
+		this.showDropdown = showDropdown;
+		this.highlight = highlight;
+		this.limit = limit;
 		setupAutoCompleter(ureq, flc, noresults, showDisplayKey, inputWidth, minChars, label);
 		setFormElement(true);
 	}
@@ -158,7 +210,10 @@ public class FlexiAutoCompleterController extends FormBasicController {
 		layoutCont.contextPut("flexi", Boolean.TRUE);
 		layoutCont.contextPut("inputValue", "");
 		layoutCont.contextPut("inputClear", Boolean.FALSE);
-		layoutCont.contextPut("inputReadonly", Boolean.FALSE);		
+		layoutCont.contextPut("inputDropdown", showDropdown);
+		layoutCont.contextPut("inputReadonly", Boolean.FALSE);
+		layoutCont.contextPut("highlight", highlight);
+		layoutCont.contextPut("limit", limit);
 		layoutCont.getComponent().addListener(this);
 
 		// Create a mapper for the server responses for a given input
@@ -190,6 +245,7 @@ public class FlexiAutoCompleterController extends FormBasicController {
 			} else if (event.getCommand().equals(COMMAND_CHANGE)) {
 				flc.contextPut("inputReadonly", Boolean.FALSE);
 				flc.contextPut("inputClear", Boolean.FALSE);
+				flc.contextPut("inputDropdown", showDropdown);
 				if(allowNewValues) {
 					fireEvent(ureq, new NewValueChosenEvent(value));
 				} else {
@@ -240,10 +296,12 @@ public class FlexiAutoCompleterController extends FormBasicController {
 			// Normal case, add entry
 			selectedEntries.add(key);
 			flc.contextPut("inputReadonly", Boolean.TRUE);
-			flc.contextPut("inputClear", Boolean.TRUE);
+			flc.contextPut("inputClear", showClear);
+			flc.contextPut("inputDropdown", (!showClear) && showDropdown);
 		} else if (key.equals(AUTOCOMPLETER_NO_RESULT)) {
 			flc.contextPut("inputReadonly", Boolean.FALSE);
 			flc.contextPut("inputClear", Boolean.FALSE);
+			flc.contextPut("inputDropdown", showDropdown);
 			return;
 		}
 		doFireSelection(ureq, selectedEntries);
diff --git a/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/_content/autocomplete.html b/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/_content/autocomplete.html
index 80ef5539e38..80a19ff2ce2 100644
--- a/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/_content/autocomplete.html
+++ b/src/main/java/org/olat/core/gui/control/generic/ajax/autocompletion/_content/autocomplete.html
@@ -1,4 +1,4 @@
-<div class="o_form_auto_completer">
+<div class="o_form_auto_completer form-inline">
 	#if($flexi)
 		<div id='$r.getId("aj_ac_f")'>
 	#else
@@ -7,10 +7,15 @@
     #if ($autocompleter_label)
 		$autocompleter_label 
 	#end
-	<div class="$formElementClass">
+	<div class="#if($inputClear || $inputDropdown)input-group#{end} $formElementClass">
 	    <input type="text" size="$inputWidth" #if($inputReadonly)readonly="true"#{end} value="$inputValue" class="form-control" name='$r.getId("o_autocomplete_input")' id='$r.getId("o_autocomplete_input")' />
-		<i id='$r.getId("o_autocomplete_input")_remove' class="#if(!$inputClear)hidden #{end}o_icon o_icon-fw o_icon_remove"></i>
-	</div>	
+	    #if($inputClear || $inputDropdown)
+	    <span class="input-group-addon">
+	        <i id='$r.getId("o_autocomplete_input")_remove' class="#if(!$inputClear)hidden #{end}o_icon o_icon-fw o_icon_remove"></i>
+	        <i id='$r.getId("o_autocomplete_input")_dropdown' class="#if(!$inputDropdown)hidden #{end}o_icon o_icon-fw o_icon_bottom"></i>
+			</span>
+			#end
+	</div>
 	#if($flexi)
 		</div>
 	#else
@@ -20,6 +25,7 @@
 <script type="text/javascript">
 /* <![CDATA[ */ 
 jQuery(function(){
+	var current_result_length;
 	var fullNameTypeahead = new Bloodhound({
 		datumTokenizer: function (d) {
 			return Bloodhound.tokenizers.whitespace(d.value);
@@ -29,6 +35,7 @@ jQuery(function(){
 			url: '${mapuri}?term=%QUERY',
 			wildcard: '%QUERY',
 			filter: function ( response ) {
+				current_result_length = response.length;
 				return jQuery.map(response, function (object) {
 					return {
 						cssClass: object.cssClass,
@@ -46,19 +53,29 @@ jQuery(function(){
 	fullNameTypeahead.initialize();
 	jQuery('#$r.getId("o_autocomplete_input")').typeahead({
 		hint: false,
-		highlight: false,
-		minLength: 3
+		highlight: ${highlight},
+		minLength: ${minChars}
 	},{
-		minLength: 3,
+		minLength: ${minChars},
 		displayKey: 'fullName',
 		source: fullNameTypeahead.ttAdapter(),
+		limit: ${limit},
 		templates: {
 	        #if ($autocompleter_emptymessage)
 			empty: [
 		    		'<div>','$autocompleter_emptymessage','</div>' 
 	        ].join('\n'),
 	    	#end
-	    	suggestion: function(obj) {return "<div class='"+obj.cssClass+"'>"+obj.fullName+"</div>"}
+	    	suggestion: function(obj) {return "<div class='"+obj.cssClass+"'>"+obj.fullName+"</div>"},
+	      footer: function(obj,test) {
+	    	  console.log(obj);
+	    	  if (current_result_length > obj.suggestions.length) {
+		    	  return "<div class='tt-suggestion o_disabled'> ... "
+		    	  	+ ((current_result_length - obj.suggestions.length > 9) ? "mehr als 9" : (current_result_length - obj.suggestions.length))
+	  	  	  	+ " weitere Kurse"
+	  	  	  	+ " ... </div>";
+	    	  }
+	    	}
 		}
 	}).on('typeahead:render', function (e) {
 		// disabled and error items should not be selectable
@@ -87,6 +104,10 @@ jQuery(function(){
 		$r.openJavaScriptCommand("change"),'key','',
 			'$r.getId("o_autocomplete_input")','');
 	});
+	jQuery('#$r.getId("o_autocomplete_input")_dropdown').on('click', function(e) {
+		jQuery('#$r.getId("o_autocomplete_input")').typeahead('val','*');
+		jQuery('#$r.getId("o_autocomplete_input")').typeahead('open');
+	});
 });
 /* ]]> */
 </script>
\ No newline at end of file
diff --git a/src/main/webapp/static/themes/light/modules/_autocomplete.scss b/src/main/webapp/static/themes/light/modules/_autocomplete.scss
index 9b0c64972bc..d6387cedb92 100644
--- a/src/main/webapp/static/themes/light/modules/_autocomplete.scss
+++ b/src/main/webapp/static/themes/light/modules/_autocomplete.scss
@@ -3,7 +3,7 @@
 }
 
 .tt-menu {
-  	width: $o-autocomplete-width;
+  	width: 100%;
   	margin-top: $padding-base-vertical;
   	padding: 0 0 0;
   	color: $o-autocomplete-color;
@@ -33,3 +33,13 @@
 .tt-menu div.o_icon_error:before {
 	content:'';
 }
+
+// bootstrap fix for typeahead wrapper
+.twitter-typeahead {
+  	width: 100%;
+  	float: left;
+  	.input-group & {
+  		//overwrite `display:inline-block` style
+  		display: table-cell!important;
+	}
+}
diff --git a/src/main/webapp/static/themes/light/modules/_icons.scss b/src/main/webapp/static/themes/light/modules/_icons.scss
index 22bd885d4a4..56103d64a99 100644
--- a/src/main/webapp/static/themes/light/modules/_icons.scss
+++ b/src/main/webapp/static/themes/light/modules/_icons.scss
@@ -236,6 +236,7 @@ $fa-css-prefix: "o_icon" !default;
 .o_icon_tool:before { content: $fa-var-gear; }
 .o_icon_tools:before { content: $fa-var-wrench; }
 .o_icon_top:before { content: $fa-var-chevron-up; }
+.o_icon_bottom:before { content: $fa-var-chevron-down; }
 .o_icon_translation_item:before { content: $fa-var-file-code-o; }
 .o_icon_translation_package:before { content: $fa-var-folder-open-o; }
 .o_icon_user:before { content: $fa-var-user; }
@@ -505,4 +506,4 @@ a.o_icon {
 		text-decoration: none;
 	}
 }
-	
\ No newline at end of file
+	
-- 
GitLab