/**
 * JS custom listbox control
 * 
 * @author ImPuls$$
 */

/**
 * @const int VM_LISTBOX_MINWIDTH Listbox min width
 */
var VM_LISTBOX_MINWIDTH = 42;

/**
 * @const int VM_LISTBOX_MAXLENGTH Listbox max length
 */
var VM_LISTBOX_MAXLENGTH = 20;

/**
 * @const int VM_LISTBOX_ZINDEX Listbox popup z-index
 */
var VM_LISTBOX_ZINDEX = 201;

/**
 * @const int VM_LISTBOX_DEFAULT_ZINDEX Listbox z-index
 */
var VM_LISTBOX_DEFAULT_ZINDEX = 0;

/**
 * @const int VM_LISTBOX_ANIMATION_SPEED Listbox animation speed
 */
var VM_LISTBOX_ANIMATION_SPEED = 200;

/**
 * @const int VM_LISTBOX_HIDDEN_OPTION_HEIGHT For approximately calculation lisbox option list height - need fix
 */
var VM_LISTBOX_HIDDEN_OPTION_HEIGHT = 18;

/**
 * @var string vm_listbox_search_query Listbox keydown query
 */
var vm_listbox_search_query = '';
var vm_listbox_changed = false;

/**
 * @var vm_listbox vm_current_listbox Active selectbox
 */
var vm_current_listbox = null;

/**
 * @var array vm_listboxes vm_listbox container
 */
var vm_listboxes = new Array();

/**
 * vm_listbox class constructor
 *
 * @param mixed id DOM identifier
 * @param bool useAnimation True if animation needed. False as default
 * @param string name Control name
 * @param function onchange On change event
 *
 * @example
 *	new vm_listbox(
 *			'uniqid',		// listbox id
  *			true,			// listbox uses animation
 *			'name2',		// listbox name
 *			function() {	// listbox onchange event
 *				this.form.action = this.value;
 *				this.form.submit();
 *			}
 *		)
 *		.setOptions([	// set listbox options
 *			{'value': '?test=1', 'text': 'Test1'},
 *			{'value': '?test=2', 'text': 'Test2'},
 *			{'value': '?test=3', 'text': 'Test3'},
 *			{'value': '?test=4', 'text': 'Test4'},
 *			{'value': '?test=5', 'text': 'Test5'}
 *		])
 *		.render(			// render listbox to document
 *			{ 'width': '120' },
 *			'main'
 *		);
 */
function vm_listbox(id) {
	this.id = id;
	this.setIds(id);
	this.name = arguments[2] || this.container;
	if (arguments[3])
		this.onchange = arguments[3];
	if (arguments[1])
		this.useAnimation = arguments[1];
	else 
		this.useAnimation = false;
		
	this.optionsHeight = 0;
	this.optionsDefaultPosition = 0;
	
	vm_listboxes[this.container] = this;
	
	return this;
}

/**
 * Write selectbox with specified options into document
 *
 * @param array styles Array of listbox styles
 * @param string classPostfix Listbox class name postfix
 */
