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

// Checks if a field's text value is blank
function isBlank (field) {
  if (!field.type || field.type != "text") return false; // Only check text fields
  return (field.value == null || field.value == "" || field.value.match (/^\s*$/));
}

// 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) {
    addAJAXMessage ("AJAX error: cannot create XMLHTTP instance", true);
    return false;
  }

  return httpRequest;
}

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

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

// Takes a retrieved XML object and writes the data into the displayed page
function writePage (xml) {
  if (xml.getElementsByTagName ("activeUsers").length == 0) {
    addAJAXMessage ("There was a problem with the request, could not update page data", true);
    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) + " user" + (totalUsers == 1 ? "" : "s") + " viewing"));

  // Get user information
  var userInfo= xml.getElementsByTagName ("userInfo")[0];
  var currentUserId= userInfo.getAttribute ("id");
  var allowMessages= userInfo.getAttribute ("allowMessages");
  if (userInfo.getAttribute ("longShift") == 1) document.getElementById ("sidebar_home_link").setAttribute ("class", "attention");

  // 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 ? "(guests only)" : "(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");
      var viewing= (activeUsers[i].getAttribute ("viewing") == 1);
      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));
      if (viewing) node.className= "viewing";
      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 newProjects= xml.getElementsByTagName ("navigation")[0].getAttribute ("newProjects");
  var newProjectComments= xml.getElementsByTagName ("navigation")[0].getAttribute ("newProjectComments");
  var newGames= xml.getElementsByTagName ("navigation")[0].getAttribute ("newGames");
  var newGameGoals= xml.getElementsByTagName ("navigation")[0].getAttribute ("newGameGoals");
  var newGameOnlineMatches= xml.getElementsByTagName ("navigation")[0].getAttribute ("newGameOnlineMatches");
  var newGameComments= xml.getElementsByTagName ("navigation")[0].getAttribute ("newGameComments");
  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 (newProjects) {
    var sidebarLine= document.getElementById ("new_projects");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newProjects + " new project" + (newProjects != 1 ? "s" : "");
  }
  if (newProjectComments) {
    var sidebarLine= document.getElementById ("new_project_comments");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newProjectComments + " new project comment" + (newProjectComments != 1 ? "s" : "");
  }

  if (newGames) {
    var sidebarLine= document.getElementById ("new_games");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newGames + " new game" + (newGames != 1 ? "s" : "");
  }
  if (newGameGoals) {
    var sidebarLine= document.getElementById ("new_game_goals");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newGameGoals + " new goal" + (newGameGoals != 1 ? "s" : "");
  }
  if (newGameOnlineMatches) {
    var sidebarLine= document.getElementById ("new_game_online_matches");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newGameOnlineMatches + " new online match" + (newGameOnlineMatches != 1 ? "es" : "");
  }
  if (newGameComments) {
    var sidebarLine= document.getElementById ("new_game_comments");
    sidebarLine.style.display= "block";
    sidebarLine.innerHTML= newGameComments + " new game comment" + (newGameComments != 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;
    var triggerSound= false; // Only trigger sounds if another user chats

    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");

      if (userId != document.userId) triggerSound= true;

      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 (triggerSound && document.chatSound) document.beep.play ();
    if (triggerSound && !document.viewing && document.title.substring (0, 1) != "*") {
      document.title= "*" + document.title;
    }

    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";
  }

  if (document.pageOptions.chat) {
    addAJAXMessage ("Chat and Sidebar Updated");
  } else {
    addAJAXMessage ("Sidebar Updated");
  }
}

// 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 ("onclick")) {
          newNode.setAttribute ("onclick", node.getAttribute ("onclick"));
        }
        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.onreadystatechange= function () { writeMessage (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= "&focus=" + (document.viewing ? 1 : 0);

  if (document.pageOptions.chat) {
    if (document.awaitingUpdate) return void (0);
    document.awaitingUpdate= true;

    urlParams+= "&lid=" + encodeURIComponent(document.latestChatId);
  }

  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 () {
  if (document.awaitingUpdate) {
    setTimeout ('submitChat ()', 50);
    return void (0);
  }

  document.awaitingUpdate= true;

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

  var parameters= "content=" + encodeURIComponent (chatValue) + "&lid=" + document.latestChatId + "&focus=" + (document.viewing ? 1 : 0);

  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);
}

function toggleChatSound () {
  var icon= document.getElementById ("chat_sound_toggle");
  document.chatSound= !document.chatSound;
  icon.setAttribute ("src", "/images/sound_" + (document.chatSound ? "on" : "off") + ".gif");

  if (document.userId == 0) return void (0);

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_chat_sound&val=" + (document.chatSound ? 1 : 0) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  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=401,height=270,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.onreadystatechange= function () { writeMessage (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 == "") {
        addAJAXMessage ("No user by that name was found", true);
      } else if (name == "[self]") {
        addAJAXMessage ("You may not specify your own username", true);
        name= "";
      }
      inputBlock.value= name;
    } else {
      addAJAXMessage ("There was a problem with the request, could not check username", true);
    }
  }

  document.checkingUsername= false
}

// Replaces a "show image" link with the image
function showImage (htmlId) {
  document.getElementById ("show_image_link_" + htmlId).style.display= "none";
  document.getElementById ("hidden_image_" + htmlId).style.display= "block";
}

// Adds an expiring message to the screen
function addAJAXMessage (message, error) {
  error= typeof (error) != 'undefined' ? error : false;

  document.ajaxMessages++;

  var item= document.createElement ("li");
  var span= document.createElement ("span");
  if (typeof (message) == "string") {
    span.appendChild (document.createTextNode (message));
  } else {
    putXMLasHTML (span, message);
  }
  item.appendChild (span);
  item.id= "ajax_message_" + document.ajaxMessages;
  if (error) item.setAttribute ("class", "ajax_error");
  window.setTimeout ("clearAJAXMessage (" + document.ajaxMessages + ");", error ? 10000 : 3500);

  document.getElementById ("ajax_messages").appendChild (item);
}

// Removes an expired message from the screen
function clearAJAXMessage (messageId) {
  var message= document.getElementById ("ajax_message_" + messageId);
  message.parentNode.removeChild (message);
}

// Gets a message from an ajax update and prints it to the page
function writeMessage (httpRequest) {
  if (httpRequest.readyState == 4) {
    if (httpRequest.status == 200) {
      var xml= httpRequest.responseXML;
      var message= xml.getElementsByTagName ("message");
      if (message.length == 0) return false;
      var error= message[0].getAttribute ("error") == 1;
      addAJAXMessage (message[0], error);
    } else {
      addAJAXMessage ("There was a problem with the update", true);
    }
  }

  return void (0);
}

///////////////////////
// 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 "url":
    var before= '[link url="';
    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 "k":
      createTag (fieldId, "blackout");
      break;
    case "l":
      createTag (fieldId, "link");
      break;
    case "p":
      if (!long) return true;
      createTag (fieldId, "image");
      break;
    case "q":
      if (!long) return true;
      createTag (fieldId, "quote");
      break;
    case "s":
      if (long) {
        createTag (fieldId, "spoiler");
      } else {
        createTag (fieldId, "blackout");
      }
      break;
    case "u":
      createTag (fieldId, "url");
      break;
    case "y":
      if (!long) return true;
      createTag (fieldId, "youtube");
      break;
    case "z":
      createTag (fieldId, "sarcasm");
      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.onreadystatechange= function () { writeMessage (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 == "comment_content") {
    var content= document.getElementById ("comment_content").value;
    var signatureBox= document.getElementById ("use_signature");
    var signature= "";
    if (signatureBox && signatureBox.checked) signature= "[signature]" + document.getElementById ("signature_text").value + "[/signature]";

    if (signature != "") content= (trim (content) == "" ? signature : content + "\n\n" + signature);

    getPreview (content, "content_preview");
    getPreview (document.getElementById ("comment_subject").value, "subject_preview", false);

    var pollBlock= document.getElementById ("poll_choices");
    if (pollBlock) {
      pollChoices= pollBlock.value;
      getPreview (document.getElementById ("comment_subject").value, "poll_question", false);
      var choiceTable= document.getElementById ("poll_choices_preview");

      var oldRows= choiceTable.rows.length;
      while (choiceTable.rows.length > 1) {
        choiceTable.deleteRow (1);
      }

      for (var i= 1; i <= pollChoices; i++) {
        var row= choiceTable.insertRow (choiceTable.rows.length);
        var cell= row.insertCell (row.cells.length);
        cell.id= "preview_poll_choice_" + i;

        getPreview (document.getElementById ("poll_choice_"+i).value, "preview_poll_choice_" + i, false, "links chat");
      }
    }

    document.getElementById ("preview_comment").style.display= "block";
  } else if (fieldId == "blog_content") {
    getPreview (document.getElementById ("blog_content").value, "content_preview");
    getPreview (document.getElementById ("blog_subject").value, "subject_preview", false);

    var mood= document.getElementById ("blog_mood").value;
    if (trim (mood) == "") {
      document.getElementById ("mood_preview").style.display= "none";
    } else {
      mood= "[b]Mood:[/b] " + mood;
      getPreview (mood, "mood_preview", false, "links");
      document.getElementById ("mood_preview").style.display= "block";
    }

    var tags= document.getElementById ("blog_tags").value;
    if (trim (tags) == "") {
      document.getElementById ("tags_preview").style.display= "none";
    } else {
      tags= "<b>Tags:</b> " + escapeHTML (tags);
      document.getElementById ("tags_preview").innerHTML= tags;
      document.getElementById ("tags_preview").style.display= "block";
    }

    document.getElementById ("preview_blog").style.display= "block";
  } else if (fieldId == "review_content") {
    getPreview (document.getElementById ("review_content").value, "content_preview");
    getPreview (document.getElementById ("review_subject").value, "subject_preview", false);
    getPreview (document.getElementById ("review_title").value, "title_preview", false);

    document.getElementById ("category_preview").innerHTML= "(" + document.getElementById ("review_category").value + ")";

    var rating= document.getElementById ("review_rating").value;
    if (rating == "") {
      document.getElementById ("rating_preview").style.display= "none";
    } else {
      rating= "<b>Score:</b> " + rating;
      document.getElementById ("rating_preview").innerHTML= rating;
      document.getElementById ("rating_preview").style.display= "block";
    }

    document.getElementById ("preview_review").style.display= "block";
  } else if (fieldId == "module_content") {
    getPreview (document.getElementById ("module_heading").value, "heading_preview", false);
    getPreview (document.getElementById ("module_content").value, "content_preview", true, "rpgcc");

    document.getElementById ("preview_module").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";
  } else if (fieldId == "profile_signature") {
    var content= "This is sample comment text.";
    var signature= "[signature]" + trim (document.getElementById ("profile_signature").value) + "[/signature]";
    if (signature != "") content= content + "\n\n" + signature;
    getPreview (content, "content_preview");
    getPreview ("Sample Comment", "subject_preview", false);

    document.getElementById ("preview_comment").style.display= "block";
  } else if (fieldId == "profile_signaturea") {
    var signature= trim (document.getElementById ("profile_signature").value);
    if (signature != "") signature= "\n\n" + signature;
    getPreview (signature, "signature_preview");
    document.getElementById ("preview_profile_signature").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.onreadystatechange= function () { writeMessage (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.onreadystatechange= function () { writeMessage (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].replace (/\+/g, "%2B");
  }

  document.location= link;
}

function showView (view) {
  var link= document.link + "&view=" + view;

  for (var i in document.filters) {
    if (i == "view") 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 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;
}

// Saves the parameters of a search for later use
function saveSearch (session) {
  var name= prompt ("Enter a name for this saved search");
  if (!name) return void (0);

  var link= document.link + "&clear=1";

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

  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=save_search&ses=" + encodeURIComponent(session) + "&name=" + encodeURIComponent(name) + "&link=" + encodeURIComponent(link) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  return void (0);
}

function doSavedSearch (linkId) {
  if (!linkId) return void (0);
  document.location= document.saved_links[linkId];
}

function removeSavedSearch (select) {
  var linkId= select.value;
  if (!linkId) return void (0);
  var name= select.options[select.selectedIndex].text;

  if (!confirm ("Are you sure you want to remove \"" + name + "\" from your saved searches list?")) return void (0);

  select.options[select.selectedIndex]= null;

  var httpRequest= getHTTPRequest ();

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

  return void (0);
}

//////////////////////
// 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 {
      addAJAXMessage ("There was a problem with the request, could not write preview", true);
    }
  }
}

///////////////////////
// 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";
}

