// --------------------- Ajax Chat Class (c) fijiwebdesign.com -------------------------------------------

var _ms_XMLHttpRequest_ActiveX = ""; // Holds type of ActiveX to instantiate for differing browsers
var _ajax;                           // Reference to a global XMLHTTPRequest object 
var _logger = false;                 // write output to the Activity Log false on startup
var _status_area;                    // will point to the area to write status messages to

BASE_URL = ".";
if ( document.location.href.indexOf("www.yourdomain.com") > 0 ) {
	BASE_URL = "/projects";
}

if (!window.Node || !window.Node.ELEMENT_NODE) {
    var Node = { ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5,
                  ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10,
    		  DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12 };
}

/**
* From prototype.js @ www.conio.net | Returns an object reference to one or more strings
* ignore the fact that there are no arguments to this method -- javascript doesn't care how many you send (not strongly typed)
* The method checks the actual # of arguments -- returns a single object or an array
*/
function $() {
    var elements = new Array();

    for (var i = 0; i < arguments.length; i++) {
        var element = arguments[i];

        if (typeof element == 'string')
            element = document.getElementById(element);

        if (arguments.length == 1)
            return element;

        elements.push(element);
    }

    return elements;
}

/**
* Method to get text from an XML DOM object
*/
function getTextFromXML( oNode, deep ) {
    var s = "";
    var nodes = oNode.childNodes;

    for (var i = 0; i < nodes.length; i++) {
        var node = nodes[i];

        if (node.nodeType == Node.TEXT_NODE) {
            s += node.data;
        } else if (deep == true && (node.nodeType == Node.ELEMENT_NODE || node.nodeType == Node.DOCUMENT_NODE
                                       || node.nodeType == Node.DOCUMENT_FRAGMENT_NODE)) {
            s += getTextFromXML(node, true);
        };
    }

    ;
    return s;
}

;

/**
* If you plan on doing anything outside of North America, then you'd better encode the things you pass back and forth
* the escape() method in Javascript is deprecated -- should use encodeURIComponent if available
*/
function encode( uri ) {
    if (encodeURIComponent) {
        return encodeURIComponent(uri);
    }
    if (escape) {
        return escape(uri);
    }
}

/**
* Decodes URI
*/
function decode( uri ) {
    uri = uri.replace(/\+/g, ' ');

    if (decodeURIComponent) {
        return decodeURIComponent(uri);
    }
    if (unescape) {
        return unescape(uri);
    }
    return uri;
}



/**
* log information to the status area textfield
*/
function logger( text, clear ) {
    if (_logger) {
        if (!_status_area) {
            _status_area = document.getElementById("status_area");
        }

        if (_status_area) {
            if (clear) {
                _status_area.value = "";
            }

            var old = _status_area.value;
            _status_area.value = text + ((old) ? "\r\n" : "") + old;

        }
    }
}


/**
 * Executes the returned text from the xmlHttp Requrest 
 * If the function executeReturnExt() exists it will be executed instead
 */
function executeReturn( AJAX ) {
    if (AJAX.readyState == 4) {
        if (AJAX.status == 200) {
            logger('AJAXRequest is complete: ' + AJAX.readyState + "/" + AJAX.status + "/" + AJAX.statusText);
	    if ( AJAX.responseText ) {	
		    logger(AJAX.responseText);
		    if (!executeReturnExt) {
		    	eval(AJAX.responseText); // evaluate returned text
			} else {
				executeReturnExt(AJAX); // execute custom function
			}
	    }
	}
    }
}

/**
* Converts HTML to HTML Entities
*/
function html2entities(str){
  //alert(str);
    var newstr;
    newstr = str.replace("/</", "&lt;");
    newstr = str.replace("/>/", "&gt;");
    newstr = str.replace("/\"/", "&quot;");
    newstr = str.replace("/'/", "&#039;");
    newstr = str.replace("/&/", "&amp;");
    return newstr;

}
var ix = 0;
var responseXML = new Array();

