summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Machniak <alec@alec.pl>2012-03-21 13:56:31 (GMT)
committerAleksander Machniak <alec@alec.pl>2012-03-21 13:56:31 (GMT)
commit9117d4f73fe8daf847864ce422df93c3c1e4a0e8 (patch)
tree737e99f49296d2e1adc1c582018928b11f4413d2
parent5246c66ba6a80334d9d0c8860cf89b204fe92e48 (diff)
downloadkolab-wap-9117d4f73fe8daf847864ce422df93c3c1e4a0e8.tar.gz
Autocompletion list widget
-rw-r--r--lib/client/kolab_client_task_group.php34
-rw-r--r--lib/client/kolab_client_task_main.php3
-rw-r--r--lib/kolab_client_task.php23
-rw-r--r--lib/kolab_form.php4
-rw-r--r--lib/kolab_html.php7
-rw-r--r--lib/kolab_utils.php9
-rw-r--r--lib/locale/en_US.php3
-rw-r--r--public_html/js/kolab_admin.js294
-rw-r--r--public_html/skins/default/images/buttons.pngbin831 -> 1193 bytes
-rw-r--r--public_html/skins/default/style.css73
-rw-r--r--public_html/skins/default/ui.js161
11 files changed, 563 insertions, 48 deletions
diff --git a/lib/client/kolab_client_task_group.php b/lib/client/kolab_client_task_group.php
index 3d8c876..821d438 100644
--- a/lib/client/kolab_client_task_group.php
+++ b/lib/client/kolab_client_task_group.php
@@ -261,6 +261,27 @@ class kolab_client_task_group extends kolab_client_task
);
}
+ // Members (get member names)
+ if (!empty($data['group'])) {
+ // find members attribute name
+ foreach (array('member', 'uniquemember') as $attr) {
+ if (isset($fields[$attr]) && isset($data[$attr])) {
+ $attr_name = $attr;
+ }
+ }
+ if (!empty($attr_name)) {
+ $result = $this->api->get('group.members_list', array('group' => $data['group']));
+ $list = (array) $result->get('list');
+ $data[$attr_name] = $this->parse_members($list);
+ }
+ }
+
+$fields['debug'] = array(
+ 'label' => 'debug',
+ 'section' => 'system',
+ 'value' => '<pre>'.kolab_html::escape(print_r($data, true)).'</pre>',
+);
+
// Create form object and populate with fields
$form = $this->form_create('group', $attribs, $sections, $fields, $fields_map, $data);
@@ -271,6 +292,19 @@ class kolab_client_task_group extends kolab_client_task
return $form->output();
}
+ private function parse_members($list)
+ {
+ // convert to key=>value array, see kolab_api_service_form_value::list_options_uniquemember()
+ foreach ($list as $idx => $value) {
+ $list[$idx] = $value['displayname'];
+ if (!empty($value['mail'])) {
+ $list[$idx] .= ' <' . $value['mail'] . '>';
+ }
+ }
+
+ return $list;
+ }
+
/**
* Returns list of group types.
*
diff --git a/lib/client/kolab_client_task_main.php b/lib/client/kolab_client_task_main.php
index 9996526..b7b4621 100644
--- a/lib/client/kolab_client_task_main.php
+++ b/lib/client/kolab_client_task_main.php
@@ -40,7 +40,8 @@ class kolab_client_task_main extends kolab_client_task
$this->output->set_env('watermark', $this->output->get_template('watermark'));
// assign default set of translations
- $this->output->add_translation('loading', 'saving', 'deleting', 'servererror', 'search');
+ $this->output->add_translation('loading', 'saving', 'deleting', 'servererror',
+ 'search', 'search.loading', 'search.acchars');
// Create list of tasks for dashboard
// @TODO: check capabilities
diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php
index bd49cd3..6ee696f 100644
--- a/lib/kolab_client_task.php
+++ b/lib/kolab_client_task.php
@@ -618,6 +618,9 @@ class kolab_client_task
if (!empty($field['maxlength'])) {
$result['data-maxlength'] = $field['maxlength'];
}
+ if (!empty($field['autocomplete'])) {
+ $result['data-autocomplete'] = true;
+ }
break;
default:
@@ -781,6 +784,7 @@ class kolab_client_task
}
$form = new kolab_form($attribs);
+ $assoc_fields = array();
// Parse elements and add them to the form object
foreach ($sections as $section_idx => $section) {
@@ -792,20 +796,24 @@ class kolab_client_task
}
if (empty($field['label'])) {
- $field['label'] = "user.$idx";
+ $field['label'] = "$name.$idx";
}
$field['label'] = kolab_html::escape($this->translate($field['label']));
- $field['description'] = "user.$idx.desc";
+ $field['description'] = "$name.$idx.desc";
$field['section'] = $section_idx;
if (!empty($data[$idx])) {
- if (is_array($data[$idx])) {
- $field['value'] = array_map(array('kolab_html', 'escape'), $data[$idx]);
- $field['value'] = implode("\n", $field['value']);
+ $field['value'] = $data[$idx];
+
+ // Convert data for the list field with autocompletion
+ if ($field['data-type'] == kolab_form::TYPE_LIST && kolab_utils::is_assoc($data[$idx])) {
+ $assoc_fields[$idx] = $data[$idx];
+ $field['value'] = array_keys($data[$idx]);
}
- else {
- $field['value'] = kolab_html::escape($data[$idx]);
+
+ if (is_array($field['value'])) {
+ $field['value'] = implode("\n", $field['value']);
}
}
/*
@@ -858,6 +866,7 @@ class kolab_client_task
}
$this->output->set_env('form_id', $attribs['id']);
+ $this->output->set_env('assoc_fields', $assoc_fields);
return $form;
}
diff --git a/lib/kolab_form.php b/lib/kolab_form.php
index c78486f..850ca8f 100644
--- a/lib/kolab_form.php
+++ b/lib/kolab_form.php
@@ -34,6 +34,7 @@ class kolab_form
const INPUT_SUBMIT = 7;
const INPUT_SELECT = 8;
const INPUT_HIDDEN = 9;
+ const INPUT_CUSTOM = 10;
const TYPE_LIST = 1;
@@ -279,9 +280,10 @@ class kolab_form
break;
case self::INPUT_SELECT:
- $content = kolab_html::select($attribs);
+ $content = kolab_html::select($attribs, true);
break;
+ case self::INPUT_CUSTOM:
default:
if (is_array($attribs)) {
$content = isset($attribs['value']) ? $attribs['value'] : '';
diff --git a/lib/kolab_html.php b/lib/kolab_html.php
index afce918..9680ef5 100644
--- a/lib/kolab_html.php
+++ b/lib/kolab_html.php
@@ -316,6 +316,13 @@ class kolab_html
public static function escape($value)
{
+ if (is_array($value)) {
+ foreach ($value as $idx => $val) {
+ $value[$idx] = self::escape($val);
+ }
+ return $value;
+ }
+
return htmlspecialchars($value, ENT_COMPAT, KADM_CHARSET);
}
}
diff --git a/lib/kolab_utils.php b/lib/kolab_utils.php
index 2694f71..b80bc6a 100644
--- a/lib/kolab_utils.php
+++ b/lib/kolab_utils.php
@@ -142,4 +142,13 @@ class kolab_utils
return false;
}
+
+ /**
+ * Finds wether an array is associative or not.
+ */
+ public static function is_assoc ($arr)
+ {
+ return is_array($arr) && count(array_filter(array_keys($arr), 'is_string')) == count($arr);
+ }
+
}
diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php
index efd7439..c0bbbf1 100644
--- a/lib/locale/en_US.php
+++ b/lib/locale/en_US.php
@@ -25,6 +25,8 @@ $LANG['search.prefix'] = 'begins with';
$LANG['search.name'] = 'name';
$LANG['search.email'] = 'email';
$LANG['search.uid'] = 'UID';
+$LANG['search.loading'] = 'Searching...';
+$LANG['search.acchars'] = 'At least $min characters required for autocompletion';
$LANG['menu.users'] = 'Users';
$LANG['menu.groups'] = 'Groups';
@@ -98,6 +100,7 @@ $LANG['group.group_type_id'] = 'Group type';
$LANG['group.add.success'] = 'Group created successfully.';
$LANG['group.delete.success'] = 'Group deleted successfully.';
$LANG['group.gidnumber'] = 'Primary group number';
+$LANG['group.uniquemember'] = 'Members';
$LANG['group.system'] = 'System';
$LANG['group.other'] = 'Other';
diff --git a/public_html/js/kolab_admin.js b/public_html/js/kolab_admin.js
index bdcf1e2..f5075e2 100644
--- a/public_html/js/kolab_admin.js
+++ b/public_html/js/kolab_admin.js
@@ -38,7 +38,6 @@ function kolab_admin()
beforeSend: function(xmlhttp) { xmlhttp.setRequestHeader('X-Session-Token', ref.env.token); }
});
-
/*********************************************************/
/********* basic utilities *********/
/*********************************************************/
@@ -357,7 +356,7 @@ function kolab_admin()
this.display_message(msg, 'error');
// Logout on invalid-session error
- if (response.code == 403)
+ if (response && response.code == 403)
this.main_logout();
return false;
@@ -412,6 +411,262 @@ function kolab_admin()
}
};
+
+ /*********************************************************/
+ /********* keyboard autocomplete methods *********/
+ /*********************************************************/
+
+ this.ac_init = function(obj, props)
+ {
+ obj.keydown(function(e) { return kadm.ac_keydown(e, props); })
+ .attr('autocomplete', 'off');
+ };
+
+ // handler for keyboard events on autocomplete-fields
+ this.ac_keydown = function(e, props)
+ {
+ if (this.ac_timer)
+ clearTimeout(this.ac_timer);
+
+ var highlight, key = e.which;
+
+ switch (key) {
+ case 38: // arrow up
+ case 40: // arrow down
+ if (!this.ac_visible())
+ break;
+
+ var dir = key == 38 ? 1 : 0;
+
+ highlight = $('.selected', this.ac_pane).get(0);
+
+ if (!highlight)
+ highlight = this.ac_pane.__ul.firstChild;
+
+ if (highlight)
+ this.ac_select(dir ? highlight.previousSibling : highlight.nextSibling);
+
+ return e.stopPropagation();
+
+ case 9: // tab
+ if (e.shiftKey || !this.ac_visible()) {
+ this.ac_stop();
+ return;
+ }
+
+ case 13: // enter
+ if (!this.ac_visible())
+ return false;
+
+ // insert selected item and hide selection pane
+ this.ac_insert(this.ac_selected);
+ this.ac_stop();
+
+ return e.stopPropagation();
+
+ case 27: // escape
+ this.ac_stop();
+ return;
+
+ case 37: // left
+ case 39: // right
+ if (!e.shiftKey)
+ return;
+ }
+
+ // start timer
+ this.ac_timer = window.setTimeout(function() { kadm.ac_start(props); }, 200);
+ this.ac_input = e.target;
+
+ return true;
+ };
+
+ this.ac_visible = function()
+ {
+ return (this.ac_selected !== null && this.ac_selected !== undefined && this.ac_value);
+ };
+
+ this.ac_select = function(node)
+ {
+ if (!node)
+ return;
+
+ var current = $('.selected', this.ac_pane);
+
+ if (current.length)
+ current.removeClass('selected');
+
+ $(node).addClass('selected');
+ this.ac_selected = node._id;
+ };
+
+ // autocomplete search processor
+ this.ac_start = function(props)
+ {
+ var q = this.ac_input ? this.ac_input.value : null,
+ min = this.env.autocomplete_min_length,
+ old_value = this.ac_value,
+ ac = this.ac_data;
+
+ if (q === null)
+ return;
+
+ // trim query string
+ q = $.trim(q);
+
+ // Don't (re-)search if the last results are still active
+ if (q == old_value)
+ return;
+
+ // Stop and destroy last search
+ this.ac_stop();
+
+ if (q.length && q.length < min) {
+ if (!this.ac_info) {
+ this.ac_info = this.display_message(
+ this.t('search.acchars').replace('$min', min));
+ }
+ return;
+ }
+
+ this.ac_value = q;
+
+ // ...string is empty
+ if (!q.length)
+ return;
+
+ // ...new search value contains old one, but the old result was empty
+ if (old_value && old_value.length && q.indexOf(old_value) == 0 && this.ac_result && !this.ac_result.length)
+ return;
+
+ var i, xhr, data = props,
+ action = props && props.action ? props.action : 'form_value.list_options';
+
+ this.ac_oninsert = props.oninsert;
+ data.search = q;
+ delete data['action'];
+ delete data['insert_func'];
+
+ this.display_message(this.t('search.loading'), 'loading');
+ xhr = this.api_post(action, data, 'ac_result');
+ this.ac_data = xhr;
+ };
+
+ this.ac_result = function(response)
+ {
+ // search stopped in meantime?
+ if (!this.ac_value)
+ return;
+
+ if (!this.api_response(response))
+ return;
+
+ // ignore this outdated search response
+ if (this.ac_input && response.result.search != this.ac_value)
+ return;
+
+ // display search results
+ var i, ul, li, text,
+ result = response.result.list,
+ pos = $(this.ac_input).offset(),
+ value = this.ac_value,
+ rx = new RegExp('(' + RegExp.escape(value) + ')', 'ig');
+
+ // create results pane if not present
+ if (!this.ac_pane) {
+ ul = $('<ul>');
+ this.ac_pane = $('<div>').attr('id', 'autocompletepane')
+ .css({ position:'absolute', 'z-index':30000 }).append(ul).appendTo(document.body);
+ this.ac_pane.__ul = ul[0];
+ }
+
+ ul = this.ac_pane.__ul;
+
+ // reset content
+ ul.innerHTML = '';
+ // move the results pane right under the input box
+ this.ac_pane.css({left: (pos.left - 1)+'px', top: (pos.top + this.ac_input.offsetHeight - 1)+'px', display: 'none'});
+
+ // add each result line to the list
+ for (i in result) {
+ text = result[i];
+ li = document.createElement('LI');
+ li.innerHTML = text.replace(rx, '##$1%%').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/##([^%]+)%%/g, '<b>$1</b>');
+ li.onmouseover = function() { kadm.ac_select(this); };
+ li.onmouseup = function() { kadm.ac_click(this) };
+ li._id = i;
+ ul.appendChild(li);
+ }
+
+ if (ul.childNodes.length) {
+ this.ac_pane.show();
+
+ // select the first
+ li = $('li:first', ul);
+ li.addClass('selected');
+ this.ac_selected = li.get(0)._id;
+ }
+
+ this.env.ac_result = result;
+ };
+
+ this.ac_click = function(node)
+ {
+ if (this.ac_input)
+ this.ac_input.focus();
+
+ this.ac_insert(node._id);
+ this.ac_stop();
+ };
+
+ this.ac_insert = function(id)
+ {
+ var val = this.env.ac_result[id];
+
+ if (typeof this.ac_oninsert == 'function')
+ this.ac_oninsert(id, val);
+ else
+ $(this.ac_input).val(val);
+ };
+
+ this.ac_blur = function()
+ {
+ if (this.ac_timer)
+ clearTimeout(this.ac_timer);
+
+ this.ac_input = null;
+ this.ac_stop();
+ };
+
+ this.ac_stop = function()
+ {
+ this.ac_selected = null;
+ this.ac_value = '';
+
+ if (this.ac_pane)
+ this.ac_pane.hide();
+
+ this.ac_destroy();
+ };
+
+ // Clears autocomplete data/requests
+ this.ac_destroy = function()
+ {
+ if (this.ac_data)
+ this.ac_data.abort();
+
+ if (this.ac_info)
+ this.hide_message(this.ac_info);
+
+ if (this.ac_msg)
+ this.hide_message(this.ac_msg);
+
+ this.ac_data = null;
+ this.ac_info = null;
+ this.ac_msg = null;
+ };
+
+
/*********************************************************/
/********* Forms *********/
/*********************************************************/
@@ -615,9 +870,32 @@ function kolab_admin()
this.command('group.list', {page: page});
};
-};
+ this.group_save = function(reload, section)
+ {
+ var data = this.serialize_form('#'+this.env.form_id);
-var kadm = new kolab_admin();
+ if (reload) {
+ data.section = section;
+ this.http_post('group.add', {data: data});
+ return;
+ }
+
+ this.form_error_clear();
+
+ this.set_busy(true, 'saving');
+ this.api_post('group.add', data, 'group_save_response');
+ };
+
+ this.group_save_response = function(response)
+ {
+ if (!this.api_response(response))
+ return;
+
+ this.display_message('group.add.success');
+ this.command('group.list', {page: this.env.list_page});
+ };
+
+};
// Add escape() method to RegExp object
// http://dev.rubyonrails.org/changeset/7271
@@ -625,3 +903,11 @@ RegExp.escape = function(str)
{
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
+
+var kadm = new kolab_admin();
+
+// general click handler
+$(document).click(function() {
+ // destroy autocompletion
+ kadm.ac_stop();
+});
diff --git a/public_html/skins/default/images/buttons.png b/public_html/skins/default/images/buttons.png
index 0492d09..586851e 100644
--- a/public_html/skins/default/images/buttons.png
+++ b/public_html/skins/default/images/buttons.png
Binary files differ
diff --git a/public_html/skins/default/style.css b/public_html/skins/default/style.css
index 96cfa30..bb52537 100644
--- a/public_html/skins/default/style.css
+++ b/public_html/skins/default/style.css
@@ -25,6 +25,7 @@ textarea {
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
padding-left: 2px;
+ color: black;
}
table.list {
@@ -494,6 +495,9 @@ textarea.readonly {
span.listarea {
display: block;
width: 370px;
+ max-height: 209px;
+ overflow-y: auto;
+ overflow-x: hidden;
margin: 0 0 1px;
padding: 0;
background-color: white;
@@ -520,7 +524,8 @@ span.listelement input {
border: none;
background-color: transparent;
padding-left: 2px;
- width: 330px;
+ /* FIXME: it should be 330px, but when listarea has scroller, input jumps below action buttons */
+ width: 314px;
height: 19px;
}
@@ -561,18 +566,84 @@ span.listelement span.actions span.add {
background-position: -43px 0;
}
+span.listelement span.actions span.search {
+ background-position: -65px 0;
+ cursor: default;
+}
+
span.listarea.readonly {
background-color: #f5f5f5;
}
+input.readonly,
span.listarea.readonly span.listelement input {
color: #a0a0a0;
+ cursor: default;
+}
+
+span.listarea.autocomplete span.listelement input {
+ color: #514949;
+}
+
+span.listarea.autocomplete span.listelement input.autocomplete {
+ color: black;
}
span.listarea.readonly span.listelement span.actions {
opacity: .5;
}
+.autocomplete > span.listelement input {
+ /* FIXME: it should be 348px, but when listarea has scroller, input jumps below action buttons */
+ width: 332px;
+}
+
+.autocomplete > span.listelement span.actions {
+ width: 18px;
+}
+
+.autocomplete > span.listelement span.actions span.reset {
+ border-left: none;
+}
+
+.autocomplete > span.listelement span.actions span.search:hover {
+ background-color: #f0f0f0;
+}
+
+/***** autocomplete list *****/
+
+#autocompletepane
+{
+ background-color: white;
+ border: 1px solid #d0d0d0;
+ min-width: 351px;
+}
+
+#autocompletepane ul
+{
+ margin: 0px;
+ padding: 2px;
+ list-style-image: none;
+ list-style-type: none;
+}
+
+#autocompletepane ul li
+{
+ display: block;
+ height: 16px;
+ font-size: 11px;
+ padding-left: 6px;
+ padding-top: 2px;
+ padding-right: 6px;
+ white-space: nowrap;
+ cursor: pointer;
+}
+
+#autocompletepane ul li.selected
+{
+ background-color: #d6efff;
+}
+
/***** tabbed interface elements *****/
div.tabsbar
diff --git a/public_html/skins/default/ui.js b/public_html/skins/default/ui.js
index 850eb93..32f5dfc 100644
--- a/public_html/skins/default/ui.js
+++ b/public_html/skins/default/ui.js
@@ -143,7 +143,7 @@ function form_serialize(data)
// replace some textarea fields with pretty/smart input lists
$('textarea[data-type="list"]', form).not('disabled').each(function() {
var i, v, value = [],
- re = RegExp('^' + RegExp.escape(this.name) + '\[[0-9]+\]$');
+ re = RegExp('^' + RegExp.escape(this.name) + '\[[0-9-]+\]$');
for (i in data.json) {
if (i.match(re)) {
@@ -152,6 +152,14 @@ function form_serialize(data)
delete data.json[i];
}
}
+
+ // autocompletion lists data is stored in env variable
+ if (kadm.env.assoc_fields[this.name]) {
+ value = [];
+ for (i in kadm.env.assoc_fields[this.name])
+ value.push(i);
+ }
+
data.json[this.name] = value;
});
@@ -187,67 +195,152 @@ function form_init(id)
// Replaces form element with smart element
function form_element_wrapper(form_element)
{
- var i, len, elem, e = $(form_element),
- list = form_element.value.split("\n"),
- area = $('<span class="listarea"></span>'),
- disabled = e.attr('disabled') || e.attr('readonly');
+ var i, j = 0, len, elem, e = $(form_element),
+ list = kadm.env.assoc_fields[form_element.name],
+ disabled = e.attr('disabled') || e.attr('readonly'),
+ autocomplete = e.attr('data-autocomplete'),
+ maxlength = e.attr('data-maxlength'),
+ area = $('<span class="listarea"></span>');
e.hide();
- for (i=0, len=list.length; i<len; i++) {
+ // add autocompletion input
+ if (!disabled && autocomplete) {
+ elem = form_list_element(form_element.form, {
+ maxlength: maxlength,
+ autocomplete: autocomplete,
+ element: e
+ }, -1);
+
+ elem.appendTo(area);
+ kadm.ac_init(elem, {attribute: form_element.name, oninsert: form_element_insert_func});
+ }
+
+ if (!list && form_element.value)
+ list = $.extend({}, form_element.value.split("\n"));
+
+ // add input rows
+ for (i in list) {
elem = form_list_element(form_element.form, {
- name: form_element.name+'['+i+']',
value: list[i],
+ key: i,
disabled: disabled,
- maxlength: e.attr('data-maxlength')
- });
+ maxlength: maxlength,
+ autocomplete: autocomplete,
+ element: e
+ }, j++);
+
elem.appendTo(area);
}
if (disabled)
area.addClass('readonly');
+ if (autocomplete)
+ area.addClass('autocomplete');
area.appendTo(form_element.parentNode);
}
// Creates smart list element
-function form_list_element(form, data)
+function form_list_element(form, data, idx)
{
- var elem = $('<span class="listelement"><span class="actions">'
- + '<span title="" class="add"></span><span title="" class="reset"></span>'
- + '</span><input></span>');
+ var content, elem, input,
+ key = data.key,
+ orig = data.element
+ ac = data.autocomplete;
+
+ data.name = data.name || orig.attr('name') + '[' + idx + ']';
+ data.disabled = data.disabled || (ac && idx >= 0);
+ data.readonly = data.readonly || (ac && idx >= 0);
+
+ // remove internal attributes
+ delete data['element'];
+ delete data['autocomplete'];
+ delete data['key'];
+
+ // build element content
+ content = '<span class="listelement"><span class="actions">'
+ + (!ac ? '<span title="" class="add"></span>' : ac && idx == -1 ? '<span title="" class="search"></span>' : '')
+ + (!ac || idx >= 0 ? '<span title="" class="reset"></span>' : '')
+ + '</span><input></span>';
+
+ elem = $(content);
+ input = $('input', elem);
+
+ // Set INPUT attributes
+ input.attr(data);
- $('input', elem).attr(data);
+ if (data.readonly)
+ input.addClass('readonly');
- if (data.disabled)
+ if (ac && idx == -1)
+ input.addClass('autocomplete');
+
+ if (data.disabled && !ac)
return elem;
// attach element creation event
- $('span[class="add"]', elem).click(function() {
- var dt = (new Date()).getTime(),
- span = $(this.parentNode.parentNode),
- name = data.name.replace(/\[[0-9]+\]$/, ''),
- elem = form_list_element(form, {name: name+'['+dt+']'});
-
- span.after(elem);
- $('input', elem).focus();
- });
+ if (!ac)
+ $('span[class="add"]', elem).click(function() {
+ var dt = (new Date()).getTime(),
+ span = $(this.parentNode.parentNode),
+ name = data.name.replace(/\[[0-9]+\]$/, ''),
+ elem = form_list_element(form, {name: name}, dt);
+
+ span.after(elem);
+ $('input', elem).focus();
+ kadm.ac_stop();
+ });
// attach element deletion event
- $('span[class="reset"]', elem).click(function() {
- var l, span = $(this.parentNode.parentNode),
- name = data.name.replace(/\[[0-9]+\]$/, ''),
- l = $('input[name^="' + name + '"]', form);
-
- if (l.length > 1)
- span.remove();
- else
- $('input', span).val('').focus();
- });
+ if (!ac || idx >= 0)
+ $('span[class="reset"]', elem).click(function() {
+ var span = $(this.parentNode.parentNode),
+ name = data.name.replace(/\[[0-9]+\]$/, ''),
+ l = $('input[name^="' + name + '"]', form),
+ key = $(this).data('key');
+
+ if (ac || l.length > 1)
+ span.remove();
+ else
+ $('input', span).val('').focus();
+
+ // delete key from internal field representation
+ if (key !== undefined && kadm.env.assoc_fields[name])
+ delete kadm.env.assoc_fields[name][key];
+
+ kadm.ac_stop();
+ }).data('key', key);
return elem;
}
+function form_element_insert_func(key, val)
+{
+ var elem, input = $(this.ac_input).get(0),
+ dt = (new Date()).getTime(),
+ span = $(input.parentNode),
+ name = input.name.replace(/\[-1\]$/, '');
+
+ // reset autocomplete input
+ input.value = '';
+
+ // check if element doesn't exist on the list already
+ if (kadm.env.assoc_fields[name][key])
+ return;
+
+ // add element
+ elem = form_list_element(input.form, {
+ name: name,
+ autocomplete: true,
+ value: val
+ }, dt);
+ span.after(elem);
+
+ // update field variable
+ kadm.env.assoc_fields[name][key] = val;
+}
+
/**
* UI Initialization
*/