Util.Dom = {
	getBody : function(){
		return $(document.body || document.documentElement);
	},
	
	getViewWidth : function(full, doc){
        return full ? this.getDocumentWidth(doc) : this.getViewportWidth(doc);
    },

    getViewHeight : function(full, doc){
        return full ? this.getDocumentHeight(doc) : this.getViewportHeight(doc);
    },

    getDocumentHeight: function(doc) { // missing from prototype?
		doc = doc || document;
        var scrollHeight = (doc.compatMode != "CSS1Compat") ? doc.body.scrollHeight : doc.documentElement.scrollHeight;
        return Math.max(scrollHeight, this.getViewportHeight(doc));
    },

    getDocumentWidth: function(doc) { // missing from prototype?
		doc = doc || document;
        var scrollWidth = (doc.compatMode != "CSS1Compat") ? doc.body.scrollWidth : doc.documentElement.scrollWidth;
        return Math.max(scrollWidth, this.getViewportWidth(doc));
    },

    getViewportHeight: function(doc) { // missing from prototype?
		doc = doc || document;
        var height = self.innerHeight;
        var mode = doc.compatMode;

        if ( (mode || Browser.IE) && !Browser.Opera ) {
            height = (mode == "CSS1Compat") ?
                    doc.documentElement.clientHeight : // Standards
                    doc.body.clientHeight; // Quirks
        }
        return height;
    },

    getViewportWidth: function(doc) { // missing from prototype?
        doc = doc || document;
		var width = self.innerWidth;  // Safari
        var mode = doc.compatMode;

        if (mode || Browser.IE) { // IE, Gecko, Opera
            width = (mode == "CSS1Compat") ?
                    doc.documentElement.clientWidth : // Standards
                    doc.body.clientWidth; // Quirks
        }
        return width;
    },
	
	isAncestor : function(p, c){ // missing from prototype?
        p = $(p);
        c = $(c);
        if (!p || !c) {return false;}

        if(p.contains && !Browser.Safari) {
            return p.contains(c);
        }else if(p.compareDocumentPosition) {
            return !!(p.compareDocumentPosition(c) & 16);
        }else{
            var parent = c.parentNode;
            while (parent) {
                if (parent == p) {
                    return true;
                }
                else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
                    return false;
                }
                parent = parent.parentNode;
            }
            return false;
        }
    },
	
	getRegion : function(el){
        return Util.Region.getRegion(el);
    }, 
	
	getY : function(el){
        return this.getXY(el)[1];
    },

    getX : function(el){
        return this.getXY(el)[0];
    },

    getXY : function(el){ // this initially used Position.cumulativeOffset but it is not accurate enough
        var p, pe, b, scroll, bd = (document.body || document.documentElement);

        if(el == bd){
            return [0, 0];
        }
        if (el.getBoundingClientRect) {
            b = el.getBoundingClientRect();
            scroll = Element.getScroll(document);
            return [b.left + scroll.left, b.top + scroll.top];
        }
        var x = 0, y = 0;

        p = el;

        var hasAbsolute = $(el).getStyle("position") == "absolute";
        while (p) {

            x += p.offsetLeft;
            y += p.offsetTop;

            if (!hasAbsolute && $(p).getStyle("position") == "absolute") {
                hasAbsolute = true;
            }

            if (Browser.Gecko) {
                pe = $(p);

                var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
                var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;


                x += bl;
                y += bt;


                if (p != el && pe.getStyle('overflow') != 'visible') {
                    x += bl;
                    y += bt;
                }
            }
            p = p.offsetParent;
        }
        if (Browser.Safari && hasAbsolute) {
            x -= bd.offsetLeft;
            y -= bd.offsetTop;
        }
        if (Browser.Gecko && !hasAbsolute) {
            var dbd = $(bd);
            x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
            y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
        }
        p = el.parentNode;
        while (p && p != bd) {
            if (!Browser.Opera || (p.tagName != 'TR' && $(p).getStyle("display") != "inline")) {
                x -= p.scrollLeft;
                y -= p.scrollTop;
            }
            p = p.parentNode;
        }
        return [x, y];
    },
	
	setXY : function(el, xy){ // this initially used Position.cumulativeOffset but it is not accurate enough
        el = $(el);
        el.position();
        var pts = el.translatePoints(xy);
        if(xy[0] !== false){
            el.style.left = pts.left + "px";
        }
        if(xy[1] !== false){
            el.style.top = pts.top + "px";
        }
    },

    setX : function(el, x){
        this.setXY(el, [x, false]);
    },

    setY : function(el, y){
        this.setXY(el, [false, y]);
    },

	addUnits: function(v, defaultUnit){
		if(v === "" || v == "auto" || typeof(v) === "undefined") return v;
		if(typeof(v) == "number" || !this.unitPattern.test(v)){
			return v + (defaultUnit || 'px');
		}
		return v;
	},
	
	unitPattern: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i
};

