From 33cf7b22af33b6c4d30119fb598e275d6cf6c97b Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 14 Dec 2016 23:13:52 +0100 Subject: [PATCH] #584 added AJAX for the few events supported by the underlying JS component --- .../component/ajax/AJAXRenderer.java | 20 +- .../gyroscope/GyroscopeRenderer.java | 2 +- .../selectMultiMenu/SelectMultiMenu.java | 49 +++- .../selectMultiMenu/SelectMultiMenuCore.java | 216 +++++++++++++++++- .../SelectMultiMenuRenderer.java | 49 ++-- .../meta/META-INF/bootsfaces-b.taglib.xml | 78 ++++++- xtext/BootsFaces.jsfdsl | 14 +- 7 files changed, 392 insertions(+), 36 deletions(-) diff --git a/src/main/java/net/bootsfaces/component/ajax/AJAXRenderer.java b/src/main/java/net/bootsfaces/component/ajax/AJAXRenderer.java index 1ae1e8863..02a1960cb 100644 --- a/src/main/java/net/bootsfaces/component/ajax/AJAXRenderer.java +++ b/src/main/java/net/bootsfaces/component/ajax/AJAXRenderer.java @@ -185,7 +185,7 @@ public static void generateBootsFacesAJAXAndJavaScript(FacesContext context, Cli if (null != jQueryEvents) if (jQueryEvents.containsKey(keyClientBehavior)) continue; - generatedAJAXCall |= generateAJAXCallForASingleEvent(context, component, rw, specialEvent, + generatedAJAXCall |= generateAJAXCallForASingleEvent(context, component, rw, null, specialEvent, specialEventHandler, isJQueryCallback, keyClientBehavior, null, null); } } @@ -292,7 +292,7 @@ private static void generateOnClickHandler(FacesContext context, ResponseWriter } public static boolean generateAJAXCallForASingleEvent(FacesContext context, ClientBehaviorHolder component, - ResponseWriter rw, String specialEvent, String specialEventHandler, boolean isJQueryCallback, + ResponseWriter rw, StringBuffer code, String specialEvent, String specialEventHandler, boolean isJQueryCallback, String keyClientBehavior, StringBuilder generatedJSCode, String optionalParameterList) throws IOException { boolean generatedAJAXCall = false; String jsCallback = ""; @@ -311,8 +311,13 @@ public static boolean generateAJAXCallForASingleEvent(FacesContext context, Clie jsCallback = jsCallback + ";javascript:" + specialEventHandler; } jsCallback = convertAJAXToJavascript(context, jsCallback, component, keyClientBehavior, optionalParameterList); + if (null != code) { + code.append(jsCallback); + } if ("dragstart".equals(keyClientBehavior)) { + if (null != rw) { rw.writeAttribute("draggable", "true", "draggable"); + } } break; } @@ -358,7 +363,12 @@ public static boolean generateAJAXCallForASingleEvent(FacesContext context, Clie if (component instanceof CommandButton) if (generatedAJAXCall && "click".equals(keyClientBehavior)) script += ";return false;"; - rw.writeAttribute("on" + keyClientBehavior, jsCallback + script, null); + if (null != rw) { + rw.writeAttribute("on" + keyClientBehavior, jsCallback + script, null); + } + if (null != code) { + code.append(jsCallback + script); + } } } if (null != generatedJSCode) { @@ -640,7 +650,7 @@ public void generateBootsFacesAJAXAndJavaScriptForJQuery(FacesContext context, U if (null != ajaxComponent.getJQueryEventParameterListsForAjax().get(event)) parameterList = ajaxComponent.getJQueryEventParameterListsForAjax().get(event); } - generateAJAXCallForASingleEvent(context, (ClientBehaviorHolder) component, rw, event, + generateAJAXCallForASingleEvent(context, (ClientBehaviorHolder) component, rw, null, event, additionalEventHandler, true, event, code, parameterList); if (code.length() > 0) { rw.startElement("script", component); @@ -662,7 +672,7 @@ public String generateBootsFacesAJAXAndJavaScriptForAnMobileEvent(FacesContext c StringBuilder code = new StringBuilder(); String additionalEventHandler = null; - generateAJAXCallForASingleEvent(context, component, rw, event, additionalEventHandler, true, event, code, null); + generateAJAXCallForASingleEvent(context, component, rw, null, event, additionalEventHandler, true, event, code, null); return code.toString(); } } diff --git a/src/main/java/net/bootsfaces/component/gyroscope/GyroscopeRenderer.java b/src/main/java/net/bootsfaces/component/gyroscope/GyroscopeRenderer.java index f90c10633..9dfb1ce4a 100644 --- a/src/main/java/net/bootsfaces/component/gyroscope/GyroscopeRenderer.java +++ b/src/main/java/net/bootsfaces/component/gyroscope/GyroscopeRenderer.java @@ -167,7 +167,7 @@ public void encodeBegin(FacesContext context, UIComponent component) throws IOEx StringBuilder jsCode = new StringBuilder(); // Render Ajax Capabilities AJAXRenderer.generateAJAXCallForASingleEvent( - FacesContext.getCurrentInstance(), gyroscope, rw, + FacesContext.getCurrentInstance(), gyroscope, rw, null, null, null, false, "rotation", jsCode, null); String js = jsCode.toString().replace("callAjax(this,", "callAjax(document.getElementById('" + clientId + ".alpha'),"); rw.write("window.addEventListener('deviceorientation', function(event) {\n"); diff --git a/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenu.java b/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenu.java index 22a19b993..e96aec528 100644 --- a/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenu.java +++ b/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenu.java @@ -18,6 +18,13 @@ package net.bootsfaces.component.selectMultiMenu; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import javax.el.ValueExpression; import javax.faces.application.ResourceDependencies; import javax.faces.application.ResourceDependency; @@ -35,7 +42,8 @@ @ResourceDependency(library = "bsf", name = "js/dropdown.js", target = "body"), }) @FacesComponent("net.bootsfaces.component.selectMultiMenu.SelectMultiMenu") -public class SelectMultiMenu extends SelectMultiMenuCore implements net.bootsfaces.render.IHasTooltip, IResponsive, IResponsiveLabel { +public class SelectMultiMenu extends SelectMultiMenuCore implements net.bootsfaces.render.IHasTooltip, IResponsive, IResponsiveLabel, + net.bootsfaces.component.ajax.IAJAXComponent{ public static final String COMPONENT_TYPE = "net.bootsfaces.component.selectMultiMenu.SelectMultiMenu"; @@ -59,4 +67,43 @@ public void setValueExpression(String name, ValueExpression binding) { public String getFamily() { return COMPONENT_FAMILY; } + + + @Override + public Map getJQueryEvents() { + Map result = new HashMap(); + result.put("selectall", "onSelectAll"); + result.put("dropdownshow", "onDropdownShow"); + result.put("dropdownhidden", "onDropdownHidden"); + result.put("selectall", "onSelectAll"); + result.put("change", "onChange"); + result.put("dropdownhide", "onDropdownHide"); + result.put("deselectall", "onDeselectAll"); + result.put("dropdownshown", "onDropdownShown"); + result.put("unitialized", "onInitialized"); + return result; + } + + @Override + public Map getJQueryEventParameterLists() { + Map result = new HashMap(); + result.put("change", "option, checked, select"); + return result; + } + + @Override + public Map getJQueryEventParameterListsForAjax() { + return null; + } + + private static final Collection EVENT_NAMES = Collections.unmodifiableCollection( + new ArrayList()); + + public Collection getEventNames() { + return EVENT_NAMES; + } + + public String getDefaultEventName() { + return "valueChange"; + } } diff --git a/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenuCore.java b/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenuCore.java index 892faff41..662a7732d 100644 --- a/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenuCore.java +++ b/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenuCore.java @@ -25,6 +25,7 @@ public abstract class SelectMultiMenuCore extends HtmlInputText implements net.b protected enum PropertyKeys { accesskey, + ajax, allSelectedText, alt, binding, @@ -72,8 +73,18 @@ protected enum PropertyKeys { offsetSm, offsetXs, onchange, + onclick, + oncomplete, + ondeselectall, + ondropdownhidden, ondropdownhide, ondropdownshow, + ondropdownshown, + onerror, + oninitialized, + onselectall, + onsuccess, + process, radiobuttons, readonly, renderLabel, @@ -94,6 +105,7 @@ protected enum PropertyKeys { tooltipDelayHide, tooltipDelayShow, tooltipPosition, + update, visible; String toString; @@ -125,6 +137,22 @@ public void setAccesskey(String _accesskey) { getStateHelper().put(PropertyKeys.accesskey, _accesskey); } + /** + * Whether the Button submits the form with AJAX.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public boolean isAjax() { + return (boolean) (Boolean) getStateHelper().eval(PropertyKeys.ajax, false); + } + + /** + * Whether the Button submits the form with AJAX.

