/**
 * $Id: Drag.js,v 1.29 2010/08/24 17:28:23 cvs Exp $
 *
 * @author Frederick J Richart
 * @copyright Copyright © 2006-2009, Useful Media Planet, LLC, All rights reserved.

 * PortableDrag.js:
 * beginDrag() is designed to be called from an onmousedown event handler.
 * elementToDrag may be the element that received the mousedown event, or it
 * may be some containing element.  event must be the Event object for the
 * mousedown event.  This implementation works with both the DOM Level 2
 * event model and the IE Event model.
 **/
var isMSIE = (navigator.appName == "Microsoft Internet Explorer");
var isMSIE6 = isMSIE && (navigator.userAgent.indexOf('MSIE 6') != -1);

function beginDragId(elementID, event, callback) {
	var element = document.getElementById(elementID);
	if (element) beginDrag(element, event, callback);
}
function beginDrag(elementToDrag, event, callback) {
    // Compute the distance between the upper-left corner of the element
    // and the mouse click. The moveHandler function below needs these values.
    // Note: style values don't exist if using class or id in stylesheet. 
    var startX = parseInt(elementToDrag.offsetLeft);
    var startY = parseInt(elementToDrag.offsetTop);
    var deltaX = event.clientX - startX; //style.left);
    var deltaY = event.clientY - startY  //style.top);
	var timestamp = (new Date()).getTime();

    // Register the event handlers that will respond to the mousemove events
    // and the mouseup event that follow this mousedown event.  
    if (document.addEventListener) {  // DOM Level 2 Event Model
		// Register capturing event handlers
	    document.addEventListener("mousemove", moveHandler, true);
		document.addEventListener("mouseup", upHandler, true);
    } else if (document.attachEvent) {  // IE 5+ Event Model
		// In the IE Event model, we can't capture events, so these handlers
		// are triggered when only if the event bubbles up to them.
		// This assumes that there aren't any intervening elements that
		// handle the events and stop them from bubbling.
		document.attachEvent("onmousemove", moveHandler);
		document.attachEvent("onmouseup", upHandler);
    } else {  // IE 4 Event Model
		// In IE 4 we can't use attachEvent(), so assign the event handlers
		// directly after storing any previously assigned handlers so they 
		// can be restored.  Note that this also relies on event bubbling.
		var oldmovehandler = document.onmousemove;
		var olduphandler = document.onmouseup;
		document.onmousemove = moveHandler;
		document.onmouseup = upHandler;
    }

    // We've handled this event.  Don't let anybody else see it.  
    if (event.stopPropagation) event.stopPropagation();   // DOM Level 2
    else window.event.cancelBubble = true;                       // IE

    // Now prevent any default action.
    if (event.preventDefault) event.preventDefault();     // DOM Level 2
    else window.event.returnValue = false;                       // IE

    /**
     * This is the handler that captures mousemove events when an element
     * is being dragged.  It is responsible for moving the element.
     **/
    function moveHandler(e) {
		if (!e) e = window.event;  // IE event model
		// Move the element to the current mouse position, adjusted as
		// necessary by the offset of the initial mouse click.
		elementToDrag.style.left = (e.clientX - deltaX) + "px";
		elementToDrag.style.top = (e.clientY - deltaY) + "px";
		
		// And don't let anyone else see this event.
		if (e.stopPropagation) e.stopPropagation();       // DOM Level 2
		else window.event.cancelBubble = true;            // IE

	    // Now prevent any default action. ### I added
	    if (e.preventDefault) event.preventDefault();     // DOM Level 2
	    else window.event.returnValue = false;                       // IE
		var delta = {x:deltaX,y:deltaY};
	    if (callback) callback.MoveEvent(e, delta);
	    deltaX = delta.x;
	    deltaY = delta.y;
    }

    /**
     * This is the handler that captures the final mouseup event that
     * occurs at the end of a drag.
     **/
    function upHandler(e) {
		if (!e) e = window.event;  // IE event model		
		// Unregister the capturing event handlers.
		if (document.removeEventListener) {    // DOM Event Model
		    document.removeEventListener("mouseup", upHandler, true);
		    document.removeEventListener("mousemove", moveHandler, true);
		} else if (document.detachEvent) {       // IE 5+ Event Model
		    document.detachEvent("onmouseup", upHandler);
		    document.detachEvent("onmousemove", moveHandler);
		} else {                                 // IE 4 Event Model
		    document.onmouseup = olduphandler;
		    document.onmousemove = oldmovehandler;
		}	
		
 		// Have we moved? If not we won't block the event propagation.
		if (startX == parseInt(elementToDrag.offsetLeft) &&
    		startY == parseInt(elementToDrag.offsetTop)) {
			
			// ...or perhaps we have been dragging around for more than 200ms and dropped back in place
    		var now = (new Date()).getTime();
    		if (now < (timestamp + 200)) {
    			if (callback) callback.UpEvent(e, false);
    			return;
    		}
		}
 		if (callback) callback.UpEvent(e, true);
 
		// And don't let the event propagate any further.
		if (e.stopPropagation) e.stopPropagation();       // DOM Level 2
		else e.cancelBubble = true;                       // IE
   }
   return upHandler;
}
/**
 * PortableDrag.js:
 * beginResize() is designed to be called from an onmousedown event handler.
 * elementToResize may be the element that received the mousedown event, or it
 * may be some containing element.  Event must be the Event object for the
 * mousedown event.  This implementation works with both the DOM Level 2
 * event model and the IE Event model.
 **/
