function ContextMenu()
{
	this.id             	= '__CTXMENU_'+Math.ceil(Math.random()*10000);
    this.validType      	= ["chkbox", "radio", "html", "separator", "default"];
    this.noCloseT       	= ["chkbox", "radio"];
	this.onBeforeDisplay   	= null;
    this.onAfterDisplay   	= null;
    this.onHide				= null;	
	this.init();
	
	addEvent(document, 'mousedown', this, 'EventJSonMouseDown');
	addEvent(document, 'mousemove', this, 'EventJSonMouseMove');	
	addEvent(document, 'click', this, 'EventJSonMouseClick');	
};

ContextMenu.prototype.init = function()
{
	this.mouse				= {_x:0,_y:0};
	this.itemCount      	= 0;
	this.mouseOverLevel 	= 0;
	this.mouseOverItem		= null;
	this.opener         	= null; 
    this.idxByName      	= new Object();
	this.hasScroll			= new Array();
	this.scrollBTN			= new Array();
	this.scrollInterval		= new Array();
	this.offsetTop			= new Array();
	this.refreshItemBounds 	= new Array();
	this.openedLevels   	= new Array();
    this.levelStorage   	= new Array();
	this.containerList		= new Array();
	this.itemList       	= new Array();	
};

/**
 * Új elemet ad hozzá a menühöz
 * 
 * @param Object param  - name: Ezen a néven lehet hivatkozni majd az elemre, ha nincs megadva akkor az "item_elemSorszáma" lesz
 *                      - type: Elem típus lehet chkbox, radio, html, default, ha nincs megadva a deafult lesz az értéke
 *                      - onClickClose: Ha az elemre kattintanak bezárja-e a menut
 *                      - content: ez fog a menuitemben szerepelni
 *                      - cb: onclick eseményre lefutot callback függvény
 *                      - className: ha egyéni class-t akarunk adni egy elemnek
 *                      - img: ha egy képet szeretnék a bal oldalán az elemnek
 *                      - sub: ha rendelkezik submenuvel akkor a level számát megadva azt a menut jeleníti meg
 *                      - level: melyik szinten helyezkedjen el az adott menu
 *                      - radioGroup: ha radiobutton típusú itemekte csoportosítani kell akkor azok lesznek egy csoportba ahol ez az érték ugyanaz lehetőleg string legyen és egyedi
 *                      - selected: ha chkbox vagy radio akkor lehet ezt megadni és értelem szerűen true/false 
 *                      - disabled: ha true akkor a menu nem kattintható
 *                      - visible: true/false
 *
 * @return Object Az aktuális elem objektuma
 */
ContextMenu.prototype.addItem = function(param)
{
    var name        = (isEmpty(param.name)          ? 'item_'+this.itemCount    : param.name);
    var type        = (isEmpty(param.type)          ? 'default'                 : (this.validType.exists(param.type.toLowerCase()) ? param.type.toLowerCase() : 'default'));
    var close       = (isEmpty(param.onClickClose)  ? (this.noCloseT.exists(type.toLowerCase()) ? false : true) : param.onClickClose);
    var content     = (isEmpty(param.content)       ? name+': Undefined Content': param.content);
    var cb          = (param.cb instanceof CallBack ? param.cb                  : null);
    var className   = (isEmpty(param.className)     ? ''                        : param.className);
    var img         = (isEmpty(param.img)           ? null                      : param.img);
    var sub         = (isEmpty(param.sub)           ? false                     : param.sub);
    var level       = (isEmpty(param.level)         ? 0                         : param.level);
    var radioGroup  = (isEmpty(param.radioGroup)    ? null                      : param.radioGroup);
    var selected    = (isEmpty(param.selected)      ? false                     : param.selected);
    var disabled    = (isEmpty(param.disabled)      ? false                     : param.disabled);
    var visible     = (isEmpty(param.visible)       ? true                      : param.visible);
	
	if(!isObject(this.itemList[level])) this.itemList[level] = {};
    this.level(level).addLevel(sub);
	
	this.itemList[level][name] = new this.item({
		name        : name,
        type        : type,
        close       : close,
        content     : content,
        cb          : cb,
        className   : className,
        icon        : img,
        sub         : sub,
        level       : level,
        radioGroup  : radioGroup,
        selected    : selected,
        disabled    : disabled,
        visible     : visible,
		parent		: this
	});
	
	this.itemCount++;
    this.idxByName[name] = {level:level,name:name};
	
	return this.itemList[level][name];
};

