-
FEATURED COMPONENTS
First time here? Check out the FAQ!
I am trying to render a chosenbox with different styles based on an active flag. In the drop down, I would like to bold the item based on the flag, and when selected I would like to have it with a different background color. Is this possible?
Here is the way I am working with the chosenbox. I have tried to change the style on the label with no success.
<chosenbox style="overflow-y:auto;" width="40%" height="10%" model="@load(vm.list)" selectedobjects="@bind(vm.selectedList)">
<template name="model">
<label value="@load(each.name)"/>
</template>
</chosenbox>
yes, its possible but you need to rewrite the Chosenbox.java and the js for it.
I have a sample in mine project, but I need to say that the java file isn't very clean (I check for mine classes).
very fast the changes :
private transient Map<Object, String> _optElementSclassMap;
with getters,.../* Chosenbox.java
Purpose:
Description:
History:
Tue Nov 16 15:15:52 TST 2011, Created by benbai
Copyright (C) 2011 Potix Corporation. All Rights Reserved.
*/
package org.zkoss.zkmax.zul;
import be.chillworld.model.office.Office;
import be.chillworld.model.office.enums.OfficeType;
import be.chillworld.model.tag.Tag;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.zkoss.lang.Classes;
import org.zkoss.lang.Objects;
import org.zkoss.lang.Strings;
import org.zkoss.xel.VariableResolver;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.InputEvent;
import org.zkoss.zk.ui.event.OpenEvent;
import org.zkoss.zk.ui.event.SelectEvent;
import org.zkoss.zk.ui.sys.DesktopCtrl;
import org.zkoss.zk.ui.util.ComponentCloneListener;
import org.zkoss.zk.ui.util.ForEachStatus;
import org.zkoss.zk.ui.util.Template;
import org.zkoss.zul.ItemRenderer;
import org.zkoss.zul.Label;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListSubModel;
import org.zkoss.zul.event.ListDataEvent;
import org.zkoss.zul.event.ListDataListener;
import org.zkoss.zul.ext.Selectable;
/**
* A component that similar to Combobox but handle the multi-selection and the
* select order.
* <p>
* Default {@link #getZclass}: z-chosenbox. It does not create child widgets for
* each data, so the memory usage is much lower at the server.
*
* @author benbai
* @since 6.0.1
*
*/
public class Chosenbox extends HtmlBasedComponent {
private static final long serialVersionUID = -4281187335573856218L;
/**
* The name of this component.
*/
private String _name;
private String _value = "";
/**
* Whether this component is disabled.
*/
private boolean _disabled;
// for Bug ZK-1746, we use three states, positive, -1, and Integer.MIN_VALUE
private int _jsel = -1;
private boolean _disabledEventListener = false; // for Bug ZK-1746
/**
* The open state of this component.
*/
private boolean _open;
/**
* Whether send event back to server when user do select operation with a
* value that not in the model.
*/
private boolean _creatable;
/**
* The place holder of this component.
*/
private String _emptyMessage;
/**
* The text that displayed while no match result, and not creatable.<br/>
* Creatable denotes the component is set to creatable and the value not in
* the model.
*/
private String _noResultsText;
/**
* The text that displayed while no match result and creatable.<br/>
* Creatable denotes the component is set to creatable and the value not in
* the model.
*/
private String _createMessage;
/**
* The specified keys that works as ENTER key, will not considered as input
* but do select action.
*/
private String _separator;
/**
* The model of this component.
*/
private transient ListModel<?> _model;
/**
* The submodel used if _model is ListSubModel.
*/
private transient ListModel<?> _subModel;
private transient ListDataListener _dataListener;
private transient EventListener<InputEvent> _eventListener;
private transient ItemRenderer<?> _renderer;
private transient boolean _childable;
/**
* For uuid - element - renderedContent
*/
private transient Map<String, Object> _optIdElementMap;
private transient Map<Object, String> _optElementIdMap;
private transient Map<Object, String> _optElementContentMap;
private transient Map<Object, String> _optElementSclassMap;
private transient List<Object> _options;
private transient List<Object> _chgSel;
private static final String ATTR_ON_INIT_RENDER_POSTED = "org.zkoss.zul.onInitLaterPosted";
private static final String ATTR_ON_INIT_RENDERSEL_POSTED = "org.zkoss.zul.onInitSelLaterPosted";
static {
addClientEvent(Chosenbox.class, Events.ON_SELECT, CE_DUPLICATE_IGNORE | CE_IMPORTANT);
addClientEvent(Chosenbox.class, Events.ON_OPEN, CE_IMPORTANT);
addClientEvent(Chosenbox.class, "onSelectSubModel", CE_IMPORTANT);
addClientEvent(Chosenbox.class, "onSearch", CE_DUPLICATE_IGNORE | CE_IMPORTANT);
addClientEvent(Chosenbox.class, "onSearching", CE_DUPLICATE_IGNORE | CE_IMPORTANT);
// ZK-1053
addClientEvent(Chosenbox.class, Events.ON_FOCUS, CE_DUPLICATE_IGNORE);
addClientEvent(Chosenbox.class, Events.ON_BLUR, CE_DUPLICATE_IGNORE);
// ZK-3098
addClientEvent(Chosenbox.class, "onItemClick", CE_DUPLICATE_IGNORE);
}
public String getZclass() {
return _zclass == null ? "z-chosenbox" : _zclass;
}
public void setOpen(boolean open) {
if (_open != open) {
_open = open;
smartUpdate("open", _open);
}
}
public boolean isOpen() {
return _open;
}
/**
* Returns whether it is disabled.
* <p>
* Default: false.
*/
public boolean isDisabled() {
return _disabled;
}
/**
* Sets whether it is disabled.
*/
public void setDisabled(boolean disabled) {
if (_disabled != disabled) {
_disabled = disabled;
smartUpdate("disabled", _disabled);
}
}
/**
* Returns the name of this component.
* <p>
* Default: null.
* <p>
* The name is used only to work with "legacy" Web application that handles
* user's request by servlets. It works only with HTTP/HTML-based browsers.
* It doesn't work with other kind of clients.
* <p>
* Don't use this method if your application is purely based on ZK's
* event-driven model.
*/
public String getName() {
return _name;
}
/**
* Sets the name of the input element of this component.
* <p>
* The name is used only to work with "legacy" Web application that handles
* user's request by servlets. It works only with HTTP/HTML-based browsers.
* It doesn't work with other kind of clients.
* <p>
* Don't use this method if your application is purely based on ZK's
* event-driven model.
*
* @param name the name of this component.
*/
public void setName(String name) {
if (name != null && name.length() == 0) {
name = null;
}
if (!Objects.equals(_name, name)) {
_name = name;
smartUpdate("name", name);
}
}
/**
* Returns the emptyMessage of the input of this component.
* <p>
* Default: null.
* <p>
* The emptyMessage will be displayed in input if nothing selected and not
* focused.
*/
public String getEmptyMessage() {
return _emptyMessage;
}
/**
* Sets the emptyMessage of the input of this component.
* <p>
* The emptyMessage will be displayed in input if nothing selected and not
* focused.
*
* @param emptyMessage the emptyMessage of the input of this component.
*/
public void setEmptyMessage(String emptyMessage) {
if (emptyMessage != null && emptyMessage.length() == 0) {
emptyMessage = null;
}
if (!Objects.equals(_emptyMessage, emptyMessage)) {
_emptyMessage = emptyMessage;
smartUpdate("emptyMessage", getEmptyMessage());
}
}
/**
* Returns the no-result text of this component.
* <p>
* Default: null.
* <p>
* The no-result text will be displayed in popup if nothing match to the
* input value and can not create either, the syntax "{0}" will be replaced
* with the input value at client side.
*/
public String getNoResultsText() {
return _noResultsText;
}
/**
* Sets the no-result text of this component.
* <p>
* The no-result text will be displayed in popup if nothing match to the
* input value and can not create either, the syntax "{0}" will be replaced
* with the input value at client side.
*
* @param noResultsText the no-result text of this component.
*/
public void setNoResultsText(String noResultsText) {
if (noResultsText != null && noResultsText.length() == 0) {
noResultsText = null;
}
if (!Objects.equals(_noResultsText, noResultsText)) {
_noResultsText = noResultsText;
smartUpdate("noResultsText", getNoResultsText());
}
}
/**
* Returns the create message of this component.
* <p>
* Default: null.
* <p>
* The create message will be displayed in popup if nothing match to the
* input value but can create as new label, the syntax "{0}" will be
* replaced with the input value at client side.
*/
public String getCreateMessage() {
return _createMessage;
}
/**
* Sets the create message of this component.
* <p>
* The create message will be displayed in popup if nothing match to the
* input value but can create as new label, the syntax "{0}" will be
* replaced with the input value at client side.
*
* @param createMessage the create message of this component.
*/
public void setCreateMessage(String createMessage) {
if (createMessage != null && createMessage.length() == 0) {
createMessage = null;
}
if (!Objects.equals(_createMessage, createMessage)) {
_createMessage = createMessage;
smartUpdate("createMessage", getCreateMessage());
}
}
/**
* Returns the separate chars of this component.
* <p>
* Support: 0-9, A-Z (case insensitive), and ,.;'[]/\-=
* <p>
* Default: null.
* <p>
* The separate chars will work as 'Enter' key, it will not considered as
* input value but send onSerch or onSearching while key up.
*/
public String getSeparator() {
return _separator;
}
/**
* Sets the separate chars of this component.
* <p>
* Support: 0-9, A-Z (case insensitive), and ,.;'[]/\-=
* <p>
* The separate chars will work as 'Enter' key, it will not considered as
* input value but send onSerch or onSelect while key up.
*/
public void setSeparator(String separator) {
if (separator != null && separator.length() == 0) {
separator = null;
}
if (!Objects.equals(_separator, separator)) {
_separator = separator;
smartUpdate("separator", getSeparator());
}
}
/**
* Returns the selected objects.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> Set<T> getSelectedObjects() {
if (getModel() == null) {
return null;
}
Set selection = new LinkedHashSet();
selection.addAll(getSelectableModel().getSelection());
return selection;
}
/**
* Sets the selected objects.
*
* @param objects the objects to select.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public void setSelectedObjects(Collection<?> objects) {
if (getModel() != null) {
Set selection = new LinkedHashSet();
Selectable smodel = getSelectableModel();
ListModel<String> lm = getModel();
boolean found = false;
Iterator it = objects.iterator();
while (it.hasNext()) {
Object obj = it.next();
for (int i = 0; i < lm.getSize(); i++) {
if (Objects.equals(lm.getElementAt(i), obj)) {
if (_jsel <= -1 || _jsel > i) {
_jsel = i;
}
found = true;
selection.add(lm.getElementAt(i));
break;
}
}
if (!found) {
throw new UiException("No such item: " + obj);
}
found = false;
}
smodel.setSelection(selection);
prepareChgSel();
smartUpdate("chgSel", encloseOptions(getChgSel()));
}
}
/**
* Returns the index of the selected item (-1 if no one is selected).
*/
public int getSelectedIndex() {
if (_jsel == Integer.MIN_VALUE) {
Selectable<Object> selectableModel = this.getSelectableModel();
if (selectableModel != null) {
if (selectableModel.isSelectionEmpty()) {
_jsel = -1;
} else {
syncSelectedIndex(selectableModel.getSelection().iterator().next());
}
} else {
_jsel = -1;
}
}
return _jsel;
}
/**
* Sets the index of the selected item (-1 if no one is selected). If model
* already exists, it will update the selection of model.
*
* @param jsel the index to select.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public void setSelectedIndex(int jsel) {
// update selection if model already exists
ListModel model = getModel();
if (jsel < -1) {
jsel = -1;
}
if (jsel != _jsel || (model != null && model.getSize() > 1)) {
_jsel = jsel;
if (model != null) {
if (model.getSize() <= _jsel) {
_jsel = model.getSize() - 1;
}
Set selection = new LinkedHashSet();
if (_jsel >= 0) {
selection.add(model.getElementAt(_jsel));
}
getSelectableModel().setSelection(selection);
}
smartUpdate("selectedIndex", _jsel);
}
}
/**
* Returns whether can create new item.
* <p>
* Default: false.
* <p>
* true: will show create message while value of input not exists.
* <p>
* false: will show no result message while value of input not exists.
*/
public boolean isCreatable() {
return _creatable;
}
/**
* Sets whether can create new item.
* <p>
* Default: false.
* <p>
* true: will show create message while value of input not exists.
* <p>
* false: will show no-result text while value of input not exists.
*
* @param creatable the boolean value.
*/
public void setCreatable(boolean creatable) {
if (_creatable != creatable) {
_creatable = creatable;
smartUpdate("creatable", _creatable);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> ItemRenderer<T> getRealRenderer() {
final ItemRenderer renderer = getItemRenderer();
return renderer != null ? renderer : (ItemRenderer) _defRend;
}
/**
* Returns the renderer to render each item, or null if the default renderer
* is used.
*/
@SuppressWarnings("unchecked")
public <T> ItemRenderer<T> getItemRenderer() {
return (ItemRenderer<T>) _renderer;
}
/**
* Returns the model associated with this chosenbox, or null if this
* chosenbox is not associated with any list data model.
*/
@SuppressWarnings("unchecked")
public <T> ListModel<T> getModel() {
return (ListModel<T>) _model;
}
/**
* Sets the list model associated with this chosenbox. If a non-null model
* is assigned, no matter whether it is the same as the previous, it will
* always cause re-render.
*
* @param model the list model to associate, or null to dis-associate any
* previous model.
* @exception UiException if failed to initialize with the model
*/
@SuppressWarnings({"rawtypes"})
public void setModel(ListModel<?> model) {
if (model != null) {
if (!(model instanceof Selectable)) {
throw new UiException(model.getClass() + " must implement " + Selectable.class);
}
if (_model != model) {
Selectable smodel;
if (_model != null) {
_model.removeListDataListener(_dataListener);
// B70-ZK-2004: Remove event listener and sub model.
if (_model instanceof ListSubModel) {
removeEventListener("onSearching", _eventListener);
_subModel = null;
}
}
_model = model;
resetOpts();
smodel = getSelectableModel();
smodel.setMultiple(true);
// B70-ZK-2004: Fix selected index, clear all the previous selection.
if (_jsel >= 0) {
_jsel = -1;
}
initDataListener();
}
} else if (_model != null) {
_model.removeListDataListener(_dataListener);
if (_model instanceof ListSubModel) {
removeEventListener("onSearching", _eventListener);
// B70-ZK-2004: Remove sub model.
_subModel = null;
}
_model = null;
}
smartUpdate("renderByServer", _model instanceof ListSubModel);
postOnInitRender();
}
private String getOptIdByElement(Object element, boolean autoCreate) {
String uuid = getOptElementIdMap().get(element);
if (uuid == null && autoCreate) {
uuid = ((DesktopCtrl) getDesktop()).getNextUuid(this);
getOptIdElementMap().put(uuid, element);
getOptElementIdMap().put(element, uuid);
}
return uuid;
}
private Object getOptElementById(String uuid) {
return getOptIdElementMap().get(uuid);
}
private String getOptContentByElement(Object element) {
return getOptElementContentMap().get(element);
}
private String getOptSclassByElement(Object element) {
return getOptElementSclassMap().get(element);
}
private Map<String, Object> getOptIdElementMap() {
if (_optIdElementMap == null) {
_optIdElementMap = new HashMap<String, Object>();
}
return _optIdElementMap;
}
private Map<Object, String> getOptElementContentMap() {
if (_optElementContentMap == null) {
_optElementContentMap = new HashMap<Object, String>();
}
return _optElementContentMap;
}
private Map<Object, String> getOptElementSclassMap() {
if (_optElementSclassMap == null) {
_optElementSclassMap = new HashMap<Object, String>();
}
return _optElementSclassMap;
}
private Map<Object, String> getOptElementIdMap() {
if (_optElementIdMap == null) {
_optElementIdMap = new HashMap<Object, String>();
}
return _optElementIdMap;
}
private List<Object> getChgSel() {
if (_chgSel == null) {
_chgSel = new LinkedList<Object>();
}
return _chgSel;
}
private List<Object> getOptions() {
if (_options == null) {
_options = new LinkedList<Object>();
}
return _options;
}
private void resetOpts() {
_optElementContentMap = null;
_optIdElementMap = null;
_optElementIdMap = null;
_options = null;
_chgSel = null;
}
private List<Map<String, Object>> encloseOptions(List<Object> opts) {
List<Map<String, Object>> result = new LinkedList<Map<String, Object>>();
if (opts != null) {
for (Object opt : opts) {
Map<String, Object> map = new HashMap<String, Object>(2);
map.put("id", getOptIdByElement(opt, true));
map.put("content", getOptContentByElement(opt));
map.put("sclass", getOptSclassByElement(opt));
result.add(map);
}
}
return result;
}
/**
* Clear all selected objects.
*/
@SuppressWarnings("rawtypes")
public void clearSelection() {
Selectable smodel = getSelectableModel();
if (smodel != null) {
_jsel = -1;
smodel.clearSelection();
_chgSel = null;
smartUpdate("chgSel", encloseOptions(null));
}
}
/**
* Add an item into selection.
*
* @param o the object to add.
*/
@SuppressWarnings("rawtypes")
public void addItemToSelection(Object o) {
if (getModel() != null) {
ListModel lm = getModel();
int size = lm.getSize();
for (int i = 0; i < size; i++) {
Object elmn = lm.getElementAt(i);
if (Objects.equals(elmn, o)) {
getSelectableModel().addToSelection(elmn);
if (i < _jsel || _jsel <= 0) {
_jsel = i;
}
}
}
}
}
/**
* Remove an item from selection.
*
* @param o the object to remove.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public void removeItemFromSelection(Object o) {
if (getModel() != null) {
Selectable smodel = getSelectableModel();
Set selection = new LinkedHashSet();
selection.addAll(smodel.getSelection());
if (selection.remove(o)) {
smodel.setSelection(selection);
}
}
}
// prepare the selected items
private void prepareChgSel() {
prepareItems(null, true, _model);
}
// used to 'fake' that it can has children
protected boolean isChildable() {
return _childable;
}
// prepare all data, include options and selected items
private void prepareData() {
if (!(_model instanceof ListSubModel)) {
prepareItems(null, false, _model);
}
if (_model != null && !getSelectableModel().isSelectionEmpty()) {
prepareChgSel();
}
}
/**
* prepare the list content or selected items to render,
*
* @param prefix Only add the item starts with it if it is not null.
* @param excludeUnselected Only add selected item, with select order.
* @param model the model to render.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private void prepareItems(String prefix, boolean excludeUnselected, ListModel model) {
if (model != null) {
List<Object> optList = new LinkedList<Object>();
final boolean old = _childable;
final boolean isSubModel = getModel() instanceof ListSubModel;
if (prefix != null) {
prefix = prefix.toLowerCase(java.util.Locale.ENGLISH);
}
try {
_childable = true;
final ItemRenderer renderer = getRealRenderer();
if (excludeUnselected) { // render selected item
Selectable smodel = getSelectableModel();
Set selection = smodel.getSelection();
// render selected items with index
for (Iterator it = selection.iterator(); it.hasNext();) {
Object item = it.next();
for (int i = 0, sz = model.getSize(); i < sz; i++) { // the index i is required by render
if (Objects.equals(model.getElementAt(i), item)) {
String s = renderer.render(this, item, i);
// B65-ZK-2019: Add all if is ListSubModel
if (isSubModel || prefix == null
|| s.toLowerCase(java.util.Locale.ENGLISH).startsWith(prefix)) {
getOptElementContentMap().put(item, s);
optList.add(item);
}
}
}
}
if (!optList.isEmpty()) {
_chgSel = optList;
} else {
_chgSel = new LinkedList<Object>();
}
} else {
Template temp = getTemplate("model");
// Label createdLabel = (Label) (temp.create(null, null, null, null)[0]);
// String itemSclass = createdLabel.getSclass();
for (int i = 0, sz = model.getSize(); i < sz; i++) {
Object o = model.getElementAt(i);
String s = renderer.render(this, o, i);
// B65-ZK-2019: Add all if is ListSubModel
// ZK-2080: should skip if template exists
if ((isSubModel && temp == null) || prefix == null
|| s.toLowerCase(java.util.Locale.ENGLISH).startsWith(prefix)) {
getOptElementContentMap().put(o, s);
if (o instanceof Office) {
Office office = (Office) o;
String color = office.getActive()?(OfficeType.values(OfficeType.SubType.GROUP).contains(office.getType())?"blue":""):"red";
getOptElementSclassMap().put(o, color);
}else if (o instanceof Tag) {
getOptElementSclassMap().put(o,((Tag) o).getColor().getCssClass());
}else {
getOptElementSclassMap().put(o,"");
}
optList.add(o);
}
}
if (!optList.isEmpty()) {
_options = optList;
} else {
_options = new LinkedList<Object>();
}
}
} catch (Exception e) {
throw UiException.Aide.wrap(e);
} finally {
//clear possible children created in renderer
_childable = old;
getChildren().clear();
}
} else // model is null
{
if (excludeUnselected) {
_chgSel = new LinkedList<Object>();
} else {
_options = new LinkedList<Object>();
}
}
}
// selectable
@SuppressWarnings({"unchecked", "rawtypes"})
private Selectable<Object> getSelectableModel() {
return (Selectable) _model;
}
// update drop down content only
@SuppressWarnings("rawtypes")
private void updateListContent(String prefix, ListModel subModel) {
if (Strings.isEmpty(prefix)) {
smartUpdate("listContent", new String[0]);
} else {
prepareItems(prefix, false, subModel);
smartUpdate("listContent", encloseOptions(getOptions()));
}
}
@SuppressWarnings("rawtypes")
private void syncSelectedIndex(Object item) {
ListModel lm = getModel();
if (lm != null) {
Set selection = getSelectableModel().getSelection();
if (!selection.isEmpty()) {
// B65-ZK-2028: Sync SelectedIndex when using ListSubModel
Object element;
if (item != null) {
int end = _jsel >= 0 ? _jsel : lm.getSize();
for (int i = 0; i < end; i++) {
element = lm.getElementAt(i);
if (Objects.equals(element, item) && selection.contains(element)) {
_jsel = i;
break;
}
}
} else {
int end = lm.getSize();
for (int i = 0; i < end; i++) {
element = lm.getElementAt(i);
if (selection.contains(element)) {
_jsel = i;
break;
}
}
}
} else {
_jsel = -1;
}
}
}
@SuppressWarnings("rawtypes")
private void initDataListener() {
if (_dataListener == null) {
_dataListener = new ListDataListener() {
public void onChange(ListDataEvent event) {
// do not need invalidate if just SELECTION_CHANGED
// only update selection
if (event.getType() == ListDataEvent.SELECTION_CHANGED) {
if (!_disabledEventListener) // Bug ZK-1746, ignore wrong loop sent from client
{
postOnInitSelection();
}
} else // update all data
{
postOnInitRender();
}
}
};
}
if (_eventListener == null) {
_eventListener = new EventListener<InputEvent>() {
public void onEvent(InputEvent event) throws Exception {
if (getModel() instanceof ListSubModel) {
// use -1 to be consistent with Combobox
// ZK-2080: should not use getSubModel() when use template
Template temp = getTemplate("model");
if (temp == null) {
_subModel = ((ListSubModel) _model).getSubModel(event.getValue(), -1);
} else {
_subModel = _model;
}
updateListContent(event.getValue(), _subModel);
}
}
};
}
_model.addListDataListener(_dataListener);
if (_model instanceof ListSubModel) {
addEventListener("onSearching", _eventListener);
}
}
/**
* Sets the renderer which is used to render each item if {@link #getModel}
* is not null.
*
* <p>
* Note: changing a render will not cause the chosenbox to re-render. If you
* want it to re-render, you could assign the same model again (i.e.,
* setModel(getModel())), or fire an {@link ListDataEvent} event.
*
* @param renderer the renderer, or null to use the default.
* @exception UiException if failed to initialize with the model
*/
public void setItemRenderer(ItemRenderer<?> renderer) {
if (_renderer != renderer) {
_renderer = renderer;
invalidate();
}
}
/**
* Sets the renderer by use of a class name. It creates an instance
* automatically.
*/
@SuppressWarnings("rawtypes")
public void setItemRenderer(String clsnm) throws ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InstantiationException, java.lang.reflect.InvocationTargetException {
if (clsnm != null) {
setItemRenderer((ItemRenderer) Classes.newInstanceByThread(clsnm));
}
}
// post the event that update data
private void postOnInitRender() {
if (getAttribute(ATTR_ON_INIT_RENDER_POSTED) == null) {
setAttribute(ATTR_ON_INIT_RENDER_POSTED, Boolean.TRUE);
// remove onInitSelection if exist
removeAttribute(ATTR_ON_INIT_RENDERSEL_POSTED);
Events.postEvent("onInitRender", this, null);
}
}
public void onInitRender() {
removeAttribute(ATTR_ON_INIT_RENDER_POSTED);
// remove onInitSelection if exist
removeAttribute(ATTR_ON_INIT_RENDERSEL_POSTED);
resetOpts();
prepareData();
super.invalidate();
}
private void postOnInitSelection() {
// do not post if already posted or onInitRender is posted
if (getAttribute(ATTR_ON_INIT_RENDER_POSTED) == null && getAttribute(ATTR_ON_INIT_RENDERSEL_POSTED) == null) {
setAttribute(ATTR_ON_INIT_RENDERSEL_POSTED, Boolean.TRUE);
Events.postEvent("onInitSelection", this, null);
}
}
public void onInitSelection() {
// do nothing if attribute is removed
if (getAttribute(ATTR_ON_INIT_RENDERSEL_POSTED) != null) {
removeAttribute(ATTR_ON_INIT_RENDERSEL_POSTED);
prepareData();
smartUpdate("chgSel", encloseOptions(getChgSel()));
}
}
// -- ComponentCtrl --//
public void invalidate() {
if (_model != null && (_model.getSize() > 0 && ((_options == null && !(_model instanceof ListSubModel))
|| (getSelectableModel().getSelection().size() > 0)))) {
// post onInitRender to rerender content
postOnInitRender();
} else {
super.invalidate();
}
org.zkoss.zkex.rt.Runtime.init(this);
}
// Cloneable//
@SuppressWarnings("rawtypes")
public Object clone() {
final Chosenbox clone = (Chosenbox) super.clone();
if (clone._model != null) {
if (clone._model instanceof ComponentCloneListener) {
final ListModel model = (ListModel) ((ComponentCloneListener) clone._model).willClone(clone);
if (model != null) {
clone._model = model;
}
}
clone.postOnInitRender();
// we use the same data model but we have to create a new listener
clone._dataListener = null;
clone.initDataListener();
}
return clone;
}
// -- Serializable --//
// NOTE: they must be declared as private
private synchronized void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
s.defaultWriteObject();
willSerialize(_model);
s.writeObject(
_model instanceof java.io.Serializable || _model instanceof java.io.Externalizable ? _model : null);
willSerialize(_renderer);
s.writeObject(_renderer instanceof java.io.Serializable || _renderer instanceof java.io.Externalizable
? _renderer : null);
}
@SuppressWarnings("rawtypes")
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
_model = (ListModel) s.readObject();
didDeserialize(_model);
_renderer = (ItemRenderer) s.readObject();
didDeserialize(_renderer);
if (_model != null) {
initDataListener();
}
postOnInitRender();
}
public void sessionWillPassivate(Page page) {
super.sessionWillPassivate(page);
willPassivate(_model);
willPassivate(_renderer);
}
public void sessionDidActivate(Page page) {
super.sessionDidActivate(page);
didActivate(_model);
didActivate(_renderer);
if (_model != null) {
postOnInitRender();
}
}
protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer) throws IOException {
super.renderProperties(renderer);
org.zkoss.zkex.rt.Runtime.init(this);
render(renderer, "items", encloseOptions(getOptions()));
render(renderer, "chgSel", encloseOptions(getChgSel()));
render(renderer, "name", _name);
render(renderer, "disabled", isDisabled());
render(renderer, "emptyMessage", getEmptyMessage());
render(renderer, "noResultsText", getNoResultsText());
render(renderer, "separator", getSeparator());
render(renderer, "createMessage", getCreateMessage());
renderer.render("creatable", _creatable);
renderer.render("renderByServer", _model instanceof ListSubModel);
render(renderer, "open", _open);
}
private <T> Set<T> collectUnselectedObjects(Set<T> previousSelection, Set<T> currentSelection) {
Set<T> prevSeldItems = previousSelection != null ? new LinkedHashSet<T>(previousSelection)
: new LinkedHashSet<T>();
if (currentSelection != null && prevSeldItems.size() > 0) {
prevSeldItems.removeAll(currentSelection);
}
return prevSeldItems;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public void service(org.zkoss.zk.au.AuRequest request, boolean everError) {
final String cmd = request.getCommand();
if (cmd.equals(Events.ON_SELECT)) {
Map<String, Object> data = request.getData();
int newMin = (Integer) data.get("min");
List<String> selItemUuids = (List<String>) data.get("selectedUuids");
final Set<Object> prevSet = getSelectableModel().getSelection();
final Set prevSelection = prevSet != null && !prevSet.isEmpty() ? new LinkedHashSet(prevSet) : null;
ListModel model = getModel();
Selectable smodel = getSelectableModel();
Set selection = new LinkedHashSet();
//Bug ZK-1660: selection should be Object in ListModel instead of String
for (String itemUuid : selItemUuids) {
Object o = getOptElementById(itemUuid);
if (o != null) {
selection.add(o);
}
}
if (selection.size() == 0) { // selection is empty
_jsel = -1;
} else if (_jsel > newMin) {
_jsel = newMin;
}
try {
_disabledEventListener = true;
smodel.setSelection(selection);
} finally {
_disabledEventListener = false;
}
Set unselection = collectUnselectedObjects(prevSelection, selection);
Events.postEvent(new SelectEvent(Events.ON_SELECT, this, null, null, null, selection, prevSelection,
unselection, null, null, 0));
} else if (cmd.equals("onSelectSubModel")) {
// B65-ZK-2028: Select or deselect item when using ListSubModel
String itemUuid = (String) request.getData().get("itemUuid");
Boolean select = (Boolean) request.getData().get("select");
Selectable smodel = getSelectableModel();
final Set<Object> prevSet = getSelectableModel().getSelection();
final Set prevSelection = prevSet != null && !prevSet.isEmpty() ? new LinkedHashSet(prevSet) : null;
Set selection;
Object selectItem = null;
try {
_disabledEventListener = true;
selectItem = getOptElementById(itemUuid);
if (selectItem != null) {
List<Object> chgSel = getChgSel();
if (select) {
smodel.addToSelection(selectItem);
chgSel.add(getOptElementById(itemUuid));
} else {
smodel.removeFromSelection(selectItem);
for (int i = 0; i < chgSel.size(); i++) {
if (itemUuid.equals(chgSel.get(i))) {
chgSel.remove(i);
break;
}
}
}
_chgSel = chgSel;
}
selection = smodel.getSelection();
if (selection.isEmpty()) { // selection is empty
_jsel = -1;
} else {
_jsel = Integer.MIN_VALUE; // force to resync;
}
} finally {
_disabledEventListener = false;
}
Set unselection = collectUnselectedObjects(prevSelection, selection);
Events.postEvent(new SelectEvent(Events.ON_SELECT, this, null, null, null, selection, prevSet, unselection,
null, null, 0));
} else if (cmd.equals(Events.ON_OPEN)) {
_open = (Boolean) request.getData().get("open");
Events.postEvent(OpenEvent.getOpenEvent(request));
} else if (cmd.equals("onSearch")) {
Events.postEvent(new InputEvent(cmd, this, (String) request.getData().get("text"), ""));
} else if (cmd.equals("onSearching")) {
String data = (String) request.getData().get("text");
Events.postEvent(new InputEvent(cmd, this, data, _value));
_value = data;
} else if (cmd.equals("onItemClick")) { // ZK-3098
String clickedItemUuid = (String) request.getData().get("itemUuid");
if (clickedItemUuid != null && clickedItemUuid.length() != 0) {
Events.postEvent("onItemClick", this, getOptElementById(clickedItemUuid));
}
} else // ZK-1053
{
super.service(request, everError);
}
}
private static final ItemRenderer<Object> _defRend = new ItemRenderer<Object>() {
public String render(final Component owner, final Object data, final int index) {
final Chosenbox self = (Chosenbox) owner;
final Template tm = self.getTemplate("model");
if (tm == null) {
return Objects.toString(data);
} else {
final Component[] items = tm.create(owner, null,
new VariableResolver() {
public Object resolveVariable(String name) {
if ("each".equals(name)) {
return data;
} else if ("forEachStatus".equals(name)) {
return new ForEachStatus() {
public ForEachStatus getPrevious() {
return null;
}
public Object getEach() {
return data;
}
public int getIndex() {
return index;
}
public Integer getBegin() {
return 0;
}
public Integer getEnd() {
return ((Chosenbox) owner).getModel().getSize();
}
};
} else {
return null;
}
}
}, null);
if (items.length != 1) {
throw new UiException(
"The model template must have exactly one item, not "
+ items.length);
}
if (!(items[0] instanceof Label)) {
throw new UiException(
"The model template can only support Label component, not "
+ items[0]);
}
items[0].detach(); //remove the label from owner
return ((Label) items[0]).getValue();
}
}
};
}
For the js, put it under Web Pages/WEB_INF/cwr/js/zkmax/inp
.
Don't forget, this is the src file, you will need Chosenbox.js
to have it work.
That's the minified js file.
/*ZK 8.0.2.1 version of chosenbox*/
(function () {
function clearAllData(wgt) {
wgt._ppMaxHeight = wgt.fixDisplay = wgt._separatorCode = wgt._startOnSearching = wgt._chgSel = wgt.fixInputWidth = null;
}
function startOnSearching(wgt) {
if (!wgt._startOnSearching)
wgt._startOnSearching = setTimeout(function () {
if (wgt.$n('inp'))
wgt._fireOnSearching(wgt.$n('inp').value);
wgt._startOnSearching = null;
}, wgt._getSearchingInterval());
}
zkmax.inp.Chosenbox = zk.$extends(zul.Widget, {
$init: function () {
this.$supers('$init', arguments);
this._selItems = [];
this._separatorCode = [];
this._ppMaxHeight = 350;
},
$define: {
items: function (v) {
if (!this._renderByServer)
this.setListContent();
},
tabindex: function (tabindex) {
var n = this.$n('inp');
if (n) {
if (tabindex == null)
n.removeAttribute('tabindex');
else
n.tabIndex = tabindex;
}
},
selectedIndex: function (v) {
var options,
sel;
if (!this._renderByServer) {
this._clearSelection();
if ((sel = this.$n('sel')) && v >= 0) {
options = jq(sel).children();
if (v < options.length)
this._doSelect(options[v]);
}
}
if (v == -1)
this._fixEmptyMessage(true);
},
renderByServer: function (v) {
if (v && this.$n())
this._clearListContent();
},
disabled: function (disabled) {
var n = this.$n('inp');
if (n)
n.disabled = disabled ? 'disabled' : '';
n = this.$n();
if (n)
jq(n)[disabled ? 'addClass' : 'removeClass'](this.$s('disabled'));
},
name: function (name) {
var n = this.$n('inp');
if (n) n.name = name;
},
emptyMessage: null,
noResultsText: null,
createMessage: null,
separator: function (v) {
var separatorCode = this._separatorCode;
separatorCode.length = 0;
if (v.indexOf(',') != -1)
separatorCode.push(44);
if (v.indexOf('.') != -1)
separatorCode.push(46);
if (v.indexOf('/') != -1)
separatorCode.push(47);
if (v.indexOf(';') != -1)
separatorCode.push(59);
if (v.indexOf("'") != -1)
separatorCode.push(39);
if (v.indexOf('[') != -1)
separatorCode.push(91);
if (v.indexOf(']') != -1)
separatorCode.push(93);
if (v.indexOf('\\') != -1)
separatorCode.push(92);
if (v.indexOf('-') != -1)
separatorCode.push(45);
if (v.indexOf('=') != -1)
separatorCode.push(61);
},
creatable: null,
open: null
},
setListContent: function (v) {
var sel,
out,
oldHlite,
id;
if (sel = this.$n('sel')) {
if (oldHlite = jq(this.$n('sel')).find('.' + this.$s('option-hover'))[0])
id = oldHlite.id.replace('-opt', '');
out = new zk.Buffer();
this._renderItems(out, v);
this._clearListContent();
sel.innerHTML = out.join('');
if (id && (oldHlite = this._getOptionById(id)))
this._hliteOpt(oldHlite, true);
this._startFixDisplay({hliteFirst: true, fromServer: true});
}
},
_clearListContent: function () {
if (this.$n()) {
this.$n('sel').innerHTML = '';
this.$n('empty').style.display = 'none';
}
},
_renderItems: function (out, content) {
var s = $eval(content ? content : this._items) || [];
for (var i = 0, j = s.length; i < j; i++) {
out.push('<div id="', s[i].id ,'-opt" class="',this.$s('option'),' ', s[i].sclass,'">', s[i].content, '</div>');
}
},
setChgSel: function (val) {
if (val && val.$equals(this._selItems))
return;
this._clearSelection();
var sel,
options;
if (sel = this.$n('sel')) {
options = jq(sel).children();
var s = $eval(val),
item,
value;
for (var i = 0; i < s.length; i++) {
value = s[i];
if (item = this._getOptionById(value.id))
this._doSelect(item);
else
this._selectItemDirectly(value);
}
} else
this._chgSel = val;
this._fixEmptyMessage(true);
},
bind_: function () {
this.$supers(zkmax.inp.Chosenbox, 'bind_', arguments);
var n = this.$n(),
inp = this.$n('inp'),
chgSel = this._chgSel || this._selItems;
this.domListen_(inp, 'onFocus', 'doFocus_')
.domListen_(inp, 'onBlur', 'doBlur_');
zWatch.listen({onFloatUp: this, onSize: this, onScroll: this});
if (chgSel) {
if (this._selItems && this._selItems.length)
this._selItems = [];
this.setChgSel(chgSel);
this._chgSel = null;
}
this._fixEmptyMessage(true);
if (this._open && !this.isDisabled())
this.setOpen(true);
},
unbind_: function () {
var inp = this.$n('inp');
this.domUnlisten_(inp, 'onFocus', 'doFocus_')
.domUnlisten_(inp, 'onBlur', 'doBlur_');
zWatch.unlisten({onFloatUp: this, onSize: this, onScroll: this});
this.setOpen(false);
clearAllData(this);
this.$supers(zkmax.inp.Chosenbox, 'unbind_', arguments);
},
onSize: function () {
this._fixInputWidth();
this._fixWidth(this.$n());
},
_fixWidth: function (n) {
if (this._width)
n.style.width = this._width;
var pp = this.$n('pp');
pp.style.width = zk(pp).revisedWidth(n.offsetWidth) + 'px';
},
doBlur_: function (evt) {
jq(this.$n()).removeClass(this.$s('focus'));
var self = this;
setTimeout(function () {
if (self.desktop && zk.currentFocus != self) {
self._fixEmptyMessage(true);
}
}, 200);
return this.$supers('doBlur_', arguments);
},
doFocus_: function (evt) {
if (!this.isDisabled()) {
jq(this.$n()).addClass(this.$s('focus'));
var inp = this.$n('inp');
if (inp && inp.value == this._emptyMessage) {
inp.value = '';
}
}
return this.$supers('doFocus_', arguments);
},
doMouseOver_: function (evt) {
var target = evt.domTarget;
if (jq(target).hasClass(this.$s('option')))
this._hliteOpt(target, true);
},
doMouseOut_: function (evt) {
var target = evt.domTarget;
if (jq(target).hasClass(this.$s('option-hover')))
this._hliteOpt(target, false);
},
_hliteOpt: function (target, highlight) {
var zcls = this.$s('option-hover');
if (highlight) {
var oldHlite = jq(this.$n('pp')).find('.' + zcls)[0];
if (oldHlite)
jq(oldHlite).removeClass(zcls);
jq(target).addClass(zcls);
} else
jq(target).removeClass(zcls);
},
_doArrowDown: function (key, evt) {
if (key == 'up')
this._moveOptionFocus('prev');
else if (key == 'down')
this._moveOptionFocus('next');
else {
var inp = this.$n('inp'),
pos = zk(inp).getSelectionRange(),
label = jq(this.$n()).find('.' + this.$s('item-focus'))[0];
if (pos[0] == 0 && pos[1] == 0) {
if (key == 'left')
this._moveLabelFocus(label, 'prev');
else if (key == 'right') {
if (label)
evt.stop();
this._moveLabelFocus(label, 'next');
}
}
}
},
_moveOptionFocus: function (dir) {
var sel = this.$n('sel'),
pp = this.$n('pp'),
$pp = jq(pp),
oldHlite = $pp.find('.' + this.$s('option-hover'))[0],
newHlite,
next = dir == 'next';
if (!oldHlite) {
oldHlite = $pp.find('.' + this.$s('option:hover'))[0];
this._hliteOpt(oldHlite, true);
}
if (next && !this._open)
this.setOpen(true, {sendOnOpen: true});
else {
var creatableItem = $pp.find('.' + this.$s('empty-creatable'))[0],
selFirstItem = sel.firstChild,
selLastItem = sel.lastChild;
if (this._creatable && creatableItem) {
if (oldHlite) {
var prevS = (oldHlite == creatableItem) ? selLastItem : (oldHlite == selFirstItem) ? creatableItem : oldHlite.previousSibling,
nextS = (oldHlite == creatableItem) ? selFirstItem : (oldHlite == selLastItem) ? creatableItem : oldHlite.nextSibling;
newHlite = next ? nextS : prevS;
} else {
newHlite = next ? creatableItem : selLastItem;
}
if (newHlite)
while (newHlite && newHlite.style.display == 'none') {
var prevS = (newHlite == selFirstItem) ? creatableItem : newHlite.previousSibling,
nextS = (newHlite == selLastItem) ? creatableItem : newHlite.nextSibling;
newHlite = next ? nextS : prevS;
}
} else {
if (oldHlite)
newHlite = next ? oldHlite.nextSibling : oldHlite.previousSibling;
else
newHlite = next ? sel.firstChild :
!next ? sel.lastChild : null;
if (newHlite)
while (newHlite && newHlite.style.display == 'none')
newHlite = next ? newHlite.nextSibling : newHlite.previousSibling;
}
if (newHlite) {
this._hliteOpt(newHlite, true);
zk(newHlite).scrollIntoView(pp);
this._currentTop = pp.scrollTop;
} else if (oldHlite) {
this._hliteOpt(oldHlite, false);
zk(oldHlite).scrollIntoView(pp);
this._currentTop = pp.scrollTop;
}
}
},
_moveLabelFocus: function (label, dir) {
var zcls = this.$s('item-focus'),
newLabel,
next = dir == 'next';
if (label) {
jq(label).removeClass(zcls);
newLabel = next ? label.nextSibling : label.previousSibling;
if (!next && !newLabel)
newLabel = label;
else if (next && newLabel == this.$n('inp'))
newLabel = null;
} else if (!next)
newLabel = this.$n('inp').previousSibling;
if (newLabel)
jq(newLabel).addClass(zcls);
},
_deleteLabel: function (key, evt) {
var inp = this.$n('inp'),
pos = zk(inp).getSelectionRange(),
label;
if (pos[0] == 0 && pos[1] == 0) {
var zcls = this.$s('item-focus');
if (label = jq(this.$n()).find('.' + zcls)[0]) {
var dir = (label.previousSibling && key == 'backspace') ? 'prev' : 'next';
this._moveLabelFocus(label, dir);
this._doDeselect(label, {sendOnSelect: true});
evt.stop();
this._startFixDisplay();
}
else if ((label = inp.previousSibling) && key == 'backspace')
jq(label).addClass(zcls);
}
},
_removeLabelFocus: function () {
var zcls = this.$s('item-focus'),
label = jq(this.$n()).find('.' + zcls)[0];
if (label)
jq(label).removeClass(zcls);
},
_doEnterPressed: function () {
var $sel,
hlited,
old;
if (old = this.fixDisplay)
clearTimeout(old);
this._fixDisplay();
if (this._open) {
if ((hlited = this.$n('empty')) && jq(hlited).hasClass(this.$s('empty-creatable')) && jq(hlited).hasClass(this.$s('option-hover'))) {
this._fireOnSearch(this.$n('inp').value);
if (this._open)
this.setOpen(false, {sendOnOpen: true});
} else if ($sel = jq(this.$n('sel'))) {
hlited = $sel.find('.' + this.$s('option-hover'))[0];
if (!hlited && !(hlited = $sel.find('.' + this.$s('option:hover'))[0])) return;
var options = $sel.children();
this._doSelect(hlited, {sendOnSelect: true});
if (this._open)
this.setOpen(false, {sendOnOpen: true});
}
}
},
doClick_: function (evt) {
if (!this.isDisabled()) {
var target = evt.domTarget,
$target = jq(target),
inp = this.$n('inp'),
stopBuble = false,
pcls = 'z-page',
zcls = this.getZclass(),
inpCls = this.$s('input'),
itemCls = this.$s('item'),
optCls = this.$s('option'),
opthoverCls = this.$s('option-hover'),
isOpt = false,
isOptHover = false;
this._removeLabelFocus();
if (inp.value == this._emptyMessage)
inp.value = '';
if (!$target.hasClass(inpCls) && !$target.hasClass(zcls)) {
do {
if ($target.hasClass(this.$s('empty-creatable'))) {
this._fireOnSearch(this.$n('inp').value);
if (this._open)
this.setOpen(false, {sendOnOpen: true});
return;
} else if ($target.hasClass(optCls)) {
this._doSelect($target[0], {sendOnSelect: true});
stopBuble = true;
isOpt = true;
break;
} else if ($target.hasClass(opthoverCls)) {
this._fireOnSearch(inp.value);
isOpt = isOptHover = true;
break;
} else if ($target.hasClass(itemCls)) {
$target.addClass(this.$s('item-focus'));
var uuid = $target.attr('id').replace('-sel', '');
this.fire('onItemClick', {itemUuid: uuid});
break;
}
$target = $target.parent();
} while (!$target.is('body') && !$target.hasClass(pcls));
}
if (!this._open && !isOpt)
this.setOpen(true, {sendOnOpen: true});
else if (this._open && isOpt) {
if (isOptHover)
this.setOpen(false, {sendOnOpen: true});
else
this.setOpen(false, {sendOnOpen: true, fixEmptyMessage: true});
}
inp.focus();
if (stopBuble)
evt.stop();
this.$supers('doClick_', arguments);
}
},
_doSelect: function (target, opts) {
this._hliteOpt(target, false);
var id = target.id.replace('-opt', '');
if (this._getItemById(id, true) == null) {
var item = this._getItemById(id);
if (item == null)
item = {id: id, content: target.innerHTML};
this._createLabel(item);
target.style.display = 'none';
this._selItems.push(item);
this._fixEmptyMessage(true);
if (opts && opts.sendOnSelect) {
if (!this._renderByServer)
this.fireOnSelect();
else
this.selectSubModel(id);
}
}
},
doSelect_: function (evt) {
if (evt.domTarget == this.$n()) {
evt.stop({revoke: true});
}
this.$super('doSelect_', arguments);
},
_selectItemDirectly: function (value) {
if (this._selItems.indexOf(value) == -1) {
this._createLabel(value);
this._selItems.push(value);
this._fixEmptyMessage(true);
}
},
_doDeselect: function (selectedOption, opts) {
var id = selectedOption.id.replace('-sel', ''),
element = this._getOptionById(id),
_selItems = this._selItems,
index = -1;
if (this._open)
this.setOpen(false, {sendOnOpen: true});
for (var i = 0, length = _selItems.length; i < length; i++)
if (_selItems[i].id == id) index = i;
_selItems.splice(index, 1);
if (element)
element.style.display = 'block';
jq(selectedOption).remove();
this._updatePopupPosition();
if (opts && opts.sendOnSelect) {
if (!this._renderByServer)
this.fireOnSelect();
else
this.deselectSubModel(id);
}
this._startFixDisplay();
},
_getItemById: function (id, isSel) {
var opt = null,
options = isSel ? this._selItems : this._items;
if (options) {
for (var i = 0, length = options.length; i < length; i++) {
if (options[i].id == id) opt = options[i];
}
}
return opt;
},
_getOptionById: function (id) {
var options = jq(this.$n('sel')).children(),
opt;
id += '-opt';
for (var i = 0; i < options.length; i++) {
if ((opt = options[i]) && opt.id == id)
return opt;
else if (!opt)
return null;
}
},
_createLabel: function (item) {
var span = document.createElement('span'),
content = document.createElement('div'),
delbtn = document.createElement('div'),
delbtnicon = document.createElement('i'),
wgt = this;
span.className = this.$s('item');
span.id = item.id + '-sel';
content.innerHTML = item.content;
content.className = this.$s('item-content ') + ' ' + item.sclass;
delbtn.className = this.$s('button') + ' ' + this.$s('delete');
delbtnicon.className = this.$s('icon') + ' ' + 'z-icon-times';
span.appendChild(content);
span.appendChild(delbtn);
delbtn.appendChild(delbtnicon);
jq(delbtn).bind('click', function () {
if (!wgt.isDisabled()) {
wgt.$n('inp').focus();
wgt._doDeselect(span, {sendOnSelect: true});
}
});
this.$n().insertBefore(span, this.$n('inp'));
},
_clearSelection: function (opts) {
var n = this.$n(),
inp = this.$n('inp'),
c,
del;
if (n)
c = n.firstChild;
while (c && c != inp) {
del = c;
c = c.nextSibling;
this._doDeselect(del, opts);
}
this._selItems.length = 0;
},
fireOnSelect: function () {
var options = jq(this.$n('sel')).children(),
minIdx = -1,
selItems = this._selItems,
selItemUuids = [];
for (var i = 0; i < options.length; i++)
if (options[i].style.display == 'none') {
minIdx = i;
break;
}
for (var i = 0; i < selItems.length; i++)
selItemUuids.push(selItems[i].id);
this.fire('onSelect', {selectedUuids: selItemUuids, min: minIdx});
},
selectSubModel: function (optId) {
data = {itemUuid: optId, select: true};
this.fire('onSelectSubModel', data, {toServer: this.isListen('onSelect')});
},
deselectSubModel: function (optId) {
data = {itemUuid: optId, select: false};
this.fire('onSelectSubModel', data, {toServer: this.isListen('onSelect')});
},
_fireOnSearch: function (value) {
var data = {text: value};
this.fire('onSearch', data);
},
_fireOnSearching: function (value) {
if (value || this._prevSearchingValue) {
var data = {text: value};
this.fire('onSearching', data);
this._prevSearchingValue = value;
}
},
onFloatUp: function (ctl) {
if (ctl.origin != this) {
if (this._open)
this.setOpen(false, {sendOnOpen: true, fixEmptyMessage: true});
this._removeLabelFocus();
}
},
doKeyDown_: function (evt) {
var keyCode = evt.keyCode;
switch (keyCode) {
case 8:
this._deleteLabel('backspace', evt);
break;
case 9:
break;
case 13:
break;
case 27:
break;
case 37:
this._doArrowDown('left', evt);
break;
case 38:
this._doArrowDown('up');
break;
case 39:
this._doArrowDown('right', evt);
break;
case 40:
this._doArrowDown('down');
break;
case 46:
this._deleteLabel('del', evt);
break;
default:
this._updateInput(evt);
if (!this._open)
this.setOpen(true, {sendOnOpen: true});
}
if (!(keyCode == 39 || keyCode == 46 || keyCode == 8 || keyCode == 37))
this._removeLabelFocus();
},
doKeyPress_: function (evt) {
var keyCode = evt.which || evt.charCode || evt.keyCode || 0;
this._keyPressedIsSeparator = this._isSeparator(keyCode);
if (this._keyPressedIsSeparator)
evt.stop();
},
doKeyUp_: function (evt) {
var keyCode = evt.keyCode,
opts = {hliteFirst: true};
switch (keyCode) {
case 13:
this._doEnterPressed();
break;
case 27:
if (this._open)
this.setOpen(false, {sendOnOpen: true});
this._fixEmptyMessage();
break;
default:
if (this._keyPressedIsSeparator)
this._doEnterPressed();
else {
this._fixInputWidth();
if (keyCode == 38 || keyCode == 40)
opts = null;
if (!this._renderByServer)
this._startFixDisplay(opts);
}
}
if (!(keyCode >= 37 && keyCode <= 40 || keyCode == 13 || keyCode == 9))
startOnSearching(this);
this._keyPressedIsSeparator = false;
},
focus_: function (timeout) {
if (zk.ie11_ && !timeout)
timeout = 0;
zk(this.getInputNode()).focus(timeout);
return true;
},
_isSeparator: function (keyCode) {
var separator = this._separator,
separatorCode = this._separatorCode;
return (separatorCode && separatorCode.indexOf(keyCode) != -1)
|| ((keyCode >= 48 && keyCode <= 122) && separator
&& separator.toUpperCase().indexOf(String.fromCharCode(keyCode)) != -1);
},
_updateInput: function (evt) {
var wgt = this;
if (!this.fixInputWidth)
this.fixInputWidth = setTimeout(function () {
if (wgt.$n())
wgt._fixInputWidth();
}, 100);
},
setOpen: function (open, opts) {
if (!this.isDisabled())
this._open = open;
if (this.$n('pp')) {
var pp = this.$n('pp');
if (open)
this.open(this.$n(), pp, opts);
else
this.close(pp, opts);
}
},
open: function (n, pp, opts) {
var offset,
ppstyle = pp.style;
this._fixsz(pp);
zk(pp).makeVParent();
this.setFloating_(true);
this.setTopmost();
offset = jq(n).offset();
ppstyle.left = offset.left + 'px';
ppstyle.top = offset.top + jq(n).outerHeight() + 'px';
ppstyle.zIndex = n.style.zIndex;
zk(pp).slideDown(this, {duration: 100});
this._startFixDisplay({hliteFirst: true});
if (opts && opts.sendOnOpen)
this.fire('onOpen', {open: true});
},
close: function (pp, opts) {
zk(pp).undoVParent();
this.setFloating_(false);
pp.style.display = 'none';
if (opts) {
if (opts.sendOnOpen)
this.fire('onOpen', {open: false});
if (opts.fixEmptyMessage)
this._fixEmptyMessage();
}
if (this._renderByServer)
this._clearListContent();
},
_fixsz: function (pp) {
var ppstyle = pp.style,
maxh = this._ppMaxHeight;
ppstyle.height = 'auto';
ppstyle.left = '-10000px';
ppstyle.display = 'block';
ppstyle.visibility = 'hidden';
if (jq(pp).height() > maxh)
ppstyle.height = maxh + 'px';
ppstyle.display = 'none';
ppstyle.visibility = 'visible';
},
_fixInputWidth: function () {
var n = this.$n(),
inp = this.$n('inp'),
txcnt = this.$n('txcnt'),
oldh = jq(n).height(),
width,
max = parseInt(this._width) - 10;
txcnt.innerHTML = inp.value;
width = jq(txcnt).width() + 30;
if (width > max)
inp.style.width = max + 'px';
else
inp.style.width = width + 'px';
if (jq(n).height() != oldh)
this._updatePopupPosition(n, this.$n('pp'));
if (this.fixInputWidth)
clearTimeout(this.fixInputWidth);
this.fixInputWidth = null;
},
_startFixDisplay: function (opts) {
if (opts && opts.fromServer)
this._fixDisplay(opts);
else {
var wgt = this,
old;
if (old = this.fixDisplay)
clearTimeout(old);
this.fixDisplay = setTimeout(function () {
if (wgt.$n()) {
wgt._fixDisplay(opts);
if (wgt._currentTop)
wgt.$n('pp').scrollTop = wgt._currentTop;
}
}, 200);
}
},
_fixDisplay: function (opts) {
if (!this._open) return;
var fromServer = opts && opts.fromServer;
if (!this._renderByServer || fromServer) {
var str = this.$n('inp').value,
oldhlite = jq(this.$n('sel')).find('.' + this.$s('option-hover'))[0],
existance = this._fixSelDisplay(opts && opts.hliteFirst, str, fromServer);
str = str ? str.trim() : '';
this._fixEmptyDisplay({showExistance: true}, str, existance._found, existance._exist, opts && opts.hliteFirst);
} else {
this._fixEmptyDisplay({showBlank: !this.$n('sel').firstChild});
}
},
_fixSelDisplay: function (hliteFirst, str, fromServer) {
var pp = this.$n('pp'),
$pp = jq(pp),
maxh = this._ppMaxHeight,
ppstyle = pp.style,
selItems = this._selItems,
options = jq(this.$n('sel')).children(),
found = false,
exist = false,
index, element, showAll, selected;
str = str ? str.trim() : '';
showAll = str && str == this._emptyMessage || str == '';
for (index = 0, element = options[index];
index < options.length;
index++, element = options[index]) {
selected = this._getItemById(element.id.replace('-opt', ''), true) != null;
if (fromServer || !selected) {
if (!selected &&
(showAll || this._renderByServer || str && element.innerHTML.toLowerCase().startsWith(str.toLowerCase()))) {
if (!found) {
found = true;
if (hliteFirst)
this._hliteOpt(element, true);
}
element.style.display = 'block';
}
else {
this._hliteOpt(element, false);
element.style.display = 'none';
}
}
if (!exist && str && element.innerHTML.toLowerCase() == str.toLowerCase())
exist = true;
}
ppstyle.height = 'auto';
if ($pp.height() > maxh)
ppstyle.height = maxh + 'px';
return {_found: found, _exist: exist};
},
_fixEmptyDisplay: function (type, str, found, exist, hliteFirst) {
var ecls = this.$s('empty-creatable'),
eOptCls = this.$s('option'),
empty = this.$n('empty');
if (type && (type.showBlank
|| type.showExistance && this._renderByServer && !str)) {
empty.innerHTML = ' ';
empty.style.display = 'block';
} else if (type && type.showExistance) {
if (this._creatable && !exist && str) {
var createMsg = this._createMessage,
icon = document.createElement('i');
icon.className = this.$s('icon') + ' ' + this.$s('create') + ' ' + 'z-icon-plus-square';
if (createMsg)
createMsg = zUtl.encodeXML(createMsg.replace(/\{0\}/g, str)).replace(/\n/g, '<br />');
else
createMsg = ' ';
var text = document.createElement('span');
text.innerHTML = createMsg;
empty.innerHTML = '';
empty.appendChild(text);
empty.insertBefore(icon, text);
jq(empty).addClass(ecls);
jq(empty).addClass(eOptCls);
empty.style.display = 'block';
if (hliteFirst)
this._hliteOpt(empty, true);
} else {
if (!found) {
var empMsg = this._noResultsText;
if (empMsg)
empMsg = zUtl.encodeXML(empMsg.replace(/\{0\}/g, str)).replace(/\n/g, '<br />');
else
empMsg = ' ';
empty.innerHTML = empMsg;
jq(empty).removeClass(ecls);
jq(empty).removeClass(eOptCls);
empty.style.display = 'block';
} else {
empty.style.display = 'none';
jq(empty).removeClass(ecls);
jq(empty).removeClass(eOptCls);
}
}
}
},
_updatePopupPosition: function () {
var n = this.$n(),
pp = this.$n('pp'),
offset = jq(n).offset();
pp.style.left = offset.left + 'px';
pp.style.top = offset.top + jq(n).outerHeight() + 'px';
},
_fixEmptyMessage: function (force) {
var inp;
if ((!this._open || force) && (inp = this.$n('inp'))) {
inp.value = this._selItems.length == 0 ? zUtl.encodeXML(this.getEmptyMessage()) : '';
this._fixInputWidth();
if (this._open) {
this._startFixDisplay();
}
}
},
domClass_: function (no) {
var s1 = this.$supers('domClass_', arguments),
s2 = this.isDisabled() ? this.$s('disabled') : '';
return s1 + (s1 && s2 ? ' ' : '') + s2;
},
_getSearchingInterval: function () {
return 350;
},
onScroll: function (wgt) {
if (this._open) {
if (wgt) {
var n = this.$n();
if (n && zk(n).isRealScrollIntoView(true))
zk(this.$n('pp')).position(n, 'after_start');
else
this.setOpen(false, {sendOnOpen: true});
}
}
},
getInputNode: function () {
return this.$n('inp') || this.$n();
}
});
})();
Chill.
Thank you!
Asked: 2017-05-25 23:24:33 +0800
Seen: 37 times
Last updated: May 31 '17
How to let chexkbox horizontally arranged
Choosenbox : selectedObjects background color
extend the chosenbox component
radiogroup onCheck/onChange not calling to the binded command
Tooltips in chosenbox elements
Customize chosenbox emptymessage styling
Can't catch onSearch event when using SimpleListModel with chosenbox