$KT_CSSMENU_DEFAULTS = {
	classname: 'ktcssmenu',
	type: 'horizontal', 
	showseparator: 'KTSHOW',
	hideseparator: 'KTHIDE',
	showtimeout: 100,
	hidetimeout: 400,
	imgdir: 'img/',
	// specify if text on first level items is replaced by images
	imgreplace: 'no',
	// specify naming scheme for replacement images
	imgnames: '{text}.gif',
	// specify how the image replacement is made, possible values are:
	// 	- img - replace using image backgrounds
	// 	- css - replace using images to replace the text
	imgreplacestyle: 'css'
}

function cssmenuhelper_getmenu(menu) {
	if (typeof menu == 'string') {
		// marker passed
		menu = KT_cssmenus[menu];
	} else {
		// LI passed
		if (typeof menu.kt_marker != 'undefined') menu = KT_cssmenus[menu.kt_marker];
	}

	if (
		typeof menu == 'undefined' || 
		typeof menu.marker == 'undefined' ||
		typeof KT_cssmenus[menu.marker] == 'undefined'
	) throw('Cannot find menu');

	return menu;
}

/* binding */
KT_cssmenus = [];
CssMenu = function(id, marker) {
	this.id = id;
	this.div = utility.dom.getElem(id);
	this.div2 =  utility.dom.getChildrenByTagName(this.div, 'DIV')[0];
	this.error = false;
	var tmpul = utility.dom.getChildrenByTagName(this.div2, 'UL');
	if (tmpul.length == 0) {
		this.error = true;
		return;
	}
	var tmplis = utility.dom.getChildrenByTagName(tmpul[0], 'LI');
	if (tmplis.length == 0) {
		this.error = true;
		return;
	}

	if (!this.can_attach()) {
		this.error = true;
		return false;
	}
	if (!this.extract_options()) {
		this.error = true;
		return false;
	}

	if (typeof(marker) == 'undefined') marker = this.div.id;
	this.marker = marker;

	this.current_path = '';

	// save current position
	var pos = utility.dom.getAbsolutePos(this.div);
	this.oldpos = pos;

	// add a dummy div
	var dummy = document.createElement('DIV');
	dummy.id = this.div.id + "_dummy";
	this.div.parentNode.insertBefore(dummy, this.div);
	utility.dom.classNameAdd(dummy, "ktcssdummy");
	this.dummy = dummy;

	// move out of container
	this.div.style.position = 'absolute';
	document.body.appendChild(this.div);
	this.div.style.left = '-10000px';
	this.div.style.top = '-10000px';
	if (typeof first_was_rendered == 'undefined') {
        this.div.style.zIndex = "10000";
        first_was_rendered = true;
    }

}
CssMenu.prototype.can_attach = function() {
	var res = true;
	return res;
}
CssMenu.prototype.extract_options = function() {
	// load defaults
	var x, cn, menu = this;
	var options = {};
	for(x in $KT_CSSMENU_DEFAULTS) {
		options[x] = $KT_CSSMENU_DEFAULTS[x];
	}
	// extract options
	cn = utility.dom.getClassNames(this.div);
	Array_each(cn, function(cnpart, i){
		if (cnpart.indexOf('ktskin') == 0) {
			menu.skin = cnpart;
		}
	});
	cn = utility.dom.getClassNames(this.div2);
	Array_each(cn, function(cnpart, i){
		var opts, optstmp;
		if (i == 0) {
			options.type = cnpart.replace(/^kt/, '');
		} else if (cnpart.indexOf('ktopts_') == 0) {
			opts = cnpart.replace(/^ktopts_/, '').split('_');
			optstmp = Array_splice(opts, 1, cnpart.length-1).join('_');
			opts.length = 1;
			Array_push(opts, optstmp);
			for (var j=0; j<opts.length-1; j+=2) {
				options[unescape(opts[j])] = unescape(opts[j+1]);
			}
		}
	});
	this.options = options;
	var allowedtypes = ['horizontal', 'vertical', 'horizontal2'];
	return (Array_indexOf(allowedtypes, options.type) != -1);
}

