
/**
 * -----------------------------------------------------
 * The URL function is the constructor for url objects.
 * A url object represents either an absolute url or an
 * absolute path part of a url.
 * It can be used to resolve relative urls.
 *
 * new URL(absStr) returns a new url object correspnding
 * to the absolute url string or absolute path string
 * absStr.
 */
function URL(absStr) {
  var components = URL.parsePattern.exec(absStr);
  this.stringSource = absStr;
  this.schema = components[1] ? components[1] : URL.emptyString;
  this.authority = components[2] ? components[2].toLowerCase() : URL.emptyString;
  this.path = components[3];
  this.query = components[4] ? components[4] : URL.emptyString;
  this.fragment = components[5] ? components[5] : URL.emptyString;
  if (this.schema.length > 0) {
    this.normalize();
  }
};
/**
 * The following are private static members used in
 * parsing a url string.
 */
URL.parsePattern = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
URL.directoryPattern = /^((.*\/)*)?/;
URL.absolutePattern = /^\//;
URL.dotDotReplacePattern = /\/([^\.\/]*\/|\.[^\.\/][^\/]*\/|\.\.[^\/]+\/)\.\.\//;
URL.dotDotEndReplacePattern = /\/[^\/]*\/\.\.$/;
URL.dotReplacePattern = /\/\.\//;
URL.dotEndReplacePattern = /\/\.$/g;
URL.emptyString = "";
/**
 * URL.getDocumentURL() returns a URL object for the
 * document global variable.
 * This is the starting point for resolving relative
 * urls.
 */
URL.getDocumentURL = function() {
  if (!URL.documentURL) {
    URL.documentURL = new URL(document.URL);
  }
  return URL.documentURL;
};
/**
 * intent: private
 * url.normalize() normalizes the path part of url,
 * eliminating "./" and "../" segments, and extracts
 * its components into properties of url.
 */
URL.prototype.normalize = function() {
  var path = this.path;
  while (URL.dotReplacePattern.test(path)) {
    path = path.replace(URL.dotReplacePattern, "/");
  }
  path = path.replace(URL.dotEndReplacePattern, "/")
  while (URL.dotDotReplacePattern.test(path)) {
    path = path.replace(URL.dotDotReplacePattern, "/");
  }
  path = path.replace(URL.dotDotEndReplacePattern, "/")
  this.path = path;
  this.asString = this.schema;
  this.asString += this.authority;
  this.asString += this.path;
  this.asString += this.query;
  this.fileString = this.asString;
  this.asString += this.fragment;
  this.directory = URL.directoryPattern.exec(this.path)[1];
  this.pageID = this.path + this.query;
  this.server = this.schema + this.authority;
};
/**
 * urlObj.resolve(relStr) returns a url object for relStr,
 * which is is resolved relative to urlObj.
 */
URL.prototype.resolve = function(rel) {
  var mergePaths = true;
  var newURL = new URL(rel);
  newURL.base = this;
  if (newURL.schema.length <= 0) {
    newURL.schema = this.schema;
  } else {
    mergePaths = false;
  }
  if (newURL.authority.length <= 0) {
    if (mergePaths) {
      newURL.authority = this.authority;
    }
  } else {
    mergePaths = false;
  }
  if (!URL.absolutePattern.test(newURL.path)) {
    if (newURL.path.length <= 0) {
      if (mergePaths) {
        newURL.path = this.path;
        if (newURL.query.length <= 0) {
          newURL.query = this.query;
        }
      }
    } else if (mergePaths) {
      newURL.path = this.directory + newURL.path;
    }
  }
  newURL.normalize();
  return newURL;
};
/**
 * urlObj.toString() returns the string form of urlObj.
 *
 */
URL.prototype.toString = function() {
  return this.asString;
};
/**
 * urlObj.getFileURL() returns a url object for the file
 * part of urlObj.
 * The file part is the entire url except for the
 * fragment part.
 */
URL.prototype.getFileURL = function() {
  return new URL(this.fileString);
};
/**
 * urlObj.getIdentifier() returns the identifier part of
 * urlObj as a string.
 * The identifier part is the path plus the fragment.
 */
URL.prototype.identifier = function() {
  return this.path + this.fragment;
};
/**
 * urlObj.createDescription() returns a DOM table element
 * that shows the various properties of urlObj.
 */
URL.prototype.createDescription = function() {
  var description = document.createElement("div");
  description.appendChild(DOM.createElement("h4", this.stringSource));
  if (this.base) {
    description.appendChild(DOM.createElement("p", "base = " + this.base));
  }
  var table = document.createElement("table");
  table.appendChild(DOM.createTableRow(this, "schema"));
  table.appendChild(DOM.createTableRow(this, "authority"));
  table.appendChild(DOM.createTableRow(this, "path"));
  table.appendChild(DOM.createTableRow(this, "query"));
  table.appendChild(DOM.createTableRow(this, "fragment"));
  table.appendChild(DOM.createTableRow(this, "asString"));
  table.appendChild(DOM.createTableRow(this, "directory"));
  table.appendChild(DOM.createTableRow(this, "pageID"));
  description.appendChild(table);
  return description;
};

