/*
	title: jQuery.selectable.js
	required: jQuery(tested on 1.3.2)
	copy: Copyright 2009 nori (norimania@gmail.com)
	license: MIT
	author: 5509 - http://moto-mono.net
	archive: http://moto-mono.net/2008/09/14/jqueryselectable.html
	update: 2009-07-30 01:30
	date: 2008-09-14 02:34
 */
 
(function($j){

	// jQuery.selectable
	// Make selectbox so usuful and accesible
	// @ 2009-07-30 (1.0.1)
	$j.selectable = function(select,options,callback){
		
		// Options
		var c = $j.extend({
				style: 'selectable', // or 'simpleBox'
				set: 'show', // 'show', 'slideDown' or 'fadeIn'
				out: 'hide', // 'hide', 'slideUp' or 'fadeOut'
				inDuration: 'normal', // 'slow', 'normal', 'fast' or 0~1
				outDuration: 'normal',
				opacity: 1, // pulldown mat
				pos: 0
			},options),
			selectbox = $j(select),
			// Declare attrs
			attrs = {
				id: selectbox.attr('id'),
				cl: selectbox.attr('class')
			},
			// Declare flag
			has_optgroup = ($j('optgroup',selectbox).length>0) ? true : false;
																 
		// Get optgroup & option elements
		if(has_optgroup){
			var optgroup = $j('optgroup',selectbox);
			var option = new Array();
			for(var i=0;i<optgroup.length;i++){
				option[i] = $j('option',optgroup[i]);
			}
		}else{
			var option = $j('option',selectbox);
		}
			
		// Generate input elements
		var m_input = $j(document.createElement('a')),
			m_text = $j(document.createElement('span')),
			style = (c.style.match(/simpleBox/)) ? 'sBox' : 'sctble';
		m_input.append(m_text).attr({
			id: attrs.id+'_dummy',
			href: '#'
		}).addClass('sctble_display').addClass(style).addClass(attrs.cl).insertAfter(select);
		
		// Hide select elements
		selectbox.hide();
		
		// Set target(mat) position
		function setPos(t){
			var pos = m_input.offset();
			t.css({
				top: pos.top + m_input.height()*1.3+c.pos,
				left: pos.left
			});
		};
					
		// Generate mat
		var mat = $j(document.createElement('div'));
		mat.attr({
			id: attrs.id+'_mat'
		}).addClass(style).addClass(attrs.cl);
		var generateAnchor = function(obj,par){
			var a = $j(document.createElement('a'));
			$j(par).append(a);
			a.text(obj.text()).attr({
				href: '#'+encodeURI(obj.text()),
				name: obj.val()
			});
			if(obj.is(':selected')){
				m_text.text(obj.text());
				a.addClass('selected');
			}
			if(obj.hasClass('br')){
				var br = $j(document.createElement('br'));
				br.insertAfter(a);
			}
		}
		if(has_optgroup){
			var dl = $j(document.createElement('dl'));
			for(var j=0;j<optgroup.length;j++){
				var dt = $j(document.createElement('dt'));
				dt.text($j(optgroup[j]).attr('label'));
				var dd = $j(document.createElement('dd'));
				for(var k=0;k<option[j].length;k++){
					generateAnchor($j(option[j][k]),dd);
				}
				dl.append(dt).append(dd);
			}
			mat.append(dl).addClass('optg');
		}else{
			var p = document.createElement('p');
			for(var j=0;j<option.length;j++){
				generateAnchor($j(option[j]),p);
			}
			mat.append(p).addClass('nooptg');
		}
		$j('body').append(mat);
		if(document.all && typeof document.body.style.maxHeight == 'undefined'){
			if(c.height<mat.height()) mat.css('height',c.height);
		}else{
			mat.css('maxHeight',c.height);
		}
		mat.addClass('sctble_mat').css({
			position: 'absolute',
			zIndex: 1000,
			display: 'none'
		});
		$j('*:first-child',mat).addClass('first-child');
		$j('*:last-child',mat).addClass('last-child');
		
		// Event apply
		var hide = function(tm){
			switch(c.out){
				case 'slideUp':
					tm.slideUp(c.outDuration);
					break;
				case 'fadeOut':
					tm.fadeOut(c.outDuration);
					break;
				default:
					tm.hide();
					break;
			}
		}
		
		// Flag checking where the events was called
		var is_called = true;
		
		// Display the mat
		var show = function(target){
			hide($j('.sctble_mat'));
			switch(c.set){
				case 'slideDown':
					mat.slideDown(c.inDuration).css('opacity',c.opacity);
					break;
				case 'fadeIn':
					mat.css({
						display: 'block',
						opacity: 0
					}).fadeTo(c.inDuration,c.opacity);
					break;
				default:
					mat.show().css('opacity',c.opacity);
					break;
			}
			
			var interval = isNaN(c.inDuration) ? null : (c.inDuration+10);
			if(interval==null){
				if(c.inDuration.match(/slow/)) interval = 610;
				else if(c.inDuration.match(/normal/)) interval = 410;
				else interval = 210;
			}
			var chk = setInterval(function(){
				$j('a.selected',mat).focus();
				clearInterval(chk);
			},interval);
		}
		
		// Call selectable
		m_input.click(function(event){
			setPos(mat);
			$j(this).addClass('sctble_focus');
			$j('a.sctble_focus').not(this).removeClass('sctble_focus');
			show(this);
			event.stopPropagation();
			return false;
		}).keyup(function(event){
			if(is_called){
				setPos(mat);
				show(this);
				event.stopPropagation();
			}else{
				is_called = true;
			}
		});
		mat.click(function(event){
			event.stopPropagation();
		});
		
		// Hide tha mat
		$j('body,a').not('a.sctble_display').click(function(event){
			$j('a.sctble_focus').removeClass('sctble_focus');
			hide(mat);
		}).not('a').keyup(function(e){
			if(e.keyCode==27){
				$j('a.sctble_focus').removeClass('sctble_focus');
				is_called = false;
				m_input.blur();
				hide(mat);
			}
		});
		
		// Click value append to both dummy and change original select value
		$j('a',mat).click(function(){
			var self = $j(this);
			m_text.text(decodeURI(self.attr('href').split('#')[1]));
			$j('option[value="'+self.attr('name')+'"]',select).attr('selected','selected');
			$j('.selected',mat).removeClass('selected');
			self.addClass('selected');
			m_input.removeClass('sctble_focus');
			is_called = false;
			hide(mat);
			if(callback) callback();
			m_input.focus();
			return false;
		});
		
		// Be able to click original select label
		$j('label[for="'+attrs.id+'"]').click(function(event){
			setPos(mat);
			m_input.addClass('sctble_focus');
			$j('a.sctble_focus').not(m_input).removeClass('sctble_focus');
			show(m_input);
			event.stopPropagation();
			return false;
		});
			
	}
	
	// Extense the namespace of jQuery as method
	$j.fn.selectable = function(options,callback){	
		$j(this).each(function(){$j.selectable(this,options,callback)});
	}
	
})(jQuery);