+ * Usually this method is called internally by the JSF engine. + */ + public void setAjax(boolean _ajax) { + getStateHelper().put(PropertyKeys.ajax, _ajax); + } + /** * Text which is displayed if every option has been selected.

* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. @@ -862,7 +890,7 @@ public void setOffsetXs(String _offsetXs) { } /** - * Client side callback to execute when input element loses focus and its value has been modified since gaining focus.

+ * AJAX event: A function which is triggered on the change event of the options. Note that the event is not triggered when selecting or deselecting options using the select and deselect methods provided by the plugin.

* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. */ public String getOnchange() { @@ -870,7 +898,7 @@ public String getOnchange() { } /** - * Client side callback to execute when input element loses focus and its value has been modified since gaining focus.

+ * AJAX event: A function which is triggered on the change event of the options. Note that the event is not triggered when selecting or deselecting options using the select and deselect methods provided by the plugin.

* Usually this method is called internally by the JSF engine. */ public void setOnchange(String _onchange) { @@ -878,7 +906,71 @@ public void setOnchange(String _onchange) { } /** - * Client side callback called when the drop-down area is hidden.

+ * The onclick attribute.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getOnclick() { + return (String) getStateHelper().eval(PropertyKeys.onclick); + } + + /** + * The onclick attribute.

+ * Usually this method is called internally by the JSF engine. + */ + public void setOnclick(String _onclick) { + getStateHelper().put(PropertyKeys.onclick, _onclick); + } + + /** + * JavaScript to be executed when ajax completes.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getOncomplete() { + return (String) getStateHelper().eval(PropertyKeys.oncomplete); + } + + /** + * JavaScript to be executed when ajax completes.

+ * Usually this method is called internally by the JSF engine. + */ + public void setOncomplete(String _oncomplete) { + getStateHelper().put(PropertyKeys.oncomplete, _oncomplete); + } + + /** + * AJAX event: This function is triggered when the select all option is used to deselect all options. Note that this can also be triggered manually using the .multiselect('deselectAll') method.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getOndeselectall() { + return (String) getStateHelper().eval(PropertyKeys.ondeselectall); + } + + /** + * AJAX event: This function is triggered when the select all option is used to deselect all options. Note that this can also be triggered manually using the .multiselect('deselectAll') method.

+ * Usually this method is called internally by the JSF engine. + */ + public void setOndeselectall(String _ondeselectall) { + getStateHelper().put(PropertyKeys.ondeselectall, _ondeselectall); + } + + /** + * AJAX event: A callback called after the dropdown has been closed.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getOndropdownhidden() { + return (String) getStateHelper().eval(PropertyKeys.ondropdownhidden); + } + + /** + * AJAX event: A callback called after the dropdown has been closed.

+ * Usually this method is called internally by the JSF engine. + */ + public void setOndropdownhidden(String _ondropdownhidden) { + getStateHelper().put(PropertyKeys.ondropdownhidden, _ondropdownhidden); + } + + /** + * AJAX event: A callback called when the dropdown is closed.

* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. */ public String getOndropdownhide() { @@ -886,7 +978,7 @@ public String getOndropdownhide() { } /** - * Client side callback called when the drop-down area is hidden.

+ * AJAX event: A callback called when the dropdown is closed.

* Usually this method is called internally by the JSF engine. */ public void setOndropdownhide(String _ondropdownhide) { @@ -894,7 +986,7 @@ public void setOndropdownhide(String _ondropdownhide) { } /** - * Client side callback called when the drop-down area is shown.

+ * AJAX event: A callback called when the dropdown is shown.

* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. */ public String getOndropdownshow() { @@ -902,13 +994,109 @@ public String getOndropdownshow() { } /** - * Client side callback called when the drop-down area is shown.

+ * AJAX event: A callback called when the dropdown is shown.

* Usually this method is called internally by the JSF engine. */ public void setOndropdownshow(String _ondropdownshow) { getStateHelper().put(PropertyKeys.ondropdownshow, _ondropdownshow); } + /** + * AJAX event: A callback called after the dropdown has been shown.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getOndropdownshown() { + return (String) getStateHelper().eval(PropertyKeys.ondropdownshown); + } + + /** + * AJAX event: A callback called after the dropdown has been shown.

+ * Usually this method is called internally by the JSF engine. + */ + public void setOndropdownshown(String _ondropdownshown) { + getStateHelper().put(PropertyKeys.ondropdownshown, _ondropdownshown); + } + + /** + * JavaScript to be executed when ajax results on an error (including both network errors and Java exceptions).

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getOnerror() { + return (String) getStateHelper().eval(PropertyKeys.onerror); + } + + /** + * JavaScript to be executed when ajax results on an error (including both network errors and Java exceptions).

+ * Usually this method is called internally by the JSF engine. + */ + public void setOnerror(String _onerror) { + getStateHelper().put(PropertyKeys.onerror, _onerror); + } + + /** + * AJAX event: A function which is triggered when the multiselect is finished initializing.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getOninitialized() { + return (String) getStateHelper().eval(PropertyKeys.oninitialized); + } + + /** + * AJAX event: A function which is triggered when the multiselect is finished initializing.

+ * Usually this method is called internally by the JSF engine. + */ + public void setOninitialized(String _oninitialized) { + getStateHelper().put(PropertyKeys.oninitialized, _oninitialized); + } + + /** + * AJAX event: This function is triggered when the select all option is used to select all options. Note that this can also be triggered manually using the .multiselect('selectAll') method.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getOnselectall() { + return (String) getStateHelper().eval(PropertyKeys.onselectall); + } + + /** + * AJAX event: This function is triggered when the select all option is used to select all options. Note that this can also be triggered manually using the .multiselect('selectAll') method.

+ * Usually this method is called internally by the JSF engine. + */ + public void setOnselectall(String _onselectall) { + getStateHelper().put(PropertyKeys.onselectall, _onselectall); + } + + /** + * JavaScript to be executed when ajax completes with success (i.e. there's neither a network error nor a Java exception).

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getOnsuccess() { + return (String) getStateHelper().eval(PropertyKeys.onsuccess); + } + + /** + * JavaScript to be executed when ajax completes with success (i.e. there's neither a network error nor a Java exception).

+ * Usually this method is called internally by the JSF engine. + */ + public void setOnsuccess(String _onsuccess) { + getStateHelper().put(PropertyKeys.onsuccess, _onsuccess); + } + + /** + * Comma or space separated list of ids or search expressions denoting which values are to be sent to the server.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getProcess() { + return (String) getStateHelper().eval(PropertyKeys.process); + } + + /** + * Comma or space separated list of ids or search expressions denoting which values are to be sent to the server.

+ * Usually this method is called internally by the JSF engine. + */ + public void setProcess(String _process) { + getStateHelper().put(PropertyKeys.process, _process); + } + /** * Set to true to display radiobuttons instead of checkboxes. Of course, in this case you can only select one option, so the widget's name is sort of a misnomer.

* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. @@ -1230,6 +1418,22 @@ public void setTooltipPosition(String _tooltipPosition) { getStateHelper().put(PropertyKeys.tooltipPosition, _tooltipPosition); } + /** + * Component(s) to be updated with ajax.

+ * @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. + */ + public String getUpdate() { + return (String) getStateHelper().eval(PropertyKeys.update); + } + + /** + * Component(s) to be updated with ajax.

+ * Usually this method is called internally by the JSF engine. + */ + public void setUpdate(String _update) { + getStateHelper().put(PropertyKeys.update, _update); + } + /** * This column is shown on a certain screen size and above. Legal values: lg, md, sm, xs.

* @return Returns the value of the attribute, or null, if it hasn't been set by the JSF file. diff --git a/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenuRenderer.java b/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenuRenderer.java index 3b2e61b66..569ae4989 100644 --- a/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenuRenderer.java +++ b/src/main/java/net/bootsfaces/component/selectMultiMenu/SelectMultiMenuRenderer.java @@ -32,6 +32,7 @@ import javax.faces.component.UIForm; import javax.faces.component.UISelectItem; import javax.faces.component.UISelectItems; +import javax.faces.component.behavior.ClientBehaviorHolder; import javax.faces.component.html.HtmlOutputText; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; @@ -116,11 +117,13 @@ public void decode(FacesContext context, UIComponent component) { } menu.setSubmittedValue(result); menu.setValid(true); + new AJAXRenderer().decode(context, component, clientId+"Inner"); return; } menu.setSubmittedValue(null); menu.setValid(true); + new AJAXRenderer().decode(context, component, clientId+"Inner"); } /** Generates the HTML code for this component. */ @@ -240,21 +243,6 @@ public void encodeBegin(FacesContext context, UIComponent component) throws IOEx options += "," + "dropRight:" + "true"; } - String onChange = menu.getOnchange(); - if (onChange != null) { - options += "," + "onChange:" + onChange; - } - - String onDropdownShow = menu.getOndropdownshow(); - if (onDropdownShow != null) { - options += "," + "onDropdownShow:" + onDropdownShow; - } - - String onDropdownHide = menu.getOndropdownhide(); - if (onDropdownHide != null) { - options += "," + "onDropdownHide:" + onDropdownHide; - } - String buttonClass = menu.getButtonClass(); if (buttonClass != null) { options += "," + "buttonClass:'" + buttonClass + "'"; @@ -295,14 +283,40 @@ public void encodeBegin(FacesContext context, UIComponent component) throws IOEx if (buttonWidth > 0) { options += "," + "buttonWidth:'" + buttonWidth + "px'"; } + + for (String event: menu.getJQueryEvents().keySet()) { + String optionalParameterList="event"; + if (menu.getJQueryEventParameterLists()!=null) { + if (menu.getJQueryEventParameterLists().get(event) != null) { + optionalParameterList = menu.getJQueryEventParameterLists().get(event); + } + } + StringBuffer code = new StringBuffer(); + AJAXRenderer.generateAJAXCallForASingleEvent(context, menu, + null, code, null, null, true, + event, null, optionalParameterList); + if (code.length()>0) { + String result = menu.getJQueryEvents().get(event); + result += ":"; + String realCode = code.toString().replace(".callAjax(this", ".callAjax(document.getElementById('" + clientId + "Inner')" ); + if (!realCode.startsWith("function")) { + result += "function(" + optionalParameterList + ")"; + result += "{"; + result += realCode; + result += "}"; + } else { // backward compatibility to BootsFaces 1.0.0 and earlier + result += realCode; + } + options += "," + result; + } + } + if (options.length() > 0) { options = "{" + options.substring(1, options.length()) + "}"; } - String js = "$(document).ready(function() {$('#" + clientId + "Inner').multiselect(" + options + ");});\n"; context.getResponseWriter().write(""); - } /** @@ -476,6 +490,7 @@ protected void renderSelectTag(FacesContext context, ResponseWriter rw, String c SelectMultiMenu menu) throws IOException { renderSelectTag(rw, menu); renderSelectTagAttributes(rw, clientId, name, menu); + AJAXRenderer.generateBootsFacesAJAXAndJavaScript(FacesContext.getCurrentInstance(), menu, rw, false); Object selectedOption = getValue2Render(context, menu); String[] optionList; if (selectedOption == null) { diff --git a/src/main/meta/META-INF/bootsfaces-b.taglib.xml b/src/main/meta/META-INF/bootsfaces-b.taglib.xml index 947de2810..8b67e4a30 100644 --- a/src/main/meta/META-INF/bootsfaces-b.taglib.xml +++ b/src/main/meta/META-INF/bootsfaces-b.taglib.xml @@ -19396,6 +19396,12 @@ false java.lang.String + + + ajax + false + java.lang.Boolean + all-selected-text @@ -19877,23 +19883,83 @@ java.lang.String - + onchange false java.lang.String - + + onclick + false + java.lang.String + + + + oncomplete + false + java.lang.String + + + + ondeselectall + false + java.lang.String + + + + ondropdownhidden + false + java.lang.String + + + ondropdownhide false java.lang.String - + ondropdownshow false java.lang.String + + + ondropdownshown + false + java.lang.String + + + + onerror + false + java.lang.String + + + + oninitialized + false + java.lang.String + + + + onselectall + false + java.lang.String + + + + onsuccess + false + java.lang.String + + + + process + false + java.lang.String + radiobuttons @@ -20086,6 +20152,12 @@ false java.lang.String + + + update + false + java.lang.String + value diff --git a/xtext/BootsFaces.jsfdsl b/xtext/BootsFaces.jsfdsl index f8eaecb5e..0e79c39d5 100644 --- a/xtext/BootsFaces.jsfdsl +++ b/xtext/BootsFaces.jsfdsl @@ -1855,9 +1855,6 @@ widget selectMultiMenu radiobuttons Boolean default "false" "Set to true to display radiobuttons instead of checkboxes. Of course, in this case you can only select one option, so the widget's name is sort of a misnomer." disable-if-empty Boolean default "false" "If true, the button is disabled if no options are given." dropRight Boolean default "false" "Moves the drop-down-area from the left hand side to the right hand side." - onchange "Client side callback to execute when input element loses focus and its value has been modified since gaining focus." - ondropdownshow "Client side callback called when the drop-down area is shown." - ondropdownhide "Client side callback called when the drop-down area is hidden." buttonClass String "The CSS class of the button." style-class "Style class of the input element. Is translated to the buttonContainer attribute." button-width Integer "The width of the multiselect button may be fixed using this option." @@ -1873,6 +1870,17 @@ widget selectMultiMenu tabindex "Position of this element in the tabbing order for the current document. This value must be an integer between 0 and 32767." title "Advisory tooltip information." value inherited "Value." + + onselectall "AJAX event: This function is triggered when the select all option is used to select all options. Note that this can also be triggered manually using the .multiselect('selectAll') method." + ondropdownshow "AJAX event: A callback called when the dropdown is shown." + ondropdownhidden "AJAX event: A callback called after the dropdown has been closed." + onchange "AJAX event: A function which is triggered on the change event of the options. Note that the event is not triggered when selecting or deselecting options using the select and deselect methods provided by the plugin." + ondropdownhide "AJAX event: A callback called when the dropdown is closed." + ondeselectall "AJAX event: This function is triggered when the select all option is used to deselect all options. Note that this can also be triggered manually using the .multiselect('deselectAll') method." + ondropdownshown "AJAX event: A callback called after the dropdown has been shown." + oninitialized "AJAX event: A function which is triggered when the multiselect is finished initializing." + + +ajax +tooltip +responsive +responsiveLabel