Merge branch 'master' into srpski
This commit is contained in:
@@ -19,21 +19,34 @@
|
||||
|
||||
/**
|
||||
* Usage: $( 'inputbox' ).languagefilter();
|
||||
* The values for autocompletion is from the options.languages.
|
||||
* The data is in the format of languagecode:languagename.
|
||||
* The values for autocompletion is from the options.languages or options.searchAPI.
|
||||
*/
|
||||
( function ( $ ) {
|
||||
'use strict';
|
||||
|
||||
var LanguageFilter, delay;
|
||||
|
||||
/**
|
||||
* Check if a prefix is visually prefix of a string
|
||||
*
|
||||
* @param {string} prefix
|
||||
* @param {string} string
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isVisualPrefix( prefix, string ) {
|
||||
// Pre-base vowel signs of Indic languages. A vowel sign is called pre-base if
|
||||
// consonant + vowel becomes [vowel][consonant] when rendered. Eg: ക + െ => കെ
|
||||
var prebases = 'െേൈൊോൌெேைொோௌେୈୋୌિਿिিেৈোৌෙේෛොෝෞ';
|
||||
return prebases.indexOf( string[ prefix.length ] ) <= 0;
|
||||
}
|
||||
|
||||
LanguageFilter = function ( element, options ) {
|
||||
this.$element = $( element );
|
||||
this.options = $.extend( {}, $.fn.languagefilter.defaults, options );
|
||||
this.$element.addClass( 'languagefilter' );
|
||||
this.resultCount = 0;
|
||||
this.$suggestion = this.$element.parents().find( '#' + this.$element.data( 'suggestion' ) );
|
||||
this.$clear = this.$element.parents().find( '#' + this.$element.data( 'clear' ) );
|
||||
this.$suggestion = this.$element.siblings( '.' + this.$element.data( 'suggestion' ) );
|
||||
this.$clear = this.$element.siblings( '.' + this.$element.data( 'clear' ) );
|
||||
this.selectedLanguage = null;
|
||||
this.init();
|
||||
this.listen();
|
||||
@@ -54,12 +67,8 @@
|
||||
},
|
||||
|
||||
listen: function () {
|
||||
this.$element.on( 'keypress', $.proxy( this.keyup, this ) )
|
||||
.on( 'keyup', $.proxy( this.keyup, this ) );
|
||||
|
||||
if ( this.eventSupported( 'keydown' ) ) {
|
||||
this.$element.on( 'keydown', $.proxy( this.keyup, this ) );
|
||||
}
|
||||
this.$element.on( 'keydown', $.proxy( this.keypress, this ) );
|
||||
|
||||
if ( this.$clear.length ) {
|
||||
this.$clear.on( 'click', $.proxy( this.clear, this ) );
|
||||
@@ -68,62 +77,62 @@
|
||||
this.toggleClear();
|
||||
},
|
||||
|
||||
keyup: function ( e ) {
|
||||
keypress: function ( e ) {
|
||||
var suggestion, query, languageFilter;
|
||||
|
||||
switch ( e.keyCode ) {
|
||||
case 9: // Tab -> Autocomplete
|
||||
suggestion = this.$suggestion.val();
|
||||
case 9: // Tab -> Autocomplete
|
||||
suggestion = this.$suggestion.val();
|
||||
|
||||
if ( suggestion && suggestion !== this.$element.val() ) {
|
||||
this.$element.val( suggestion );
|
||||
if ( suggestion && suggestion !== this.$element.val() ) {
|
||||
this.$element.val( suggestion );
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
break;
|
||||
case 13: // Enter
|
||||
if ( !this.options.onSelect ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Avoid bubbling this 'enter' to background page elements
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
break;
|
||||
case 13: // Enter
|
||||
if ( !this.options.onSelect ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Avoid bubbling this 'enter' to background page elements
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
query = $.trim( this.$element.val() ).toLowerCase();
|
||||
|
||||
query = $.trim( this.$element.val() ).toLowerCase();
|
||||
|
||||
if ( this.selectedLanguage ) {
|
||||
// this.selectLanguage will be populated from a matching search
|
||||
this.options.onSelect( this.selectedLanguage );
|
||||
} else if ( this.options.languages[ query ] ) {
|
||||
// Search is yet to happen (in timeout delay),
|
||||
// but we have a matching language code.
|
||||
this.options.onSelect( query );
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
languageFilter = this;
|
||||
|
||||
if ( e.which < 32 &&
|
||||
e.which !== 8 // Backspace
|
||||
) {
|
||||
// ignore any ASCII control characters
|
||||
break;
|
||||
}
|
||||
|
||||
this.selectedLanguage = null;
|
||||
|
||||
delay( function () {
|
||||
if ( !languageFilter.$element.val() ) {
|
||||
languageFilter.clear();
|
||||
} else {
|
||||
languageFilter.options.$target.empty();
|
||||
languageFilter.search();
|
||||
if ( this.selectedLanguage ) {
|
||||
// this.selectLanguage will be populated from a matching search
|
||||
this.options.onSelect( this.selectedLanguage );
|
||||
} else if ( this.options.languages[ query ] ) {
|
||||
// Search is yet to happen (in timeout delay),
|
||||
// but we have a matching language code.
|
||||
this.options.onSelect( query );
|
||||
}
|
||||
}, 300 );
|
||||
|
||||
this.toggleClear();
|
||||
break;
|
||||
default:
|
||||
languageFilter = this;
|
||||
|
||||
if ( e.which < 32 &&
|
||||
e.which !== 8 // Backspace
|
||||
) {
|
||||
// ignore any ASCII control characters
|
||||
break;
|
||||
}
|
||||
|
||||
this.selectedLanguage = null;
|
||||
|
||||
delay( function () {
|
||||
if ( !languageFilter.$element.val() ) {
|
||||
languageFilter.clear();
|
||||
} else {
|
||||
languageFilter.options.lcd.empty();
|
||||
languageFilter.search();
|
||||
}
|
||||
}, 300 );
|
||||
|
||||
this.toggleClear();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -167,86 +176,75 @@
|
||||
},
|
||||
|
||||
search: function () {
|
||||
var langCode, scriptGroup, langNum, languagesInScript,
|
||||
languages = $.uls.data.getLanguagesByScriptGroup( this.options.languages ),
|
||||
query = $.trim( this.$element.val() );
|
||||
var languages = Object.keys( this.options.languages ),
|
||||
results = [],
|
||||
query = $.trim( this.$element.val() ).toLowerCase();
|
||||
|
||||
this.resultCount = 0;
|
||||
for ( scriptGroup in languages ) {
|
||||
languagesInScript = languages[ scriptGroup ];
|
||||
languagesInScript.sort( $.uls.data.sortByAutonym );
|
||||
for ( langNum = 0; langNum < languagesInScript.length; langNum++ ) {
|
||||
langCode = languagesInScript[ langNum ];
|
||||
if ( query === '' || this.filter( langCode, query ) ) {
|
||||
if ( this.resultCount === 0 ) {
|
||||
// Autofill the first result.
|
||||
this.autofill( langCode );
|
||||
}
|
||||
|
||||
if ( query.toLowerCase() === langCode ) {
|
||||
this.selectedLanguage = langCode;
|
||||
}
|
||||
|
||||
if ( this.render( langCode ) ) {
|
||||
this.resultCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( query === '' ) {
|
||||
this.options.lcd.setGroupByRegionOverride( null );
|
||||
this.resultHandler( query, languages );
|
||||
return;
|
||||
}
|
||||
|
||||
// Also do a search by search API
|
||||
if ( !this.resultCount && this.options.searchAPI && query ) {
|
||||
this.searchAPI( query );
|
||||
this.options.lcd.setGroupByRegionOverride( false );
|
||||
// Local search results
|
||||
results = languages.filter( function ( langCode ) {
|
||||
return this.filter( langCode, query );
|
||||
}.bind( this ) );
|
||||
|
||||
// Use the searchAPI if available, assuming that it has superior search results.
|
||||
if ( this.options.searchAPI ) {
|
||||
this.searchAPI( query )
|
||||
.done( this.resultHandler.bind( this ) )
|
||||
.fail( this.resultHandler.bind( this, query, results, undefined ) );
|
||||
} else {
|
||||
this.resultHandler( query );
|
||||
this.resultHandler( query, results );
|
||||
}
|
||||
},
|
||||
|
||||
searchAPI: function ( query ) {
|
||||
var languageFilter = this;
|
||||
return $.get( this.options.searchAPI, { search: query } ).then( function ( result ) {
|
||||
var autofillLabel,
|
||||
results = [];
|
||||
|
||||
$.get( languageFilter.options.searchAPI, {
|
||||
search: query
|
||||
}, function ( result ) {
|
||||
$.each( result.languagesearch, function ( code, name ) {
|
||||
var target;
|
||||
|
||||
if ( languageFilter.resultCount === 0 ) {
|
||||
// Autofill the first result.
|
||||
languageFilter.autofill( code, name );
|
||||
}
|
||||
|
||||
if ( languageFilter.options.languages[ code ] &&
|
||||
languageFilter.render( code )
|
||||
) {
|
||||
languageFilter.resultCount++;
|
||||
if ( this.options.languages[ code ] ) {
|
||||
autofillLabel = autofillLabel || name;
|
||||
results.push( code );
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to hide issues caused by inconsistent language codes
|
||||
target = $.uls.data.isRedirect( code );
|
||||
if ( languageFilter.options.languages[ target ] &&
|
||||
languageFilter.render( target )
|
||||
) {
|
||||
languageFilter.resultCount++;
|
||||
if ( target && this.options.languages[ target ] ) {
|
||||
autofillLabel = autofillLabel || name;
|
||||
results.push( target );
|
||||
}
|
||||
} );
|
||||
|
||||
languageFilter.resultHandler( query );
|
||||
} );
|
||||
return $.Deferred().resolve( query, results, autofillLabel );
|
||||
}.bind( this ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler method to be called once search is over.
|
||||
* Based on search result triggers resultsfound or noresults events
|
||||
* @param query string
|
||||
* @param {string} query
|
||||
* @param {string[]} results
|
||||
* @param {string} [autofillLabel]
|
||||
*/
|
||||
resultHandler: function ( query ) {
|
||||
if ( this.resultCount === 0 ) {
|
||||
resultHandler: function ( query, results, autofillLabel ) {
|
||||
if ( results.length === 0 ) {
|
||||
this.$suggestion.val( '' );
|
||||
this.$element.trigger( 'noresults.uls', query );
|
||||
} else {
|
||||
this.$element.trigger( 'resultsfound.uls', [ query, this.resultCount ] );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( query ) {
|
||||
this.selectedLanguage = results[ 0 ];
|
||||
this.autofill( results[ 0 ], autofillLabel );
|
||||
}
|
||||
|
||||
results.map( this.render.bind( this ) );
|
||||
this.$element.trigger( 'resultsfound.uls', [ query, results.length ] );
|
||||
},
|
||||
|
||||
autofill: function ( langCode, languageName ) {
|
||||
@@ -261,7 +259,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedLanguage = langCode;
|
||||
languageName = languageName || this.options.languages[ langCode ];
|
||||
|
||||
if ( !languageName ) {
|
||||
@@ -291,17 +288,11 @@
|
||||
},
|
||||
|
||||
render: function ( langCode ) {
|
||||
var $target = this.options.$target;
|
||||
|
||||
if ( !$target ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $target.append( langCode );
|
||||
return this.options.lcd.append( langCode );
|
||||
},
|
||||
|
||||
escapeRegex: function ( value ) {
|
||||
return value.replace( /[\-\[\]{}()*+?.,\\\^$\|#\s]/g, '\\$&' );
|
||||
return value.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&' );
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -311,6 +302,9 @@
|
||||
* b) Language autonym 'starts with' search string.
|
||||
* c) ISO 639 code match with search string.
|
||||
* d) ISO 15924 code for the script match the search string.
|
||||
* @param {string} langCode
|
||||
* @param {string} searchTerm
|
||||
* @return {boolean}
|
||||
*/
|
||||
filter: function ( langCode, searchTerm ) {
|
||||
// FIXME script is ISO 15924 code. We might need actual name of script.
|
||||
@@ -321,17 +315,6 @@
|
||||
matcher.test( $.uls.data.getAutonym( langCode ) ) ||
|
||||
matcher.test( langCode ) ||
|
||||
matcher.test( $.uls.data.getScript( langCode ) );
|
||||
},
|
||||
|
||||
eventSupported: function ( eventName ) {
|
||||
var isSupported = eventName in this.$element;
|
||||
|
||||
if ( !isSupported ) {
|
||||
this.$element.setAttribute( eventName, 'return;' );
|
||||
isSupported = typeof this.$element[ eventName ] === 'function';
|
||||
}
|
||||
|
||||
return isSupported;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -352,24 +335,16 @@
|
||||
};
|
||||
|
||||
$.fn.languagefilter.defaults = {
|
||||
$target: null, // Where to append the results
|
||||
searchAPI: null,
|
||||
languages: null, // Languages as code:name format.
|
||||
onSelect: null // Language select handler - like enter in filter textbox.
|
||||
// LanguageCategoryDisplay
|
||||
lcd: undefined,
|
||||
// URL to which we append query parameter with the query value
|
||||
searchAPI: undefined,
|
||||
// Object of language tags to language names
|
||||
languages: [],
|
||||
// Callback function when language is selected
|
||||
onSelect: undefined
|
||||
};
|
||||
|
||||
$.fn.languagefilter.Constructor = LanguageFilter;
|
||||
|
||||
/**
|
||||
* Check if a prefix is visually prefix of a string
|
||||
*
|
||||
* @param {string} prefix
|
||||
* @param {string} string
|
||||
*/
|
||||
function isVisualPrefix( prefix, string ) {
|
||||
// Pre-base vowel signs of Indic languages. A vowel sign is called pre-base if
|
||||
// consonant + vowel becomes [vowel][consonant] when rendered. Eg: ക + െ => കെ
|
||||
var prebases = 'െേൈൊോൌெேைொோௌେୈୋୌિਿिিেৈোৌෙේෛොෝෞ';
|
||||
return prebases.indexOf( string[ prefix.length ] ) <= 0;
|
||||
}
|
||||
}( jQuery ) );
|
||||
|
||||
Reference in New Issue
Block a user