/*
 * $Id: xbXMLHttpRequest.js,v 1.7 2003/09/14 21:22:27 bc Exp $
 *
 */

//
// implements a wrapper class meant to support Mozilla's XMLHttpRequest
// and Microsoft's XMLHttpRequest objects.

/* ***** BEGIN LICENSE BLOCK *****
 * The contents of this file are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at 
 * http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Bob Clary code.
 *
 * The Initial Developer of the Original Code is
 * Bob Clary.
 * Portions created by the Initial Developer are Copyright (C) 2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s): Bob Clary <http://bclary.com/>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 ***** END LICENSE BLOCK ***** */

_classes.registerClass('xbXMLDOMParseError');

function xbXMLDOMParseError(errorCode, filePos, line, linepos, reason, srcText, url)
{
  _classes.defineClass('xbXMLDOMParseError', _prototype_func);
  
  this.init(errorCode, filePos, line, linepos, reason, srcText, url);
  
  function _prototype_func()
  {
    xbXMLDOMParseError.prototype.init = 
    function init(errorCode, filePos, line, linepos, reason, srcText, url)
    {
      xbDEBUG.dump('xbXMLDOMParseError::init');
      this.parentMethod('init');
      this.errorCode  = errorCode;
      this.filePos    = filePos;
      this.line       = line;
      this.linepos    = linepos;
      this.reason     = reason;
      this.srcText    = srcText;
      this.url        = url;
    }

    xbXMLDOMParseError.prototype.toString = 
    function toString()
    {
      var s = 'parseError:' +
          ' errorCode=' + this.errorCode +
          ' filePos='   + this.filePos +
          ' line='      + this.line +
          ' linepos='   + this.linepos +
          ' reason='    + this.reason +
          ' srcText='   + this.srcText +
          ' url='       + this.url;
      return s;
    }

  }
}

_classes.registerClass('xbXMLHttpRequest');