CssMenu.prototype.clear_showtimeouts = function(path) { this.clear_timeouts(path, 'show'); }
CssMenu.prototype.clear_hidetimeouts = function(path) { this.clear_timeouts(path, 'hide'); }
CssMenu.prototype.clear_timeouts = function(path, mode) {
	// clear all path timeouts
	menu = cssmenuhelper_getmenu(this);
	while (path.length) {
		this.clear_timeout(path, mode);
		path = path.replace(/_\d+$/, '');
	}
}
CssMenu.prototype.clear_showtimeout = function(path, mode) { this.clear_timeout(path, 'show'); }
CssMenu.prototype.clear_hidetimeout = function(path, mode) { this.clear_timeout(path, 'hide'); }
CssMenu.prototype.clear_timeout = function(path, mode) {
	var to = this.marker + this.options[mode + 'separator'] + path;
	//trace(to, 'red');
	clearTimeout(window[to]);
}
CssMenu.prototype.set_showtimeout = function(obj) { this.set_timeout(obj, 'show'); }
CssMenu.prototype.set_hidetimeout = function(obj) { this.set_timeout(obj, 'hide'); }
CssMenu.prototype.set_timeout = function(obj, mode) {
	var to = this.marker + this.options[mode + 'separator'] + obj.kt_path;
	//trace(to, 'blue');
	var tostr = 'cssmenu_' + mode + '("' + obj.kt_marker + '", "' + obj.kt_path + '")';
	window[to] = setTimeout(tostr, menu.options[mode + 'timeout']);
	//get_object_from_path
}

// walk up the ancestry
CssMenu.prototype.get_objects_from_ancestry = function(obj) {
	var result = [];
	while (obj.tagName.toUpperCase() != 'DIV' && obj.parentNode.tagName.toUpperCase() != 'DIV') {
		Array_push(result, obj);
		obj = obj.parentNode.parentNode;
	}
	return result;
}

// walk down the path
CssMenu.prototype.get_objects_from_path = function(path, lastonly) {
	var obj, ul, lev, pathbits, result=[];
	if (typeof path == 'undefined') path = this.current_path;
	if (typeof lastonly == 'undefined') lastonly = false;
	
	lev = 0;
	pathbits = path.split('_');
	obj = this.div2;
	while (lev < pathbits.length - 1) {
		lev++;
		ul = utility.dom.getChildrenByTagName(obj, 'UL')[0];
		obj = utility.dom.getChildrenByTagName(ul, 'LI')[pathbits[lev]-1];
		Array_push(result, obj);
	}
	if (lastonly) {
		if (result.length) {
			return result[result.length-1];
		} else {
			return null;
		}
	} else {
		return result;
	}
}
CssMenu.prototype.get_object_from_path = function(path) {
	if (typeof path.kt_path != 'undefined') {
		return path;
	}
	return this.get_objects_from_path(path, true);
}

// hide "current" elements that are not one of the ancestors of the given element
CssMenu.prototype.hide_current = function(o) {
	var menu = this;
	var crtnodes = menu.get_objects_from_ancestry(o);
	var oldnodes = menu.get_objects_from_path();

	// hide oldnodes starting from the deepest
	Array_each(oldnodes.reverse(), function(li, i){
		if (Array_indexOf(crtnodes, li) == -1) {
			menu.clear_showtimeout(li.kt_path);
			cssmenu_hide(li.kt_marker, li.kt_path);
			menu.set_replacement_image(li, '');
		}
	});

}

