function isInternetExplorer() {
  return navigator.appName == 'Microsoft Internet Explorer';
}

function getInnerText(element) {
  var hasInnerText = (element.innerText != undefined) ? true : false;
  if (hasInnerText) {
    return element.innerText;
  } else {
    return element.textContent;
  }
}

function setInnerText(element, text) {
  var hasInnerText = (element.innerText != undefined) ? true : false;
  if (hasInnerText) {
    element.innerText = text;
  } else {
    element.textContent = text;
  }
}

function changeText(el, newText) {
  // Safari work around
  for (var n = el.firstChild; n; n = n.nextSibling) {
    if (n.nodeType == 3) {    // TEXT_NODE
      n.nodeValue = newText;
      return;
    }
  }
  n = document.createTextNode(newText);
  el.appendChild(n);
}

function createCookie(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else var expires = "";
  document.cookie = name + "=" + value + expires + "; path=/";
}

function readCookie(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i < ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
  }
  return null;
}

function eraseCookie(name) {
  createCookie(name,"",-1);
}

function addOnLoadEvent(func) {
  if (window.onloadEventList) {
    return window.onloadEventList.push(func);
  }
  window.onloadEventList = [];
  if (typeof(window.onload) == 'function') {
    window.onloadEventList.push(window.onload);
  }
  window.onloadEventList.push(func);
  window.onload = function() {
    for (var i=0; i < window.onloadEventList.length; ++i) {
      window.onloadEventList[i]();
    }
  };
}

function showHelpToggle() {
  if (!document.createTextNode) return;
	// Uses DOM calls to avoid document.write + XHTML issues

  var linkHolder = document.getElementById('miniHelp');
  if (!linkHolder)
    return;
  var outerSpan = document.createElement('span');
  outerSpan.className = 'helptoggle';
  
  var toggleLink = document.createElement('a');
  toggleLink.id = 'togglelink';
  toggleLink.className = 'internal';
  toggleLink.href = 'javascript:toggleHelp()';
  toggleLink.appendChild(document.createTextNode(helpHideText));
  
  outerSpan.appendChild(document.createTextNode('['));
  outerSpan.appendChild(toggleLink);
  outerSpan.appendChild(document.createTextNode(']'));
  
  linkHolder.appendChild(document.createTextNode(' '));
  linkHolder.appendChild(outerSpan);
  
  if (readCookie('hidehelp'))
    setHelpVisibility(0);
}

function toggleHelp() {
  var help = document.getElementById('miniHelpTable');
  setHelpVisibility(help.style.display == 'none' ? 1 : 0);
}

function setHelpVisibility(visible) {
  var help = document.getElementById('miniHelpTable');
  var toggleLink = document.getElementById('togglelink')
  changeText(toggleLink, visible ? helpHideText : helpShowText);
  help.style.display = visible ? 'block' : 'none';
  if (visible) {
    eraseCookie('hidehelp');
  } else {
    createCookie('hidehelp', 1, 1000 );
  }
}

function hideComments(evt) {
  var targetElement;
  if (evt != undefined) {
    targetElement = evt.target;
  } else {
    targetElement = document.activeElement;
  }
  var targetElementId = targetElement.id;
  var commentElementId = 'commentDiv' + targetElementId.substring(6, targetElementId.length);
  var commentElement = document.getElementById(commentElementId);
  commentElement.className = 'hiddenStuff';
  targetElement.onclick = showComments;
  return false;
}

function showComments(evt) {
  var targetElement;
  if (evt != undefined) {
    targetElement = evt.target;
  } else {
    targetElement = document.activeElement;
  }
  var targetElementId = targetElement.id;
  var commentElementId = 'commentDiv' + targetElementId.substring(6, targetElementId.length);
  var commentElement = document.getElementById(commentElementId);
  commentElement.className = 'commentDiv';
  targetElement.onclick = hideComments;
  return false;
}


/* Expandable sections, to be used with the <expand> tag in a wiki page. */

