/**
 * Returns AJAX XMLHttpRequest object.
 *
 * @return    XMLHttpRequest object.
 */
function psGetXmlHttpObject() {
  var xmlHttp = null;

  try {
    /* Firefox, Opera 8.0+, Safari */
    xmlHttp = new XMLHttpRequest();
  } catch(e) {
    try {
      /* Internet Explorer 6.0+ */
      xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
      /* Internet Explorer 5.5+ */
      xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
  }

  return xmlHttp;
}

/**
 * Encodes a string to be used in a query part of a URL.
 *
 * @param     str                 String to be encoded.
 * @return    Encoded string.
 */
function psUrlEncode(str) {
  str = escape(str);
  str = str.replace(/\+/g, "%2B");
  str = str.replace(/%20/g, "+");
  str = str.replace(/\*/g, "%2A");
  str = str.replace(/\//g, "%2F");
  str = str.replace(/@/g, "%40");

  return str;
}

/**
 * Decodes URL-encoded string.
 *
 * @param     str                 String to be decoded.
 * @return    Decoded string.
 */
function psUrlDecode(str) {
  str = str.replace(/\+/g, " ");
  str = unescape(str);

  return str;
}

/**
 * Sets the source filename of an image by ID.
 *
 * @param     id                  Image element ID.
 * @param     src                 Source filename.
 */
function psSetImageSrcById(id, src) {
  document.getElementById(id).src = src;
}

/**
 * Returns numeric value of an element.
 *
 * @param     name                Element name.
 * @return    Valid numeric value, else Number.NaN.
 */
function psGetElementNumValue(name) {
  var str = document.getElementsByName(name)[0].value;

  return (str != "" && !isNaN(Number(str))) ? Number(str) : Number.NaN;
}

/**
 * Sets the opacity of an element.
 *
 * @param     element             HTML image element.
 * @param     opacityPct          Opacity (%).
 */
function psSetOpacity(element, opacityPct) {
  /* Internet Explorer */
  element.style.filter = "alpha(opacity=" + opacityPct + ")";
  /* Old Mozilla and Firefox */
  element.style.MozOpacity = opacityPct / 100;
  /* Others */
  element.style.opacity = opacityPct / 100;
}

/**
 * Trims leading whitespaces.
 *
 * @param     str                 String.
 * @return    String.
 */
function psLtrim(str) {
  return str.replace(/^\s+/, "");
}

/**
 * Trims trailing whitespaces.
 *
 * @param     str                 String.
 * @return    String.
 */
function psRtrim(str) {
  return str.replace(/\s+$/, "");
}

/**
 * Trims leading and trailing whitespaces.
 *
 * @param     str                 String.
 * @return    String.
 */
function psTrim(str) {
  return str.replace(/^\s+|\s+$/g, "");
}

/**
 * Makes first letter upper-case.
 *
 * @param     str                 String.
 * @return    String.
 */
function psUcfirst(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * Displays an error message.
 *
 * @param     message             Error message.
 */
function psDisplayError(message) {
  alert(message);
}

/**
 * Displays an error message caused by not being able to create XMLHttpRequest
 * object.
 */
function psDisplayXmlHttpError() {
  psDisplayError("FATAL: Unable to create XMLHttpRequest object.");
}

/**
 * Displays error messages from the XML response of AJAX scripts.
 *
 * @param     xmlData             XML response of AJAX scripts.
 */
function psDisplayXmlError(xmlData) {
  var messages = "";
  var elements = xmlData.getElementsByTagName("description");

  for (var i = 0; i < elements.length; i++) {
    messages += "- " + elements[i].childNodes[0].nodeValue + "\n";
  }

  alert(messages);
}

/**
 * Returns the integer number value of a string.
 *
 * @param     str                 String.
 * @return    Integer number value, else Number.NaN.
 */
function psStrInt(str) {
  str = psTrim(str);
  if (str == "") {
    return Number.NaN;
  }

  var num = Number(str);
  if (isNaN(num)) {
    return Number.NaN;
  }

  return (str === num.toString()) ? num : Number.NaN;
}

/**
 * Returns the floating-point number value of a string.
 *
 * @param     str                 String.
 * @return    Floating-point number value, else Number.NaN.
 */
function psStrFloat(str) {
  str = psTrim(str);

  return (str != "" && str.indexOf("0x") == -1 && !isNaN(Number(str)))
       ? Number(str) : Number.NaN;
}

