///////////////////////
// General Functions //
///////////////////////

// Escapes HTML content
function escapeHTML (html) {
  return html.replace (/&/g, "&amp;").replace (/</g, "&lt;").replace (/>/g, "&gt;");
}

// Gets the offset of an element relative to the window
function getAbsoluteOffset (element) {
  if (element.offsetTop > 0) {
    return element.offsetTop + getAbsoluteOffset (element.offsetParent);
  } else {
    return 0;
  }
}

// Scrolls to the element with the given ID
function scrollToId (id) {
  var element= document.getElementById (id);

  var offset= getAbsoluteOffset (element);

  if (document.documentElement && document.documentElement.scrollTop != undefined) {
    document.documentElement.scrollTop= offset;
  } else if (document.body) {
    document.body.scrollTop= offset;
  }
}

// Scrolls to the top of the chat
function refreshChatScroll () {
  var chat= document.getElementById ("chat");
  var topChat= document.getElementById ("top_chat");

  if (chat == topChat.offsetParent) {
    // If the offset is based on the chat window, just scroll
    chat.scrollTop= topChat.offsetTop;
  } else {
    // Otherwise scroll relative to the chat window
    chat.scrollTop= topChat.offsetTop - chat.offsetTop;
  }
}

// Updates the font family and color for user preferences chat preview
function updateChatPreview () {
  var chatPreview= document.getElementById ("chat_preview");

  chatPreview.style.fontFamily= document.getElementById("user_chat_font").value;
  chatPreview.style.color= document.getElementById("user_chat_color").value;
}

function getHTTPRequest () {
  var httpRequest= false;

  if (window.XMLHttpRequest) {
    httpRequest= new XMLHttpRequest ();
    if (httpRequest.overrideMimeType) {
      httpRequest.overrideMimeType('text/xml');
    }
  } else if (window.ActiveXObject) {
    try {
      httpRequest= new ActiveXObject ("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        httpRequest= new ActiveXObject ("Microsoft.XMLHTTP");
      } catch (e) {}
    }
  }

  if (!httpRequest) {
    alert ("AJAX error: cannot create XMLHTTP instance");
    return false;
  }

  return httpRequest;
}

function checkUpdate (httpRequest) {
  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      var xml= httpRequest.responseXML;
      writePage (xml);
    } else {
      alert ("There was a problem with the request, could not update page data");
    }
  }
}

function writePreview (httpRequest, contentBlock) {
  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      contentBlock.innerHTML= httpRequest.responseText;
    } else {
      alert ("There was a problem with the request, could not write preview");
    }
  }
}

// Takes a retrieved XML object and writes the data into the displayed page
function writePage (xml) {
  if (xml.getElementsByTagName ("activeUsers").length == 0) {
    alert ("There was a problem with the request, could not update page data");
    return;
  }

  // Overwrite active user total
  var totalUsers= xml.getElementsByTagName ("activeUsers")[0].getAttribute ("total");
  var activeUserTotal= document.getElementById ("active_user_total");
  clearNodes (activeUserTotal);
  activeUserTotal.appendChild (document.createTextNode ((totalUsers == 0 ? "no" : totalUsers) + " active user" + (totalUsers == 1 ? "" : "s")));

  // Get user information
  var userInfo= xml.getElementsByTagName ("userInfo")[0];
  var currentUserId= userInfo.getAttribute ("id");
  var allowMessages= userInfo.getAttribute ("allowMessages");

  // Overwrite active user list
  var activeUserList= document.getElementById ("active_user_list_data");
  clearNodes (activeUserList);
  var activeUsers= xml.getElementsByTagName ("activeUser");
  if (activeUsers.length == 0) {
    var nodeText= totalUsers > 0 ? "(anonymous)" : "(none)";
    var node= document.createElement ("span");
    node.className= "no_active_users";
    node.appendChild (document.createTextNode (nodeText));
    activeUserList.appendChild (node);
  } else {
    for (var i= 0; i < activeUsers.length; i++) {
      var username= activeUsers[i].getAttribute ("username");
      var userId= activeUsers[i].getAttribute ("id");
      if (allowMessages && currentUserId != userId) {
        var messageNode= document.createElement ("a");
        messageNode.setAttribute ("href", "javascript:popMessage ("+userId+");");
        var imageNode= document.createElement ("img");
        imageNode.setAttribute ("src", "/images/message.gif");
        messageNode.appendChild (imageNode);
        activeUserList.appendChild (messageNode);
        activeUserList.appendChild (document.createTextNode ("\u00a0"));
      }

      var node= document.createElement ("a");
      node.setAttribute ("href", "/user/?m=view_profile&id=" + userId);
      node.appendChild (document.createTextNode (username));
      activeUserList.appendChild (node);
      activeUserList.appendChild (document.createTextNode (", "));
    }
    activeUserList.removeChild (activeUserList.lastChild);
  }

  // Update sidebar information
  var newForumPosts= xml.getElementsByTagName ("navigation")[0].getAttribute ("newForumPosts");

  var newMessages= xml.getElementsByTagName ("navigation")[0].getAttribute ("newMessages");
  var unackedMessages= xml.getElementsByTagName ("navigation")[0].getAttribute ("unackedMessages");
  var newBlogs= xml.getElementsByTagName ("navigation")[0].getAttribute ("newBlogs");
  var newBlogComments= xml.getElementsByTagName ("navigation")[0].getAttribute ("newBlogComments");
  var newReviews= xml.getElementsByTagName ("navigation")[0].getAttribute ("newReviews");
  var newReviewComments= xml.getElementsByTagName ("navigation")[0].getAttribute ("newReviewComments");
  var newCharacters= xml.getElementsByTagName ("navigation")[0].getAttribute ("newCharacters");
  var newCharacterComments= xml.getElementsByTagName ("navigation")[0].getAttribute ("newCharacterComments");
  var newRewards= xml.getElementsByTagName ("navigation")[0].getAttribute ("newRewards");
  var newErrors= xml.getElementsByTagName ("navigation")[0].getAttribute ("newErrors");

  // In case of some weirdness with the totals, override new messages with the unacked total
  if (parseInt (unackedMessages) > parseInt (newMessages)) newMessages= unackedMessages;

  if (newMessages) {
    var sidebarLine= document.getElementById ("new_messages");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newMessages + " new message" + (newMessages != 1 ? "s" : "");
  }

  if (unackedMessages) {
    document.numMessages= unackedMessages;
    document.getElementById ("message_window").style.display= "block";
    document.getElementById ("message_window_text").innerHTML= "You have " + newMessages + " new message" + (newMessages != 1 ? "s" : "");
  }

  if (newBlogs) {
    var sidebarLine= document.getElementById ("new_blogs");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newBlogs + " new blog entr" + (newBlogs == 1 ? "y" : "ies");
  }
  if (newBlogComments) {
    var sidebarLine= document.getElementById ("new_blog_comments");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newBlogComments + " new blog comment" + (newBlogComments != 1 ? "s" : "");
  }

  if (newReviews) {
    var sidebarLine= document.getElementById ("new_reviews");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newReviews + " new review" + (newReviews != 1 ? "s" : "");
  }
  if (newReviewComments) {
    var sidebarLine= document.getElementById ("new_review_comments");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newReviewComments + " new review comment" + (newReviewComments != 1 ? "s" : "");
  }

  if (newCharacters) {
    var sidebarLine= document.getElementById ("new_rpgcc");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newCharacters + " new character" + (newCharacters != 1 ? "s" : "");
  }
  if (newCharacterComments) {
    var sidebarLine= document.getElementById ("new_rpgcc_comments");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newCharacterComments + " new character comment" + (newCharacterComments != 1 ? "s" : "");
  }

  if (newRewards) {
    var sidebarBlock= document.getElementById ("new_rewards_div");
    var sidebarLine= document.getElementById ("new_rewards");
    sidebarBlock.style.display= "block";
    sidebarLine.innerHTML= newRewards + " new reward" + (newRewards != 1 ? "s" : "");
  }

  if (newErrors) {
    var sidebarBlock= document.getElementById ("new_errors_div");
    var sidebarLine= document.getElementById ("new_errors");
    sidebarBlock.style.display= "block";
    sidebarLine.innerHTML= newErrors + " new error" + (newErrors != 1 ? "s" : "");
  }

  var forums= xml.getElementsByTagName ("forum");
  for (var i= 0; i < forums.length; i++) {
    var forum= document.getElementById ("nav_forum_" + forums[i].getAttribute ("id"));

    if (newForumPosts) {
      forum.style.fontWeight= "bold";
    }

    forum.childNodes[1].innerHTML= "&nbsp;(" + forums[i].getAttribute ("posts") + ")";
  }

  if (document.pageOptions.chat) {
    // Add new lines of chat
    var chat= document.getElementById ("chat");

    document.latestChatId= xml.getElementsByTagName ("chat")[0].getAttribute ("latestId");

    var newChatLines= xml.getElementsByTagName ("chatLine");
    var previousHeight= chat.scrollHeight;

    for (var i= 0; i < newChatLines.length; i++) {
      var newChatLine;
      if (document.reverseScroll == 1) {
        newChatLine= newChatLines[i];
      } else {
        newChatLine= newChatLines[newChatLines.length - i - 1];
      }

      var username= newChatLine.getAttribute ("username");
      var userId= newChatLine.getAttribute ("userId");
      var timestamp= newChatLine.getAttribute ("timestamp");
      var chatFont= newChatLine.getAttribute ("chatFont");
      var chatColor= newChatLine.getAttribute ("chatColor");

      var node= document.createElement ("div");

      var header= document.createElement ("span");
      header.className= "chat_header";
      node.appendChild (header);

      var nameLink= document.createElement ("a");
      nameLink.setAttribute ("href", "/user/?m=view_profile&id=" + userId);
      nameLink.appendChild (document.createTextNode (username));
      header.appendChild (nameLink);

      header.appendChild (document.createTextNode (" ("+timestamp+"): "));

      var contents= document.createElement ("span");
      contents.style.fontFamily= chatFont;
      contents.style.color= chatColor;
      contents.className= "user_line";
      node.appendChild (contents);

      putXMLasHTML (contents, newChatLine);

      if (document.reverseScroll == 1) {
        chat.appendChild (node);
      } else {
        chat.insertBefore (node, chat.firstChild);
      }
    }

    if (document.reverseScroll == 1) {
      chat.scrollTop+= (chat.scrollHeight - previousHeight);
    }
  }

  // If there is an alert message waiting, display it
  var alertXML= xml.getElementsByTagName ("alertMessage");
  if (alertXML.length > 0) {
    var alertMessage= alertXML[0];
    var alertBlock= document.getElementById ("alert_message");

    var messageBlock= document.createElement ("p");
    putXMLasHTML (messageBlock, alertMessage);
    alertBlock.appendChild (messageBlock);
    alertBlock.style.display= "block";
  }
}