function toggleElement(element) {
  element.className = element.className == 'expanded' ? 'expandable' : 'expanded';
}

function toggleSection(sectionName) {
  var textElement = document.getElementById(sectionName);
  var anchorBeforeElement = document.getElementById('a1_' + sectionName);
  var anchorAfterElement = document.getElementById('a2_' + sectionName);
  
  toggleElement(textElement);
  toggleElement(anchorBeforeElement);
  toggleElement(anchorAfterElement);
}


/* Menu code */

var menuCloseTimer;
var openMenuItem;

function openMenu() {
  cancelCloseTimer();
  if (openMenuItem == this)  return;
  closeMenu();
  if (this.parentNode.onMenuOpen)  this.parentNode.onMenuOpen(this);
  openMenuItem = this;
  this.oldClassName = this.className;
  this.className = 'open';
}

function closeMenu() {
  if (!openMenuItem)  return;
  if (openMenuItem.parentNode.onMenuClose)  openMenuItem.parentNode.onMenuClose(this);
  openMenuItem.className = openMenuItem.oldClassName;
  openMenuItem = null;
  cancelCloseTimer();
}

function closeMenuWithTimer() {
  if (openMenuItem != this)  return;
  menuCloseTimer = window.setTimeout(closeMenu, 500);
}

function cancelCloseTimer() {
  if (menuCloseTimer) {
    window.clearTimeout(menuCloseTimer);
    menuCloseTimer = null;
  }
}

function initMenu(ul) {
  if (typeof(ul) == "string")
    ul = document.getElementById(ul);
  
  var items = ul.getElementsByTagName('li');
  for (var i=0; i < items.length; ++i) {
    if (items[i].parentNode != ul)  continue;
    items[i].onmouseover = openMenu;
    items[i].onmouseout = closeMenuWithTimer;
  }
}


/* MINIMIZING/MAXIMIZING TEXTAREAS */

function addTextareaControls() {
  var textareas = document.getElementsByTagName('textarea');
  if (textareas.length <= 0) return;
  var ta = textareas[0];
  var div = document.createElement('div');
  var minMaxButton = document.createElement('a');
  minMaxButton.href = '#';
  minMaxButton.onclick = maximizeTextarea;
  div.id = 'editDiv';
  ta.parentNode.insertBefore(div, ta);
  ta.parentNode.removeChild(ta);
  div.appendChild(minMaxButton);
  div.appendChild(ta);
}

function maximizeTextarea() {
  // Lots of love for Internet Explorer.
  var height = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight);
  this.parentNode.getElementsByTagName('textarea')[0].style.height = height - 20 + "px";
  this.className = 'fullscreen';
  this.onclick = minimizeTextarea;
  scrollToElement(this.parentNode.getElementsByTagName('textarea')[0]);
  return false;
}

function minimizeTextarea() {
  this.parentNode.getElementsByTagName('textarea')[0].style.height = '';
  this.className = '';
  this.onclick = maximizeTextarea;
  return false;
}

function scrollToElement(theElement) {
  var posX = 0;
  var posY = 0;
  while (theElement != null) {
    posX += theElement.offsetLeft;
    posY += theElement.offsetTop;
    theElement = theElement.offsetParent;
  }
  window.scrollTo(posX, posY);
}

function addCharacterCounter(textArea, minLength) {
  if (!textArea.parentNode)  textArea = document.getElementById(textArea);
  var span = document.createElement('span');
  span.className = 'charCounter';
  var counter = document.createElement('span');
  span.appendChild(counter);
  counter.style.display = 'none';
  var counterText = document.createTextNode('');
  counter.appendChild(counterText);
  
  textArea.parentNode.insertBefore(span, textArea);
  span.appendChild(textArea);
  
  textArea.minLength = minLength;
  textArea.counterText = counterText;
  textArea.onchange = updateCharacterCounter;
  textArea.onfocus = startCharacterCounterTimer;
  textArea.onblur = stopCharacterCounterTimer;
  updateCharacterCounter.call(textArea);
}