Util.Region = function(t, r, b, l) {
    this.top = t;
    this[1] = t;
    this.right = r;
    this.bottom = b;
    this.left = l;
    this[0] = l;
};

Util.Region.prototype = {
    contains : function(region) {
        return ( region.left   >= this.left   &&
                 region.right  <= this.right  &&
                 region.top    >= this.top    &&
                 region.bottom <= this.bottom    );
    },
    getArea : function() {
        return ( (this.bottom - this.top) * (this.right - this.left) );
    },
    intersect : function(region) {
        var t = Math.max( this.top,    region.top    );
        var r = Math.min( this.right,  region.right  );
        var b = Math.min( this.bottom, region.bottom );
        var l = Math.max( this.left,   region.left   );

        if (b >= t && r >= l) {
            return new Util.Region(t, r, b, l);
        } else {
            return null;
        }
    },
    union : function(region) {
        var t = Math.min( this.top,    region.top    );
        var r = Math.max( this.right,  region.right  );
        var b = Math.max( this.bottom, region.bottom );
        var l = Math.min( this.left,   region.left   );

        return new Util.Region(t, r, b, l);
    },
    constrainTo : function(r) {
            this.top = this.top.constrain(r.top, r.bottom);
            this.bottom = this.bottom.constrain(r.top, r.bottom);
            this.left = this.left.constrain(r.left, r.right);
            this.right = this.right.constrain(r.left, r.right);
            return this;
    },
    adjust : function(t, l, b, r){
        this.top += t;
        this.left += l;
        this.right += r;
        this.bottom += b;
        return this;
    }
};

Util.Region.getRegion = function(el) {
    var p = Util.Dom.getXY(el);

    var t = p[1];
    var r = p[0] + el.offsetWidth;
    var b = p[1] + el.offsetHeight;
    var l = p[0];

    return new Util.Region(t, r, b, l);
};

Util.Point = function(x, y) {
   if (x instanceof Array) {
      y = x[1];
      x = x[0];
   }
    this.x = this.right = this.left = this[0] = x;
    this.y = this.top = this.bottom = this[1] = y;
};

Util.Point.prototype = new Util.Region();
 
