MediaWiki:Common.js: различия между версиями
(Служебное обновление wikibrand) |
(Поиск: подсказки с изображениями) |
||
| Строка 13: | Строка 13: | ||
} ); | } ); | ||
} | } | ||
// 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(); | |||
} | |||
}() ); | |||
Версия от 18:01, 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 );
}
}
} );
}
// 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();
}
}() );