(bug 38686) Autocompletion support

Provide atocopletion capabilities to the search box: showing the remaining
 part of the matching element and allow user selection with keyboard.

Change-Id: Ic591c3c87632fb56af16b9b15ecedbf9558d674a
This commit is contained in:
pginer
2012-07-25 21:29:56 +02:00
committed by Santhosh Thottingal
parent 5b055bdf5b
commit d8038afad9
4 changed files with 85 additions and 16 deletions

View File

@@ -106,7 +106,10 @@ class UniversalLanguageSelectorHooks {
<span class='search-label'></span> <span class='search-label'></span>
</div> </div>
<div class='ten columns'> <div class='ten columns'>
<input type='text' class='filterinput' id='languagefilter' placeholder='Language search' bound='true'/> <div id='search-input-block'>
<input type='text' class='filterinput' id='filtersuggestion' disabled='true' autocomplete='off' role='textbox' bound='true'/>
<input type='text' class='filterinput' data-suggestion='filtersuggestion' id='languagefilter' placeholder='Language search' bound='true'/>
</div>
</div> </div>
<div class='one column'> <div class='one column'>
<span class='clear-button'></span> <span class='clear-button'></span>

View File

@@ -93,7 +93,10 @@
<span class="search-label"></span> <span class="search-label"></span>
</div> </div>
<div class="ten columns"> <div class="ten columns">
<input type="text" class="filterinput" id="languagefilter" placeholder="Language search" autocomplete="off" role="textbox" style="" bound="true"/> <div id="search-input-block">
<input type="text" class="filterinput" id="filtersuggestion" disabled="true" autocomplete="off"/>
<input type="text" class="filterinput" id="languagefilter" data-suggestion="filtersuggestion" placeholder="Language search" autocomplete="off"/>
</div>
</div> </div>
<div class="one column"> <div class="one column">
<span class="clear-button"></span> <span class="clear-button"></span>

View File

@@ -9,7 +9,7 @@
display: none; display: none;
min-width: 715px; min-width: 715px;
margin-top: 1px; margin-top: 1px;
width:45%; width: 45%;
/* Styling */ /* Styling */
background-color: #ffffff; background-color: #ffffff;
border: 1px solid #ccc; border: 1px solid #ccc;
@@ -120,13 +120,9 @@ input#languagefilter {
-moz-transition: all 0.15s linear 0s; -moz-transition: all 0.15s linear 0s;
border-radius: 2px 2px 2px 2px; border-radius: 2px 2px 2px 2px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset;
color: #777; color: #333;
display: block; display: block;
font-size: 14px; background-color: transparent;
height: 32px;
padding: 6px;
width: 100%;
text-align: left;
} }
input:focus#languagefilter { input:focus#languagefilter {
border: 1px solid #08C; border: 1px solid #08C;
@@ -157,3 +153,27 @@ span.clear-button {
width: 32px; width: 32px;
margin-left: -32px; margin-left: -32px;
} }
.filterinput {
position: absolute;
top: 0px;
left: 0px;
font-size: 14px;
height: 32px;
padding: 6px;
width: 100%;
text-align: left;
}
#filtersuggestion {
background-color: white;
color: #888;
border: 1px transparent;
border-radius: 2px 2px 2px 2px;
box-shadow: 0 1px 2px transparent inset;
left: 1px;
}
#search-input-block {
position: relative;
}

View File

@@ -31,21 +31,35 @@
this.options = $.extend( {}, $.fn.regionselector.defaults, options ); this.options = $.extend( {}, $.fn.regionselector.defaults, options );
this.$element.addClass( 'languagefilter' ); this.$element.addClass( 'languagefilter' );
this.resultCount = 0; this.resultCount = 0;
this.$suggestion = $( '#' + this.$element.data( 'suggestion' ) );
this.listen(); this.listen();
}; };
LanguageFilter.prototype = { LanguageFilter.prototype = {
listen: function() { listen: function() {
this.$element.on( 'keyup', $.proxy( this.keyup, this )); this.$element.on( 'keypress', $.proxy( this.keyup, this ) )
.on( 'keyup', $.proxy( this.keyup, this ) );
if ( $.browser.webkit || $.browser.msie ) { if ( $.browser.webkit || $.browser.msie ) {
this.$element.on( 'keydown', $.proxy( this.keyup, this ) ); this.$element.on( 'keydown', $.proxy( this.keyup, this ) );
} }
}, },
keyup: function( e ) { keyup: function( e ) {
switch( e.keyCode ) {
case 9: // Tab -> Autocomplete
var suggestion = this.$suggestion.val();
if ( suggestion && suggestion !== this.$element.val() ) {
this.$element.val( suggestion );
e.preventDefault();
e.stopPropagation();
} else {
this.options.$target.focus();
}
default:
this.options.$target.empty(); this.options.$target.empty();
this.search(); this.search();
}
}, },
search: function() { search: function() {
@@ -57,6 +71,10 @@
for ( langNum = 0; langNum < languages[scriptGroup].length; langNum++ ) { for ( langNum = 0; langNum < languages[scriptGroup].length; langNum++ ) {
langCode = languages[scriptGroup][langNum]; langCode = languages[scriptGroup][langNum];
if ( query === "" || this.filter( langCode, query ) ) { if ( query === "" || this.filter( langCode, query ) ) {
if ( this.resultCount === 0 ) {
// Autofill the first result.
this.autofill( langCode );
}
this.render( langCode ); this.render( langCode );
this.resultCount++; this.resultCount++;
} }
@@ -94,6 +112,30 @@
} }
}, },
autofill: function( langCode ) {
if ( !this.$suggestion.length ) {
return;
}
if ( !this.$element.val() ) {
this.$suggestion.val( '' );
return;
}
var autonym,
languageName = this.options.languages[langCode],
userInput = this.$element.val(),
suggestion = userInput + languageName.substring( userInput.length, languageName.length );
if ( suggestion !== languageName ) {
// see if it was autonym match
autonym = $.uls.data.autonym( langCode );
suggestion = userInput + autonym.substring( userInput.length, autonym.length );
if ( suggestion !== autonym ) {
// Give up. It may be iso/script code match.
suggestion = "";
}
}
this.$suggestion.val( suggestion );
},
render: function( langCode, languageName ) { render: function( langCode, languageName ) {
var $target = this.options.$target; var $target = this.options.$target;
if ( !$target ) { if ( !$target ) {
@@ -103,14 +145,15 @@
}, },
escapeRegex: function( value ) { escapeRegex: function( value ) {
return value.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&" ); // This is a prefix search.
return value.replace( /^[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&" );
}, },
/** /**
* A search match happens if any of the following passes: * A search match happens if any of the following passes:
* a) Language name in current user interface language * a) Language name in current user interface language
* 'starts with' or 'contains' search string. * 'starts with' search string.
* b) Language autonym 'starts with' or 'contains' search string. * b) Language autonym 'starts with' search string.
* c) ISO 639 code match with search string. * c) ISO 639 code match with search string.
* d) ISO 15924 code for the script match the search string. * d) ISO 15924 code for the script match the search string.
*/ */