vm_listbox.prototype.render = function(styles) { // out of date
	var classPostfix = '';
	if (arguments[1])
		classPostfix = arguments[1];

	var dummySelect = new Array();
	var currentSelect = new Array();
	var selectedElement = '';
	var _selected = '';
	var height = 0;
	
	var iterations = this.options.length;
	for (index = 0; index < iterations; index++) {
		var option = this.options[index];
		height++;
		if (selectedElement == '') {
			selectedElement = option.text;
		} 
		if (option.selected) {
			_selected = ' selected';
			selectedElement = option.text;
		} else _selected = '';
		
		currentSelect[currentSelect.length] = '<li onclick="vm_listboxes[\'';
		currentSelect[currentSelect.length] = this.container;
		currentSelect[currentSelect.length] = '\'].value(';
		currentSelect[currentSelect.length] = index;
		currentSelect[currentSelect.length] = ')" onmouseover="this.className = \'over\'" onmouseout="this.className=\'\'">';
		currentSelect[currentSelect.length] = option.text;
		currentSelect[currentSelect.length] = '</li>';
				
		dummySelect[dummySelect.length] = '<option value="';
		dummySelect[dummySelect.length] = option.value;
		dummySelect[dummySelect.length] = '"';
		dummySelect[dummySelect.length] = _selected;
		dummySelect[dummySelect.length] = '>';
		dummySelect[dummySelect.length] = option.text;
		dummySelect[dummySelect.length] = '</option>';
	}

	if (!styles) styles = new Object();
	
	if (!styles.width) styles.width = 42;

	height = height + 2;
	height = (height > VM_LISTBOX_MAXLENGTH) ? VM_LISTBOX_MAXLENGTH : height;
				
	var selectBox = new Array();
	
	selectBox[selectBox.length] = '<div id="';
	selectBox[selectBox.length] = vm_listboxes[this.container].container;
	selectBox[selectBox.length] = '" class="vm_listbox';
	selectBox[selectBox.length] = classPostfix;
	selectBox[selectBox.length] = '" style="width: ';
	selectBox[selectBox.length] = styles.width;
	selectBox[selectBox.length] = 'px"><div id="';
	selectBox[selectBox.length] = vm_listboxes[this.container].currentItem;
	selectBox[selectBox.length] = '" class="vm_listbox_current';
	selectBox[selectBox.length] = classPostfix;
	selectBox[selectBox.length] = '" onclick="vm_listboxes[\'';
	selectBox[selectBox.length] = this.container;
	selectBox[selectBox.length] = '\'].toggle()">';
	selectBox[selectBox.length] = selectedElement;
	selectBox[selectBox.length] = '</div><div class="vm_listbox_button';
	selectBox[selectBox.length] = classPostfix;
	selectBox[selectBox.length] = '" onclick="vm_listboxes[\'';
	selectBox[selectBox.length] = this.container;
	selectBox[selectBox.length] = '\'].toggle()">&nbsp;</div><ul id="';
	selectBox[selectBox.length] = vm_listboxes[this.container].optionList;
	selectBox[selectBox.length] = '" class="vm_listbox_popup';
	selectBox[selectBox.length] = classPostfix;
	selectBox[selectBox.length] = '" style="display: none; height: ';
	selectBox[selectBox.length] = height;
	selectBox[selectBox.length] = 'em">';
	selectBox[selectBox.length] = currentSelect.join('');
	selectBox[selectBox.length] = '</ul><select name="';
	selectBox[selectBox.length] = this.name;
	selectBox[selectBox.length] = '" id="';
	selectBox[selectBox.length] = this.listboxControl;
	selectBox[selectBox.length] = '" onchange="vm_listboxes[\'';
	selectBox[selectBox.length] = this.container;
	selectBox[selectBox.length] = '\'].value(this.selectedIndex)" class="vm_listbox_listboxBlock" style="display: none;">';
	selectBox[selectBox.length] = dummySelect.join('');
	selectBox[selectBox.length] = '</select></div>';
	
	document.write(selectBox.join(''));
	
	this.optionsDefaultPosition = $('#'+this.optionList).offset().top;
	
	return this;
}

/**
 * Replace selectbox with the same id
 *
 * @param string classPostfix Listbox class name postfix
 */
