Benutzer:TMg/advancedSearch.js
Erscheinungsbild
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
( function ( mw, $ ) {
'use strict';
// Shorten out if we are not on [[Special:Search]]
if ( mw.config.get( 'wgCanonicalSpecialPageName' ) !== 'Search' ) {
return;
}
function isLoaded() {
if ( !mw.libs ) {
mw.libs = {};
}
if ( !mw.libs.advancedSearch ) {
mw.libs.advancedSearch = {};
}
if ( mw.libs.advancedSearch.advancedOptionsLoaded ) {
return true;
}
mw.libs.advancedSearch.advancedOptionsLoaded = true;
return false;
}
if ( isLoaded() ) {
return;
}
/**
* @param {string} val
* @return {string}
*/
function trimQuotes( val ) {
val = val.replace( /^"((?:\\.|[^"\\])+)"$/, '$1' );
if ( !/^"/.test( val ) ) {
val = val.replace( /\\(.)/g, '$1' );
}
return val;
}
/**
* @param {string} val
* @return {string}
*/
function enforceQuotes( val ) {
return '"' + trimQuotes( val ).replace( /(["\\])/g, '\\$1' ) + '"';
}
/**
* @param {string} val
* @return {string}
*/
function optionalQuotes( val ) {
return /\s/.test( val ) ? enforceQuotes( val ) : trimQuotes( val );
}
/**
* @param {string} val
* @return {string}
*/
function formatSizeConstraint( val ) {
return val.replace( /[\s.]+/g, '' ).replace( /(\d)\D+(?=\d)/g, '$1,' );
}
var advancedOptions = [
// Text
{
group: 'text',
id: 'phrase',
placeholder: '"…"',
formatter: function ( val ) {
return enforceQuotes( val );
},
parser: /(?:^| +)"((?:\\.|[^"\\])+)"(?= |$)/gi
},
{
group: 'text',
id: 'fuzzy',
placeholder: '…~2',
formatter: function ( val ) {
return optionalQuotes( val ) + '~2';
},
parser: /(?:^| +)("(?:\\.|[^"\\])+"|[^\s":]+)~2(?= |$)/gi
},
{
group: 'text',
id: 'not',
placeholder: '-…',
formatter: function ( val ) {
return '-' + optionalQuotes( val );
},
parser: /(?:^| +)-("(?:\\.|[^"\\])+"|[^\s":]+)(?= |$)/gi
},
{
group: 'text',
id: 'hastemplate',
placeholder: 'hastemplate:…',
formatter: function ( val ) {
return 'hastemplate:' + optionalQuotes( val );
},
parser: /(?:^| +)\bhastemplate:("(?:\\.|[^"\\])+"|[^\s"]+)/gi
},
{
group: 'text',
id: 'insource',
placeholder: 'insource:…',
formatter: function ( val ) {
return 'insource:' + ( /^\/.*\/$/.test( val ) ? val : optionalQuotes( val ) );
},
parser: /(?:^| +)\binsource:(\/\S+\/|"(?:\\.|[^"\\])+"|[^\s"]+)/gi
},
// Titles and headlines
// local:…
{
group: 'title',
id: 'prefix',
placeholder: 'prefix:…',
formatter: function ( val ) {
return 'prefix:' + val;
},
parser: /(?:^| +)\bprefix:(.+)/gi,
greedy: true
},
{
group: 'title',
id: 'intitle',
placeholder: 'intitle:…',
formatter: function ( val ) {
return 'intitle:' + optionalQuotes( val );
},
parser: /(?:^| +)\bintitle:("(?:\\.|[^"\\])+"|[^\s"]+)/gi
},
// Categories
{
group: 'categories',
id: 'deepcat',
placeholder: 'deepcat:…',
/* enabled: function () {
return !!mw.libs.deepCat;
}, */
formatter: function ( val ) {
return 'deepcat:' + optionalQuotes( val );
},
parser: /(?:^| +)\bdeepcat:("(?:\\.|[^"\\])+"|\S+)/gi
},
{
group: 'categories',
id: 'deepcat2',
placeholder: 'deepcat:…',
/* enabled: function () {
return !!mw.libs.deepCat;
}, */
formatter: function ( val ) {
return 'deepcat:' + optionalQuotes( val );
},
parser: /(?:^| +)\bdeepcat:("(?:\\.|[^"\\])+"|\S+)/gi
},
/* {
group: 'categories',
id: 'incategory',
placeholder: 'incategory:…',
formatter: function ( val ) {
return 'incategory:' + optionalQuotes( val );
},
parser: /(?:^| +)\bincategory:("(?:\\.|[^"\\])+"|\S+)/gi
}, */
// Files
// filebits:…
// filesize:…
{
group: 'files',
id: 'filetype',
placeholder: 'filetype:…',
formatter: function ( val ) {
var types = '';
val.split( ',' ).forEach( function ( type ) {
type = $.trim( type ).replace( /\W+/g, '/' ).replace( /^\w*\W+(?=\w{3})/, '' ).toLowerCase();
switch ( type ) {
case '':
break;
// Individual file types with non-standard alternatives
case 'bitmap':
case 'image':
case 'bild':
types += 'filetype:bitmap';
break;
case 'audio':
case 'music':
case 'musik':
types += 'filetype:audio';
break;
case 'drawing':
case 'vector':
case 'vektor':
case 'zeichnung':
types += 'filetype:drawing';
break;
// Other known file types
case 'multimedia':
case 'office':
case 'video':
types += 'filetype:' + type;
break;
// Individual MIME types with non-standard alternatives
case 'flac':
types += 'filemime:audio/flac';
break;
case 'midi':
case 'mid':
types += 'filemime:audio/midi';
break;
case 'wav':
case 'wave':
types += 'filemime:audio/wav';
break;
case 'jpg':
types += 'filemime:image/jpeg';
break;
case 'tif':
types += 'filemime:image/tiff';
break;
case 'svg':
case 'xml':
types += 'filemime:xml/svg';
break;
// Other known MIME types
case 'ogg':
case 'pdf':
types += 'filemime:application/' + type;
break;
default:
types += 'filemime:' + ( /\W/.test( type ) ? type : 'image/' + type );
break;
}
} );
return types;
},
requiredNamespace: 6,
parser: /(?:^| +)\bfile(?:mime|type):(?:\w{3,}\/)?(\w{3,})/gi
},
{
group: 'files',
id: 'filew',
placeholder: 'filew:…',
formatter: function ( val ) {
return 'filew:' + formatSizeConstraint( val );
},
requiredNamespace: 6,
parser: /(?:^| +)\bfilew(?:idth)?\b:?([<>\d.,]+)/gi
},
{
group: 'files',
id: 'fileh',
placeholder: 'fileh:…',
formatter: function ( val ) {
return 'fileh:' + formatSizeConstraint( val );
},
requiredNamespace: 6,
parser: /(?:^| +)\bfileh(?:eight)?\b:?([<>\d.,]+)/gi
}
/* {
group: 'files',
id: 'fileres',
placeholder: 'fileres:…',
formatter: function ( val ) {
return 'fileres:' + formatSizeConstraint( val );
},
requiredNamespace: 6,
parser: /(?:^| +)\bfileres\b:?([<>\d.,]+)/gi
} */
// Ordering
// prefer-recent:…
// boost-templates:…
// Meta
// linksto:…
// neartitle:…
// morelike:…
];
var i18n = {
'de': {
'advanced-search': 'Erweiterte Suchoptionen',
text: 'Text',
phrase: 'Genau diese Wortgruppe:',
fuzzy: 'Ungefähr dieses Wort:',
not: 'Nicht dieses Wort:',
hastemplate: 'Nur Seiten mit dieser Vorlage:',
insource: 'Suche im Wikitext:',
title: 'Titel',
prefix: 'Seitentitel beginnt mit:',
intitle: 'Seitentitel enthält:',
categories: 'Kategorien',
deepcat: 'In dieser oder tieferer Kategorie:',
deepcat2: 'Zweite Kategorie zur Querschnittssuche:',
incategory: 'Nur direkt in dieser Kategorie:',
files: 'Dateien',
filetype: 'Dateien dieses Typs:',
filew: 'Dateibreite in Pixel:',
fileh: 'Dateihöhe in Pixel:',
fileres: 'Diagonalauflösung in Pixel:'
},
'en': {
'advanced-search': 'Advanced search options',
text: 'Text',
phrase: 'Exactly this phrase:',
fuzzy: 'Approximately this word:',
not: 'Not this word:',
hastemplate: 'Only pages with this template:',
insource: 'Search in wikitext source:',
title: 'Title',
prefix: 'Page title starts with:',
intitle: 'Page title contains:',
categories: 'Categories',
deepcat: 'In this or deeper category:',
deepcat2: 'Second category to intersect with:',
incategory: 'Only in this category:',
files: 'Files',
filetype: 'File type:',
filew: 'File width in pixels:',
fileh: 'File height in pixels:',
fileres: 'Diagonal resolution in pixels:'
}
};
/**
* @param {string} key
* @return {string}
*/
function msg( key ) {
var lang = mw.config.get( 'wgUserLanguage' );
return i18n[lang] && i18n[lang][key] || i18n.en[key] || '<' + key + '>';
}
/**
* @param {RegExp|undefined} regexp
* @param {string} val
* @return {Array|boolean}
*/
function lastMatch( regexp, val ) {
var match,
lastMatch = false;
while ( regexp && ( match = regexp.exec( val ) ) ) {
lastMatch = match;
}
return lastMatch;
}
/**
* @param {string} fullQuery
* @return {string}
*/
function parseSearchOptions( fullQuery ) {
[ true, false ].forEach( function ( parseGreedyOptions ) {
for ( var i = advancedOptions.length; i--; ) {
var option = advancedOptions[i];
if ( parseGreedyOptions !== !!option.greedy ) {
continue;
}
var $field = $( '#advancedSearchOption-' + option.id );
if ( !$field.length || $.trim( $field.val() ) ) {
continue;
}
var match = lastMatch( option.parser, fullQuery );
if ( match ) {
fullQuery = fullQuery.slice( 0, match.index )
+ fullQuery.slice( match.index + match[0].length );
$field.val( trimQuotes( match[1] ) );
}
}
} );
return $.trim( fullQuery );
}
/**
* @param {string} [fullQuery]
* @return {string}
*/
function formatSearchOptions( fullQuery ) {
var greedyQuery = '';
fullQuery = fullQuery || '';
advancedOptions.forEach( function ( option ) {
var $field = $( '#advancedSearchOption-' + option.id );
if ( !$field.length ) {
return;
}
var val = $.trim( $field.val() );
if ( val ) {
// FIXME: Should fail if there is more than one greedy option!
if ( option.greedy && !greedyQuery ) {
greedyQuery += ' ' + option.formatter( val );
} else {
fullQuery += ' ' + option.formatter( val );
}
$field.val( '' );
if ( option.requiredNamespace ) {
$( '#mw-search-ns' + option.requiredNamespace ).prop( 'checked', true );
}
}
} );
return $.trim( fullQuery + greedyQuery );
}
mw.loader.using( [ 'user.options', 'oojs-ui' ], function () {
// Shorten out if AdvancedSearch extension is enabled as Beta feature
if ( mw.user.options.get( 'advancedsearch' ) ) {
return;
}
var $search = $( 'form#search, form#powersearch' ),
optionSets = {};
advancedOptions.forEach( function ( option ) {
if ( option.enabled && !option.enabled() ) {
return;
}
var id = 'advancedSearchOption-' + option.id;
if ( !optionSets[option.group] ) {
optionSets[option.group] = [];
}
optionSets[option.group].push(
$( '<div>' )
.css( { display: 'table-row' } )
.append(
$( '<label>' )
.prop( { 'for': id, title: option.placeholder || '' } )
.css( {
display: 'table-cell',
'text-align': 'right',
'padding-right': '0.5em',
width: '23em'
} )
.text( msg( option.id ) ),
$( '<input>' )
.prop( { id: id } )
.css( { display: 'table-cell' } )
)
);
} );
var $allOptions = $( '<fieldset>' )
.css( {
background: 'linear-gradient(rgba(0, 0, 0, 0.1), #fff 0.5em)',
'border-color': '#c8ccd1',
'border-top': 0,
'box-sizing': 'border-box',
margin: 0,
'margin-top': '-1px',
'max-width': '50em',
padding: '0.2em 1em'
} )
.hide();
for ( var group in optionSets ) {
var $optionSet = $( '<fieldset>' )
.css( {
border: 'none',
'border-top': 'solid 1px #c8ccd1',
margin: 0
} )
.append( $( '<legend>' )
.css( { color: '#666' } )
.text( msg( group ) )
);
optionSets[group].forEach( function ( $option ) {
$optionSet.append( $option );
} );
$allOptions.append( $optionSet );
}
var advancedButton = new OO.ui.ButtonWidget( {
label: msg( 'advanced-search' )
// indicator: 'down'
} ).on( 'click', function () {
var $searchField = $search.find( 'input[name="search"]' ),
query = $searchField.val();
$allOptions.toggle();
if ( $allOptions.is(':visible') ) {
// Clean the top-right search box to avoid confusion
var $topSearchField = $( '#searchInput' );
if ( $topSearchField.val() === query ) {
$topSearchField.val( '' );
}
query = parseSearchOptions( query );
} else {
query = formatSearchOptions( query );
}
$searchField.val( query );
} );
var $advancedButton = advancedButton.$element.css( {
clear: 'both',
display: 'block',
margin: 0,
'max-width': '50em',
'padding-top': '0.3em',
position: 'relative'
} );
$advancedButton.children().css( {
'background-image': 'url(//de.wikipedia.org/w/load.php?modules=oojs-ui.styles.indicators&image=down)',
'background-position': '99%',
'background-repeat': 'no-repeat',
'background-size': '18px',
display: 'block',
'text-align': 'left'
} );
$search.append( $advancedButton, $allOptions );
$search.on( 'submit', function () {
var $searchField = $( this ).find( 'input[name="search"]' ),
query = formatSearchOptions( $searchField.val() );
$searchField.val( query );
// Copy to the top-right search box for the sake of completeness
$( '#searchInput' ).val( query );
} );
} );
mw.loader.load( '//de.wikipedia.org/w/index.php?title=Benutzer:TMg/advancedSearch.js/namespaceFilters.js&action=raw&ctype=text/javascript' );
mw.loader.load( '//de.wikipedia.org/w/index.php?title=MediaWiki:Gadget-DeepCat.js&action=raw&ctype=text/javascript' );
} )( mediaWiki, jQuery );