Skip to content
This repository was archived by the owner on Feb 12, 2022. It is now read-only.

Commit 3c01dfd

Browse files
committed
Merge pull request fnagel#181 from thg2k/selectmenu-typeahead
New sequential typeahead implementation
2 parents 1b775fd + bd1865e commit 3c01dfd

File tree

1 file changed

+76
-53
lines changed

1 file changed

+76
-53
lines changed

ui/jquery.ui.selectmenu.js

Lines changed: 76 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ $.widget("ui.selectmenu", {
1717
eventPrefix: "selectmenu",
1818
options: {
1919
transferClasses: true,
20-
typeAhead: "sequential",
20+
typeAhead: 1000,
2121
style: 'dropdown',
2222
positionOptions: {
2323
my: "left top",
@@ -131,13 +131,18 @@ $.widget("ui.selectmenu", {
131131
case $.ui.keyCode.TAB:
132132
ret = true;
133133
break;
134+
case $.ui.keyCode.HOME:
135+
self.index(0);
136+
break;
134137
default:
135138
ret = true;
136139
}
137140
return ret;
138141
})
139142
.bind('keypress.selectmenu', function(event) {
140-
self._typeAhead(event.which, 'mouseup');
143+
if (event.which > 0) {
144+
self._typeAhead(event.which, 'mouseup');
145+
}
141146
return true;
142147
})
143148
.bind('mouseover.selectmenu focus.selectmenu', function() {
@@ -250,7 +255,9 @@ $.widget("ui.selectmenu", {
250255
return ret;
251256
})
252257
.bind('keypress.selectmenu', function(event) {
253-
self._typeAhead(event.which, 'focus');
258+
if (event.which > 0) {
259+
self._typeAhead(event.which, 'focus');
260+
}
254261
return true;
255262
})
256263
// this allows for using the scrollbar in an overflowed list
@@ -460,63 +467,79 @@ $.widget("ui.selectmenu", {
460467
$.Widget.prototype.destroy.apply(this, arguments);
461468
},
462469

463-
_typeAhead: function( code, eventType ){
464-
var self = this, focusFound = false, C = String.fromCharCode(code).toUpperCase();
465-
c = C.toLowerCase();
470+
_typeAhead: function( code, eventType ) {
471+
var self = this,
472+
c = String.fromCharCode(code).toLowerCase(),
473+
items = this.list.find( 'li a' ),
474+
matchee = null,
475+
nextIndex = null;
476+
477+
// Clear any previous timer if present
478+
if ( self._typeAhead_timer ) {
479+
window.clearTimeout( self._typeAhead_timer );
480+
self._typeAhead_timer = undefined;
481+
}
466482

467-
if ( self.options.typeAhead == 'sequential' ) {
468-
// clear the timeout so we can use _prevChar
469-
window.clearTimeout('ui.selectmenu-' + self.selectmenuId);
483+
// Store the character typed
484+
self._typeAhead_chars = (self._typeAhead_chars === undefined ? "" : self._typeAhead_chars).concat(c);
470485

471-
// define our find var
472-
var find = typeof( self._prevChar ) == 'undefined' ? '' : self._prevChar.join( '' );
486+
// Detect if we are in cyciling mode or direct selection mode
487+
if ( self._typeAhead_chars.length < 2 ||
488+
(self._typeAhead_chars.substr(-2, 1) === c && self._typeAhead_cycling) ) {
489+
self._typeAhead_cycling = true;
473490

474-
function focusOptSeq( elem, ind, c ){
475-
focusFound = true;
476-
$( elem ).trigger( eventType );
477-
typeof( self._prevChar ) == 'undefined' ? self._prevChar = [ c ] : self._prevChar[ self._prevChar.length ] = c;
478-
}
479-
this.list.find( 'li a' ).each( function( i ) {
480-
if ( !focusFound ) {
481-
// allow the typeahead attribute on the option tag for a more specific lookup
482-
var thisText = $( this ).attr( 'typeahead' ) || $(this).text();
483-
if ( thisText.indexOf( find + C ) === 0 ) {
484-
focusOptSeq( this, i, C );
485-
} else if (thisText.indexOf(find+c) === 0 ) {
486-
focusOptSeq( this, i, c );
487-
}
488-
}
489-
});
490-
// set a 1 second timeout for sequenctial typeahead
491-
// keep this set even if we have no matches so it doesnt typeahead somewhere else
492-
window.setTimeout( function( el ) {
493-
self._prevChar = undefined;
494-
}, 1000, self );
491+
// Match only the first character and loop
492+
matchee = c;
493+
}
494+
else {
495+
// We won't be cycling anymore until the timer expires
496+
self._typeAhead_cycling = false;
495497

496-
} else {
497-
// define self._prevChar if needed
498-
if ( !self._prevChar ) { self._prevChar = [ '' , 0 ]; }
499-
500-
focusFound = false;
501-
function focusOpt( elem, ind ){
502-
focusFound = true;
503-
$( elem ).trigger( eventType );
504-
self._prevChar[ 1 ] = ind;
505-
}
506-
this.list.find( 'li a' ).each(function( i ){
507-
if (!focusFound){
508-
var thisText = $(this).text();
509-
if ( thisText.indexOf( C ) === 0 || thisText.indexOf( c ) === 0 ) {
510-
if (self._prevChar[0] == C){
511-
if ( self._prevChar[ 1 ] < i ){ focusOpt( this, i ); }
512-
} else{
513-
focusOpt( this, i );
514-
}
498+
// Match all the characters typed
499+
matchee = self._typeAhead_chars;
500+
}
501+
502+
// We need to determine the currently active index, but it depends on
503+
// the used context: if it's in the element, we want the actual
504+
// selected index, if it's in the menu, just the focused one
505+
// I copied this code from _moveSelection() and _moveFocus()
506+
// respectively --thg2k
507+
var selectedIndex = (eventType !== 'focus' ?
508+
this._selectedOptionLi().data('index') :
509+
this._focusedOptionLi().data('index')) || 0;
510+
511+
for (var i = 0; i < items.length; i++) {
512+
var thisText = items.eq(i).text().substr(0, matchee.length).toLowerCase();
513+
514+
if ( thisText === matchee ) {
515+
516+
if ( self._typeAhead_cycling ) {
517+
if ( nextIndex === null )
518+
nextIndex = i;
519+
520+
if ( i > selectedIndex ) {
521+
nextIndex = i;
522+
break;
515523
}
524+
} else {
525+
nextIndex = i;
516526
}
517-
});
518-
this._prevChar[ 0 ] = C;
527+
}
519528
}
529+
530+
if ( nextIndex !== null ) {
531+
// Why using trigger() instead of a direct method to select the
532+
// index? Because we don't what is the exact action to do, it
533+
// depends if the user is typing on the element or on the popped
534+
// up menu
535+
items.eq(nextIndex).trigger( eventType );
536+
}
537+
538+
self._typeAhead_timer = window.setTimeout(function() {
539+
self._typeAhead_timer = undefined;
540+
self._typeAhead_chars = undefined;
541+
self._typeAhead_cycling = undefined;
542+
}, self.options.typeAhead);
520543
},
521544

522545
// returns some usefull information, called by callbacks only

0 commit comments

Comments
 (0)