vm_listbox.prototype.replace = function() { // out of date
	var listboxBlock = document.getElementById(this.id);
	if (listboxBlock) {
		if (listboxBlock.nodeName.toUpperCase() != 'SELECT') return;
		
		listbox = jQuery(listboxBlock);
		
		var classPostfix = '';
		if (arguments[1])
			classPostfix = arguments[1];

		this.name = listboxBlock.name || this.container;
		this.onchange = listboxBlock.onchange;
		
		var width = parseInt(listboxBlock.offsetWidth);
		width = (width > VM_LISTBOX_MINWIDTH) ? width : VM_LISTBOX_MINWIDTH;
			
		listboxBlock.style.display = 'none';
		listboxBlock.id = this.listboxControl;
		
		if (listboxBlock.className != '') {
			classPostfix = '_'+listboxBlock.className;
			listboxBlock.className += ' vm_listbox_listboxBlock';
		} else {
			listboxBlock.className = 'vm_listbox_listboxBlock';
		}
		
		listbox.before('<div id="'+this.container+'" class="vm_listbox'+classPostfix+'" style="width: '+width+'px"></div>');
	
		document.getElementById(this.container).innerHTML = 
			'<div id="'+this.currentItem+'" class="vm_listbox_current'+classPostfix+'" onclick="vm_listboxes[\''+this.container+'\'].toggle()">'+
				((listboxBlock.options.length > 0 && listboxBlock.selectedIndex >= 0) ? listboxBlock.options[listboxBlock.selectedIndex].text : '') +
			'</div>'+
			'<div class="vm_listbox_button'+classPostfix+'" onclick="vm_listboxes[\''+this.container+'\'].toggle()">&nbsp;</div>';
	
		currentSelect = new Array();
		var iterations = listboxBlock.options.length;
		for (index = 0; index < iterations; index++) {
			var option = listboxBlock.options[index];
			if (option && option.nodeName && option.nodeName.toUpperCase() == 'OPTION') {
				currentSelect.push('<li onclick="vm_listboxes[\'');
				currentSelect.push(this.container);
				currentSelect.push('\'].value(');
				currentSelect.push(index);
				currentSelect.push(')" onmouseover="this.className = \'over\'" onmouseout="this.className=\'\'">');
				currentSelect.push(option.text);
				currentSelect.push('</li>');
			}
		}
		
		var height = listboxBlock.options.length + 2;
		height = (height > VM_LISTBOX_MAXLENGTH) ? VM_LISTBOX_MAXLENGTH : height;
		
			
		jQuery('#'+this.container).append(
			'<ul id="'+this.optionList+'" class="vm_listbox_popup'+classPostfix+'" style="display: none; width: '+width+'px; height: '+height+'em">'+
				currentSelect.join('')+
			'</ul>'
		);
		
		this.optionsDefaultPosition = $('#'+this.optionList).offset().top;
		
		if (!jQuery.browser.msie) {
			var _optionList = document.getElementById(_listbox.optionList);
			var _computedStyle = document.defaultView.getComputedStyle(_optionList, null);

			var diffWidth = parseInt(_computedStyle.borderLeftWidth) + parseInt(_computedStyle.borderRightWidth) + 
				parseInt(_computedStyle.paddingLeft) + parseInt(_computedStyle.paddingRight);
			_optionList.style.width = width - diffWidth;
		}
	}
}

/**
 * Class initialization
 *
 * @param  mixed id DOM identifier
 */
vm_listbox.prototype.setIds = function(id) {
	this.container = id;
	this.currentItem = this.container + '_current';
	this.optionList = this.container + '_list';
	this.listboxControl = this.container + '_value';
	
	return this;
}

/**
 * Set options array
 *
 * @param array options Options array / { id: {'value': int, 'title': string, 'selected': bool} }
 */
vm_listbox.prototype.setOptions = function(options) {
	this.options = options;
	return this;
}

/**
 * Toggle list visibility
 */
vm_listbox.prototype.toggle = function() {
	if (vm_current_listbox != this.container)
		vm_hide(vm_current_listbox);
		
	if (vm_gid(this.optionList)) {
		if (vm_gid(this.optionList).style.display != 'block') {
			vm_show(this.container);
		} else
			vm_hide(this.container);
	}
	
	return this;
}

/**
 * Change lisbox value
 *
 * @param int index Item selected index
 * @param bool doEvents Item selected index
 */
