
function OpenNewWindow(url)
{
  window.open(url);
}


function StrDeleteSymbols(src, symbols)
{
  var strRet = "";
  for(var i = 0; i < src.length; i++)
  {
    var bAddLetter = true;
    for(var j = 0; j < symbols.length; j++)
    {
      if(src.charAt(i) == symbols.charAt(j))  
      {
        bAddLetter = false;
        break;
      }
    }
    if(bAddLetter) strRet += src.charAt(i);
  }
  return strRet;
}

function StrArrayDeleteSymbols(src, symbols)
{
  var arrRet = Array();
  var str;
  
  for(var k = 0; k < src.length; k++)
  {
    str = "";
    for(var i = 0; i < src[k].length; i++)
    {
      var bAddLetter = true;
      for(var j = 0; j < symbols.length; j++)
        if(src[k].charAt(i) == symbols.charAt(j))  
      {
        bAddLetter = false;
        break;
      }
      if(bAddLetter)  str += src[k].charAt(i);
    }
    arrRet.push(str);
  }
  return arrRet;
}

function getPxNumber(strSrc)
{
  var endIndex = strSrc.indexOf('p');
  if(endIndex == -1)  endIndex = strSrc.indexOf('P');
  if(endIndex == -1)  endIndex = strSrc.length;

  return parseInt(strSrc.slice(0, endIndex));
}
function BeginFont(modifier, modifierVal)
{
  if(modifier != "")
  {
    return "<FONT " + modifier + "=\'" + modifierVal + "\'>";
  }
  return "<FONT>";
}

function EndFont()
{
  return "</FONT>";
}

function BeginBold()
{
  return "<b>";
}

function EndBold()
{
  return "</b>";
}

function BeginItalic()
{
  return "<i>";
}

function EndItalic()
{
  return "</i>"; 
}

function BeginP(modifier, modifierVal)
{
  if(modifier == undefined  && modifierVal == undefined)  return "<P>";
  if(modifier != "")
  {
    return "<P " + modifier + "=\'" + modifierVal + "\'>";
  }
  return "<P>";
}

function EndP()
{
  return "</P>";
}


function BeginNobr()
{
  return "<NOBR>";
}

function EndNobr()
{
  return "</NOBR>";
}

function Br()
{
  return "<BR/>";
}

function BeginDiv(modifier, modifierVal)
{
  if(modifier != "")
  {
    return "<DIV " + modifier + "=\'" + modifierVal + "\'>";
  }
  return "<DIV>";
}

function EndDiv()
{
  return "</DIV>";
}

function BeginSpan(modifier, modifierVal)
{
  if(modifier != "")
  {
    return "<SPAN " + modifier + "=\'" + modifierVal + "\'>";
  }
  return "<SPAN>";
}

function EndSpan()
{
  return "</SPAN>";
}

function Write(s)
{
  document.write(s);
  //document.writeln(s);
  //document.getElementById('Exercise').innerHTML += s;
  //alert('document = ' + document.getElementById('Exercise').innerHTML + '\ns = ' + s);
}

function AddToSection(secID, text)
{
  document.getElementById(secID).innerHTML += text;
}

function Table()
{
  this.SetTWidth = SetTWidth;
  this.SetTHeight = SetTHeight;
  this.SetTID = SetTID;
  this.SetTClass = SetTClass;
  this.SetTColor = SetTColor;
  this.ResetT = ResetT;
  this.BeginT = BeginT;
  this.BeginTable = BeginTable;
  this.EndTable = EndTable;
  this.EndTd = EndTd;
  this.BeginTr = BeginTr;
  this.EndTr = EndTr;
  this.BeginTd = BeginTd;
  this.EndTd = EndTd;
  this.SetTRowSpan = SetTRowSpan;
  this.SetTAlign = SetTAlign;
  this.SetTValign = SetTValign;
  this.SetTStyle = SetTStyle; 
  
  this.SetTCellspacing = SetTCellspacing;
  this.SetTCellpadding = SetTCellpadding;
  this.SetTColspan = SetTColspan;
  
  this.SetAutoReset = SetAutoReset;
  
  this.ResetT();
  
  this.Width = 0;
  this.Height = 0;
  this.ID = "";
  this.Class = "";
  this.BkColor = "";
  this.RowSpan = "1";
  this.Style = "";
  
  this.bAutoReset = false;
  
  this.TableCnt = 0;
  this.TrCnt = 0;
  this.TdCnt = 0;
  this.TestNotInTable = TestNotInTable;

function SetAutoReset(bVal)
{
  this.bAutoReset = bVal;
}

function SetTWidth(width)
{
  this.bWidthPresent = true;
  this.Width = width;
}

function SetTHeight(height)
{
  this.bHeightPresent = true;
  this.Height = height;  
}

function SetTID(id)
{
  this.ID = id;
  this.bIDPresent = true;
}

function SetTClass(class_)
{
  this.Class = class_;
  this.bClassPresent = true;
}

function SetTStyle(style)
{
  this.Style = style;
  this.bStyle = true;
}

function SetTColor(bkColor)
{
  this.BkColor = bkColor;
  this.bBkColorPresent = true;
}

function SetTRowSpan(rowSpan)
{
  this.RowSpan = rowSpan;
  this.bRowSpanPresent = true;
}

function SetTAlign(align)
{
  this.Align = align;
  this.bAlignPresent = true;
}

function SetTValign(valign)
{
  this.Valign = valign;
  this.bValignPresent = true;
}

function SetTCellspacing(cellspacing)
{
  this.Cellspacing = cellspacing;
  this.bCellspacing = true;
}

function SetTCellpadding(cellpadding)
{
  this.Cellpadding = cellpadding;
  this.bCellpadding = true;
}

function SetTColspan(colspan)
{
  this.Colspan = colspan;
  this.bColspan = true;
}

function ResetT()
{
  this.bWidthPresent = false;
  this.bHeightPresent = false;
  this.bIDPresent = false;
  this.bClassPresent = false;
  this.bBkColorPresent = false;
  this.bRowSpanPresent = false;
  this.bAlignPresent = false;
  this.bValignPresent = false;
  this.bCellspacing = false;
  this.bCellpadding = false;
  this.bStyle = false;
  this.bColspan = false;
}

function BeginT(element)
{

  var addStr = "";
  
  if(this.bWidthPresent)  addStr += ' width=\"' + this.Width + '\"';
  if(this.bHeightPresent)  addStr += ' height=\"' + this.Height + '\"';
  if(this.bIDPresent)  addStr += ' id=\"' + this.ID + '\"';
  if(this.bClassPresent)  addStr += ' class=\"' + this.Class + '\"';
  if(this.bBkColorPresent)  addStr += " style=\"background-color: " + this.BkColor + "\"";
  if(this.bRowSpanPresent)  addStr += " rowspan=\"" + this.RowSpan + "\"";
  if(this.bAlignPresent)  addStr += " align=\"" + this.Align + "\"";
  if(this.bValignPresent)  addStr += " valign=\"" + this.Valign + "\"";
  
  if(this.bColspan)  addStr += " colspan=\"" + this.Colspan + "\"";
  
  if(this.bCellspacing)  addStr += " cellspacing=\"" + this.Cellspacing + "\"";
  if(this.bCellpadding)  addStr += " cellpadding=\"" + this.Cellpadding + "\"";
  if(this.bStyle)  addStr += " style=\"" + this.Style + "\"";
 
  if(this.bAutoReset) this.ResetT(); 
  
  return "<" + element + addStr + ">";
}

function BeginTable()
{
  this.TableCnt++;
  return this.BeginT("table border=1");
}

function EndTable()
{
  this.TableCnt--;
  return "</table>";
}

function BeginTr()
{
  this.TrCnt++;
  return this.BeginT("tr");
}

function EndTr()
{
  this.TrCnt--;
  return "</tr>";
}

function BeginTd()
{
  this.TdCnt++;
  return this.BeginT("td");
}

function EndTd()
{
  this.TdCnt--;
  return "</td>";
}

function TestNotInTable()
{
  if(this.TableCnt == 0  &&  this.TrCnt == 0  &&  this.TdCnt == 0)
    return true;
  else return false;
}

}

function BeginCenter()
{
  return "<center>";
}

function EndCenter()
{
  return "</center>";
}

function Img(src, map, id)
{
  var str = "<img src='" + src + "'";
  
  if(map == undefined)  return str + ">";
  if(id == undefined)
    return str + " usemap='#" + map + "'>";
  return str + " usemap='#" + map + "' id='" + id +"'>";
}

function TArrow(id, idDelimiter, numOfArrow)
{
  this.NumOfArrow = numOfArrow;
  this.Id = id;
  this.IdDelimiter = idDelimiter;
  this.Generate = function()
  {
    return "<span style='display: none' id='" + this.Id + "'>" + 
      "<img style='display: block' src='/study/exercise/img/etc/Arrow"+String(this.NumOfArrow) + ".PNG'>" + EndSpan();
  }
  this.Hide = function()
  {
    document.getElementById(this.Id).style.display = "none";
    document.getElementById(this.IdDelimiter).innerHTML = "";
  }
  this.Show = function()
  {
    document.getElementById(this.Id).style.display = "";
    document.getElementById(this.IdDelimiter).innerHTML = "&nbsp";
  }
}

// user input field

/*Common functions*/

function GetNumberOfWordsToFinishString(numOfLine, text)
{
  var line = 0, i = 0, numOfWords = 0;
  
  for(; line < numOfLine + 1  &&  i < text.length; i++)
  {
    if(text.charAt(i) == '\n')
    {
      line++;
      numOfWords++;
    }
    if(text.charAt(i) == ' ')  numOfWords++;
  }
  return numOfWords;
}

function EmptySpan(letter)
{
  var style1 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size: 1px; margin: 0 1px";
  var style2 = "padding: 0px 2px; margin: 0;" +
                      " font-weight: bold; height: 100%; font-family: Courier New; font-size: 12pt;";

  var letter_ = letter;
  
  if(letter_ == " ")  letter_ = "&nbsp;";

  var res = "<b style='" + style1 + "'></b>" +
		  "<div style='" + style2 + "'>" + letter_ +
		  "</div>" +
		  "<b style='" + style1 + "'></b>";  
  
  return res;
}

function GetWordBeginStrIndex(src, wordInd)
{
  var wI, i;
  for(wI = 0, i = 0; wI < wordInd && i < src.length; i++)
  {
    if(src.charAt(i) == " " || src.charAt(i) == "\n")  wI++;
  }

  return i;
}

function IsWordLastAtString(s, ind)
{
  var i;
  for(i = GetWordBeginStrIndex(s, ind); i < s.length; i++)
  {
    if(s.charAt(i) == ' ')  
    {
      return false;
    }
    if(s.charAt(i) == '\n')
    {
      return true;
    }
  }
}

function GetNumOfWords(str)
{
  var numOfWords = 0;
  for(i = 0; i < str.length; i++)
  {
    if(str.charAt(i) == " "  ||  str.charAt(i) == "\n") numOfWords++;
  }
  if(str.charAt(i-1) != "\n"  &&  str.charAt(i-1) != " "  &&  i == str.length)  numOfWords++; 
  return numOfWords;
}

function TInputField(id, strLength, numOfLines, numOfLinesBeforeCurrentLine)
{
  this.Id = id;
  this.LetterId = 'Letter';
  this.StrLength = strLength;
  this.NumOfLines = numOfLines;
  this.NumOfLinesBeforeCurrentLine = numOfLinesBeforeCurrentLine;
  
  this.Text = String("");
  this.InnerHTML = String("");
  
  this.Index = 0;
  
  this.SetText = SetText;
  this.SetCaret = SetCaret;
  this.Render = Render;
  this.PlaceHere = PlaceHere;
  this.SetLastChar = SetLastChar;
//  this.RenderMarkChar = RenderMarkChar;
  this.SetBkRedCurrentChar = SetBkRedCurrentChar;
  this.SetBkGreenCurrentChar = SetBkGreenCurrentChar;
  
  this.LetterSpan = LetterSpan;
  
  this.EmptyField = new TEmptyField("Text", "#f6f6f6");
  this.GetEmptyField = GetEmptyField;
  this.LastChar = -1;
  this.MarkError = false;
  this.BkCurrentCharColor = 'green';
  this.CurrentCharColor = 'white';
  
  this.GetStringId = GetStringId;
  this.GetLetterSpanId = GetLetterSpanId;
  
  this.SetCharColorBkColor = SetCharColorBkColor;
  this.SetCharBkColor = SetCharBkColor;
  this.SetCharColor = SetCharColor;
  
  this.GetIndex = GetIndex;
  this.GetCurLine = GetCurLine;
  
  this.StepForward = StepForward;
  this.GoToBegOfLine = GoToBegOfLine;
  this.SetError = SetError;
  this.ResetError = ResetError;
  this.GoToBeg = GoToBeg;
  
  this.CurLine = 0;
  this.PrevLine = 0;
  this.NumOfLinesAtText = 0; // num of lines at exercise
  this.WholeTextNumOfLines = 0;
  
  this.ShowElement = ShowElement;
  
  this.BWordsMode = false;
  
  this.GetCurLine = function()
  {
    return this.CurLine;
  }
  this.GetNumOfLinesAtText = function()
  {
    return this.WholeTextNumOfLines;
  }
  
  this.SetWordsMode = function(bMode)
  {
    this.BWordsMode = bMode;
  }
  
  this.GetWordNum = function(num)
  {
    var obj = document.getElementById(this.GetLetterSpanId(num));
    if(obj == undefined)
      return;
    
    return document.getElementById(this.GetLetterSpanId(num)).innerHTML;
  }
  
function PlaceHere()
{
  return BeginSpan('id', this.Id) + EndSpan();
}  
    
function GetEmptyField()
{
  return this.EmptyField;
} 

function GetLetterSpanId(i)
{
  return this.Id + this.LetterId + String(i);
}

function SetText(text)
{
  var obj = document.getElementById(this.Id);
  
  this.Text = text;
  
  for(i = 0; i < this.Text.length; i++)
  {
    this.InnerHTML += this.LetterSpan(i, this.Text.charAt(i));
  }
}

function GetText()
{
  return this.Text; 
}

function SetError()
{
  this.SetCharBkColor(this.Index, '#AB0000');
}

function ResetError()
{
  this.SetCharBkColor(this.Index, '');
}

this.RestoreCursor = function()
{
  this.SetCharColorBkColor(this.Index, "white", "green");
}

function SetLastChar(lastChar)
{
  this.LastChar = lastChar;
}

function SetBkRedCurrentChar()
{
  this.BkCurrentCharColor = 'red';
}

function SetBkGreenCurrentChar()
{
  this.BkCurrentCharColor = 'green';
}

function ShowElement(i, bShow)
{
  var obj = document.getElementById(this.GetStringId(i));
  if(bShow)  obj.style.display = "";
  else  obj.style.display = "none";
}

function SetCaret(index, bIsBackward)
{
  // change here the manner of vision...
  if(index == 0)  
  {
    this.CurLine = 0;
	  this.Index = 0;
    if(bIsBackward == true)  this.Render();
  }
  else
  if(index > 0)
  {
    if(this.BWordsMode == false)
    {
      if(this.Text.charAt(index - 1) == '\n'  &&  bIsBackward != true)  
      {
        this.CurLine++;
      }
    }
    else
    {
      if(index == GetNumberOfWordsToFinishString(this.CurLine, this.Text))
      {
        this.CurLine++;
      }
    }
  }
  
  if(this.CurLine != this.PrevLine  &&  this.CurLine > this.NumOfLinesBeforeCurrentLine  &&  
      this.CurLine < this.NumOfLinesAtText - 1  &&  bIsBackward != true)  
  { 
    this.ShowElement(this.CurLine - this.NumOfLinesBeforeCurrentLine - 1, false);
    this.ShowElement(this.CurLine + 1, true);
  }    
  
  var color = "";
  
  if(bIsBackward == undefined  ||  bIsBackward == false)  color = "green";
  
  this.SetCharColorBkColor(this.Index, color, "");
  this.SetCharColorBkColor(index, "white", "green");
  
  this.Index = index;
  
  this.PrevLine = this.CurLine;
}

function GetIndex()
{
  return this.Index;
}

function StepForward()
{
  /*if(this.GetIndex() < this.Text.length - 1)  */this.SetCaret(this.GetIndex() + 1);
}

function GoToBeg()
{
  this.SetCaret(0, true);
}

function GoToBegOfLine()
{
  var i = this.GetIndex();
  if(this.Text.charAt(i) == '\n')
    i--;
  
  for(; i > 0  &&  this.Text.charAt(i) != '\n'; i--)
  {
    this.SetCharColor(i, "")
    this.SetCharBkColor(i, "");    
  }
  
  if(this.Text.charAt(i) == '\n')  i++;
  
  this.SetCaret(i, true);
}

function SetCharColorBkColor(i, color, bkColor)
{
  /*var style1 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size: 1px; margin: 0 1px; background-color: " + bkColor;
  var style2 = "padding: 0 2px; margin: 0; background-color: " + bkColor + 
                      "; font-weight: bold;  text-align: left; color: " + color + "; vertical-align: top;";*/
                      
  var obj1 = document.getElementById(this.GetLetterSpanId(i) + "El1"); 
  var obj2 = document.getElementById(this.GetLetterSpanId(i));
  var obj3 = document.getElementById(this.GetLetterSpanId(i) + "El2");  
  
  obj1.style.background  = bkColor;
  obj2.style.color = color;
  obj2.style.background = bkColor;
  obj3.style.background  = bkColor;                           
}

function LetterSpan(i, letter, bGreen)
{               
  //var addColorStr = "";
  //if(bGreen == true)  addColorStr += " color: green;";

  /*var style1 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size: 1px; margin: 0 1px";
  var style2 = "padding: 0px 2px; margin: 0;" +
                      " font-weight: bold; height: 100%; font-family: Courier New; font-size: 12pt;" + addColorStr;*/ // + " text-align: center; vertical-align: center;"

  //var letter_ = letter;
  
  if(letter == " ")  letter = "&nbsp;";

  if(bGreen == false)
    return  "<b id='" + this.GetLetterSpanId(i) + "El1' class='Input1Class1'></b><div id='" + 
      this.GetLetterSpanId(i) + 
      "' class='Input2ColorNone'>" + letter +
		  "</div><b id='" + this.GetLetterSpanId(i) + "El2" + "' class='Input1Class1'></b>";
	else
	 return  "<b id='" + this.GetLetterSpanId(i) + "El1' class='Input1Class1'></b><div id='" + 
      this.GetLetterSpanId(i) + 
      "' class='Input2ColorGreen'>" + letter +
		  "</div><b id='" + this.GetLetterSpanId(i) + "El2" + "' class='Input1Class1'></b>";

}

function SetCharBkColor(i, color)
{

  this.Style1 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size:1px; margin: 0 1px; background-color: " + this.BkColor;
  this.Style2 = "padding: 0 2px; margin: 0; background-color: " + this.BkColor + 
                      "; font-weight: bold;  text-align: left; color: " + this.Color + "; vertical-align: top;";

  document.getElementById(this.GetLetterSpanId(i)).style.background = color;
  return;
}

function SetCharColor(i, color)
{
  document.getElementById(this.GetLetterSpanId(i)).style.color = color;
}

function GetCurLine(curPos)
{
  return this.CurLine;
}

function GetStringId(numOfString)
{
  return "InputFieldString" + String(numOfString);
}

this.GetNumOfWords = function()
{
  return this.NumOfWords;
}

this.GetWord = function(index, str)
{
  var i;
  var index_ = 0;
  
  for(i = 0; i < str.length; i++)
  {
    if(index == index_)
    {
      var strRet = "";
      for(;str.charAt(i) != " " && str.charAt(i) != "\n"; i++)
        strRet += str.charAt(i);
      return strRet;
    }
    
    if(str.charAt(i) == " "  ||  str.charAt(i) == "\n")  index_++;
  }
}

this.GetWordLength = function(begIndex, text)
{
  var i, len = 0;
  for(i = begIndex; text.charAt(i) != " "  &&  text.charAt(i) != "\n"  &&
          i < text.length; i++)
            len++;
  return len;
}

/*function TInterval()
{
  alert("test");
}*/

function Render() // new (without color/bkcolor of text )
{
  var obj = document.getElementById(this.Id);
  
//  setInterval("TInterval()", 300);
  
  var exStr = "";
  
  var curLine = 0;
    
  var text = this.Text;
  if(this.LastChar != -1)  text = this.Text.substring(0, this.LastChar);
  
  curLine = this.GetCurLine();
 
// теперь надо решить с какой строки начинать отображение 
  var beginningLine = curLine - this.NumOfLinesBeforeCurrentLine;
  if(beginningLine < 0) beginningLine = 0; 
 
  var i = 0;
  for(var k = 0; k < beginningLine; i++)
  {
    if(text.charAt(i) == '\n') k++;
  }
  endLine = beginningLine + this.NumOfLines;
  
  var cntWord;
  
  if(this.BWordsMode == false)
  {
    for(i = 0, k = 0; i < text.length; i++)
    {
      if(k >= beginningLine && k < endLine)  exStr += "<table id='" + this.GetStringId(k) + "' cellspacing='0' cellpadding='0' border='0'><tr>";
      else  exStr += "<table id='" + this.GetStringId(k) + "' cellspacing='0' cellpadding='0' border='0' style='display: none'><tr>";
          
      for(var j = 0; j < this.StrLength && i < text.length  &&  text.charAt(i) != '\n'; j++, i++)
      {
        exStr += "<td>";
        var bGreen = false;
        if(this.Index > i)  bGreen = true;    
        
        // params: this.BWordsMode
        
        exStr += this.LetterSpan(i, text.charAt(i), bGreen);
        
        exStr += "</td>";
      }
    
      if(text.charAt(i) == '\n')
      {
        exStr += "<td>";
        exStr += this.LetterSpan(i, '&nbsp;');
        exStr += "</td>";   
        k++;
        //i++;
      }    
      exStr += "</tr></table>";
    }
  }
  else
  {      
   // var bBeginStr = true;
   var NumOfWord = 0;
   for(i = 0, k = 0; i < text.length; i++)
   {
      if(k >= beginningLine && k < endLine)  exStr += "<table cellspacing='0' cellpadding='0' border='0'><tr id='" + this.GetStringId(k) + "'>";
      else  exStr += "<table cellspacing='0' cellpadding='0' border='0'><tr id='" + this.GetStringId(k) + "' style='display: none'>";
          
      for(var j = 0; j < this.StrLength && i < text.length; j++, i++)
      {
      
        if(text.charAt(i) == ' ' || text.charAt(i) == '\n')
        {      
          exStr += "<td>";
          var bGreen = false;
          if(this.Index > NumOfWord)  bGreen = true;    
          exStr += this.LetterSpan(NumOfWord, this.GetWord(NumOfWord, text), bGreen);
          //alert(this.LetterSpan(NumOfWord, this.GetWord(NumOfWord, text), bGreen));
          exStr += "</td>";
          NumOfWord++;
        }
        if(text.charAt(i) == '\n')  break;
      }
    
      if(text.charAt(i) == '\n')
      {
        exStr += "<td>";
        exStr += /*this.LetterSpan(i, '&nbsp;&nbsp;')*/EmptySpan('&nbsp;');
        //alert(this.LetterSpan(i, '&nbsp;&nbsp;'));
        exStr += "</td>";   
        k++;
        //i++;
      }    
      exStr += "</tr></table>";
    }
    
    this.NumOfWords = NumOfWord;
  }
  
  this.WholeTextNumOfLines = k;
  
  for(; k < this.NumOfLines; k++) // fill empty strings
  {
    exStr += "<table cellspacing='0' cellpadding='0' border='0'><tr><td>&nbsp;</td></tr></table>";
  }
  
  this.NumOfLinesAtText = k; // Init NumOfLines
  //exStr += "</tr></td></table>";
  obj.innerHTML = this.EmptyField.HTMLPreInputField(this.Id) + exStr + this.EmptyField.HTMLPostInputField();
}
  
}  

///////

function TCiteField(id)
{
  this.Id = id;
  this.FieldId = id + 'Field';
  this.SetCite = SetCite;
  this.Update = Update;
  this.Render = Render;
  this.PlaceHere = PlaceHere;
  this.RenderError = RenderError;
  this.EmptyField = new TEmptyField("Cite", "#FFFFFF");
  this.GetEmptyField = GetEmptyField;
  
  this.Cite = "";
  this.ErrorText = "";
  this.Author = "";
  this.AuthorInfo = "";
  
  this.Hide = function()
  {
    var obj = document.getElementById(this.Id);
    obj.style.visibility = "hidden";
  }
  
  this.Show = function()
  {
    var obj = document.getElementById(this.Id);
    obj.style.visibility = "visible";
  }
  
function PlaceHere()
{
  return BeginSpan('id', this.Id) + EndSpan();
} 
  
function GetEmptyField()
{
  return this.EmptyField;
} 

this.Init = function()
{

 //if(this.BInited == false)
  //{
  //}
  this.OText = document.getElementById(this.CiteId);
  this.OCiteCornerTopLeft = document.getElementById("IDCiteCornerTopLeft");
  this.OCiteTop = document.getElementById("IDCiteTop");
  this.OCiteCornerTopRight = document.getElementById("IDCiteCornerTopRight");
  this.OCiteLeft = document.getElementById("IDCiteLeft");
  this.OCiteRight = document.getElementById("IDCiteRight");
  this.OCiteSpeech = document.getElementById("IDCiteSpeech");
  this.OCiteRight2 = document.getElementById("IDCiteRight2");
  this.OCiteLeft2 = document.getElementById("IDCiteLeft2");
  this.OCiteRight3 = document.getElementById("IDCiteRight3");
  this.OCiteRight3 = document.getElementById("IDCiteRight3");
  this.OCiteCornerBottomLeft = document.getElementById("IDCiteCornerBottomLeft");
  this.OCiteBottom = document.getElementById("IDCiteBottom");
  this.OCiteCornerBottomRight = document.getElementById("IDCiteCornerBottomRight");
  this.OCiteSpeechFiller = document.getElementById("IDCiteSpeechFiller");
} 

this.BInited = false;

this.SetBorder = function(pre)
{  

//  alert("SetBorder("+pre);
  if(pre == "exBlack")
    this.OText.style.color = "black";
  else
    this.OText.style.color = "red";
  
  this.OCiteTop.className = pre + "CiteBkTop";
  this.OCiteCornerTopLeft.className = pre + "CiteCornerTopLeft";
  this.OCiteTop.className = pre + "CiteBkTop";
  this.OCiteCornerTopRight.className = pre + "CiteCornerTopRight";
  this.OCiteLeft.className = pre + "CiteBkLeft";
  this.OCiteRight.className = pre + "CiteBkRight";
  this.OCiteSpeech.className = pre + "CiteSpeechBk";
  this.OCiteRight2.className = pre + "CiteBkRight";
  this.OCiteLeft2.className = pre + "CiteBkLeft";
  this.OCiteRight3.className = pre + "CiteBkRight";
  this.OCiteCornerBottomLeft.className = pre + "CiteCornerBottomLeft";
  this.OCiteBottom.className = pre + "CiteBkBottom";
  this.OCiteCornerBottomRight.className = pre + "CiteCornerBottomRight";
  this.OCiteSpeechFiller.className = pre + "CiteFiller";
}

this.SetBlack = function()
{
//  alert("SetBlack");
  this.SetBorder("exBlack");
}

this.SetError = function()
{
//  alert("SetError");
  this.SetBorder("exError");
}

this.MergeInt = 0;


function citeFieldMerge(obj)
{
   if(CiteField.MergePh)
   {
      CiteField.SetBlack();
   }
   else
   {
      CiteField.SetError();
   }
   CiteField.MergePh = !CiteField.MergePh;
}

this.BeginMerge = function()
{
  this.MergeInt = setInterval(function(){citeFieldMerge(this);}, 300);
  this.MergeF0 = this.SetBlack;
  this.MergeF1 = this.SetError; 
  this.MergePh = false;
}
this.StopMerge = function()
{
  clearInterval(this.MergeInt);
}

function SetCite(cite, author, authorInfo)
{
  this.Store(this.Cite, this.Author, this.AuthorInfo);
  this.Cite = cite;
  if(author != undefined)
  {
    this.Author = author.replace(/,/, ",<br>");
  }
  else
    this.Author = "";
  if(authorInfo != undefined)
  {
    this.AuthorInfo = authorInfo;
  }
  else this.AuthorInfo = "";
}

this.TPrev = function()
{
  this.Cite = "not inited";
  this.Author = "not inited";
  this.AuthInfo = "not inited";
}
this.Prev = new this.TPrev();

this.Store = function(cite, auth, authInfo)
{
  this.Prev.Cite = cite;
  this.Prev.Author = auth;
  this.Prev.AuthInfo = authInfo;  
}

this.Restore = function()
{
  this.Cite = this.Prev.Cite;
  this.Author = this.Prev.Author;
  this.AuthorInfo = this.Prev.AuthInfo;
}


function Update()
{
  var objCite = document.getElementById(this.CiteId);
  var objAuthor = document.getElementById(this.AuthorId);
  
  this.Init();
  
  objCite.innerHTML = "<div style='color: #000000; font-size: 12pt; font-weight: bold; font-family: Tahoma; line-height: 1.2'>" + this.Cite + "</div>";
  objAuthor.innerHTML = "<div style='color: #000000; font-size: 9pt; font-family: Arial; font-style:italic; line-height: 1.1'>" + this.Author + this.AuthorInfo + "</div>";
  
}

this.SetErrorText = function(val)
{
  this.Store(this.Cite, this.Author, this.AuthorInfo);
  this.ErrorText = val;
}

this.UpdateErrorText = function()
{
  var objCite = document.getElementById(this.CiteId);
  var objAuthor = document.getElementById(this.AuthorId);
  
  var boldness = 400;
  
  if(window.navigator.appName == "Opera")  boldness = 600;
  
  objCite.innerHTML = "<center><span style='font-size: 14pt; font-family: Arial; line-height: 125%;'>" + this.ErrorText + "</span></center>";
  objAuthor.innerHTML = "";
}

this.SetWarning = function()
{
  
}

this.GetCiteField = function(bError)
{
  var class_ = undefined;
  var vertAlign = "top";
  if(bError == true)
  {
    class_ = "ErrorCite";
    vertAlign = "middle";
  }
  return  BeginSpan('id',this.FieldId) + this.EmptyField.HTMLPreCiteField(class_) +
  "<table cellpadding=4 border=0 width='100%' height='90px'><tr><td valign='" + vertAlign + "'>" + 
  BeginSpan("id", this.CiteId) + EndSpan() +
  "</td></tr><tr valign='bottom' align='right'><td>" + 
  BeginSpan("id", this.AuthorId) + EndSpan() +
  "</td></tr></table>" + 
    this.EmptyField.HTMLPostCiteField(class_) + EndSpan();
}

function Render()
{
  var obj = document.getElementById(this.Id);

  this.CiteId = this.Id + "Cite";
  this.AuthorId = this.Id + "Author";

  obj.innerHTML = this.GetCiteField(false);   
  
//  TableBuilder.ResetT();
  
  //alert(BeginP('align', 'right') + "" + EndP());
}

function RenderError()
{
  var obj = document.getElementById(this.Id);
  obj.innerHTML = this.GetCiteField(true);
  this.Update();
  
  this.UpdateErrorText();
}

this.SetBlackBorder = function()
{
// rename all the classes
  
}

}


////////////
  
function TEmptyField(style, bk, height)
{
  this.Style = style;
  this.HTMLPreInputField = HTMLPreInputField;
  this.HTMLPreInputField2 = HTMLPreInputField2;
  this.HTMLPostInputField = HTMLPostInputField;
  this.HTMLPostInputField2 = HTMLPostInputField2;
  this.Bk = bk;
  this.Height = height;
  
  
  this.RowHeight = 0;
  this.RowWidth = 0;
  this.SetRowHeight = SetRowHeight;
  this.GetRowHeight = GetRowHeight;
  this.SetRowWidth = SetRowWidth;
  this.GetRowWidth = GetRowWidth;
  
function SetRowHeight(val)
{
  this.RowHeight = val;
}

function GetRowHeight()
{
  return this.RowHeight;
}

function SetRowWidth(val)
{
  this.RowWidth = val;
}

function GetRowWidth()
{
  return this.RowWidth;
}
    
function HTMLPreInputField(id)
{
  var strHeight = "";
  if(this.Height != 0 && this.Height != undefined)  strHeight = " height=\'" + String(this.Height) + "\'";
  var res = "<table border=0 id='" + id + "Table' border=0 cellspacing=0 cellpadding=0 width='600' class=\'ex" + this.Style + "Table\'>" +
  "<tr height='4px'>" +
  "<td class='ex" + this.Style + "CornerTopLeft' width='4px'>" + 
  "</td>" +
  "<td height='4px' class='ex" + this.Style + "BkTop'>" +
  "</td>" +
  "<td width='4px' height='4px' class='ex" + this.Style + "CornerTopRight'>" +
  "</td>" +  
  "</tr>" +
  "<tr" +
  ">" +
  "<td width='4px' class='ex" + this.Style + "BkLeft'>" +
  "</td>" +
  "<td  valign='middle'>" +
  "<table border=0 cellspacing=0 cellpadding=7 width=100% align='center' bgcolor=\'" + this.Bk + "\'>" +
  "<tr>" +
  "<td valign='middle'>" +
  "<span class='exercise" + this.Style + "'>";
  return res;
}

function HTMLPostInputField()
{
  return "</span>" +
  "</td>" +
  "</tr>" +
  "</table>" +
  "</td>" +
  "<td width='4px' class='ex" + this.Style + "BkRight'>" +
  "</td>" + 
  "</tr>" +
  "<tr height='4px'>" +
  "<td width='4px' height='4px' class='ex" + this.Style + "CornerBottomLeft'>" +
  "</td>" +
  "<td  height='4px' class='ex" + this.Style + "BkBottom'>" +
  "</td>" +
  "<td width='4px'  height='4px' class='ex" + this.Style + "CornerBottomRight'>" +
  "</td>" +  
  "</tr>" +  
  "</table>";
}

function HTMLPreInputField2(id)
{
  var strHeight = "";
  if(this.Height != 0 && this.Height != undefined)  strHeight = " height=\'" + String(this.Height) + "\'";
  var res = "<table id='" + id + "Table' border=0 cellspacing=0 cellpadding=0 width='600' class=\'ex" + this.Style + "Table\'>" +
  "<tr height='4px'>" +
  "<td class='ex" + this.Style + "CornerTopLeft' width='4px'>" + 
  "</td>" +
  "<td height='4px' class='ex" + this.Style + "BkTop'>" +
  "</td>" +
  "<td width='4px' height='4px' class='ex" + this.Style + "CornerTopRight'>" +
  "</td>" +  
  "</tr>" +
  "<tr" +
  ">" +
  "<td width='4px' class='ex" + this.Style + "BkLeft'>" +
  "</td>" +
  "<td  valign='middle'>" +
  "<table border=0 cellspacing=0 cellpadding=5 width=100% align='center' bgcolor=\'" + this.Bk + "\'>" +
  "<tr>" +
  "<td valign='middle'>" +
  "<span class='exercise" + this.Style + "'>";
  //alert("HTMLPreInputField2: test");
  return res;
}  

function HTMLPostInputField2()
{
  return "</span>" +
  "</td>" +
  "</tr>" +
  "</table>" +
  "</td>" +
  "<td width='4px' class='ex" + this.Style + "BkRight'>" +
  "</td>" + 
  "</tr>" +
  "<tr height='4px'>" +
  "<td width='4px' height='4px' class='ex" + this.Style + "CornerBottomLeft'>" +
  "</td>" +
  "<td  height='4px' class='ex" + this.Style + "BkBottom'>" +
  "</td>" +
  "<td width='4px'  height='4px' class='ex" + this.Style + "CornerBottomRight'>" +
  "</td>" +  
  "</tr>" +  
  "</table>";
}

this.HTMLPreCiteField = function(class_)
{
if(class_ == undefined)  class_ = "Cite";

return "<TABLE class='ex" + class_ + "Table' WIDTH=100% BORDER='0' CELLPADDING=0 CELLSPACING=0>" +
"	<TR VALIGN=TOP height='4px'>" +
"		<TD WIDTH='19px'>" +
"		</TD>" +
"		<TD id='IDCiteCornerTopLeft' class='ex" + class_ + "CornerTopLeft' width='4px'>" +
"		</TD>" +
"		<TD id='IDCiteTop' class='ex" + class_ + "BkTop'>" +
"		</TD>" +
"		<TD id='IDCiteCornerTopRight' class='ex" + class_ + "CornerTopRight' width='4px'>" +
"		</TD>" +
"	</TR>" +
"	<TR VALIGN=TOP height='35px'>" +
"		<TD width='4px'><font size='2pt' face='Times New Roman'>&nbsp;</font>" +
"		</TD>" +
"		<TD id='IDCiteLeft' class='ex" + class_ + "BkLeft' WIDTH='4px'>" +
"		</TD>" +
"		<TD ROWSPAN=3>";
}

this.HTMLPostCiteField = function(class_)
{
  if(class_ == undefined)  class_ = "Cite";

  /*var speechHeight = 19;
  if(window.navigator.appName == "Microsoft Internet Explorer")
    speechHeight = 19;*/    

return "</TD>" +
"		<TD id='IDCiteRight' class='ex" + class_ + "BkRight' WIDTH='4px'>" +
"		</TD>" +
"	</TR>" +
"	<TR VALIGN=TOP style='height: 19px'>" +
"		<TD id='IDCiteSpeech' style='height: 19px' class='ex" + class_ + "SpeechBk' WIDTH='19px'>" +
"		</TD>" +
"		<TD id='IDCiteSpeechFiller' WIDTH='4px' class='exCiteSpeechFiller'" +
"		</TD>" +
"		<TD id='IDCiteRight2' class='ex" + class_ + "BkRight' WIDTH='4px'>" +
"		</TD>" +
"	</TR>" +
"	<TR VALIGN=TOP height='35px'>" +
"		<TD WIDTH='19px'><font size='2pt' face='Times New Roman'>&nbsp;</font>" +
"   </TD>" +
"		<TD id='IDCiteLeft2'  class='ex" + class_ + "BkLeft' WIDTH='4px'>" +
"		</TD>" +
"		<TD id='IDCiteRight3' WIDTH='4px' class='ex" + class_ + "BkRight' >" +
"		</TD>" +
"	</TR>" +
"	<TR VALIGN=TOP height='4px'>" +
"		<TD WIDTH='19px'>" +
"		</TD>" +
"		<TD id='IDCiteCornerBottomLeft' class='ex" + class_ + "CornerBottomLeft' WIDTH='4px'>" +
"		</TD>" +
"		<TD id='IDCiteBottom' class='ex" + class_ + "BkBottom'>" +
"		</TD>" +
"		<TD id='IDCiteCornerBottomRight' class='ex" + class_ + "CornerBottomRight' WIDTH='4px'>" +
"		</TD>" +
"	</TR>" +
"</TABLE>";
}


this.HTMLPreResultMessageField = function()
{
  var strHeight = "";
  if(this.Height != 0 && this.Height != undefined)  strHeight = " height=\'" + String(this.Height) + "\'";
  var res = "<table width='100%' border=0 cellspacing=0 cellpadding=0 class='exResultMessageTable'>" +
  "<tr height='4px'>" +
  "<td id='IDTdResultMessageCornerTopLeft' class='exResultMessageCornerTopLeft' width='4px'>" + 
  "</td>" +
  "<td id='IDTdResultMessageBkTop' height='4px' class='exResultMessageBkTop'>" +
  "</td>" +
  "<td id='IDTdResultMessageCornerTopRight' width='4px' height='4px' class='exResultMessageCornerTopRight'>" +
  "</td>" +  
  "</tr>" +
  "<tr" +
  ">" +
  "<td id='IDTdResultMessageBkLeft' width='4px' class='exResultMessageBkLeft'>" +
  "</td>" +
  "<td valign='middle'>" +
  "<table border=0 cellspacing=0 cellpadding=7 width=100% align='center' bgcolor=\'" + this.Bk + "\'>" +
  "<tr>" +
  "<td valign='middle'>";
  return res;
}

this.HTMLPostResultMessageField = function()
{
  return "</td>" +
  "</tr>" +
  "</table>" +
  "</td>" +
  "<td id='IDTdResultMessageBkRight' width='4px' class='exResultMessageBkRight'>" +
  "</td>" + 
  "</tr>" +
  "<tr height='4px'>" +
  "<td id='IDTdResultMessageCornerBottomLeft' width='4px' height='4px' class='exResultMessageCornerBottomLeft'>" +
  "</td>" +
  "<td id='IDTdResultMessageBkBottom' height='4px' class='exResultMessageBkBottom'>" +
  "</td>" +
  "<td id='IDTdResultMessageCornerBottomRight' width='4px'  height='4px' class='exResultMessageCornerBottomRight'>" +
  "</td>" +  
  "</tr>" +  
  "</table>";
}

}