CssMenu.prototype.set_replacement_image = function(li, mode) {
	// only replace on first level items
	var menu = this;
	if (menu.options.imgreplace != "yes" || li.kt_lev > 1) return;
	if (typeof mode == 'undefined') mode = '';
	if (mode == '' && Array_indexOf(utility.dom.getClassNames(li), 'selected') != -1) {
		mode = 'selected';
	}

	var newimg = '', imgpath;
	var a = utility.dom.getChildrenByTagName(li, 'A')[0];
	if (typeof a.kt_text == 'undefined') {
		if (typeof a.innerText != 'undefined') {
			a.kt_text = a.innerText;
		} else {
			a.kt_text = String_normalize_space(a.innerHTML.replace(/<[^>]*>/));
		}
	}
	
	// create replacements if they don't exist
	var img, span;
	switch (menu.options.imgreplacestyle) {
		case 'img':
			if (utility.dom.getChildrenByTagName(a, 'IMG').length == 0) {
				img = document.createElement("IMG");
				var fn = function() { cssmenu_reflow(this); };
				// hack to force opera to recalc sizes 
				// because it doesn't trigger onload when it already has the images in cache
				if (navigator.userAgent.toLowerCase().indexOf('opera') != -1) {
					setTimeout("cssmenu_reflow('" + li.kt_marker + "," + li.kt_path + "')", li.kt_pos * 50);
				}
				img.onload = fn;
				a.innerHTML = '';
				a.appendChild(img);
				img.border = "0";
			}
			break;
		case 'css':
			if (utility.dom.getChildrenByTagName(a, 'SPAN').length == 0) {
				span = document.createElement("SPAN");
				span.innerHTML = a.innerHTML;
				a.innerHTML = '';
				a.appendChild(span);
			}
			break;
	}

	// decide on the new image path
	newimg = menu.options.imgnames;
	newimg = newimg.replace('{text}', a.kt_text);
	newimg = newimg.replace('{name}', a.kt_text);
	newimg = newimg.replace('{order}', li.kt_pos);
	newimg = newimg.replace('{position}', li.kt_pos);

	if (newimg == '') return;
	
	if (mode != '') {
		newimg = newimg.replace(/(\.\w+)$/, '_' + mode + "$1");
	}
	imgpath = menu.options.imgdir + newimg;

	// don't set mode if the same is already set
	if (typeof a.kt_mode != 'undefined' && a.kt_mode == mode) {
		return;
	} else {
		a.kt_mode = mode;
	}
	
	// set new mode
	switch (menu.options.imgreplacestyle) {
		case 'img':
			img = utility.dom.getChildrenByTagName(a, 'IMG')[0];
			img.src = imgpath;
			break;
		case 'css':
			a.style.backgroundImage = 'url(' + imgpath + ')';
			break;
	}
}

CssMenu.prototype.remove_node_class = function(li, cn) {
	if (typeof cn == 'undefined') cn = "current";
	var a = utility.dom.getChildrenByTagName(li, 'A')[0];
	if (a.className.indexOf(cn) == -1) return;

	var toadd = "_haschildren";
	var cls = [cn, "lev" + li.kt_lev + "_" + cn, cn + toadd, "lev" + li.kt_lev + "_" + cn + toadd];

	//trace(li.kt_path, 'red')
	//utility.dom.classNameRemove(li, cls);
	utility.dom.classNameRemove(a, cls);
}

CssMenu.prototype.add_node_class = function(li, cn) {
	if (typeof cn == 'undefined') cn = "current";
	var a = utility.dom.getChildrenByTagName(li, 'A')[0];
	if (a.className.indexOf(cn) != -1) return;

	var cls = [cn, "lev" + li.kt_lev + "_" + cn];
	if (a.className.indexOf('haschildren') != -1) {
		toadd = "_haschildren";
		Array_push(cls, cn + toadd, "lev" + li.kt_lev + "_" + cn + toadd);
	}

	//trace(li.kt_path, 'blue')
	//utility.dom.classNameAdd(li, cls);
	utility.dom.classNameAdd(a, cls);
}


////////////////////////////////////////////////////////////////////////////////