// Converts XML recursively into equivalent HTML (works for tags allowed in short-form content processing)
function putXMLasHTML (parentNode, xml) {
  for (var i= 0; i < xml.childNodes.length; i++) {
    var node= xml.childNodes[i];

    if (node.nodeName == "#text") {
      node.nodeValue= node.nodeValue.replace (/&ndash;/g, '\u2013').replace (/&bull;/g, '\u2022');
      // This special pattern is used to make sure IE notes empty nodes
      if (node.nodeValue == "__%__") node.nodeValue= " ";
      parentNode.appendChild (document.createTextNode (node.nodeValue));
    } else {
      var newNode= document.createElement (node.nodeName);

      switch (node.nodeName) {
      case "a":
        if (node.getAttribute ("href")) {
          newNode.setAttribute ("href", node.getAttribute ("href"));
        }
        if (node.getAttribute ("onclick")) {
          newNode.setAttribute ("onclick", node.getAttribute ("onclick"));
        }
        if (node.getAttribute ("onkeypress")) {
          newNode.setAttribute ("onkeypress", node.getAttribute ("onkeypress"));
        }
        if (node.getAttribute ("style")) {
          newNode= parseAndApplyStyle (newNode, node.getAttribute ("style"));
        }
        break;
      case "img":
        if (node.getAttribute ("src")) {
          newNode.setAttribute ("src", node.getAttribute ("src"));
        }
        if (node.getAttribute ("alt")) {
          newNode.setAttribute ("alt", node.getAttribute ("alt"));
        }
        break;
      case "span":
        if (node.getAttribute ("class")) {
          newNode.className= node.getAttribute ("class");
        }
        if (node.getAttribute ("style")) {
          newNode= parseAndApplyStyle (newNode, node.getAttribute ("style"));
        }
        if (node.getAttribute ("title")) {
          newNode.setAttribute ("title", node.getAttribute ("title"));
        }
        break;
      }

      parentNode.appendChild (newNode);
      putXMLasHTML (newNode, node);
    }
  }
}

// Takes a node and a style string, parsing the styles and applying them
function parseAndApplyStyle (node, styleString) {
  var styles= trim (styleString).split (";");

  for (var i= 0; i < styles.length; i++) {
    var parse= trim (styles[i]).split (":");
    var styleName= convertStyleNameFromCSS (trim (parse[0]));
    var styleValue= trim (parse[1]);

    node.style[styleName]= styleValue;
  }

  return node;
}

// Converts from css-style to javascriptStyle names
function convertStyleNameFromCSS (styleName) {
  var names= styleName.split ("-");

  var finalName= names[0];

  for (var i= 1; i < names.length; i++) {
    finalName+= names[i].substring (0, 1).toUpperCase () + names[i].substring (1, names[i].length);
  }

  return finalName;
}

// Expands the chat window
function expandChat () {
  if (document.reverseScroll) {
    document.getElementById ("chat").scrollTop-= 255;
  }

  document.getElementById ("chat").style.height= "300px";
  document.getElementById ("chat_expand").style.display= "none";
  document.getElementById ("chat_collapse").style.display= "inline";

  if (document.getElementById ("active_user_list")) {
    document.getElementById ("active_user_list").style.display= "block";
  }

  if (document.trackChatSize) {
    saveChatSize (1);
  }

  return void (0);
}

// Collapses the chat window
function collapseChat () {
  document.getElementById ("chat").style.height= "45px";
  document.getElementById ("chat_expand").style.display= "inline";
  document.getElementById ("chat_collapse").style.display= "none";

  if (document.reverseScroll) {
    document.getElementById ("chat").scrollTop+= 255;
  }

  if (document.getElementById ("active_user_list")) {
    document.getElementById ("active_user_list").style.display= "none";
  }

  if (document.trackChatSize) {
    saveChatSize (0);
  }

  return void (0);
}

function saveChatSize (large) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_chat_size&size=" + large + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Determines whether the enter key was pressed
function enterPressed (event) {
  return ((event.keyCode && event.keyCode == 13) || (event.which && event.which == 13));
}

// Turns spoiler display on
function showSpoiler (uniqueId, spoilerId) {
  var spoiler= document.getElementById ("spoiler_"+uniqueId+"_"+spoilerId);
  var message= document.getElementById ("message_"+uniqueId+"_"+spoilerId);

  spoiler.style.display= "inline";
  message.style.display= "none";
}

// Removes all children from a node
function clearNodes (node) {
  while (node.hasChildNodes ()) {
    node.removeChild (node.firstChild);
  }
}

function updatePage () {
  var urlParams= "";

  if (document.pageOptions.chat) {
    var chatId= document.latestChatId;

    if (chatId == 0) {
      return void (0);
    }

    document.latestChatId= 0;

    urlParams= "&lid=" + encodeURIComponent(chatId);
  }

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { checkUpdate (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=get_xml_update" + urlParams + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  return void (0);
}

function submitChat () {
  var chatId= document.latestChatId;

  if (chatId == 0) {
    setTimeout ('submitChat ()', 50);
    return void (0);
  }

  document.latestChatId= 0;

  var chatInput= document.getElementById ("chat_input");
  var chatValue= chatInput.value;

  var parameters= "content=" + encodeURIComponent (chatValue) + "&lid=" + chatId;

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { checkUpdate (httpRequest); };
    httpRequest.open ("POST", document.homePage + "ajax/?m=submit_chat", true);
    httpRequest.setRequestHeader ("Content-type", "application/x-www-form-urlencoded");
    httpRequest.setRequestHeader ("Content-length", parameters.length);
    httpRequest.setRequestHeader ("Connection", "close");
    httpRequest.send (parameters);
  }

  chatInput.value= "";

  return void (0);
}

// Gets a processed content preview
function getPreview (content, tagId, long, special) {
  long= typeof (long) != 'undefined' ? long : true;
  special= typeof (special) != 'undefined' ? special : "";

  var contentBlock= document.getElementById (tagId);
  var parameters= "content=" + encodeURIComponent (content) + "&type=" + (long ? "l" : "s") + "&special=" + special;

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writePreview (httpRequest, contentBlock); };
    httpRequest.open ("POST", document.homePage + "ajax/?m=get_preview", true);
    httpRequest.setRequestHeader ("Content-type", "application/x-www-form-urlencoded");
    httpRequest.setRequestHeader ("Content-length", parameters.length);
    httpRequest.setRequestHeader ("Connection", "close");
    httpRequest.send (parameters);
  }
}