///////

function TInputField2(id, strLength, numOfLines, numOfLinesBeforeCurrentLine)
{
  this.Id = id;
  this.LetterId = 'Letter';
  this.StrLength = strLength;
  this.NumOfLines = numOfLines;
  this.NumOfLinesBeforeCurrentLine = numOfLinesBeforeCurrentLine;
  
  this.Text = String("");
  this.InnerHTML = String("");
  
  this.Index = 0;
  
  this.SetText = SetText;
  this.SetCaret = SetCaret;
  this.Render = Render;
  this.SetLastChar = SetLastChar;
//  this.RenderMarkChar = RenderMarkChar;
  this.SetBkRedCurrentChar = SetBkRedCurrentChar;
  this.SetBkGreenCurrentChar = SetBkGreenCurrentChar;
  
  this.LetterSpan = LetterSpan;
  
  this.EmptyField = new TEmptyField("Text", "#f6f6f6");
  this.GetEmptyField = GetEmptyField;
  this.LastChar = -1;
  this.MarkError = false;
  this.BkCurrentCharColor = 'green';
  this.CurrentCharColor = 'white';
  
  this.GetLetterSpanId = GetLetterSpanId;
  
  this.SetCharBkColor = SetCharBkColor;
  this.SetCharColor = SetCharColor;
  
  this.GetIndex = GetIndex;
  
  this.GetCurLine = GetCurLine;
  this.GetStringId = GetStringId;
  
  this.StepForward = StepForward;
  this.SetError = SetError;
  this.GoToBegOfLine = GoToBegOfLine;
  this.SetChar = SetChar;
  this.AppendLastChar = AppendLastChar;
  this.GoToBeg = GoToBeg; 
  this.ClearAfterError = ClearAfterError;
  this.PlaceHere = PlaceHere;
  
  this.SetSpanVisible = SetSpanVisible;
  this.SetSpanInvisible = SetSpanInvisible;

  this.NumOfLinesAtText = 0; // num of lines at exercise  
  
  this.CurLine = 0;
  this.ShowElement = ShowElement;
  
  this.BWordsMode = false;
  this.CurNumOfWord = 0; // actually number of active span
  this.NumOfWords = 0;
  this.EnteredText = "";
  
  this.WordSymbolIndex = 0;
  
  this.bUseFragmentLength = false;
  
  this.SetUseFragmentLength = function(bVal)
  {
    this.bUseFragmentLength = bVal;
  }
  
  this.AddToEnteredText = function(ch)
  {
    this.EnteredText += ch;
  }
  
this.SetWordsMode = function(bMode)
{
  this.BWordsMode = bMode;
  this.CurNumOfWord = 0;
}

this.GetCurNumOfWord = function()
{
  return this.CurNumOfWord;
}

this.NextWord = function()
{
  this.CurNumOfWord++;
}

this.ClearCurWord = function()
{
  this.CurWord = "";
  this.WordSymbolIndex = 0;
}
this.GetCurWord = function()
{
  return this.CurWord;
}

this.AddToCurWord = function(strWord)
{
  var obj = document.getElementById(this.GetLetterSpanId(this.CurNumOfWord));
  var s = obj.innerHTML;
  s += strWord;
  this.CurWord = s;
  obj.innerHTML = s;
  this.WordSymbolIndex++;
}

this.GetWordSymbolIndex = function()
{
  return this.WordSymbolIndex;
}

this.GetWordNum = function(num)
{
  var obj = document.getElementById(this.GetLetterSpanId(num));
  if(obj == undefined)
  {
    alert("Can't get word number " + num);
    return;
  }
  
  return document.getElementById(this.GetLetterSpanId(num)).innerHTML;
}

function PlaceHere()
{
  return BeginSpan('id', this.Id) + EndSpan();
}   
  
function GetEmptyField()
{
  return this.EmptyField;
} 

function GetLetterSpanId(i)
{
  return this.Id + this.LetterId + String(i);
}

function LetterSpan(i, letter, bGreen, bVisible)
{
  /*var style1 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size: 1px; margin: 0 1px";
  var style2;                  
  if(bVisible == true)
    style2 = "padding: 0px 2px; margin: 0;" +
                      " font-weight: bold; height: 100%; font-family: Courier New; font-size: 12pt";
  else
    style2 = "visibility: hidden; padding: 0px 2px; margin: 0;" +
                      " font-weight: bold; height: 100%; font-family: Courier New; font-size: 12pt";*/

  var letter_ = letter;
  
  if(letter_ == " ")  letter_ = "&nbsp;";

  if(bVisible)
    return "<b class='Input2Class1'></b><div id='" + this.GetLetterSpanId(i) + "' class='Input2Class2Visible'>" + letter_ +
		  "</div><b class='Input2Class1'></b>"; 
	else
	  return "<b class='Input2Class1'></b><div id='" + this.GetLetterSpanId(i) + "' class='Input2Class2Invisible'>" + letter_ +
		  "</div><b class='Input2Class1'></b>"; 
}

this.WordModeWordSpan = function(word)
{
  /*var style1 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size: 1px; margin: 0 1px";
  var style2 = "padding: 0px 2px; margin: 0;" +
                      " font-weight: bold; height: 100%; font-family: Courier New; font-size: 12pt";*/// + " text-align: center; vertical-align: center;"
  
  if(word == " ")  word = "&nbsp;";

  return "<b class='Input2Class1'></b><div class='Input2Class2Visible'>" + word_ +
		  "</div><b style='Input2Class1'></b>"; 
}

this.WordModeLetterSpan = function(i, letter)
{
  var letter_ = letter;
  var style = "visibility: hidden;";
  if(letter_ == " ")  letter_ = "&nbsp;";  
  return "<span id='" + this.GetLetterSpanId(i) + "' style='" + style + "'>" + letter_ + "</span>";
}

function SetText(text)
{
  var obj = document.getElementById(this.Id);
  
  this.Text = text;
  
  this.WordSymbolIndex = 0; // for word mode
  
  for(i = 0; i < this.Text.length; i++)
  {
    this.InnerHTML += this.LetterSpan(i, this.Text.charAt(i));
  }
  
}

function GetText()
{
  return this.Text;
}

function SetError(errorCh)
{
  this.AppendLastChar(errorCh);
  this.SetCharColor(this.Index, '#AB0000');
}

this.SetBkRed = function(index)
{
  document.getElementById(this.GetLetterSpanId(index)).style.background = "#AB0000";
}
this.ClearBk = function(index)
{
  document.getElementById(this.GetLetterSpanId(index)).style.background = "";
}

this.TrimLastChar = function()
{
  this.ClearBk(this.Index);
  document.getElementById(this.GetLetterSpanId(this.Index)).style.visibility = "hidden";  
  this.SetCharColor(this.Index, ''); // clear after error //  
  this.SetChar(this.Index, this.Text.charAt(this.Index));
}

function AppendLastChar(ch)
{
  var ch_ = ch;
  if(ch_ == '\r') ch_ = "&#182;";
  if(ch_ == ' ')
  {
    document.getElementById(this.GetLetterSpanId(this.Index)).innerHTML = '&nbsp;';
    document.getElementById(this.GetLetterSpanId(this.Index)).style.visibility = "visible";
    this.SetBkRed(this.Index);
  }
  else
  {
    document.getElementById(this.GetLetterSpanId(this.Index)).innerHTML = ch_;
    document.getElementById(this.GetLetterSpanId(this.Index)).style.visibility = "visible";
  }
}

function SetLastChar(lastChar)
{
  this.LastChar = lastChar;
}

function SetBkRedCurrentChar()
{
  this.BkCurrentCharColor = 'red';
}

function SetBkGreenCurrentChar()
{
  this.BkCurrentCharColor = 'green';
}

function ShowElement(i, bShow)
{
  var obj = document.getElementById(this.GetStringId(i));
  if(bShow)  obj.style.display = "";
  else  obj.style.display = "none";
}

function SetCaret(index, bIsBackward)
{   
  this.Index = index;
}

function GoToBeg()
{
  this.SetCaret(0, true);
/*Next actions are for word mode*/
  this.CurLine = 0;
  this.CurNumOfWord = 0;
/**/
  this.Render();
}

function GetIndex()
{
  return this.Index;
}

function SetSpanVisible(index)
{ 
  document.getElementById(this.GetLetterSpanId(index)).style.visibility = "visible";
}

function SetSpanInvisible(index)
{
  document.getElementById(this.GetLetterSpanId(index)).style.visibility = "hidden";
}

function StepForward()
{
  
  var newIndex;
  
  if(this.bUseFragmentLength == false)
    newIndex = this.GetIndex() + 1;
  else
    newIndex = this.GetIndex() + TextFragmentsData.GetLengthAddFrag(CaretPos);
/*Very bad, context depending...*/    

  
  if(newIndex == 0)  
  {
    this.CurLine = 0;
    if(bIsBackward == true)  this.Render();
  }
  else
  if(newIndex > 0)
  {
    if(this.BWordsMode == false)
    {
      if(this.Text.charAt(newIndex - 1) == '\n')  this.CurLine++;
    }
    else
    {
      if(IsWordLastAtString(this.Text, this.GetIndex()))  this.CurLine++;
    }
  }
  
  if(this.CurLine != this.PrevLine  &&  this.CurLine > this.NumOfLinesBeforeCurrentLine  &&  
      this.CurLine < this.NumOfLinesAtText - 1)  
  {
    this.ShowElement(this.CurLine - this.NumOfLinesBeforeCurrentLine - 1, false);
    this.ShowElement(this.CurLine + 1, true);
  }   
    
  for(var i = this.Index; i < newIndex; i++)  
    this.SetSpanVisible(i);
  
  this.Index = newIndex;
}

function SetChar(i, ch)
{
  if(ch != " ") document.getElementById(this.GetLetterSpanId(i)).innerHTML = ch;
  else  document.getElementById(this.GetLetterSpanId(i)).innerHTML = "&nbsp;";
}

function ClearAfterError()
{
 this.ClearBk(this.Index); // if error char is " "
 this.SetCharColor(this.Index, ''); // clear after error //  
 this.SetChar(this.Index, this.Text.charAt(this.Index));
}

function GoToBegOfLine()
{
  var i = this.GetIndex();
  if(this.Text.charAt(i) == '\n')
    i--;
  
  this.ClearAfterError();
  
  for(; i > 0  &&  this.Text.charAt(i) != '\n'; i--)
  {
    this.SetSpanInvisible(i);   
  }
  
//  alert("GoToBegOfLine():i=" + i);
  
  if(this.Text.charAt(i) == '\n')  i++;
  else  this.SetSpanInvisible(i);
  
  this.SetCaret(i);
}

function SetCharBkColor(i, color)
{
  document.getElementById(this.GetLetterSpanId(i)).style.background = color;
}

function SetCharColor(i, color)
{
  document.getElementById(this.GetLetterSpanId(i)).style.color = color;
}

function GetCurLine(curPos)
{
  return this.CurLine;
}

function GetStringId(numOfString)
{
  return "InputField2String" + String(numOfString);
}

function Render() // new (without color/bkcolor of text )
{
  var obj = document.getElementById(this.Id);
  
  var exStr = "";
  
  var curLine = 0;
    
  var text = this.Text;
  if(this.LastChar != -1)  text = this.Text.substring(0, this.LastChar);
  
  var curLine = this.GetCurLine();
 
// теперь надо решить с какой строки начинать отображение 
  var beginningLine = curLine - this.NumOfLinesBeforeCurrentLine;
  if(beginningLine < 0) beginningLine = 0; 
 
  var i = 0;
  for(var k = 0; k < beginningLine; i++)
  {
    if(text.charAt(i) == '\n') k++;
  }
  
  endLine = beginningLine + this.NumOfLines;
  
  if(this.BWordsMode == false)
  {
    for(i = 0, k = 0; i < text.length;)
    {
      
      if(k >= beginningLine && k < endLine)  exStr += "<table id='" + this.GetStringId(k) + "' cellspacing='0' cellpadding='0' border='0'><tr>";
      else  exStr += "<table id='" + this.GetStringId(k) + "' cellspacing='0' cellpadding='0' border='0' style='display: none'><tr>";

      for(var j = 0; j < this.StrLength && i < text.length  &&  text.charAt(i) != '\n'; j++, i++)
      {
        exStr += "<td>";
        exStr += this.LetterSpan(i, text.charAt(i));
        exStr += "</td>";
      }
    
      if(text.charAt(i) == '\n')
      {
        exStr += "<td>";
        exStr += this.LetterSpan(i, '&nbsp;');   
        exStr += "</td>";
        k++;
        i++;
      }    
      exStr += "</tr></table>";
    }
  }
  else
  {
    var NumOfWord = 0;
    for(i = 0, k = 0; i < text.length; i++)
    {
      if(k >= beginningLine && k < endLine)  exStr += "<table cellspacing='0' cellpadding='0' border='0'><tr id='" + this.GetStringId(k) + "'>";
      else  exStr += "<table cellspacing='0' cellpadding='0' border='0'><tr id='" + this.GetStringId(k) + "' style='display: none'>";
          
      for(var j = 0; j < this.StrLength && i < text.length; j++, i++)
      {
        if(text.charAt(i) == ' ' || text.charAt(i) == '\n')
        {      
          exStr += "<td>";
          var bGreen = false;
          if(this.Index > NumOfWord)  bGreen = true;    
          exStr += this.LetterSpan(NumOfWord, "", bGreen, true); // we should fill this words later
          exStr += "</td>";
          NumOfWord++;
        }
        if(text.charAt(i) == '\n')  break;
      }
    
      if(text.charAt(i) == '\n')
      {
        exStr += "<td>";
        exStr += /*this.LetterSpan(i, '&nbsp;&nbsp;')*/EmptySpan('&nbsp;');
        exStr += "</td>";   
        k++;
        //i++;
      }    
      exStr += "</tr></table>";
    }    
        
  }
  
  for(; k < this.NumOfLines; k++) // fill empty strings
  {
    exStr += "<table cellspacing='0' cellpadding='0' border='0'><tr><td>&nbsp;</td></tr></table>";
  }  
  
  this.NumOfLinesAtText = k; // Init NumOfLines
  //exStr += "</table>";
  obj.innerHTML = this.EmptyField.HTMLPreInputField(this.Id) + exStr + this.EmptyField.HTMLPostInputField();  
}
  
} 
///////

function TRoundField(id, color, bkColor)
{
  this.Value = "";
  this.Id = id;
  this.BkColor = bkColor;
  this.Color = color;
  
  this.Style1 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size:1px; margin: 0 1px; background-color: " + this.BkColor;
  this.Style2 = "padding: 0 2px; margin: 0; background-color: " + this.BkColor + 
                      "; font-weight: bold;  text-family: Arial; text-size: 11px; text-align: left; color: " + this.Color + "; vertical-align: top;";
  
  this.StrPre = "";
  this.StrPost = "";
  
  this.SetStrPre = function(val)
  {
    this.StrPre = "<td>" + "<span style='font-family: Arial; font-size: 8pt'>" + val + "</span>" + "</td>";
  }
  
  this.SetStrPost = function(val)
  {
    this.StrPost = "<td>" + "<span style='font-family: Arial; font-size: 8pt'>" + val + "</span>" + "</td>";
  }
  
  this.Place = function()
  {
    var res = "<table><tr>" + this.StrPre +
		  "<td>" +
		  "<b style='" + this.Style1 + "'></b>" +
		  "<div id='" + this.Id + "' style='" + this.Style2 + "'>" +
		  "</div>" +
		  "<b style='" + this.Style1 + "'></b>" +
		  "</td>" + this.StrPost + "</tr></table>";
//    alert(res);
    return res;
  }
  
  this.Update = function(val)
  {
    document.getElementById(this.Id).innerHTML = val;
  }
  
  this.Show = function()
  {
    var obj = document.getElementById(this.Id);
    obj.style.visibility = "visible"; 
  }
  
  this.Hide = function()
  {
    var obj = document.getElementById(this.Id);
    obj.style.visibility = "hidden";  
  }
}


function TProgressRoundField(id, color, bkColor1, bkColor2)
{
  this.Value = "";
  this.Id = id;
  this.IdEntered = this.Id + "Entered";
  this.IdTotal = this.Id + "Total";
  this.BkColor1 = bkColor1;
  this.BkColor2 = bkColor2;
  this.Color = color;
  
  this.Style1 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size:1px; margin: 0 1px; background-color: " + this.BkColor1;
  this.Style2 = "padding: 0 2px; margin: 0; background-color: " + this.BkColor1 + 
                      "; font-weight: bold;  text-family: Arial; text-size: 11px; text-align: left; color: " + this.Color + "; vertical-align: top;";
  this.Style3 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size:1px; margin: 0 1px; background-color: " + this.BkColor2;
  this.Style4 = "padding: 0 2px; margin: 0; background-color: " + this.BkColor2 + 
                      "; font-weight: bold;  text-family: Arial; text-size: 11px; text-align: left; color: " + this.Color + "; vertical-align: top;";                      
  
  this.StrPre = "";
  this.StrPost = "";
  
  this.Place = function()
  {
    var res = "<center><table><tr>" +
		  "<td>" +
		  "<b style='" + this.Style1 + "'></b>" +
		  "<div id='" + this.IdEntered + "' style='" + this.Style2 + "'>" +
		  "</div>" +
		  "<b style='" + this.Style1 + "'></b>" +
		  "</span></td>" +
		  "<td><span style='font-family: Arial; font-size: 10pt'><b>/</b></span></td>" + 
      "<td>" +
		  "<b style='" + this.Style3 + "'></b>" +
		  "<div id='" + this.IdTotal + "' style='" + this.Style4 + "'>" +
		  "</div>" +
		  "<b style='" + this.Style3 + "'></b>" +
		  "</td>" +
      "</tr></table></center>";
//    alert(res);
    return res;
  }
  
  this.Update = function(valEntered, valTotal)
  {
    document.getElementById(this.IdEntered).innerHTML = valEntered;
    document.getElementById(this.IdTotal).innerHTML = valTotal;
  }
  
  this.Show = function()
  {
    var obj = document.getElementById(this.Id);
    obj.style.visibility = "visible"; 
  }
  
  this.Hide = function()
  {
    var obj = document.getElementById(this.Id);
    obj.style.visibility = "hidden";  
  }
}


function TTimeRoundField(id, color, bkColor)
{
  this.Value = "";
  this.Id = id;
  this.IdMin = this.Id + "Min";
  this.IdSec = this.Id + "Sec";
  this.BkColor = bkColor;
  this.Color = color;
  
  this.Style1 = "display: block; height: 1px; overflow: hidden; " + 
                    "font-size:1px; margin: 0 1px; background-color: " + this.BkColor;
  this.Style2 = "padding: 0 2px; margin: 0; background-color: " + this.BkColor + 
                      "; font-weight: bold; text-family: Arial; text-size: 11px; text-align: left; color: " + this.Color + "; vertical-align: top;";
  
  this.StrPre = "";
  this.StrPost = "";
  
  this.Place = Place;
  this.Update = Update;
  
  
  function Place()
  {
    var res = "<table><tr>" +
		  "<td>" +
		  "<b style='" + this.Style1 + "'></b>" +
		  "<div id='" + this.IdMin + "' style='" + this.Style2 + "'>" +
		  "</div>" +
		  "<b style='" + this.Style1 + "'></b>" +
		  "</span></td>" +
		  "<td><span style='font-family: Arial; font-size: 8pt'>&nbsp;минут</span></td>" + 
      "<td>" +
		  "<b style='" + this.Style1 + "'></b>" +
		  "<div id='" + this.IdSec + "' style='" + this.Style2 + "'>" +
		  "</div>" +
		  "<b style='" + this.Style1 + "'></b>" +
		  "</td><td><span style='font-family: Arial; font-size: 8pt'>&nbsp;секунд</span></td>" +
      "</tr></table>";
    
    return res;
  }
  
  function Update(min, sec)
  {
    document.getElementById(this.IdMin).innerHTML = min;
    document.getElementById(this.IdSec).innerHTML = sec;
  }
}

TTextFragmentsData = function()
{
  this.BlockingPositions = new Array();
  this.LengthAddFrag = new Array();
  this.SetBlockingWords = function(text)
  {
    var nOfWord = 0;
    this.BlockingPositions.splice(0, this.BlockingPositions.length); // clear an array
    
    for(var i = 0; i < text.length; i++)
    {
      switch(text.charAt(i))
      {
      case ' ': case '\n':
        for(var j = i-1; text.charAt(j) != ' '  &&  text.charAt(j) != '\n'  &&
                text.charAt(j) != '«'  &&  j > 0; j--);
        if(text.charAt(j) != '«')  this.BlockingPositions.push(nOfWord);
        nOfWord++;
        break;
      case '«':
        //this.BlockingPositions.push(nOfWord);
        break;
      default:
        ;
      }
    }
  }
  
  this.IsWordBlocked = function(nOfWord)
  {
    for(var i = 0; i < this.BlockingPositions.length; i++)
    {
      if(this.BlockingPositions[i] == nOfWord)  return true;
    }
    return false;
  }
  
  this.GetFragmentWordNum = function(src, num)
  {
    var wNum = -1;
    var sRet = "";
    for(var i = 0; i < src.length; i++)
    {
      if(src.charAt(i) == "«")  wNum++;
      if(wNum == num)
      {
        for(var k = i+1; k < src.length  &&  src.charAt(k) != '»'; k++)
          sRet += src.charAt(k);
        return sRet;
      }
    }
  }
  
  this.ReplaceFragText = function(text, text2)
  {
    var sRet = "";
    var wNum = -1;
    
    for(var i = 0; i < text.length; i++)
    {
      if(text.charAt(i) == '«')
      {
        wNum++;
        var frag = this.GetFragmentWordNum(text2, wNum);
        
        if(frag == undefined) continue;
        //alert("frag="+frag);
          
        this.LengthAddFrag.push(frag.length);
          
        sRet += frag;
        for(var j = i+1; j < text.length  &&  text.charAt(j) != '»'; j++);
        i = j;
        continue;
      }
      sRet += text.charAt(i);
      this.LengthAddFrag.push(1);
    }
    
    return sRet;
  }
  
  this.GetLengthAddFrag = function(num)
  {
    if(num < this.LengthAddFrag.length)
      return this.LengthAddFrag[num];
    else
      alert("[!] GetLengthAddFrag: " + "Array range error");
  }
}

var TextFragmentsData = new TTextFragmentsData();



function flashInputOnBlur()
{
  alert("focus");
}

function flashInputMouseIn()
{
  FlashInput.SetMouseIn(true);
}

function flashInputMouseOut()
{
  FlashInput.SetMouseIn(false);
}

/*function InputFields_DoFSCommand()
{
  alert("test");
}*/
    
function flashErrorLoaded()
{  
  if(isIe6()) return;             
  FlashError.IndErrorLimit( ErrorObj.GetNumOfMistakes() > ErrorObj.GetMaxNumOfMistakes() );
}    
    
function TFlashError()
{

  this.CallMethod = 0;
  if(document.flashError)  this.CallMethod = 1;
  this.Call = function(p, arg)
  {
    try
    {
      if(window.document["flashError"]) 
      {
        window.document["flashError"].SetVariable(p, arg);
      }
      else
      {
        document.flashError.SetVariable(p, arg);
      }
    }
    catch(e)
    {
// warn about problem to access to FlashInput   
    }
  }
  
  this.IndErrorLimit = function(bVal)
  {
    this.Call("listener.isError", String(bVal));
  }
  
  this.Place = function()
  {
    return "<OBJECT codebase='' ID='flashError'><PARAM NAME='allowScriptAccess' VALUE='sameDomain' />" +
	       "<PARAM NAME='movie' VALUE='/study/exercise/flash/onlineError.swf'>" +
	       "<EMBED WIDTH='911' HEIGHT='240' play='true' swliveconnect='true' name='flashError' src='/study/exercise/flash/onlineError.swf' quality='high' bgcolor='#FFFFFF' allowScriptAccess='sameDomain' TYPE='application/x-shockwave-flash'></EMBED></OBJECT>";  
  }
  
  this.ID = "flashErrorSpan";
  this.Reset = function()
  {
//    alert(this.ID);
     document.getElementById(this.ID).innerHTML = this.Place();
     //alert();
  }
}

function TFlashKeyboard()
{
  this.Call = function(p, arg)
  {
    try
    {
      if(this.CallMethod == 0) 
      {
        window.document["flashKeyboard"].SetVariable(p, arg);
      }
      else
      {
        document.flashKeyboard.SetVariable(p, arg);
      }
      
      //alert("Caller: "+this.Call.caller.toString());
      //alert("FlashKeyboard.SetVariable(" + p + ", " + arg + ")");
    }
    catch(e)
    {
// warn about problem to access to FlashInput  
    }
  }                
  
  this.bParamsLoaded = false;
  
  this.GetParamsLoaded = function()
  {
	return this.bParamsLoaded;  
  };
  
  this.SetParamsLoaded = function(b)
  {
	this.bParamsLoaded = b;  
  };
  
  this.SetLanguage = function(lang)
  {
    this.Call("listener.language", lang);    
  }
  this.SetLayout = function(layout)
  {
    this.Call("listener.layout", layout);    
  }
  this.SetLayoutChanged = function()
  {
    this.Call("listener.layoutChanged", "");
  }
  this.BBackspMode = false;
  this.BBackspSet = false;
  this.PreBkspKey = "";
  this.SetBkspMode = function(bVal)
  {
    this.BBackspMode = bVal;
    this.BBackspSet = !bVal;
    if(this.BBackspMode) 
      this.SetKey("backspace");
    else
      this.SetKey(this.PreBkspKey);
  }
  this.SetKey = function(key)
  {
    if(this.BBackspMode)
    {
      if(this.BBackspSet) return;
      this.Call("listener.key", "backspace");
      this.BBackspSet = true;
    }
    else
    {
      this.Call("listener.key", key);
      this.PreBkspKey = key;
    }      
  }
  this.SetParams = function(sXml)
  {
    this.Call("listener.params", sXml);
  }
  
  this.Activate = function()
  {
            var params = "layout=" + KeyboardInfo.Layout + "&next_key=" + String(KeyboardInfo.bNextKey) + "&show_chars=" + String(KeyboardInfo.bShowChars) + "&colored=" + String(KeyboardInfo.bColored);
            //alert("Activate: params="+params);
            this.SetParams(params);
  }
  
  this.InitLanguage = function()
  {
    try
    {
      this.SetLanguage(GetCurLanguage());
    }
    catch(e)
    {
// init failed because the component has not loaded    
    }
  }
}

function TFlashInput()
{
  this.ID = "FlashInput";
  this.CallMethod = 0;
  
  this.BMouseIn = false;
  this.BLowResolution = false;

  this.dx = 600;
  this.dy = 224;
  
  this.SetLowResolution = function(bVal)
  {
    if(bVal)
      this.Call("listener.resolution", "low");
    else
      this.Call("listener.resolution", "high");
  }
  
  this.SetHighXYRatio = function(bVal)
  {
    if(bVal)
      this.Call("listener.XYRatio", "high");
    else
      this.Call("listener.XYRatio", "low");
  }
  
  this.SetMouseIn = function(bVal)
  {
    this.BMouseIn = bVal;
  }
  
  this.OnFlashLoaded = function()
  {
    /*if(this.BLowResolution)
    {
      this.SetYToStretch(this.dy);
    }*/
  }
  
  this.BFlashLoaded = false;
  
  this.IsFlashLoaded = function()
  {
    return this.BFlashLoaded;
  }
  
  this.SetFlashLoaded = function(bVal)
  {
    this.BFlashLoaded = bVal;
  }
  
  if(document.flashKeyboard)  this.CallMethod = 1;
  
  this.PlaceHere = function()
  {
    if(window.navigator.appName == "Microsoft Internet Explorer")
    {        
//      alert("this.ID="+this.ID);
      return "<span id='ID"+this.ID+"'><OBJECT codebase='' ID='IDFlashInput'><PARAM NAME='allowScriptAccess' VALUE='sameDomain' />"+
        "<PARAM NAME='movie' VALUE='/study/exercise/flash/InputFields.swf'>" +
        "<EMBED WIDTH='" + this.dx + "' HEIGHT='" + this.dy + "' play='true' swliveconnect='true' name='FlashInput' src='/study/exercise/flash/InputFields.swf' quality='high' bgcolor='#FFFFFF' allowScriptAccess='sameDomain' TYPE='application/x-shockwave-flash'></EMBED>"+
        "</OBJECT></span>";
    }
    return "<span id='ID"+this.ID+"'></span>";
  }
  
  this.Reset = function()
  {
    document.getElementById("ID"+this.ID).innerHTML = "<OBJECT codebase='' ID='IDFlashInput'><PARAM NAME='allowScriptAccess' VALUE='sameDomain' /><PARAM NAME='movie' VALUE='/study/exercise/flash/InputFields.swf'><EMBED style='display: block' WIDTH='" + this.dx + "' HEIGHT='" + this.dy + "' play='true' swliveconnect='true' name='FlashInput' src='/study/exercise/flash/InputFields.swf' quality='high' bgcolor='#FFFFFF' allowScriptAccess='sameDomain' TYPE='application/x-shockwave-flash'></EMBED></OBJECT>";
  }
  
  this.Clear = function()
  {
    document.getElementById("ID"+this.ID).innerHTML = "";
  }
  
  this.Focus = function()
  {
    document.getElementById(this.ID).focus();    
  }
  
  this.Call = function(p, arg)
  {
    try
    {
      if(this.CallMethod == 0) 
      {
        window.document["FlashInput"].SetVariable(p, arg);
      }
      else
      {
        document.FlashInput.SetVariable(p, arg);
      }
    }
    catch(e)
    {
// warn about problem to access to FlashInput   
    }
  }
  
  this.SetText = function(text)
  {
//    alert("FlashInput.SetText");
    this.Call("listener.text", text);
  }
  this.SetText1 = function(text)
  {
    this.Call("listener.text1", text);
  }
  this.SetText2 = function(text)
  {
    this.Call("listener.text2", text); 
  }
  this.SetRegime = function(text)
  {
    this.Call("listener.regime", text); 
  }
  this.SetMode = function(text)
  {
    this.Call("listener.mode", text);
  }
  this.Error = function(ch)
  {
    this.Call("listener.error", ch);
  }
  this.FastError = function(ch)
  {
    this.Call("listener.fastError", ch);
  }
  this.StepForward = function()
  {
    this.Call("listener.action", "StepForward");
  }
  this.BegLine = function()
  {
    //this.Call("listener.action", "BeginExercise");
    this.Call("listener.action", "BeginLine");
  }
  this.BegExercise = function()
  {
    this.Call("listener.action", "BeginExercise");
  }
  this.SetModeNormal = function()
  {
     this.Call("listener.action", "SetModeNormal");
  }
  this.SetModeBottomFragments = function()
  {
     this.Call("listener.action", "SetModeBottomFragments");
  }
  this.ShowTypeHere = function()
  {
    this.Call("listener.action", "SetTypeHere");
  }  
  this.ResetContent = function() // use it for ie6 instead of Reset()
  {
    this.Call("listener.action", "Reset");
  }
  this.SetOperationSystem = function(opSystem)
  {
    this.Call("listener.operationSystem", opSystem);
  }
  this.BCanType = true;
  this.SetCanType = function(bCanType)
  {
//    alert("SetOperationSystem");
    if(bCanType == false) this.Call("listener.canType", "false");
    else
        this.Call("listener.canType", "true");
    
    this.BCanType = bCanType;
  }
  this.SetLayout = function(layout)
  {
      this.Call("listener.layout", layout);
  }
  this.GenerateOnloadMessage = function()
  {
      this.Call("listener.spec", "onloadMessage");
  }
  
  this.InitOS = function()
  {
    try
    {
      if(GetCurLanguage() == "English")
        this.SetOperationSystem("Windows"); // do not recode chars
      else                
      if(IsWindows())
      {
	     this.SetOperationSystem("Windows"); // do not recode chars
      }
      else
      {
  	   this.SetOperationSystem("Other (not Windows)"); // recode chars
      }
    }
    catch(e) { }
  }
}

function TPhotoInfo()
{
  this.IsHint = function(numEx, typEx, nErrors)
  {
    switch(typEx)
    {
      case 1: case 6:
      if(nErrors == 0)
        return true;
      return false;
      default:;
    }
    return false;
  }
  this.GetNumOfPic = function(numEx, typEx, nErrors)
  {
     switch(typEx)
    {
      case 1: case 6:
      if(nErrors == 0) 
      {
        return parseInt(numEx);    
      }
      else 
      {
        return parseInt(numEx) + 100;
      }
      default:
        return 201;
    } 
  }
}

var PhotoInfo = new TPhotoInfo();

function TResultImg(id, path, ext)
{
  this.ID = id;
  this.Path = path;
  this.Ext = ext;
  this.GetNumOfPic = function(numEx, typEx, nErrors)
  {
      return PhotoInfo.GetNumOfPic(numEx, typEx, nErrors);
  }
  
  this.Update = function(numEx, typEx, nErrors)
  {
    var sPic = this.Path +
      String( this.GetNumOfPic(numEx, typEx, nErrors) ) + this.Ext;
    document.getElementById(this.ID).src = sPic; 
  }
  
}


String.prototype.SelectLinks = function()
{
  var mess = this;
  
  var mess2 = mess.match(/(http:\/\/|https:\/\/|http:\/\/www\.|www\.)([0-9a-zA-Z]+(\.|\/)){1,16}[a-zA-Z]+(\/)?(\?[0-9a-zA-Z]+=[%0-9a-zA-Z]+)?\S+/g);
  
  if(mess2 != null)
  for(var i = 0; i < mess2.length; i++)
  {
    mess3 = mess.replace(mess2[i], "<a href='" + mess2[i] + "'>"+mess2[i]+"</a>");
    mess = mess3;      
  }
  return mess;
}


var IDRoundFieldErrorCaption = "IDRoundFieldErrorCaption";
var IDRoundFieldErrorLimitCaption = "IDRoundFieldErrorLimitCaption";
var IDRoundFieldSpeedCaption = "IDRoundFieldSpeedCaption";
var IDRoundFieldTimeCaption = "IDRoundFieldTimeCaption";
var IDRoundFieldProgressCaption = "IDRoundFieldProgressCaption";

var RoundFieldErrorCaption = new TRoundField(IDRoundFieldErrorCaption, "white", "#AB0000");
var RoundFieldErrorLimitCaption = new TRoundField(IDRoundFieldErrorLimitCaption, "white", "#939393");
var RoundFieldSpeedCaption = new TRoundField(IDRoundFieldSpeedCaption, "white", "#5FAC20");
var RoundFieldTimeCaption = new TTimeRoundField(IDRoundFieldTimeCaption, "white", "#3B91D3");
var RoundFieldProgressCaption = new TProgressRoundField(IDRoundFieldProgressCaption, "white", /*"#3B91D3"*/"#5FAC20", "#5FAC20");

function TError(iDErrorSpan)
{
  this.Header = "Ошибки";
  this.IDErrorSpan = iDErrorSpan;
  this.IDBody = "ErrorPnlBody";
  this.IDHeader = "ErrorPnlHeader";
  this.Width = "180";
  this.BRemoved = false;
  
  this.NumOfMistakes = 0;
  this.MaxNumOfMistakes = -1;
  this.MaxNumOfMistakesCaption = "Лимит";
  
  this.SetNumOfMistakes = SetNumOfMistakes;
  this.SetMaxNumOfMistakes = SetMaxNumOfMistakes;
  this.SetMaxNumOfMistakesCaption = SetMaxNumOfMistakesCaption;
  this.GetNumOfMistakes = GetNumOfMistakes;
  this.GetMaxNumOfMistakes = GetMaxNumOfMistakes;
  this.Generate = Generate;
  this.Update = Update;
  
  this.Hide = function()
  {
    document.getElementById(this.IDErrorSpan).style.display = "none";
  };
  this.Show = function()
  {
    document.getElementById(this.IDErrorSpan).style.display = "";
  };
  
  this.Remove = function()
  {
    document.getElementById(this.IDErrorSpan).innerHTML = "";
    this.BRemoved = true;
  };

function SetNumOfMistakes(num)
{
  this.NumOfMistakes = num;
}

function SetMaxNumOfMistakes(num)
{
  this.MaxNumOfMistakes = num;
}

function SetMaxNumOfMistakesCaption(text)
{
  this.MaxNumOfMistakesCaption = text;
}

function Update()
{
if(this.BRemoved) return;
//alert("test");
document.getElementById('NumOfMistakes').innerHTML = RoundFieldErrorCaption.Place();
//stop;

RoundFieldErrorCaption.Update(this.NumOfMistakes);

if(GetExType() != 32  &&
    GetExType() != 3  &&
    GetExType() != 4)
{
  RoundFieldErrorLimitCaption.SetStrPre(this.MaxNumOfMistakesCaption);
  document.getElementById('MaxNumOfMistakes').innerHTML = RoundFieldErrorLimitCaption.Place();
  RoundFieldErrorLimitCaption.Update(this.MaxNumOfMistakes);
}
else
{
//  RoundFieldErrorCaption.Hide();
  RoundFieldErrorLimitCaption.SetStrPre("&nbsp;");
  document.getElementById('MaxNumOfMistakes').innerHTML = "&nbsp;";
  //RoundFieldErrorLimitCaption.Update("&nbsp;");
}

}

function GetNumOfMistakes()
{
  return this.NumOfMistakes;
}

function GetMaxNumOfMistakes()
{
  return this.MaxNumOfMistakes;
}

this.SetFat = function(bFat)
{
  var sFat = "";
  if(bFat)
    var sFat = "Fat";
    
  for(var i = 1; i < 10; i++)
    document.getElementById("IDTdPanelError"+String(i)).className =
      "PanelError" + String(i) + sFat;    
};

function Generate()
{
  /*
  // old variant
  var EmptyPanel = new TEmptyPanel(this.IDHeader, this.IDBody, "Error", this.Width);
  return EmptyPanel.Generate();*/
  
	var res = "<table style='width: 150px; table-layout: fixed; margin-top: 5px;' border='0' cellpadding='0' cellspacing='0'>" +
		"<tr height='18'>" +
			"<td id='IDTdPanelError1' class='PanelError1' width='4'/>" +
			"<td id='IDTdPanelError2' width='54' class='PanelError2'/>" +
			"<td id='IDTdPanelError3' class='PanelError3'/>" +
			"<td id='IDTdPanelError4' class='PanelError4' width='4'/>" +
		"</tr><tr>" +
			"<td id='IDTdPanelError5' class='PanelError5'/>" +
			"<td colspan='2'>" +
				"<table width='100%'><tr><td width='54' align='center'>" +
				"<div id='NumOfMistakes'></div>" +
				"</td><td  align='center'>" +
				"<div id='MaxNumOfMistakes'>10</div>" +
			"</td></tr></table>" +
	  	"</td>" +
			"<td id='IDTdPanelError6' class='PanelError6'/>" +
		"</tr><tr height='4'>" +
			"<td id='IDTdPanelError7' class='PanelError7'/>" +
			"<td id='IDTdPanelError8' colspan='2' class='PanelError8'/>" +
			"<td id='IDTdPanelError9' class='PanelError9'/>" +
		"</tr></table>"/*"<div id='NumofMistakes'></div><div id='MaxNumOfMistakes'></div>"*/;
 
   return res;      
}

}