ContextMenu.prototype.getItem = function()
{
	var args = arguments;
	var argv = args.length;
	if(argv == 2)
	{
		if(!isUndefined(this.itemList[args[0]]))
			if(!isUndefined(this.itemList[args[0]][args[1]]))
				return this.itemList[args[0]][args[1]];
	}
	else if(argv == 1 && isString(args[0]))
	{
		if( (idx = this.idxByName[args[0]]) )
			return this.getItem(idx.level, idx.name);
	}
	return null;
};

ContextMenu.prototype.getOpener = function()
{
	return this.opener;
};

ContextMenu.prototype.display = function(level, x, y)
{
    level 	= isEmpty(level)	? 0 : parseInt(level, 10);
	x		= isEmpty(x)		? this.mouse._x : x;
	y		= isEmpty(y)		? this.mouse._y : y;
	var id  = this.id+'-level-'+level;
	this.openedLevels[level] = true;
	this.offsetTop[level] = 0;
	var self = this;
	this.hasScroll[level] = false;
	
	if(this.onBeforeDisplay instanceof CallBack) this.onBeforeDisplay.execute(this);
	
	if(!$(id))
	{
		var iframe						= create.iframe(id+'iframe', true, null, {position:'absolute', display:'none'}, document.getElementsByTagName('body').item(0));
		this.containerList[level] 		= create.div(id, null, 'ctx-menu', {left:x+'px', top:y+'px', display:'block', visibility:'hidden'}, document.getElementsByTagName('body').item(0));
		this.containerList[level].table = create.table(null, null, {height:'auto'}, this.containerList[level], {cellPadding:'0',cellSpacing:'0'});
		this.containerList[level].onMouseOut = function(){ self.stopScroll(level); }
	
		if(!isUndefined(this.itemList[level]))
			for(var k in this.itemList[level])
			{
				if(isFunction(this.itemList[level][k])) continue;
				this.containerList[level].table.appendChild(this.itemList[level][k].container);
				this.itemList[level][k].setChecked(this.itemList[level][k].data.selected);	
			}
	}
	
	if(isUndefined(this.refreshItemBounds[level]))
		this.refreshItemBounds[level] = true;
	
	if(!isUndefined(this.itemList[level]))
	{
		if( $(id) )
		{
			this.containerList[level].setStyle({left:x+'px', top:y+'px', display:'block', visibility:'hidden', height:'auto'});
			this.containerList[level].bound	= getBounds(this.containerList[level]);
			var iframe = $(id+'iframe');
			this.containerList[level].table.parentNode.style.marginTop = '0px';
			
			if(isElement(this.containerList[level].scrollBtnTop))
				this.containerList[level].scrollBtnTop.del();
			if(isElement(this.containerList[level].scrollBtnBottom))
				this.containerList[level].scrollBtnBottom.del();
		}
		
		this.containerList[level].table.parentNode.height = this.containerList[level].table.parentNode.offsetHeight;
		var cntBound = this.containerList[level].bound	= getBounds(this.containerList[level]);
		var height = getWindowHeight();
		
		if(cntBound._y + cntBound.height >= height + getScrollTop() && height >= cntBound.height)
		{
			this.containerList[level].style.top = height + getScrollTop() - cntBound.height + 'px';
			cntBound._y = height + getScrollTop() - cntBound.height;
		}
		else if( height < cntBound.height )
		{
			this.containerList[level].style.top 	= getScrollTop()+'px';
			this.containerList[level].style.height 	= height-2 + 'px';
			cntBound._y = getScrollTop();
			cntBound.height = height-2;
			
			this.hasScroll[level] = true;
			
			this.containerList[level].scrollBtnTop = create.div(null, null, 'scroll-btn scroll-top', null, this.containerList[level]);
			this.containerList[level].scrollBtnBottom = create.div(null, null, 'scroll-btn scroll-bottom', null, this.containerList[level]);
			
			
			var startScrollTop		= function(){ self.startScroll(level, 22, 40); };
			var endScrollTop		= function(){ self.stopScroll(level); };
			var startScrollBottom	= function(){ self.startScroll(level, -22, 40); };
			var endScrollBottom		= function(){ self.stopScroll(level); };
			this.scrollBTN[level] = new Array();
			this.scrollBTN[level].push({bound:getBounds(this.containerList[level].scrollBtnTop),startScroll:startScrollTop, endScroll:endScrollTop});
			this.scrollBTN[level].push({bound:getBounds(this.containerList[level].scrollBtnBottom),startScroll:startScrollBottom, endScroll:endScrollBottom});
		}
		
		if(this.refreshItemBounds[level])
		{
			for(var k in this.itemList[level])
				if(!isFunction(this.itemList[level][k]))
					this.itemList[level][k].bound = getBounds(this.itemList[level][k].container);
			
			this.refreshItemBounds[level] = false;
		}
				
		if(this.hasScroll[level])
			this.scroll(level, 22);
			
		this.containerList[level].expand = true;
		if(isElement(iframe))
			iframe.setStyle({width:this.containerList[level].bound.width+'px', height:this.containerList[level].bound.height+'px', top:this.containerList[level].bound._y+'px', left:this.containerList[level].bound._x+'px'});
		this.containerList[level].setStyle({visibility:'visible'});
	}
	
	if(this.onAfterDisplay instanceof CallBack) this.onAfterDisplay.execute(this);
};

