
// Provides base functions for using AJAX

// Cross browser compatible creation of XmlHttpRequest object
function createRequest()
{
	var request;

	// IE
	try
	{
		request = new ActiveXObject("Msxml2.XMLHTTP");
	}
	catch(e)
	{
		try
		{
			request = new ActiveXObject("Microsoft.XMLHTTP");
		} 
		catch(oc)
		{
			request = null;
		}
	}
	
	// Mozilla
	if(!request && typeof XMLHttpRequest != "undefined") 
	{
		request = new XMLHttpRequest();
	}
	
	return request;
}

// Fires a callback, sending data to page, with the response handled by handler
function submitCallback(data, page, handler)
{
	var request = createRequest();
	if(request)
	{
		if(typeof ajaxRelativePrefix != 'undefined')
		{
			// prefix page with relative path prefix var that was inserted by MatrixPage
			page = ajaxRelativePrefix + page;

			// prepare request
			request.open('POST', page, true);
			request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

			// when the request is answered, relayResponse() will be called
			request.onreadystatechange = function(){ relayResponse(request, handler); };

			// fire off the request
			request.send(data);
		}
		else
		{
			// relative path var not found, cannot correctly set path to AJAX page
			handler(false, 'Relative prefix not found (be sure to use Page.HasAjax).');
		}	
	}
	else
	{
		// request object wasn't instantiated
		handler(false, 'Unable to create request object.');
	}
}

// Passes response data from a callback to the handler function
function relayResponse(request, handler)
{
    var responseText;
    var index;
	if(request.readyState == 4)
	{
		if(request.status == 200){
			responseText = request.responseText;
			
			// ugly hack for international pages adding extra HTML
			index = responseText.indexOf("<!DOCTYPE");
			if(index != -1)
			{
				responseText = responseText.substring(0, index);
			}
			
			// success, give response text to handler function
			handler(true, responseText);
		}
		else
		{
			// page returned error code (such as 404)
			handler(false, 'Ajax page returned error code ' + request.status + '.');
		}
	}
}


// designed to be used as a singleton object, so you probably shouldn't inherit from this guy
var AJAX = function (){
    return {
        currentVisiblePopup: null,
        isPopupLocked: false,
        
        // Cross browser compatible creation of XmlHttpRequest object
        createRequest: function(){
            var request;

            // IE
            try{
	            request = new ActiveXObject("Msxml2.XMLHTTP");
            }
            catch(e){
	            try{
		            request = new ActiveXObject("Microsoft.XMLHTTP");
	            } 
	            catch(oc){
		            request = null;
	            }
            }
        	
            // Mozilla
            if(!request && typeof XMLHttpRequest != "undefined") {
	            request = new XMLHttpRequest();
            }
        	
            return request;
        },
        
        // Fires a callback, sending data to page, with the response handled by handler
        submitCallback: function(data, page, handler){
            var request = this.createRequest();
            if(request){
	            if(typeof ajaxRelativePrefix != 'undefined'){
		            // prefix page with relative path prefix var that was inserted by MatrixPage
		            page = ajaxRelativePrefix + page;

		            // prepare request
		            request.open('POST', page, true);
		            request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

		            // when the request is answered, relayResponse() will be called
		            request.onreadystatechange = function(){ AJAX.relayResponse(request, handler); };

                    AJAX.isPopupLocked = true;
		            // fire off the request
		            request.send(data);
	            }
	            else{
		            // relative path var not found, cannot correctly set path to AJAX page
		            handler(false, 'Relative prefix not found (be sure to use Page.HasAjax).');
	            }	
            }
            else{
	            // request object wasn't instantiated
	            handler(false, 'Unable to create request object.');
            }
        },
        
        // Passes response data from a callback to the handler function
        relayResponse: function (request, handler){
            var responseText;
            var index;
            if(request.readyState == 4){
	            if(request.status == 200){
		            responseText = request.responseText;
        			
		            // ugly hack for international pages adding extra HTML
		            index = responseText.indexOf("<!DOCTYPE");
		            if(index != -1){
			            responseText = responseText.substring(0, index);
		            }
        			
		            // success, give response text to handler function
		            AJAX.isPopupLocked = false;
		            handler(true, responseText);
	            }
	            else{
		            // page returned error code (such as 404)
		            AJAX.isPopupLocked = false;
		            handler(false, 'Ajax page returned error code ' + request.status + '.');
	            }
            }
        },
        hidePopups: function () {
            if (this.currentVisiblePopup && !AJAX.isPopupLocked) {
	            this.currentVisiblePopup.hide();
	        }
	    }
    };
} ();