/**
* views any recieved xml Response (debugging)
*/
function viewResp(id) {
    return responseXML[id];
}

/**
* Encapsulated xmlHttpRequest Object
*/
function AJAXRequest( method, url, data, process, async, dosend) {
    // self = this; creates a pointer to the current function
    // the pointer will be used to create a "closure". A closure
    // allows a subordinate function to contain an object reference to the
    // calling function. We can't just use "this" because in our anonymous
    // function later, "this" will refer to the object that calls the function
    // during runtime, not the AJAXRequest function that is declaring the function
    // clear as mud, right?
    // Java this ain't

    var self = this;

    // check the dom to see if this is IE or not
    if (window.XMLHttpRequest) {
	// Not IE
        self.AJAX = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
	// Hello IE!
        // Instantiate the latest MS ActiveX Objects
        if (_ms_XMLHttpRequest_ActiveX) {
            self.AJAX = new ActiveXObject(_ms_XMLHttpRequest_ActiveX);
        } else {
	    // loops through the various versions of XMLHTTP to ensure we're using the latest
	    var versions = ["Msxml2.XMLHTTP.7.0", "Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP",
                        "Microsoft.XMLHTTP"];

            for (var i = 0; i < versions.length ; i++) {
                try {
		    // try to create the object
		    // if it doesn't work, we'll try again
		    // if it does work, we'll save a reference to the proper one to speed up future instantiations
                    self.AJAX = new ActiveXObject(versions[i]);

                    if (self.AJAX) {
                        _ms_XMLHttpRequest_ActiveX = versions[i];
                        break;
                    }
                }
                catch (e) {
                // trap; try next one
                } ;
            }

            ;
        }
    }

    // if no callback process is specified, then assing a default which executes the code returned by the server
    if (typeof process == 'undefined' || process == null) {
        process = executeReturn;
    }

    self.process = process;

    // create an anonymous function to log state changes
    self.AJAX.onreadystatechange = function( ) {
        //logger("AJAXRequest Handler: State =  " + self.AJAX.readyState);
        self.process(self.AJAX);
    }

    // if no method specified, then default to POST
    if (!method) {
        method = "POST";
    }

    method = method.toUpperCase();

    if (typeof async == 'undefined' || async == null) {
        async = true;
    }

    logger("----------------------------------------------------------------------");
    logger("AJAX Request: " + ((async) ? "Async" : "Sync") + " " + method + ": URL: " + url + ", Data: " + data);

    self.AJAX.open(method, url, async);

    if (method == "POST") {
        self.AJAX.setRequestHeader("Connection", "close");
        self.AJAX.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        self.AJAX.setRequestHeader("Method", "POST " + url + "HTTP/1.1");
    }

    // if dosend is true or undefined, send the request
    // only fails is dosend is false
    // you'd do this to set special request headers
    if ( dosend || typeof dosend == 'undefined' ) {
	    if ( !data ) data="";
	    self.AJAX.send(data);
    }
    return self.AJAX;
}

//---------------------------------- event listerner ----------------------------//

/**
* Simple event listerner to attach fns to and call when needed
* features: Add multiple events per listerner obj, remove events, make arguments accessible from event
* todo: Add events execution ordering, remove all this instantiations of objects and arrays.. oilei
*/
function ajcEventsListerner() {
	this.fns = new Object();
	this.errors = new Object();
	this.onError = new Object();
	this.args = new Object();
	this.callback = new Array();
}

/**
* Add an event to the eventListerner
* @param string event name
* @param function A function to run when an error occurs
*/
ajcEventsListerner.prototype.addEvent = function(event) {
	this.fns[event] = new Array();
	this.args[event] = new Array();
}

/**
* Set the callback for the event
* @param string event name
* @param function callback
*/
ajcEventsListerner.prototype.setCallback = function(event, fn) {
	this.callback[event] = fn;
}