(function(){
		  
// speedy lookup for elements never to box adjust
var noBoxAdjust = Browser.Strict ? {
    select:1
} : {
    input:1, select:1, textarea:1
};
if(Browser.IE || Browser.Gecko){
    noBoxAdjust['button'] = 1;
}

var unselectableFn = function(e){ Event.stop(e) }.bindAsEventListener(this);

Element.addMethods({
	position : function(element, pos, zIndex, x, y){
		element = $(element);
        if(!pos){
           if(element.getStyle('position') == 'static'){
               element.setStyle({'position': 'relative'});
           }
        }else{
            element.setStyle({"position": pos});
        }
        if(zIndex){
            element.setStyle({"z-index": zIndex});
        }
        if(x !== undefined && y !== undefined){
            element.setXY([x, y]);
        }else if(x !== undefined){
            element.setX(x);
        }else if(y !== undefined){
            element.setY(y);
        }
    },
    getX : function(el){
        return Util.Dom.getX(el);
    },
    getY : function(el){
        return Util.Dom.getY(el);
    },
    getXY : function(el){ // this initially used Position.cumulativeOffset but it is not accurate enough
        return Util.Dom.getXY(el);
    },
	setX : function(el, x){
        Util.Dom.setX(el, x);
        return el;
    },
	setY : function(el, y){
        Util.Dom.setY(el, y);
        return el;
    },
	setXY : function(el, xy){
        Util.Dom.setXY(el, xy);
        return el;
    },
    getTop : function(el, local) {
		el = $(el);
        if(!local){
            return el.getY();
        }else{
            return parseInt(el.getStyle("top"), 10) || 0;
        }
    },
    getLeft : function(el, local){
		el = $(el);
        if(!local){
            return el.getX();
        }else{
            return parseInt(el.getStyle("left"), 10) || 0;
        }
    },
	translatePoints : function(element, x, y){
		element = $(element);
        if(typeof x == 'object' || x instanceof Array){
            y = x[1]; x = x[0];
        }
        var p = element.getStyle('position');
        var o = element.getXY();

        var l = parseInt(element.getStyle('left'), 10);
        var t = parseInt(element.getStyle('top'), 10);

        if(isNaN(l)){
            l = (p == "relative") ? 0 : element.offsetLeft;
        }
        if(isNaN(t)){
            t = (p == "relative") ? 0 : element.offsetTop;
        }

        return {left: (x - o[0] + l), top: (y - o[1] + t)};
    },
	getScroll: function(element) {
		element = $(element);
		var doc = document;
        if(element == doc || element == doc.body){
            var l, t;
            if(Browser.IE && Browser.Strict){
                l = doc.documentElement.scrollLeft || (doc.body.scrollLeft || 0);
                t = doc.documentElement.scrollTop || (doc.body.scrollTop || 0);
            }else{
                l = window.pageXOffset || (doc.body.scrollLeft || 0);
                t = window.pageYOffset || (doc.body.scrollTop || 0);
            }
            return {left: l, top: t};
        }else{
            return {left: element.scrollLeft, top: element.scrollTop};
        }
	},
	getRegion : function(el){
        return Util.Dom.getRegion(el);
    },
	getWidth : function(el, contentWidth){
		el = $(el);
        var w = el.offsetWidth || 0;
        w = contentWidth !== true ? w : w-el.getBorderWidth("lr")-el.getPadding("lr");
        return w < 0 ? 0 : w;
    },
	getHeight : function(el, contentHeight){
		el = $(el);
        var h = el.offsetHeight || 0;
        h = contentHeight !== true ? h : h-el.getBorderWidth("tb")-el.getPadding("tb");
        return h < 0 ? 0 : h;
    },
	getComputedHeight : function(el){
		el = $(el);
        var h = Math.max(el.offsetHeight, el.clientHeight);
        if(!h){
            h = parseInt(el.getStyle('height'), 10) || 0;
            if(!el.isBorderBox()){
                h += el.getFrameWidth('tb');
            }
        }
        return h;
    },
    getComputedWidth : function(el){
		el = $(el);
        var w = Math.max(el.offsetWidth, el.clientWidth);
        if(!w){
            w = parseInt(el.getStyle('width'), 10) || 0;
            if(!el.isBorderBox()){
                w += el.getFrameWidth('lr');
            }
        }
        return w;
    },
	getFrameWidth : function(el, sides, onlyContentBox){
        return onlyContentBox && Browser.BorderBox ? 0 : (el.getPadding(sides) + el.getBorderWidth(sides));
    }, 
	getBorderWidth : function(el, side){
        return $(el).addStyles(side, {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"});
    },
	getPadding : function(el, side){
        return $(el).addStyles(side, {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"});
    },
	getMargins : function(el, side){
		el = $(el);
        if(!side){
            return {
                top: parseInt(el.getStyle("margin-top"), 10) || 0,
                left: parseInt(el.getStyle("margin-left"), 10) || 0,
                bottom: parseInt(el.getStyle("margin-bottom"), 10) || 0,
                right: parseInt(el.getStyle("margin-right"), 10) || 0
            };
        }else{
            return el.addStyles(side, {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"});
        }
    },
	// private
    addStyles : function(el, sides, styles){
		el = $(el);
        var val = 0, v, w;
        for(var i = 0, len = sides.length; i < len; i++){
            v = el.getStyle(styles[sides.charAt(i)]);
            if(v){
                 w = parseInt(v, 10);
                 if(w){ val += (w >= 0 ? w : -1 * w); }
            }
        }
        return val;
    },
	setSize : function(el, width, height){
		el = $(el);
        if(typeof width == "object"){ // in case of object from getSize()
            height = width.height; width = width.width;
        }
        width = el.adjustWidth(width); height = el.adjustHeight(height);
        el.style.width = Util.Dom.addUnits(width);
        el.style.height = Util.Dom.addUnits(height);
        return el;
    },
	// private
    adjustWidth : function(el, width){
		el = $(el);
        if(typeof width == "number"){
            if(!el.isBorderBox()){
               width -= (el.getBorderWidth("lr") + el.getPadding("lr"));
            }
            if(width < 0) width = 0;
        }
        return width;
    },
    // private
    adjustHeight : function(el, height){
		el = $(el);
        if(typeof height == "number"){
           if(!el.isBorderBox()){
               height -= (el.getBorderWidth("tb") + el.getPadding("tb"));
           }
           if(height < 0) height = 0;
        }
        return height;
    },
	isBorderBox : function(el){
        return noBoxAdjust[$(el).tagName.toLowerCase()] || Browser.BorderBox;
    },
	mask : function(element, cls, msg, indicator){
		element = $(element);
		element.position();
		//if(element.getStyle("position") == "static") element.setStyle({"position":"relative"});
        if(element._maskMsg) Element.remove(element._maskMsg);
        if(element._mask) Element.remove(element._mask);
		if(typeof(indicator) == 'undefined') indicator = true;
		
		var mask = $(document.createElement("div"));
		element.addClassName("x-masked").appendChild(mask);
		mask.className = "x-element-mask";
		mask.innerHTML = "<span></span>";
        mask.show(true);

		var maskMsg = $(document.createElement('div'));
		element.appendChild(maskMsg);
		if(typeof msg == 'string') {
			if (indicator) maskMsg.className = (indicator ? 'x-element-mask-loading' : 'x-element-mask-msg');
			maskMsg.update(msg);
		} else if (indicator) {
			maskMsg.className = 'x-element-mask-indicator-inline';
		}
		if (cls) {
			maskMsg.className = cls;
		}
		maskMsg.show();
        maskMsg.center(element);

		if(Browser.IE && !(Browser.IE7 && Browser.Strict)/* && element.getStyle('height') == 'auto'*/){ // ie will not expand full height automatically
			//mask.setXY([mask.getX() - element.getBorderWidth("l") - element.getPadding("l"), mask.getY()]);
            mask.setSize(element.getWidth(), element.getHeight());
        }

		element._mask = mask;
		element._maskMsg = maskMsg;
		
        return element;
	},
	unmask : function(element){
		element = $(element);
        if(element._mask){
            if(element._maskMsg){
                Element.remove(element._maskMsg);
                //delete element._maskMsg;
            }
            Element.remove(element._mask);
            //delete element._mask;
        }
        return element.removeClassName("x-masked");
    },
	isMasked : function(el){
		el = $(el);
        return el._mask && el._mask.visible();
    },
	alignTo : function(el, toEl, position, offsets){
		el = $(el);
        var xy = el.getAlignToXY(toEl, position, offsets);
        el.setXY(xy);
        return el;
    },
	center : function(el, centerIn){
        $(el).alignTo(centerIn || Util.Dom.getBody(), 'c-c');
        return el;
    },
	getAnchorXY : function(element, anchor, local, s){
		element = $(element);

        var w, h, vp = false;
        if(!s){
            if(element == document.body || element == document){
                vp = true;
                w = Util.Dom.getViewWidth(); h = Util.Dom.getViewHeight();
            }else{
                w = element.getWidth(); h = element.getHeight();
            }
        }else{
            w = s.width;  h = s.height;
        }
        var x = 0, y = 0, r = Math.round;
        switch((anchor || "tl").toLowerCase()){
            case "c":
                x = r(w*.5);
                y = r(h*.5);
            break;
            case "t":
                x = r(w*.5);
                y = 0;
            break;
            case "l":
                x = 0;
                y = r(h*.5);
            break;
            case "r":
                x = w;
                y = r(h*.5);
            break;
            case "b":
                x = r(w*.5);
                y = h;
            break;
            case "tl":
                x = 0;
                y = 0;
            break;
            case "bl":
                x = 0;
                y = h;
            break;
            case "br":
                x = w;
                y = h;
            break;
            case "tr":
                x = w;
                y = 0;
            break;
        }
        if(local === true){
            return [x, y];
        }
        if(vp){
            var sc = element.getScroll();
            return [x + sc.left, y + sc.top];
        }
        //Add the element's offset xy
        var o = element.getXY();
        return [x+o[0], y+o[1]];
    },

    getAlignToXY : function(el, toEl, p, o){
		el = $(el);
        toEl = $(toEl);
        if(!toEl){
            throw "Element.alignTo with an element that doesn't exist";
        }
        var c = false; //constrain to viewport
        var p1 = "", p2 = "";
        o = o || [0,0];

        if(!p){
            p = "tl-bl";
        }else if(p == "?"){
            p = "tl-bl?";
        }else if(p.indexOf("-") == -1){
            p = "tl-" + p;
        }
        p = p.toLowerCase();
        var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
        if(!m){
           throw "Element.alignTo with an invalid alignment " + p;
        }
        p1 = m[1]; p2 = m[2]; c = !!m[3];

        //Subtract the aligned el's internal xy from the target's offset xy
        //plus custom offset to get the aligned el's new offset xy
        var a1 = el.getAnchorXY(p1, true);
        var a2 = toEl.getAnchorXY(p2, false);

        var x = a2[0] - a1[0] + o[0];
        var y = a2[1] - a1[1] + o[1];

        if(c){
            //constrain the aligned el to viewport if necessary
            var w = el.getWidth(), h = el.getHeight(), r = toEl.getRegion();
            // 5px of margin for ie
            var dw = Util.Dom.getViewWidth()-5, dh = Util.Dom.getViewHeight()-5;

            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
            //perpendicular to the vp border, allow the aligned el to slide on that border,
            //otherwise swap the aligned el to the opposite border of the target.
            var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
			var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
			var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
			var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
			
			var doc = document;
			var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
			var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
			
			if((x+w) > dw + scrollX){
				x = swapX ? r.left-w : dw+scrollX-w;
			}
			if(x < scrollX){
			    x = swapX ? r.right : scrollX;
			}
			if((y+h) > dh + scrollY){
				y = swapY ? r.top-h : dh+scrollY-h;
			}
			if (y < scrollY){
			    y = swapY ? r.bottom : scrollY;
			}
        }
        return [x,y];
    },
	
	fitToParent: function(el, parentEl){
		el = $(el);
		parentEl = $(parentEl || el.parentNode);
		el.setSize(parentEl.getComputedWidth() - parentEl.getFrameWidth("lr"), parentEl.getComputedHeight() - parentEl.getFrameWidth("tb"));
		return el;
	},
	
	cumulativeOffset: function(el) {
		var valueT = 0, valueL = 0;
		do {
			valueT += el.offsetTop  || 0;
			valueL += el.offsetLeft || 0;
			el = el.offsetParent;
		} while (el);
		return Element._returnOffset(valueL, valueT);
	},

	cumulativeScrollOffset: function(el) {
		var valueT = 0, valueL = 0;
		do {
			valueT += el.scrollTop  || 0;
			valueL += el.scrollLeft || 0;
			el = el.parentNode;
		} while (el);
		return Element._returnOffset(valueL, valueT);
	},
	
	positionedOffset: function(el) {
		var valueT = 0, valueL = 0;
		do {
			valueT += el.offsetTop  || 0;
			valueL += el.offsetLeft || 0;
			el = el.offsetParent;
			if (el) {
				if (el.tagName == 'BODY') break;
				var p = Element.getStyle(el, 'position');
				if (p == 'relative' || p == 'absolute') break;
			}
		} while (el);
		return Element._returnOffset(valueL, valueT);
	},
	
	absolutize: function(el) {
		el = $(el);
		if (el.getStyle('position') == 'absolute') return;
		// Position.prepare(); // To be done manually by Scripty when it needs it.
		
		var offsets = el.positionedOffset();
		var top     = offsets[1];
		var left    = offsets[0];
		var width   = el.clientWidth;
		var height  = el.clientHeight;
		
		el._originalLeft   = left - parseFloat(el.style.left  || 0);
		el._originalTop    = top  - parseFloat(el.style.top || 0);
		el._originalWidth  = el.style.width;
		el._originalHeight = el.style.height;
		
		el.style.position = 'absolute';
		el.style.top    = top + 'px';
		el.style.left   = left + 'px';
		el.style.width  = width + 'px';
		el.style.height = height + 'px';
		return el;
	},
	
	relativize: function(el) {
		el = $(el);
		if (el.getStyle('position') == 'relative') return;
		// Position.prepare(); // To be done manually by Scripty when it needs it.
		
		el.style.position = 'relative';
		var top  = parseFloat(el.style.top  || 0) - (el._originalTop || 0);
		var left = parseFloat(el.style.left || 0) - (el._originalLeft || 0);
		
		el.style.top    = top + 'px';
		el.style.left   = left + 'px';
		el.style.height = el._originalHeight;
		el.style.width  = el._originalWidth;
		return el;
	},

	/**
     * Sets up event handlers to call the passed functions when the mouse is over this element. Automatically
     * filters child element mouse events.
     * @param {Element} el
     * @param {Function} overFn
     * @param {Function} outFn
     * @param {Object} scope (optional)
     * @return {Element} el
     */
    hover : function(el, overFn, outFn, scope){
		el = $(el);
        var preOverFn = function(event){
            if(!Event.within(event, el, true)){
                overFn.apply(scope || this, arguments);
            }
        };
        var preOutFn = function(event){
            if(!Event.within(event, el, true)){
                outFn.apply(scope || this, arguments);
            }
        };
        el.observe("mouseover", preOverFn);
        el.observe("mouseout",  preOutFn);
        return el;
    }, 
	
	/**
     * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
     * @param {String/Array} className The CSS class to add, or an array of classes
     * @return {Element} el
     */
    radioClass : function(el, className){
		el = $(el);
        var siblings = el.siblings();
        for(var i = 0; i < siblings.length; i++) {
        	var s = siblings[i];
        	if(s.nodeType == 1){
        	    $(s).removeClassName(className);
        	}
        }
        el.addClassName(className);
        return el;
    },
	
	makeUnselectable : function(el){
		el = $(el);
		el.unselectable = "on";
		el.setStyle({"-moz-user-select": "none", "-khtml-user-select": "none"});
		el.addClassName("x-unselectable");
		if (!el._unselectableByPrototype) {
			el.observe("selectstart", unselectableFn);
			el._unselectableByPrototype = true;
		}
		return el;
	},
	
	makeSelectable : function(el){
		el = $(el);
		el.unselectable = "";
		el.setStyle({"-moz-user-select": "", "-khtml-user-select": ""});
		el.removeClassName("x-unselectable");
		if (el._unselectableByPrototype) {
			el.stopObserving("selectstart", unselectableFn);
			el._unselectableByPrototype = false;
		}
		return el;
	},
	
	repaint : function(el){
		el = $(el);
		el.addClassName("x-repaint");
		setTimeout(function(){
			el.removeClassName("x-repaint");
		}, 1);
		return el;
	},

	_remove : Browser.IE ? function(){
		var d;
		return function(element){
			if(element){
				d = d || document.createElement('div');
				d.appendChild(element);
				d.innerHTML = '';
			}
		}
	}() : function(element){
		if(element && element.parentNode){
			element.parentNode.removeChild(element);
		}
	},
	
	remove: function(element) {
		if (!element) return;
		
		var a = element.attributes, i, l, n;
		if (a) {
			l = a.length;
			for (i = 0; i < l; i++ ) {
				n = a[i].name;
				if (typeof element[n] === 'function') {
					element[n] = null;
				}
			}
		}
		a = element.childNodes;
		if (a) {
			l = a.length;
			for (i = 0; i < l; i++ ) {
				if (a = element.childNodes[i]){
					Element.remove(a);
				}
			}
		}
		Element._remove(element);
	}

});

Element._returnOffset = function(l, t) {
	var result = [l, t];
	result.left = l;
	result.top = t;
	return result;
};

Object.extend(Event, {
	/**
	 * Returns true if the target of this event equals el or is a child of el
	 * @param {Mixed} el
	 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
	 * @return {Boolean}
	 */
	within : function(event, el, related){
		var t = Event[related ? "getRelatedTarget" : "element"](event);
		return t && Util.Dom.isAncestor(el, t);
	},
	/**
	 * Gets the related target.
	 * @return {HTMLElement}
	 */
	getRelatedTarget : function(event){ // missing from prototype? 
        var t = event.relatedTarget;
        if (!t) {
            if (event.type == "mouseout") {
                t = event.toElement;
            } else if (event.type == "mouseover") {
                t = event.fromElement;
            }
        }

        return Event.resolveTextNode(t); 
	},
	
	resolveTextNode: function(node) {
        if (node && 3 == node.nodeType) {
            return node.parentNode;
        } else {
            return node;
        }
    }

});

})();

var Overlay = function(){
	var element, opacity;
	
	function getElement(){
		if (!element) {
			element = $(document.createElement('div'));
			Util.Dom.getBody().appendChild(element);
			element.setSize(Util.Dom.getViewWidth(true), Util.Dom.getViewHeight(true));
			element.addClassName('x-overlay');
			opacity = element.getOpacity();
			element.setOpacity(0).hide();
			Event.observe(window, "resize", function(){
				element.setSize(Util.Dom.getViewWidth(true), Util.Dom.getViewHeight(true));
			});
		}
		return element;
	}
	
	return {
		show: function(animate, options){
			var element = getElement();
			element.show();
			if (animate && Effect){
				var defaultOptions = {
					duration: 0.2
				};
				options = Object.extend(
					Object.extend(
						defaultOptions, options || {}
					), {
						from: 0,
						to: opacity
					}
				);
				new Effect.Opacity(element, options);
			} else {
				element.setOpacity(opacity);
			}
		},
		hide: function(animate, options){
			var element = getElement();
			if (animate && Effect){
				var defaultOptions = {
					duration: 0.2
				};
				options = Object.extend(
					Object.extend(
						defaultOptions, options || {}
					), {
						from: opacity,
						to: 0
					}
				);
				function afterFinish(){
					element.hide();
				}
				if (typeof options.afterFinish == 'function') {
					options.afterFinish.wrap(function(fn){
						afterFinish();
						fn();
					});
				} else {
					options.afterFinish = afterFinish;
				}
				
				new Effect.Opacity(element, options);
			} else {
				element.setOpacity(0).hide();
			}
		}
	}
}();
