MediaWiki:Gadget-advanced-search.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
This user script seems to have a documentation page at MediaWiki:Gadget-advanced-search. |
/**
* Improve advanced search by adding
* options suitable for Commons
*
* Maintainer: [[User:Bawolff]]
* Created in 2014
* Version 1.1 [Jan 2018]
*/
/* eslint-disable vars-on-top, one-var, no-use-before-define, no-jquery/no-global-selector */
mw.loader.using( [ 'mw.Api', 'jquery.suggestions' ], function () {
'use strict';
if ( mw.config.get( 'wgCanonicalSpecialPageName' ) !== 'Search' ) {
return;
}
var i18n = {
en: {
'header-cat': 'Search by category:',
'header-file': 'File options:',
'cat-any': 'In any of these categories (comma separated):',
'cat-all': 'In all of these categories:',
'cat-none': 'In none of these categories:',
'cat-depth': 'Include subcategories up to a depth of:',
'file-type': 'Only include the following types of files:',
'file-license': 'Copyright status:',
'file-type-all': 'All',
'file-type-multimedia': 'Multimedia',
'file-type-jpeg': 'Photographs (JPEG)',
'file-type-vector': 'Vector images (SVG)',
'file-type-drawing': 'Drawing',
'file-pd': 'Do absolutely anything with it (Public domain)',
'file-cc': 'Freely re-usable under a Creative Commons license',
'file-any': 'Show all (All files on commons can be re-used under certain conditions)'
},
ru: {
'header-cat': 'Поиск по категориям:',
'header-file': 'Типы файлов:',
'cat-any': 'В любой из этих категорий:',
'cat-all': 'Во всех этих категориях:',
'cat-none': 'Ни в одной из этих категорий:',
'cat-depth': 'Включать подкатегории до глубины:',
'file-type': 'Включать только следующие типы файлов:',
'file-license': 'Правовой статус:',
'file-type-all': 'Все',
'file-type-multimedia': 'Медиафайлы',
'file-type-jpeg': 'Фотографии (JPEG)',
'file-type-vector': 'Векторные изображения (SVG)',
'file-type-drawing': 'Рисунки и чертежи',
'file-pd': 'Абсолютно никаких ограничений (общественное достояние)',
'file-cc': 'Допускается свободное повторное использование по лицензии Creative Commons',
'file-any': 'Показать все (все файлы на Викискладе могут быть повторно использованы при определённых условиях)'
}
},
lookup = function ( key ) {
var lang = mw.config.get( 'wgUserLanguage' );
if ( i18n[ lang ] && i18n[ lang ][ key ] ) {
return i18n[ lang ][ key ];
}
return i18n.en[ key ];
},
expand = function ( str ) {
return str.replace( /\$([a-z-]+)/g, function ( match, p1 ) {
return lookup( p1 );
} );
},
renderSearchSuggest = function ( suggestion ) {
var split = splitField( suggestion ),
lastBit;
if ( split.length >= 2 ) {
lastBit = split[ split.length - 2 ];
} else {
mw.log( 'No commas in search suggestion?' );
lastBit = suggestion;
}
$( this ).text( lastBit );
},
fetchSearchQuery = function ( input, callback ) {
// http://commons.wikimedia.org/w/api.php?action=query&generator=allpages&gapnamespace=14&gaplimit=7&gapprefix=Cat&prop=categoryinfo|links|templates&tllimit=max&pllimit=max&tltemplates=Template:Category%20redirect&plnamespace=14
var lastInput = splitField( input );
lastInput = lastInput ? lastInput[ lastInput.length - 1 ] : '';
if ( lastInput.match( /^\s*$/ ) ) {
callback( [] );
return;
}
var api = new mw.Api();
api.get( {
action: 'query',
generator: 'allpages',
gapnamespace: 14,
gaplimit: 5,
gapprefix: lastInput,
prop: 'categoryinfo|links|templates',
tllimit: 'max',
pllimit: 'max',
tltemplates: 'Template:Category_redirect',
plnamespace: 14,
format: 'json'
} ).done( function ( data1 ) {
// This would be much more efficient if we did at same time.
api.get( {
action: 'query',
generator: 'search',
gsrnamespace: 14,
gsrlimit: 5,
gsrsearch: lastInput,
prop: 'categoryinfo|links|templates',
tllimit: 'max',
pllimit: 'max',
tltemplates: 'Template:Category_redirect',
plnamespace: 14,
format: 'json'
} ).done( function ( data2 ) {
var suggestions = [],
formatSuggestion = function ( item ) {
// Kill the namespace, add the comma separator as a hint to user.
// Needs to be full search text. renderSearchSuggest will cause only part to be displayed.
if ( input.match( /,\s/ ) ) {
return input.replace( /,\s[^,]*$/, '' ) + ', ' + item.replace( /^[^:]*:/, '' ) + ', ';
} else {
return item.replace( /^[^:]*:/, '' ) + ', ';
}
};
if ( !( data1 && data1.query && data1.query.pages && data2 && data2.query && data2.query.pages ) ) {
mw.log( 'error fetching data' );
return;
}
var combinedResults = $.extend( {}, data1.query.pages, data2.query.pages );
for ( var i in combinedResults ) {
// XXX does not do de-duplication in regards to cat redirects.
if ( combinedResults[ i ].templates !== undefined ) {
for ( var j in combinedResults[ i ].links ) {
suggestions[ suggestions.length ] = formatSuggestion( combinedResults[ i ].links[ j ].title );
if ( j > 5 ) {
break;
}
}
} else if ( combinedResults[ i ].categoryinfo.size > 5 ) {
// Realistically, most people won't want super small categories.
suggestions[ suggestions.length ] = formatSuggestion( combinedResults[ i ].title );
}
}
callback( suggestions );
} );
} );
},
add = function () {
if ( !$( '#mw-searchoptions' ).length ) {
return;
}
// This doesn't convert numbers...
var html = '<div class="sr-category"><h4>$header-cat </h4><div class="divider"/><table border="0"><tbody><tr><td><label for="search-cat-any">$cat-any </label></td><td><input id="search-cat-any" name="search-cat-any" size="50"></input></td></tr><tr><td><label for="search-cat-all">$cat-all </label></td><td><input id="search-cat-all" name="search-cat-all" size="50"></input></td></tr><tr><td><label for="search-cat-none">$cat-none </label></td><td><input id="search-cat-none" name="search-cat-none" size="50"></input></td></tr><!--<tr><td><label for="search-cat-depth">$cat-depth </label></td><td><select id="search-cat-depth" name="search-cat-depth"><option value="1">1</option><option value="2" selected>2</option><option value="3">3</option></select></td></tr>--></tbody></table></div><div class="sr-file" style="clear:both"><h4>$header-file </h4><div class="divider"><table border="0"><tr><td>$file-license </td><td><select id="search-file-copyright" name="search-file-copyright"><option value="any">$file-any </option><option value="pd">$file-pd</option><option value="by">$file-cc</option></select></td></tr><tr><td>$file-type</td><td><select id="search-file-type" name="search-file-type"><option value="">$file-type-all</option><!--<option value="ogv oga ogg webm mid midi flac wav gif">$file-type-multimedia</option>--><option value="webm">WebM</option><option value="pdf">PDF</option><option value="png">PNG</option><option value="gif">GIF</option><option value="jpg">$file-type-jpeg</option><option value="svg">$file-type-vector</option></select></td></tr></table><input type="hidden" name="search-orig-query" value="" id="search-orig-query"/></div><br style="clear:both">';
$( '#mw-searchoptions' ).prepend( $( expand( html ) ) );
restoreDefaults( [
'search-cat-any', 'search-cat-all', 'search-cat-none', 'search-cat-depth', 'search-file-copyright', 'search-file-type'
] );
if ( mw.util.getParamValue( 'search-orig-query' ) !== null ) {
OO.ui.infuse( $( '#searchText' ) ).setValue( mw.util.getParamValue( 'search-orig-query' ) );
}
$( '#powersearch' ).on( 'submit', fixForm );
$( '#search-cat-all, #search-cat-any, #search-cat-none' ).suggestions( {
fetch: fetchSearchQuery,
cache: true,
result: {
render: renderSearchSuggest
}
} );
},
fixForm = function () {
var altCat, cats,
searchBox = OO.ui.infuse( $( '#searchText' ) ),
catsAll = $( '#search-cat-all' )[ 0 ],
catsAny = $( '#search-cat-any' )[ 0 ],
catNone = $( '#search-cat-none' )[ 0 ],
addedText = ' ',
trimField = function ( input ) {
if ( input === null || input === undefined ) {
input = '';
}
input = String( input ); // FIXME why was I casting to string in first place?
input = input.replace( /^\s*/, '' ).replace( /\s*$/, '' );
return input;
};
if ( catsAny ) {
altCat = splitField( catsAny.value );
cats = altCat.join( '|' ).replace( /\|$/, '' );
}
if ( cats ) {
addedText += ' incategory:"' + cats + '"';
}
var anyCat = catsAll ? splitField( catsAll.value ) : [];
anyCat.forEach( function () {
var field = trimField( this );
if ( field === '' ) {
return;
}
addedText += ' incategory:"' + field + '"';
} );
var noCat = catNone ? splitField( catNone.value ) : [];
noCat.forEach( function () {
var field = trimField( this );
if ( field === '' ) {
return;
}
addedText += ' -incategory:"' + field + '"';
} );
var fileTypes = ( String( $( '#search-file-type' ).val() ) ).split( ' ' );
fileTypes.forEach( function () {
var field = trimField( this );
if ( field === '' ) {
return;
}
addedText += ' intitle:' + field;
} );
var copyright = $( '#search-file-copyright' ).val();
if ( copyright === 'pd' ) {
addedText += ' hastemplate:PD-Layout';
}
if ( copyright === 'cc' ) {
addedText += ' hastemplate:CC-Layout';
}
$( '#search-orig-query' ).val( searchBox.getValue() );
searchBox.setValue( searchBox.getValue() + addedText );
},
splitField = function ( text ) {
var res = ( String( text ) ).split( /,\s+/ );
if ( res.length === 1 && res[ 0 ].match( /^\s*$/ ) !== null ) {
return [];
}
return res;
},
// Security note: The elements of list should not come from user input.
restoreDefaults = function ( list ) {
list.forEach( function ( item ) {
var val = mw.util.getParamValue( item );
if ( val === null || val === '' ) {
return;
}
$( '#' + item ).val( val );
} );
};
add();
} );