function beginResizeId(elementID, event, ie6HeightAdjust) {
	var element = document.getElementById(elementID);
	if (element) beginResize(element, event, ie6HeightAdjust);
}
function beginResize(elementToResize, event, ie6HeightAdjust) {
    // Compute the distance between the upper-left corner of the element
    // and the mouse click. The moveHandler function below needs these values.
    // Note: style values don't exist if using class or id in stylesheet. 
    if (!event) event = window.event;
    var startX = event.clientX; // - parseInt(elementToResize.style.left);
    var startY = event.clientY; // - parseInt(elementToResize.style.top);
    var startW = elementToResize.offsetWidth;
    var startH = elementToResize.offsetHeight;
    if (isMSIE6 && ie6HeightAdjust) startH += ie6HeightAdjust;
    
    // Register the event handlers that will respond to the mousemove events
    // and the mouseup event that follow this mousedown event.  
    if (document.addEventListener) {  // DOM Level 2 Event Model
		// Register capturing event handlers
	    document.addEventListener("mousemove", moveHandler, true);
		document.addEventListener("mouseup", upHandler, true);
    } else if (document.attachEvent) {  // IE 5+ Event Model
		// In the IE Event model, we can't capture events, so these handlers
		// are triggered when only if the event bubbles up to them.
		// This assumes that there aren't any intervening elements that
		// handle the events and stop them from bubbling.
		document.attachEvent("onmousemove", moveHandler);
		document.attachEvent("onmouseup", upHandler);
    } else {                            // IE 4 Event Model
		// In IE 4 we can't use attachEvent(), so assign the event handlers
		// directly after storing any previously assigned handlers so they 
	    // can be restored.  Note that this also relies on event bubbling.
		var oldmovehandler = document.onmousemove;
		var olduphandler = document.onmouseup;
		document.onmousemove = moveHandler;
		document.onmouseup = upHandler;
    }

    // We've handled this event.  Don't let anybody else see it.  
    if (event.stopPropagation) event.stopPropagation();   // DOM Level 2
    else event.cancelBubble = true;                       // IE

    // Now prevent any default action.
    if (event.preventDefault) event.preventDefault();     // DOM Level 2
    else event.returnValue = false;                       // IE
    
    /**
     * This is the handler that captures mousemove events when an element
     * is being dragged.  It is responsible for moving the element.
     **/
    var skip=true;
    function moveHandler(e) {
		if (!e) e = window.event;  // IE event model
	
	    // Move the element to the current mouse position, adjusted as
		// necessary by the offset of the initial mouse click.
		elementToResize.style.width = (startW + e.clientX - startX) + "px";
		elementToResize.style.height = (startH + e.clientY - startY) + "px";
	
		// And don't let anyone else see this event.
		if (e.stopPropagation) e.stopPropagation();       // DOM Level 2
		else e.cancelBubble = true;                       // IE
		
	    // Now prevent any default action. ### I added
	    if (e.preventDefault) event.preventDefault();     // DOM Level 2
	    else e.returnValue = false;                       // IE	    
    }

    /**
     * This is the handler that captures the final mouseup event that
     * occurs at the end of a drag.
     **/
    function upHandler(e) {
		if (!e) e = window.event;  // IE event model		
		// Unregister the capturing event handlers.
		if (document.removeEventListener) {    // DOM Event Model
		    document.removeEventListener("mouseup", upHandler, true);
		    document.removeEventListener("mousemove", moveHandler, true);
		} else if (document.detachEvent) {       // IE 5+ Event Model
		    document.detachEvent("onmouseup", upHandler);
		    document.detachEvent("onmousemove", moveHandler);
		} else {                                 // IE 4 Event Model
		    document.onmouseup = olduphandler;
		    document.onmousemove = oldmovehandler;
		}		
		// And don't let the event propagate any further.
		if (e.stopPropagation) e.stopPropagation();       // DOM Level 2
		else e.cancelBubble = true;                       // IE
		
	    // Now prevent any default action. ### I added
	    if (e.preventDefault) event.preventDefault();     // DOM Level 2
	    else e.returnValue = false;                       // IE
    }
}
Number.prototype.NaN0=function(){return isNaN(this)?0:this;}
function getAbsPosition(e, className){
	var left = 0;
	var top  = 0;
	while (e.offsetParent){
		if (className && (' ' + e.className + ' ').indexOf(' ' + className + ' ') !== -1) return {left:left, top:top};
		left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0);
		top += e.offsetTop + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0);
		e = e.offsetParent;
	}
	left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0);
	top  += e.offsetTop + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0);
	return {left:left, top:top};
}
function GeneralEventEngine() {
	var t = this;
	t.Registry = new Array();
	t.Zindex = 20025;
	t.popupsOpen = 0;
	t.otherOnLoad = window.onload;
	window.onload = function(){t.onPageLoad()};
	t.otherOnUnload = window.onunload;
	window.onunload = function() {t.onPageUnload()};
	t.pageLoaded = false;
}
GeneralEventEngine.prototype = {
	onPageLoad : function() {
		var i, r;
		if (this.otherOnLoad) this.otherOnLoad();
		this.pageLoaded = true;
		for(i in this.Registry) {
			r = this.Registry[i];
			if (r.onPageLoad) r.onPageLoad();
		}
	},
	onPageUnload : function() {
		var i, r;
		if (this.otherOnUnload) this.otherOnUnload();
		this.pageLoaded = false;
		for(i in this.Registry) {
			r = this.Registry[i];
			if (r.onPageUnload)	r.onPageUnload();
		}
	},
	register : function(id, obj) {
		this.Registry[id] = obj;
		if (this.pageLoaded && obj.onPageLoad) obj.onPageLoad();
	},
	lookup : function(id) {
		return this.Registry[id];
	},
	Drag : function(e, event) {
		var a = e.parentNode;
		a.style.zIndex = ++this.Zindex;
		beginDrag(a, event);
	},
	Click : function(e, event) {
		var a = e.parentNode;
		a.style.zIndex = ++this.Zindex;
	},
	Send : function(e, id, formid) {
		var o = this.Registry[id];
		if (o && o.Send) o.Send(e, formid);
	},
	ActionLink : function(e, id, formid, varname, action) {
		var o = this.Registry[id];
		if (o && o.ActionLink) o.Send(e, formid, varname, action);		
	},
	Close : function(e, id) {
		var o = this.Registry[id];
		if (o && o.Close) o.Close(e);
	},
	Expand : function(e, id) {
		var o = this.Registry[id];
		if (o && o.Expand) o.Expand(e); 
	},
	Config : function(e, id, opts) {
		var o = this.Registry[id];
		if (o && o.Config) o.Config(e, opts);
	}
}
GeneralEvent = new GeneralEventEngine;
function popupAjaxForm(id, ex, cb, u, f) {
	this.Registry = new Array();
	this.id = id;
	this.xdomExtension = ex;
	this.callbackClass = cb;
	this.requestURL = u;
	this.formID = f;
	this.popupOpen = false;
	GeneralEvent.register(id, this);
}
/**
 * Implement a popup form that can have its control values sent to the server
 * using AJAX.  Currently the popup form cannot handle input file controls and
 * select controls that have the multiple attribute set.
 */
