/**
 * This file is part of What Language Is This?
 * found at http://whatlanguageisthis.com/
 * and the Language Analyzer found at
 * http://henrikfalck.com/languageanalyzer/
 *
 * Copyright 2006-2010 Karl Henrik Falck <f@lck.nu>
 *
 * Feel free to browse through the code, but copying it for
 * use elsewhere is forbidden by law. Thank you.
 */

if (RegExp)
  {
    String.prototype.count = function(str)
      {
	var m = this.match(new RegExp(str, "gim"));
	return m ? m.length : 0;
      };
  }
else
  {
    String.prototype.count = function(str)
      {
	return this.split(str).length - 1;
      };
  }
String.prototype.countArray = function(ary)
{
  var sum = 0;
  for (i = 0; i < ary.length; ++i)
    sum += this.count(ary[i]);
  return sum;
};

var debug = false;

var langgroups = {};
var langchars = {};
var langstrings = {};
var additionalstrings = {};
var langs = [];
var babel = {};
var dataLoaded = false;
var waitingForDataLoad = false;
var analyzedText = null;
var examplelanguage = null;

var groups = [["id", "ms"], ["et", "fi"], ["fr", "es", "ca", "pt", "it", "ro"], ["nl", "de", "da", "sv", "no"], ["ja", "zh", "zh-yue"], ["ko"], ["fa", "ar", "ur"], ["bg", "ru", "uk", "sr", "mk", "be"], ["sk", "cs", "pl", "sl", "hr", "bs", "sr-latin"], ["eu"], ["bn", "hi", "bpy"], ["te", "ta", "ml", "kn"]];

function get_string(id)
{
  var r = strings[id];
  return r != undefined ? r : id;
}

function include(filename)
{
  var head = document.getElementsByTagName("head").item(0);
  var elm = document.createElement("script");
  elm.setAttribute("type", "text/javascript");
  elm.setAttribute("charset", "utf-8");
  elm.setAttribute("src", filename);
  head.appendChild(elm);
}

function initialize()
{
  $("input").observe("change", oninputchanged);
  $("input").observe("keyup", oninputchanged);
  $("input").observe("cut", oninputchanged);
  $("input").observe("paste", oninputchanged);
  $("input").observe("focus", oninputchanged);
  $("input").observe("click", oninputchanged);
  //  setInterval(oninputchanged, 1000);
  for (var i = 0; i < groups.length; i++)
    {
      var g = groups[i];
      for (var j = 0; j < g.length; j++)
	{
	  var l = g[j];
	  langgroups[l] = g;
	}
    }
  onresize();
  include(dataFile);
}

function dataLoadingFinished()
{
  dataLoaded = true;
  if (waitingForDataLoad)
    analyze();
}

function onresize()
{
  if ($("rightcolumn"))
    {
      var pos = Position.cumulativeOffset($("input"));
      var y = 0;
      var x = pos[0] + $("input").getWidth() + 20;
      $("rightcolumn").setStyle(
				{top: "" + y + "px", left: "" + x + "px",
					visibility: "visible"
					});
    }

  if ($("ranking") && $("beta"))
    {
      var pos = Position.cumulativeOffset($("beta"));
      var y = pos ? pos[1] + $("beta").getHeight() + 20 : 100;
      $("ranking").setStyle(
			    {top: "" + y + "px",
				    visibility: "visible"
				    });
    }
}

function getlangname(lang)
{
  var n = strings[lang];
  return n != undefined ? n : lang;
}

function getchars()
{
  var text = analyzedText;
  var len = text.length;

  var chars = {};
  var numchars = 0;
  var hanchars = 0;
  for (var i = 0; i < len; i++)
    {
      var c = text.charAt(i);
      if (c.strip().length == 0)
	continue;
      if (chars[c])
	chars[c] += 1;
      else
	{
	  chars[c] = 1;
	  numchars++;
	  var code = c.charCodeAt(0);
	  if (code >= 0x3000 && ((code >= 0x4e00 && code <= 0x9fff) ||
				 (code >= 0xf900 && code <= 0xfaff) ||
				 (code >= 0x3400 && code <= 0x4dbf) ||
				 (code >= 0x3000 && code <= 0x303f)))
	    hanchars += 1;
	}
    }
  chars.hanchars = hanchars;

  return chars;
}

