diff --git a/etemplate/inc/class.etemplate_widget_ajax_select.inc.php b/etemplate/inc/class.etemplate_widget_ajax_select.inc.php new file mode 100644 index 0000000000..8b2684ca5c --- /dev/null +++ b/etemplate/inc/class.etemplate_widget_ajax_select.inc.php @@ -0,0 +1,70 @@ +sel_options to be used on the client + * + * @param string $cname + * @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont' + */ + public function beforeSendToClient($cname, array $expand=null) + { + $matches = null; + if ($cname == '$row') // happens eg. with custom-fields: $cname='$row', this->id='#something' + { + $form_name = $this->id; + } + // happens with fields in nm-header: $cname='nm', this->id='${row}[something]' or '{$row}[something]' + elseif (preg_match('/(\${row}|{\$row})\[([^]]+)\]$/', $this->id, $matches)) + { + $form_name = $matches[2]; + } + // happens in auto-repeat grids: $cname='', this->id='something[{$row}]' + elseif (preg_match('/([^[]+)\[({\$row})\]$/', $this->id, $matches)) + { + $form_name = $matches[1]; + } + // happens with autorepeated grids: this->id='some[$row_cont[thing]][else]' --> just use 'else' + elseif (preg_match('/\$row.*\[([^]]+)\]$/', $this->id, $matches)) + { + $form_name = $matches[1]; + } + else + { + $form_name = self::form_name($cname, $this->id, $expand); + } + + // Make sure  s, etc. are properly encoded when sent, and not double-encoded + $options = (isset(self::$request->sel_options[$form_name]) ? $form_name : $this->id); + if(is_array(self::$request->sel_options[$options])) + { + + // Fix any custom options from application + self::fix_encoded_options(self::$request->sel_options[$options],true); + + if(!self::$request->sel_options[$options]) + { + unset(self::$request->sel_options[$options]); + } + } + } +} + +etemplate_widget::registerWidget('etemplate_widget_ajax_select', array('ajax_select')); diff --git a/etemplate/inc/class.etemplate_widget_customfields.inc.php b/etemplate/inc/class.etemplate_widget_customfields.inc.php index 6c23d03414..52355e42a0 100644 --- a/etemplate/inc/class.etemplate_widget_customfields.inc.php +++ b/etemplate/inc/class.etemplate_widget_customfields.inc.php @@ -287,7 +287,7 @@ class etemplate_widget_customfields extends etemplate_widget_transformer break; default: - if (substr($type, 0, 7) !== 'select-') break; + if (substr($type, 0, 7) !== 'select-' && $type != 'ajax_select') break; // fall-through for all select-* widgets case 'select': $this->attrs['multiple'] = $field['rows'] > 1; diff --git a/etemplate/js/et2_extension_customfields.js b/etemplate/js/et2_extension_customfields.js index 27317c5eda..06a179acd2 100644 --- a/etemplate/js/et2_extension_customfields.js +++ b/etemplate/js/et2_extension_customfields.js @@ -427,6 +427,17 @@ var et2_customfields_list = et2_valueWidget.extend([et2_IDetachedDOM, et2_IInput } return true; }, + _setup_ajax_select: function(field_name, field, attrs) { + var attributes = ['get_rows','get_title','id_field','template']; + for(var i = 0; i < attributes.length; i++) + { + if(typeof field.values[attributes[i]] !== 'undefined') + { + attrs[attributes[i]] = field.values[attributes[i]]; + } + } + return true; + }, _setup_float: function(field_name, field, attrs) { // No label on the widget itself delete(attrs.label); diff --git a/etemplate/js/et2_widget_ajaxSelect.js b/etemplate/js/et2_widget_ajaxSelect.js index 649a5ab416..e9d26600c2 100644 --- a/etemplate/js/et2_widget_ajaxSelect.js +++ b/etemplate/js/et2_widget_ajaxSelect.js @@ -36,7 +36,7 @@ var et2_ajaxSelect = et2_inputWidget.extend( "name": "Data source", "type": "any", "default": "", - "description": "Function to get search results." + "description": "Function to get search results, either a javascript function or server-side." }, 'get_title': { "name": "Title function", @@ -47,7 +47,7 @@ var et2_ajaxSelect = et2_inputWidget.extend( 'id_field': { "name": "Result ID field", "type": "string", - "default": "", + "default": "value", "description": "Which key in result sub-array to look for row ID. If omitted, the key for the row will be used." }, 'template': { @@ -74,12 +74,6 @@ var et2_ajaxSelect = et2_inputWidget.extend( "default": "true", "description": "If readonly, widget will be text. If link is set, widget will be a link." }, - 'icon': { - "name": "Icon", - "type": "string", - "default": "", - "description": "Prevent all from looking the same. Use an icon." - }, // Pass by code only 'values': { @@ -95,9 +89,17 @@ var et2_ajaxSelect = et2_inputWidget.extend( * * @memberOf et2_ajaxSelect */ - init: function() { + init: function(parent, attrs) { this._super.apply(this, arguments); + if(typeof attrs.get_rows == 'string') + { + attrs.get_rows = this.egw().link('/index.php', { + menuaction: this.options.get_rows + }) + } + this.createInputWidget(); + this.input = null; this.createInputWidget(); @@ -109,17 +111,73 @@ var et2_ajaxSelect = et2_inputWidget.extend( this.input.addClass("et2_textbox"); this.setDOMNode(this.input[0]); + + var widget = this; + this.input.autocomplete({ + delay: 100, + source: this.options.get_rows ? + this.options.get_rows : + et2_selectbox.find_select_options(this,this.options.values), + select: function(event, ui) { + widget.value = ui.item[widget.options.id_field]; + if(widget.options.get_title) + { + if(typeof widget.options.get_title == 'function') + { + widget.input.val(widget.options.get_title.call(widget.value)); + } + else if (typeof widget.options.get_title == 'string') + { + // TODO: Server side callback + } + } + else + { + widget.input.val(ui.item.label); + } + // Prevent default action of setting field to the value + return false; + } + }); }, getValue: function() { if(this.options.blur && this.input.val() == this.options.blur) return ""; - return this._super.apply(this, arguments); + return this.value; + }, + + set_value: function(_value) + { + this.value = _value; + if(this.input.autocomplete('instance')) + { + var source = this.input.autocomplete('option','source'); + if(typeof source == 'object') + { + for(var i in source) + { + if(typeof source[i].value != 'undefined' && typeof source[i].label != 'undefined' && source[i].value === _value) + { + this.input.val(source[i].label) + } + else if (typeof source[i] == 'string') + { + this.input.val(source[_value]); + break; + } + } + } + else if(typeof source == 'function') + { + // TODO + } + } }, set_blur: function(_value) { if(_value) { - this.input.attr("placeholder", _value + "!"); // HTML5 + this.input.attr("placeholder", _value + ""); // HTML5 if(!this.input[0].placeholder) { // Not HTML5 if(this.input.val() == "") this.input.val(this.options.blur); @@ -172,20 +230,20 @@ var et2_ajaxSelect_ro = et2_valueWidget.extend([et2_IDetachedDOM], this.span.text(_value); }, /** - * Code for implementing et2_IDetachedDOM - */ - getDetachedAttributes: function(_attrs) - { - _attrs.push("value"); - }, + * Code for implementing et2_IDetachedDOM + */ + getDetachedAttributes: function(_attrs) + { + _attrs.push("value"); + }, - getDetachedNodes: function() - { - return [this.span[0]]; - }, + getDetachedNodes: function() + { + return [this.span[0]]; + }, - setDetachedAttributes: function(_nodes, _values) - { + setDetachedAttributes: function(_nodes, _values) + { this.span = jQuery(_nodes[0]); if(typeof _values["value"] != 'undefined') {