ContextMenu.prototype.hide = function(level, onlyChild, del)
{
    level 		= isEmpty(level)	? 0 : parseInt(level, 10);
	var child   = this.level(level).getChildList();
	
	if(isArray(child))
        for(var i=0 ; i<child.length ; i++)
            this.hide(child[i]);
    
    if(isElement(this.containerList[level]) && !onlyChild)
    {
        this.openedLevels[level] = false;
        
        if(!del)
		{
			this.containerList[level].style.display = 'none';
			this.containerList[level].expand = false;
			var ifr = $(this.containerList[level].id+'iframe');
			if(isElement(ifr))
				ifr.style.display = 'none';
		}
		else
		{
			this.containerList[level].del();
			var ifr = $(this.containerList[level].id+'iframe');
			if(isElement(ifr))
				ifr.del();			
			delete this.containerList[level];
			delete this.itemList[level];
		}
    }
	
	if(this.onHide instanceof CallBack) this.onHide.execute(this);
};

ContextMenu.prototype.assign = function(obj)
{
    if(isElement(obj))
	{
		obj.contextMenu = this;
		obj.oncontextmenu = this.EventJSonContextMenu;
	}
};

ContextMenu.prototype.EventHTMLonContextMenu = function(e)
{
    var evt = e || window.event;
	this.opener = (window.event) ? evt.srcElement : evt.target;
	this.display(0);
	return false;
};

ContextMenu.prototype.EventJSonContextMenu = function(e)
{
    var evt = e || window.event;
	this.contextMenu.opener = (window.event) ? evt.srcElement : evt.target;
	this.contextMenu.display(0);
	return false;
};

ContextMenu.prototype.EventJSonMouseDown = function(e)
{
    if(isBoolean(this.mouseOverLevel) && this.mouseOverLevel == false)
	{
		this.refreshItemBounds = new Array();
		this.hide(0);
    }
};
             
ContextMenu.prototype.EventJSonMouseClick = function(e)
{
	if(this.mouseOverItem instanceof this.item)
		if(isFunction(this.mouseOverItem.onClick))
			this.mouseOverItem.onClick(e);
};