function getstrings()
{
  var text = analyzedText;
  var len = text.length;

  var strings = {};
  var numstrings = 0;
  for (var l = 2; l <= 3; l++)
    for (var i = 0; i < text.length; i++)
      {
	var s = text.substr(i, l);
	if (s.strip().length != s.length)
	  continue;
	if (strings[s])
	  strings[s] += 1;
	else
	  {
	    strings[s] = 1;
	    numstrings++;
	  }
      }
  
  return strings;
}

function charscore(corpus, chars)
{
  var score = 0.0;

  $H(chars).each(function(p)
		 {
		   c = p.key;
		   var count = p.value;
		   if (!corpus[c])
		     return;
		   var cs = count * corpus[c];
		   score += cs;
		 });

  return score;
}

function stringscore(corpus, strings)
{
  var score = 0.0;

  $H(strings).each(function(p)
		   {
		     var s = p.key;
		     if (!corpus[s])
		       return;
		     score += 1;
		   });
  
  return score;
}

function scorechars(langs, progressfn, completefn)
{
  var chars = getchars();
  var ary = [];

  scorecharsrun(0, ary, chars, langs, progressfn, completefn);
}

function scorecharslang(lang, chars)
{
  return charscore(langchars[lang], chars);
}

function scorecharsrun(step, ary, chars, langs, progressfn, completefn)
{
  var lang = langs[step];
  var score = scorecharslang(lang, chars);
  if (lang == "zh" || lang == "zh-yue")
    score += chars.hanchars / 200;
  else if (lang == "ja")
    score += chars.hanchars / 200;
  else if (lang == "ko")
    score += chars.hanchars / 400;
  ary.push([lang, score]);
  progressfn(0, step, langs.length);

  if (step < langs.length - 1)
    setTimeout(function() { scorecharsrun(step + 1, ary, chars, langs, progressfn, completefn); }, 10);
  else
    completefn(ary);
}

function scorestrings(langs, progressfn, completefn)
{
  var strings = getstrings();
  var ary = [];

  scorestringsrun(0, ary, strings, langs, progressfn, completefn);
}

function scorestringsrun(step, ary, strings, langs, progressfn, completefn)
{
  var lang = langs[step];
  var score = scorestringslang(lang, strings);
  ary.push([lang, score]);
  progressfn(1, step, langs.length);

  if (step < langs.length - 1)
    setTimeout(function() { scorestringsrun(step + 1, ary, strings, langs, progressfn, completefn); }, 10);
  else
    completefn(ary);
}

function scorestringslang(lang, strings)
{
  var score = stringscore(langstrings[lang], strings);
  if (additionalstrings[lang]) {
    var add = additionalstrings[lang];
    for (var i = 0; i < add.length; ++i)
      if (analyzedText.indexOf(add[i]) != -1)
	score += 1;
  }
  if (lang == "zh" || lang == "zh-yue")
    score *= 3;
  else if (lang == "ja")
    score *= 1.5;
  return score;
}

var lastlength = 0;

function analyze()
{
  if (!autoanalyzing)
    hideresult();
  if ($("intro").visible())
    new Effect.SwitchOff($("intro"), {duration:1.5});
  if (!dataLoaded)
    {
      $("bogus").innerHTML = '<b>' + get_string("loading data") + '</b><br><img src="loading.gif">';
      $("bogus").style.visibility = "visible";
      waitingForDataLoad = true;
      return;
    }
  $("analyze").disabled = true;
  $("bogus").style.visibility = "visible";
  $("progress").style.visibility = "visible";
  $("progressbar").show();
  analyzedText = $F("input").toLowerCase();
  lastlength = analyzedText.length;
  scorechars(langs, updateprogress, oncharscomplete);
}

var mishmash = false;