function cssmenu_over(e) {
	var o = utility.dom.setEventVars(e);
	var menu = cssmenuhelper_getmenu(this.kt_marker);

	// cancel hiding of current element and it's parents
	menu.clear_hidetimeouts(this.kt_path);

	if (this.kt_path != menu.current_path) {
		
		menu.set_replacement_image(this, 'over');
		
		// instantly hide all previous current elements
		// but not for horizontal2, because it's sticky
		// and the hiding will happen at the exact time the new one is shown (in cssmenu_show)
		if (menu.options.type != 'horizontal2') {
			menu.hide_current(this)
		}

		// stop event early if on a horziontal2 menu
		if (menu.options.type == 'horizontal2' && this.kt_lev > 1) {
			return utility.dom.stopEvent(o.e);
		}

		menu.set_showtimeout(this);
	}
	return utility.dom.stopEvent(o.e);
}
function cssmenu_out(e) {
	var o = utility.dom.setEventVars(e);
	var menu = KT_cssmenus[this.kt_marker];

	// hide previous current and stop event early if on a horziontal2 menu
	if (menu.options.type == 'horizontal2') {
		menu.clear_showtimeout(this.kt_path);
		return utility.dom.stopEvent(o.e);
	}

	menu.clear_showtimeout(this.kt_path);
	menu.set_hidetimeout(this);

	// allow event to go up to all LI'sw, don't uncomment the following line
	// this cascading is what makes the menu close when the mouse moves outside all items
	//return utility.dom.stopEvent(o.e);
}
function cssmenu_show(marker, opath) {
	var menu = KT_cssmenus[marker];
	var o = menu.get_object_from_path(opath);
	var uls = utility.dom.getChildrenByTagName(o, 'UL');
	if (menu.options.type == 'horizontal2') {
		menu.hide_current(o);
		var	lis = utility.dom.getChildrenByTagName(utility.dom.getChildrenByTagName(menu.div2, 'UL')[0], 'LI');
		Array_each(lis, function(li, i){
			if (o.kt_path != li.kt_path) {
				menu.set_replacement_image(o, '');
				menu.remove_node_class(o, 'current');
			}
		});
	}
	// disable all but first level for ie5mac
	if (uls.length && (!(document.all && typeof window.print == 'undefined'))) {
		o.style.left = "-10000px";
		o.style.top = "-10000px";
		var i;
		if (typeof uls[0].filters != 'undefined') {
			for (i=0; i<uls[0].filters.length; i++) {
				uls[0].filters[i].apply();
			}
		}
		utility.dom.showElem(uls[0]);
		if (typeof uls[0].filters != 'undefined') {
			for (i=0; i<uls[0].filters.length; i++) {
				uls[0].filters[i].play();
			}
		}
		cssmenu_recalc(o);
	}
	menu.add_node_class(o, 'current');
	menu.set_replacement_image(o, 'over');
	// set current path
	menu.current_path = o.kt_path;
	cssmenu_handle_selects(marker);
}
function cssmenu_hide(marker, opath) {
	var menu = KT_cssmenus[marker];
	var o = menu.get_object_from_path(opath);
	var uls = utility.dom.getChildrenByTagName(o, 'UL');
	if (uls.length) {
		utility.dom.hideElem(uls[0]);
	}
	menu.set_replacement_image(o, '');
	menu.remove_node_class(o, 'current');
	if (o.kt_path == menu.current_path) {
		menu.current_path = menu.current_path.replace(/_\d+$/, '');
		cssmenu_handle_selects(marker);
	}
}

function cssmenu_recalc(li) {
	var menu = KT_cssmenus[li.kt_marker];
	var uls = utility.dom.getChildrenByTagName(li, 'UL');
	if (uls.length == 0) return;
	var a = utility.dom.getChildrenByTagName(li, 'A')[0];
	li.style.height = a.offsetHeight + "px";
	if (uls[0].className.indexOf("lev") == -1) {
		utility.dom.classNameAdd(uls[0], uls[0].classes);
		Array_each(utility.dom.getChildrenByTagName(uls[0], 'LI'), function(li, i){
			utility.dom.classNameAdd(li, li.classes);
			utility.dom.classNameAdd(utility.dom.getChildrenByTagName(li, "A")[0], li.classes);
		});
	}

	var crt = {x: 0, y: 0};
	var crtabs = utility.dom.getAbsolutePos(li.parentNode);
	switch(menu.options.type) {
		case 'horizontal':
			if (li.kt_lev == 1) {
				crt = { x: li.offsetLeft, y: li.offsetTop + li.offsetHeight };
			} else {
				crt = { x: li.offsetLeft + li.offsetWidth, y: li.offsetTop };
			}
			break;
		case 'horizontal2':
			crt = { x: 0, y: li.offsetTop + li.offsetHeight };
			break;
		case 'vertical':
			crt = { x: li.offsetLeft + li.offsetWidth, y: li.offsetTop };
			break;
	}
	if (menu.options.type != 'horizontal2') {
		// but what does it all mean, Basil?
		// 
		// uls[0] - the item we want to position
		// uls[0].offsetWidth, uls[0].offsetHeight - size of the current item we want to position
		// buffer - constant to specifiy minimum distance to viewport we want to allow
		// 
		// crt    - position to closest parent (in relative coordinates)
		// crtabs - position of the closest parent (in absolute coordinates)
		// crtpag - visible viewport size

		var crtpag = utility.dom.getPageInnerSize();
		var crtscr = utility.dom.getPageScroll();
		var buffer = {x: 20, y: 20};

		// our right margin must not be bigger than the current page width + scroll
		if (crtabs.x + crt.x + uls[0].offsetWidth + buffer.x > crtpag.x + crtscr.x) {
			crt.x -= (crtabs.x + crt.x + uls[0].offsetWidth + buffer.x) - (crtpag.x + crtscr.x);
		}
		
		// make sure we don't completely overlap the parent (only if not on a first level of a horizontal menu)
		if (crt.x < 20 && !('horizontal' == menu.options.type && li.kt_lev == 1)) {
			crt.x -= uls[0].offsetWidth - 40;
		}

		// our bottom margin must not be bigger than the current page height + scroll
		if (crtabs.y + crt.y + uls[0].offsetHeight + buffer.y > crtpag.y + crtscr.y) {
			crt.y -= (crtabs.y + crt.y + uls[0].offsetHeight + buffer.y) - (crtpag.y + crtscr.y);
		}

		// if the top of the item will be outside the current viewport, move it down until it fits
		if (crtabs.y + crt.y < crtscr.y + buffer.y) {
			crt.y += (crtscr.y + buffer.y) - (crtabs.y + crt.y);
		}

	}

	uls[0].style.left = crt.x + 'px';
	uls[0].style.top = crt.y + 'px';
}