vm_listbox.prototype.value = function() {
	var listbox = vm_gid(this.listboxControl);
	if (listbox) {
		var oldValue = listbox.selectedIndex;
		
		if (arguments.length > 0) {
			var index = arguments[0];
			
			listbox.selectedIndex = index;
			
			if (oldValue != listbox.selectedIndex)
				vm_listbox_changed = true;
			
			var $optionList = jQuery('#'+this.optionList);
			if ($optionList.length > 0) {
				var optionsCount = $optionList.find('li').length;
				var offset = $optionList.
					find('.selected').removeClass('selected').end().
					find('li').eq(index % optionsCount).addClass('selected')[0].offsetTop;
				$optionList.scrollTop(offset);
			}
			
			var currentItem = vm_gid(this.currentItem);
			if (currentItem && listbox.options[index]) currentItem.innerHTML = listbox.options[index].text;
		}

		var doEvents = true;
		if (arguments.length > 1)
			doEvents = arguments[1];

		if (listbox && (oldValue != listbox.selectedIndex || vm_listbox_changed) && (this.onchange != null) && doEvents) {
			this.onchange.call(listbox);
		}
	}
	
	return this;
}

/**
 * Replace document selectboxes specified by param
 *
 * @param mixed listboxes jQuery 
 * @param bool useAnimation True if animation needed. False as default
 * @param string classPostfix Listbox class name postfix
 *
 * @example vm_listbox_replace('#selectbox_for_replace', true, 'main');
 */
vm_listbox_replace = function(listboxes) {
	var useAnimation = false;
	if (arguments[1])
		useAnimation = arguments[1];
		
	var classPostfix = '';
	if (arguments[2])
		classPostfix = '_'+arguments[2];
			
	jQuery(listboxes).each(function () {
		var listbox = jQuery(this);
		var listboxBlock = listbox[0];
		if (listboxBlock.nodeName.toUpperCase() != 'SELECT') return;
			
		var oldId = listboxBlock.id;
		var name = listboxBlock.name || '';
		
		var container = oldId || vm_uniq(name);
		var _listbox = new vm_listbox(container, useAnimation, name, listboxBlock.onchange);
		
		
		var width = parseInt(listboxBlock.offsetWidth) || parseInt(listboxBlock.style.width);
		width = (width > VM_LISTBOX_MINWIDTH) ? width : VM_LISTBOX_MINWIDTH;
		
		if (listboxBlock.style.display == 'none') 
			_display = 'display: none;';
		else
			_display = '';

		listboxBlock.style.display = 'none';
		listboxBlock.id = _listbox.listboxControl;
		
		if (listboxBlock.className != '') {
			classPostfix = '_'+listboxBlock.className;
			listboxBlock.className += ' vm_listbox_listboxBlock';
		} else if (arguments[2]) {
			classPostfix = '_'+arguments[2];
			listboxBlock.className = 'vm_listbox_listboxBlock';
		} else {
			classPostfix = '';
			listboxBlock.className = 'vm_listbox_listboxBlock';
		}
		
		var stringBuilder = new StringBuilder('<div tabindex="0" id="', _listbox.container, '" class="vm_listbox', classPostfix, '" style="width: ', width, 'px;', _display, '"></div>');
			
		listbox.before(stringBuilder.ToString());
		
		stringBuilder.Empty();
		
		stringBuilder.Append('<div id="', _listbox.currentItem, '" class="vm_listbox_current', classPostfix, '" onclick="vm_listboxes[\'', _listbox.container, '\'].toggle()">');
		
		/*if (listboxBlock.options.length > 0 && listboxBlock.selectedIndex >= 0)
			stringBuilder.Append(listboxBlock.options[listboxBlock.selectedIndex].text);*/
			
		stringBuilder.Append('</div><div class="vm_listbox_button', classPostfix, '" onclick="vm_listboxes[\'', _listbox.container, '\'].toggle()">&nbsp;</div>');
		
		vm_gid(_listbox.container).innerHTML = stringBuilder.ToString();
			
		currentSelect = new StringBuilder();
		
		var iterations = listboxBlock.options.length;
		for (index = 0; index < iterations; index++) {
			var option = listboxBlock.options[index];		
			if (option && option.nodeName && option.nodeName.toUpperCase() == 'OPTION') {
				currentSelect.Append(
					'<li id="',
					index,
					'" onclick="vm_listboxes[\'', 
					_listbox.container, 
					'\'].value(', 
					index, 
					')" onmouseover="jQuery(this).addClass(\'over\')" onmouseout="jQuery(this).removeClass(\'over\')" accesskey="', 
					option.text.toLowerCase(),
					'">', 
					option.text, 
					'</li>'
				);
			}
		}
						
		stringBuilder.Empty();
		stringBuilder.Append('<ul id="', _listbox.optionList, '" class="vm_listbox_popup', classPostfix, '" style="width: ', width, 'px;">', currentSelect.ToString(), '</ul>');
		
		var $container = jQuery('#'+_listbox.container);
	
		$container.append(
			stringBuilder.ToString()
		);
		
		var $optionList = jQuery('#'+_listbox.optionList);
		
		if (listboxBlock.options.length > 0 && listboxBlock.selectedIndex >= 0) {
			_listbox.value(listboxBlock.selectedIndex, false);
			
			var height = listboxBlock.options.length;
			height = (height > VM_LISTBOX_MAXLENGTH) ? VM_LISTBOX_MAXLENGTH : height;
			
			var optionHeight = parseInt($('li', $optionList).eq(0).css('height'));
			optionHeight = (optionHeight > 0) ? optionHeight : VM_LISTBOX_HIDDEN_OPTION_HEIGHT;
			
			_listbox.optionsHeight = height * optionHeight + (jQuery.browser.msie ? 2 : 0);
			$optionList.height(_listbox.optionsHeight);
		}
		
		_listbox.optionsDefaultPosition = parseInt($optionList[0].offsetTop);
		
		_listbox.optionsDefaultPosition = _listbox.optionsDefaultPosition > 0
												? _listbox.optionsDefaultPosition
												: (jQuery.browser.msie ? 1 + parseInt($optionList.css('top')) : parseInt($('li', $optionList).eq(0).css('top')));

		$optionList.hide();
				
		$container.click(function(event) {
			jQuery(this).focus();
		});
		
		var key_event = (jQuery.browser.opera) ? "keypress" : "keydown";
			
		$container.bind(key_event, function(event) {
			switch (event.keyCode) {
				case 13:
				case 32:
					vm_listbox_search_query = '';		
					_listbox.toggle();
					if (vm_listbox_changed) {
						_listbox.value();
						vm_listbox_changed = false;
					}
										
					preventDefault(event);
					return false;
				case 38:				
					var $selected = $optionList.find('.selected').prev();
					if ($selected.length > 0) {
						_listbox.value($selected.attr('id'), false);
						
						vm_listbox_search_query = '';
					}
						
					preventDefault(event);
					return false;
				case 40:				
					var $selected = $optionList.find('.selected').next();
					if ($selected.length > 0) {
						_listbox.value($selected.attr('id'), false);
						
						vm_listbox_search_query = '';
					}
						
					preventDefault(event);
					return false;
				default:
					var currentLetter = String.fromCharCode(event.keyCode).toLowerCase();

					vm_listbox_search_query += currentLetter;
					
					var $selected = jQuery("li[accesskey^='" + vm_listbox_search_query + "']", $optionList);
					if ($selected.length == 0) {
						if (!onlyChars(currentLetter, vm_listbox_search_query)) {
							vm_listbox_search_query = vm_listbox_search_query.charAt(vm_listbox_search_query.length - 1);
							if (vm_listbox_search_query != currentLetter)
								vm_listbox_search_query += currentLetter;

							$selected = jQuery("li[accesskey^='" + vm_listbox_search_query + "']", $optionList);
						}
						if ($selected.length == 0)
							$selected = jQuery("li[accesskey^='" + currentLetter + "']", $optionList);
					}

					if ($selected.length == 0) {
						vm_listbox_search_query = '';
					} else {
						_listbox.value($selected.eq(onlyChars(currentLetter, vm_listbox_search_query) ? (vm_listbox_search_query.length - 1) % $selected.length : 0).attr('id'), false);
					}
			}
		});
		
		$container.blur(function(event) {
			vm_listbox_search_query = '';
			if (vm_listbox_changed) {
				_listbox.value();
				vm_listbox_changed = false;
			}
		});
		
		if (!jQuery.browser.msie) {
			var _optionList = vm_gid(_listbox.optionList);
			var _computedStyle = document.defaultView.getComputedStyle(_optionList, null);

			var diffWidth = parseInt(_computedStyle.borderLeftWidth) + parseInt(_computedStyle.borderRightWidth) + 
				parseInt(_computedStyle.paddingLeft) + parseInt(_computedStyle.paddingRight);
			_optionList.style.width = width - diffWidth;
		}
	});
}