function oncharscomplete(ary)
{
  ary = ary.sort(function(a, b) { return b[1] - a[1]; });
  var langs = [];
  var half = ary[0][1] / 2;
  var third = ary[0][1] / 3 + (ary[0][1] < 50 ? ary[0][1] / 2 : 0);
  var fifth = ary[0][1] / 5;
  var map = {};
  for (var i = 0; i < ary.length; i++)
    {
      if (ary[i][1] > half)
	langs.push(ary[i][0]);
      if (ary[i][1] > third || (ary[i][0] == "zh" && ary[i][1] > fifth))
	map[ary[i][0]] = ary[i][1];
    }
  var fn = function(lang) { return map[lang] ? 1 : 0; };
  mishmash = (fn("en") + 5 * fn("zh") + fn("el") + fn("ru") + fn("vi") +
	      fn("ko") + fn("es") + fn("ar") + fn("te") + fn("th") > 7);

  if (debug) {
    str = ary.collect(function(x) { return x[0] + ":" + x[1]; }).join("\n");
    alert(str);
  }

  scorestrings(langs, updateprogress, onanalyzecomplete);
}

function onanalyzecomplete(scores)
{
  if (debug) {
    str = scores.collect(function(x) { return x[0] + ":" + x[1]; }).join("\n");
    alert(str);
  }

  preparearrays(scores);
  var prgelm = $("progress");
  //  new Effect.SwitchOff("progress", {afterFinish: function() { prgelm.style.visibility = "hidden";}});
  prgelm.style.visibility = "hidden";
  var bgselm = $("bogus");
  bgselm.innerHTML = "";
  addownskillz();
  eastereggerize();
  showresult();
  $("analyze").disabled = false;
  $("input").disabled = false;
  autoanalyzing = false;
  if (selftesting)
    onselftestdone();
  else if (!isexample && !dontreport)
    reportresult();
  else if (isexample && probable[0] != examplelanguage)
    isexample = false;
}

