/*
 * jQuery UI Selectmenu version 1.5.0pre
 *
 * Copyright (c) 2009-2010 filament group, http://filamentgroup.com
 * Copyright (c) 2010-2014 Felix Nagel, http://www.felixnagel.com
 * Licensed under the MIT (MIT-LICENSE.txt)
 *
 * https://github.com/fnagel/jquery-ui/wiki/Selectmenu
 */

/* global define */
/* global jQuery */

( function( factory ) {
	if ( typeof define === 'function' && define.amd ) {

		// AMD. Register as an anonymous module.
		define( ['jquery','jqueryUI'], factory );
	} else {

		// Browser globals
		factory( jQuery );
	}
} ( function( $ ) {

	$.widget('ui.selectmenu', {
		options: {
			appendTo: 'body',
			typeAhead: 1000,
			style: 'dropdown',
			positionOptions: null,
			width: null,
			menuWidth: null,
			handleWidth: 26,
			maxHeight: null,
			icons: null,
			format: null,
			escapeHtml: false,
			bgImage: function() {}
		},

		_create: function() {
			var self = this, o = this.options;

			// make / set unique id
			var selectmenuId = this.element.uniqueId().attr( 'id' );

			// quick array of button and menu id's
			this.ids = [ selectmenuId, selectmenuId + '-button', selectmenuId + '-menu' ];

			// define safe mouseup for future toggling
			this._safemouseup = true;
			this.isOpen = false;

			// create menu button wrapper
			this.newelement = $( '<a></a>', {
				'class': 'ui-selectmenu ui-widget ui-state-default ui-corner-all',
				'id' : this.ids[ 1 ],
				'role': 'button',
				'href': '#nogo',
				'tabindex': this.element.attr( 'disabled' ) ? 1 : 0,
				'aria-haspopup': true,
				'aria-owns': this.ids[ 2 ]
			});
			this.newelementWrap = $( '<span></span>' )
				.append( this.newelement )
				.insertAfter( this.element );

			// transfer tabindex
			var tabindex = this.element.attr( 'tabindex' );
			if ( tabindex ) {
				this.newelement.attr( 'tabindex', tabindex );
			}

			// save reference to select in data for ease in calling methods
			this.newelement.data( 'selectelement', this.element );

			// menu icon
			this.selectmenuIcon = $( '<span class="ui-selectmenu-icon ui-icon"></span>' )
				.prependTo( this.newelement );

			// append status span to button
			this.newelement.prepend( '<span class="ui-selectmenu-status"></span>' );

			// make associated form label trigger focus
			this.element.on({
				'click.selectmenu':  function( event ) {
					self.newelement.focus();
					event.preventDefault();
				}
			});

			// click toggle for menu visibility
			this.newelement
				.on( 'mousedown.selectmenu', function( event ) {
					self._toggle( event, true );
					// make sure a click won't open/close instantly
					if ( o.style == 'popup' ) {
						self._safemouseup = false;
						setTimeout( function() { self._safemouseup = true; }, 300 );
					}

					event.preventDefault();
				})
				.on( 'click.selectmenu', function( event ) {
					event.preventDefault();
				})
				.on( 'keydown.selectmenu', function( event ) {
					var ret = false;
					switch ( event.keyCode ) {
					case $.ui.keyCode.ENTER:
						ret = true;
						break;
					case $.ui.keyCode.SPACE:
						self._toggle( event );
						break;
					case $.ui.keyCode.UP:
						if ( event.altKey ) {
							self.open( event );
						} else {
							self._moveSelection( -1 );
						}
						break;
					case $.ui.keyCode.DOWN:
						if ( event.altKey ) {
							self.open( event );
						} else {
							self._moveSelection( 1 );
						}
						break;
					case $.ui.keyCode.LEFT:
						self._moveSelection( -1 );
						break;
					case $.ui.keyCode.RIGHT:
						self._moveSelection( 1 );
						break;
					case $.ui.keyCode.TAB:
						ret = true;
						break;
					case $.ui.keyCode.PAGE_UP:
					case $.ui.keyCode.HOME:
						self.index( 0 );
						break;
					case $.ui.keyCode.PAGE_DOWN:
					case $.ui.keyCode.END:
						self.index( self._optionLis.length );
						break;
					default:
						ret = true;
					}
					return ret;
				})
				.on( 'keypress.selectmenu', function( event ) {
					if ( event.which > 0 ) {
						self._typeAhead( event.which, 'mouseup' );
					}
					return true;
				})
				.on( 'mouseover.selectmenu', function() {
					if ( !o.disabled ) $( this ).addClass( 'ui-state-hover' );
				})
				.on( 'mouseout.selectmenu', function() {
					if ( !o.disabled ) $( this ).removeClass( 'ui-state-hover' );
				})
				.on( 'focus.selectmenu', function() {
					if ( !o.disabled ) $( this ).addClass( 'ui-state-focus' );
				})
				.on( 'blur.selectmenu', function() {
					if (!o.disabled) $( this ).removeClass( 'ui-state-focus' );
				});

			// document click closes menu
			$( document ).on( 'mousedown.selectmenu-' + this.ids[ 0 ], function( event ) {
			//check if open and if the clicket targes parent is the same
				if ( self.isOpen && !$( event.target ).closest( '#' + self.ids[ 1 ] ).length ) {
					self.close( event );
				}
			});

			// change event on original selectmenu
			this.element
				.on( 'click.selectmenu', function() {
					self._refreshValue();
				})
			// FIXME: newelement can be null under unclear circumstances in IE8
			// TODO not sure if this is still a problem (fnagel 20.03.11)
				.on( 'focus.selectmenu', function() {
					if ( self.newelement ) {
						self.newelement[ 0 ].focus();
					}
				});

			// set width when not set via options
			if ( !o.width ) {
				o.width = this.element.outerWidth();
			}
			// set menu button width
			this.newelement.width( o.width );

			// hide original selectmenu element
			this.element.hide();

			// create menu portion, append to body
			this.list = $( '<ul></ul>', {
				'class': 'ui-widget ui-widget-content',
				'aria-hidden': true,
				'role': 'listbox',
				'aria-labelledby': this.ids[ 1 ],
				'id': this.ids[ 2 ]
			});
			this.listWrap = $( '<div></div>', {
				'class': 'ui-selectmenu-menu'
			}).append( this.list ).appendTo( o.appendTo );

			// transfer menu click to menu button
			this.list
				.on('keydown.selectmenu', function(event) {
					var ret = false;
					switch ( event.keyCode ) {
					case $.ui.keyCode.UP:
						if ( event.altKey ) {
							self.close( event, true );
						} else {
							self._moveFocus( -1 );
						}
						break;
					case $.ui.keyCode.DOWN:
						if ( event.altKey ) {
							self.close( event, true );
						} else {
							self._moveFocus( 1 );
						}
						break;
					case $.ui.keyCode.LEFT:
						self._moveFocus( -1 );
						break;
					case $.ui.keyCode.RIGHT:
						self._moveFocus( 1 );
						break;
					case $.ui.keyCode.HOME:
						self._moveFocus( ':first' );
						break;
					case $.ui.keyCode.PAGE_UP:
						self._scrollPage( 'up' );
						break;
					case $.ui.keyCode.PAGE_DOWN:
						self._scrollPage( 'down' );
						break;
					case $.ui.keyCode.END:
						self._moveFocus().last();
						break;
					case $.ui.keyCode.ENTER:
					case $.ui.keyCode.SPACE:
						self.close( event, true);
						$( event.target ).parents( 'li' ).eq(0).trigger( 'mouseup' );
						break;
					case $.ui.keyCode.TAB:
						ret = true;
						self.close( event, true );
						$( event.target ).parents( 'li' ).eq(0).trigger( 'mouseup' );
						break;
					case $.ui.keyCode.ESCAPE:
						self.close( event, true );
						break;
					default:
						ret = true;
					}
					return ret;
				})
				.on( 'keypress.selectmenu', function( event ) {
					if ( event.which > 0 ) {
						self._typeAhead( event.which, 'focus' );
					}
					return true;
				})
			// this allows for using the scrollbar in an overflowed list
				.on( 'mousedown.selectmenu mouseup.selectmenu', function() { return false; });

			// needed when window is resized
			$( window ).on( 'resize.selectmenu-' + this.ids[ 0 ], self.close.bind(this) );
		},

		_init: function() {
			var self = this, o = this.options;

			// serialize selectmenu element options
			var selectOptionData = [];
			this.element.find( 'option' ).each( function() {
				var opt = $( this );
				selectOptionData.push({
					value: opt.attr( 'value' ),
					text: self._formatText( opt.text(), opt ),
					selected: opt.attr( 'selected' ),
					disabled: opt.attr( 'disabled' ),
					classes: opt.attr( 'class' ),
					typeahead: opt.attr( 'typeahead'),
					parentOptGroup: opt.parent( 'optgroup' ),
					bgImage: o.bgImage.call( opt )
				});
			});

			// active state class is only used in popup style
			var activeClass = ( self.options.style == 'popup' ) ? ' ui-state-active' : '';

			// empty list so we can refresh the selectmenu via selectmenu()
			this.list.html( '' );

			// write li's
			if ( selectOptionData.length ) {
				for ( var i = 0; i < selectOptionData.length; i++ ) {
					var thisLiAttr = { role : 'presentation' };
					if ( selectOptionData[ i ].disabled ) {
						thisLiAttr[ 'class' ] = 'ui-state-disabled';
					}
					var thisAAttr = {
						html: selectOptionData[ i ].text || '&nbsp;',
						href: '#nogo',
						tabindex : -1,
						role: 'option',
						'aria-selected' : false
					};
					if ( selectOptionData[ i ].disabled ) {
						thisAAttr[ 'aria-disabled' ] = true;
					}
					if ( selectOptionData[ i ].typeahead ) {
						thisAAttr[ 'typeahead' ] = selectOptionData[ i ].typeahead;
					}
					var thisA = $( '<a></a>', thisAAttr )
						.on( 'focus.selectmenu', function() {
							$( this ).parent().mouseover();
						})
						.on( 'blur.selectmenu', function() {
							$( this ).parent().mouseout();
						});
					var thisLi = $( '<li></li>', thisLiAttr )
						.append( thisA )
						.data( 'index', i )
						.addClass( selectOptionData[ i ].classes )
						.data( 'optionClasses', selectOptionData[ i ].classes || '' )
						.on( 'mouseup.selectmenu', function( event ) {
							if ( self._safemouseup && !self._disabled( event.currentTarget ) && !self._disabled( $( event.currentTarget ).parents( 'ul > li.ui-selectmenu-group ' ) ) ) {
								self.index( $( this ).data( 'index' ) );
								self.select( event );
								self.close( event, true );
							}
							return false;
						})
						.on( 'click.selectmenu', function() {
							return false;
						})
						.on('mouseover.selectmenu', function( e ) {
						// no hover if disabled
							if ( !$( this ).hasClass( 'ui-state-disabled' ) && !$( this ).parent( 'ul' ).parent( 'li' ).hasClass( 'ui-state-disabled' ) ) {
								if ( self._typeAhead_timer === undefined ) {
									e.optionValue = self.element[ 0 ].options[ $( this ).data( 'index' ) ].value;
									self._trigger( 'hover', e, self._uiHash() );
									self._selectedOptionLi().addClass( activeClass );
									self._focusedOptionLi().removeClass( 'ui-selectmenu-item-focus ui-state-hover' );
									$( this ).removeClass( 'ui-state-active' ).addClass( 'ui-selectmenu-item-focus ui-state-hover' );
								}
							}
						})
						.on( 'mouseout.selectmenu', function( e ) {
							if ( $( this ).is( self._selectedOptionLi() ) ) {
								$( this ).addClass( activeClass );
							}
							e.optionValue = self.element[ 0 ].options[ $( this ).data( 'index' ) ].value;
							self._trigger( 'blur', e, self._uiHash() );
							$( this ).removeClass( 'ui-selectmenu-item-focus ui-state-hover' );
						});

					// optgroup or not...
					if ( selectOptionData[ i ].parentOptGroup.length ) {
						var optGroupName = 'ui-selectmenu-group-' + this.element.find( 'optgroup' ).index( selectOptionData[ i ].parentOptGroup );
						if ( this.list.find( 'li.' + optGroupName ).length ) {
							this.list.find( 'li.' + optGroupName).last().find('ul').append( thisLi );
						} else {
							$( '<li role="presentation" class="ui-selectmenu-group ' + optGroupName + ( selectOptionData[ i ].parentOptGroup.attr( 'disabled' ) ? ' ' + 'ui-state-disabled" aria-disabled="true"' : '"' ) + '><span class="ui-selectmenu-group-label">' + selectOptionData[ i ].parentOptGroup.attr( 'label' ) + '</span><ul></ul></li>' )
								.appendTo( this.list )
								.find( 'ul' )
								.append( thisLi );
						}
					} else {
						thisLi.appendTo( this.list );
					}

					// append icon if option is specified
					if ( o.icons ) {
						for ( var j in o.icons ) {
							if (thisLi.is(o.icons[ j ].find)) {
								thisLi
									.data( 'optionClasses', selectOptionData[ i ].classes + ' ui-selectmenu-hasIcon' )
									.addClass( 'ui-selectmenu-hasIcon' );
								var iconClass = o.icons[ j ].icon || '';
								thisLi
									.find( 'a' )
									.eq(0)
									.prepend( '<span class="ui-selectmenu-item-icon ui-icon ' + iconClass + '"></span>' );
								if ( selectOptionData[ i ].bgImage ) {
									thisLi.find( 'span' ).css( 'background-image', selectOptionData[ i ].bgImage );
								}
							}
						}
					}
				}
			} else {
				$('<li role="presentation"><a href="#nogo" tabindex="-1" role="option"></a></li>' ).appendTo( this.list );
			}
			// we need to set and unset the CSS classes for dropdown and popup style
			var isDropDown = ( o.style == 'dropdown' );
			this.newelement
				.toggleClass( 'ui-selectmenu-dropdown', isDropDown )
				.toggleClass( 'ui-selectmenu-popup', !isDropDown );
			this.list
				.toggleClass( 'ui-selectmenu-menu-dropdown ui-corner-bottom', isDropDown )
				.toggleClass( 'ui-selectmenu-menu-popup ui-corner-all', !isDropDown )
			// add corners to top and bottom menu items
				.find( 'li' )
				.first()
				.toggleClass( 'ui-corner-top', !isDropDown )
				.end().find( 'li' ).last()
				.addClass( 'ui-corner-bottom' );
			this.selectmenuIcon
				.toggleClass( 'ui-icon-triangle-1-s', isDropDown )
				.toggleClass( 'ui-icon-triangle-2-n-s', !isDropDown );

			// set menu width to either menuWidth option value, width option value, or select width
			if ( o.style == 'dropdown' ) {
				this.list.width( o.menuWidth ? o.menuWidth : o.width );
			} else {
				this.list.width( o.menuWidth ? o.menuWidth : o.width - o.handleWidth );
			}

			// Android 2 (Gingerbread) doesn't support internal scrollable divs.
			// Don't limit the menu height for that browser.
			if ( !navigator.userAgent.match( /Android 2/ ) ) {
				var listH = this.listWrap.height();
				var winH = $( window ).height();
				// calculate default max height
				var maxH = o.maxHeight ? Math.min( o.maxHeight, winH ) : winH / 3;
				if ( listH > maxH ) this.list.height( maxH );
			}

			// save reference to actionable li's (not group label li's)
			this._optionLis = this.list.find( 'li:not(.ui-selectmenu-group)' );

			// transfer disabled state
			if ( this.element.attr( 'disabled' ) ) {
				this.disable();
			} else {
				this.enable();
			}

			// update value
			this._refreshValue();

			// set selected item so movefocus has intial state
			this._selectedOptionLi().addClass( 'ui-selectmenu-item-focus' );

			// needed when selectmenu is placed at the very bottom / top of the page
			clearTimeout( this.refreshTimeout );
			this.refreshTimeout = window.setTimeout( function () {
				self._refreshPosition();
			}, 200 );
		},

		destroy: function() {
			this.element.removeData( this.widgetName )
				.removeClass( 'ui-selectmenu-disabled' + ' ' + 'ui-state-disabled' )
				.removeAttr( 'aria-disabled' )
				.off( '.selectmenu' );

			$( window ).off( '.selectmenu-' + this.ids[ 0 ] );
			$( document ).off( '.selectmenu-' + this.ids[ 0 ] );

			this.newelementWrap.remove();
			this.listWrap.remove();

			// unbind click event and show original select
			this.element
				.off( '.selectmenu' )
				.show();

			// call widget destroy function
			$.Widget.prototype.destroy.apply( this, arguments );
		},

		_typeAhead: function( code, eventType ) {
			var self = this,
				c = String.fromCharCode( code ).toLowerCase(),
				matchee = null,
				nextIndex = null;

			// Clear any previous timer if present
			if ( self._typeAhead_timer ) {
				window.clearTimeout( self._typeAhead_timer );
				self._typeAhead_timer = undefined;
			}
			// Store the character typed
			self._typeAhead_chars = ( self._typeAhead_chars === undefined ? '' : self._typeAhead_chars ).concat( c );
			// Detect if we are in cyciling mode or direct selection mode
			if ( self._typeAhead_chars.length < 2 || ( self._typeAhead_chars.substr( -2, 1 ) === c && self._typeAhead_cycling ) ) {
				self._typeAhead_cycling = true;
				// Match only the first character and loop
				matchee = c;
			} else {
			// We won't be cycling anymore until the timer expires
				self._typeAhead_cycling = false;
				// Match all the characters typed
				matchee = self._typeAhead_chars;
			}

			// We need to determine the currently active index, but it depends on
			// the used context: if it's in the element, we want the actual
			// selected index, if it's in the menu, just the focused one
			var selectedIndex = ( eventType !== 'focus' ? this._selectedOptionLi().data( 'index' ) : this._focusedOptionLi().data( 'index' )) || 0;
			for ( var i = 0; i < this._optionLis.length; i++ ) {
				var thisText = this._optionLis.eq( i ).text().substr( 0, matchee.length ).toLowerCase();
				if ( thisText === matchee ) {
					if ( self._typeAhead_cycling ) {
						if ( nextIndex === null )
							nextIndex = i;
						if ( i > selectedIndex ) {
							nextIndex = i;
							break;
						}
					} else {
						nextIndex = i;
						break;
					}
				}
			}

			if ( nextIndex !== null ) {
			// Why using trigger() instead of a direct method to select the index? Because we don't what is the exact action to do,
			// it depends if the user is typing on the element or on the popped up menu
				this._optionLis.eq( nextIndex ).find( 'a' ).trigger( eventType );
			}

			self._typeAhead_timer = window.setTimeout( function() {
				self._typeAhead_timer = undefined;
				self._typeAhead_chars = undefined;
				self._typeAhead_cycling = undefined;
			}, self.options.typeAhead );
		},

		// returns some usefull information, called by callbacks only
		_uiHash: function() {
			var index = this.index();
			return {
				index: index,
				option: $( 'option', this.element ).get( index ),
				value: this.element[ 0 ].value
			};
		},

		open: function( event ) {
			if ( this.newelement.attr( 'aria-disabled' ) != 'true' ) {
				var self = this,
					o = this.options,
					selected = this._selectedOptionLi(),
					link = selected.find('a');

				self._closeOthers( event );
				self.newelement.addClass( 'ui-state-active' );
				self.list.attr( 'aria-hidden', false );
				self.listWrap.addClass( 'ui-selectmenu-open' );

				if ( o.style == 'dropdown' ) {
					self.newelement.removeClass( 'ui-corner-all' ).addClass( 'ui-corner-top' );
				} else {
				// center overflow and avoid flickering
					this.list
						.css( 'left', -5000 )
						.scrollTop( this.list.scrollTop() + selected.position().top - this.list.outerHeight() / 2 + selected.outerHeight() / 2 )
						.css( 'left', 'auto' );
				}

				self._refreshPosition();

				if ( link.length ) {
					link[ 0 ].focus();
				}

				self.isOpen = true;
				self._trigger( 'open', event, self._uiHash() );
			}
		},

		close: function( event, retainFocus ) {
			if ( this.newelement.is( '.ui-state-active') ) {
				this.newelement.removeClass( 'ui-state-active' );
				this.listWrap.removeClass( 'ui-selectmenu-open' );
				this.list.attr( 'aria-hidden', true );
				if ( this.options.style == 'dropdown' ) {
					this.newelement.removeClass( 'ui-corner-top' ).addClass( 'ui-corner-all' );
				}
				if ( retainFocus ) {
					this.newelement.focus();
				}
				this.isOpen = false;
				this._trigger( 'close', event, this._uiHash() );
			}
		},

		change: function( event ) {
			this.element.trigger( 'change' );
			this._trigger( 'change', event, this._uiHash() );
		},

		select: function( event ) {
			if ( this._disabled( event.currentTarget ) ) { return false; }
			this._trigger( 'select', event, this._uiHash() );
		},

		widget: function() {
			return this.listWrap.add( this.newelementWrap );
		},

		_closeOthers: function( event ) {
			$( '.ui-selectmenu.ui-state-active' ).not( this.newelement ).each( function() {
				$( this ).data( 'selectelement' ).selectmenu( 'close', event );
			});
			$( '.ui-selectmenu.ui-state-hover' ).trigger( 'mouseout' );
		},

		_toggle: function( event, retainFocus ) {
			if ( this.isOpen ) {
				this.close( event, retainFocus );
			} else {
				this.open( event );
			}
		},

		_formatText: function( text, opt ) {
			if ( this.options.format ) {
				text = this.options.format( text, opt );
			} else if ( this.options.escapeHtml ) {
				text = $( '<div></div>' ).text( text ).html();
			}
			return text;
		},

		_selectedIndex: function() {
			return this.element[ 0 ].selectedIndex;
		},

		_selectedOptionLi: function() {
			return this._optionLis.eq( this._selectedIndex() );
		},

		_focusedOptionLi: function() {
			return this.list.find( '.ui-selectmenu-item-focus' );
		},

		_moveSelection: function( amt, recIndex ) {
		// do nothing if disabled
			if ( !this.options.disabled ) {
				var currIndex = parseInt( this._selectedOptionLi().data( 'index' ) || 0, 10 );
				var newIndex = currIndex + amt;
				// do not loop when using up key
				if ( newIndex < 0 ) {
					newIndex = 0;
				}
				if ( newIndex > this._optionLis.length - 1 ) {
					newIndex = this._optionLis.length - 1;
				}
				// Occurs when a full loop has been made
				if ( newIndex === recIndex ) {
					return false;
				}

				if ( this._optionLis.eq( newIndex ).hasClass( 'ui-state-disabled' ) ) {
				// if option at newIndex is disabled, call _moveFocus, incrementing amt by one
					( amt > 0 ) ? ++amt : --amt;
					this._moveSelection( amt, newIndex );
				} else {
					this._optionLis.eq( newIndex ).trigger( 'mouseover' ).trigger( 'mouseup' );
				}
			}
		},

		_moveFocus: function( amt, recIndex ) {
			if ( !isNaN( amt ) ) {
				var currIndex = parseInt( this._focusedOptionLi().data( 'index' ) || 0, 10 );
				var newIndex = currIndex + amt;
			} else {
				var newIndex = parseInt( this._optionLis.filter( amt ).data( 'index' ), 10 );
			}

			if ( newIndex < 0 ) {
				newIndex = 0;
			}
			if ( newIndex > this._optionLis.length - 1 ) {
				newIndex = this._optionLis.length - 1;
			}

			//Occurs when a full loop has been made
			if ( newIndex === recIndex ) {
				return false;
			}

			var activeID = 'ui-selectmenu-item-' + Math.round( Math.random() * 1000 );

			this._focusedOptionLi().find( 'a' ).eq(0).attr( 'id', '' );

			if ( this._optionLis.eq( newIndex ).hasClass( 'ui-state-disabled' ) ) {
			// if option at newIndex is disabled, call _moveFocus, incrementing amt by one
				( amt > 0 ) ? ++amt : --amt;
				this._moveFocus( amt, newIndex );
			} else {
				this._optionLis.eq( newIndex ).find( 'a' ).eq(0).attr( 'id',activeID ).focus();
			}

			this.list.attr( 'aria-activedescendant', activeID );
		},

		_scrollPage: function( direction ) {
			var numPerPage = Math.floor( this.list.outerHeight() / this._optionLis.first().outerHeight() );
			numPerPage = ( direction == 'up' ? -numPerPage : numPerPage );
			this._moveFocus( numPerPage );
		},

		_setOption: function( key, value ) {
			this.options[ key ] = value;
			if ( key == 'disabled' ) {
				if ( value ) this.close();
				this.element
					.add( this.newelement )
					.add( this.list )[ value ? 'addClass' : 'removeClass' ]( 'ui-selectmenu-disabled ' + 'ui-state-disabled' )
					.attr( 'aria-disabled' , value )
					.attr( 'tabindex' , value ? 1 : 0 );
			}
		},

		disable: function( index, type ){
		// if options is not provided, call the parents disable function
			if ( typeof( index ) == 'undefined' ) {
				this._setOption( 'disabled', true );
			} else {
				this._toggleEnabled( ( type || 'option' ), index, false );
			}
		},

		enable: function( index, type ) {
		// if options is not provided, call the parents enable function
			if ( typeof( index ) == 'undefined' ) {
				this._setOption( 'disabled', false );
			} else {
				this._toggleEnabled( ( type || 'option' ), index, true );
			}
		},

		_disabled: function( elem ) {
			return $( elem ).hasClass( 'ui-state-disabled' );
		},

		// true = enabled, false = disabled
		_toggleEnabled: function( type, index, flag ) {
			var element = this.element.find( type ).eq( index ),
				elements = ( type === 'optgroup' ) ? this.list.find( 'li.ui-selectmenu-group-' + index ) : this._optionLis.eq( index );

			if ( elements ) {
				elements
					.toggleClass( 'ui-state-disabled', !flag )
					.attr( 'aria-disabled', !flag );

				if ( flag ) {
					element.removeAttr( 'disabled' );
				} else {
					element.attr( 'disabled', 'disabled' );
				}
			}
		},

		index: function( newIndex ) {
			if ( arguments.length ) {
				if ( !this._disabled( $( this._optionLis[ newIndex ] ) ) && newIndex != this._selectedIndex() ) {
					this.element[ 0 ].selectedIndex = newIndex;
					this._refreshValue();
					this.change();
				} else {
					return false;
				}
			} else {
				return this._selectedIndex();
			}
		},

		value: function( newValue ) {
			if ( arguments.length && newValue != this.element[ 0 ].value ) {
				this.element[ 0 ].value = newValue;
				this._refreshValue();
				this.change();
			} else {
				return this.element[ 0 ].value;
			}
		},

		_refreshValue: function() {
			var activeClass = ( this.options.style == 'popup' ) ? ' ui-state-active' : '';
			var activeID = 'ui-selectmenu-item-' + Math.round( Math.random() * 1000 );
			// deselect previous
			this.list
				.find( '.ui-selectmenu-item-selected' )
				.removeClass( 'ui-selectmenu-item-selected' + activeClass )
				.find('a')
				.attr( 'aria-selected', 'false' )
				.attr( 'id', '' );
			// select new
			this._selectedOptionLi()
				.addClass( 'ui-selectmenu-item-selected' + activeClass )
				.find( 'a' )
				.attr( 'aria-selected', 'true' )
				.attr( 'id', activeID );

			// toggle any class brought in from option
			var currentOptionClasses = ( this.newelement.data( 'optionClasses' ) ? this.newelement.data( 'optionClasses' ) : '' );
			var newOptionClasses = ( this._selectedOptionLi().data( 'optionClasses' ) ? this._selectedOptionLi().data( 'optionClasses' ) : '' );
			this.newelement
				.removeClass( currentOptionClasses )
				.data( 'optionClasses', newOptionClasses )
				.addClass( newOptionClasses )
				.find( '.ui-selectmenu-status' )
				.html( this._selectedOptionLi().find( 'a' ).eq(0).html() );

			this.list.attr( 'aria-activedescendant', activeID );
		},

		_refreshPosition: function() {
			var o = this.options,
				positionDefault = {
					of: this.newelement,
					my: 'left top',
					at: 'left bottom',
					collision: 'flip'
				};

			// if its a pop-up we need to calculate the position of the selected li
			if ( o.style == 'popup' ) {
				var selected = this._selectedOptionLi();
				positionDefault.my = 'left top' + ( this.list.offset().top - selected.offset().top - ( this.newelement.outerHeight() + selected.outerHeight() ) / 2 );
				positionDefault.collision = 'fit';
			}

			this.listWrap
				.removeAttr( 'style' ).css('z-index', this.element.css('z-index')+2)
			//.zIndex( this.element.zIndex() + 2 )
				.position( $.extend( positionDefault, o.positionOptions ) );
		}
	});

}));