///////////////////////////////
// Project Manager Functions //
///////////////////////////////

function verifyProject () {
  var messages= new Array ();

  if (isBlank (document.getElementById ("title"))) messages.push ("-You must specify a title");
  if (document.getElementById ("client_id").value == 0) messages.push ("-You must specify a client");

  var employeeSelect= document.getElementById ("employees");
  var employeesChosen= 0;
  for (var i= 0; i < employeeSelect.options.length; i++) {
    if (employeeSelect.options[i].selected) employeesChosen++;
  }
  if (employeesChosen < 1) messages.push ("-You must specify at least one employee");

  if (messages.length > 0) {
    alert ("This form cannot be submitted until you correct the following errors:\n\n" + messages.join ("\n"));
    return false;
  }

  return true;
}

function updateProjectField (field, projectId, value) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=update_project_field&f=" + field + "&id=" + projectId + "&val=" + value + "&ts=" + new Date ().getTime ());
    httpRequest.send (null);
  }
}

function processSubmitProject () {
  document.main_form.parent_id.value= document.getElementById ("client_projects_" + document.main_form.client_id.value).value;
}

function expandProject (projectId) {
  var rows= document.getElementsByTagName ("tr");

  for (var i= 0; i < rows.length; i++) {
    if (rows[i].getAttribute("parent_id") == projectId) {
      rows[i].style.display= "";
    }
  }

  var button= document.getElementById ("button_" + projectId);
  button.innerHTML= "&ndash;";
  button.onclick= function () { collapseProject (projectId); };
}

function collapseProject (projectId) {
  var rows= document.getElementsByTagName ("tr");

  for (var i= 0; i < rows.length; i++) {
    var parentId= rows[i].getAttribute ("parent_id");
    if (parentId == projectId) {
      rows[i].style.display= "none";
      if (rows[i].getAttribute("expandable") == "expandable") {
        collapseProject (rows[i].id.substring(8));
      }
    }
  }

  var button= document.getElementById ("button_" + projectId);
  button.innerHTML= "+";
  button.onclick= function () { expandProject (projectId); };
}

function expandAllProjects (button) {
  var rows= document.getElementsByTagName ("tr");

  for (var i= 0; i < rows.length; i++) {
    if (rows[i].getAttribute("expandable") == "expandable") {
      expandProject (rows[i].id.substring (8));
    }
  }

  button.innerHTML= "Collapse All";
  button.onclick= function () { collapseAllProjects (button); };
}

function collapseAllProjects (button) {
  var rows= document.getElementsByTagName ("tr");

  for (var i= 0; i < rows.length; i++) {
    if (rows[i].getAttribute("expandable") == "expandable") {
      collapseProject (rows[i].id.substring (8));
    }
  }

  button.innerHTML= "Expand All";
  button.onclick= function () { expandAllProjects (button); };
}