function addownskillz()
{
  var text = analyzedText;
  var l1 = probable[0];
  var l2 = probable.length > 1 ? probable[1] : null;
  var a1 = l2 ? l2 : (possible.length > 0 ? possible[0] : null);
  var a2 = a1 == l2 ? possible[0] : possible[1];
  // Danish and Swedish often get confused, but it's actually quite simple
  if ((l1 == "da" && l2 == "sv") ||
      (l1 == "sv" && l2 == "da")) {
    var osv = text.count("\u00f6") + text.count("\u00d6");
    var oda = text.count("\u00f8") + text.count("\u00d8");
    var asv = text.count("\u00e4") + text.count("\u00c4");
    var ada = text.count("\u00e6") + text.count("\u00c6");
    if (osv > oda && asv > ada) {
      probable = ["sv"];
      possible.unshift("da");
    }
    else if (oda > osv && ada > asv) {
      probable = ["da"];
      possible.unshift("sv");
    }
    return;
  }
  // distinguish Danish and Norwegian
  if ((l1 == "da" && a1 == "no") ||
	   (l1 == "no" && a1 == "da"))
  {
      var no = (l1 == "no" ? 1.5 : 1.0) * (text.count(" \u00e5 ") + text.count(" seg ") + text.count(" meg ") + text.count(" deg ") + text.count(" av ") + text.count(" ble ") + text.count("ei") / 3 + text.count("\u00f8y") / 3);
      var da = (l1 == "da" ? 1.5 : 1.0) * (text.count(" at ") / 2 + text.count(" sig ") + text.count(" mig ") + text.count(" dig ") + text.count(" af ") + text.count(" blev ") + text.count("ej") / 3 + text.count("\u00f8j") / 3);
      if (no > 2 * da ||
	  da > 2 * no)
      {
	probable[0] = no > da ? "no" : "da";
	if (l2)
          probable.pop();
	else
	  possible.shift();
	l1 = probable[0];
	l2 = null;
	a1 = null;
      }
  }
  // distinguish Norwegian nynorsk
  if (l1 == "no")
  {
      var nn = text.count(" ei ") + text.count(" ein ") + text.count(" noko ") + text.count(" ikkje ") + text.count(" fr\u00e5 ") + text.count(" dei ");
      var nb = text.count(" en ") + text.count(" noe ") + text.count( " ikke ") + text.count(" fra ") + text.count(" de ");
      if (nn > 2 * nb)
	probable[0] = "no-nn";
      else if (nb > 2 * nn)
	probable[0] = "no-nb";
      return;
  }
  // try to distinguish Bosnian, Croatian, Serbian
  if (l1 == "bs" || l1 == "hr" || l1 == "sr-latin") {
    bs = ["\u0107", " \u0161to ", " \u0161ta ", "lac ", "ica ",          "ho ", "han ", "ahk", "ehk", "ahv", "ehv", "ljev", "lijev", "tjec", " sta", " spol ", " osoba ", " da se ", " neka ", " por", "klo ", " da ", " biti ", " cijele ", "nle ", "\u0161i ", "ini ", "potom ", "skoj ", " vrhom ", "li\u010dnu", "svagdje"];
    bsm = [" su ", " onde ", " vrh "];
    hr = ["\u0107",               "elj ", "ica ",          "ho ", "han ", "ol", " hr", "ljev", "lijev", "tjec", "ak ", " skr", " spol ", " osoba ", " neka ", " podr", "tlo ", "ti ", " biti ", " jedan ", "nde ", "obr", "\u0161io ", "su ", "initi ", "onda ", "earu ", " vrhom ", "nitko", "svatko", "osobnu", "svugdje"];
    hrm = [" su ", " vrh ", "niko", "svako"];
    sr = ["\u0161", " \u0161ta ", "lac ", "rka ", "inja ", "vo ", "van ",          "liv",           "tic", " sta", " pol ", " lice ", " neko ", " por", "klo ", " bude ", " cele ", "nde ", "su ", "ini ", "posle ", "skoj ", " kad ", " vrh ", "li\u010dnu", "svuda", " da se "];
    srm = [" spol ", " nitko ", " svatko ", " ondje ", " su ", " svoj ", " vrhom "];
    bsc = text.countArray(bs) - text.countArray(bsm);
    hrc = text.countArray(hr) - text.countArray(hrm);
    src = text.countArray(sr) - text.countArray(srm);
    //    alert("bs: " + bsc + " hr: " + hrc + " sr: " + src);
    var ary = [["bs", bsc], ["hr", hrc], ["sr-latin", src]];
    if (a1 == "sl" || a2 == "sl")
      ary.push(["sl", 0]);
    ary = ary.sort(function(a, b) { return b[1] - a[1]; });
    probable = [ary[0][0]];
    possible = [];
    if (ary[1][1] > 0.85 * ary[0][1])
      probable.push(ary[1][0]);
    else
      possible.push(ary[1][0]);
    possible.push(ary[2][0]);
    if (ary.length > 3)
      possible.push(ary[3][0]);
    return;
  }
  // distinguish Afrikaans from Dutch
  if (l1 == "nl" || l2 == "nl") {
    var af = text.count(" 'n ") + text.count("my") + text.count("ik ") + text.count(" die ") + text.count("vir") + text.count("y") / 10;
    var nl = text.count(" een ") + text.count("mijn") + text.count("ek ") + text.count(" de " ) + text.count(" het ") + text.count("voor") + text.count("ij") / 10;
    if (af > nl) {
      probable[l1 == "nl" ? 0 : 1] = "af";
      if (nl > 0 && af / nl < 10)
	possible.unshift("nl");
    }
    return;
  }
  // Czech and Slovak
  if ((l1 == "cs" && a1 == "sk") ||
      (l1 == "sk" && a1 == "cs")) {
    var cs = text.count("\u011b") + text.count("\0159");
    var sk = text.count("\u013e") + text.count("\0155");
    if (l1 == "cs" && sk > 1.5 * cs)
      probable[0] = "sk";
    else if (l1 == "sk" && cs > 1.5 * sk)
      probable[0] = "cs";
    else
      return;
    possible = [];
    if (l2) {
      possible.unshift(l2);
      probable = [probable[0]];
    }
    possible.unshift(l1);
    return;
  }
  // Tagalog and Cebuano
  if (l1 == "tl") {
      var tl  = [" at ", " iisa ", " isa ", " ano ",  " tayo", "'y ", "'t ", " mo"];
      var ceb = [" ug ", " usa ",           " unsa ", " kita", " kay ",       "[^ ]mo"];
      var tlc = text.countArray(tl);
      var cebc = text.countArray(ceb);
      if (cebc > 2 * tlc) {
	  probable[0] = "ceb";
	  if (cebc < 5 * tlc) {
	      if (probable.length > 1)
		  possible.unshift(probable[1]);
	      probable[1] = "tl";
	  }
	  else if (a1)
	      possible.unshift("tl");
      }
      else if (tlc < 1.5 * cebc) {
	  if (probable.length > 1)
	      possible.unshift(probable[1]);
	  probable[1] = "ceb";
      }
      else if (tlc < 3 * cebc)
	  possible.unshift("ceb");
      return;
  }

  // distinguish Macedonian from Serbian and Bulgarian
  if (l1 == "sr" || l1 == "bg") {
    if (text.indexOf("\u045c") != -1 || text.indexOf("\u0453") != -1) {
      // it's Macedonian
      probable = ["mk"];
      possible = ["sr", "bg"];
    }
  }

  // check for Belarusian
  if (l1 == "uk" || l1 == "bg" || l1 == "ru") {
    if (text.indexOf("\u045e") != -1) {
      // it's Belarusian
      if (l2)
	possible.unshift(l2);
      possible.unshift(l1);
      probable = ["be"];
    }
  }
}

function eastereggerize()
{
  var text = analyzedText;
  if ((text.indexOf(" bork") != -1 && text.indexOf("bork ") != -1 && text.indexOf("bork") != text.lastIndexOf("bork")) || text.indexOf("hurty flurty schnipp schnipp") != -1)
    {
      probable = ["sv-chef"];
      possible = [];
    }
}

var bogus = ["Analyzing constituent order typology", "Analyzing morphological compounding", "Checking denominalization patterns", "Determining case markings", "Identifying grammatical relations", "Checking for ergativity", "Checking for split intransitivity", "Analyzing syntactic valence", "Resolving semantic valence", "Resolving pragmatically marked structures", "Checking for marked focus", "Identifying derivational negation patterns", "Calculating frequently used chacaters", "Calculating frequent characters strings", "Verifying constituent order", "Checking participant reference marking", "Identifying patterns for incorporation", "Analyzing morphological reflexives", "Do applicatives increase valence?", "Analyzing causative formation", "Formalizing attributive clauses", "Describing morphosyntax"];

var lastupdate = 0;
var lastupdatestep = 0;

function updateprogress(half, step, total)
{
  var to = 100;
  var halfway = to / 2;
  var i = Math.min(parseInt(step / total * halfway + (half * halfway) + 1.3), to);
  i = Math.min(i + 5, 100);
  var prgelm = $("progress");
  var barelm = $("progressbar");
  //  barelm.innerHTML = "" + i + "%";
  barelm.setStyle({width: parseInt(prgelm.getWidth() * i / 100 - 2) + "px"});
  if (new Date().getTime() > lastupdate + 500 + lastupdatestep * 100 - Math.random() * 800)
    {
      lastupdatestep = step;
      var bgselm = $("bogus");
      bgselm.innerHTML = bogus[parseInt(Math.random() * bogus.length)];
      lastupdate = new Date().getTime();
    }
}

var probable = null;
var possible = null;
var suspicious = false;

function preparearrays(ary)
{
  probable = [];
  possible = [];
  suspicious = false;

  if (ary.length == 0)
    return;

  var ary = ary.sort(function(a, b) { return b[1] - a[1]; });

  //  var max = ary[0][1];
  //  for (var i = 1; i < ary.length; i++)
  //    ary[i][1] = ary[i][1] / max;

  if (ary.length >= 2 &&
      ary[0][0] == "zh-yue" && ary[1][0] == "zh")
    {
      ary[0][1] = 0.55 * ary[0][1];
      ary = ary.sort(function(a, b) { return b[1] - a[1]; });
    }
  if (ary[0][0] == "zh" || ary[0][0] == "zh-yue")
    mishmash = false;

  var sum = 0.0;
  for (var i = 0; i < ary.length; i++)
    sum += ary[i][1];
  var avg = sum / ary.length;
  var high = ary[0][1] - 0.1 * ary[0][1];
  var same = ary[0][1] - 0.01 * ary[0][1];

  var i = 0;
  for (i = 0; i < ary.length; i++)
    {
      var lang = ary[i][0];
      var score = ary[i][1];
      if (score > same && probable.length < 2)
	//      if (score > 0.85)
	probable.push(lang);
      else if (score > high && possible.length < 3)
	//      else if (score > 0.7)
	possible.push(lang);
      else
	break;
    }

  var gs = [];
  for (var j = 0; j < i; j++)
    {
      var g = langgroups[ary[j][0]];
      if (g != null)
	{
	  gs.push(g);
	  for (var k = 0; k < gs.length; k++)
	    {
	      var g2 = gs[k];
	      if (g2 != g)
		{
		  suspicious = true;
		  break;
		}
	    }
	}
    }
}

function showresult()
{
  var reselm = $("result");
  var res = '<span id="caption">';

  if (mishmash && suspicious)
    res += get_string('languages appear');
  else
    res += get_string('language is') + (mishmash ? get_string('mostly') : "");
  res += '</span><br>';

  if (suspicious)
    {
      res += '<big><span id="language"><strong>' + get_string('mixed') + (!mishmash ? get_string('or unknown') : '') + '</strong></span></big><br>';
      if (probable.length > 0)
	res += get_string('but might be') + '<br><em>' + getlangname(probable[0]) + '</em>';
      if (probable.length > 1)
	res += get_string('or') + '<em>' + getlangname(probable[1]) + '</em>';
      else if (possible.length > 0)
	res += get_string('or') + '<em>' + getlangname(possible[0]) + '</em>';
      res += get_string('might be copula');
    }
  else if (probable.length > 0)
    {
      res += '<big><span id="language"><em>' + getlangname(probable[0]) + '</em>';
      if (probable.length > 1)
	res += '<br>' + get_string('or') + '<br><em>' + getlangname(probable[1]) + '</em>';
      else
	res = "<br>" + res;
      res += '</span></big><br>';
      res += get_string('copula');
      if (!mishmash && possible.length > 0)
	{
	  res += '<small><span id="subscript">' + get_string('parens start') + get_string('or possibly') + '<em>' + getlangname(possible[0]) +'</em>';
	  if (possible.length > 2)
	    res += get_string('comma') + '<em>' + getlangname(possible[1]) + '</em>' + get_string('comma or') + '<em>' + getlangname(possible[2]) + '</em>';
	  else if (possible.length == 2)
	    res += get_string('comma or') + '<em>' + getlangname(possible[1]) + '</em>';
	  res += get_string('doubt copula');
	  res += get_string('parens end') + '</span></small>';
	}
    }
  else
    {
      res = '<br><br>' + res;
      res += '<strong>' + get_string('completely unknown') + '</strong>';
    }
  if (mishmash && !suspicious)
    res += get_string('appears a mix');

  if (autoanalyzing && reselm.innerHTML == res)
    {
      showorhidetip();
      return;
    }

  if (autoanalyzing && reselm.visible())
    new Effect.Fade(reselm, {duration:0.5, afterFinish: function() { setresult(res, true); }});
  else
    setresult(res);
}

function showorhidetip(hide)
{
  if (!hide && !isexample) {
    $("nudge").setStyle({visibility: "visible"});
    new Effect.Appear("nudge", {delay: 4});
  }
  else if ($("nudge").visible())
    new Effect.Fade("nudge");
  if (!hide && (suspicious || (probable.length > 1 && possible.length > 0)))
    {
      if ($("tip").visible())
	return;
      var tip = get_string('try more data');
      if ($F("input").length > 150)
	tip += get_string('or less text');
      tip += get_string("period");
      $("tip").innerHTML = tip;
      new Effect.Appear("tip", { delay: 2 });
    }
  else if ($("tip").visible())
    new Effect.SwitchOff("tip");
}

function setresult(res, fade)
{
  var reselm = $("result");
  reselm.innerHTML = res;
  //  reselm.show();
  if (fade)
    new Effect.Appear(reselm, {duration:0.5});
  else
    new Effect.BlindDown(reselm);
  showorhidetip();
}

function hideresult()
{
  new Effect.SwitchOff("result");
  showorhidetip(true);
}

function displayerror(str)
{
  var inputelm = $("input");
  var errorelm = $("error");

  errorelm.innerHTML = str;

  new Effect.Appear("error");
  setTimeout(hideerror, 500 + str.length * 100);
}

function hideerror()
{
  var inputelm = $("input");
  var errorelm = $("error");

  new Effect.SwitchOff("error");
}

function inputtooshort(strict)
{
  var text = $F("input");
  if (text.length < 5)
    return true;
  var len = 0;
  for (var i = 0; i < text.length; i++) {
    var c = text.charCodeAt(i);
    if (c > 64 && c < 256)
      len += 1;
    else if (c > 256)
      len += 2;
  }
  return len < (strict ? 40 : 20);
}

function onanalyzebuttonclicked()
{
  if (inputtooshort())
    {
      displayerror(get_string('text too short'));
      return;
    }

  $("input").disabled = true;
  analyze();
}

var autoanalyzetimeout = null;
var autoanalyzing = false;
var autoanalyzetime = 0;
var inputinitialized = false;
var dontreport = true;
var isexample = false;
var reported = [];

function oninputchanged()
{
  if (!inputinitialized) {
    inputinitialized = true;
    dontreport = false;
    if ($F("input").strip() == get_string('textarea'))
      $("input").value = "";
    $("input").style.color = "black";
  }
  clearTimeout(autoanalyzetimeout);
  autoanalyzetimeout = setTimeout(autoanalyze, 1000);
}

function autoanalyze()
{
  var len = $F("input").length;
  if (!inputtooshort())
    {
      var ago = parseInt((new Date().getTime() - autoanalyzetime) / 1000);
      var diff = Math.abs(len - lastlength);
      if (ago * diff > 20)
	{
	  autoanalyzetime = new Date().getTime();
	  autoanalyzing = true;
	  dontreport = false;
	  analyze();
	}
      else
	setTimeout(oninputchanged, 5000);
    }
  else if ($("result").visible())
    {
      hideresult();
      isexample = false;
      dontreport = false;
    }
}

function onclearbuttonclicked()
{
  $("input").value = "";
  hideresult();
  isexample = false;
  dontreport = false;
}

function onexamplebuttonclicked()
{
  if (autoanalyzing)
    return;
  dontreport = true;
  isexample = true;
  reportresult(true);
  hideresult();
  while (true) {
    var keys = $H(babel).keys();
    var k = keys[parseInt(keys.length * Math.random())];
    examplelanguage = k;
    var b = babel[k];
    if (b == $("input").value)
      continue;
    $("input").value = b;
    break;
  }
  oninputchanged();
}

var selftesting = false;
var selftestidx = -1;
var selftestlangs = null;
var testdatalangs = [];
var testdatatexts = [];
var selftestbabel = true;

function selftest()
{
  selftesting = true;
  selftestresult = [];
  selftestlangs = $H(babel).keys();
  include("testdata.js");
}

function testdataLoaded() {
  onselftestdone();
}

function onselftestdone()
{
  var keys = selftestlangs;
  if (selftestidx >= 0) {
    var lang = keys[selftestidx];
    if (probable.length > 1 || probable[0] != lang) {
      alert(lang + " FAILED! probable: " + probable.join(" "));
      selftesting = false;
      return;
    }
  }
  if (selftestbabel && selftestidx < keys.length - 1) {
    var b = babel[keys[++selftestidx]];
    $("input").value = b;
    analyze();
  }
  else if (selftestbabel) {
    selftestbabel = false;
    selftestlangs = testdatalangs;
    selftestidx = 0;
    $("input").value = testdatatexts[0];
    analyze();
  }
  else if (!selftestbabel && selftestidx < keys.length - 1) {
    $("input").value = testdatatexts[++selftestidx];
    analyze();
  }
  else {
    selftesting = false;
    alert("selftest completed successfully");
  }
}

function reportresult(babel)
{
  if (babel) {
    var r = "babel";
    var r2 = null;
  }
  else {
    if (probable.length == 0 || suspicious || mishmash || reported.length > 12)
      return;
    var r = probable[0];
    var r2 = probable[1];
    var i = r.indexOf("-");
    var i2 = r2 ? r2.indexOf("-") : -1;
    if (i != -1)
      r = r.substring(0, i);
    if (i2 != -1)
      r2 = r2.substring(0, i2);
  }
  if (reported.indexOf(r) != -1)
    r = null;
  if (reported.indexOf(r2) != -1)
    r2 = null;
  if (!r && !r2)
    return;
  if (!serverUrl) {
    $("footer").innerHTML = "report: " + r + " and " + r2;
    return;
  }
  if (r2 && !r) {
    r = r2;
    r2 = null;
  }
  reported.push(r);
  if (r2)
    reported.push(r2);
  else
    r2 = "null";
  new Ajax.Request(serverUrl + "result",
		   {
		       method: "post",
			   parameters: {lang: r, lang2: r2}
		   });
}