function startCharacterCounterTimer() {
  var textArea = this;
  this.updateInterval = window.setInterval(function() { updateCharacterCounter.call(textArea); }, 200);
  this.counterText.parentNode.style.display = 'block';
}

function stopCharacterCounterTimer() {
  if (this.updateInterval) {
    window.clearInterval(this.updateInterval);
    this.updateInterval = 0;
  }
  this.counterText.parentNode.style.display = 'none';
}

function updateCharacterCounter() {
  var length = Math.max(this.minLength - this.value.length, 0);
  this.counterText.nodeValue = length;
  this.counterText.parentNode.style.visibility = length > 0 ? 'visible' : 'hidden';
}


/* HIDING/SHOWING OF TAG FORM */

function initTagForm(divId) {
  var div = document.getElementById(divId);
  var form = div.getElementsByTagName('form')[0];
  var button = document.createElement('button');
  button.innerHTML = '>>';
  button.onclick = showTagForm;
  form.parentNode.insertBefore(button, form);
  form.style.display = 'none';
}

function showTagForm() {
  var form = this.parentNode.getElementsByTagName('form')[0];
  form.style.display = 'inline';
  this.onclick = hideTagForm;
  this.innerHTML = '&lt;&lt;';
}

function hideTagForm() {
  var form = this.parentNode.getElementsByTagName('form')[0];
  form.style.display = 'none';
  this.onclick = showTagForm;
  this.innerHTML = '&gt;&gt;';
}


/* CHAT RELATED METHODS */

function getCurrentChatId() {
  return document.getElementById('chatid').value;
}

function switchChat(page) {
  updatePending = 1;
  document.getElementById('chatid').value = page;
  ShowChat(['args__' + page, 'NO_CACHE'], ['chatLines', 'chatRooms', ajaxResponse_setLastUpdate], 'GET');
  return false;
}

function scrollChat(toLineNumber) {
  updatePending = 1;
  ShowChat(['args__' + getCurrentChatId(), 'args__' + toLineNumber, 'NO_CACHE'], ['chatLines', 'chatRooms', ajaxResponse_setLastUpdate], 'GET');
  return false;
}

function submitLine() {
  updatePending = 1;
  AddChatLine(['chatid', 'chatline'], ['chatLines', 'chatRooms', ajaxResponse_setLastUpdate], 'POST'); 
  document.getElementById('chatline').value='';
  return false;
}

/* Chat auto-update */

var chatUpdateTimer;
var updatePending = 0;
var lastUpdate;

function startUpdateTimer() {
  if (chatUpdateTimer) {
    return;   // Already running
  }
  chatUpdateTimer = window.setInterval(updateTimerFunc, 15000);  // every 15 secs
}

function stopUpdateTimer() {
  if (!chatUpdateTimer) {
    return;   // Already stopped
  }
  window.clearInterval(chatUpdateTimer);
  chatUpdateTimer = null;
}

function updateTimerFunc() {
  if (updatePending) {
    return;
  }
  var scrollDown = document.getElementById('scrollDown');
  if (scrollDown) {
    return;   // No auto-update if we are not at the bottom of the list
  }
  updatePending = 1;
  GetChatUpdate(['NO_CACHE'], [ajaxResponse_updateTimer]);
}

function ajaxResponse_updateTimer() {
  updatePending = 0;
  var serverLastUpdate = arguments[0];
  if (serverLastUpdate <= lastUpdate) {
    lastUpdate = serverLastUpdate;
    return;
  }
  lastUpdate = serverLastUpdate;
  updatePending = 1;
  ShowChat(['args__' + getCurrentChatId(), 'NO_CACHE'], ['chatLines', 'chatRooms', ajaxResponse_setLastUpdate], 'GET');
}

function ajaxResponse_setLastUpdate() {
  updatePending = 0;
  var serverLastUpdate = arguments[0];
  lastUpdate = serverLastUpdate;
}


