function checkRequiredFields(form) {
  /* Ensures that all required form fields have a value before submission */
  var childNodes = form.childNodes;
  var allIsWell=true;
  var completedInputs = {};  // Will contain class names of all <inputs> where user has given a value
  getCompletedInputs(form.childNodes, completedInputs);
  for (var i=0; i<childNodes.length; i++) {
    if (childNodes[i].nodeName.toUpperCase() == "SPAN") {
      var className = childNodes[i].className;
      if (className.indexOf("required_") == 0) {
        if (completedInputs[className] == undefined) {
          allIsWell=false;
          childNodes[i].style.color="red";
          childNodes[i].style.fontWeight="bold";
        }
        else {  // We might have to re-set the style of something that turned red the last time around.
          childNodes[i].style.color="black";
          childNodes[i].style.fontWeight="normal";
        }
      }
    }
  }
  if (!allIsWell) {
    alert ("Please supply values for all of the required fields.\nThose which still require values are marked in red.");
  }
  return allIsWell;
}

function getCompletedInputs(nodeSet, completedInputs) {
  /* Returns a hash whose keys are <input> and <textarea> fields where values have been set */
  for (var i=0; i<nodeSet.length; i++) {
    var nodeName = nodeSet[i].nodeName.toUpperCase();
    var className = nodeSet[i].className;
    if ((nodeName == "INPUT" && nodeSet[i].type == "text") || (nodeName == "TEXTAREA")) {
      var strippedText = nodeSet[i].value.replace(/^\s*/, '').replace(/\s*$/, '');
      // If the user must give a value for EITHER field A OR field B (but does not have to
      // give values for both), we assign the two <input> elements to the same class, which
      // much start with the string "required_".  If either one has a value, the class name
      // will appear in the "completedInputs" hash.
      if (strippedText != "") {
        completedInputs[className] = 1;  // Doens't matter what value we use, as long as it's something
      }
    }
    else if(nodeName=="INPUT" && nodeSet[i].type == "checkbox") {
      if (nodeSet[i].checked == true) {
        completedInputs[className] = 1;  // See note above.
      }
    }
    if (nodeSet[i].hasChildNodes()) {
      // Recurse down the tree looking for matching input elements
      getCompletedInputs(nodeSet[i].childNodes, completedInputs);
    }
  }
  // This function doesn't return anything as it's acting on a hash passed in from above.
}

function fixFormInputs(prepend) {
  /* Forms submitted via a post form can cause 404 errors in Wordpress if the "name"
     attributes of the input fields conflict with Wordpress's own.  Therefore, add a
     plugin-specific prefix to all such elements once the html is set. */
  var forms = document.getElementsByTagName("FORM");
  for (var i=0; i<forms.length; i++) {
    var namedNodes = new Array();
    getNamedElements(forms[i], namedNodes);
    for (var j=0; j<namedNodes.length; j++) {
      var origName = namedNodes[j].getAttribute("name");
      namedNodes[j].setAttribute("name", prepend+origName);
    }   
  }
}

function getNamedElements(parentNode, nodeSet) {
  /* Find all children (and, recursively, all descendants) of "parentNode" that have
     a "name" attribute (and are therefore probably input elements in a form), and
     add them to array "nodeSet". Finding all elements with a "name" attribute seemed
     cleaner that finding all "<input>", "<select>", and "<textarea>" elements. */
  var childNodes = parentNode.childNodes;
  for (var i=0; i<childNodes.length; i++) {
    // Check if each child node is an element node (and not a text node).
    // The DOM specifies constants for such things (e.g., Node.ELEMENT_NODE)
    // but these are not defined in IE, so I just use the raw value (1).
    if (childNodes[i].nodeType == 1) {
      var nameNode = childNodes[i].getAttribute("name");
      if (nameNode != null) {
        nodeSet.push(childNodes[i]);
      }
      if (childNodes[i].hasChildNodes()) {
        getNamedElements(childNodes[i], nodeSet);
      }
    }
  }
  // This function doesn't return anything as it's acting on an array passed in from above.
}