function TSpeed()
{
  this.Header = "Скорость";
  
  this.IDBody = "SpeedPnlBody";
  this.IDHeader = "SpeedPnlHeader";
  this.Width = "180";
  
  this.Speed = 0;
  this.MaxSpeed = 1000;
  this.MinSpeed = 20;
  
  this.SetSpeed = SetSpeed;

  this.GetSpeed = GetSpeed;
  
  this.Generate = Generate;
  this.Update = Update;

  this.SpeedSum = 0;
  this.SpeedSumCnt = 0;

  this.bInited = false;

function SetSpeed(speed)
{
//  return;
  if(speed > this.MinSpeed)
    this.Speed = speed;
  else
    this.Speed = 0;
  this.UpdateSpeedSum(this.Speed);
}

this.ClearSpeedSum = function()
{
  this.SpeedSum = 0;
  this.SpeedSumCnt = 0;
};

this.UpdateSpeedSum = function(val)
{
  this.SpeedSum += val;
  this.SpeedSumCnt++;
};

this.GetTotalSpeed = function()
{
  /*try
  {
    return Math.floor(this.SpeedSum / this.SpeedSumCnt);
  }
  catch(e)
  {
    alert("[e] Zero division while calculating total speed");
  }*/
  
  var speed = Math.floor(60000 * NumOfTypedChars / Time.GetTime());
  
  if(speed == Infinity) speed = SupposeSpeed_;
  
  return  speed;
};

function GetSpeed()
{
  return this.Speed;
}

function Update()
{

  var str = "";

  if(this.bInited == false)
  {
    str = BeginCenter();
    str += SpeedObj.Place();
    RoundFieldSpeedCaption.SetStrPost("знак&nbsp;/&nbsp;мин."); 
    str += RoundFieldSpeedCaption.Place(); 
    str += EndCenter();
    document.getElementById(this.IDBody).innerHTML = str;
    this.bInited = true;
  }
  
  RoundFieldSpeedCaption.Update(this.Speed);

  var speed_ = this.Speed;
  
  if(speed_ > 800)  speed_ = 800;
  SpeedObj.UpdatePercents(100.0 * speed_ / 800);
}

function Generate()
{
/*  var EmptyPanel = new TEmptyPanel(this.IDHeader, this.IDBody, "Speed", this.Width);
  return EmptyPanel.Generate();*/
  
var res = "<table style='width: 150px; table-layout: fixed; margin-top: 5px;' border='0' cellpadding='0' cellspacing='0'>" +
		"<tr height='18'>" +
			"<td background='/study/test/speed_0.png' width='4'/>" +
			"<td width='61' style='background: url(/study/test/speed_1.png) no-repeat'/>" +
			"<td style='background: url(/study/test/speed_2.png) repeat-x'/>" +
			"<td background='/study/test/speed_4.png' width='4'/>" +
		"</tr><tr>" +
			"<td background='/study/test/speed_5.png'/>" +
			"<td colspan='2'>" +
				"<span id='" + this.IDBody + "'></span>" +
	  	"</td>" +
			"<td background='/study/test/speed_9.png'/>" +
		"</tr><tr height='4'>" +
			"<td background='/study/test/speed_15.png'/>" +
			"<td colspan='2' background='/study/test/speed_17.png'/>" +
			"<td background='/study/test/speed_19.png'/>" +
		"</tr></table>";  
  return res;
}

}


function TProgress()
{
  this.Header = "Прогресс";
  
  this.IDBody = "ProgressPnlBody";
  this.IDHeader = "ProgressPnlHeader";
  this.Width = "180";
  
  this.Progress = 0;
  this.MaxProgress = 100;

  this.GetProgress = GetProgress;
  
  this.Generate = Generate;
  this.Update = Update;
  
  this.bProgressObjPlaced = false;
  this.CurLine = 0;
  this.NumOfLinesAtText = 0; 


this.SetAllPars = function(progress, curLine, numOfLinesAtText)
{
  this.Progress = progress;
  this.CurLine = curLine;
  this.NumOfLinesAtText = numOfLinesAtText; 
};

this.SetProgress = function(progress)
{
  this.Progress = progress;
};

this.SetLine = function(line)
{
  this.CurLine = line;
};

this.GetCurLine = function()
{
   return this.CurLine;
};

this.SetMaxLine = function(mline)
{
  this.NumOfLinesAtText = mline;
};

function GetProgress()
{
  return this.Progress;
}

function Update()
{
  var str = "";
  if(this.bProgressObjPlaced == false)
  {
    str = BeginDiv('style', 'line-height:70%') + BeginCenter();
    str += ProgressObj.Place();
    str += EndCenter();
    str += EndDiv();
    str += RoundFieldProgressCaption.Place();
    
    this.bProgressObjPlaced = true;
    document.getElementById(this.IDBody).innerHTML = str;
  }

  RoundFieldProgressCaption.Update(String(this.CurLine),String(this.NumOfLinesAtText));
  ProgressObj.UpdatePercents(100.0 * this.Progress / this.MaxProgress);  
    
  //document.getElementById(this.IDHeader).innerHTML = "<center>" + this.Header + "</center>";
  
}

function Generate()
{
var res = "<table style='width: 150px; table-layout: fixed; margin-top: 5px;' border='0' cellpadding='0' cellspacing='0'>" +
		"<tr height='18'>" +
			"<td background='/study/test/progress_0.png' width='4'/>" +
			"<td width='61' style='background: url(/study/test/progress_1.png) no-repeat'/>" +
			"<td style='background: url(/study/test/progress_2.png) repeat-x'/>" +
			"<td background='/study/test/progress_4.png' width='4'/>" +
		"</tr><tr>" +
			"<td background='/study/test/progress_5.png'/>" +
			"<td colspan='2'>" +
				"<span id='" + this.IDBody + "'></span>" +
	  	"</td>" +
			"<td background='/study/test/progress_9.png'/>" +
		"</tr><tr height='4'>" +
			"<td background='/study/test/progress_15.png'/>" +
			"<td colspan='2' background='/study/test/progress_17.png'/>" +
			"<td background='/study/test/progress_19.png'/>" +
		"</tr></table>";  

  return res;  
}
}


function TTime()
{
  this.Header = "Время";
  
  this.IDBody = "TimePnlBody";
  this.IDHeader = "TimePnlHeader";
  this.Width = "180";
  
  this.Time = 0;
  
  this.SetTime = SetTime;
  this.IncTypingTime = IncTypingTime;

  this.GetTime = GetTime;
  
  this.Generate = Generate;
  this.Update = Update;
  

function SetTime(time)
{
  this.Time = time;
}

function IncTypingTime(valMs)
{
  this.Time += valMs;
}

function GetTime()
{
  return this.Time;
}

function Update()
{

  var str = "";
  var style = 'color: white; background: #3b91d3; font-weight: bold';
  str = /*BeginDiv('style', 'line-height:100%') + */BeginCenter();
  //str += BeginSpan('style', style) + String(Math.floor(this.Time / 60000)) + EndSpan() + "&nbsp;мин.&nbsp;" + BeginSpan('style', style) + String(Math.floor(this.Time / 1000) % 60) + EndSpan() + "&nbsp;сек.";
  
  str += RoundFieldTimeCaption.Place();
  
  str += EndCenter();
  //str += EndDiv();
  
  document.getElementById(this.IDBody).innerHTML = str;
  RoundFieldTimeCaption.Update(Math.floor(this.Time / 60000), Math.floor(this.Time / 1000) % 60);
  
//  alert("str=" + document.getElementById(this.IDBody).innerHTML);  
//  document.getElementById(this.IDHeader).innerHTML = "<center>" + this.Header + "</center>";
  
}

function Generate()
{
/*  var EmptyPanel = new TEmptyPanel(this.IDHeader, this.IDBody, "Time", this.Width);
  return EmptyPanel.Generate();*/
  
var res = "<table style='width: 150px; table-layout: fixed; margin-top: 5px;' border='0' cellpadding='0' cellspacing='0'>" +
		"<tr height='18'>" +
			"<td background='/study/test/time_0.png' width='4'/>" +
			"<td width='47' style='background: url(/study/test/time_1.png) no-repeat'/>" +
			"<td style='background: url(/study/test/time_2.png) repeat-x'/>" +
			"<td background='/study/test/time_4.png' width='4'/>" +
		"</tr><tr>" +
			"<td background='/study/test/time_5.png'/>" +
			"<td colspan='2'>" +
				"<span id='" + this.IDBody + "'></span>" +
	  	"</td>" +
			"<td background='/study/test/time_9.png'/>" +
		"</tr><tr height='4'>" +
			"<td background='/study/test/time_15.png'/>" +
			"<td colspan='2' background='/study/test/time_17.png'/>" +
			"<td background='/study/test/time_19.png'/>" +
		"</tr></table>";  
  
  return res;   
}
}

function FingerZonesMouseOver()
{
  //alert("mouse over");
  //return;
  FingerZonesObj.BHighlighted = true;
  FingerZonesCloseOpenDirect();
}
function FingerZonesMouseOut()
{
  //alert("mouse out");
  //FingerZonesObj.SetHighlighted(true);
  //FingerZonesCloseOpenDirect();
}

function FingerZonesCloseOpenDirect()
{
  if(FingerZonesObj.IsClosed())
    FingerZonesObj.Close();
  else
    FingerZonesObj.Open();
}

function FingerZonesCloseOpen()
{
  //alert("FingerZonesObj.IsClosed()="+FingerZonesObj.IsClosed());
  if(FingerZonesObj.IsClosed())
    FingerZonesObj.Open();
  else
    FingerZonesObj.Close();
    
  FlashInput.ShowTypeHere();
}

function TFingerZones(id)
{
  this.Header = "Пальцы";
  
  this.IDBody = "FingerZonesPnlBody";
  this.IDHeader = "FingerZonesHeader";
  this.Width = "200";
  this.BClosed = false;
  this.BHighlighted = false;
  this.SetHighlighted = function(bVal)
  {
    this.BHighlighted = bVal;
  };
  this.Time = 0;
  
  this.ID = id;
  this.MapName = this.ID + "MapName";

this.GetID = function()
{
  return this.ID;
};

this.Generate = function()
{
/*  var EmptyPanel = new TEmptyPanel(this.IDHeader, this.IDBody, "FingerZones", this.Width);
  return EmptyPanel.Generate();*/
  
  var res = this.ExportAreas();
  res += "<span id='" + this.ID + "'>"; 
  res += "<img usemap='#" + this.MapName + "' src='/study/test/fingers.png' style='display: block'>";
  res += "</span>";
  
  return res;   
};

this.ExportAreas = function()
{
    var res = "";
    
    res += "<MAP name=\'" + this.MapName + "\'>";
    res += "<AREA shape='rect' coords=\'53, 0, 70, 14\' alt='Скрыть/раскрыть распальцовку' onclick='FingerZonesCloseOpen();'>";
    res += "</MAP>";
    
    return res;
};

this.Open = function()
{
  if(this.BHighlighted == false)
  {
//    alert("this.BHighlighted = false");
    document.getElementById(this.ID).innerHTML = "<img usemap='#" + this.MapName + "' src='/study/test/fingers.png' style='display: block'>";
  }
  else
  {
//    alert("this.BHighlighted = true");
    document.getElementById(this.ID).innerHTML = "<img usemap='#" + this.MapName + "' src='/study/test/fingers_highlighted.png' style='display: block'>";
  }    
  this.BClosed = false;
};

this.Close = function()
{
  if(this.BHighlighted == false)
    document.getElementById(this.ID).innerHTML = "<img usemap='#" + this.MapName + "' src='/study/test/fingers_closed.png' style='display: block'>";
  else
    document.getElementById(this.ID).innerHTML = "<img usemap='#" + this.MapName + "' src='/study/test/fingers_closed_highlighted.png' style='display: block'>";
  this.BClosed = true;
};

this.IsClosed = function()
{
  return this.BClosed;
};

this.Insert = function() // does not insert span
{
/*  var EmptyPanel = new TEmptyPanel(this.IDHeader, this.IDBody, "FingerZones", this.Width);
  return EmptyPanel.Generate();*/
  
  //var res = "<img src='/study/test/fingers.png'>";
  if(this.BClosed)
    var res = "<img usemap='#" + this.MapName + "' src='/study/test/fingers_closed.png' style='display: block'>";
  else
    var res = "<img usemap='#" + this.MapName + "' src='/study/test/fingers.png' style='display: block'>";
  
  return res;   
};

}


function TResultPanel()
{
  this.Header = "Результат";
  
  this.IDBody = "ResultPnlBody";
  this.IDHeader = "ResultPnlHeader";
  this.Width = "180";
  
  
  this.Generate = Generate;
  this.Update = Update;
  
  this.Rhythmicity = "";
  this.NumOfErrors = "";
  this.Speed = "";
  
  this.SetRhythmicity = SetRhythmicity;
  this.SetNumOfErrors = SetNumOfErrors;
  this.SetSpeed = SetSpeed;

function SetRhythmicity(val)
{
  this.Rhythmicity = val;
}

function SetNumOfErrors(val)
{
  this.NumOfErrors = val;
}

function SetSpeed(val)
{
  this.Speed = val;
}

function Update()
{
  var str = "";

  str = BeginDiv('class', 'fontDoubleLineheight');
  str += "&nbsp;&nbsp;&nbsp;&nbsp;Ритмичность&nbsp;" + this.Rhythmicity + Br();
  str += "&nbsp;&nbsp;&nbsp;&nbsp;Количество&nbsp;ошибок&nbsp;" + this.NumOfErrors + Br();
  str += "&nbsp;&nbsp;&nbsp;&nbsp;Скорость&nbsp;" + this.Speed;
  str += EndDiv();

  document.getElementById(this.IDBody).innerHTML = str;
    
  document.getElementById(this.IDHeader).innerHTML = "<center>" + this.Header + "</center>";
  
}

function Generate()
{
  var EmptyPanel = new TEmptyPanel(this.IDHeader, this.IDBody, "Speed", this.Width);
  return EmptyPanel.Generate();
  
}

}


function TEmptyPanel(idCaption, idBody, borderStyle, width)
{
  this.IdCaption = idCaption;
  this.IdBody = idBody;
  this.BorderStyle = borderStyle; 
  this.Width = width;
  
  this.Generate = Generate;

  

function Generate()
{
  var res = "";
  
  TableBuilder.ResetT();
  TableBuilder.SetTCellspacing("0");
  TableBuilder.SetTCellpadding("0");
  TableBuilder.SetTWidth(width);
  res += TableBuilder.BeginTable();


  TableBuilder.ResetT();
  res += TableBuilder.BeginTr();
  TableBuilder.SetTClass('ex' + borderStyle + 'CornerTopLeft');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  TableBuilder.SetTClass('ex' + borderStyle + 'BkTop');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();  
  TableBuilder.SetTClass('ex' + borderStyle + 'CornerTopRightCenter');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd(); 
  TableBuilder.ResetT();
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr();     

  TableBuilder.ResetT();
  res += TableBuilder.BeginTr();
  TableBuilder.SetTClass('ex' + borderStyle + 'BkLeft');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  TableBuilder.ResetT();
  res += TableBuilder.BeginTd();
  res += BeginFont("color", "#3b91d3");
  res += BeginDiv("id", this.IdCaption);
  res += EndFont();
  res += EndDiv();
  res += TableBuilder.EndTd();  
  TableBuilder.SetTClass('ex' + borderStyle + 'BkRightCenter');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd(); 
  TableBuilder.ResetT();
  res += TableBuilder.BeginTd();
  
  for(var i = 0; i < 20; i++)
    res += "&nbsp;";
  
  res += TableBuilder.EndTd();
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr();   
  

  TableBuilder.ResetT();
  res += TableBuilder.BeginTr();
  TableBuilder.SetTClass('ex' + this.BorderStyle + 'BkLeft');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  TableBuilder.ResetT();
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();  
  TableBuilder.SetTClass('ex' + this.BorderStyle + 'CornerBottomLeftCenter');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd(); 
  TableBuilder.SetTClass('ex' + this.BorderStyle + 'BkBottomTopVCenter');

  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  TableBuilder.SetTClass('ex' + this.BorderStyle + 'CornerTopRightVCenter');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr();  


  TableBuilder.ResetT();
  res += TableBuilder.BeginTr();
  TableBuilder.SetTClass('ex' + this.BorderStyle + 'BkLeft');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  TableBuilder.ResetT();
  
  TableBuilder.ResetT();
  TableBuilder.SetTColspan('3');
  res += TableBuilder.BeginTd();
  res += BeginDiv("id", this.IdBody);
  res += EndDiv();
  res += TableBuilder.EndTd();  
  
  TableBuilder.SetTClass('ex' + this.BorderStyle + 'BkRight');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr();  

  TableBuilder.ResetT();
  res += TableBuilder.BeginTr();
  TableBuilder.SetTClass('ex' + this.BorderStyle + 'CornerBottomLeft');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  TableBuilder.ResetT();
  TableBuilder.SetTClass('ex' + this.BorderStyle + 'BkBottom');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd(); 
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();  
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd(); 
  TableBuilder.SetTClass('ex' + this.BorderStyle + 'CornerBottomRight');
  res += TableBuilder.BeginTd();
  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr(); 


  res += TableBuilder.EndTable();

  return res;  
}
}
function ColorLittleFinger()
{
  return "#FDE3DE";
}

function ColorThirdFinger() 
{
  return "#FCF5E0";
}

function ColorMiddleFinger()
{
  return "#EAF4DD";
}

function ColorForeFingerLeft()
{
  return "#DDF0EC";
}

function ColorForeFingerRight()
{
  return "#DEE8F2";
}

function ColorThumb()
{
  return "#E6DFEE";
}

function ColorWhite()
{
  return "white";
}

function ColorSelected()
{
  return "#9ED7C9";
}

var CurLayout = "russianBasic";
var Keys = new Array();
Keys["russianBasic"] = new Array(); // 1st Lyout
Keys["russianTyping"] = new Array(); // 2nd Layout

Keys["russianBasic"][0] = new Array(); // 1st Lyout
Keys["russianBasic"][1] = new Array(); // 1st Lyout
Keys["russianBasic"][2] = new Array(); // 1st Lyout\n
Keys["russianBasic"][3] = new Array(); // 1st Lyout
Keys["russianBasic"][4] = new Array(); // 1st Lyout

Keys["russianTyping"][0] = new Array(); // 2nd Layout
Keys["russianTyping"][1] = new Array(); // 2nd Layout
Keys["russianTyping"][2] = new Array(); // 2nd Layout\n
Keys["russianTyping"][3]= new Array(); // 2nd Layout
Keys["russianTyping"][4]= new Array(); // 2nd Layout



Keys["russianBasic"][0][0] = new KeyStruct(new TypingKeys('ё', 'Ё', 'l'), "basicTop", ColorLittleFinger(), "~", "", "`", "Ё", 0, 1);
Keys["russianBasic"][0][1] = new KeyStruct(new TypingKeys('1', '!', 'l'), "basicTop", ColorLittleFinger(), "!", "", "1", "", 0, 1);
Keys["russianBasic"][0][2] = new KeyStruct(new TypingKeys('2', '\"', 'l'), "basicTop", ColorThirdFinger(), "@", "\"", "2", "", 0, 1);
Keys["russianBasic"][0][3] = new KeyStruct(new TypingKeys('3', '№', 'l'), "basicTop", ColorMiddleFinger(), "#", "№", "3", "", 0, 1);
Keys["russianBasic"][0][4] = new KeyStruct(new TypingKeys('4', ';', 'l'), "basicTop", ColorForeFingerLeft(), "$", ";", "4", "", 0, 1);
Keys["russianBasic"][0][5] = new KeyStruct(new TypingKeys('5', '%', 'l'), "basicTop", ColorForeFingerLeft(), "%", "", "5", "", 0,  1);
Keys["russianBasic"][0][6] = new KeyStruct(new TypingKeys('6', ':', 'l'), "basicTop", ColorForeFingerRight(), "^", ":", "6", "", 0, 1);
Keys["russianBasic"][0][7] = new KeyStruct(new TypingKeys('7', '?', 'r'), "basicTop", ColorForeFingerRight(),"&", "?", "7", "", 0, 1);
Keys["russianBasic"][0][8] = new KeyStruct(new TypingKeys('8', '*', 'r'), "basicTop", ColorMiddleFinger(), "*", "", "8", "", 0, 1);
Keys["russianBasic"][0][9] = new KeyStruct(new TypingKeys('9', '(', 'r'), "basicTop", ColorThirdFinger(), "(", "", "9", "", 0, 1);
Keys["russianBasic"][0][10] = new KeyStruct(new TypingKeys('0', ')', 'r'), "basicTop", ColorLittleFinger(),")", "", "0", "", 0, 1);
Keys["russianBasic"][0][11] = new KeyStruct(new TypingKeys('-', '_', 'r'), "basicTop", ColorLittleFinger(), "_", "", "-", "", 0, 1);
Keys["russianBasic"][0][12] = new KeyStruct(new TypingKeys('=', '+', 'r'), "basicTop", ColorLittleFinger(), "+", "", "=", "", 0, 1);
Keys["russianBasic"][0][13] = new KeyStruct(new TypingKeys('', '', ''), "backspace", ColorWhite(), "Backspace", "&nbsp;", "&nbsp;", "&nbsp;", 0, 2.5);
Keys["russianBasic"][0][14] = new KeyStruct(new TypingKeys('', '', ''), "tab", ColorWhite(), "Tab", "&nbsp;", "&nbsp;", "&nbsp;", 1, 1.5);
Keys["russianBasic"][0][15] = new KeyStruct(new TypingKeys('й', 'Й', 'l'), "basic", ColorLittleFinger(), "Q", "", "", "Й", 1, 1);
Keys["russianBasic"][0][16] = new KeyStruct(new TypingKeys('ц', 'Ц', 'l'), "basic", ColorThirdFinger(), "W", "", "", "Ц", 1, 1);
Keys["russianBasic"][0][17] = new KeyStruct(new TypingKeys('у', 'У', 'l'), "basic", ColorMiddleFinger(), "E", "", "", "У", 1, 1);
Keys["russianBasic"][0][18] = new KeyStruct(new TypingKeys('к', 'К', 'l'), "basic", ColorForeFingerLeft(), "R", "", "", "К", 1, 1);
Keys["russianBasic"][0][19] = new KeyStruct(new TypingKeys('е', 'Е', 'l'), "basic", ColorForeFingerLeft(), "T", "", "", "Е", 1, 1);
Keys["russianBasic"][0][20] = new KeyStruct(new TypingKeys('н', 'Н', 'r'), "basic", ColorForeFingerRight(), "Y", "", "", "Н", 1, 1);
Keys["russianBasic"][0][21] = new KeyStruct(new TypingKeys('г', 'Г', 'r'), "basic", ColorForeFingerRight(), "U", "", "", "Г", 1, 1);
Keys["russianBasic"][0][22] = new KeyStruct(new TypingKeys('ш', 'Ш', 'r'), "basic", ColorMiddleFinger(), "I", "", "", "Ш", 1, 1);
Keys["russianBasic"][0][23] = new KeyStruct(new TypingKeys('щ', 'Щ', 'r'), "basic", ColorThirdFinger(), "O", "", "", "Щ", 1, 1);
Keys["russianBasic"][0][24] = new KeyStruct(new TypingKeys('з', 'З', 'r'), "basic", ColorLittleFinger(), "P", "", "", "З", 1, 1);
Keys["russianBasic"][0][25] = new KeyStruct(new TypingKeys('х', 'Х', 'r'), "basic", ColorLittleFinger(), "{", "[", "", "Х", 1, 1);
Keys["russianBasic"][0][26] = new KeyStruct(new TypingKeys('ъ', 'Ъ', 'r'), "basic", ColorLittleFinger(), "}", "]", "", "Ъ", 1, 1);
Keys["russianBasic"][0][27] = new KeyStruct(new TypingKeys('\\', '/', 'r'), "basic", ColorLittleFinger(), "|", "", "", "\"", 1, 1.9);
Keys["russianBasic"][0][28] = new KeyStruct(new TypingKeys('', '', ''), "capsLock", ColorWhite(), "CapsLock", "&nbsp;", "&nbsp;", "&nbsp;", 2, 1.6);
Keys["russianBasic"][0][29] = new KeyStruct(new TypingKeys('ф', 'Ф', 'l'), "basic", ColorLittleFinger(), "A", "", "", "Ф", 2, 1);
Keys["russianBasic"][0][30] = new KeyStruct(new TypingKeys('ы', 'Ы', 'l'), "basic", ColorThirdFinger(), "S", "", "", "Ы", 2, 1);
Keys["russianBasic"][0][31] = new KeyStruct(new TypingKeys('в', 'В', 'l'), "basic", ColorMiddleFinger(), "D", "", "", "В", 2, 1);
Keys["russianBasic"][0][32] = new KeyStruct(new TypingKeys('а', 'А', 'l'), "basic", ColorForeFingerLeft(), "F", "", "", "А", 2, 1);
Keys["russianBasic"][0][33] = new KeyStruct(new TypingKeys('п', 'П', 'l'), "basic", ColorForeFingerLeft(), "G", "", "", "П", 2, 1);
Keys["russianBasic"][0][34] = new KeyStruct(new TypingKeys('р', 'Р', 'r'), "basic", ColorForeFingerRight(), "H", "", "", "Р", 2, 1);
Keys["russianBasic"][0][35] = new KeyStruct(new TypingKeys('о', 'О', 'r'), "basic", ColorForeFingerRight(), "J", "", "", "О", 2, 1);
Keys["russianBasic"][0][36] = new KeyStruct(new TypingKeys('л', 'Л', 'r'), "basic", ColorMiddleFinger(), "K", "", "", "Л", 2, 1);
Keys["russianBasic"][0][37] = new KeyStruct(new TypingKeys('д', 'Д', 'r'), "basic", ColorThirdFinger(), "L", "", "", "Д", 2, 1);
Keys["russianBasic"][0][38] = new KeyStruct(new TypingKeys('ж', 'Ж', 'r'), "basic", ColorLittleFinger(), ":", "", ";", "Ж", 2, 1);
Keys["russianBasic"][0][39] = new KeyStruct(new TypingKeys('э', 'Э', 'r'), "basic", ColorLittleFinger(), "\"", "", "\'", "Э", 2, 1);
Keys["russianBasic"][0][40] = new KeyStruct(new TypingKeys('\n', '\n', ''), "enter", ColorWhite(), "Enter", "&nbsp;", "&nbsp;", "&nbsp;", 2, 2.5);
Keys["russianBasic"][0][41] = new KeyStruct(new TypingKeys('', '', ''), "lshift", ColorWhite(), "Shift", "&nbsp;", "&nbsp;", "&nbsp;", 3, 2.5);
Keys["russianBasic"][0][42] = new KeyStruct(new TypingKeys('я', 'Я', 'l'), "basic", ColorLittleFinger(), "Z", "", "", "Я", 3, 1);
Keys["russianBasic"][0][43] = new KeyStruct(new TypingKeys('ч', 'Ч', 'l'), "basic", ColorThirdFinger(), "X", "", "", "Ч", 3, 1);
Keys["russianBasic"][0][44] = new KeyStruct(new TypingKeys('с', 'С', 'l'), "basic", ColorMiddleFinger(), "C", "", "", "C", 3, 1);
Keys["russianBasic"][0][45] = new KeyStruct(new TypingKeys('м', 'М', 'l'), "basic", ColorForeFingerLeft(), "V", "", "", "М", 3, 1);
Keys["russianBasic"][0][46] = new KeyStruct(new TypingKeys('и', 'И', 'l'), "basic", ColorForeFingerLeft(), "B", "", "", "И", 3, 1);
Keys["russianBasic"][0][47] = new KeyStruct(new TypingKeys('т', 'Т', 'r'), "basic", ColorForeFingerRight(), "N", "", "", "Т", 3, 1);
Keys["russianBasic"][0][48] = new KeyStruct(new TypingKeys('ь', 'Ь', 'r'), "basic", ColorForeFingerRight(), "M", "", "", "Ь", 3, 1);
Keys["russianBasic"][0][49] = new KeyStruct(new TypingKeys('б', 'Б', 'r'), "basic", ColorMiddleFinger(), "&lt;", "", ",", "Б", 3, 1);
Keys["russianBasic"][0][50] = new KeyStruct(new TypingKeys('ю', 'Ю', 'r'), "basic", ColorThirdFinger(), "&gt;", "", ".", "Ю", 3, 1);
Keys["russianBasic"][0][51] = new KeyStruct(new TypingKeys('.', ',', 'r'), "basic", ColorLittleFinger(), "?", "", "/", ".", 3, 1);
Keys["russianBasic"][0][52] = new KeyStruct(new TypingKeys('', '', ''), "rshift", ColorWhite(), "Shift", "&nbsp;", "&nbsp;", "&nbsp;", 3, 3);
Keys["russianBasic"][0][53] = new KeyStruct(new TypingKeys('', '', ''), "lCtrl", ColorWhite(), "Ctrl", "&nbsp;", "&nbsp;", "&nbsp;", 4, 1.2);
Keys["russianBasic"][0][54] = new KeyStruct(new TypingKeys('', '', ''), "start", ColorWhite(), "Start", "&nbsp;", "&nbsp;", "&nbsp;", 4, 1);
Keys["russianBasic"][0][55] = new KeyStruct(new TypingKeys('', '', ''), "alt", ColorWhite(), "Alt", "&nbsp;", "&nbsp;", "&nbsp;", 4, 1);
Keys["russianBasic"][0][56] = new KeyStruct(new TypingKeys(' ', '', ''), "space", ColorWhite(), "&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;", 4, 4.2);
Keys["russianBasic"][0][57] = new KeyStruct(new TypingKeys('', '', ''), "alt", ColorWhite(), "Alt", "&nbsp;", "&nbsp;", "&nbsp;", 4, 1);
Keys["russianBasic"][0][58] = new KeyStruct(new TypingKeys('', '', ''), "contextMenu", ColorWhite(), "Menu", "&nbsp;", "&nbsp;", "&nbsp;", 4, 1);
Keys["russianBasic"][0][59] = new KeyStruct(new TypingKeys('', '', ''), "start", ColorWhite(), "Win", "&nbsp;", "&nbsp;", "&nbsp;", 4, 1);
Keys["russianBasic"][0][60] = new KeyStruct(new TypingKeys('', '', ''), "rCtrl", ColorWhite(), "Ctrl", "&nbsp;", "&nbsp;", "&nbsp;", 4, 1.4);

Keys["russianBasic"][1][0] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 0, 0.5);
Keys["russianBasic"][1][1] = new KeyStruct(new TypingKeys('', '', ''), "insert", ColorWhite(), "Ins", "", "&nbsp;", "", 0, 1);
Keys["russianBasic"][1][2] = new KeyStruct(new TypingKeys('', '', ''), "home", ColorWhite(), "Ho", "", "&nbsp;", "", 0, 1);
Keys["russianBasic"][1][3] = new KeyStruct(new TypingKeys('', '', ''), "pageUp", ColorWhite(), "Pa", "", "Up", "", 0, 1);
Keys["russianBasic"][1][4] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 0, 0.6);
Keys["russianBasic"][1][5] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 1, 0.5);
Keys["russianBasic"][1][6] = new KeyStruct(new TypingKeys('', '', ''), "delete", ColorWhite(), "Del", "", "&nbsp;", "", 1, 1);
Keys["russianBasic"][1][7] = new KeyStruct(new TypingKeys('', '', ''), "end", ColorWhite(), "End", "", "&nbsp;", "", 1, 1);
Keys["russianBasic"][1][8] = new KeyStruct(new TypingKeys('', '', ''), "pageDown", ColorWhite(), "Pa", "", "Do", "", 1, 1);
Keys["russianBasic"][1][9] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 1, 0.6);
Keys["russianBasic"][1][10] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 2, 1);
Keys["russianBasic"][1][11] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 2, 1);
Keys["russianBasic"][1][12] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 2, 1);
Keys["russianBasic"][1][13] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 2, 1);

Keys["russianBasic"][2][0] = new KeyStruct(new TypingKeys('', '', ''), "numLock", ColorWhite(), "Num", "", "Lock", "", 0, 1);
Keys["russianBasic"][2][1] = new KeyStruct(new TypingKeys('/', '/', 'r'), "numpad", ColorWhite(), "/", "", "&nbsp;", "", 0, 1);
Keys["russianBasic"][2][2] = new KeyStruct(new TypingKeys('*', '*', 'r'), "numpad", ColorWhite(), "*", "", "&nbsp;", "", 0, 1);
Keys["russianBasic"][2][3] = new KeyStruct(new TypingKeys('7', '7', 'r'), "numpad", ColorForeFingerRight(), "7", "", "Home", "", 1, 1);
Keys["russianBasic"][2][4] = new KeyStruct(new TypingKeys('8', '8', 'r'), "numpad", ColorMiddleFinger(), "8", "", "&nbsp;", "", 1, 1);
Keys["russianBasic"][2][5] = new KeyStruct(new TypingKeys('9', '9', 'r'), "numLock", ColorThirdFinger(), "9", "", "PgUp", "", 1, 1);
Keys["russianBasic"][2][6] = new KeyStruct(new TypingKeys('4', '4', 'r'), "numpad", ColorForeFingerRight(), "4", "", "&nbsp;", "", 2, 1);
Keys["russianBasic"][2][7] = new KeyStruct(new TypingKeys('5', '5', 'r'), "numpad", ColorMiddleFinger(), "5", "", "&nbsp;", "", 2, 1);
Keys["russianBasic"][2][8] = new KeyStruct(new TypingKeys('6', '6', 'r'), "numpad", ColorThirdFinger(), "6", "", "&nbsp;", "", 2, 1);
Keys["russianBasic"][2][9] = new KeyStruct(new TypingKeys('1', '1', 'r'), "numpad", ColorForeFingerRight(), "1", "", "&nbsp;", "", 3, 1);
Keys["russianBasic"][2][10] = new KeyStruct(new TypingKeys('2', '2', 'r'), "numpad", ColorMiddleFinger(), "2", "", "&nbsp;", "", 3, 1);
Keys["russianBasic"][2][11] = new KeyStruct(new TypingKeys('3', '3', 'r'), "numpad", ColorThirdFinger(), "3", "", "&nbsp;", "", 3, 1);
Keys["russianBasic"][2][12] = new KeyStruct(new TypingKeys('0', '0', 'r'), "numpad", ColorThumb(), "0", "", "Ins", "", 4, 2);
Keys["russianBasic"][2][13] = new KeyStruct(new TypingKeys('.', '.', 'r'), "numpad", ColorWhite(), ".", "", "Del", "", 4, 1);

Keys["russianBasic"][3][0] = new KeyStruct(new TypingKeys('-', '-', 'r'), "numpadvertical", ColorWhite(), "-", "", "&nbsp;", "", 0, 1, 1); // also specify height there
Keys["russianBasic"][3][1] = new KeyStruct(new TypingKeys('+', '+', 'r'), "numpadvertical", ColorWhite(), "+", "", "&nbsp;", "", 1, 1, 2);
Keys["russianBasic"][3][2] = new KeyStruct(new TypingKeys('', '', 'r'), "numpadvertical", ColorWhite(), "enter", "", "&nbsp;", "", 2, 1, 2);

Keys["russianBasic"][4][0] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 0, 1.5);
Keys["russianBasic"][4][1] = new KeyStruct(new TypingKeys('', '', ''), "width/3", ColorWhite(), "Up", "", "&nbsp;", "", 0, 1);
Keys["russianBasic"][4][2] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 0, 1.5);
Keys["russianBasic"][4][3] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 1, 0.5);
Keys["russianBasic"][4][4] = new KeyStruct(new TypingKeys('', '', ''), "arrowpad", ColorWhite(), "Left", "", "&nbsp;", "", 1, 1);
Keys["russianBasic"][4][5] = new KeyStruct(new TypingKeys('', '', ''), "arrowpad", ColorWhite(), "Do", "", "&nbsp;", "", 1, 1);
Keys["russianBasic"][4][6] = new KeyStruct(new TypingKeys('', '', ''), "arrowpad", ColorWhite(), "Rig", "", "&nbsp;", "", 1, 1);
Keys["russianBasic"][4][7] = new KeyStruct(new TypingKeys('', '', ''), "empty", ColorWhite(), "&nbsp;", "", "&nbsp;", "", 1, 0.6);

var KeyHeight = 0;

function TypingKeys(notShiftedKey, shiftedKey, side)
{
  this.NotShiftedKey = notShiftedKey;
  this.ShiftedKey = shiftedKey; 
  this.Side = side;
}

function KeyStruct(typingKeys, type, color, leftTop, rightTop, leftBottom, rightBottom, row, width, height)
{
  if(type == undefined)
  {
    alert("Exception: {type = undefined} at KeyStruct(...)");
    return;
  }
  this.TypingKeys = typingKeys;
  this.Type = type;
  this.InitKeyPress = InitKeyPress;
  if(color == undefined) this.Color = "white"; else this.Color = color; 
  if(leftTop == undefined) this.LeftTop = ''; else   this.LeftTop = leftTop;
  if(rightTop == undefined) this.RightTop = ''; else   this.RightTop = rightTop;
  if(leftBottom == undefined)  this.LeftBottom = ''; else  this.LeftBottom = leftBottom;
  if(rightBottom == undefined)  this.RightBottom = ''; else  this.RightBottom = rightBottom;
  if(row == undefined) this.Row = 0; else this.Row = row; 
  if(width == undefined)  this.Width = 1;  else  this.Width = width;
  if(height == undefined) this.Height = 1;  else  this.Height = height; 
}

function AlertKeyboardTable()
{
  alert('Keys[0][0].Type=' + Keys["russianBasic"][3][0].Type + 
      '; Keys[0][0].leftTop=' + Keys["russianBasic"][3][0].LeftTop +
      '; Keys[0][0].rightTop=' + Keys["russianBasic"][3][0].RightTop +
      '; Keys[0][0].leftBottom=' + Keys["russianBasic"][3][0].LeftBottom +
      '; Keys[0][0].rightBottom=' + Keys["russianBasic"][3][0].RightBottom +
      '; Keys[0][0].width=' + Keys["russianBasic"][3][0].Width +
      '; Keys[0][0].heigth=' + Keys["russianBasic"][3][0].Height);
}