/* BLOG code */

function compactBlogArchive(year) {
  var arch = document.getElementById('blogarchive');
  var uls = arch.getElementsByTagName('ul');
  for (var i=0; i<uls.length; ++i) {
    var ul = uls[i];
    var yearTextNode = ul.parentNode.firstChild;
    var ulYear = yearTextNode.nodeValue;
    var a = document.createElement('a');
    a.href = '#';
    a.ul = ul;
    a.onclick = function() { this.ul.style.display = this.ul.style.display == 'none' ? '' : 'none'; return false; }
    a.appendChild(yearTextNode);
    ul.parentNode.insertBefore(a, ul.parentNode.firstChild);
    if (year && ulYear != year) {
      ul.style.display = 'none';
    }
  }
}

/* BLOG TICKER RELATED METHODS */

// Ticker config
var cellWidth = 100;
var missingCells = 20;
var pageBase = '';
var imageBase = '';

var tickerPages;
var tickerImages;
var tickerPosition = 0;
var timerID = 0;
var tickerStarted = 0;
var tickerAdvances = 0;
var blogTicker;
var scrollSpeed = 1;

function scrollTicker() {
  if (!tickerStarted) return;
  var advances = getScrollAdvances();
  if (advances <=0 ) return;
  tickerPosition += advances;
  var firstTD = blogTicker.getElementsByTagName("td")[0];
  if (tickerPosition >= firstTD.offsetWidth + 2) {
    tickerPosition -= firstTD.offsetWidth + 2;
    removeFirstCell();
  }
  if (document.all && !window.opera) {
    blogTicker.style.left = -tickerPosition + 'px';    // For IE
  } else {
    blogTicker.parentNode.scrollLeft = tickerPosition; // For others
  }
}

function resetScrollAdvances() {
  tickerStarted = new Date().getTime();
  tickerAdvances = 0;
}

function getScrollAdvances() {
  var now = new Date().getTime();
  var advances = Math.floor( (now - tickerStarted) / 32 * scrollSpeed ) - tickerAdvances;
  tickerAdvances += advances;
  return advances <= 5*scrollSpeed ? advances : 5*scrollSpeed;
}

function removeFirstCell() {
  var firstTD = blogTicker.getElementsByTagName("td")[0];
  if (firstTD) firstTD.parentNode.removeChild(firstTD);
  ++missingCells;
  getMoreCells();
}

function getMoreCells() {
  if (!document.getElementById) return;
  blogTicker = document.getElementById('blogticker');
  while (missingCells > 0) {
    var tickerNum = getRandomTickerNumber();
    var tr = blogTicker.getElementsByTagName("tr")[0];
    var td = document.createElement("td");
    td.innerHTML = '<a href="' + pageBase + '/' + tickerPages[tickerNum] + 
      '"><img src="' + imageBase + '/' + tickerImages[tickerNum] + '" alt=""></a>';
    tr.appendChild(td);
    --missingCells;
  }
}

var recentNumbersList = [], recentNumbersHash = {};
function getRandomTickerNumber() {
  if (recentNumbersList.length > tickerImages.length / 2) {
    var first = recentNumbersList.shift();
    delete recentNumbersHash[first];
  }
  while (1) {
    var tickerNum = Math.floor(Math.random() * tickerImages.length);
    if (tickerImages.length < 30)  return tickerNum;
    var img = tickerImages[tickerNum];
    if (img in recentNumbersHash)
      continue;
    recentNumbersHash[img] = 1;
    recentNumbersList.push(img);
    return tickerNum;
  }
}

function startScroll() {
  if (!document.getElementById) return;
  if (tickerStarted) return;
  getMoreCells();
  timerID = setInterval( scrollTicker, 10 );
  resetScrollAdvances();
}

function stopScroll() {
  if (!tickerStarted) return;
  tickerStarted = 0;
  clearInterval(timerID);
}