popupAjaxForm.prototype = {
	onPageLoad : function() {
		var t = this, e;
		e = t.element = document.getElementById(t.id);
		if (t.formID) t.formElement = document.getElementById(t.formID);
		t.xdomAjax = new XdomAjax(e, "VerifiedControl", t.requestURL);
		t.xdomAjax.requestor = t;
		t.busy = new BusyIndicator();
	},
	/**
	 * Use this method to register an object as a listener.  The listener object
	 * will be notified when events occur.
	 * @param obj
	 */
	register : function(obj) {
		this.Registry.push(obj);
	},
	getElement : function() {
		var t = this, e;
		if (!(e = t.element)) {
			t.onPageLoad();
			e = t.element;
		}
		return e;
	},
	/**
	 * Closes the popup window
	 */
	Close : function() {
		var t = this, g = GeneralEvent, i, r;
		t.element.className = "xdomAjaxPopupShrink";
		t.popupOpen = false;
		g.popupsOpen--;
		if (g.popupsOpen > 0) return;
		g.popupsOpen = 0;
		for(i=0; i < t.Registry.length; i++) if ((r = t.Registry[i]).popupFormClose) r.popupFormClose(t);
	},
	/**
	 * Expands or pops up the form window.
	 */
	Expand : function(el, top, left) {
		var t = this, i, e = t.getElement(), r;
		if (t.popupOpen) return;
		t.popupOpen = true;
		top = top ? top : 0;
		left = left ? left : 100;
		e.className = "xdomAjaxPopupExpand";
		e.style.top = top + 'px';
		e.style.left = left + 'px';
		for(i=0; i < t.Registry.length; i++) if ((r = t.Registry[i]).popupFormExpand) r.popupFormExpand(t);
		t.Adjust(e, top, left);
	},
	/**
	 * Adjusts a popup window to make sure it isn't off the bottom of the browser
	 * view area.
	 */
	Adjust : function(el, top, left) {
		var t = this, e = t.getElement(), pos, wh, ww, wt, wr, st, sr, v, newtop, newleft;
		newtop = top = top ? top : 0;
		newleft = left = left ? left : 100;
		pos = Utilities.getAbsPosition(e, null, true);
		wh = Utilities.IE ? document.documentElement.clientHeight : window.innerHeight;
		ww = Utilities.IE ? document.documentElement.clientWidth : window.innerWidth;
		st = Utilities.IE ? document.documentElement.scrollTop : window.pageYOffset;
		sl = Utilities.IE ? document.documentElement.scrollLeft : window.pageXOffset;
		wt = st + wh;
		wr = sl + ww;
		if (pos.bottom > wt) {
			v = wt - pos.bottom;
			e.style.top = (newtop = top + v) + 'px';
		}
		if (pos.right > wr) {
			v = wr - pos.right;
			e.style.left = (newleft = left + v) + 'px';
		}
		// Make sure we didn't push off top or left of browser
		pos = Utilities.getAbsPosition(e, null, true);
		if (pos.top < 0) newtop -= pos.top;
		e.style.top = newtop + 'px';
		if (pos.left < 0) newleft -= pos.left;
		e.style.left = newleft + 'px';
		GeneralEvent.popupsOpen++;		
	},
	/**
	 * Called to set special configuration such as a callback function or object.  Configuration
	 * options are an object with the following possible values
	 * {CallbackObject: object // Can have methods SendRequest(popup,args) and ServerResponse(popup, xdomAjax, response) 
	 *  SendRequestEvent: function(popup, args) // Must return args or modified args
	 *  ServerResponseEvent: function(popup, xdomAjax, response)} // Can clear popup.errmsg to prevent popup or clear popup.ok to prevent closing dialog.
	 *  
	 */
	Config : function(e, opts) {
		var i;
		for(i in opts) {
			this[i] = opts[i];
		}
	},
	ActionLink : function(e, formid, varname, action) {
		this.Send(e, formid, varname, action);
	},
	/**
	 * Called when the form is to be submitted via AJAX to the server.  Plugins MUST
	 * return the p argument even if unchanged. Normally varname and action are not
	 * required.  When they are set the form post values will have varname set to 
	 * action.  
	 * 
	 * Send is broken up into Send and DoSend.  We do this in case the form has
	 * a secure login password field.  That requires that we first secure the
	 * password and once done the SecureLogin object will call DoSend to complete
	 */
	Send : function(e, formid, varname, action) {
		var t = this, f, sl;
		if (t.busy) t.busy.show();
		if (formid && (f = xdom_getElementById(formid))) t.formElement = f;
		if (sl = t.formElement.SecureLogin) {
			sl.AjaxFormSend(this, varname, action);
			return;
		}
		t.DoSend(varname, action);
	},
	DoSend : function(varname, action) {
		var t = this, p, cb, i, r; 
		p = 'callback=' + t.callbackClass + '&xdomExtension=' + t.xdomExtension + "&controlID=" + t.id + "&controlValue=send";
		if ((cb = t.CallbackObject) && cb.SendRequest) p = cb.SendRequest(t, p);
		if ((cb = t.SendRequestEvent) && typeof cb == "function") p = cb(t, p);
		for(i=0; i < t.Registry.length; i++) if ((r = t.Registry[i]).popupFormSend) p = r.popupFormSend(t, p);
		this.xdomAjax.sendRequest(p, xdom.formPost(t.formElement, varname, action));
	},
	/**
	 * Method to handle the AJAX server response.  It will display an alert if any
	 * text is sent as an errmsg and will close the popup if the response indicated
	 * success.
	 */
	serverResponse : function(xdomAjax, response) {
		var t = this, e, i, r;
		t.errmsg = xdomAjax.errmsg;
		t.ok = xdomAjax.err == "0"
		if ((cb = t.CallbackObject) && cb.ServerResponse) cb.ServerResponse(t, xdomAjax, response);
		if ((cb = t.ServerResponseEvent) && typeof cb == "function") cb(t, xdomAjax, response); 
		for(i=0; i < t.Registry.length; i++) if ((r = t.Registry[i]).popupFormServerResponse) r.popupFormServerResponse(t, xdomAjax, response);
		if (t.busy) t.busy.hide();
		if (t.errmsg) alert(t.errmsg);
		if (t.ok) this.Close(null);		
	},
	/**
	 * Load the HTML into this popup form using the xml element srcID for the encoded
	 * HTML. If openPopup is true the popup will be expanded (opened) and position 
	 * adjusted not be off screen otherwise the popup will be adjusted only.  If successful, 
	 * the popup element is returned to the caller.
	 */
	loadPopup : function(srcID, response, openPopup) {
		var t = this, e, id, be, pe, te, v;
		// Load popup form content and display
		e = xdom_GetFirstElementWithTagName(response, srcID);
		if (!e) return null;
		id = e.getAttribute("id");
		pe = xdom_getElementById(id);
		if (id && pe) {
				if (te = xdom_getElementById(id + '_title') && (v = e.getAttribute("title"))) te.innerHTML = decodeURIComponent(v);
				if (be = xdom_getElementById(id + '_body')) {
					v = decodeURIComponent(decodeURIComponent(xdom_GetData(e)));
					be.innerHTML = v;
				}
				if (v = e.getAttribute('width')) pe.style.width = v;
				if (openPopup) t.Expand(pe);
				else t.Adjust(pe);
				return pe;
		}
		return null;
	}
}
function expandElement(id, sw, ew, sh, eh, ms, unit, delay) {
	var e;
	if (!unit) unit = "px";
	this.unit = unit;
	this.id = id;
	if (!(this.element = e = document.getElementById(id))) return this;
	this.pw = e.parentNode.offsetWidth;
	this.ph = e.parentNode.offsetHeight;
	e.style.width = sw + unit;
	this.sw = sw;
	this.ew = ew;
	e.style.height = sh + unit;
	this.sh = sh;
	this.eh = eh;
	this.ms = ms;
	this.inc = Math.round(2000/ms) + 1;
	this.delay = delay;
	GeneralEvent.register(id, this);
}
expandElement.prototype = {
	onPageLoad : function() {
		var t = this;
		if (t.delay) {			
			this.delayVar = setInterval(function(){t.start();}, t.delay);
		} else {
			t.start();
		}
	},
	start : function() {
		var t = this;
		if (this.delayVar) clearInterval(this.delayVar);
		this.timerVar = setInterval(function(){t.expand();}, this.ms / 100);
		this.pct = 0;
	},
	expand : function() {
		var pct = this.pct = this.pct + this.inc;
		if (pct >=100) {
			 pct = 100;
			 clearInterval(this.timerVar);
		}
		var w = this.sw + ((this.ew -this.sw) * pct / 100);
		var h = this.sh + ((this.eh -this.sh) * pct / 100);
		if (this.unit == "%") {
			w = this.pw * w / 100;
			h = this.ph * h / 100;
		}
		this.element.style.width = w + "px";
		this.element.style.height = h + "px";
	}
}
function uncoverElement(id, ms, doW, doH, delay, parentWidth) {
	this.id = id;
	if (!(this.element = e = document.getElementById(id))) return this;
	this.ms = ms;
	this.inc = Math.round(2000/ms) + 1;
	e.style.visibility = 'hidden';
	if (parentWidth) {
		var p = e.parentNode;
		p.style.width = p.offsetWidth + "px";
		p.style.height = p.offsetHeight + "px";
	}
	this.ew = e.offsetWidth;
	this.eh = e.offsetHeight;
	this.sw = doW ? 0 : this.ew;
	this.sh = doH ? 0 : this.eh;
	e.style.width = this.sw + "px";
	e.style.height = this.sh + "px";
	e.style.overflow = 'hidden';	
	e.style.visibility = 'visible';
	this.delay = delay;
	GeneralEvent.register(id, this);
}
uncoverElement.prototype = {
	onPageLoad : function() {
		var t = this;
		if (t.delay) {			
			this.delayVar = setInterval(function(){t.start();}, t.delay);
		} else {
			t.start();
		}
	},
	start : function() {
		var t = this;
		if (this.delayVar) clearInterval(this.delayVar);
		this.timerVar = setInterval(function(){t.expand();}, this.ms / 100);
		this.pct = 0;
	},
	expand : function() {
		var pct = this.pct = this.pct + this.inc;
		var w = this.sw + ((this.ew -this.sw) * pct / 100);
		var h = this.sh + ((this.eh -this.sh) * pct / 100);
		if (pct >= 100) {
			pct = 100;
			clearInterval(this.timerVar);
			this.element.style.overflow = "visible";
			this.element.style.height = 'auto';
			this.element.style.width = w + 'px';
			return;
		}
		this.element.style.width = w + 'px';
		this.element.style.height = h + 'px';
	}
}
