////////////
// jQuery //
////////////

jQuery.ajaxSetup({dataFilter:function(data,type)
{
  if(type=="json" && typeof data=="string" && data.length>=4 && data.substring(0,2)=="/*" && data.substring(data.length-2)=="*/")
    return data.substring(2,data.length-2);
  return data;
}});

$(function()
{
  // Focus first input element
  $(":input[type!='hidden']:first").focus();
  // Handle enter
  $("body").keydown(function(event)
  {
    if(event.keyCode==13)
      return submitForm.call(event.target);
  });
  // Hover for span, div and tr links
  $("span.link, div.link, tr.link").hover(
    function()
    {
      var span=$(this);
      var classnames=span.attr("class").split(/\s+/);
      for(var i=0;i<classnames.length;i++)
        if(classnames[i].search(/-hover$/)==-1)
          span.addClass(classnames[i]+"-hover");
    },
    function()
    {
      var span=$(this);
      var classnames=span.attr("class").split(/\s+/);
      for(var i=0;i<classnames.length;i++)
        if(classnames[i].search(/-hover$/)!=-1)
          span.removeClass(classnames[i]);
    }
  );
  if(window["addEvent"]===undefined)
    setTableClasses();
});

function getChildElements(parent)
{
  var i,j,childs,elements,count;

  childs=parent.childNodes;
  count=0;
  for(i=0;i<childs.length;i++)
    if(childs[i].nodeType==1)
      count++;
  elements=new Array(count);
  j=0;
  for(i=0;i<childs.length;i++)
    if(childs[i].nodeType==1)
      elements[j++]=childs[i];
  return elements;
}

function setTableClasses() {
  var t,r,c,e2,e2,a;
  t=document.getElementsByTagName("table");
  for (var i=0; i<t.length; i++) {
    r=t[i].getElementsByTagName("tr");
    for (var j=0; j<r.length; j++) {
      if (j % 2 == 0)
        r[j].className = "oddRow " + r[j].className;
      else
        r[j].className = "evenRow " + r[j].className;
      c=getChildElements(r[j]);
      c[0].className = "firstCell " + c[0].className;
      c[c.length-1].className = "lastCell " + c[c.length-1].className;

      if(t[i].className == "zebra")
      {
        e1 = document.createElement(c[0].tagName);
        a = document.createAttribute("class");
        a.nodeValue = "beforeFirst";
        e1.setAttributeNode(a);
        if (j % 2 == 0)
        {
          e2 = document.createElement("img");
          a = document.createAttribute("src");
          a.nodeValue = "/media/background/oddrow_topleft.png";
          e2.setAttributeNode(a);
          e1.appendChild(e2);
        }
        r[j].insertBefore(e1,c[0]);
  
        e1 = document.createElement(c[0].tagName);
        a = document.createAttribute("class");
        a.nodeValue = "afterLast";
        e1.setAttributeNode(a);
        if (j % 2 == 0)
        {
          e2 = document.createElement("img");
          a = document.createAttribute("src");
          a.nodeValue = "/media/background/oddrow_topright.png";
          e2.setAttributeNode(a);
          e1.appendChild(e2);
        }
        r[j].appendChild(e1);
      }
    }
  }
}

/**
 * Opens an URL.
 *
 * @param url
 *   String, the URL.
 * @param target
 *   String, the target (can be null).
 */
function openURL(url,target)
{
  window.open(url,target==null || target==""?"_self":target);
  return false;
}

/**
 * Submit a form.
 *
 * @param this
 *   HTMLElement, an element in the form.
 * @param processback
 *   Optional, Number, the processback value.
 */
function submitForm(processback)
{
  if($("#journaldata").length>0)
    submitJournalForm.call(this,processback===-1);
  else
  {
    var form=$(this).parents("form");
    $("input[name='processback']",form).val(processback===undefined?"":processback);
    form.submit();
  }
  return false;
}

/**
 * Does a POST request.
 *
 * @param url
 *   String, the URL. 
 * @param data
 *   String/Object, the data to post.
 * @param process
 *   Function(response), function to process the response.  
 */
function postRequest(url,data,process)
{
  $.ajax(
  {
    url: url,
    type: "POST",
    dataType: "html",
    data: typeof data=="string"?data:$.param(data),
    complete: function(res, status)
    {
      if(status=="error" && res.status==401)
        // Session timeout => reload complete page to show login
        window.location.reload();
      else if(status!="success")
        alert("Es ist ein Fehler aufgetreten. Bitte probieren Sie es sp\u00E4ter nochmal.");
    },
    success: function(data, status)
    {
      process(data);
    }
  });
}