function updateHours () {
  var year= document.main_form.date_yyyy.value;
  var month= document.main_form.date_mm.value - 1;
  var day= document.main_form.date_dd.value;

  var startHour= Number (document.main_form.start_time_hh.value) + ((document.main_form.start_time_ap.value == "PM") ? 12 : 0);
  var startMinute= document.main_form.start_time_mm.value;
  var startDate= new Date (year, month, day, startHour, startMinute);

  var endHour= Number (document.main_form.end_time_hh.value) + ((document.main_form.end_time_ap.value == "PM") ? 12 : 0);
  var endMinute= document.main_form.end_time_mm.value;
  var endDate= new Date (year, month, day, endHour, endMinute);

  var difference= endDate.valueOf() - startDate.valueOf();
  var hours= difference / 36000;

  hours= Math.round(hours) / 100;

  if (hours < 0) {
    hours+= 24;
    document.main_form.overnight.value= 1;
  } else {
    document.main_form.overnight.value= 0;
  }

  if (document.main_form.hours.value == document.main_form.billable_hours.value) {
    document.main_form.billable_hours.value= hours;
  }
  document.main_form.hours.value= hours;

  checkBillable ();
}

function updateEndTime (hours) {
  checkBillable ();

  var year= document.main_form.date_yyyy.value;
  var month= document.main_form.date_mm.value - 1;
  var day= document.main_form.date_dd.value;

  var startHour= Number (document.main_form.start_time_hh.value) + ((document.main_form.start_time_ap.value == "PM") ? 12 : 0);
  var startMinute= document.main_form.start_time_mm.value;
  var startDate= new Date (year, month, day, startHour, startMinute);

  var endDate= new Date (startDate.valueOf() + (hours * 3600000));

  if (endDate.getDay () != startDate.getDay ()) {
    document.main_form.overnight.value= 1;
  } else {
    document.main_form.overnight.value= 0;
  }

  var endHour= endDate.getHours () % 12;
  if (endHour < 10) {
    endHour= "0"+endHour;
  }

  var endMinute= endDate.getMinutes ();
  if (endMinute < 10) {
    endMinute= "0"+endMinute;
  }

  var endAMPM= (endDate.getHours () < 12) ? "AM" : "PM";

  document.main_form.end_time_hh.value= endHour;
  document.main_form.end_time_mm.value= endMinute;
  document.main_form.end_time_ap.value= endAMPM;
}

function updateProjects (clientId, updatePriority) {
  updatePriority= typeof (updatePriority) != 'undefined' ? updatePriority : false;

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

  for (var i= 0; i < selectBoxes.length; i++) {
    var selectBox= selectBoxes[i];
    if (selectBox.getAttribute ("client")) {
      selectBox.style.display= "none";
      selectBox.disabled= true;
    }
  }

  document.getElementById ("client_projects_" + clientId).style.display= "inline";
  document.getElementById ("client_projects_" + clientId).disabled= false;

  if (updatePriority) {
    document.getElementById ("client_priority_" + clientId).style.display= "inline";
    document.getElementById ("client_priority_" + clientId).disabled= false;
  } else if (typeof (document.defaultPriority != "undefined") && document.defaultPriority) {
    document.getElementById ("priority").options[document.clientPriority[clientId] - 1].selected= true;
  }
}

function updateProjectBox () {
  var preset= document.getElementById ("preset_project");

  var status;
  if (preset) {
    status= preset.getAttribute ("status_code");
  } else {
    var select= document.getElementById ("client_" + document.main_form.client_id.value);

    var status= select.options[select.selectedIndex].getAttribute("status");
    if (select.value == 0) {
      status= "Complete"; // If no project is selected, always hide
    }
  }

  var hide= status == "Complete" || status == "Closed";

  document.getElementById ("project_box").style.display= (hide ? "none" : "block");
  document.main_form.project_complete.disabled= (hide ? true : false);
}

function checkBillable () {
  if (parseFloat (document.main_form.billable_hours.value) > parseFloat (document.main_form.hours.value)) {
    document.main_form.billable_hours.value= parseFloat (document.main_form.hours.value);
  }
}