function CalcWeightedTotal(KeyArray, isCalcWidth, begIndex, endIndex)
{
  if(KeyArray == undefined  ||  begIndex == undefined)
  {
    alert("Exception: {type = undefined} at CalcWeightedTotal(...)");
    return;
  }
  
  var sum = 0.0;
  if(endIndex == undefined)
  {
    var curRow = KeyArray[begIndex].Row;
    if(isCalcWidth)  for(i = begIndex; i > 0 && curRow == KeyArray[i].Row; i--);
    else ;
    
    if(isCalcWidth)
    {
      if(curRow != KeyArray[i].Row) i++;
      for(; i < KeyArray.length && curRow == KeyArray[i].Row; i++)
      {
        sum += KeyArray[i].Width;
      }
    }
    else
      for(i = 0; i < KeyArray.length; i++)
      {
        sum += KeyArray[i].Height;
      }
  }
  else
  {
    for(var i = begIndex; i < endIndex; i++)
    {
      if(isCalcWidth)  sum += KeyArray[i].Width;
      else  sum += KeyArray[i].Height;
    }
  }
  return sum;
}

function CalcWeightedTotalWidth(KeyArray, begIndex, endIndex)
{
  return CalcWeightedTotal(KeyArray, true, begIndex, endIndex);
}

function CalcWeightedTotalHeight(KeyArray, begIndex, endIndex)
{
  return CalcWeightedTotal(KeyArray, false, begIndex, endIndex);
}

function GetPercentageWidth(KeyArray, index)
{
// grupp - not using here
  return Math.round( KeyArray[index].Width * 100.0 / CalcWeightedTotalWidth(KeyArray, index));
}

function GetAbsoluteWidth(KeyArray, index, srcWidth)
{
  return Math.round(srcWidth * KeyArray[index].Width / CalcWeightedTotalWidth(KeyArray, index));
}

function GetPercentageHeight(KeyArray, index, grupp)
{
  if(grupp == undefined)
  {
    alert("Exception: GetPercentageHeight(...): parameter grupp not specified");
    return;
  }
  switch(grupp)
  {
    case 0:
      return 20.0; 
      break;
    case 1:
      return 50.0;
    case 2:
      return 20.0;
    case 3:
      return Math.floor(KeyArray[index].Height * 100.0 / CalcWeightedTotalHeight(KeyArray, index) * 100) / 100.0;
    case 4:
      return 50.0;
    default:
      alert("Exception: wrong grupp argument passing to GetPercentageHeight(...)");
  }

  return Math.floor(KeyArray[index].Height * 100.0 / CalcWeightedTotalHeight(KeyArray, index) * 100) / 100.0;
}

function AddLetterTable(LeftTop, RightTop, LeftBottom, RightBottom, tableID, width, color, empty)
{
  var res = "";
  
  if(color == undefined) color="white";
  
  res += BeginCenter();
  if(RightTop == ""  &&  LeftBottom == ""  &&  RightBottom == "")
  {
    TableBuilder.ResetT();
    
//    alert(tableID);
    TableBuilder.SetTID(tableID);
    TableBuilder.SetTWidth(width);
    TableBuilder.SetTHeight("100%");
    if(empty == "empty")  TableBuilder.SetTClass("letterTableEmpty");  
    else  TableBuilder.SetTClass("letterTable");
    res += TableBuilder.BeginTable();
    TableBuilder.ResetT();
    res += TableBuilder.BeginTr();
    
    TableBuilder.SetTID(tableID + String(" 0"));
    res += "<div id=\'" + (tableID + String(" 1")) + "\'>";
    res += "<div id=\'" + (tableID + String(" 2")) + "\'>";
    res += "<div id=\'" + (tableID + String(" 3")) + "\'>";
    
    TableBuilder.SetTClass("letterTableTd");
    TableBuilder.SetTColor(color);
    
    res += TableBuilder.BeginTd();
    
    res += LeftTop;
      
    res += TableBuilder.EndTd();
    res += TableBuilder.EndTr();      
    res += TableBuilder.EndTable();
  }
  else
  {
    TableBuilder.ResetT();
  
    TableBuilder.SetTID(tableID);
    TableBuilder.SetTHeight("100%");
    TableBuilder.SetTWidth(width);
    if(empty == "empty")  TableBuilder.SetTClass("letterTableEmpty");  
    else  TableBuilder.SetTClass("letterTable");
    BeginCenter();
    res += TableBuilder.BeginTable();
    
    TableBuilder.ResetT();
    res += TableBuilder.BeginTr();
    TableBuilder.ResetT();
    TableBuilder.SetTClass("letterTableTd");
    TableBuilder.SetTColor(color);
    TableBuilder.SetTID(tableID + String(" 0"));
    res += TableBuilder.BeginTd();
    res += LeftTop;
    res += TableBuilder.EndTd();
    TableBuilder.SetTID(tableID + String(" 1"));
    res += TableBuilder.BeginTd();
    res += RightTop;
    res += TableBuilder.EndTd();
    res += TableBuilder.EndTr();
  
    TableBuilder.ResetT();
    res += TableBuilder.BeginTr();
    TableBuilder.SetTClass("letterTableTd");
    TableBuilder.SetTColor(color);
    TableBuilder.SetTID(tableID + String(" 2"));
    res += TableBuilder.BeginTd();
    res += LeftBottom;
    res += TableBuilder.EndTd();
    TableBuilder.SetTID(tableID + String(" 3"));
    res += TableBuilder.BeginTd();
    res += RightBottom;
    res += TableBuilder.EndTd();
    res += TableBuilder.EndTr();  
    
    res += TableBuilder.EndTable();
  }  
  res += EndCenter();
  return res;
}

function SetWidthWithRegardToEmptyCells(keys, srcWidth)
{
  var numOfCells = 0;
  var numOfNotEmptyCells = 0;
  var curRow = keys[0].Row;
  for(var i = 0; i < numOfCells  &&  keys[i].Row == curRow; i++)
  {
    if(keys[i].Type != "empty")  numOfNotEmptyCells++;
    numOfCells++;
  }
 
  
  width = Math.floor(numOfNotEmptyCells * srcWidth / numOfCells);
  
  return width;
}



function InitKeyHeight(val)
{
  if(KeyHeight == 0)
  {
    KeyHeight = val;
  }
}

function GenerateKeyboardSegment(SectionID, Keys, grupp, width, height)
{
  var res = "";
  var rowHeight = 0;
//  var res = "";
  
  var tdClass = "keyboardInner", tdClassEmpty = "keyboardInnerEmpty";

  var id;

  for(var i = 0, j = 0; i < Keys.length; j++)
  {
    curRow = Keys[i].Row;
    id = "key" + String(grupp) + String(j);
    TableBuilder.ResetT();
    TableBuilder.SetTID(id);
    
    var width_ = width;
/*    if(Keys[i].Type == "empty")  
    {
      width_ /= 3;
      width += 1;
    }*/
    
    TableBuilder.SetTWidth(width_);
    if(height == undefined) TableBuilder.SetTHeight(String(GetPercentageHeight(Keys, i, grupp)) + "%");
    else
      TableBuilder.SetTHeight(String(height * 5 * GetPercentageHeight(Keys, i, grupp) / 100.0));
    TableBuilder.SetTClass("keyboardTable");
    res += BeginCenter();
    res += TableBuilder.BeginTable();
    for(;i < Keys.length && curRow == Keys[i].Row;i++)
    {
      var id1 = "key" + String(grupp) + String(" ") + String(i);
      
      if(i < Keys.length - 1)
      {
        if(curRow != Keys[i + 1].Row)
        {
          TableBuilder.ResetT();
          TableBuilder.SetTID(id1);
          if(Keys[i].Type == "empty")  TableBuilder.SetTClass(tdClassEmpty);
          else  TableBuilder.SetTClass(tdClass);
          res += TableBuilder.BeginTd();
        }
        else
        {
          TableBuilder.ResetT();
          TableBuilder.SetTID(id1);
          if(Keys[i].Type == "empty")  TableBuilder.SetTClass(tdClassEmpty);
          else  TableBuilder.SetTClass(tdClass);
//          TableBuilder.SetTWidth(document.getElementById(id).width * GetPercentageWidth(Keys, i));
          TableBuilder.SetTWidth(GetAbsoluteWidth(Keys, i, width));
//          alert(TableBuilder.BeginTd());
          res += TableBuilder.BeginTd();
        }
      }
      else
      {
        TableBuilder.ResetT();
        TableBuilder.SetTID(id1);
        if(Keys[i].Type == "empty")  TableBuilder.SetTClass(tdClassEmpty);
        else  TableBuilder.SetTClass(tdClass);     
        res += TableBuilder.BeginTd();
      }
      
      if(Keys[i].Type == "empty")  
      {
        res += AddLetterTable(Keys[i].LeftTop, Keys[i].RightTop, Keys[i].LeftBottom, Keys[i].RightBottom, id1+" Inner",
                "100%", Keys[i].Color, "empty");
      }
      else
        res += AddLetterTable(Keys[i].LeftTop, Keys[i].RightTop, Keys[i].LeftBottom, Keys[i].RightBottom, id1+"Inner",
                "100%", Keys[i].Color);
            

      res += TableBuilder.EndTd();
    }
    res += TableBuilder.EndTr() + TableBuilder.EndTable();
    res += EndCenter();
  }
  
  return res;
} 

var ActiveKeys = new Array(0);

var PrevKey = new KeyDefinition(-1, 0, 0);

function KeyDefinition(grupp, num, shift)
{
  this.Grupp = grupp;
  this.Num = num;
  this.Shift = shift;
}


function SetActiveKey(keyDef, bActive)
{
  /*for(var i = 0; i < 4; i++)
  {
//    alert("key" + String(keyDef.Grupp) + " " + String(keyDef.Num) + String("Inner ") + String(i));
    var obj = document.getElementById("key" + String(keyDef.Grupp) + " " + String(keyDef.Num) + String("Inner ") + String(i));
    
// this (obj = NULL) if not from global space... Why?
//    alert('SetActiveKey1: obj = ' + obj);
    if(bActive)  
    {
      obj.style.backgroundColor = ColorSelected();
      ActiveKeys[ActiveKeys.length] = keyDef;
//      alert('SetActiveKey2');
    }
    else   obj.style.backgroundColor = Keys[CurLayout][keyDef.Grupp][keyDef.Num].Color;
//    alert('SetActiveKey3');
  }*/
//  alert("OK, length = " + ActiveKeys.length);
}

function SetActiveKeyLeft(keyDef, bActive)
{
    var obj = document.getElementById("key" + String(keyDef.Grupp) + " " + String(keyDef.Num) + String("Inner ") + String(0));
    var obj1 = document.getElementById("key" + String(keyDef.Grupp) + " " + String(keyDef.Num) + String("Inner ") + String(2));
    if(bActive)  
    {
      obj.style.backgroundColor = ColorSelected();
      obj1.style.backgroundColor = ColorSelected();
      ActiveKeys[ActiveKeys.length] = keyDef;
    }
    else   SetActiveKey(keyDef, false);  
}

function SetActiveKeyRight(keyDef, bActive)
{
    var obj = document.getElementById("key" + String(keyDef.Grupp) + " " + String(keyDef.Num) + String("Inner ") + String(1));
    var obj1 = document.getElementById("key" + String(keyDef.Grupp) + " " + String(keyDef.Num) + String("Inner ") + String(3));
    if(bActive)  
    { 
      obj.style.backgroundColor = ColorSelected();
      obj1.style.backgroundColor = ColorSelected();
      ActiveKeys[ActiveKeys.length] = keyDef;
    }
    else   SetActiveKey(keyDef, false);  
}

function ResetActiveKeys()
{
  if(ActiveKeys.length < 1) return;
//  alert('ResetActiveKeys1');
  for(var i = 0; i < ActiveKeys.length; i++)
    this.SetActiveKey(ActiveKeys[i], false);
//  alert('ResetActiveKeys2');
  ActiveKeys.splice(0, ActiveKeys.length);
//  alert("OK, after deleting length = " + ActiveKeys.length);
}

function FindKeyDefinition(symbol)
{
  var keyDef = new KeyDefinition();

  switch(symbol)
  {
  case '\n':
  
  for(var j = 0; j < Keys[CurLayout].length; j++)
  {
    for(var i = 0; i < Keys[CurLayout][j].length; i++)
    {
      if(Keys[CurLayout][j][i].Type == "enter")
      {
        keyDef.Grupp = j;
        keyDef.Num = i;      
        keyDef.Shift = '';  
        return keyDef;
      } 
    }
  }
    
  default:
  for(var j = 0; j < Keys[CurLayout].length; j++)
  {
    for(var i = 0; i < Keys[CurLayout][j].length; i++)
    {
      if(Keys[CurLayout][j][i].TypingKeys.NotShiftedKey == symbol  ||  Keys[CurLayout][j][i].TypingKeys.ShiftedKey == symbol)
      {
        keyDef.Grupp = j;
        keyDef.Num = i;
        if(Keys[CurLayout][j][i].TypingKeys.ShiftedKey == symbol)
        {
          if(Keys[CurLayout][j][i].TypingKeys.Side == 'r')  keyDef.Shift = 'l';
          else   keyDef.Shift = 'r';
          
          return keyDef;  
        }  
        keyDef.Shift = '';
        return keyDef;      
      }
    }
  }
  
  
  }
}

function FindKeyWithType(type)
{
  var keyDef = new KeyDefinition();
  for(var j = 0; j < Keys[CurLayout].length; j++)
  {
    for(var i = 0; i < Keys[CurLayout][j].length; i++)
    {
      if(Keys[CurLayout][j][i].Type == type)
      {
        keyDef.Grupp = j;
        keyDef.Num = i;
        if(Keys[CurLayout][j][i].TypingKeys.Side == 'r')  keyDef.Shift = 'l';
        else
        if(Keys[CurLayout][j][i].TypingKeys.Side == 'l')  keyDef.Shift = 'r';
        else
        keyDef.Shift = '';
        return keyDef;
      }
    }
  }  
}

function Type(e)
{
  var obj = document.getElementById("typer");
  this.TypeFunc(obj.value.charAt(obj.value.length - 1));
}

function OnBlur()
{
//  return false;
  if(keyboard.IsFocus)
  {
    document.getElementById("typer").focus();
    document.getElementById("typer").value = document.getElementById("typer").value;
  }
}


function OnFocus()
{
  //alert("OK");
  //return true;
}

//var i = 0;
//var userInput = new TInputField('input1', 60, 6, 3);

function TKeyboardInput(typeFunc)
{
  this.TypeFunc = typeFunc;
  this.PlaceKeyboard = placeKeyboard;
//  this.InitKeyInput = InitKeyInput;
  this.InitKeyboard = InitKeyboard;
  this.IsFocus = false;
  
//  this.SetFocus = SetFocus;
  this.ResetActiveKeys = ResetActiveKeys;
  this.SetActiveKey = SetActiveKey;
  
  this.SetTypeBlock = SetTypeBlock;
  this.GetTypeBlock = GetTypeBlock;
  this.TypeBlocked = false;
  this.SetInputFocus = SetInputFocus;
  
  this.Show = function(bShow)
  {
    var obj1 = document.getElementById('flashKeyboardSpan');
    var obj2 = document.getElementById('flashErrorSpan');
    if(bShow == false)
    {
      obj1.style.display="none";
      obj2.style.dislay="none";
      //obj1.style.visibility = "hidden";
      //obj2.style.visibility = "hidden";
    }
    else
    {
      obj1.style.display="";
      obj2.style.dislay="";    
    }
  }  
}

function SetTypeBlock(bBlock)
{
  this.TypeBlocked = bBlock;
}

function GetTypeBlock()
{
  return this.TypeBlocked; 
}

function SetInputFocus()
{
  if(document.getElementById('IDInputRadio') != undefined)
  {  
//    alert("SetInputFocus()");
    if(document.getElementById('IDInputRadio').style.display != "none")
    {
      if(document.getElementById("Exercise").style.display != "none")
      {
        if(navigator.appName == "Netscape")
        {
          setTimeout(function(){document.getElementById('IDInputRadio').focus();},10);
        }
        else  
        document.getElementById('IDInputRadio').focus();
      }
    }
  }
}

function OnFocus()
{
  
}

function InitKeyPress()
{

}

function flashKillFocus()
{
 //document.getElementById('IDInputRadio').focus();
 FlashInput.ShowTypeHere();
}

function flashKeyboardLoaded()
{
//   alert("point 2"); 

// alert("flashKeyboardLoaded");

//  if(IsKeyboard) SetCurKeyboardKey();
//  alert("KeyboardInfo.Layout="+KeyboardInfo.Layout);
  
//  alert("keyboard loaded");
  
  if(!isIe())
  {
    if(UserCaption.GetUserName() == "")
    {
       KeyboardInfo.Layout = "rus";
    } 
  }
  
  if(Context.IsError() || FlashKeyboard.GetParamsLoaded())
  {
	  if(!isIe()) 
	  {	
          FlashKeyboard.Activate();
          FlashKeyboard.InitLanguage();          
          FlashInput.SetLayout(KeyboardInfo.Layout);
          SetCurKeyboardKey();
	  }
	  Context.SetError(false);
  }
}

function inputFlashLoaded()
{

}

function placeKeyboard(width)
{
  
//  this.Width = width;
  
  var res = "";
  
  res += "<span id='flashKeyboardSpan'><OBJECT codebase='' ID='flashKeyboard'>" +
  "<PARAM NAME='allowScriptAccess' VALUE='sameDomain' />" +
	"<PARAM NAME='movie' VALUE='/study/exercise/flash/onlineKeyboard.swf'>" +
	"<EMBED WIDTH='911' HEIGHT='240' play='true' swliveconnect='true' name='flashKeyboard' src='/study/exercise/flash/onlineKeyboard.swf' quality='high' bgcolor='#FFFFFF' allowScriptAccess='sameDomain' TYPE='application/x-shockwave-flash'></EMBED>" +
  "</OBJECT></span>";
  res += "<span id='flashErrorSpan' style='display: none'>" +
  FlashError.Place() + "</span>";
  
/*  var classForInput = "";
  if(navigator.appName == "Microsoft Internet Explorer")
   classForInput = "hiderRadioExplorer";
  else
  if(navigator.appName == "Netscape")
   classForInput = "hiderRadioNetscape";
  else
   classForInput = "hiderRadioOtherBrowsers";
   
  res += "<input id='IDInputRadio' type='radio' class='" + classForInput + "' onKeyPress='Type(event);' onblur='SetInputFocus_();'>"; 
  
  InitKeyPress();*/

  return res;
}

function ResetKeyboard()
{
  document.getElementById('IDKeyboard').innerHTML = keyboard.PlaceKeyboard();
}

function InitKeyboard()
{

}

function flashTrace(mess)
{
  alert(mess);
}
var SpeedObj = new TProgressObject("SpeedObj", 13, 32, "#5FAC20", "#939393");
var ProgressObj = new TProgressObject("ProgressObj", 13, 32, "#5E5E5E", "#939393");
var ProgressObj2 = new TProgressObject2("ProgressObj", 13, 32, "#5E5E5E", "#939393");

function TProgressObject(id, height, numOfCells, activeColor, inactiveColor)
{
  this.NumOfCells = numOfCells;
  this.ActiveColor = activeColor;
  this.InactiveColor = inactiveColor;
  this.ID = id;
  //this.Width = width;
  this.Height = height;
  
  this.Place = Place;
  this.Update = Update;
  this.UpdatePercents = UpdatePercents;

  this.NumOfActiveBars = 0;

}

function Place()
{
  var res = "";
  
  res = "<table border='0' cellspacing='1' cellpadding='0' height='"+this.Height+/*"' width='"+this.Width+*/"'><tr>";
  
  for(var i = 0; i < this.NumOfCells - 1; i++)
  {
    res += "<td id='"+this.ID+String(i)+"' style='background: " + this.InactiveColor + 
          "' width='3px'></td>";
  }
  
  res += "</tr></table>";
  
  return res;
}

function Update(activeCells)
{
  var id;
  for(var i = 0; i < activeCells; i++)
  {
    id = this.ID + String(i);
    if(document.getElementById(id) != undefined)
      document.getElementById(id).style.backgroundColor = this.ActiveColor;      
  }
  
  for(; i < this.NumOfCells; i++)
  {
    id = this.ID + String(i);
    if(document.getElementById(id) != undefined)
      document.getElementById(id).style.backgroundColor = this.InactiveColor;
  }
}

function UpdatePercents(activeCellsPercents)
{
  if(activeCellsPercents > 100)  activeCellsPercents = 100; // auto correction

  var prevNumOfActiveBars = this.NumOfActiveBars;
  this.NumOfActiveBars = Math.floor((activeCellsPercents / 100.0) * this.NumOfCells);

  if(prevNumOfActiveBars == this.NumOfActiveBars)  return; // nothing changed

  var id;
  for(var i = prevNumOfActiveBars; i < this.NumOfActiveBars; i++)
  {
    id = this.ID + String(i);
    if(document.getElementById(id) != undefined)
      document.getElementById(this.ID + String(i)).style.backgroundColor = this.ActiveColor;      
  }
  
  for(i = prevNumOfActiveBars - 1; i >= this.NumOfActiveBars; i--)
  {
    id = this.ID + String(i);
    if(document.getElementById(id) != undefined)
      document.getElementById(this.ID + String(i)).style.backgroundColor = this.InactiveColor;
  }
}


function TProgressObject2(id, height, numOfCells, activeColor, inactiveColor)
{
  this.NumOfCells = numOfCells;
  this.ActiveColor = activeColor;
  this.InactiveColor = inactiveColor;
  this.ID = id;
  //this.Width = width;
  this.Height = height;

  this.NumOfActiveBars = 0;


this.GetDigitalViewID = function()
{
  return this.ID + "DigitalValue";
};

this.GetDigitalView = function()
{
  return document.getElementById(this.GetDigitalViewID());
};

this.Place = function()
{
  var res = "";
  
  res = "<table border='0' cellspacing='1' cellpadding='0' height='"+this.Height+/*"' width='"+this.Width+*/"'><tr>";
  
  for(var i = 0; i < this.NumOfCells - 1; i++)
  {
    res += "<td id='"+this.ID+String(i)+"' style='background: " + this.InactiveColor + 
          "' width='3px'></td>";
  }
  
  res += "</tr></table>";
  
  return res;
};

this.Update = function(activeCells)
{
  var id;
  for(var i = 0; i < activeCells; i++)
  {
    id = this.ID + String(i);
    if(document.getElementById(id) != undefined)
      document.getElementById(id).style.backgroundColor = this.ActiveColor;      
  }
  
  for(; i < this.NumOfCells; i++)
  {
    id = this.ID + String(i);
    if(document.getElementById(id) != undefined)
      document.getElementById(id).style.backgroundColor = this.InactiveColor;
  }
};

this.UpdatePercents = function(activeCellsPercents)
{
  if(activeCellsPercents > 100)  activeCellsPercents = 100; // auto correction

  var prevNumOfActiveBars = this.NumOfActiveBars;
  this.NumOfActiveBars = Math.floor((activeCellsPercents / 100.0) * this.NumOfCells);

  if(prevNumOfActiveBars == this.NumOfActiveBars)  return; // nothing changed

  var id;
  for(var i = prevNumOfActiveBars; i < this.NumOfActiveBars; i++)
  {
    id = this.ID + String(i);
    if(document.getElementById(id) != undefined)
      document.getElementById(this.ID + String(i)).style.backgroundColor = this.ActiveColor;      
  }
  
  for(i = prevNumOfActiveBars - 1; i >= this.NumOfActiveBars; i--)
  {
    id = this.ID + String(i);
    if(document.getElementById(id) != undefined)
      document.getElementById(this.ID + String(i)).style.backgroundColor = this.InactiveColor;
  }
};

}



function CloseRedoCloseMouseOver(objName)
{
  eval(objName).HighlightClose();
}

function CloseRedoRedoMouseOver(objName)
{
  eval(objName).HighlightRedo();
}

function CloseRedoMouseOut(objName)
{
  eval(objName).HighlightNone();
}

function TCloseRedoObj(objName, closeFunc, redoFunc, closeAlt, redoAlt)
{
  this.ObjName = objName;
  this.Place = Place;
  this.CloseAlt = closeAlt;
  this.RedoAlt = redoAlt;
  this.CloseFunc = closeFunc;
  this.RedoFunc = redoFunc;
  
  this.MapName = "mapCloseRedo" + this.ObjName;
  this.ID = this.ObjName;
  this.IDImg = this.ObjName + "Img";
  this.IDImg1 = "IDCloseRedoImg1" + this.ObjName;
  this.IDImg2 = "IDCloseRedoImg2" + this.ObjName;
  this.IDImg3 = "IDCloseRedoImg3" + this.ObjName;
  
  this.SrcImg = new Array('/study/exercise/img/Features/CloseRedo.PNG',
                          '/study/exercise/img/Features/CloseRedoCloseHigh.PNG',
                          '/study/exercise/img/Features/CloseRedoRedoHigh.PNG');
  
  this.ExportAreas = function()
  {
    var res = "";
    
    res += "<MAP name=\'" + this.MapName + "\'>";
    res += "<AREA shape=\'circle\' coords=\'74, 16, 15\' title=\'" + this.CloseAlt + "\' onmouseover='CloseRedoCloseMouseOver(\"" + this.ObjName + "\")' onmouseout='CloseRedoMouseOut(\"" + this.ObjName + "\")' onclick=\'" + this.CloseFunc + "();\'>";
    res += "<AREA shape=\'circle\' coords=\'28, 41, 30\' title=\'" + this.RedoAlt + "\' onmouseover='CloseRedoRedoMouseOver(\"" + this.ObjName + "\")' onmouseout='CloseRedoMouseOut(\"" + this.ObjName + "\")' onclick=\'" + this.RedoFunc + "();\'>";
    res += "</MAP>";
    
    return res;
  }
  
  this.Generate = function()
  {
    var res = "";
    
    res += this.ExportAreas();
    if(isOpera())
    {
        res += "<div style='display: none' id='" + this.ID + "'><div id='" + this.IDImg1 + "' style='position: relative; visibility: visible; z-index: 0; width: 150px;'><img align='right' style='cursor: pointer' usemap='#" + this.MapName + "' src='" + this.SrcImg[0] + "'></div>" +
                  "<div id='" + this.IDImg2 + "' style='position: relative; top: -71px; visibility: hidden; z-index: 1; width: 150px;'><img align='right' style='cursor: pointer' usemap='#" + this.MapName + "' src='" + this.SrcImg[1] + "'></div>" +
                  "<div id='" + this.IDImg3 + "' style='position: relative; top: -142px; visibility: hidden; z-index: 2; width: 150px;'><img align='right' style='cursor: pointer' usemap='#" + this.MapName + "' src='" + this.SrcImg[2] + "'></div></div>";
   }
   else
   {
      res += "<span style='cursor: pointer'>" + Img(this.SrcImg[0], this.MapName, this.IDImg) + "</span>"; 
   }
    
    return res;
  }
  
  this.Get = function()
  {
    return document.getElementById(this.ID);
  }  
  
  this.Show = function(bVal)
  {
    if(!isOpera()) return;
    if(bVal)
    {
      this.Get().style.display = "";
    }
    else
    {
      this.Get().style.display = "none";
    }
  }   
  
  this.ShowEl = function(ind)
  {    
    document.getElementById(this.IDImg).src = this.SrcImg[ind];            
  }
  
  this.ShowElOpera = function(ind)
  {
    var obj = new Array(document.getElementById(this.IDImg1),
                        document.getElementById(this.IDImg2),
                        document.getElementById(this.IDImg3));                                        
                        
    for(var i = 0; i < 3; i++)
      if(i == ind)
        obj[i].style.visibility = "visible";
      else
        obj[i].style.visibility = "hidden";
  }
  
  this.HighlightClose = function()
  {
    if(isOpera())
      this.ShowElOpera(1);
    else
      this.ShowEl(1);
  }
  
  this.HighlightRedo = function()
  {
    if(isOpera())
      this.ShowElOpera(2);
    else
      this.ShowEl(2);
  }
  
  this.HighlightNone = function()
  {
    if(isOpera())
      this.ShowElOpera(0);
    else
      this.ShowEl(0);
  }
}


function TMiksanatik()
{
  this.Generate = Generate;
  
  function Generate()
  {
    var res = "";
    
    res += Img("/study/exercise/img/Miksanatik/mainMiksanatik.PNG");
    
    return res;
  }
  
}
// test
function TTaskCaption(num)
{
  if(num == undefined)
  {
    alert("Exception: TTask(num) - num is undefined");
  }
  this.ID = 'TaskCaption';

  this.Number = num;
  
  this.Generate = Generate;
  this.IsTaskCaption = true;
  
  function Generate()
  {
    var res = "";
    
    res += /*BeginCenter() + */BeginSpan('id', this.ID) + "Задание " + BeginFont("style", "color: green; font-weight: bold") + 
               String(this.Number) + EndFont() + EndSpan()/* + EndCenter()*/;
    
    return res;
  }
  
  this.SetTaskCaption = function(text)
  {
    this.Number = text;
    document.getElementById(this.ID).innerHTML = "Задание " + BeginFont("style", "color: green; font-weight: bold") + 
               String(this.Number) + EndFont();
  }
  
  this.SetSpeedTestCaption = function()
  {
    document.getElementById(this.ID).innerHTML = "Тест скорости";
  }
  
  this.Show = function(bShow)
  {           
    if(bShow == false)
      document.getElementById(this.ID).style.display = "none";
    else
      document.getElementById(this.ID).style.display = "";
  }
  
}

function TUserCaption(userName)
{
  this.UserName = userName;
  
  this.ID = 'UserCaption';
  
  this.Generate = Generate;
  this.Set = SetUserCaption;
  
  function Generate()
  {
    var res = "";
    
    res += "<b>" + BeginSpan('id', this.ID) + this.TrimUserCaption(this.UserName) + EndSpan() + "</b>";
    
    return res;
  }
  
  function SetUserCaption(val)
  {
    this.UserName = val;
    document.getElementById(this.ID).innerHTML = this.TrimUserCaption(this.UserName);
  }
  
  this.GetUserName = function()
  {
    return this.UserName;
  }
  
  this.IsEmpty = function()
  {
    return  (this.UserName == "");
  }
  
  this.TrimUserCaption = function(name)
  {
    if(name.length > 17)
      return name.slice(0, 16) + "&hellip;";
    return name;
  }
}


function TLastError()
{
  this.CiteArr = new Array("Довольно ошибаться!",
                        "Все ошибки сделаны, ошибок больше нет.",
                        "Ещё одно неверное движение — и...",
                        "Не упустите шанс!",
                        "Осталась одна попытка.",
                        "Ещё не всё потеряно!",
                        "Предельная концентрация!");

  this.Get = function()
  {
    var j = Math.floor(Math.random() * this.CiteArr.length + 0.5);
    
    return this.CiteArr[j];    
  }
}

var LastErrorObj = new TLastError();
function TWriter(id, zIndex)
{
  if(id == undefined || zIndex == undefined)
  {
    alert("TWriter(id, zIndex): exception - id == undefined || zIndex == undefined");
    return;
  }

  this.ID = id;
  this.ZIndex = zIndex;
  this.GenerateDiv = GenerateDiv;
  this.Write = write;
  this.Clear = Clear;
  this.Show = Show;
  this.Hide = Hide;
  
  this.GenerateDiv();
  
  function GenerateDiv()
  {
    Write("<div id=\'" + this.ID + "\' style=\'z-index: " + this.ZIndex + "\'>" + "</div>");
  }
  
  function write(str)
  {
    document.getElementById(this.ID).innerHTML += str;
  }
  
  function Clear()
  {
    document.getElementById(this.ID).innerHTML = "";
  }
  
  function Show()
  {
    document.getElementById(this.ID).style.display = "";
  }
  
  function Hide()
  {
    document.getElementById(this.ID).style.display = "none";
  }  
}

function TAfterErrorCounter(id, thisObjName)
{
  this.ID = id;
  this.ThisObjName = thisObjName;
  this.SetTime = SetTime;
  this.GetTime = GetTime;
  this.AutoUpdate = AutoUpdate;
  
  this.Time = 0;
  
function SetTime(sec)
{
  this.Time = sec;
  
  if(this.Time > 0)  this.AutoUpdate();
}

function GetTime()
{
  return this.Time;
}

function AutoUpdate()
{
  if(this.Time > 0)
  {
    document.getElementById(this.ID).innerHTML = BeginCenter() + this.Time + EndCenter();
    this.Time--;
    //alert("Before setTimeout");
    setTimeout(this.ThisObjName + ".AutoUpdate()", 1000);
    //alert("After setTimeout");
    return;
  }
  
  document.getElementById(this.ID).innerHTML = keyboard.PlaceKeyboard('900');
//  keyboard.InitKeyInput();
  keyboard.InitKeyboard();
  FlashKeyboard.SetKey(ExerciseText.charAt(CaretPos));
  CiteField.Render();
  CiteField.Update();
  
  SetLeftSideTyping();
  ExerciseAfterError();
}

}
///

function TExerciseInfo()
{
  this.BFormed = false;
  this.BRequested = false;
  this.SortID = "";
  this.Redirect = "";
  this.LessonNumber = "";  
  this.ExerciseNumber = "";
  this.MaxNumOfMistakes = "";
  this.TypeOfExercise = "";
  this.RegWarning = "";
  this.HighSpeed = "";
  this.Difficulty = "";
  this.Text = "";
  this.Text2 = "";
  this.Language = "Russian";
  this.UserName = "";
  this.RefToNext = "";
  this.RefToLast = "";
  this.Reset = Reset;
  this.IsFormed = IsFormed;
  this.IsRequested = IsRequested;
  
  function Reset()
  {
    this.BFormed = false;
    this.BRequested = false;
  }
  
  function IsFormed()
  {
    return this.BFormed;
  }
  
  function IsRequested()
  {
    return this.BRequested;
  }
}

function TSpeedTestInfo()
{
  this.BFormed = false;
  this.BRequested = false;
  this.Text = "";
  this.UserName = "";
  this.ID = -1;
  
  this.Reset = function()
  {
    this.BFormed = false;
    this.BRequested = false;
  }
  
  this.IsFormed = function()
  {
    return this.BFormed;
  }
  
  this.IsRequested = function()
  {
    return this.BRequested;
  }
  
// Имитация, что тест был запущен и сформирован.
  this.SimFormed = function()
  {
    this.BRequested = true;
    this.BFormed = true;
  } 
  
}

function TCiteInfo()
{
  this.BFormed = false;
  this.BRequested = false;
  this.Text = "";
  this.Author = "";
  this.AuthorInfo = "";
  this.Reset = Reset;
  this.IsFormed = IsFormed;
  this.IsRequested = IsRequested;
  
  function Reset()
  {
    this.BFormed = false;
    this.BRequested = false;
  }
  
  function IsFormed()
  {
    return this.BFormed;
  }
  
  function IsRequested()
  {
    return this.BRequested;
  }
}

function TTodayResultInfo()
{
  this.BFormed = false;
  this.BRequested = false;
  this.Reset = Reset;
  this.IsFormed = IsFormed;
  this.IsRequested = IsRequested;
  
  this.NumOfTasks = "";
  this.AvarageSpeed = "";
  this.SymbolsTyped = "";
  this.NumOfErrors = "";
  
  function Reset()
  {
    this.BFormed = false;
    this.BRequested = false;
  }
  
  function IsFormed()
  {
    return this.BFormed;
  }
  
  function IsRequested()
  {
    return this.BRequested;
  }   
}

function TResultInfo()
{
  this.BFormed = false;
  this.BRequested = false;
  this.Reset = Reset;
  this.IsFormed = IsFormed;
  this.IsRequested = IsRequested;
  
  this.Mark = "";
  this.Text = "";
  this.Header = "";
  this.RefToNext = "";
  this.IDAdd = "";
  this.IDRemove = "";
  this.IDNext = "";
  
  function Reset()
  {
    this.BFormed = false;
    this.BRequested = false;
  }
  
  function IsFormed()
  {
    return this.BFormed;
  }
  
  function IsRequested()
  {
    return this.BRequested;
  } 
}

function TResultSpeedTestInfo()
{
  this.BFormed = false;
  this.BRequested = false;
  
  this.Text = "";
  this.Header = "";
  
  this.nFlds = 8;
  this.nCrit = 0;
  this.sCrits = new Array("dt", "avg_speed", "errors");
  this.nPage = 0;
  
  this.SetNumFields = function(val)
  {
	this.nFlds = val;
  }
  
  this.APos = new Array();
  this.ADate = new Array();
  this.AAvgSpeed = new Array();
  this.AErrors = new Array();
  
  this.Reset = function()
  {
    this.BFormed = false;
    this.BRequested = false;
    this.nPage = 0;
  }
  
  this.IsFormed = function()
  {
    return this.BFormed;
  }
  
  this.IsRequested = function()
  {  
    return this.BRequested;
  } 
}

function TErrorsInfo()
{
  this.BFormed = false;
  this.BRequested = false;
  
  this.MaxNumOfErrors = 1000;
  this.NumOfErrors = 0; // not inited
  
  this.Errors = new Array();
  this.BErrorUsed = new Array();
  
  this.Reset = function()
  {
    this.BFormed = false;
    this.BRequested = false;
  }
  
  this.IsFormed = function()
  {
    return this.BFormed;
  }
  
  this.IsRequested = function()
  {
    return this.BRequested;
  } 
  
  this.MixErrors = function()
  {
    for(var i = 0; i < this.NumOfErrors / 2; i++)
    {
      var first = Math.floor(Math.random() * this.NumOfErrors);
      if(first == this.NumOfErrors) first--;
      var second = Math.floor(Math.random() * this.NumOfErrors);
      if(second == this.NumOfErrors) second--;
      
      var val = this.Errors[first];
      this.Errors[first] = this.Errors[second];
      this.Errors[second] = val;
    }
  }
  
  this.GetError = function()
  {
    var i;
    for(i = 0; i < this.NumOfErrors; i++)
    {
      if(this.BErrorUsed[i] == false)
      {
        this.BErrorUsed[i] = true;
        return this.Errors[i];
      }
    }
    
    for(i = 1; i < this.NumOfErrors; i++)
    {
      this.BErrorUsed[i] = false;
    }
    
    return this.Errors[0];
  }
  
}

function TKeyboardInfo()
{
  this.layout = "rus";
  this.bNextKey = true;
  this.bShowChars = true;
  this.bColored = true;
}

function RequestSpeedRepeat()
{
  req = null;
    if (window.XMLHttpRequest) {
        try {
            req = new XMLHttpRequest();
        } catch (e){}
    } else if (window.ActiveXObject) {
        try {
            req = new ActiveXObject('Msxml2.XMLHTTP');
        } catch (e){
            try {
                req = new ActiveXObject('Microsoft.XMLHTTP');
            } catch (e){}
        }
    } 
  req.open("GET", "/test/get_text.html", true);
  req.send(null);  
  req.onreadystatechange = ReqSpeedTestRepeat;  
  
}

function ReqSpeedTestRepeat()
{
      if (req.readyState == 4) {
        if (req.status == 200) 
        {   
            var xmlDoc=req.responseXML.documentElement;
            
            SpeedTestInfo.Text = GetXMLElement(xmlDoc, "text"); 
            try{
              SpeedTestInfo.UserName = GetXMLElement(xmlDoc, "user-name");
            }
            catch(e) {}
            SetExerciseUser(SpeedTestInfo.UserName);                 
            SpeedTestInfo.ID = GetXMLElement(xmlDoc, "text_id");
            SpeedTestInfo.Language = GetXMLElement(xmlDoc, "language");
            
            FlashKeyboard.InitLanguage();
            FlashInput.InitOS();
            ExerciseText = SpeedTestInfo.Text;
            
            FlashInput.ResetContent();
            ExerciseText = FormatToStrings(SpeedTestInfo.Text, 40);
            FlashInput.SetText(ExerciseText);
            
            GoToBeginningOfExercise(); 
            
            SpeedTestInfo.BFormed = true;
           
        }
      }

}