/**
 * Remove whitespace (\r, \n, \t, space) from the beginning and end.
 *
 * @param text
 *   String, the string to trim.
 * @return
 *   String, The trimmed string.  
 */
function trimWhitespace(text)
{
  return text.replace(/^[\t\n\r ]+|[\t\n\r ]+$/g,"");
}

/**
 * Remove whitespace (\r, \n, \t, space) from the beginning and end and
 * replace whitespace in the middle by a single space.
 *
 * @param text
 *   String, the string to normalize.
 * @return
 *   String, The trimmed string.  
 */
function normalizeWhitespace(text)
{
  return trimWhitespace(text).replace(/[\t\n\r ]+/g," ");
}

/**
 * Does a binary search.
 *
 * @param elements
 *   Array, a sorted array. 
 * @param value
 *   Object, the value to find.
 * @param startindex
 *   Optional, Number, the start index (incl.) in elements (0 if undefined).
 * @param endindex
 *   Optional, Number, the end index (excl.) in elements (elements.length if undefined).
 * @param comparefn
 *   Optional, Function(a,b), function to compare two elements, s. Array.sort (the values are sorted as strings if undefined).
 * @return
 *   The first index of the value in elements; if value is not in elements ~index where to insert value.  
 */
function searchBinary(elements,value,startindex,endindex,comparefn)
{
  // Set default parameter values
  if(startindex===undefined)
    startindex=0;
  if(endindex===undefined)
    endindex=elements.length;
  if(comparefn===undefined)
  {
    comparefn=function(a,b)
    {
      a=String(a);
      b=String(b);
      if(a<b)
        return -1;
      else if(a==b)
        return 0;
      else
        return 1;
    };
  }
  // Range empty?
  if(startindex>=endindex)
    return ~startindex;
  // Binary search
  while(startindex<endindex-1)
  {
    var index=(startindex+endindex)>>>1;
    var result=comparefn(value,elements[index-1]);
    if(result<=0)
      endindex=index;
    else
      startindex=index;
  }
  // Compare value with best element
  var result=comparefn(value,elements[startindex]);
  if(result==0)
    return startindex;
  else if(result<0)
    return ~startindex;
  else
    return ~endindex;
}

///////////////
// FoodInput //
///////////////

// Indices of foods deleted in foodnames1,foodkeys1 (??? not used)
var fooddel2=[];
// Appendix for foodnames1
var foodnames2=[];
// Appendix for foodkeys1
var foodkeys2=[];
// Appendix for foodquantitygroups1
var foodquantitygroups2=[];
// Appendix for foodkeywordnames1
var foodkeywordnames2=[];
// Appendix for foodkeywordkeys1
var foodkeywordkeys2=[];
// Values to add/overwrite (null to delete) in foodkeywordfoods1
var foodkeywordfoods2={}; 
// Values to add/overwrite (null to delete) in foodkeywordindices1
var foodkeywordindices2={};

/**
 * Returns the keywords contained in the text.
 *
 * @param text
 *   String, the text. 
 * @return
 *   Array of Number, the indices of the keywords.  
 */
function getKeywordsA(text)
{
  text=text.toLowerCase().replace(/-/g," ");
  var lastwhitespace=/[\t\n\r ]$/.test(text);
  var words=normalizeWhitespace(text).split(" ");
  var keywords={};
  var autoextensionlength="";
  var autoextension="";
  for(wordindex=0;wordindex<words.length;wordindex++)
  {
    var word=words[wordindex];
    for(var index=0;index<=word.length-2;index++)
    {
      if(index==word.length-2)
      {
        // Special handling for ei
        if(word.substring(index)=="ei" && (index==0 || (index>=4 && word.substring(index-2)!="frei" && word.substring(index-2)!="brei" && word.substring(index-4)!="salbei")))
        {
          var keywordindex=searchBinary(foodkeywordnames1,"ei");
          if(keywordindex>=0)
          {
            var key=foodkeywordkeys1[keywordindex];
            keywords[key]=key;
          }
        }
      }
      else
      {
        // foodkeywordnames1
        var prefix=word.substring(index,index+3);
        var startindex=undefined;
        var endindex=undefined;
        for(;;)
        {
          // Get range
          var newstartindex=searchBinary(foodkeywordnames1,prefix,startindex,endindex);
          if(newstartindex>=0)
          {
            var key=foodkeywordkeys1[newstartindex];
            keywords[key]=key;
          }
          else
            newstartindex=~newstartindex;
          var newendindex=~searchBinary(foodkeywordnames1,prefix+"\uffff",startindex,endindex);
          // Empty range?
          if(newstartindex>=newendindex)
            break;
          // End of word?
          if(index+prefix.length==word.length)
          {
            // Last word?
            if(wordindex==words.length-1 && !lastwhitespace)
            {
              // Use all in range
              for(var keyindex=newstartindex;keyindex<newendindex;keyindex++)
              {
                var key=foodkeywordkeys1[keyindex];
                keywords[key]=key;
              }
              // Auto extension?
              if(autoextensionlength==0)
              {
                autoextensionlength=prefix.length;
                autoextension=foodkeywordnames1[newstartindex];
              }
            }
            break;
          }
          // Prolong prefix
          prefix=word.substring(index,index+prefix.length+1);
          startindex=newstartindex;
          endindex=newendindex;
        }
        // foodkeywordnames2
        var prefix=word.substring(index,index+3);
        var startindex=undefined;
        var endindex=undefined;
        for(;;)
        {
          // Get range
          var newstartindex=searchBinary(foodkeywordnames2,prefix,startindex,endindex);
          if(newstartindex>=0)
          {
            var key=foodkeywordkeys2[newstartindex];
            keywords[key]=key;
          }
          else
            newstartindex=~newstartindex;
          var newendindex=~searchBinary(foodkeywordnames2,prefix+"\uffff",startindex,endindex);
          // Empty range?
          if(newstartindex>=newendindex)
            break;
          // End of word?
          if(index+prefix.length==word.length)
          {
            // Last word?
            if(wordindex==words.length-1 && !lastwhitespace)
            {
              // Use all in range
              for(var keyindex=newstartindex;keyindex<newendindex;keyindex++)
              {
                var key=foodkeywordkeys2[keyindex];
                keywords[key]=key;
              }
              if(autoextensionlength<prefix.length || (autoextensionlength==prefix.length && foodkeywordnames2[newstartindex]<autoextension))
              {
                autoextensionlength=prefix.length;
                autoextension=foodkeywordnames2[newstartindex];
              }
            }
            break;
          }
          // Prolong prefix
          prefix=word.substring(index,index+prefix.length+1);
          startindex=newstartindex;
          endindex=newendindex;
        }
      }
    }
  }
  return ({
    keywords: keywords,
    autoextension: autoextension.substring(autoextensionlength)
  });
}

/**
 * Splits a word into keywords.
 *
 * @param word
 *   String, the word.
 * @param last
 *   Boolean, is this the last entered word?
 * @param keywords
 *   Object, the keywords are added to this object ([key]: key).
 * @return
 *   Boolean, was the word completely split into keywords? 
 */
function splitWord(word,last,keywords)
{
  var result=(word.length==0 || (word.length<=2 && last));
  // foodkeywordnames1
  var startindex=0;
  var endindex=foodkeywordnames1.length;
  var minlength=(word.substring(0,2)=="ei"?2:3);
  for(var index=minlength;index<=word.length;index++)
  {
    var keyword=word.substring(0,index);
    // Get range
    var newstartindex=searchBinary(foodkeywordnames1,keyword,startindex,endindex);
    if(newstartindex>=0)
    {
      var key=foodkeywordkeys1[newstartindex];
      keywords[key]=key;
      result|=splitWord(word.substring(index),last,keywords);
      newstartindex++;
    }
    else
      newstartindex=~newstartindex;
    var newendindex=~searchBinary(foodkeywordnames1,keyword+"\uffff",startindex,endindex);
    // Empty range?
    if(newstartindex>=newendindex)
      break;
    startindex=newstartindex;
    endindex=newendindex;
    if(index==word.length && last)
    {
      // Use all in range
      for(var keyindex=newstartindex;keyindex<newendindex;keyindex++)
      {
        var key=foodkeywordkeys1[keyindex];
        keywords[key]=key;
      }
      result=true;
    }
  }
  // foodkeywordnames2
  var startindex=0;
  var endindex=foodkeywordnames2.length;
  for(var index=minlength;index<=word.length;index++)
  {
    var keyword=word.substring(0,index);
    // Get range
    var newstartindex=searchBinary(foodkeywordnames2,keyword,startindex,endindex);
    if(newstartindex>=0)
    {
      var key=foodkeywordkeys2[newstartindex];
      keywords[key]=key;
      result|=splitWord(word.substring(index),last,keywords);
      newstartindex++;
    }
    else
      newstartindex=~newstartindex;
    var newendindex=~searchBinary(foodkeywordnames2,keyword+"\uffff",startindex,endindex);
    // Empty range?
    if(newstartindex>=newendindex)
      break;
    startindex=newstartindex;
    endindex=newendindex;
    if(index==word.length && last)
    {
      // Use all in range
      for(var keyindex=newstartindex;keyindex<newendindex;keyindex++)
      {
        var key=foodkeywordkeys2[keyindex];
        keywords[key]=key;
      }
      result=true;
    }
  }
  return result;
}