function addProjectComment (parentId) {
  document.comment_form.style.display= "block";
  document.comment_form.parent.value= parentId;
  document.comment_form.subject.focus ();
}

function updateEmployeeId (userId, employeeId) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=update_employee_id&uid=" + userId + "&eid=" + employeeId + "&ts=" + new Date ().getTime ());
    httpRequest.send (null);
  }
}

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

function updateMonth (block) {
  var month= document.getElementById (block+"_mm").value - 1;
  var year= document.getElementById (block+"_yyyy").value;
  var oldDay= document.getElementById (block+"_dd").value;

  if (document.abbreviate) {
    var dayNames= ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  } else {
    var dayNames= ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  }

  var date= new Date (year, month, 1);
  weekday= date.getDay ();

  options= document.getElementById (block+"_dd").options;
  while (options.length > 0) {
    options[0]= null;
  }

  var monthLength= getMonthLength (month, year);
  for (var i= 0; i < monthLength; i++) {
    options[i]= new Option (""+(i+1)+" "+dayNames[(i+weekday)%7], i+1);
  }

  if (oldDay > monthLength) {
    oldDay= monthLength;
  }
  document.getElementById (block+"_dd").value= oldDay;
}

function getMonthLength (month, year) {
  var monthLengths= [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

  if (month == 1) {
    var date= new Date (year, 1, 29);
    if (date.getDate () == 29) {
      return 29;
    }
  }

  return monthLengths[month];
}

function resetDate (block) {
  var today= new Date ();

  var year= today.getYear ();
  if (year < 1900) year+= 1900;

  document.getElementById (block+"_mm").value= today.getMonth () + 1;
  document.getElementById (block+"_dd").value= today.getDate ();
  document.getElementById (block+"_yyyy").value= year;
}

function setupCalendar (block, monthsShown) {
  var select= document.getElementById (block+"_calendar_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 < monthsShown; 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 (block, select.value);
}

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

  writeCalendar (block, month, year);
}

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

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

  if (block == 'delivery') {
    today.setDate (today.getDate()+1);
  }

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

  day.setDate (day.getDate()-day.getDay());
  while (true) {
    if (day.getDay() == 0) {
      html+= "</tr><tr>";
    }

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

    html+= '<td'+classString+'>'+writeDateLink(day, block)+"</td>";

    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 (block+"_calendar").innerHTML= html;
}

function writeDateLink (day, block) {
  return '<a href="javascript:updateDate (\''+block+'\', '+(day.getYear()+1900)+', '+(day.getMonth()+1)+', '+day.getDate()+');">'+day.getDate()+'</a>';
}

function updateDate (block, year, month, day) {
  document.getElementById (block+"_mm").value= month;
  document.getElementById (block+"_yyyy").value= year;
  updateMonth (block);
  document.getElementById (block+"_dd").value= day;

  showCalendar (block, false);
}

function toggleDate (block, disabled) {
  document.getElementById (block+"_mm").disabled= disabled;
  document.getElementById (block+"_yyyy").disabled= disabled;
  document.getElementById (block+"_dd").disabled= disabled;
}

function showCalendar (block, shown) {
  if (shown) {
    document.getElementById (block+"_popup").style.display= "block";
  } else {
    document.getElementById (block+"_popup").style.display= "none";
  }
}


/////////////////////////////
// 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.onreadystatechange= function () { writeMessage (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.onreadystatechange= function () { writeMessage (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 {
      addAJAXMessage ("There was a problem with the request, could not update point total", true);
    }
  }
}

// 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;
  if (document.getElementById ("reward_" + rewardId + "_points")) {
    points= forumId ? 0 : parseInt (document.getElementById ("reward_" + rewardId + "_points").value);
    if (isNaN (points) || points < 1) points= 1;
  } else {
    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;
        }

        addAJAXMessage ("There was a problem with the request, could not update reward preview", true);
        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 {
      addAJAXMessage ("There was a problem with the request, could not update reward preview", true);
    }
  }
}

///////////////////
// 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;
}