// Pops up a "send message" window
function popMessage (recipient, replyTo) {
  window.open ("/messages/?m=write_message&to="+recipient+(replyTo > 0 ? "&r="+replyTo : ""), "write_message", "width=394,height=262,scrollbars=0,resizable=0");
}

// Delay for a given interval (in milliseconds)
function timeDelay (interval) {
  var today= new Date ();
  var test= null;

  do {
    test= new Date ();
  } while (test - today < interval);
}

// Takes any URL and gets the relative directory for it
function parseURLDirectory (url) {
  return url.replace (/(.*\/)[^\/]*/, "$1");
}

// Hides the message popup window
function hideMessagePopup () {
  document.getElementById ("message_window").style.display= "none";

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.open ("GET", document.homePage + "ajax/?m=ack_msgs&n=" + document.numMessages + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  return void (0);
}

// Makes sure a username input has an existing username
function checkUsername (input, allowSelf) {
  allowSelf= typeof (allowSelf) != 'undefined' ? allowSelf : true;

  if (input.value == "") return null;

  var httpRequest= getHTTPRequest ();

  var name= input.value;

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { updateUsername (httpRequest, input) };
    httpRequest.open ("GET", document.homePage + "ajax/?m=check_username&name=" + encodeURIComponent(name) + "&self=" + (allowSelf ? 1 : 0) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Rewrites a username or clears a field if invalid, and triggers an error message
function updateUsername (httpRequest, inputBlock) {
  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      var name= httpRequest.responseText;
      if (name == "") {
        alert ("No user by that name was found");
      } else if (name == "[self]") {
        alert ("You may not specify your own username");
        name= "";
      }
      inputBlock.value= name;
    } else {
      alert ("There was a problem with the request, could not check username");
    }
  }

  document.checkingUsername= false
}

///////////////////////
// Comment Functions //
///////////////////////

// Turns spoiler display on or off
function toggleSpoiler (commentId, spoilerId) {
  var spoiler= document.getElementById ("spoiler_"+commentId+"_"+spoilerId);
  var message= document.getElementById ("message_"+commentId+"_"+spoilerId);

  if (spoiler.style.display == "inline") {
    spoiler.style.display= "none";
    message.style.display= "inline";
  } else {
    spoiler.style.display= "inline";
    message.style.display= "none";
  }
}

// Gives the user a prompt for text and puts it in a tag
function createTag (fieldId, tagName) {
  var field= document.getElementById (fieldId);

  var promptText= "Enter the tag text";

  var fillOption= true; // Set true for options that get set when highlighted (as opposed to content)

  switch (tagName) {
  case "font_color":
    var before= '[font color="';
    var after= '"][/font]';
    fillOption= false;
    break;
  case "font_type":
    var before= '[font type="';
    var after= '"][/font]';
    fillOption= false;
    break;
  case "font_size":
    var before= '[font size="';
    var after= '"][/font]';
    fillOption= false;
    break;
  case "link":
    var before= '[link="';
    var after= '"][/link]';
    break;
  default:
    var before= "["+tagName+"]";
    var after= "[/"+tagName+"]";
  }

  InsertTagAroundCursor (field, before, after, fillOption);

  return void (0);
}

// Quotes the entirety of the post being replied to
function quotePost (fieldId) {
  var field= document.getElementById (fieldId);

  insertAtCursor(field, '[quote name="'+document.getElementById ("parent_poster").value+'"]'+document.getElementById ("parent_post").value+'[/quote]');
}

// Adds more poll choices
function addPollChoice () {
  var choiceNumber= 1 + document.getElementById ("poll_choices").value++;
  var pollChoices= document.getElementById ("poll_choice_block");
  var newPollChoice;

  newPollChoice= document.createElement ("input");
  newPollChoice.setAttribute ("name", "poll_choice_" + choiceNumber);
  newPollChoice.setAttribute ("type", "text");
  newPollChoice.setAttribute ("tabindex", 80 + choiceNumber);
  newPollChoice.style.display= "block";
  newPollChoice.style.width= "819px";
  newPollChoice.id= "poll_choice_" + choiceNumber;
  newPollChoice.onkeydown= function (evt) { checkTagPress (evt, newPollChoice.id, false); }

  pollChoices.appendChild (newPollChoice);

}

// Trims whitespace from both ends of a string
function trim (string) {
  return typeof string == 'string' ? string.replace (/^\s+|\s+$/g, "") : string;
}

// Verifies that at least two valid poll choices were entered
function pollChoicesEntered () {
  if (trim (document.getElementById ("comment_subject").value).length <= 0) {
    alert ("You must enter a poll question");
    return false;
  }

  var pollChoice= "";

  for (var i= 1; i <= document.getElementById ("poll_choices").value; i++) {
    var choice= trim (document.getElementById ("poll_choice_"+i).value);
    if (choice.length > 0) {
      if (pollChoice == "") {
        pollChoice= choice;
      } else {
        // This ensures at least two different non-blank choices
        if (pollChoice != choice) return true;
      }
    }
  }

  alert ("You must enter at least two unique poll choices");
  return false;
}

// Shows poll results and hides the link to show them
function showPollResults () {
  document.getElementById ("show_poll_link").style.display= "none";
  document.getElementById ("poll_total").style.display= "inline";

  var cells= document.getElementsByTagName ("td");
  for (var i= 0; i < cells.length; i++) {
    if (cells[i].id.substring (0, 5) == "poll_") cells[i].style.display= "";
  }

  var divs= document.getElementsByTagName ("div");
  for (var i= 0; i < divs.length; i++) {
    if (divs[i].id.substring (0, 5) == "poll_") divs[i].style.display= "";
    if (divs[i].id.substring (0, 12) == "poll_choice_") divs[i].className= "poll_choice";
  }
}

// Inserts a block of text at the current cursor position within an input block
function insertAtCursor(block, insertText) {
  if (document.selection) {
    // IE support
    block.focus();
    sel= document.selection.createRange();
    sel.text= insertText;
  } else if (block.selectionStart || block.selectionStart == "0") {
    // Firefox support
    var startPos= block.selectionStart;
    var endPos= block.selectionEnd;
    block.value= block.value.substring (0, startPos) + insertText + block.value.substring (endPos, block.value.length);
  } else {
    // If all else fails, just put it at the end
    block.value+= insertText;
  }
}

// Inserts blocks of text before and after the current cursor position within an input block
function InsertTagAroundCursor(block, beforeText, afterText, fillOption) {
  // For [tag param="|"][/tag] usage on highlighting
  // Put the cursor inside the tag if the param is filled
  var postAdjust= 0;
  if (beforeText.substring (beforeText.length - 1) != "]") {
    postAdjust= afterText.indexOf ("]") + 1;
  }

  var cursor= getCursorPosition (block);

  if (document.selection) {
    // IE support
    block.focus();
    range= document.selection.createRange ();
    priorTextLength= range.text.length;

    if (!fillOption && range.text.length > 0) {
      beforeText+= afterText.substring (0, postAdjust);
      afterText= afterText.substring (postAdjust, afterText.length);
      postAdjust= -postAdjust - range.text.length;
    }

    // If the selected text starts and ends with these tags, remove them instead of adding them
    if (range.text.substring (0, beforeText.length) == beforeText && range.text.substring (range.text.length - afterText.length) == afterText) {
      range.text= range.text.substring (beforeText.length, range.text.length - afterText.length);
      return true;
    }

    var tempRange= document.selection.createRange ();
    tempRange.moveStart ("character", -beforeText.length);
    tempRange.moveEnd ("character", afterText.length);

    // If the selected text is wrapped with these tags, remove them instead of adding them
    if (tempRange.text.substring (0, beforeText.length) == beforeText && tempRange.text.substring (tempRange.text.length - afterText.length) == afterText) {
      tempRange.text= tempRange.text.substring (beforeText.length, tempRange.text.length - afterText.length);
      return true;
    } else if (range.text.length == 0 && tempRange.text.substring (tempRange.text.length - afterText.length) == afterText && beforeText.substring (beforeText.length - 1, beforeText.length) == "]" && afterText.substring (0, 1) == "[") {
      // If you're at the end of a tag, but there is content before the cursor, "end" the tag by moving past it
      range.move ("character", afterText.length);
      range.select ();
      return true;
    }

    range.text= beforeText + range.text + afterText;

    // If nothing was highlighted, put the cursor between the added blocks
    if (priorTextLength == 0) {
      range.move ('character', -1 * afterText.length);
      range.select ();
    } else if (postAdjust != 0) {
      range.move ('character', -1 * afterText.length + postAdjust);
      range.select ();
    }

    return true;
  } else if (block.selectionStart || block.selectionStart == "0") {
    // Firefox support
    var startPos= block.selectionStart;
    var endPos= block.selectionEnd;
    var selectionSize= endPos - startPos;

    if (!fillOption && selectionSize > 0) {
      beforeText+= afterText.substring (0, postAdjust);
      afterText= afterText.substring (postAdjust, afterText.length);
      postAdjust= -postAdjust - selectionSize;
    }

    if (block.value.substring (startPos, startPos + beforeText.length) == beforeText && block.value.substring (endPos - afterText.length, endPos) == afterText) {
      // If the selected text starts and ends with these tags, remove them instead of adding them
      block.value= block.value.substring (0, startPos) + block.value.substring (startPos + beforeText.length, endPos - afterText.length) + block.value.substring (endPos);
      selectionSize-= (beforeText.length + afterText.length);

      setHighlight (block, cursor, cursor + selectionSize);
      return true;
    } else if (block.value.substring (startPos - beforeText.length, startPos) == beforeText && block.value.substring (endPos, endPos + afterText.length) == afterText) {
      // If the selected text is already wrapped in these tags, remove them as well
      block.value= block.value.substring (0, startPos - beforeText.length) + block.value.substring (startPos, endPos) + block.value.substring (endPos + afterText.length);

      cursor-= beforeText.length;
      setHighlight (block, cursor, cursor + selectionSize);
      return true;
    } else if (selectionSize == 0 && block.value.substring (endPos, endPos + afterText.length) == afterText && beforeText.substring (beforeText.length - 1, beforeText.length) == "]" && afterText.substring (0, 1) == "[") {
      // If you're at the end of a tag, but there is content before the cursor, "end" the tag by moving past it
      setCursorPosition (block, cursor + afterText.length);
      return true;
    } else {
      block.value= block.value.substring (0, startPos) + beforeText + (startPos != endPos ? block.value.substring (startPos, endPos) : "") + afterText + block.value.substring (endPos, block.value.length);
    }

    // If nothing was highlighted, put the cursor between the added blocks
    if (selectionSize == 0) {
      setCursorPosition (block, cursor + beforeText.length);
    } else if (postAdjust != 0) {
      setCursorPosition (block, cursor + beforeText.length + selectionSize + postAdjust);
    } else {
      setHighlight (block, cursor, cursor + selectionSize + beforeText.length + afterText.length);
    }

    return true;
  } else {
    // If all else fails, just put it at the end
    block.value+= beforeText + afterText;

    return false;
  }
}

// Highlights text in an input block from character position start to position end
function setHighlight (block, start, end) {
  if (block.setSelectionRange) {
    block.setSelectionRange (start, end);
    block.focus ();
  } else if (block.createTextRange) {
    var range= block.createTextRange ();
    range.collapse (true);
    range.moveEnd ('character', end);
    range.moveStart ('character', start);
    range.select ();
  }
}

// Sets the cursor position of the specified input block
function setCursorPosition (block, position) {
  setHighlight (block, position, position);
}

// Finds the cursor position of the specified input block (only in Firefox)
function getCursorPosition (block) {
  if (block.selectionStart || block.selectionStart == "0") {
    return block.selectionStart;
  } else {
    block.focus ();
    var range= document.selection.createRange ();
    range.moveStart ('character', -block.value.length);
    return range.text.length;
  }
}

// Allows users to press Ctrl-B or Ctrl-I to insert tags
function checkTagPress (evt, fieldId, long) {
  long= typeof (long) != 'undefined' ? long : true;

  evt= (evt) ? evt : window.event;

  if (!evt.ctrlKey) return true;

  switch (String.fromCharCode (evt.keyCode).toLowerCase ()) {
    case "b":
      createTag (fieldId, "b");
      break;
    case "e":
      createTag (fieldId, "edit");
      break;
    case "i":
      createTag (fieldId, "i");
      break;
    case "l":
      createTag (fieldId, "link");
      break;
    default:
      return true;
    }

    evt.cancelBubble= true;
    evt.returnValue= false;

    if (evt.stopPropagation) {
      evt.stopPropagation ();
      evt.preventDefault ();
    }

    return false;
}

function toggleSticky (sticky, id) {
  document.getElementById ("list_form").setting.value= sticky ? 1 : 0;
  document.getElementById ("list_form").thread_id.value= id;
  document.getElementById ("list_form").mode.value= "sticky";
  document.getElementById ("list_form").submit ();
}

// Toggles a checkbox value
function toggleBox (field, itemId, checked) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.open ("GET", document.homePage + "ajax/?m=toggle&field=" + encodeURIComponent(field) + "&id=" + encodeURIComponent(itemId) + "&val=" + (checked ? "1" : "0") + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  return void (0);
}

function refreshPreview (fieldId) {
  if (fieldId == "page_content") {
    var content= document.getElementById ("page_content").value;

    getPreview (content, "content_preview", true, "preview");
    getPreview (document.getElementById ("page_title").value, "title_preview", false);

    document.getElementById ("page_preview").style.display= "block";
  } else if (fieldId == "faq_answer") {
    getPreview (document.getElementById ("faq_question").value, "question_preview", false);
    getPreview (document.getElementById ("faq_answer").value, "answer_preview");

    document.getElementById ("preview_faq").style.display= "block";
  } else if (fieldId == "profile_blurb") {
    getPreview (document.getElementById ("profile_blurb").value, "blurb_preview");
    document.getElementById ("preview_profile_blurb").style.display= "block";
  }

  return void (0);
}

// Sets whether a thread is ignored
function ignoreThread (threadId, ignore, inThread) {
  if (inThread) {
    document.getElementById ("ignore_thread").style.display= ignore ? "none" : "inline";
    document.getElementById ("unignore_thread").style.display= ignore ? "inline" : "none";
  } else {
    ignore= false; // You can only un-ignore from the thread list
    document.getElementById ("ignore_thread_"+threadId).style.display= "none";
    document.getElementById ("thread_notes_"+threadId).style.display= "inline";
    var newPosts= document.getElementById ("thread_new_"+threadId);
    if (newPosts) newPosts.style.display= "block";
  }

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.open ("GET", document.homePage + "ajax/?m=ignore_thread&tid=" + encodeURIComponent(threadId) + "&val=" + (ignore ? "1" : "0") + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Sets a user's forum access level (only works for admins)
function setAccessLevel (userId, forumId, accessLevel) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_access_level&uid=" + encodeURIComponent(userId) + "&fid=" + encodeURIComponent(forumId) + "&lev=" + encodeURIComponent(accessLevel) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

//////////////////////
// Paging Functions //
//////////////////////

function sort (sortBy, defDir) {
  if (sortBy == document.filters["s"]) {
    defDir= document.filters["d"] == 'a' ? 'd' : 'a';
  }
  var link= document.link + "&s=" + sortBy + "&d=" + defDir;

  for (var i in document.filters) {
    if (i == "s" || i == "d") continue;
    link+= "&" + i + "=" + document.filters[i];
  }

  document.location= link;
}

function showPage (pageNum) {
  var link= document.link + "&p=" + pageNum;

  for (var i in document.filters) {
    if (i == "p") continue;
    link+= "&" + i + "=" + document.filters[i];
  }

  document.location= link;
}

function checkNum (field, min, max) {
  if (isNaN (field.value) || field.value == "") {
    field.value= "";
  } else if (field.value < min) {
    field.value= min;
  } else if (field.value > max) {
    field.value= max;
  } else {
    field.value= Math.round (field.value);
  }
}

function checkFloat (field, decimals, blank, min, max) {
  decimals= typeof (decimals) != "undefined" ? decimals : 1;
  blank= typeof (blank) != "undefined" ? blank : 0;
  min= typeof (min) != "undefined" ? min : null;
  max= typeof (max) != "undefined" ? max : null;

  if (isNaN (field.value) || field.value == "") {
    field.value= blank;
  } else if (min && field.value < min) {
    field.value= min;
  } else if (max && field.value > max) {
    field.value= max;
  }

  field.value= parseFloat (field.value).toFixed (decimals);

  return void (0);
}

function swapStatSort (num) {
  var link= document.link + "&s=" + document.filters["f"+num] + "&f" + num + "=" + document.filters["s"];

  for (var i in document.filters) {
    if (i == "s" || i == ("f"+num)) continue;
    link+= "&" + i + "=" + document.filters[i];
  }

  document.location= link;
}

//////////////////////
// Quotes Functions //
//////////////////////

function toggleQuote (active, id) {
  var toggle_form= document.getElementById ("toggle_form");

  toggle_form.active.value= active ? 1 : 0;
  toggle_form.quote_id.value= id;
  toggle_form.submit ();
}

// Check the quote submission form
function verifyQuote () {
  if (document.checkingUsername) {
//    checkUsername (document.getElementById ("other_user"), false);
    return false;
  }

  var quote= document.getElementById ("quote_input").value;

  if (quote == "") {
    alert ("You forgot enter a quote!");
    return false;
  }

  if (document.getElementById ("other_user").style.display != "none" && document.getElementById ("other_user").value == "") {
    alert ("You must enter a valid username (or select \"no attribution\")");
    return false;
  }

  document.getElementById ('quote_form').submit ();
}

////////////////////////////
// User Ratings Funcitons //
////////////////////////////

// Processes a user's rating of an item
function rateItem (type, itemId, rating) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeRating (httpRequest, type, itemId); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=rate_item&type=" + encodeURIComponent(type) + "&id=" + encodeURIComponent(itemId) + "&rat=" + encodeURIComponent(rating) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Re-writes the user rating area after a rating (to remove the rating links and update the rating)
function writeRating (httpRequest, type, itemId) {
  var contentBlock= document.getElementById ("rating_" + type + "_" + itemId);
  if (!contentBlock) return false;

  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      contentBlock.innerHTML= httpRequest.responseText;
    } else {
      alert ("There was a problem with the request, could not write preview");
    }
  }
}

///////////////////////
// Message Functions //
///////////////////////

// Verifies that a submitted message is valid
function checkMessage () {
  if (trim (document.getElementById ("message_subject").value) == "" && trim (document.getElementById ("message_content").value) == "") {
    alert ("Please include a subject or content in your message");
    return false;
  }

  return true;
}

///////////////////////////
// Splash Page Functions //
///////////////////////////

// Brings up the specified leaderboard
function showLeaderboard (boardId) {
  if (typeof (document.leaderboardShown) == 'undefined') {
    var boards= document.getElementsByTagName ("div");

    for (var i= 0; i < boards.length; i++) {
      if (boards[i].id.substring (0, 6) == "board_") {
        boards[i].style.display= "none";
      }
    }
  } else {
    document.getElementById ("board_" + document.leaderboardShown).style.display= "none";
  }

  document.getElementById ("board_" + boardId).style.display= "block";
  document.leaderboardShown= boardId;
}

////////////////////////////
// User Profile Functions //
////////////////////////////

// Disables or re-enables options if they are being overridden by a reset
function checkPreferenceReset (value) {
  var disabled= value == "keep" ? false : true;

  var selects= document.getElementsByTagName ("select");

  for (var i= 0; i < selects.length; i++) {
    if (selects[i].id.substring (0, 16) == "site_preference_") {
      selects[i].disabled= disabled;
    }
  }
}

// Shows your own inactive reward list
function showInactiveRewards () {
  document.getElementById ("inactive_reward_link").style.display= "none";
  document.getElementById ("inactive_rewards").style.display= "block";
}

/////////////////////////////
// Administrator Functions //
/////////////////////////////

// Hides or unhides site suboptions based on the option value
function updateSuboptions (value, index) {
  var display= (value == "no" || value == "0") ? "none" : "";
  var rows= document.getElementsByTagName ("tr");
  var check= "option_" + index + "_sub_";

  for (var i= 0; i < rows.length; i++) {
    if (rows[i].id.substring (0, check.length) == check) {
      rows[i].style.display= display;
    }
  }
}

// Saves, loads, or copies a created statistic
function actOnStat (action) {
  document.getElementById ("stat_action").value= action;
  document.getElementById ("create_stat_form").submit ();
}

// Changes the stat form from query-based to calculated or back
function checkStatType (calculated) {
  calculated= parseInt (calculated);

  document.getElementById ("query_fields").style.display= (calculated ? "none" : "block");
  document.getElementById ("calculated_fields").style.display= (calculated ? "block" : "none");
}

// Updates the list of available stats based on reward type
function checkRewardType (type) {
  var site= type == "site";

  document.getElementById ("site_stat_list").style.display= (site ? "inline" : "none");
  document.getElementById ("forum_stat_list").style.display= (site ? "none" : "inline");

  updateStatDescription (document.getElementById (site ? "site_insert_id" : "forum_insert_id"));
}

// Updates the description of a selected statistic
function updateStatDescription (select) {
//  var select= document.getElementById (selectId);

  var description= select.options[select.selectedIndex].getAttribute ("description");

  if (description == null) description= "";

  document.getElementById ("stat_description").innerHTML= description;
}

// Inserts a statistic in the calculations field
function insertStatistic (name, blockName) {
  if (!name) return;

  var block= document.getElementById (blockName);

  insertAtCursor (block, "[" + name + "]");
}

// Confirms that the given scheduled stat should be removed
function removeScheduledStat (statId) {
  var statName= document.getElementById ("name_" + statId).firstChild.data;

  if (confirm ("Are you sure you wish to remove \""+ statName + "\" from the statistic processing schedule?")) {
    var httpRequest= getHTTPRequest ();

    if (httpRequest) {
      httpRequest.open ("GET", document.homePage + "ajax/?m=remove_stat&id=" + encodeURIComponent(statId) + "&ts=" + new Date ().getTime (), true);
      httpRequest.send (null);
    }

    var table= document.getElementById ("schedule_table");
    table.deleteRow (document.getElementById ("row_" + statId).rowIndex);
    if (table.rows.length == 1) {
      document.getElementById ("schedule_table_area").style.display= "none";
    }
  }

  return void (0);
}

// Revises the saved notes on a scheduled stat
function changeStatNotes (statId) {
  var statName= document.getElementById ("name_" + statId).firstChild.data;

  var noteSpan= document.getElementById ("notes_" + statId);
  var statNotes= noteSpan.firstChild ? noteSpan.firstChild.data : "";

  var newNotes= prompt ("Enter the new notes for \"" + statName + "\":", statNotes);

  if (newNotes == null) return void (0);

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.open ("GET", document.homePage + "ajax/?m=change_stat_notes&id=" + encodeURIComponent(statId) + "&notes=" + encodeURIComponent(newNotes) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  noteSpan.replaceChild (document.createTextNode (newNotes), noteSpan.firstChild);
  document.getElementById ("row_" + statId).setAttribute ("title", newNotes);

  return void (0);
}

// Checks to make sure a value is a float, returns 1 otherwise
function checkMultiplier (value) {
  value= parseFloat (value);
  if (isNaN (value)) value= 1;

  return value;
}

// Checks to make sure an input contains an int, and is a unique point value for its path
function checkPoints (input, pathId, rewardId) {
  rewardId= typeof (rewardId) != 'undefined' ? rewardId : 0;
  // This variable has three levels: 0 for network-wide, 1 for site-wide, and 2 for forum-wide

  var value= input.value;

  value= parseInt (value);
  if (isNaN (value) || value < 1) value= 1;

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { updatePoints (httpRequest, input); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=check_points&p=" + encodeURIComponent(value) + "&rp=" + encodeURIComponent(pathId) + "&rid=" + encodeURIComponent(rewardId) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  input.value= value;
}

// Changes a point total and shows a message if the points total is taken
function updatePoints (httpRequest, inputBlock) {
  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      var points= httpRequest.responseText;
      if (points == "[ok]") {
        return true;
      } else if (points = parseInt (points)) {
        inputBlock.value= points;
      }
    } else {
      alert ("There was a problem with the request, could not update point total");
    }
  }
}

// Updates the "new path" input based on the path selection, and checks the point total
function updatePath (selection) {
  var disabled= true;
  var display= "none";

  if (selection == "[new]") {
    disabled= false;
    display= "inline";
  }

  var newInput= document.getElementById ("new_path");

  newInput.disabled= disabled;
  newInput.style.display= display;
}

// Adds a new row to the criteria table
function addCriteriaRow (table) {
  var number= table.rows.length - 2;
  var row= table.insertRow (table.rows.length - 1);

  var select= document.createElement ("select");
  select.setAttribute ("name", "stat_id_" + number);
  select.style.width= "100%";
  select.options[0]= new Option ("(remove field)", "", true, true);

  for (var id in document.statFields) {
    select.options[select.options.length]= new Option (document.statFields[id], id, false, false);
  }

  var statId= row.insertCell (row.cells.length);
  statId.appendChild (select);

  var weight= row.insertCell (row.cells.length);
  weight.appendChild (createFloatInput ("weight_" + number));

  var power= row.insertCell (row.cells.length);
  power.appendChild (createFloatInput ("power_" + number));

  var blank= row.insertCell (row.cells.length);
  blank.setAttribute ("colSpan", 12);
  blank.style.textAlign= "center";
  blank.appendChild (document.createTextNode ("Update Results to See Data"));
}

// Creates an input for weight or power
function createFloatInput (name) {
  var input= document.createElement ("input");

  input.setAttribute ("type", "text");
  input.setAttribute ("maxLength", 6);
  input.setAttribute ("name", name);
  input.style.width= "24px";
  input.value= 1;
  input.onblur= function () { this.value= checkMultiplier (this.value); };

  return input;
}

// Shows the "edit custom title" UI for a given user
function editCustomTitle (userId) {
  document.getElementById ("row_" + userId).style.display= "";
  document.getElementById ("edit_reward_" + userId).style.display= "none";

  return void (0);
}

// Saves a moderator- or admin-assigned title reward
function saveCustomTitle (userId, forumId) {
  forumId= typeof (forumId) != 'undefined' ? forumId : false;

  var title= document.getElementById ("reward_" + userId + "_title").value;
  var font= document.getElementById ("reward_" + userId + "_font").value;
  var color= document.getElementById ("reward_" + userId + "_color").value;

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { updateRewardPreview (httpRequest, userId); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_custom_reward&uid=" + encodeURIComponent(userId) + "&title=" + encodeURIComponent(title) + "&font=" + encodeURIComponent(font) + "&color=" + encodeURIComponent (color) + (forumId ? "&fid=" + encodeURIComponent (forumId) : "") + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  return void (0);
}

// Removes a moderator- or admin-assigned title reward
function removeCustomTitle (userId, forumId) {
  forumId= typeof (forumId) != 'undefined' ? forumId : false;

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { updateRewardPreview (httpRequest, userId); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_custom_reward&uid=" + encodeURIComponent(userId) + "&title=&font=&color=" + (forumId ? "&fid=" + encodeURIComponent (forumId) : "") + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  document.getElementById ("reward_" + userId + "_title").value= "";

  return void (0);
}

// Saves a site- or forum-specific version of a reward
function saveReward (rewardId, forumId) {
  forumId= typeof (forumId) != 'undefined' ? forumId : false;

  var active= document.getElementById ("reward_" + rewardId + "_active");
  var title= document.getElementById ("reward_" + rewardId + "_title").value;
  var font= document.getElementById ("reward_" + rewardId + "_font").value;
  var color= document.getElementById ("reward_" + rewardId + "_color").value;
  var save= 1;

  var points= forumId ? 0 : parseInt (document.getElementById ("reward_" + rewardId + "_points").value);
  if (isNaN (points) || points < 1) points= 1;

  if (!forumId && active.options[0].value == "") {
    if (active.options[0].selected) {
      save= 0;
    } else {
      active.options[0]= null;
    }
  }
  active= active.value;

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { updateRewardPreview (httpRequest, rewardId); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=change_reward&id=" + encodeURIComponent(rewardId) + "&active=" + encodeURIComponent(active) + "&title=" + encodeURIComponent(title) + "&points=" + encodeURIComponent(points) + "&font=" + encodeURIComponent(font) + "&color=" + encodeURIComponent (color) + "&save=" + encodeURIComponent(save) + (forumId ? "&fid=" + encodeURIComponent (forumId) : "") + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  return void (0);
}

// Rewrites the reward preview for an edited reward
function updateRewardPreview (httpRequest, id) {
  var previewBlock= document.getElementById ("reward_" + id + "_preview");

  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      var xml= httpRequest.responseXML;

      if (xml.getElementsByTagName ("title").length == 0) {
        if (xml.getElementsByTagName ("deleted").length > 0) {
          previewBlock.style.display= "none";
          return;
        }

        alert ("There was a problem with the request, could not update reward preview");
        return;
      }

      while (previewBlock.childNodes.length > 0) previewBlock.removeChild (previewBlock.firstChild);
      putXMLasHTML (previewBlock, xml.getElementsByTagName ("title")[0]);

      var font= xml.getElementsByTagName ("font")[0].firstChild.nodeValue;
      var color= xml.getElementsByTagName ("color")[0].firstChild.nodeValue;

      previewBlock.style.fontFamily= font;
      previewBlock.style.color= color;

      var points= xml.getElementsByTagName ("points")[0].firstChild.nodeValue;
      if (points > 0) {
        var pointInput= document.getElementById ("reward_" + id + "_points");
        if (pointInput) pointInput.value= points;
      }

      previewBlock.style.display= "inline";
    } else {
      alert ("There was a problem with the request, could not update preview");
    }
  }
}

///////////////////
// FAQ Functions //
///////////////////

// Verifies that a submitted question is valid
function checkFAQ () {
  if (trim (document.getElementById ("faq_question").value) == "" || trim (document.getElementById ("faq_answer").value) == "") {
    alert ("Please include a question and answer");
    return false;
  }

  return true;
}

////////////////////
// Blog Functions //
////////////////////

// Verifies that a submitted blog is valid
function checkBlog () {
  if (trim (document.getElementById ("blog_content").value) == "") {
    alert ("Please include blog content");
    return false;
  }

  return true;
}

// Checks that the entered version is valid, and updates it to the minimum if not
function checkVersion (input) {
  var min= parseFloat (document.getElementById ('min_version').value);
  var max= parseFloat (document.getElementById ('max_version').value);

  if (isNaN (input.value) || input.value == "" || input.value < min) {
    input.value= min;
  } else if (input.value > max) {
    input.value= max;
  }

  input.value= Math.floor (input.value * 100) / 100;
  if (input.value == Math.floor (input.value)) input.value+= ".0";

  return false;
}

//////////////////////
// Review Functions //
//////////////////////

// Verifies that a submitted review is valid
function checkReview () {
  if (trim (document.getElementById ("review_subject").value) == "") {
    alert ("Please specify what you are reviewing");
    return false;
  }

  if (trim (document.getElementById ("review_content").value) == "" && document.getElementById ("review_rating").value == "") {
    alert ("Please include review content or a rating");
    return false;
  }

  return true;
}

/////////////////////
// RPGCC Functions //
/////////////////////

// Verifies that a submitted character is valid
function checkCharacter () {
  if (trim (document.getElementById ("character_name").value) == "") {
    alert ("Please name your character");
    return false;
  }

  return true;
}

// Verifies that a submitted character module is valid
function checkModule () {
  if (trim (document.getElementById ("module_heading").value) == "") {
    alert ("Please specify a module name");
    return false;
  }

  return true;
}

// Toggles stat block display
function toggleModule (moduleId) {
  var module= document.getElementById ("module_"+moduleId);
  var message= document.getElementById ("message_"+moduleId);

  if (module.style.display == "none") {
    module.style.display= "block";
    message.innerHTML= "hide";
  } else {
    module.style.display= "none";
    message.innerHTML= "show";
  }
}

//////////////////////////
// Google Map Functions //
//////////////////////////

var map;
var directionsPanel;
var directions;

function loadGMap () {
  if (GBrowserIsCompatible ()) {
    map= new GMap2 (document.getElementById ("gmap"));
    directionsPanel= document.getElementById ("gmappanel");
    var school= new GLatLng (40.8209767, -73.3888494);
    map.setCenter (school, 13);
    map.addOverlay (new GMarker (school, { title : "Huntington Montessori" }));
    map.addControl (new GLargeMapControl ());
    map.addControl (new GMapTypeControl ());
    directions= new GDirections (map, directionsPanel);
  }
}

function getDirections (address) {
  //directions.load ("from: " + address + " to: 165 Pidgeon Hill Rd., Huntington Station, NY 11746");
  directions.load ("from: " + address + " to: Pidgeon Hill Rd., Huntington Station, NY 11746");
}

///////////////////////////
// File Upload Functions //
///////////////////////////

function chooseFile (fieldId) {
  document.fileWindow= window.open ("/index.php?m=choose&t=f&field=" + fieldId, "file_popup", "width=300,height=290");
}

function chooseImage (fieldId) {
  document.fileWindow= window.open ("/index.php?m=choose&t=i&field=" + fieldId, "file_popup", "width=550,height=290");
}

function chooseSlideshow (fieldId) {
  document.fileWindow= window.open ("/index.php?m=choose&t=s&field=" + fieldId, "file_popup", "width=340,height=290");
}

function uploadFile (fieldId, type) {
  document.fileWindow= window.open ("/index.php?m=upload&t=" + type + "&field=" + fieldId, "file_popup", "width=640,height=200");
}

function chooseShowImage (fieldId) {
  document.fileWindow= window.open ("/index.php?m=choose&s=1&t=i&field=" + fieldId, "file_popup", "width=550,height=290");
}

function uploadShowImage (fieldId, type) {
  document.fileWindow= window.open ("/index.php?m=upload&s=1&t=i&field=" + fieldId, "file_popup", "width=640,height=200");
}

// Puts a file or image tag in the text
function insertFileTag (fieldId, tagType, value) {
  var field= document.getElementById (fieldId);

  InsertTagAroundCursor (field, "[" + tagType + "=\""+value+"\"]", "", false);

  return void (0);
}

// Saves show changes including adding an image to the slideshow
function saveShowTag (imageId) {
  document.getElementById ("new_image_id").value= imageId;
  document.getElementById ("show_form").submit ();
}

// Processes slide movement or removal for a show
function moveSlide (slideId, func) {
  if (func == "r") {
    if (!confirm ("Are you sure you want to remove this slide from the show?")) {
      return;
    }
  }

  document.getElementById ("edit_slide_id").value= slideId;
  document.getElementById ("edit_slide_func").value= func;
  document.getElementById ("show_form").submit ();
}

/////////////////////////
// Drop Menu Functions //
/////////////////////////

function dropmenuOpen () {
  dropmenuCancelTimer ();
  dropmenuClose ();
  var currentMenu= document.openMenu;
  document.menuItem= $(this).find('ul').not('.submenu');
  document.menuItem.css('visibility', 'visible');

  if (document.menuItem.context.id) document.openMenu= document.menuItem.context.id;
  if (currentMenu == document.openMenu) return;

  document.menuItem.css('opacity', '0');
  document.menuItem.animate ( { opacity: 0.95 }, 500 );
}

function dropmenuClose () {
  if (document.menuItem) document.menuItem.css ('visibility', 'hidden');
}

function dropmenuTimer () {
  document.closeTimer= window.setTimeout (dropmenuClose, document.dropmenuTimeout);
}

function dropmenuCancelTimer () {
  if (document.closeTimer) {
    window.clearTimeout (document.closeTimer);
    document.closeTimer= null;
  }
}

function submenuOpen () {
  submenuCancelTimer ();
  submenuClose ();
  var currentSubmenu= document.openSubmenu;
  document.submenuItem= $(this).find('ul');
  document.submenuItem.css('visibility', 'visible');

  if (document.submenuItem.context.id) document.openSubmenu= document.submenuItem.context.id;
  if (currentSubmenu == document.openSubmenu) return;

  document.submenuItem.css('opacity', '0');
  document.submenuItem.animate ( { opacity: 0.9 }, 500 );
}

function submenuClose () {
  if (document.submenuItem) document.submenuItem.css ('visibility', 'hidden');
}

function submenuTimer () {
  document.submenuCloseTimer= window.setTimeout (submenuClose, document.dropmenuTimeout);
}

function submenuCancelTimer () {
  if (document.submenuCloseTimer) {
    window.clearTimeout (document.submenuCloseTimer);
    document.submenuCloseTimer= null;
  }
}

function allmenuClose () {
  submenuClose ();
  dropmenuClose ();
}

$(document).ready(function () {
  $('#dropmenu > li').bind('mouseover', dropmenuOpen)
  $('#dropmenu > li').bind('mouseout', dropmenuTimer)
  $('#dropmenu ul.submenu').parent ().bind('mouseover', submenuOpen)
  $('#dropmenu ul.submenu').parent ().bind('mouseout', submenuTimer)
});

document.onclick= allmenuClose;

////////////////////////
// Calendar Functions //
////////////////////////

function setupCalendar () {
  var select= document.schedule_form["schedule_month"];
  var today= new Date ();
  today.setMonth (today.getMonth () - 1);

  var months= new Array (12);
  months[0]  = "January";
  months[1]  = "February";
  months[2]  = "March";
  months[3]  = "April";
  months[4]  = "May";
  months[5]  = "June";
  months[6]  = "July";
  months[7]  = "August";
  months[8]  = "September";
  months[9]  = "October";
  months[10] = "November";
  months[11] = "December";

  for (var i= 0; i < 12; i++) {
    var year= today.getYear ();
    if (year < 1900) {
      year+= 1900;
    }

    select.options[select.options.length]= new Option (months[today.getMonth()]+" "+year, year+"_"+today.getMonth());

    today.setMonth (today.getMonth()+1);
  }

  select.options[1].selected= true;

  updateCalendar (select.value);
}

function updateCalendar (value) {
  var month= value.substring (5);
  var year= value.substring (0, 4);

  writeCalendar (month, year);
}

function writeCalendar (month, year) {
  var today= new Date ();

  var day= new Date (year, month, 1, 22);

  var html= '<table class="calendar"><tr>'

  day.setDate (day.getDate()-day.getDay());

  var limit= 0; // Limit this loop to a reasonable maximum number of days
  while (limit < 50) {
    limit++;

    if (day.getDay() == 0 && limit > 1) {
      html+= "</tr><tr>";
    }

    var classString= "";
    if (day.getMonth() != month) {
      classString= ' class="other"';
    }

    var year= day.getYear() <= 1900 ? day.getYear() + 1900 : day.getYear();
    var cellId= ''+year+'-'+twoDigit(day.getMonth()+1)+'-'+twoDigit(day.getDate());
    var cell= '<td valign="top"'+classString+' id="'+cellId+'"><div class="cell">';
    cell+= '<div class="label"><b>'+day.getDate()+'</b>';

    if (document.admin && day.getMonth() == month) {
      cell+= '<span id="editlink-'+cellId+'"> [<a href="javascript:editDay (\''+cellId+'\');">edit</a>]</span>';
      cell+= '<span id="savelink-'+cellId+'" style="display: none;"> [<a href="javascript:saveDay (\''+cellId+'\');">save</a>]</span>';
    }

    cell+= '</div><div id="text-'+cellId+'">';
    var today= document.schedule[cellId];
    if (today) cell+= today;
    cell+= '</div><textarea class="day_edit" id="edit-'+cellId+'" style="display: none;"></textarea>';

    cell+= '</div></td>';
    html+= cell;

    day.setDate (day.getDate()+1);

    if (day.getDay() == 0 && day.getMonth() != month) {
      break;
    }
  }

  if (day.getDay < 6) {
    for (var i= day.getDay(); i < 7; i++) {
      html+= '<td>&nbsp;</td>';
    }
  }

  html+= "</tr></table>";

  document.getElementById ("calendar").innerHTML= html;
}

function twoDigit (number) {
  return number < 10 ? "0" + number : number;
}

function editDay (dayId) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { startDayEdit (httpRequest, dayId); };
    httpRequest.open ("GET", document.homePage + "/?m=edit_day&d=" + dayId + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  return void(0);
}

function startDayEdit (httpRequest, dayId) {
  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      var block= document.getElementById ("edit-"+dayId);
      block.value= httpRequest.responseText;

      block.style.display= "block";
      document.getElementById ("editlink-"+dayId).style.display= "none";
      document.getElementById ("text-"+dayId).style.display= "none";
      document.getElementById ("savelink-"+dayId).style.display= "inline";
    } else {
      alert ("There was a problem with the request, could not write preview");
    }
  }
}