/**
 * Splits a word into keywords from right to left.
 *
 * @param word
 *   String, the word.
 * @param keywords
 *   Object, the keywords are added to this object ([key]: key).
 */
function splitWordBackwards(word,keywords)
{
  // foodkeywordnames1
  for(var index=word.length-3;index>=0;index--)
  {
    var keyword=word.substring(index);
    // Get range
    var keyindex=searchBinary(foodkeywordnames1,keyword);
    if(keyindex>=0)
    {
      var key=foodkeywordkeys1[keyindex];
      keywords[key]=key;
      splitWordBackwards(word.substring(0,index),keywords);
    }
  }
  // foodkeywordnames2
  for(var index=word.length-3;index>=0;index--)
  {
    var keyword=word.substring(index);
    // Get range
    var keyindex=searchBinary(foodkeywordnames2,keyword);
    if(keyindex>=0)
    {
      var key=foodkeywordkeys2[keyindex];
      keywords[key]=key;
      splitWordBackwards(word.substring(0,index),keywords);
    }
  }
}

/**
 * Returns the keywords contained in the text.
 *
 * @param text
 *   String, the text. 
 * @return
 *   Array of Number, the indices of the keywords.  
 */
function getKeywords(text)
{
  text=text.toLowerCase().replace(/-/g," ");
  var lastwhitespace=/[\t\n\r ]$/.test(text);
  var words=normalizeWhitespace(text).split(" ");
  var keywords={};
  var autoextensionlength="";
  var autoextension="";
  for(wordindex=0;wordindex<words.length;wordindex++)
  {
    var word=words[wordindex];
    if(!splitWord(word,wordindex==words.length-1 && !lastwhitespace,keywords))
      splitWordBackwards(word,keywords);
  }
  return ({
    keywords: keywords,
    autoextension: autoextension.substring(autoextensionlength)
  });
}

/**
 * Highlights the keywords contained in the name.
 *
 * @param name
 *   String, the food name. 
 * @param keywords
 *   {keywordkey: {...}} 
 * @return
 *   String, the HTML.  
 */
function highlightKeywords(name,keywords)
{
  var words=name.split("$");
  var result="";
  for(wordindex=0;wordindex<words.length;wordindex++)
  {
    var word=words[wordindex];
    var highlight=false;
    // foodkeywordnames1
    var keywordindex=searchBinary(foodkeywordnames1,word);
    if(keywordindex>=0 && keywords[foodkeywordkeys1[keywordindex]]!==undefined)
      highlight=true;
    else
    {
      // foodkeywordnames2
      var keywordindex=searchBinary(foodkeywordnames2,word);
      if(keywordindex>=0 && keywords[foodkeywordkeys2[keywordindex]]!==undefined)
        highlight=true;
    }
    if(highlight)
      result+="<b>"+word+"</b>";
    else
      result+=word;
  }
  return result;
}

/**
 * Compares food names ignoring $ characters.
 *
 * @param a
 *   String, the first food name (can contain $).
 * @param b
 *   String, the second food name (can contain $).
 */
function foodNameComparator(a,b)
{
  a=a.replace(/\$/g,"");
  b=b.replace(/\$/g,"");
  if(a<b)
    return -1;
  else if(a==b)
    return 0;
  else
    return 1;
};

/**
 * Returns the foods starting with text.
 *
 * @param text
 *   String, the text.
 * @return
 *   Array of Number, the indices of the keywords.  
 */
function getFoods(text)
{
  var foods=[];
  var autoextension="";
  text=text.toLowerCase().replace(/-/g,"");
  var lastwhitespace=/[\t\n\r ]$/.test(text);
  text=normalizeWhitespace(text);
  if(lastwhitespace)
    text+=" ";
  if(text.length>=3 || text=="ei")
  {
    // foodnames1
    var startindex=searchBinary(foodnames1,text,0,foodnames1.length,foodNameComparator);
    if(startindex<0)
      startindex=~startindex;
    var endindex=~searchBinary(foodnames1,text+"\uffff",0,foodnames1.length,foodNameComparator);
    for(var index=startindex;index<endindex;index++)
      foods.push(index);
    // Set auto extension
    if(startindex<endindex)
    {
      var name=foodnames1[startindex].replace(/ \$/g," ");
      for(;;)
      {
        var pos=name.indexOf("$");
        if(pos==-1)
          pos=name.length;
        if(pos>text.length)
        {
          autoextension=name.substring(text.length,pos);
          break;
        }
        if(pos==name.length)
          break;
        name=name.replace("$","");
      }
    }
    // foodnames2
    var startindex=searchBinary(foodnames2,text,0,foodnames2.length,foodNameComparator);
    if(startindex<0)
      startindex=~startindex;
    var endindex=~searchBinary(foodnames2,text+"\uffff",0,foodnames2.length,foodNameComparator);
    for(var index=startindex;index<endindex;index++)
      foods.push(foodnames1.length+index);
    // Set auto extension
    if(startindex<endindex)
    {
      var name=foodnames2[startindex].replace(/ \$/g," ");
      for(;;)
      {
        var pos=name.indexOf("$");
        if(pos==-1)
          pos=name.length;
        if(pos>text.length)
        {
          if(autoextension>name.substring(text.length,pos))
            autoextension=name.substring(text.length,pos);
          break;
        }
        if(pos==name.length)
          break;
        name=name.replace("$","");
      }
    }
  }
  if(foods.length==0)
    return undefined;
  return ({
    foods: foods,
    prefixlength: text.length,
    autoextension: autoextension
  });
}
  
/**
 * Highlights the prefix of the name.
 *
 * @param name
 *   String, the food name.
 * @param prefixlength
 *   Number, the length of the prefix.
 * @return
 *   String, the HTML.
 */
function highlightPrefix(name,prefixlength)
{
  name=name.replace(/\$/g,"");
  return "<b>"+name.substring(0,prefixlength)+"</b>"+name.substring(prefixlength);
}

/**
 * Returns an index for a text.
 *
 * @param text
 *   String, the text.
 * @return
 *   Array of Number, the indices of the keywords.  
 */
function getIndex(text)
{
  text=text.toLowerCase().replace(/-/g,"");
  if(!/[\t\n\r ]$/.test(text))
  {
    text=normalizeWhitespace(text);
    if(text.length>=3)
    {
      var keyword=undefined;
      var keywordkey=undefined;
      // foodkeywordnames1
      var startindex=searchBinary(foodkeywordnames1,text);
      if(startindex<0)
        startindex=~startindex;
      var endindex=~searchBinary(foodkeywordnames1,text+"\uffff");
      if(startindex<endindex)
      {
        var key=foodkeywordkeys1[startindex];
        if((foodkeywordindices2[key]!==undefined && foodkeywordindices2[key]!==null) ||
           (foodkeywordindices2[key]===undefined && foodkeywordindices1[key]!==undefined))
        {
            keywordkey=key;
            keyword=foodkeywordnames1[startindex];
        }
      }
      // foodkeywordnames2
      var startindex=searchBinary(foodkeywordnames2,text);
      if(startindex<0)
        startindex=~startindex;
      var endindex=~searchBinary(foodkeywordnames2,text+"\uffff");
      if(startindex<endindex)
      {
        var key=foodkeywordkeys2[startindex];
        if(((foodkeywordindices2[key]!==undefined && foodkeywordindices2[key]!==null) ||
            (foodkeywordindices2[key]===undefined && foodkeywordindices1[key]!==undefined)) &&
           (keyword===undefined || foodkeywordnames2[startindex]<keyword))
        {
            keywordkey=key;
            keyword=foodkeywordnames2[startindex];
        }
      }
      if(keyword!==undefined)
      {
        if(foodkeywordindices2[keywordkey]!==undefined && foodkeywordindices2[keywordkey]!==null)
          var foods=foodkeywordindices2[keywordkey];
        else
          var foods=foodkeywordindices1[keywordkey];
        return ({
          key: keywordkey,
          word: keyword,
          foods: foods,
          autoextension: keyword.substring(text.length)
        });
      }
    }
  }
  return undefined;
}

/**
 * Highlights the index.
 *
 * @param name
 *   String, the food name.
 * @param index
 *   {key, word, foods, autoextension}, the index data.
 * @return
 *   String, the HTML.
 */
function highlightIndex(name,index)
{
  name=name.replace(/\$/g,"");
  return "<b>"+index.word+": </b>"+name;
}

/**
 * Updates the food input.
 */
function updateFoodInput(event)
{
  var foodinput=$(this).closest("div.foodinput");
  if(event.type=="keydown" && (event.keyCode==38 || event.keyCode==40))
  {
    var foodnameinput=$("input[name='foodname']",foodinput);
    var table=$("table",foodinput);
    if($("td",table).length>0)
    {
      var oldtd=$("td.selected",table);
      if(event.keyCode==38)
        var newtd=$("td",oldtd.parent().prev());
      else
        var newtd=$("td",oldtd.parent().next());
      if(newtd.length==0)
      {
        if(event.keyCode==38)
          var newtd=$("td:last",table);
        else
          var newtd=$("td:first",table);
      }
      var foodindex=Number(/^(|.* )foodindex-([0-9]+)(| .*)$/.exec(newtd.attr("class"))[2]);
      if(foodindex<foodnames1.length)
        var foodname=foodnames1[foodindex];
      else
        var foodname=foodnames2[foodindex-foodnames1.length];
      foodnameinput.val(foodname.replace(/\$/g,""));
      oldtd.removeClass("selected");
      newtd.addClass("selected");
      updateFoodKeyUnit(foodinput);
    }
  }
  else if(!(event.type=="keypress" && (event.keyCode==38 || event.keyCode==40)))
  {
    // Delay until input value is changed
    window.setTimeout(function()
    {
      updateFoodInputIntern(foodinput);
      updateFoodKeyUnit(foodinput);
    },1);
  }
  return true;
}

/**
 * Opens the food input.
 */
function openFoodInput(event)
{
  var foodinput=$(this).closest("div.foodinput");
  updateFoodInputIntern(foodinput);
  var foodnameinput=$("input[name='foodname']",foodinput);
  var table=$("table",foodinput);
  table.css("top",foodnameinput.outerHeight()+"px");
  table.css("visibility","visible");
  return true;
}

/**
 * Closes the food input.
 */
function closeFoodInput(event)
{
  var foodinput=$(this).closest("div.foodinput");
  var table=$("table",foodinput);
  table.css("visibility","");
  return true;
}

/**
 * Click on a food input cell.
 */
function clickFoodInputCell(event)
{
  var foodinput=$(this).closest("div.foodinput");
  var foodnameinput=$("input[name='foodname']",foodinput);
  var table=$("table",foodinput);
  var td=$(this);
  var foodindex=Number(/^(|.* )foodindex-([0-9]+)(| .*)$/.exec(td.attr("class"))[2]);
  if(foodindex<foodnames1.length)
    var foodname=foodnames1[foodindex];
  else
    var foodname=foodnames2[foodindex-foodnames1.length];
  foodnameinput.val(foodname.replace(/\$/g,""));
  updateFoodKeyUnit(foodinput);
  var form=foodinput.closest("form");
  var foodquantityinput=$("input[name='quantity']",form);
  foodquantityinput.focus();
  return false;
}

function updateFoodInputIntern(foodinput)
{
  var foodnameinput=$("input[name='foodname']",foodinput);
  var text=foodnameinput.val();
  var count=0;
  var countmax=20;
  var autoextension=undefined;
  var allfoodindices={};
  // Get index foods
  var index=getIndex(text);
  var foodindexarray=[];
  if(index!==undefined)
  {
    autoextension=index.autoextension;
    for(var foodsindex=0;foodsindex<index.foods.length;foodsindex++)
    {
      var foodindex=index.foods[foodsindex];
      if(allfoodindices[foodindex]===undefined)
      {
        allfoodindices[foodindex]=true;
        count++;
        if(foodindex<foodnames1.length)
          var foodname=foodnames1[foodindex];
        else
          var foodname=foodnames2[foodindex-foodnames1.length];
        foodname=foodname.replace(/\$/g,"");
        foodindexarray.push({
          index: foodindex,
          name: foodname
        });
      }
    }
    // Sort foods
    foodindexarray.sort(function(food1,food2)
    {
      return food1.name.localeCompare(food2.name);
    });
  }
  // Get prefix foods
  var foodprefixarray=[];
  if(count<countmax)
  {
    var foods=getFoods(text);
    if(foods!==undefined)
    {
      if(autoextension===undefined)
        autoextension=foods.autoextension;
      for(var foodsindex=0;foodsindex<foods.foods.length;foodsindex++)
      {
        var foodindex=foods.foods[foodsindex];
        if(allfoodindices[foodindex]===undefined)
        {
          allfoodindices[foodindex]=true;
          count++;
          if(foodindex<foodnames1.length)
            var foodname=foodnames1[foodindex];
          else
            var foodname=foodnames2[foodindex-foodnames1.length];
          foodname=foodname.replace(/\$/g,"");
          foodprefixarray.push({
            index: foodindex,
            name: foodname
          });
        }
      }
    }
    // Sort foods
    foodprefixarray.sort(function(food1,food2)
    {
      return food1.name.localeCompare(food2.name);
    });
  }
  // Get keyword foods
  var foodkeywordarray=[];
  if(count<countmax)
  {
    var keywords=getKeywords(text);
    // Sort by keyword count
    if(autoextension===undefined)
      autoextension=keywords.autoextension;
    // Collect foods
    var foodkeywordmap={};
    for(var key in keywords.keywords)
    {
      var keyfoods=foodkeywordfoods2[key];
      if(keyfoods===undefined)
        var keyfoods=foodkeywordfoods1[key];
      if(keyfoods!==undefined && keyfoods!==null)
      {
        for(var keyfoodindex=0;keyfoodindex<keyfoods.length;keyfoodindex++)
        {
          var foodindex=keyfoods[keyfoodindex];
          var food=foodkeywordmap[foodindex];
          if(food===undefined)
          {
            if(allfoodindices[foodindex]===undefined)
            {
              allfoodindices[foodindex]=true;
              if(foodindex<foodnames1.length)
                var foodname=foodnames1[foodindex];
              else
                var foodname=foodnames2[foodindex-foodnames1.length];
              var length1=foodname.length;
              foodname=foodname.replace(/\$ \$/g," ");
              var length2=foodname.length;
              foodname=foodname.replace(/\$/g,"");
              var length3=foodname.length;
              food={
                index: foodindex,
                name: foodname,
                words: 1+(length1-length2)/2+(length2-length3),
                keywords: 1
              };
              foodkeywordmap[foodindex]=food;
              foodkeywordarray.push(food);
            }
          }
          else
            food.keywords++;
        }
      }
    }
    // Sort foods
    foodkeywordarray.sort(function(food1,food2)
    {
      if(food1.keywords!=food2.keywords)
        return food2.keywords-food1.keywords;
      if(food1.words!=food2.words)
        return food1.words-food2.words;
      return food1.name.localeCompare(food2.name);
    });
  }
  // Create table rows
  rows="";
  count=0;
  for(var arrayindex=0;arrayindex<foodindexarray.length && count<countmax;arrayindex++)
  {
    var food=foodindexarray[arrayindex];
    if(food.index<foodnames1.length)
    {
      var foodname=foodnames1[food.index];
      var foodkey=foodkeys1[food.index];
    }
    else
    {
      var foodname=foodnames2[food.index-foodnames1.length];
      var foodkey=foodkeys2[food.index-foodnamess1.length];
    }
    rows=rows+'<tr><td class="foodindex-'+food.index+'" onmousedown="return clickFoodInputCell.call(this,event)">'+highlightIndex(foodname,index)+'</td></tr>';
    count++;
  }
  separator="";
  if(count>0)
    separator=" separator";
  for(var arrayindex=0;arrayindex<foodprefixarray.length && count<countmax;arrayindex++)
  {
    var food=foodprefixarray[arrayindex];
    if(food.index<foodnames1.length)
      var foodname=foodnames1[food.index];
    else
      var foodname=foodnames2[food.index-foodnames1.length];
    rows=rows+'<tr><td class="foodindex-'+food.index+separator+'" onmousedown="return clickFoodInputCell.call(this,event)">'+highlightPrefix(foodname,foods.prefixlength)+'</td></tr>';
    separator="";
    count++;
  }
  if(count>0)
    separator=" separator";
  for(var arrayindex=0;arrayindex<foodkeywordarray.length && count<countmax;arrayindex++)
  {
    var food=foodkeywordarray[arrayindex];
    if(food.index<foodnames1.length)
      var foodname=foodnames1[food.index];
    else
      var foodname=foodnames2[food.index-foodnames1.length];
    rows=rows+'<tr><td class="foodindex-'+food.index+separator+'" onmousedown="return clickFoodInputCell.call(this,event)">'+highlightKeywords(foodname,keywords.keywords)+'</td></tr>';
    separator="";
    count++;
  }
  // Update table
  var table=$("table",foodinput);
  $("> tbody",table).replaceWith("<tbody>"+rows+"</tbody>");
  if(count>0)
    table.css("border","1px solid #bbbbbb");
  else
    table.css("border","none");
}