// Saves an updated piece of data in edit mode
function updateField (table, idField, id, field, value, database) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=update_field&t=" + encodeURIComponent (table) + "&idf=" + encodeURIComponent (idField) + "&id=" + encodeURIComponent (id) + "&f=" + encodeURIComponent (field) + "&val=" + encodeURIComponent (value) + "&db=" + encodeURIComponent (database) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }

  return void (0);
}

////////////////////
// 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;
}

/////////////////////////
// Game List Functions //
/////////////////////////

// Pops up a "copy goals" window
function popGoalCopy (gameId) {
  window.open ("/games/?m=copy_goals&id=" + gameId, "copy_goals", "width=520,height=200,scrollbars=0,resizable=0");
}

// Executes a goal copy
function copyGoals (fromId, toId) {
  if (fromId == toId) return;
  document.location= document.homePage + "games/?m=goal_copy&from=" + fromId + "&to=" + toId;
}

// Verifies that a submitted video game is valid
function checkGame () {
  if (trim (document.getElementById ("game_name").value) == "") {
    alert ("Please enter a game name");
    return false;
  }

  if (document.getElementById ("series_select").value == "new" && trim (document.getElementById ("new_series").value) == "") {
    alert ("Please enter a series name, select a game series, or select (none)");
    return false;
  }

  return true;
}

// Verifies that a submitted video game platform is valid
function checkPlatform () {
  if (trim (document.getElementById ("platform_name").value) == "") {
    alert ("Please enter a platform name");
    return false;
  }

  if (trim (document.getElementById ("platform_abbr").value) == "") {
    alert ("Please enter a platform abbreviation");
    return false;
  }

  return true;
}

// Verifies that a submitted video game series is valid
function checkSeries () {
  if (trim (document.getElementById ("series_name").value) == "") {
    alert ("Please enter a series name");
    return false;
  }

  return true;
}

// Updates the "new game series" dialog based on the game series selection box
function updateGameSeries (value) {
  document.getElementById ("new_series").style.display= (value == "new" ? "block" : "none");
}

