MediaWiki:Common.js: Difference between revisions
Content deleted Content added
Removing customization policy violations. Moving imports to MediaWiki:ImportJS. |
Improve Main Page browsing, article search, featured article links and independent-wiki information |
||
Line 30:
/* END - switching images for the main page navigation buttons */
/* BEGIN Elite Main Page search */
if ( mw.config.get( "wgIsMainPage" ) ) {
mw.loader.using( [ "mediawiki.searchSuggest", "mediawiki.util" ] ).then( function () {
/* Main Page InputBox enhancement. Load after:
* mw.loader.using( [ 'mediawiki.searchSuggest', 'mediawiki.util' ] )
* The first dependency supplies jquery.suggestions, mediawiki.api and user.options.
* Keep the ordinary InputBox form: no-JS/error/short-query submission still works.
*/
( function ( mw, $ ) {
'use strict';
if ( !mw.config.get( 'wgIsMainPage' ) ) {
return;
}
$( function () {
// mediawiki.searchSuggest's ready callback runs first. Reconfigure its
// existing widget, rather than attaching a competing autocomplete.
mw.hook( 'wikipage.content' ).add( function () {
$( '.ed-home-search input.mw-searchInput' ).each( function () {
const input = this;
const $input = $( input );
if ( $input.data( 'elite-home-search' ) || !input.form ) {
return;
}
if ( !$.fn.suggestions || !mw.searchSuggest || !mw.Api || !mw.util ) {
return; // Leave the working form alone if dependencies are absent.
}
let request = null;
let generation = 0;
let composing = false;
const api = new mw.Api(); // Configured local wiki only, never ForeignApi.
// A stable per-input ID also permits more than one hero InputBox.
const listId = 'elite-home-suggestions-' + $( '.ed-home-search input.mw-searchInput' ).index( input );
let context;
function cancel() {
generation++;
if ( request ) {
const previous = request;
request = null;
previous.abort();
}
}
function syncAria() {
const container = context.data.$container[ 0 ];
const results = container.querySelectorAll( '.suggestions-result' );
const expanded = container.style.display !== 'none' && results.length > 0;
input.setAttribute( 'aria-expanded', String( expanded ) );
input.removeAttribute( 'aria-activedescendant' );
results.forEach( function ( result, index ) {
result.id = listId + '-' + index;
result.setAttribute( 'role', 'option' );
const selected = expanded && result.classList.contains( 'suggestions-result-current' );
result.setAttribute( 'aria-selected', String( selected ) );
if ( selected ) {
input.setAttribute( 'aria-activedescendant', result.id );
}
} );
}
function hide() {
context.data.$container.hide();
syncAria();
}
function localUrl( title ) {
// Ignore OpenSearch's URL array, deriving links through MediaWiki.
const url = new URL( mw.util.getUrl( title ), window.location.href );
return url.origin === window.location.origin ? url.href : null;
}
$input.suggestions( {
$region: $input,
delay: 250,
maxRows: 6,
maxExpandFactor: 1,
cache: false,
highlightInput: false,
submitOnClick: false,
special: {},
fetch: function ( query, response ) {
if ( !context ) {
return; // Initial native construction may fetch synchronously.
}
cancel();
const current = generation;
if ( composing || query.trim().length < 2 || query.length > 200 ) {
response( [] );
hide();
return;
}
// [0], not 0: native request() otherwise falls back to the
// user's configured namespaces because zero is falsy.
request = mw.searchSuggest.request( api, query, function ( titles ) {
if ( current !== generation || query !== input.value ||
document.activeElement !== input || !input.isConnected ) {
return;
}
const safeTitles = Array.isArray( titles ) ? titles.filter( function ( title ) {
return typeof title === 'string' && title.length > 0 &&
!/[\u0000-\u001f\u007f]/.test( title ) && localUrl( title );
} ).slice( 0, 6 ) : [];
response( safeTitles );
if ( safeTitles.length === 0 ) {
hide();
}
}, 6, [ 0 ] );
request.fail( function () {
if ( current === generation ) {
response( [] );
hide();
}
} );
},
cancel: cancel,
update: { after: function () { if ( context ) { syncAria(); } } },
result: {
render: function ( title ) {
this[ 0 ].textContent = title;
const link = document.createElement( 'a' );
link.href = localUrl( title );
link.tabIndex = -1; // Focus stays on the combobox.
link.className = 'mw-searchSuggest-link';
this.wrap( link );
},
select: function ( textbox, source ) {
if ( source === 'keyboard' ) {
const url = localUrl( this.data( 'text' ) );
if ( url ) {
cancel();
window.location.assign( url );
return false;
}
}
return true; // Mouse uses the ordinary local anchor.
}
}
} );
context = $input.data( 'suggestions-context' );
cancel(); // Abort any generic request started before reconfiguration.
const genericRequest = $.data( input, 'request' );
if ( genericRequest ) {
genericRequest.abort();
$.removeData( input, 'request' );
}
$input.data( 'elite-home-search', true );
$input.attr( {
role: 'combobox',
'aria-autocomplete': 'list',
'aria-haspopup': 'listbox',
'aria-controls': listId,
'aria-expanded': 'false'
} ); // Preserve InputBox's existing accessible label.
context.data.$container.addClass( 'ed-home-suggestions' );
const results = context.data.$container[ 0 ].querySelector( '.suggestions-results' );
results.id = listId;
results.setAttribute( 'role', 'listbox' );
results.setAttribute( 'aria-label', 'Suggested articles' );
hide();
// Native keyboard handling owns arrows/Enter/Escape. Input covers
// paste, mobile keyboards and other edits without a keypress event.
function changed() {
cancel();
hide();
if ( !composing ) {
context.data.keypressed = 0;
$input.trigger( 'keypress' ); // Native 250 ms debounce + cancel.
}
}
$input.on( 'input.eliteHomeSearch', changed )
.on( 'compositionstart.eliteHomeSearch', function () {
composing = true;
cancel();
hide();
} )
.on( 'compositionend.eliteHomeSearch', function () {
composing = false;
changed();
} );
$( input.form ).on( 'submit.eliteHomeSearch', function () { cancel(); hide(); } );
const observer = new MutationObserver( syncAria );
observer.observe( context.data.$container[ 0 ], {
subtree: true, childList: true, attributes: true,
attributeFilter: [ 'class', 'style' ]
} );
if ( document.activeElement === input && input.value.length >= 2 ) {
changed();
}
} );
} );
} );
}( mediaWiki, jQuery ) );
} );
}
/* END Elite Main Page search */
| |||