ContextMenu.prototype.EventJSonMouseMove = function(e)
{
    var openedLevels 	= this.openedLevels.filter(function(v){return v}, true);
	this.mouse 			= getMouseCoords(e);
	this.mouseOverLevel = false;
	this.mouseOverItem 	= null;
	
	if(openedLevels.length)
	{
		for(var k in openedLevels)
		{
			if(isFunction(openedLevels[k])) continue;
			var level = k;
			if(pointInBox(this.containerList[level].bound, this.mouse))
			{
				this.mouseOverLevel = level;				
			}
			else
			{
				this.containerList[level].onMouseOut();
			}
		}
		
		if(this.hasScroll[this.mouseOverLevel])
		{
			for(var i=0 ; i<this.scrollBTN[this.mouseOverLevel].length ; i++)
			{
				if(pointInBox(this.scrollBTN[this.mouseOverLevel][i].bound, this.mouse))
				{
					if( !(this.scrollInterval[this.mouseOverLevel] instanceof Interval) )
						this.scrollBTN[this.mouseOverLevel][i].startScroll();
					return true;
				}
				else
					this.scrollBTN[this.mouseOverLevel][i].endScroll();
			}			
		}
		
		for(var k in this.itemList[this.mouseOverLevel])
		{
			var curr = this.itemList[this.mouseOverLevel][k];
			if(isFunction(curr)) continue;
			if(curr.data.disabled) continue;
			
			if(pointInBox(curr.bound, this.mouse, {_y:this.offsetTop[this.mouseOverLevel], height:this.offsetTop[this.mouseOverLevel]}))
			{
				curr.onMouseOver();
				this.mouseOverItem = curr;				
			}
			else
				curr.onMouseOut();			
		}
	}	
};

ContextMenu.prototype.level = function(lvl)
{
    var level 	= lvl;
    var self	= this;
    
    return {
        actLevel    : level,
        stack       : self.levelStorage,
        
        getParent: function()
        {
            if(this.actLevel == 0) return null;
            p = -1;
            for(var k in this.stack)
            {
                if(isArray(this.stack[k]))
                {
                    if(this.stack[k].exists(this.actLevel))
                    {
                        p = k;   
                    }                    
                }
            }
            return (p != -1 ? p : null);
        },              
        
        getChildList: function()
        {
            return this.stack[this.actLevel];
        },
        
        addLevel: function(level)
        {
            if(level)
            {
                if(!isArray(this.stack[this.actLevel]))         this.stack[this.actLevel] = new Array();                
                if(!this.stack[this.actLevel].exists(level))    this.stack[this.actLevel].push(level);
            }
        }    
    };
}

ContextMenu.prototype.startScroll = function(level, amount, time)
{
	this.scrollInterval[level] = new Interval(function(o,p)
	{
		p.ctx.scroll(p.level, p.amount);
	}, time, {ctx:this,amount:amount,level:level});
};

ContextMenu.prototype.stopScroll = function(level)
{
	if(this.scrollInterval[level] instanceof Interval)
	{
		this.scrollInterval[level].destroy();
		delete this.scrollInterval[level];		
	}
};

ContextMenu.prototype.scroll = function(level, amount)
{
	var marginTop 		= 16;
	var marginBottom 	= 18;
	var container 		= this.containerList[level];
	var table			= container.table.parentNode;
	var newMargin		= isNaN(parseInt(table.style.marginTop, 10)) ? 0 : parseInt(table.style.marginTop, 10) + amount;
	
	if(newMargin >= marginTop) newMargin = marginTop;
	if(newMargin <= container.offsetHeight - table.height - marginBottom) newMargin = container.offsetHeight - table.height - marginBottom;
	
	table.style.marginTop = newMargin + 'px';
	this.offsetTop[level] = newMargin;	
};

ContextMenu.prototype.destroy = function()
{
	this.hide(0, false, true);	
};



/*****************************************************************************
 * Egy menuitem
 *****************************************************************************/
