Skip to content
Snippets Groups Projects
Commit 16944b9d authored by srosse's avatar srosse
Browse files

OO-990: replace jquery ui autocomplete with typeahead ( which is loaded on demand)

parent 9703566b
No related branches found
No related tags found
No related merge requests found
Showing
with 1899 additions and 97 deletions
...@@ -60,11 +60,7 @@ public class CustomJSComponent extends AbstractComponent { ...@@ -60,11 +60,7 @@ public class CustomJSComponent extends AbstractComponent {
this.jsFilePaths = jsFilePaths; this.jsFilePaths = jsFilePaths;
} }
/** @Override
*
* @see org.olat.core.gui.components.Component#validate(org.olat.core.gui.UserRequest,
* org.olat.core.gui.render.ValidationResult)
*/
public void validate(UserRequest ureq, ValidationResult vr) { public void validate(UserRequest ureq, ValidationResult vr) {
super.validate(ureq, vr); super.validate(ureq, vr);
JSAndCSSAdder jsadder = vr.getJsAndCSSAdder(); JSAndCSSAdder jsadder = vr.getJsAndCSSAdder();
...@@ -86,5 +82,4 @@ public class CustomJSComponent extends AbstractComponent { ...@@ -86,5 +82,4 @@ public class CustomJSComponent extends AbstractComponent {
public ComponentRenderer getHTMLRendererSingleton() { public ComponentRenderer getHTMLRendererSingleton() {
return RENDERER; return RENDERER;
} }
}
} \ No newline at end of file
/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.core.gui.components.htmlheader.jscss;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.form.flexible.impl.FormItemImpl;
/**
*
* Initial date: 21.05.2014<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class CustomJSFormItem extends FormItemImpl {
private final CustomJSComponent component;
public CustomJSFormItem(String name, String[] jsPath) {
super(name);
component = new CustomJSComponent(name, jsPath);
}
@Override
protected Component getFormItemComponent() {
return component;
}
@Override
protected void rootFormAvailable() {
//
}
@Override
public void evalFormRequest(UserRequest ureq) {
//
}
@Override
public void reset() {
//
}
}
\ No newline at end of file
...@@ -28,14 +28,7 @@ ...@@ -28,14 +28,7 @@
*/ */
package org.olat.core.gui.components.htmlheader.jscss; package org.olat.core.gui.components.htmlheader.jscss;
import org.olat.core.gui.components.Component; import org.olat.core.gui.components.DefaultComponentRenderer;
import org.olat.core.gui.components.ComponentRenderer;
import org.olat.core.gui.render.RenderResult;
import org.olat.core.gui.render.Renderer;
import org.olat.core.gui.render.RenderingState;
import org.olat.core.gui.render.StringOutput;
import org.olat.core.gui.render.URLBuilder;
import org.olat.core.gui.translator.Translator;
/** /**
* Description:<br> * Description:<br>
...@@ -45,35 +38,6 @@ import org.olat.core.gui.translator.Translator; ...@@ -45,35 +38,6 @@ import org.olat.core.gui.translator.Translator;
* *
* @author Felix Jost * @author Felix Jost
*/ */
public class JSAndCSSComponentRenderer implements ComponentRenderer { public class JSAndCSSComponentRenderer extends DefaultComponentRenderer {
/**
* @see org.olat.core.gui.render.ui.ComponentRenderer#render(org.olat.core.gui.render.Renderer,
* org.olat.core.gui.render.StringOutput, org.olat.core.gui.components.Component,
* org.olat.core.gui.render.URLBuilder, org.olat.core.gui.translator.Translator,
* org.olat.core.gui.render.RenderResult, java.lang.String[])
*/
public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator,
RenderResult renderResult, String[] args) {
// nothing to render
//throw new OLATRuntimeException(JSAndCSSComponentRenderer.class, "render method should never be called for JsCssComponentRenderer. You must not include this component using $r.render(...) in a velocity page, simply adding it to a container using container.put(\"header\", thiscomponent) will do", null);
}
/**
* @see org.olat.core.gui.render.ui.ComponentRenderer#renderBodyOnLoadJSFunctionCall(org.olat.core.gui.render.Renderer,
* org.olat.core.gui.render.StringOutput, org.olat.core.gui.components.Component)
*/
public void renderBodyOnLoadJSFunctionCall(Renderer renderer, StringOutput sb, Component source, RenderingState rstate) {
// do nothing
}
/**
* @see org.olat.core.gui.render.ui.ComponentRenderer#renderHeaderIncludes(org.olat.core.gui.render.Renderer,
* org.olat.core.gui.render.StringOutput, org.olat.core.gui.components.Component,
* org.olat.core.gui.render.URLBuilder, org.olat.core.gui.translator.Translator)
*/
public void renderHeaderIncludes(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator,
RenderingState rstate) {
// do nothing
}
} }
...@@ -34,6 +34,7 @@ import org.json.JSONObject; ...@@ -34,6 +34,7 @@ import org.json.JSONObject;
import org.olat.core.dispatcher.mapper.Mapper; import org.olat.core.dispatcher.mapper.Mapper;
import org.olat.core.gui.UserRequest; import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component; import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.htmlheader.jscss.CustomJSComponent;
import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event; import org.olat.core.gui.control.Event;
...@@ -110,6 +111,11 @@ public class AutoCompleterController extends BasicController { ...@@ -110,6 +111,11 @@ public class AutoCompleterController extends BasicController {
if (label != null) { if (label != null) {
myContent.contextPut("autocompleter_label", label); myContent.contextPut("autocompleter_label", label);
} }
myContent.put("typeahead", new CustomJSComponent("typeahead", new String[] {
"js/jquery/typeahead/typeahead.bundle.js"
}));
myContent.contextPut("showDisplayKey", Boolean.valueOf(showDisplayKey)); myContent.contextPut("showDisplayKey", Boolean.valueOf(showDisplayKey));
myContent.contextPut("inputWidth", Integer.valueOf(inputWidth)); myContent.contextPut("inputWidth", Integer.valueOf(inputWidth));
myContent.contextPut("minChars", Integer.valueOf(minChars)); myContent.contextPut("minChars", Integer.valueOf(minChars));
......
...@@ -45,10 +45,8 @@ public class AutoCompleterMapper implements Mapper { ...@@ -45,10 +45,8 @@ public class AutoCompleterMapper implements Mapper {
this.gprovider = gprovider; this.gprovider = gprovider;
} }
@Override @Override
@SuppressWarnings({ "synthetic-access" })
public MediaResource handle(String relPath, HttpServletRequest request) { public MediaResource handle(String relPath, HttpServletRequest request) {
// Read query and generate JSON result // Read query and generate JSON result
String lastN = request.getParameter(PARAM_QUERY); String lastN = request.getParameter(PARAM_QUERY);
JSONArray result; JSONArray result;
...@@ -61,4 +59,4 @@ public class AutoCompleterMapper implements Mapper { ...@@ -61,4 +59,4 @@ public class AutoCompleterMapper implements Mapper {
} }
return new JSONMediaResource(result, "UTF-8"); return new JSONMediaResource(result, "UTF-8");
} }
} }
\ No newline at end of file
...@@ -38,6 +38,7 @@ import org.olat.core.gui.components.form.flexible.FormItemContainer; ...@@ -38,6 +38,7 @@ import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.impl.Form; import org.olat.core.gui.components.form.flexible.impl.Form;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
import org.olat.core.gui.components.htmlheader.jscss.CustomJSFormItem;
import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event; import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.WindowControl;
...@@ -146,6 +147,10 @@ public class FlexiAutoCompleterController extends FormBasicController { ...@@ -146,6 +147,10 @@ public class FlexiAutoCompleterController extends FormBasicController {
protected void setupAutoCompleter(UserRequest ureq, FormLayoutContainer layoutCont, String noresults, boolean showDisplayKey, int inputWidth, int minChars, String label) { protected void setupAutoCompleter(UserRequest ureq, FormLayoutContainer layoutCont, String noresults, boolean showDisplayKey, int inputWidth, int minChars, String label) {
String noResults = (noresults == null ? translate("autocomplete.noresults") : noresults); String noResults = (noresults == null ? translate("autocomplete.noresults") : noresults);
// Configure displaying parameters // Configure displaying parameters
layoutCont.add("typeahead", new CustomJSFormItem("typeahead", new String[] {
"js/jquery/typeahead/typeahead.bundle.js"
}));
if (label != null) { if (label != null) {
layoutCont.contextPut("autocompleter_label", label); layoutCont.contextPut("autocompleter_label", label);
......
<div class="b_form_auto_completer"> <div class="b_form_auto_completer">
#if($flexi) #if($flexi)
<div id="$r.getId("aj_ac_f")"> <div id='$r.getId("aj_ac_f")'>
#else #else
<form id="$r.getId("aj_ac_f")" action="$r.formURIbg('select')" method="post" $r.bgTarget()> <form id='$r.getId("aj_ac_f")' action="$r.formURIbg('select')" method="post" $r.bgTarget()>
#end #end
#if ($autocompleter_label) #if ($autocompleter_label)
$autocompleter_label $autocompleter_label
#end #end
<div class="$formElementClass"> <div class="$formElementClass">
<input type="text" size="$inputWidth" name='$r.getId("b_autocomplete_input")' id='$r.getId("b_autocomplete_input")' /> <input type="text" size="$inputWidth" class="form-control" name='$r.getId("b_autocomplete_input")' id='$r.getId("b_autocomplete_input")' />
</div> </div>
#if($flexi) #if($flexi)
</div> </div>
...@@ -16,46 +16,42 @@ ...@@ -16,46 +16,42 @@
</form> </form>
#end #end
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
/* <![CDATA[ */ /* <![CDATA[ */
jQuery(function(){ jQuery(function(){
var myAuto = jQuery('#$r.getId("b_autocomplete_input")').autocomplete({ var fullNameTypeahead = new Bloodhound({
minLength: 3, datumTokenizer: function (d) {
source: function( request, response ) { return Bloodhound.tokenizers.whitespace(d.value);
jQuery.ajax({ },
url: '$mapuri', queryTokenizer: Bloodhound.tokenizers.whitespace,
data: request, remote: {
dataType: "json", url: '${mapuri}?term=%QUERY',
type: "POST", filter: function ( response ) {
success: function( data ) { return jQuery.map(response, function (object) {
response(jQuery.map( data, function( item ) { return {
return { value: '' + object.key,
label: item.value, #if($showDisplayKey)
username: item.displayKey, fullName: object.displayKey + ": " + object.value
cssClass: item.cssClass, #else
value: item.key fullName: object.value
} #end
})); };
} });
}); }
},
open: function() {
jQuery(this).autocomplete('widget').css('z-index', 10000);
},
select: function( event, ui ) {
$r.javaScriptBgCommand("select") + '?key=' + ui.item.value;
return false;
} }
}).__renderItem = function(ul, item) { });
var itemVal = "<a><div class='b_form_auto_completer_item search-item " + item.cssClass + "'>"; fullNameTypeahead.initialize();
#if($showDisplayKey) jQuery('#$r.getId("b_autocomplete_input")').typeahead({
itemVal += "<span class=\'b_key\'>" + item.username + ": </span>"; hint: false,
#end highlight: false,
itemVal += "<span class='b_value'>" + item.label + "</span>"; minLength: 3
itemVal += "</div></a>"; },{
return jQuery("<li>").append(itemVal).appendTo(ul); minLength: 3,
}; displayKey: 'fullName',
source: fullNameTypeahead.ttAdapter()
}).on('typeahead:selected', function (e, object) {
$r.javaScriptBgCommand("select") + '?key=' + object.value;
});
}); });
/* ]]> */ /* ]]> */
</script> </script>
\ No newline at end of file
...@@ -63,18 +63,16 @@ public class JSONMediaResource extends DefaultMediaResource { ...@@ -63,18 +63,16 @@ public class JSONMediaResource extends DefaultMediaResource {
super.prepare(hres); super.prepare(hres);
try { try {
hres.setCharacterEncoding(encoding); hres.setCharacterEncoding(encoding);
} catch (Exception e1) { } catch (Exception e) {
e1.printStackTrace(); log.warn("", e);
} }
try { try {
if(jsonObject != null) { if(jsonObject != null) {
jsonObject.write(hres.getWriter()); jsonObject.write(hres.getWriter());
} else if(jsonArray != null) { } else if(jsonArray != null) {
jsonArray.write(hres.getWriter()); jsonArray.write(hres.getWriter());
} }
} catch (JSONException e) { } catch (JSONException e) {
log.error("", e); log.error("", e);
} catch (IOException e) { } catch (IOException e) {
...@@ -82,9 +80,7 @@ public class JSONMediaResource extends DefaultMediaResource { ...@@ -82,9 +80,7 @@ public class JSONMediaResource extends DefaultMediaResource {
} }
} }
/** @Override
* @see org.olat.core.gui.media.MediaResource#getInputStream()
*/
public InputStream getInputStream() { public InputStream getInputStream() {
return null; return null;
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
@import "modules/comment"; @import "modules/comment";
@import "modules/chart"; @import "modules/chart";
@import "modules/various_modules"; @import "modules/various_modules";
@import "modules/autocomplete";
/* sites specific styles */ /* sites specific styles */
@import "modules/coursesite"; @import "modules/coursesite";
......
.typeahead,
.tt-query,
.tt-hint {
width: 396px;
height: 30px;
padding: 8px 12px;
font-size: 24px;
line-height: 30px;
border: 2px solid #ccc;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
outline: none;
}
.typeahead {
background-color: #fff;
}
.typeahead:focus {
border: 2px solid #0097cf;
}
.tt-query {
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.tt-hint {
color: #999
}
.tt-dropdown-menu {
width: 422px;
margin-top: 12px;
padding: 8px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
}
.tt-suggestion {
padding: 3px 20px;
font-size: 18px;
line-height: 24px;
}
.tt-suggestion.tt-cursor {
color: #fff;
background-color: #0097cf;
}
.tt-suggestion p {
margin: 0;
}
\ No newline at end of file
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment