/* jQuery Stylised Dropdown Plugin 
   Author: Tom Chislett
   Version: 1.0.0
*/

jQuery.styledDropdown = function(element,params)
{
	var _self = this;
	this.element = this.element ? this.element : element;
	this.field;
	this.ddelement = null;
	this.options = this.options ? this.options : [];
	this.selectedIndex = 0;
	this.width = 0;
	this.position = [];
	this.change = [];
	this.attributes = {};
	this.className = this.className ? this.className : 'ui-dropdown';
	this.value = null;
	this.keySelected = 0;
	this.maxHeight = arguments.length > 1 && params.maxHeight ? params.maxHeight : 1000;
	this.cssPosition = arguments.length > 1 && params.position ? params.position : 'absolute';

	this.eventOpen = this.eventOpen ? this.eventOpen : function(e) { _self.open(e) };
	this.eventSelect = this.eventSelect ? this.eventSelect : function(e) { _self.select(e) };
	this.eventKeydown = this.eventKeydown ? this.eventKeydown : function(e) { _self.keydown(e) };
	this.eventClose = this.eventClose ? this.eventClose : function(e) { _self.close(e) };
	this.eventCancel = this.eventCancel ? this.eventCancel : function(e) { _self.cancel(e) };

	if ( this.element )
	{
		// save options data
		jQuery( this.element ).find( 'option' ).each( function(i)
		{
			if ( !_self.options[i] ) _self.options[i] = [];
			_self.options[i][0] = jQuery( this ).val();
			_self.options[i][1] = jQuery( this ).text();
			_self.options[i][2] = jQuery( this ).attr( 'class' )

			if ( this.selected ) _self.selectedIndex = i;
		} );

		// get name
		var name = jQuery( this.element ).attr( 'name' );

		// save attributes
		var attributes = this.element.attributes;
		for ( var x = 0; x < attributes.length; x++ ) this.attributes[ attributes[x].nodeName ] = attributes[x].nodeValue;

		// save class names
		this.className = this.className + ' ' + this.element.className.replace( this.className, '' );
	
		// copy over events
		var events = jQuery.data( this.element, 'events' ) ? jQuery.data( this.element, 'events' )[ 'change' ] : null;
		if ( events ) for ( var handler in events ) this.change.push( events[handler] );

		// calculate max width
		var tmp = jQuery( '<div class="ui-dropdown-menu" style="position:static"><ul><li style="display:inline; padding:0"></li></ul></div>' );
		jQuery( 'body' ).append( tmp );
		for ( var x = 0; x < this.options.length; x++ )
		{
			tmp.find( 'li' ).text( this.options[x][1] );
			if ( tmp.find( 'li' )[0].offsetWidth > this.width ) this.width = tmp.find( 'li' )[0].offsetWidth /***************/ + 20;
		}
		tmp.remove();
		var cssWidth = jQuery( this.element ).css( 'width' );
		if ( cssWidth != 'auto' && parseInt( cssWidth ) > this.width ) this.width = parseInt( cssWidth ) - 16;
	
		// draw styled dropdown
		this.draw();

		// save reference to object in jQuery
		this.element.data( 'uiDropdown', _self );

		// create hidden field with data
		this.field = jQuery( '<input type="hidden" name="' + name + '" value="' + this.options[this.selectedIndex][0] + '" />' );
		this.element.after( this.field );
	
		// save data into jQuery
		this.element.val( this.options[this.selectedIndex][0] );
	
		// save selected value
		this.value = this.options[this.selectedIndex][0];

		// bind window close event to replace normal selects for server data
		// jQuery( window ).bind( 'beforeunload', unload );
	}
}

jQuery.styledDropdown.prototype.draw = function()
{
	var self = this;

	// get id if there is one
	var id = ( this.element.id ) ? this.element.id : null;

	// build up dropdown
	var width = this.width;// + 3; //( jQuery( this.element ).css( 'width' ) != 'auto' ) ? ( parseInt( jQuery( this.element ).css( 'width' ) ) > this.width ) ? parseInt( jQuery( this.element ).css( 'width' ) ) : this.width + 5 : this.width + 5;
//	if ( jQuery.browser.msie && jQuery.browser.version < 7 ) width = width - 10;
	var dropdown = jQuery( '<a href="#" class="' + this.className + '"><span class="ui-dropdown-content">' + this.options[this.selectedIndex][1] + '</span></a>' );

	// bind events
	dropdown.bind( 'click', self.eventOpen );
	dropdown.bind( 'keydown', self.eventKeydown );

	if ( id ) dropdown.attr( 'id', id );

	// replace existing dropdown with styled one
	var select = jQuery( this.element );
	this.element = jQuery( dropdown );
	select.replaceWith( this.element );

	// fix width
//	width -= parseInt( dropdown.css( 'padding-right' ) );
	if ( width < 0 ) width = 0;
	dropdown.find( 'span' ).css( { width:width+'px' } );
}


jQuery.styledDropdown.prototype.unload = function()
{
	var select = jQuery( '<select></select>' ).css( { visibility:'hidden' } );
	
	// copy back attributes
	jQuery.each( this.attributes, function(key, value) { select[0].setAttribute( key, value ) } );

	// add in options
	jQuery.each( this.options, function() { select.append( jQuery( '<option value="' + this[0] + '">' + this[1] + '</option>' ) ) } );

	// set selected option
	select.find( 'option' )[ this.selectedIndex ].selected = 'selected';

	// replace ui-dropdown
	this.element.replaceWith( select.removeClass( 'ui-dropdown' ) );
	this.element = select;
}

jQuery.styledDropdown.prototype.open = function(e)
{
	var self = this;

	if ( this.element.attr( 'disabled' ) != 'disabled' )
	{
		// get position for dropdown
		var jquery_offset = this.element.offset( { border:true, padding:true } );
		this.position = [ jquery_offset.left, jquery_offset.top + this.element.outerHeight() ];
		var width = ( this.element.innerWidth() > this.width ) ? this.element.innerWidth() : this.width+20;
		var top = this.position[1];
		var cssPosition = !jQuery.browser.msie || jQuery.browser.version >= 7 ? this.cssPosition : 'absolute';
		if ( cssPosition == 'fixed' ) top = top - jQuery( document ).scrollTop();

		// apply open class to dropdown
		this.element.addClass( 'ui-dropdown-open' );

		// create dropdown
		this.ddelement = jQuery( '<div class="ui-dropdown-menu"><div class="ui-dropdown-menu-top"><div class="ui-dropdown-menu-top-inner"></div></div><ul class="ui-dropdown-menu-content"></ul><div class="ui-dropdown-menu-bottom"><div class="ui-dropdown-menu-bottom-inner"></div></div></div>' ).css( { width:width });
		this.ddelement.css( { position:cssPosition, top:top+'px', left:this.position[0]+'px' } );
//		if ( jQuery.browser.msie && jQuery.browser.version < 7 ) { var dd = this.ddelement; jQuery( window ).bind( 'scroll', function() { dd.css( { top:top + jQuery( document ).scrollTop() } ) } ) };
		for ( var x = 0; x < this.options.length; x++ )
		{
			var item = jQuery( '<li>' + this.options[x][1] + '</li>' );

			if ( x == this.selectedIndex ) item.addClass( 'ui-dropdown-selected' );

			if (this.options[x][2]) item.addClass( this.options[x][2] )

			item.bind( 'click', self.eventSelect );
			item.bind( 'mouseover', function(e)
			{
				self.ddelement.find( 'li.ui-dropdown-selected' ).removeClass( 'ui-dropdown-selected' );
				jQuery( this ).addClass( 'ui-dropdown-selected' );

				self.keySelected = self.ddelement.find( 'li' ).index( this );
			} );

			this.ddelement.find( 'ul' ).append( item );
		}

		// append dropdown to container
		jQuery( 'body' ).append( this.ddelement );

		// set height for ie6
		if ( jQuery.browser.msie && jQuery.browser.version < 7 && this.ddelement.find( 'ul' ).height() > this.maxHeight ) this.ddelement.find( 'ul' ).css( { height:this.maxHeight } );

		// remove open event and add cancel event
		this.element.unbind( 'click', self.eventOpen );
		this.element.bind( 'click', self.eventCancel );
	
		// add close event
		setTimeout( function() { jQuery( 'body' ).bind( 'click', self.eventClose ) }, 1);
	}

//	e.stopPropagation();
	e.preventDefault();
}

