/** * Universal Language Selector * Language category display component - Used for showing the search results, * grouped by regions, scripts * * Copyright (C) 2012 Alolita Sharma, Amir Aharoni, Arun Ganesh, Brandon Harris, * Niklas Laxström, Pau Giner, Santhosh Thottingal, Siebrand Mazeland and other * contributors. See CREDITS for a list. * * UniversalLanguageSelector is dual licensed GPLv2 or later and MIT. You don't * have to do anything special to choose one license or the other and you don't * have to notify anyone which license you are using. You are free to use * UniversalLanguageSelector in commercial projects as long as the copyright * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details. * * @file * @ingroup Extensions * @licence GNU General Public Licence 2.0 or later * @licence MIT License */ ( function ( $ ) { 'use strict'; // eslint-disable-next-line no-multi-str var noResultsTemplate = '
\

No results found

\
\
\
You can search by language name, script name, ISO code of language or you can browse by region.
\
'; /** * Language category display * @param {Element} element The container element to which the languages to be displayed * @param {Object} [options] Configuration object * @cfg {Object} [languages] Selectable languages. Keyed by language code, values are autonyms. * @cfg {string[]} [showRegions] Array of region codes to show. Default is * [ 'WW', 'AM', 'EU', 'ME', 'AF', 'AS', 'PA' ] * @cfg {number} [itemsPerColumn] Number of languages per column. * @cfg {number} [columns] Number of columns for languages. Default is 4. * @cfg {Function} [languageDecorator] Callback function to be called when a language * link is prepared - for custom decoration. * @cfg {Function|string[]} [quickList] The languages to display as suggestions for quick * selection. * @cfg {Function} [clickhandler] Callback when language is selected. * @cfg {jQuery|Function} [noResultsTemplate] */ function LanguageCategoryDisplay( element, options ) { this.$element = $( element ); this.options = $.extend( {}, $.fn.lcd.defaults, options ); // Ensure the internal region 'all' is always present if ( this.options.showRegions.indexOf( 'all' ) === -1 ) { this.options.showRegions.push( 'all' ); } this.$element.addClass( 'uls-lcd' ); this.regionLanguages = {}; this.renderTimeout = null; this.cachedQuicklist = null; this.groupByRegionOverride = null; this.render(); this.listen(); } LanguageCategoryDisplay.prototype = { constructor: LanguageCategoryDisplay, /** * Adds language to the language list. * @param {string} langCode * @param {string} [regionCode] * @return {boolean} Whether the language was known and accepted */ append: function ( langCode, regionCode ) { var i, regions; if ( !$.uls.data.languages[ langCode ] ) { // Language is unknown or not in the list of languages for this context. return false; } if ( !this.isGroupingByRegionEnabled() ) { regions = [ 'all' ]; // Make sure we do not get duplicates if ( this.regionLanguages.all.indexOf( langCode ) > -1 ) { return true; } } else { if ( regionCode ) { regions = [ regionCode ]; } else { regions = $.uls.data.getRegions( langCode ); } } for ( i = 0; i < regions.length; i++ ) { this.regionLanguages[ regions[ i ] ].push( langCode ); } // Work around the bad interface, delay rendering until we have got // all the languages to speed up performance. clearTimeout( this.renderTimeout ); this.renderTimeout = setTimeout( function () { this.renderRegions(); }.bind( this ), 50 ); return true; }, /** * Whether we should render languages grouped to geographic regions. * @return {boolean} */ isGroupingByRegionEnabled: function () { if ( this.groupByRegionOverride !== null ) { return this.groupByRegionOverride; } else if ( this.options.groupByRegion !== 'auto' ) { return this.options.groupByRegion; } else { return this.options.columns > 1; } }, /** * Override the default region grouping setting. * This is to allow LanguageFilter to disable grouping when displaying search results. * * @param {boolean|null} val True to force grouping, false to disable, null * to undo override. */ setGroupByRegionOverride: function ( val ) { this.groupByRegionOverride = val; }, render: function () { var $section, $quicklist = this.buildQuicklist(), regions = [], regionNames = { // These are fallback text when i18n library not present all: 'All languages', // Used if there is quicklist and no region grouping WW: 'Worldwide', SP: 'Special', AM: 'America', EU: 'Europe', ME: 'Middle East', AS: 'Asia', AF: 'Africa', PA: 'Pacific' }; if ( $quicklist.length ) { regions.push( $quicklist ); } else { // We use CSS to hide the header for 'all' when quicklist is NOT present this.$element.addClass( 'uls-lcd--no-quicklist' ); } this.options.showRegions.forEach( function ( regionCode ) { this.regionLanguages[ regionCode ] = []; $section = $( '
' ) .addClass( 'uls-lcd-region-section hide' ) .attr( 'data-region', regionCode ); $( '

' ) .attr( 'data-i18n', 'uls-region-' + regionCode ) .addClass( 'uls-lcd-region-title' ) .text( regionNames[ regionCode ] ) .appendTo( $section ); regions.push( $section ); }.bind( this ) ); this.$element.append( regions ); this.i18n(); }, /** * Renders a region and displays it if it has content. */ renderRegions: function () { var languages, lcd = this; this.$element.removeClass( 'uls-no-results' ); this.$element.children( '.uls-lcd-region-section' ).each( function () { var $region = $( this ), regionCode = $region.data( 'region' ); if ( $region.is( '.uls-lcd-quicklist' ) ) { return; } $region.children( '.uls-language-block' ).remove(); languages = lcd.regionLanguages[ regionCode ]; if ( !languages || languages.length === 0 ) { $region.addClass( 'hide' ); return; } lcd.renderRegion( $region, languages, lcd.options.itemsPerColumn, lcd.options.columns ); $region.removeClass( 'hide' ); lcd.regionLanguages[ regionCode ] = []; } ); }, /** * Adds given languages sorted into rows and columns into given element. * @param {jQuery} $region Element to add language list. * @param {Array} languages List of language codes. * @param {number} itemsPerColumn How many languages fit in a column. * @param {number} columnsPerRow How many columns fit in a row. */ renderRegion: function ( $region, languages, itemsPerColumn, columnsPerRow ) { var columnsClasses, i, lastItem, currentScript, nextScript, force, languagesCount = languages.length, items = [], columns = [], rows = []; languages = $.uls.data.sortByScriptGroup( languages.sort( $.uls.data.sortByAutonym ) ); if ( columnsPerRow === 1 ) { columnsClasses = 'twelve columns'; } else if ( columnsPerRow === 2 ) { columnsClasses = 'six columns'; } else { columnsClasses = 'three columns'; } if ( this.options.columns === 1 ) { // For one-column narrow ULS, just render all the languages // in one simple list without separators or script groups for ( i = 0; i < languagesCount; i++ ) { items.push( this.renderItem( languages[ i ] ) ); } columns.push( $( '