' );
var optionHeight = parseInt( tmp.find( '.selectBox-options A:first' ).html( ' ' ).outerHeight() );
tmp.remove();
control.height( optionHeight * size );
}
this.disableSelection( control );
} else {
// Dropdown controls
var label = $( '' ),
arrow = $( '' );
// Update label
label.attr( 'class', this.getLabelClass() ).text( this.getLabelText() );
options = this.getOptions( 'dropdown' );
options.appendTo( 'BODY' );
control
.data( 'selectBox-options', options )
.addClass( 'selectBox-dropdown' )
.append( label )
.append( arrow )
.bind( 'mousedown.selectBox', function ( event ) {
if ( 1 === event.which ) {
if ( control.hasClass( 'selectBox-menuShowing' ) ) {
self.hideMenus();
}
else {
event.stopPropagation();
// Webkit fix to prevent premature selection of options
options
.data( 'selectBox-down-at-x', event.screenX )
.data( 'selectBox-down-at-y', event.screenY );
self.showMenu();
}
}
})
.bind( 'keydown.selectBox', function ( event ) {
self.handleKeyDown( event );
})
.bind( 'keypress.selectBox', function ( event ) {
self.handleKeyPress( event );
})
.bind( 'open.selectBox',function ( event, triggerData ) {
if ( triggerData && triggerData._selectBox === true ) {
return;
}
self.showMenu();
})
.bind( 'close.selectBox', function ( event, triggerData ) {
if ( triggerData && triggerData._selectBox === true ) {
return;
}
self.hideMenus();
})
.insertAfter( select );
// Set label width
var labelWidth =
control.width()
- arrow.outerWidth()
- ( parseInt( label.css( 'paddingLeft' ) ) || 0 )
- ( parseInt( label.css( 'paddingRight' ) ) || 0 );
label.width( labelWidth );
this.disableSelection( control );
}
// Store data for later use and show the control
select
.addClass( 'selectBox' )
.data( 'selectBox-control', control )
.data( 'selectBox-settings', settings )
.hide();
};
/**
* @param {String} type 'inline'|'dropdown'
* @returns {jQuery}
*/
SelectBox.prototype.getOptions = function ( type ) {
var options;
var select = $( this.selectElement );
var self = this;
// Private function to handle recursion in the getOptions function.
var _getOptions = function ( select, options ) {
// Loop through the set in order of element children.
select.children( 'OPTION, OPTGROUP' ).each( function () {
// If the element is an option, add it to the list.
if ( $( this ).is( 'OPTION' ) ) {
// Check for a value in the option found.
if ( $( this ).length > 0 ) {
// Create an option form the found element.
self.generateOptions( $( this ), options );
}
else {
// No option information found, so add an empty.
options.append( '
\u00A0
' );
}
}
else {
// If the element is an option group, add the group and call this function on it.
var optgroup = $( '' );
optgroup.text( $( this ).attr( 'label' ) );
options.append( optgroup );
options = _getOptions( $( this ), options );
}
});
// Return the built string
return options;
};
switch ( type ) {
case 'inline':
options = $( '
' );
options = _getOptions( select, options );
options
.find( 'A' )
.bind( 'mouseover.selectBox', function ( event ) {
self.addHover( $( this ).parent() );
})
.bind( 'mouseout.selectBox',function ( event ) {
self.removeHover( $( this ).parent() );
})
.bind( 'mousedown.selectBox',function ( event ) {
if ( 1 !== event.which ) {
return
}
// Prevent options from being "dragged"
event.preventDefault();
if ( !select.selectBox( 'control' ).hasClass( 'selectBox-active' ) ) {
select.selectBox( 'control' ).focus();
}
})
.bind( 'mouseup.selectBox', function ( event ) {
if ( 1 !== event.which ) {
return;
}
self.hideMenus();
self.selectOption( $( this ).parent(), event );
});
this.disableSelection( options );
return options;
case 'dropdown':
options = $( '
' );
options = _getOptions( select, options );
options
.data( 'selectBox-select', select )
.css( 'display', 'none' )
.appendTo( 'BODY' )
.find( 'A' )
.bind( 'mousedown.selectBox', function ( event ) {
if ( event.which === 1 ) {
event.preventDefault();
if ( event.screenX === options.data( 'selectBox-down-at-x' ) &&
event.screenY === options.data( 'selectBox-down-at-y' ) ) {
options.removeData( 'selectBox-down-at-x' ).removeData( 'selectBox-down-at-y' );
if ( /android/i.test( navigator.userAgent.toLowerCase() ) &&
/chrome/i.test( navigator.userAgent.toLowerCase() ) ) {
self.selectOption( $( this ).parent() );
}
self.hideMenus();
}
}
})
.bind( 'mouseup.selectBox', function ( event ) {
if ( 1 !== event.which ) {
return;
}
if ( event.screenX === options.data( 'selectBox-down-at-x' ) &&
event.screenY === options.data( 'selectBox-down-at-y' ) ) {
return;
}
else {
options.removeData( 'selectBox-down-at-x' ).removeData( 'selectBox-down-at-y' );
}
self.selectOption( $( this ).parent() );
self.hideMenus();
})
.bind( 'mouseover.selectBox', function ( event ) {
self.addHover( $( this ).parent() );
})
.bind( 'mouseout.selectBox', function ( event ) {
self.removeHover( $( this ).parent() );
});
// Inherit classes for dropdown menu
var classes = select.attr( 'class' ) || '';
if ( '' !== classes ) {
classes = classes.split( ' ' );
for ( var i = 0; i < classes.length; i++ ) {
options.addClass( classes[i] + '-selectBox-dropdown-menu' );
}
}
this.disableSelection( options );
return options;
}
};
/**
* Returns the current class of the selected option.
*
* @returns {String}
*/
SelectBox.prototype.getLabelClass = function () {
var selected = $(this.selectElement).find('OPTION:selected');
return ('selectBox-label ' + (selected.attr('class') || '')).replace(/\s+$/, '');
};
/**
* Returns the current label of the selected option.
*
* @returns {String}
*/
SelectBox.prototype.getLabelText = function () {
var selected = $( this.selectElement ).find( 'OPTION:selected' );
return selected.text() || '\u00A0';
};
/**
* Sets the label.
* This method uses the getLabelClass() and getLabelText() methods.
*/
SelectBox.prototype.setLabel = function () {
var select = $( this.selectElement );
var control = select.data( 'selectBox-control' );
if ( !control ) {
return;
}
control
.find( '.selectBox-label' )
.attr( 'class', this.getLabelClass() )
.text( this.getLabelText() );
};
/**
* Destroys the SelectBox instance and shows the origin select element.
*
*/
SelectBox.prototype.destroy = function () {
var select = $( this.selectElement );
var control = select.data( 'selectBox-control' );
if ( !control ) {
return;
}
var options = control.data( 'selectBox-options' );
options.remove();
control.remove();
select
.removeClass( 'selectBox' )
.removeData( 'selectBox-control' )
.data( 'selectBox-control', null )
.removeData( 'selectBox-settings' )
.data( 'selectBox-settings', null )
.show();
};
/**
* Refreshes the option elements.
*/
SelectBox.prototype.refresh = function () {
var select = $( this.selectElement ),
control = select.data( 'selectBox-control' ),
type = control.hasClass( 'selectBox-dropdown' ) ? 'dropdown' : 'inline',
options;
// Remove old options
control.data( 'selectBox-options' ).remove();
// Generate new options
options = this.getOptions( type );
control.data( 'selectBox-options', options );
switch ( type ) {
case 'inline':
control.append( options );
break;
case 'dropdown':
// Update label
this.setLabel();
$( "BODY" ).append( options );
break;
}
// Restore opened dropdown state (original menu was trashed)
if ( 'dropdown' === type && control.hasClass( 'selectBox-menuShowing' ) ) {
this.showMenu();
}
};
/**
* Shows the dropdown menu.
*/
SelectBox.prototype.showMenu = function () {
var self = this,
select = $( this.selectElement ),
control = select.data( 'selectBox-control' ),
settings = select.data( 'selectBox-settings' ),
options = control.data( 'selectBox-options' );
if ( control.hasClass( 'selectBox-disabled' ) ) {
return false;
}
this.hideMenus();
// Get top and bottom width of selectBox
var borderBottomWidth = parseInt( control.css( 'borderBottomWidth' ) ) || 0;
var borderTopWidth = parseInt( control.css( 'borderTopWidth' ) ) || 0;
// Get proper variables for keeping options in viewport
var pos = control.offset(),
topPositionCorrelation = ( settings.topPositionCorrelation ) ? settings.topPositionCorrelation : 0,
bottomPositionCorrelation = (settings.bottomPositionCorrelation) ? settings.bottomPositionCorrelation : 0,
optionsHeight = options.outerHeight(),
controlHeight = control.outerHeight(),
maxHeight = parseInt(options.css('max-height')),
scrollPos = $(window).scrollTop(),
heightToTop = pos.top - scrollPos,
heightToBottom = $(window).height() - ( heightToTop + controlHeight ),
posTop = (heightToTop > heightToBottom) && (settings.keepInViewport == null ? true : settings.keepInViewport),
top = posTop ? pos.top - optionsHeight + borderTopWidth + topPositionCorrelation : pos.top + controlHeight - borderBottomWidth - bottomPositionCorrelation;
// If the height to top and height to bottom are less than the max-height
if( heightToTop < maxHeight && heightToBottom < maxHeight ){
// Set max-height and top
if( posTop ){
var maxHeightDiff = maxHeight - ( heightToTop - 5 );
options.css( { 'max-height': maxHeight - maxHeightDiff + 'px' } );
top = top + maxHeightDiff;
}
else {
var maxHeightDiff = maxHeight - ( heightToBottom - 5 );
options.css( { 'max-height': maxHeight - maxHeightDiff + 'px' } );
}
}
// Save if position is top to options data
options.data('posTop',posTop);
// Menu position
options
.width( control.innerWidth() )
.css({
top: top,
left: control.offset().left
})
// Add Top and Bottom class based on position
.addClass( 'selectBox-options selectBox-options-'+( posTop?'top':'bottom' ) );
if ( select.triggerHandler( 'beforeopen' ) ) {
return false;
}
var dispatchOpenEvent = function () {
select.triggerHandler( 'open', {
_selectBox: true
});
};
// Show menu
switch ( settings.menuTransition ) {
case 'fade':
options.fadeIn( settings.menuSpeed, dispatchOpenEvent );
break;
case 'slide':
options.slideDown( settings.menuSpeed, dispatchOpenEvent );
break;
default:
options.show( settings.menuSpeed, dispatchOpenEvent );
break;
}
if ( !settings.menuSpeed ) {
dispatchOpenEvent();
}
// Center on selected option
var li = options.find( '.selectBox-selected:first' );
this.keepOptionInView( li, true );
this.addHover( li );
control.addClass( 'selectBox-menuShowing selectBox-menuShowing-'+( posTop?'top':'bottom' ) );
$(document).bind('mousedown.selectBox', function ( event ) {
if ( 1 === event.which ) {
if ( $( event.target ).parents().andSelf().hasClass( 'selectBox-options' ) ) {
return;
}
self.hideMenus();
}
});
};
/**
* Hides the menu of all instances.
*/
SelectBox.prototype.hideMenus = function () {
if ( $( ".selectBox-dropdown-menu:visible" ).length === 0 ) {
return;
}
$( document ).unbind( 'mousedown.selectBox' );
$( ".selectBox-dropdown-menu" ).each(function () {
var options = $( this ),
select = options.data( 'selectBox-select' ),
control = select.data( 'selectBox-control' ),
settings = select.data( 'selectBox-settings' ),
posTop = options.data( 'posTop' );
if ( select.triggerHandler( 'beforeclose' ) ) {
return false;
}
var dispatchCloseEvent = function () {
select.triggerHandler( 'close', {
_selectBox: true
});
};
if ( settings ) {
switch ( settings.menuTransition ) {
case 'fade':
options.fadeOut( settings.menuSpeed, dispatchCloseEvent );
break;
case 'slide':
options.slideUp( settings.menuSpeed, dispatchCloseEvent );
break;
default:
options.hide( settings.menuSpeed, dispatchCloseEvent );
break;
}
if ( !settings.menuSpeed ) {
dispatchCloseEvent();
}
control.removeClass( 'selectBox-menuShowing selectBox-menuShowing-' + ( posTop?'top':'bottom' ) );
} else {
$( this ).hide();
$( this ).triggerHandler( 'close', {
_selectBox: true
});
$( this ).removeClass( 'selectBox-menuShowing selectBox-menuShowing-' + ( posTop?'top':'bottom' ) );
}
options.css( 'max-height','' )
//Remove Top or Bottom class based on position
options.removeClass( 'selectBox-options-'+( posTop?'top':'bottom' ) );
options.data( 'posTop' , false );
});
};
/**
* Selects an option.
*
* @param {HTMLElement} li
* @param {DOMEvent} event
* @returns {Boolean}
*/
SelectBox.prototype.selectOption = function ( li, event ) {
var select = $( this.selectElement );
li = $( li );
var control = select.data( 'selectBox-control' ),
settings = select.data( 'selectBox-settings' );
if ( control.hasClass( 'selectBox-disabled' ) ) {
return false;
}
if ( 0 === li.length || li.hasClass( 'selectBox-disabled' ) ) {
return false;
}
if ( select.attr( 'multiple' ) ) {
// If event.shiftKey is true, this will select all options between li and the last li selected
if ( event.shiftKey && control.data( 'selectBox-last-selected' ) ) {
li.toggleClass( 'selectBox-selected' );
var affectedOptions;
if ( li.index() > control.data( 'selectBox-last-selected' ).index() ) {
affectedOptions = li
.siblings()
.slice( control.data( 'selectBox-last-selected' ).index(), li.index() );
} else {
affectedOptions = li
.siblings()
.slice( li.index(), control.data( 'selectBox-last-selected' ).index() );
}
affectedOptions = affectedOptions.not( '.selectBox-optgroup, .selectBox-disabled' );
if ( li.hasClass( 'selectBox-selected' ) ) {
affectedOptions.addClass( 'selectBox-selected' );
}
else {
affectedOptions.removeClass( 'selectBox-selected' );
}
}
else if ( ( this.isMac && event.metaKey ) || ( !this.isMac && event.ctrlKey ) ) {
li.toggleClass( 'selectBox-selected' );
}
else {
li.siblings().removeClass( 'selectBox-selected' );
li.addClass( 'selectBox-selected' );
}
} else {
li.siblings().removeClass( 'selectBox-selected' );
li.addClass( 'selectBox-selected' );
}
if ( control.hasClass( 'selectBox-dropdown' ) ) {
control.find( '.selectBox-label' ).text( li.text() );
}
// Update original control's value
var i = 0, selection = [];
if ( select.attr( 'multiple' ) ) {
control.find( '.selectBox-selected A' ).each( function () {
selection[ i++ ] = $( this ).attr( 'rel' );
});
}
else {
selection = li.find( 'A' ).attr( 'rel' );
}
// Remember most recently selected item
control.data( 'selectBox-last-selected', li );
// Change callback
if ( select.val() !== selection ) {
select.val( selection );
this.setLabel();
select.trigger( 'change' );
}
return true;
};
/**
* Adds the hover class.
*
* @param {HTMLElement} li
*/
SelectBox.prototype.addHover = function( li ) {
li = $( li );
var select = $( this.selectElement ),
control = select.data( 'selectBox-control' ),
options = control.data( 'selectBox-options' );
options.find( '.selectBox-hover' ).removeClass( 'selectBox-hover' );
li.addClass( 'selectBox-hover' );
};
/**
* Returns the original HTML select element.
*
* @returns {HTMLElement}
*/
SelectBox.prototype.getSelectElement = function () {
return this.selectElement;
};
/**
* Remove the hover class.
*
* @param {HTMLElement} li
*/
SelectBox.prototype.removeHover = function( li ) {
li = $( li );
var select = $(this.selectElement),
control = select.data('selectBox-control'),
options = control.data('selectBox-options');
options.find( '.selectBox-hover' ).removeClass( 'selectBox-hover' );
};
/**
* Checks if the widget is in the view.
*
* @param {jQuery} li
* @param {Boolean} center
*/
SelectBox.prototype.keepOptionInView = function ( li, center ) {
if ( !li || li.length === 0 ) {
return;
}
var select = $( this.selectElement ),
control = select.data('selectBox-control'),
options = control.data('selectBox-options'),
scrollBox = control.hasClass('selectBox-dropdown') ? options : options.parent(),
top = parseInt(li.offset().top -scrollBox.position().top),
bottom = parseInt(top + li.outerHeight());
if ( center ) {
scrollBox.scrollTop( li.offset().top - scrollBox.offset().top + scrollBox.scrollTop() -
( scrollBox.height() / 2 ) );
} else {
if ( top < 0 ) {
scrollBox.scrollTop( li.offset().top - scrollBox.offset().top + scrollBox.scrollTop() );
}
if ( bottom > scrollBox.height() ) {
scrollBox.scrollTop( ( li.offset().top + li.outerHeight()) - scrollBox.offset().top +
scrollBox.scrollTop() - scrollBox.height() );
}
}
};
/**
* Enables the selectBox.
*/
SelectBox.prototype.enable = function () {
var select = $( this.selectElement );
select.prop( 'disabled', false );
var control = select.data( 'selectBox-control' );
if ( !control ) {
return;
}
control.removeClass( 'selectBox-disabled' );
};
/**
* Disables the selectBox.
*/
SelectBox.prototype.disable = function () {
var select = $( this.selectElement );
select.prop( 'disabled', true );
var control = select.data( 'selectBox-control' );
if ( !control ) {
return;
}
control.addClass( 'selectBox-disabled' );
};
/**
* Sets the current value.
*
* @param {String} value
*/
SelectBox.prototype.setValue = function ( value ) {
var select = $( this.selectElement );
select.val( value );
value = select.val();
if ( null === value ) {
value = select.children().first().val();
select.val( value );
}
var control = select.data( 'selectBox-control' );
if ( !control ) {
return;
}
var settings = select.data( 'selectBox-settings' ),
options = control.data( 'selectBox-options' );
// Update label
this.setLabel();
// Update control values
options.find( '.selectBox-selected' ).removeClass( 'selectBox-selected' );
options.find( 'A' ).each( function () {
if ( typeof( value ) === 'object' ) {
for ( var i = 0; i < value.length; i++ ) {
if ( $( this ).attr( 'rel' ) == value[ i ] ) {
$( this ).parent().addClass( 'selectBox-selected' );
}
}
}
else {
if ( $( this ).attr( 'rel' ) == value ) {
$( this ).parent().addClass( 'selectBox-selected' );
}
}
});
if ( settings.change ) {
settings.change.call( select );
}
};
/**
* Sets the option elements.
*
* @param {String|Object} options
*/
SelectBox.prototype.setOptions = function ( options ) {
var select = $( this.selectElement ),
control = select.data( 'selectBox-control' );
switch ( typeof( options ) ) {
case 'string':
select.html( options );
break;
case 'object':
select.html( '' );
for ( var i in options ) {
if ( options[ i ] === null ) {
continue;
}
if ( typeof( options[ i ] ) === 'object' ) {
var optgroup = $( '' );
for ( var j in options[ i ] ) {
optgroup.append( '' );
}
select.append( optgroup );
}
else {
var option = $( '' );
select.append( option );
}
}
break;
}
if ( control ) {
// Refresh the control
this.refresh();
}
};
/**
* Disables the selection.
*
* @param {*} selector
*/
SelectBox.prototype.disableSelection = function ( selector ) {
$(selector).css( 'MozUserSelect', 'none' ).bind( 'selectstart', function ( event ) {
event.preventDefault();
});
};
/**
* Generates the options.
*
* @param {jQuery} self
* @param {jQuery} options
*/
SelectBox.prototype.generateOptions = function ( self, options ) {
var li = $( '' ), a = $( '' );
li.addClass( self.attr( 'class' ) );
li.data( self.data() );
a.attr( 'rel', self.val() ).text( self.text() );
li.append( a );
if ( self.attr( 'disabled' ) ) {
li.addClass( 'selectBox-disabled' );
}
if ( self.attr( 'selected' ) ) {
li.addClass( 'selectBox-selected' );
}
options.append( li );
};
/**
* Extends the jQuery.fn object.
*/
$.extend( $.fn, {
selectBox: function ( method, options ) {
var selectBox;
switch ( method ) {
case 'control':
return $( this ).data( 'selectBox-control' );
case 'settings':
if ( !options ) {
return $( this ).data( 'selectBox-settings' );
}
$( this ).each(function () {
$( this ).data( 'selectBox-settings', $.extend( true, $( this ).data( 'selectBox-settings' ), options ) );
});
break;
case 'options':
// Getter
if ( undefined === options ) {
return $( this ).data( 'selectBox-control' ).data( 'selectBox-options' );
}
// Setter
$( this ).each( function () {
if ( selectBox = $( this ).data( 'selectBox' ) ) {
selectBox.setOptions( options );
}
});
break;
case 'value':
// Empty string is a valid value
if ( undefined === options ) {
return $( this ).val();
}
$( this ).each( function () {
if ( selectBox = $( this ).data( 'selectBox' ) ) {
selectBox.setValue( options );
}
});
break;
case 'refresh':
$( this ).each( function () {
if ( selectBox = $( this ).data( 'selectBox' ) ) {
selectBox.refresh();
}
});
break;
case 'enable':
$( this ).each( function () {
if ( selectBox = $( this ).data( 'selectBox' ) ) {
selectBox.enable( this );
}
});
break;
case 'disable':
$( this ).each( function () {
if ( selectBox = $( this ).data( 'selectBox' ) ) {
selectBox.disable();
}
});
break;
case 'destroy':
$( this ).each( function () {
if ( selectBox = $( this ).data( 'selectBox' ) ) {
selectBox.destroy();
$( this ).data( 'selectBox', null );
}
});
break;
case 'instance':
return $( this ).data( 'selectBox' );
default:
$( this ).each( function ( idx, select ) {
if ( !$( select ).data( 'selectBox' ) ) {
$( select ).data( 'selectBox', new SelectBox( select, method ) );
}
});
break;
}
return $( this );
}
});
})( jQuery );