MediaWiki:Common.js: различия между версиями
(Поиск: подсказки с изображениями) |
(Fix search suggestions init: wait for mediawiki.util before thumbnails dropdown bootstrap) |
||
| Строка 14: | Строка 14: | ||
} | } | ||
mw.loader.using( [ 'mediawiki.util' ] ).then( function () { | |||
// Wikibrand: custom search suggestions with thumbnails in header search | // Wikibrand: custom search suggestions with thumbnails in header search | ||
( function () { | ( function () { | ||
| Строка 264: | Строка 265: | ||
} | } | ||
}() ); | }() ); | ||
} ); | |||
Текущая версия от 18:04, 11 марта 2026
if ( typeof mw !== 'undefined' && !mw.config.get( 'wgUserName' ) ) {
mw.loader.using( [ 'mediawiki.util' ] ).then( function () {
var action = mw.util.getParamValue( 'action' );
var isMissing = mw.config.get( 'wgArticleId' ) === 0 || mw.config.get( 'wgCurRevisionId' ) === 0;
var isSpecial = mw.config.get( 'wgCanonicalNamespace' ) === 'Special';
if ( action === 'edit' && isMissing && !isSpecial ) {
var target = mw.config.get( 'wgPageName' );
var redirectUrl = mw.util.getUrl( 'Special:BrandRequest', { target: target } );
if ( window.location.href.indexOf( 'Special:BrandRequest' ) === -1 ) {
window.location.replace( redirectUrl );
}
}
} );
}
mw.loader.using( [ 'mediawiki.util' ] ).then( function () {
// Wikibrand: custom search suggestions with thumbnails in header search
( function () {
if ( typeof mw === 'undefined' || typeof fetch === 'undefined' ) {
return;
}
var API = mw.util.wikiScript( 'api' );
var imgCache = Object.create( null );
function addStyles() {
if ( document.getElementById( 'wbg-search-style' ) ) {
return;
}
var style = document.createElement( 'style' );
style.id = 'wbg-search-style';
style.textContent = '' +
'.wbg-search-form{position:relative!important;}' +
'.wbg-search-panel{position:absolute;left:0;right:0;top:calc(100% + 2px);z-index:1200;background:#fff;border:1px solid #a2a9b1;box-shadow:0 4px 14px rgba(0,0,0,.12);max-height:420px;overflow:auto;}' +
'.wbg-search-item{display:flex;align-items:center;gap:10px;padding:8px 10px;text-decoration:none;color:#202122;border-top:1px solid #eaecf0;}' +
'.wbg-search-item:first-child{border-top:none;}' +
'.wbg-search-item:hover{background:#f8f9fa;}' +
'.wbg-search-thumb{width:38px;height:38px;border-radius:4px;object-fit:cover;flex:0 0 38px;background:#eaecf0;}' +
'.wbg-search-fallback{width:38px;height:38px;border-radius:4px;display:flex;align-items:center;justify-content:center;background:#eaecf0;color:#54595d;font-weight:700;font-size:12px;flex:0 0 38px;}' +
'.wbg-search-title{font-size:14px;line-height:1.25;}' +
'.wbg-search-meta{font-size:12px;color:#72777d;margin-top:2px;}' +
'.wbg-search-form.wbg-search-active .cdx-menu,.wbg-search-form.wbg-search-active .suggestions-results{display:none!important;}';
document.head.appendChild( style );
}
function apiQuery( params ) {
params.format = 'json';
var qs = new URLSearchParams( params );
return fetch( API + '?' + qs.toString(), { credentials: 'same-origin' } ).then( function ( r ) {
return r.json();
} );
}
function firstPage( data ) {
if ( !data || !data.query || !data.query.pages ) {
return null;
}
var pages = data.query.pages;
for ( var k in pages ) {
if ( Object.prototype.hasOwnProperty.call( pages, k ) ) {
return pages[ k ];
}
}
return null;
}
function firstLetter( title ) {
if ( !title ) {
return '?';
}
return title.charAt( 0 ).toUpperCase();
}
function getImageForTitle( title ) {
if ( Object.prototype.hasOwnProperty.call( imgCache, title ) ) {
return Promise.resolve( imgCache[ title ] );
}
return apiQuery( {
action: 'query',
titles: title,
prop: 'images',
imlimit: '1'
} ).then( function ( data1 ) {
var p = firstPage( data1 );
var imgTitle = p && p.images && p.images[ 0 ] && p.images[ 0 ].title;
if ( !imgTitle ) {
imgCache[ title ] = null;
return null;
}
return apiQuery( {
action: 'query',
titles: imgTitle,
prop: 'imageinfo',
iiprop: 'url',
iiurlwidth: '76'
} ).then( function ( data2 ) {
var fp = firstPage( data2 );
var ii = fp && fp.imageinfo && fp.imageinfo[ 0 ];
var url = ii && ( ii.thumburl || ii.url ) ? ( ii.thumburl || ii.url ) : null;
imgCache[ title ] = url;
return url;
} );
} ).catch( function () {
imgCache[ title ] = null;
return null;
} );
}
function buildItemNode( item ) {
var a = document.createElement( 'a' );
a.className = 'wbg-search-item';
a.href = item.fullurl || mw.util.getUrl( item.title );
if ( item.thumb ) {
var img = document.createElement( 'img' );
img.className = 'wbg-search-thumb';
img.src = item.thumb;
img.alt = item.title;
img.loading = 'lazy';
a.appendChild( img );
} else {
var fb = document.createElement( 'div' );
fb.className = 'wbg-search-fallback';
fb.textContent = firstLetter( item.title );
a.appendChild( fb );
}
var textWrap = document.createElement( 'div' );
var t = document.createElement( 'div' );
t.className = 'wbg-search-title';
t.textContent = item.title;
textWrap.appendChild( t );
var m = document.createElement( 'div' );
m.className = 'wbg-search-meta';
m.textContent = 'Открыть статью';
textWrap.appendChild( m );
a.appendChild( textWrap );
return a;
}
function debounce( fn, ms ) {
var timer = 0;
return function () {
var args = arguments;
clearTimeout( timer );
timer = window.setTimeout( function () {
fn.apply( null, args );
}, ms );
};
}
function attachToInput( input ) {
var form = input.closest( 'form' ) || input.parentElement;
if ( !form || form.dataset.wbgSearchReady === '1' ) {
return;
}
form.dataset.wbgSearchReady = '1';
form.classList.add( 'wbg-search-form' );
var panel = document.createElement( 'div' );
panel.className = 'wbg-search-panel';
panel.style.display = 'none';
form.appendChild( panel );
var lastReq = 0;
function hidePanel() {
panel.style.display = 'none';
panel.innerHTML = '';
form.classList.remove( 'wbg-search-active' );
}
function renderItems( list ) {
panel.innerHTML = '';
if ( !list.length ) {
hidePanel();
return;
}
list.forEach( function ( item ) {
panel.appendChild( buildItemNode( item ) );
} );
panel.style.display = 'block';
form.classList.add( 'wbg-search-active' );
}
var runSearch = debounce( function () {
var q = ( input.value || '' ).trim();
if ( q.length < 2 ) {
hidePanel();
return;
}
lastReq += 1;
var reqId = lastReq;
apiQuery( {
action: 'query',
generator: 'prefixsearch',
gpssearch: q,
gpslimit: '8',
prop: 'info',
inprop: 'url'
} ).then( function ( data ) {
if ( reqId !== lastReq ) {
return;
}
var pagesObj = data && data.query && data.query.pages ? data.query.pages : {};
var pages = Object.keys( pagesObj ).map( function ( k ) {
return pagesObj[ k ];
} ).sort( function ( a, b ) {
return ( a.index || 0 ) - ( b.index || 0 );
} );
return Promise.all( pages.map( function ( p ) {
return getImageForTitle( p.title ).then( function ( thumb ) {
return {
title: p.title,
fullurl: p.fullurl,
thumb: thumb
};
} );
} ) );
} ).then( function ( items ) {
if ( reqId !== lastReq || !items ) {
return;
}
renderItems( items );
} ).catch( function () {
hidePanel();
} );
}, 180 );
input.addEventListener( 'input', runSearch );
input.addEventListener( 'focus', runSearch );
input.addEventListener( 'keydown', function ( e ) {
if ( e.key === 'Escape' ) {
hidePanel();
}
} );
document.addEventListener( 'click', function ( e ) {
if ( !form.contains( e.target ) ) {
hidePanel();
}
} );
}
function init() {
addStyles();
var inputs = document.querySelectorAll( 'input#searchInput, input[name="search"]' );
for ( var i = 0; i < inputs.length; i++ ) {
attachToInput( inputs[ i ] );
}
}
if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', init );
} else {
init();
}
}() );
} );