// Saves a change in user game rating
function saveUserGameRating (gameId, value) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_user_game_rating&id=" + encodeURIComponent(gameId) +  "&val=" + encodeURIComponent(value) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Saves a change in user game ownership
function saveUserGameOwnership (gameId, value) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_user_game_ownership&id=" + encodeURIComponent(gameId) +  "&val=" + encodeURIComponent(value) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Saves a change in user game status
function saveUserGameStatus (gameId, value) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_user_game_status&id=" + encodeURIComponent(gameId) +  "&val=" + encodeURIComponent(value) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Saves a change in user game online status
function saveUserGameOnline (gameId, value) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_user_game_online&id=" + encodeURIComponent(gameId) +  "&val=" + encodeURIComponent(value) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Saves a change in user game notes
function saveUserGameNotes (gameId, value) {
  var httpRequest= getHTTPRequest ();

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_user_game_notes&id=" + encodeURIComponent(gameId) +  "&val=" + encodeURIComponent(value) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Saves a change in user game goal priority
function saveUserGoalPriority (goalId, value) {
  var httpRequest= getHTTPRequest ();

  var goalTitle= document.getElementById ("goal_title_" + goalId);
  if (goalTitle) {
    var type= value == "Standard" ? "standard" : (value == "Important" ? "important" : "ignored");
    goalTitle.setAttribute ("class", type + "_goal_title");
  }

  if (document.getElementById ("goal_priority_" + goalId).value == "") {
    return false;
  }
  removeBlankPriority (goalId);

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_user_goal_priority&id=" + encodeURIComponent(goalId) +  "&val=" + encodeURIComponent(value) + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Saves a change in user game goal completion
function saveUserGoalComplete (goalId, box) {
  var httpRequest= getHTTPRequest ();

  value= box.checked ? 1 : 0;

  var priority= document.getElementById ("goal_priority_" + goalId);
  if (priority.value == "") {
    priority.value= "Standard";
  }
  removeBlankPriority (goalId);

  if (httpRequest) {
    httpRequest.onreadystatechange= function () { writeMessage (httpRequest); };
    httpRequest.open ("GET", document.homePage + "ajax/?m=set_user_goal_complete&id=" + encodeURIComponent(goalId) +  "&val=" + value + "&ts=" + new Date ().getTime (), true);
    httpRequest.send (null);
  }
}

// Removes the blank option from a goal priority list
function removeBlankPriority (goalId) {
  var select= document.getElementById ("goal_priority_" + goalId);
  if (select.options[0].value == "") {
    select.removeChild (select.options[0]);
  }
}

// Creates a new game goal block and adds it to the end of the list
function newGameGoal () {
  var list= document.getElementById ("goal_list");
  var newId= ++document.newGoals;

  var item= document.createElement ("li");
  item.id= "goal_" + newId;

  var titleDiv= document.createElement ("div");
  var title= document.createElement ("b");
  title.appendChild (document.createTextNode ("Title:"));
  titleDiv.appendChild (title);
  titleDiv.appendChild (document.createTextNode (" "));
  var titleInput= document.createElement ("input");
  titleInput.setAttribute ("type", "text");
  titleInput.setAttribute ("maxlength", 80);
  titleInput.setAttribute ("name", "goal_title_new_" + newId);
  titleInput.style.width= "450px";
  titleDiv.appendChild (titleInput);

  titleDiv.appendChild (document.createTextNode (" "));
  var removeLink= document.createElement ("a");
  removeLink.setAttribute ("href", "javascript:removeNewGoal (" + newId + ");");
  removeLink.setAttribute ("class", "link_button");
  removeLink.appendChild (document.createTextNode ("remove"));
  titleDiv.appendChild (removeLink);

  item.appendChild (titleDiv);

  var detailsDiv= document.createElement ("div");
  var details= document.createElement ("b");
  details.appendChild (document.createTextNode ("Description:"));
  detailsDiv.appendChild (details);
  item.appendChild (detailsDiv);

  var detailsText= document.createElement ("textarea");
  detailsText.setAttribute ("name", "goal_details_new_" + newId);
  detailsText.style.width= "600px";
  detailsText.setAttribute ("rows", 3);
  item.appendChild (detailsText);

  list.appendChild (item);

  document.getElementById ("bottom_new_goal").style.display= "block";

  return void (0);
}

// Marks an existing, removeable goal for removal upon submission
function removeGoal (goalId) {
  var goal= document.getElementById ("goal_" + goalId);
  if (!goal) return void (0);

  var removeInput= document.createElement ("input");
  removeInput.setAttribute ("type", "hidden");
  removeInput.setAttribute ("name", "goal_removed_" + goalId);
  removeInput.value= 1;
  goal.appendChild (removeInput);
  goal.style.display= "none";

  return void (0);
}

// Removes a block for a new, unsaved goal
function removeNewGoal (goalId) {
  var list= document.getElementById ("goal_list");
  var goal= document.getElementById ("goal_" + goalId);

  list.removeChild (goal);
}

// Toggles the display of the game user list
function toggleGameUserList (show) {
  document.getElementById ("game_user_data").style.display= show ? "block" : "none";
  document.getElementById ("game_user_toggle").style.display= show ? "none" : "block";
}

// Executes a goal reset for a given game
function setGoalPriority (gameId) {
  var target= document.getElementById ("mass_priority_target").value;
  var priority= document.getElementById ("mass_priority").value;

  document.location= document.homePage + "games/?m=reset_goals&id=" + gameId + "&target=" + target + "&priority=" + priority;
}

/////////////////////
// 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;
}

// Verifies that a submitted RPG system is valid
function checkSystem () {
  if (trim (document.getElementById ("system_name").value) == "") {
    alert ("Please enter an RPG system name");
    return false;
  }

  if (trim (document.getElementById ("system_abbr").value) == "") {
    alert ("Please enter an RPG system abbreviation");
    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";
  }
}