/**
 * Updates the units.
 *
 * @param foodinput
 *   jQuery, the foodinput element.
 */
function updateFoodKeyUnit(foodinput)
{
  var foodquantitygroup;

  var foodname=$("input[name='foodname']",foodinput).val();
  var foodkeyinput=$("input[name='foodkey']",foodinput);
  var foodindex=searchBinary(foodnames1,foodname,0,foodnames1.length,foodNameComparator);
  if(foodindex>=0)
  {
    foodquantitygroup=foodquantitygroups1[foodindex];
    foodkeyinput.val(foodkeys1[foodindex]);
  }
  else
  {
    foodindex=searchBinary(foodnames2,foodname,0,foodnames2.length,foodNameComparator);
    if(foodindex>=0)
    {
      foodquantitygroup=foodquantitygroups2[foodindex];
      foodkeyinput.val(foodkeys2[foodindex]);
    }
    else
    {
      foodquantitygroup=0;
      foodkeyinput.val("");
    }
  }
  var form=foodinput.closest("form");
  var foodunitinput=$("select[name='unit']",form);
  var oldunitkey=foodunitinput.val();
  if(foodquantitygroup==0)
    var options='<option value=""></option>';
  else
  {
    var options="";
    var units=foodunits1[foodquantitygroup];
    for(var unitindex=0;unitindex<units.length;unitindex++)
    {
      var unit=units[unitindex];
      options+='<option value="'+unit.key+'"'+(oldunitkey==unit.key?' selected="selected"':'')+'>'+unit.name+'</option>';
    }
  }
  foodunitinput.empty();
  foodunitinput.append(options);
}

/**
 * Refreshes the journal.
 *
 * @param this
 *   HTMLElement, an element.
 * @param event
 *   Event, the event.
 * @param url
 *   The URL of the function.
 * @param key
 *   The key of the row.
 */
function updateJournal(event,url,key)
{
  var journaldata=$("#journaldata");
  postRequest(url,key===undefined?{}:{key: key},function(response)
  {
    journaldata.replaceWith(response);
    setTableClasses();
    var foodinput=$("#journaldata div.foodinput");
    if(foodinput.length>0)
      updateFoodKeyUnit(foodinput);
  });
  return false;
}

/**
 * Submits a journal form.
 *
 * @param this
 *   HTMLElement, an element in the form.
 * @param cancel
 *   Optional, cancel instead of ok?
 * @param cancel
 *   Optional, Boolean, cancel instead of ok?
 * @param extraparameters
 *   Optional, Object, further parameters.
 */
function submitJournalForm(cancel,extraparameters)
{
  var form=$(this).closest("form");
  var parameters=$("input, select, textarea",form).serialize();
  if(cancel===true)
    parameters=parameters+"&"+$.param({processback: -1});
  if(extraparameters!==undefined)
    parameters=parameters+"&"+$.param(extraparameters);
  var journaldata=$("#journaldata");
  postRequest(form.attr("action"),parameters,function(response)
  {
    journaldata.replaceWith(response);
    setTableClasses();
    var foodinput=$("#journaldata div.foodinput");
    if(foodinput.length>0)
      updateFoodKeyUnit(foodinput);
  });
  return false;
}