/**
* Create a new error listerner for an event
* @param string event name
* @param function A function to run when an error occurs
*/
ajcEventsListerner.prototype.newOnError = function(event, fn) {
	if (typeof(fn) == 'function') this.onError[event] = fn;
}

/**
* Adds a function to be called when the appropriate event occurs
* @param function Function to execute 
* @param string Event name
* @param string Function name. If it exists it will be overwritten. 
*/
ajcEventsListerner.prototype.addListerner = function(event, fn, name) {
	this.newErrorHandler(event);
	if (typeof(fn) == 'function') {
		try {
			this.fns[event][name] = fn;
		}  catch(e) {
			this.newError(event, "1st Param, "+event+", is not a valid event Object. \r\nException: "+e); 
		}
	} else {
		this.newError(event, '2nd Param is not a Function'); 
	}
	return this.handleErrors(event);
}

/**
* Removes a Function (event listerner) from the specified event
* @param string Event name
* @param string Event Listerner Function Name
*/
ajcEventsListerner.prototype.removeListerner = function(event, name) {
	this.newErrorHandler(event);
	if (typeof(fn) == 'function') {
		try {
			this.fns[event][name] = function() { /* do nothing but keep reference */ };
		}  catch(e) {
			this.newError(event, "First Param, "+event+", is not a valid event Object. \r\nException: "+e); 
		}
	} else {
		this.newError(event, '2nd Param, event Listerner Name, does not reference an attached event listerner.'); 
	}
	return this.handleErrors(event);
}

/**
* Execute the event listerners for a particular event
* @param string event name
*/
ajcEventsListerner.prototype.exec = function(event) { 
	this.newErrorHandler(event);
	if ((n = arguments.length) > 1) {
		for (var i=1; i < n; i++) { 
			this.args[event][i-1] = arguments[i]; // save arguments ref
		}
	}
	if (typeof(this.fns[event]) == 'object') {
		if (propertyCount(this.fns[event] ) > 0) {
			for (x in this.fns[event]) {
				if (typeof(this.fns[event][x]) == 'function') {
					try {
						if (typeof(this.callback[event]) == 'function') {
							this.callback[event](this.fns[event][x], this.args[event]);
						} else {
							this.fns[event][x]( this.args[event]);
						}
					} catch(e) { alert(e);
						this.newError(event, e);
					}
				} else {
					this.newError(event, x+' is not a function'); 
				}
			}
		} else { 
			this.newError(event, 'Event, '+event+', has no handlers.');
		}
	} else {
		this.newError(event, 'Event, '+event+', is not a valid event Object.');
	}
	return this.handleErrors(event, true);
	
}

/**
* Add a new error handler for an event handler
* @param string event name
*/
ajcEventsListerner.prototype.newErrorHandler = function(event) {
	this.errors[event] = new Array();
}

/**
* Adds an Error
* @param string event name
* @param string error msg 
*/
ajcEventsListerner.prototype.newError = function(event, e) {
	this.errors[event][this.errors[event].length] = e;
}

/**
* Handles Errors
* @param string event name
*/
ajcEventsListerner.prototype.handleErrors = function(event, onerror) { 
	if (this.errors[event].length > 0) {
		for (e in this.errors[event]) {
			_debug(this.errors[event][e]);
		}
		if (onerror) this.onError[event]();
		return false;
	} else {
		return true;
	}
}

// ----------------------------- obj debug ----------------------------------- //

/**
* Alerts the properties of an obj. Used for debugging objects
* @param object Object to debug
* @param string object name
*/
function alertObj(obj, name) { 
	if (typeof(obj) == 'object') {
		for (x in obj) {
			alertObj(obj[x], x);
		}
	} else {
		_debug(name+'=>'+obj);
	}
}

/**
* Counts the properties of an obj. Slower but seems to be the only method that works on every object
* @param object Object to count properties of
*/
function propertyCount(obj) {
	var i = 0;
	for (x in obj) { i++; }
	return i;
}
		
