/*


    Copyright (c) 2009, David R. Thorsrud
    All Rights Reserved
  
    Licensing information available through http://www.thorsrud.com/licensing
    
    This javascript library is intended to create a lightweight alternative to
    industry standards like Prototype & Scriptaculous or Dojo specifically for 
    use with ThorsrudCM.  Since ThorsrudCM has a specific server-side architecture 
    and because the solution is meant to improve the capabilities of content owners
    rather than web developers, these libraries are not intended to be as extensible 
    as Prototype or Dojo.  Rather, this code should reflect AJAX, JSON and other 
    modern web development practices but with a focus on optimal performance for 
    ThorsrudCM and tighter integration in an architecture that also includes IIS 
    .NET components and a SQL Server database.
    
    
*/

if (typeof (thor) == "undefined"){

    function tfn_Exists(name, object){ 
        return (typeof (object || this)[name] == "undefined"); 
    }

    var Class = {
      create: function() {
        return function() {
          this.initialize.apply(this, arguments);
        }
      }
    }
    
    var thor = {};

    thor.version = {
	major: 0, minor: 1, patch: 0, revision: 1,
	toString: function(){
		    with (thor.version) { return major + "." + minor + "." + patch + " (" + revision + ")"; } 
	        }
    }



    /*
     *********************************************************************************************
     
     OBJECT AND ELEMENT HANDLING
     
     *********************************************************************************************
    */
    
    thor.evalObject = function(name, object, make){
	    if(!object || !name) return undefined; 
    	if(!tfn_Exists(name, object)) return object[name]; 
	    return (make ? (object[name]={}) : undefined);	
    }
    
    thor.get = function(id) {
        return document.getElementById(id);
    }
    thor.getElement = function(parentObj, type, id, class_name, html){
        var element = thor.get(id);
        if (element) //Exists
        {
            //Used for HiddenContent: element.style.visibility = 'visible';
            if (type == "input")
            		element.value = html;
            
            return element;
        }
        else //Create
        {
            element = document.createElement(type);
            element.setAttribute('id', id);
            element.className = class_name;
            if (type != "img" && html.length > 0) {
            	if (type == "input")
            		element.value = html;
            	else 	
            		element.innerHTML = html;
            }
            parentObj.appendChild(element);
        }
        return element;
    }

    

    /*
     *********************************************************************************************
        EVENT HANDLING
     *********************************************************************************************
    */

    thor.addEvent = function(element, call_function, event_name){
    	if (typeof(call_function) == "string")
            call_function = eval(call_function);
        else if (typeof(call_function) != "function")
            call_function = null;
	     
        if (typeof(element) == "string") { element = thor.get(element); }
        
        if (element)
    	{
	    	if (event_name == "Event.CLICK") { element.onclick = call_function; }
	        else if (event_name == "Event.FOCUS") { element.onfocus = call_function; }
	        else if (event_name == "Event.KEYDOWN") { element.onkeydown = call_function; }
	        else if (event_name == "Event.KEYUP") { element.onkeyup = call_function; }   
	        else if (event_name == "Event.KEYPRESS") { element.onkeypress = call_function; }   
	        else if (event_name == "Event.MOUSEDOWN") { element.onmousedown = call_function; }
	        else if (event_name == "Event.MOUSEMOVE") { element.onmousemove = call_function; }
	        else if (event_name == "Event.MOUSEOUT") { element.onmouseout = call_function; }
	        else if (event_name == "Event.MOUSEOVER") { element.onmouseover = call_function; }
	        else if (event_name == "Event.MOUSEUP") { element.onmouseup = call_function; }   
	       
	        if (element.captureEvents) element.captureEvents(eval(event_name));
        } 
    }

    thor.raise = function(desc, error){
        alert(desc + " - " + error); //Keep it simple for now.  No formal Error objects.
    }




    /*
     *********************************************************************************************
    
        IO / AJAX:

     *********************************************************************************************
    */
    
   thor.io = {   };
   
   thor.io.requestQueue = null;
   
   thor.io.getTransport = function() {
            var http = null;
            var error = null;
    		
            //Standard first:
            try{ http = new XMLHttpRequest(); }catch(e){}
    		
		    if(!http)
            {
	            var _XMLHTTP = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
                for(var i=0; i<3; ++i){
		             try{ http = new ActiveXObject(_XMLHTTP[i]); }catch(e){ error = e; }
		        }
		    }
    		
            if(!http)
	            return thor.raise("Dynamic updating of page content disabled.  (XMLHTTP not available with your browser.)", error);
    		
            return http; 
        
    };
    
    /*
    thor.io.request = function() {
            url: "",
            mimetype: "",
            method: "",
            targetElement: "",
            contentDestination: "",
            completionEvents: null,
            keyvalues: function() {},
            http: function() {},
            JSON: false,
            load: function() {}
    };
    */
    
    thor.io.Request = function() {
    	id = "";
    };
    
	thor.io.setRequest = function(url,type){
        
        req = new thor.io.Request(); //Object(); //thor.io.request();
        req.url = url;
		req.type = type;
		req.isProcessing = false;

        if (thor.io.requestQueue == null) thor.io.requestQueue = new Array();
	    thor.io.requestQueue.push(req);
   			
        req.load = function(target_element, content_destination, method, key_values, target_ready, completion_events) { 
            
			target_element.request = req;
			req.targetElement = target_element;
            req.contentDestination = content_destination;
            req.method = method;
            req.targetReady = target_ready;
			req.key_values = key_values;
			req.completionEvents = completion_events;
			req.http = null;
			thor.io.checkAJAXQueue();
        }
        return req;
    };	
	
	thor.io.checkAJAXQueue = function() {
		var req = thor.io.getNextAvailableRequest();
		//Check if request is active...
		if (req) {
			req.http = thor.io.getTransport(); //xmlHttp;
            var env;
			if (req.type == "soap")
			{
              req.http.open("POST", req.url, true);
              /*Soap 1.2*/
			  //Use method and key_values to build SOAP envelope:
              env = thor.io._getSOAP_Envelope(req.method, req.key_values);
              req.http.setRequestHeader("Content-Type", "application/soap+xml"); 
              req.http.setRequestHeader("Content-Length", env.length);
  			}
			else if (req.type == "xml_rpc" || req.type == "JSON")
			{
              req.http.open("POST", req.url, true);
              req.http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 
              env = thor.io._getFormParams(req.method, req.key_values);
			}
			else if (req.type == "template")
			{
			  req.http.open("GET", req.url, true);
              req.http.setRequestHeader("Content-Type", "text/xml"); 
  			}
            
			req.http.onreadystatechange = function(){ thor.io.getAJAXData(); };
          	req.http.send(env);
		}
	}
	
	thor.io.getNextAvailableRequest = function() {
		if (thor.io.requestQueue.length > 0) {
			var request = thor.io.requestQueue[0];		
			if (request.isProcessing)
				return null;
			else
			{
				request.isProcessing = true;
				return request;
			}
		}
		else
			return null;
	}
    thor.io.getAJAXData = function() {
        
        var request = thor.io.requestQueue[0];
        if (request != null && request.http.readyState == 4 && request.targetReady) //animation and loading done...
        {
        	if (request.http.status != 200)
            {
                /*Debugging:*/ 
                alert('http status ' + request.http.status + ': \r\n' + request.http.statusText + '\r\n\r\n' + request.http.responseText);
                request.targetElement.clearPopup(request.id);
            }
            else
            {
                var results = null;
                
                if (request.type == "template")
				{
				 	 var doc = request.http.responseText; //responseXML:  IE and FF bugs and differences make it unusable here.
				 	 request.contentDestination.innerHTML = doc;
				}
				else if (request.type == "xml_rpc" || request.type == "JSON")
				{
			 	 	 var doc = request.http.responseText; 
					 request.contentDestination.innerHTML = doc;
					 //alert(doc);
					 //First version only works in IE:
				}
				else
				{	 
					//CONVERT TO XMLDOM:
  					//responseXML does not work with all browsers....be sure to test...            
                  	//if (!request.http.responseXML)
					/*
  					if (window.ActiveXObject){
	                    doc = new ActiveXObject("Microsoft.XMLDOM");
	                    doc.async = "false"; 
	                    doc.loadXML(xmlRaw);
						if (doc.parseError.line > 0) 
  						alert('Error on line: ' + doc.parseError.line + ':  ' + doc.parseError.srcText );
  					}
		            else {
		  				doc = (new DOMParser()).parseFromString(xmlRaw, "text/xml");
		            }
                 
                    if (request.type == "soap") {
    					var method = request.method + 'Result'; //Standard return xml tagname 
    	    		    results = doc.getElementsByTagName(method)[0].childNodes[0].nodeValue;
        	   		} 
  					else if (request.type == "xml_rpc") {
  						results = doc.getElementsByTagName('html_results')[0].childNodes[0]; 
  					} 
  					else if (request.type == "template") {
  						results = doc.getElementsByTagName('html_results')[0].childNodes[0]; 
  					}
  					*/		
				}
				var strStatus = document.cookie;
				var iStatus = thor.io.GetKeyValue(strStatus,"AJAX_Status",";");
				if (iStatus == 0 && request.completionEvents.getItem("success") != null) request.completionEvents.getItem("success")();
				else if (iStatus == 1 && request.completionEvents.getItem("fail") != null) request.completionEvents.getItem("fail")();
				else if (iStatus == 2 && request.completionEvents.getItem("error") != null) request.completionEvents.getItem("error")();
											 
		    }
		    //Remove this request from the queue to allow next request through.
		    //Seems as though PHP has session locking or something which forces sequential processing of calls.
		    //Had to implement queueing:
		    thor.io.requestQueue.reverse();
		    oldreq = thor.io.requestQueue.pop();
		    thor.io.requestQueue.reverse();
        }
        thor.io.checkAJAXQueue();
    }


	
    thor.io.GetKeyValue = function(source_string,target_key,keyval_delimiter) {
    	var results = "";
    	if (source_string != null && source_string.length > 0) {
	    	var iStart = source_string.indexOf(target_key);
	    	var iValueStart = source_string.indexOf("=",iStart)+1;
	    	if (iValueStart > 0) {
		    	var iEnd = source_string.indexOf(keyval_delimiter,iStart);
				iEnd = (iEnd == -1 ? source_string.length : iEnd);
				if (iValueStart > 0 && iEnd > 0 && iEnd > iValueStart) { 
					results = source_string.substring(iValueStart, iEnd); 
				} 
			}
		}
		return results;		 
    }
    
    thor.io.InputToInput = function(ws_name, input_name) {
    	var ws_value = thor.io.GetHtmlInput(ws_name);
    	thor.io.SetHtmlInput(input_name, ws_value);
    	return ws_value;
    }
    thor.io.GetHtmlInput = function(input_name) {
    	if (thor.get(input_name) != null && thor.get(input_name).value != null)
    		return thor.get(input_name).value;
    	else
    		return null;
    }
    thor.io.SetHtmlInput = function(input_name, input_value) {
    	if (thor.get(input_name) != null && thor.get(input_name).value != null) {
    		thor.get(input_name).value = input_value;
    	}
    	else {
    		//Add to PHP globals class if missing...
    		alert("Could not find input..." + input_name + ", " + input_value);
    	}
    		
    }
    
    /* PRIVATE :: */
	thor.io._getFormParams = function(method,keyvalues){
         var results = "method_name=" + method + "&";
  	     for (k=0; k < keyvalues.length; k++)
         {
            key = keyvalues.getKey(k);
            results += key + "=" + keyvalues.getItem(key) + "&";
         }
  		 return results;
    };
		
	/* PRIVATE */
	thor.io._getSOAP_Envelope = function(method,keyvalues){
        var results = "";
        //Key Value Parsing:
         for (k=0; k < keyvalues.length; k++)
         {
            key = keyvalues.getKey(k);
            results += "<" + key + ">" + keyvalues.getItem(key) + "</" + key + ">";
         }
        //Bookend xml:
        results = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">"
             + "<soap12:Body><" + method + " xmlns=\"http://www.thorsrud.com/webservices/\">"
             + results + "</" + method + "></soap12:Body></soap12:Envelope>";
        
        return results;
    };
	     
    

    /*
     *********************************************************************************************
     IO 
     *********************************************************************************************
    */
    
    /* HASH / KEYVALUE-TYPE CLASS */
    
     thor.io.KeyValue = function(){
    
        this.length = 0;
        this.keys = new Array(); //True array
        this.items = new Object(); //Associative array
        
        this.addItem = function() {
            for (var i=0; i < this.addItem.arguments.length; i+=2)
            {
               	this.items[this.addItem.arguments[i]] = this.addItem.arguments[i+1];
                this.keys[this.length] = this.addItem.arguments[i];
                this.length++;
            }
        }
        
        this.removeItem = function() {
            for (var i=0; i < this.removeItem.arguments.length; i++) 
            {
            	//Slow way to do this, but works...
	            this.length = 0;
	            var newkeys = new Array();
	            var newitems = new Object();
            	for (var k=0; k < this.keys.length; k++) {
    		      	if (this.keys[k] != this.removeItem.arguments[i]) {
                  		newitems[this.keys[k]] = this.items[this.keys[k]];
                		newkeys[this.length] = this.keys[k];
                		this.length++;
                    }
                }
                this.keys = newkeys;
                this.items = newitems;
            }
        }
        
        this.clear = function() { 
        	this.length = 0;
        	this.keys = new Array();
        	this.items = new Object();
        }
        
        this.getKey = function(index) {
            return this.keys[index];
        }
        this.getItem = function(key) {
            return (this.items[key]);
        }
       
       this.hasItem = function(key) {
            return typeof(this.getItem(key)) != 'undefined';
       }
       
    };

	thor.io.DataCell = function() {
		this.id = 0;
		this.col = -1;
		this.row = -1;
		this.originalHTML = '';
	}


    /* THIS IS USED TO PASS EVENTS INTO A POPUP OR OTHER OBJECT */
    
    thor.io.eventRegister = new thor.io.KeyValue();




    /*
     *********************************************************************************************
        User Interface:
     *********************************************************************************************
    */
    thor.ui = {};
    thor.ui.PopupFlags = { //Additive
        MODAL: 1,
        CENTERONSCREEN: 2,
        SHOWCLOSE: 4,
        SHOWLOGO: 8,
        SHOWCANCEL: 16,
        SHOWSUBMIT: 32
    }
    
    thor.ui.AnimationType = { //Mutually Exclusive
        NONE: 0,
        ANIMATE: 1,
        ANIMATETORIGHT: 2,
        ANIMATETOLEFT: 3,
        ANIMATEFROMCENTER: 4
    }
    
    
    
    /* 
        This is a generic "class" for any (animated) popup window on the page. 
        May include rollover information, searchbox results or an editor window.
        Use of thor.ui.popup.prototype is used to extend this object as necessary.
    */
    thor.ui.CurrentInput; //Can only have one at a time.
	thor.ui.getNewTextEntry = function(unique_id, obj_parent, x, y, width, ev_tab, ev_update, ev_submit, text) {
		if (thor.ui.CurrentInput != null) thor.ui.CurrentInput.clearInput();
		thor.ui.CurrentInput = new thor.ui.textentry(unique_id, obj_parent, x, y, width, ev_tab, ev_update, ev_submit, text);
		return thor.ui.CurrentInput;
	};
	
	thor.ui.textentry = function(unique_id, obj_parent, x, y, width, ev_tab, ev_update, ev_submit, text) {
		this.id = unique_id;
		//this.parent = obj_parent;
		this.initialText = text;
		this.content = thor.getElement(obj_parent, 'input', unique_id + '_content', 'TextEntry', text);
		this.content.style.left = x + "px";
		this.content.style.top = (y - 2)+ "px";
		this.content.style.width = width + "px";
		this.content.style.visibility = 'visible'
		
		thor.addEvent(this.content, thor.ui.__processInputKey, "Event.KEYUP");
		thor.addEvent(this.content, thor.ui.__processInputKeyDown, "Event.KEYDOWN");
		thor.addEvent(this.content, thor.ui.__processInputClick, "Event.CLICK");
		this.content.select();
		
		this.clearInput = function() {
			if (this.content != null) {
				var parent = this.content.parentNode;
				if (parent != null)	parent.removeChild(this.content);
				this.content = null;
			}
			this.visible = false;
		}
		
		this.Tab = function(ev) { eval(ev_tab + "(ev);"); }
		this.Update = function(ev) { eval(ev_update + "(ev);"); }
		this.Submit = function(ev) { eval(ev_submit + "(ev);"); }

	}
	thor.ui.__processInputClick = function(ev) {
		ev = ev || window.event;
		if (thor.ui.CurrentDropDown != null && thor.ui.CurrentDropDown.list != null) {
				thor.ui.CurrentDropDown.selected = -1;
					thor.ui.highlightDropDownElement();	
		}
	}
	thor.ui.__processInputKeyDown = function(ev) {
		ev = ev || window.event;
		if(ev.keyCode == 9) { //Tab key
			thor.ui.CurrentInput.Tab();
			if (ev.preventDefault) ev.preventDefault();
			ev.returnValue = false; //Prevent IE from tabbing to address bar or other form element.
		} 
	}
	thor.ui.__processInputKey = function(ev) {
		ev = ev || window.event;
		
		//alert("Input Key: " + ev.keyCode);
		if(ev.keyCode == 27) { //Escape key.  Clear dropdown.
			thor.ui.CurrentInput.clearInput();
		} else if(ev.keyCode == 13) { //Return key.  Submit.
			thor.ui.CurrentInput.Submit(ev);
		} else if((ev.keyCode >= 48 && ev.keyCode <= 90) || ev.keyCode == 8) { //Entered letter or backspace...don't search context on numbers
			if (thor.ui.CurrentDropDown != null && thor.ui.CurrentDropDown.list != null) 
				thor.ui.CurrentDropDown.selected = -1;
			thor.ui.CurrentInput.Update();			
		} else if (ev.keyCode == 40) { //Down arrow
			if (thor.ui.CurrentDropDown != null && thor.ui.CurrentDropDown.list != null) {
				if (thor.ui.CurrentDropDown.selected < thor.ui.CurrentDropDown.list.length-1) 	
					thor.ui.CurrentDropDown.selected += 1;	
				thor.ui.highlightDropDownElement();	
			}
		} else if (ev.keyCode == 38) { //Up arrow
			if (thor.ui.CurrentDropDown != null && thor.ui.CurrentDropDown.list != null) {
				if (thor.ui.CurrentDropDown.selected >= 0) 	
					thor.ui.CurrentDropDown.selected -= 1;
				thor.ui.highlightDropDownElement();	
			}
		}
		
 	}

	thor.ui.CurrentDropDown; //Can only have one at a time.
	thor.ui.getNewDropDown = function(type, unique_id, obj_parent, x, y, width, ev_tab, ev_click, list) {
		if (thor.ui.CurrentDropDown != null) thor.ui.CurrentDropDown.clearDropDown();
		thor.ui.CurrentDropDown = new thor.ui.__dropdown(type, unique_id, obj_parent, x, y, width, ev_tab, ev_click, list);
		return thor.ui.CurrentDropDown;
	};
	
	thor.ui.__dropdown = function(type, unique_id, obj_parent, x, y, width, ev_tab, ev_click, list) {
		
		html = thor.ui.__getDropDownList(unique_id,ev_click, list);
		this.id = unique_id;
		this.parent = obj_parent;
		this.visible = true;
		this.selected = -1;
		this.list = list;
		this.clickEvent = ev_click;
		this.content = thor.getElement(obj_parent, 'div', unique_id + '_dropdowncontent', 'DropDown', html);
		this.content.style.left = x + "px";
		this.content.style.top = y + "px";
		this.content.style.width = width + "px";
		this.content.style.visibility = 'visible'
		
		if (type.indexOf("Context")<0) //Fix bug with this...
		{
			this.keycapture = thor.getElement(this.content, 'input', 'key_capture', 'KeyCapture', '');
			thor.addEvent(this.keycapture, function(ev) {thor.ui.__processDropDownKey(ev,ev_click,list);}, "Event.KEYDOWN");
			this.keycapture.focus();
		}
		this.clearDropDown = function() {
			if (this.parent != null && this.content != null) {
				this.parent.removeChild(this.content);
				this.content = null;
				this.visible = false;
			}
		}
		//Called with key capture:
		this.Click = function() {eval(ev_click + "(" + this.selected + ");");}
		this.Tab = function() {eval(ev_tab + "(" + this.selected + ");");}
		
	}
	thor.ui.clearCurrentDropDown = function() {
				if (thor.ui.CurrentDropDown != null) thor.ui.CurrentDropDown.clearDropDown();
	}

	thor.ui.__processDropDownKey = function(ev,ev_click,list) {
		ev = ev || window.event;
		
		if(ev.keyCode == 27) { //Escape key.  Clear dropdown.
			thor.ui.CurrentDropDown.clearDropDown();
		} else if(ev.keyCode == 9) { //Tab key
			thor.ui.CurrentDropDown.Tab();
			if (ev.preventDefault) ev.preventDefault();
			ev.returnValue = false; //Prevent IE from tabbing to address bar or other form element.
		} else if(ev.keyCode == 13) { //Enter key
			if (thor.ui.CurrentDropDown.selected == -1)
				thor.ui.CurrentDropDown.clearDropDown();
			else
				thor.ui.CurrentDropDown.Click();
		} else if (ev.keyCode == 40) { //Down arrow
			if (thor.ui.CurrentDropDown.selected < list.length-1) 	
				thor.ui.CurrentDropDown.selected += 1;	
			thor.ui.highlightDropDownElement();	
		} else if (ev.keyCode == 38) { //Up arrow
			if (thor.ui.CurrentDropDown.selected >= 0) 	
				thor.ui.CurrentDropDown.selected -= 1;
			thor.ui.highlightDropDownElement();	
		} else {
			var charPressed = String.fromCharCode(ev.keyCode);	
			for (var i=0; i<list.length; i++) 
				if (charPressed.toUpperCase() == list.getKey(i).substr(0,1).toUpperCase()) {
					selectDropDownIndex(i);
					break;
				}
		}

		return false;
 	}
 	thor.ui.selectDropDownIndex = function(index) {
 		if (thor.ui.CurrentDropDown != null) { //Can be called before initialized
 			thor.ui.CurrentDropDown.selected = index;
 			thor.ui.highlightDropDownElement();
 		}
 	}
	thor.ui.highlightDropDownElement = function() {
		if (thor.ui.CurrentDropDown != null && thor.ui.CurrentDropDown.list != null) { //Can be called before initialized
	 		for (var i=0; i<thor.ui.CurrentDropDown.list.length; i++) {
				var element = thor.get(thor.ui.CurrentDropDown.id + "_" + i);
				if (element != null) element.className = (thor.ui.CurrentDropDown.selected == i ? "DropDownElement_Hover" : "DropDownElement");
			}
		}
	}
	thor.ui.__getDropDownList =  function(id, ev_click, list) {
		var html = "";
		var type = "";
		var listname = "";
		for (var i=0; i<list.length; i++) {
			html += "<div id=\"" + id + "_" + i + "\" class=\"DropDownElement\" onclick=\"" + ev_click + "(" + i + ");\" onmouseover=\"thor.ui.selectDropDownIndex(" + i + ");\" >" + list.getKey(i) + "</div>";
		}
		return html;
	}

    //thor.ui.popups = new thor.io.KeyValue();
	thor.ui.CurrentPopup;
	thor.ui.getNewPopup = function(unique_id, x, y, min_w, w, h, pu_style, flags, animation_type, popup_events) {
		//Clear Previous Popup.  There can be only one.
    	if (thor.ui.CurrentPopup != null) thor.ui.CurrentPopup.clearPopup();
    	thor.ui.CurrentPopup = new thor.ui.__popup(unique_id, x, y, min_w, w, h, pu_style, flags, animation_type, popup_events);
    	return thor.ui.CurrentPopup;
	}
	thor.ui.__popup = function(unique_id, x, y, min_w, w, h, pu_style, flags, animation_type, popup_events) {
    	
    	//If flags say center...adjust these:
        var x = (x==-1 || ((flags & thor.ui.PopupFlags.CENTERONSCREEN) == thor.ui.PopupFlags.CENTERONSCREEN) ? thor.ui.getInnerWidth()/2 + thor.ui.getScrollXY()[0] - w/2: x); //Center on page if x==-1
        var y = (y==-1 || ((flags & thor.ui.PopupFlags.CENTERONSCREEN) == thor.ui.PopupFlags.CENTERONSCREEN) ? thor.ui.getInnerHeight()/2 + thor.ui.getScrollXY()[1] - h/2: y); 
        
        var x_anchor = x;
        var y_anchor = y;
        
        var animate = (animation_type > 0 ? true : false);
        var animationType = animation_type;
        
        var targetWidth = (w < min_w ? min_w : w);
        var right = (animationType == thor.ui.AnimationType.ANIMATETOLEFT ? x : x + targetWidth);
        var center = x + (targetWidth/2);
        var bottom = y + h;
        
        var width = (animate ? min_w : w);
        
        var popupStyle = pu_style;
        var popupFlags = flags;
        this.request = null; //Will be set by request.load() function.
        var popupEvents = popup_events;
        var isclosing=false;
        
        //Modal...simply covers screen with a semi-transparent PNG file that blocks mouse events on page
        //Be sure to set focus on contents of popup to ensue true modal operation.
        var modal = null;
        if ((flags & thor.ui.PopupFlags.MODAL) == thor.ui.PopupFlags.MODAL)
        {
            modal = thor.getElement(document.body, 'img', 'modal', '', '');
            modal.src = 'images/modal/blackout_charcoal.png';
        }
        
        var pu = thor.getElement(document.body, 'div', 'popup_'+pu_style, '', '');
        this.id = unique_id;
        pu.style.left = x + 'px';
        pu.style.top = y + 'px';
        pu.style.width = width + 'px';
        pu.targetWidth = (width < 70 ? 70 : width);
        
       
        //Top
        var upr = thor.getElement(pu, 'div', 'popup_' + pu_style + '_upr', '', '');
		var ul = thor.getElement(upr, 'div', 'popup_'+pu_style+'_ul', '', '');
        var u = thor.getElement(upr, 'div', 'popup_'+pu_style+'_u', '', '');
        var ur = thor.getElement(upr, 'div', 'popup_'+pu_style+'_ur', '', '');
         
        //Mid
        var mid = thor.getElement(pu, 'div', 'popup_' + pu_style + '_mid', '', '');
		var row = thor.getElement(mid, 'div', 'popup_' + pu_style + '_row', '', '');
		var l = thor.getElement(row, 'div', 'popup_' + pu_style + '_l', '', '');
		var r = thor.getElement(l, 'div', 'popup_' + pu_style + '_r', '', '');
        
        var content = thor.getElement(r, 'div', 'popup_'+pu_style+'_content', '', '');
        while(content.hasChildNodes()) content.removeChild(content.childNodes[0]); //Clear previous content if existing.
        
        
        //Btm
        var btm = thor.getElement(pu, 'div', 'popup_' + pu_style + '_btm', '', '');
        var bl = thor.getElement(btm, 'div', 'popup_'+pu_style+'_bl', '', '');
        var b = thor.getElement(btm, 'div', 'popup_'+pu_style+'_b', '', '');
        var br = thor.getElement(btm, 'div', 'popup_'+pu_style+'_br', '', '');
        
        
        var logo = null;
        var close_button = null;
        var cancel_button = null;
        var submit_button = null;
        
        //Logo
        if ((flags & thor.ui.PopupFlags.SHOWLOGO) == thor.ui.PopupFlags.SHOWLOGO)
        {
            logo = thor.getElement(document.body, 'div', 'logo_button', '', '');
            logo.style.left = (left-55) + 'px';
            logo.style.top = (top+10) + 'px';
        }
            
        //Close Button
        if ((flags & thor.ui.PopupFlags.SHOWCLOSE) == thor.ui.PopupFlags.SHOWCLOSE) { 
            close_button = thor.getElement(pu, 'img', 'close_'+pu_style, 'CloseButton', '');         
            close_button.src = "images/buttons/x_" + pu_style + "_off.png";
        }
        
        var fShowCancel = (((flags & thor.ui.PopupFlags.SHOWCANCEL) == thor.ui.PopupFlags.SHOWCANCEL) ? true : false);
        var fShowSubmit = (((flags & thor.ui.PopupFlags.SHOWSUBMIT) == thor.ui.PopupFlags.SHOWSUBMIT) ? true : false);
        
        var pu_buttons;
        if (fShowCancel || fShowSubmit)
        	pu_buttons = thor.getElement(pu, 'div', 'popup_buttons_' + pu_style, 'PopupButtons', '');
        
        
        //Cancel Button
        if (fShowCancel) { 
            cancel_button = thor.getElement(pu_buttons, 'img', 'cancel_'+pu_style, 'CancelButton', '');         
            cancel_button.src = "images/buttons/cancel_off.png";
        }
        
        //Submit Button
        if (fShowSubmit) { 
            submit_button = thor.getElement(pu_buttons, 'img', 'submit_'+pu_style, 'SubmitButton', '');         
            submit_button.src = "images/buttons/submit_off.png";
        }
        
        
    	var updateSize = function(){
                if 	(animationType == thor.ui.AnimationType.ANIMATETOLEFT) {
	            	pu.style.left = right-width;
	            }
	            else if (animationType == thor.ui.AnimationType.ANIMATEFROMCENTER) {
	            	pu.style.left = center-(width/2);
	            }
	            
	                
	            //Top of popup:
	            upr.style.width = width + 'px';
	            mid.style.width = width + 'px';
	            btm.style.width = width + 'px';
	            
	            //Content area of popup:
	            l.style.width = width + 'px';
	            r.style.width = width + 'px';
	            
	            
	            //Bottom area of popup:

	            if (popupStyle == "bubble") {
		       		u.style.width = (width-38) + 'px';
	            	ur.style.left = (width-19) + 'px';
				    b.style.width = (width-60) + 'px';
		            br.style.left = (width-30) + 'px';
	            	content.style.width = (width-38) + 'px';
	            } else if (popupStyle == "wired") {
		            u.style.width = (width-38) + 'px';
	            	ur.style.left = (width-19) + 'px';
				    b.style.width = (width-60) + 'px';
		            br.style.left = (width-30) + 'px';
	            	content.style.width = (width-38) + 'px';
	            } else if (popupStyle == "plate") {
		            u.style.width = (width-51) + 'px';
	            	ur.style.left = (width-34) + 'px';
				    b.style.width = (width-51) + 'px';
		            br.style.left = (width-34) + 'px';
	            	content.style.width = (width-51) + 'px';
	            }

	            if (close_button)  
	                close_button.style.left = (width-28) + 'px';
	
	            var iRight = 0;
	            if (submit_button)
	            {
	                submit_button.style.left = (width-110) + 'px';
	                iRight += 79;        
	            }
	            if (cancel_button)
	            {
	                cancel_button.style.left = (width-iRight-110) + 'px';
	                iRight += 79;        
	            }
         	
         }
         this.expandPopup = function() {
         	if (animate)
            {
	            if (width <targetWidth-5) {
	            	if (width < targetWidth-1) width += (targetWidth-width)/2;
                    var t = setTimeout("thor.ui.expandPopup('" + this.id + "')", 100);
                }
                else {
                    animate = false;
                    width = targetWidth;
                    if (this.request) { //May be loaded via static javascript:
	                    this.request.targetReady = true;
						thor.io.getAJAXData();
					}
					activateContent(); //Enables Cancel, Submit, etc.
				}
            }
            else //Show as soon as AJAX data is returned
            {
            	 if (this.request) {this.request.targetReady = true;}
           		thor.io.getAJAXData();
				activateContent();
		 	}
			updateSize();
        }
        
        this.clearPopup = function (){
                if (logo) document.body.removeChild(logo);
                if (close_button && pu.hasChildNodes()) pu.removeChild(close_button); 
                if (pu_buttons) {
                  	if (pu_buttons.hasChildNodes()) {
	                	if (cancel_button) pu_buttons.removeChild(cancel_button); 
    	            	if (submit_button) pu_buttons.removeChild(submit_button);
        			}
        			try { if (pu && pu.hasChildNodes()) pu.removeChild(pu_buttons); } catch(err) { /*Object already cleared*/ }
                }
                pu.innerHTML = "";
                try { document.body.removeChild(pu); } catch(err) { /*Object already cleared*/ }
                try { if (modal) document.body.removeChild(modal); } catch(err) { /*Object already cleared*/ }
                
                //Assume we want to close any existing editing boxes:
                if (thor.ui.CurrentInput != null) thor.ui.CurrentInput.clearInput();
				if (thor.ui.CurrentDropDown != null) thor.ui.CurrentDropDown.clearDropDown();
	    }

        var activateContent = function(){
            if (close_button) 
               thor.addEvent(close_button, function() {thor.ui.clearPopup(unique_id);}, "Event.CLICK");
            
            if (cancel_button) 
                thor.addEvent(cancel_button, function() {thor.ui.clearPopup(unique_id);}, "Event.CLICK");
                        
            if (submit_button)
                thor.addEvent(submit_button, popupEvents.getItem("submit"), "Event.CLICK");
            
        }
    
        this.updateHtml = function(xml) {
			content.innerHTML = xml;
		}
        this.updateContent = function(node) {
			content.innerHTML = node;
        }
        this.getContentDiv = function() {
        	return content;
        }

		//Drag functions:
		var isDragging = false;
		var x_delta = 0;
		var y_delta = 0;
		var dragReset = function(e) {
			if (!e) e = window.event;
			x_anchor = e.screenX;
			y_anchor = e.screenY;
			isDragging = false;
		}
		var dragStart = function(e) {
			if (!e) e=window.event;
			dragReset(e);
			isDragging = true;
		}
		var dragMe = function(e) {
			if (!e) e = window.event;
			if (isDragging) {
				x_delta = e.screenX - x_anchor;
				y_delta = e.screenY - y_anchor;
				if (x+x_delta >= 0 && x+targetWidth+x_delta < thor.ui.getInnerWidth()) pu.style.left = x + x_delta + "px";
				if (y+y_delta >= 0 && y+y_delta < thor.ui.getInnerHeight()) pu.style.top = (y + y_delta) + "px";
			} 
		}
		var dragStop = function(e) {
			if (!e) e = window.event;
			isDragging = false;
			x+= x_delta;
			y+= y_delta;
			x_delta = 0;
			y_delta = 0;
			//Prevent bubble up:
			//e.cancelBubble = true;
			//if (e.stopPropagation) e.stopPropagation();
    		//return false;
    	}
				
		thor.addEvent(bl, dragStart, "Event.MOUSEDOWN");
		thor.addEvent(bl, dragMe, "Event.MOUSEMOVE");
		thor.addEvent(bl, dragStop, "Event.MOUSEUP");
		thor.addEvent(b, dragStart, "Event.MOUSEDOWN");
		thor.addEvent(b, dragMe, "Event.MOUSEMOVE");
		thor.addEvent(b, dragStop, "Event.MOUSEUP");
		thor.addEvent(br, dragStart, "Event.MOUSEDOWN");
		thor.addEvent(br, dragMe, "Event.MOUSEMOVE");
		thor.addEvent(br, dragStop, "Event.MOUSEUP");
		thor.addEvent(pu, dragStop, "Event.MOUSEOUT");
		thor.ui.disableTextSelect(bl);
		thor.ui.disableTextSelect(b);
		thor.ui.disableTextSelect(br);
		
        //Finalize popup         
        updateSize();
        var t = setTimeout("thor.ui.expandPopup('" + unique_id + "')", 100);
        
    }
    thor.ui.disableTextSelect = function(obj) {
        if (typeof obj.onselectstart!="undefined") //IE route
			obj.onselectstart=function(){return false}
		else if (typeof obj.style.MozUserSelect!="undefined") //Firefox route
			obj.style.MozUserSelect="none"
	}
    thor.ui.cancelEventProp = function(e) {
    	e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
		return false; //Prevents bubble up to image itself.  Draggin DIV.
    }
    
    /* ANIMATION FUNCTION CALLED BY TIMER */
    thor.ui.expandPopup = function(id) {
       //var popup = thor.ui.popups.getItem(id);
       //popup.expandPopup();
       if (thor.ui.CurrentPopup != null) thor.ui.CurrentPopup.expandPopup();
    }
    
    /* PUBLICLY ACCESSIBLE FUNCTION CLOSE CLOSE POPUP BY ID */
    thor.ui.clearPopup = function() {
    	if (thor.ui.CurrentPopup != null) thor.ui.CurrentPopup.clearPopup();
    }

    
    
    thor.ui.getInnerHeight = function() {
        if (typeof(window.innerHeight) == 'number') 
				     return window.innerHeight;
				else if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientHeight > 0) 
				    return document.documentElement.clientHeight;
				else 
				     return document.body.clientHeight;
		}
    
    thor.ui.getInnerWidth = function() {
        if (typeof(window.innerWidth) == 'number')
            return window.innerWidth;
        else if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientWidth > 0)
            return document.documentElement.clientWidth;
        else
            return document.body.clientWidth;
    }
    
    thor.ui.getScrollXY = function() {
      var iX = 0;
      var iY = 0;
      if( typeof(window.pageYOffset) == 'number' ) 
      { //Netscape:
        iX = window.pageXOffset;
        iY = window.pageYOffset;
      } 
      else if( document.body && (document.body.scrollLeft || document.body.scrollTop)) 
      { //DOM compliant
        iX = document.body.scrollLeft;
        iY = document.body.scrollTop;
      } 
      else if( document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) 
      { //IE6 
        iX = document.documentElement.scrollLeft;
        iY = document.documentElement.scrollTop;
      }
      return [iX,iY];
    }


	/*
     *********************************************************************************************
    
        String Handling:

     *********************************************************************************************
    */
    
    thor.string = {   };
  
  	thor.string.trim = function(source) {
  		var re = (/^\s+|\s+$/g);
		return source.replace(re, "");	
  	}
  	thor.string.removeQuotes = function(source) {
  		var re = /\'|"/g;
  		return source.replace(re, "");	
  	}
  	
  	thor.string.formatPhone = function(numsonly) {
  		var phone = "";
  		if (numsonly.length == 10)
	  		phone = "(" + numsonly.substr(0,3) + ") " + numsonly.substr(3,3) + "-" + numsonly.substr(6,4);
  		else if (numsonly.length > 10)
  			phone = "(" + numsonly.substr(0,3) + ") " + numsonly.substr(3,3) + "-" + numsonly.substr(6,4) + " x" + numsonly.substring(10);
  		else 
  			phone = numsonly;	
  		return phone;
  	}
  	

} //(typeof thor == "undefined")

