Skip to content

Commit

Permalink
#403 simple implementation of `<b:inputText typeahead="true" />
Browse files Browse the repository at this point in the history
  • Loading branch information
stephanrauh committed Jun 28, 2016
1 parent c4480e5 commit 3bd7f7e
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 8 deletions.
1 change: 1 addition & 0 deletions gradleResources/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ def copySpecCssResources = copySpec {
from 'css/jquery.minicolors.css'
from 'css/sticky-footer-navbar.css'
from 'css/bootstrap-datetimepicker.min.css'
from 'css/typeahead.css'
}
task copyCssResources << {
allThemes.each { dest ->
Expand Down
22 changes: 22 additions & 0 deletions gradleResources/js/bsf.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,25 @@ function treeDataMapper(data) {
}
return "";
}

var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substringRegex;

// an array that will be populated with substring matches
matches = [];

// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');

// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});

cb(matches);
};
};
26 changes: 24 additions & 2 deletions mavenResources/META-INF/resources/bsf/js/bsf.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2014 Riccardo Massera (TheCoder4.Eu)
BootsFaces JS
author: TheonSuccessCallbackCoder4.eu
Expand All @@ -10,4 +10,26 @@ BsF.ajax.onevent=function(a){if("complete"===a.status){var b=a.source.id.replace
BsF.ajax.callAjax=function(a,b,c,d,f,g,h,m){var k=a.id,l=k.replace(/[^a-zA-Z0-9]+/g,"_"),e={};m&&(e.params="BsFEvent="+m);(c=BsF.ajax.resolveJQuery(c))&&null!=c&&(e.render=c);(d=BsF.ajax.resolveJQuery(d))&&null!=d&&(e.execute=d);e[k]=k;BsF.onCompleteCallback[l]=f&&null!=f?f:null;BsF.onErrorCallback[l]=g&&null!=g?g:null;BsF.onSuccessCallback[l]=h&&null!=h?h:null;e.onevent=BsF.ajax.onevent;jsf.ajax.request(a,b,e);$.blockUI&&null!=$.blockUI&&$.blockUI();return!1};
BsF.ajax.resolveJQuery=function(a){if("undefined"==typeof a||null==a)return"";var b="";a=a.split(" ");for(i=0;i<a.length;i++)if(part=a[i],0==part.indexOf("@(")&&part.lastIndexOf(")")==part.length-1){var c=part.substring(2,part.length-1);(c=$(c))&&c.each(function(a,c){b+=" "+c.id})}else b+=part+" ";return b.trim()};BsF.ajax.paginate=function(a,b,c,d,f){a={execute:"@this"};a.render=f;a[d]=c;jsf.ajax.request(d,b,a);return!1};
if($.datepicker){var generateHTML_orig=$.datepicker._generateHTML;$.datepicker._generateHTML=function(){var a=generateHTML_orig.apply(this,arguments),a=a.replace(/<span\s+class='ui-icon\s+ui-icon-circle-triangle-w'\s*>[^<]+<\/span>/,'<span class="glyphicon glyphicon-chevron-left"></span>');return a=a.replace(/<span\s+class='ui-icon\s+ui-icon-circle-triangle-e'\s*>[^<]+<\/span>/,'<span class="glyphicon glyphicon-chevron-right"></span>')}}
function jq(a){return"#"+a.replace(/(:|\.|\[|\]|,)/g,"\\$1")}function treeDataMapper(a){return a&&"undefined"!==a?a.nodeInternalId+"|#*#|"+a.text+"|#*#|"+a.state.checked+"|#*#|"+a.state.disabled+"|#*#|"+a.state.expanded+"|#*#|"+a.state.selected:""};
function jq(a){return"#"+a.replace(/(:|\.|\[|\]|,)/g,"\\$1")}function treeDataMapper(a){return a&&"undefined"!==a?a.nodeInternalId+"|#*#|"+a.text+"|#*#|"+a.state.checked+"|#*#|"+a.state.disabled+"|#*#|"+a.state.expanded+"|#*#|"+a.state.selected:""};

var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substringRegex;

// an array that will be populated with substring matches
matches = [];

// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');

// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});

cb(matches);
};
};
19 changes: 19 additions & 0 deletions src/main/java/net/bootsfaces/component/inputText/InputText.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,24 @@ public void setTags(boolean _tags) {
super.setTags(_tags);
}

