function MenuCloser() {
	this.subs = new Array();
	this.subscribe = function(obj) {
		this.subs.push(obj);
	};
	this.handler = function(e) {
		jQuery.each(this.subs, function() {
			this.checkForClose(e.target);
		});
	};
	var obj = this;
	$(document.body).bind('mouseover', function(e){obj.handler(e);});
}

function Menu() {}
function Menu(activator, menu, closer, isRightAligned, isHoverable) {
	this.isOpen = false;
	this.isRightAligned = isRightAligned;
	this.isHoverable = isHoverable;
	this.activator = activator;
	this.menu = menu;
	this.closer = closer;
	this.closer.subscribe(this);
	this.closeTimer = null;
	
	this.checkForClose = function(target) {
	    if(!this.isOpen) {
	        return;
        }
        if(target == this.activator.get(0) || target == this.menu.get(0)) {
            return;
        }
        var c = allChildren(this.menu).get();
        for(var i = 0; i < c.length; i++) {
            if(target == c[i]) {
                return;
            }
        }
	    this.delayedClose();
	};
	this.delayedClose = function() {
	    var obj = this;
	    if(!this.closeTimer) {
	        this.closeTimer = setTimeout(function(){obj.close()}, 400);
        }
    }
    this.cancelClose = function() {
        if(this.closeTimer) {
            clearTimeout(this.closeTimer);
            this.closeTimer = null;
            this.open();
        }
    }
	this.close = function() {
		this.menu.css('display','none');
		this.isOpen = false;
	};
	this.open = function() {
		var offset = this.activator.offset();
		var top = offset.top + this.activator.outerHeight();
		var left = offset.left;
		if(this.isRightAligned) {
			left += this.activator.outerWidth() - this.menu.outerWidth();
		}
		this.menu.css('position','absolute').css('left',left).css('top',top);
		var l = this.menu.offset();
		this.menu.css('display','block');
		this.isOpen = true;
	};
	this.toggle = function() {
        if(this.isOpen) {
            this.close();
        }
        else {
            this.open();
        }
    };

    var obj = this;
	if(isHoverable) {
		this.activator.bind('mouseover', function() { obj.cancelClose(); obj.open(); });
		this.activator.bind('click', function() { return false; });
	}
	else {
		this.activator.bind('click', function() { obj.toggle(); return false; });
	}
	this.menu.bind('mouseover', function() { obj.cancelClose(); });
}

function addFocusHighlight(obj, className) {
	obj.bind("focus", function() { obj.addClass(className); });
	obj.bind("blur", function() { obj.removeClass(className); });
}

function allChildren(container) {
    var results = container;
    var current = container;
    while(1) {
        var more = current.children();
        if(0 == more.length) {
            break;
        }
        results = results.add(more);
        current = more;
    }
    return results;
}