// the base object of all AJAX widgets.  These will most likely be comprised of DIVs
AJAX.Popup = function ()
{
    var cancelBubble = false;
    document.onclick = function(){
            if(!window.ie) { // right now IE doesn't support this because they don't give us the arguments we need.
                var shadowBox = CB.e('shadowAJAX');
                if (!shadowBox || !shadowBox.lastAnchor || shadowBox.lastAnchor.id != arguments[0].target.id) {
                    AJAX.hidePopups();
                }
            }
            else{
                if(!cancelBubble){
                    AJAX.hidePopups()
                }
                else{
                    cancelBubble = false;
                }
            }
        };
	// find X coordinate of an element:  anchor - the element we are trying to position relative to, element - the element we are positioning
	function findX(anchor, element)	{
	    // we need to be really intelligent about what we spit back out.
		var curleft = 0;
		var leftcenter = anchor.offsetWidth / 3;
		var rightcenter = anchor.offsetWidth - leftcenter;
		if(anchor.offsetParent){
			while(anchor.offsetParent){
				curleft += anchor.offsetLeft;
				anchor = anchor.offsetParent;
			}
		}
		else if(anchor.x){
			curleft += anchor.x;
		}
		if (curleft > window.getWidth() / 2) {
		    // we are on the right side of the window
		    return (curleft + rightcenter) - (element.scrollWidth);
		}
		else {
		    // we are on the left side of the window
		    return curleft + leftcenter;
		}
	}

	// find Y coordinate of an element
	function findY(anchor, element)	{
		var curtop = 0;
		if(anchor.offsetParent){
			while(anchor.offsetParent){
				curtop += anchor.offsetTop;
				anchor = anchor.offsetParent;
			}
		}
		else if(anchor.y){
			curtop += anchor.y;
		}
		
		// using a hard number feels like a hack, but it should serve us well
		// I used to use anchor.offsetHeight, but it counts padding and so is inconsistent
		if (!window.ie) {
		    if (curtop + 20 + element.offsetHeight > window.getScrollHeight()) {
		        return window.getScrollHeight() - element.offsetHeight;
		    }
		    else {
		        return curtop + 20;
		    }
		}
		else {
		    if (curtop + 15 + element.offsetHeight > window.getScrollHeight()) {
		        return window.getScrollHeight() - element.offsetHeight;
		    }
		    else {
		        return curtop + 15;
		    }
		}
	}
	
	function anchorOnClick(currentFunction){
	    return function(eventObj){
	        currentFunction();
	        eventObj.cancelBubble = true;
	    }
	}
	
    return {
        containerElm: null,
        parentContainer: null,
    
        initialize: function(container){
            // do some initialization here
            this.containerElm = container;
            if (container) {
                this.parentContainer = container.parentNode;
            }
        },
    
        // options is an optional variable where you can stuff any desired parameters for onShow
        show: function(anchor, options){
            // we have to get our shadow
            var shadowBox = CB.e('shadowAJAX');
            var diff,i;
            if (!shadowBox) {
                shadowBox = document.createElement("div");
                shadowBox.className = 'cb_style shadowing';
                shadowBox.style.textAlign = 'left';
                shadowBox.id = 'shadowAJAX';
                shadowBox.onclick = function(eventObj){if(eventObj){eventObj.cancelBubble = true;}else{cancelBubble=true;}};
                document.body.appendChild(shadowBox);
            }
            
            if (this.containerElm  && !AJAX.isPopupLocked) {
                if (anchor) {
                    // if we have an anchor, then we can position based on it
                    //  otherwise, just use our current position
                    anchor.blur();
                    shadowBox.x = findX(anchor, this.containerElm);
                    shadowBox.y = findY(anchor, this.containerElm);
                    shadowBox.style.left = shadowBox.x + 'px';
                    shadowBox.style.top = shadowBox.y + 'px';
                    shadowBox.lastAnchor = anchor;
                    if (window.ie){cancelBubble = true;}
                }
                
                // now we need to hide all the other windows
                AJAX.hidePopups();
                if (window.hideCurrentPopup) {   // this will close windows in the other popup management system
                    hideCurrentPopup();
                }
                this.onShow(options);
                shadowBox.appendChild(this.containerElm);
                
                if (!window.ie) {  // we are not in IE
                    shadowBox.style.height = (this.containerElm.scrollHeight + 1) + 'px';
                    shadowBox.style.width = (this.containerElm.scrollWidth + 4) + 'px';
                }
                else {  // we are in IE
                    shadowBox.style.height = (this.containerElm.scrollHeight + 6) + 'px';
                    shadowBox.style.width = (this.containerElm.scrollWidth + 6) + 'px';
                }
                this.containerElm.style.visibility = 'visible';
                shadowBox.style.visibility = 'visible';
                AJAX.currentVisiblePopup = this;
                diff = (shadowBox.offsetHeight + shadowBox.y - window.getScrollTop()) - window.getHeight();
                for (i = 0; i < diff; i += 8){
                    document.documentElement.scrollTop = window.getScrollTop() + 8;
                }
            }
        },
        
        onShow: function(anchor, options) {
            // this function is designed to be overridden  (technically an onBeforeShow)
            // so if you have any special processing that happens when your div is opened, then
            // change onShow so that it points to a different/custom function
        },
        
        hide: function() {
            var shadowBox = CB.e('shadowAJAX');
            if (shadowBox) {
                shadowBox.style.visibility = 'hidden';
            }
            
        	if (this.containerElm.style.visibility != 'hidden') {
	            this.containerElm.style.visibility = 'hidden';
	            this.parentContainer.appendChild(this.containerElm);
	            this.onHide();
	        }
        },
        
        onHide: function() {
            // this function is designed to be overridden  (technically an onAfterShow)
            // so if you have any special processing that happens when your div is closed, then
            // change onHide so that it points to a different/custom function
        }
        
        // TODO: have a max height and width variable to check against and use that to determine scrolling
        // TODO: make the close button an option that automatically gets painted.  localization concerns?
        // TODO: if the window resizes, then reposition yourself (this may require us to hold onto the anchor's coordinates)
    };
} ();