/**
 * Change block visibility
 *
 * @param mixed id DOM identifier
 */
function vm_show(id) {
	if (vm_listboxes[id]) {
		vm_current_listbox = id;
		vm_gid(id).style.zIndex = VM_LISTBOX_ZINDEX;
		
		var element = vm_gid(vm_listboxes[id].optionList);
		var container = vm_gid(vm_listboxes[id].container);
		if (element && container) {
			if (jQuery) {
				var $element = $(element);
				if ($(container).offset().top + vm_listboxes[id].optionsDefaultPosition + vm_listboxes[id].optionsHeight - jQuery(window).scrollTop() > jQuery(window).height()) {
					$element.css('top', $(container).height() - vm_listboxes[id].optionsHeight - vm_listboxes[id].optionsDefaultPosition - (jQuery.browser.msie ? 0 : 2));
				} else {
					$element.css('top', vm_listboxes[id].optionsDefaultPosition);
				}
			}
			
			if (vm_listboxes[id].useAnimation && jQuery)
				jQuery('#' + vm_listboxes[id].optionList).slideDown(VM_LISTBOX_ANIMATION_SPEED);
			else
				element.style.display = 'block';
		}
	}
}

/**
 * Change block visibility
 *
 * @param mixed id DOM identifier
 */
function vm_hide(id) {
	if (vm_listboxes[id]) {
		vm_current_listbox = null;
		
		vm_gid(id).style.zIndex = VM_LISTBOX_DEFAULT_ZINDEX;
		var element = vm_gid(vm_listboxes[id].optionList);
		if (element)
			element.style.display = 'none';
	}
}

/**
 * Generate unique id
 *
 * @param mixed id DOM identifier
 */
function vm_uniq(id) {
	if (!vm_gid(id))
		return id;
	else
		return vm_uniq(id + Math.round(Math.random()*1000))
}

/**
 * Get DOM element by id
 *
 * @param mixed id DOM identifier
 */
function vm_gid(id) {
	var element = document.getElementById(id);
	if (element)
		return element;
	else
		return false;
}

/**
 * Checks if string consist of only one char
 *
 * @param char letter
 * @param string str
 */
function onlyChars(letter, str) {
	var length = str.length;
	return (str.replace(new RegExp(letter, "g"), "").length == 0);
}

function preventDefault(event) {
	if (event.preventDefault)
		event.preventDefault();
	else
		event.returnValue = false;
}

/**
 * Set document onclick event handler / Close all active selectboxes
 */
jQuery(document.body).bind('click', function(event) 
{
	var element = event.target || event.srcElement;
	if ((element.className.indexOf('vm_listbox_button') < 0) && (element.className.indexOf('vm_listbox_current') < 0)) 
	{
		if (vm_current_listbox)
			vm_hide(vm_current_listbox);
	}
});
/*document.onclick = function(e) {
	e = e || event;
	var element = e.target || e.srcElement;
	if ((element.className.indexOf('vm_listbox_button') < 0) && (element.className.indexOf('vm_listbox_current') < 0)) {
		if (vm_current_listbox)
			vm_hide(vm_current_listbox);
	
	
	return true;
}*/