function saveDay (dayId) {
  var content= document.getElementById ("edit-"+dayId).value;
  var parameters= "content=" + encodeURIComponent (content) + "&day=" + dayId;
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeDayText (httpRequest, dayId); };
    httpRequest.open ("POST", document.homePage + "/?m=save_day", true);
    httpRequest.setRequestHeader ("Content-type", "application/x-www-form-urlencoded");
    httpRequest.setRequestHeader ("Content-length", parameters.length);
    httpRequest.setRequestHeader ("Connection", "close");
    httpRequest.send (parameters);
  }

  return void(0);
}

function writeDayText (httpRequest, dayId) {
  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      var content= httpRequest.responseText;
      var block= document.getElementById ("text-"+dayId);
      block.innerHTML= content;
      document.schedule[dayId]= content;

      block.style.display= "block";
      document.getElementById ("editlink-"+dayId).style.display= "inline";
      document.getElementById ("edit-"+dayId).style.display= "none";
      document.getElementById ("savelink-"+dayId).style.display= "none";

    } else {
      alert ("There was a problem with the request, could not write preview");
    }
  }
}

/////////////////////////////////
// Ticker Management Functions //
/////////////////////////////////

// Removes a news ticker item from the list
function removeTicker (rowNum) {
  var table= document.getElementById ("ticker_table");
  var ticker= document.getElementById ("row_" + rowNum);

  table.deleteRow (ticker.rowIndex);

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.open ("GET", document.homePage + "ajax/?m=remove_ticker&id=" + rowNum + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Moves a ticker item down one spot
function moveTickerDown (rowNum) {
  var ticker= document.getElementById ("row_" + rowNum);

  if (ticker.rowIndex + 1 == ticker.parentNode.rows.length) return;

  swapTickers (ticker.rowIndex, ticker.rowIndex + 1);
}

// Moves a ticker item up one spot
function moveTickerUp (rowNum) {
  var ticker= document.getElementById ("row_" + rowNum);

  if (ticker.rowIndex == 1) return;
  swapTickers (ticker.rowIndex - 1, ticker.rowIndex);
}

// Performs a swap of two tickers, based on row index
function swapTickers (row1, row2) {
  // Always swap downward; if swap is upward, reverse parameters and try again
  if (row1 > row2) return swapTickers (row2, row1);

  var table= document.getElementById ("ticker_table");
  table.rows[row1].parentNode.insertBefore (table.rows[row2], table.rows[row1]);

  var tickerId1= (table.rows[row1].id.substr (4));
  var tickerId2= (table.rows[row2].id.substr (4));

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.open ("GET", document.homePage + "ajax/?m=swap_tickers&id1=" + tickerId1 + "&id2=" + tickerId2 + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Brings up the edit form for a given news ticker item
function editTicker (rowNum) {
  document.getElementById ("formatted_" + rowNum).style.display= "none";
  document.getElementById ("edit_" + rowNum).style.display= "none";

  document.getElementById ("input_" + rowNum).style.display= "inline";
  document.getElementById ("cancel_" + rowNum).style.display= "inline";

  document.backupTicker= new Array (0);
  document.backupTicker[rowNum]= document.getElementById ("content_" + rowNum).value;
}

// Brings up the edit form for a given news ticker item
function cancelTickerEdit (rowNum) {
  document.getElementById ("formatted_" + rowNum).style.display= "inline";
  document.getElementById ("edit_" + rowNum).style.display= "inline";

  document.getElementById ("input_" + rowNum).style.display= "none";
  document.getElementById ("cancel_" + rowNum).style.display= "none";

  document.getElementById ("content_" + rowNum).value= document.backupTicker[rowNum];
}

// Saves an edited news ticker item
function saveTicker (rowNum) {
  document.getElementById ("formatted_" + rowNum).style.display= "inline";
  document.getElementById ("edit_" + rowNum).style.display= "inline";

  document.getElementById ("input_" + rowNum).style.display= "none";
  document.getElementById ("cancel_" + rowNum).style.display= "none";

  var content= document.getElementById ("content_" + rowNum).value;
  document.backupTicker[rowNum]= content;

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { updateTicker (httpRequest, rowNum) };
    httpRequest.open ("GET", document.homePage + "ajax/?m=save_ticker&id=" + rowNum + "&content=" + encodeURIComponent (content) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Writes the updated ticker format
function updateTicker (httpRequest, rowNum) {
  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      document.getElementById ("formatted_" + rowNum).innerHTML= httpRequest.responseText;
    } else {
      alert ("There was a problem with the request, could not update ticker item");
    }
  }
}

// Adds a new item to the news ticker list
function addTicker () {
  var content= document.getElementById ("new_content").value;
  document.getElementById ("new_content").value= "";

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { newTicker (httpRequest, content) };
    httpRequest.open ("GET", document.homePage + "ajax/?m=add_ticker&content=" + encodeURIComponent (content) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Interpets the AJAX call to write a new ticker row
function newTicker (httpRequest, content) {
  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      var components= httpRequest.responseText.split ("<>");
      writeNewTicker (components[0], content, components[1]);
    } else {
      alert ("There was a problem with the request, could not add ticker item");
    }
  }
}

// Writes a new row into the news ticker list
function writeNewTicker (id, content, formattedContent) {
  var table= document.getElementById ("ticker_table");

  var ticker= table.insertRow (table.rows.length);
  ticker.id= "row_" + id;

  var cell= ticker.insertCell (ticker.cells.length);
  var link= document.createElement ("a");
  link.setAttribute ("href", "javascript:moveTickerUp (" + id +");");
  var arrow= document.createElement ("img");
  arrow.setAttribute ("src", "/images/arrowup.gif");
  link.appendChild (arrow);
  cell.appendChild (link);

  cell= ticker.insertCell (ticker.cells.length);
  link= document.createElement ("a");
  link.setAttribute ("href", "javascript:moveTickerDown (" + id +");");
  arrow= document.createElement ("img");
  arrow.setAttribute ("src", "/images/arrowdn.gif");
  link.appendChild (arrow);
  cell.appendChild (link);

  cell= ticker.insertCell (ticker.cells.length);
  cell.style.minWidth= "550px";
  var formattedSpan= document.createElement ("span");
  formattedSpan.id= "formatted_" + id;
  formattedSpan.style.display= "inline";
  formattedSpan.innerHTML= formattedContent;
  cell.appendChild (formattedSpan);
  var inputSpan= document.createElement ("span");
  inputSpan.id= "input_" + id;
  inputSpan.style.display= "none";
  var input= document.createElement ("input");
  input.setAttribute ("type", "text");
  input.setAttribute ("name", "content_" + id);
  input.setAttribute ("maxlength", 255);
  input.id= "content_" + id;
  input.style.width= "500px";
  input.value= content;
  input.onkeypress= function () { if (enterPressed (event)) saveTicker (id); };
  input.onkeydown= function () { checkTagPress (event, 'content_' + id, false); };
  inputSpan.appendChild (input);
  inputSpan.appendChild (document.createTextNode (" ["));
  link= document.createElement ("a");
  link.setAttribute ("href", "javascript:saveTicker (" + id + ");");
  link.appendChild (document.createTextNode ("save"));
  inputSpan.appendChild (link);
  inputSpan.appendChild (document.createTextNode ("]"));
  cell.appendChild (inputSpan);

  cell= ticker.insertCell (ticker.cells.length);
  cell.style.minWidth= "60px";
  var editSpan= document.createElement ("span");
  editSpan.id= "edit_" + id;
  editSpan.style.display= "inline";
  editSpan.appendChild (document.createTextNode ("["));
  link= document.createElement ("a");
  link.setAttribute ("href", "javascript:editTicker (" + id + ");");
  link.appendChild (document.createTextNode ("edit"));
  editSpan.appendChild (link);
  editSpan.appendChild (document.createTextNode ("]"));
  cell.appendChild (editSpan);
  var cancelSpan= document.createElement ("span");
  cancelSpan.id= "cancel_" + id;
  cancelSpan.style.display= "none";
  cancelSpan.appendChild (document.createTextNode ("["));
  link= document.createElement ("a");
  link.setAttribute ("href", "javascript:cancelTickerEdit (" + id + ");");
  link.appendChild (document.createTextNode ("cancel"));
  cancelSpan.appendChild (link);
  cancelSpan.appendChild (document.createTextNode ("]"));
  cell.appendChild (cancelSpan);

  cell= ticker.insertCell (ticker.cells.length);
  cell.appendChild (document.createTextNode ("["));
  link= document.createElement ("a");
  link.setAttribute ("href", "javascript:removeTicker (" + id + ");");
  link.appendChild (document.createTextNode ("remove"));
  cell.appendChild (link);
  cell.appendChild (document.createTextNode ("]"));
}

function popTagWindow () {
  window.open ("/index.php?m=tag_reference");
  return void (0);
}

function popSlideshowWindow () {
  window.open ("/slideshow/");
  return void (0);
}

/////////////////////////
// Slideshow Functions //
/////////////////////////

// Flips to the next slide in sequence
function nextSlide (showNumber) {
  var current= document.getElementById ("show_" + showNumber + "_slide_" + document.slideshows[showNumber].currentSlide);
  $(current).fadeOut ();

  document.slideshows[showNumber].currentSlide++;
  if (document.slideshows[showNumber].currentSlide > document.slideshows[showNumber].maxSlide) document.slideshows[showNumber].currentSlide= 1;

  var next= document.getElementById ("show_" + showNumber + "_slide_" + document.slideshows[showNumber].currentSlide);
  $(next).fadeIn ();
}

function showSlideshowPreview (showId) {
  if (showId) document.getElementById ("show_preview_" + showId).style.display= "block";
}

function checkSummaryLength () {
  var length= $('#page_summary').val().length;
  $('#summary_length').html(length);
  if (length == 0 || length >= 150) {
    $('#summary_length_warning').hide();
  } else {
    $('#summary_length_warning').show();
  }
}

