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

/**
 * Returns error descriptions from the XML response of AJAX scripts.
 *
 * @param     xmlData             XML response of AJAX scripts.
 * @return    Error descriptions.
 */
function psGetXmlError(xmlData) {
  var descriptions = "";
  var elements = xmlData.getElementsByTagName("description");

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

  return descriptions;
}

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

/**
 * Returns a string representation of a decimal value in fixed format.
 *
 * @param     value               Decimal value.
 * @param     numDecimals         Number of decimals.
 * @return    String representation in fixed format.
 */
function psDecimalString(value, numDecimals) {
  return (new Number(value)).toFixed(numDecimals);
}

/**
 * Returns a string representation of a decimal value in exponential format.
 *
 * @param     value               Decimal value.
 * @return    String representation in exponential format.
 */
function psExponentString(value) {
  return (new Number(value)).toExponential(6).toUpperCase();
}

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

/**
 * Validates numeric value of an element.
 *
 * @param     name                Element name.
 * @return    True if the element contains a valid numeric value, else false.
 */
function psValidateNumericElement(name) {
  var value = document.getElementsByName(name)[0].value;

  return (value != "" && !isNaN(Number(value)));
}

/**
 * Increments or decrements numeric value of an element.
 *
 * @param     name                Element name.
 * @param     step                Step size.
 * @param     min                 Minimum value.
 * @param     max                 Maximum value.
 * @param     periodic            Periodic numeric value flag.
 */
function psSetElementUsingStepper(name, step, min, max, periodic) {
  var value = Number(document.getElementsByName(name)[0].value) + Number(step);

  if (periodic) {
    value %= max;
    if (value < 0) {
      value += max;
    }
  } else {
    if (value < min) {
      value = min;
    } else if (value > max) {
      value = max;
    }
  }

  document.getElementsByName(name)[0].value = (step.indexOf(".") == -1)
                                            ? psDecimalString(value, 0)
                                            : psDecimalString(value, 2);
}

/**
 * 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) {
  var index;

  for (index = 0; index < str.length; index++) {
    if (str.charAt(index) != " " && str.charAt(index) != "\n") {
      break;
    }
  }

  return str.substring(index);
}

/**
 * Trims trailing whitespaces.
 *
 * @param     str                 String.
 * @return    String.
 */
function psRtrim(str) {
  var index;

  for (index = str.length - 1; index >= 0; index--) {
    if (str.charAt(index) != " " && str.charAt(index) != "\n") {
      break;
    }
  }

  return str.substring(0, index + 1);
}

/**
 * Trims leading and trailing whitespaces.
 *
 * @param     str                 String.
 * @return    String.
 */
function psTrim(str) {
  return psLtrim(psRtrim(str));
}