function showFeedbackForm()
{
  $("gray").setStyle({opacity: 0.5,
	display: "block",
	width: "100%",
	height: "100%"});
  var x = $("gray").getWidth() / 2 - $("feedback").getWidth() / 2;
  $("feedback").setStyle({display: "block",
	top: "30px",
	left: "" + x + "px"});
  //  document.getElementsByTagName("body").item(0).setStyle({overflow: "hidden"});

  $("fb_probable").innerHTML = probable.collect(function(x) { return getlangname(x); }).join(", ");
  if (possible.length > 0) {
    $("fb_possible").innerHTML = possible.collect(function(x) { return getlangname(x); }).join(", ");
    $("fb_possible_row").show();
  }
  else
    $("fb_possible_row").hide();
 
  var choice = $("fb_choice");
  $("fb_corrected").hide();
  $("fb_freeform").hide();
  fb_hideSample();
  $("fb_correct_yes").checked = true;
  choice.selectedIndex = 0;
  $("fb_other").value = "";
  $("fb_sample_no").checked = true;
  $("fb_comment").value = "";

  if (choice.length == 2)
    {
      var opts = [];
      for (var i = 0; i < supported_langs.length; ++i)
	  opts.push([getlangname(supported_langs[i]), supported_langs[i]]);
      opts.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; });
      for (var i = 0; i < opts.length; ++i)
	{
	  var elm = document.createElement("option");
	  elm.text = opts[i][0];
	  elm.value = opts[i][1];
	  try {
	    choice.add(elm, null);
	  }
	  catch (e) {
	    choice.add(elm, choice.length);
	  }
	}
    }
}

function fb_choice()
{
  var choice = $("fb_choice");
  var opt = choice.options[choice.selectedIndex];
  if (opt.value == "other") {
    $("fb_freeform").show();
    return $("fb_freeform").value;
  }
  else {
    $("fb_freeform").hide();
    return opt.value;
  }
}

function fb_showChoice()
{
  $("fb_corrected").show();
  fb_choice();
}

function fb_hideChoice()
{
  $("fb_corrected").hide();
  $("fb_freeform").hide();
}

function fb_showSample()
{
  $("fb_sample_text_row").show();
  $("fb_sample_beg").hide();
  var text = analyzedText;
  if (text.length > 100)
    text = text.slice(0, 100) + "...";
  $("fb_sample_text").innerHTML = text.escapeHTML();
}

function fb_hideSample()
{
  $("fb_sample_text_row").hide();
  $("fb_sample_beg").show();
}

function hideFeedbackForm()
{
  $("gray").setStyle({display: "none"});
  $("feedback").setStyle({display: "none"});
  //  document.getElementsByTagName("body").item(0).setStyle({overflow: "auto"});
  $("gray2").setStyle({display: "none"});
  $("sending").setStyle({display: "none"});
}

function sendFeedback()
{
  $("gray2").setStyle({opacity: 0.5,
	display: "block",
	width: "100%",
	height: "100%"});
  var x = $("gray2").getWidth() / 2 - $("sending").getWidth() / 2;
  var y = $("gray2").getHeight() / 2 - $("sending").getHeight();
  $("sending").setStyle({display: "block",
	top: "" + y + "px",
	left: "" + x + "px"});
  $("sending_loading").show();
  $("sending_status").innerHTML = get_string('sending feedback');
  var parm = {};
  parm.probable = probable.join(",");
  if (possible.length > 0)
    parm.possible = possible.join(",");
  parm.actual = fb_choice();
  parm.comment = $("fb_comment").value;
  //todo
  new Ajax.Request(serverUrl + "feedback",
		   {
		       method: "post",
		       parameters: {probable: probable.join(","),
			 possible: possible.join(","),
			 actual: fb_choice(),
			 text: analyzedText.slice(0, 1024),
			 comment: $("fb_comment").value
			 },
		       onComplete: feedbackSent
		   });
}

function feedbackSent()
{
  $("sending_loading").hide();
  $("sending_status").innerHTML = get_string('feedback sent');
  setTimeout(hideFeedbackForm, 4000);
}