function RequestAJAX(url, itemName)
{
    req = null;
    if (window.XMLHttpRequest) {
        try {
            req = new XMLHttpRequest();
        } catch (e){}
    } else if (window.ActiveXObject) {
        try {
            req = new ActiveXObject('Msxml2.XMLHTTP');
        } catch (e){
            try {
                req = new ActiveXObject('Microsoft.XMLHTTP');
            } catch (e){}
        }
    }
 
    if(req)
    {
      if(itemName == "cite")
      { 
        CiteInfo.BFormed = false;
        req.onreadystatechange = ReqCiteResult;
      }
      else
      if(itemName == "citeupdate")
      {
        req.onreadystatechange = ReqCiteUpdate;
      }
      else
      if(itemName == "exercise")
      {
        ExerciseInfo.BFormed = false;
        req.onreadystatechange = ReqExerciseResult;
      }
      else
      if(itemName == "speedTest")
      {
        SpeedTestInfo.BFormed = false;
        req.onreadystatechange = ReqSpeedTest;
      }
      else
      if(itemName == "speedTestUpdate")
      {
        req.onreadystatechange = ReqSpeedTestUpdate;
      }      
      else
      if(itemName == "result")
      {
        ResultInfo.BFormed = false;
        req.onreadystatechange = ReqResult;        
      }
      else
      if(itemName == "resultSpeedTest")
      {
        ResultSpeedTestInfo.BFormed = false;
        req.onreadystatechange = ReqResultSpeedTest;
      }
      else
      if(itemName == "rating")
      {
        RatingInfo.BFormed = false;
        req.onreadystatechange = ReqRating;   
      }
      else
      if(itemName == "todayResult")
      {
        TodayResultInfo.BFormed = false;
        req.onreadystatechange = ReqTodayResult;
      }
      else
      if(itemName == "errors")
      {
        ErrorsInfo.BFormed = false;
        req.onreadystatechange = ReqErrors;
      }
      else
      if(itemName == "getLayout")
      {
        req.onreadystatechange = KeyboardLayoutGot;
      }
      else
      if(itemName == "getKeybParams")
      {
        req.onreadystatechange = KeyboardParamsGot;
      }
      else
      if(itemName == "saveParam"  ||  itemName == undefined)
      {
        req.onreadystatechange = function(){};
      }
      else
      {
        alert("RequestAJAX: exception (wrong itemName)");
        return;
      }
      
      req.open("GET", url, true);
      req.send(null);
//        reqTimeout = setTimeout("req.abort();", 5000);
    } else {
        alert("Ваш браузер не поддерживает AJAX. Пришлите описание проблемы на zaharov@ergosolo.ru");
    } 

function GetXMLElementHTML(xmlDoc, element, index)
{
  if(index == undefined)
  {
    if(xmlDoc.getElementsByTagName(element)[0] != null)
      return  xmlDoc.getElementsByTagName(element)[0];
      /*if(xmlDoc.getElementsByTagName(element)[0].firstChild != null)
      {
        return xmlDoc.getElementsByTagName(element)[0].firstChild.nodeValue;
      }
      else
      {
        return "";
      }*/
    else return "";
  }
}
 
function GetXMLElement(xmlDoc, element, index)
{
//  alert("element = " + element);
  if(index == undefined)
  {
    if(xmlDoc.getElementsByTagName(element)[0] != null)
      if(xmlDoc.getElementsByTagName(element)[0].firstChild != null)
      {
        return xmlDoc.getElementsByTagName(element)[0].firstChild.nodeValue;
      }
      else
      {
        return "";
      }
    else return "";
  }
  else
  {
    if(xmlDoc.getElementsByTagName(element)[index] == undefined)  return "";
    if(xmlDoc.getElementsByTagName(element)[index].firstChild != null)
    {
      return xmlDoc.getElementsByTagName(element)[index].firstChild.nodeValue;
    }
    else
    {
      return "";
    }    
  }
}

function ReqErrors()
{
  var errorText;
  var i;
  
    if (req.readyState == 4) {
        if (req.status == 200) {
            var xmlDoc=req.responseXML.documentElement;
            for(i = 0; i < ErrorsInfo.MaxNumOfErrors; i++)
            {
              errorText = GetXMLElement(xmlDoc, "text", i);
              if(errorText != "")  
              {
                ErrorsInfo.Errors.push(errorText);
                ErrorsInfo.BErrorUsed.push(false);
              }
              else break;
            }            
            ErrorsInfo.NumOfErrors = i;
            ErrorsInfo.MixErrors();
            ErrorsInfo.BFormed = true;
        } else {
            alert("ReqErrors: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }
}

// moved to resultPage
function ReqRating() {
//    document.form1.state.value = stat(req.readyState); 
 
    if (req.readyState == 4) {
//        clearTimeout(reqTimeout);
        if (req.status == 200) {
            var xmlDoc=req.responseXML.documentElement;
              
            for(i = 0; i < ResultPage.nPageUsers; i++)
            {  
              RatingInfo.User[i] = "";
              RatingInfo.UserId[i] = "";
              RatingInfo.Attempts[i] = "";
              RatingInfo.AvarageSpeed[i] = "";
              RatingInfo.Number[i] = 0;
              RatingInfo.Errors[i] = "";
              RatingInfo.Rating[i] = "";              
            }  
              
            RatingInfo.NumOfPages = GetXMLElement(xmlDoc, "pages");  
            RatingInfo.UserPage = GetXMLElement(xmlDoc, "page");
            
            if(RatingInfo.UserPage != "")
            {
              // page specified... set it selected...
              ResultPage.SetCurPage(parseInt(RatingInfo.UserPage) - 1);
              
              try
              {
            	ResultPage.CurPage = parseInt(RatingInfo.UserPage) - 1;
            	ResultPage.NumOfPages = parseInt(RatingInfo.NumOfPages);
                document.getElementById("IDResLsnSel").innerHTML = ResultPage.ResultPageSelector();
              }
              catch(e){}
              
            }            
            
            for(i = 0; i < ResultPage.nPageUsers; i++)
            {  
              RatingInfo.Number[i] = GetXMLElement(xmlDoc, "no", i);
              if(RatingInfo.Number[i] == "")  break;
              RatingInfo.UserId[i] = GetXMLElement(xmlDoc, "auser_id", i);
              RatingInfo.User[i] = GetXMLElement(xmlDoc, "auser_name", i);
              RatingInfo.Attempts[i] = GetXMLElement(xmlDoc, "attempts", i);
              RatingInfo.Errors[i] = GetXMLElement(xmlDoc, "errors", i);
              RatingInfo.AvarageSpeed[i] = GetXMLElement(xmlDoc, "avg_speed", i);
              RatingInfo.Rating[i] = GetXMLElement(xmlDoc, "rating", i);   
              RatingInfo.Active[i] = GetXMLElement(xmlDoc, "active", i);          
            }
            
            RatingInfo.NumOfElemenents = i;
          
            RatingInfo.BFormed = true;
            ResultPage.UpdateUserTable();
            ResultPage.PostForm();
        } else {
            alert("ReqRating: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }  
} 
    
function ReqTodayResult()
{
    if (req.readyState == 4) {
//        clearTimeout(reqTimeout);
        if (req.status == 200) {
            var xmlDoc=req.responseXML.documentElement;
            
            TodayResultInfo.NumOfTasks = GetXMLElement(xmlDoc, "exercises");
            TodayResultInfo.AvarageSpeed = GetXMLElement(xmlDoc, "avg_speed");
            TodayResultInfo.SymbolsTyped = GetXMLElement(xmlDoc, "length");
            TodayResultInfo.NumOfErrors = GetXMLElement(xmlDoc, "errors");
          
            TodayResultInfo.BFormed = true;
            
        } else {
            alert("ReqTodayResult: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }  
}    
    
function ReqCiteResult() {
 
    if (req.readyState == 4) {

        if (req.status == 200) {
            var xmlDoc=req.responseXML.documentElement;  
            CiteInfo.Text = GetXMLElement(xmlDoc, "text");
            CiteInfo.Author = GetXMLElement(xmlDoc, "author");
            CiteInfo.AuthorInfo = GetXMLElement(xmlDoc, "author-info");
          
            CiteInfo.BFormed = true;
            
        } else {
            alert("ReqCiteResult: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }  
}

function ReqCiteUpdate()
{
    if (req.readyState == 4) {
        if (req.status == 200) {
            var xmlDoc=req.responseXML.documentElement;  
            CiteInfo.Text = GetXMLElement(xmlDoc, "text");
            CiteInfo.Author = GetXMLElement(xmlDoc, "author");
            CiteInfo.AuthorInfo = GetXMLElement(xmlDoc, "author-info");
            
            SetExerciseCite(CiteInfo);
            
            /*if(ExerciseInfo.TypeOfExercise == 32)
            {
              RequestSpeedTestUpdate();
            }  
            else
              GoToBeginningOfExercise();*/ 
              
            
            if(GetExType() == 32) 
            { 
              RequestSpeedRepeat(); 
            }
            else  
              GoToBeginningOfExercise();                      
            
        } else {
            alert("ReqCiteResult: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }    
}

function ReqExerciseResult()
{
    if (req.readyState == 4) {
        if (req.status == 200) {
        
            var xmlDoc=req.responseXML.documentElement;
            
            ExerciseInfo.SortID = GetXMLElement(xmlDoc, "sort-id");
            ExerciseInfo.LessonNumber = GetXMLElement(xmlDoc, "lesson-no"); 
//            alert("ExerciseInfo.LessonNumber = " + ExerciseInfo.LessonNumber); 
            ExerciseInfo.ExerciseNumber = GetXMLElement(xmlDoc, "exercise-no");
            ExerciseInfo.MaxNumOfMistakes = GetXMLElement(xmlDoc, "errors");
            ExerciseInfo.TypeOfExercise = parseInt(GetXMLElement(xmlDoc, "type"));
            ExerciseInfo.RegWarning = GetXMLElement(xmlDoc, "regwarning");
            ExerciseInfo.HighSpeed = GetXMLElement(xmlDoc, "highspeed");
            ExerciseInfo.Difficulty = GetXMLElement(xmlDoc, "difficulty");
            ExerciseInfo.Text = GetXMLElement(xmlDoc, "text");
            ExerciseInfo.Redirect = GetXMLElement(xmlDoc, "redirect");
            
            //alert("ExerciseInfo.Text="+req.responseText);
            ExerciseInfo.Text2 = GetXMLElement(xmlDoc, "text2");
            ExerciseInfo.Language = GetXMLElement(xmlDoc, "language");
            FlashKeyboard.InitLanguage();
            FlashInput.InitOS();
            ExerciseInfo.UserName = GetXMLElement(xmlDoc, "user-name");
            SetExerciseUser(ExerciseInfo.UserName);
            ExerciseInfo.RefToNext = GetXMLElement(xmlDoc, "next");
            ExerciseInfo.BFormed = true;
            
        } else {
            alert("ReqExerciseResult: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }    
}

function ReqSpeedTest()
{
    if (req.readyState == 4) {
        if (req.status == 200) {
        
            var xmlDoc=req.responseXML.documentElement;
          
            SpeedTestInfo.Text = GetXMLElement(xmlDoc, "text");
            try{
              SpeedTestInfo.UserName = GetXMLElement(xmlDoc, "user-name");
            }
            catch(e) {}  
            SetExerciseUser(SpeedTestInfo.UserName);
            SpeedTestInfo.ID = GetXMLElement(xmlDoc, "text_id"); 
            SpeedTestInfo.Language = GetXMLElement(xmlDoc, "language");
            FlashKeyboard.InitLanguage();
            FlashInput.InitOS();
            SpeedTestInfo.BFormed = true;
            ExerciseInfo.TypeOfExercise = 32; // bad...
        } else {
            alert("ReqSpeedTest: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }   
}

function ReqSpeedTestUpdate()
{
    if (req.readyState == 4) {
        if (req.status == 200) {
            var xmlDoc=req.responseXML.documentElement;
            SpeedTestInfo.Text = GetXMLElement(xmlDoc, "text");   
            try{
              SpeedTestInfo.UserName = GetXMLElement(xmlDoc, "user-name");
            }
            catch(e) {}
            SetExerciseUser(SpeedTestInfo.UserName);
            SpeedTestInfo.ID = GetXMLElement(xmlDoc, "text_id"); 
            SpeedTestInfo.Language = GetXMLElement(xmlDoc, "language");  
            FlashKeyboard.InitLanguage();  
            FlashInput.InitOS();
//            SetExerciseText(FormatToStrings(SpeedTestInfo.Text, 40));
//            GoToBeginningOfExercise();
        } else {
            alert("ReqSpeedTest: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }   
}

function ReqResult()
{
    if (req.readyState == 4) {
//        clearTimeout(reqTimeout);
        if (req.status == 200) {
        
            var xmlDoc=req.responseXML.documentElement;            
            
            //alert(req.responseText);
            
            ResultInfo.Rating = GetXMLElement(xmlDoc, "rating");
            ResultInfo.Header = GetXMLElement(xmlDoc, "header");
            ResultInfo.Text = GetXMLElement(xmlDoc, "text");
            
            with(ResultPage)
            {
            	RegRes = RegExRes;
            }
            
            //alert("ResultInfo.Text="+ResultInfo.Text);
            
            if(ResultPage.IsSetRefToNext() == false)  
            {
              ResultInfo.RefToNext = GetXMLElement(xmlDoc, "next_lesson");
              ResultPage.RefToNextHasSet(true);
            }    
            
            ResultInfo.IDAdd = GetXMLElement(xmlDoc, "add_id");
            ResultInfo.IDRemove = GetXMLElement(xmlDoc, "remove_id");
            ResultInfo.IDNext = GetXMLElement(xmlDoc, "next_id");
            ResultInfo.TextRepeat = GetXMLElement(xmlDoc, "repeat");
            
            ResultInfo.RefToLast = GetXMLElement(xmlDoc, "last_lesson");
            
            if(ResultInfo.RefToLast != "")
            {
              ResultPage.SetRefToLast(ResultInfo.RefToLast);      
            }  
            
//            alert("ResultInfo.IDAdd="+ResultInfo.IDAdd+"\nResultInfo.IDRemove="+ResultInfo.IDRemove+"\nResultInfo.IDNext="+ResultInfo.IDNext);
            
//            if(ResultInfo.RefToNext != "")  ResultPage.SetRefToNextExercise(ResultInfo.RefToNext);
            
            ResultInfo.BFormed = true;
            
        } else {
            alert("ReqResult: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }    
}

function ReqResultSpeedTest()
{
    if (req.readyState == 4) {
        if (req.status == 200) {
            var xmlDoc=req.responseXML.documentElement;
            
            ResultSpeedTestInfo.Header = GetXMLElement(xmlDoc, "header");
            
            ResultSpeedTestInfo.Text = GetXMLElement(xmlDoc, "text");
            
            with(ResultPage)
            {
            	RegRes = RegSpdRes;
            }
            
            if(IsUserLogin())
            {
            	
            	with(ResultPage)
            	{
            		NumOfPages = parseInt(GetXMLElement(xmlDoc, "pages"));
            		CurPage = parseInt(GetXMLElement(xmlDoc, "page"));
            		// ###
            		//
            	}
                
                document.getElementById("IDResSpeedSel").innerHTML = ResultPage.ResultPageSelector();
                
                var d = new Date();
            	for(var i = 0; i < ResultSpeedTestInfo.nFlds; i++)
            	{
            		with(ResultSpeedTestInfo)
            		{
            			APos[i] = GetXMLElement(xmlDoc, "num", i);
            			try
            			{ 
            				ADate[i] = d.GetRusStr(GetXMLElement(xmlDoc, "dt", i)); 
            			} 
            			catch(e)
            			{
            				ADate[i] = "";
            			}
            			AAvgSpeed[i] = GetXMLElement(xmlDoc, "avg_speed", i);
            			AErrors[i] = GetXMLElement(xmlDoc, "errors", i);
            		}
            	}
            	for(var j in ResultSpeedTestInfo.ADate)
            	{
            		with(ResultSpeedTestInfo)
            		{
            			if(isNaN(parseInt(APos[j])))
            			{
            				ResultPage.SetTabSpeedRowVis(j, false);
            			}
            			else
            			{
            				ResultPage.SetTabSpeedRowVis(j, true);
            				ResultPage.SetResSpeedPos(j, String(parseInt(APos[j]) + 1));
            				ResultPage.SetResSpeedDate(j, ADate[j]); // test
            				ResultPage.SetResSpeedErrors(j, AErrors[j]);
            				ResultPage.SetResSpeedSpeed(j, AAvgSpeed[j]);
            			}
            		}
            	}
            }
            
            ResultSpeedTestInfo.BFormed = true;
        } else {
            alert("ReqResultSpeedTest: Could not receive data from server:\n" + req.statusText);
            stop();
        }
    }    
}

}

function RequestRatingTableUser(exercise_id)
{ 
  var reqStr = "/study/exercise_rating.html?exercise_id=" + exercise_id + "&count=" + ResultPage.nPageUsers;
  RequestAJAX(reqStr, "rating");
  RatingInfo.BRequested = true;  
}

function RequestRatingTablePage(exercise_id, page, sort_order)
{
    var reqStr = "/study/exercise_rating.html?exercise_id=" + exercise_id + "&page=" + page + "&order=" + sort_order + "&count=" + ResultPage.nPageUsers;
    RequestAJAX(reqStr, "rating");  
    RatingInfo.BRequested = true;
}

function RequestSpeedTest()
{
  RequestAJAX("/test/get_text.html", "speedTest");
  SpeedTestInfo.BRequested = true;
}

function RequestSpeedTestUpdate()
{
  RequestAJAX("/test/get_text.html", "speedTestUpdate");
  SpeedTestInfo.BRequested = true;
}

function RequestCite()
{
  RequestAJAX("/study/cite.html", "cite");
  CiteInfo.BRequested = true;
}

function RequestCiteSpeedUpdate()
{
  RequestAJAX("/study/cite.html", "citeupdate");
}

function RequestExercise(sortID)
{
  var s = "/study/get_exercise.html?sort_id=" + sortID;
  RequestAJAX(s, "exercise");
  ExerciseInfo.BRequested = true;
}

var SupposeSpeed_ = 200;
var SupposeDuration_ = 60; 

function RequestResult(exerciseID, avgSpeed, numOfErrors, duration)
{

  var avgSpeed_ = avgSpeed;
  var duration_ = duration;
  
  if(duration_ == 0)  duration_ = SupposeDuration_;


  var s = "exercise_id=" + exerciseID;
  s += "&avg_speed=" + avgSpeed_;
  s += "&errors=" + numOfErrors;
  s += "&duration=" + duration_;
  
  RequestAJAX("/study/result.html?" + s, "result");
  
  ResultInfo.BRequested = true;  
}

function RequestSpeedTestResult(avgSpeed, numOfErrors, duration)
{
  var s = "";
 
  if(avgSpeed != undefined)
  {
	  if(avgSpeed == Infinity) avgSpeed = SupposeSpeed_;	  
	  s += "avg_speed=" + avgSpeed;
	  s += "&errors=" + numOfErrors;
	  s += "&duration=" + duration;
	  s += "&text_id=" + SpeedTestInfo.ID;
	  s += "&bfin=1&";
  }
  
  with(ResultSpeedTestInfo)
  {
	  s += "count=" + nFlds;
	  s += "&sort=" + sCrits[nCrit];
	  s += "&page=" + nPage;
  }
  
  var iLen = 0;
  if(SpeedTestInfo.Language == "English") iLen = 1;
  s += "&language="+String(iLen);
  RequestAJAX("/test/get_result.html?" + s, "resultSpeedTest");
  ResultSpeedTestInfo.BRequested = true;  
}
 
function RequestTodayResult()
{
  RequestAJAX("/study/get_stat.html", "todayResult");
  TodayResultInfo.BRequested = true;
}

function RequestErrors()
{
  RequestAJAX("/study/error.html", "errors");
  ErrorsInfo.BRequested = true;
}

function RequestLayout()
{
   RequestAJAX("/study/keyboard.html", "getLayout");
}

function RequestKeyboardParams()
{
    RequestAJAX("/study/keyboard.html", "getKeybParams");
}

function RequestSaveKeyboardParam(param, val)
{
   RequestAJAX("/study/keyboard.html?paramName=" + param + "&val="+String(val), "saveParam");
}
var PhotoCaptions = new Array(
"Проделки Миксанатика, проделки Миксанатика… Авторы программы готовы на меня свалить все свои огрехи. Но я терпеливый. А Вы?",
"2008 год. Ну вот, Вы начали заниматься по «СОЛО на клавиатуре». Каждый раз, когда Вы выполните задание на пятёрку, будет мой портрет. И с каждым заданием я буду молодеть. На этом снимке мне 68 лет. Пока мы живы, мы можем улучшить свою жизнь. Не так ли?",
"2008 год. Ну вот, Вы начали заниматься по «СОЛО на клавиатуре». Каждый раз, когда Вы выполните задание на пятёрку, будет мой портрет. И с каждым заданием я буду молодеть. На этом снимке мне 68 лет. Пока мы живы, мы можем улучшить свою жизнь. Не так ли?",
"2008 год. Майя Рошаль, дочь Григория Рошаля. Постоянно сейчас живёт в Лондоне, наездами бывает в Москве. Мы знакомы около шестидесяти лет. Она уже прабабушка.",
"2007 год. С нашим программистом Дмитрием Юрьевичем Митрофановым. Хорошо быть молодым и талантливым!",
"2007 год. С Шарлем Азнавуром я встречался и говорил десять раз и каждую встречу помню до мельчайших подробностей. Человек-эпоха, знаменитость мира, гордость армян. Он же армянин.",
"2006 год. В зрительном зале Государственного Кремлёвского дворца. Случайная встреча с Олегом Табаковым. Мы знакомы более сорока лет и тепло относимся друг к другу. Сын Олега Павловича знает «СОЛО на клавиатуре».",
"2006 год. На радиостанции «Эхо Москвы». Слева — юрист Михаил Барщевский, справа — журналист Матвей Ганапольский.",
"2006 год. Владимир Михайлович Платонов, председатель Московской городской думы, интереснейший собеседник.",
"2006 год. С талантливой французской певицей Мирей Матье, которая нравилась мне с юности.",
"2006 год. С Давидом Яном, автором интересных компьютерных программ, организатором перформансов и вообще уникальным человеком.",
"2005 год. С Сергеем Алексеевичем Медновым, вице-президентом Альфа-банка, у меня дома.",
"2005 год. С Петром Валерьевичем Ивановым, начальником Мосгортранса, у меня дома. Он прошёл «СОЛО на клавиатуре», и это прекрасно.",
"2005 год. Пытаюсь научить Бари Алибасова слепому методу набора.",
"2004 год. Случайная встреча с талантливым Григорием Остером. Я читаю его книжки с наслаждением, и два раза в год мы случайно встречаемся. Это продолжается уже более десяти лет.",
"2004 год. С художником Алексеем Викторовичем Андреевым. Огромное ему спасибо за всё!",
"2004 год. Друзья шутят, что я могу и лошадей научить набирать на клавиатуре слепым методом — четырьмя копытами.",
"2003 год. Мы давно знакомы с Александром Ширвиндтом. Он обещал пройти «СОЛО», но слово своё так и не сдержал.",
"2003 год. На одной из телепередач, которую вёл Михаил Швыдкой. Мы знакомы с ним более сорока лет.",
"2003 год. О чём я думал? Не знаю. Наверное, о том, как найти корпоративных солистов.",
"2003 год. Пусть мне уже за 60, но студентки меня пока ещё любят. Это обнадёживает.",
"2002 год. Мария Александровна Казицкая — замечательный редактор и добрейшей души человек.",
"2002 год. С Игорем Кио и его женой в каком-то ресторане. Мы дружили с Игорем Эмильевичем несколько десятилетий.",
"2002 год. С моим учителем, выдающимся врачом-психиатром, психоаналитиком Ароном Белкиным у него дома.",
"2001 год. С моим самым близким другом Вячеславом Лисовским. Последние годы он живёт в Италии, но время от времени приезжает в Москву.",
"2001 год. С Валерием Плотниковым в Доме актёра. А много лет назад он ходил в мою студию «Юный кинематографист». Я руководитель, он ученик. У нас разница в возрасте — пять лет. Теперь он — один из лучших фотографов мира, автор прекрасных фотоальбомов.",
"2001 год. Если гора не идёт к Магомету, то Магомет идёт к горе. Я вышел на Пушкинскую площадь для встречи с читателями.",
"2001 год. С Алексеем Михеевичем Брячихиным. Он многое сделал для Москвы и москвичей. Его любимая фраза: «Не место красит человека, а человек место».",
"2000 год. С удивительным человеком, талантливым доктором, великолепным организатором (главный врач больницы имени С. Боткина) Владимиром Николаевичем Яковлевым, которому я многим обязан.",
"2000 год. У меня дома с прекрасным доктором Галиной Геннадьевной Жаровой. Спасибо ей за многое! А между нами — мой любимый пёс, проживший 15 лет у меня дома, славный Кристоф…",
"1999 год. На 50-летии главного редактора газеты «Московский комсомолец» Павла Гусева. Почему он в инвалидной коляске? Сломал ногу.",
"1999 год. В рабочем кабинете Николая Васильевича Куликова, руководителя столичной милиции.",
"1999 год. С Евгением Касперским, главным борцом с компьютерными вирусами, и его женой у меня дома. Слева — Евгений Медовый, который в то время помогал мне по «СОЛО на клавиатуре».",
"1998 год. С министром здравоохранения России Татьяной Борисовной Дмитриевой.",
"1998 год. С Мариной Васильевной Едемской. Доброй души человек. Спасибо ей за многое!",
"1998 год. Дома у Мстислава Леопольдовича Ростроповича.",
"1997 год. Компьютер я начал изучать в 57 лет и тут же решил написать книжку о компьютерах, которую так и не выпустил. Но появилось «СОЛО на клавиатуре».",
"1997 год. С Дмитрием Анатольевичем Волковым. Вместе с ним мы основали сайт «Спокойное место российского Интернета». Через месяц он отошёл от дел, но сайт пока живёт.",
"1996 год. С Александром Калягиным. Я много писал об этом артисте. Человек фантастической энергии, поразительного таланта и организаторских способностей.",
"1996 год. Слева — человек трагической судьбы Анатолий Колеватов (театральный деятель, руководитель Союзгосцирка), справа — Игорь Кио, известный иллюзионист.",
"1995 год. С Евгением Мендрелюком, основателем «Компьютерры», и его семьёй в цирке на Цветном бульваре.",
"1995 год. Норвежский Лесной — это его ник. Николай Николаевич Данилов, талантливый писатель. Я хорошо знал его деда.",
"1992 год. В кабинете директора цирка на Цветном бульваре Юрия Никулина.",
"1991 год. Юрия Михайловича Лужкова я ценю и уважаю.",
"1990 год. Беру интервью у Леонида Петровича Кравченко, одного из руководителей Гостелерадио СССР.",
"1989 год. С детства я любил вопрос «Почему?». И одной из первых настольных книг стала «Почемучка» Бориса Житкова.",
"1989 год. С Михаилом Фёдоровичем Ненашевым мы дружили более двадцати лет. Удивительной порядочности человек. На фото я в его кабинете, когда он работал председателем Госкомпечати СССР.",
"1989 год. Беру интервью у министра специального и высшего образования Геннадия Алексеевича Ягодина.",
"1988 год. У меня дома — Андрей Макаревич и литератор Юрий Михальцев. Мы делаем статью о Макаревиче для журнала «Огонёк».",
"1988 год. У меня дома — молодой журналист Сергей Корзун и испытывавший в то время серьёзные сложности в жизни рок-певец Борис Гребенщиков. Тогда мы с ним говорили дней десять подряд, каждый раз по пять-семь часов.",
"1988 год. С известным режиссёром-документалистом Юрисом Подниексом.",
"1987 год. Беру интервью у первого секретаря ЦК ВЛКСМ Виктора Мироненко.",
"1987 год. С Виталием Коротичем, главным редактором журнала «Огонёк», на одной из редакционных летучек.",
"1986 год. Иногда я брился налысо. В гостях у Джуны. Слева от меня — поэт Андрей Дементьев, Джуна Давиташвили, журналист Юрий Гусинский.",
"1986 год. Читаю лекцию о кино в одном из Домов культуры в Москве.",
"1986 год. Беру интервью на улице. В те годы я много времени отдавал журналистике.",
"1985 год. Певец Валерий Леонтьев даёт мне интервью у меня дома. Некоторое время мы были с ним дружны.",
"1982 год. В цирке на проспекте Вернадского. Клоуны Пётр Талдонов и Геннадий Ротман. Кто третий из клоунов — не помню. Цирк я очень любил. В детстве мечтал быть клоуном.",
"1981 год. Екатерина Васильева, известная актриса, женщина сложной судьбы. Мы пригласили её выступить перед студентами моего семинара.",
"1980 год. Цирк на Цветном бульваре. Аркадий Бурдецкий (акробат) и… Я помню её очень хорошо. Красивая женщина со сложной судьбой. Оставим её имя за скобками.",
"1975 год. С певицей Еленой Камбуровой и журналистом Владимиром Усановым в цирке на Цветном бульваре. О Лене Камбуровой я очень много писал. Великая певица!",
"1974 год. С Михаилом Ивановичем Кондаковым, заместителем министра просвещения России, в цирке. Его сын Александр Кондаков (ныне директор крупного издательства) в кадр почему-то не попал. А он был с нами в цирке, я помню.",
"1973 год. С Юрием Владимировичем Никулиным (с ним мы дружили около тридцати лет) у меня дома. Позируем фотографу. Я помогал Ю. В. Никулину в написании книги «Почти серьёзно…».",
"1973 год. С Борисом Амарантовым и фотографом Александром Божко (он стал телеоператором).",
"1972 год. Автор снимка — Тофик Шахвердиев. Мою безумную сущность он передал точно.",
"1972 год. С детьми — Сергеем и Наташей. Это было недавно, это было давно…",
"1970 год. Автор снимка — Тофик Шахвердиев, известный фотограф, режиссёр. Я на Тургеневской площади. Нынче она совсем другая.",
"1969 год. Беру интервью у Дмитрия Дмитриевича Шостаковича. В то время я работал на радио, на «Маяке — Последние известия».",
"1968 год. Я в редакции журнала «Журналист», где проработал менее года.",
"1966 год. Совершенно случайно я познакомился с Валерием Ободзинским и начал следить за его судьбой. Писал о нём в «Московском комсомольце», «Советской культуре» и других газетах. В своё время этот певец пользовался заслуженной славой. Человек трагической судьбы.",
"1966 год. В квартире Григория Львовича Рошаля. Когда мне бывает грустно, я приезжаю к дому на Большой Полянке, где жил Григорий Львович, и смотрю на окна его квартиры. Кто там сейчас живёт — не знаю. В том же доме жили Михаил Ромм, Юрий Райзман, Александр Птушко, Роман Кармен, Иван Пырьев и другие кинематографисты.",
"1965 год. Почему у меня микрофон, где я, кто снимал?.. Тогда я ещё не знал, что буду активно сотрудничать с радио.",
"1965 год. Дома у известного режиссёра Григория Львовича Рошаля. Он принимал участие в моём воспитании с моих 12 лет. Многим хорошим, что есть у меня, я обязан ему. Мне четверть века, и я всё ещё мечтаю о кинорежиссуре.",
"1965 год. Редакция газеты «Московский комсомолец». Снимок сделан журналистом Борисом Алексеевым. Наши пути потом несколько раз пересекались. Часто мы виделись на радиостанции «Эхо Москвы».",
"1965 год. Я в Саратове. В этом городе бывал раз двести. С ним многое связано: друзья, любовь, увлечения… Рядом со мной — Анатолий Калугин. Мечтал быть актёром, а стал журналистом.",
"1964 год. В один из приездов из Москвы в Ленинград я заехал к Виктору Нелепецу, моему школьному другу. На фото я с его мамой, Фаиной Давидовной Манилович. Удивительная была женщина по интеллигентности, образованности, порядочности. Работала в Публичной библиотеке.",
"1964 год. Я приехал из Москвы на несколько дней в Ленинград. Кафе «Север» на Невском. Встреча с Эдиком Геллером, школьным другом (кажется, он стал физиком), и его девушкой (а может быть, женой).",
"1962 год. Я часто приезжал в Москву к Григорию Львовичу Рошалю, известному в то время режиссёру. Я в его кресле и с его палкой.",
"1962 год. Мне 22 года. Сам себе казался старым. Многое у меня в то время не получалось. Мне казалось, что так будет вечно. Неужели стану неудачником? Чаще всего именно этот вопрос я задавал сам себе.  ",
"1960 год. Пионерский галстук я носил без всякого стеснения. Показывал пример ребятам: работа обязывала. Моя взрослая пионерская юность… Лет двадцать подряд после ухода из школы я получал письма от пионеров, с которыми работал. Где они сейчас, кто они?..",
"1960 год. Я старший вожатый 87-й школы Ленинграда. На каникулы вместе с пионерским активом поехали в Москву. Снимок сделан в Колонном зале Дома Союзов.",
"1959 год. Работаю в 91-й школе Ленинграда. Старший пионерский вожатый. Старшеклассники мне всегда помогали. Всё мечтаю написать книгу о том времени…",
"1959 год. Я старший пионервожатый в лагере. Рядом со мной — физрук и музрук. Где они, что с ними, как их звали?.. Но вожатым я был хорошим.",
"1958 год. Лебяжья канавка, Ленинград. Когда мне в юности бывало грустно, я любил бродить по городу. Чаще всего гулял по набережным Невы. Вода успокаивала.",
"1958 год. Автор снимка — Юрий Векслер. Он стал известным кинооператором. А тогда принимал участие в студии «Юный кинематографист», которой я руководил.",
"1958 год. Я работаю такелажником на киностудии «Ленфильм» и одновременно организовал студию «Юный кинематографист». Сергей Соловьёв, ныне известный режиссёр, Валерий Плотников, талантливый фотомастер, Саша Стефанович, кинорежиссёр, — они из нашей студии.",
"1958 год. Я старший вожатый пионерского лагеря Академии наук, посёлок Рощино. Вставал в шесть утра, ложился в двенадцать ночи, а то и позже. Но как же интересно было!",
"1957 год. Я у стены кинотеатра «Приморский», в котором работал киномехаником. В руках у меня последняя десятка.",
"1957 год. Мила Мартынова (слева от меня). Многим я говорил, что это моя гражданская жена. Зачем? Не знаю. Хотелось быть взрослым. Мила стала доктором наук, преподавала студентам историю искусств. Умная, добрая, талантливая девушка.",
"1957 год. Я дома у своего приятеля Льва Шварца. Он же и автор снимка. Мы вместе работали киномеханиками в кинотеатре «Приморский». Лев поступил на киноведческий во ВГИК. А меня на режиссёрский факультет ВГИКа не приняли.",
"1956 год. В одном из пионерских лагерей я руководил драматическим кружком. На переднем плане — Станислав Розен, слева — Кирилл Шишкин. Станислав стал известным театральным постановщиком, Кирилл — одним из лучших режиссёров Ленинградского телевидения. А кто рядом со мной — не помню.",
"1955 год. Я в роли Деда Мороза на утреннике, который организовала в одном из Домов культуры Светлана Бойкова. Она мечтала стать театральным режиссёром. Стала ли? Не знаю.",
"1955 год. Город Луга. Я приехал к Леониду Дятлову, талантливому молодому поэту. Мы вместе занимались в литературной студии при газете «Ленинские искры».",
"1954 год. Репетиция спектакля «Гимназисты». Передо мной — Володя Колокольцев. В 60-х годах прошлого столетия он получал тысячи писем от поклонниц после нескольких ролей в кино.",
"1954 год. Я в гриме. Спектакль по пьесе К. Тренёва «Гимназисты». Играю роль инспектора Адамова. Слева от меня Валерий Сибекин и Владимир Колокольцев (на заднем плане). Они стали киноактёрами. Дворец пионеров в Ленинграде. Руководил студией Леонид Николаевич Новиков.",
"1954 год. Снимок сделан в городе Луга. Я зачем-то лезу в пустой ящик из-под мороженого. Фотографировал юный поэт Сергей Макаров.",
"1953 год. Отлично помню: его звали Вадик, а её — Людмила. Она нравилась мне и Вадику. Но дружбе это не мешало. Мы в пионерском лагере.",
"1952 год. Мне двенадцать лет. В классе — самый низенький. Именно тогда я сказал: «Хочу быть кинорежиссёром». До этого мечтал быть учителем. А лет до девяти хотел стать клоуном.",
"1950 год. Станция Сиверская, Ленинградская область. Мне десять лет. Я в пионерском лагере, и ко мне приехала мама. Я счастлив.",
"1947 год. Первый класс «В» 69-й мужской школы Ленинграда. Учительницу звали Юлия Дмитриевна. Я сижу слева от неё.",
"1917 год. Мой дедушка, отец моей мамы. Он родом из Карабаха. Умер в 1940 году, за два месяца до моего рождения. В советское время трудился бухгалтером, а до революции владел небольшой фабрикой в Ереване.",
"1916 год. Моя бабушка, мама мамы. Умерла в 1919 году, когда моей маме было семь лет. Бабушка была дворянкой.");
// global functions...

function TestResultPage(obj)
{
  if(obj == undefined)  
  {
    alert('Can\'t test result page. Obj is undefined');
    return;
  }

  for(var i = 0; i < ResultPage.nPageUsers; i++)
  {
    obj.UsersTableSetUser(i, i+1, "User" + String(i+1), String((i+1)*100), String(i+2), String((i+1)*1000));
  } 
  
  obj.UsersTableSetActiveField(0);
  ResultPage.SetNumOfPages(30);
}

// handle functions
function ClickResultUserTableTyper()
{
  ResultPage.UsersTableSetActiveField(1);
  ResultPage.SetCrit(0);
  ResultPage.RequestUserTable();
}
function ClickResultUserTableSpeed()
{
  ResultPage.UsersTableSetActiveField(2);
  ResultPage.SetCrit(1);
  ResultPage.RequestUserTable();
}
function ClickResultUserNumOfErrors()
{
  ResultPage.UsersTableSetActiveField(3);
  ResultPage.SetCrit(2);
  ResultPage.RequestUserTable();
}
function ClickResultUserTableRating()
{
  ResultPage.UsersTableSetActiveField(4);
  ResultPage.SetCrit(3);
  ResultPage.RequestUserTable();
}


function UpdatePageNumber(pageNum, bUpdate)
{
  ResultPage.SetCurPage(pageNum);
  
  with(ResultPage)
  {  
	 if(RegRes == RegExRes)
	 {
	  if(ExerciseInfo.TypeOfExercise == 1  ||  ExerciseInfo.TypeOfExercise == 6)
		  RequestUserTable();
	  else
		  PostForm();
	 }
	 else
	 {
		 try
		  {
			  //ResultSpeedTestInfo.nPage = pageNum; //Это берется со страницы
			  //####
			  ResultSpeedTestInfo.nPage = pageNum;
			  RequestSpeedTestResult();
			  //if(bUpdate == undefined  ||  bUpdate == true) RequestSpeedTestResult();
		  }
		  catch(e){alert("UpdatePageNumber(): " + e.description);} 
	 }
  }
}

function UpdatePage()
{
  if(ExerciseInfo.TypeOfExercise == 1  ||  ExerciseInfo.TypeOfExercise == 6)
    RequestRatingTableUser(IDExercise);
  else
    ResultPage.PostForm();
}

var d = new Date();
d.getSeconds();

function TRatingInfo()
{
  this.BFormed = false;
  this.BRequested = false;
  this.IsFormed = IsFormed;
  this.IsRequested = IsRequested;
  
  this.NumOfPages = 0;
  this.UserPage = 0;
  this.UserId = new Array("","","","","","","","","","");
  this.User = new Array("","","","","","","","","","");
  this.Attempts = new Array("","","","","","","","","","");
  this.AvarageSpeed = new Array("","","","","","","","","","");
  this.Number = new Array(0,0,0,0,0,0,0,0,0,0);
  this.Errors = new Array("","","","","","","","","","");
  this.Rating = new Array("","","","","","","","","","");
  this.Active = new Array("","","","","","","","","","");
  
  this.NumOfElemenents = 0;
  
  this.Reset = function()
  {
    this.BFormed = false;
    this.BRequested = false;
  };
  
  function IsFormed()
  {
    return this.BFormed;
  }
  
  function IsRequested()
  {
    return this.BRequested;
  }
}



// TResultPage...
function TResultPage(blockName, exerciseBlockName)
{
  this.BlockName = blockName; 
  this.ExerciseBlockName = exerciseBlockName; 
  
  this.Set = Set;
  this.WindowTop = WindowTop;
  this.WindowLeft = WindowLeft;
  this.NumOfPages = 0;
  this.CurPage = 0;
  
  this.CritTyper = "Typer";
  this.CritSpeed = "Speed";
  this.CritNumOfMistakes = "NumOfMistakes";
  this.CritRating = "Rating";
  this.CurCrit = this.CritPosition;
  this.nPageUsers = 8; // num of users at the result table(s)
  
  this.ExerciseID = 1;
  this.bSet = false;
  
  this.UserProfilePath = "/profile/";
  
  this.ResultMessageEmptyField = new TEmptyField("default", "white");
  
  this.RegExRes = 0;
  this.RegSpdRes = 1;
  this.RegRes = this.RegExRes;
  
  this.SetGrammarResult = function(correctWords, typedWords)
  {
    var tableCorrectWords = "";
    var tableIncorrectWords = "";
    var bWasFirstCorrectWord = false;
    var bWasFirstIncorrectWord = false;
    var unnecSymbols = String(",.;:-");
    var correctWords_ = StrArrayDeleteSymbols(correctWords, unnecSymbols);
    var typedWords_ = StrArrayDeleteSymbols(typedWords, unnecSymbols);
    
    for(var i = 0; i < typedWords_.length; i++)
    {
      if(correctWords_[i] == typedWords_[i])
      {
        if(bWasFirstCorrectWord) tableCorrectWords += ", "; 
        tableCorrectWords += typedWords_[i];
        bWasFirstCorrectWord = true;
      }
      else
      {
        if(bWasFirstIncorrectWord)  tableIncorrectWords += ", "; 
        tableIncorrectWords += "<i>"+typedWords_[i] + "</i> &ndash; " + correctWords_[i];
        bWasFirstIncorrectWord = true;
      }
    }
    
    if(tableCorrectWords != "")  
    {
      document.getElementById('IDCorrectWords').innerHTML = tableCorrectWords + ".";
    }
    if(tableIncorrectWords != "") 
    {
      document.getElementById('IDIncorrectWords').innerHTML = tableIncorrectWords + ".";
    }
  };
  
  this.ShowGrammarResult = function()
  {
    var obj1 = document.getElementById('IDRatingTableSpan');
    var obj2 = document.getElementById('IDResultMiksanatik');
    var obj3 = document.getElementById('IDLiteracySpan');
    
    if(obj1 != undefined) obj1.style.display = "none";
    if(obj2 != undefined) obj2.style.display = "none";
    if(obj3 != undefined) obj3.style.display = "";
  };
  
  this.ShowRatingTable = function()
  {
    var obj1 = document.getElementById('IDRatingTableSpan');
    var obj2 = document.getElementById('IDResultMiksanatik');
    var obj3 = document.getElementById('IDLiteracySpan');
    
    if(obj1 != undefined) obj1.style.display = "";
    if(obj2 != undefined) obj2.style.display = "none";
    if(obj3 != undefined) obj3.style.display = "none";      
  };
  
  this.ShowMiksanatik = function()
  {
    var obj1 = document.getElementById('IDRatingTableSpan');
    var obj2 = document.getElementById('IDResultMiksanatik');
    var obj3 = document.getElementById('IDLiteracySpan');
    
    if(obj1 != undefined) obj1.style.display = "none";
    if(obj2 != undefined) obj2.style.display = "";
    if(obj3 != undefined) obj3.style.display = "none";     
    
    this.ShowRating(false);
  };
  
  this.HideMain = function()
  {
    var obj1 = document.getElementById('IDRatingTableSpan');
    var obj2 = document.getElementById('IDResultMiksanatik');
    var obj3 = document.getElementById('IDLiteracySpan');
    
    if(obj1 != undefined) obj1.style.display = "none";
    if(obj2 != undefined) obj2.style.display = "none";
    if(obj3 != undefined) obj3.style.display = "none";      
  };
  
  this.IsSet = function()
  {
    return this.bSet;
  };
  
  this.SetExerciseID = function(exID)
  {
    this.ExerciseID = exID;
  };
  
  this.SetWindowTop = function(val)
  {
    this.WindowTop = val;
  };
  this.SetWindowLeft = function(val)
  {
    this.WindowLeft = val;
  };
  
  this.SetLessonExercise = function(lesson, exercise)
  {
    try
    {
      document.getElementById('IDTaskNumber').innerHTML = lesson + '.' + exercise;
    }
    catch(e){} 
  };
  
  this.SetPanelSpeed = function(speed)
  {
    document.getElementById('resultPanelSpeed').innerHTML = speed;
  };
  
  this.UpdatePanelFullTime = function()
  {
    var oFlTm = document.getElementById('IDResultTimeElapsed');
    oFlTm.innerHTML = /*GetFullExTime()*/GetActiveExTime();
  };
  
  this.SetPanelNumOfErrors = function(numOfErrors)
  {
    document.getElementById('resultPanelNumOfErrors').innerHTML = numOfErrors;
  };
  
  this.SetHeader = function(header)
  {
    document.getElementById('IDResultHeader').innerHTML = header;
  };
  
  this.SetPanelRating = function(rating)
  {
    document.getElementById('resultPanelRating').innerHTML = rating;
  };
  
  this.SetPanelAvarageSpeed = function(avarageSpeed)
  {
    try
    {
      document.getElementById('resultPanelAvarageSpeed').innerHTML = avarageSpeed;
    }
    catch(e){}
  };
  
  this.SetPanelTextTyped = function(textTyped)
  {
    try
    {
      document.getElementById('resultPanelTextTyped').innerHTML = textTyped;
    }
    catch(e){}
  };
  
  this.SetPanelTotalNumOfErrors = function(numOfErrors)
  {
    document.getElementById('resultPanelTotalNumOfErrors').innerHTML = numOfErrors;
  };
  
  this.SetNumOfTasks = function(val)
  {
    try
    {
      document.getElementById('resultPanelExercisesPassed').innerHTML = val;
    }
    catch(e){}  
  };
  this.SetAvarageSpeed = function(val)
  {
    try
    {
      document.getElementById('resultPanelAvarageSpeed').innerHTML = val;
    } 
    catch(e) {}
  };
  this.SetNumOfSymbolsTyped = function(val)
  {
    try
    {
      document.getElementById('resultPanelTextTyped').innerHTML = val;
    }
    catch(e){} 
  };
  this.SetNumOfErrors = function(val)
  {
    try
    {
      document.getElementById('resultTotalNumOfErrors').innerHTML = val;
    }
    catch(e){} 
  };
  
  this.UserTableHeadFieldsIds = new Array("resultUserTablePosition", "resultUserTableTyper", "resultUserTableSpeed", 
    "resultUserTableNumOfErrors", "resultUserTableRating");
  this.UserTableHeadFieldsNames = new Array("Позиция", "Солист", "Скорость", "Ошибок", "Рейтинг");
  this.PrevTableHeadIndex = 0;
  
  this.UsersTableSetActiveField = function(index)
  {
    if(this.PrevTableHeadIndex == index) return;
    if(this.PrevTableHeadIndex != 0) document.getElementById(this.UserTableHeadFieldsIds[this.PrevTableHeadIndex]).innerHTML = 
      "<font class='resultUserTableHeadDashed'>" + this.UserTableHeadFieldsNames[this.PrevTableHeadIndex] + "</font>";
    document.getElementById(this.UserTableHeadFieldsIds[index]).innerHTML = 
      "<font class='resultUserTableHeadWithoutDashed'>" + this.UserTableHeadFieldsNames[index] + "</font>"; 
    
    this.PrevTableHeadIndex = index;
  };
  
  this.PrevTableRowIndex = 0;
  
  this.UsersTableSetActiveRow = function(index)
  {
	var o = $("resultUserTableRow" + String(index));
	var o2 = $("resultUserTableRow" + String(this.PrevTableRowIndex));
    
	if(this.PrevTableRowIndex == index) return;
  
    if(index != -1) 
    {
    	o.style.background = "#D1EDBB";
    }
    if(this.PrevTableRowIndex != -1)
    {
    	if(this.PrevTableRowIndex % 2) o2.style.background = "rgb(238,238,238)";
    	else  o2.style.background = "rgb(255,255,255)";
    }
    
    this.PrevTableRowIndex = index;
  };
  
  this.UsersTableSetRowDisplay = function(index, BDisplay)
  {
    if(BDisplay)
    {
      document.getElementById("resultUserTableRow" + String(index)).style.visibility = "visible";
      if(window.navigator.appName == "Microsoft Internet Explorer")
      {
        document.getElementById("IDRatingTableSpeedBPre" + String(index)).style.visibility = "visible";
        document.getElementById("IDRatingTableSpeedBPost" + String(index)).style.visibility = "visible";
        document.getElementById("IDRatingTableErrorsBPre" + String(index)).style.visibility = "visible";
        document.getElementById("IDRatingTableErrorsBPost" + String(index)).style.visibility = "visible";
        document.getElementById("IDRatingTableRatingBPre" + String(index)).style.visibility = "visible";
        document.getElementById("IDRatingTableRatingBPost" + String(index)).style.visibility = "visible";
      }
    }
    else
    {
      document.getElementById("resultUserTableRow" + String(index)).style.visibility = "hidden";
      if(window.navigator.appName == "Microsoft Internet Explorer")
      {
        document.getElementById("IDRatingTableSpeedBPre" + String(index)).style.visibility = "hidden";
        document.getElementById("IDRatingTableSpeedBPost" + String(index)).style.visibility = "hidden";
        document.getElementById("IDRatingTableErrorsBPre" + String(index)).style.visibility = "hidden";
        document.getElementById("IDRatingTableErrorsBPost" + String(index)).style.visibility = "hidden";
        document.getElementById("IDRatingTableRatingBPre" + String(index)).style.visibility = "hidden";
        document.getElementById("IDRatingTableRatingBPost" + String(index)).style.visibility = "hidden";
      }
    } 
  };
  
  this.TrimUserName = function(name)
  {
    if(name.length > 30)
      return name.slice(0, 30) + "&hellip;";
    return name;
  };
  
  this.UsersTableSetUser = function(index, pos, user, speed, numOfErrors, rating)
  {
    document.getElementById("resultUserTablePosition" + index).innerHTML = pos;
    document.getElementById("resultUserTableUserName" + index).innerHTML = 
      "<span style=\"cursor: pointer\" onclick=\"OpenNewWindow('" + this.GetRatingTableRowUserRef(index) + "')\">" + 
            this.TrimUserName(user) + "</span>";
    document.getElementById("resultUserTableSpeed" + index).innerHTML = speed;
    document.getElementById("resultUserTableNumOfErrors" + index).innerHTML = numOfErrors;
    document.getElementById("resultUserTableRating" + index).innerHTML = rating;
  };
  
  this.SetResultMessage = function(message)
  {
    var message_ = message.replace(/\n/,"");
    
    var photoText = "";
    if(PhotoInfo.IsHint(ExerciseInfo.LessonNumber, ExerciseInfo.TypeOfExercise, ErrorObj.GetNumOfMistakes()))
         photoText = "<span style='font-weight: bold; font-size: 10pt'>На фото: " + PhotoCaptions[parseInt(ExerciseInfo.LessonNumber) + 1] + "</span><hr>";
    
    document.getElementById("resultMessage").innerHTML = photoText + message_.replace(/\n/g,"<br><div class='resultAddSpace'> </div>");
  };
  
  this.SetNumOfPages = function(numOfPages)
  {
    this.NumOfPages = numOfPages;
  };
  
  this.SetCurPage = function(curPage)
  {
    this.CurPage = curPage;
  };
  
  this.GetCurPage = function()
  {
    return this.CurPage;
  };
  
  this.SetCrit = function(col)
  {
    switch(col)
    {
    case 0:
      this.CurCrit = this.CritTyper;
      break;
    case 1:
      this.CurCrit = this.CritSpeed;
      break;
    case 2:
      this.CurCrit = this.CritNumOfMistakes;
      break;
    case 3:
      this.CurCrit = this.CritRating;
      break;
    default:
      alert("Exception: SetCrit() : wrong criterion '" + col + "' was specified");  
    }
    
    this.UsersTableSetActiveField(col+1);
  };
  
  // private function : do not call directly
  this.UpdateUserTable = function()
  {
    this.UsersTableSetActiveRow(-1); // clear selection
    for(var i = 0; i < this.nPageUsers; i++)
    {
      //alert("i="+i);
      this.UsersTableSetUser(i, i+1 + this.nPageUsers*this.GetCurPage(), RatingInfo.User[i], RatingInfo.AvarageSpeed[i], 
              RatingInfo.Errors[i], RatingInfo.Rating[i]);
      if(RatingInfo.User[i] != "")  this.UsersTableSetRowDisplay(i, true);
      else  this.UsersTableSetRowDisplay(i, false);
      if(RatingInfo.Active[i] == "1")             
      {
        this.UsersTableSetActiveRow(i);
      }
    }    
    
    this.SetNumOfPages(RatingInfo.NumOfPages);
  };
  
  this.RequestUserTable = function() /*This function may require some time (use AJAX request)*/
  { /*this.Crit, this.CurPage*/
      
      if(this.CurCrit == this.CritTyper)
      {
        RequestRatingTablePage(this.ExerciseID, this.CurPage + 1, "name");
      }
      else
      if(this.CurCrit == this.CritSpeed)
      {
        RequestRatingTablePage(this.ExerciseID, this.CurPage + 1, "avg_speed");
      }
      else
      if(this.CurCrit == this.CritNumOfMistakes)
      { 
        RequestRatingTablePage(this.ExerciseID, this.CurPage + 1, "errors");
      } 
      else
      if(this.CurCrit == this.CritRating)
      {
        RequestRatingTablePage(this.ExerciseID, this.CurPage + 1, "rating");
      }
      else
      {
        alert("Exception: UpdateUserTable() : object has wrong .CurCrit");
        return;
      }
  };
  
  this.Selector = {
	black: function(arri)
	{
	  return "<FONT COLOR='black'>" + arri + "</FONT>&nbsp;";
	},
    blue: function(i, arri)
    {
		return "<FONT COLOR='#3B91D3'><span onclick='UpdatePageNumber(" + i + 
        ");' style='cursor: pointer'><U>" + arri + "</U></span></FONT>&nbsp;";
    }
  };
  
  this.ResultPageSelector = function()
  {
    var res = "";
    var i;
    
    if(this.NumOfPages < 2) return "";
    
    var arr = USelector.Get(this.CurPage, this.NumOfPages, 10);
    
    var s = "";
    for(var i in arr)
    {
    	s += arr[i] + " ";
    }
//    alert("s="+s);
    
    var j = USelector.Min;
    
    if(USelector.Min > 0)
    	res += this.Selector.blue(0, "1");
    
    for(var i in arr)
    {
    	if(j == this.CurPage)
    		res += this.Selector.black(arr[i]);
    	else
    		res += this.Selector.blue(j, arr[i]);
    	j++;
    }
    
    if(j < USelector.MaxPg)
    	res += this.Selector.blue(USelector.MaxPg, String(USelector.MaxPg + 1));
    
    return res;
  };
  
  this.SetErrorsCritical = function(bVal)
  {
    var obj = document.getElementById("IDResultHeader");
    var objScrollDiv = document.getElementById('resultMessage');
    var objCornerTopLeft = document.getElementById('IDTdResultMessageCornerTopLeft');
    var objBkTop = document.getElementById('IDTdResultMessageBkTop');
    var objCornerTopRight = document.getElementById('IDTdResultMessageCornerTopRight');
    var objBkLeft = document.getElementById('IDTdResultMessageBkLeft');
    var objBkRight = document.getElementById('IDTdResultMessageBkRight');
    var objCornerBottomLeft = document.getElementById('IDTdResultMessageCornerBottomLeft');
    var objBkBottom = document.getElementById('IDTdResultMessageBkBottom');
    var objCornerBottomRight = document.getElementById('IDTdResultMessageCornerBottomRight');
     
    if(bVal)
    {
      objScrollDiv.className = "ClassResultTextRed";
      obj.style.color = "#D00000";  
      objCornerTopLeft.className = "exResultMessageCornerTopLeftRed";
      objBkTop.className = "exResultMessageBkTopRed";
      objCornerTopRight.className = "exResultMessageCornerTopRightRed";
      objBkLeft.className = "exResultMessageBkLeftRed";
      objBkRight.className = "exResultMessageBkRightRed";
      objCornerBottomLeft.className = "exResultMessageCornerBottomLeftRed";
      objBkBottom.className = "exResultMessageBkBottomRed";
      objCornerBottomRight.className = "exResultMessageCornerBottomRightRed";  
    }
    else
    {
      objScrollDiv.className = "ClassResultText";
      obj.style.color = "#5FAC20";
      objCornerTopLeft.className = "exResultMessageCornerTopLeft";
      objBkTop.className = "exResultMessageBkTop";
      objCornerTopRight.className = "exResultMessageCornerTopRight";
      objBkLeft.className = "exResultMessageBkLeft";
      objBkRight.className = "exResultMessageBkRight";
      objCornerBottomLeft.className = "exResultMessageCornerBottomLeft";
      objBkBottom.className = "exResultMessageBkBottom";
      objCornerBottomRight.className = "exResultMessageCornerBottomRight";
    }
  }; 
  
  this.ShowGoToLessonList = function(bVal)
  {
    var obj = document.getElementById("IDResPGoToLessonList");
    
    if(bVal)  obj.style.display = "";
    else  obj.style.display = "none";  
  };
  
  this.ShowContinue = function(bVal)
  {
    var obj = document.getElementById("IDResPContinue");
    if(bVal)  obj.style.display = "";
    else  obj.style.display = "none";    
  };
  
  function Set()
  {                        
    CloseRedoObj.Show(false);
    ShowMainWindow(false);
  
    var obj = document.getElementById(this.BlockName); 
    
    var str = this.GetContent();

    str += '</TABLE>';

    obj.innerHTML = str;
    
    switch(GetExType())
    {
    case 0: case 4:
      this.SetRatingTableDisplay(false);
      this.SetPanelRatingDisplay(false);
      this.SetPanelErrorsDisplay(false);
      this.SetMiksanatikDisplay(true);
      this.SetLessonSelectorDisplay(false);
      this.ShowRating(false);
      this.ShowToday(true);
      this.ShowResSpeedSpan(false);
      break;
    case 5:
      this.SetRatingTableDisplay(false);
      this.SetPanelRatingDisplay(false);
      this.SetPanelErrorsDisplay(false);
      this.SetMiksanatikDisplay(false);
      this.SetLessonSelectorDisplay(false);
      this.ShowRating(false);
      this.SetLessonSelectorDisplay(false);
      this.ShowToday(true);
      this.ShowResSpeedSpan(false);
      alert("case 5");
      break;
    case 32:
      this.SetRatingTableDisplay(false);
      this.SetPanelRatingDisplay(false);
      this.SetPanelErrorsDisplay(true);
      this.SetMiksanatikDisplay(true);
      this.SetLessonSelectorDisplay(false);
      this.ShowRating(false);
      this.ShowToday(false);
      this.FillResSpeedSpan();
      if(IsUserLogin())
    	  this.ShowResSpeedSpan(true);
      else
    	  this.ShowResSpeedSpan(false);
//      this.SetRepeatSpan('<a onclick="UnsetResultPageClose();" style="cursor: pointer"><font color="#AB0000"><U><b><i>Закрыть</i></b></U></font></a>');
      break;      
    case 2:
      this.SetPanelSpeedDisplay(false);
      this.SetRatingTableDisplay(true);
      this.SetPanelRatingDisplay(true);
      this.SetPanelErrorsDisplay(false);
      this.SetMiksanatikDisplay(false);
      this.SetLessonSelectorDisplay(true);
      this.ShowRating(true);
      this.ShowToday(true);
      this.ShowResSpeedSpan(false);
      break;
    case 3:
      this.SetRatingTableDisplay(true);
      this.SetPanelRatingDisplay(true);
      this.SetPanelErrorsDisplay(false);
      this.SetMiksanatikDisplay(false);
      this.SetLessonSelectorDisplay(true);
      this.ShowRating(true);
      this.ShowToday(true);
      this.ShowResSpeedSpan(false);
      break;
    case 6:
      this.SetRatingTableDisplay(true);
      this.SetPanelRatingDisplay(true);
      this.SetPanelErrorsDisplay(false);
      this.SetMiksanatikDisplay(false);
      this.SetLessonSelectorDisplay(true);
      this.ShowRating(true);
      this.ShowToday(true);
      this.ShowResSpeedSpan(false);
      break;
    case 7:           
      this.SetRatingTableDisplay(true);
      this.SetPanelRatingDisplay(true);
      this.SetPanelErrorsDisplay(false);
      this.SetMiksanatikDisplay(false);
      this.SetLessonSelectorDisplay(false);
      this.ShowRating(true);
      this.ShowToday(true);
      this.ShowResSpeedSpan(false);
      break;
    case 8:
      this.SetRatingTableDisplay(true);
      this.SetPanelRatingDisplay(true);
      this.SetPanelErrorsDisplay(true);
      this.SetLessonSelectorDisplay(false);
      this.ShowResSpeedSpan(false);
      break;
    default:
      this.SetRatingTableDisplay(true);
      this.SetPanelRatingDisplay(true);
      this.SetPanelErrorsDisplay(true);
      this.ShowResSpeedSpan(false);
      
// means for usual exercise (type 1)      
      if(ErrorObj.GetNumOfMistakes() > ErrorObj.GetMaxNumOfMistakes())
        this.SetErrorsCritical(true);
      else
        this.SetErrorsCritical(false);
      
      this.SetMiksanatikDisplay(false);
      this.SetLessonSelectorDisplay(true);
      this.ShowRating(true);
      this.ShowToday(true);
    }
    
    this.HideResultPanelIfEmpty();
//    this.HideTodayIfEmpty();
    obj.style.top = this.WindowTop;
    obj.style.left = this.WindowLeft;
    obj.style.display = "";
    
    if(BScreenResolutionLowRegime) document.getElementById('IDResultToday').style.display = "none";
    
    document.getElementById(this.ExerciseBlockName).style.display = "none";
    
    ResultImg.Update(ExerciseInfo.LessonNumber, ExerciseInfo.TypeOfExercise, ErrorObj.GetNumOfMistakes());
    
    var bUCptNotEmp = !UserCaption.IsEmpty();
    this.ShowGoToLessonList(bUCptNotEmp); 
    this.ShowContinue(bUCptNotEmp); 
    
    this.bSet = true;
  }

this.ShowRefToLast = function(bVal)
{
   var o = document.getElementById("IDRefToLast");
   if(bVal) o.style.visibility = "visible";
   else
      o.style.visibility = "hidden";
};

this.SetRefToLast = function(ref)
{
  var o = document.getElementById("IDRefToLast");  
  o.innerHTML = "<a onclick='UnsetResultPageClose(\""+ref+"\")'><nobr>К последнему доступному</nobr></a>";  
};

this.IsResultPanelSpeedDisplay = function()
{
  if(document.getElementById('IDResultPanelSpeed').style.display == "none") return false;
  return true;
};
this.IsResultPanelErrorsDisplay = function()
{
  if(document.getElementById('IDResultPanelErrors').style.display == "none") return false;
  return true;
};
this.IsResultPanelRatingDisplay = function()
{
  if(document.getElementById('IDResultPanelRating').style.display == "none") return false;
  return true;
};

this.ShowResultPanel = function(bVal)
{
  var obj = document.getElementById("IDResultPanel");
  if(bVal)  obj.style.display = "";
  else    obj.style.display = "none";
};

this.HideResultPanelIfEmpty = function()
{
  if(
    this.IsResultPanelSpeedDisplay() == false  &&
    this.IsResultPanelErrorsDisplay() == false  /*&&
    this.IsResultPanelRatingDisplay() == false*/
  )
  {
    this.ShowResultPanel(false);
  }
};
/*this.HideTodayIfEmpty = function()
{

}*/

  
this.Unset = function()
{
  this.ShowResBottomCaptions(false);
  document.getElementById(this.BlockName).style.display = "none";
  document.getElementById(this.ExerciseBlockName).style.display = "";
  
  this.bSet = false;
  FlashInput.SetFlashLoaded(false);
  if(!isIe6()) 
  {
    FlashInput.Reset();
  }
  
  if(window.navigator.appName == "Opera")
  {
    ResetKeyboard();
  }                                                                  
  
  CloseRedoObjRes.Show(false);
  CloseRedoObj.Show(true);
  
  ResetBegTime();
};
  
this.BSetRefToNext = false;  
  
this.SetRefToNextExercise = function(ref)
{
  ResultInfo.RefToNext = ref;
  var obj = document.getElementById('IDToNextExercise'); 
  
    if(this.BSetRefToNext == false)
    {
      obj.innerHTML =
        '<a style="cursor: pointer" onclick="CloseOrGoToPage(\'' + ResultInfo.RefToNext + 
        '\');"><font color="#5FAC20"><U><b><i>Продолжить</i></b></U></font></a>';
      this.BSetRefToNext = true;
    }
};  

this.RefToNextHasSet = function(bVal)
{
  this.BSetRefToNext = bVal;
};

this.IsSetRefToNext = function()
{
  return this.BSetRefToNext;
};

this.ShowRating = function(bVal)
{
  try{
    if(bVal)
    {
      document.getElementById("IDRatingTable").style.display="";
      document.getElementById("IDStrTaskRating").style.display="inline";
      document.getElementById("IDTaskNumber").style.display="inline";
    }
    else
    {
      document.getElementById("IDRatingTable").style.display="none";
      document.getElementById("IDStrTaskRating").style.display="none";
      document.getElementById("IDTaskNumber").style.display="none";
    }
  }
  catch(e){}
};

this.SetLessonSelectorDisplay = function(bVal)
{
  try
  {
    if(bVal) document.getElementById("IDResLsnSel").style.display="inline";
    else document.getElementById("IDResLsnSel").style.display="none";
  }
  catch(e){}
};

this.SetMiksanatikDisplay = function(bVal)
{

};
  
this.SetPanelErrorsDisplay = function(bVal)
{
  if(bVal)  document.getElementById('IDResultPanelErrors').style.display = "";
  else  document.getElementById('IDResultPanelErrors').style.display = "none";  
}; 

this.SetResultPanelDisplay = function(bVal)
{
  if(bVal)  document.getElementById('IDResultPanel').style.display = "";
  else  document.getElementById('IDResultPanel').style.display = "none";  
};

this.SetPanelSpeedDisplay = function(bVal)
{
  if(bVal)  document.getElementById('IDResultPanelSpeed').style.display = "";
  else  document.getElementById('IDResultPanelSpeed').style.display = "none";    
};

this.SetPanelRatingDisplay = function(bVal)
{
  if(bVal)  document.getElementById('IDResultPanelRating').style.display = "";
  else  document.getElementById('IDResultPanelRating').style.display = "none"; 
};
  
this.SetRatingTableDisplay = function(bVal)
{
  if(bVal)  
  {
    document.getElementById('IDRatingTable').style.display = "inline";
  }
  else  document.getElementById('IDRatingTable').style.display = "none";   
};

this.SetSpeedTestRegisterEnterTrDisplay = function(bVal)
{
  if(bVal)  
  {
    document.getElementById('IDSpeedTestRegisterEnterTr').style.display = "";
  }
  else  document.getElementById('IDSpeedTestRegisterEnterTr').style.display = "none";
};

this.PostForm = function()
{
  ShowMainWindow(true);
  CloseRedoObjRes.Show(true); // show result key for opera
};

this.GetUserProfilePath = function()
{
  return this.UserProfilePath; 
};

this.GetRatingTableRowUserRef = function(userNum)
{
  return this.GetUserProfilePath() + RatingInfo.UserId[userNum] + "/";
};

this.GetRatingTableRow = function(index)
{
  var scls = "";	
  if((index % 2) == 1) scls = "class='bkGray'";
	
  return  '				<TR VALIGN=CENTER ' + scls + ' id="resultUserTableRow' + index + '">' +
'					<TD WIDTH=20% align="center">' +
'						<FONT Size=2><B><span id="resultUserTablePosition' + index + '">11</span></B></FONT></TD>' +
'					<TD WIDTH=20% align="CENTER">' +
'						<FONT Size=2><U><nobr><span id="resultUserTableUserName' + index + '">Луноход 1</span></nobr></U></FONT></TD>' +
'					<TD WIDTH=20% align="CENTER">' +
'					' +
'						<table><tr><td><b class="classGreenB" id="IDRatingTableSpeedBPre' + index + '"></b>' +
'            <div display="inline" class="classGreenDiv"><FONT Size=2><span id="resultUserTableSpeed' + index + '">000</span></FONT></div>' +
'            <b class="classGreenB" id="IDRatingTableSpeedBPost' + index + '"></b></td></tr></table></TD>' +
'					<TD WIDTH=20% align="center">' +
'						<table><tr><td><b class="classRedB" id="IDRatingTableErrorsBPre' + index + '"></b>' +
'            <div display="inline" class="classRedDiv"><FONT Size=2><span id="resultUserTableNumOfErrors' + index + '">121</span></FONT></div>' +
'            <b class="classRedB" id="IDRatingTableErrorsBPost' + index + '"></b></td></tr></table></TD>' +
'					<TD WIDTH=20% align="center">' +
'						<table><tr><td><b class="classBlueB" id="IDRatingTableRatingBPre' + index + '"></b>' +
'            <div display="inline" class="classBlueDiv"><FONT Size=2><span id="resultUserTableRating' + index + '">80</span></FONT></div>' +
'            <b class="classBlueB" id="IDRatingTableRatingBPost' + index + '"></b></td></tr></table></TD>' +
'				</TR>';
};

//

this.TabSpeedTable = function()
{
  return '<table width="100%" height="100%" border="0" cellpadding="1" cellspacing="0" id="IDTableSpeedRes" class="noWrap">';
};

this.TabSpeedUntable = function()
{
  return '</table>';
};

this.TabSpeedHead = function()
{
  return '<tr style="text-align: left;" valign="bottom"><td>&nbsp;</td><td class="GreenGeorgia" colspan="4"><div style="margin-top: 0px">Статистика</div></td></tr><tr><td colspan="5" id="IDResSpeedSel">... 1 2 ...</td></tr>';
};

this.TabSpeedHead2 = function()
{
  return '<tr valign="middle" style="font-size: 14pt; text-align: center"><td rowspan="'+String(this.nPageUsers+1)+'" valign="bottom"><div class="margRight10">' + this.Tables.Today() + '</div></td><td width="15%"></td><td width="35%"><div class="marginTopBottom5"><span id="IDResHeadDate" onclick="ResultPage.ResSpdTabClck(this.id)" class="resultUserTableHeadWithoutDashed">Дата и время</span></div></td><td width="25%"><div class="marginTopBottom5"><span id="IDResHeadSpeed" class="resultUserTableHeadDashed" onclick="ResultPage.ResSpdTabClck(this.id)">Скорость</span></div></td><td width="25%"><div class="marginTopBottom5"><span id="IDResHeadErrors" class="resultUserTableHeadDashed" onclick="ResultPage.ResSpdTabClck(this.id)">Ошибок</span></div></td></tr>';
};


this.ResSpdIds = new Array("IDResHeadDate", "IDResHeadSpeed", "IDResHeadErrors");
this.ResSpdTabClck = function(id)
{	
	var j = 0;
	for(i in this.ResSpdIds)
	{
		var o = document.getElementById(this.ResSpdIds[i]);
		if(id == this.ResSpdIds[i])
		{
			o.className = 'resultUserTableHeadWithoutDashed';
			ResultSpeedTestInfo.nCrit = j;
		}
		else
			o.className = 'resultUserTableHeadDashed';
		
		j++;
	}
	UpdatePageNumber(ResultSpeedTestInfo.nPage);
};

this.GetIDResSpeedPos = function(i) { return "IDResSpeedPos" + String(i); };
this.GetIDResSpeedDate = function(i) { return "IDResSpeedDate" + String(i); };
this.GetIDResSpeedSpeed = function(i) { return "IDResSpeedSpeed" + String(i); };
this.GetIDResSpeedErrors = function(i) { return "IDResSpeedErrors" + String(i); };
this.GetIDTabSpeedRow = function(i) { return "IDResTabSpeedRow" + String(i); };
this.ResSpeedPos = function(i) { return document.getElementById(this.GetIDResSpeedPos(i)); };
this.ResSpeedDate = function(i) { return document.getElementById(this.GetIDResSpeedDate(i)); };
this.ResSpeedSpeed = function(i) { return document.getElementById(this.GetIDResSpeedSpeed(i)); };
this.ResSpeedErrors = function(i) { return document.getElementById(this.GetIDResSpeedErrors(i)); };
this.SetResSpeedPos = function(i, t) { this.ResSpeedPos(i).innerHTML = t; };

this.TabSpeedVisArr = new Array(true, true, true, true, true, true, true, true);
this.SetTabSpeedRowVis = function(i, v) 
{
	if(v == this.TabSpeedVisArr[i]) return;

	if(v)
	{
		document.getElementById(this.GetIDTabSpeedRow(i)).style.visibility = "visible";
		this.TabSpeedVisArr[i] = true;
	}
	else
	{
		document.getElementById(this.GetIDTabSpeedRow(i)).style.visibility = "hidden";
		this.TabSpeedVisArr[i] = false;
	}
};

this.SetResSpeedDate = function(i, t) {
	try
	{
		this.ResSpeedDate(i).innerHTML = t;
	}
	catch(e)
	{
		alert("this.ResSpeedDate="+this.ResSpeedDate);
	}
};
this.SetResSpeedSpeed = function(i, t) { this.ResSpeedSpeed(i).innerHTML = t; };
this.SetResSpeedErrors = function(i, t) { this.ResSpeedErrors(i).innerHTML = t; };

this.TabSpeedBody = function()
{
  var s = "";
  
  var cls = "class='bkGray'";
  
  for(var i = 0; i < this.nPageUsers; i++)
  {
	if((i % 2) == 1) scls = cls;
	else scls = "";
		
    s += '<tr ' + scls + ' id="' + this.GetIDTabSpeedRow(i) + '"><td align="left">' + this.TabSpeedColPos(i) + '</td>' +
          '<td align="center">' + this.TabSpeedCol(this.GetIDResSpeedDate(i)) + '</td>' +
          '<td align="center">' + this.TabSpeedCol(this.GetIDResSpeedSpeed(i), "green") + '</td>' +
          '<td align="center">' + this.TabSpeedCol(this.GetIDResSpeedErrors(i), "red") + '</td>' + '</tr>';
  }  
  
  return s; //###                                                                
};

this.TabSpeedColPos = function(i)
{
  return '<b><div class="leftLitMarg" id="' + this.GetIDResSpeedPos(i) + '"></div></b>'; 
};

this.TabSpeedCol = function(id, cls_)
{
  if(cls_ == undefined)
  {
	  var sRes = '<b><span id="' + id + '"></span></b>';
	  return sRes;
  }
	  
  var cls = "classGreen"; 
  if(cls_ == "blue")  cls = "classBlue";
  else
  if(cls_ == "red")  cls = "classRed";

  var clsB = cls + "B"; 
  var clsDiv = cls + "Div";
  
  var sRes = '<table><tr><td><b class="' + clsB + '"></b><div id="' + id + '" class="' + clsDiv + '"></div><b class="' + clsB + '"></b></td></tr></table>';
  
  return sRes; 
};

this.TabSpeed = function()
{
  return this.TabSpeedTable() + this.TabSpeedHead() + this.TabSpeedHead2() + this.TabSpeedBody() + this.TabSpeedUntable();
};

this.FillResSpeedSpan = function() { document.getElementById('IDResSpeedTable').innerHTML = this.TabSpeed(); };

this.ShowResSpeedSpan = function(b) { if(b)  document.getElementById('IDResSpeedTable').style.display = "";
										else
											document.getElementById('IDResSpeedTable').style.display = "none"; };
//

this.FormRatingTableRows = function()
{
  var retS = "";
  for(var i = 0; i < this.nPageUsers; i++)
    retS += this.GetRatingTableRow(i);
  return retS;
};

this.ShowToday = function(bShow)
{
  if(bShow)
    document.getElementById('IDResultToday').style.display = "";
  else
    document.getElementById('IDResultToday').style.display = "none";
};

this.ShowRepeat = function(bShow)
{
  if(bShow)
    document.getElementById('IDResultRepeat').style.display = "";
  else
    document.getElementById('IDResultRepeat').style.display = "none";  
};

this.SetRepeatSpan = function(html_)
{
  document.getElementById('IDResultRepeat').innerHTML = html_;
};

this.ScreenResFragment2 = function()
{
  if(window.navigator.appName == "Microsoft Internet Explorer"  ||  BScreenResolutionLowRegime == false)
  {
    return  '<TR>' +
'		<TD COLSPAN=4 valign="middle">' +
'<table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td VALIGN="middle" ALIGN="center">' +
'			<FONT FACE="Georgia"><I><span style="font-size: 20pt; color: #5FAC20" id="IDResultHeader">Поздравляю!</span></I></FONT></td>' +
'<td align="right" width="91px">' + CloseRedoObjRes.Generate() + '</td></tr></table></TD>' +
'	</TR>';
  }
  else
  {
    return '<TR>' +
'		<TD COLSPAN=4 valign="middle">' +
'<table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td VALIGN="middle" ALIGN="center">' +
'			<FONT FACE="Georgia"><FONT SIZE=6><I><span style="font-size: 20pt; color: #5FAC20" id="IDResultHeader">Поздравляю!</span></I></FONT></td>' +
'<td align="right" width="91px">' + CloseRedoObjRes.Generate() + '</td></tr></table></TD>' +
'	</TR>'; 
  }
};
this.SetResultMessageHeight = function(val)
{
  var obj = document.getElementById('resultMessage');
  if(obj != null)
    obj.style.height = val;
  this.ResultMessageHeight = val;
};
this.GetResultMessageHeight = function()
{
  return this.ResultMessageHeight;
};

this.SetResultPageContinueRef = function(caption)
{
  document.getElementById('IDResultPageContinueRef').innerHTML = caption;
};

this.ShowResBottomCaptions = function(bVal)
{
  var o = document.getElementById('IDResBottomCaptions');
  
  if(bVal)
    o.style.visibility = "visible";
  else
    o.style.visibility = "hidden";
    
  if(ResultInfo.RefToLast != "")
  {
    ResultPage.ShowRefToLast(true);     
  }
  else
    ResultPage.ShowRefToLast(false);
};

this.ExTable = {
	Show: function()
	{
		$('IDResExTable').style.display = "";
	},
	Hide: function()
	{
		$('IDResExTable').style.display = "none";
	}
};

this.Tables = {
		Today: function()
		{
	return '		<span id="IDResultToday"><table style="width: 231px; table-layout: fixed; margin-top: 5px;" border="0" cellpadding="0" cellspacing="0">' +
	'		<tr height="18">' +
	'			<td background="/study/test/today_0.png" width="4"/>' +
	'			<td width="64" style="background: url(/study/test/today_1.png) no-repeat"/>' +
	'			<td style="background: url(/study/test/today_2.png) repeat-x"/>' +
	'			<td background="/study/test/today_4.png" width="4"/>' +
	'		</tr><tr>' +
	'			<td background="/study/test/today_5.png"/>' +
	'			<td colspan="2">' +
	'				<table border="0" width="100%">' +
	'        <tr><td width="64" align="left">' +
	'				<span class="classSmallGrayPanelFont"><nobr>Пройдено заданий</nobr></span>' +
	'				</td><td  align="right">' +
	'				<div id="resultSpeed"><table><tr><td><b class="classBlueB"></b>' +
	'            <div display="inline" class="classBlueDiv"><FONT Size=2><span id="resultPanelExercisesPassed">121</span></FONT></div>' +
	'            <b class="classBlueB"></b></td></tr></table></div>' +
	'			</td></tr>' +
	'			  <tr><td width="64" align="left">' +
	'				<span class="classSmallGrayPanelFont"><nobr>Средняя скорость</nobr></span>' +
	'				</td><td  align="right">' +
	'				<div id="resultSpeed"><table><tr><td><b class="classGreenB"></b>' +
	'            <div display="inline" class="classGreenDiv"><FONT Size=2><span id="resultPanelAvarageSpeed">121</span></FONT></div>' +
	'            <b class="classGreenB"></b></td></tr></table></div>' +
	'			</td></tr>' + 
	'			<tr><td width="64" align="left">' +
	'				<span class="classSmallGrayPanelFont"><nobr>Всего набрано</nobr></span>' +
	'				</td><td  align="right">' +
	'				<div id="resultSpeed"><table><tr><td><b class="classBlueB"></b>' +
	'            <div display="inline" class="classBlueDiv"><FONT Size=2><span id="resultPanelTextTyped">121</span></FONT></div>' +
	'            <b class="classBlueB"></b></td></tr></table></div>' +
	'			</td></tr>' +
	'			<tr><td width="64" align="left">' +
	'				<span class="classSmallGrayPanelFont"><nobr>Всего ошибок</nobr></span>' +
	'				</td><td align="right">' +
	'				<div id="resultSpeed"><table><tr><td><b class="classRedB"></b>' +
	'            <div display="inline" class="classRedDiv"><FONT Size=2><span id="resultTotalNumOfErrors">121</span></FONT></div>' +
	'            <b class="classRedB"></b></td></tr></table></div>' +
	'			</td></tr>' +
	'      </table>' +
	'	  	</td>' +
	'			<td background="/study/test/today_9.png"/>' +
	'		</tr><tr height="4">' +
	'			<td background="/study/test/today_15.png"/>' +
	'			<td colspan="2" background="/study/test/today_17.png"/>' +
	'			<td background="/study/test/today_19.png"/>' +
	'		</tr></table></span> ';	
		}
};

this.GetContent = function()
{ 
  return '<TABLE WIDTH=900 BORDER="0" CELLPADDING=4 CELLSPACING=0  style="cursor: default;" id="IDMainResultTable">' +
'	<COL WIDTH=73*>' +
'	<COL WIDTH=73*>' +
'	<COL WIDTH=37*>' +
'	<COL WIDTH=73*>' +
this.ScreenResFragment2() +
'	<TR>' +
'<td colspan="4">'+
'<table border="0"><tr><td style="width:330px; height: 222px; vertical-align: top; background: url(/study/exercise/img/Result/frame.PNG) top left no-repeat"><img id="IDResultImg" style="position: relative; left: 9px; top: 9px; width: 297px; height: 202px" src="/study/exercise/img/Result/201.jpg"></td><td style="vertical-align: top">' +
'<FONT COLOR="#333333">' + 
this.ResultMessageEmptyField.HTMLPreResultMessageField() +
'<div id="resultMessage" class="ClassResultText"></div></FONT>' +
this.ResultMessageEmptyField.HTMLPostResultMessageField() +
'</td></tr></table></td>' +
'	</TR>' +
'	<TR height="30px">' +
'  <td colspan="4"><span id="IDResSpeedTable"></span>' +
'<span id="IDResExTable"><table style="margin-top: 10px" width="100%" height="100%" cellpadding="0px" cellspacing="0px" border="0px"> <tr valign="bottom"> <td width="29%"></td><TD WIDTH=29% align="left" valign="bottom">' +
'			<FONT COLOR="#5FAC20"><FONT FACE="Georgia"><FONT SIZE="5"><I><nobr><span id="IDStrTaskRating">Рейтинг по заданию</span>&nbsp;<span id="IDTaskNumber"></span></nobr></I></FONT></FONT></FONT></TD>' +
'		<TD COLSPAN=2 WIDTH=43% align="right" valign="middle">' +
'			<span id="IDResLsnSel" class="FntSlctor"></span></TD> </tr> </table> </span> </td>' +
'	</TR>' +
'	<TR>' +
'		<TD WIDTH=29% valign="top">' +
'		<table id="IDResultPanel" style="width: 231px; table-layout: fixed; margin-top: 5px;" border="0" cellpadding="0" cellspacing="0">' +
'		<tr height="18">' +
'			<td background="/study/test/result_0.png" width="4"/>' +
'			<td width="64" style="background: url(/study/test/result_1.png) no-repeat"/>' +
'			<td style="background: url(/study/test/result_2.png) repeat-x"/>' +
'			<td background="/study/test/result_4.png" width="4"/>' +
'		</tr><tr id="IDResultPanelSpeed">' +
'			<td background="/study/test/result_5.png"/>' +
'			<td colspan="2">' +
'				<table width="100%">' +
        '<tr><td width="64">' +
'				<span class="classSmallGrayPanelFont">Скорость</span>' +
'				</td><td align="right">' +
'				<div id="resultSpeed"><table><tr><td><b class="classGreenB"></b>' +
'            <div display="inline" class="classGreenDiv"><FONT Size=2><span id="resultPanelSpeed">000</span></FONT></div>' +
'            <b class="classGreenB"></b></td></tr></table></div>' +
'			</td></tr>' +
        '<tr><td width="64">' +
'				<span class="classSmallGrayPanelFont">Затрачено&nbsp;времени</span>' +
'				</td><td align="right">' +
'				<div id="resultTimeElapsed"><table><tr><td><b class="classBlueB"></b>' +
'            <div display="inline" class="classBlueDiv"><FONT Size=2><span id="IDResultTimeElapsed">&nbsp;</span></FONT></div>' +
'            <b class="classBlueB"></b></td></tr></table></div>' +
'			</td></tr>' +
'      <tr id="IDResultPanelErrors"><td width="64">' +
'				<span class="classSmallGrayPanelFont">Ошибок</span>' +
'				</td><td align="right">' +
'				<div id="resultNumOfMistakes"><table><tr><td><b class="classRedB"></b>' +
'            <div display="inline" class="classRedDiv"><FONT Size=2><span id="resultPanelNumOfErrors">121</span></FONT></div>' +
'            <b class="classRedB"></b></td></tr></table></div>' +
'			</td></tr>' +
'			<tr id="IDResultPanelRating"><td width="64" align="left">' +
'				<span class="classSmallGrayPanelFont">Рейтинг</span>' +
'				</td><td  align="right">' +
'				<div id="resultRating"><table><tr><td><b class="classBlueB"></b>' +
'            <div display="inline" class="classBlueDiv"><FONT Size=2><span id="resultPanelRating">121</span></FONT></div>' +
'            <b class="classBlueB"></b></td></tr></table></div>' +
'			</td></tr>' +
'      </table>' +
'	  	</td>' +
'			<td background="/study/test/result_9.png"/>' +
'		</tr><tr height="4">' +
'			<td background="/study/test/result_15.png"/>' +
'			<td colspan="2" background="/study/test/result_17.png"/>' +
'			<td background="/study/test/result_19.png"/>' +
'		</tr></table> ' +
'    ' +
'		</TD>' +
'		<TD id="IDUserTableTd" ROWSPAN=2 COLSPAN=3 WIDTH=71% align="center" style="vertical-align: bottom">' +
'<span id="IDRatingTableSpan">' +
'			<TABLE WIDTH="100%" HEIGTH="100%" BORDER=0 CELLPADDING=1 CELLSPACING=0 id="IDRatingTable">' +
'				<TR VALIGN=TOP height="25px">' +
'<TD WIDTH=22% align="center">' +
'						<FONT SIZE=4><span id="resultUserTablePosition" style="cursor: pointer">Позиция</span></FONT></p>' + 
'</TD>' +
'<TD WIDTH=42% align="center">' +
'<FONT SIZE=4><span id="resultUserTableTyper">Солист</span></FONT></p>' +
'</TD>' +
'<TD WIDTH=12% align="center">' +
'						<FONT SIZE=4><span id="resultUserTableSpeed" onclick="ClickResultUserTableSpeed();" style="cursor: pointer"><FONT class="resultUserTableHeadDashed">Скорость</FONT></span></FONT></p>' +
'</TD>' +
'<TD WIDTH=12% align="center">' +
'						<FONT SIZE=4><span id="resultUserTableNumOfErrors" onclick="ClickResultUserNumOfErrors();" style="cursor: pointer"><FONT class="resultUserTableHeadDashed">Ошибок</FONT></span></FONT></p>'+
'</TD>' +
'<TD WIDTH=12% align="center">' +
'						<FONT SIZE=4><span id="resultUserTableRating" onclick="ClickResultUserTableRating();" style="cursor: pointer"><FONT class="resultUserTableHeadDashed">Рейтинг</FONT></span></FONT></p>' +
'</TD>' +
'				</TR>' + this.FormRatingTableRows() +
'</TABLE></span>' +
'<img style="margin: 50px" id="IDResultMiksanatik" src="/study/exercise/img/Miksanatik/bigMainMiksanatik.PNG">' +
'<span id="IDResBottomCaptions" style="visibility: hidden"><table width="100%" height="30px" cellpadding="0" cellspacing="0" border="0"><tr>'+
'<td align="left" valign="bottom"><span style="visibility: hidden" id="IDRefToLast" class="ResultPageBotCaptRed"></span></td>'+
'<td align="right" valign="bottom" width="33%"><span id="IDResPGoToLessonList" class="ResultPageBotCaptBlue"><a onclick="GoToLessonList()"><nobr>К списку уроков</nobr></a></span></td>'+
'<td align="right" valign="bottom" width="33%"><span id="IDResPContinue" class="ResultPageBotCaptGreen"><a id="IDResultPageContinueRef" onclick="CloseOrGoToPage()"></a></span>'+
'</tr></table></span>' +
'		</TD>' +
'	</TR>' +
'	<TR VALIGN=top>' +
'		<TD WIDTH=29%>' + this.Tables.Today() +
'		</TD>' +
'	</TR>' +
'	<TR id="IDSpeedTestRegisterEnterTr" VALIGN="bottom">' +
'		<TD colspan="4" align="left">' +
'<span id="IDSpeedTestRegisterLabel"><p><span class="SpeedTestRegisterLabel"><a href="/profile/">Зарегистрируйтесь</a> в виртуальной школе &laquo;Соло на клавиатуре&raquo;.</span></p>'+
'<p><span class="SpeedTestEnterLabel">Если Вы уже зарегистрированы, <a href="/">войдите.</a></span></p></span></TD></TR>';
};
  
}

function convertSolo9Online(src)
{
  var i;
  
  if(src == ""  ||  src == undefined)  return "";
  src = StrDeleteSymbols(src, "\r");
  
  for(i = 0; i < src.length && (src.charAt(i) == '\n' || src.charAt(i) == '\r' || 
          src.charAt(i) == ' ' || src.charAt(i) == '\t'); i++);
        
  for(var j = i; j < src.length && src.charAt(j) != '…'; j++);
  
  for(; j >= 0 && (src.charAt(j) == '…' || src.charAt(j) == '\n' || src.charAt(j) == '\r' || 
    src.charAt(i) == ' ' || src.charAt(i) == '\t'); j--);
  
  var retS = "";
  for(var k = i; k <= j; k++)
  {
    if(k+1 < src.length)
      if(src.charAt(k) == ' ' && (src.charAt(k+1) == '\r' || src.charAt(k+1) == '\n') )  continue;
    if(src.charAt(k) != '\r')  retS += src.charAt(k);
  }
    
  retS += '\n';
  return retS;
}

function convertSolo9Online2(src)
{
  var retS = "";
  
  for(var i = 0; src.charAt(i) == '\n' || src.charAt(i) == '\r' || src.charAt(i) == ' '; i++);
  
  var s = "";
  for(; src.charAt(i) != '…'; i++)
  {
    //if(src.charAt(i) == 0x20  &&  src.charAt(i+1) == "\r\n"/*  &&  src.charCodeAt(i+1) == 0x0A*/)
    /*{
      retS += "\n";
      i += 2;
      continue;
    }*/
    retS += src.charAt(i);
  }
  return retS;
}

function FormatToStrings(strSrc, maxStrLen)
{
  var strRes = "";
  for(var i = 0; i < strSrc.length; i++)
  {
    var j = i + maxStrLen;
    if(j > strSrc.length) j = strSrc.length;
    else
    for(; strSrc.charAt(j) != " "; j--);
    strRes += strSrc.substr(i, j - i) + "\n";
    i = j;
  }
  return strRes;
}




var IDExercise = 0;
var BOpened = false;

//stop();

function SetWindowOpened(bVal)
{          
  BOpened = bVal;
}
function GetWindowOpened()
{
  return BOpened;
}

// debug constants...
var IsDebug = false;
var IsModeShowResult = true;
//

var IDUserCite = "IDCite";
var IDUserInput = "IDUserInput";
var IDUserInput2 = "IDUserInput2";
var CaretPos = 0;
var ExerciseText = "";
var ExerciseText2 = "";
var ReplacedExerciseText = "";
var keyboard = new TKeyboardInput(Type);
var bSymbolTyped = false;

function TimerSymbolTyped()
{
  bSymbolTyped = true;
}

var AfterErrorCounter = new TAfterErrorCounter("keyboardPlace", "AfterErrorCounter");

var CurTime = 0;
var LastKeyTime = 0;
var TypingTimeout = 1000;
var IsTimer = true;

var CloseRedoObj = new TCloseRedoObj("CloseRedoObj", "Close", "ClickBegExercise", "Закрыть", "К началу");
var CloseRedoObjRes = new TCloseRedoObj("CloseRedoObjRes", "CloseOrGoToPage", "RepeatExercise", "Далее", "Повторить");

var IDFingerZones = "IDFingerZones";
var IDFingerZonesTd = "IDFingerZonesTd"; 
var FingerZonesObj = new TFingerZones(IDFingerZones);

var UserName = "";
var UserCaption = new TUserCaption(UserName);

var TaskNum = "10.2";
var TaskCaption = new TTaskCaption(TaskNum);

var Miksanatik = new TMiksanatik();
var IDErrorSpan = "IDErrorSpan";
var ErrorObj = new TError(IDErrorSpan);
var Speed = new TSpeed();
var Progress = new TProgress();
var Time = new TTime();
var ResultPanel = new TResultPanel();
var MiksWaiter = new TMiksWaiter();

var IDCiteInputTable = 'CiteInputTable';
var IDCloseRedoFingersTable = 'CloseRedoFingersTable';

var InputField;
var InputField2;

var FlashInput = new TFlashInput();
var FlashKeyboard = new TFlashKeyboard();
var FlashError = new TFlashError();

var WindowTop = 0;
var WindowLeft = 0;

var BScreenResolutionLowRegime = false;
var BHighXYRatio = false;

var CaptionArrow = new TArrow("IDCaptionArrow0", "IDArrowDelimiter",0);

var ResultPage = new TResultPage(/*"IDResultPage"*/"ResultContent", "ExerciseContent");
var ResultImg = new TResultImg("IDResultImg", "/study/exercise/img/Result/", ".jpg");
var RESULT_RATING_TABLE = 0;
var RESULT_LITERACY = 1;
var RESULT_MIKSANATIK = 2;
var RESULT_NONE = 3;

function isIe()
{
  return (navigator.appName == "Microsoft Internet Explorer");
}

function isIe6()
{
  var appVersion = navigator.appVersion;
  if(appVersion.indexOf("MSIE") != -1)
  {
    var i = appVersion.indexOf("MSIE") + 4;
    
    while(isNaN(parseInt(appVersion.charAt(i)))) i++;
    
    for(var sd = ""; !isNaN(parseInt(appVersion.charAt(i))); i++)
      sd += appVersion.charAt(i);
      
    var d = parseInt(sd);
    
    if(d == 6) return true;
  }
  
  return false;
}

function isOpera()
{
  var sApp = navigator.appName;
  if(sApp.indexOf("Opera") != -1)
  {
    return true;
  }
  return false;
}

function getScrollY() {

       if( typeof( window.pageYOffset ) == 'number' ) {
               //Netscape compliant
               return window.pageYOffset;
       } else if( document.body && (document.body.scrollTop ) ) {
               //DOM compliant
               return document.body.scrollTop;
       } else if( document.documentElement && (document.documentElement.scrollTop ) ) {
               //IE6 Strict
               return document.documentElement.scrollTop;
       }
       return 0;
}

// exercise text get functions 
function GetCaretProgress()
{
  return 100.0*(CaretPos+1)/ExerciseText.length;
}
function ExNumOfLines()
{
  // count num of lines
  var begInd, i;
  
  for(i = 0, begInd = ExerciseText.indexOf('\n', begInd) + 1; begInd != 0; i++, begInd = ExerciseText.indexOf('\n', begInd) + 1);
         
  return i;  
}
//


InitForScreenResolution();

var BUserLogin = false;
function IsUserLogin()
{
  return (ExerciseInfo.UserName != "" || SpeedTestInfo.UserName != "");
}


function InitForScreenResolution()
{
// current variant commented... this is only a variant for low resolution
  BScreenResolutionLowRegime = !(screen.height >= 1024);
  BHighXYRatio = (screen.height * 1.6 <= screen.width);
  var numStr;
  if(!BScreenResolutionLowRegime)
  {
    numStr = 6;
    WindowTop = 50;
    ResultPage.nPageUsers = 8;
  }
  else
  {
    numStr = 5;
    WindowTop = -5;
    ResultPage.nPageUsers = 5;
  }
  
  WindowLeft = (screen.width - 1000) / 2;
  if(WindowLeft < 0)  WindowLeft = 10;
  
  
// low resoltion (delete later)
//
}


var BAutoTyping = false;

function GoToLastAvailable()
{
   document.location.href = "/study/10/#10.1";
}

function GoToLessonList()
{
   document.location.href = "/study/#"+ExerciseInfo.LessonNumber;
}

function CloseOrGoToPage(url)
{
//  alert("url="+url);

  if(GetExType() == 32)
  {
    UnsetResultPageClose();
    return;
  }

  var bCloseWindow = true;

  if(ResultInfo.RefToNext != "")
  {
    document.location.href = ResultInfo.RefToNext;
    return;  
    //bCloseWindow = false;
  }
  if(ResultInfo.IDNext != "")
  {
    var obj1 = document.getElementById("start"+ResultInfo.IDNext);
    var objActive = document.getElementById("start_link"+String(ResultInfo.IDNext));
    var objInactive = document.getElementById("start_link_inactive"+String(ResultInfo.IDNext));
    objActive.style.display = "";
    objInactive.style.display = "none";
    obj1.className = "start-exercise";
  }
  if(ResultInfo.IDRemove != "")
  {
    var obj = document.getElementById("start"+ResultInfo.IDRemove);
    obj.style.display = "none";
  }
  if(ResultInfo.IDAdd != "")
  {
    var obj = document.getElementById("start"+ResultInfo.IDAdd);
    var objText = document.getElementById("repeat"+ResultInfo.IDAdd);
    obj.style.display = "";
    objText.innerHTML = ResultInfo.TextRepeat; 
  }

  if(bCloseWindow)
  {
//    alert("bCloseWindow=true");
    UnsetResultPageClose();
    return;
  }

}

// for test grammar exercise
var CorrectWords = new Array();
//

function FillCorrectWords(textCorrect)
{
  var curWord = Array();
  for(var i = 0; i < textCorrect.length; i++)
  {
    if(textCorrect.charAt(i) != ' '  &&  textCorrect.charAt(i) != '\n')  
      curWord += textCorrect.charAt(i);
    else
    {
      CorrectWords.push(curWord);
      curWord = "";
    }  
  }
  //alert(CorrectWords);
}

// global function to change content of fields and panels


// FLASH. The information from flash

var FlashText = "", FlashText2 = "";

// flash event handlers

window.onblur = function()
{
  FlashInput.ShowTypeHere();
}

//

function IsWindows()
{
  return   navigator.appVersion.indexOf("Windows") != -1;
}
          
function GetCurLanguage()
{
  if(GetExType() == 32)
    return SpeedTestInfo.Language;
  
  return ExerciseInfo.Language;  
}          
                   
function fInputLoaded()
{
  //if(bDontExit != true) return;
 
  if(!FlashInput.IsFlashLoaded())
  {  
    setTimeout(fInputLoaded, 500);        
    return;
  }
  
  FlashInput.InitOS();
  		
  FlashInput.OnFlashLoaded();

  fSetRegime(GetExType());

  try
  {
    if(GetExType() != 32)
    { 
        SetExerciseText(ExerciseInfo.Text, ExerciseInfo.Text2);
    }
    else
    {
      if(isIe6())
      {
        SetExerciseText(FormatToStrings(SpeedTestInfo.Text, 40));
      }
      else
        RequestCiteSpeedUpdate();        
    }
  }
  catch(e)
  {
    alert(e.message);
  }

  Progress.SetLine(0);
  Progress.SetMaxLine(ExNumOfLines());
  Progress.Update(); 
}

function keyboardSetVal(listenerParam, val)
{
  if(window.flashKeyboard) 
  {
    window.document["flashKeyboard"].SetVariable("listener."+listenerParam, val);
  }
  else
	if(document.flashKeyboard)
  {
    document.flashKeyboard.SetVariable("listener."+listenerParam, val);
  } 
}

function keyboardSetDefaultLayout()
{
  KeyboardInfo.Layout = "rus";
  FlashKeyboard.SetLayout(KeyboardInfo.Layout); 
  FlashKeyboard.InitLanguage();
}

function KeyboardLayoutGot()
{
      if (req.readyState == 4) {
        if (req.status == 200) {
            var xmlDoc=req.responseXML.documentElement;
            KeyboardInfo.Layout = GetXMLElement(xmlDoc, "layout");
            FlashKeyboard.SetLayout(KeyboardInfo.Layout);  
            FlashInput.SetLayout(KeyboardInfo.Layout);          
        } else {
            alert("KeyboardLayoutGot: Could not receive data from server:\n" + req.statusText);
            //stop();
        }
    }    
}

function KeyboardParamsGot()
{
      if (req.readyState == 4) {
        if (req.status == 200) {
        
            var xmlDoc=req.responseXML.documentElement;
            
            KeyboardInfo.Layout = GetXMLElement(xmlDoc, "layout");
            KeyboardInfo.bNextKey = GetXMLElement(xmlDoc, "next_key");
            KeyboardInfo.bShowChars = GetXMLElement(xmlDoc, "show_chars");
            KeyboardInfo.bColored = GetXMLElement(xmlDoc, "keyboard_colored");  
                               
            FlashKeyboard.SetParamsLoaded(true);
                               
            FlashKeyboard.Activate();
            FlashKeyboard.InitLanguage();
            
            FlashInput.SetLayout(KeyboardInfo.Layout);          
            
            SetCurKeyboardKey();
            
//            FlashKeyboard.InitLanguage();                      
        } else {
            alert("KeyboardParamsGot: Could not receive data from server:\n" + req.statusText);
            //stop();
        }
    }   
}

function flashKeyboardChangeLayout(layout)
{
  FlashInput.ShowTypeHere();
  FlashInput.SetCanType(false);
  
  if(!IsWindows())
  {
    FlashInput.SetLayout(layout);  
  }
  
  if(UserCaption.GetUserName() != "")
    RequestSaveKeyboardParam("layout", layout);
  
  FlashInput.SetCanType(true);
  
  KeyboardInfo.Layout = layout;
                                          
  FlashKeyboard.SetLayoutChanged();
}                                   

function flashKeyboardChangeShowNextKey(bShowNextKey)
{
    RequestSaveKeyboardParam("next_key", bShowNextKey);
}
function flashKeyboardChangeColored(bColored)
{
    RequestSaveKeyboardParam("keyboard_colored", bColored);
}
function flashKeyboardChangeShowChars(bShowChars)
{
    RequestSaveKeyboardParam("show_chars", bShowChars);
}


function flashShowAboutLayout()
{
  window.open("/study/");
}

function flashInputLoaded()
{
  FlashInput.SetHighXYRatio(BHighXYRatio);
  FlashInput.SetFlashLoaded(true);
}

function flashFastError()
{
  ExerciseError(false);
}

function flashError(errorCh, index, sLayError)
{  
  Context.SetError(true);	
	
  FlashInput.SetCanType(false);
  ExerciseError(IsWrongLayout(errorCh));
  CaretPos = parseInt(index);
}

var NumOfTypedChars = 0;


function IsTypChError(ch)
{
  if(ch == String(null))  return false;
  else
    return true;
}

function flashType(typCh, newLinN)
{

  if(newLinN != null)
  {
    Progress.SetLine(parseInt(newLinN));
  }
  
  if(!IsTypChError(typCh))  
  {
    ExerciseStepForward(true);
    NumOfTypedChars++;
    Progress.SetProgress(GetCaretProgress());
    Progress.Update();
  }
  else
    ExerciseStepForward(false);
  
  if(IsExFinished())
  {
    fFinishExercise();
  }
    
  TimerSymbolTyped();
}

function flashErrorFix()
{
  FlashKeyboard.SetBkspMode(false);
}

function flashStepBackward(linN, isError)
{
  TimerSymbolTyped();
  
  ExerciseStepBackward(linN, isError);
}

function flashMiksErrFixed()
{
  CiteField.Restore();
  CiteField.Render();
  CiteField.Update();
}

function flashMiksFixErr()
{
  CiteField.SetErrorText("Исправьте последнюю ошибку!");
  CiteField.RenderError();
}

  function fFinishExercise()
  {
    switch(GetExType())
    {    
    case 0: case 4: case 7: case 8:
      PrepareResult(RESULT_MIKSANATIK);  
      return;
    case 3:
      BAutoTyping = false;
      PrepareResult(RESULT_MIKSANATIK);
      return;       
    case 5:
  // grammar test
      return;
    case 2: case 32:
       PrepareResult(RESULT_NONE);
       return;
    default:
       PrepareResult(RESULT_RATING_TABLE);      
    }
  }  
  
  function fSetRegime(typeOfExercise)
  {
    switch(typeOfExercise)
    {
      case 0: case 2:
        FlashInput.SetRegime("qerror");
        FlashInput.SetModeNormal();
        return;
      case 32:
        FlashInput.SetRegime("speedtest");
        FlashInput.SetModeNormal();
        return;
      case 1: case 6: case 8:
        FlashInput.SetRegime("error");
         FlashInput.SetModeNormal();
        return;
      case 3:
        FlashInput.SetRegime("autotype");
        FlashInput.SetModeNormal();
        return;
      case 4:
        FlashInput.SetRegime("skiperror");
        FlashInput.SetModeNormal();
        return;
      case 7:
        FlashInput.SetRegime("qerror");
        FlashInput.SetModeBottomFragments(); 
        return;
    }
  }

// ...

function IsExFinished()
{
  return  (CaretPos == ExerciseText.length);
}

function GetExType()
{
  return ExerciseInfo.TypeOfExercise;
}

function SetExerciseText(text_, text2_)
{ 
  var text, text2;

  FlashText = text_;
  FlashText2 = text2_;
  ExerciseText = FlashText; 
  
  if(GetExType() != 32)
  {
    text = convertSolo9Online2(text_);
    text2 = convertSolo9Online(text2_);
  }
  else
  {
    text = text_;   
  }
  switch(GetExType())
  {
  case 5:
    TextFragmentsData.SetBlockingWords(text); // set numbers of words not allowing to type if they are incorrect
    
    FlashInput.SetText1(text);
    FlashInput.SetText2(text2);
    
    text = StrDeleteSymbols(text, "«»"); 
    text2 = StrDeleteSymbols(text2, "«»");       
    FillCorrectWords(text2);
    break;
  case 7:
    FlashInput.SetText1(text);
    text = StrDeleteSymbols(text, "«»"); 
    FlashInput.SetText2(text2);      
    break;
  default: 
    FlashInput.SetText(text);
  }
  FlashInput.BegExercise();
  ExerciseText = text;
  ReplacedExerciseText = StrReplaceAll(ExerciseText, "\n", " ");
  
  RequestKeyboardParams();
}

function SetExerciseNumberCaption(str)
{
  TaskCaption.SetTaskCaption(str);
}

function SetSpeedTestCaption()
{
  TaskCaption.SetSpeedTestCaption();
}

function SetExerciseUser(val)
{
  if(val != "") BUserLogin = true;
  else  BUserLogin = false;
  UserCaption.Set(val);
}

function SetExerciseMaxNumOfMistakes(val)
{
  ErrorObj.SetMaxNumOfMistakes(val);
  ErrorObj.Update();
}

function SetExerciseCite(citeInfo)
{
  CiteField.SetCite(citeInfo.Text, citeInfo.Author, citeInfo.AuthorInfo);
  CiteField.Update();
}

// for AJAX requests...

var ExerciseInfo = new TExerciseInfo();
var SpeedTestInfo = new TSpeedTestInfo();
var CiteInfo = new TCiteInfo();
var ResultInfo = new TResultInfo();

var ResultSpeedTestInfo = new TResultSpeedTestInfo();
if(BScreenResolutionLowRegime)  ResultSpeedTestInfo.SetNumFields(5);
else  ResultSpeedTestInfo.SetNumFields(8);

var RatingInfo = new TRatingInfo();
var ErrorsInfo = new TErrorsInfo();
var TodayResultInfo = new TTodayResultInfo();
var KeyboardInfo = new TKeyboardInfo();
var TimeoutAJAX = 250; // ms

// end of "for AJAX requests"

function TMouse()
{
  this.X = -1; this.Y = -1;
  this.PrevX = -1; this.PrevY = -1;
  this.GetX = function()
  {
    return this.X;
  }
  this.GetY = function()
  {
    return this.Y;
  }
  
  this.BChangeWndPos = false;
  
  this.Capture = function()
  {
    this.BChangeWndPos = true;
    FlashInput.ShowTypeHere();
  } 
  this.Release = function()
  {
    this.BChangeWndPos = false;
  } 
  this.IsCaptured = function()
  {
    return this.BChangeWndPos;
  }
  
}

var Mouse = new TMouse();

function SetExercsiePos(left, top)
{
  var obj = document.getElementById("Exercise");
  obj.style.left = String(left) + "px";
  obj.style.top = String(top) + "px";
}

function MouseMove(e)
{
  if (window.navigator.appName == "Microsoft Internet Explorer") { // grab the x-y pos.s if browser is IE
    Mouse.X = event.clientX;
    Mouse.Y = event.clientY;
  } else {  // grab the x-y pos.s if browser is NS
    Mouse.X = e.pageX;
    Mouse.Y = e.pageY;
  }
  
  if(Mouse.X < 0)  Mouse.X = 0;
  if(Mouse.Y < 0)  Mouse.Y = 0;
  
  if(Mouse.IsCaptured())
  {
    var shX = Mouse.X - Mouse.PrevX;
    var shY = Mouse.Y - Mouse.PrevY;
    
    var prevX = getPxNumber(WindowSys.Main.style.left);
    var prevY = getPxNumber(WindowSys.Main.style.top);
    
    WindowSys.Main.style.left = String(prevX + shX) + "px";
    WindowSys.Main.style.top = String(prevY + shY) + "px";
  }
  
  Mouse.PrevX = Mouse.X;
  Mouse.PrevY = Mouse.Y; 
  
  return true;  
}

InitMouse();

function InitMouse()
{
  if(window.navigator.appName != "Microsoft Internet Explorer") document.captureEvents(Event.MOUSEMOVE);
  document.onmousemove = MouseMove;
}

var PrevText = "";
var ReturnSpecChar = ' ';

var DbgTypeCntr = 0;

function TypeDebug()
{
  if(IsDebug == false)  return false;
  
  if(DbgTypeCntr == 2)
  {
    setTimeout("FinishExercise()", 0);  
    ShowResult(RESULT_RATING_TABLE);
  }
  else
  {
    ExerciseStepForward();
    UpdatePanels(); 
  }
  DbgTypeCntr++;
  return true;
}

function PrepareResult(iResult)
{
  CaretPos = 0;     
  keyboard.SetTypeBlock(true);
  ResultSpeedTestInfo.Reset();
  setTimeout("FinishExercise()", 0);
  ShowResult(iResult); 
}

function TIntermSpeed()
{
  this.Intervals = new Array(0, 0, 0, 0, 0, 0);
  this.NumOfInt = 0;
  this.BInited = false;
  this.PrevTime = 0;
  this.LastChangeTime = 0;
  this.Mark = function()
  {
    if(this.BInited)
    {
      var curTime = getCurTime();
      var interval = curTime - this.PrevTime;
      if(interval > 2000) interval = 0;
      this.Intervals[this.NumOfInt] = interval;
      this.PrevTime = curTime;
      this.NumOfInt++;
      if(this.NumOfInt > 2)  this.NumOfInt = 0;
    }
    else  
    {
      this.PrevTime = getCurTime();
      this.BInited = true;
    }
  }
  this.Get = function()
  {
    var sumI = 0;
    for(var i = 0, j = 0; i < 6; i++)
    {
      if(this.Intervals[i] != 0)
      {      
        sumI += this.Intervals[i];
        j++;
      }
    }
    
    if(sumI == 0) return 0;
    return Math.floor(j * 60000 / sumI);
  }
  this.Reset = function()
  {
    this.Intervals[0] = 0;
    this.Intervals[1] = 0;
    this.Intervals[2] = 0;
    this.Intervals[3] = 0;
    this.Intervals[4] = 0;
    this.Intervals[5] = 0;
    this.BInited = false;
  }
  this.Init = function()
  {
    this.BInited = true;
  }
  this.IsInited = function()
  {
    return this.BInited;
  }
  this.DoChange = function()
  {
    var curTime = getCurTime();
    if(curTime - this.LastChangeTime > 1000) 
    {
      this.LastChangeTime = curTime;
      return true;
    }
    return false;
  }
}
var IntermSpeed = new TIntermSpeed();

function Type(ch /*not using*/)
{
  var keyCode;

  if(BAutoTyping == true  &&
      ch != '') return;   

  if(keyboard.GetTypeBlock()) return; // we can't type...
  
  bSymbolTyped = true;
  
  if(BAutoTyping == false)
  {
    if(navigator.appName == 'Microsoft Internet Explorer')
      keyCode = window.event.keyCode;
    else
    if(navigator.appName == 'Netscape')
      keyCode = ch.which;
    else
      keyCode = window.event.which;
  }  
  
  var key=String.fromCharCode(keyCode);
  
  
  if(keyCode == 27)  
  {
    Close();
    return;
  }
  
  if(keyCode == 16 || keyCode == 17 || keyCode == 18 ||// shift, ctrl, alt
     keyCode == 116 ||
     keyCode == 8) return; // Backspace, Delete    
  
  
  if(TypeDebug())  return;    
     
  switch(GetExType())
  {
  /*case 2: /*Exam*/
    
    //break;
  case 7:
    
    break;  
    
  case 5:
    if(key == ' '  ||  key == '\r')
    {
      if(InputField2.GetCurWord() != "")
      {
        // test if exercise has finished...
        
        if(TextFragmentsData.IsWordBlocked(InputField2.GetCurNumOfWord()))
        {
          if(InputField2.GetCurWord() != CorrectWords[InputField2.GetCurNumOfWord()]) return;
        }
        
        var numOfWords = InputField.GetNumOfWords();
        
        if(numOfWords == InputField2.GetCurNumOfWord() + 1)
        { 
          var strErrorWords = "";
          var typedWords = new Array();  
          PrepareResult(RESULT_LITERACY);
          ResultPage.SetGrammarResult(CorrectWords, typedWords);
          
          document.getElementById("IDUserTableTd").style.verticalAlign = "top";
          return;
        }
    
        ExerciseStepForward(true);
        UpdatePanels(true);
      }
    }
    else
    { 
      if(TextFragmentsData.IsWordBlocked(InputField2.GetCurNumOfWord()))
      {
        if(key == CorrectWords[InputField2.GetCurNumOfWord()].charAt(InputField2.GetWordSymbolIndex()))
        {
          ExerciseStepForward(false/*complete word*/);
        }        
            
        return;
      }
      
      ExerciseStepForward(false/*complete word*/);
    }
    
    return;
  case 3:
    if(CaretPos < ExerciseText.length - 1)
    {
      setTimeout("Type('')", 100);
      BAutoTyping = true; 
      ExerciseStepForward();
      UpdatePanels();
    }
    else  
    {
      BAutoTyping = false;
      PrepareResult(RESULT_MIKSANATIK);
    } 
    return;
  case 4:
    if(CaretPos < ExerciseText.length - 1)
    {
      ExerciseStepForward();
      UpdatePanels();
    }
    else  
    {
      PrepareResult(RESULT_MIKSANATIK);
    }  
    return;   
  default: ;
  }        
     
  if(ExerciseText.charAt(CaretPos) == '\n')
  {
    if(keyCode == 13  ||  keyCode == 32)
    {
      if(CaretPos < ExerciseText.length - 1)
      {
        ExerciseStepForward();
        UpdatePanels();
      }
      else
      {
        if(GetExType() == 0  || GetExType() == 7  ||  GetExType() == 8)
          PrepareResult(RESULT_MIKSANATIK); 
        else
        if(GetExType() == 32)
          PrepareResult(RESULT_NONE);
        else 
          PrepareResult(RESULT_RATING_TABLE);    
        return;        
      }     
    }
    else  
    {
      ExerciseError(false);
    }
  }
  else
  {
    if(key == ExerciseText.charAt(CaretPos))
    {
      if(CaretPos < ExerciseText.length - 1)
      {
        ExerciseStepForward();
        UpdatePanels();
      }
      else
      {
        PrepareResult(RESULT_RATING_TABLE);      
        return;        
      }
    }
    else
    {
      switch(GetExType())
      {
        case 8:
          if(ErrorObj.GetNumOfMistakes() > ErrorObj.GetMaxNumOfMistakes())
            ErrorObj.SetNumOfMistakes(ErrorObj.GetMaxNumOfMistakes());
                
        break;
        default:;
      }     
      ExerciseError(false);
    }
  }
}

function UpdatePanels(bWordMode)
{
  if(bWordMode == true)
  {
    if(progress > 100)  progress = 100;
  }
  
  Progress.Update();
  Speed.Update();
}

function IsSymbolCorrect(correctSymbol, enteredSymbol)
{
  return ((correctSymbol == enteredSymbol)  ||  (correctSymbol == '\n'  &&  enteredSymbol == ' '));
}

function DoBuffersMatch()
{
  
}


var IntervalFirstKeyboardKeyID = ""; 
function SetCurKeyboardKey()
{
  clearInterval(IntervalFirstKeyboardKeyID);
  FlashKeyboard.SetKey(ExerciseText.charAt(CaretPos));
}

function ExerciseInit()
{

  IntermSpeed.Reset();
  CaretPos = 0;
  ErrorObj.SetNumOfMistakes(0);
  Speed.SetSpeed(0);
  Time.SetTime(0); 
  
  
  UpdatePrevText(ExerciseText.slice(0, CaretPos));
  
// Spec actions for types of exercise...
  switch(GetExType())
  {
  case 0: case 5: case 7:
    ErrorObj.Hide();
    break;
  default: 
    ErrorObj.Show();
  }    
  
  SetTimer(true);
  TypingTimer(); // Start timer
  
  Progress.Update();   
  
  NumOfTypedChars = 0;
  
  fInputLoaded(); // flash may be initialized now   
}



function StrReplaceAll(str, chIn, chOut)
{
  var res = str;
  var res1 = "";
  while(res != res1)
  {
    res1 = res;
    res = res1.replace(chIn, chOut);
  }
    
  return res;
}

var BErrorMovieFinished = false;

function OnFinishErrorMovie()
{ 
  if(window.navigator.appName == "Opera")
  	document.getElementById('IDKeyboard').innerHTML = keyboard.PlaceKeyboard('900');

  document.getElementById("flashKeyboardSpan").style.display = "";
  document.getElementById("flashErrorSpan").style.display = "none";
  
  var numOfMistakes = ErrorObj.GetNumOfMistakes();
  var maxNumOfMistakes = ErrorObj.GetMaxNumOfMistakes();
  
  if(numOfMistakes > maxNumOfMistakes)
  {
    ErrorObj.SetFat(true);
  }
  
  SetLeftSideTyping();
  CiteField.Render();
  CiteField.Update();
  keyboard.SetTypeBlock(false); // remove type blocking
  keyboard.SetInputFocus();
  
  document.getElementById("IDTimeLowResolution").style.display = "";
  
  SetTimer(true);
  TypingTimer(); // Start timer
  
  BErrorMovieFinished = true;
  
  CiteField.StopMerge();
  
 //alert("OnFinishErrorMovie");
  FlashInput.SetCanType(true);
  FlashInput.BegLine();
  
  //FlashKeyboard.Activate();
  //FlashKeyboard.InitLanguage(); // (actually we show keyboard)
  
  //RequestKeybParams();
}


function SetFlashErrorDuration()
{
//  alert("SetFlashErrorDuration()");
  if(IsKeyboard)  window.document["flashError"].SetVariable("listener.duration", "3000");
}

function SetFlashErrorDuration2()
{
//  alert("SetFlashErrorDuration2()");
  if(IsKeyboard)  document.flashError.SetVariable("listener.duration", "3000");
}

function RestoreAfterFastError()
{
//  InputField.RestoreCursor();
//  InputField2.TrimLastChar();
  keyboard.SetTypeBlock(false);
}

var MessWrongLayout = "Выбрана неверная раскладка клавиатуры!<br>Используйте русскую раскладку.";
var MessWrongLayoutEng = "Выбрана неверная раскладка клавиатуры!<br>Используйте английскую раскладку.";

function GetMessWrongLayout()
{
  if(GetCurLanguage() == "English")
    return MessWrongLayoutEng;
  else
    return MessWrongLayout;
}

function IsWrongLayout(errorCh)
{
  if(GetCurLanguage() == "English")
  {
    var str = "йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ";
    for(var i = 0; str.charAt(i) != errorCh && i < str.length; i++);
    if(str.charAt(i) == errorCh)  return true;
    return false;
  }
  else
  {
    var str = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
    for(var i = 0; str.charAt(i) != errorCh && i < str.length; i++);
    if(str.charAt(i) == errorCh)  return true;
    return false;
  }
}

function SetCiteErrorText(bWrongLay)
{
  if(bWrongLay)
    CiteField.SetErrorText(GetMessWrongLayout());
  else
    CiteField.SetErrorText(ErrorsInfo.GetError());
}

function ExerciseError(bWrongLay)
{

//  FlashInput.BegLine();

//alert(String(bWrongLay));
  
  BErrorMovieFinished = false;
  
  //alert("errorCh="+errorCh);
  
//  var bWrongKeyboardLayout = IsWrongKeyboardLayout(errorCh);
  
  if(ErrorObj.GetNumOfMistakes() + 1 == ErrorObj.GetMaxNumOfMistakes())
  {
    SetCiteErrorText(bWrongLay);
    CiteField.BeginMerge();
    CiteField.SetWarning();
  }
  else
  {
    SetCiteErrorText(bWrongLay);
  }
  
//  alert("GetExType()="+GetExType());
  switch(GetExType())
  {
  case 0:
    keyboard.SetTypeBlock(true);      
    return; 
  case 2: case 7: case 32:
    ErrorObj.SetNumOfMistakes(ErrorObj.GetNumOfMistakes() + 1);
    ErrorObj.Update();
    FlashKeyboard.SetBkspMode(true);
    keyboard.SetTypeBlock(true);  
    return;
  case 5:
    alert("[!] Error can't occur while doing grammar test");
    return;
  case 8:
    if(ErrorObj.GetNumOfMistakes() < ErrorObj.GetMaxNumOfMistakes())
    {
      if(bWrongLay == false  ||  isIe6()/*!*/)  
      {
        if(Progress.GetCurLine() > 0)  ErrorObj.SetNumOfMistakes(ErrorObj.GetNumOfMistakes() + 1);
      }
    }      
    break;
  default:
/*    if(ErrorObj.GetNumOfMistakes() < ErrorObj.GetMaxNumOfMistakes() + 1)
    {*/
      if(bWrongLay == false  ||  isIe6()/*!*/)  
      {
        if(Progress.GetCurLine() > 0)  ErrorObj.SetNumOfMistakes(ErrorObj.GetNumOfMistakes() + 1);
      }
//    }  
  }
  
  SetTimer(false);
  
  keyboard.SetTypeBlock(true); // set type blocking
    
  CiteField.RenderError();
  SetLeftSideError();
   
  ErrorObj.Update();
  Speed.Update();
  Time.Update(); 
  
  document.getElementById("flashKeyboardSpan").style.display = "none";
  document.getElementById("flashErrorSpan").style.display = "";
  
  /*try
  {
    if(IsKeyboard)  document.flashError.SetVariable("listener.duration", "3000");
  } 
  catch(e)
  { }*/
  
  
  
  if(!isIe6())  
    FlashError.Reset();
  else
    setTimeout(function(){FlashError.IndErrorLimit( ErrorObj.GetNumOfMistakes() > ErrorObj.GetMaxNumOfMistakes() )},
                    100);
// Delete this attepmt later...
//
}


function ExerciseStepBackward(linN, isError)
{
  CaretPos--;
  UpdateKey();
   
  if(isError == "false" /*error==false*/)
  {
    Progress.SetProgress(GetCaretProgress());
    Progress.SetLine(linN);
    Progress.Update();
  }
}

function UpdateKey()
{
  var ch = ExerciseText.charAt(CaretPos);
  if(ch == '\n') ch = ' ';

  if(IsKeyboard)  
  {
    try{
      FlashKeyboard.SetKey(ch);
    }
    catch(e)
    {
    }
  }
}

function ExerciseStepForward(bVal)
{
  switch(GetExType())
  {
  case 7:
    CaretPos++;
    
    break;
  case 5:
    if(bVal /*word completed*/)
    {
      CaretPos++;
      return;
    }
    else
    {
       return;     
    }
    break;

  default:

    CaretPos++;
  }

  UpdateKey();
  
// next - use bVal not to take in account speed
  if(bVal == undefined || bVal == true)
  {
    iSymbolsTypedOneSecond++;
    IntermSpeed.Mark();
    if(IntermSpeed.DoChange())
    {
      Speed.SetSpeed(IntermSpeed.Get());
      Speed.Update();
    }
  }
}

function ExercisePos(pos)
{
  CaretPos = pos;
}

function UpdatePrevText(val)
{
  var typer = document.getElementById("typer");
  PrevText = StrReplaceAll(val, "\n", " "); // store exercise text in typer
}

function GoToBeginningOfExercise()
{    
  if(FlashInput.BCanType == false) return;     
  IntermSpeed.Reset();
  
  CaretPos = 0;
  FlashInput.BegExercise();
  FlashInput.ShowTypeHere();
  Progress.SetProgress(0);
  Progress.Update();
  ErrorObj.SetNumOfMistakes(0); 
  ErrorObj.Update();
  Speed.SetSpeed(0);
  Speed.Update();
  CurTime = 0;
  Speed.ClearSpeedSum();
  Time.SetTime(CurTime);
  Time.Update();  
  ErrorObj.SetFat(false);
  
  NumOfTypedChars = 0;
 
  FlashKeyboard.SetBkspMode(false);
 
//  SetCurKeyboardKey();
  RequestKeyboardParams();
  
  Progress.SetLine(0);
  Progress.SetMaxLine(ExNumOfLines());
  Progress.Update();
  
// restore cite field
  if(GetExType() == 32)
  {
    CiteField.Render();
    CiteField.Update();
  }
}

function ShowResult(iResOption)
{
  ResultPage.Set();
  switch(iResOption)
  {
    case RESULT_MIKSANATIK:
      ResultPage.ShowMiksanatik();
      break;
    case RESULT_LITERACY:
      ResultPage.ShowGrammarResult();
      break;
    case RESULT_NONE:
      ResultPage.HideMain();
      break;
    default:  
      ResultPage.ShowRatingTable();
  }
}

this.CharsRusCaption = function(num)
{
  if(num >= 10  &&  num < 20)
    return "знаков";

  var end = new Array("ов", "", "а", "а", "а", "ов", "ов", "ов", "ов", "ов");
  
  var sNum = String(num);
  var lastNum = parseInt(sNum.charAt(sNum.length-1));
  return  "знак"+end[lastNum];
}
this.ErrorsRusCaption = function(num)
{
  if(num >= 10  &&  num < 20)
    return "ошибок";

  var end = new Array("ок", "ка", "ки", "ки", "ки", "ок", "ок", "ок", "ок", "ок");
  
  var sNum = String(num);
  var lastNum = parseInt(sNum.charAt(sNum.length-1));
  return  "ошиб"+end[lastNum];
}

function FinishExercise()
{
  SetTimer(false);
  keyboard.SetTypeBlock(true); // set type blocking
  
  
  if(GetExType() == 32)
  {  
	if(ResultExtPage != "")
    {
      document.location.href = ResultExtPage + "?speed="+Speed.GetTotalSpeed()+"&errors="+ErrorObj.GetNumOfMistakes();
      return;
    }  
  
    if(ResultSpeedTestInfo.IsFormed() == false)
    {
      if(ResultSpeedTestInfo.IsRequested() == false)  
      {
          RequestSpeedTestResult(Speed.GetTotalSpeed(), ErrorObj.GetNumOfMistakes(), Math.floor(Time.GetTime() / 1000));
      }
      setTimeout("FinishExercise()", TimeoutAJAX );
      return;
    }   
  }
  else
  {
	
    if(ResultInfo.IsFormed() == false)
    {
      if(ResultInfo.IsRequested() == false)  
      {                   
        RequestResult(ExerciseInfo.SortID, /*Speed.GetSpeed()*/Speed.GetTotalSpeed(), ErrorObj.GetNumOfMistakes(), Math.floor(Time.GetTime() / 1000));
      }
      setTimeout("FinishExercise()", TimeoutAJAX );
      return;
    }  
  }    
  
  if(TodayResultInfo.IsFormed() == false)
  {
    if(TodayResultInfo.IsRequested() == false)  
    {
      RequestTodayResult();
    }
    setTimeout("FinishExercise()", TimeoutAJAX );
    return;
  }    
  
  ResultSpeedTestInfo.Reset();
  ResultInfo.Reset();
  TodayResultInfo.Reset();
  
  if(GetExType() == 32)
  {
    with(ResultPage)
    {
    	SetPanelSpeed(Speed.GetTotalSpeed());
    	UpdatePanelFullTime();
    	SetPanelNumOfErrors(ErrorObj.GetNumOfMistakes());
    }
    var speed_ = Speed.GetTotalSpeed();
    var errors_ = ErrorObj.GetNumOfMistakes();
    ResultPage.SetHeader("Ваш результат: " + String(speed_) + " " + this.CharsRusCaption(speed_) + " в минуту, " + String(errors_) + " " + this.ErrorsRusCaption(errors_));
    
    with(ResultPage)
    {
    	SetResultMessage(ResultSpeedTestInfo.Text);
    	SetResultPanelDisplay(false);
    }
    
    with(ResultPage)
    {
		SetNumOfTasks(TodayResultInfo.NumOfTasks);
		SetAvarageSpeed(TodayResultInfo.AvarageSpeed);
		SetNumOfSymbolsTyped(TodayResultInfo.SymbolsTyped);
		SetNumOfErrors(TodayResultInfo.NumOfErrors);
    	
    	if(IsUserLogin())  SetSpeedTestRegisterEnterTrDisplay(false);
    	else
    		SetSpeedTestRegisterEnterTrDisplay(true);
    	SetCrit(3);
    
    	SetResultPageContinueRef("Закрыть");
    	PostForm();
    	ExTable.Hide();
    }
  }
  else
  {
	with(ResultPage)
	{
		SetPanelSpeed(/*Speed.GetSpeed()*/Speed.GetTotalSpeed());
		UpdatePanelFullTime();
		SetPanelNumOfErrors(ErrorObj.GetNumOfMistakes());
		SetPanelRating(Math.floor(ResultInfo.Rating * 100) / 100);
		SetHeader(ResultInfo.Header);
		SetResultMessage(ResultInfo.Text);
		SetLessonExercise(ExerciseInfo.LessonNumber, ExerciseInfo.ExerciseNumber);
  
		SetNumOfTasks(TodayResultInfo.NumOfTasks);
		SetAvarageSpeed(TodayResultInfo.AvarageSpeed);
		SetNumOfSymbolsTyped(TodayResultInfo.SymbolsTyped);
		SetNumOfErrors(TodayResultInfo.NumOfErrors);
		
		SetSpeedTestRegisterEnterTrDisplay(false);
		
		SetCrit(3);
		SetExerciseID(IDExercise);
		ExTable.Show();
	}
    UpdatePage(); // Set curPage here     
    
    ResultPage.SetResultPageContinueRef("Продолжить");
  }
    
  ResultPage.ShowResBottomCaptions(true);
}

function SetTimer(val) { IsTimer = val; }

var SymbolsTyped = new Array(0,0,0);
var iSymbolsTypedOneSecond = 0;


function getCurTime()
{
  var d = new Date();
  return d.getTime();
}

function TimerGetElapse()
{
  var ct = getCurTime();
  var dt = ct - PrevTime;
  PrevTime = ct;
  return dt;
}

var BTimerWasStopped = true;
var PrevTime;

function TypingTimer()
{

//  return;

  if(IsTimer == false)  
  {
    bSymbolTyped = false;
    PrevTime = getCurTime();
    return;
  }
  
  if(bSymbolTyped == false)
  {
    setTimeout("TypingTimer()", 1000);
    PrevTime = getCurTime();
    return;
  }

  CurTime += TimerGetElapse();
  Time.SetTime(CurTime);
  Time.Update();

  bSymbolTyped = false;
  setTimeout("TypingTimer()", 1000);
}

function LeftSideTyping(IDRightSide)
{
  var res = "";
  TableBuilder.ResetT();
  TableBuilder.SetTWidth("100%");
  if(IDRightSide == undefined)
  {
    TableBuilder.SetTHeight("100%");
  }
  else  
  {
    TableBuilder.SetTHeight(document.getElementById(IDRightSide).offsetHeight);
  }
  TableBuilder.SetTID(IDCloseRedoFingersTable);
  res += TableBuilder.BeginTable();
  TableBuilder.ResetT();
  res += TableBuilder.BeginTr();
  TableBuilder.SetTValign("top");
  res += TableBuilder.BeginTd();

  res += CloseRedoObj.Generate();
  res += TableBuilder.EndTd();
  TableBuilder.ResetT();
  res += TableBuilder.EndTr();
  res += TableBuilder.BeginTr();
  TableBuilder.SetTValign("bottom");
  res += TableBuilder.BeginTd();
  
  res += FingerZonesObj.Generate();

  res += TableBuilder.EndTd();

  res += TableBuilder.EndTr();
  res += TableBuilder.EndTable(); 
  
  return res;
}

function LeftSideError()
{
  var res = "";
  TableBuilder.ResetT();
  
  TableBuilder.ResetT();
  TableBuilder.SetTHeight("100%");
  TableBuilder.SetTWidth(WidthLeftSideTyping);
  res += TableBuilder.BeginTable();
  
  TableBuilder.SetTHeight(String(CiteField.GetEmptyField().GetRowHeight()));
  
  res += TableBuilder.BeginTr();
  TableBuilder.SetTValign("top");
  res += TableBuilder.BeginTd();

  res += CloseRedoObj.Generate();
  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr();
  
  res += TableBuilder.BeginTr();
  TableBuilder.SetTValign("top");
  TableBuilder.SetTAlign('right');
  res += TableBuilder.BeginTd();

  res += "Вместо";

  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr();
  
  TableBuilder.ResetT();
  
  res += TableBuilder.BeginTr();
  TableBuilder.SetTValign("top");
  TableBuilder.SetTAlign('right');
  res += TableBuilder.BeginTd();

  res += "Вы набрали";

  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr();  

  
  res += TableBuilder.EndTable();
  return res;  
}

function SetLeftSideTyping()
{
  document.getElementById(FingerZonesObj.GetID()).innerHTML = FingerZonesObj.Insert();
}

var WidthLeftSideTyping = 0;

function InsertRightSide(id)
{
  var res = "";
  var obj = document.getElementById(id); 
  
  TableBuilder.ResetT();
  TableBuilder.SetTHeight(obj.offsetHeight)
  res += TableBuilder.BeginTable();
  TableBuilder.ResetT();
  res += TableBuilder.BeginTr();
  TableBuilder.SetTValign('top');
  res += TableBuilder.BeginTd();

  res += UserCaption.Generate() + Br();
  res += TaskCaption.Generate() + Br() + Br();

  res += BeginCenter();
  res += Miksanatik.Generate();
  res += EndCenter();
  res += Br();

  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr();

  res += TableBuilder.BeginTr();
  TableBuilder.SetTValign('bottom');
  res += TableBuilder.BeginTd();

  res += "<span id='" + IDErrorSpan + "'>" + ErrorObj.Generate() + Br() + "</span>";

  res += Speed.Generate() + Br();
  
  res += Time.Generate() + Br();


  res += Progress.Generate();

  res += TableBuilder.EndTd();
  res += TableBuilder.EndTr();
  res += TableBuilder.EndTable();

  obj.innerHTML = res;
  
  Speed.SetSpeed(0);
  Speed.Update();  
  Progress.Update();  
  Time.SetTime(0);
  Time.Update();  
}


function SetLeftSideError()
{

  document.getElementById("IDTimeLowResolution").style.display = "none";

  var style= 'font-family: Arial; font-size: 14pt; color: #555555;';
  var tableHeight = height;
  var tableWidth = document.getElementById(IDFingerZonesTd).offsetWidth;
  
  var heightInput1 = document.getElementById('IDTdInput').offsetHeight / 2;
  var heightInput2 = document.getElementById('IDTdInput').offsetHeight / 2;
  var heightInterval = 0;
  
  var height = heightInput1 + heightInput2 + heightInterval;
  var height1 = heightInput1 + heightInterval;
  var height2 = height - height1;
  
  document.getElementById(FingerZonesObj.GetID()).innerHTML =
  "<table height=" + tableHeight + " width=" + tableWidth + " cellspacing=0 cellpadding=0 border=0><tr height='" + height1 + "px'><td align='right' valign='top'>" +
  "<font style='" + style + "'>Вместо</font></td></tr><tr height='" + height2 + "px'><td align=right valign=top>" +
  "<font style='" + style + "'>Вы набрали</font></td></tr></table>";
}

function PlaceInitInputCntrl()
{
  var classForInput = "";
  classForInput = "hiderRadioOtherBrowsers";
  Write("<input id='IDInputRadio' type='text' class='" + classForInput + "' onKeyPress='Type(event);' onblur='SetInputFocus_();'>");   
  InitKeyPress();
}


function TMiksWaiter()
{ 
  this.GetId = function()
  {
    return "IDMiksWaiter";
  }
  this.BVisible = false;
  this.Generate = function()
  {
    
    return "<div id='" + this.GetId() + "' style='display: none'>"+
    "<table border='0' cellpadding='0' cellspacing='0'>"+
    "<tr height='10px'><td width='10px' class='bevelTopLeft'></td><td style='background: #FFFFFF'></td><td width='10px' class='bevelTopRight'></td></tr>" +
    "<tr><td width='10px' style='background: #FFFFFF'></td><td style='background: #FFFFFF'>" +
    "<OBJECT codebase='' ID='IDFlashMiksWaiter'>" +
    "<PARAM NAME='allowScriptAccess' VALUE='sameDomain' />" +
	  "<PARAM NAME='movie' VALUE='/study/exercise/flash/miksWait.swf'>" +
  	 "<EMBED WIDTH='100' HEIGHT='92' play='true' swliveconnect='true' name='flashMiksWaiter' src='/study/exercise/flash/miksWait.swf' quality='high' bgcolor='#FFFFFF' allowScriptAccess='sameDomain' TYPE='application/x-shockwave-flash'></EMBED>" +
    "</OBJECT>" +
    "</td><td width='10px' style='background: #FFFFFF'></td></tr>" +
    "<tr height='10px'><td width='10px' class='bevelBottomLeft'></td><td style='background: #FFFFFF'></td><td width='10px' class='bevelBottomRight'></td></tr>" +
    "</table></div>";   
    
  }
  
  this.Show = function()
  {
    if(this.BVisible) return;
    this.SetPosition(document.getElementById("full-page").offsetWidth / 2 + 50, 200);
    document.getElementById(this.GetId()).style.display = "";
    this.BVisible = true;
  }
  this.Hide = function()
  {
    if(this.BVisible == false) return;
    document.getElementById(this.GetId()).style.display = "none";
    this.BVisible = false;
  }
  
  this.SetPosition = function(x, y)
  {
    document.getElementById(this.GetId()).style.left = String(x) + "px";
    document.getElementById(this.GetId()).style.top = String(y) + "px";
  }
}

function ShowTdUser(bShow)
{
  if(bShow == false)
    document.getElementById("IDTdUser").style.display = "none";
  else
    document.getElementById("IDTdUser").style.display = "";
}
function ShowTdFingerZones(bShow)
{
  if(bShow == false)
    document.getElementById("IDTdLeft").style.display = "none";
  else
    document.getElementById("IDTdLeft").style.display = "";  
}
function SetMainTypingTableWidth(w)
{
  document.getElementById('IDMainTypingTable').style.width = String(w) + "px";
}

function GenerateExercise()
{
CiteField = new TCiteField(IDUserCite);
CiteField.SetCite("", "");

TableBuilder = new Table();

TableBuilder.ResetT();

Write("<div id=\'ExerciseBackground\' style=\'width: 3000px; height: 2000px;\'></div>");


Write("<div id=\'Exercise\' style='top: " + WindowTop + "px; left:" + WindowLeft+ "px;'>"); // ?
Write("<table border=0 cellspacing=0 cellpadding=0>");
// Bevels...
Write("<tr height='10px'><td width='10px'  class='bevelTopLeft'>" + 
"</td><td style='background: white' onmousedown='Mouse.Capture()' onmouseup='Mouse.Release()'></td><td width='10px'  class='bevelTopRight'></td></tr>");
Write("<tr><td style='background: white'></td><td style='background: white'>");
//

Write(BeginSpan('id', 'ExerciseContent'));

var br = "";
if(window.navigator.appName != 'Netscape') br = Br();

Write("<table id='IDMainTypingTable' border='0' width='900' cellspacing=8 cellpadding=0 style='background: white'>");

Write("<tr><td id='IDTdUser' align='left' valign='top'>");

Write(UserCaption.Generate() + Br());
Write("<table cellpadding='0' cellspacing='0' border='0'><tr>");
Write("<td valign='middle'>"+CaptionArrow.Generate()+"</td>");
Write("<td id='IDArrowDelimiter'></td>");
Write("<td valign='middle'>"+TaskCaption.Generate()+"</td>");
Write("</tr></table>");
//Write(Br() + Br());
Write("</td><td valign='top' rowspan='2'>");


Write("<table border='0' cellpadding='0' cellspacing='0' width='100%' height='100%'>");
Write("<tr height='120px'><td valign='top'>");

Write("<div style='margin-bottom: 10px'>");
Write("<table border='0' cellpadding=0 cellspacing=0  width='600'>");
Write("<tr><td align='left' valign='middle' width='73px'>");
Write(Miksanatik.Generate());
Write("</td>");
Write("<td align='left'>");
Write(CiteField.PlaceHere());
 
Write("</td>");
Write("<td valign='top' align='right'>");
Write("</td>");
Write("</tr>");
Write("</table>");
Write("</div>");

Write("</td></tr>");

Write("<tr><td>");

Write("<table id='IDTdCenterInnerTable' border='0' cellpadding='0' cellspacing='0'>");
Write("<tr><td id='IDTdInput' valign='bottom'>");

/*if(BScreenResolutionLowRegime)
{
  FlashInput.SetLowResolution(true);
} */

Write(FlashInput.PlaceHere());

Write("</td></tr>");
Write("</table>");


Write("</td></tr>");
Write("</table>");


Write("</td>");
Write("<td valign='top' align='right'>");
Write(CloseRedoObj.Generate());
Write("</td></tr>");

Write("<TR>");
Write("<TD id='IDTdLeft' valign='bottom'>");
Write("<table id='IDLeftTable' border='0' cellpadding='0' cellspacing='0' style='visibility: hidden'>");
Write("<tr><td align='left' valign='top'>");
Write("</td></tr>");
Write("<tr><td id='IDTimeLowResolution' valign='bottom'>");

Write("<div style='margin-bottom: 20px'>"+Time.Generate()+"</div>");
Time.SetTime(0);
Time.Update();

Write("</td></tr>");
Write("<tr>");
Write("<td height='50%' id='" + IDFingerZonesTd + "' valign='bottom'>");
Write(FingerZonesObj.Generate());
Write("</td></tr></table>");
Write("</TD>");

Write("<TD id='IDTdRight' valign='bottom'>");
Write("<table id='IDRightTable' border='0' cellpadding='0' cellspacing='0' style='visibility: hidden'>");
Write("<tr><td align='right' valign='bottom'>");
Write("<span id='" + IDErrorSpan + "'>" + ErrorObj.Generate() + br + "</span>");
Write(Speed.Generate() + br);
Speed.SetSpeed(0);
Speed.Update();            
                        
Write(Progress.Generate());
Progress.Update();
Write("</td></tr></table>");
Write("</TD>");
Write("</TR>");

Write("<TR>");
Write("<TD COLSPAN=3 WIDTH=100% VALIGN=TOP>");
Write("<span id='IDKeyboard'>" + keyboard.PlaceKeyboard('900')) + "</span>";
Write("</TD>");
Write("</TR>");
Write("<tr><TD id='IDDebugTd' style='display: none' COLSPAN=3 WIDTH=100% VALIGN=TOP><span style='font-weight:bold'>Debug: </span><span id='IDDebug'></span></TD></tr>");
Write("</TABLE>");

Write(EndSpan());
Write(BeginSpan("id", "ResultContent") + EndSpan());

// Bevels...
Write("</td><td style='background: white'></td><tr height='10px'>" + 
"<td class='bevelBottomLeft'></td>" +
"<td valign='bottom' style='background: white'></td>" +
"<td class='bevelBottomRight'></td></tr></table>");
// 

Write(EndDiv()); // id=Exercise

Write("<div id=\'IDResultPage\'></div>");
Write(EndDiv()); 

// patch
document.getElementById("ExerciseBackground").style.display = "none";
document.getElementById('Exercise').style.display = "none";

CiteField.Render();
CiteField.Update();             

} // end of GenerateExercise

var IsDebugActivated = false;
function DebugOut(s)
{
  var obj = document.getElementById('IDDebug');
  if(IsDebugActivated == false)
  {
    document.getElementById('IDDebugTd').style.display = "";
    IsDebugActivated = true;
  }
  obj.innerHTML = s;
}

function UpdatePageIfSkipMode()
{
  if(ExerciseInfo.RefToNext != "")
    CloseOrGoToPage(ExerciseInfo.RefToNext);
}


function UnsetResultPageClose(ref)
{
  ResultPage.Unset();
  Close();                       
  if(ref != undefined)
    document.location.href = ref;
  
}

function Close()
{
  SetWindowOpened(false);
  
  Context.SetError(false);
  
  /*try
  {
    ErrorObj.SetNumOfMistakes(0);
    ErrorObj.Update();
    ErrorObj.SetFat(true);
  }
  catch(e) {alert("error");}*/

  document.getElementById("ExerciseBackground").style.display = "none";
  document.getElementById("Exercise").style.display = "none";
  
  try
  {
    GoToBeginningOfExercise();
  }
  catch(e){}
  
  IsTimer = false;
  OnFinishErrorMovie(); // finish error movie
  UpdatePageIfSkipMode();
//  SetInputRadioBk(0); // not using with flash
  ShowStickoutWnds();
//  if(window.navigator.appName == "Microsoft Internet Explorer")
  if(!isIe6()) FlashInput.SetFlashLoaded(false);
  else
  {
    FlashInput.SetFlashLoaded(true);
    FlashInput.ResetContent();
  }
  
  //alert("Close()");

}

var OpacityCounter = 0;

var IDStickWnds = "videoblock"; 

function HideStickoutWnds()
{
  if(document.getElementById(IDStickWnds) != undefined)
    document.getElementById(IDStickWnds).style.visibility="hidden";
  //document.getElementById(videoblock2).innerHTML  
}

function ShowStickoutWnds()
{
  if(document.getElementById(IDStickWnds) != undefined)
   document.getElementById(IDStickWnds).style.visibility="visible";
  //document.getElementById().style.visibility="none";
}

var IDSpeedTest = 65532;
var ResultExtPage = "";
var IsKeyboard = true; // do show/update keyboard

function ShowSpeedTest()
{
  var objExtPage = document.getElementById('IDExtSpeedTestResultPage');
  if(objExtPage != null)
  {
    ResultExtPage = objExtPage.value;
    IsKeyboard = false;
    keyboard.Show(false);
    ShowTdUser(false);
    ShowTdFingerZones(false);
    SetMainTypingTableWidth(600);
    // hide table cells (left)
    SetExercsiePos(2, 2);
  }  
  ShowExercise(IDSpeedTest);
}

var TimeBegEx = 0;
function ResetBegTime()
{
  var d = new Date();
  TimeBegEx = d.getTime();
}

function GetFullExTime()
{
   var d = new Date();
   d.setTime(d.getTime() - TimeBegEx);
   var min = String(d.getMinutes());
   var sec = String(d.getSeconds());
   if(min.length < 2) min = "0" + min;
   if(sec.length < 2) sec = "0" + sec;
   return min + ":" + sec;
}

function GetActiveExTime()
{
   var csec = Math.floor(CurTime / 1000);
   var k = Math.floor(csec / 60);
   var min = String(k);
   var sec = String(csec - 60*min);
   if(min.length < 2) min = "0" + min;
   if(sec.length < 2) sec = "0" + sec;
   return min + ":" + sec;
}

function Redirect(href)
{
  document.location.href = href;
}

WindowSys = {
	Bk: undefined,
	Main: undefined,
	Init: function()
	{
		this.Bk = document.getElementById("ExerciseBackground");
		this.Main = document.getElementById("Exercise");
	}
};

function ShowExercise(exerciseId, bClicked)
{
//  alert("PreTest");
  if(bClicked == undefined)
  {
    if(GetWindowOpened())
      return;
  }
  SetWindowOpened(true);

  HideStickoutWnds();
  
  if(exerciseId == IDSpeedTest)
  {
    if(SpeedTestInfo.IsFormed() == false)
    {  
      if(SpeedTestInfo.IsRequested() == false)  
      {
        if(isIe6()) RequestSpeedTest();
        else  
        {
// Имитация того, что тест был запущен и сформирован. В основных браузерах формируем его по событию загрузки флеша,
// в IE6 - здесь (выше), т. к. в IE6 не формируется события загрузки флеша         
          SpeedTestInfo.SimFormed();
          ExerciseInfo.TypeOfExercise = 32; // bad...
        }
      }
      setTimeout("ShowExercise(" + exerciseId + ", false)", TimeoutAJAX );
      return;
    }
  }
  else
  if(ExerciseInfo.IsFormed() == false)
  {
    if(ExerciseInfo.IsRequested() == false)  
    {
      RequestExercise(exerciseId);
    }
    setTimeout("ShowExercise(" + exerciseId + ", false)", TimeoutAJAX );
    return;
  }
  else
  {
    if(ExerciseInfo.Redirect != "")
    {
      Redirect(ExerciseInfo.Redirect);
      return;
    }   
  }
// discomment later
// also skip 
  if( !( GetExType() == 32  &&  !isIe6() ) )
  {
    if(CiteInfo.IsFormed() == false)
    {
      if(CiteInfo.IsRequested() == false)  
      {
        RequestCite();
      }
      setTimeout("ShowExercise(" + exerciseId + ", false)", TimeoutAJAX );
      return;
    }
  }
  
  if(GetExType() != 32)
  if(ErrorsInfo.IsFormed() == false)
  {
    if(ErrorsInfo.IsRequested() == false)  
    {
      RequestErrors();
    }
    setTimeout("ShowExercise(" + exerciseId + ", false)", TimeoutAJAX );
    return;
  }   
      
  if(OpacityCounter < 5)
  {
    if(OpacityCounter == 0)
    {
      WindowSys.Init();
      if (window.navigator.appName != "Netscape")
      {      
        WindowSys.Bk.style.width = document.getElementById("full-page").offsetWidth;
        WindowSys.Bk.style.height = document.getElementById("full-page").offsetHeight;
      }      
      
    }
    OpacityCounter++;
    if(window.navigator.appName == "Microsoft Internet Explorer")
    {
    	WindowSys.Bk.style.filter = "alpha(opacity=" + String(OpacityCounter * 10) + ")";
    }
    else  WindowSys.Bk.style.opacity = OpacityCounter / 10.0;
    
    WindowSys.Bk.style.display = "";   
    setTimeout("ShowExercise(" + exerciseId + ", false)", 50);
    return;
  }
  
  SetExerciseCite(CiteInfo); 
  
  if(GetExType() == 32)
  {
    var strText = FormatToStrings(SpeedTestInfo.Text, 40);
    SetExerciseMaxNumOfMistakes("0");
    SetSpeedTestCaption();
    CaptionArrow.Show();
  }
  else
  { 
    SetExerciseNumberCaption(String(ExerciseInfo.LessonNumber) + "." + String(ExerciseInfo.ExerciseNumber));
    SetExerciseMaxNumOfMistakes(ExerciseInfo.MaxNumOfMistakes);
    ExerciseInfo.Reset();   
    CaptionArrow.Hide();
  }
  
  SpeedTestInfo.Reset();
  CiteInfo.Reset();
  
  OpacityCounter = 0;
  
  ResetBegTime();
  document.getElementById("Exercise").style.visibility = "hidden";
  document.getElementById("Exercise").style.display = "";
  
  CloseRedoObj.Show(true);
  
  if(FlashInput.IsFlashLoaded() == false)
  {          
    if(!isIe6())  
    {
      FlashInput.Reset();
    }
    else  
    {
      FlashInput.ResetContent();
    } 
  } 
  ExerciseInit();
  
  keyboard.SetInputFocus();
  
  IDExercise = exerciseId; 
  document.getElementById('IDRightTable').style.height = document.getElementById('IDTdRight').clientHeight+"px";
  document.getElementById('IDLeftTable').style.height = document.getElementById('IDTdLeft').clientHeight+"px";  
  document.getElementById('IDRightTable').style.visibility = "visible";
  document.getElementById('IDLeftTable').style.visibility = "visible";
  //style.height=String(element_name.offsetHeight+1500)+"px";
  
  //MiksWaiter.Hide();    
        
  if(isIe6()) document.getElementById("Exercise").style.top = getScrollY() + WindowTop;      
        
  document.getElementById("Exercise").style.visibility = "visible"; 
  ResultPage.RefToNextHasSet(false);
  /*if(isIe())
  {
    if(UserCaption.GetUserName() != "")
    { 
    }
    else
    {
        setTimeout("keyboardSetDefaultLayout()", 2000);
    }
  }*/

//  alert("ShowExercise g");
}

function ClickBegExercise()
{
  RequestCiteSpeedUpdate();
// something to update user text
}

function RepeatExercise()
{
  ResultPage.Unset();
  
  if(!isIe6()) ExerciseInit();
  
  GoToBeginningOfExercise();
  
/*  FlashInput.BegExercise();
  FlashInput.ShowTypeHere();*/
  if(IsTimer == false)
  {
    SetTimer(true);
    TypingTimer();
  }  
  
  keyboard.SetInputFocus();
  
  CiteField.Render();
  CiteField.Update();

// ###
/*  if(GetExType() == 32)
  {
    RequestCiteSpeedUpdate();
  }
  else
  {*/
/*    FlashInput.BegExercise();
    FlashInput.ShowTypeHere();*/  
  //}
  
  
}

function ShowMainWindow(bShow)
{
  if(bShow)
  {
    document.getElementById("Exercise").style.visibility = "visible";
  }
  else
  {
    document.getElementById("Exercise").style.visibility = "hidden"; 
  }
}

function SetInputFocus_()
{
  if(ResultPage.IsSet() == false  &&  keyboard.GetTypeBlock() == false)
  {
    keyboard.SetInputFocus();
  }
}

GenerateExercise(); // HTML mainly



// resault page: returns array of page selector  {... 1 2 3 ...}


function TSelectorArr()
{
	this.I = 0;
	this.Arr = new Array();
	this.SForwBackw = "...";
	this.Min = 0;
	this.MinPg = 0;
	this.MaxPg = 0;

	this.Get = function(cPg, nmPgs, nmOpts)
	{
		this.Arr.splice(0, this.Arr.length);
		
		var minPg = cPg - nmOpts/2;
		if(minPg < 0) minPg = 0;
		var maxPg = minPg + nmOpts;
		var lastPg = nmPgs - 1;
		if(maxPg > lastPg) maxPg = lastPg;
		
		this.Min = minPg;
		this.MaxPg = nmPgs - 1;
		
		
		if(minPg > 0) this.Arr[0] = this.SForwBackw;
		else
		{
			this.Arr[0] = String(minPg);
		}
		
		for(var i = minPg + 1, j = 1; i < maxPg; i++, j++)
		{
			this.Arr[j] = String(i);
		}
		
		if(maxPg < lastPg) 
			this.Arr[nmOpts-1] = this.SForwBackw;
		else
			this.Arr[nmOpts-1] = String(maxPg);
		
		for(i in this.Arr)
		{
			if(!isNaN(parseInt(this.Arr[i])))
			  this.Arr[i] = String(parseInt(this.Arr[i])+1);
			
			//alert("arri="+this.Arr[i]);
		}
		
		return this.Arr;
	};
}

var USelector = new TSelectorArr(); 

Date.prototype.GetRusStr = function(s)
{
	var m2 = s.match(/[0-9]{2}/g);
	var m4 = s.match(/[0-9]{4}/g);
	
	var d = m2[3];
	var m = m2[2];
	var y = m4[0];
	var hh = m2[4];
	var mm = m2[5];
	var ss = m2[6];
	
	var p = ".";
	var c = ":";
	
	return d + p + m + p + y + " " + hh + c + mm + c + ss;
};

function $(id)
{
	return document.getElementById(id);
}

var Context = {
		BError: false,
		SetError: function(bVal)
		{
			this.BError = bVal;
		},
		IsError: function()
		{
			return this.BError;
		}
};