function cssmenu_handle_selects(marker) {
	var menu = KT_cssmenus[marker];
	coords = [];
	var elems = menu.get_objects_from_path();
	Array_each(elems, function(li, i){
		var pos;
		var obj = li.parentNode;
		pos = utility.dom.getAbsolutePos(obj);
		Array_push(coords, {x1: pos.x, y1: pos.y, 
			x2: pos.x + obj.offsetWidth, y2: pos.y + obj.offsetHeight});
		
		// for the last element in path, add its child UL too
		if (i == elems.length - 1) {
			var ul = utility.dom.getChildrenByTagName(li, 'UL');
			if (ul.length == 1) {
				var p = utility.dom.getAbsolutePos(ul[0]);
				Array_push(coords, {x1: p.x, y1: p.y, 
					x2: p.x + ul[0].offsetWidth, y2: p.y + ul[0].offsetHeight});
			}
		}
	});

	var buf = {x: 0, y: 0};
	var tags = ["object", "select", "applet"];
	if (!(
		typeof window.opera == 'undefined' && 
		(
			navigator.userAgent.toLowerCase().indexOf('msie 6') != -1 || 
			navigator.userAgent.toLowerCase().indexOf('msie 5.5') != -1
		)
	)) {
		Array_push(tags, "iframe");
	}
	Array_each(tags, function(tag){
		Array_each(utility.dom.getElementsByTagName(document, tag), function(elem, i){
			var p = utility.dom.getAbsolutePos(elem);
			p = {x1: p.x, y1: p.y, 
				x2: p.x + elem.offsetWidth, y2: p.y + elem.offsetHeight};
			
			var v = true;
			Array_each(coords, function(c){
				v = v && (
					c.x1 - buf.x > p.x2 || 
					c.x2 + buf.x < p.x1 || 
					c.y1 - buf.y > p.y2 || 
					c.y2 + buf.y < p.y1
				);
			});
			elem.style.visibility = v ? "" : "hidden";
		});
	});
}