jQuery.styledDropdown.prototype.keydown = function(e)
{
	if ( e.keyCode == 32 ) // space
	{
		if ( !this.ddelement )
		{
			this.keySelected = this.selectedIndex;

			this.open(e);
		}
		else
		{
			this.ddelement.find( 'li.ui-dropdown-selected' ).click();

			e.preventDefault();
		}
	}
	else if ( e.keyCode == 40 ) // down
	{
		if ( ++this.keySelected > this.options.length - 1 ) this.keySelected = 0;

		if ( this.ddelement )
		{
			this.ddelement.find( 'li.ui-dropdown-selected' ).removeClass( 'ui-dropdown-selected' );
			this.ddelement.find( 'li:eq(' + this.keySelected + ')' ).addClass( 'ui-dropdown-selected' );
		}
		else
		{
			this.selectedIndex = this.keySelected;

			// set text dropdown
			this.element.find( 'span' ).text( this.options[this.selectedIndex][1] );
	
			// save value in jQuery
			this.element.data( 'value', this.options[this.selectedIndex][0] );
	
			// save selected value
			this.value = this.options[this.selectedIndex][0];
			this.element.val( this.options[this.selectedIndex][0] );
			this.field.val( this.options[this.selectedIndex][0] );
	
			// perform custom event listeners if they exist
			e.target = this.element[0];
			if ( this.change.length ) for ( var x = 0; x < this.change.length; x++ ) this.change[x].call( this, e);
		}

		e.preventDefault();
	}
	else if ( e.keyCode == 38 ) // up
	{
		if ( --this.keySelected < 0 ) this.keySelected = this.options.length - 1;

		if ( this.ddelement )
		{
			this.ddelement.find( 'li.ui-dropdown-selected' ).removeClass( 'ui-dropdown-selected' );
			this.ddelement.find( 'li:eq(' + this.keySelected + ')' ).addClass( 'ui-dropdown-selected' );
		}
		else
		{
			this.selectedIndex = this.keySelected;

			// set text dropdown
			this.element.find( 'span' ).text( this.options[this.selectedIndex][1] );
	
			// save value in jQuery
			this.element.data( 'value', this.options[this.selectedIndex][0] );
	
			// save selected value
			this.value = this.options[this.selectedIndex][0];
			this.element.val( this.options[this.selectedIndex][0] );
			this.field.val( this.options[this.selectedIndex][0] );
	
			// perform custom event listeners if they exist
			e.target = this.element[0];
			if ( this.change.length ) for ( var x = 0; x < this.change.length; x++ ) this.change[x].call( this, e);
		}

		e.preventDefault();
	}
	else if ( e.keyCode == 13 ) // return
	{
		this.ddelement.find( 'li:eq(' + this.keySelected + ')' ).click();
		
		e.preventDefault();
	}
	else if ( e.keyCode == 9 && this.ddelement ) // tab
	{
		this.ddelement.find( 'li:eq(' + this.keySelected + ')' ).click();
	}
}

jQuery.styledDropdown.prototype.select = function(e)
{
	// get index of selected item
	var new_selection = -1;
	var target_li = e.target;
	if ( target_li.nodeName != 'LI' ) target_li = jQuery( target_li ).parents( 'li' )[0];
	var list_items = this.ddelement.find( 'li' );
	for ( var x = 0; x < list_items.length; x++ ) if ( list_items[x] == target_li ) { new_selection = x; break }

	if ( new_selection != this.selectedIndex )
	{
		this.selectedIndex = new_selection;

		// set text dropdown
		this.element.find( 'span' ).html( this.options[this.selectedIndex][1] );

		// save value in jQuery
		this.element.data( 'value', this.options[this.selectedIndex][0] );

		// save selected value
		this.value = this.options[this.selectedIndex][0];
		this.element.val( this.options[this.selectedIndex][0] );
		this.field.val( this.options[this.selectedIndex][0] );

		// perform custom event listeners if they exist
		e.target = this.element[0];
		if ( this.change.length ) for ( var x = 0; x < this.change.length; x++ ) this.change[x].call( this, e);
	}

	this.eventClose(e);

	e.stopPropagation();
	e.preventDefault();
}

jQuery.styledDropdown.prototype.close = function(e)
{
	var self = this;

	this.ddelement.remove();
	delete this.ddelement;

	// remove open class
	this.element.removeClass( 'ui-dropdown-open' );

	// remove close event
	jQuery( 'body' ).unbind( 'click', self.eventClose );

	// add open event and remove cancel event
	setTimeout( function() { self.element.bind( 'click', self.eventOpen ) }, 1 );
	this.element.unbind( 'click', self.eventCancel );

	if (e) e.preventDefault();
}
	
jQuery.styledDropdown.prototype.cancel = function(e)
{
	this.eventClose(e);

	e.stopPropagation();
}

jQuery.styledDropdown.prototype.set = function(index)
{
	if ( index != this.selectedIndex )
	{
		this.selectedIndex = index;

		// set text dropdown
		this.element.find( 'span' ).html( this.options[this.selectedIndex][1] );

		// save value in jQuery
		this.element.data( 'value', this.options[this.selectedIndex][0] );

		// save selected value
		this.value = this.options[this.selectedIndex][0];
		this.element.val( this.options[this.selectedIndex][0] );
		this.field.val( this.options[this.selectedIndex][0] );
	}
}

jQuery.styledDropdown.prototype.reset = function( dropdown )
{
	var select = jQuery( '<select></select>' );
	
	// copy back attributes
	jQuery.each( this.attributes, function(key, value) { select[0].setAttribute( key, value ) } );
	if ( dropdown.id ) select[0].id = dropdown.id;

	// add in options
	jQuery.each( this.options, function() { select.append( jQuery( '<option value="' + this[0] + '">' + this[1] + '</option>' ) ) } );

	// set selected option
	select.find( 'option' )[ this.selectedIndex ].selected = 'selected';

	// replace ui-dropdown
	jQuery( dropdown ).replaceWith( select.removeClass( 'ui-dropdown' ) );

	// remove adjacent hidden field
	select.next( 'input[type=hidden]' ).remove();

	return select[0];
}

jQuery.fn.styledDropdown = function(params) { this.each( function() { new jQuery.styledDropdown( this, params ) } ) };