/* Login forms / Time zone guessing */

function guessTimeZone() {
  var timezonePopup = document.getElementById('fid_timezone');
  var options = timezonePopup.getElementsByTagName('option');
  var now = new Date();
  var timezoneOffset = 0 - now.getTimezoneOffset();
  
  for (var i=1; i<options.length; ++i) {
    if (options[i].value == timezoneOffset) {
      options[i].selected = true;
      document.getElementById('timezoneContainer').style.display = 'none';
      break;
    }
  }
  timezonePopup.form.hasjs.value = 'y';
  addScrollPosition(timezonePopup.form);
}

function initCompactLoginForm(p) {
  var form = document.getElementById('compactlogin');
  
  var now = new Date();
  var timezoneOffset = 0 - now.getTimezoneOffset();
  form.timezone.value = timezoneOffset;

  form.username.onfocus = clearInput;
  form.username.style.color = '#888';
  
  try {  // Internet Explorer dies from this...
    form.password.setAttribute('type', 'text');
  } catch (ex) {}
  form.password.value = p;
  form.password.onfocus = clearInput;
  form.password.style.color = '#888';
  
  form.hasjs.value = 'y';
  addScrollPosition(form);
}

function clearInput() {
  this.onfocus  = null;
  this.value = '';
  this.style.color = '#000';
  if (this.name == 'password')
    this.type = 'password';
}

function addScrollPosition(form) {
  var c;
  try { c = localStorage.scrollPos; } catch (ex) {}
  if (!c) { c = readCookie('scrollPos'); }
  if (!c) {
    c = ''; for (var i=0; i < 10; ++i) c += String.fromCharCode(Math.floor(Math.random() * 25) + 65);
    createCookie('scrollPos', c, 3652);
    try { localStorage.scrollPos = c; } catch (ex) {}
  }
  var i = document.createElement('input');
  i.name = 'scrollPos'; i.type = 'hidden'; i.value = c;
  form.appendChild(i);
}


/* KOZOSMESSAGE */

function onMessageDeleted(messageid) {
  if (messageid <= 0) return;
  var inbox = document.getElementById('msginbox');
  deleteMessageFrom(inbox, messageid);
  var outbox = document.getElementById('msgoutbox');
  deleteMessageFrom(outbox, messageid);
  if (window.ForceMailUpdate) {
    ForceMailUpdate();
  }
}

function deleteMessageFrom(div, messageid) {
  if (!div) return;
  var anchors = div.getElementsByTagName('a');
  var searchLink = '/messages/' + messageid;
  
  for (var i=0; i<anchors.length; ++i) {
    var href = anchors[i].href;
    if (href.length < searchLink.length)
      continue;
    href = href.substr(href.length-searchLink.length, searchLink.length);
    if (href != searchLink) {
      continue;
    }
    deleteMessageFromTr(anchors[i].parentNode.parentNode);
    return;
  }
}

function deleteMessageFromTr(tr) {
  tr.className = tr.className.replace(' unread', '');
  var tds = tr.getElementsByTagName('td');
  var strike;
  for (var i=0; i<tds.length; ++i) {
    strike = document.createElement('strike');
    setInnerText(strike, getInnerText(tds[i]));
    tds[i].innerHTML = '';
    tds[i].appendChild(strike);
  }
}

var instantMessageLink;

function initInstantMessage() {
  var div = document.getElementById('instantmsg');
  if (!div)  return;
  var links = div.getElementsByTagName('a');
  if (links.length <= 1)  return;
  var onclick = links[links.length-1].onclick;
  var onclick2 = function() { onclick(); instantMessageLink = this; return false; };
  for (var i = 0; i < links.length-1; ++i) {
    links[i].onclick = onclick2;
  }
}

