Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Issues/995 #1141

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions gradleResources/staticResources/css/ext/bootstrap-tagsinput.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* bootstrap-tagsinput v0.7.2
* bootstrap-tagsinput v0.8.0
*
*/

Expand Down Expand Up @@ -44,22 +44,17 @@
margin-right: 2px;
color: white;
}
.bootstrap-tagsinput .tag [data-role='remove'] {
.bootstrap-tagsinput .tag [data-role="remove"] {
margin-left: 8px;
cursor: pointer;
}
.bootstrap-tagsinput .tag [data-role='remove']:after {
content: 'x';
.bootstrap-tagsinput .tag [data-role="remove"]:after {
content: "x";
padding: 0px 2px;
}
.bootstrap-tagsinput .tag [data-role='remove']:hover {
.bootstrap-tagsinput .tag [data-role="remove"]:hover {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.bootstrap-tagsinput .tag [data-role='remove']:hover:active {
.bootstrap-tagsinput .tag [data-role="remove"]:hover:active {
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}

.bootstrap-tagsinput .twitter-typeahead {
position: absolute !important;
width: auto;
}
4 changes: 3 additions & 1 deletion gradleResources/staticResources/css/ext/input-tags.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
.bootstrap-tagsinput .twitter-typeahead {
width: auto;
float: none;
position: relative !important;
position: absolute !important;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephanrauh i have move the css from bootstrap-tagsinput.css to input-tags.css but i'm not sure if we need relative or absolute

/* solution for #721 */
width: auto;
}

.bootstrap-tagsinput .twitter-typeahead .tt-menu {
Expand Down
4 changes: 2 additions & 2 deletions gradleResources/staticResources/js/bootstrap-tagsinput.min.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

95 changes: 57 additions & 38 deletions src/main/java/net/bootsfaces/component/inputText/InputText.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@
*
* @author thecoder4.eu
*/
@ListenersFor({ @ListenerFor(systemEventClass = PostAddToViewEvent.class) })
@ListenersFor({
@ListenerFor(systemEventClass = PostAddToViewEvent.class)})
@FacesComponent(InputText.COMPONENT_TYPE)
public class InputText extends InputTextCore implements IHasTooltip, IAJAXComponent, IAJAXComponent2, IResponsive, IResponsiveLabel {

protected enum PropertyKeys {
protected enum PropertyKeys {
mask
}
}

private String renderLabel = null;

Expand All @@ -69,24 +70,24 @@ protected enum PropertyKeys {
public static final String COMPONENT_FAMILY = C.BSFCOMPONENT;

private static final Collection<String> EVENT_NAMES = Collections
.unmodifiableCollection(Arrays.asList("blur", "change", "click", "dblclick", "focus", "input", "keydown",
"keypress", "keyup", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup", "select"));
.unmodifiableCollection(Arrays.asList("blur", "change", "click", "dblclick", "focus", "input", "keydown",
"keypress", "keyup", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup", "select"));

/**
* returns the subset of AJAX requests that are implemented by jQuery
* callback or other non-standard means (such as the onclick event of
* b:tabView, which has to be implemented manually).
* returns the subset of AJAX requests that are implemented by jQuery callback or other non-standard means (such
* as the onclick event of b:tabView, which has to be implemented manually).
*
* @return
*/
@Override
public Map<String, String> getJQueryEvents() {
return null;
}

/**
* Returns the subset of the parameter list of jQuery and other non-standard JS callbacks which is sent to the server via AJAX.
* If there's no parameter list for a certain event, the default is simply null.
*
* Returns the subset of the parameter list of jQuery and other non-standard JS callbacks which is sent to the
* server via AJAX. If there's no parameter list for a certain event, the default is simply null.
*
* @return A hash map containing the events. May be null.
*/
@Override
Expand All @@ -95,20 +96,22 @@ public Map<String, String> getJQueryEventParameterListsForAjax() {
}

/**
* Returns the parameter list of jQuery and other non-standard JS callbacks.
* If there's no parameter list for a certain event, the default is simply "event".
*
* Returns the parameter list of jQuery and other non-standard JS callbacks. If there's no parameter list for a
* certain event, the default is simply "event".
*
* @return A hash map containing the events. May be null.
*/
@Override
public Map<String, String> getJQueryEventParameterLists() {
return null;
}

@Override
public Collection<String> getEventNames() {
return EVENT_NAMES;
}

@Override
public String getDefaultEventName() {
return "change";
}
Expand All @@ -124,33 +127,44 @@ public InputText() {
}
}

@Override
public void setValueExpression(String name, ValueExpression binding) {
name = BsfUtils.snakeCaseToCamelCase(name);
super.setValueExpression(name, binding);

if ("mask".equals(name)) addInputmaskResources();
else if ("typeahead".equals(name)) addTypeaheadResources();
else if ("tags".equals(name)) addTagsResources();
if ("mask".equals(name)) {
addInputmaskResources();
} else if ("typeahead".equals(name)) {
addTypeaheadResources();
} else if ("tags".equals(name)) {
addTagsResources();
}
}

@Override
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
if (isAutoUpdate()) {
if (FacesContext.getCurrentInstance().isPostback()) {
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add(getClientId());
}
super.processEvent(event);
}
super.processEvent(event);
}
}

public String getFamily() {
@Override
public String getFamily() {
return COMPONENT_FAMILY;
}

/**
* Show the words of the input text as tags (similar to price tags in the supermarket). You can select one or more tags. The list is sent to the backend bean as a comma-separated list. <P>
* Show the words of the input text as tags (similar to price tags in the supermarket). You can select one or
* more tags. The list is sent to the backend bean as a comma-separated list.
* <P>
* Usually this method is called internally by the JSF engine.
*
* @param _tags
*/
@Override
public void setTags(boolean _tags) {
if (_tags) {
addTagsResources();
Expand All @@ -159,8 +173,11 @@ public void setTags(boolean _tags) {
}

/**
* Activates the type-ahead aka autocomplete function. The list of values has to be defined in typeahead-values. <P>
* 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.
*
* @param _typeahead
*/
@Override
public void setTypeahead(boolean _typeahead) {
Expand All @@ -175,37 +192,39 @@ public void setTypeaheadValues(Object _typeaheadValues) {
setTypeahead(true);
super.setTypeaheadValues(_typeaheadValues);
}

/**
* Returns input mask.
*
* @return Input mask.
*/

/**
* Returns input mask.
*
* @return Input mask.
*/
@Override
public String getMask() {
return (String) getStateHelper().eval(PropertyKeys.mask);
}

/**
* Sets input mask and triggers JavaScript to be loaded.
*
* @param mask Input mask to set.
*/
/**
* Sets input mask and triggers JavaScript to be loaded.
*
* @param mask Input mask to set.
*/
@Override
public void setMask(String mask) {
if (mask != null && !mask.isEmpty()) {
addInputmaskResources();
}
getStateHelper().put(PropertyKeys.mask, mask);
getStateHelper().put(PropertyKeys.mask, mask);
}

private void addInputmaskResources() {
AddResourcesListener.addResourceToHeadButAfterJQuery(C.BSF_LIBRARY, "js/jquery.inputmask.bundle.min.js");
AddResourcesListener.addResourceToHeadButAfterJQuery(C.BSF_LIBRARY, "js/jquery.inputmask.bundle.min.js");
}

private void addTypeaheadResources() {
AddResourcesListener.addResourceToHeadButAfterJQuery(C.BSF_LIBRARY, "js/typeahead.js");
AddResourcesListener.addExtCSSResource("typeahead.css");
}

private void addTagsResources() {
AddResourcesListener.addResourceToHeadButAfterJQuery(C.BSF_LIBRARY, "js/bootstrap-tagsinput.min.js");
AddResourcesListener.addExtCSSResource("bootstrap-tagsinput.css");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@
import net.bootsfaces.beans.BsfBeanInfo;

/**
* BeanInfo class to provide mapping
* of snake-case attributes to camelCase ones
*
* BeanInfo class to provide mapping of snake-case attributes to camelCase ones
*
* @author durzod
*/
public class InputTextBeanInfo extends BsfBeanInfo {

/**
* Get the reference decorated class
*
* @return
*/
@Override
public Class<?> getDecoratedClass() {
return InputText.class;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,9 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce

if (visible && label != null) {
rw.startElement("label", component);
rw.writeAttribute("for", fieldId, "for"); // "input_" +
// clientId
generateErrorAndRequiredClass(inputText, rw, clientId, inputText.getLabelStyleClass(), responsiveLabelClass,
"control-label");
rw.writeAttribute("for", fieldId, "for"); // "input_" + clientId
generateErrorAndRequiredClass(inputText, rw, clientId, inputText.getLabelStyleClass(), responsiveLabelClass, "control-label");
writeAttribute(rw, "style", inputText.getLabelStyle());

rw.writeText(label, null);
rw.endElement("label");
}
Expand All @@ -204,12 +201,13 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
// Input
rw.startElement("input", inputText);
rw.writeAttribute("id", fieldId, null); // "input_" + clientId

String name = inputText.getName();
// System.out.println(name);
if (null == name) {
name = "input_" + clientId;
}
rw.writeAttribute("name", name, null);

rw.writeAttribute("type", t, null);

generateStyleClass(inputText, rw);
Expand All @@ -234,7 +232,7 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
if ((autocomplete != null) && (autocomplete.equals("off") || autocomplete.equals("false"))) {
rw.writeAttribute("autocomplete", "off", null);
}

String v = getValue2Render(context, component);
if (inputText instanceof InputSecret) {
if (!((InputSecret) inputText).isRenderValue()) {
Expand All @@ -260,25 +258,23 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
numberOfDivs--;
}

// The following lines fix issue #1079 on basic tags (without typeahead).
// They initialize tagsinput manually and empty duplicated 'name' attribute
// on generated input (which holds the original HTML id).
if (inputText.isTags() && (!inputText.isTypeahead())) {
String id = fieldId; // input id
id = id.replace(":", "\\\\:"); // escape the id for jQuery
rw.startElement("script", null);
String js = "$('#" + id + "').tagsinput();" + //
"$('#" + id + "').attr('name','');";
String js = "$('#" + id + "').tagsinput();";
js += "$('#" + id + "').siblings('.bootstrap-tagsinput').addClass('form-control');";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the update to 0.8.0 the bug with the 'name' was fixed, but there is a new bug fix a missing class

rw.writeText(js, null);
rw.endElement("script");
}

Tooltip.activateTooltips(context, inputText);
if (inputText.isTypeahead()) {
String id = component.getClientId();
id = id.replace(":", "_"); // we need to escape the id for jQuery
String componentClientId = component.getClientId();
// generateStyleClass() add the id as css class before
String escapedClientId = componentClientId.replace(":", "_"); // we need to escape the id for jQuery
rw.startElement("script", component);
String typeaheadname = id + "_typeahead";
String typeaheadname = escapedClientId + "_typeahead";
if (inputText.isTags()) {
String js = "var engine = new Bloodhound({" + //
"name: '" + typeaheadname + "'," + //
Expand All @@ -288,7 +284,8 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
"}," + //
"queryTokenizer: Bloodhound.tokenizers.whitespace" + //
"});";
js += "$('." + id + "').tagsinput({" + //

js += "$('." + escapedClientId + "').tagsinput({" + //
"typeaheadjs: {" + //
" name: 'animals'," + //
" displayKey: 'val'," + //
Expand All @@ -297,27 +294,19 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
"}" + //
"});";//

// The following lines fix issue #1079 on tags with typeahead.
// They empty duplicated 'name' attribute on generated input (which holds the original HTML id).
String inputId = fieldId; // input id
inputId = inputId.replace(":", "\\\\:"); // escape the id for jQuery
js += "$('#" + inputId + "').attr('name','');";
js += "$('." + escapedClientId + "').siblings('.bootstrap-tagsinput').addClass('form-control');";

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the update to 0.8.0 the bug with the 'name' was fixed, but there is a new bug fix a missing class

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: BsF.substringMatcher(" + getTypeaheadValueArray(inputText) + ")");

rw.writeText("$('." + id + "').typeahead({" + options + "},{" + options2 + "});", null);
options2 = addOption(options2, "source: BsF.substringMatcher(" + getTypeaheadValueArray(inputText) + ")");
rw.writeText("$('." + escapedClientId + "').typeahead({" + options + "},{" + options2 + "});", null);
}
rw.endElement("script");
}
Expand Down