ContextMenu.prototype.item = function(data)
{
    this.data = data;
	this.container = create.tr(null, (data.disabled || data.type == 'separator' ? (data.disabled ? 'disabled' : 'separator') : null));
	this.create();
	this.setVisibility(this.data.visible);	
};

ContextMenu.prototype.item.prototype.getData = function(property)
{
	if(property)
		if(!isUndefined(this.data[property]))
			return this.data[property];
		else
			return null;
	return this.data;	
};

ContextMenu.prototype.item.prototype.setTitel = function(title)
{
	if(isElement(this.container.text))
		this.container.text.innerHTML = '<nobr>'+title+'</nobr>';
};

ContextMenu.prototype.item.prototype.enable = function()
{
    this.container.delClassName('disabled');
	this.enabled = true;
	
	if(this.container.icon)
	{
		if(isElement(this.container.icon.chkbox))
			this.container.icon.chkbox.enable();
		
		if(isElement(this.container.icon.radio))
			this.container.icon.radio.enable();
	}
};

ContextMenu.prototype.item.prototype.disable = function()
{
    this.container.addClassName('disabled');
	this.enabled = false;
	
	if(this.container.icon)
	{
		if(isElement(this.container.icon.chkbox))
			this.container.icon.chkbox.disable();
		
		if(isElement(this.container.icon.radio))
			this.container.icon.radio.disable();
	}
};

ContextMenu.prototype.item.prototype.setVisibility = function(state)
{
    this.data.visible = state;
	this.container.style.display = (state) ? '' : 'none';	
};

ContextMenu.prototype.item.prototype.isChecked = function()
{
    if(this.data.type == 'chkbox' || this.data.type == 'radio')
	{
		if(isElement(this.container.icon.chkbox))
			return this.container.icon.chkbox.checked;
		
		if(isElement(this.container.icon.radio))
			return this.container.icon.radio.checked;
	}
	
	return false;	
};

ContextMenu.prototype.item.prototype.setChecked = function(state)
{
    if(this.data.type == 'chkbox' || this.data.type == 'radio')
	{
		if(isElement(this.container.icon.chkbox))
			this.container.icon.chkbox.setChecked(state);
		
		if(isElement(this.container.icon.radio))
			this.container.icon.radio.setChecked(state);
	}
	
	return false;
};

ContextMenu.prototype.item.prototype.onMouseOver = function()
{
	if(this.data.sub && !this.data.parent.openedLevels.filter(function(v){return v;}, true).existsKey(this.data.sub))
	{
		this.data.parent.display(this.data.sub, this.bound._x+this.bound.width-5, this.bound._y+5+this.data.parent.offsetTop[this.data.level]);
	}
};

ContextMenu.prototype.item.prototype.onMouseOut = function()
{
	if(this.data.sub && this.data.parent.containerList[this.data.sub] && this.data.parent.containerList[this.data.sub].expand)
		this.data.parent.hide(this.data.sub);
};

ContextMenu.prototype.item.prototype.onClick = function()
{
	if(this.enabled && this.data.type != 'separator')
	{
		if(this.data.type == 'radio')
		{
			if(isElement(this.container.icon.radio))
				this.container.icon.radio.setChecked(!this.container.icon.radio.checked);
		}
		else if(this.data.type == 'chkbox')
		{
			if(isElement(this.container.icon.chkbox))
				this.container.icon.chkbox.setChecked(!this.container.icon.chkbox.checked);
		}
		
		if(this.data.cb instanceof CallBack)
			this.data.cb.execute(this.data.parent);
		
		if(this.data.close && !this.data.sub)
			this.data.parent.hide(0);
	}
};