function cssmenu_reflow(img) {
	// only do this repositioning once for each image
	img.onload = function(){}
	var menu, a, li, ul;
	// opera hack
	if (typeof img == 'string') {
		menu = KT_cssmenus[img.split(',')[0]];
		li = menu.get_object_from_path(img.split(',')[1]);
		a = utility.dom.getChildrenByTagName(li, 'A')[0];
		img = utility.dom.getChildrenByTagName(a, 'IMG')[0];
	} else {
		a = img.parentNode;
		li = img.parentNode.parentNode;
		menu = KT_cssmenus[li.kt_marker];
	}

	ul = li.parentNode;
	// try to improve a bit the safari 1.0 rendering
	if (menu.options.type == "vertical") {
		a.style.width = "10px";
		a.style.height = "10px";
	}
	a.style.width = li.style.width = "auto";
	a.style.height = li.style.height = "auto";
	li.style.cssFloat = "left";

	cssmenu_position(menu);
	// we need the cssmenu_show to make mozilla recalc the position of the selected UL
	// 
	// HACK:  && typeof document.all != 'undefined'
	// ie throws a wierd error if it runs the cssmenu_show
	// could be related to the img onload event firing more often than it should
	if (menu.options.type == 'horizontal2' && typeof document.all == 'undefined') {
		cssmenu_show(li.kt_marker, li.kt_path);
	}
}
function cssmenu_position(menu) {
	menu.div.style.left = menu.oldpos.x + 'px';
	menu.div.style.top = menu.oldpos.y + 'px';
	
	// get new width and height
	var ul = utility.dom.getChildrenByTagName(menu.div2, 'UL')[0];
	var lis = utility.dom.getChildrenByTagName(ul, 'LI');
	var nw = Array_last(lis).offsetWidth;
	var nh = Array_last(lis).offsetHeight;
	switch (menu.options.type) {
		case 'horizontal':
		case 'horizontal2':
			nw += Array_last(lis).offsetLeft;
			if (lis.length > 1) {
				// for image replacement, the calculation is a bit more relaxed, 
				// but still a bit defensive
				// we take as the width of an item the max between 
				// - it's reported width and 
				// - the difference of the left offset between the item and it's successor
				if (menu.options.imgreplace == 'yes') {
					var tnw = Array_last(lis).offsetWidth;
					for (var i=0; i<lis.length-1; i++) {
						tnw += max(lis[i+1].offsetLeft - lis[i].offsetLeft, lis[i].offsetWidth);
					}
					nw = tnw;
				// for menus without image replacement
				// we take the max between
				// - max of the differences between left offsets of adjacent items
				// - max of the reported item widths
				// and multiply it by the number of items
				} else {
					var lisw = [lis[0].offsetWidth];
					for (var i=1; i<lis.length; i++) {
						Array_push(lisw, 
							max(lis[i].offsetLeft - lis[i-1].offsetLeft, lis[i].offsetWidth)
						);
					}
					// nw = max(nw, Array_max(lisw) * lis.length);
				}
			}
			break;
		case 'vertical':
			nh += Array_last(lis).offsetTop;
			break;
	}

	// add buffer
	if (Array_indexOf(['horizontal', 'horizontal2'], menu.options.type) != -1) {
		nw += 0;
	}
	nh += 0;

	nw += "px";
	nh += "px";
	ul.style.width = menu.div.style.width = menu.div2.style.width = menu.dummy.style.width = nw;
	// don't set height for vertical since it'll usually be smaller than it should
	if (menu.options.type != 'vertical') {
		ul.style.height = menu.div.style.height = menu.div2.style.height = menu.dummy.style.height = nh;
	}
}

function cssmenu_attach(menu, parentObj, path, lev) {
	if (typeof(lev) == 'undefined') lev = 0;
	if (!path) path = '';

	lev++;
	var uls = utility.dom.getChildrenByTagName(parentObj, 'UL');
	var lis = [];

	if (uls.length == 0) {
		return;
	}
	// partially fix ie5, *g*
	if (lev == 3 && document.all && typeof document.compatMode == 'undefined' && typeof Object.prototype.hasOwnProperty == "undefined") {
		parentObj.removeChild(uls[0]);
		return;
	}

	if (menu.options.type == 'horizontal2' && lev == 3) {
		return;
	}
	var ul = uls[0];
	if (uls.length == 1) {
		var toaddul = ['lev' + lev];
		if (lev == 1) {
			Array_push(toaddul, 'clearfix');
			utility.dom.classNameAdd(ul, toaddul);
		} else {
			ul.classes = toaddul;
		}
	}
	lis = utility.dom.getChildrenByTagName(ul, 'LI');

	// init box calculator based on type of menu
	Array_each(lis, function(li, i) {
		// markers
		li.kt_marker = menu.marker;
		li.kt_lev = lev;
		li.kt_pos = (i+1);
		li.kt_path = path + '_' + (i+1);

		// class names
		var addclass;
		addclass = [
			'lev' + lev, 
			'pos' + (i+1), 
			'lev' + lev + '_pos' + (i+1)
		];
		if (i == 0) 
			Array_push(addclass, 'first', 'lev' + lev + '_first');
		if (i == (lis.length - 1)) 
			Array_push(addclass, 'last', 'lev' + lev + '_last');
		if (utility.dom.getChildrenByTagName(li, 'UL').length && !(menu.options.type == 'horizontal2' && lev > 1)) 
			Array_push(addclass, 'haschildren', 'lev' + lev + '_haschildren');
		
		var issel = false;
		if (
			Array_indexOf(utility.dom.getClassNames(li), 'selected') != -1 || 
			utility.dom.getElementsByClassName(li, 'selected', 'LI').length
		) {
			issel = true;
			Array_push(addclass, 'selected', 'lev' + lev + '_selected');
		}
		var link = utility.dom.getChildrenByTagName(li, 'A')[0];
		if (Array_indexOf(['', '#', window.location.href, window.location + '#'], link.href) != -1) {
			utility.dom.attachEvent(link, 'onclick', function(e){
				var o = utility.dom.setEventVars(e);
				return utility.dom.stopEvent(o.e);
			});
		}
		if (lev == 1) {
			utility.dom.classNameAdd(link, addclass);
			utility.dom.classNameAdd(li, addclass);
		} else {
			li.classes = addclass;
		}

		// possibly replace images
		menu.set_replacement_image(li, '');
		
		// events
		utility.dom.attachEvent(li, 'onmouseover', cssmenu_over);
		utility.dom.attachEvent(li, 'onmouseout', cssmenu_out);

		// recurse
		cssmenu_attach(menu, li, path + '_' + (i+1), lev);

		// make the selected ul visible in a horizontal2 menu
		if (menu.options.type == 'horizontal2' && issel && ul && lev == 1) {
			cssmenu_show(li.kt_marker, li.kt_path);
		}
	});
}