function xbXMLHttpRequest()
{
  _classes.defineClass('xbXMLHttpRequest', _prototype_func);
  
  this.init();
  
  function _prototype_func()
  {
    xbXMLHttpRequest.prototype.init = init;
    function init()
    {
      xbDEBUG.dump('xbXMLHttpRequest::init');
      var eError;
      
      this.parentMethod('init');
      this.parseError = new xbXMLDOMParseError(0, 0, 0, 0, '', '', '');

      this.progId = null;
      this.url = '';
      this.document = null;
      
      if (typeof(XMLHttpRequest) != 'undefined')
      {
        this.progId = 'Mozilla.XMLHTTP';
        this._native = new XMLHttpRequest();
      }
      else
      {
        try
        {
          this.progId = 'Msxml2.XMLHTTP.4.0';
          this._native = new ActiveXObject(this.progId);
        }
        catch (eError)
        {
          try
          {
            this.progId = 'Msxml2.XMLHTTP';
            this._native = new ActiveXObject(this.progId);
          }
          catch (eError)
          {
          }
        }
      }

      this._onload = this._onerror = null;
    }
    
    // ie and mozilla
    xbXMLHttpRequest.prototype.abort = abort;
    function abort()
    {
      xbDEBUG.dump('xbXMLHttpRequest::abort');
      this._native.abort();
    }
    
    // ie and mozilla
    xbXMLHttpRequest.prototype.getAllResponseHeaders = getAllResponseHeaders;
    function getAllResponseHeaders()
    {
      xbDEBUG.dump('xbXMLHttpRequest::getAllResponseHeaders');
      return this._native.getAllResponseHeaders();
    }
    
    // ie and mozilla
    xbXMLHttpRequest.prototype.getResponseHeader = getResponseHeader;
    function getResponseHeader(header)
    {
      xbDEBUG.dump('xbXMLHttpRequest::getResponseHeader("' + header + '")');
      return this._native.getReponseHeader(header);
    }
    
    // ie and mozilla
    xbXMLHttpRequest.prototype.open = open
    function open(method, url, async, user, password)
    {
      xbDEBUG.dump('xbXMLHttpRequest::open("' + method + '", "' + url + '", "' + async + '", "' + user + '", "' + password + '")');
      
      var eError;
      
      if (!async) async = false;
      
      //bc: added 2/14/01 to force absolute paths
      if (url && !url.match(/^http[s]?:\/\//))
      {
        url = getCurrentDirectory() + url;
        xbDEBUG.dump('adjusting relaive url');
        xbDEBUG.dump('xbXMLHttpRequest::open("' + method + '", "' + url + '", "' + async + '", "' + user + '", "' + password + '")');
      }
        
      this.url = url;
      
      try
      {
        if (typeof(XMLHttpRequest) != 'undefined')
          netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");      

        if (arguments.length == 2 || arguments.length == 3)
          this._native.open(method, url, async);
        else 
          this._native.open(method, url, async, user, password);
      }
      catch(eError)
      {
        if (xbDEBUG.on)
        {
           var dbgProp;
           for (dbgProp in eError)
           {
             xbDEBUG.dump('Exception in xbXMLHttpRequest.js = ' + eError);
             xbDEBUG.dump(dbgProp + ' = ' + eError[dbgProp]);
           }
        }
        throw new xbException('Unknown Error', 'xbXMLHttpRequest.js', 'xbXMLHttpRequest::open', eError);
      }
    }
    
    // mozilla
    xbXMLHttpRequest.prototype.openRequest = open;
    
    // ie and mozilla
    xbXMLHttpRequest.prototype.send = send;
    function send(data)
    {
      var eError;
      
      if (!data) data = null;
      xbDEBUG.dump('xbXMLHttpRequest::send()');
      try
      {
        this._native.send(data);
      }
      catch(eError)
      {
        throw new xbException('Error', 'xbXMLHttpRequest.js', 'xbXMLHttpRequest::send', eError);
      }
    }
    
    // ie and mozilla
    xbXMLHttpRequest.prototype.setRequestHeader = setRequestHeader;
    function setRequestHeader(header, value)
    {
      xbDEBUG.dump('xbXMLHttpRequest::setRequestHeader("' + header + '", "' + value + '")');
      this._native.setRequestHeader(header, value);
    }
    
    // mozilla
    xbXMLHttpRequest.prototype.addEventListener = addEventListener;
    function addEventListener(type, listener)
    {
      xbDEBUG.dump('xbXMLHttpRequest::addEventListener("' + type + '", "' + listener + '")');
      switch(type)
      {
      case 'load':
        this.setOnLoad(listener);
        break;
      case 'error':
        this.setOnError(listener);
        break;
      }
    }
    
    // mozilla
    xbXMLHttpRequest.prototype.removeEventListener = removeEventListener;
    function removeEventListener(type, listener)
    {
      xbDEBUG.dump('xbXMLHttpRequest::removeEventListener("' + type + '", "' + listener + '")');
      switch(type)
      {
      case 'load':
        this.setOnLoad(null);
        break;
      case 'error':
        this.setOnError(null);
        break;
      }
    }
    
    // wrapper functions
    
    xbXMLHttpRequest.prototype.getOnLoad = getOnLoad;
    function getOnLoad()
    {
      xbDEBUG.dump('xbXMLHttpRequest::getOnLoad()');
      return this._onload;
    }
    
    // for the setOnLoad and setOnError methods, IE
    // requires a means of binding the xbXMLHttpRequest_onchange method
    // to the correct object, otherwise IE will call it
    // in the window context and not the object context...
    // that is what objName is for.
    xbXMLHttpRequest.prototype.setOnLoad = setOnLoad;
    function setOnLoad(onload, objName)
    {
      xbDEBUG.dump('xbXMLHttpRequest::setOnLoad("' + onload + '", "' + objName + '")');
      this._onload = onload;
      if (typeof(XMLHttpRequest) != 'undefined')
        this._native.onload = onload;
      else if (typeof(this._native.onreadystatechange) != 'object')
        this._native.onreadystatechange = new Function ("xbXMLHttpRequest_onchange(" + objName + ")");
    }
    
    xbXMLHttpRequest.prototype.getOnError = getOnError;
    function getOnError()
    {
      xbDEBUG.dump('xbXMLHttpRequest::getOnError()');
      return this._onerror;
    }
    
    xbXMLHttpRequest.prototype.setOnError = setOnError;
    function setOnError(onerror, objName)
    {
      xbDEBUG.dump('xbXMLHttpRequest::setOnError("' + onerror+ '", "' + objName + '")');
      this._onerror = onerror;
      if (typeof(XMLHttpRequest) != 'undefined')
        this._native.onerror = onerror;
      else if (typeof(this._native.onreadystatechange) != 'object')
        this._native.onreadystatechange = new Function ("xbXMLHttpRequest_onchange(" + objName + ")");
    }
    
    xbXMLHttpRequest.prototype.getResponseXML = getResponseXML;
    function getResponseXML()
    {
      xbDEBUG.dump('xbXMLHttpRequest::getResponseXML()');
      var eError;
      
      if (this.document)
        return this.document;
        
      this.document = this._native.responseXML;
      if (this.document.documentElement != null)
        return this.document;
        
      if (typeof(XMLHttpRequest) != 'undefined')
      {
        try
        {
          // this is just a stub until Mozilla implements responseText...
          this.document = document.implementation.createDocument(null, '', null);
          if (hasProperty(this._native, 'responseText'))
          {
            var domParser = new DOMParser()
            this.document = domParser.parseFromString(this._native.responseText, 'text/xml');
          }
          else
          {
            throw '';
          }
        }
        catch(eError)
        {
          this.parseError = new xbXMLDOMParseError(-1, -1, -1, -1, 'Unable to Load Document', '', this.url)
        }
      }
      
      if (typeof(ActiveXObject) != 'undefined')
      {
        this.document = new ActiveXObject('MSXML.DOMDocument');
        this.document.loadXML(this._native.responseText);
      }

      return this.document;
    }
    
    xbXMLHttpRequest.prototype.getStatus = getStatus;
    function getStatus()
    {
      xbDEBUG.dump('xbXMLHttpRequest::getStatus()');
      return this._native.status;
    }
    
    xbXMLHttpRequest.prototype.getStatusText = getStatusText;
    function getStatusText()
    {
      xbDEBUG.dump('xbXMLHttpRequest::getStatusText()');
      return this._native.statusText;
    }

    xbXMLHttpRequest.prototype.getParseError = getParseError;
    function getParseError()
    {
      xbDEBUG.dump('xbXMLHttpRequest::getParseError()');
      if (!this.document)
        this.document = this.getResponseXML();

      if (!this.document)
        throw new xbException('Document not loaded: ' + this.url, 'xbXMLHttpRequest.js', 'xbXMLHttpRequest::getParseError');

      if (typeof(ActiveXObject) != 'undefined')
      {
        this.parseError.errorCode = this.document.parseError.errorCode;
        this.parseError.filePos   = this.document.parseError.filePos;
        this.parseError.line      = this.document.parseError.line;
        this.parseError.linepos   = this.document.parseError.linepos;
        this.parseError.reason    = this.document.parseError.reason;
        this.parseError.srcText   = this.document.parseError.srcText;
        this.parseError.url       = this.document.parseError.url;
        return this.parseError;
      }
              
      if (typeof(XMLHttpRequest) != 'undefined')
      {
        var errorCode = 0;
        var filePos   = 0;
        var errorLine = 0;
        var errorCol  = 0;
        var errorMsg  = '';
        var srcText   = '';
            
        if (!this.document.documentElement)
        {
          errorMsg = 'Document not Loaded';
          errorLine = -1;
          errorCol  = -1;
        }
        else
        {
          var errorElement = null;
          var errorList;
          
          errorList = this.document.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'parsererror');
          errorElement = errorList.item(0);
          
          if (errorElement)
          {
            errorCode   = -1;
            filePos     = -1;
            errorMsg  = errorElement.firstChild.data;
            errorLine  = parseInt(errorMsg.substring(errorMsg.indexOf('Line Number') + 11));
            errorCol  = parseInt(errorMsg.substring(errorMsg.indexOf('Column') + 6));
            srcText    = this.document.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'sourcetext').item(0).firstChild.data
            
            srcText = srcText.replace(/&/g, '&amp;');
            srcText = srcText.replace(/</g, '&lt;');
            srcText = srcText.replace(/>/g, '&gt;');
          }
        }

        this.parseError.errorCode  = errorCode;
        this.parseError.filePos    = filePos;
        this.parseError.line    = errorLine;
        this.parseError.linepos    = errorCol;
        this.parseError.reason    = errorMsg;
        this.parseError.srcText    = srcText;
        this.parseError.url      = this.url;

        return this.parseError;
      }

      throw new xbException('Unsupported Browser', 'xbXMLHttpRequest.js', 'xbXMLHttpRequest::getParseError');
      return null;
    }
  }
}

function xbXMLHttpRequest_onchange(obj)
{
  if (typeof(obj) == 'undefined' || obj == null)
    return;
    
  // ie fires this onchange handler every time that
  // an alert pops up. do this to keep it from being
  // called while it is already running.
  
  if (typeof(obj._inonchange) != 'undefined')
    return;
    
  obj._inonchange = true;
  
  if (obj._native.readyState == 4 ) // loaded  
  {
    if (obj._native.status == 200 && obj._native.responseXML.parseError.errorCode != 0)
    {
      if (obj._onload)
      {
        obj._onload();
        // IE fires this event more than once... turn it off after the first
        obj._onload = null;
      }
    }
    else
    {
      if (obj._onerror)
      {
        obj._onerror();
        // IE fires this event more than once... turn it off after the first
        obj._onerror = null;
      }
    }
  }
}


// eof: xbXMLHttpRequest.js