/**
* Activates the type-ahead aka autocomplete function. The list of values has to be defined in typeahead-values. <P>
* Usually this method is called internally by the JSF engine.
*/
@Override
public void setTypeahead(boolean _typeahead) {
if (_typeahead) {
AddResourcesListener.addResourceToHeadButAfterJQuery(C.BSF_LIBRARY, "js/typeahead.js");
AddResourcesListener.addThemedCSSResource("typeahead.css");
}
super.setTypeahead(_typeahead);
}

@Override
public void setTypeaheadValues(String _typeaheadValues) {
setTypeahead(true);
super.setTypeaheadValues(_typeaheadValues);
}


}
102 changes: 102 additions & 0 deletions src/main/java/net/bootsfaces/component/inputText/InputTextCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ protected enum PropertyKeys {
tooltipDelayShow,
tooltipPosition,
type,
typeahead,
typeaheadHighlight,
typeaheadHint,
typeaheadLimit,
typeaheadMinLength,
typeaheadValues,
update,
visible;
String toString;
Expand Down Expand Up @@ -739,6 +745,102 @@ public void setType(String _type) {
getStateHelper().put(PropertyKeys.type, _type);
}

/**
* Activates the type-ahead aka autocomplete function. The list of values has to be defined in typeahead-values. <P>
* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file.
*/
public boolean isTypeahead() {
return (boolean) (Boolean) getStateHelper().eval(PropertyKeys.typeahead, false);
}

/**
* Activates the type-ahead aka autocomplete function. The list of values has to be defined in typeahead-values. <P>
* Usually this method is called internally by the JSF engine.
*/
public void setTypeahead(boolean _typeahead) {
getStateHelper().put(PropertyKeys.typeahead, _typeahead);
}

/**
* Highlights the part of the suggestions that has already been entered. Defaults to true. <P>
* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file.
*/
public boolean isTypeaheadHighlight() {
return (boolean) (Boolean) getStateHelper().eval(PropertyKeys.typeaheadHighlight, true);
}

/**
* Highlights the part of the suggestions that has already been entered. Defaults to true. <P>
* Usually this method is called internally by the JSF engine.
*/
public void setTypeaheadHighlight(boolean _typeaheadHighlight) {
getStateHelper().put(PropertyKeys.typeaheadHighlight, _typeaheadHighlight);
}

/**
* If set to false, the typeahead will not show a hint. Defaults to true. <P>
* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file.
*/
public boolean isTypeaheadHint() {
return (boolean) (Boolean) getStateHelper().eval(PropertyKeys.typeaheadHint, true);
}

/**
* If set to false, the typeahead will not show a hint. Defaults to true. <P>
* Usually this method is called internally by the JSF engine.
*/
public void setTypeaheadHint(boolean _typeaheadHint) {
getStateHelper().put(PropertyKeys.typeaheadHint, _typeaheadHint);
}

/**
* Maximum number of suggestions to be shown. Defaults to 5. <P>
* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file.
*/
public int getTypeaheadLimit() {
return (int) (Integer) getStateHelper().eval(PropertyKeys.typeaheadLimit, 5);
}

/**
* Maximum number of suggestions to be shown. Defaults to 5. <P>
* Usually this method is called internally by the JSF engine.
*/
public void setTypeaheadLimit(int _typeaheadLimit) {
getStateHelper().put(PropertyKeys.typeaheadLimit, _typeaheadLimit);
}

/**
* Minimum number of characters to be entered before a suggestion is shown. <P>
* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file.
*/
public int getTypeaheadMinLength() {
return (int) (Integer) getStateHelper().eval(PropertyKeys.typeaheadMinLength, 1);
}

/**
* Minimum number of characters to be entered before a suggestion is shown. <P>
* Usually this method is called internally by the JSF engine.
*/
public void setTypeaheadMinLength(int _typeaheadMinLength) {
getStateHelper().put(PropertyKeys.typeaheadMinLength, _typeaheadMinLength);
}

/**
* Comma-separated list of values that can be used for the typeahead list. <P>
* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file.
*/
public String getTypeaheadValues() {
return (String) getStateHelper().eval(PropertyKeys.typeaheadValues);
}

/**
* Comma-separated list of values that can be used for the typeahead list. <P>
* Usually this method is called internally by the JSF engine.
*/
public void setTypeaheadValues(String _typeaheadValues) {
getStateHelper().put(PropertyKeys.typeaheadValues, _typeaheadValues);
}

/**
* Component(s) to be updated with ajax. <P>
* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
if ((autocomplete != null) && (autocomplete.equals("off"))) {
rw.writeAttribute("autocomplete", "off", null);
}
if (inputText.isTags()) {
if (inputText.isTags() && (!inputText.isTypeahead())) {
rw.writeAttribute("data-role", "tagsinput", null);
}

Expand All @@ -237,15 +237,101 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
}

Tooltip.activateTooltips(context, inputText);
if (inputText.isTags()) {
// String id = component.getClientId();
// id = id.replace(":", "\\\\:"); // we need to escape the id for jQuery
// rw.startElement("script", component);
// rw.endElement("script");
if (inputText.isTypeahead()) {
String id = component.getClientId();
id = id.replace(":", "_"); // we need to escape the id for jQuery
rw.startElement("script", component);
String typeaheadname = id + "_typeahead";
if (inputText.isTags()) {
String js = "var engine = new Bloodhound({" + //
"name: '" + typeaheadname + "'," + //
"local: " + getTypeaheadObjectArray(inputText) + "," + //
"datumTokenizer: function(d) {" + //
" return Bloodhound.tokenizers.whitespace(d.val);" + //
"}," + //
"queryTokenizer: Bloodhound.tokenizers.whitespace" + //
"});";
js += "$('." + id + "').tagsinput({" + //
"typeaheadjs: {" + //
" name: 'animals'," + //
" displayKey: 'val'," + //
" valueKey: 'val'," + //
" source: engine.ttAdapter()" + //
"}" + //
"});";//
rw.writeText(js, null);

} else {

String options = "";
options = addOption(options, "hint:" + inputText.isTypeaheadHint());
options = addOption(options, "highlight:" + inputText.isTypeaheadHighlight());
options = addOption(options, "minLength:" + inputText.getTypeaheadMinLength());
String options2 = "";
options2 = addOption(options2, "limit:" + inputText.getTypeaheadLimit());
options2 = addOption(options2, "name:'" + typeaheadname + "'");
options2 = addOption(options2, "source: substringMatcher(" + getTypeaheadValueArray(inputText) + ")");

rw.writeText("$('." + id + "').typeahead({" + options + "},{" + options2 + "});", null);
}
rw.endElement("script");
}
}

private String addOption(String options, String newOption) {
if (options.length() > 0) {
options += ",";
}
return options + newOption;
}

private String getTypeaheadValueArray(InputText inputText) {
String s = inputText.getTypeaheadValues();
if (null == s)
return null;
s = s.trim();
if (!s.contains("\'")) {
String[] parts = s.split(",");
StringBuilder b = new StringBuilder(s.length() * 2);
for (String p : parts) {
if (b.length() > 0) {
b.append(',');
}
b.append('\'');
b.append(p.trim());
b.append('\'');
}
s = b.toString();

}
return "[" + s + "]";
}

private String getTypeaheadObjectArray(InputText inputText) {
String s = inputText.getTypeaheadValues();
if (null == s)
return null;
s = s.trim();
if (!s.contains("\'")) {
String[] parts = s.split(",");
StringBuilder b = new StringBuilder(s.length() * 2);
for (String p : parts) {
if (b.length() > 0) {
b.append(',');
}
b.append("{val:");
b.append('\'');
b.append(p.trim());
b.append('\'');
b.append('}');
}
s = b.toString();

}
return "[" + s + "]";
}


private void generateStyleClass(InputText inputText, ResponseWriter rw) throws IOException {
StringBuilder sb;
String s;
Expand All @@ -265,6 +351,9 @@ private void generateStyleClass(InputText inputText, ResponseWriter rw) throws I
}

sb.append(" ").append(getErrorAndRequiredClass(inputText, inputText.getClientId()));
if (inputText.isTypeahead()) {
sb.append(" ").append(inputText.getClientId().replace(":","_"));
}
s = sb.toString().trim();
if (s != null && s.length() > 0) {
rw.writeAttribute("class", s, "class");
Expand Down
Loading

0 comments on commit 3bd7f7e

Please sign in to comment.