ContextMenu.prototype.item.prototype.create = function()
{
    switch(this.data.type.toUpperCase())
	{
		case 'DEFAULT':
		case 'HTML':
			this.container.icon = create.td(null, 'icon', null, this.container);
			this.container.text = create.td(null, 'text', null, this.container);
			this.container.arrow = create.td(null, 'sub-arrow', null, this.container);
			this.createIcon();
			this.createText();
			this.createSubArrowIcon();
		break;
	
		case 'CHKBOX':
			this.container.icon = create.td(null, 'icon', null, this.container);
			this.container.text = create.td(null, 'text', null, this.container);
			this.container.arrow = create.td(null, 'sub-arrow', null, this.container);
			this.createCheckBox();
			this.createText();
			this.createSubArrowIcon();
		break;
	
		case 'RADIO':
			this.container.icon = create.td(null, 'icon', null, this.container);
			this.container.text = create.td(null, 'text', null, this.container);
			this.container.arrow = create.td(null, 'sub-arrow', null, this.container);
			this.createRadio();
			this.createText();
			this.createSubArrowIcon();
		break;
	
		case 'SEPARATOR':
			this.container.sep = create.td(null, null, null, this.container, {colSpan:'3'});
			this.container.sep.content = create.div(null, null, null, null, this.container.sep);
		break;
	}
	
	if(this.data.disabled)
		this.disable();
	else
		this.enable();		
	
};

ContextMenu.prototype.item.prototype.createCheckBox = function()
{
    var container 	= create.div(null, null, null, {width:'16px', height:'16px', position: 'relative'}, this.container.icon);
	var chkbox		= document.createElement('INPUT');
	chkbox.type     = 'checkbox';
	chkbox.name 	= this.data.name;
	chkbox.style.margin 	= '1px';
	chkbox.style.padding 	= '0px';
	if(getBrowser().ie)
	{
		chkbox.style.position   = 'absolute';
		chkbox.style.left = '-3px';
		chkbox.style.top = '-3px';
	}
	
	chkbox.enable 		= function(){ this.disabled = false; this.removeAttribute('disabled'); };
	chkbox.disable 		= function(){ this.disabled = true; this.setAttribute('disabled', 'disabled') };
	chkbox.setChecked 	= function(){ this.checked = arguments[0]; if(arguments[0]){this.setAttribute('checked', 'checked');}else{this.removeAttribute('checked');} };
	
	this.container.icon.chkbox = chkbox;
	container.appendChild(chkbox);
};

ContextMenu.prototype.item.prototype.createRadio = function()
{
    var container 	= create.div(null, null, null, {width:'16px', height:'16px', position: 'relative'}, this.container.icon);
	var radio		= document.createElement('INPUT');
	radio.type     = 'radio';
	if(this.data.radioGroup)radio.name = this.data.radioGroup;
	radio.style.margin 	= '1px';
	radio.style.padding = '0px';
	if(getBrowser().ie)
	{
		radio.style.position   = 'absolute';
		radio.style.left = '-3px';
		radio.style.top = '-3px';
	}
	
	radio.enable 		= function(){ this.disabled = false; this.removeAttribute('disabled'); };
	radio.disable 		= function(){ this.disabled = true; this.setAttribute('disabled', 'disabled'); };
	radio.setChecked 	= function(){ this.checked = arguments[0]; if(arguments[0]){this.setAttribute('checked', 'checked');}else{this.removeAttribute('checked');} };
	radio.onclick 		= function(){ this.setChecked(!this.checked); };
	
	this.container.icon.radio = radio;
	container.appendChild(radio);
};

ContextMenu.prototype.item.prototype.createIcon = function()
{
    if(isElement(this.container.icon))
	{
		if(this.data.icon)
			this.container.icon.img = create.img(null, this.data.icon, null, null, this.container.icon);
		else
			create.div(null, null, null, null, this.container.icon);		
	}
};

ContextMenu.prototype.item.prototype.createText = function()
{
    if(isElement(this.container.text))
	{
		this.container.text.innerHTML = '<nobr>'+this.data.content+'</nobr>';
	}
};

ContextMenu.prototype.item.prototype.createSubArrowIcon = function()
{
    if(isElement(this.container.arrow))
	{
		if(this.data.sub)
		{
			this.container.arrow.icon = create.div(null, null, null, null, this.container.arrow);
		}
		else
		{
			this.container.arrow.innerHTML = '&nbsp;';
		}
	} 
};