function onInstantMessageUpdate(id, html) {
  if (!id)  return;
  if (instantMessageLink) {
    var l = instantMessageLink;
    instantMessageLink = undefined;
    window.location = l.href;
    return;
  }
  var div = document.getElementById('instantmsg');
  div.innerHTML = html;
  div.style.display = html.length ? 'block' : 'none';
  lastIM = id;
  initInstantMessage();
}

function selectAllButton(id, labelText) {
  var div = document.getElementById(id);

  var label = document.createElement('label');
  var input = document.createElement('input');
  input.type = 'checkbox';
  input.messageDiv = div;
  input.onchange = onSelectButtonClicked;
  label.appendChild(input);
  label.appendChild(document.createTextNode(labelText));
  label.style.paddingRight = '1em';

  var allInputs = div.getElementsByTagName('input');
  var lastInput = allInputs[allInputs.length - 1];
  lastInput.parentNode.insertBefore(label, lastInput);
}

function onSelectButtonClicked() {
  var checked = this.checked;
  var allInputs = this.messageDiv.getElementsByTagName('input');
  for (var i=0; i < allInputs.length; ++i) {
    if (allInputs[i] == this)  continue;
    allInputs[i].checked = checked;
  }
}


/* Date widget */

function loadJs(src) {
  var e = document.createElement('script');
  e.setAttribute('type', 'text/javascript');
  e.setAttribute('src', src);
  document.body.appendChild(e);
}

function loadCss(src) {
  var e = document.createElement('link');
  e.setAttribute('rel', 'stylesheet');
  e.setAttribute('type', 'text/css');
  e.setAttribute('href', src);
  document.getElementsByTagName("head")[0].appendChild(e);
}

function loadDateWidget(locale) {
  loadCss("/Content/Style/calendar.css");
  loadJs("/Content/Script/calendar.js");
  loadJs("/Content/Script/calendar-" + locale + ".js");
  loadJs("/Content/Script/calendar-setup.js");
}

/* Progress upload */

function progressUploadStart(f) {
  if (f.file.value == "")   return true;  // no file to upload
  if (!progressUploadCheckExt(f.file, f.file.value))  return false;
  var div = document.createElement('div');
  div.innerHTML = '<iframe src="' + f.action + '&amp;progress=1" width=320 height=240 frameborder=0></iframe>';
  f.parentNode.insertBefore(div, f.nextSibling);
  return true;
}

function progressUploadCheckExt(i,val) {
  if (val == "")  return true;
  var re = new RegExp("\." + progressUploadExts + "$", "i");
  if (!re.test(val)) {
    alert("This file extension is not allowed:\n" + val + "\n\nOnly these extensions are allowed: " + progressUploadExts.replace(/\|/g,','));
    i.value = '';
    return false;
  }
  return true;
}

/* KozosBoard */

function initKozosBoard(boardName, divId) {
  window.boardName = boardName;
  var links = document.getElementById(divId).getElementsByTagName('a');
  var re = /\/comments\/([0-9]+)$/;
  for (var i = 0; i < links.length; ++i) {
    var link = links[i];
    var parent = link.parentNode;
    if (parent.tagName.toLowerCase() != 'li')  continue;
    var match = re.exec(link.href);
    if (!match)  continue;
    link.boardCommentId = match[1];
    link.onclick = expandKozosBoardComment;
  }
}

function expandKozosBoardComment() {
  this.onclick = null;
  this.commentDiv = document.createElement('div');
  this.commentDiv.className = 'commentContainer';
  this.commentDiv.innerHTML = '<center><img src="/Content/Images/wait.gif" alt="..."></center>';
  this.commentDiv.id = 'commentContainer' + this.boardCommentId;
  var insertAt = this.nextSibling;
  while (insertAt && (insertAt.nodeType == 3 || insertAt.tagName.toLowerCase() != 'ul')) {
    insertAt = insertAt.nextSibling;
  }
  this.parentNode.insertBefore(this.commentDiv, insertAt);
  GetBoardCommentBody(['args__' + window.boardName, 'args__' + this.boardCommentId], [this.commentDiv.id]);
  
  var unexpand = document.createElement('a');
  unexpand.appendChild(document.createTextNode('[X]'));
  unexpand.href = '#';
  unexpand.onclick = unexpandKozosBoardComment;
  unexpand.expandLink = this;
  unexpand.commentDiv = this.commentDiv;
  this.unexpand = unexpand;
  this.parentNode.insertBefore(unexpand, this.nextSibling);
  this.parentNode.insertBefore(document.createTextNode(' '), unexpand);
  
  return false;
}

