Merge "Reorganize the compact links code, make it a jquery plugin"

This commit is contained in:
jenkins-bot
2014-04-01 10:42:13 +00:00
committed by Gerrit Code Review
2 changed files with 265 additions and 236 deletions

View File

@@ -21,235 +21,282 @@
'use strict';
/**
* Add a language to the interlanguage list
* @param {string} name Name of language in Autonym font
* @param {string} url Link of the article in the respective language wiki
* For the given array, remove duplicates
* @param {Array} originalArray
* @return de-duplicated array
*/
function addLanguage( name, url ) {
var $linkNode,
$listNode,
$interlanguageList;
function unique( originalArray ) {
var uniqueArray = [];
$linkNode = $( '<a>' )
.addClass( 'active' )
.attr( 'href', url )
.text( name );
$listNode = $( '<li>' )
.append( $linkNode );
$interlanguageList = $( '#p-lang > div > ul' );
$interlanguageList.append( $listNode );
$.each( originalArray, function ( i, v ) {
if ( $.inArray( v, uniqueArray ) === -1 ) {
uniqueArray.push( v );
}
} );
return uniqueArray;
}
/**
* @class
*/
function CompactInterlanguageList( interlanguageList, options ) {
this.$interlanguageList = $( interlanguageList );
this.options = $.extend( {}, $.fn.compactInterlanguageList.defaults, options );
this.interlanguageList = {};
this.compactList = {};
this.$trigger = null;
this.compactSize = 0;
this.listSize = 0;
this.init();
}
CompactInterlanguageList.prototype = {
/**
* Initialize the plugin
*/
init: function () {
this.interlanguageList = this.getInterlanguageList();
this.listSize = this.getListSize();
if ( this.listSize <= this.options.max ) {
// Not enough languages to compact the list
return;
}
// if the interlanguage list is of moderate size, the compact size is 7.
this.compactSize = ( this.listSize <= 12 ) ? 7 : this.options.max;
this.hideOriginal();
this.compactList = this.getCompactList();
this.render();
this.listen();
},
/**
* Render the compacted interlanguage list and triggers
*/
render: function () {
var language;
for ( language in this.compactList ) {
this.addLanguage( language );
}
this.addTrigger();
},
/**
* Bind to event handlers and listen for events
*/
listen: function () {
var cl = this,
triggerPosition = this.$trigger.offset(),
ulsLanguageList = {},
languages;
languages = $.map( this.interlanguageList, function ( language, languageCode ) {
ulsLanguageList[ languageCode ] = language.autonym;
return languageCode;
} );
// Attach ULS to the trigger
this.$trigger.uls( {
onReady: function () {
this.$menu.addClass( 'interlanguage-uls-menu' );
},
/**
* Language selection handler
* @param {string} language language code
*/
onSelect: function ( language ) {
var previousLanguages = mw.uls.getPreviousLanguages();
previousLanguages.push( language );
previousLanguages = unique( previousLanguages );
mw.uls.setPreviousLanguages( previousLanguages );
window.location.href = cl.interlanguageList[ language ].href;
},
// Use compact version of ULS
compact: true,
// Left position of the language selector
left: triggerPosition.left + cl.$trigger.width() + cl.$interlanguageList.width() + 'px',
// Top position of the language selector. Top it 250px above to take care of
// caret pointing the trigger. See .interlanguage-uls-menu:after style definition
top: triggerPosition.top - cl.$trigger.height() / 2 - 250 + 'px',
// List of languages to be shown
languages: ulsLanguageList,
// Show common languages
quickList: cl.filterByCommonLanguages( languages )
} );
},
/**
* Get the compacted interlanguage list as associative array
* @return {Object}
*/
getCompactList: function () {
var language, languages, compactLanguages, index, compactedList = {};
languages = $.map( this.interlanguageList, function ( element, index ) {
return index;
} );
compactLanguages = this.compact( languages );
for ( index = 0; index < compactLanguages.length; index++ ) {
language = compactLanguages[ index ];
compactedList[ language ] = this.interlanguageList[ language ];
}
return compactedList;
},
/**
* Compact a given array of languages
* @param {Array} languages
* @return {Array} compacted array
*/
compact: function ( languages ) {
var compactLanguages = [];
// Add previous language selections to this array. Previous languages are always
// the better suggestion because user had explicitly chosen them.
compactLanguages = compactLanguages.concat( this.filterByPreviousLanguages() );
// Add all common languages to the beginning of array. These are the most probable
// languages predicted by ULS
compactLanguages = compactLanguages.concat( this.filterByCommonLanguages( languages ) );
// Finally add the whole languages array too. We will remove duplicate and cut down
// to required size.
compactLanguages = compactLanguages.concat( languages );
// Remove duplicates
compactLanguages = unique( compactLanguages );
// Cut to compact size and sort
compactLanguages = compactLanguages.slice( 0, this.compactSize ).sort();
return compactLanguages;
},
/**
* Filter the language list by previous languages. Not all previous languages
* will be present in interlanguage links. So filtering them.
* @return {Array} List of those language codes which are supported by article
*/
filterByPreviousLanguages: function ( languages ) {
var previousLanguages;
previousLanguages = mw.uls.getPreviousLanguages();
return $.grep( previousLanguages, function ( language ) {
return $.inArray( language, languages ) >= 0;
} );
},
/**
* Filter the language list by common languages. Common languages are the most
* probable languages predicted by ULS
* @return {Array} List of those language codes which are supported by article
*/
filterByCommonLanguages: function ( languages ) {
var commonLanguages;
commonLanguages = mw.uls.getFrequentLanguageList();
return $.grep( commonLanguages, function ( language ) {
return $.inArray( language, languages ) >= 0;
} );
},
/**
* Find out the existing languages supported
* by article and fetch their href
* @return {Object} List of exiting language codes and their hrefs
* @return {Object} List of existing language codes and their hrefs
*/
function getInterlanguageList() {
var interlangList = {},
selectedElement;
$( '#p-lang > div > ul > li > a' ).each( function() {
selectedElement = $( this );
interlangList[ selectedElement.attr( 'lang' ) ] = selectedElement.attr( 'href' );
getInterlanguageList: function getInterlanguageList() {
var interlanguageList = {};
this.$interlanguageList.find( 'li.interlanguage-link > a' ).each( function () {
var $this = $( this );
interlanguageList[ $this.attr( 'lang' ) ] = {
href: $this.attr( 'href' ),
autonym: $this.text()
};
} );
return interlangList;
}
return interlanguageList;
},
/**
* Fetch list of language names(in Autonym) which are supported by the article
* @return {Object} List of Language names in Autonym supported by article
* Get the size of the interlanguage list
*/
function getCurrentLanguages() {
var acceptedLanglist = {},
interlangList = getInterlanguageList(), i;
for ( i in interlangList ) {
if( interlangList.hasOwnProperty( i ) ) {
acceptedLanglist[ i ] = $.uls.data.getAutonym(i);
}
}
return acceptedLanglist;
}
/**
* Frequently spoken languages which are supported by the article for
* the Common Languages section of the ULS
* @return {Array} List of those language codes which are supported by article and appears
* in the Common Languages section of ULS
*/
function getCommonLanguages() {
// From ext.uls.init.js
var frequentLang = mw.uls.getFrequentLanguageList(),
$acceptedLang = $.map( getCurrentLanguages(), function ( element, index ) {
getListSize: function () {
return $.map( this.interlanguageList, function ( item, index ) {
return index;
} ),
commonLanguages = [], i;
for ( i = 0; i < frequentLang.length; i++ ) {
if ( $.inArray( frequentLang[i], $acceptedLang ) >= 0 ) {
commonLanguages.push( frequentLang[i] );
}
}
return commonLanguages;
}
} ).length;
},
/**
* Push the selected language into the previous languages list
* @param {string} Language code of language selected (clicked on)
* Hide the original interlanguage list
*/
function insertPreviousLanguage( currentLang ) {
mw.uls.insertPreviousLanguage( currentLang );
}
hideOriginal: function () {
this.$interlanguageList.find( '.interlanguage-link' ).hide();
},
/**
* Add a ULS trigger beneath the interlanguage links
* Add the trigger at the bottom of the language list
*/
function addULSlink() {
var $newLinknode,
$interlanguageList,
supportedLangs,
posOfTrigger;
addTrigger: function () {
var $trigger, $triggerLabel;
$newLinknode = $( '<div>' )
$trigger = $( '<div>' )
.addClass( 'mw-interlanguage-selector mw-ui-button active' )
.html( '&#8230' )
.append( $newLinknode );
.html( '&#8230' ); // '…'
$interlanguageList = $( '#p-lang > div > ul' );
$interlanguageList.append( $newLinknode );
posOfTrigger = $newLinknode.offset();
$( '.mw-interlanguage-selector' ).uls( {
onReady: function() {
this.$menu.addClass( 'interlanguage-uls-menu' );
$triggerLabel = $( '<label>' )
.attr( 'id', 'more-lang-label' )
.text( $.i18n( 'ext-uls-compact-link-count', this.listSize - this.compactSize ) );
this.$interlanguageList.append( $trigger, $triggerLabel );
this.$trigger = $trigger;
},
onSelect: function( language ) {
supportedLangs = getInterlanguageList();
// To set selected language as a previous language
insertPreviousLanguage( language );
window.location.href = supportedLangs[language];
},
/**
* Add a language to the interlanguage list
* @param {string} language
*/
addLanguage: function ( language ) {
var $link, $listItem, languageLink;
compact: true,
left: posOfTrigger.left + $newLinknode.width() + 50 + 'px',
top: posOfTrigger.top - $newLinknode.height()/2 - 250 + 'px',
languages: getCurrentLanguages(),
quickList: getCommonLanguages()
languageLink = this.interlanguageList[ language ];
$link = $( '<a>' )
.addClass( 'active' )
.attr( 'href', languageLink.href )
.text( languageLink.autonym );
$listItem = $( '<li>' )
.addClass( 'interlanguage-link interwiki-' + language )
.append( $link );
this.$interlanguageList.append( $listItem );
}
};
/**
* CompactInterlanguageList Plugin
* @param {Object} [option]
*/
$.fn.compactInterlanguageList = function ( option ) {
return this.each( function () {
var $this = $( this ),
data = $this.data( 'compactinterlanguagelist' ),
options = typeof option === 'object' && option;
if ( !data ) {
$this.data( 'compactinterlanguagelist', ( data = new CompactInterlanguageList( this, options ) ) );
}
if ( typeof option === 'string' ) {
data[ option ]();
}
} );
}
};
/**
* Hide existing languages displayed on the page
* Defaults
*/
function hideLanguages() {
var $languages = $( '.interlanguage-link' );
$languages.hide();
}
$.fn.compactInterlanguageList.defaults = {
max: 9 // Compact the list to this size
};
/**
* Returns all languages returned by the commonLanguages function
* and randomly some more if the number falls short of numberOfLanguagesToShow parameter
* @param {number} numberOfLanguagesToShow Number of languages to be shown in sidebar
* @return {Array} Language codes of the final list to be displayed
*/
function displayLanguages( numberOfLanguagesToShow ) {
var commonLang = getCommonLanguages(),
acceptedLangs = $.map( getCurrentLanguages(), function ( element, index ) {
return index;
} ), i,
prevLangs = mw.uls.getPreviousLanguages(),
count,
finalList = [];
// Add languages in the common list and accepted by article
for ( i = 0; i < commonLang.length; i++ ) {
finalList.push( commonLang[i] );
}
// Add languages in previous choices to list if not already in it
for ( i = 0; i < prevLangs.length; i++ ) {
if (
$.inArray( prevLangs[i], finalList ) < 0 &&
$.inArray( prevLangs[i], acceptedLangs ) >= 0
) {
finalList.push( prevLangs[i] );
}
}
// Add random languages to make the language list long enough, if it isn't already
count = finalList.length;
if ( count < numberOfLanguagesToShow ) {
for ( i = 0; i < acceptedLangs.length; i++ ) {
if ( $.inArray( acceptedLangs[i], finalList ) < 0 ) {
finalList.push( acceptedLangs[i] );
count++;
if ( count === numberOfLanguagesToShow ) {
break;
}
}
}
}
// Sorting the language list in alphabetical order of ISO codes
finalList = finalList.sort();
return finalList;
}
/*
* Adds a label stating the number of more languages
* beneath the ULS link
* @param {Number} numberOfLanguagesSupported Number of languages supported by article
* @param {Number} numberOfLanguagesToShow Number of languages to be shown in the sidebar
*/
function addLabel( numberOfLanguagesSupported, numberOfLanguagesToShow ) {
var $interlanguageList = $( '#p-lang > div > ul' ),
newLabel = $( '<label>' )
.attr( 'id', 'more-lang-label' ),
numberOfLanguagesHidden = numberOfLanguagesSupported - numberOfLanguagesToShow;
newLabel.text( $.i18n( 'ext-uls-compact-link-count', numberOfLanguagesHidden ) );
$interlanguageList.append( newLabel );
}
/*
* Driver function to manipulate interlanguage list
* Computes number of languages to be shown
* and passes appropriate parameters to displayLanguages
* and addLabel functions
*/
function manageInterlaguageList() {
var $numOfLangCurrently = $( '.interlanguage-link' ).length,
currentLangs = getInterlanguageList(),
longListLength = 9,
maxLanguageCheck = 12,
shortListLength = 7,
i,
finalList; // Final list of languages to be displayed on page
// If the total number of languages are between 9(longListLength) and 12(inclusive) then
// we show only 7 (shortListLength) languages (to avoid displaying "2/3 more languages")
// Else, we show 9 languages. Please note that as per the current design of the system, this
// does not always hold true. The language list might exceed 9. This will be fixed as we refine
// the algo for the languages being shown.
if ( $numOfLangCurrently > longListLength ) {
hideLanguages();
if ( $numOfLangCurrently > longListLength && $numOfLangCurrently <= maxLanguageCheck ) {
finalList = displayLanguages( shortListLength );
} else {
finalList = displayLanguages( longListLength );
}
// Output all the languages we have in the finalList to the page
for ( i in finalList ) {
addLanguage( $.uls.data.getAutonym( finalList[i] ), currentLangs[ finalList[i] ] );
}
addULSlink();
addLabel( $numOfLangCurrently, finalList.length );
}
}
$( document ).ready( manageInterlaguageList() );
$( document ).ready( function () {
$( '#p-lang-list' ).compactInterlanguageList();
} );
}( jQuery, mediaWiki ) );

View File

@@ -26,7 +26,7 @@
slice = Array.prototype.slice;
return function ( name ) {
var list = lists[name] || ( lists[name] = $.Callbacks( 'memory' ) );
var list = lists[ name ] || ( lists[ name ] = $.Callbacks( 'memory' ) );
return {
add: list.add,
@@ -42,7 +42,7 @@
mw.uls = mw.uls || {};
mw.uls.previousLanguagesCookie = 'uls-previous-languages';
mw.uls.previousLanguageAutonymCookie = 'uls-previous-language-autonym';
mw.uls.languageSettingsModules = ['ext.uls.inputsettings', 'ext.uls.displaysettings'];
mw.uls.languageSettingsModules = [ 'ext.uls.inputsettings', 'ext.uls.displaysettings' ];
// What was the last thing that the user did to select the language:
// * 'map' - clicked the map
@@ -104,8 +104,9 @@
mw.uls.setPreviousLanguages = function ( previousLanguages ) {
$.cookie( mw.uls.previousLanguagesCookie,
$.toJSON( previousLanguages ),
{ path: '/' }
$.toJSON( previousLanguages ), {
path: '/'
}
);
};
@@ -120,25 +121,6 @@
return $.parseJSON( previousLanguages ).slice( -5 );
};
/**
* Push the selected language into the previous languages list
* if it isn't there already
* @param {string} Language code of language to be pushed into list
*/
mw.uls.insertPreviousLanguage = function ( prevLangCode ) {
var previousLanguages = mw.uls.getPreviousLanguages() || [],
currentLangIndex;
// Checking if it already exists in array
currentLangIndex = $.inArray( prevLangCode, previousLanguages );
if ( currentLangIndex < 0 ) {
previousLanguages.push( prevLangCode );
} else {
previousLanguages.splice( currentLangIndex, 1 );
previousLanguages.push( prevLangCode );
}
mw.uls.setPreviousLanguages( previousLanguages );
};
/**
* Returns the browser's user interface language or the system language.
* The caller should check the validity of the returned language code.
@@ -150,7 +132,7 @@
// userLanguage is only for IE and returns system locale.
// Empty string is a fallback in case both are undefined
// to avoid runtime error with split().
return ( window.navigator.language || window.navigator.userLanguage || '' ).split( '-' )[0];
return ( window.navigator.language || window.navigator.userLanguage || '' ).split( '-' )[ 0 ];
};
/*jshint camelcase:false*/
@@ -201,7 +183,7 @@
var target;
// If the language is already known and defined, just use it
if ( $.fn.uls.defaults.languages[langCode] !== undefined ) {
if ( $.fn.uls.defaults.languages[ langCode ] !== undefined ) {
return true;
}
@@ -212,7 +194,7 @@
if ( target ) {
// Check that the redirect's target is known
// to this instance of ULS
return $.fn.uls.defaults.languages[target] !== undefined;
return $.fn.uls.defaults.languages[ target ] !== undefined;
}
return false;
@@ -229,13 +211,13 @@
function isBrowserSupported() {
var blacklist = {
'msie': [
['<=', 7]
[ '<=', 7 ]
]
};
// jquery.client changed in MediaWiki 1.22.
// FIXME: Remove when ULS minimum MW version is 1.22.
if ( parseInt( mw.config.get( 'wgVersion' ).split( '.' )[1], '10' ) < 22 ) {
if ( parseInt( mw.config.get( 'wgVersion' ).split( '.' )[ 1 ], '10' ) < 22 ) {
return !/MSIE [67]/i.test( navigator.userAgent );
}