egroupware/etemplate/js/et2_widget_selectbox.js
Nathan Gray 4ae05cb5b8 - Implement detached interface for read only selectbox
- When looking for selectbox options, try last part of the name at the root first
2011-10-19 21:03:22 +00:00

426 lines
11 KiB
JavaScript

/**
* eGroupWare eTemplate2 - JS Selectbox object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @author Andreas Stöckel
* @copyright Nathan Gray 2011
* @version $Id$
*/
"use strict";
/*egw:uses
lib/tooltip;
jquery.jquery;
et2_core_xml;
et2_core_DOMWidget;
et2_core_inputWidget;
*/
var et2_selectbox = et2_inputWidget.extend({
attributes: {
"multiple": {
"name": "multiple",
"type": "boolean",
"default": false,
"description": "Allow selecting multiple options"
},
"rows": {
"name": "Rows",
"type": "any", // Old options put either rows or empty_label in first space
"default": 1,
"description": "Number of rows to display"
},
"empty_label": {
"name": "Empty label",
"type": "string",
"default": "",
"description": "Textual label for first row, eg: 'All' or 'None'. ID will be ''"
},
"select_options": {
"type": "any",
"name": "Select options",
"default": {},
"description": "Internaly used to hold the select options."
}
},
legacyOptions: ["rows"],
init: function() {
this._super.apply(this, arguments);
// 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.options.rows;
this.options.rows = 1;
}
if(this.options.rows > 1) this.options.multiple = true;
this.createInputWidget();
},
destroy: function() {
this._super.apply(this, arguments);
this.input = null;
},
transformAttributes: function(_attrs) {
this._super.apply(this, arguments);
// If select_options are already known, skip the rest
if(this.options && this.options.select_options && !jQuery.isEmptyObject(this.options.select_options))
{
return;
}
var name_parts = this.id.replace(/]/g,'').split('[');
// Try to find the options inside the "sel-options" array
if(this.getArrayMgr("sel_options"))
{
// Select options tend to be defined once, at the top level, so try that first
var content_options = this.getArrayMgr("sel_options").getRoot().getEntry(name_parts[name_parts.length-1]);
// Try again according to ID
if(!content_options) content_options = this.getArrayMgr("sel_options").getEntry(this.id);
if(_attrs["select_options"] && content_options)
{
_attrs["select_options"] = jQuery.extend({},_attrs["select_options"],content_options);
} else if (content_options) {
_attrs["select_options"] = content_options;
}
}
// Check whether the options entry was found, if not read it from the
// content array.
if (_attrs["select_options"] == null)
{
// Again, try last name part at top level
var content_options = this.getArrayMgr('content').getRoot().getEntry(name_parts[name_parts.length-1]);
// If that didn't work, check according to ID
_attrs["select_options"] = content_options ? content_options : this.getArrayMgr('content')
.getEntry("options-" + this.id)
}
// Default to an empty object
if (_attrs["select_options"] == null)
{
_attrs["select_options"] = {};
}
},
_appendOptionElement: function(_value, _label, _title) {
if(_value == "" && (_label == null || _label == "")) {
_label = this.options.empty_label;
}
var option = $j(document.createElement("option"))
.attr("value", _value)
.text(_label+"");
if (typeof _title != "undefined" && _title)
{
option.attr("title", _title);
}
option.appendTo(this.input);
},
createInputWidget: function() {
// Create the base input widget
this.input = $j(document.createElement("select"))
.addClass("et2_selectbox")
.attr("size", this.options.rows);
this.setDOMNode(this.input[0]);
// Add the empty label
if(this.options.empty_label)
{
this._appendOptionElement("" == this.getValue() ? "selected" : "",
this.options.empty_label);
}
// Set multiple
if(this.options.multiple)
{
this.input.attr("multiple", "multiple");
}
},
loadFromXML: function(_node) {
// Read the option-tags
var options = et2_directChildrenByTagName(_node, "options");
for (var i = 0; i < options.length; i++)
{
this._appendOptionElement(
et2_readAttrWithDefault(options[i], "value", options[i].textContent),
options[i].textContent,
et2_readAttrWithDefault(options[i], "title", "")
);
}
},
set_value: function(_value) {
jQuery("option",this.input).attr("selected", false);
this.value = _value;
if(typeof _value == "array")
{
for(var i = 0; i < _value.length; i++)
{
jQuery("option[value='"+_value[i]+"']", this.input).attr("selected", true);
}
}
else if (typeof _value == "object")
{
for(var i in _value)
{
jQuery("option[value='"+_value[i]+"']", this.input).attr("selected", true);
}
}
else
{
if(jQuery("option[value='"+_value+"']", this.input).attr("selected", true).length == 0)
{
et2_debug("warning", "Tried to set value that isn't an option", this, _value);
//console.trace();
}
}
},
/**
* The set_select_options function is added, as the select options have to be
* added after the "option"-widgets were added to selectbox.
*/
set_select_options: function(_options) {
// Add the select_options
for (var key in _options)
{
var attrs = {
"value": key
};
if (_options[key] instanceof Object)
{
this._appendOptionElement(key,
_options[key]["label"] ? _options[key]["label"] : "",
_options[key]["title"] ? _options[key]["title"] : "");
}
else
{
this._appendOptionElement(key, _options[key]);
}
}
// Sometimes value gets set before options
if(this.value) this.set_value(this.value);
}
});
et2_register_widget(et2_selectbox, ["menupopup", "listbox", "select", "select-cat",
"select-account", "select-percent", 'select-priority', 'select-access',
'select-country', 'select-state', 'select-year', 'select-month',
'select-day', 'select-dow', 'select-hour', 'select-number', 'select-app',
'select-lang', 'select-bool', 'select-timezone' ]);
/**
* et2_selectbox_ro is the readonly implementation of the selectbox.
*/
var et2_selectbox_ro = et2_selectbox.extend([et2_IDetachedDOM], {
init: function() {
this._super.apply(this, arguments);
this.supportedWidgetClasses = [];
this.optionValues = {};
},
createInputWidget: function() {
this.span = $j(document.createElement("span"))
.addClass("et2_selectbox readonly")
.text(this.options.empty_label);
this.setDOMNode(this.span[0]);
},
loadFromXML: function(_node) {
// Read the option-tags
var options = et2_directChildrenByTagName(_node, "options");
for (var i = 0; i < options.length; i++)
{
this.optionValues[et2_readAttrWithDefault(options[i], "value", 0)] =
{
"label": options[i].textContent,
"title": et2_readAttrWithDefault(options[i], "title", "")
}
}
},
set_select_options: function(_options) {
for (var key in _options)
{
this.optionValues[key] = _options[key];
}
},
set_value: function(_value) {
this.value = _value;
var option = this.optionValues[_value];
if (option instanceof Object)
{
this.span.text(option.label);
this.set_statustext(option.title);
}
else if (typeof option == "string")
{
this.span.text(option);
}
else
{
this.span.text("");
}
},
/**
* Functions for et2_IDetachedDOM
*/
/**
* Creates a list of attributes which can be set when working in the
* "detached" mode. The result is stored in the _attrs array which is provided
* by the calling code.
*/
getDetachedAttributes: function(_attrs) {
_attrs.push("value");
},
/**
* Returns an array of DOM nodes. The (relatively) same DOM-Nodes have to be
* passed to the "setDetachedAttributes" function in the same order.
*/
getDetachedNodes: function() {
return [this.span[0]];
},
/**
* Sets the given associative attribute->value array and applies the
* attributes to the given DOM-Node.
*
* @param _nodes is an array of nodes which have to be in the same order as
* the nodes returned by "getDetachedNodes"
* @param _values is an associative array which contains a subset of attributes
* returned by the "getDetachedAttributes" function and sets them to the
* given values.
*/
setDetachedAttributes: function(_nodes, _values) {
this.span = jQuery(_nodes[0]);
this.set_value(_values["value"]);
}
});
et2_register_widget(et2_selectbox_ro, ["menupopup_ro", "listbox_ro", "select_ro", "select-cat_ro",
"select-account_ro", "select-percent_ro", 'select-priority_ro', 'select-access_ro',
'select-country_ro', 'select-state_ro', 'select-year_ro', 'select-month_ro',
'select-day_ro', 'select-dow_ro', 'select-hour_ro', 'select-number_ro', 'select-app_ro',
'select-lang_ro', 'select-bool_ro', 'select-timezone_ro' ]);
/**
* Widget class which represents a single option inside a selectbox
*/
/*var et2_option = et2_baseWidget.extend({
attributes: {
"value": {
"name": "Value",
"type": "string",
"description": "Value which is sent back to the server when this entry is selected."
},
"label": {
"name": "Label",
"type": "string",
"description": "Caption of the option element"
},
"width": {
"ignore": true
},
"height": {
"ignore": true
},
"align": {
"ignore": true
}
},
init: function() {
this._super.apply(this, arguments);
// Only allow other options inside of this element
this.supportedWidgetClasses = [et2_option];
this.option = $j(document.createElement("option"))
.attr("value", this.options.value)
.attr("selected", this._parent.options.value == this.options.value ?
"selected" : "");
if (this.options.label)
{
this.option.text(this.options.label);
}
this.setDOMNode(this.option[0]);
},
destroy: function() {
this._super.apply(this, arguments);
this.option = null;
},
loadContent: function(_data) {
this.option.text(_data);
}
/* Doesn't work either with selectboxes
set_statustext: function(_value) {
this.statustext = _value;
this.option.attr("title", _value);
}*/
//});*/
//et2_register_widget(et2_option, ["option"]);
/**
* Class which just implements the menulist container
*/
var et2_menulist = et2_DOMWidget.extend({
init: function() {
this._super.apply(this, arguments);
this.supportedWidgetClasses = [et2_selectbox, et2_selectbox_ro];
},
// Just pass the parent DOM node through
getDOMNode: function(_sender) {
if (_sender != this)
{
return this._parent.getDOMNode(this);
}
return null;
}
});
et2_register_widget(et2_menulist, ["menulist"]);