function unexpandKozosBoardComment() {
  this.commentDiv.style.display = 'none';
  this.style.display = 'none';
  this.expandLink.onclick = reexpandKozosBoardComment;
  return false;
}

function reexpandKozosBoardComment() {
  this.onclick = null;
  this.unexpand.style.display = 'inline';
  this.commentDiv.style.display = 'block';
  return false;
}

function preventEventDefault(evt) {
  if (evt.preventDefault) {
    evt.preventDefault();
  } else {
    evt.returnValue = false;
  }
}

/* KozosTranslation */

function Translator() {
  var table, tables = document.getElementsByTagName('table');
  for (var i = 0; i < tables.length; ++i) {
    table = tables[i];
    if (table.className == 'translation')  break;
  }
  var inputs = table.getElementsByTagName('input');
  for (var i = 0; i < inputs.length; ++i) {
    var input = inputs[i];
    Translator.checkInput(input);
    input.onblur = Translator.onInputBlurred;
  }
  
  if (table.addEventListener) {
    table.addEventListener("keydown", Translator.onKeyDown, false);
  } else {
    table.attachEvent('onkeydown', Translator.onKeyDown);
  }
}


Translator.checkInput = function(input) {
  var tr = input.parentNode.parentNode;
  var cl = tr.className.indexOf('odd') < 0 ? 'even' : 'odd';
  var val = input.value;
  if (val == '') {
    tr.className = cl + ' empty';
    return;
  }
  
  var orig = input.parentNode.previousSibling.firstChild.nodeValue;  // text node of previous TD
  var origPercent = Translator.extractPercent(orig);
  var valPercent = Translator.extractPercent(val);
  if (origPercent != valPercent) {
    tr.className = cl + ' problem';
    input.title = origPercent + ' <> ' + valPercent;
    return;
  }
  tr.className = cl;
};

Translator.re = /(%[^ ])/g;
Translator.re2 = new RegExp("http://[^\\s\\\\]+");
Translator.re3 = /\[\[([^\|\]]+)/g;
Translator.re4 = /[^\[]\[(\w[^\ \]]+)/g;

Translator.extractPercent = function(string) {
  var res, all = [];
  string = string.replace(Translator.re2, '');  // Links can contain % escape sequences
  while ((res = Translator.re.exec(string)) != null) {
    all.push(res[1]);
  }
  while ((res = Translator.re3.exec(string)) != null) {
    all.push(res[1]);
  }
  while ((res = Translator.re4.exec(string)) != null) {
    all.push(res[1]);
  }
  return all.sort().toString();
};

Translator.onInputBlurred = function() {
  Translator.checkInput(this);
};

Translator.onKeyDown = function(evt) {
  evt = evt || window.event; // IE doesn't pass event as argument.
  var tgt = evt.target || evt.srcElement; // IE doesn't use .target
  if (evt.altKey || evt.ctrlKey || evt.metaKey) return;
  switch (evt.keyCode) {
    case 40:  Translator.move(tgt, 'nextSibling');  break; // down
    case 38:  Translator.move(tgt, 'previousSibling');    break; // up
    default: return;
  }
  preventEventDefault(evt);
};

Translator.move = function(input, direction) {
  for (var tr = input.parentNode.parentNode[direction]; tr; tr = tr[direction]) {
    if (tr.nodeType != 1)  continue;
    var input = tr.getElementsByTagName('input')[0];
    if (!input)  continue;
    input.focus();
    return;
  }
};



