function AutoComplete(inputField, tbl, viewFld, additionViewFld, hiddenFld, hiddenValTemplate)
{
  var itemsXml = null;
  var waitForRequest = false;
  var markedItem = null;
  var lastKey = null;
  var curValue = '';
  var divHovered = false;
  var mousepressed = false;

  var minChars = 2;

  var hiddenField = document.createElement('input');
  hiddenField.type = 'hidden';
  hiddenField.name = inputField.name;
  hiddenField.id   = inputField.id;
  hiddenField.value = inputField.value;
  inputField.removeAttribute('name');
  inputField.removeAttribute('id');

  var pos = LDMain.findPos(inputField);
  var div = document.createElement('div');
  div.style.position = 'absolute';
  div.style.border = '1px solid #000000';
  div.style.display = 'none';
  div.style.backgroundColor = '#FFFFFF';
  div.style.color = '#000000';
  div.style.overflowX = 'hidden';
  div.style.overflowY = 'auto';
  div.style.cursor = 'default';

  var emptyAlert = document.createElement('img');
  emptyAlert.src = APPLICATION_PATH + 'cmsimages/emptyAlert.png';
  emptyAlert.style.position = 'absolute';
  emptyAlert.style.display = 'none';

  var waitPng = document.createElement('img');
  waitPng.src = APPLICATION_PATH + 'cmsimages/wait.png';
  waitPng.style.position = 'absolute';
  waitPng.style.display = 'none';

  divPos();

  inputField.parentNode.appendChild(hiddenField);
  inputField.ownerDocument.getElementsByTagName('body')[0].appendChild(div);
  inputField.ownerDocument.getElementsByTagName('body')[0].appendChild(emptyAlert);
  inputField.ownerDocument.getElementsByTagName('body')[0].appendChild(waitPng);

  inputField.onkeyup = update;
  inputField.onkeydown = keydown;
  inputField.onpaste = update;
  inputField.onblur  = blur;
  div.onmouseover = function() {hoverDiv(true);};
  div.onmouseout = function() {hoverDiv(false);};
  div.onmousedown = function() {mousepressed = true; return true;};
  div.onmouseup = function() {mousepressed = false; return true;};
  if (typeof(window.attachEvent) != 'undefined')
    window.attachEvent('onresize', divPos);
  else
    window.addEventListener('resize', divPos, true);

  if (inputField.value != '')
    initValue();

  this.setMinChars = function(val)
  {
    if (typeof(val) == 'number')
      minChars = val;
  }

  function blur()
  {
    if ((itemsXml != null) && !mousepressed)
    {
      var items = itemsXml.selectNodes('return/data/items/item');
      var cnt = items.length;
      var exactItem = null;
      for (var i = 0; i < cnt; i++)
      {
        if (inputField.value.toLowerCase() == items[i].getAttribute('value').toLowerCase())
        {
          var val = items[i].getAttribute('value');
          var hidVal = hiddenValTemplate.replace('[[[value]]]', items[i].getAttribute('hiddenValue'));
          exactItem = document.createElement('div');
          exactItem.setAttribute('xml:value', val);
          exactItem.setAttribute('xml:hiddenValue', hidVal);
          break;
        }
      }
      if (exactItem != null)
        select(null, exactItem);
      else
      {
        reset();
        emptyAlert.style.display = 'block';
      }
    } else if (!divHovered)
        reset();
  }

  function divPos()
  {
    var pos = LDMain.findPos(inputField);
    div.style.left = pos.x + 1 + 'px';
    div.style.top  = (pos.y + inputField.offsetHeight) + 'px';
    div.style.width = (inputField.offsetWidth - 2) + 'px';
    div.style.height = '';
    div.style.maxHeight = '200px';
    if (div.offsetHeight > 202)
      div.style.height = '200px';

    emptyAlert.style.top = (pos.y + (inputField.offsetHeight / 2 - 8)) + 'px';
    emptyAlert.style.left = (pos.x + inputField.offsetWidth - 20) + 'px';

    waitPng.style.top = (pos.y + (inputField.offsetHeight / 2 - 8)) + 'px';
    waitPng.style.left = (pos.x + inputField.offsetWidth - 20) + 'px';
  }

  function hoverDiv(hover)
  {
    window.status = 'hovered: ' + hover;
    divHovered = hover;
  }

  function update(event)
  {
    if (typeof(event) == 'undefined')
      event = window.event;
    if (curValue != inputField.value)
      hiddenField.value = '';

    lastKey = event.keyCode;

    if ((event.keyCode == 13) && (div.style.display == 'block')) //Enter
      select(null, markedItem);
    else if (event.keyCode == 27) //Esc
      reset();
    else if ((event.keyCode == 8) || (event.keyCode == 46)) //Backspace || Del
    {
      if (inputField.value.length < minChars)
        reset();
      else
      {
        if (itemsXml != null)
          valuesReady(itemsXml);
        else
          getValues();
      }
    } else if ((inputField.value.length >= minChars) || ((event.keyCode == 13/*Enter*/) && (div.style.display == 'none')) || (inputField.value == '*') || ((itemsXml != null) && (div.style.display == 'block')))
    {
      if ((event.keyCode != 8) && (event.keyCode != 38) && (event.keyCode != 40)) //Backspace, up, down
      {
        if ((itemsXml != null) && ((inputField.value.length >= minChars) || (div.style.display == 'block')))
          valuesReady(itemsXml);
        else
          getValues();
      }
    }

    return;
  }

  function keydown(event)
  {
    if (typeof(event) == 'undefined')
      event = window.event;
    if (event.keyCode == 13)
      return false;
    if ((event.keyCode == 38) || (event.keyCode == 40)) //up-/down-key
    {
      if (div.style.display == 'block')
        moveMark(event.keyCode == 40);
      else
        getValues();
    }
  }

  function moveMark(down)
  {
    if (down)
    {
      if (markedItem == null)
        mark(null, div.firstChild);
      else if (markedItem.nextSibling != null)
        mark(null, markedItem.nextSibling);
      else
        mark(null, div.firstChild);
    } else
    {
      if (markedItem == null)
        mark(null, div.lastChild);
      else if (markedItem.previousSibling != null)
        mark(null, markedItem.previousSibling);
      else
        mark(null, div.lastChild);
    }
    if (((markedItem.offsetTop + markedItem.offsetHeight) > (div.scrollTop + div.offsetHeight)) || (markedItem.offsetTop < div.scrollTop))
      if (down)
        div.scrollTop = (markedItem.offsetTop + markedItem.offsetHeight - (div.offsetHeight - 3));
      else
        div.scrollTop = markedItem.offsetTop + 1;
  }

  function mark(event, item)
  {
    if (typeof(event) == 'undefined')
      event = window.event;
    if ((item != null) || (event != null))
    {
      if (item == null)
        item = event.target ? event.target : event.srcElement;
      if (markedItem != null)
      {
        markedItem.style.backgroundColor = '';
        markedItem.style.color = '';
      }
      item.style.backgroundColor = 'highlight';
      item.style.color = 'highlightText';
      markedItem = item;
    }
  }

  function getValues()
  {
    if (waitForRequest)
      return;
    var val = inputField.value.replace(/\*/g, '%').replace(/\?/g, '_');
    if (val.length > minChars)
      val = val.substring(0, minChars);
    var ajax = new Ajax(valuesReady);
    ajax.send('func=autocomplete&tbl='+tbl+'&viewFld='+viewFld+'&additionalViewFld='+additionViewFld+'&hiddenFld='+hiddenFld+'&value='+val);
    divPos();
    waitPng.style.display = 'block';
    emptyAlert.style.display = 'none';
    waitForRequest = true;
  }

  function initValue()
  {
    var val = inputField.value.replace(hiddenValTemplate.replace(/\[\[\[value\]\]\]/, ''), '');
    var ajax = new Ajax(valuesReady);
    ajax.send('func=autocomplete&tbl='+tbl+'&viewFld='+viewFld+'&additionalViewFld='+additionViewFld+'&hiddenFld='+hiddenFld+'&value='+val+'&init=1');
    divPos();
    inputField.value = '';
    waitPng.style.display = 'block';
    waitForRequest = true;
  }

  function valuesReady(xmlDoc)
  {
    waitPng.style.display = 'none';
    waitForRequest = false;

    itemsXml = xmlDoc;

    markedItem = null;
    clearElement(div);

    var value = inputField.value.replace(/\*/g, '.*').replace(/\?/g, '.');

    var items = xmlDoc.selectNodes('return/data/items/item');
    var cnt = items.length;
    var cntShown = 0;;
    for (var i = 0; i < cnt; i++)
    {
      if (!new RegExp('^'+value.toLowerCase()).test(items[i].getAttribute('value').toLowerCase()))
        continue;
      else
        cntShown++;

      var val = items[i].getAttribute('value');
      var hidVal = hiddenValTemplate.replace('[[[value]]]', items[i].getAttribute('hiddenValue'));
      var optVal = ' ' + items[i].getAttribute('additionalValue');
      if (optVal == ' []')
        optVal = '';
      var item = document.createElement('div');
      item.style.whiteSpace = 'nowrap';
      item.setAttribute('title', val + optVal);
      item.setAttribute('xml:value', val);
      item.setAttribute('xml:hiddenValue', hidVal);
      item.appendChild(document.createTextNode(val + optVal));

      item.onselectstart = function() {return false;};
      item.onmousedown = function() {mousepressed = true;};
      item.onmouseup = select;
      item.onmouseover = mark;

      div.appendChild(item);
    }
    if (cntShown > 1)
    {
      div.style.display = 'block';
      div.scrollTop = (div.lastChild.offsetTop + div.lastChild.offsetHeight - (div.offsetHeight - 3));
      div.scrollTop = 0;
      divPos();
      emptyAlert.style.display = 'none';
    } else if ((lastKey != 8) && (lastKey != 46))
    {
      if (cntShown == 1)
        select(null, div.firstChild);
      else
      {
        div.style.display = 'none';
        emptyAlert.style.display = 'block';
      }
    } else if(cntShown == 1)
    {
      if (emptyAlert.style.display == 'block')
        select(null, div.firstChild);
      emptyAlert.style.display = 'none';
    }
  }

  function select(event, el)
  {
    if (typeof(event) == 'undefined')
      event = window.event;
    if ((event == null) && (el == null))
      return;
    if (el == null)
      el = event.target ? event.target : event.srcElement;
    inputField.value  = el.getAttribute('xml:value');
    hiddenField.value = el.getAttribute('xml:hiddenValue');
    curValue = hiddenField.value;
    mousepressed = false;
    reset();
  }

  function reset()
  {
    itemsXml = null;
    markedItem = null;
    lastKey = null;
    emptyAlert.style.display = 'none';
    div.style.display = 'none';
  }

  function clearElement(el)
  {
    if (!el.hasChildNodes())
      return;
    var len = el.childNodes.length;
    for (var i=0; i<len; i++)
    {
      el.removeChild(el.childNodes[0]);
    }
    return;
  }
}