////////////////////////////////////////////////////////////////
// binding
////////////////////////////////////////////////////////////////
function cssmenu_bind(id, marker) {
	var newmenu = null;
	if (typeof KT_cssmenus[id] == 'undefined') {
		newmenu = new CssMenu(id)
		if (!newmenu.error) {
			KT_cssmenus[id] = newmenu;
			cssmenu_attach(newmenu, newmenu.div2);
			cssmenu_position(newmenu);
		} else {
			//alert('Cannot attach menu: ' + id)
		}
	} else {
		var newmenu = KT_cssmenus[id];
		cssmenu_position(newmenu);
	}
}
function cssmenu_bind_all() {
	var uidgen = new UIDGenerator();
	Array_each(utility.dom.getElementsByClassName(document, $KT_CSSMENU_DEFAULTS.classname, 'div'), function(div, i){
		if (div.className.indexOf('kttreeskin') == -1) {
			if (typeof(div.id) == 'undefined' || !div.id) {
				div.id = uidgen.generate();
			}
			cssmenu_bind(div.id);
		}
	});
	cssmenu_page_resize();
}

// prevent leaks
// CBA: if opera has memory cache set to automatic it will not free memory until it eats up some percentage of available system memory
//	- in my test, with 512mb, it went up to 75mb, so, the percentage seems to be about 15%
//	also, even tho Opera seems to leak, it appears it's hogging ram only if you have a lot of it :)
//	http://my.opera.com/forums/showthread.php?threadid=48854&highlight=memory+leak
window.onunload = function() {
	var i;
	try {
		for (i=0; i<document.all.length; i++) {
			utility.dom.stripAttributes(document.all[i]);
		}
		for (i in KT_cssmenus) {
			utility.dom.stripAttributes(KT_cssmenus[i]);
		}
	} catch(e) {}
}

function cssmenu_page_resize() {
	//KT_cssmenus[id];
	Array_each(utility.dom.getElementsByClassName(document, $KT_CSSMENU_DEFAULTS.classname, 'div'), function(div, i){
		if (div.className.indexOf('kttreeskin') == -1) {
			var mn = KT_cssmenus[div.id];
			if (typeof mn != 'undefined') {
				var pr = mn.dummy;
				if (pr.tagName.toLowerCase() != 'body') {
					var pos = utility.dom.getAbsolutePos(pr);
					mn.oldpos = pos;
					cssmenu_position(mn);
				}
			}
		}
	});
}

function cssmenu_fix_height() {
var prop_count = 0;
for (i in KT_cssmenus) {
prop_count++;
}
if (prop_count == 0) {
setTimeout('cssmenu_fix_height()', 100);
return;
} else {
for (i in KT_cssmenus) {
var m = KT_cssmenus[i];
if (m.options.type == 'vertical') {
divs = m.div;
if (divs.offsetHeight) {
var divHeight = divs.offsetHeight;
} else if (divs.style.pixelHeight) {
var divHeight = divs.style.pixelHeight;
} 
m.dummy.style.height = divHeight + 'px';
}
};
}
}
utility.dom.attachEvent2(window, 'onload', cssmenu_fix_height);
// attach
utility.dom.attachEvent2(window, 'onload', cssmenu_bind_all);
utility.dom.attachEvent2(window, 'onresize', cssmenu_page_resize);


