diff --git a/etemplate/js/et2_extension_customfields.js b/etemplate/js/et2_extension_customfields.js
index 647bbc2227..1601cce878 100644
--- a/etemplate/js/et2_extension_customfields.js
+++ b/etemplate/js/et2_extension_customfields.js
@@ -220,6 +220,7 @@ var et2_customfields_list = et2_baseWidget.extend([et2_IDetachedDOM], {
if(!this.options.customfields) return;
for(var field_name in this.options.customfields)
{
+ // Make sure widget is created, and has the needed function
if(!this.widgets[field_name] || !this.widgets[field_name].set_value) continue;
var value = _value[this.prefix + field_name] ? _value[this.prefix + field_name] : null;
this.widgets[field_name].set_value(value);
diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js
index 0b07bbcb3d..d2d90ff16c 100644
--- a/etemplate/js/et2_extension_nextmatch.js
+++ b/etemplate/js/et2_extension_nextmatch.js
@@ -561,7 +561,12 @@ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, {
// Build the popup
if(!this.selectPopup)
{
- var select = et2_createWidget("select", {multiple: true, rows: 8}, this);
+ var select = et2_createWidget("select", {
+ multiple: true,
+ rows: 8,
+ empty_label:this.egw().lang("select columns"),
+ selected_first: false
+ }, this);
select.set_select_options(columns);
select.set_value(columns_selected);
@@ -629,9 +634,8 @@ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, {
self.selectPopup.toggle();
}
- this.selectPopup = jQuery(document.createElement("fieldset"))
- .addClass("colselection ui-dialog")
- .append("")
+ this.selectPopup = jQuery(document.createElement("div"))
+ .addClass("colselection ui-dialog ui-widget-content")
.append(select.getDOMNode())
.append(okButton.getDOMNode())
.append(cancelButton.getDOMNode())
diff --git a/etemplate/js/et2_widget_selectbox.js b/etemplate/js/et2_widget_selectbox.js
index 64a5622ec6..85e0eeb60b 100644
--- a/etemplate/js/et2_widget_selectbox.js
+++ b/etemplate/js/et2_widget_selectbox.js
@@ -46,6 +46,12 @@ var et2_selectbox = et2_inputWidget.extend({
"name": "Select options",
"default": {},
"description": "Internaly used to hold the select options."
+ },
+ "selected_first": {
+ "name": "Selected options first",
+ "type": "boolean",
+ "default": true,
+ "description": "For multi-selects, put the selected options at the top of the list when first loaded"
}
},
@@ -53,17 +59,35 @@ var et2_selectbox = et2_inputWidget.extend({
init: function() {
this._super.apply(this, arguments);
+
+ this.input = null;
// Allow no other widgets inside this one
this.supportedWidgetClasses = [];
// Legacy options could have row count or empty label in first slot
- if(typeof this.options.rows == "string" && isNaN(this.options.rows)) {
- this.options.empty_label = this.egw().lang(this.options.rows);
- this.options.rows = 1;
+ if(typeof this.options.rows == "string")
+ {
+ if(isNaN(this.options.rows))
+ {
+ this.options.empty_label = this.egw().lang(this.options.rows);
+ this.options.rows = 1;
+ }
+ else
+ {
+ this.options.rows = parseInt(this.options.rows);
+ }
+ }
+
+ if(this.options.rows > 1)
+ {
+ this.options.multiple = true;
+ this.createMultiSelect();
+ }
+ else
+ {
+ this.createInputWidget();
}
- if(this.options.rows > 1) this.options.multiple = true;
- this.createInputWidget();
},
destroy: function() {
@@ -117,11 +141,19 @@ var et2_selectbox = et2_inputWidget.extend({
}
},
+ /**
+ * Add an option to regular drop-down select
+ */
_appendOptionElement: function(_value, _label, _title) {
if(_value == "" && (_label == null || _label == "")) {
_label = this.options.empty_label;
}
+ if(this.input == null)
+ {
+ return this._appendMultiOption(_value, _label, _title);
+ }
+
var option = $j(document.createElement("option"))
.attr("value", _value)
.text(_label+"");
@@ -133,6 +165,61 @@ var et2_selectbox = et2_inputWidget.extend({
option.appendTo(this.input);
},
+ /**
+ * Append a value to multi-select
+ */
+ _appendMultiOption: function(_value, _label, _title) {
+ var option_data = null;
+ if(typeof _label == "object")
+ {
+ option_data = _label;
+ _label = option_data.label;
+ }
+
+ // Already in header
+ if(_label == this.options.empty_label) return;
+
+ var opt_id = this.id + "_opt_" + _value;
+ var label = jQuery(document.createElement("label"))
+ .attr("for", opt_id)
+ .hover(
+ function() {jQuery(this).addClass("ui-state-hover")},
+ function() {jQuery(this).removeClass("ui-state-hover")}
+ );
+ var option = jQuery(document.createElement("input"))
+ .attr("type", "checkbox")
+ .attr("id",opt_id)
+ .attr("value", _value)
+ .appendTo(label);
+ if(typeof _title !== "undefined")
+ {
+ option.attr("title",_title);
+ }
+
+ // Some special stuff for categories
+ if(option_data)
+ {
+ if(option_data.data.icon)
+ {
+ var img = this.egw().image(option_data.data.icon);
+ jQuery(document.createElement("img"))
+ .attr("src", img)
+ .appendTo(label);
+ }
+ if(option_data.data.color)
+ {
+ label.css("background-color",option_data.data.color);
+ }
+ }
+ label.append(jQuery(""+_label+""))
+ var li = jQuery(document.createElement("li")).append(label);
+
+ li.appendTo(this.multiOptions);
+ },
+
+ /**
+ * Create a regular drop-down select box
+ */
createInputWidget: function() {
// Create the base input widget
this.input = $j(document.createElement("select"))
@@ -155,6 +242,73 @@ var et2_selectbox = et2_inputWidget.extend({
}
},
+ /**
+ * Create a list of checkboxes
+ */
+ createMultiSelect: function() {
+ var node = jQuery(document.createElement("div"))
+ .addClass("et2_selectbox");
+
+ var header = jQuery(document.createElement("div"))
+ .addClass("ui-widget-header ui-helper-clearfix")
+ .appendTo(node);
+
+ if(this.options.empty_label)
+ {
+ jQuery(document.createElement("span"))
+ .text(this.options.empty_label)
+ .addClass("ui-multiselect-header")
+ .appendTo(header);
+ }
+
+ // Set up for options to be added later
+ var options = this.multiOptions = jQuery(document.createElement("ul"));
+ this.multiOptions.addClass("ui-multiselect-checkboxes ui-helper-reset")
+ .css("height", 1.9*this.options.rows + "em")
+ .appendTo(node);
+
+ if(this.options.rows >= 5)
+ {
+ // Check / uncheck all
+ var header_controls = {
+ check: {
+ icon_class: 'ui-icon-check',
+ label: 'Check all',
+ click: function(e) {
+ jQuery("input",e.data).attr("checked", true);
+ }
+ },
+ uncheck: {
+ icon_class: 'ui-icon-closethick',
+ label: 'Uncheck all',
+ click: function(e) {
+ jQuery("input",e.data).attr("checked", false);
+ }
+ }
+ }
+ var controls = jQuery(document.createElement("ul"))
+ .addClass('ui-helper-reset')
+ .appendTo(header);
+ for(var key in header_controls)
+ {
+ jQuery(document.createElement("li"))
+ .addClass("et2_clickable")
+ .click(options, header_controls[key].click)
+ .append('')
+ .append(""+this.egw().lang(header_controls[key].label)+"")
+ .appendTo(controls);
+ }
+
+ }
+ else if (header[0].childNodes.length == 0)
+ {
+ // Hide header - nothing there but padding
+ header.hide();
+ }
+
+ this.setDOMNode(node[0]);
+ },
+
loadFromXML: function(_node) {
// Read the option-tags
var options = et2_directChildrenByTagName(_node, "options");
@@ -169,6 +323,14 @@ var et2_selectbox = et2_inputWidget.extend({
},
set_value: function(_value) {
+ if(typeof _value == "string" && _value.match(/[,0-9]+$/) !== null)
+ {
+ _value = _value.split(',');
+ }
+ if(this.input == null)
+ {
+ return this.set_multi_value(_value);
+ }
jQuery("option",this.input).attr("selected", false);
this.value = _value;
if(typeof _value == "array")
@@ -190,11 +352,41 @@ var et2_selectbox = et2_inputWidget.extend({
if(jQuery("option[value='"+_value+"']", this.input).attr("selected", true).length == 0)
{
this.egw().debug("warning", "Tried to set value that isn't an option", this, _value);
- //console.trace();
}
}
},
+ set_multi_value: function(_value) {
+ jQuery("input",this.multiOptions).attr("checked", false);
+ if(typeof _value == "array")
+ {
+ for(var i = 0; i < _value.length; i++)
+ {
+ jQuery("input[value='"+_value[i]+"']", this.multiOptions).attr("checked", true);
+ }
+ }
+ else if (typeof _value == "object")
+ {
+ for(var i in _value)
+ {
+ jQuery("input[value='"+_value[i]+"']", this.multiOptions).attr("checked", true);
+ }
+ }
+ else
+ {
+ if(jQuery("input[value='"+_value+"']", this.multiOptions).attr("checked", true).length == 0)
+ {
+ this.egw().debug("warning", "Tried to set value that isn't an option", this, _value);
+ }
+ }
+
+ // Sort selected to the top
+ if(this.selected_first)
+ {
+ this.multiOptions.find("li:has(input:checked)").prependTo(this.multiOptions);
+ }
+ },
+
/**
* The set_select_options function is added, as the select options have to be
* added after the "option"-widgets were added to selectbox.
@@ -220,9 +412,18 @@ var et2_selectbox = et2_inputWidget.extend({
if (typeof _options[key] === 'object')
{
- this._appendOptionElement(key,
- _options[key]["label"] ? _options[key]["label"] : "",
- _options[key]["title"] ? _options[key]["title"] : "");
+ if(this.input == null)
+ {
+ // Allow some special extras for objects by passing the whole thing
+ _options[key]["label"] = _options[key]["label"] ? _options[key]["label"] : "";
+ this._appendMultiOption(key, _options[key], _options[key]["title"]);
+ }
+ else
+ {
+ this._appendOptionElement(key,
+ _options[key]["label"] ? _options[key]["label"] : "",
+ _options[key]["title"] ? _options[key]["title"] : "");
+ }
}
else
{
@@ -231,6 +432,19 @@ var et2_selectbox = et2_inputWidget.extend({
}
// Sometimes value gets set before options
if(this.value) this.set_value(this.value);
+ },
+
+ getValue: function() {
+ if(this.input == null)
+ {
+ var value = [];
+ jQuery("input:checked",this.multiOptions).each(function(){value.push(this.value);});
+ return value;
+ }
+ else
+ {
+ return this._super.apply(this, arguments);
+ }
}
});
diff --git a/etemplate/js/test/test.css b/etemplate/js/test/test.css
index a26c71143d..1d9840599c 100644
--- a/etemplate/js/test/test.css
+++ b/etemplate/js/test/test.css
@@ -174,6 +174,54 @@ button.et2_button_text:focus {
font-style: italic;
}
+/**
+ * Multi-select widget
+ */
+.et2_selectbox .ui-widget-header {
+ padding: 2px 6px 0px 6px;
+}
+.et2_selectbox .ui-widget-header ul {
+ float: right;
+}
+.et2_selectbox .ui-widget-header li {
+ float: left;
+ padding: 2px;
+ padding-top: 0px
+}
+.et2_selectbox .ui-widget-header li span.ui-icon {
+ float: left;
+ margin-top: -2px;
+}
+.et2_selectbox .ui-multiselect-checkboxes {
+ overflow-y: scroll;
+ position: relative;
+ text-align: left;
+}
+.et2_selectbox .ui-multiselect-checkboxes li {
+ margin: 0px;
+ clear: both;
+ padding-left: 26px;
+ padding-right: 3px;
+ text-decoration: none;
+ list-style-image: none;
+ list-style-type: none;
+ text-indent: -26px;
+}
+.et2_selectbox .ui-multiselect-checkboxes label {
+ display: block;
+ border: 1px solid transparent;
+}
+.et2_selectbox input[type="checkbox"] {
+ margin: 3px;
+}
+.et2_selectbox .ui-multiselect-checkboxes img {
+ float: right;
+ height: 1.8em;
+}
+
+/**
+ * Date / Time widgets
+ */
span.et2_date {
min-width: 130px;
}
@@ -537,7 +585,7 @@ label input, label span, label div, label select, label textarea {
}
.nextmatch_header > .filters {
padding-left: 5px;
-}
+}
.nextmatch_header .et2_button_icon {
vertical-align: middle;
}